JAXenter.de

Das Portal für Java, Architektur, Cloud & Agile
X

Schon abgestimmt im Quickvote? Docker versus Rocket - wer hat Recht?

Philosophisches über Enterprise Computing

Wenn man in einer ruhigen Stunde die historische Entwicklung von Enterprise Java Revue passieren lässt und in einem Gedanken-Experiment fortschreibt, kann man auf die in diesem Artikel beschriebene Vision schlußfolgern. Es könnte so kommen, vielleicht aber auch nicht ...

Ich habe einmal meine Gedanken über Enterprise Computing (nicht nur Java) spielen lassen und bin zu der folgenden Vision gekommen, die ich hier kurz vorstellen möchte. Vorab: Mit einem Gedankenexperiment ist es so eine Sache. Eine falsche Schlussfolgerung in der Kette und alle weiteren Folgerungen sind Unsinn. Doch dieses Risiko muss ich eingehen, vielleicht ist der Rest dieses Textes einfach falsch.

Dem Thema will ich mich von verschiedenen Seiten annähern. Die jeweils verschiedenen Richtungen kumulieren in der geäußerten Vision.

1. Richtung: Alles bleibt anders

Wenn ich mich mit manchen Kollegen unterhalte, habe ich den Eindruck, dass diese glauben, der Status Quo des Enterprise Computing gilt für ewig. Ich denke, dies trifft nicht zu.

2. Richtung: semantische Lücke

Eine bestimmte fachliche Anforderung kann in verschiedenen Programmiersprachen implementiert werden.

Auch wenn ausschließlich Java verwendet wird, kann die Implementierung je nach Umgebung und nichtfunktionalen Anforderungen (Geschwindigkeit, Ressourcenverbrauch, Fehlertoleranz) unterschiedlich ausfallen. Beispielsweise kann eine Implementierung für Multi-Threading (Multi-Core) völlig anders aussehen als eine Single-Thread-Implementierung. Weiter fällt mir ein: Batch vs. GUI, lokal vs. verteilt, Java-Code vs. Stored Procedures der Datenbank, JavaScript-Client-Code vs. Java-Server-Code.

Die semantische Idee bleibt aber dieselbe. Leider kann man eine semantische Idee nicht einfach je nach Situation (dynamisch) durch die jeweils sinnvollste Implementierung umsetzen lassen, sozusagen die Implementierung umschalten.

3. Richtung: virtuelle Maschinen und Bytecode

Wir haben verinnerlicht, dass die JVM den Code optimiert und zur Laufzeit lernt.

Einerseits sind lernende VMs gut genug gegenüber statisch kompilierenden und optimierenden Systemen (C/C++).  Andererseits kann eine Optimierung zur Laufzeit zusätzlich dynamische Einflüsse wie die zu verarbeitende Datenmenge  oder die Anzahl zur Verfügung stehender Kerne mit einbeziehen und sollte also eventuell sogar besser als eine statische Optimierung sein.

In virtualisierten Umgebungen, unter konkurrierender Last oder in der „Cloud“ (Aua, ich habe dieses Wort benutzt) weiß die JVM erst zur Laufzeit, wie viel Kerne, Speicher und Netz-Leistung zur Verfügung stehen. Bei der Optimierung des Codes benötigt die JVM eine abstrakte Sicht auf die Idee des Codes, zum Beispiel, ob über ein Array iteriert wird.

Da die Bytecode-Operationen feingranularer als eine semantische Idee sind (zum Beispiel eine Schleife über ein Array mit irgendeiner Operation), muss die JVM den Bytecode anhand von Mustern analysieren, um von der niedrigeren Abstraktionsstufe des Bytecodes auf eine höhere Abstraktionsstufe zu schließen, also sozusagen dekompilieren.

Daraus schließe ich, dass Java-Bytecode nur eine willkürlich und aus historischen Gründen gezogene Abstraktionslinie ist. Selbst Java-Quellcode ist eine willkürliche Abstraktionsebene, welche die semantische Idee eventuell eher verschleiert als verdeutlicht, diese aber eindeutig, vollständig und korrekt abbilden muss.

