Your SlideShare is downloading. ×
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell

6,027

Published on

Diese Bachelor-Arbeit vergleicht das Java EE Programmiermodell mit dem Programmiermodell der Sprach Scala und dem Web Framework Lift.

Diese Bachelor-Arbeit vergleicht das Java EE Programmiermodell mit dem Programmiermodell der Sprach Scala und dem Web Framework Lift.

Published in: Technology
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total Views
6,027
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
38
Comments
0
Likes
0
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. Bachelorthesis zur Erlangung des akademischen Grades Bachelor of Science in Wirtschaftsinformatik Vergleich des Scala Web-Frameworks Lift mit dem Java EE Programmiermodell Bachelorthesis im Fachbereich Wirtschaftswissenschaften II im Studiengang Wirtschaftsinformatik der Hochschule für Technik und Wirtschaft Berlin in Zusammenarbeit mit der adesso AGvorgelegt von: Felix MüllerMatrikelnummer: 524063eingereicht am: 9. Juni 2011Erstbetreuer: Prof. Dr. Ingo ClaßenZweitbetreuer: Prof. Dr. Harald BrandenburgBetrieblicher Betreuer: Eberhard Wolff
  • 2. InhaltsverzeichnisAbbildungsverzeichnis IIIListingsverzeichnis IVAbkürzungsverzeichnis VIII1 Einleitung 1 1.1 Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1.2 Zielsetzung der Arbeit . . . . . . . . . . . . . . . . . . . . . . . . . 1 1.3 Abgrenzung zu anderen Arbeiten . . . . . . . . . . . . . . . . . . . 2 1.4 Aufbau der Arbeit . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 Grundlagen 4 2.1 Ajax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 2.2 Java EE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 2.2.1 Java Persistence API . . . . . . . . . . . . . . . . . . . . . . 7 2.2.2 Bean Validation . . . . . . . . . . . . . . . . . . . . . . . . . 10 2.2.3 Dependency Injection for Java . . . . . . . . . . . . . . . . . 10 2.2.4 JavaServer Faces . . . . . . . . . . . . . . . . . . . . . . . . 11 2.3 Programmiersprache Scala . . . . . . . . . . . . . . . . . . . . . . . 16 2.3.1 Klassen und Objekte . . . . . . . . . . . . . . . . . . . . . . 17 2.3.2 Traits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 2.3.3 Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 2.3.4 Pattern Matching . . . . . . . . . . . . . . . . . . . . . . . . 26 2.3.5 Self-Type Annotationen . . . . . . . . . . . . . . . . . . . . 27 I
  • 3. Inhaltsverzeichnis 2.3.6 Implicit Conversions . . . . . . . . . . . . . . . . . . . . . . 29 2.4 Lift Web-Framework . . . . . . . . . . . . . . . . . . . . . . . . . . 303 Methodik der Evaluation 34 3.1 Beschreibung der Evaluation . . . . . . . . . . . . . . . . . . . . . . 34 3.2 Evaluationskriterien . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 3.2.1 Allgemeines . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 3.2.2 Funktionalität . . . . . . . . . . . . . . . . . . . . . . . . . . 36 3.2.3 Architektur . . . . . . . . . . . . . . . . . . . . . . . . . . . 384 Entwicklung der Beispielanwendung 40 4.1 Entwurf der Anwendung . . . . . . . . . . . . . . . . . . . . . . . . 40 4.1.1 Domänenmodell . . . . . . . . . . . . . . . . . . . . . . . . . 40 4.1.2 Anwendungsfälle . . . . . . . . . . . . . . . . . . . . . . . . 42 4.2 Umsetzung mit Java EE . . . . . . . . . . . . . . . . . . . . . . . . 43 4.2.1 Domänenmodell . . . . . . . . . . . . . . . . . . . . . . . . . 43 4.2.2 Anwendungsfälle . . . . . . . . . . . . . . . . . . . . . . . . 48 4.3 Umsetzung mit Scala und Lift . . . . . . . . . . . . . . . . . . . . . 68 4.3.1 Domänenmodell . . . . . . . . . . . . . . . . . . . . . . . . . 68 4.3.2 Anwendungsfälle . . . . . . . . . . . . . . . . . . . . . . . . 775 Durchführung der Evaluation 99 5.1 Allgemeines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99 5.2 Funktionalität . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104 5.3 Architektur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1136 Fazit 122 6.1 Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122 6.2 Ausblick . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123Literaturverzeichnis IXA Inhalt der CD XII II
  • 4. Abbildungsverzeichnis 2.1 Prozessfluss des Ajax Modells von [Wik] . . . . . . . . . . . . . . . 5 2.2 Lebenszyklus einer Persistent Entity aus [Obj] . . . . . . . . . . . . 8 2.3 MVC-Architektur von JSF aus [MM10] . . . . . . . . . . . . . . . . 12 2.4 JSF Standard Request-Response-Zyklus aus [EJ10] . . . . . . . . . 13 2.5 Kontrollfluss im View-First-Ansatz von Lift aus [Per11] . . . . . . . 31 2.6 Architektur von Lift aus [DCB11] . . . . . . . . . . . . . . . . . . . 32 4.1 Domänenmodell der Beispielanwendung . . . . . . . . . . . . . . . . 41 III
  • 5. Listingsverzeichnis 2.1 Eintragung des Faces-Servlet in der web.xml . . . . . . . . . . . . . 14 2.2 Einbinden der Namensräume der beiden Standard-Tag-Libraries . . 16 2.3 Einfache Scala Klasse . . . . . . . . . . . . . . . . . . . . . . . . . . 17 2.4 Klasse mit dazugehörigem Companion Objekt . . . . . . . . . . . . 19 2.5 Beispiel der Nutzung von Traits . . . . . . . . . . . . . . . . . . . . 20 2.6 Beispiel einer einfachen Funktion . . . . . . . . . . . . . . . . . . . 22 2.7 Beispiel der Zuweisung eines Funktionswertes . . . . . . . . . . . . 22 2.8 Anwendung einer Funktion höherer Ordnung . . . . . . . . . . . . . 22 2.9 Vollständig ausgeschriebener Aufruf von map . . . . . . . . . . . . . 23 2.10 Aufruf der map Funktion mit einem Platzhalter . . . . . . . . . . . 23 2.11 Beispiel einer Funktion mit mehreren Parameterlisten . . . . . . . . 24 2.12 Abbildung der sub Funktion auf mehrere Funktionen . . . . . . . . 24 2.13 Beispiel einer partiell angewandten Funktion . . . . . . . . . . . . . 25 2.14 Kontrollstruktur-ähnliche Konstrukte mit subTwoWith . . . . . . . 25 2.15 Bespiel von Pattern Matching in printInt . . . . . . . . . . . . . . . 26 2.16 Pattern Matching in Form einer partiellen Funktion . . . . . . . . . 27 2.17 Beispiel einer Self-Type Annotation . . . . . . . . . . . . . . . . . . 28 2.18 Beispiel einer Implicit Conversion . . . . . . . . . . . . . . . . . . . 29 4.1 JPA Grundgerüst der Java Gebäude Fachklasse . . . . . . . . . . . 43 4.2 String Attribute der Gebäude Klasse . . . . . . . . . . . . . . . . . 45 4.3 Implementierung der Beziehung zwischen Räumen und Gebäuden . 46 4.4 gebaeude Attribut in der Java Raum Fachklasse . . . . . . . . . . . 46 4.5 Anzahl der Fenster eines Raumes . . . . . . . . . . . . . . . . . . . 47 IV
  • 6. Listingsverzeichnis4.6 Raumausstattungen in der Raum Klasse . . . . . . . . . . . . . . . 474.7 Dao Interface für den Datenbankzugriff . . . . . . . . . . . . . . . . 484.8 Spezifisches Dao Interface für die Raum Klasse . . . . . . . . . . . . 494.9 EntityManager und findById Methode des AbstractJpaDao . . . . . 494.10 configure Methode des PersistenceModule . . . . . . . . . . . . . . 504.11 Erzeugung und Konfiguration des PersistenceModules . . . . . . . . 514.12 warp-persist Servlet Filter Eintrag in der web.xml . . . . . . . . . . 514.13 Bindung der DAO Interfaces an ihre Implementierungen . . . . . . 524.14 Ablegen des Injectors im ServletContext . . . . . . . . . . . . . . . 534.15 Injection durch Guice in AbstractController . . . . . . . . . . . . . 534.16 XHTML Grundgerüst in layout.xhtml . . . . . . . . . . . . . . . . . 554.17 Auszug aus messages Datei für das de Länderkürzel . . . . . . . . . 564.18 Anzeige aller Gebäude in gebaeude.xhtml . . . . . . . . . . . . . . . 564.19 GebaeudeTableModel Eintrag in faces-config.xml . . . . . . . . . . . 574.20 TableModel Interface für die Abbildung von Tabellen . . . . . . . . 584.21 Laden aller Gebäude im GebaeudeTableController . . . . . . . . . . 594.22 Ajax Support zum Nachladen des ausgewählten Gebäudes . . . . . 594.23 GebaeudeTableSelectionHandler Eintrag in faces-config.xml . . . . . 604.24 Ereignisbehandlung in onSelectionChange . . . . . . . . . . . . . . 614.25 Anzeige der Bezeichnung des ausgewählten Gebäudes . . . . . . . . 624.26 Button Tag zum Speichern eines Gebäudes in gebaeude.xml . . . . 634.27 GebaeudeTableService Eintrag in faces-config.xml . . . . . . . . . . 634.28 Speichern des selektierten Gebäudes in AbstractTableController . . 644.29 Auszug des Tags für die Anzeige der Suchergebnisse . . . . . . . . . 654.30 Tag für den Suchbutton . . . . . . . . . . . . . . . . . . . . . . . . 664.31 Suchlogik in sucheRaeume Methode . . . . . . . . . . . . . . . . . . 664.32 Finder Methode für die Raumsuche . . . . . . . . . . . . . . . . . . 664.33 Auszug des Tags für das Löschen von Ausstattungsmerkmalen . . . 674.34 Abfragelogik, ob ein Ausstattungsmerkmal löschbar ist . . . . . . . 684.35 Finder Annotation von getAusstattungenByMerkmal . . . . . . . . 684.36 Deklaration der Scala/Lift Gebäude Fachklasse . . . . . . . . . . . 69 V
  • 7. Listingsverzeichnis4.37 Attribute der Building Klasse . . . . . . . . . . . . . . . . . . . . . 704.38 name Attribut der Building Klasse . . . . . . . . . . . . . . . . . . 704.39 Generische Signatur einer Mapper Validierungsfunktion . . . . . . . 714.40 Signatur von valMinLen . . . . . . . . . . . . . . . . . . . . . . . . 714.41 rooms Objekt der Building Klasse . . . . . . . . . . . . . . . . . . . 724.42 Überschriebene Löschfunktion der Building Klasse . . . . . . . . . . 734.43 Companion Objekt der Building Klasse . . . . . . . . . . . . . . . . 734.44 Rückgabe des Companion Objekts in getSingleton . . . . . . . . . . 744.45 Deklaration der Scala/Lift Raum Fachklasse . . . . . . . . . . . . . 744.46 Abbildung der Referenz auf ein Gebäude in Room . . . . . . . . . . 754.47 Anzahl der Fenster in der Scala/Lift Room Klasse . . . . . . . . . . 754.48 Erweiterter Validator für Ganzzahlen vom Typ Integer . . . . . . . 754.49 Raumausstattungen in der Room Klasse . . . . . . . . . . . . . . . 764.50 isDeletable Funktion der Attribute Klasse . . . . . . . . . . . . . . 774.51 Datenbankabfrage in der findByAttribute Funktion . . . . . . . . . 774.52 Verbindungseinstellungen für die Datenbank . . . . . . . . . . . . . 784.53 Datenbanksetup der Lift Anwendung . . . . . . . . . . . . . . . . . 794.54 Initialisieren der Datenbank . . . . . . . . . . . . . . . . . . . . . . 804.55 Snippet Konfiguration und Aufbau der SiteMap . . . . . . . . . . . 804.56 Ressourceneinträge für die Sitemap . . . . . . . . . . . . . . . . . . 814.57 Lift Servlet Filter Eintrag in web.xml . . . . . . . . . . . . . . . . . 814.58 Ausschnitt des allgemeinen Templates in default.html . . . . . . . . 834.59 Ausschnitt der Tabelle zum Anzeigen von Gebäuden . . . . . . . . . 844.60 Aufbau einer Tabelle im BaseTableSnippet . . . . . . . . . . . . . . 854.61 Generierung einer Tabellenzeile zur Anzeige eines Gebäudes . . . . 864.62 Erzeugung von selektierbaren Tabellenzeilen in SelectableTableRows 884.63 Ereignisbehandlung in Buildings für einen Klick auf eine Tabellenzeile 904.64 Speichern des ausgewählten Gebäudes in der Session . . . . . . . . 904.65 Anzeige der Gebäudesbezeichnung und des Speichern Buttons . . . 914.66 Validierung der Eingaben für ein Gebäude . . . . . . . . . . . . . . 924.67 Anzeige von Fehlern durch Lift . . . . . . . . . . . . . . . . . . . . 93 VI
  • 8. Listingsverzeichnis4.68 Speichern des ausgewählten Gebäudes . . . . . . . . . . . . . . . . . 944.69 Snippet-Funktionen für den Such-Button . . . . . . . . . . . . . . . 954.70 Snippet-Funktion für die Ausgabe der Suchergebnisse . . . . . . . . 964.71 Finder-Funktion für die Suche nach Räumen . . . . . . . . . . . . . 964.72 Tags für die Buttons zum Verwalten von Ausstattungsmerkmalen . 974.73 Bedingtes Rendern des Lösch-Button . . . . . . . . . . . . . . . . . 985.1 Exemplarische Gebäude Fachklasse mit CouchDB Anbindung . . . . 1075.2 Injector Trait von Lift für Dependency Injection . . . . . . . . . . . 1145.3 Deklaration der registerInjection Funktion . . . . . . . . . . . . . . 1155.4 Lift Dependency Injection Beispiel . . . . . . . . . . . . . . . . . . 1165.5 Dekoration mit withTransaction . . . . . . . . . . . . . . . . . . . . 1185.6 Service Trait . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1195.7 Mögliche Dynamic Proxy Integration in Scala . . . . . . . . . . . . 120 VII
  • 9. AbkürzungsverzeichnisAOP Aspektorientierte ProgrammierungAPI Application Programming InterfaceCRUD Create, Read, Update, DeleteDAO Data Access ObjectDI Dependency InjectionDSL Domain Specific LanguageHTML HyperText Markup LanguageIDE Integrated Development EnvironmentJCP Java Community ProcessJSON JavaScript Object NotationJSR Java Specification RequestXHTML eXtensible HyperText Markup LanguageXML Extensible Markup Language VIII
  • 10. 1 Einleitung1.1 MotivationDas Entwickeln von Anwendungen für das World Wide Web gewinnt stetig anBedeutung. Bereits heutzutage verdrängen Web-Anwendungen (z.B. Google Docs)einzelne Desktop-Anwendungen. Auf mobilen Geräten sind Web-Anwendungenbereits eine wirkliche Konkurrenz zu den nativen Applikationen. Daher ist esinteressant und wichtig, Frameworks, die das Entwickeln von Web-Anwendungenerleichtern und verallgemeinern, auf die tatsächliche Einsatztauglichkeit zu prüfenund zu testen.Auf der Hauptseite des Lift Web-Frameworks wird Lift als das derzeit mächtigs-te Framework für die Entwicklung von Web-Anwendungen bezeichnet.1 Mit derProgrammiersprache Scala setzt Lift zudem eine neue Sprache für die JVM einund zeigt damit gleichzeitig den Einsatz dieser neuen Sprache in einem größerenProjekt. Aufgrunddessen wird das Lift Web-Framework in dieser Arbeit mit demetablierten Java EE Programmiermodell verglichen.1.2 Zielsetzung der ArbeitZiel der Arbeit ist es, durch den Vergleich des Lift Web-Frameworks mit dem JavaEE Programmiermodell Antworten auf mehrere Fragestellungen zu erhalten, diefolgend zusammengefasst dargelegt werden. 1 siehe http://liftweb.net 1
  • 11. 1.3 Abgrenzung zu anderen ArbeitenLift soll aus möglichst vielen Blickwinkeln betrachtet werden, damit zum Endeder Arbeit die Vor- und Nachteile des Frameworks herausgearbeitet sind. Es giltaußerdem zu untersuchen, was die besten Einsatzbereiche des Lift Frameworkssind. Das Hauptziel ist dabei, Lift im Vergleich zum Java EE Programmiermodellzu bewerten, da dieses einen der am häufigsten eingesetzten Standards in derWeb-Entwicklung darstellt. Außerdem setzt Lift auf die Java EE Plattform auf,wie später noch erklärt wird, und tritt damit in mögliche Konkurrenz zum JavaEE Programmiermodell.1.3 Abgrenzung zu anderen ArbeitenAnders als in bisherigen Arbeiten wird Scala in dieser Arbeit nicht auf die Integrationmit bestehenden Java Anwendungen geprüft ([Kal10]) oder die Entwicklung einerWeb-Anwendung mit Scala und Lift ([TF11]) gezeigt. Im Mittelpunkt dieser Arbeitsteht der Vergleich von Scala und Lift als Technologiestack mit dem Java EEProgrammiermodell. Natürlich wurde dafür eine Beispielanwendung jeweils mitScala/Lift und Java EE entwickelt, was auch entsprechend ausführlich erläutertwird. Ebenso wird an einigen Stellen kurz die Integration mit Java angesprochen.Aber dies geschieht dann zwecks des Vergleichs und der Bewertung.1.4 Aufbau der ArbeitZu Beginn der Arbeit werden die Grundlagen der verwendeten Konzepte undeingesetzten Technologien erklärt. Dabei erfolgt eine Einführung in die Program-miersprache Scala. Allerdings kann keine vollständige Sprachdefinition gegebenwerden. Alle Sprachmittel, die zum Verständnis der Arbeit nötig sind, werdengezeigt und erläutert. Außerdem wird davon ausgegangen, dass der Leser dieserArbeit bereits mit der Sprache Java entwickelt, sodass eine Einführung in dieeingesetzten Java EE Techniken für das Verständnis ausreicht. 2
  • 12. 1.4 Aufbau der ArbeitDanach wird die Methodik der Evaluation beschrieben. Dabei werden die ver-wendeten Methoden erläutert und die Evaluationskriterien vorgestellt. Da fürden Vergleich der beiden Technologiestacks jeweils eine Beispielanwendung ent-wickelt wurde, folgt im Kapitel 4 die Darstellung der Entwicklung. Dabei wirddie Umsetzung der Prototypen anhand von Programmcodeauszügen erläutert. An-schließend wird die Evaluation durchgeführt. Dafür werden die zuvor festgelegtenEvaluationskriterien verwendet.Zum Abschluss wird das Ergebnis der Arbeit im Fazit zusammengefasst und einAusblick gegeben, indem weitere untersuchenswerte Themen benannt werden, diedurch die Arbeit entstanden sind.Die entwickelten Beispielanwendungen liegen dieser Arbeit auf einer CD bei, derenInhalt im Anhang kurz beschrieben wird. 3
  • 13. 2 Grundlagen2.1 AjaxDie Begrifflichkeit Ajax ist heutzutage ein gängiges Schlagwort bei der Entwicklungvon Web-Anwendungen. Damit wird ein Konzept bezeichnet, dass unter Verwendunggängiger Technologien die asynchrone Übertragung von Daten zwischen Server undClient, meistens dem Browser, ermöglicht. Ajax steht dabei für „AsynchronousJavaScript and XML“.1Web-Anwendungen, in denen Ajax eingesetzt wird, verhalten sich oftmals ähnlicheiner Desktop-Anwendung. Die Webseiten reagieren auf Interaktionen des Nut-zers flüssiger. Dies liegt daran, dass die Webseiten beim Einsatz von Ajax nichtkomplett neu geladen werden. Vielmehr werden sukzessive einzelne Seitenbereichenachgeladen. Damit dies funktioniert, wird auf Seiten des Clients eine Zwischen-schicht eingebaut, die Ajax Engine. Diese Engine ist üblicherweise eine existierendeJavaScript-Bibliothek, die die asynchrone Datenübetragung steuert.Eine Anfrage des Clients wird dabei nicht direkt an den Server gesendet, sondernüber eine Ajax Engine mittels XML, wie in Abbildung 2.1 zu sehen ist.2 Währendder Anfragebearbeitung auf dem Server kann der Client somit noch auf Nutzerin-teraktionen reagieren. Durch die Ajax Engine muss die Server-Antwort nur dienotwendigen Daten zur Änderung der Webseite enthalten, die anschließend von derAjax Engine zur Seitenaktualisierung genutzt werden.3 1 Vgl. [Wik] 2 Neben XML ist auch die Verwendung von HTML oder JSON häufig anzutreffen. 3 siehe [MM10, S. 230 ff.] 4
  • 14. 2.2 Java EE Abb. 2.1: Prozessfluss des Ajax Modells von [Wik]2.2 Java EEDie Java Platform Enterprise Edition (Java EE) gilt umgangssprachlich als dasJava für Geschäftsanwendungen. Da in diesem Zusammenhang oft unklar ist, wasJava EE wirklich ist und wie es sich in das bestehende Java Ökosystem eingliedert,erfolgt eine Einordnung von Java EE.Oftmals wird nicht zwischen Java als Sprache und Java als Plattform unterschie-den. Wobei mit Java SE, der Java Platform Standard Edition, durchaus dieseUnterscheidung vorliegt. Java SE definiert eine Plattform, die sich aus der JavaStandardbibliothek, der Java Virtual Machine und den Entwicklungstools (z.B. derJava Compiler) zusammensetzt. Hinzu kommen viele Bibliotheken von Drittanbie-tern. Die Java Standardbibliothek implementiert dabei die Kernfunktionalitätender Programmiersprache Java und liefert eine Fülle an bereits implementiertenKlassen. Darunter sind zum Beispiel Klassen für die Entwicklung grafischer Benut-zeroberflächen, für die Kommunikation über das Netzwerk oder auch Klassen zum 5
  • 15. 2.2 Java EEParsen von XML-Dokumenten.4„Die Java Platform, Enterprise Edition (Java EE) ist ein Aufsatz für die Java SEund integriert Pakete, die zur Entwicklung von Geschäftsanwendungen (Enterprise-Applikationen genannt) nötig sind.“5 Diese Beschreibung fasst es gut zusammen.Zusätzlich ist bei Java EE zwischen der Plattform und dem Programmiermodell zuunterscheiden. Unter Java EE als Plattform werden in dieser Arbeit die Komponen-ten der Laufzeitumgebung für Java Enterprise-Applikationen und die dafür nötigenTechnologien verstanden. So werden z.B. Anwendungen, die mit Java EE entwickeltwurden, üblicherweise in einem Application Server oder Webcontainer installiert,die als Laufzeitumgebung dienen. Java EE als Programmiermodell definiert Pro-grammierschnittstellen (APIs) und Frameworks, mit deren Nutzung mehrschichtige,lose gekoppelte und skalierbare Geschäftsanwendungen entwickelt werden.Java EE besteht dabei aus einer Vielzahl an Spezifikationen bzw. Standards, diedurch den JCP erarbeitet werden. JCP steht für den Java Community Processund bezeichnet einen Zusammenschluss von mehreren Firmen, Vereinigungen undEinzelpersonen, die in einem vorgeschriebenen Verfahren Spezifikationen für dieJava Technologie erarbeiten. Diese Spezifikationen werden Java Specification Re-quests (JSR) genannt. Dabei ist es üblich, dass durch eine Spezifikation lediglichdie abstrakte Programmierschnittstelle definiert wird und die konkreten Implemen-tierungen dafür durch mehrere Anbieter zur Verfügung gestellt werden. Dadurch istder Entwickler nicht von einem Hersteller abhängig und kann die Implementierungjederzeit austauschen.6In den folgenden Abschnitten wird auf die in der Arbeit verwendeten Spezifikationendes Java EE Programmiermodells eingegangen. 4 Vgl. [EJ10, Kapitel 2] 5 [Ull11, Kapitel 1.4.4] 6 siehe [Sta06, S. 22] 6
  • 16. 2.2 Java EE2.2.1 Java Persistence APIDer Standard für die Persistenzabbildung in Java EE wurde mit JPA im JSR 317spezifiziert. Die Java Persistence API ist dabei zusammen mit EJB 3.0 im JSR 220entstanden.7 Zwar ist JPA eine Java EE Spezifikation, es kann aber genauso inJava SE Anwendungen eingesetzt werden.8JPA definiert unter Nutzung eines objektrelationalen Mappers das Persistierenvon Java Objekten in einer relationalen Datenbank. Dabei wird das Data MapperPattern umgesetzt. Das Data Mapper Pattern ist ein Entwurfsmuster zur Separie-rung von Objekten und der Abbildung dieser in Datenbanktabellen. Die Objektehaben im Data Mapper Pattern keine direkte Kenntniss von der Datenbank. DieAbbildung in den Tabellen wird durch einen korrespondierenden Mapper geregelt.9Die Grundkonzepte von JPA sind Persistent Entities, der Entity Manager, derPersistence Context, die Persistence Unit und der JPA Provider.Der zentrale Bestandteil von JPA sind die Persistent Entities.10 Damit werdeneinfache Java-Objekte (Plain Old Java Objects, abgekürzt POJOs) bezeichnet, dieüber die Entity-Annotation durch JPA persistierbar werden.11 Als POJO wird einJava Objekt bezeichnet, dass keine externen Abhängigkeiten zu anderen Schnitt-stellen oder Frameworks hat und somit keiner Konvention, z.B. hinsichtlich derBenennung von Klassen oder Methoden, unterworfen ist.12 Sämtliche Informationenfür die Abbildung in der relationalen Datenbank werden in der Persistent Entityüber Annotationen angegeben. Alternativ können diese Informationen in Form vonDeployment Deskriptoren beschrieben werden, was dem Vorgehen vor der Nutzungvon Annotationen entspricht. Ein Deployment Deskriptor ist ein XML Dokument, 7 EJB (Enterprise Java Beans) beschreibt ein Programmiermodell zur Entwicklung von Java Komponenten, wobei dem Entwickler die Implementierung von infrastrukturellen Code, z.B. zur Transaktionssteuerung oder Lastverteilung, abgenommen wird. (Vgl. [OI07, S. 32]) 8 siehe [Wol10, S. 189] 9 Vgl. [Fow03, S. 165 ff.]10 In der Arbeit werden für die Persistent Entities auch Synonyme wie JPA Fachklassen oder JPA Entity verwendet.11 Vgl. [OI07, S. 187 f.]12 siehe [Fow] 7
  • 17. 2.2 Java EEdas unteranderem Meta-Informationen für Persistent Entities enthalten kann.13Das Speichern, Löschen, Laden und Suchen von Persistent Entities in einer Daten-bank übernimmt ein so genannter Entity Manager. Dabei werden die PersistentEntities von einem Entity Manager verwaltet. Mit dem Begriff Persistent Unitwerden dabei alle vom Entity Manager verwalteten Klassen bezeichnet. Im Ge-gensatz dazu werden alle vom Manager verwalteten Entities, also die Objekte derKlassen einer Persistent Unit, unter dem Begriff Persistence Context zusammenge-fasst. Dabei unterliegen die Persistent Entities einem Lebenszyklus, der durch dieVerwendung des Entity Managers bestimmt wird, wie Abbildung 2.2 zeigt.14 Abb. 2.2: Lebenszyklus einer Persistent Entity aus [Obj]Wurde eine Persistent Entity mit dem new-Operator instanziiert, befindet siesich im Zustand New. Zu dem Zeitpunkt ist die Entity nicht in der Datenbankabgebildet. Durch die persist- oder merge-Operation des Entity Managers kanndie Persistent Entity dem Persistence Context hinzugefügt werden. Dann wird dieEntity vom Entity Manager verwaltet und ist im so genannten Managed Zustand.Gleichzeitig wird die Persistent Entity dabei in der Datenbank abgebildet. Entwederwerden neue Datenbankeinträge angelegt oder bestehende aktualisiert. Je nachKonfiguration kann das auch erst beim Commit einer Transaktion oder durch denexpliziten Aufruf der flush-Methode erfolgen. Dadurch dass die Persistent Entities13 siehe [OI07, S. 189 f.]14 Vgl. [OI07, S. 194 ff.] 8
  • 18. 2.2 Java EEim Zustand Managed sind, werden Änderungen der Attributswerte vom EntityManager registriert und mit der Datenbank synchronisiert. Mit der find-Methodedes Entity Managers können Persistent Entities aus der Datenbank geladen werden.Diese sind dann ebenfalls im Managed Zustand. Sollen Entities in der Datenbankgelöscht werden, genügt es, die remove-Methode vom Entity Manager aufzurufen.Dafür müssen die Entities jedoch im Managed Zustand sein. Wird der EntityManager geschlossen, werden sämtliche Persistent Entities entkoppelt und gelangenin den Detached Zustand.Für die Abfrage von Persistent Entities aus der Datenbank wird in JPA eineAbfragesprache definiert, die Java Persistence Query Language (JPQL). Die JPQList unabhängig von der Persistenzschicht. Das bedeutet, die Abfragen werden nichtin SQL formuliert, sondern auf Ebene der Objekte. Diese Abfragesprache ist jedochSQL-ähnlich bei der Formulierung von Abfragen. Die formulierten JPQL Abfragenwerden über den Entity Manager ausgeführt.15Da JPA lediglich eine Programmierschnittstelle spezifiziert, muss für die Ver-wendung eine Implementierung der Schnittstellen eingebunden werden. SolcheImplementierungen werden JPA Provider genannt. Für JPA in der Version 2.0 istdas Projekt EclipseLink16 die Referenzimplementierung eines JPA Providers.17 Indieser Arbeit wird jedoch Hibernate als JPA Provider eingesetzt.18Konfiguriert wird ein JPA Provider über den Deployment Deskriptor in der Dateipersistence.xml. In der persistence.xml werden die Persistence Units definiertund der Entity Manager konfiguriert. So wird dort z.B. eingestellt, zu welchemZeitpunkt Persistent Entities in die Datenbank geschrieben werden oder welcheCaching Methode verwendet wird, um die Performance der Persistenzschicht zuverbessern.1915 Vgl. [OI07, S. 300 ff.]16 siehe http://www.eclipse.org/eclipselink17 siehe dazu http://www.eclipse.org/org/press-release/20080317_Eclipselink.php18 siehe http://www.hibernate.org19 Vgl. [OI07, S. 200 ff.] 9
  • 19. 2.2 Java EE2.2.2 Bean ValidationDer JSR 303 (Bean Validation) definiert für Java einen Standard zur Validierung vonObjekten. Dabei werden Klassen und deren Attribute mit Annotationen versehen,die die Informationen zur Validierung enthalten. Einige Annotationen werden durchden Bean Validation Standard bereits vorgegeben. Mit der Size-Annotation kannbeispielsweise die minimale und maximale Größe von Zeichenketten, Arrays undListen festgelegt werden. Möglichkeiten zur Formulierung eigener Annotationensind ebenso gegeben.20Dadurch dass die Validierungsregeln deklarativ mit Annotationen hinterlegt wer-den, ist die Ausführung der Validierung entkoppelt. Diese kann somit an mehrerenStellen in der Anwendung durchgeführt werden, z.B. im Webframework bei derEingabe von Daten in Formularen oder in der Persistenzschicht beim Speichernvon Anwendungsdaten. Der JPA Provider Hibernate bietet mit der Referenzim-plementierung des JSR 303, Hibernate Validator, unteranderem die Möglichkeit,die mit Bean-Validation-Annotationen hinterlegten Validierungsregeln im EntityManager einzusetzen und somit mit JPA zu vereinen.212.2.3 Dependency Injection for JavaWenn Anwendungen in Java entwickelt werden, bestehen diese aus vielen Klassen,die sich gegenseitig nutzen, um die erforderten Funktionalitäten umzusetzen. DieKlassen haben dadurch häufig mehrere Abhängigkeiten zu anderen Klassen. Diesemüssen aufgelöst werden. Das geschieht, indem Objekte von abhängigen Klassen amOrt ihrer Nutzung erzeugt werden oder bestenfalls bei der Erzeugung von Klassenüber den Konstruktor reingereicht werden. Das Problem bei diesem Vorgehen ist dieenge Kopplung der Klassen untereinander und die Unflexibilität bei Änderungen,z.B. beim Austausch einzelner Klassen für Testzwecke. Soll die Implementierungeiner Klasse ausgetauscht werden, müssen überall im Programmcode Änderungenerfolgen. Eine Lösung für dieses Problem ist Dependency Injection. Dabei werden20 Vgl. [Wol10, S. 250 ff.]21 siehe http://www.hibernate.org/subprojects/validator.html 10
  • 20. 2.2 Java EEabhängige Objekte zur Laufzeit injiziert. Das Objekt, dessen Abhängigkeitenaufgelöst werden, ist passiv bei diesem Vorgang. Das hat den Vorteil, dass zumeinen große Objektnetze einfach erzeugt werden können, da die Objekte ihreAbhängigkeiten injiziert bekommen, und zum anderen ist der Austausch vonImplementierungen darüber sehr leicht möglich.22Mit dem JSR 330, Dependency Injection for Java, werden im Paket javax.injectAnnotationen zur Verfügung gestellt, die die Nutzung und Implementierung vonDI in Java standardisieren. Das Versehen von Attributen, Konstruktoren oderMethoden mit der Inject-Annotation sorgt dafür, dass die abhängigen Objektean dieser Stelle injiziert werden. Um die Dependency Injection feiner zu steuern,können die Qualifier- und Named-Annotationen verwendet werden.23 In der Arbeitwird ausschließlich die Inject-Annotation genutzt.Genau genommen zählt der JSR 330 nicht zu den Java EE Spezifikationen. Aller-dings ermöglicht die Spezifikation das Bauen von lose gekoppelten Systemen in dergesamten Java Technologie, wovon Java EE eine Teilmenge ist.2.2.4 JavaServer FacesJavaServer Faces (JSF) ist ein komponentenbasiertes Framework für die Entwicklungvon Web-Anwendungen.24 Die in der Arbeit verwendete Version 1.2 wird durchden JSR 252 spezifiziert.25 Die Referenzimplementierung von JSF ist das MojarraProjekt, welches ebenfalls in dieser Arbeit eingesetzt wird.26Das zugrundeliegende Entwurfsmuster von JSF ist das Model-View-Controller-Muster (MVC). Durch das MVC-Entwurfsmuster sollen Anwendungen mit einergrafischen Oberfläche besser strukturiert werden. Ziel ist es, die Bestandteile derAnwendung wiederverwendbarer, erweiterbarer und wartbarer zu gestalten, indem22 Vgl. [Wol10, S. 20]23 Vgl. [Wol10, S. 81 ff.]24 Vgl. [MM10, S. 6]25 siehe http://www.jcp.org/en/jsr/detail?id=25226 siehe http://javaserverfaces.java.net 11
  • 21. 2.2 Java EEdie Daten (Model), die Darstellung (View) und das Verhalten (Controller) vonein-ander getrennt implementiert werden.27 Der Einsatz des MVC-Entwurfsmusters inJSF wird in Abbildung 2.3 gezeigt. Abb. 2.3: MVC-Architektur von JSF aus [MM10]Der Controller wird in JSF durch das Faces-Servlet implementiert. Dabei wird dieServlet Technologie der Java EE Plattform verwendet. Servlets werden durch denJSR 154 beschrieben.28 Für diese Arbeit genügt es zu wissen, dass Servlets denersten Versuch darstellten, dynamische Webseiten mit Java zu erzeugen. ServletKlassen können auf HTTP Anfragen reagieren und diese beantworten, indem sieim Java Code z.B. über Streams HTML schreiben.29 In JSF wird die ServletTechnologie nur intern verwendet. Das Faces-Servlet enthält die Steuerungslogikeiner JSF Anwendung. Dieses Servlet steuert aufgrund einer Anfrage des Clientsden Aufruf des benötigten Views und erzeugt die korrespondierenden Models.JSF unterstützt bisher zwei verschiedene View-Technologien: Facelets und JSP(JavaServer Pages). In dieser Arbeit wird Facelets verwendet. Der View wird beider Nutzung von Facelets durch XHTML-Dokumente implementiert, die spezifischeTags aus der Facelets Tag Library enthalten. Eine Tag Library ist eine Sammlungvon XHTML-Tags, die über einen eigenen XML Namensraum (engl. Namespace)27 Vgl. [Bos04, S. 45 ff.]28 sieh http://www.jcp.org/en/jsr/detail?id=15429 Vgl. [MM10, S. 2 ff.] 12
  • 22. 2.2 Java EEeindeutig identifizierbar sind. Außerdem kann die Darstellung bei Verwendung vonFacelets modular aufgebaut werden.30Ein View bzw. eine Ansicht wird in JSF aus Komponenten zusammengesetzt. Kom-ponenten sind eigenständige und wiederverwendbare Bausteine. Beim Erzeugeneines Views wird in JSF ein Komponentenbaum erzeugt. Durch den Komponen-tenbaum werden die einzelnen Komponenten einer Ansicht miteinander verknüpft.Das Wurzelelement des Baums wird dabei durch ein Objekt der Klasse UIViewRootrepräsentiert. Die Komponenten einer Ansicht werden als Kindelemente an dasWurzelelement gehängt. Die Bearbeitung einer Anfrage, die sich in JSF in meh-rere Phasen unterteilt, wie Abbildung 2.4 zeigt, beginnt immer mit einem Aufrufeiner Methode des UIViewRoot-Elements, das den Methodenaufruf rekursiv an dieKindelemente weiterleitet. Über diesen Mechanismus kann jede Komponente einerAnsicht auf eine Anfrage reagieren.31 Abb. 2.4: JSF Standard Request-Response-Zyklus aus [EJ10]30 siehe [MM10, S. 157 f.]31 Vgl. [MM10, S. 20 ff.] 13
  • 23. 2.2 Java EEEin weiteres wichtiges Konzept stellen die Managed Beans dar, auch Backing Beansgenannt, über die das Model in JSF implementiert wird. Managed Beans sindnormale POJOs. Sie liefern die Werte für die im View definierten Komponenten.Managed Beans können dabei verschiedene Gültigkeitsbereiche haben, die sichin der Dauer der Lebenszeit eines Managed Bean Objekts unterscheiden. In JSF1.2 ist dafür die Dauer der Requestbearbeitung, einer HTTP Session oder auchdie gesamte Dauer der Applikationsausführung möglich. Bevor ein normales JavaObjekt als Managed Bean gilt, muss ein Eintrag in der Konfiguration von JSFerfolgen.32Die Konfiguration von JSF wird in der faces-config.xml Datei formuliert. Außerdemmuss JSF in der web.xml Konfigurationsdatei eingerichtet werden. Damit eineJSF Anwendung funktioniert, muss ein Servlet Container (auch Webcontainergenannt) als Laufzeitumgebung genutzt werden.33 Ein solcher Servlet Containernutzt die Informationen aus der web.xml Datei, um z.B. Servlets zu starten. Dieweb.xml Datei ist ein Deployment Deskriptor für einen Servlet Container, eineBereitstellungsbeschreibung einer Anwendung. Das Faces-Servlet muss dort übereinen Eintrag hinterlegt werden.§ Listing 2.1: Eintragung des Faces-Servlet in der web.xml ¤< s e r v l e t> <s e r v l e t −name>Faces S e r v l e t</ s e r v l e t −name> <s e r v l e t −c l a s s> j a v a x . f a c e s . webapp . F a c e s S e r v l e t </ s e r v l e t −c l a s s> <load−on−s t a r t u p>1</ load−on−s t a r t u p></ s e r v l e t>¦ ¥Eine JSF-Anwendung wird in Form einer WAR-Datei bereitgestellt. Mit demWeb Application Archive (WAR) wird eine standardisierte Verzeichnisstruktur für32 Das gilt für JSF 1.2. Im neueren JSF 2.0 Standard ist die Definition von Managed Beans über Annotationen möglich.33 Der Servlet Container war bereits in Abbildung 2.3 in Form der Servlet Engine dargestellt, die ein Teil eines Servlet Containers ist. 14
  • 24. 2.2 Java EEWebanwendungen in Java EE definiert. So enthält beispielsweise das VerzeichnisWEB-INF in einer WAR-Datei die Konfiguration und Seitenbeschreibungen ei-ner Web-Anwendung. Als Archivierungsformat wird das bekannte JAR-Formatverwendet.Unified Expression LanguageMit der Unified Expression Language werden die Komponenten der Ansicht unddie dahinterliegenden Managed Beans miteinander verbunden. Darüber könnenim View Daten aus den Managed Beans gelesen und in diese geschrieben werden.Weiterhin werden über Expression-Language-Ausdrücke (kurz EL-Ausdruck) Me-thoden für die Ereignisbehandlung von Komponenten im View angegeben. EinEL-Ausdruck beginnt mit einer Raute und wird von geschweiften Klammern um-schlossen. Zwischen den Klammern kann der Name einer Managed Bean stehen unddurch Punkte getrennt die Attribute der Managed Bean. Mit dem Ausdruck #{per-son.vorname} wird beispielsweise auf das Attribut vorname der Managed Beanperson zugegriffen. Ebenfalls können einfache Operationen in einem EL-Ausdruckuntergebracht werden.34Standard-KomponentenJSF bietet eine Palette an Standard-Komponenten. Das sind bereits fertige JSFKomponenten, die die Grundlage für die Entwicklung von JSF Anwendungen bilden.Die Standard-Komponenten von JSF werden in den beiden Tag Libraries HTML-Custom-Tag-Library und Core-Tag-Library implementiert und können durch dasEinbinden des jeweiligen XML Namespaces in einer View-Beschreibung genutztwerden.35Die HTML-Custom-Tag-Library im XML-Namensraum h stellt Komponenten zurHTML-Ausgabe zur Verfügung. Die Core-Tag-Library mit dem XML-Namensraumf vereint wiederum alle Basis-Komponenten, die für die Nutzung von JSF benötigt34 Vgl. [MM10, S. 36 ff.]35 Vgl. [MM10, S. 109] 15
  • 25. 2.3 Programmiersprache Scalawerden. So wird beispielsweise mit dem f:view Tag der Core-Tag-Library derWurzelknoten eines Komponentenbaums in der Ansicht definiert.Das Einbinden der Namensräume erfolgt bei der Deklaration des HTML-Tags inder Seitenbeschreibung.§ Listing 2.2: Einbinden der Namensräume der beiden Standard-Tag-Libraries ¤<html xmlns=" h t t p : / /www. w3 . o r g /1999/ xhtml " xmlns : h=" h t t p : / / j a v a . sun . com/ j s f / html " xmlns : f=" h t t p : / / j a v a . sun . com/ j s f / c o r e ">¦ ¥RichFaces KomponentenbibliothekDie RichFaces Bibliothek erweitert die JSF Standard-Komponenten um vieleZusatzkomponenten. Außerdem implementiert RichFaces einige Komponenten mitUnterstützung für Ajax, was in JSF 1.2 noch nicht vorhanden ist. Zusätzlich dazubesteht die Möglichkeit, RichFaces Komponenten über einen Theming Mechanismusdurch eine Einstellung in der faces-config.xml im Aussehen anzupassen.2.3 Programmiersprache ScalaAn der École polytechnique fédérale de Lausanne (EPFL) in der Schweiz begann2001 eine Projektgruppe unter Leitung von Martin Odersky mit dem Entwurfeiner neuen Sprache, die objektorientierte und funktionale Programmierparadigmenvereinen sollte. Das Ergebnis dieser Arbeit wurde im Jahr 2003 mit der erstenVersion der Programmiersprache Scala veröffentlicht.36Die Namensgebung soll ausdrücken, dass Scala (aus dem Englischen scalable,skalierbar) an den Anforderungen des Entwicklers wächst.Die Programmiersprache Scala setzt auf die Java SE Plattform auf. Scala Codewird in Java Bytecode kompiliert und durch eine JVM ausgeführt. Außerdem kann36 Vgl. [Bra11, Kapitel 1] 16
  • 26. 2.3 Programmiersprache ScalaScala interpretiert ausgeführt werden. Dazu liegt der Scala Laufzeitumgebung eininteraktiver Interpreter bei, eine so genannte REPL.37Im Gegensatz zu Java ist Scala vollständig objektorientiert. Alles ist ein Objekt,auch primitive Datentypen. Dadurch dass Scala in Java-Bytecode kompiliert wird,kann von Scala aus Java Code genutzt werden. Scala ist interoperabel mit Java. InScala steht also die gesamte Java Standardbibliothek zur Verfügung und sämtlicheandere Java Bibliotheken.In den nächsten Abschnitten werden die in dieser Arbeit eingesetzten Sprachfeaturesvon Scala erläutert.2.3.1 Klassen und ObjekteScala ist objektorientiert und unterstützt daher das Konzept der Klassen undObjekte. Wie in Java wird das Schlüsselwort class zum Definieren einer Klasseverwendet. Methoden bzw. Funktionen werden mit dem Schlüsselwort def eingeleitet.Das folgende Listing zeigt eine einfach Scala Klasse, die ein typisches Hello-World-Beispiel umsetzt.§ Listing 2.3: Einfache Scala Klasse ¤c l a s s HelloWorld ( val g r e e t i n g : S t r i n g ) { def s a y H e l l o ( ) = p r i n t l n ( g r e e t i n g )}new HelloWorld ( " H a l l o ! " ) . s a y H e l l o ( ) // Ausgabe : H a l l o !¦ ¥Mit dem new-Operator wird wie in Java eine Instanz von HelloWorld erzeugt undanschließend die Methode sayHello ausgeführt.Anders als bei Java wird in Scala der Konstruktor direkt in Form einer Parame-terliste hinter dem Klassennamen angegeben. Die HelloWorld Klasse hat einen37 Die Read-Execute-Print-Loop beschreibt einen Interpreter, der einen Befehl einliest, ausführt, das Ergebnis ausgibt und anschließend auf die nächste Befehlseingabe wartet. 17
  • 27. 2.3 Programmiersprache ScalaParameter vom Typ String, greeting. Die Typangabe erfolgt in Scala nach der An-gabe des Bezeichners und mit einem Doppelpunkt getrennt. Mit dem Schlüsselwortval wird greeting als unveränderbares Feld definiert. Das ist mit der Verwendungvon final in Java vergleichbar. Soll greeting veränderbar sein, wird das Schlüsselwortvar anstalle von val verwendet.Die Sichtbarkeit von sayHello ist automatisch public. Anders als in Java habenMethoden und Felder standardmäßig nicht die Sichtbarkeit friendly, sondern public.Mit den Zugriffsmodifizierern private und protected kann die Sichtbarkeit wie inJava eingestellt werden.Die Methode sayHello hat nur eine Aufgabe: den mit greeting übergebenen Wertauf dem Standardausgabestrom ausgeben. Dazu wird die Methode println desPredef -Objektes aufgerufen. Das Predef -Objekt wird automatisch vom Compilerin jede Scala-Datei importiert und stellt oft benötigte Funktionen zur Verfügung.38Obwohl Scala statisch typisiert ist, muss der Rückgabetyp von sayHello nichtangegeben werden, da Scala über einen Typinferenz-Mechanismus verfügt. Darüberkann der Compiler feststellen, welchen Typ ein Wert im Aufrufkontext hat. Indiesem Fall, wird der Typ Unit hergeleitet, vergleichbar mit dem void Typ in Java.Da println lediglich eine Zeichenkette auf dem Standardausgabestrom ausgibt, wirdder Typ Unit zurückgegeben.Aufgrund der funktionalen Natur von Scala hat alles in Scala einen Wert. Andersals in Java muss zum Zurückgeben eines Wertes nicht return verwendet werden, esgilt der Wert des letzten Ausdrucks eines Programmblocks als Rückgabewert.Neben der Typinferenz verfügt Scala ebenfalls über eine Semikoloninferenz. Semi-kolons sind in Scala nicht notwendig, um das Ende eines Befehls zu signalisierenund werden größtenteils weggelassen.Ein Scala eigenes Sprachmittel sind Singleton Objekte.39 Die Idee des Singletonsentstammt einem Entwurfsmuster, bei dem von einer Klasse genau eine Instanz38 Außerdem werden dort auch wichtige Typ-Synonyme und Implicit Conversions definiert.39 Die Begriffe Objekt und Singleton werden in dieser Arbeit als Synonyme für den Begriff der Singleton Objekte verwendet. 18
  • 28. 2.3 Programmiersprache Scalaexistiert und ein globaler Zugriff darauf.40 Scala verallgemeinert dieses Muster mitder Nutzung des Schlüsselworts object und unterstützt es direkt in der Sprache.In Singleton Objekten werden in Scala statische Werte und Methoden implemen-tiert. Statische Methoden mit dem Schlüsselwort static wie in Java zu definieren,ist nicht möglich. Hierbei trennt Scala zwischen nicht-statischen und statischenProgrammcode-Teilen. Sollen einer Klasse statische Felder oder Methoden hinzuge-fügt werden, existiert in Scala dafür das Companion Objekt, welches im folgendenListing für das Hello-World-Beispiel implementiert ist. An der Implementierungder HelloWorld-Klasse ändert sich dabei nichts.§ Listing 2.4: Klasse mit dazugehörigem Companion Objekt ¤c l a s s HelloWorld ( val g r e e t i n g : S t r i n g ) { def s a y H e l l o ( ) = p r i n t l n ( g r e e t i n g )}object HelloWorld { def apply ( ) = new HelloWorld ( " Standard Meldung " ) def sayHi ( ) = p r i n t l n ( " Hi ! " )}HelloWorld ( ) . s a y H e l l o ( ) // Ausgabe : Standard MeldungHelloWorld . sayHi ( ) // Ausgabe : Hi !¦ ¥Ein Companion Objekt ist ein spezielles Singleton Objekt, das den gleichen Na-men wie eine Klasse hat und sich in der selben Quellcode-Datei befindet. ImCompanion Objekt HelloWorld werden zwei Methoden definiert, apply und say-Hi. Die sayHi Methode stellt eine normale statische Methode dar, die über denNamen des Companion Objekts aufgerufen werden kann. Die abgebildete apply-Methode ist eine Besonderheit bei der Nutzung von Companion Objekten. Mit40 Vgl. [EG95, S. 127] 19
  • 29. 2.3 Programmiersprache Scalader apply-Methode existiert eine eingebaute Fabrik-Methode in Scala. In einerapply-Methode wird eine Instanz der Klasse des Companion Objektes erzeugt undzurückgegeben. Der Vorteil dabei ist, Scala erlaubt das Weglassen des Aufrufs derapply-Methode. So kann HelloWorld().sayHello() geschrieben werden, was vomCompiler zu HelloWorld.apply().sayHello() erweitert wird. Die apply-Methode kannnatürlich beliebige Parameter aufnehmen.2.3.2 TraitsEin neues Sprachmittel, dass mit Scala eingeführt wird, sind Traits, die die aus Javabekannten Interfaces ersetzen. Ein Trait ist mit einem Java Interface vergleichbar.Allerdings mit dem Unterschied, dass Traits neben der Deklaration von Methodenauch Implementierungen beinhalten können. Ein Trait wird mit dem Schlüsselworttrait definiert, wie im folgenden Listing zu sehen ist.§ Listing 2.5: Beispiel der Nutzung von Traits ¤t r a i t HasName { val name = "Name von etwas "}t r a i t HasAge { def c a l c u l a t e A g e : I n t}c l a s s Person extends HasName with HasAge { def c a l c u l a t e A g e = 18 override val name = "Name e i n e r Person "}¦ ¥ 20
  • 30. 2.3 Programmiersprache ScalaWie bei der Klasse Person zu sehen ist, können Klassen mehrere Traits implemen-tieren. Der erste Trait wird immer mit extends hinzugefügt, alle folgenden mitdem Schlüsselwort with. Scala unterstützt allerdings keine Mehrfachvererbung. Eshandelt sich dabei um einen so genannten Mixin-Mechnismus. Ein Mixin ist einewiederverwendbare Einheit, die zusammengehörige Funktionen bündelt.41 In diesemZusammenhang wird auch vom Einmixen eines Traits gesprochen. Mögliche Proble-me, die bei Mehrfachvererbung auftreten, wie z.B. das Diamond Problem, werdenbei Traits mit Hilfe von Linearisierung umgangen. Dabei werden die eingemixtenTraits und Klassen der Vererbungshierarchie in der Reihenfolge des Einmixen inForm einer Liste aufeinander gelegt, wodurch Methoden, die in unterschiedlichenTypen mehrfach definiert sind, für den Compiler auflösbar bleiben.42Im Listing 2.5 ist außerdem zu erkennen, dass in Scala mit override ein speziellesSchlüsselwort für das Überschreiben von Methoden und Feldern existiert. DacalculateAge noch keine Implementierung in HasAge hat, ist override bei dieserMethode nicht notwendig. Es kann jedoch angegeben werden.2.3.3 FunktionenDa Scala neben den objektorientierten Programmierparadigmen ebenfalls funktio-nale Paradigmen einschließt, sind Funktionen einer der wichtigsten Bestandteileder Sprache. Funktionen sind in Scala so genannte First Class Values, auch FirstClass Citizens genannt, was bedeutet, sie werden als Werte behandelt und könnenals solche Variablen zugewiesen werden oder als Parameter anderen Funktionenübergeben werden. Ebenso ist es möglich, dass Funktionen andere Funktionen alsRückgabewert zurückgeben.43Eine Funktion wird mit dem Schlüsselwort def definiert, was bereits in den voran-gegangenen Abschnitten gezeigt wurde. Alternativ dazu besteht die Möglichkeit,Funktionen über Funktionsliterale zu definieren Das folgende Listing zeigt die41 Vgl. [Bra11, S. 84]42 siehe [MO08, Kapitel 12.6]43 Vgl. [Bra11, Kapitel 5.2] 21
  • 31. 2.3 Programmiersprache ScalaFunktion dec, die über def angelegt wird und anschließend alternativ als Funkti-onsliteral.§ Listing 2.6: Beispiel einer einfachen Funktion ¤def dec ( i : I n t ) = i − 1// F u n k t i o n s l i t e r a l f ü r dec( i : Int ) = i − 1 >¦ ¥Ein Funktionsliteral wird dabei mit einem Doppelpfeil angegeben.44 Links davonsteht die Parameterliste der Funktion und rechts davon die Befehle der Funktion.Beide Schreibweisen erzeugen das gleiche Ergebnis. Der Vorteil des Funktionsliteralsist, dass eine Funktion darüber sehr einfach als Wert zugewiesen werden kann,was im Listing 2.7 zu sehen ist. Ebenfalls wird durch Funktionsliterale die Angabeanonymer Funktionen erleichtert.§ Listing 2.7: Beispiel der Zuweisung eines Funktionswertes ¤val d e c F u n c t i o n = ( i : I n t ) = i − 1 >p r i n t l n ( d e c F u n c t i o n ( 4 ) ) // Ausgabe : 3¦ ¥Funktionen höherer OrdnungMit dem Begriff der Funktionen höherer Ordnung werden Funktionen bezeichnet,bei denen Parameter oder der Rückgabewert aus einer Funktion bestehen. Das istein hilfreiches Mittel zur Abstraktion von allgemeinem Verhalten, wie das folgendeListing zeigt.45§ Listing 2.8: Anwendung einer Funktion höherer Ordnung ¤def dec ( i : I n t ) = i − 1def d e c L i s t ( l i s t : L i s t [ I n t ] ) = l i s t map dec44 Der Doppelpfeil setzt sich aus einem Gleichheitszeichen und einem Pfeil zusammen.45 siehe [Bra11, Kapitel 5.3] 22
  • 32. 2.3 Programmiersprache Scalap r i n t l n ( d e c L i s t ( L i s t ( 1 , 2 , 3 ) ) ) // Ausgabe : L i s t ( 0 , 1 , 2 )¦ ¥In der Funktion decList wird die Dekrementierungsfunktion dec auf alle Elementeder übergebenen Liste angewendet. Dazu wird die Funktion höherer Ordnungnamens map verwendet. Die map Funktion gehört zur Collections API von Scala.map wendet die übergebene Funktion dec auf alle Elemente der Liste an und gibtdie dadurch neu enstandene Liste als Ergebnis zurück. In der decList Funktionkommen gleich mehrere Scala-Besonderheiten zum Einsatz, die den Aufruf vonmap verkürzen. In der vollen Länge wird map hier wie folgt aufgerufen.§ Listing 2.9: Vollständig ausgeschriebener Aufruf von map ¤l i s t . map ( ( i : I n t ) = dec ( i ) ) >¦ ¥In dieser Form macht das Funktionsliteral, das map als Parameter übergebenwird, deutlich, dass es sich um eine Funktion höherer Ordnung handelt. Durch dieTypinferenz kann Int in der Parameterliste des Funktionsliterals weggelassen werden.Da dec ledigleich einen Parameter erwartet, kann die Angabe des Parameters iebenfalls entfallen. Der Scala Compiler setzt dann einen automatisch generiertenPlatzhalter an der Stelle für i ein. Dieser Platzhalter hätte auch explizit mit einemUnterstrich angegeben werden können.§ Listing 2.10: Aufruf der map Funktion mit einem Platzhalter ¤l i s t . map( dec (_) )¦ ¥Als weitere Möglichkeit der Vereinfachung wird abschließend die Infix-Operator-Schreibweise46 für Funktionen verwendet, wodurch der in Listing 2.8 gezeigte Aufrufder map Funktion zustandekommt. Da Operatoren in Scala auch nur Methodenvon Klassen sind, kann die Infix-Operator-Schreibweise bei Funktionen verwendetwerden.4746 Dabei steht ein Operator zwischen seinen zwei Operanden.47 Vgl. [MO08, Kapitel 5.3] 23
  • 33. 2.3 Programmiersprache ScalaBeim Aufruf von decList in der println Methode wird das Companion Objekt vonList genutzt und intern die apply-Methode aufgerufen. Denn der Aufruf List(1, 2,3) wird vom Compiler zu List.apply(1, 2, 3) erweitert.CurryingUnter Currying wird das Abbilden einer Funktion mit mehreren Parametern, aufmehrere Funktionen mit je einem Parameter verstanden. In Scala gilt dies ebensofür Funktionen mit Parameterlisten. Dadurch können Funktionen mit mehrerenParameterlisten definiert werden, wie das folgende Listing zeigt.48§ Listing 2.11: Beispiel einer Funktion mit mehreren Parameterlisten ¤def sub ( x : I n t ) ( y : I n t ) = x − yp r i n t l n ( sub ( 2 ) ( 3 ) ) // Ausgabe : −1¦ ¥Die Auflösung einer solchen Funktion durch den Compiler kann wie folgt veran-schaulicht werden.§ Listing 2.12: Abbildung der sub Funktion auf mehrere Funktionen ¤def sub ( x : I n t ) = ( y : I n t ) = x − y >¦ ¥Die sub Funktion hat nur noch eine Parameterliste und in der Funktion selbst wirdein Funktionsliteral mit der zweiten Parameterliste definiert. Der Aufruf dieserFunktion gestaltet sich genauso, wie von der in Listing 2.11 gezeigten Funktion.Partielle FunktionenBei den partiellen Funktionen muss zwischen den partiellen und den partiell ange-wandten Funktionen unterschieden werden.Eine partielle Funktion ist eine Funktion, die nicht für alle Werte ihres Definitions-bereichs definiert ist.49 Anwendungen solcher Funktionen werden im nachfolgenden48 siehe [Bra11, Kapitel 5.5]49 Vgl. [Bra11, S. 124] 24
  • 34. 2.3 Programmiersprache ScalaKapitel über Pattern Matching gezeigt. Mit dem Trait PartialFunction existierenaußerdem Hilfsfunktionen, um beispielsweise zu bestimmen, ob eine Funktion füreinen bestimmten Wert ihres Definitionsbereichs definiert ist.50Eine partiell angewandte Funktion hingegen ist eine Funktion, die auf einen Teilihrer Parameter bereits angewendet ist.51 Es ist häufig der Fall, dass partiellangewandte Funktionen zusammen mit Currying eingesetzt werden, indem eineParameterliste bereits angewendet wird und eine weitere zur Aufnahme zusätzlicherParameter dient. Im folgenden Beispiel wird die sub Funktion aus dem Listing 2.11partiell angewendet und einem Wert zugewiesen.§ Listing 2.13: Beispiel einer partiell angewandten Funktion ¤val subTwoWith = sub ( 2 ) _p r i n t l n ( subTwoWith ( 3 ) ) // Ausgabe : −1¦ ¥Der Unterstrich dient hierbei als Platzhalter für die zweite Parameterliste. DieSignatur der in subTwoWith gespeicherten Funktion lautet Int => Int. Es wirddabei also eine neue Funktion mit nur einer Parameterliste erzeugt, die intern aufdie sub Funktion zugreift.Die wirkliche Stärke dieses Sprachmittels wird deutlich, wenn anstatt des einfachenParameters mit dem Zahlenwert 3 eine Funktion verwendet wird, wie im folgendenListing.§ Listing 2.14: Kontrollstruktur-ähnliche Konstrukte mit subTwoWith ¤p r i n t l n ( subTwoWith { val n i n e = 4 + 5 val s i x = 2 + 4 nine − s i x})¦ ¥50 Die Funktion dafür lautet isDefinedAt.51 Vgl. [MO08, S. 147] 25
  • 35. 2.3 Programmiersprache ScalaDas Ergebnis dieses Aufrufs von subToWith ist das gleiche wie im vorangegangenenListing. Durch die Verwendung der geschweiften Klammern und der Formulierungeines Funktionsblocks wirkt die Verwendung von subTwoWith wie eine Scala-eigeneKontrollstruktur. Solche Konstrukte sind bei der Verwendung von Scala häufiganzutreffen.2.3.4 Pattern MatchingPattern Matching kann für einen Java Entwickler als Erweiterung der Switch-Case-Kontrollstruktur erklärt werden. Beim Pattern Matching wird ein Wert mitMustern verglichen. Die Besonderheit bei Scala ist, dass nicht nur Werte als Musterangegeben werden können, sondern auch Typen.52 Eine einfache Anwendung vonPattern Matching zeigt das folgende Beispiel.53§ Listing 2.15: Bespiel von Pattern Matching in printInt ¤def p r i n t I n t ( i : I n t ) = i match { case 1 = p r i n t l n ( " Eins " ) > case 2 = p r i n t l n ( " Zwei " ) > case _ = p r i n t l n ( " e i n e a n d e r e Zahl " ) >}p r i n t I n t ( 2 ) // Ausgabe : Zwei¦ ¥Mit dem Schlüsselwort match wird ein Pattern-Matching-Ausdruck eingeleitet undin diesem Fall auf den Parameter i angewendet. Mit case werden die einzelnen Mus-ter definiert, gegen die i verglichen wird. Der Doppelpfeil leitet den Codeblock ein,der ausgeführt wird, sollte das jeweilige Muster passt. Nachdem ein Muster gepassthat, kehrt die Ausführung aus dem Match-Ausdruck zurück. Der Rückgabewertentspricht dem des ausgeführten Codeblocks.Mit dem Unterstrich wird das Wildcard-Pattern definiert, das auf jeden Wert undTypen passt. Wird das Wildcard-Pattern weggelassen, ist der Match-Ausdruck52 siehe [Bra11, Kapitel 5.4]53 in Anlehnung an [Bra11, S. 115] 26
  • 36. 2.3 Programmiersprache Scalamitunter nicht für alle Werte des Definitionsbereichs definiert. Ist ein solcher Match-Ausdruck die einzige Anweisung einer Funktion, wird die Funktion zur partiellenFunktion, wie das folgende Listing zeigt.54§ Listing 2.16: Pattern Matching in Form einer partiellen Funktion ¤def printType ( v a l u e : Any) = v a l u e match { case i : I n t = p r i n t l n ( " I n t : " + i ) > case d : Double = p r i n t l n ( " Double : " + d ) >}printType ( 2 ) // Ausgabe : I n t : 2¦ ¥Die printType Funktion verwendet für den Mustervergleich Typen. Außerdemist diese Funktion eine partielle Funktion, da sie nur für Int und Double Typendefiniert ist, obwohl der Definitionsbereich Any ist. Any ist der Basistyp des Scala-Typsystems. Wird printType beispielsweise mit einer Zeichenkette aufgerufen, wirdzur Laufzeit ein MatchError erzeugt, da die Funktion für den String-Typ nichtdefiniert ist.2.3.5 Self-Type AnnotationenSelf-Type-Annotationen ermöglichen es, den Typ des this Wertes explizit zu dekla-rieren. Dadurch können Funktionalitäten einer Klasse modular verteilt in mehrerenTraits implementiert werden oder aber ein Trait kann Abhängigkeiten festlegen,die seine Funktionen benötigen und durch das Einmixen in die richtigen Klassenaufgelöst werden.55 Ein Beispiel für den Einsatz einer Self-Type Annotation wirdim folgenden Listing gezeigt.5654 in Anlehnung an [Bra11, S. 116]55 siehe [Bra11, S. 160 ff.]56 entnommen aus [Bra11, S. 161] 27
  • 37. 2.3 Programmiersprache Scala§ Listing 2.17: Beispiel einer Self-Type Annotation ¤trait Noise { def makeNoise : Unit}t r a i t Food { def e a t : Unit}t r a i t Animal { t h i s : N o i s e with Food => def run = { makeNoise eat makeNoise }}¦ ¥Eine Self-Type-Annotation wird zu Beginn einer Typ-Definition formuliert. Siebesteht dabei aus dem Schlüsselwort this,57 der Typangabe, die gewohnt mit einemDoppelpunkt vom Bezeichner getrennt wird und einem Doppelpfeil. Dadurchdass der Animal Trait die Self-Type Annotation Noise with Food hat, kann ernur in Klassen eingemixt werden, die diese beiden Traits ebenfalls einmixen undimplementieren. Animal kann außerdem auf die Methoden (und Felder) von Noiseund Food zugreifen.57 Alternativ kann auch self verwendet werden. Da this jedoch deutlicher aussagt, dass der Typ des this Wertes überschrieben wird, ist diese Variante zu bevorzugen. 28
  • 38. 2.3 Programmiersprache Scala2.3.6 Implicit ConversionsMit Implicit Conversions können in Scala Typumwandlungen in Form von Methodendefiniert werden. Wird eine Methode mit dem vorangestellten Schlüsselwort implicitdefiniert, gilt sie als Implicit Conversion.Erwartet der Compiler ein Objekt eines bestimmten Typs, findet jedoch ein Objekteines inkompatiblen Typen vor, sucht er nach einer Implicit Conversion. Findetder Compiler eine passende Umwandlung, führt er die Methode aus. Durch diesesSprachmittel können in Scala bestehende Klassen um zusätzliche Funktionenerweitert werden. Das wird auch als Pimp-my-Library-Pattern bezeichnet.58 Wieeinfach die Nutzung von Implicit Conversions ist, zeigt das nachfolgende Listing.§ Listing 2.18: Beispiel einer Implicit Conversion ¤c l a s s F a n c y S t r i n g ( val s t r : S t r i n g ) { def d o F a n c y S t u f f = s t r + " f a n c y "}object F a n c y S t r i n g { i m p l i c i t def s t r i n g T o F a n c y S t r i n g ( s t r : S t r i n g ) : F a n c y S t r i n g = new F a n c y S t r i n g ( s t r )}import F a n c y S t r i n g ._p r i n t l n ( " H a l l o " . d o F a n c y S t u f f ) // Ausgabe : H a l l o f a n c y¦ ¥Die Klasse String der Java Standardbibliothek wird hierbei durch FancyStringund die im Companion Objekt implementiere Typumwandlung um die MethodedoFancyStuff erweitert. Obwohl diese Methode nicht in der String Klasse definiertist, kann sie dadurch an einem String Objekt aufgerufen werden. Vor dem Aufrufvon doFancyStuff wird der Compiler das String-Objekt „Hallo“ dafür mit Hilfe58 siehe http://www.artima.com/weblogs/viewpost.jsp?thread=179766 29
  • 39. 2.4 Lift Web-Frameworkvon stringToFancyString in ein Objekt des Typs FancyString umwandeln. Damitdie Implicit Conversion vom Compiler gefunden wird, importiert die Anweisungimport FancyString._ die Felder und Methoden von FancyString.2.4 Lift Web-FrameworkIm Jahr 2007 startete die Entwicklung von Lift. Das Lift Framework ist ein inScala entwickeltes Framework zur Entwicklung von Web-Anwendungen. Es istein so genanntes Full-Stack Web-Framework. Das bedeutet, neben Funktionenzur Erzeugung von Webseiten werden z.B. auch Funktionalitäten zum Speichernder Anwendungsdaten in einer Datenbank angeboten oder für die Validierungder Nutzereingaben. Ohne die Nutzung anderer Bibliotheken kann mit Lift einekomplette Web-Anwendung entwickelt werden.Lift baut auf bewährte Konzepte anderer Web-Frameworks auf und versucht diesezu vereinen. So wird beispielsweise der Ansatz der Convention over Configurationbefolgt, der vom Web-Framework Ruby on Rails59 inspiriert wurde. Mit Conventionover Configuration soll der Aufwand und die Komplexität der Konfiguration undNutzung des Frameworks gesenkt werden, indem sinnvolle Vorgaben und Annahmenvom Framework über die Nutzung getroffen werden, wie z.B. eine vorgegebeneVerzeichnisstuktur.Lift ist allerdings kein MVC-Framework wie JSF.60 In Lift wird ein selbstdefiniertesEntwurfsmuster verwendet. Die Lift-Entwickler verfolgen den View-First-Ansatzfür den Entwurf von Web-Anwendungen, wobei ein als View-ViewModel-Modelbeschreibbares Entwurfsmuster eingesetzt wird, das in Abbildung 2.5 zu sehen ist.61Der View-First-Ansatz besagt, dass in der Beschreibung der Seitendarstellungausschließlich valide Standard XHTML-Tags verwendet werden und wurde durch59 siehe http://rubyonrails.org60 Wobei MVC mit Lift sehr wohl umsetzbar ist. Das hat der Hauptentwickler von Lift, David Pollak, exemplarisch gezeigt. (siehe dazu [Pol, Kapitel 12 und 13])61 Vgl. [Per11, S. 7 f.] 30
  • 40. 2.4 Lift Web-Framework Abb. 2.5: Kontrollfluss im View-First-Ansatz von Lift aus [Per11]das Java Web-Framework Wicket62 inspiriert. In Lift wird ein View daher in Formvon XML-Dokumenten beschrieben, die bestenfalls ausschließlich Standard XHTMLTags enthalten.Snippets sind das wichtigste Konzept von Lift. Als Snippet wird eine Funktionbezeichnet, die XML-Knoten transformiert.63 Snippets sind das ViewModel imView-First-Ansatz und sorgen für die Generierung von dynamischen Seiteninhalten.Dabei verbinden sie den View mit dem Model, das die Fachentitäten der Anwendungdarstellt. Dabei kann ein View durchaus mehrere Snippets nutzen. Im Gegensatzzum View können Snippets zustandsbehaftet sein. Auch wenn das Snippet in derAbbildung 2.5 wie ein Controller gemäß dem MVC-Muster aussieht, ist es das nicht.Ein Snippet übernimmt nicht die Steuerung des Kontrollflusses.Außerdem baut das Lift Framework auf der Java EE Plattform auf. Über die ServletTechnologie integriert sich Lift in Java EE Webcontainer, was unteranderem inAbbildung 2.6 dargestellt wird. Wie eine JSF-Anwendung werden Lift-Anwendungenin Form von WAR-Dateien bereitgestellt.62 siehe http://wicket.apache.org63 In der Arbeit werden teilweise auch die Klassen als Snippet bezeichnet, die eine solche Funktion enthalten. 31
  • 41. 2.4 Lift Web-Framework Abb. 2.6: Architektur von Lift aus [DCB11]Lift implementiert mehrere Persistenzlösungen. Die Standard-Persistenzlösungnennt sich Mapper. Die Mapper Bibliothek implementiert unter Nutzung des ActiveRecord Patterns die Abbildung von Objekten in einer relationalen Datenbank.Beim Active Record Pattern befindet sich die Logik für den Datenbankzugriff imDomänenobjekt. Außerdem repräsentiert ein Domänenobjekt bei Active Recordeinen Datenbankeintrag.64 Gleichzeitig ist Mapper, wie es der Name bereits andeutet,ein objektrelationaler Mapper. Die Doppeldeutigkeit von Mapper als Umsetzung desActive Record Patterns und objektrelationaler Mapper ist auf die Art und Weise derImplementierung zurückzuführen. Lift Mapper Funktionalitäten werden mit Traitsin die Domänenobjekte eingemixt. Dadurch ist die Logik für den Datenbankzugriffzwar in den Domänenobjekten, aber implementiert wird sie separiert in den Mapper64 Vgl. [Fow03, S. 160 f.] 32
  • 42. 2.4 Lift Web-FrameworkTraits. Intern nutzt Mapper die Java SE Datenbankschnittstelle JDBC (JavaDatabase Connectivity).65Zusätzlich bietet Lift eine Abstraktionsschicht für Ajax und JavaScript, wodurchJavaScript-Befehle und Ajax Aufrufe in reinem Scala-Code formuliert werdenkönnen. Dabei wird intern die JavaScript-Bibliothek JQuery66 eingesetzt. Alternativkann YUI67 verwendet werden.65 Vgl. [Per11, S. 13]66 siehe http://jquery.com67 siehe http://developer.yahoo.com/yui 33
  • 43. 3 Methodik der Evaluation3.1 Beschreibung der EvaluationFür die Evaluation der beiden Technologiestacks werden folgende zwei Methodeneingesetzt: • Literaturstudium • Umsetzung einer prototypischen BeispielanwendungDurch das Literaturstudium wird der Blick in die Interna der jeweiligen Frameworksermöglicht. Dieser wäre ohne ein solches Studium nicht möglich. Die alleinige Im-plementierung einer prototypischen Anwendung führt zwar zur Auseinandersetzungmit der API der jeweiligen Technologie. Jedoch sind dadurch nicht zwangsläufigdie internen Mechanismen geklärt. Mögliche Stärken und Schwächen, die sich nichtdurch die Nutzung der API zeigen, würden somit auch nicht in die Evaluationeinfließen.Die Ergebnisse aus dem Literaturstudium werden beim Vergleich der beiden imple-mentierten Lösungen eingebracht.Die wichtigste Grundlage für die Evaluation ist die Entwicklung einer prototypischenBeispielanwendung. Für jeden Technologiestack wird ein Prototyp für die imAbschnitt 4.1.2 formulierten Anwendungsfälle entwickelt. Der Fokus liegt dabei aufdem Einsatz der jeweiligen Frameworkspezifika und nicht auf der bestmöglichenUmsetzung der Anforderungen. Die implementierten Prototypen sind nicht für denproduktiven Einsatz vorgesehen. 34
  • 44. 3.2 Evaluationskriterien3.2 EvaluationskriterienDie Evaluation wird anhand von Evaluationskriterien durchgeführt, um das Ergebnismöglichst objektiv und für den Leser transparent zu gestalten.Die Auswahl der Evaluationskriterien für diese Arbeit orientiert sich an gängigenStandards und Erkenntnissen der Softwareentwicklung sowie den Erfahrungswertendes Autors. Dabei spielen ebenso einfache Faktoren wie die reine Funktionalitäteine Rolle als auch Faktoren, die die Struktur und den Aufbau des Programmcodesbetreffen.3.2.1 AllgemeinesBeim Vergleich von Technologien und Frameworks gibt es allgemeingültige Kriterienwie z.B. die Integration in die Entwicklungswerkzeuge, die bei jedem Vergleichanwendbar sind.DokumentationBeschäftigt sich ein Entwickler mit Technologien, so wird eine gute Dokumentationerwartet. Die Dokumentation sollte dabei eine Spezifikation der API enthalten,als auch beschreibende Anleitungen zu Anwendungsfällen. Ebenso zählt zur Doku-mentation das Vorhandensein von Büchern zur Einführung und Vertiefung in dieTechnologie.Im Verlauf der Entwicklung der Prototypen hat sich gezeigt, dass es sehr wohl Unter-schiede in der Qualität und Quantität der Dokumentationsmaterialien gibt. Daherwird bei diesem Kriterium die Dokumentation der Technologiestacks beurteilt.LernkurveEignen sich Softwareentwickler Kenntnisse über neue Sprachen und Frameworksan, gibt es eine so genannte Lernkurve. Unter der Lernkurve wird der nötige 35
  • 45. 3.2 EvaluationskriterienLernaufwand verstanden, der zum Erreichen eines bestimmten Ergebnisses nötigist.Der Autor hat Java EE im November 2010 kennengelernt. Für diese Arbeit hat erScala und Lift gelernt. Daher sind die Ergebnisse zur Lernkurve reine Erfahrungs-werte, die einen Eindruck vermitteln sollen, in welchem Verhältnis die Lernaufwändefür die beiden Technologiestacks stehen.ToolchainDie Toolchain bezeichnet umgangssprachlich die Werkzeugkiste eines jeden Soft-wareentwicklers. Damit sind die Programme gemeint, die der Entwickler für seineArbeiten einsetzt. Im heutigen Entwicklungsgeschäft ist es wichtig, dass sich dieeingesetzen Techniken reibungslos in die Toolchain integrieren lassen. Dies steigertdie Produktivität und ist somit von großer Bedeutung.Es gilt bei der Toolchain zu untersuchen, in welchem Maße sich die jeweiligenFrameworks in die eingesetzten Programme integrieren lassen und welche Vor- undNachteile durch diese Integration entstehen.3.2.2 FunktionalitätBei der Entwicklung von Web-Anwendungen treten oft ähnliche Anforderungenan die verwendeten Frameworks und Technologien auf. Daher werden die beidenentwickelten Lösungen auf die Unterstützung der im folgenden Abschnitt beschrie-benen Funktionalitäten untersucht und bewertet. Die Auswahl dieser Kriterienwurde maßgeblich durch [Wan08] beeinflusst.PersistenzmechanismenHeutzutage kommt keine Web-Anwendung ohne einen entsprechenden Persistenz-mechanismus aus. Die Daten der Anwendung und des Nutzers müssen für die Dauereiner Sitzung und über die Laufzeit der Anwendung hinweg gespeichert werden. 36
  • 46. 3.2 EvaluationskriterienEs wird untersucht, welche Persistenztechnologien die Frameworks unterstützenund gezeigt, wie der so genannte objektrelationale Mismatch gelöst wird. Aufgrundder Verschiedenheit Daten objektorientiert und relational abzubilden, gibt es beider Persistierung eines objektorientierten Domänenmodells in eine releationaleDatenbank eine Unverträglichkeit; einen Mismatch, den es zu lösen gilt.1 Weiterhinwird die Umsetzung in den jeweiligen Lösungen aufgrund der Integration in dasbestehende Domänenmodell und der Funktionalität bewertet.DarstellungskonzeptJede Anwendung stellt die Daten für den Nutzer auf dem Bildschirm in irgend-einer Form dar. Frameworks bieten unterschiedliche Konzepte, wie diese Ansichtaufzubauen und mit dem Rest der Anwendung zu verbinden ist.Hier gilt es zu untersuchen, wie naht- und reibungslos sich die unterschiedlichen Dar-stellungskonzepte mit den anderen Anwendungsteilen verbinden lassen. Ebenfallswird aufgezeigt, welche Vor- und Nachteile die unterschiedlichen Ansätze bieten.Ajax-UnterstützungIn den letzten Jahren hat sich Ajax als Technologie für hochgradig interaktiveAnwendungen durchgesetzt. Dabei werden Inhalte vom Server asynchron in Formvon XML durch den Client nachgeladen. Dies geschieht für den Nutzer transparentim Hintergrund. Ein Neuladen der Seite ist nicht erforderlich.Die eingesetzten Frameworks werden daraufhin untersucht und bewertet, inwiefernAjax unterstützt wird und welche Qualität die Ajax-Integration aufweist.ValidierungDie Validierung von Nutzereingaben ist in jeder Anwendung wichtig. Die Eingabenmüssen auf bewusst falsche Angaben und fachliche Inkonsistenzen geprüft werden. 1 Vgl. [Röd10, Kapitel 1] 37
  • 47. 3.2 EvaluationskriterienFür die Validierung wird bewertet, wie sich die formulierten Validierungsregelnin das Domänenmodell einpflegen lassen, welche Möglichkeiten zur Validierungdurch das Framework bereits angeboten werden und wie sich die Validierung in dieanderen Frameworkteile integriert.InternationalisierungWeb-Anwendungen werden nicht ausschließlich in der Muttersprache des Kundenausgeliefert. Da diese Anwendungen in den meisten Fällen von Nutzern unterschied-licher Sprachen genutzt werden, sind die Beschriftungen und Texte so zu gestalten,dass sie dynamisch ladbar sind. Abhängig von der Spracheinstellung des Nutzerswird die Anwendung in einer anderen Sprache dargestellt.Es gilt zu untersuchen, welche Mechanismen die Technologiestacks für diese Formder Internationalisierung von Anwendungen zur Verfügung stellen und wie sichdiese in die Anwendung integrieren.3.2.3 ArchitekturLaut [Bal01] kann Softwarearchitektur als „eine strukturierte oder hierarchischeAnordnung der Systemkomponenten sowie Beschreibung ihrer Beziehungen“ defi-niert werden. Dem zugrundeliegend werden die beiden Technologiestacks aufgrundarchitekturspezifischer Funktionalitäten bewertet. Dabei wird bewertet, welcheFunktionen existieren, um Enterprise-Anwendungen strukturiert und lose gekoppeltzu entwickeln.Lose KopplungBei der Komplexität heutiger Anwendungen ist es von großer Bedeutung, Objekt-netze flexibel aufzubauen. Dabei steht die lose Kopplung einzelner Komponentenund einfache Konfiguration dieser im Vordergrund. Unteranderem soll dadurch dieTestbarkeit eines Systems verbessert werden. 38
  • 48. 3.2 EvaluationskriterienEs gilt zu untersuchen, welche Möglichkeiten und Funktionalitäten die Frameworksfür die lose Kopplung eines Anwendungssystems anbieten.Cross Cutting ConcernsIn Softwaresystemen gibt es so genannte Querschnittsbelange (engl. Cross CuttingConcerns), die mit herkömmlicher objektorientierter Programmierung ohne Verlet-zung des DRY-Prinzips nicht umsetzbar sind.2 Querschnittsbelange werden auchorthogonale Belange genannt. Dies sind z.B. Funktionalitäten wie Tracing,3 Trans-aktionssteuerung oder Exception Handling. Solche Funktionen sind in irgendeinerForm in jeder Anwendungsschicht vorhanden. Sie befinden sich orthogonal zu denAnwendungsschichten. So finden sich z.B. in vielen Methoden eines Anwendungs-systems Programmzeilen, die für das Tracing verantwortlich sind. Dadurch wirdCode mit gleicher Funktionalität regelmäßig in den einzelnen Methoden dupliziert.Dies widerspricht dem DRY-Prinzip. Dieses besagt „Don’t Repeat Yourself“ undist ein Grundprinzip guter Softwareentwicklunng.Es wird untersucht, ob es Möglichkeiten gibt, diese Querschnittsbelange separatvom Anwendungscode zu formulieren. Wenn dies nicht möglich ist, wird diskutiert,welche anderen Arten der Implementierung existieren und wie gehaltvoll diese sind. 2 Vgl. [Wol10, S. 100 f.] 3 Tracing bezeichnet das detaillierte Loggen von Methoden. In den allermeisten Fällen wird dabei der Methodeneintritt mit den übergebenen Parameter sowie der Austritt geloggt. 39
  • 49. 4 Entwicklung der Beispielanwendung4.1 Entwurf der AnwendungFür den Vergleich des Scala/Lift Technologiestacks mit dem Java EE Programmier-modell wird eine prototypische Raumverwaltungssoftware entwickelt. Der Nutzerist mit dieser Software in der Lage, Gebäude mit Räumen zu verwalten. Dabei giltdie Vorgabe, dass für die Verwaltung der Gebäude und Räume so wenige Seitenwie möglich aufzurufen sind. Bestenfalls existiert eine Seite pro Anwendungsfall.4.1.1 DomänenmodellDas Domänenmodell einer Anwendung beschreibt die fachlichen Entitäten einerProblemlösung und die Beziehungen dieser untereinander. Bei der zu entwickeln-den Raumverwaltungsanwendung besteht das Domänenmodell aus vier Entitäten:Gebäuden, Räumen, Ausstattungen der Räume und Ausstattungsmerkmalen. DieAbbildung 4.1 zeigt das Domänenmodell der Beispielanwendung. Zusätzlich existie-ren Regeln für die zulässigen Werte einiger Attribute.Sämtliche Bezeichnungen dürfen nicht leer sein. Das heißt, die Zeichenkettenmüssen mindestens ein Zeichen enthalten und Null ist ebenfalls nicht als Wertzulässig. Des Weiteren sind sämtliche Zahlen als positive ganze Zahlen abzubilden,Primärschlüssel ausgeschlossen. 40
  • 50. 4.1 Entwurf der AnwendungAbb. 4.1: Domänenmodell der Beispielanwendung 41
  • 51. 4.1 Entwurf der Anwendung4.1.2 AnwendungsfälleEin Anwendungsfall beschreibt ein mögliches Szenario für die Nutzung eines An-wendungssystems. Die Beispielanwendung behandelt drei Anwendungsfälle: dieVerwaltung von Gebäuden, die Suche nach Räumen und das Verwalten von Aus-stattungsmerkmalen.Gebäude verwaltenDer Hauptanwendungsfall ist das Verwalten von Gebäuden. Dabei soll der Nutzerneue Gebäude anlegen, Veränderungen an Gebäudedaten speichern und existierendeGebäude löschen können. Dies gilt natürlich ebenfalls für die Räume der Gebäudeund Ausstattungen der Räume.Räume suchenDer Nutzer soll die Möglichkeit haben, nach Räumen mit einem bestimmtenAusstattungsmerkmal zu suchen. Dabei kann der Benutzer die Wahl treffen zwischenallen vorhandenen Ausstattungsmerkmalen.Ausstattungsmerkmale verwaltenEin Raum kann mehrere Raumaustattungen besitzen. Eine Raumaustattung istdabei die Zuordnung eines Ausstattungsmerkmals mit einer Menge zu einem Raum.Diese Ausstattungsmerkmale soll der Nutzer ebenfalls verwalten können. Dabeigilt die Vorgabe, dass Ausstattungsmerkmale nur gelöscht werden dürfen, wenndiese bei keinem Raum in einer Raumausstattung verwendet werden. Andernfallswürden bestehende Daten fehlerhaft. Das Hinzufügen und Verändern bestehenderAusstattungsmerkmale soll ebenso möglich sein. 42
  • 52. 4.2 Umsetzung mit Java EE4.2 Umsetzung mit Java EEDie Java EE Implementierung der Beispielanwendung setzt JSF 1.2 mit Faceletsals View-Technologie ein. Für die Persistenzschicht wird JPA 2.0 mit Hibernate alsJPA Provider verwendet.4.2.1 DomänenmodellDas Domänenmodell wird im Paket de.htw.berlin.jroomyweb.domain entwickelt.Dabei wird für jede fachliche Entität eine Klasse implementiert. Ein Gebäude wirdalso durch die Klasse Gebaeude repräsentiert, wie das folgende Listing zeigt.§ Listing 4.1: JPA Grundgerüst der Java Gebäude Fachklasse ¤@Entitypublic c l a s s Gebaeude implements S e r i a l i z a b l e { @Id @GeneratedValue private Long i d ; protected Gebaeude ( ) { // f ü r JPA } public Long g e t I d ( ) { return i d ; } public void s e t I d ( Long i d ) { this . id = id ; }}¦ ¥ 43
  • 53. 4.2 Umsetzung mit Java EEDa die Objekte der Domänenschicht mittels JPA in einer relationalen Datenbankpersistiert werden, hat jede Fachklasse die Entity-Annotation. Diese Annotationkennzeichnet für JPA eine Klasse, die auf eine Datenbank abzubilden ist. Weiterhinfällt auf, dass Gebaeude das Interface Serializable implementiert. Das SerializableInterface kennzeichnet Klassen, die serialisierbar sind. Damit ist das Abbilden einesObjektes der Klasse auf einen Bytestrom oder anderes Übetragungsformat (z.B.XML) gemeint. Für die reine Persistierung über JPA ist das nicht notwendig. Jedochist es eine Best-Practice, im Java EE Kontext die JPA Fachklassen serialisierbar zumachen. Sollte die Anwendung in einem verteilten System eingesetzt werden, kannes vorkommen, dass Objekte der Domänenschicht zwischen verschiedenen Servernhin- und hergeschickt werden. Dafür müssen die Objekte serialisiert werden. DasSpeichern von Domänenobjekten in der HTTP Session eines Nutzers kann ebenfallsdas Serialisieren für die Zwischenspeicherung auf der Festplatte des ApplicationServers zur Folge haben.Alle Domänenobjekte werden mit einer eindeutigen Identifikationsnummer gespei-chert. Diese entspricht dem Primärschlüssel in der Datenbank. Daher hat dieGebaeude Fachklasse ein id Attribut. Dieses ist mit einer Id-Annotation versehen,die den Primärschlüssel der Objekte für die Abbildung in der Datenbank kennzeich-net. Außerdem wird mit der GeneratedValue-Annotation festgelegt, dass der Wertvon id automatisch durch JPA generiert wird. In den meisten Konfigurationen be-wirkt dies das Anlegen einer automatisch hochzählenden Datenbankspalte.1 Damitein JPA Provider auf das id Attribut von Gebaeude zugreifen kann, muss ein sogenanntes Getter-Setter-Methodenpaar implementiert werden. Diese Vorgabe derGetter-Setter-Zugriffsmethoden gilt für alle Attribute, die durch JPA persistiertwerden.Die Verwendung von JPA in der Gebaeude Klasse verlangt des Weiteren die Imple-mentierung eines Konstruktors mit einer leeren Parameterliste. Das ist notwendig,da ein JPA Provider Objekte von Gebaeude über die Java Reflections API erzeugt,indem dieser leere Konstruktor aufgerufen wird, und die Gebaeude Objekte z.B.anschließend über die Setter-Methoden mit den Daten aus der Datenbank befüllt. 1 Oftmals wird dafür der Terminus AUTO_INCREMENT verwenden. 44
  • 54. 4.2 Umsetzung mit Java EEDa die Gebaeude Klasse einen Konstruktor mit einer nicht leeren Parameterliste hat,der den Default-Konstruktor von Java überdeckt, musste dieser leere Konstruktorzusätzlich fomuliert werden.2 Um zu verhindern, dass ein Gebaeude Objekt uner-laubter Wise im Programmcode über diesen leeren Konstruktor erzeugt wird, hater die Sichtbarkeit protected und ist damit nur im Paket der Fachklassen sichtbar.Alle Attribute von Gebaeude werden durch die Nutzung von JPA automatisch inder Datenbank abgebildet.3 So auch die vier String-Attribute der Gebäude Entitätaus dem Domänenmodell, wie das folgende Listing zeigt. Nicht abgebildet, sind dieobligatorischen Getter- und Setter-Methoden.§ Listing 4.2: String Attribute der Gebäude Klasse ¤@NotEmpty ( message= " Der Name e i n e s Gebäudes d a r f n i c h t l e e r s e i n . " )private S t r i n g b e z e i c h n u n g ;private S t r i n g s t r a s s e ;private S t r i n g p l z ;private S t r i n g o r t ;¦ ¥Das Domänenmodell verlangt, dass sämtliche Bezeichnungen nicht leer sein dürfen.Daher wurde das Attribut bezeichnung mit NotEmpty annotiert. Die NotEm-pty-Annotation ist eine Erweiterung des Bean Validation Standards und stehtdurch die Nutzung der Hibernate Validator Bibliothek zur Verfügung. Durch dieAnnotation wird festgelegt, dass bezeichnung weder den Wert Null noch eine Zei-chenkette einer Länge kleiner 1 aufnehmen kann. Mit dem message Attribut derNotEmpty-Annotation wird die Fehlernachricht angegeben, die bei Verletzung derValidierungsregel ausgegeben wird.Die Fehlernachrichten von Bean-Validation-Annotationen sind zusätzlich überRessourcendateien lokalisierbar. Dazu muss mindestens eine ValidationMessa-ges.properties Datei im Klassenpfad abgelegt werden, die in Schlüssel-Wert-Paaren 2 Der Konstruktor mit der nicht leeren Parameterliste ist in dem Listing aus Gründen der Übersicht nicht dargestellt ist. 3 Sollen einzelne Attribute nicht persistiert werden, sind diese mit @Transient zu annotieren. 45
  • 55. 4.2 Umsetzung mit Java EEVariablen und ihren Wert enthält. Mit dem Ausdruck {Variablenbezeichner} kannein Wert im message Attribut einer Bean-Validation-Annotation geladen werden.Über Länder- und Sprachkürzel als Suffix des Dateinamen können einzelne Valida-tionMessages_xx_XX.properties Dateien für bestimmte Sprachen definiert werden.Das wurde im Prototypen jedoch nicht implementiert.Die 1-n Beziehung zwischen Gebäuden und Räumen wird in der Gebaeude Klassedurch ein Set implementiert. Ein Raum wird in der Java EE Lösung durch diegleichnamige Klasse Raum repräsentiert.§ Listing 4.3: Implementierung der Beziehung zwischen Räumen und Gebäuden ¤@OneToMany( c a s c a d e=CascadeType . ALL, mappedBy=" gebaeude " )private Set<Raum> raeume = new HashSet<Raum>() ;¦ ¥Durch das Annotieren des raeume Sets mit OneToMany wird die Art der Beziehungdefiniert. Die zwei Attribute, cascade und mappedBy, konfigurieren diese Beziehung.Über das cascade Attribut der OneToMany-Annotation werden kaskadierendeOperationen eingestellt. Das heißt, ist das umgebene Gebaeude Objekt durcheine JPA Operation, wie z.B. das Speichern oder Löschen in der Datenbank,betroffen, wird diese Operation auf alle Raum Objekte in raeume angewendet.In diesem Fall werden durch die Angabe von CascadeType.ALL alle Operationenkaskadierend auf das Set raeume angewendet. Das zweite Attribut, mappedBy,betrifft die Abbildung der Beziehung in der Datenbank. Darüber wird definiert, dassdie Beziehung in der Datenbank durch die Spalte des gebaeude Attributs der RaumKlasse abgebildet wird. Dies entspricht der Speicherung eines Fremdschlüssels in derRaum Tabelle auf die ID Spalte der Gebäude Tabelle. Damit diese Konfigurationüber mappedBy funktioniert, wird das gebaeude Attribut der Raum Klasse mitManyToOne annotiert.§ Listing 4.4: gebaeude Attribut in der Java Raum Fachklasse ¤@ManyToOne( o p t i o n a l=f a l s e )private Gebaeude gebaeude ;¦ ¥ 46
  • 56. 4.2 Umsetzung mit Java EEDas Setzen des optional Attributs auf den Wert false, bewirkt das Generieren einesNotNull Constraints für die gebaeude Spalte in der Datenbank. Ein Raum kanndemnach nicht ohne ein dazugehöriges Gebäude existieren.Neben der Beziehung zu einem Gebäude hat ein Raum mehrere Attribute, wie imfolgenden Listing exemplarisch anhand der Fensteranzahl gezeigt wird.§ Listing 4.5: Anzahl der Fenster eines Raumes ¤@Min( v a l u e =1, message= " Die Anzahl d e r F e n s t e r kann n i c h t k l e i n e r 0 s e i n . " )private int a n z a h l F e n s t e r ;¦ ¥In der Beschreibung des Domänenmodells wurde festgelegt, dass sämtliche Zahlenals positive Ganzzahlen abzubilden sind. Dies wird durch die Validierungsregel derMin-Annotation umgesetzt. Durch das value Attribut wird der kleinste zulässigeWert von anzahlFenster festgelegt. Die Min-Annotation ist eine Annotation desBean Validation Standards.Wie bereits die 1-n Beziehung zwischen Gebäuden und Räumen wird auch dieBeziehung zwischen einem Raum und mehreren Raumausstattungen über einSet implementiert. Eine Raumausstattung wird durch die Klasse Ausstattungimplementiert.§ Listing 4.6: Raumausstattungen in der Raum Klasse ¤@OneToMany( c a s c a d e=CascadeType . ALL, f e t c h=FetchType .EAGER, mappedBy=" raum " )private Set<Ausstattung > a u s s t a t t u n g e n = new HashSet<Ausstattung >() ;¦ ¥Das fetch Attribut legt in diesem Fall fest, dass alle Raumausstattungen beimLaden eines Raumes aus der Datenbank sofort mitgeladen werden. Andernfallsgeschieht dies verzögert (engl. lazy), was bei der Umsetzung des Prototypen zuLadefehlern geführt hat.Die Implementierungen der Klassen Ausstattung und Merkmal, die das Ausstattungs-merkmal des Domänenmodells repräsentiert, verwenden keine neuen Annotation 47
  • 57. 4.2 Umsetzung mit Java EEoder Mechnismen und lassen sich daher anhand der bisherigen Erläuterungennachvollziehen.4.2.2 AnwendungsfälleIn allen Anwendungsfällen wird auf die Datenbank zugegriffen. Die Klassen derDomänenschicht sind zwar mit JPA Annotationen versehen, jedoch muss derDatenbankzugriff noch implementiert werden. Dazu wird im Java EE Prototypendas DAO-Pattern verwendet. Ein DAO implementiert den Datenbankzugriff füreinen Typen (z.B. die Gebaeude Klasse) und kapselt diesen damit vom Rest derAnwendung. Das erhöht außerdem die Testbarkeit des Anwendungssystems, daüber DAOs der Zugriff auf die Datenbank mit Mocks ausgetauscht werden kann.4Da es mehrere DAOs gibt, wird ein abstraktes Super-Interface namens Dao be-schrieben, das die wichtigsten CRUD-Operationen5 beinhaltet.§ Listing 4.7: Dao Interface für den Datenbankzugriff ¤public i n t e r f a c e Dao<T, ID extends S e r i a l i z a b l e > { T f i n d B y I d ( ID i d ) ; L i s t <T> f i n d A l l ( ) ; void p e r s i s t (T e n t i t y ) ; T merge (T e n t i t y ) ; void d e l e t e (T e n t i t y ) ;}¦ ¥Von diesem abstrakten DAO Interface erben konkretere DAO Interfaces, die al-lerdings typisiert sind. Jede Fachklasse hat daher eine eigene spezialisierte DAO-Schnittstelle. Dies ist exemplarisch im folgenden Listing für das RaumDao Interfacezu sehen. 4 Als Mocks werden Objekte bezeichnet, die einen Dummy bzw. eine Attrappe darstellen. Mocks werden dazu genutzt, um Klassen isoliert von ihrer Umgebung und ihren Abhängigkeiten testen zu können. 5 Mit CRUD (Create, Read, Update, Delete) werden die vier grundlegendsten Datenbankopera- tionen bezeichnet. 48
  • 58. 4.2 Umsetzung mit Java EE§ Listing 4.8: Spezifisches Dao Interface für die Raum Klasse ¤public i n t e r f a c e RaumDao extends Dao<Raum, Long> { L i s t <Raum> getRaeumeByMerkmal ( Long merkmalId ) ;}¦ ¥In den spezialisierten DAO Interfaces werden für den jeweiligen Typen spezifischeAbfragemethoden formuliert, wie die getRaeumeByMerkmal Methode im Raum-Dao. Wohingegen die Methoden des abstrakten DAO Interfaces für alle DAOsgemeinsames Verhalten beschreiben.Jedes spezialisierte DAO hat eine JPA Implementierung (z.B. RaumJpaDao für dasRaumDao Interface). Um dabei nicht jedesmal die Methoden des abstrakten DAOInterfaces zu implementieren, erben die xxxJpaDao Klassen von AbstractJpaDao.§ Listing 4.9: EntityManager und findById Methode des AbstractJpaDao ¤public abstract c l a s s AbstractJpaDao<T, ID extends S e r i a l i z a b l e > implements Dao<T, ID> { @Inject protected EntityManager e n t i t y M a n a g e r ; @Transactional public T f i n d B y I d ( ID i d ) { return e n t i t y M a n a g e r . f i n d ( p e r s i s t e n t C l a s s , i d ) ; }}¦ ¥Die AbstractJpaDao Klasse ist letztendlich nicht anderes als ein Wrapper umeinen EntityManager, wie hier am Beispiel der findById Methode zu sehen ist.Über den EntityManager erfolgt in der Java EE Beispielanwendung der eigentlicheDatenbankzugriff. Damit auch die anderen, spezialisierten DAO Implementierungdas Attribut entityManager nutzen können, hat es den Zugriffsmodifizierer protected. 49
  • 59. 4.2 Umsetzung mit Java EEMit der Inject-Annotation wird der EntityManager in AbstractJpaDao injeziert.Dies ist durch die Nutzung der Google Guice Bibliothek möglich. Guice ist eineImplementierung des JSR 330 (Dependency Injection for Java).6 Im Java EEPrototypen wird außerdem eine Erweiterung der Guice Bibliothek genutzt: warp-persist.7 Diese ermöglicht die Nutzung deklarativer Transaktionen. Das heißt,sämtliches Transaktionshandling wie das Öffnen, Committen und der Rollback beieinem Fehler werden von Guice bzw. der warp-persist Erweiterung gehandhabt.Dafür muss eine Methode lediglich mit Transactional annotiert werden, wie z.B.hier die findById Methode. Weitere Voraussetzungen für diese Funktionalität sind,dass der EntityManager über Guice injeziert wird und die DAOs ebenfalls überGuice erzeugt werden. Dazu muss die Google Guice Bibliothek konfiguriert werden.Die Konfiguration von Guice wird über so genannte Module vorgenommen. Mo-dule sind Klassen, die das Interface Module implementieren, welches die configureMethode beschreibt. Für die Beispielanwendung wird daher ein PersistenceModuleimplementiert, welches die Konfiguration der Persistenzschicht übernimmt.§ Listing 4.10: configure Methode des PersistenceModule ¤public c l a s s P e r s i s t e n c e M o d u l e extends AbstractModule { protected void c o n f i g u r e ( ) { bindConstant ( ) . annotatedWith ( JpaUnit . c l a s s ) . t o ( " jroomy " ) ; i n s t a l l ( createPersistenceModule () ) ; bindDaoInfrastructure () ; }}¦ ¥Zu Beginn der configure Methode wird die in der persistence.xml definierte Persis-tence Unit jroomy an eine im warp-persist Code mit JpaUnit annotierte Konstantegebunden. Dies ist ein Implementierungsdetail bei der Nutzung von warp-persist. 6 siehe http://code.google.com/p/google-guice 7 siehe http://code.google.com/p/warp-persist 50
  • 60. 4.2 Umsetzung mit Java EEDabei wird bereits deutlich, dass Guice als auch warp-persist ein Fluent API ver-wenden.8 Anschließend erfolgt das Installieren des eigentlichen PersistenceModule,das von der privaten Hilfsmethode createPersistenceModule erzeugt wird.§ Listing 4.11: Erzeugung und Konfiguration des PersistenceModules ¤private Module c r e a t e P e r s i s t e n c e M o d u l e ( ) { return P e r s i s t e n c e S e r v i c e . u s i n g J p a ( ) . a c r o s s ( UnitOfWork .REQUEST) . buildModule ( ) ;}¦ ¥Besonders wichtig ist dabei der Aufruf von across(UnitOfWork.REQUEST). Da-durch wird das Open-Session-in-View-Pattern implementiert.9 In Java Web Anwen-dungen ist es häufig ein Problem, dass der EntityManager nach der Abarbeitungder Applikationslogik geschlossen wird und in der Phase des Renderns der Ant-wort nicht mehr offen ist. Das führt zu Fehlern beim verzögerten Nachladen vonObjekten in den JPA Fachklassen, da diese dann im Detached Zustand sind; alsonicht mehr durch einen EntityManager verwaltet werden. Durch die Anwendungdes Open-Session-in-View-Patterns wird ein EntityManager zu Beginn der Anfrage-bearbeitung geöffnet und bleibt über die gesamte Dauer der Bearbeitung und desRenderns offen. Durch die Inject-Annotation in AbstractJpaDao regelt warp-persistdas korrekte Injezieren eines EntityManager für diesen Zeitraum. Intern verwendetwarp-persist dazu einen Servlet Filter. Dieser muss der web.xml hinzugefügt werden.§ Listing 4.12: warp-persist Servlet Filter Eintrag in der web.xml ¤< f i l t e r> < f i l t e r −name>w a r p P e r s i s t F i l t e r</ f i l t e r −name> < f i l t e r −c l a s s> com . w i d e p l a y . warp . p e r s i s t . P e r s i s t e n c e F i l t e r </ f i l t e r −c l a s s></ f i l t e r> 8 Ein Fluent API ist eine interne Domain Specific Language, die eine leicht verständliche und gut lesbare Schnittstelle ermöglicht. Ein Stück Code liest sich dabei fast wie ein normaler Satz. 9 siehe http://community.jboss.org/wiki/OpenSessionInView 51
  • 61. 4.2 Umsetzung mit Java EE< f i l t e r −mapping> < f i l t e r −name>w a r p P e r s i s t F i l t e r</ f i l t e r −name> <u r l −p a t t e r n>/∗</ u r l −p a t t e r n></ f i l t e r −mapping>¦ ¥In einem letzten Schritt müssen die DAOs konfiguriert werden. Hierbei gilt es,Interfaces und Implementierungen aneinander zu binden, damit im Rest der An-wendung Attribute vom Typ eines DAO Interfaces durch Guice instanziiert werden.Binden bezeichnet dabei, das Zuweisen von Interfaces zu ihren Implementierungen.§ Listing 4.13: Bindung der DAO Interfaces an ihre Implementierungen ¤private void b i n d D a o I n f r a s t r u c t u r e ( ) { bind (new T y p e L i t e r a l <Dao<Gebaeude , Long>>() { } ) . t o ( GebaeudeDao . c l a s s ) ; bind (new T y p e L i t e r a l <Dao<Raum, Long>>() { } ) . t o (RaumDao . c l a s s ) ; // . . . bind ( GebaeudeDao . c l a s s ) . t o ( GebaeudeJpaDao . c l a s s ) ; bind (RaumDao . c l a s s ) . t o ( RaumJpaDao . c l a s s ) ; // . . .}¦ ¥Das Binden erfolgt durch die Methoden bind und to. Aufgrund der Type Erasurevon Java muss vor dem eigentlichen Binden der Interfaces und Implementierungenein Binden der spezialisierten DAO Interfaces an ihre typisierte abstrakte DAOSchnittstelle erfolgen.10 Dafür bietet Guice die Hilfsklasse TypeLiteral, die dengenerischen Typen zur Laufzeit enthält. Würde dieser Mehraufwand nicht geschehen,könnten z.B. Attribute vom Typ Dao<Gebaeude, Long> nicht injiziert werden.10 Type Erasure bezeichnet das Entfernen von generischen Typinformationen durch den Java Compiler. Dies wird aus Gründen der Abwärtskompatibilität mit al- tem Java Code gemacht, da es generische Typen erst seit Java 5 gibt. (siehe http://download.oracle.com/javase/tutorial/java/generics/erasure.html) 52
  • 62. 4.2 Umsetzung mit Java EEGuice hätte keine Informationen, welche Klasse auf den Typ passt, obwohl dieKlasse GebaeudeJpaDao offenkundig Dao<Gebaeude, Long> implementiert.Um das fertig implementierte PersistenceModule zu nutzen, muss es beim Erzeu-gen eines Injectors angegeben werden. Ein Injector Objekt ist die Zentrale fürDependency Injection in Guice. Damit Dependency Injection in allen Komponentendes Prototypen genutzt werden kann, wird der Injector im ServletContext derAnwendung abgelegt. Dafür wird ein ServletContextListener hinzugefügt, GuiceI-nitializationListener. Während der Initialisierungsphase werden in diesem Listenerdie folgenden Programmzeilen ausgeführt.§ Listing 4.14: Ablegen des Injectors im ServletContext ¤i n j e c t o r = Guice . c r e a t e I n j e c t o r (new P e r s i s t e n c e M o d u l e ( ) ) ;s e r v l e t C o n t e x t . s e t A t t r i b u t e ( I n j e c t o r . c l a s s . getName ( ) , injector ) ;¦ ¥Durch die statische Methode createInjector wird ein Injector erzeugt. Dabei wirddas beschriebene PersistenceModule als Konfiguration im Konstruktor übergeben.Anschließend wird injector unter seinem Klassennamen im ServletContext abgelegt.Jede Klasse kann den im ServletContext abgelegten Injector nutzen, um Interfacesund Klassen zu injizieren, die von ihm verwaltet werden; in diesem Fall also dieDAOs und den EntityManager.Im Prototypen werden DAO-Objekte ausschließlich in den so genannten Controller-Klassen verwendet. Daher wird eine abstrakte Oberklasse namens AbstractControllerformuliert, die das automatische Injizieren durch Guice steuert.§ Listing 4.15: Injection durch Guice in AbstractController ¤@PostConstructpublic void i n i t i a l i z e B e a n ( ) { // . . . Injector injector = ( Injector ) s e r v l e t C o n t e x t . g e t A t t r i b u t e ( I n j e c t o r . c l a s s . getName ( ) ) ; i f ( i n j e c t o r != null ) { 53
  • 63. 4.2 Umsetzung mit Java EE i n j e c t o r . injectMembers ( this ) ; }}¦ ¥Die Klasse AbstractController hat nur diese eine Methode. In initializeBean wirddas Injector Objekt aus dem ServletContext geholt. Anschließend wird der Injizie-rungsmechanismus auf das Controller Objekt mittels injectMembers angewendet,wodurch alle mit Inject annotierten Felder injiziert werden. Jede Klasse, die vonAbstractController erbt, hat folglich Dependency Injection integriert.Die initializeBean Methode ist mit PostConstruct annotiert. Durch diese Annotationist es in Java EE Anwendungen möglich, zusätzlichen Code nach dem Instanziiereneines Objektes auszuführen.11Damit ist die Persistenzschicht in Form von DAOs implementiert und wird überGuice, warp-persist und AbstractController sehr einfach eingebunden in den Restder Anwendung.Gebäude verwaltenIm folgenden Abschnitt wird die Umsetzung der Gebäudeverwaltung exemplarischam Speichern von geänderten Gebäudedaten gezeigt. Dafür müssen alle Gebäudein der Anwendung angezeigt werden. Das Selektieren eines bestimmten Gebäudesmuss für den Nutzer möglich sein. Es muss möglich sein, die Daten des ausgewähltenGebäudes zu bearbeiten und natürlich muss eine Funktion zum Speichern existieren.Ein Teil der Erläuterungen gilt ebenso für die Umsetzung der anderen beidenAnwendungsfälle.Die Verwaltung von Gebäuden, wie auch die Raumsuche und die Verwaltung vonAusstattungsmerkmalen, wird in einer einzigen Seite abgebildet. Da das Layout derSeiten für die unterschiedlichen Anwendungsfälle gleich ist, wird ein Grundgerüstentworfen, welches im folgenden Listing teilweise abgebildet ist.11 Die Idee Guice auf diese Art und Weise in Java EE Anwendungen einzubinden, wurde von Cagatay Civici formuliert, dem Hauptentwickler der JSF Komponentenbibliothek PrimeFaces. (siehe http://cagataycivici.wordpress.com/2007/03/26/integrating_guice_and_jsf) 54
  • 64. 4.2 Umsetzung mit Java EE§ Listing 4.16: XHTML Grundgerüst in layout.xhtml ¤<html xmlns=" h t t p : / /www. w3 . o r g /1999/ xhtml " xmlns : f=" h t t p : / / j a v a . sun . com/ j s f / c o r e " xmlns : u i=" h t t p : / / j a v a . sun . com/ j s f / f a c e l e t s "><body> <f : loadBundle basename=" i 1 8 n . m ess ag es " var=" msg " /> <div id=" c o n t e n t "> <u i : i n s e r t name=" c o n t e n t ">Content</ u i : i n s e r t> </ div></body></html>¦ ¥Die Verwendung von Facelets gestattet es, eine Vorlage (engl. Template) zu definie-ren. In dieser Vorlage werden unterschiedliche Platzhalter für den späteren Inhalthinterlegt. Im diesem Fall wird mit dem Facelets Tag ui:insert ein austauschbarerPlatzhaler festgelegt, der über content referenzierbar ist. Seiten, die dieses Templatenutzen, können an dieser Stelle die Vorlage überschreiben und somit mit Inhaltbefüllen. Dabei wird das ui:insert Tag ersetzt.Zu Beginn des body Tags wird mit dem JSF Tag f:loadBundle eine .properties-Ressourcendatei geladen. Im Attribut basename wird dafür der Basisname der zuladenden Datei angegeben. Dieser Basisname wird wie ein normaler Java Paketnameauf eine Verzeichnisstruktur abgebildet. In diesem Fall wird also im Verzeichnisi18n nach einer messages_xx.properties gesucht. Der Dateiname wird dabei miteinem Suffix erweitert, der das jeweilige Länder- und Sprachkürzel des Nutzersenthält, sodass immer die passende Sprache geladen wird, wenn sie denn vorhandenist. Das Attribut var legt einen referenzierbaren Wert fest, der über einen ValueAusdruck in Expression Language Ausdrücken verwendet werden kann. Über msgkann daher auf Werte in der Ressourcendatei zugegriffen werden.Der Aufbau der Ressourcendatei gestaltet sich nach einem einfachen Schlüssel-Wert-Paar Prinzip, wie das folgende Listing zeigt. 55
  • 65. 4.2 Umsetzung mit Java EE§ Listing 4.17: Auszug aus messages Datei für das de Länderkürzel ¤main_site=H a u p t s e i t eroom_search=Raumsucher o o m _ a t t r i b u t e s=Ausstattungsmerkmale¦ ¥So wird durch die Einbindung von f:loadBundle z.B. mit dem EL-Ausdruck#{msg.main_site} der Wert „Hauptseite“ für die Länderkennung „de“ geladen.Die Seite für die Verwaltung von Gebäuden ist in der Datei gebaeude.xhtmlformuliert. Im folgenden Auszug dieser Seite wird die Anzeige aller Gebäude ineiner Tabelle gezeigt. Der Nutzer kann aus dieser Tabelle ein Gebäude auswählenund dieses anschließend bearbeiten, speichern oder löschen.§ Listing 4.18: Anzeige aller Gebäude in gebaeude.xhtml ¤<u i : c o m p o s i t i o n t e m p l a t e=" / l a y o u t / l a y o u t . xhtml "><u i : d e f i n e name=" c o n t e n t "> <r i c h : extendedDataTable var=" gebaeude " value="#{gebaeudeTableModel . a l l E n t i t i e s } " s e l e c t i o n="#{gebaeudeTableModel . s e l e c t i o n } "> <a 4 j : s u p p o r t event=" o n s e l e c t i o n c h a n g e " action="#{g e b a e u d e T a b l e S e l e c t i o n H a n d l e r . onSelectionChange () } " reRender=" mainPanel " /> <r i c h : column> <f : f a c e t name=" h e a d e r ">#{msg . i d }</ f : f a c e t> <h : outputText value="#{gebaeude . i d } " /> </ r i c h : column> <r i c h : column> <f : f a c e t name=" h e a d e r ">#{msg . name}</ f : f a c e t> <h : outputText value="#{gebaeude . b e z e i c h n u n g } " /> </ r i c h : column> 56
  • 66. 4.2 Umsetzung mit Java EE </ r i c h : extendedDataTable></ u i : d e f i n e></ u i : c o m p o s i t i o n>¦ ¥Mit dem ui:composition Tag wird der Facelets View-Handler angewiesen, das imtemplate Attribut angegebene Template zu laden und den Inhalt von gebaeude.xhtmldamit zu ersetzen. Dabei wird durch die Nutzung von ui:define der im Templatedefinierte content-Bereich mit dem Inhalt des ui:define Tags ersetzt.Das Tag rich:extendedDataTable fügt die Tabelle zur Anzeige aller Gebäude indie Seite ein. Mit dem var Attribut wird eine Laufvariable namens gebaeudedefiniert, über die beim Aufbau der Tabelle das aktuelle Gebäude angesprochenwird. Weiterhin wird mit dem selection Attribut die Speicherung der vom Nutzerselektierten Tabellenzeile festgelegt. Die Werte mit denen die Tabelle befüllt wird,kommen aus der Managed Bean gebaeudeTableModel und deren allEntities Attribut.Die rich:column Tags definieren die Spalten der Tabelle. Mit f:facet wird dabeidie Beschriftung im Spaltenkopf festgelegt. Der Inhalt der Spalten stammt aus derLaufvariable gebaeude. Pro Spalte wird ein Attribute von Gebaeude angezeigt.Damit die Managed Bean gebaeudeTableModel in einer JSF Seite genutzt werdenkann, wird ein Eintrag in der faces-config.xml Datei angelegt.§ Listing 4.19: GebaeudeTableModel Eintrag in faces-config.xml ¤<managed−bean> <managed−bean−name>gebaeudeTableModel</managed−bean−name> <managed−bean−c l a s s> de . htw . b e r l i n . jroomyweb . bean . model . impl . GebaeudeTableModel </managed−bean−c l a s s> <managed−bean−s c o p e>s e s s i o n</managed−bean−s c o p e></managed−bean>¦ ¥Dadurch werden Objekte der Klasse GebaeudeTableModel zur Managed Bean mitdem Gültigkeitsbereich einer Session. 57
  • 67. 4.2 Umsetzung mit Java EEIn der gesamten Beispielanwendung werden die Daten in Tabellenform angezeigt.Daher gibt es mehrere TableModels. Aufgrunddessen wird ein generisches Interfacezur allgemeinen Beschreibung der Funktionen und Werte einer Tabelle definiert.§ Listing 4.20: TableModel Interface für die Abbildung von Tabellen ¤public i n t e r f a c e TableModel<T> { L i s t <T> g e t A l l E n t i t i e s ( ) ; void s e t A l l E n t i t i e s ( L i s t <T> a l l E n t i t i e s ) ; void addEntity (T e n t i t y ) ; void removeEntity (T e n t i t y ) ; T getSelectedEntity () ; void s e t S e l e c t e d E n t i t y (T s e l e c t e d E n t i t y ) ; SimpleSelection getSelection () ; void s e t S e l e c t i o n ( S i m p l e S e l e c t i o n s e l e c t i o n ) ; void c l e a r S e l e c t i o n ( ) ; void s e l e c t E n t i t y (T e n t i t y ) ; void s e l e c t L a s t E n t i t y ( ) ; void s e l e c t E n t i t y A b o v e (T e n t i t y ) ;}¦ ¥Demnach besteht eine Tabelle aus mehreren Entitäten, die in allEntities gespei-chert werden, verfügt über Funktionen zum Hinzufügen und Löschen von Entitätenund speichert die aktuell ausgewählte Entität. Außerdem ist ein einfaches Selek-tionshandling implementiert. Es können bestimmte Entitäten mit selectEntity,selectLastEntity und selectEntityAbove ausgewählt werden.Dieses funktionsreiche Interface ist die Grundlage der Backing Beans Klassen (wiez.B. GebaeudeTableModel), die in der Anwendung an die RichFaces Tabellenkom-ponente rich:extendedDataTable angebunden werden. Dadurch werden die Daten 58
  • 68. 4.2 Umsetzung mit Java EEaus der Datenbank zur Anzeige in den View gebracht. Dies funktioniert intern überController Objekte, die DAOs nutzen, um die Daten zu laden und z.B. anschließendüber die setAllEntities Methode des TableModel in das Frontend zu bringen. DieController verbinden dabei den View mit der Persistenzschicht.12Das folgende Listing zeigt exemplarisch, wie im GebaeudeTableController der View,repräsentiert durch das Backing Bean Objekt tableModel,13 mit der Persistenzschichtverbunden wird.§ Listing 4.21: Laden aller Gebäude im GebaeudeTableController ¤public void r e l o a d A l l G e b a e u d e ( ) { t a b l e M o d e l . s e t A l l E n t i t i e s ( dao . f i n d A l l ( ) ) ; // . . .}¦ ¥Neben dem Laden der Daten und der Anzeige dieser durch rich:extendedDataTablegibt es die Möglichkeit der Selektion eines Gebäudes in der Tabelle. Damit dieAnwendung für den Nutzer transparent das ausgewählte Gebäude lädt, wird Ajaxverwendet. Dazu wird das a4j:support Tag von RichFaces verwendet. Dies warbereits im Listing 4.18 abgebildet und wird im folgenden Listing erneut gezeigt.§ Listing 4.22: Ajax Support zum Nachladen des ausgewählten Gebäudes ¤<a 4 j : s u p p o r t event=" o n s e l e c t i o n c h a n g e " action="#{g e b a e u d e T a b l e S e l e c t i o n H a n d l e r . onSelectionChange () } " reRender=" mainPanel " />¦ ¥Mit dem a4j:support Tag können RichFaces Komponenten Ajax Funktionalitätenhinzugefügt werden. Das event Attribut gibt dabei das Ereignis an, bei dem der AjaxRequest abgesetzt wird; in diesem Fall bei einem Selektionswechsel. Die auf dem12 In größeren Anwendungen wird zwischen Controllern und der Persistenzschicht eine weitere Schicht eingebaut, die Serviceschicht. Darin wird die Geschäftslogik implementiert. Da es sich bei der Beispielanwendung um eine reine CRUD-Applikation handelt, erübrigt sich die Definition einer Serviceschicht.13 Das ist die im Listing 4.18 verwendete Managed Bean gebaeudeTableModel. 59
  • 69. 4.2 Umsetzung mit Java EEServer auszuführende Logik wird mit dem action Attribut in Form einer Methodeangegeben. Zusätzlich unterstützt RichFaces das automatische Neurendern voneinzelnen Komponenten, die im reRender Attribut aufgelistet werden. Hierbeiwird die Komponente mit der ID mainPanel bei der Auswahl eines Gebäudes neugerendert. Das mainPanel beinhaltet die Darstellung des ausgewählten Gebäudes.Die dazu benötigten Daten werden vom Server nachgeladen.Der im action Attribut angegebene GebaeudeTableSelectionHandler ist wie dasGebaeudeTableModel eine Managed Bean und hat daher ebenfalls einen Eintrag inder faces-config.xml Datei, wie das folgende Listing zeigt.§ Listing 4.23: GebaeudeTableSelectionHandler Eintrag in faces-config.xml ¤<managed−bean> <managed−bean−name> gebaeudeTableSelectionHandler </managed−bean−name> <managed−bean−c l a s s> de . htw . b e r l i n . jroomyweb . bean . a c t i o n . impl . GebaeudeTableSelectionHandler </managed−bean−c l a s s> <managed−bean−s c o p e>r e q u e s t</managed−bean−s c o p e> <managed−p r o p e r t y> <p r o p e r t y −name>gebaeudeTableModel</ p r o p e r t y −name> <v a l u e>#{gebaeudeTableModel }</ v a l u e> </managed−p r o p e r t y></managed−bean>¦ ¥Der GebaeudeTableSelectionHandler wird als Managed Bean mit einer Gültigkeits-dauer eines Requests deklariert. Außerdem wird eine einfache Form von DependencyInjection angewendet. Mit managed-property Tags können Abhängigkeiten einerManaged Bean über die faces-config.xml aufgelöst werden. Das interessante da- 60
  • 70. 4.2 Umsetzung mit Java EEbei, die Angabe von anderen Managed Beans ist möglich. So ist es hier mit derManaged Bean gebaeudeTableModel geschehen. GebaeudeTableSelectionHandlerhat ein Attribute vom Typ des GebaeudeTableModel, das darüber automatischinitialisiert wird. Voraussetzung dafür ist das Vorhandensein einer entsprechendenSetter-Methode für gebaeudeTableModel im GebaeudeTableSelectionHandler.Damit kann die onSelectionChange Methode über einen EL-Ausdruck angepro-chen werden, um über Ajax aufgerufen zu werden und auf die Änderung derGebäudeauswahl zu reagieren.§ Listing 4.24: Ereignisbehandlung in onSelectionChange ¤public void o n S e l e c t i o n C h a n g e ( ) { I t e r a t o r <Object> k e y s I t e r a t o r = gebaeudeTableModel . g e t S e l e c t i o n ( ) . getKeys ( ) ; i f ( k e y s I t e r a t o r . hasNext ( ) ) { Gebaeude s e l e c t e d G e b a e u d e = gebaeudeTableModel . g e t A l l E n t i t i e s ( ) . g e t ( ( I n t e g e r ) k e y s I t e r a t o r . next ( ) ) ; gebaeudeTableModel . s e t S e l e c t e d E n t i t y ( selectedGebaeude ) ; }}¦ ¥In der Ereignisbehandlung von onSelectionChange wird der Index des selektierenGebäudes aus dem vom GebaeudeTableModel verwalteten SimpleSelection 14 Objektmittels getSelection().getKeys() gelesen. Anschließend wird das Gebäudeobjekt ausdem Model geholt und als selektierte Entität in diesem abgelegt. Durch dieseneinfachen Mechanismus kann im Rest der Anwendung jederzeit auf das aktuellausgewählte Gebäude zugegriffen werden, indem das selectedEntity Attribut des14 Die SimpleSelection Klasse ist eine RichFaces Klasse und dient zur Speicherung einer einfachen Selektion. 61
  • 71. 4.2 Umsetzung mit Java EEGebaeudeTableModel angesprochen wird. Dies wird auch im folgenden Listingdeutlich.§ Listing 4.25: Anzeige der Bezeichnung des ausgewählten Gebäudes ¤<r i c h : me ssa ge s e r r o r C l a s s=" e r r o r " /><h : inputText id=" buildingName " s i z e=" 15 " value="#{gebaeudeTableModel . s e l e c t e d E n t i t y . b e z e i c h n u n g } "> <r i c h : b e a n V a l i d a t o r /></h : inputText>¦ ¥Dieser Abschnitt befindet sich im mainPanel, das durch eine Selektionsänderungneu gerendert wird. Dem abgebildeten JSF Tag h:inputText wird durch die Angabeim value Attribut die Bezeichnung des ausgewählten Gebäudes zugewiesen undda h:inputText ein Textfeld darstellt, kann die Bezeichnung somit vom Nutzerbearbeitet werden. Durch die Angabe des value Attributs nimmt JSF eine Bindungzwischen Komponente und dem Attribut der Managed Bean vor. Änderungen ander Oberfläche werden in die Managed Bean überführt.Die in der Domänenschicht definierten Validierungsregeln werden in der Ober-fläche mit Hilfe von RichFaces Komponenten integriert. RichFaces bietet mitrich:beanValidator die direkte Einbindung von Bean Validations an. Dazu mussdieses Tag lediglich in eine JSF Komponente eingebettet werden, wie hier beih:inputText für die Gebäudebezeichnung geschehen ist. Dadurch werden Eingabenautomatisch mit den Validierungsregeln des bezeichnung Attributs der GebaeudeKlasse geprüft. Der Vorteil dieser Methode ist natürlich, dass Validierungsregeln nuran einer Stelle, nämlich im Domänenmodell, angegeben werden und keine Dopplungin verschiedenen Schichten erfolgt, was ohne die einfache Integration der BeanValidations der Fall gewesen wäre. Mögliche Fehler werden durch rich:messagesauf der Seite ausgegeben.Solche h:inputText Felder und andere Eingabekomponenten sind für jedes Attributeines Gebäudes auf der Seite vorhanden und werden, wie bei der Gebäudebe-zeichnung erläutert, durch den Zugriff auf das aktuell ausgewählte Gebäude zur 62
  • 72. 4.2 Umsetzung mit Java EEBearbeitung angezeigt.Das Speichern der geänderten Gebäudedaten erfolgt mit dem Drücken eines Buttons,der durch das im folgenden Listing dargestellte a4j:commandButton Tag in dieSeite integriert wird.§ Listing 4.26: Button Tag zum Speichern eines Gebäudes in gebaeude.xml ¤<a 4 j : commandButton value="#{msg . s a v e _ b u i l d i n g } " action="#{g e b a e u d e T a b l e S e r v i c e . s a v e S e l e c t e d E n t i t y ( ) } " />¦ ¥Die a4j:commandButton RichFaces Komponente entspricht der h:commandButtonJSF Standardkomponente. Allerdings wird bei einem Klick auf den Button nichtdas Formular über eine HTTP Methode abgeschickt. Es erfolgt hingegen ein AjaxCall. Auf dem Server wird somit die saveSelectedEntity Methode der ManagedBean gebaeudeTableService aufgerufen.Bevor die Methode zum Speichern gezeigt wird, soll kurz der Managed Bean Eintragin der faces-config.xml für GebaeudeTableService betrachtet werden.§ Listing 4.27: GebaeudeTableService Eintrag in faces-config.xml ¤<managed−bean> <managed−bean−name>g e b a e u d e T a b l e S e r v i c e</managed−bean−name> <managed−bean−c l a s s> de . htw . b e r l i n . jroomyweb . bean . c o n t r o l l e r . impl . GebaeudeTableController </managed−bean−c l a s s> <managed−bean−s c o p e>r e q u e s t</managed−bean−s c o p e> <managed−p r o p e r t y> <p r o p e r t y −name>t a b l e M o d e l</ p r o p e r t y −name> <v a l u e>#{gebaeudeTableModel }</ v a l u e> </managed−p r o p e r t y> < !−− w e i t e r e managed−p r o p e r t y E i n t r ä g e −−></managed−bean>¦ ¥ 63
  • 73. 4.2 Umsetzung mit Java EEWas bei der Deklaration auffällt, ist die unterschiedliche Namensgebung der Klasse(ein Controller) und der Managed Bean (ein Service). Zu Beginn der Entwicklungwar der Gedanke dabei, dass die Tabellenmodelle durch Services um Funktionenergänzt werden. Jedoch stellte sich die Benennung als irreführend heraus, wodurchdie Services zu Controllern umbenannt wurden. Der Refactoringmechanismus dereingesetzten Entwicklungswerkzeuge ermöglichte jedoch nicht die Umbenennungder Managed Beans in der faces-config.xml, wodurch in den XHTML-Dokumentenweiterhin Services referenziert werden.Der GebaeudeTableController erbt wie jede TableController Klasse von der Basis-klasse AbstractTableController. Da das Speichern für jede Tabelle in der Applikationidentisch zu implementieren ist, wird die saveSelectedEntity Methode in der Basis-klasse formuliert.§ Listing 4.28: Speichern des selektierten Gebäudes in AbstractTableController ¤public abstract c l a s s A b s t r a c t T a b l e C o n t r o l l e r <T> extends A b s t r a c t C o n t r o l l e r implements T a b l e C o n t r o l l e r { @Inject protected Dao<T, Long> dao ; protected TableModel<T> t a b l e M o d e l ; public void s a v e S e l e c t e d E n t i t y ( ) { T s e l e c t e d E n t i t y = tableModel . g e t S e l e c t e d E n t i t y ( ) ; i f ( s e l e c t e d E n t i t y != null ) { dao . merge ( s e l e c t e d E n t i t y ) ; } }}¦ ¥Da AbstractTableController von AbstractController erbt, kann das benötigte DAOfür den Datenbankzugriff über Google Guice injiziert werden. Das TableModel 64
  • 74. 4.2 Umsetzung mit Java EEwird durch den Dependency Injection Mechanismus von JSF injiziert. Die Logikzum Speichern des ausgewählten Gebäudes ist relativ simpel. Zuerst wird vomTableModel die selektierte Entität geholt; in diesem Fall ist die Entität vom generi-schen Typ T mit Gebaeude typisiert. Anschließend wird das Objekt selectedEntity,welches in diesem Anwendungsfall das ausgewählte Gebäude ist, über das DAOgespeichert.Damit wurde das Speichern von geänderten Gebäudedaten ausführlich erläutert.Dabei sind die Grundkonzepte der Umsetzung des Java EE Prototypen gezeigtworden, die so auch für die beiden folgenden Anwendungsfälle gelten. TableModelsbilden die Basis für die Anzeige und Bearbeitung von Daten. Über TableControllerkönnen Tabelleneinträge gespeichert, gelöscht und neu angelegt werden. Selek-tiert der Nutzer eine Tabellenzeile wird eine Ereignisbehandlungsroutine in einemSelectionHandler ausgeführt.Räume suchenBeim Anwendungsfall der Raumsuche wird noch einmal das Zusammenspiel derverschiedenen Anwendungskomponenten dargestellt.Die Ergebnisse der Suche werden, wie alle anderen Daten auch, in einer Tabelledargestellt, deren Definition nachfolgend gekürzt dargestellt ist.15§ Listing 4.29: Auszug des Tags für die Anzeige der Suchergebnisse ¤<r i c h : extendedDataTable id=" raumSucheTable " value="#{raumSucheTableModel . a l l E n t i t i e s } ">¦ ¥Die Suchergebnisse werden dabei aus der Managed Bean RaumSucheTableModelgeladen.Die Suche wird durch einen Klick auf die Buttonkomponente a4j:commandButtongestartet, die einen Ajax Call absetzt. Dabei wird die Methode sucheRaeume desDefaultRaumSucheController aufgerufen, der über den Bezeichner raumSucheSer-vice als Managed Bean zur Verfügung steht.15 Das Tag sowie die gesamte Beschreibung der Suchseite befindet sich in der Datei suche.xhtml. 65
  • 75. 4.2 Umsetzung mit Java EE§ Listing 4.30: Tag für den Suchbutton ¤<a 4 j : commandButton value="#{msg . s t a r t _ s e a r c h } " action="#{raumSucheService . sucheRaeume ( ) } " reRender=" e r g e b n i s P a n e l " />¦ ¥Die Logik für die Raumsuche gestaltet sich in der sucheRaeume Methode sehreinfach, wie das folgende Listing zeigt.§ Listing 4.31: Suchlogik in sucheRaeume Methode ¤raumSucheTableModel . s e t A l l E n t i t i e s ( raumDao . getRaeumeByMerkmal ( s e l e c t e d M e r k m a l . g e t I d ( ) ) ) ;¦ ¥Im RaumDao existiert die Abfragemethode getRaeumeByMerkmal, die alle Räumemit einem bestimmten Ausstattungsmerkmal zurückgibt. Das Ergebnis der Abfragewird dem RaumSucheTableModel zugewiesen.Für die Abfragemethode wird eine Funktionalität der Guice Erweiterung warp-persist genutzt, um die Abfrage möglichst einfach und elegant zu formulieren. Eswird die warp-persist Finder-Annotation genutzt.§ Listing 4.32: Finder Methode für die Raumsuche ¤@Transactional@Finder ( query=" s e l e c t d i s t i n c t a . raum from A u s s t a t t u n g a where a . merkmal . i d = : merkmalId " )public L i s t <Raum> getRaeumeByMerkmal ( @Named( " merkmalId " ) Long merkmalId ) { throw new A s s e r t i o n E r r o r ( ) ;}¦ ¥In der Finder-Annotation genügt es die Abfrage im query Attribut in Form einesJava Persistence Query Language Ausdrucks abzulegen. Dadurch wird die MethodegetRaeumeByMerkmal zu einer Finder Methode, die von warp-persist implementiertwird. Daher wirft der Methodenrumpf einen AssertionError, denn an diese Stelleim Programmcode wird die ausführende Umgebung bei einer funktionierenden und 66
  • 76. 4.2 Umsetzung mit Java EErichtig konfigurierten warp-persist Bibliothek nicht gelangen. Sollte dies bei einerFehlkonfiguration der Fall sein, wird ein Laufzeitfehler vom Typ AssertionErrorgeworfen und der Entwickler somit auf den schwerwiegenden Fehler aufmerksamgemacht.16 Mit der Named-Annotation, die dem Parameter merkmalId hinzugefügtwird, erhält warp-persist die Information, dass der Wert dieses Parameters denPlatzhalter :merkmalId in der formulierten Abfrage ersetzen soll.Aufgrund der bereits implementierten Infrastruktur in Form von DAOs und derNutzung von Guice/warp-persist war die Umsetzung der Raumsuche unkompliziert.Ausstattungsmerkmale verwaltenNach den bisherigen Erläuterungen zu den Umsetzungen der Anwendungsfälle istes für die Verwaltung der Ausstattungsmerkmale interessant zu betrachten, wiedas Löschen implementiert wurde. In der Beschreibung des Anwendungsfalls hießes, ein Ausstattungsmerkmal ist nur löschbar, wenn es in keiner Raumausstattungverwendet wird.Um diese Anforderung umzusetzen, wird der Button zum Löschen von Ausstattungs-merkmalen nur dargestellt, wenn die im folgenden Listing abgebildete Bedingungerfüllt ist.17§ Listing 4.33: Auszug des Tags für das Löschen von Ausstattungsmerkmalen ¤<a 4 j : commandButton id=" merkmalDeleteButton " r e n d e r e d="#{m e r k m a l T a b l e S e r v i c e . i s S e l e c t e d M e r k m a l D e l e t a b l e ( ) } " />¦ ¥Mit dem rendered Attribut wird das Rendern eines Tags von einer Bedingung ab-hänig gemacht. Nur wenn isSelectedMerkmalDeletable() den Wert true zurückgibt,wird der Button zum Löschen dargestellt. In dieser Methode des MerkmalTable-16 Dieses Vorgehen bei Finder Methoden wird von den warp-persist Entwicklern empfohlen. (siehe http://code.google.com/p/warp-persist/wiki/UsingDynamicFinders)17 Das Tag befindet sich in der Datei merkmale.xhtml, die die Seitenbeschreibung zur Verwaltung von Ausstattungsmerkmalen enthält. 67
  • 77. 4.3 Umsetzung mit Scala und LiftController erfolgt die Datenbankabfrage mit Hilfe eines DAOs, wie das folgendeListing zeigt.§ Listing 4.34: Abfragelogik, ob ein Ausstattungsmerkmal löschbar ist ¤L i s t <Ausstattung > a l l A u s s t a t t u n g s m e r k m a l e = ( ( MerkmalDao ) dao ) . getAusstattungenByMerkmal ( s e l e c t e d M e r k m a l . g e t I d ( ) ) ;return a l l A u s s t a t t u n g s m e r k m a l e . isEmpty ( ) ;¦ ¥Wenn die Liste mit allen Ausstattungsmerkmalen leer ist, wird true zurückgege-ben. Dabei wird die Abfragemethode getAusstattungenByMerkmal des speziali-sierten MerkmalDao verwendet. Wie die Abfragemethode zur Raumsuche wirdgetAusstattungenByMerkmal mit einer Finder-Annotation versehen, die nachfolgendabgebildet ist.§ Listing 4.35: Finder Annotation von getAusstattungenByMerkmal ¤@Finder ( query= " from A u s s t a t t u n g a where a . merkmal . i d = : merkmalId " )¦ ¥Somit ist sichergestellt, dass die Funktion zum Löschen von Ausstattungsmerkmalennur zur Verfügung steht, wenn das ausgewählte Merkmal löschbar ist.4.3 Umsetzung mit Scala und LiftDer Prototyp wird mit Scala in der Version 2.8.1 und Lift 2.2 entwickelt. Währendder Entwicklung ist ein Wechsel auf Lift 2.3 erfolgt, das am 6. April erschienen ist.4.3.1 DomänenmodellDamit die Klassen der Domänenschicht in einer relationalen Datenbank per-sistierbar sind, erfolgt die Umsetzung des Domänenmodells mit Hilfe der LiftMapper Bibliothek. Alle Klassen des Domänenmodells befinden sich im Paket 68
  • 78. 4.3 Umsetzung mit Scala und Liftde.htw.berlin.jroomyweb.model. Ein Gebäude wird durch die Klasse Building re-präsentiert und ein Raum durch die Klasse Room. Die Raumausstattungen undAusstattungsmerkmale des Domänenmodells werden durch die Klassen Equipmentund Attribute umgesetzt.Folgend werden die wichtigsten Implementierungsdetails des Domänenmodellsbesprochen.§ Listing 4.36: Deklaration der Scala/Lift Gebäude Fachklasse ¤c l a s s B u i l d i n g extends LongKeyedMapper [ B u i l d i n g ] with IdPK with OneToMany [ Long , B u i l d i n g ]¦ ¥Building erbt vom Trait LongKeyedMapper. Der LongKeyedMapper Trait bildetdie Grundlage für das Persistieren einer Klasse in einem relationalen Datenbank-system.18 Durch das eckige Klammernpaar wird der generische Typ des Long-KeyedMappers mit Building typisiert. Mit diesem Trait gelangen Methoden zumSpeichern und Löschen von Datenbankeinträgen des Typs Building in die Klasse.Weiterhin muss eine Methode namens primaryKeyField definiert werden, die einObjekt vom Typ MappedLongIndex zurückgibt. Darüber wird der Primärschlüsselin der Datenbank abgebildet. Anstatt diese Anforderung selbst zu implementieren,wird der Trait IdPK eingemixt, der dieses Attribut und die Methode bereitsdefiniert.Um die 1-n Beziehung zwischen Gebäuden und Räumen zu implementieren, wirdzusätzlich der Trait OneToMany in Building eingemixt. Dieser Trait bildet keineDatenbankbeziehungen ab, sondern fügt zusätzliche Funktionalitäten der Klassehinzu. Zusätzliches Verhalten, wie z.B. kaskadierendes Löschen von abhängigenObjekten, wird darüber implementiert, wenn dies an entsprechender Stelle deklariertwurde.19Die vier String-Eigenschaften eines Gebäudes werden durch Attribute realisiert, dievon der Klasse MappedPoliteString erben. Bei der Verwendung von Lift Mapper18 Vgl. [Per11, S. 42]19 Mehr dazu bei der Implementierung der Beziehung zwischen Gebäuden und Räumen. 69
  • 79. 4.3 Umsetzung mit Scala und Liftwerden sämtliche Attribute persistiert, die Objekte sind und von einer Klassedes Typs MappedField erben.20 MappedPoliteString ist solch eine Klasse undbietet zusätzlich die Möglichkeit der Angabe einer maximalen Feldlänge für diespeicherbare Zeichenkette (z.B. maximal 250 Zeichen für den Straßennamen).§ Listing 4.37: Attribute der Building Klasse ¤object s t r e e t extends M a p p e d P o l i t e S t r i n g ( this , 2 5 0 )object c i t y extends M a p p e d P o l i t e S t r i n g ( this , 1 0 0 )object zipCode extends M a p p e d P o l i t e S t r i n g ( this , 6 )¦ ¥Um die zusätzliche Anforderung der nicht leeren Bezeichnung zu erfüllen, wurdebeim Attribut name, das die Bezeichnung eines Gebäudes abbildet, die Validie-rungsmethode validations von MappedPoliteString überschrieben. Ursprünglichwird diese Methode im SettableField Trait implementiert, von dem MappedFieldund daher auch MappedPoliteString erben.§ Listing 4.38: name Attribut der Building Klasse ¤object name extends M a p p e d P o l i t e S t r i n g ( this , 2 0 0 ) { override def v a l i d a t i o n s = valMinLen ( 1 , S . ? ( " buildingName " ) ) _ : : N i l}¦ ¥Bei Lift Mapper wird die Validierung auf Feldebene angegeben. Für jedes Feldkann validations überschrieben werden. In dieser Methode werden Validierungs-funktionen aneinandergereiht, die einzelne Validierungsregeln abbilden. Jede dieserFunktionen muss dabei einen Feldtypen entgegennehmen und eine Liste von Feh-lern zurückgeben.21 Die generische Signatur einer Validierungsfunktion sieht daherfolgendermaßen aus:20 Es gibt zusätzlich die Möglichkeit solche Objekte explizit von der Persistierung auszuschließen, indem die Methode ignoreField_? überschrieben wird und den Wert true zurückgibt.21 Vgl. [Per11, S. 53] 70
  • 80. 4.3 Umsetzung mit Scala und Lift§ Listing 4.39: Generische Signatur einer Mapper Validierungsfunktion ¤FieldType = L i s t [ F i e l d E r r o r ] >¦ ¥Der FieldType ist ein generischer Typ, der jeden Typen darstellen kann.Für das Feld name wird valMinLen als Validierungsregel verwendet, die den Feld-inhalt auf eine Mindestlänge prüft. Mit valMinLen :: Nil wird eine Liste ausdem Ausdruck gebildet. Nil kennzeichnet das Ende einer Liste und :: ist derListenkonkatenation-Operator. Sollen weitere Validierungsfunktionen für das Feldname gelten, so werden diese der Liste in validations mit dem ::-Operator hinzuge-fügt. Der noch nicht bekannte Aufruf im zweiten Parameter von valMinLen lädtdie zu verwendende Fehlermeldung aus einer Ressourcendatei.22Zum besseren Verständnis dieses Validierungsausdrucks wird folgend die Deklarationvon valMinLen betrachtet.§ Listing 4.40: Signatur von valMinLen ¤def valMinLen ( l e n : Int , msg : = S t r i n g ) ( v a l u e : ValueType ) > : List [ FieldError ]¦ ¥Die Funktion hat zwei Parameterlisten. Dabei dient die erste Parameterliste dazu diezusätzlichen Validierungsparameter entgegenzunehmen und die zweite nimmt erstden eigentlichen Feldinhalt entgegen. Dabei entspricht ValueType dem generischenFieldType.Somit wird die Bedeutung des Unterstrichs beim Aufruf der valMinLen Funktionin validations deutlich. Dieser dient als Platzhalter für die zweite Parameterliste.Dadurch wird vom Scala Compiler an dieser Stelle eine partiell angewandte Funktiongeneriert. Da valMinLen zwei Parameterlisten hat, wird hierbei vom Scala Compilerebenfalls Currying angewendet.Es wurden alle einfachen Attribute eines Gebäudes abgebildet und die zusätzlicheEinschränkung für die Gebäudebezeichnung implementiert. Die noch fehlende22 Das wird noch bei der Umsetzung der Anwendungsfälle näher erläutert. 71
  • 81. 4.3 Umsetzung mit Scala und LiftImplementierung der Beziehung zwischen Gebäuden und Räumen wird folgendgezeigt.§ Listing 4.41: rooms Objekt der Building Klasse ¤object rooms extends MappedOneToMany (Room , Room . b u i l d i n g , OrderBy (Room . id , Ascending ) ) with Cascade [ Room ] with Owned [ Room ]¦ ¥Mit dem Erben der MappedOneToMany Klasse werden in Lift Mapper 1-n Bezie-hungen implementiert. Gleichzeitig wird dadurch das kaskadierende Speichern derRoom Objekte eines Gebäudes implementiert. Dabei wird zuerst angegeben, fürwelchen Typ die Beziehung gilt (Room) und auf welchem Attribut die Referenz(Room.building) gespeichert wird. Optional kann die Abfrage der Räume durch einenQuery-Parameter23 ergänzt werden. In diesem Fall wird mit dem Query-ParameterOrderBy eine aufsteigende Sortierung implementiert, die auf dem Primärschlüsselder Räume operiert.Die eingemixten Traits Cascade und Owned implementieren das oben angesprochenezusätzliche Verhalten für eine 1-n Beziehung. Cascade sorgt dafür, dass alle Räumegelöscht werden, wenn das korrespondierende Gebäude gelöscht wird, was demkaskadierenden Löschen entspricht. Der Owned Trait wiederum bewirkt das Löschenvon Räumen, wenn diese aus dem rooms Objekt entfernt wurden, und das Gebäudegespeichert wird.Da Objekte der MappedOneToM any Klasse verzögert initialisiert werden,24 mussvor dem Löschen des Gebäudes das rooms Objekt mindestens einmal in irgendeinerForm aufgerufen werden, damit es initialisiert wird. Andernfalls ist das Objekt fürdie Löschfunktion der Building Klasse nicht vorhanden und die Räume werdennicht gelöscht, obwohl der Cascade Trait eingemixt wurde. Dieses teils verwirrendeVerhalten ist bekannt und soll geändert werden.2523 Ein Query-Parameter ist eine Implementierung des Traits net.liftweb.mapper.QueryParam.24 Ein sofortiges Laden aller Räume ist nicht einstellbar. Solch ein Verhalten muss extra über Funktionen zur Datenbankabfrage implementiert werden.25 siehe dazu https://groups.google.com/group/liftweb/browse_thread/thread/b800e2c28e197fc3 oder https://groups.google.com/group/liftweb/browse_thread/thread/2f748e1725fb27b1 72
  • 82. 4.3 Umsetzung mit Scala und LiftUm nicht überall im Anwendungscode vor dem Löschen eines Gebäudes das roomsObjekt zu initialisieren,26 wurde ein zentraler Workaround in der Löschfunktionder Building Klasse implementiert. Bevor die eigentliche Löschfunktion für einGebäude aufgerufen wird, werden die Räume durch den Aufruf von delete_! aufdem rooms Objekt explizit gelöscht, was jedoch mindestens einen zusätzlichenDatenbankzugriff erzeugt.§ Listing 4.42: Überschriebene Löschfunktion der Building Klasse ¤override def d e l e t e _ ! = { rooms . d e l e t e _ ! super . d e l e t e _ !}¦ ¥Bei der Nutzung von Lift Mapper wird das Companion Objekt Implementierungs-muster exzessiv eingesetzt. Das heißt, für jede Klasse, die über Mapper persistiertwird, erfolgt die Definition eines entsprechenden Companion Objekts; so auch fürBuilding.§ Listing 4.43: Companion Objekt der Building Klasse ¤object B u i l d i n g extends B u i l d i n g with LongKeyedMetaMapper [ B u i l d i n g ] { override def dbTableName = " b u i l d i n g "}¦ ¥Dem Companion Objekt der Building Klasse wird ebenfalls ein Mapper Traiteingemixt, LongKeyedMetaMapper. Dieser Trait stellt Methoden für das Erzeu-gen einer neuen Instanz der korrespondierenden Klasse und das Formulieren vonDatenbankabfragen zur Verfügung. Ebenso können dadurch Metainformationengeändert werden, wie z.B. hier der Tabellennamen durch das Überschreiben vondbTableName. Daher auch der Namenszusatz Meta in dem Trait. Das ist eine26 Das wäre in großen Anwendungen eine gefährliche Fehlerquelle. So etwas wird schnell vergessen oder übersehen und führt dann zu unvorhergesehenen Zuständen in der Anwendung. 73
  • 83. 4.3 Umsetzung mit Scala und LiftNamenskonvention von Lift Mapper.27Ungewöhnlich ist hierbar, dass das Companion Objekt von seiner Klasse erbt.Dies ist Implementierungsinterna von Lift Mapper geschuldet. Dadurch kann dasCompanion Objekt auf die definierten Felder der Building Klasse zugreifen, wasz.B. bei der Implementierung von Datenbankabfragen notwendig ist.28Diese Vernetzung findet ebenso von der Klasse Building aus statt. Jede MapperKlasse muss die Funktion getSingleton definieren. Diese gibt eine Referenz auf dasCompanion Objekt zurück.§ Listing 4.44: Rückgabe des Companion Objekts in getSingleton ¤def g e t S i n g l e t o n = B u i l d i n g¦ ¥Da es in Scala nicht ohne weiteres möglich ist, das Companion Objekt einerKlasse generisch zu ermitteln, wird darüber der Zugriff darauf ermöglicht. Für dasSpeichern in der Datenbank benötigen Mapper Klassen z.B. die Metainformationenihres Companion Objekts.In der Room Klasse, die den im Domänenmodell beschriebenen Raum abbildet,werden die selben Traits eingemixt wie in Building.§ Listing 4.45: Deklaration der Scala/Lift Raum Fachklasse ¤c l a s s Room extends LongKeyedMapper [ Room ] with IdPK with OneToMany [ Long , Room ]¦ ¥Beim rooms Objekt von Building wurde MappedOneToMany mit einer Referenzauf das Feld building in Room erzeugt. In diesem Feld wird die Referenz aufdas Gebäude eines Raumes gespeichert. Die Implementierung des building Feldsgestaltet sich wie die bisherige Integration von Lift Mapper. Es wird ein Attribut inder Room Klasse als Singleton Objekt angelegt. Dieses erbt von der Mapper KlasseLongMappedMapper, wodurch der Fremdschlüssel für die 1-n Beziehung zwischenGebäuden und Räumen implementiert wird.27 siehe http://www.assembla.com/spaces/liftweb/wiki/Mapper28 siehe http://groups.google.com/group/liftweb/browse_thread/thread/532cff6c0740d007 74
  • 84. 4.3 Umsetzung mit Scala und Lift§ Listing 4.46: Abbildung der Referenz auf ein Gebäude in Room ¤object b u i l d i n g extends LongMappedMapper ( this , B u i l d i n g )¦ ¥Ein Raum hat zwei numerische Werte, deren Implementierung am Bespiel derFensteranzahl eines Raumes gezeigt wird.§ Listing 4.47: Anzahl der Fenster in der Scala/Lift Room Klasse ¤object numberOfWindows extends MappedInt ( t h i s ) with ExtendedMappedIntValidator { override def v a l i d a t i o n s = validateNumberBiggerThanZero ( S . ? ( " roomNumberOfWindows " ) ) _ : : Nil}¦ ¥Die Mapper Klasse MappedInt bildet Ganzzahlen vom Typ Integer in der Datenbankab. Die für das Domänenmodell definierte Regel von positiven Ganzzahlen wirdhierbei mittels einer zusätzlichen Validierung gelöst, da Scala als auch Lift keinenspeziellen Typen dafür anbieten. Daher wird die Validierungsfunktion validate-NumberBiggerThanZero im selbst entwickelten ExtendedMappedIntValidator Traitimplementiert.§ Listing 4.48: Erweiterter Validator für Ganzzahlen vom Typ Integer ¤t r a i t ExtendedMappedIntValidator { this : F i e l d I d e n t i f i e r => def validateNumberBiggerThanZero ( e r r o r M e s s a g e : S t r i n g ) ( number : Int ) : List [ FieldError ] = i f ( number < 1 ) L i s t ( F i e l d E r r o r ( this , e r r o r M e s s a g e ) ) else List [ FieldError ] ( )}¦ ¥ 75
  • 85. 4.3 Umsetzung mit Scala und LiftMit einer Self-Type Annotation wird zu Beginn festgelegt, dass der Typ von thisim folgenden FieldIdentifier ist. Dies ist notwendig, da bei der Rückgabe einesFehlers ein Objekt von FieldError erzeugt wird und für diese Objekterzeugung eineReferenz auf das entsprechende Eingabefeld in Form eines FieldIdentifier Objektserwartet wird. Durch die Self-Typ Annotation ist der Trait nur für Untertypen vonFieldIdentifier nutzbar.Wie bei der bereits oben besprochenen valMinLen Funktion hat auch die valida-teNumberBiggerThanZero Funktion zwei Parameterlisten. In der ersten wird dieanzuzeigende Fehlermeldung übergeben, in der zweiten die zu validierende Zahl.Die Logik ist simpel. Ist die übergebene Zahl kleiner als Eins, wird eine Liste miteinem FieldError zurückgegeben, andernfalls eine leere Fehlerliste.Da jeder Raum mehrere Raumausstattungen haben kann, gibt es mit equipmentauch in der Room Klasse ein Feld, das von MappedOneToMany erbt. Ebenso musste,wie schon in der Building Klasse, die Löschfunktion von Room überschrieben werden,um das ordnungsgemäße Löschen von Raumausstattungen zu gewährleisten, wennein Raum gelöscht wird.§ Listing 4.49: Raumausstattungen in der Room Klasse ¤object equipment extends MappedOneToMany ( Equipment , Equipment . room , OrderBy ( Equipment . id , Ascending ) )override def d e l e t e _ ! = { equipment . f o r e a c h (_. d e l e t e _ ! ) super . d e l e t e _ !}¦ ¥In der Klasse Attribute ist die Implementierung der isDeletable Funktion inter-essant. Bei der Umsetzung des Anwendungsfalls „Ausstattungsmerkmale verwalten“erfolgt an einer Stelle im Programmcode die Abfrage, ob ein Ausstattungsmerkmalüberhaupt gelöscht werden kann. Solche domänenspezifischen Datenbankabfragenwerden bei Lift Anwendungen direkt in der Domänenschicht formuliert. 76
  • 86. 4.3 Umsetzung mit Scala und Lift§ Listing 4.50: isDeletable Funktion der Attribute Klasse ¤def i s D e l e t a b l e = Equipment . f i n d B y A t t r i b u t e ( t h i s ) . s i z e = 0 =¦ ¥Nach der Logik dieser Funktion ist ein Ausstattungsmerkmal nur löschbar, wenn esin keiner Raumausstattung verwendet wird. Das entspricht den Vorgaben aus derKonzeption der Anwendung (siehe 4.1.2). Dabei wird die findByAttribute Funktiondes Equipment Companion Objekts verwendet, die den typischen Aufbau einerDatenbankabfrage in Lift Mapper zeigt.§ Listing 4.51: Datenbankabfrage in der findByAttribute Funktion ¤def f i n d B y A t t r i b u t e ( a t t r i b u t e : A t t r i b u t e ) = f i n d A l l (By( Equipment . a t t r i b u t e , a t t r i b u t e ) )¦ ¥Da das Equipment Companion Objekt den Trait LongKeyedMetaMapper eingemixthat, stehen DSL-artige Funktionen für die Formulierung von Datenbankabfragenzur Verfügung. Dabei beziehen sich die Funktionen immer auf die Datenbankentitätdes Companion Objekts. Mit findAll wird also ausgedrückt, dass alle Raumausstat-tungen aus der Datenbank geladen werden. By formuliert eine Bedingung. EineRaumausstattung wird demnach nur geladen, wenn das Ausstattungsmerkmal demübergebenen attribute entspricht. Bei der Umwandlung zu SQL wird die Bedingungin die Where-Klausel geschrieben.4.3.2 AnwendungsfälleDamit die Anwendungsfälle umgesetzt werden können, muss die Anwendung zuerstkonfiguriert werden. Die Konfiguration einer Lift Anwendung erfolgt in der bootMethode der Boot Klasse. Die befindet sich beim entwickelten Prototypen im Paketbootstrap.liftweb.Bevor die soeben vorgestellten Mapper Klassen der Domänenschicht persistiertwerden, muss die Datenbankverbindung eingestellt werden. Dazu befinden sich inder boot Methode meherere Anweisungen. 77
  • 87. 4.3 Umsetzung mit Scala und Lift§ Listing 4.52: Verbindungseinstellungen für die Datenbank ¤val vendor = new StandardDBVendor ( Props . g e t ( " db . d r i v e r " ) openOr " o r g . h2 . D r i v e r " , Props . g e t ( " db . u r l " ) openOr " j d b c : h2 : jroomy ;AUTO_SERVER TRUE" , = Props . g e t ( " db . u s e r " ) , Props . g e t ( " db . password " ) )¦ ¥Mit einer Instanz von StandardDBVendor können in Lift Anwendungen Verbin-dungseinstellungen zu einer Datenbank gespeichert werden. Gleichzeitig können mitdem StandardDBVendor Verbindungen zur Datenbank auf- und wieder abgebautwerden. Intern ist der StandardDBVendor ein JDBC ConnectionManager. Hierbeiwird deutlich, dass Lift Mapper auf JDBC aufsetzt.In diesem Fall wird Props.get verwendet, um Einstellungen aus einer Datei zu ladenund diese im Konstruktor zu übergeben. Das Props Objekt dient in Lift dazu, Kon-figurationsdateien nach einem Namensschema einzulesen. Es ist darüber z.B. sehreinfach möglich für die Produktions- und Testumgebung getrennte Konfigurationenaufzubauen. Dazu müssen lediglich die entsprechenden Dateien mit „production.“bzw. „test.“ im Dateinamen beginnen.29Da für die Bespielanwendung kein großes Datenbanksystem notwendig ist, wurdenStandardwerte für den Datenbanktreiber und die URL angegeben, wodurch die In-Memory Datenbank H230 eingebunden wird, sollte keine Konfiguration existieren.Der Aufruf von Props.get gibt eine Box zurück. Der Box Typ ist vergleichbarmit dem Option Typ der Scala Standardbibliothek, der oft als Alternative fürden Null Wert eingesetzt wird.31 Sollte die zurückgegebene Box leer sein, wirddurch die Funktion openOr der zu verwendende Standardwert gesetzt. Für denDatenbanktreiber ist das z.B. die Zeichenkette „org.h2.Driver“.Dieses Box-openOr-Muster findet sich häufig in Lift Anwendungen, so auch im29 siehe http://scala-tools.org/mvnsites/liftweb-2.3/net/liftweb/util/Props$.html30 siehe http://www.h2database.com31 Im Gegensatz zu Option bietet Box weitere Funktionen. So kann eine Box z.B. in einen Fehler umgewandelt werden. (siehe http://scala-tools.org/mvnsites/liftweb- 2.3/net/liftweb/common/Box.html) 78
  • 88. 4.3 Umsetzung mit Scala und LiftPrototypen. Somit muss bei Fehlersituationen keine Abfrage auf Null oder dasAbfangen von Exceptions implementiert werden, sondern es kann ein Standardver-halten bzw. -rückgabewert mit openOr hinterlegt werden.Um die in vendor definierte Datenbank zu nutzen, werden weiterhin die folgendenAnweisungen in der boot Methode formuliert.§ Listing 4.53: Datenbanksetup der Lift Anwendung ¤L i f t R u l e s . unloadHooks . append ( vendor . c l o s e A l l C o n n e c t i o n s _ ! )DB. d e f i n e C o n n e c t i o n M a n a g e r ( D e f a u l t C o n n e c t i o n I d e n t i f i e r , vendor )S . addAround (DB. buildLoanWrapper )¦ ¥Über das globale Objekt LiftRules werden Lift Anwendungen konfiguriert.32 Für denPrototypen wurde ein unloadHook abgelegt, der alle offenen Datenbankverbindungenbeim Beenden der Web-Anwendung schließt. Ein unloadHook ist eine Funktion, diebeim Herunterfahren der Anwendung im Servlet-Container ausgeführt wird.Da Lift Mapper intern JDBC verwendet, wird danach der JDBC ConnectionMana-ger von vendor mit dem von Lift Mapper über das DB Objekt verbunden.33 Internimplementiert Lift ein simples Connection Pooling, damit bereits geöffnete Verbin-dungen wiederverwendet werden können und nicht für jede Datenbankoperationeine neue Verbindung aufgebaut werden muss. Das DefaultConnectionIdentifierObjekt wird dafür benötigt, um mehrere Datenbanken voneinander zu unterschei-den. Da im Prototypen lediglich eine Datenbank verwendet wurde, genügt dieStandardimplementierung.Der aktuelle Zustand (engl. State) der Bearbeitung eines HTTP Request oderResponse wird in Lift mit dem S Objekt dargestellt.34 Mit addAround könnenWrapper angegeben werden, die vor und nach der HTTP Request-Bearbeitungaufgerufen werden. Für die Beispielanwendung wird mit DB.buildLoanWrapper eineinfaches Transaktionshandling installiert. Damit wird sichergestellt, dass während32 Tatsächlich lässt LiftRules eine sehr umfangreiche Konfiguration zu. Es können dort z.B. Filterketten in Form von partiellen Funktionen zur Transformation von HTTP Request abgelegt werden. (siehe http://scala-tools.org/mvnsites/liftweb-2.3/net/liftweb/http/LiftRules.html)33 Vgl. [Per11, S. 45]34 siehe http://scala-tools.org/mvnsites/liftweb-2.3/net/liftweb/http/S.html 79
  • 89. 4.3 Umsetzung mit Scala und Liftder Bearbeitung eines HTTP Requests eine Transaktion offen ist, die am Endeder Request-Bearbeitung wieder geschlossen. Darüber ist sichergestellt, dass jedeDatenbankoperation während der Bearbeitung einer Anfrage in einem Transakti-onskontext abläuft.Lift Mapper bietet mit dem Schemifier Objekt Funktionen für das automatischeErstellen der Datenbanktabellen, wie das folgende Listing zeigt. Dafür müssen alleMapper Klassen explizit angegeben werden. Fremdschlüssel werden durch Schemifiernicht automatisch generiert. Dies ist der Kompatibilität zu Datenbanksystem ge-schuldet, die keine Fremdschlüssel unterstützen. Daher musste createForeignKeys_?überschrieben werden. MapperRules konfiguriert Lift Mapper.§ Listing 4.54: Initialisieren der Datenbank ¤MapperRules . c r e a t e F o r e i g n K e y s _ ? = _ = true >S c h e m i f i e r . s c h e m i f y ( true , S c h e m i f i e r . i n f o F _, B u i l d i n g , Room , Equipment , A t t r i b u t e )¦ ¥Neben der Konfiguration der Persistenzschicht in Boot müssen dort weitere Einstel-lungen getroffen werden. So muss angegeben werden in welchen Paketen die durchLift zu verwendenden Klassen (wie z.B. Snippets und Mapper Klassen) liegen. Dafürgenügt es, bei addToPackages das oberste Paket anzugeben. Lift sucht automatischin den Unterpaketen. Das ist vergleichbar mit dem Deklarieren der Managed Beansin der Java EE Lösung.§ Listing 4.55: Snippet Konfiguration und Aufbau der SiteMap ¤L i f t R u l e s . addToPackages ( " de . htw . b e r l i n . jroomyweb " )L i f t R u l e s . setSiteMap ( SiteMap (Menu( S . ? ( " m a i n S i t e " ) ) / " i n d e x " , Menu( S . ? ( " roomSearch " ) ) / " s e a r c h " , Menu( S . ? ( " a t t r i b u t e s " ) ) / " a t t r i b u t e s " ) )¦ ¥Ebenfalls wird die Sitemap der Beispielanwendung angegeben. Das gesamte Me-nü einer Lift Anwendung kann über ein SiteMap Objekt in LiftRules definiert 80
  • 90. 4.3 Umsetzung mit Scala und Liftwerden. Dies bringt den Vorteil, dass darüber Sicherheitsfunktionen, wie z.B. einZugriffsschutz, an zentraler Stelle implementierbar sind.35Wie bereits erwähnt wurde, können mit S.? Zeichenketten aus Ressourcendateiengeladen werden. Die Bezeichnungen für die Menüeinträge werden ebenfalls durchdiese Funktion geladen. Bei einer Lift Anwendung werden die Ressourcendateienals normale HTML-Dateien angelegt, die ein XML-Dokument enthalten.§ Listing 4.56: Ressourceneinträge für die Sitemap ¤<r e s o u r c e s> <r e s name=" m a i n S i t e ">H a u p t s e i t e</ r e s> <r e s name=" roomSearch ">Raumsuche</ r e s> <r e s name=" a t t r i b u t e s ">Ausstattungsmerkmale</ r e s></ r e s o u r c e s>¦ ¥Der Wurzelknoten einer Ressourcendatei bei Lift ist resources und jeder Resour-ceneintrag ist als res-Kindelement anzulegen. Über das vergebene name-Attributwerden die Einträge beim Aufruf von S.? referenziert. Der Suffix des Dateinamengibt die Lokalisierung der Ressourcen an (hier: _resources_de.html, de für Deutsch-land). Damit ist gemeint, für welchen Sprach- und Kulturkreis die Bezeichnungenzu verwenden sind. Die Auswahl der richtigen Ressourcendatei für den jeweiligenNutzer setzt Lift eigenständig um. In der Beispielanwendung wurde lediglich eineglobale Ressourcendatei verwendet. Lift bietet jedoch die Möglichkeit, für jedeSeite der Anwendung separate Dateien zu nutzen. Die Auflösung und Zuordnungvon Ressourcen erfolgt dabei ebenfalls über den Dateinamen (für die Seite unter/foo/bar z.B. die Ressourcendatei /foo/_resources_bar.html).36Neben der Konfiguration der Lift Anwendung in Boot muss abschließend der LiftServlet Filter in der web.xml eingetragen werden. Schließlich setzt Lift auf der JavaEE Plattform auf. Darüber wird Lift in die Bearbeitung von Anfragen bei einemServlet Container eingebunden.35 Vgl. [Pol, Kapitel 3.2]36 Vgl. [Pol, Kapitel 8.1] 81
  • 91. 4.3 Umsetzung mit Scala und Lift§ Listing 4.57: Lift Servlet Filter Eintrag in web.xml ¤< f i l t e r> < f i l t e r −name> L i f t F i l t e r</ f i l t e r −name> < f i l t e r −c l a s s>n e t . l i f t w e b . h t t p . L i f t F i l t e r</ f i l t e r −c l a s s></ f i l t e r>< f i l t e r −mapping> < f i l t e r −name> L i f t F i l t e r</ f i l t e r −name> <u r l −p a t t e r n>/∗</ u r l −p a t t e r n></ f i l t e r −mapping>¦ ¥Damit ist die Scala/Lift Beispielanwendung konfiguriert für die Umsetzung derAnwendungsfälle.Gebäude verwaltenFür die Umsetzung der Gebäudeverwaltung des Lift Prototypen wird die Imple-mentierung der selben Funktion gezeigt wie bei der Java EE Beispielapplikation:das Speichern von veränderten Gebäudedaten, wobei zuvor alle Gebäude aus derDatenbank geladen und angezeigt werden und der Nutzer ein Gebäude auswählt.Bevor die Logik für das Speichern und andere Funktionen implementiert wird,erfolgt die Definition des allgemeinen Seitentemplates. Dieses Template wird fürdie Umsetzung aller Anwendungsfälle genutzt.In Lift werden alle Templates im WEB-INF/templates-hidden Verzeichnis gespei-chert. So auch das im folgenden Listing abgebildete Seitentemplate, welches indiesem Verzeichnis in der HTML-Datei default formuliert ist. Alle Dateien indiesem Verzeichnis sind ausschließlich für den Templatingmechanimus von Liftsichtbar. Vom Nutzer aus ist das Verzeichnis nicht aufrufbar. Generell gilt das füralle Verzeichnisse in WEB-INF, die den Namenssuffix „-hidden“ haben.3737 siehe [Per11, S. 26] 82
  • 92. 4.3 Umsetzung mit Scala und Lift§ Listing 4.58: Ausschnitt des allgemeinen Templates in default.html ¤<div id=" h e a d e r "> <div id=" n a v i "> <span c l a s s=" l i f t : Menu . b u i l d e r " /> </ div></ div><div id=" contentWrapper " > < l i f t : bind name=" c o n t e n t " /></ div>¦ ¥Das Template enthält Standard XHTML-Tags. Ein Ersetzungsmechnismus, wie erin Facelets durch ui:insert definiert wird, erfolgt in Lift äquivalent durch das Taglift:bind. Die im vorigen Abschnitt angelegten Menüpunkte der Sitemap werdenmit dem class Attribut lift:Menu.builder des span Tags eingebunden. Dadurchwird an dieser Stelle die builder Funktion des Menu Snippet aufgerufen. Dasbedeutet, die builder Funktion verändert die übergebene Sequenz von XML-Knoten,wobei eine ungeordnete Liste mit dem ul HTML-Tag eingefügt wird, die jedenMenüpunkt als Listeneintrag enthält. Alternativ kann ebenso ein Snippet Tagnamens lift:Menu.builder anstelle des span Tags eingefügt werden.Wichtig bei der Referenzierung von Snippets ist die Angabe des Lift-Namensraumund dass dem Funktionsaufruf der Klassenname vorangestellt wird, wie bei demAufruf einer statischen Funktion im Scala Programmcode. Dies kann im classAttribut eines Standard XHTML-Tags geschehen oder als separates Lift-spezifischesTag geschrieben werden. Die Variante mit XHTML-Tags zu arbeiten und Lift überdas class Attribut einzubinden, entspricht dem neueren und empfohlenen Weg, dadie Templates und Seitenbeschreibungen somit für Tools zur Webseitenerstellungbearbeitbar bleiben. In der Beispielanwendung wurde größtenteils die ältere Variantemit separaten Tags verwendet. Der Grund dafür ist, dass in [Per11], der zumEntwickeln genutzten Quelle, hauptsächlich damit gearbeitet wird.Die Anzeige von allen Gebäuden erfolgt wie in der Java EE Umsetzung in Form 83
  • 93. 4.3 Umsetzung mit Scala und Lifteiner Tabelle, die im folgenden Listing gezeigt wird.38§ Listing 4.59: Ausschnitt der Tabelle zum Anzeigen von Gebäuden ¤<div c l a s s=" l i f t : s u r r o u n d ? with=d e f a u l t ; a t=c o n t e n t "><tbody id=" b u i l d i n g s T a b l e B o d y ">< l i f t : B u i l d i n g s> <b u i l d i n g : row> <tr> <td><b u i l d i n g : id /></td> <td><b u i l d i n g : name /></td> <td><b u i l d i n g : numberOfRooms /></td> </ tr> </ b u i l d i n g : row></ l i f t : B u i l d i n g s></tbody></ div>¦ ¥Zu Beginn wird das soeben definierte Template durch lift:surround im class Attributgeladen. Mit dem with Parameter wird der Templatename angegeben, at legt dieStelle fest, an der der vom div Tag eingeschlossene Inhalt im Template eingefügtwird. Das ist vergleichbar mit der Nutzung der Facelets Tags ui:composition undui:define, nur dass Lift hierbei mit weniger Code auskommt.Von der eigentliche Tabelle zur Anzeige der Gebäude ist hier der Tabellenkörper(das tbody Tag) abgebildet. Im Tabellenkörper wird sogleich ein Snippet aufgerufen,die Funktion render der Klasse Buildings. Angegeben ist nur die Klasse. Wirdder Funktionsname bei einem Snippet Tag weggelassen, wird die render Funktionder Klasse aufgerufen. Das ist eine Konvention von Lift. Der Gedanke dahinterist, dass in den meisten Fällen eine Snippet Klasse für genau eine bestimmteAnzeigefunktion entwickelt wird. Daher sollte eine allgemeine render Funktion inder Klasse ausreichen.38 Die vollständige Seitenbeschreibung befindet sich in der index.html Datei. 84
  • 94. 4.3 Umsetzung mit Scala und LiftBei der Angabe des Snippet Tags fällt auf, dass dieses weitere Tags beinhaltet.Diese dienen als Vorlage für eine Tabellenzeile und werden in der render Funktiondurch die Gebäudedaten ersetzt. Die Implementierung der render Funktion ist imfolgenden Listing zu sehen.§ Listing 4.60: Aufbau einer Tabelle im BaseTableSnippet ¤t r a i t B a s e T a b l e S n i p p e t [ T ] extends S e l e c t a b l e T a b l e R o w s { def r e n d e r = a l l T a b l e E n t r i e s . flatMap ( e n t r y => a d d S el e c t a b l eT a b l e R ow ( e n t r y ) ( bindTableEntry ( e n t r y ) ) ) private def a d d S e le c t a b l eT a b l e R o w ( e n t r y : T) : NodeSeq = NodeSeq = { > ad d S el e c t a b l eT a b l e R o w ( ( ) = i s S e l e c t e d ( e n t r y ) , > () = { handleSelectionChange ( entry ) > onSelectionChange }) } protected def a l l T a b l e E n t r i e s : L i s t [ T ] protected def bindTableEntry ( e n t r y : T) : NodeSeq protected def i s S e l e c t e d ( e n t r y : T) : Boolean protected def o n S e l e c t i o n C h a n g e : JsCmd protected def h a n d l e S e l e c t i o n C h a n g e ( e n t r y : T)}¦ ¥Da es mehrere Tabellen mit der gleichen Funktionalität gibt, wird ein Basis-Traitfür ein Tabellen-Snippet mit BaseTableSnippet implementiert. Die Snippet KlasseBuildings erbt von BaseTableSnippet. Die render Funktion ist dabei für alle Tabellengleich. Mit allTableEntries können Subklassen des Traits die Tabelleneinträgeangeben, indem die Methode überschrieben wird. Bei Buildings gibt allTableEntriesdas Ergebnis des Aufrufs von findAll am Building Domänenobjekt zurück, also allein der Datenbank gespeicherten Gebäude. Auf die Liste allTableEntries wird dann 85
  • 95. 4.3 Umsetzung mit Scala und Liftdie Funktion flatMap angewendet, eine Funktion höherer Ordnung. Dadurch werdendie Tabelleneinträge vom generischen Typ T in eine XML-Knotensequenz vom TypNodeSeq umgewandelt, indem eine Funktion auf jedes Element von allTableEntriesangewendet wird. Mit anderen Worten, die Domänenobjekte werden in XHTMLabgebildet, wobei die Funktionen addSelectableTableRow und bindTableEntry aufdiese angewendet werden.Lift bietet keine fertige Tabellenkomponente wie JSF oder RichFaces. Daher musstedas Selektionshandling selbst implementiert werden, wie an den Funktionen add-SelectableTableRow, isSelected, handleSelectionChange und onSelectionChange zusehen ist. Bevor das Selektionshandling erläutert wird, soll das Generieren einerTabellenzeile gezeigt werden.In der Buildings Klasse ist bindTableEntry entsprechend überschrieben und so imple-mentiert, dass die im Listing 4.59 abgebildete Tabellenzeile mit den Gebäudedatenersetzt wird.§ Listing 4.61: Generierung einer Tabellenzeile zur Anzeige eines Gebäudes ¤override protected def bindTableEntry ( b u i l d i n g : B u i l d i n g ) = { bind ( " b u i l d i n g " , loadTemplate ( " i n d e x " , " b u i l d i n g " , " row " ) , " i d " −> b u i l d i n g . id , " name " −> b u i l d i n g . name , " numberOfRooms " −> b u i l d i n g . rooms . s i z e )}¦ ¥Um das übergebene Gebäude in XHTML abzubilden, wird die Hilfsfunktion bind desnet.liftweb.util.BindHelpers Trait verwendet, die die Ersetzung von XML-Elementensteuert.39 Diese Funktion erwartet als ersten Parameter den XML Namensraum, indem Tags ersetzt werden. Somit erklären sich auch die unbekannten XML Tags desbuilding Namensraum im Listing 4.59. Mit building werden die Tags gekennzeichnet,die durch bind ersetzt werden.Der zweite Parameter ist die XML-Knotensequenz, die durch bind verändert wird.Mit loadTemplate wird eine eigens implementierte Hilfsfunktion verwendet, die39 siehe http://scala-tools.org/mvnsites/liftweb-2.3/net/liftweb/util/BindHelpers.html 86
  • 96. 4.3 Umsetzung mit Scala und Liftaus einer Datei im WEB-INF/templates-hidden Verzeichnis eine XML-Sequenzlädt.40 In diesem Fall wird aus der Datei index.html die XML-Knotensequenz mitdem Wurzelknoten building:row geladen. Das ist ein Umweg, der für die spätereNutzung von Ajax implementiert wird. Normalerweise wird die zu verwendendeXML-Sequenz als Parameter den Snippet Funktionen direkt übergeben. So hättez.B. bereits die render Funktion einen Parameter xhtml: NodeSeq und würde xhtmlan die bindTableEntry Funktion zur Verarbeitung weiterreichen. Das ist in derBeispielanwendung nicht implementiert worden, da render und andere SnippetFunktionen an vielen Stellen im Programmcode aufgerufen werden. Die Lösung,dass sich die Snippet Funktionen die zu verwendenden XML-Knoten selbst laden,beugt möglicher Code-Duplizierung vor. Andernfalls müsste loadTemplate beijedem Aufruf von render ebenfalls aufgerufen werden und jedesmal mit den selbenParametern.Der interessanteste Parameter ist der letzte: eine Auflistung von BindParam Objek-ten. Der Trait BindParam beschreibt die Transformation einer XML-Knotensequenz.41In BindHelpers sind Implicit Conversions definiert, die dafür sorgen, dass der Ope-rator -> in ein passendes BindParam Objekt umgewandelt wird. Der Operator-> implementiert den eigentlichen Ersetzungsprozess. Auf der linken Seite wirddabei das zu ersetzende Tag angegeben, auf der rechten Operatorseite steht derneue Wert des Tags. Das XML Tag building:id wird also durch das id Attribut vonbuilding ersetzt. Gleiches gilt für building:name und building:numberOfRooms.Damit ist die Funktionsweise des bindTableEntry Aufrufs in der render Funktionvon BaseTableSnippet verständlich. Für jedes Domänenobjekt, in diesem Fall dieGebäude, wird aus dem Template index.html die XML-Sequenz für eine Tabel-lenzeile geladen und die einzelnen Tabellenzellen mit den Attributswerten desDomänenobjekts versehen.Das bereits angesprochene Selektionshandling von Tabellen wird ebenfalls im TraitBaseTableSnippet implementiert. Dazu wird in der render Funktion addSelectable-TableRow aufgerufen. Diese private Funktion überlädt die addSelectableTableRow40 Die Funktion ist im Objekt de.htw.berlin.jroomyweb.snippet.SnippetHelper implementiert.41 siehe http://scala-tools.org/mvnsites/liftweb-2.3/net/liftweb/util/BindHelpers$BindParam.html 87
  • 97. 4.3 Umsetzung mit Scala und LiftFunktion des SelectableTableRows Trait und stellt eine Hilfsfunktion dar. Damitsoll der Programmcode lesbarer gestaltet werden. Um diese private addSelecta-bleTableRow vom BaseTableSnippet zu verstehen, ist das Verständnis vom TraitSelectableTableRows nötig, der im folgenden Listing gezeigt wird.Listing§ 4.62: Erzeugung von selektierbaren Tabellenzeilen in SelectableTableRows ¤protected val c s s S e l e c t e d R o w C l a s s = " s e l e c t e d R o w "protected def a d d S e le c t a b l eT a b l e R o w ( i s S e l e c t e d F u n c t i o n : ( ) = Boolean , > a j a x F u n c t i o n : ( ) = JsCmd ) = { > " tr [ class ] " # ( if ( isSelectedFunction () ) > Some ( c s s S e l e c t e d R o w C l a s s ) e l s e None ) & " tr [ o n c l i c k ] " # ajaxInvoke ( ajaxFunction ) >}¦ ¥Die addSelectableTableRow Funktion nimmt zwei Funktionen in der Parameterlis-te entgegen. Mit isSelectedFunction wird eine Funktion übergeben, die darüberbestimmt, ob eine Tabellenzeile selektiert ist. Der zweite Parameter, ajaxFuncti-on, stellt die Funktion dar, die bei einem Klick auf eine Tabellenzeile über Ajaxausgeführt wird.In der Funktion addSelectableTableRow werden CSS Selektor Transformationeneingesetzt. Das ist eine Technik, die seit der Version 2.2 vom Lift Frameworkunterstützt wird und eine Alternative zu bind darstellt. Dabei wird mit dem exotischanmutenden #> Operator eine Umwandlung von XHTML-Tags formuliert, dieanhand eines CSS Selektors referenziert werden.42 CSS Selektoren sind Muster, umeinzelne Elemente im XHTML-Dokumentenbaum anzusprechen.43Mit tr [class] wird von allen tr Tags das class Attribut referenziert. Die angege-bene Transformation setzt das class Attribut auf den Wert, der auf der rechten42 siehe [Pol, Kapitel 7.10]43 siehe [Jen11] 88
  • 98. 4.3 Umsetzung mit Scala und LiftOperatorseite steht. Ist die Tabellenzeile selektiert, wird eine spezielle Style Klas-se (cssSelectedRowClass) gesetzt, um diese hervorzuheben, andernfalls wird dasAttribut durch die Angabe von None nicht geschrieben. Mehrere Selektor Trans-formationen werden mit dem &-Operator verkettet. Die zweite Transformationfügt allen Tabellenzeilen eine Ereignisbehandlung für einen Klick hinzu. Durchdiesen Ausdruck wird bei einem Klick auf eine Tabellenzeile ajaxFunction überAjax auf dem Server aufgerufen. Die ajaxInvoke Funktion ist eine Hilfsfunktion vonnet.liftweb.http.SHtml 44 und abstrahiert den benötigten JavaScript-Code für denAjax Aufruf.45Der Rückgabetyp der addSelectableTableRow Funktion ist NodeSeq => NodeSeq.Die Funktion gibt also eine andere Funktion zurück, die XHTML-Knotensequenzenmit Hilfe von CSS Selektoren transformiert. Dadurch wird eine Tabelle erzeugt,die klickbare Zeilen hat.46Damit wird die Funktionsweise der addSelectableTableRow Funktion im BaseTa-bleSnippet Trait deutlich.47 Mit handleSelectionChange können Untertypen vonBaseTableSnippet ihren internen Zustand (z.B. das ausgewählte Domänenobjekt)ändern, um auf den Tabellenklick zu reagieren. Die Funktion onSelectionChangesorgt für eine Änderung der Oberfläche, indem ein JsCmd Objekt zurückgegebenwird. Ein JsCmd Objekt kapselt in Lift JavaScript-Befehle.48 Die onSelectionChangeFunktion liefert dabei das Ergebnis des Ajax Aufrufs, der mit ajaxInvoke im Listing4.62 implementiert wurde und muss daher ein JsCmd Objekt zurückgeben. Die dar-in enthaltenen JavaScript-Befehle werden zur Ajax Engine des Browsers geschickt.Dort wird der JavaScript-Code ausgeführt, was letztendlich eine Aktualisierung44 SHtml ist ein Hilfsobjekt von Lift. Es implementiert eine Vielzahl an Funktionen, die zum Generieren von XHTML-Elementen dienen. (siehe http://scala-tools.org/mvnsites/liftweb- 2.3/net/liftweb/http/SHtml.html)45 Damit die Funktion ohne das Voranstellen des Objektnamens aufgerufen werden kann, wird die Import-Anweisung import net.liftweb.http.SHtml._ verwendet, die zur Übersicht hier nicht abgebildet ist.46 Es soll ergänzend darauf hingewiesen werden, dass im Listing 4.59 anstatt der spezialisierten building Tags die Verwendung normaler div und span Tags durch den Einsatz von CSS Selektor Transformationen möglich ist. Der Autor hat das Konzept der Selektor Transformationen während der Entwicklung des Prototypen zu spät verstanden.47 siehe Listing 4.6048 siehe [Per11, S. 68] 89
  • 99. 4.3 Umsetzung mit Scala und Liftder Oberfläche bewirkt. Für die Snippet Klasse Buildings wird onSelectionChangedaher wie folgt implementiert.Listing§ 4.63: Ereignisbehandlung in Buildings für einen Klick auf eine Tabellenzeile ¤override protected def o n S e l e c t i o n C h a n g e = { rerenderTableOfBuildings & new B u i l d i n g D e t a i l s ( ) . r e r e n d e r D e t a t i l e d F o r m}def r e r e n d e r T a b l e O f B u i l d i n g s = SetHtml ( " b u i l d i n g s T a b l e B o d y " , r e n d e r )¦ ¥In der onSelectionChange Funktion der Buildings Klasse wird das Neurendernder Gebäudetabelle und der Detailansicht des ausgewählten Gebäudes angestoßen.Beide Funktionen, rerenderTableOfBuilding und rerenderDetailedForm, liefernein SetHtml Objekt zurück. SetHtml ist ein JsCmd Untertyp und sorgt dafür,dass der Inhalt eines HTML Tags über JavaScript-Befehle ersetzt wird. Dazuwird im ersten Parameter des SetHtml Konstruktors die ID des Tags angegeben(buildingsTableBody in der rerenderTableOfBuildings Funktion) und im zweitender neue Inhalt (das Ergebnis der render Funktion von Buildings). Dadurch könnenselektiv einzelne Bereiche der Webseite durch die Verwendung von JavaScript undAjax neu gerendert werden.Bevor das Neurendern beginnt, wird handleSelectionChange ausgeführt, derenImplementierung in der Buildings Klasse im folgenden Listing gezeigt wird.§ Listing 4.64: Speichern des ausgewählten Gebäudes in der Session ¤object S e l e c t e d B u i l d i n g extends S e s s i o n V a r [ Box [ B u i l d i n g ] ]override protected def h a n d l e S e l e c t i o n C h a n g e ( b u i l d i n g : Building ) = { if (! isSelected ( building ) ) SelectedBuilding ( Full ( building ) )}¦ ¥ 90
  • 100. 4.3 Umsetzung mit Scala und LiftIn der Funktion handleSelectionChange wird das ausgewählte Gebäude der Session-Variable SelectedBuilding zugewiesen, sollte sich die Auswahl verändert haben. Mitdem Trait SessionVar können in Lift auf sehr einfache Art und Weise Werte fürdie Dauer einer HTTP Session gespeichert werden.49 SessionVar Objekte sindbevorzugt mit einem Box Typen zu typisieren, um den Fall abzudecken, dass derWert der Session-Variable leer sein kann, z.B. wenn kein Gebäude vorhanden istund somit auch keines ausgewählt ist. Da SelectedBuilding als Singleton Objektimplementiert und public ist, kann darüber von der gesamten Anwendung aus aufdas ausgewählte Gebäude zugegriffen werden.Wird ein Gebäude ausgewählt, erfolgt ebenso das Neurendern der Detailansichtfür ein Gebäude.50 Dabei wird die render Funktion der BuildingDetails Klasseausgeführt, die im folgenden Listing abgebildet ist.§ Listing 4.65: Anzeige der Gebäudesbezeichnung und des Speichern Buttons ¤def r e n d e r = { var name = S e l e c t e d B u i l d i n g . map(_. name . t o S t r i n g ) openOr " " ; bind ( " e n t r y " , loadTemplate ( " i n d e x " , " b u i l d i n g " , " d e t a i l s " ) , " name " −> ajaxText ( name , i n p u t = { > S e l e c t e d B u i l d i n g . map( b u i l d i n g = { > b u i l d i n g . name ( i n p u t . t r i m ) validateBuilding } ) openOr Noop } ) , " s a v e " −> ajaxSubmit ( S . ? ( " s a v e B u i l d i n g " ) , s a v e B u i l d i n g )}¦ ¥Am Anfang der Funktion wird die Bezeichnung des ausgewählten Gebäudes ausdem SelectedBuilding Objekt mit der map Funktion extrahiert. Die map Funktionwandelt in diesem Fall das Objekt vom Typ Building in ein Objekt vom Typ49 Für die Dauer einer Request-Bearbeitung kann alternativ RequestVar verwendet werden. (siehe [DCB11, Kapitel 3.11])50 siehe Listing 4.63, der Aufruf der rerenderDetailedForm Funktion der BuildingDetails Klasse 91
  • 101. 4.3 Umsetzung mit Scala und LiftString um, wobei das name Attribut des Building Objekts zurückgegeben wird.Da SelectedBuilding ein Box Typ ist, kann das bereits erwähnte Box-openOr-Muster angewendet werden, um auf den Fall zu reagieren, dass kein Gebäudeausgewählt ist. Dass Funktionen wie openOr und map, die eigentlich zum Box Typgehören und nicht zum SessionVar Trait, in der Form genutzt werden können,liegt an einer Implicit Conversion, die im Lift Kern implementiert ist. Im Objektnet.liftweb.http.AnyVar ist dafür die Funktion whatSessionVarIs[T](in: Session-Var[T]): T zuständig.51 Diese Funktion wandelt SessionVar Objekte automatisch inihren generischen Typen um, sollten Funktionen vom generischen Typen verwendetwerden.In dieser render Funktion wird erneut bind eingesetzt, um XHTML-Elemente zugenerieren und in die Seitenbeschreibung der index.html Datei einzufügen. Dabeiwird das entry:name Tag mit einem Textfeld ersetzt, das durch die ajaxText Funk-tion des SHtml Objekts erzeugt wird. Das erzeugte Textfeld bietet Unterstützungfür Ajax. Im zweiten Parameter der ajaxText Funktion wird dafür die Funktionangegeben, die bei Änderungen des Textfeldinhalts über Ajax ausgeführt wird.Der Funktion wird dabei über input der aktuelle Inhalt des Textfelds übergeben.Da diese anonyme Funktion über Ajax aufgerufen wird, muss ein JsCmd Objektzurückgegeben werden. Dem wird dadurch begegnet, dass wieder das Box-openOr-Muster angewendet wird. Zuerst wird dem building Objekt die aktuelle Bezeichnungzugewiesen, um diese anschließend mit den Validierungsfunktionen der BuildingKlasse zu prüfen. Das geschieht mit dem Aufruf der Hilfsfunktion validateBuilding,die in der render Funktion definiert ist.§ Listing 4.66: Validierung der Eingaben für ein Gebäude ¤def v a l i d a t e B u i l d i n g = { S e l e c t e d B u i l d i n g . f o r e a c h (_. v a l i d a t e match { case e r r o r s : L i s t [ F i e l d E r r o r ] => e r r o r s . f o r e a c h ( e = S . e r r o r ( e . msg ) ) > }) Noop }¦ ¥51 siehe http://scala-tools.org/mvnsites/liftweb-2.3/net/liftweb/http/AnyVar$.html 92
  • 102. 4.3 Umsetzung mit Scala und LiftIn der validateBuilding Funktion wird mittels foreach auf das selektierte Gebäudezugegriffen. Da keine Umwandlung in einen anderen Typen notwendig ist, musskeine map oder flatMap Funktion verwendet werden. Mit der validate Funktionwird die Validierung mit den in den Mapper Klassen hinterlegten Validierungsregelnausgeführt und das Ergebnis mit Pattern Matching auf Fehler geprüft. Sind Fehlervorhanden, werden diese über die error Funktion des S Objekts für Lift sichtbargemacht. Meldungen und Nachrichten, die über S.error 52 gemeldet werden, könnenmit dem lift:msg Tag zur Anzeige gebracht werden. Mit lift:error_class kann dieAnzeige von Fehlernachrichten im Aussehen verändert werden, indem eine CSSKlasse angegeben wird.§ Listing 4.67: Anzeige von Fehlern durch Lift ¤< l i f t : msgs showAll=" t r u e "> < l i f t : e r r o r _ c l a s s>e r r o r M e s s a g e</ l i f t : e r r o r _ c l a s s></ l i f t : msgs>¦ ¥Um auf die anonyme Funktion des Ajax-Textfelds53 zurückzukommen: In beidenFällen, wenn die Box SelectedBuilding ein Building Objekt enthält oder leer ist(siehe openOr), wird Noop zurückgegeben. Noop ist wie SetHtml ein Untertyp vonJsCmd und stellt einen Platzhalter für eine leere JavaScript-Funktion dar. DiesesObjekt muss immer dann verwendet werden, wenn der Ajax-Aufruf keine neuenDaten für den Client enthält bzw. keine Änderungen bei diesem bewirken soll.54Es lässt sich bereits als Zwischenfazit der Umsetzung festhalten, dass über diebisher gezeigten Mechanismen die Domänenobjekte zur Bearbeitung in die Darstel-lungsschicht der XHTML-Dokumente gebracht werden. Auf SelectedBuilding 55 wirdmittels Funktionen höherer Ordnung zugegriffen, um vom ausgewählten GebäudeAttribute zu lesen oder Attribute mit bestimmten Werten zu setzen. Durch die52 Für normale Nachrichten ist S.notice die Alternative. Warnungen können mit S.warning hinterlegt werden.53 siehe Listing 4.6554 Die Fehlermeldungen, die durch S.error angegeben wurden, werden dennoch übertragen, da Lift die Meldungen separat in die Antwort schreibt.55 Für den ausgewählten Raum, die ausgewählte Raumausstattung usw. existieren äquivalente Selectedxxx Singleton Objekte. 93
  • 103. 4.3 Umsetzung mit Scala und LiftHilfsfunktionen des SHtml Objekts können XHTML-Elemente und Ajax Aufrufeohne Probleme in Scala Code formuliert werden.Die Generierung des Buttons zum Speichern des bearbeiteten Gebäudes ist eben-falls im Listing 4.65 zu sehen. Mit ajaxSubmit wird ein Submit-Button generiert,der die eingegebenen Informationen mit einem Ajax Call und nicht per HTTPMethode überträgt. Das Speichern des ausgewählten Gebäudes implementiert dieHilfsfunktion saveBuilding, die ebenfalls direkt in render definiert ist.§ Listing 4.68: Speichern des ausgewählten Gebäudes ¤def s a v e B u i l d i n g ( ) : JsCmd = { S e l e c t e d B u i l d i n g . map( b u i l d i n g => b u i l d i n g . v a l i d a t e match { case N i l = { > b u i l d i n g . saveMe new B u i l d i n g s ( ) . r e r e n d e r T a b l e O f B u i l d i n g s } case e r r o r s : L i s t [ F i e l d E r r o r ] = { > e r r o r s . f o r e a c h ( e = S . e r r o r ( e . msg ) ) > Noop } } ) openOr Noop}¦ ¥Wie bereits bei der Nutzung von Ajax bekannt ist, muss saveBuilding ein JsCmdObjekt zurückgeben, das die Ajax-Antwort für den Client darstellt. Die einge-setzten Mechanismen sind ebenfalls bekannt. Mit der map Funktion wird auf dasBuilding Objekt zugegriffen. Bevor dieses mit saveMe gespeichert wird, werdendie Validierungen ausgeführt (building.validate). Über Patter Matching werdenmögliche Fehler abgefragt. Mit Nil wird das Ende einer Liste gekennzeichnet. WirdNil als einziges Element einer Liste angegeben, wie hier, wird damit eine leere Listedargestellt. Werden also keine Fehler gefunden, ist die Fehlerliste, die von validatezurückgegeben wird, leer und Nil passt. Dann wird die Logik zum Speichern ausge- 94
  • 104. 4.3 Umsetzung mit Scala und Liftführt und abschließend der JavaScript-Befehl zum Neurendern der Gebäudetabellezurückgegeben, um die Bezeichnung des ausgewählten Gebäudes ebenso in derGebäudetabelle zu aktualisieren.Somit kann ein Gebäude ausgewählt, dessen Daten bearbeitet und anschließendgespeichert werden. Anders als in der Java EE Lösung, wo Anwendungsdaten durchdieAttribute der Komponenten-Tags und Managed Beans in den View gelangten,wurde die Abbildung der Daten im Frontend mit Snippets und der bind Funktionoder CSS Selelektor Transformationen selbst implementiert. Das bot mehr Frei-heiten, die Anwendung zu strukturieren. Ebenso typisch für Lift Anwendungen istdas Definieren von Hilfsfunktionen direkt in den Snippet Funktionen (z.B. valida-teBuilding und saveBuilding). Mit den Funktionen des SHtml Objekts bietet Liftreichhaltige Möglichkeiten, die Anwendung mit Logik, die in Scala Programmcodeformuliert wird, auszustatten. Dabei abstrahiert Lift die Schnittstellen zwischenXHTML, Ajax, JavaScript und Scala.Räume suchenDie Raumsuche des Scala/Lift Prototypen verwendet die gleichen Konzepte undFunktionen, die bei der Umsetzung der Gebäudeverwaltung vorgestellt wurden.Im Search Snippet wird die Seite der Raumsuche gerendert.56§ Listing 4.69: Snippet-Funktionen für den Such-Button ¤def doSearch = ajaxButton ( S . ? ( " s e a r c h " ) , () = rerenderTableOfSearchResults ) >def r e r e n d e r T a b l e O f S e a r c h R e s u l t s : JsCmd = SetHtml ( " s e a r c h R e s u l t T a b l e B o d y " , r e s u l t s )¦ ¥Die Suchfunktion wird über einen Button mit Ajax-Funktionalität in die Seite einge-bunden. Die einzige Funktion des Such-Buttons ist das Neurendern der Tabelle mit56 Die Seitenbeschreibung zu dem Snippet ist in der Datei search.html. 95
  • 105. 4.3 Umsetzung mit Scala und Liftden Suchergebnissen, was durch den Aufruf der rerenderTableOfSearchResults Funk-tion geschieht. In der Rerender-Funktion wird mit SetHtml die Snippet Funktionresults aufgerufen, die nachfolgend abgebildet ist.§ Listing 4.70: Snippet-Funktion für die Ausgabe der Suchergebnisse ¤def r e s u l t s = { val foundRooms = S e l e c t e d S e a r c h O p t i o n . map( Equipment . f i n d A l l R o o m s B y A t t r i b u t e (_) ) openOr N i l foundRooms . flatMap ( room => bind ( " s e a r c h R e s u l t " , loadTemplate ( " s e a r c h " , " s e a r c h R e s u l t " , " row " ) , " i d " −> room . id , " name " −> room . name , " s i z e I n S q u a r e M e t e r s " −> room . s i z e I n S q u a r e M e t e r s , " numberOfWindows " −> room . numberOfWindows , " numberOfEquipment " −> room . equipment . s i z e ) )}¦ ¥Das vom Nutzer ausgewählte Ausstattungsmerkmal wird der findAllRoomsByAttri-bute Abfragefunktion übergeben. Jeder gefundene Raum wird anschließend überdie bind Funktion auf eine Tabellenzeile abgebildet. Dabei wird die Ergebnisslisteder Raumsuche mit flatMap in eine Knoten-Sequenz (NodeSeq) umgewandelt.Wie bei der Java EE Lösung wird die Suche über eine einfache Datenbankabfrageimplementiert. Anders als in der Java EE Lösung ist die Abfragefunktion dafürnicht in einem DAO implementiert, sondern im Domänenobjekt Equipment, wastypisch für die Nutzung von Lift Mapper ist, wie bereits bei der Umsetzung desDomänenmodells erläutert wurde.§ Listing 4.71: Finder-Funktion für die Suche nach Räumen ¤def f i n d A l l R o o m s B y A t t r i b u t e ( a t t r i b u t e : A t t r i b u t e ) = f i n d A l l F i e l d s ( Seq [ S e l e c t a b l e F i e l d ] ( Equipment . room ) , By( Equipment . a t t r i b u t e , a t t r i b u t e ) , PreCache ( Equipment . room ) ) . flatMap (_. room . o b j )¦ ¥ 96
  • 106. 4.3 Umsetzung mit Scala und LiftDie findAllFields Funktion der Lift Mapper Bibliothek ermöglicht das selektiveLaden der im ersten Parameter als Sequenz angegebenen Attribute bzw. Tabel-lenspalten. Da die findAllRoomsByAttribute Funktion die gefundenen Räume zu-rückgeben soll, wird auch lediglich das room Feld einer Raumausstattung geladen.Der PreCache Ausdruck sorgt für das Cachen eines Objekts, welches über einenFremdschlüssel in einer Tabelle gespeichert wird. Das bedeutet, das Room Objektder Raumausstattung wird mitgeladen und im obj Feld von room abgelegt. OhnePreCache würden die Room Objekte einzeln nachgeladen werden, was zu sehr vielenDatenbankabfragen führen würde. Mit PreCache werden die Room Objekte in einerAbfrage geladen.57 Um die geladenen Fremdschlüssel, die in Form von LongMap-pedMapper Objekte vorliegen, in eine Liste mit Room Objekte umzuwandeln, wirdabschließend flatMap angewendet.Damit ist die Raumsuche in der Scala/Lift Beispielapplikation umgesetzt.Ausstattungsmerkmale verwaltenFür den Vergleich mit dem Java EE Programmiermodell wird in diesem Abschnittebenfalls die Umsetzung der bedingten Löschbarkeit von Ausstattungsmerkmalengezeigt. Das nachfolgend abgebildete Listing zeigt die Tags für das Einfügen derButtons zum Hinzufügen und Löschen von Ausstattungsmerkmalen.§ Listing 4.72: Tags für die Buttons zum Verwalten von Ausstattungsmerkmalen ¤<div id=" b u t to n P an e l "> < l i f t : A t t r i b u t e s . b u t t o n s> <a t t r i b u t e s : b ut t o nP a n e l> <button : a d d A t t r i b u t e /> <button : d e l e t e A t t r i b u t e /> </ a t t r i b u t e s : b ut t o nP a n e l> </ l i f t : A t t r i b u t e s . b u t t o n s></ div>¦ ¥ 57 Vgl. [DCB11, S. 94] 97
  • 107. 4.3 Umsetzung mit Scala und LiftDer Button für das Neuanlegen eines Merkmals, button:addAttribute, wird standard-mäßig im Attributes Snippet gerendet. Beim Lösch-Button, button:deleteAttribute,wird das Rendern mit einer Bedingung versehen, wie im Listing der buttons Funktionzu sehen ist.§ Listing 4.73: Bedingtes Rendern des Lösch-Button ¤def b u t t o n s = { bind ( " button " , loadTemplate ( " a t t r i b u t e s " , " a t t r i b u t e s " , " b u tt o n P an e l " ) , " d e l e t e A t t r i b u t e " −> S e l e c t e d A t t r i b u t e . map( a t t r i b u t e = { > if ( attribute . isDeletable ) ajaxButton ( S . ? ( " d e l e t e A t t r i b u t e " ) , () = deleteAttribute ) > else NodeSeq . Empty }) )}¦ ¥Durch die map Funktion von SelectedAttribute wird auf das ausgewählte Ausstat-tungsmerkmal zugegriffen, um dann mit der Abfragemethode des Domänenobjektsattribute zu erfahren, ob dieses löschbar ist. Da die Datenbankabfrage dazu bereitsin der Domänenschicht formuliert wurde, ist die Implementierung im Snippet trivial.Wenn das ausgewählte Ausstattungsmerkmal nicht löschbar ist, wird lediglich eineleere XML-Knotensequenz zurückgegeben (NodeSeq.Empty).Somit wird der Button zum Löschen von Ausstattungsmerkmalen nur angezeigt,wenn das ausgewählte Merkmal löschbar ist. Dieses Verhalten ist identisch zu demdes Java EE Prototypen. 98
  • 108. 5 Durchführung der Evaluation5.1 AllgemeinesDokumentationUm die Sprache und Plattform Java hat sich in den letzten zehn Jahren einetabliertes Technologieökosystem gebildet. Neben der Java Standardbibliothekexistiert eine große Anzahl an Bibliotheken und Frameworks von Drittanbietern.Die Community rund um Java ist also entsprechend groß. Daher gibt es auchweitaus mehr Bücher, Vorträge und Dokumentationsmaterial für die Standards desJava EE Programmiermodell als für Scala und Lift.Für die Programmiersprache Scala existieren jedoch ausreichend Bücher ([Bra11],[DW09], [MO08]), um sich einarbeiten zu können. Außerdem finden sich die Sprach-spezifikation und viele Anleitung auf der Scala Hauptseite.1 Aufgrund der rasantenEntwicklung von Scala kommt man nicht umhin, zusätzlich dazu regelmäßig dieMailinglisten zu verfolgen.2Das Lift Framework existiert seit 2007.3 Daher gibt es momentan auch nur zweiveröffentlichte Bücher, wobei eines davon lediglich die veraltete Printversion von[DCB11] ist. Onlinebücher der Entwickler helfen beim Einstieg ([Pol], [DCB11])und [Per11] ist bereits online in einer Vorabversion veröffentlicht. Die Dokumenta-tionsquellen drohen jedoch aufgrund der regelmäßigen vierteljährlichen Releases 1 Dokumentationsseite von Scala: http://www.scala-lang.org/node/197 2 Übersicht der Mailinglisten: http://www.scala-lang.org/node/199 3 Vgl. [Per11, S. 5] 99
  • 109. 5.1 Allgemeinesschnell obsolet zu werden. Als Beispiel dafür ist [TF11] zu nennen, welches diesesJahr erschienen ist und die bereits veraltete Version 2.2 des Frameworks verwendet.Die technische Dokumentation von Lift ist in Form einer ScalaDoc gestaltet.4 DieQualität dieser Dokumentation ist gut. Die meisten Operationen sind selbstbezeich-nend und die wichtigsten Funktionen weisen eine detaillierte Erklärung auf. Solltedas einmal nicht ausreichen, so ist natürlich jederzeit der Blick in den Programm-code von Lift möglich. Während der Entwicklung des Scala/Lift Prototypen hatdies oftmals weitergeholfen.Auch bei Lift ist das Lesen der Mailingliste notwendig, um Verständnis- und Detail-fragen zu klären.5 Hierbei ist die gute Unterstützung durch die Lift Commmunity zunennen. Die Community rund um Lift ist sehr hilfsbereit und aktiv. Fragen werdenhäufig innerhalb weniger Stunden qualitativ hochwertig beantwortet. Dies liegtauch daran, dass die Committer und der Hauptentwickler, David Pollak, nahezutäglich Fragen in der Mailingliste beantworten.Als Fazit lässt sich festhalten, dass für die Sprache Scala ausreichend Dokumentati-onsmaterialien in guter Qualität zur Verfügung stehen. Beim Lift Framework musssich der Entwickler zur Zeit noch auf wenige Quellen verlassen und ist gezwungen,die Mailingliste zu durchsuchen. Jedoch lässt sich ein positiver Trend erkennen.Während der Entwicklung der Beispielanwendung konnte der Autor feststellen, dassdie Qualität und der Umfang von [Pol], welches vom Hauptentwickler geschriebenwird, wesentlich zugenommen haben.LernkurveAus Sicht eines Softwareentwicklers, der Java seit Jahren anwendet, ist das Erlernender Grundkonzepte des Java EE Programmiermodells nicht sonderlich schwer,um die erste Anwendungen umzusetzen. Die Sprachmittel sind die gleichen. DieGrundlagen der Spezifikationen von Java EE (z.B. JPA und JSF) müssen verstanden 4 ScalaDoc ist ein Dokumentationswerkzeug ähnlich der JavaDoc. Die Syntax der Dokumentations- kommentare ist gleich denen bei einer JavaDoc. ScalaDoc bringt jedoch einige Erweiterungen mit sich wie z.B. eine integrierte Suchfunktion in der generierten Ausgabe. 5 Lift Mailingliste: https://groups.google.com/group/liftweb 100
  • 110. 5.1 Allgemeineswerden. Außerdem ist das Wissen über die genaue Konfiguration der Anwendungund die richtigen Annotationen für das gewollte Verhalten sehr wichtig bei derNutzung des Java EE Programmiermodells.Da die Programmiersprache Scala objektfunktional ist, muss der Java Entwicklerneue Konzepte erlernen. Funktionen sind Werte und können als solche ganz normalals Parameter anderen Methoden und Funktionen übergeben werden. Es gibteine strikte Trennung von statischen und nicht-statischen Programmcode. MitPattern Matching, Implicit Conversions und Self-Type Annotationen gibt es neueSprachmittel. Die Syntax ist ähnlich der von Java. Der Entwickler hat allerdingsmehr Freiheiten (z.B. die Semikoloninferenz).Die Sprachmittel von Scala sind insgesamt reichhaltiger an Funktionalität. Um alleneuen Sprachmittel effektiv einsetzen zu können, ist eine lange Lernzeit notwendig.Jedoch erlaubt Scala diesen Sprung schrittweise. Als Java Entwickler kann manauch imparativ oder rein objektorientiert in Scala entwickelt. Die Sprache skalierttatsächlich.6Lift setzt konsequent Scala Sprachfeatures ein, wie z.B. Funktionen höherer Ordnung.Dies macht es für einen Anfänger, der parallel dazu die Sprache Scala lernt, ungemeinschwer mit dem Framework einen minimalen Grad an Produktivität zu erreichen.Der Lernaufwand ist für einen Scala Neuling sehr groß. Darauf wies auch HeikoSeeberger, ein Scala Evangelist, bei seinem Vortrag zu Lift auf der Bed-Con hin.7Dort empfiehlte er, Scala und Lift nicht zeitgleich zu lernen, sondern zuerst Scalaund anschließend mit genügend Kenntnissen der Sprache das Framework Lift zunutzen. Es hat hingegen auch einen Vorteil, wenn man Scala und Lift parallel lernt.Dabei wird der Einsatzkontext für das jeweilige Scala Sprachfeature sehr schnelldeutlich, wie z.B. Implicit Conversions, die im Lift Kern häufig vorkommen. 6 Dies hat auch das Team von Guardian.co.uk bemerkt. Dieses hat vor kurzem die Content API des Guardian von Java nach Scala portiert. Die Portierung konnte schrittweise durchgeführt werden. (siehe http://www.infoq.com/articles/guardian_scala) 7 Die Berlin Expert Days (Bed-Con) waren eine Konferenz, die am 7. und 8. April in Berlin stattfand. Themen waren agile Entwicklung, Cloud Computing, Java EE, NoSQL und Spring. Der besagte Vortrag hatte den Titel „Twitter nachgebaut mit Lift in 60 Minuten“. (siehe https://bed-con.org/index.php/de/vortraege#vortrag9) 101
  • 111. 5.1 AllgemeinesToolchainFür die Java Plattform existiert eine sehr gute Unterstützung durch Entwicklungs-werkzeuge. Es gibt drei große IDEs (Eclipse, NetBeans und IntelliJ IDEA), die alledurch eine große Anzahl an Plugins erweiterbar sind. Es ist bekannt, dass die JavaPlattform eine der besten Tool-Unterstützungen als Entwicklungsplattform hat.Bei der Entwicklung der Beispielanwendung für Java EE wurde Eclipse mit denJBoss Tools8 eingesetzt. Durch die JBoss Tools wurde Code Completion undRefactoring in den XHTML-Seiten ermöglicht. Fehler durch falsche Benennungenin Java-Klassen und XHTML-Seiten wurden schnell angezeigt. Als Build- undDeploymentwerkzeug kam Maven zum Einsatz, welches die Abhängigkeiten desProjekts aufgelöst und automatisch alle benötigten Bibliotheken aus dem Internetgeladen hat. Das Deployen der Anwendung in einen Web-Container war durch dieTomcat Integration in Eclipse ohne weiteres möglich. Parallel dazu ermöglichte dieVerwendung von Maven das Hochfahren eines Web-Containers wie z.B. ApacheJetty und Deployen der Anwendung von der Kommandozeile aus.9Für die Entwicklung mit Scala und Lift wurde die NetBeans IDE verwendet. Zwarverfügen alle oben genannten IDEs über Scala Plugins. Zur Zeit der Entwicklungdes Prototypen wirkte jedoch die Scala Integration von NetBeans am Stabilsten.Die von IntelliJ IDEA ist ebenso gut gewesen. Dem Autor erschien allerdings dieBedienung von NetBeans einfacher.Das Scala Plugin für Netbeans bringt eine rudimentäre Code Completion mitund die frühzeitige Anzeige von möglichen Fehler, wobei teilweise auch korrekteAusdrücke als Fehler angezeigt werden. Lift Templates werden als normale HTML-Dateien behandelt. Code Completion und Refactoring beschränken sich dabei aufHTML-Spezifika. So werden z.B. die Namen von Snippets in den Lift-Templatesnicht automatisch aufgelöst, wenn diese eingebunden werden. 8 Die JBoss Tools sind eine Plugin Sammlung, die Eclipse um Funktionen für den Umgang mit JBoss Frameworks wie z.B. Hibernate oder RichFaces erweitern. (siehe http://www.jboss.org/tools) 9 Das Befehl dazu lautet „mvn clean jetty:run“ und kann auch beim Scala/Lift Prototypen verwendet werden. 102
  • 112. 5.1 AllgemeinesDa es ein Scala Plugin für Maven10 gibt, konnte auch beim Scala/Lift PrototypenMaven eingesetzt werden. Zusätzlich existieren vorgefertigte Maven Archetypes fürLift. Dies sind vordefinierte Projekvorlagen. Dadurch steht schnell eine fertige LiftProjektstruktur mit Konfiguration zur Verfügung.11Zum Zeitpunkt der Entwicklung unterstützte das Scala Maven Plugin keine inkre-mentelle Kompilierung, wodurch jedes Mal das gesamte Projekt kompiliert wurde.Das sorgte für lange Wartezeiten. Durch den Einsatz des Werkzeugs Simple BuildTool (SBT) konnten diese Wartezeiten minimiert werden. SBT ist ein Build- undDeploymenttool wie Maven. Anders als Maven ist es speziell für Scala entwickeltworden und unterstützt das inkrementelle Kompilieren.12 Die Konfiguration vonSBT erfolgt über Scala Code und ist daher weitaus weniger aufgebläht als einefunktionsgleiche Mavenkonfiguration, die in XML fomuliert wird. Gleichtzeitig lässtsich SBT besser anpassen, da der volle Sprachumfang von Scala zur Verfügung steht.Maven hat einige Kritiker, die die aufgeblähten und dadurch schlecht wartbarenKonfigurationsdateien und die Schwierigkeiten in der Anpassung eines Buildprozes-ses an konkrete Eigenheiten der Entwicklungsumgebung bemängeln.13 Mit SBTkönnen die Nachteile von Maven ausgeglichen werden.Zusammengefasst ist die Tool-Unterstützung für Scala und Lift ausreichend. Jedochkann sie im Vergleich zu der von Java und dem Java EE Programmiermodell nichtbestehen. Zuviele Features existieren nicht oder sind unvollständig implementiert.Allerdings lässt sich ein positiver Trend erkennen. So ist seit kurzem der Scala-Erfinder, Martin Odersky, mit seinem Team an der Entwicklung des Eclipse ScalaPlugins beteiligt. Seitdem hat die Stabilität und Funktionalität des Plugins stetigzugenommen.14 Weiterhin bietet sich neben Maven besonders SBT als Build- undDeploymentwerkzeug für Scala und Lift an. Sogar für die Nutzung in Java Projektenstellt SBT eine nutzbare Alternative dar.10 siehe http://scala-tools.org/mvnsites/maven-scala-plugin11 siehe http://www.assembla.com/wiki/show/liftweb/Using_Maven12 siehe http://code.google.com/p/simple-build-tool13 siehe http://it-republik.de/jaxenter/news/Maven—eine-Ausgeburt-der-Hoelle-053182.html14 siehe http://www.scala-ide.org/2011/03/the-next-development-phase-for-the-scala-ide-for- eclipse 103
  • 113. 5.2 Funktionalität5.2 FunktionalitätPersistenzmechanismenMit JPA verwendet die Java EE Lösung einen durch das JCP spezifizierten Standardfür die Persistierung. Dabei wird ein objektrelationaler Mapper mit Annotationenangewiesen, die Domänenklassen auf eine relationale Datenbank abzubilden. Da-durch soll der objektrelationale Mismatch gelöst werden. Dies entspricht dem DataMapper Pattern, mit dem unteranderem die Entkopplung von Domänenmodell undDatenbankmodell ermöglicht wird.15Allerdings gelangen durch JPA Abhängigkeiten zu den verwendeten Annotationenin die Domänenschicht. Jedoch sind diese Abhängigkeiten lediglich deklarativerNatur. Die Vererbungshierarchie wird nicht beeinflusst. Für andere Schichten derAnwendung ist somit die Kopplung zu JPA transparent.Der Datenbankzugriff erfolgt in der Java EE Lösung mit Hilfe einer Persistenzschicht.In dieser sind für jede Domänenklasse so genannte DAO-Klassen implementiert.Der Zugriff auf die Datenbank wird in den DAO-Klassen gekapselt. Ein möglicherAustausch der Persistenzschicht wird erleichtert und die Testbarkeit der Anwendungerhöht.16Die Implementierung der Scala/Lift Beispielanwendung verwendet die Lift MapperBibliothek. Dadurch wird das Active Record Pattern in den Domänenklasseneingeführt, wodurch sich die Logik für den Datenbankzugriff in der Domänenschichtbefindet. Anders als in der Java EE Lösung sind keine Annotationen notwendig,um die Abbildung auf Datenbankfelder zu spezifizieren. Jedes Attribut, das einSingleton Objekt ist und von einem Untertypen des MappedField Traits erbt, wirdals Feld einer Spalte persistiert. Vorausgesetzt die umgebende Klasse erbt voneinem Untertypen des BaseMapper Traits. Der objektrelationale Mismatch wird alsodadurch gelöst, dass die Objekte Eins-zu-Eins auf relationale Tabellen abgebildetwerden. Intern verwendet Mapper die JDBC Schnittstelle der Java EE Plattform15 Vgl. [Fow03, S. 165 ff.]16 Vgl. [Wol10, S. 166] 104
  • 114. 5.2 Funktionalitätund setzt damit auf eine bewährte Technologie auf.Die Verwendung von Mapper bringt eine weitere vorgefertigte Funktionalität mitsich, die in der Beispielanwendung nicht gezeigt werden konnte. Durch die beidenTraits MegaProtoUser und MetaMegaProtoUser existiert eine komplette Benutzer-verwaltung mit sicherer Passwortspeicherung, Email-Integration und Formularenzur Eingabe der Nutzerdaten.17 Um solche Scaffolding18 Funktionen zu nutzen,trifft die Mapper Bibiliothek die Annahme, dass man eine funktionsreiche Domä-nenschicht implementieren möchte. Denn jede Mapper Klasse kann sich selbst inXHTML-Code ausgeben. Dafür sind Methoden wie asHtml oder toForm zuständig.Dadurch können Mapper Klassen sehr mächtig werden. Sie sind zuständig für dieAbbildung des Domänenmodells, die Realisierung der Persistenzschicht und füreinen Teil der Darstellung. Dadurch werden Schichten vermischt, was gegen denGrundsatz der Separierung der Belange (engl. Separation of Concerns) verstößt.Daher riet Heiko Seeberger in seinem bereits oben erwähnten Vortrag auch dazu,für Lift Projekte JPA zu nutzen. Dem ist zu zustimmen, da vor allem in Software-projekten im Web-Umfeld, die Domänenschicht für mehrere Anwendungen genutztwird. Neben der eigentlichen Web-Anwendung wird oftmals eine Schnittstelle fürandere Programme angeboten, z.B. ein Web-Service. Dafür ist die Separierungunterschiedlicher Belange und die lose Kopplung des Systems von großer Bedeutung,damit die Domänenschicht möglichst unabhängig von den Anwendungen ist, die sienutzen und eine Änderung in der Domänenschicht nicht zwangsläufig die Anpassungaller abhängigen Projekte bedeutet.Da keine Trennung von Domänen- und Persistenzschicht in Lift Mapper Implemen-tierungen vorhanden ist, gestaltet sich das Testen durch Mocks natürlich ebenfallsschwieriger. Außerdem weist eine Domänenschicht durch die Nutzung von Mapperbereits Abhängigkeiten in der Vererbungsstruktur auf und ist eng an die anderenLift Frameworkbereiche gekoppelt. Hinzu kommt, dass in großen Anwendungenmit einem komplexen Domänenmodell nicht auf Basis von Datenbankbeziehun-17 Ein gutes Beispiel für den Einsatz der Lift Benutzerverwaltung ist dieses Demoprojekt von Heiko Seeberger: https://github.com/weiglewilczek/chatter18 Scaffolding bezeichnet in der Softwareentwicklung das Bereitstellen eines Grundgerüsts. Das können wie hier fertige Funktionalitäten sein. 105
  • 115. 5.2 Funktionalitätgen operiert wird, wie es das Active Record Pattern vorgibt, sondern auf Basisder Objektbeziehungen.19 Einen Second Level Cache, um Datenbankbfragen zubeschleunigen, hat Lift Mapper anders als der verwendete JPA Provider Hiberna-te ebenso nicht. Dies ist aus Gründen der Performance als kritisch zu bewerten.Aufgrund dieser Überlegungen ist eine JPA Lösung für Lift Projekte vorzuziehen.Bei der Verwendung von JPA in Scala und somit Lift ist zu beachten, dass JPA nichtauf Scala Collections operieren kann. Natürlich kann durch die Interoperabilitätmit Java die Java Collection API auch in Scala genutzt werden. Allerdings istdiese bei weitem nicht so funktionsreich wie Scala’s. Eine Lösung bietet das ObjektJavaConversions der Scala Standardbibliothek an. In diesem Objekt sind ImplicitConversions definiert, die von Scala in Java Collections umwandeln und umgekehrt.Des Weiteren gibt es mit dem Projekt JPA for Scala eine Bibliothek, die denUmgang mit JPA in Scala weiter vereinfacht.20 Weiterhin stellt das Lift Frameworkmit dem Trait RequestVarEM eine einfache Möglichkeit zur Verfügung, um fürdie Dauer einer Anfragebearbeitung die selbe Instanz eines Entity Managers inmehreren Snippets zu nutzen.Der Nachteil bei einer Lift/JPA Lösung ist, dass andere Frameworkteile von Liftauf die Verwendung von Mapper Klassen abgestimmt sind. Für die Nutzungvon LiftScreen und Wizard, zwei mächtige Scaffolding Komponenten, müsste z.B.der SettableField Trait, den BaseMapper in der Lift Mapper Bibiltiohek einge-mixt hat, für jede Domänenklasse implementiert werden, damit Funktionen wieasHtml und toForm zur Verfügung stehen. Dies ist durch das Einmixen des Set-tableField Traits in die Domänenklassen und die Nutzung von BaseField, einerStandard-Implementierung, schnell umgesetzt. Jedoch wird dadurch die Domänen-schicht erneut eng an Teile des Lift Frameworks gekoppelt. Eine mögliche Lösungfür dieses Problem der engen Kopplung an Lift ist die Implementierung einerSchnittstellenschicht, welche die jeweils erwarteten Lift Traits entsprechend für dieDomänenklassen umsetzt.19 Vgl. [Fow03, S. 162]20 So werden z.B. Funktionen bereitgestellt, um Codeblöcke in Transaktionen auszuführen oder das Verwalten eines EntityManagers wird mit spezialisierten Traits vereinfacht. (siehe https://github.com/FaKod/JPA-for-Scala) 106
  • 116. 5.2 FunktionalitätEine weitere Lift Persistenzlösung ist die Lift Record Bibliothek, welche parallel zurMapper Bibliothek entwickelt wird. Lift Record soll die neue Standardlösung zumAnschluss verschiedenster Speicherlösungen in Lift Anwendungen werden. ÜberRecord können zur Zeit die NoSQL-Datenbanken CouchDB und MongoDB in Liftverwendet werden.21 Die Nutzung ähnelt der von Lift Mapper. Mit dem Einmixenvon Traits werden Domänenklassen unter Verwendung des Active Record Patternspersistierbar gemacht, wie das folgende Listing exemplarisch zeigt.§ Listing 5.1: Exemplarische Gebäude Fachklasse mit CouchDB Anbindung ¤c l a s s B u i l d i n g extends CouchRecord [ B u i l d i n g ] { def meta = B u i l d i n g object name extends S t r i n g F i e l d ( this , 2 0 0 )}object B u i l d i n g extends B u i l d i n g with CouchMetaRecord [ B u i l d i n g ] { def c r e a t e R e c o r d = new B u i l d i n g}¦ ¥Durch das Einmixen der beiden Traits CouchRecord und CouchMetaRecord wird diebeispielhafte Building Implementierung für CouchDB persistierbar. Da CouchDBkeine relationale Datenbank ist sondern dokumentorientiert, entfällt die Angabeeines Tabellennamen. Es muss jedoch mit createRecord angegeben werden, wieeine neue Instanz von der Klasse Building erzeugt wird. Das Implementieren derFunktion meta ist äquivalent zur Implementierung von getSingleton bei MapperKlassen. Ähnlich der Mapper Klassen sind zu persistierende Attribute in Form vonObjekten (name) anzugeben, die in diesem Fall von einer Klasse (StringField) erben.21 NoSQL steht für Not only SQL und bezeichnet nicht-relationale Datenbanken. Dabei wird unteranderem versucht, eine bessere Skalierbarkeit zu erreichen, als das bisher mit relationalen Datenbanken möglich war. (Vgl. [Edl10, S. 2 ff.]) 107
  • 117. 5.2 FunktionalitätEbenso wie Mapper geht die Record Bibliothek von einer mit vielen Funktionalitätenangereicherten Dömänenschicht aus. Dadurch lassen sich Record Klassen natürlichgut in andere Lift Bibliotheken integrieren. Andererseits gelten daher für Recorddie selben Kritikpunkte wie für Mapper.Die Integration und Nutzung von NoSQL-Datenbanken ist bisher in keinem JavaEE Standard vorgesehen. Natürlich gibt es für die Java Plattform Bibliotheken,die Anbindungen für die einzelnen NoSQL-Datenbanken bereitstellen.22 Weiterhinexistiert mit Spring Data auch ein Projekt, welches unteranderem NoSQL-Datenbanken für das Java EE Ökosystem verfügbar macht.23 Allerdings integrierensich solche Java Lösungen nicht unbedingt ohne weiteres in Lift, wie bereits zu JPAangeführt wurde.Sowohl das Java EE Programmiermodell als auch Lift bieten ausreichend Möglich-keiten, um ein gegebenes Domänenmodell zu persistieren. Allerdings unterscheidensich die angebotenen Lösungen bei der Einbindung in die Anwendung und somitin der Qualität. Wohingegen die Lift Persistenzbibliotheken, Mapper und Record,eine enge Kopplung der Persistenzschicht an die Fachklassen propagieren und inLift die Domänenschicht generell weitgehend mit anderen Schichten vermischt wird,setzt JPA auf lose Kopplung und stellt damit einen besseren Ansatz dar. Außerdemist es möglich, JPA in Lift zu nutzen. Jedoch muss bei einer solchen Lösung, die In-tegration der Domänenschicht in den restlichen Teil der Lift Anwendung gesondertbetrachtet werden.DarstellungskonzeptDer Java EE Prototyp verwendet für die Darstellung von Anwendungsdaten undInteraktionselementen JSF Komponenten. Durch die Nutzung von Facelets alsView-Handler kann die Seitenbeschreibung in Form von XHTML-Dokumentenerfolgen, wobei jede JSF Komponente durch ein eigenes XHTML Tag repräsentiertwird. Die Daten und Logik der Anwendung werden über Managed Beans in die22 Dafür seien beispielhaft jcouchdb (siehe http://code.google.com/p/jcouchdb) und JRedis (siehe http://code.google.com/p/jredis) genannt.23 siehe http://www.springsource.org/spring-data 108
  • 118. 5.2 FunktionalitätDarstellungsschicht eingebunden. Dabei muss die Implementierung der ManagedBeans die jeweiligen Eigenheiten der eingesetzten JSF Komponenten beachten. JSFsetzt zudem auf eine MVC-Architektur.Im Gegensatz dazu verwendet Lift ein gänzlich anderes Konzept für den Aufbau unddie Integration der Darstellung in die Anwendungskomponenten. Durch den vonLift vorgegebenen View-First-Ansatz werden zuerst die XHTML-Dokumente fürdie Darstellung entworfen. Mit den seit Lift 2.2 existierenden CSS Selektor Trans-formationen propagiert Lift zudem die ausschließliche Verwendung von StandardXHTML Tags, anders als in JSF und RichFaces, wo jede Komponente durch einspezielles Tag repräsentiert wird. Ein Element für die Darstellung wird dabei überdas class oder id Attribut des XHTML Tags eingefügt. Das hat den Vorteil, dassdie Seitenbeschreibungen in dieser Form mit üblichen Tools zur Webseitenerstellungbearbeitbar sind, was bei der Nutzung von JSF nicht der Fall ist. Dadurch könnenDesigner unabhängig vom Softwareentwickler das Design einer Seite entwickeln.Die CSS Selektor Transformationen setzen sich in Lift durch. Die Nutzung vonbind, wie im Prototypen, wird seitens der Lift Committer nicht mehr empfohlen.Abhängig vom entworfenen XHTML-Dokument werden die Snippets implementiert.Das heißt, die Darstellung legt zu einem großen Teil das Design der Snippetsfest. Über die Snippets wird die Domänenschicht und Geschäftslogik an die Dar-stellung gebunden, indem Elemente des XHTML-Dokuments überschrieben undausgetauscht werden. Die Snippets sind dabei eine Art einfacher XML Prozessoren,die den Dokumentenbaum der Seitenbeschreibung direkt verändern. Dadurch sindSnippets eng an die Lift Templates gebunden. Eine Änderung im Template ziehtmit großer Wahrscheinlichkeit eine Änderung im Snippet nach sich.JSF und Lift unterscheiden sich wesentlich in der Art der Abstraktion der Dar-stellung. JSF abstrahiert mit der Umsetzung des MVC-Musters mehr als Lift.Durch das Model in Form der Managed Beans ist in JSF vorgegeben, wie dieAnbindung der Darstellungsschicht an den Rest der Anwendung zu erfolgen hat.Lift hat an dieser Stelle eine schwächere Art der Abstraktion und reicht die Sei-tenbeschreibung direkt in die Snippets rein. Dadurch ist es in Lift hingegen sehreinfach möglich, spezialisiertes Verhalten für Elemente einer Webseite umzusetzen. 109
  • 119. 5.2 FunktionalitätEin Vorteil, den wiederum JSF bietet, ist das Vorhandensein einer großen Anzahlvorgefertigter Komponenten. Zwar existieren für Lift so genannte Widgets, dieErweiterungen für oft benötigte Funktionen bieten, wie z.B. eine automatischeVervollständigung von Texteingaben.24 Jedoch sind die vorhandenen Lift Widgetsnicht in Anzahl und Qualität mit JSF Komponentenbibliotheken wie RichFacesvergleichbar. Aufgrund des hohen Abstraktionsgrades von JSF wird die Entwicklungsolcher Komponentenbibliotheken begünstigt.Ajax-UnterstützungIn den entwickelten Prototypen wird Ajax für das Nachladen von Anwendungsdatenverwendet und zur Übertragung von Ereignissen, wie z.B. das Selektieren einerTabellenzeile. Die Integration von Ajax in den unterschiedlichen Technologienunterscheidet sich dabei wesentlich voneinander.Beim Java EE Prototypen wird die Ajax Funktionalität in Form von RichFacesKomponenten hinzugefügt, wie z.B. das Aktualisieren der selektierten Tabellen-zeile mit a4j:support. In der verwendeten Version 1.2 unterstützt JSF ohne einesolche Zusatzbibliothek kein Ajax. Erst in JSF 2.0 wurde Ajax in Form der f:ajaxKomponente dem Java EE Programmiermodell hinzugefügt.Lift bietet eine Abstraktionsschicht für die Nutzung von JavaScript und Ajax. Sokann in den Snippets mit reinem Scala Code ein Ajax Aufruf definiert werden.Ebenfalls sind bereits Hilfsfunktionen in Lift implementiert, die das Hinzufügenvon Ajax Funktionalitäten erleichtern, wie z.B. die ajaxText Funktion des SHtmlObjekts für das Generieren eines Textfeldes mit Ajax Support.Eine weitere Funktionalität von Lift, die im Prototypen nicht zum Einsatz kommt,ist das Wiring von Komponenten.25 Mit dem Objekt net.liftweb.http.WiringUIkönnen einzelne Komponenten in der Art miteinander verbunden werden, dassÄnderungen einer Komponente das Aktualisieren und Neurendern der verbun-denen Komponenten zur Folge hat. Die gesamte Logik, die dafür in Form von24 Vgl. [Per11, S. 127 ff.]25 Vgl. [Pol, Kapitel 6] 110
  • 120. 5.2 FunktionalitätJavaScript-Routinen nötig ist und die Ajax Aufrufe werden von Lift abstrahiert.Das Anwendungsgebiet für diese Funktionalität sind vor allem Eingabeformularemit abhängigen Feldern und Feldern, deren Inhalt das Ergebnis einer Berechnungist. Generell können darüber vernetzte Seitenelemente simpel implementiert werden.Zusätzlich bietet Lift Unterstützung für die Nutzung von Comet. Damit werdenlanglebige HTTP Verbindungen bezeichnet, bei denen der Server ohne Anfrage vomClient Daten sendet. Dabei wird auch von einem Push Mechanismus oder ReverseAjax gesprochen. Dieses Konzept hat durch die Entwicklung von interaktivenWeb-Anwendungen, die immer häufiger Drittsysteme wie z.B. einen Mail- oderGameserver einbinden, an Bedeutung gewonnen. Um beim Beispiel des Mailserverszu bleiben: Ein Mail-Client in Form einer Web-Anwendung wird durch den CometEinsatz vom Mailserver automatisch über neu eingegangene Mails informiert. OhneComet müsste der Client den Mailserver in regelmäßigen Abständen nach neuenNachrichten abfragen.26 Im aktuellen HTML Standard der Version 5 hat Comet inForm von WebSockets Einzug gehalten. Es ist also eine Technologie mit Zukunft.In Lift ist Comet durch die Nutzung von Aktoren implementiert. Aktoren sind einModel, das die Entwicklung von nebenläufigen Anwendungen durch den Austauschvon Nachrichten abstrahiert.Zusammenfassend ist die Ajax-Unterstützung in Lift funktionsreicher und flexiblerin der Anwendung als die Ajax-Integration in JSF. Mit Comet unterstützt Liftdes Weiteren eine neue Web-Technologie. Insgesamt sind die Ajax-Integration undComet die größten Vorteile von Lift gegenüber dem Java EE Programmiermodell.Denn Comet wird von JSF überhaupt nicht unterstützt. Hierbei zeigt sich eben-falls, dass Lift ein Framework ist, welches zügig weiterentwickelt wird. InnovativeFunktionalitäten wie Wiring werden ohne Umwege implementiert.27 Anders alsJSF, das durch einen langwierigen Standardisierungsprozess entwickelt wird.26 Das wird auch Polling genannt.27 siehe http://groups.google.com/group/liftweb/browse_thread/thread/5b02ee88adf1d0ae 111
  • 121. 5.2 FunktionalitätValidierungIm Java EE Programmiermodell ist mit Bean Validation ein Standard für dieValidierung von Objekten spezifiziert. Es existiert eine ausreichende Anzahl anfertigen Validierungsregeln, die durch Bibliotheken wie Hibernate Validator umweitere ergänzt werden. Sollen eigene Validatoren hinzugefügt werden, müssendafür zusätzliche Annotationen implementiert werden.Lift verwendet für die Validierung von Fachentitäten reine Scala Sprachmittel.Dadurch dass Validierungsregeln in Form von Funktionen implementiert werden,können Traits eingesetzt werden, um die Validation modular zu entwickeln. Diesist durch die Nutzung von Self-Type Annotationen möglich und die Angabe derValidierungsfunktion als partiell angewandte Funktion, wie bei der Umsetzung desDomänenmodells in Scala/Lift zu sehen ist.Bei der Validierung unterscheiden sich Scala/Lift und das Java EE Programmier-modell lediglich in der Art und Weise der Integration von Validierungsregeln in dieDomänenschicht. Dabei könnte wie bei der Auswertung zu den Persistenzmechanis-men argumentiert werden, dass in Lift eine enge Kopplung erfolgt und das nachteiligist. Allerdings gehören die Validierungsregeln semantisch zum Domänenmodell,wodurch eine enge Kopplung neutral bewertet werden kann. Welche Lösung alsbesser empfunden wird, ist von den Vorlieben des Softwareentwicklers abhängig:Validierungen deklarativ über Annotationen zu formulieren oder modular mit Traitseinzumixen. Der Autor empfindet z.B. in Hinblick auf die Erweiterung mit eigenenValidatoren die Lösung von Lift angenehmer, da das Anlegen von zusätzlichenAnnotationen entfällt. Funktional sind beide Lösungen gleich.InternationalisierungIn der Java EE Umsetzung der Beispielanwendung als auch in der Lift Versionwerden Ressourcendateien in Form von Resource Bundles eingebunden. In denResource Bundles sind länderspezifische Beschriftungen und Nachrichten gespeichert.Der Suffix des Dateinamen gibt dabei die Länderkennung an und erlaubt die 112
  • 122. 5.3 ArchitekturZuordnung von Ressourcendateien zu den dazugehörigen Sprachen. Darüber wirddie Internationalisierung der Beispielanwendungen implementiert.Insgesamt unterscheiden sich das Java EE Programmiermodell und Lift marginalbei der Internationalisierung von Anwendungen. Zum einen werden in Java EEnormale Properties-Dateien verwendet, wohingegen Lift XML-Dokumente für dieTexte in den Ressourcen nutzt. Zum anderen ist der Mechnismus von Lift, Res-sourcen abzulegen und aufzulösen, flexibler. Durch den Dateinamen können proLift Template spezielle Resource Bundles angegeben werden. Außerdem ist dieNutzung von Lifts Ressourceneinbindung konsistenter. Egal in welchem Teil derAnwendung eine Funktion implementiert wird, über S.? wird ein Text aus denRessourcen eingebunden. Durch die unterschiedlichen Spezifikationen des Java EEProgrammiermodells kann sich die Einbindung von Ressourcen je nach eingesetztemStandard unterscheiden.5.3 ArchitekturLose KopplungDer Java EE Prototyp wurde unter Berücksichtigung der losen Kopplung dereinzelnen Komponenten entwickelt. Es wurde gegen Interfaces entwickelt und mitdem Einsatz von Dependency Injection wurden einzelne Komponenten verbunden.Darüber wird im Java EE Programmiermodell das Entwickeln von lose gekoppeltenAnwendungssystemen unterstützt.Für das Java EE Programmiermodell existieren mehrere Frameworks, die De-pendency Injection ermöglichen. Beispielhaft seien hier genannt: JBoss Weld alsImplementierung von CDI,28 Spring und Google Guice. Für Dependency Injectiongibt es in Java einen Standard, der durch den JCP spezifiziert wurde.29 Daher28 „Contexts and Dependency Injection for the Java EE platform“ (CDI) ist ein neu- er Standard für Dependency Injection in der Java EE 6 Spezifikation. (siehe http://www.jcp.org/en/jsr/summary?id=299)29 JSR 330: Dependency Injection for Java (siehe http://www.jcp.org/en/jsr/detail?id=330) 113
  • 123. 5.3 Architekturunterstützen die Framworks alle die Inject-Annotation. Bei der entwickelten Lösungwurde diese Annotation für das Injizieren der konkreten DAO-Implementierungverwendet. Die Konfiguration fand dabei über ein als Klasse implementiertes GoogleGuice Modul statt.30Durch die Java Interoperabilität von Scala können die Java Bibliotheken für De-pendency Injection auch in Scala Programmcode eingesetzt werden. Allerdings sinddabei Einschränkungen zu beachten.31 Außerdem hat Scala bereits Mechanismenin die Sprache integriert, die zum Bauen von Objektnetzen geeignet sind. Mög-lichkeiten Dependency Injection mit Scala Sprachmitteln umzusetzen werden in[Bon08] beschrieben. Neben der Interoperabilität mit dem DI Framework GoogleGuice werden dort drei reine Scala Ansätze gezeigt: Dependency Injection mit demCake Pattern, mit strukturellen Typen und mit Implicit Argumenten.Das Cake Pattern wurde vom Scala-Erfinder, Martin Odersky, erstmals in [MO]beschrieben. Die Benennung des Entwurfsmusters wurde durch den Vergleich einerAnwendung mit einem Kuchen inspiriert, der auch aus mehreren Schichten bestehtund in Scheiben aufteilbar ist. Das Cake Pattern ist vergleichbar mit der Injektionvon Abhängigkeiten im Konstruktor. Jedoch werden die Abhängigkeiten nicht alsParameter sondern in Form von Traits injiziert bzw. eingemixt. Es handelt sich dabeialso um eine programmatische Lösung, die zudem eine große Menge an so genanntemBoilerplate Code32 für die Konfiguration zur Folge hat, was negativ zu bewerten ist.Andererseits hat das Cake Pattern den Vorteil, dass die Konfiguration automatischvom Compiler auf Fehler geprüft wird, da Konfigurationsfehler automatisch zuKompilierfehlern führen.Lift bietet ebenfalls eine Funktionalität für Dependency Injection an.33 Dafür istder Injector Trait im Lift Framework definiert.30 siehe Listing 4.1031 So ist z.B. beim Einsatz von Spring die Verwendung von Scala-eigenen BeanProperty- Annotationen an den Feldern einer Klasse zwingend notwendig, damit für diese Getter- Setter-Methoden gemäß der Java-Bean-Konvention generiert werden. (siehe dazu [Kal10])32 Boilerplate Code bezeichnet Code, der an mehreren Stellen im Programm in ähnlicher Art vorkommt und wenig zur eigentlichen Funktionalität beiträgt.33 siehe [Pol, Kapitel 8.2] 114
  • 124. 5.3 Architektur§ Listing 5.2: Injector Trait von Lift für Dependency Injection ¤trait Injector { i m p l i c i t def i n j e c t [ T ] ( i m p l i c i t man : M a n i f e s t [ T ] ) : Box [ T ]}¦ ¥Die inject Methode gibt für den Typen T ein Objekt gekapselt in einer Box zurück.Eine Box aus dem Grund, da nicht ausgeschlossen werden kann, dass die injectMethode für Typen aufgerufen wird, die durch die Methode nicht zusammengebautwerden. Der noch unbekannte Manifest Typ dient in Scala dazu, die generischenTypinformationen, die durch die Type Erasure zerstört werden, zur Laufzeit zusichern.34Der Hilfstrait SimpleInjector, der von Injector erbt, bietet mit registerInjection eineFunktion an, um Logik zum Injizieren von Objekten des Typs T zu hinterlegen,die anschließend von der inject Methode verwendet wird.§ Listing 5.3: Deklaration der registerInjection Funktion ¤def r e g i s t e r I n j e c t i o n [ T ] ( f : ( ) = T) > ( i m p l i c i t man : M a n i f e s t [ T ] ) : Unit¦ ¥Der Funktion registerInjection können Funktionen übergeben werden, die ein Objektvom Typ T erzeugen und zurückgeben. In diesen Funktionen wird die Logik zumAufbauen von Objektnetzen formuliert.Das Zusammenspiel der registerInjection und inject Funktion, wird im folgendenListing deutlich.3534 Das Schlüsselwort implicit sorgt dafür, dass der Compiler beim Aufruf der Funktion im lexikalischen Scope nachschaut, ob ein Objekt auf den Typen des Attributs man passt und dann automatisch an inject übergibt. Das Argument man wird dadurch zum impliziten Argument.35 in Anlehnung an [Pol, Kapitel 8.2.1] 115
  • 125. 5.3 Architektur§ Listing 5.4: Lift Dependency Injection Beispiel ¤object M y I n j e c t o r extends S i m p l e I n j e c t o r// Logik f ü r das Bauen von Objekten d e s Typs S e r v i c edef b u i l d S e r v i c e ( ) : S e r v i c e = i f ( testMode ) new S e r v i c e with TestDao {} e l s e new S e r v i c e with RuntimeDao {}M y I n j e c t o r . r e g i s t e r I n j e c t i o n ( b u i l d S e r v i c e _)val myService : Box [ S e r v i c e ] = M y I n j e c t o r . i n j e c t¦ ¥Zu Beginn wird ein Injector in Form des Singletons MyInjector erzeugt. Anschlie-ßend wird in der Funktion buildService das Zusammenbauen von Objekten desTyps Service implementiert. Wenn buildService im Testmodus (duch testModedargestellt) aufgerufen wird, erfolgt das Zusammenbauen eines Service mit demEinmixen des Traits TestDao, ansonsten werden Service Objekte mit dem TraitRuntimeDao erzeugt. Die beiden Traits kapseln in diesem Fall Abhängigkeitenfür Service, vergleichbar dem Cake Pattern. Der Trait RuntimeDao stellt die nor-male DAO Implementierung für ein Service Objekt zur Verfügung, wohingegenin TestDao ein Mock DAO Objekt für Service implementiert ist. Das Einmixender Traits bewirkt das Injizieren der Abhängigkeiten. Durch die Registrierung vonbuildService beim MyInjector Singleton ist die inject Methode in der Lage, Objektedes Typs Service zu bauen, was durch die Zuweisung an myService gezeigt wird.Letztendlich ist inject lediglich eine große, globale Funktion, die für das Erzeugeneines Objekts von einem bestimmten Typ eine andere Funktion aufruft, in der dasZusammenbauen des Objekts erfolgt. Der große Nachteil dieser Methode ist, dassder Entwickler selbst dafür verantwortlich ist, die Komplexität des Objektnetzeszu kennen und eigenständig aufzulösen. Da Lift diese DI Funktion jedoch für dieinternen Hauptkomponenten verwendet, können darüber auf einfache Art und WeiseTeile des Frameworks ausgetauscht werden, z.B. für Tests. Für diesen begrenztenAnwendungsfall, das Mocken von Lift, ist die Methode gut geeignet. 116
  • 126. 5.3 ArchitekturIn der Scala/Lift Beispielanwendung wird dieser DI-Mechanismus nicht verwendet,da anders als beim Java EE Programmiermodell die Verwendung von DependencyInjection in Lift nicht propagiert wird. Außerdem war die Neuheit der SpracheScala und des Lift Frameworks ein Grund dafür, dass diese Funktion von Lift erstnach dem Entwickeln des Prototypen wirklich verstanden wurde.36In Java EE als auch in Scala und Lift ist die lose Kopplung eines Systems durchden Einsatz von Dependency Injection möglich. Die Java Frameworks können dabeiin beiden Technologiestacks eingesetzt werden. Zusätzlich dazu bietet Scala einPattern für DI an und Lift hat eingebaute Funktionen dafür. Allerdings ist dieKonfigurierbarkeit dieser Lösungen nicht befriedigend. Lifts Lösung für DependencyInjection ist ungenügend, wenn größere Objektnetze aufgebaut werden sollen.Cross Cutting ConcernsBeim Java EE Prototypen wurde der Querschnittsbelang (engl. Cross CuttingConcerns) der Transaktionen mittels Google Guice und der Guice Erweiterungwarp-persist umgesetzt. Dabei wurden so genannte deklarative Transaktionenverwendet. Methoden, die in einem Transaktionskontext ablaufen müssen, wurdenmit einer Transactional-Annotation versehen. Dadurch stellt Guice sicher, dassdiese Methoden immer in einer Transaktion aufgerufen werden. Außerdem werdendie Transaktionen durch Guice geschlossen. Entweder durch einen Commit oderes wird ein Rollback durchgeführt. Würde man diesen technischen Belang einerAnwendung manuell implementieren, ist das eine große Fehlerquelle.Des Weiteren existieren mit AspectJ und Spring AOP zwei etablierte Frameworks,um in Java aspektorientierte Programmierung umzusetzen. Die implementierteLösung mit dem Guice Framework ist zwar eine geschickte Art, Methoden in einenTransaktionskontext zu bringen. Jedoch muss die Annotation dafür an jede Methodegeschrieben werden. Mit aspektorientierter Programmierung kann z.B. in wenigenZeilen fomuliert werden: „alle Klassen im Paket persistence.jpa mit Transaktionen36 siehe Auswertungen zur Lernkurve 117
  • 127. 5.3 Architekturversehen“. AOP ist dabei die allgemeine Abstraktion solche Querschnittsbelangeumzusetzen.37In der Scala/Lift Umsetzung hat die Mapper Bibliothek das Verwalten der Trans-aktionen übernommen. Dafür wurde ein LoanWrapper eingesetzt, wie im Kapitelzur Umsetzung des Lift Prototypen gezeigt wurde. Dieser hat für einen Requesteine Transaktion geöffnet und das benennt auch gleich den Nachteil diese Lösung.Sollen für einen Anwendungsfall mehrere Transaktionen in der Bearbeitung einerAnfrage genutzt werden, ist das damit nicht umsetzbar. Daher soll folgend betrach-tet werden, wie solch ein Querschnittsbelang generell mit Scala umgesetzt werdenkann.Aspektorientierte Programmierung wie in Java ist nicht mit Scala Sprachmittelnumzusetzen. Da Scala des Weiteren Möglichkeiten für Metaprogrammierung ver-missen lässt, wie sie dynamische Sprache (z.B. Groovy und Ruby) anbieten, istdarüber auch kein Implementieren der Querschnittsbelange möglich. Jedoch könnendie Querschnittsbelange mit Scala Sprachmitteln AOP-ähnlich umgesetzt werden.Mit Funktionen höherer Ordnung können andere Funktionen dekoriert und somitum Querschnittsbelange ergänzt werden. Im folgenden Listing wird doSomethingmit der Funktion withTransaction dekoriert. Das Transaktionshandling kann inwithTransaction separat von der doSomething Funktion implementiert werden.Durch die Sprachfeatures von Scala sieht withTransaction bei der Verwendung wieeine normale Kontrollstruktur aus.§ Listing 5.5: Dekoration mit withTransaction ¤def doSomething ( ) = w i t h T r a n s a c t i o n { p r i n t l n ( " doSomething ( ) wird a u s g e f ü h r t " )}def w i t h T r a n s a c t i o n [ T ] ( f : = T) : T = > try { p r i n t l n ( " Starte Transaktion " )37 siehe [Wol10, Kapitel 3] 118
  • 128. 5.3 Architektur f } finally { p r i n t l n ( " Beende T r a n s a k t i o n " ) }¦ ¥Der Nachteil ist, dass der Entwickler zum Zeitpunkt der Implementierung wissenmuss, mit welchen Querschnittsbelangen einzelne Funktionen dekoriert werden soll.Außerdem muss die Dekoration in jeder Funktion erfolgen, wie bei der Java Lösungmit Google Guice und der Transactional-Annotation.Wenn konsequent gegen Interfaces entwickelt wird, respektive Traits, gibt es ei-ne weitere und wesentlich elegantere Methode, Querschnittsbelange in Scala zuimplementieren. Die JVM erlaubt es zur Laufzeit ein Objekt für ein Interface zu er-zeugen.38 Dabei werden so genannte Dynamic Proxies erzeugt. Alle Funkionsaufrufedes erzeugten Objekts werden dabei an einen InvocationHandler geleitet, der nebendem eigentlichen Funktionscode zusätzlichen Code, z.B. für Querschnittsbelange,ausführen kann.Für das Beispiel wird daher doSomething in einen Service-Trait eingebettet.§ Listing 5.6: Service Trait ¤trait Service { def doSomething : Unit}¦ ¥Die Service Implementierung erbt den Trait und hat einen privaten Konstruktor.Damit ist sichergestellt, dass neue Instanzen von ServiceImpl nur über die applyMethode des Companion Objekts erzeugt werden können. Dort kann transparentfür die Klasse und den Aufrufer ein Proxy erzeugt werden, der die Funktionen umzusätzliche Belange ergänzt.38 siehe [Wol10, S. 104] 119
  • 129. 5.3 Architektur§ Listing 5.7: Mögliche Dynamic Proxy Integration in Scala ¤c l a s s S e r v i c e I m p l private extends S e r v i c e { def doSomething = { p r i n t l n ( " doSomething ( ) wird a u s g e f ü h r t " ) }}object S e r v i c e I m p l { def apply ( ) = P r o x i e s . d i s p a t c h (new S e r v i c e I m p l , c l a s s O f [ S e r v i c e ] ) { ( i n s t a n c e , method , a r g s ) = { > withTransaction { method . i n v o k e ( i n s t a n c e , a r g s : _∗ ) } } }}¦ ¥Die Klasse Proxies ist eine Hilfsklasse, die das Erzeugen von Dynamic Proxiesin Scala vereinfacht. Wird nun die Methode doSomething aufgerufen, wird zuerstwithTransaction ausgeführt und anschließend durch die invoke-Funktion von methoddie eigentliche Methode.39 Soll der Service um weitere orthogonale Belange ergänztwerden, z.B. Tracing oder Sicherheitsfunktionen, muss dafür nicht mehr jedeMethode dekoriert werden. Es reicht aus, die Erweiterung bei der Proxyerzeugungin der apply Methode hinzuzufügen.Jedoch geht die Nutzung eines Proxies zu Lasten der Performance. Der Aufrufder Methode doSomething ist durch die Verwendung des Dynamic Proxy deut-lich langsamer. Das ist gleichzeitig der Nachteil dieser Methode im Vergleich zuraspektorientierten Programmierung mit AspectJ. AspectJ manipuliert direkt den39 Das gesamte Beispiel befindet sich auf der beigelegten CD. 120
  • 130. 5.3 ArchitekturBytecode, wodurch kaum Performancenachteile entstehen.Außerdem hat die Verwendung eines Proxies den Nachteil, dass Aufrufe von Service-eigenen Funktionen aus ServiceImpl heraus nicht durch den InvocationHandlerbehandelt werden und bei einem solchen Aufruf daher keine Querschnittsbelangeergänzt werden. Hätte Service bespielsweise eine weitere Methode fooBar und würdein fooBar die doSomething Methode aufgerufen werden, kann dieser Aufruf nichtmit orthogonalen Belangen ergänzt werden. Zusätzlich deckt ein Proxy lediglichöffentliche Methoden ab, was also auch nachteilig sein kann.Neben den gezeigten Varianten Querschnittsbelange in Scala zu implementieren,besteht ebenfalls die Möglichkeit, ein AOP-Framework wie AspectJ oder SpringAOP zu verwenden. Jedoch müssen dabei Einschränkungen hingenommen undBesonderheiten beachtet werden.40In Java EE können die Querschnittsbelange mit einem Framework wie Guice um-gesetzt werden. Ebenso ist aspektorientierte Programmierung durch Frameworksmöglich. Aufgrund der Java Interoperabilität von Scala können diese AOP Frame-works auch dort eingesetzt werden. Des Weiteren bieten die Scala SprachfeaturesMöglichkeiten, Querschnittsbelange elegant im Programmcode zu formulieren. Da-durch können Scala/Lift Anwendungen um AOP-ähnliche Konstrukte erweitertwerden. Es ist jedoch anzumerken, dass diese Lösungen nicht die hohe Konfigu-rierbarkeit und Flexibilität wie die aspektorientierte Programmierung unter Javaaufweisen.40 siehe dazu http://programming-scala.labs.oreilly.com/ch14.html#AspectJInterop 121
  • 131. 6 Fazit6.1 ZusammenfassungNachdem zu Beginn der Arbeit die Grundlagen des Java EE Programmiermodellsund von Scala/ Lift erläutert wurden, folgte die Festlegung der Evaluationskriterien.Dabei wurde darauf geachtet, möglichst verschiedene Aspekte der unterschiedli-chen Technologien zu bewerten. Im Anschluss daran folgte die Darstellung derEntwicklung der Beispielanwendungen. Dabei ist aufgefallen, dass sich die beidenTechnologien, das Java EE Programmiermodell und Scala/Lift, signifikant in derArchitektur und bei den eingesetzten Sprachmitteln unterscheiden.Das Domänenmodell wird in Lift bei der Nutzung der Mapper oder Record Bi-bliothek insbesondere durch Persistenz, aber auch durch Code zur Darstellungverschmutzt. Hierbei erfolgt eine Vermischung unterschiedlicher Schichten in derDomänenschicht. Das ist in einem modernen Framework schlicht nicht akzeptabel.Die Alternative der Nutzung von JPA kann mit Schwierigkeiten und Mehraufwandverbunden sein, soll eine enge Kopplung und erneute Vermischung der Schichtenverhindert werden.Der Ansatz von Lift in der Präsentationsschicht mit Snippets und der Transformati-on von XHTML zu arbeiten, ist interessant und sinnvoll. Gleichzeitig wird darübereine mächtige AJAX-Unterstützung in Lift implementiert. Zusätzlich unterstütztLift unter Verwendung des Aktoren-Modells das Comet-Technologiekonzept, wasin JSF überhaupt nicht existiert. Im Gegensatz dazu existieren in Lift keine Kom-ponentenbibliotheken wie bei JSF, was für die Entwicklung von funktionsreichenOberflächen einen nicht zu unterschätzenden Mehraufwand bedeutet. 122
  • 132. 6.2 AusblickDie Validierung konnte in beiden Technologien sinnvoll gelöst werden. Dabei unter-scheiden sich das Java EE Programmiermodell mit dem Bean Validation Standardund Lift lediglich in der Art und Weise der Integration der Validierungsregeln indas Domänenmodell.Lose Kopplung über Dependency Injection und die Integration von Cross CuttingConcerns werden im Java EE Programmiermodell deutlich besser gelöst als inScala/Lift.Im Vergleich zum Java EE Programmiermodell ist Lift momentan kaum für dieEntwicklung typischer Web-Awendungen1 geeignet. Zwar hat Lift einen interessan-ten und mächtigen Ansatz für die Präsentationsschicht, jedoch überwiegen dieNachteile der Vermischung der Schichten im Domänenmodell. Hinzu kommt, dassdie Unterstützung der Toolchain noch nicht den hohen Grad an Funktionalitäterreicht hat, wie vergleichsweise bei Java EE. Lift tritt daher auch nicht in möglicheKonkurrenz mit dem Java EE Programmiermodell.Beide Technologien adressieren unterschiedliche Anwendungstypen. Die Stärke vonLift ist die Entwicklung von hochgradig interaktiven Web-Anwendungen mit Ajaxund Comet.2 Wohingegen das Java EE Programmiermodell die Entwicklung vonverteilten, lose gekoppelten und komponentenorientierten Anwendungssystemenbegünstigt.6.2 AusblickUm den Umfang der Arbeit zu begrenzen, konnten die Lift Actors Bibliothek unddie Comet Unterstützung von Lift nicht an einem Beispiel untersucht werden. EinigeBeispielanwendungen wie z.B. eine Pokeranwendung3 oder aber auch real produktivlaufende Web-Anwendungen wie Foursquare4 zeigen, dass komplexe nebenläufigeAnwendungen mit Hilfe dieser Bibliotheken gut zu implementieren sind. Für weitere 1 Das sind beispielsweise formularbasierte Geschäftsanwendungen mit Web-Oberfläche. 2 Ein Chatsystem oder ein E-Mail Client als Web-Anwendung wären solche Anwendungen. 3 siehe https://github.com/dhobi/LiftPoker 4 siehe https://foursquare.com 123
  • 133. 6.2 AusblickArbeiten ist es interessant zu untersuchen, wie sich eine solche Applikation unterstark schwankenden und hohen Lastbedingungen verhält.Bei der implementierten Lift Beispielanwendung mussten die Tabellen mit AjaxSupport eigenhändig entwickelt werden, da es dafür keine fertige Komponente gab.Daher ist es zum einen interessant zu untersuchen, wie standardisierte Oberflächen-komponenten in Lift entwickelt werden können. Zum anderen ist die Frage, ob eineKomponentenbibliothek für Lift überhaupt Sinn macht und von der Communityangenommen wird.In der Arbeit hat sich gezeigt, dass Scala als Sprache neue Möglichkeiten eröffnet.Lift als Framework bringt zum Teil gänzlich andere Änsätze mit als das bekannteJava EE Programmiermodell. In Zukunft wird sich zeigen, ob Lift für bestimmteNischen das Framework der Wahl wird oder weiterhin ein Exot bleibt. 124
  • 134. Literaturverzeichnis[Bal01] Balzert, Helmut: Lehrbuch der Software-Technik. Spektrum Akademi- scher Verlag, 2001[Bon08] Bonér, Jonas: Real-World Scala: Dependency Injection (DI). Version: Oktober 2008. http://jonasboner.com/2008/10/06/ real-world-scala-dependency-injection-di.html, Abruf: 31.05.2011[Bos04] Bosch, Andy: Java Server Faces. Addison-Wesley Verlag, 2004[Bra11] Braun, Oliver: Scala - Objektfunktionale Programmierung. Carl Hanser Verlag München, 2011[DCB11] Derek Chen-Becker, Marius Danciu und Tyler W.: Exploring Lift. http://exploring.liftweb.net. Version: Februar 2011[DW09] Dean Wampler, Alex P.: Programming Scala. O’Reilly, 2009[Edl10] Edlich, Stefan: NoSQL. Hanser Verlag, 2010[EG95] Erich Gamma, Richard Helm und Ralph J.: Design Patterns. Addison- Wesley Verlag, 1995[EJ10] Eric Jendrock, Jennifer B.: The Java EE 5 Tutorial. Version: September 2010. http://download.oracle.com/javaee/5/ tutorial/doc/index.html, Abruf: 02.06.2011[Fow] Fowler, Martin: POJO. http://www.martinfowler.com/bliki/POJO. html, Abruf: 04.06.2011[Fow03] Fowler, Martin: Patterns of Enterprise Application Architecture. Addison-Wesley Verlag, 2003 IX
  • 135. Literaturverzeichnis[Jen11] Jendryschik, Michael: Einführung in XHTML, CSS und Webde- sign. Version: 2011. http://jendryschik.de/wsdev/einfuehrung/ css/selektoren, Abruf: 03.06.2011[Kal10] Kalm, Barbara von: Analyse und Bewertung der Einsatzmöglichkeiten von Scala in Spring / JSF / Hibernate - basierten Applikationen anhand typischer Anwendungsfälle, Hochschule Mannheim, Diplomarbeit, 2010[MM10] Martin Marinschek, Michael Kurz und Gerald M.: JavaServer Faces 2.0. dpunkt.verlag, 2010[MO] Martin Odersky, Matthias Z.: Scalable Component Abstractions. http: //lampwww.epfl.ch/~odersky/papers/ScalableComponent.html, Ab- ruf: 07.06.2011[MO08] Martin Odersky, Lex Spoon und Bill V.: Programming in Scala. First. artima Press, 2008[Obj] ObjectDB: Working with JPA Entity Objects. http://www.objectdb. com/java/jpa/persistence/managed, Abruf: 02.06.2011[OI07] Oliver Ihns, Dierk Harbeck und Stefan H.: EJB 3 professionell. dpunkt.verlag, 2007[Per11] Perrett, Timothy: Lift in Action. Manning Early Access Program. Manning Publications, 2011[Pol] Pollak, David: Simply Lift. http://stable.simply.liftweb.net, Abruf: 01.06.2011[Röd10] Röder, Daniel: JPA mit Hibernate. entwickler.press, 2010[Sta06] Stark, Thomas: Java EE 5 - Einstieg für Anspruchsvolle. Addison- Wesley Verlag, 2006[TF11] Thomas Fiedler, Christoph K.: Entwicklung von Web-Applikationen mit Lift und Scala. Shaker Verlag, 2011[Ull11] Ullenboom, Christian: Java ist auch eine Insel. Galileo Computing, 2011 X
  • 136. Literaturverzeichnis[Wan08] Wang, Peng: Comparison of Four Popular Java Web Framework Imple- mentations: Struts1.X, WebWork2.2X, Tapestry4, JSF1.2, University of Tampere, Diplomarbeit, 2008[Wik] Wikipedia: Ajax (Programmierung). http://de.wikipedia.org/wiki/ Ajax_(Programmierung), Abruf: 01.06.2011[Wol10] Wolff, Eberhard: Spring 3. dpunkt.verlag, 2010 XI
  • 137. A Inhalt der CDDieser Arbeit liegt eine CD bei, die neben den entwickelten Prototypen nochzusätzliche Materialien enthält. Es folgt eine Inhaltsbeschreibung der CD.javaAnwendung Dieses Verzeichnis enthält die in Java EE entwickelte Beispielan- wendung. In oberster Ebene liegt die generierte WAR-Datei. Im doc Ver- zeichnis befindet sich die erzeugte JavaDoc und der src Ordner enthält den Quellcode.proxyBeispiel Die Projektdateien für das im Kapitel 5.3 gezeigte Beispiel eines dynamischen Proxies in Scala befinden sich in diesem Verzeichnis.scalaAnwendung In diesem Verzeichnis befindet sich der in Scala/Lift entwickelte Prototyp. Die generierte WAR-Datei liegt in der obersten Verzeichnisebene. Im doc Verzeichnis befindet sich die erzeugte ScalaDoc und der src Ordner enthält den Quellcode.thesis Der CD liegt eine elektronische Version der Arbeit in Form einer PDF-Datei bei, die sich in diesem Verzeichnis befindet. XII
  • 138. Abschließende ErklärungIch versichere hiermit, dass ich die vorliegende wissenschaftliche Arbeit selbstständigund ohne fremde Hilfe angefertigt und keine andere als die angegebene Literaturbenutzt habe. Alle von anderen Autoren wörtlich übernommene Stellen wie auchdie sich an die Gedankengänge anderer Autoren eng anlehnenden Ausführungenmeiner Arbeit sind besonders gekennzeichnet. Diese Arbeit wurde bisher in gleicheroder ähnlicher Form keiner anderen Prüfungsbehörde vorgelegt und auch nichtveröffentlicht.Berlin, den 9. Juni 2011Felix Müller

×