Java Unified Expression Language

Enterprise Tales: Schnell, schneller, JUEL

Lars Röwekamp und Matthias Weßendorf

Webanwendungen auf Basis von JavaServer Faces stehen nicht selten in dem Ruf, unnötig langsam zu sein. Als Ursache sind schnell der komplexe Lifecycle sowie der damit einhergehende Stateful-Ansatz ausgemacht. Bei genauer Betrachtung stellt sich allerdings häufig heraus, dass die klassischen Performance-Leaks an ganz anderer Stelle zu finden sind. Das JSF-Framework gilt bei Kritikern (insbesondere bei denen, die sich nicht die Mühe gemacht haben, sich intensiv damit zu beschäftigen) als schwerfällig und „oversized“. Grund hierfür ist vor allem der 6-Phasen-Lifecycle sowie der im Hintergrund verwaltete Komponenten-State. Gerne wird dabei vergessen, dass mit der Version 2.0 gleich eine ganze Reihe sinnvoller Änderungen [1] in die Spezifikation eingeflossen ist, die sich genau dieser Thematik annehmen. Wieso also nach wir vor diese Kritik?

Ursache und Wirkung

Ist eine Webanwendung zu langsam, wird die Ursache dafür in der Regel zunächst einmal in der Service- oder Persistenzschicht gesucht. Nicht selten liegt das Problem aber deutlich weiter oben im Schichtenmodell und ist nicht einmal selbstgemachter Natur. Eine genauere Analyse mit einem Profiler zeigt oft, dass sehr viel Zeit im JSF Framework selbst verloren geht. Sollten die Kritiker also doch Recht haben und JSF vom Konzept her für große Anwendungen zu langsam sein? Natürlich nicht.

Um JavaServer Faces sinnvoll und vor allem performant zu nutzen, ist eine genaue Kenntnis des Lifecycles und der darin enthaltenen Phasen Pflicht. Hat man diese erst einmal im Detail verstanden, so wird schnell klar, wo Performance in der Anwendung verloren aber auch gewonnen werden kann. Ein Kandidat, der dabei leider viel zu selten unter die Lupe genommen wird, ist die vom JSF Framework verwendete Implementierung der Unified-Expression-Language-Spezifikation. Um das zu verstehen, muss man wissen, dass jeder U-EL-Ausdruck im Rahmen eines JSF Request/Response mindestens sechs Mal ausgewertet wird. Das macht aus Sicht von JSF durchaus Sinn, da die zur Auswertung verwendeten Werte innerhalb des JSF Lifecycles verändert werden dürfen. Verwendet man also Views mit relativ vielen EL-Ausdrücken, kann alleine das schon zu erheblichen Performanceeinbußen führen. Nachdem erst einmal das (Performanz-)Problem erkannt ist, stellt sich die Frage, wie eine mögliche Lösung aussehen kann. Ganz einfach: Man tauscht die vorhandene EL-Implementierung durch eine andere deutlich performantere aus: JUEL.

JUEL

JUEL [2] steht für Java Unified Expression Language und stellt eine auf Sourceforge gehostete Open-Source-Implementierung der im Rahmen des JSR-245 (JavaServer Pages 2.2 Maintenance Release [3]) spezifizierten U-EL 2.2 dar. Ausschlaggebend für die Umsetzung von JUEL war für das hinter der Implementierung stehende Team die Tatsache, dass die U-EL mit der Version 2.1 aus den JavaServer Pages API Packages herausgelöst wurde und mit javax.el ein eigenes Package bekam. Das ermöglicht nicht nur das separate Ausliefern der U-EL-Implementierung, sondern zusätzlich auch eine Nutzung innerhalb von Non-JSP/JSF-Anwendungen.

Eines der wesentlichen Ziele von JUEL ist die Bereitstellung eines High-Performance Parsers für EL-Ausdrücke. Laut eigenen Angaben ist der handgeschriebene und auf Performanz optimierte JUEL EL Parser bis zu einem Faktor 10 schneller als seine mit javacc [4] generierten Pendants. Eigene Tests und Projekte kamen zwar nicht ganz an diesen Wert heran, haben aber mit Faktor 2 bis Faktor 8 (je nach Art und Anzahl der verwendeten EL-Ausdrücke) durchaus einen erheblichen Performanzgewinn mit sich gebracht.

Eine weitere Performanzverbesserung wird durch die Vermeidung von unnötigen Parse-Vorgängen erreicht. Zu diesem Zweck bietet JUEL einen integrierten Caching-Mechanismus an. Sollte einem der mitgelieferte Caching-Mechanismus nicht ausreichen, kann dieser via Plug-in durch eine eigene Implementierung ausgetauscht werden.

myJUEL

Stellt sich nun die Frage, wie JUEL in das eigene Projekt integriert werden kann. JUEL nutzt hierfür den standardisierten Lookup für Service-Provider innerhalb der JAR-File-Spezifikation. In diesem Fall für den Service-Provider javax.el.ExpressionFactory, hinter dem sich die konkrete Implementierung de.odysseus.el.ExpressionFactoryImpl verbirgt. Ist JUEL die einzige U-EL-Implementierung im Classpath, so ist nichts weiter zu tun. Gibt es mehrere Implementierungen, muss der Classpath entsprechend angepasst werden. Alternativ können die zu JUEL gehörenden jars juel-impl-2.2.x.jar und juel-spi-2.2.x.jar auch direkt in das WEB-INF/lib-Verzeichnis gelegt und somit deren Nutzung forciert werden. Die Konfiguration der ExpressionFactory selbst kann via el.properties-Datei erfolgen. Aktuell stehen dazu die folgenden Properties zur Verfügung:

  • javax.el.cacheSize: Größe des zu verwendenden Expression-Cache.
  • javax.el.methodInvocations: Erlauben von parametrisierten Methodenaufrufen.
  • javax.el.varArgs: Erlauben von Methodenaufrufen mit variabler Argumentliste.
  • javax.el.nullProperties: Erlauben von Null-Properties.
  • javax.el.ignoreReturnType: Erlaubt das Ignorieren des angegebenen Return-Typs (aus Kompatibilitätsgründen mit der Referenzimplementierung).
Geschrieben von
Lars Röwekamp und Matthias Weßendorf
Kommentare

Schreibe einen Kommentar

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