Wie schon gesagt, kann dieselbe semantische Idee unterschiedlich realisiert werden.

Ich hatte bereits vor einigen Jahren die Idee, Prinzipien der Optimierung zur Laufzeit der JVM in die Anwendungsebene zu heben, zum Beispiel eine Liste, welche je nach Laufzeitsituation die günstigste Implementierung, ArrayList für effektiven wahlfreien Zugriff gegenüber LinkedList für effektives Einfügen und Löschen, austauscht.

Dafür benötigt der entsprechende Anwendungscode Informationen von der VM,  welche erst zur Laufzeit vorliegen, wie Stacktrace und lokale Variable, zu verarbeitende Datenmenge und Laufzeit.

Leider ist die Optimierung der JVM auf eine VM begrenzt. Eine lokale JVM kann innerhalb einer verteilten Applikation nicht ein Ergebnis, welches in einer anderen JVM bereits vorhanden ist (zum Beispiel eine Summe), verwenden. Es ist auch nicht möglich, einen Algorithmus durch einen effektiveren Algorithmus zu ersetzen.

4. Richtung: objekt-relationales Mapping, Hibernate und Co.

Hibernate erweitert Java-Klassen (Beans) mittels Bytecode-Instrumentierung um eine Funktionalität zum Nachladen von Daten im Rahmen der aktuellen Hibernate-Session. Dadurch ist es möglich, noch nicht geladene, aber vom Anwendungscode abgefragte Daten für den Anwendungscode transparent nachzuladen.

Es wäre denkbar, dass im Persistenzframework genauso geprüft wird, ob geladene Daten überhaupt vom Anwendungscode abgefragt wurden. Wenn nun der Stacktrace des Aufrufes und eventuelle Zusatzinformationen (Parameter) für die jeweilige Datenabfrage in einer Map als Schlüssel und die abgefragten Felder als Key vermerkt werden, kann das Persistenzframework das Verhalten des Anwendungscodes erlernen und jeweils immer die genau erforderlichen Felder laden.

Die Fetch-Strategie (Lazy, Eager, Join, Batch) ist in der Konfiguration festgelegt oder muss vom Anwendungscode gesetzt werden. Genauso wie das Erlernen der abgefragten Daten (Felder) wäre das Erlernen der jeweils günstigsten Fetch-Strategie denkbar. Dafür müsste die jeweilige Fetch-Strategie entsprechend dem jeweiligen Anwendungskontext bewertbar und auswählbar sein.

Eine in einem Last-Test getunte Fetch-Strategie kann in der echten Umgebung suboptimal sein, da die Last-Test-Umgebung nicht 100-prozentig der echten Umgebung gleichen kann und sich die Situation dynamisch ändern kann.

Natürlich muss es auch Monitoring-Möglichkeiten geben, um bei sich nicht stabilisierenden Lernergebnissen erforderliche Zusatzinformationen an das Framework zu geben. Wenn zum Beispiel alle Aufrufe durch eine Fassade mit einem Operations-Parameter laufen, reicht der StackTrace nicht zum Identifizieren des Aufrufes aus. Dieser Operations-Parameter muss dem Persistenzframework mitgeteilt werden.

Variable Parameter-Werte müssen in Äquivalenzklassen unterteilt werden. Eventuell müssen dem Persistenzframework Mapper vom Parameterwert zur Äquivalenzklasse übergeben werden, da das Persistenzframework applikationsabhängige Äquivalenzklassen nicht kennen kann.

Außerdem müssten die Lernergebnisse persistierbar sein (XML & Co), um ständiges Neulernen nach jedem Applikationsstart zu vermeiden. Ein Batch, der nur eine primäre Datenbankabfrage macht und dann die selektierten Daten verarbeitet, könnte ohne persistiertes Lernergebnis nie vom Lernen profitieren.

5. Richtung: JEE und Spring

Hinter Java EE und Spring steht die Idee, "mach es in Java". Die GUI und die Datenbank sind ein Detail, welches möglichst versteckt wird. Die Fachlogik ist in Java geschrieben, das können wir, das haben wir im Griff. Einfachste standardkonforme Grund-Operationen auf der Datenbank reichen aus. Falls es doch mal zu langsam wird, tunen wir an einer Stelle mit speziellem SQL oder Stored Procedures.

Ich bin der Meinung, dass dies nicht der Sonderfall ist. Höhere Anforderungen an die Performance oder große Datenmengen tauchen regelmäßig auf, und das Tunen wird zum Normalfall. Die Fachlogik ist nicht mehr nur in Java notiert.

Die Vision

Ich fasse die Punkte noch einmal zusammen:

-       Dieselbe semantische Idee kann unterschiedlich implementiert werden.

-       Daten und Umgebungen sind dynamisch, Optimierung zur Laufzeit und Lernen ist gut.

Falls diese Voraussetzungen stimmen komme ich zu folgender Vision des Enterprise Computing:

Es wird virtuelle Maschinen (nicht JVMs) geben, die mehrere Rechner überstreichen. Diese Meta-VMs beziehen auch Datenbanken (im Moment habe ich da nur klassisch relationale DBs im Blick, NoSQL sollte aber auch möglich sein) und Clients (GUI, JavaScript) mit ein.

Dies ist zunächst einmal ein Konfigurations- und Deployment-Problem. Mit Konfigurations-Dateien, die IP-Adressen und Ports enthalten, kommt man hier nicht weit. Diese Informationen sollten bereits in maschinenübergreifenden Betriebssystemen konfiguriert sein.

In einer höheren Sprache (nicht Java) sind die auszuführenden Operationen notiert, welche je nach dynamischer Situation in der geeignetsten Maschine ausgeführt werden. Wird zum Beispiel die Summe einer Liste von Zahlen benötigt, kann diese im Client berechnet werden, wenn dort die Einzelwerte bereits für die Anzeige vorhanden sind.

Sind die Einzelwerte auf dem Client nicht vorhanden, aber auf dem Server, so berechnet der Server die Summe. Sind die Einzelwerte nur in der Datenbank vorhanden, berechnet ein SQL-Statement oder eine dynamisch erstellte stored procedure die Summe.

Die Semantik muss bei allen Alternativen gleich und wohldefiniert sein, variabel ist nur die Ausführung und ihr Verbrauch an Ressourcen. An die Meta-VM wird der Code in der hohen Abstraktionsstufe des ursprünglichen Quellcodes, aber vorher statisch gegen Fehler geprüft, übergeben.

Mit dem Verbergen des Quellcodes (Obfuscation) sieht es dann allerdings schlecht aus, dies steht aber nicht im Fokus meiner Überlegungen.

Die Sprache und ihre Laufzeitumgebung

Bei der ganzen Überlegung ist bisher etwas ganz Wesentliches außen vor geblieben, nämlich die Frage: Wie soll denn die ominöse Supersprache aussehen?

Java kann dies nicht sein, denn die Ausführung soll schließlich auch auf dem Client und der Datenbank möglich sein. Java ist zwar turingvollständig und kann somit prinzipiell alle kodierbaren Probleme abbilden. Mit GWT gibt es zudem einen Übersetzer von Java nach JavaScript, und Stored Procedures in Java gibt es auch. Aber wir wollen der Meta-VM helfen, indem wir die zu lösenden Aufgaben so abstrakt formulieren, dass die Meta-VM ohne Zurückschließen auf die semantische Idee die beste Ausführungsvariante auswählen kann.

Ich vermute, dass die Sprache Collections besitzen wird, die als persistente Collections per Konfiguration auf Datenbank-Tabellen abgebildet werden können. NoSQL und andere Persistenzlösungen (WebServices und andere Schnittstellen) sollten auch möglich sein. Auf die Collections gibt es SQL-ähnliche Operationen wie Suchen, Einfügen, Löschen und Ändern.

Aber statt mit Einzeldatensätzen wird mit Objekt-Graphen gearbeitet, während Joins durch Pfad-Expressions (siehe Hibernate-Query-Language) ersetzt werden. Constraints (Design by Contract, Bean Validation JSR 303) werden am Modell notiert.

Aufgrund der hohen Aussagekraft funktionaler Sprachen wird die vermutete Sprache sicher funktional sein. Doch aktuelle funktionale Sprachen sind noch sehr von der letztendlich imperativen Ausführung abhängig. Eine der wenigen automatischen Optimierungen ist die Tail Call Recursion. Von einer Notation des Was statt des Wie ist man noch weit entfernt.

In der Supersprache würde eine nahe an die Ausführung angelehnte Notation die automatische Optimierung unterlaufen. Die Notation wird sicher eher in Richtung Contraint Logic Programming gehen, Bedingungen und Ziele werden als Expressions bzw. Gleichungen formuliert.

Frühere 4GL-Sprachen (ich selbst habe mit dem Clipper gearbeitet) hatten das Problem, Black-Boxen zu sein. Vorgesehene Dinge waren sehr leicht möglich, nicht vorgesehene Algorithmen wurden indes nicht unterstützt und man fiel mit einer langsamen Sprache auf ein sehr geringes Abstraktionsniveau zurück. Deshalb muss die Sprache erweiterbar sein. Zudem sollten für das jeweilige Sprachfeature wiederum Provider für die jeweilige Ziel-Umgebung zur Verfügung stehen. Ein Default-Provider in der Sprache selbst soll als Backup für den Fall, dass keine passende Provider-Zielumgebung-Paarung gefunden wird, zur Verfügung stehen.

Die 4GL-Sprachen der 90er-jahre wurden auch von den Web-GUIs verdrängt, weil die Oberfläche in der Sprache fest eingebaut war. Deshalb gehe ich davon aus, dass die Supersprache nur für das Backend geeignet ist und mit der jeweiligen GUI zum Beispiel über das bekannte MVC-Pattern verbunden wird. Sicherlich werden Mechanismen zur Bindung von Feldern des Datenmodells an GUI-Elemente benötigt.

Als Business-Sprache müssen Transaktionen unterstützt werden. Die Laufzeitumgebung enthält den lernenden Kern, welcher die Bewertung und Auswahl von Ausführungs-Alternativen erledigt. Die konkrete Ausführung der in der höheren Sprache notierten Operationen übernehmen Provider für die jeweiligen Ausführungs-Umgebungen.

So ist die Erweiterung der Sprache um nicht enthaltene/vorgesehene Operationen möglich. Als Beispiele fallen mir Geo-Location, Range-Operationen, Matrizen-Operationen, Bilderkennung usw. ein.

Da die beteiligten Komponenten (Clients, Server, Datenbanken) nicht alle von einem Hersteller stammen sollen, sind Standards für Auswahl und Bewertung der Ausführungs-Alternativen oder offene Systeme erforderlich.

Fazit

Wie gesagt, ist dies alles Spekulation und könnte auch völlig falsch sein. Aber vielleicht stimmt mir jemand in einigen Punkten zu, hat ergänzende Ideen, denkt auch einmal darüber nach oder lehnt stattdessen meine Meinung ab. Jedes Feedback ist willkommen!

 

Kommentare

von mayer81 (Unveröffentlicht) am
gibt es ein Java Framework mit dem man Stored Procedures in Java schreiben kann. Ähnlich wie GWT für Javascript?

von Heiner Kücker (Unveröffentlicht) am
Die Oracle-Datenbank und IBM-DB2 unterstützen stored procedures in Java nativ, einen solchen Konverter kenne ich nicht. Es handelt sich mehr um eine Vision als um eine konkrete Produktbeschreibung, es gibt aber bereits Datenbanken, bei denen ein Client dynamisch stored procedures anlegen kann. Genaue Kenntnisse habe ich darüber aber nicht. Eventuell reicht erst mal reines SQL.

Ihr Kommentar zum Thema

Als Gast kommentieren:

Gastkommentare werden nach redaktioneller Prüfung freigegeben (bitte Policy beachten).