Westfälische Wilhelms-Universität Münster Ausarbeitung Application Frameworks im Rahmen des Seminars Software Engineering Birgit Hecker Themensteller: Prof. Dr. Herbert Kuchen Betreuer: Christian Hermanns Institut für Wirtschaftsinformatik Praktische Informatik in der Wirtschaft Inhaltsverzeichnis 1 Aufbau und Ziel der Arbeit........................................................................................ 1 2 Grundlagen ................................................................................................................ 2 3 2.1 Application Framework ..................................................................................... 2 2.2 Dependency Injection......................................................................................... 3 2.3 Aspektorientierte Programmierung .................................................................... 4 Spring Framework ..................................................................................................... 5 3.1 Beispielanwendung Bibliothek .......................................................................... 5 3.2 Spring-Container ................................................................................................ 6 3.3 Spring-Bean........................................................................................................ 7 3.4 Dependency Injection......................................................................................... 7 3.5 Aspektorientierte Programmierung .................................................................. 10 3.6 Model View Controller .................................................................................... 11 3.7 Datenbankzugriff.............................................................................................. 14 3.8 Sicherheit.......................................................................................................... 17 4 Vergleich Spring und Java EE ................................................................................. 21 5 Zusammenfassung und Ausblick ............................................................................. 22 Anhang A: Klassendiagramm ......................................................................................... 23 Anhang B: Quellcode...................................................................................................... 24 Literaturverzeichnis ........................................................................................................ 60 II Kapitel 1: Aufbau und Ziel der Arbeit 1 Aufbau und Ziel der Arbeit Die Nachfrage nach Geschäftsanwendungen im Java-Bereich steigt. Dabei werden die Anwendungen immer komplexer. Folgen davon sind, dass eine einfache Verwendung von Java ohne Bibliotheken nicht möglich ist. Eine Bibliothek stellt Funktionen zu bestimmten Aufgabenstellungen bereit, in Form einer Sammlung von Klassen dieses Aufgabengebietes. Die Einarbeitung in eine solche Anwendung ist für den Entwickler sehr zeitintensiv. Einen Ausweg bieten Frameworks. Sie stellen eine Schicht über verschiedene Bibliotheken dar und vereinfachen somit die Softwareentwicklung [Wo07, Kap. 1.1]. Frameworks enthalten bereits Funktionalität, die in die eigentliche Implementierung eingebunden werden kann. Im Gegensatz zur Bibliothek aus der der Programmierer bei Bedarf einzelne Klassen verwendet, gibt das Framework die Struktur für die Anwendung vor [GHJV04]. Der Programmierer erstellt und registriert Klassen auf Grundlage der vorgegebenen Schnittstellen. So wird die konkrete Implementierung durch das Framework gesteuert und benutzt. Es wird zwischen programmiersprachenund zweckabhängigen Frameworks differenziert. Beispiele für Frameworks sind Java EE, .NET und Spring. Für das Java-Framework Spring bildete das Buch „Expert One-On-One J2EE Design und Development“ die Grundlage für das OpenSource-Projekt Spring. Durch die große Resonanz und Beteiligung an dem OpenSource-Projekt wurde Spring stetig weiterentwickelt. Ziel dieser Arbeit ist die detaillierte Darstellung der Softwareentwicklung mit dem Spring Framework. Dazu wird zunächst im 2. Kapitel der Begriff Framework näher beleuchtet. Außerdem werden die Programmiertechniken Dependency Injection und Aspektorientierte Programmierung beschrieben. Diese sind von großer Bedeutung für das Spring Framework, welches in Kapitel 3 vorgestellt wird. Anhand von Beispielen aus der Entwicklung einer Bibliotheksanwendung wird die Softwareentwicklung mit diesem Framework erläutert. Kapitel 4 vergleicht schließlich Spring mit dem Java EE Framework. Abschließend erfolgen in Kapitel 5 eine Zusammenfassung und ein Ausblick. 1 Kapitel 2: Grundlagen 2 Grundlagen 2.1 Application Framework In der Literatur finden sich verschiedene Definitionen für den Begriff Framework. Eine Definition, die häufig verwendet wird, stammt von [GHJV04]: „Ein Framework ist eine Menge von kooperierenden Klassen, die einen wiederverwendbaren Entwurf für einen bestimmten Anwendungskontext vorgeben. Das Framework bestimmt dabei die Architektur der Applikation.“ Ein Framework ist somit eine wiederverwendbare Umgebung für die Softwareentwicklung. Für die objektorientierte Programmierung bedeutet dies, dass ein bestimmtes Paket an Klassen zur Verfügung gestellt wird, um aus diesem Softwareanwendungen erstellen zu können. Das Framework gibt durch die verfügbaren Schnittstellen und abstrakte Klassen die grobe Anwendungsstruktur und den Kontrollfluss der Anwendung vor. Mit Hilfe dieser zur Verfügung gestellten Funktionen erstellt der Entwickler eine konkrete Anwendung. Da diese Funktionen nicht ohne Berücksichtigung eines konkreten Anwendungskontextes erstellt werden können, sind Frameworks meist domänenspezifisch. Um die Anwendungsarchitektur vorzugeben, findet eine Umkehrung des Kontrollflusses (Inversion of Control) statt. Dabei werden in der Erstellung einer Anwendung auf Basis eines Frameworks Klassen und Methoden aus dem Framework aufgerufen. Dieses Prinzip wird auch als Hollywood-Prinzip („Don’t call us, we call you“) bezeichnet [GHJV04]. Ein Vorteil von Frameworks ist, dass sie bereits fertig entworfene und implementierte Softwarestrukturen bereitstellen, die wieder verwendet werden können. So muss der Entwickler z. B. Grundfunktionalitäten wie das Überprüfen von Benutzerrechten, die in jeder Anwendung vorkommen, nicht jedesmal neu implementieren. Durch die Kapselung von Implementierungsdetails wird die Anwendung modularer und die Softwarequalität steigt. Ein weiterer Vorteil von Frameworks ist die Erweiterbarkeit. Das Framework stellt dem Entwickler feste Punkte bereit an denen er intervenieren und das Framework konfigurieren und erweitern kann [FaSc97]. Bei Frameworks differenzieren [FaSc97] zwischen Black- und White-Box-Frameworks. Die Grundlage für White-Box-Frameworks bilden Vererbung und dynamisches Binden, um Erweiterbarkeit zu gewährleisten. Die bereitgestellte Funktionalität wird 2 Kapitel 2: Grundlagen wiederverwendet und kann durch Vererbung der bereitgestellten Klassen oder durch Überschreiben von vordefinierten Methoden ergänzt werden. In Black-BoxFrameworks wird die Erweiterbarkeit durch Komposition in das Framework integriert, d. h. die zur Verfügung stehenden Komponenten müssen vom Entwickler konfiguriert und zusammengesetzt werden. Bei Black-Box-Framework kennt der Benutzer daher nur die Schnittstelle und nicht die innere Struktur. Im Gegensatz dazu muss der Benutzer des White-Box-Frameworks die innere Struktur des Frameworks genau kennen. Daher sind Black-Box-Framework leichter zu verwenden und erweitern als White-BoxFrameworks. Allerdings sind Black-Box-Frameworks schwieriger zu entwickeln, da hier Schnittstellen zur Verfügung gestellt werden müssen, die eine Reihe von potentiellen Anwendungsfällen abdecken [FaSc97]. In dieser Ausarbeitung wird das Application Framework Spring vorgestellt. Dabei handelt es sich um eine Mischform aus Black- und White-Box-Framework, da es Vererbung und dynamisches Binden verwendet ebenso wie Komposition. Dazu werden die Programmiertechniken Dependency Injection und Aspektorientierte Programmierung benutzen. 2.2 Dependency Injection Dependency Injection ist eine Programmiertechnik, die in der objektorientierten Programmierung eingesetzt wird, um die Abhängigkeiten zwischen Objekten zu minimieren. Solche Abhängigkeiten entstehen z. B. durch den Verweis eines Objekt auf ein anderes Objekt. Ohne die Anwendung von Dependency Injection ist jedes Objekt selbst für die Erzeugung und Verwaltung seiner Abhängigkeiten zuständig. Für die Erzeugung anderer Objekte muss das abhängige Objekt Getter- und Setter-Methoden für das zu erstellende Objekt zur Verfügung stellen. Bei Dependency Injection werden diese Abhängigkeiten verringert, indem die Verantwortung für die abhänigen Objekten an das Framework übergeben wird. Die Abhängigkeiten werden zur Laufzeit den Objekten injiziert. Dies wird meistens deklarativ in XML-Dateien konfiguriert. Außerdem lassen sich Klassen so einfacher testen, da ihre abhängigen Objekte durch Mock-Objekte ersetzt werden können. Mock-Objekte dienen als Platzhalter für echte Objekte innerhalb von Tests [Wo07]. Es gibt drei Varianten von Dependency Injection: Interface-Injection, Setter-Injection und Constructor-Injection. Hierbei entsteht die größte Abhängigkeit bei Interface3 Kapitel 2: Grundlagen Injection. Bei Interface-Injection muss eine Klasse alle Interfaces und deren vorgegebene Methoden implementieren damit alle abhängigen Objekte zugeordnet werden können. Es muss also für jede Art der Abhängigkeit eine Schnittstelle definiert werden. Um für Setter-Injection Abhängigkeiten zur Laufzeit erzeugen zu können, muss der Client die entsprechende Setter-Methode zum Setzen des benötigen Objektes anbieten. Bei Constructor-Injection werden sämtliche Abhängigkeiten einer Klasse über den Konstruktor übergeben. Damit kann ein Objekt der Klasse nur erzeugt werden, wenn alle Abhängigkeiten bei der Erstellung übergeben werden [Fo04]. 2.3 Aspektorientierte Programmierung Aspektorientierte Programmierung (AOP) ist ein Ansatz, um Codewiederholungen zu vermeiden. Dabei werden die sich ggf. wiederholenden Codestücke ausgelagert. Dies entspricht dem Prinzip „Separation of Concerns“. Das bedeutet der Basisbestandteil einer Methode wird von speziell zweckgebundenen und sich wiederholenden Teilen wie dem Überprüfen von Benutzerrechten getrennt. Die ausgelagerten Teile werden in der AOP Aspekte bezeichnet. Ein Aspekt kann z. B. für Logging, Transaktionen oder Sicherheit zuständig sein. Durch AOP entfällt die Duplizierung von Code. Er wird an einer zentralen Stelle implementiert und ist dadurch wiederverwendbar. Deshalb werden Aspekte auch als Querschnittsbelange (Cross Cutting Concerns) bezeichnet. Ein Beispiel hierfür ist die Sicherheit, für die in jeder Methode der Benutzer und seine Rechte überprüft werden. Mit AOP ist es möglich bei einem Methodenaufruf einzugreifen und zusätzlichen Code auszuführen. Die zentrale Speicherung des Quellcodes erhöht die Verständlichkeit und Wartbarkeit des Codes [Wo07]. Im Umfeld von AOP werden neben dem Begriff Aspekt noch weitere Termini verwendet, die im Weiteren näher erläutert werden. Ein Advice beschreibt die Aufgabe eines Aspektes. Außerdem gibt der Advice an, wann die Aufgabe ausgeführt wird, z. B. vor dem Methodenaufruf. Stellen, an denen in der Anwendung ein Aspekt eingebunden werden kann, werden als Joinpoint bezeichnet. Ein Joinpoint kann z. B. vor einem Methodenaufruf oder bei einem geänderten Feldwert auftreten. Pointcuts bilden eine Teilmenge der Joinpoints. Sie schränken die Aufrufe von Aspekten ein, indem sie definieren wo der Advice ausgeführt wird. Dies kann z. B. über reguläre Ausdrücke erfolgen. Ein Aspekt ist in diesem Zusammenhang die Vereinigung von Advice und Pointcut. Es ist bekannt welcher Code wo und wann ausgeführt wird [Wa08] . 4 Kapitel 3: Spring Framework 3 Spring Framework Bei Spring handelt es sich um ein OpenSource-Framework, welches insbesondere die Programmierung von Java-EE-Anwendungen erleichtert. Grundlage für das Spring Framework bildete das Buch "Expert One-On-One J2EE Design and Development“. In diesem wurden die Programmiertechniken Dependency Injection und AOP beschrieben, die die wichtigsten Merkmale in Spring darstellen. Ziel der Entwicklung von Spring war es, die Erstellung von Unternehmensanwendungen zu vereinfachen. Die Basis für jede Spring-Anwendung bilden Anwendungsobjekte, sogenannte Spring-Beans, und der Spring-Container, über den die Beans konfiguriert und über den kompletten Lebenszyklus verwaltet werden. Diese werden in den beiden folgenden Abschnitten näher beschrieben. Insgesamt setzt sich das Spring Framework aus sechs Modulen zusammen. Das Kernmodul stellt den Anwendungscontainer (Spring-Container) zur Verfügung, der die Grundlage für den Gebrauch von Dependency Injection bildet. Je nach Bedarf können weitere Spring Module an das Kernmodul angebunden werden, so dass die Nutzung von z. B. Hibernate ohne Probleme möglich ist. In den einzelnen Abschnitten dieses Kapitels werden außerdem noch die Module DAO und ORM, die für den Datenbankzugriff notwendig sind und die Module AOP und MVC erläutert. Die Beschreibung von Spring und dessen Elementen erfolgt anhand eines Beispiels für die Implementierung einer Bibliotheksanwendung. 3.1 Beispielanwendung Bibliothek Diese Beispielanwendung besteht aus Benutzern, die Exemplare von Büchern ausleihen können. Ein Klassendiagramm hierzu befindet sich im Anhang A. Es zeigt, dass Bücher eine 1:N-Beziehung zu Exemplaren haben, d. h. es kann mehrere Exemplare von einem Buch geben. Außerdem sind einem Benutzer mehrere Ausleihen zugeordnet. In diesem werden das Ausleihdatum, der Benutzer und das ausgeliehene Exemplar gespeichert. Ein Exemplar kann einer Ausleihe zugeordnet sein. Aus diesem Klassendiagramm wird mit Hilfe des Modules MVC eine Webanwendung erstellt. Mit dieser ist es möglich sich nach erfolgreichem Login alle Bücher sowie die aktuellen Ausleihen zu dem angemeldeten Benutzer anzeigen zu lassen. Der Administrator der Anwendung kann die Benutzer der Anwendung einsehen. In dieser Arbeit wird zunächst in Abschnitt 3.4 Dependency Injection anhand der Klasse User erklärt. Diese wird in Abschnitt 3.7 um 5 Kapitel 3: Spring Framework einen Datenbankzugriff erweitert. Die gesamten Daten der Anwendung sind in einer MySQL Datenbank gespeichert und werden über JDBC in die Anwendung integriert. In Abschnitt 3.5 wird AOP am Beispiel vom Logging-Aspekt erläutert. Das Module MVC wird in Abschnitt 3.6 anhand der Webseite zur Anzeige aller Bücher näher beschrieben. Damit sichergestellt ist, dass der authentifizierte Benutzer auch die erforderlichen Rechte besitzt um bestimmte Services in Anspruch zu nehmen, wird in Abschnitt 3.8 vorgestellt wie ein Sicherheitsaspekt mit Hilfe es Moduls Spring Security angelegt wird. 3.2 Spring-Container Die Basis für jede Spring-Anwendung bilden Spring-Beans und der Spring-Container. Der Container erstellt, verbindet, konfiguriert und verwaltet alle Anwendungsobjekte, die Spring-Beans, über deren kompletten Lebenszyklus. Dazu wird Dependency Injection eingesetzt. Es gibt zwei Typen von Spring-Containern. Der Unterschied beider liegt im Leistungsumfang. Die BeanFactory ist eine Implementierung des FactoryEntwurfsmusters. Ihre Aufgabe ist es Spring-Beans zu erstellen und zu verteilen. Es handelt sich dabei um eine Allzweck-Factory, die unterschiedliche Bean-Typen zurückgibt. Die meistverwendete BeanFactory-Implementierung ist XmlBeanFactory. Sie arbeitet nach dem Lazy-Loading-Prinzip, d. h. die Instanzierung der Spring-Bean findet erst beim Aufruf der Bean durch die Methode getBean() statt. [Wa08, Kap. 2.1] Der ApplicationContext ist mächtiger als die BeanFactory. Deshalb wird dieser in komplexeren Anwendungen verwendet. Der Kontext umfasst neben den Funktionen der BeanFactory Möglichkeiten zum Auslösen von Textnachrichten, Laden von Dateiressourcen wie Bilder und Veröffentlichen von Ereignissen für registrierte SpringBeans. Im Gegensatz zur BeanFactory werden Singleton-Beans direkt instanziiert. BeanFactory factory = new XmlBeanFactory (new FileSystemResource("applicationContext.xml")); User peter = (User) factory.getBean("U4711"); Listing 1: Erzeugung einer BeanFactory Ein Spring-Container kann aus jeder normalen Java-Anwendung aufgerufen werden. Der Aufruf beider Container-Typen ist ähnlich. Zunächst wird der Container erzeugt und anschließend die Bean über die Methode getBean() des Containers aufgerufen. Listing 1 zeigt die Erzeugung einer Spring-Bean über die BeanFactory. Dabei wird mit Hilfe einer FileSystemResource ein Objekt der XmlBeanFactory erzeugt. Die BeanDefinition wird aus einer XML-Datei ausgelesen. Üblicherweise wird eine Datei für die 6 Kapitel 3: Spring Framework Konfiguration einer Spring-Anwendung erstellt. Aus dieser wird im Beispiel ein UserBean, also der konkreter Benutzer Peter mit der ID U4711, über getBean() erzeugt [Wa08, Kap. 2.1]. 3.3 Spring-Bean Spring-Beans stellen Java-Klassen dar, die mit Hilfe des Spring-Containers konfiguriert werden. Für jedes Spring-Bean wird zunächst eine Java-Klasse mit setter- und getterMethoden für die Attribute bzw. einem Konstruktor erstellt. Für die Injizierung erfolgt die Konfiguration über eine XML-Datei oder über Annotationen in der jeweiligen JavaKlasse. Bei XML-basierter Konfiguration wird eine Bean unter Verwendung des Tag <bean> erzeugt. Der Name über den auf das Bean über den Spring-Container zugegriffen werden kann, wird über das Attribut id übergeben. Im Attribut class wird der vollständige Name der Java-Klasse, auf den sich das Bean bezieht, angegeben [Wa08, Kap. 2.2]. In Listing 2 wird eine Bean der Klasse User erzeugt. Auf dieses kann über die ID U4711 zugegriffen werden. Geltungsbereiche für Beans Jede Bean hat einen bestimmten Gültigkeitsbereich. Dieser wird über das Tag <scope> definiert. Dabei wird zwischen Singleton, Prototype, Request, Session und GlobalSession differenziert. Standardmäßig sind alle Beans Singleton-Beans. Hierbei existiert nur eine Bean pro Spring-Container. Alle Aufrufe einer konkreten Bean werden von derselben Instanz durchgeführt. Mit Prototype ist eine mehrfache Instanziierung einer Bean ist möglich. Bei jedem Aufruf einer Bean wird eine neue Instanz erzeugt. Die Geltungsbereiche Request, Session und Global-Session finden nur im Rahmen von webbasierten Anwendungen Einsatz (z. B. bei Spring MVC) [Wa08, Kap. 2.5.1]. 3.4 Dependency Injection In Spring können zwei Typen von Dependency Injection eingesetzt werden: SetterInjection und Konstruktor-Injection. 7 Kapitel 3: Spring Framework Setter-Injection Bei Setter-Injection werden die Attribute der Bean über das <property>-Tag gesetzt. Dafür muss die Java-Klasse die entsprechenden Setter-Methoden aufweisen. <bean id="U4711" class="bib.User"> <property name=”id” value=”4711” /> <property name=”username” value=”Peter” /> <property name=”password” value=”1234” /> <property name="firstname" value="Peter" /> <property name="lastname" value="Muster" /> </bean> Listing 2: Setter-Injection Das Beispiel in Listing 2 zeigt, dass das <property>-Tag die Attribute name und value besitzt. Mit name wird das Attribut der Bean-Klasse spezifiziert und mit value wird diesem eine Ausprägung übergeben. Hier wird ein Benutzer mit der ID 4711 und dem Namen Peter Muster erzeugt. Dies ist ein Beispiel ohne die Verwendung einer Datenbank. Auf die Verwendung einer Datenbank wird in Kapitel 3.7 näher eingegangen [Wa08, Kap. 2.3]. Constructor-Injection Wenn die Injektion über einen Konstruktor erfolgen soll, passiert dies mit dem Tag <constructor-arg>. Über das Attribut value wird der Wert für den ersten Konstruktorwert übergeben. Durch Wiederholung des <constructor-arg>-Tags können bei mehreren Parametern im Konstruktor diesem Werte übergeben werden [Wa08, Kap. 2.2.2]. <bean id="U4711" class="bib.User"> <constructor-arg value="4711" /> <constructor-arg value=”Peter” /> <constructor-arg value=”1234” /> <constructor-arg value="Peter" /> <constructor-arg value="Muster" /> </bean> Listing 3: Konstruktor-Injektion In Listing 3 wird der gleiche Benutzer wie in Listing 2 erzeugt. In diesem Fall allerdings durch Übergabe der Attributwerte an den Konstruktor. Die Beispiele zeigen, dass dem Attribut value immer Strings übergeben werden. Intern wird dieser String auf den entsprechenden Datentyp des Klassenattributes gecastet. Das Attribut value kann nur für primitive Datentypen und deren entsprechende Wrapper- 8 Kapitel 3: Spring Framework Klassen verwendet werden. Falls auf eine andere Bean referenziert werden soll, ist hierfür das Attribut ref zu verwenden. Collections verschachteln Falls eine Bean eine Eigenschaft, die eine Liste von Ausprägungen wie eine Collection besitzt, kann diese auf vier unterschiedliche Arten mit Spring konfiguriert werden. Die Tags <list> und <set> verschachteln eine Liste von Werten, wobei nur bei <list> doppelte Werte erlaubt sind. Die dritte Möglichkeit Mehrfacheigenschaften zu konfigurieren ist über das <map>-Tag. Diese entspricht der Collection java.util.Map. Das <props>-Tag stellt die vierte Möglichkeit zur Konfiguration von Mehrfacheigenschaften dar und entspricht java.util.Properties [Wa08, Kap. 2.3]. <bean id="U4711" class="bib.User"> . . . <property name="lendings"> <list> <ref bean="ex3462"> <ref bean="ex4458"> <ref bean="ex5697"> </list> </property> </bean> Listing 4: Injizierung von Collections In Listing 4 wird über das Tag <list> eine Collection für das Attribut lending zum Benutzer U4711 angelegt d. h. der Benutzer hat zur Zeit drei Exemplare ausgeliehen. Autowiring Statt alle Eigenschaften einer Bean über die Tags <property> oder <constructor-arg> zu setzen, kann vor allem bei komplexen Anwendungen, die Verknüpfung der Beans untereinander automatisch erfolgen. Dabei wird bei jedem Bean, das automatisch verbunden werden soll, die Eigenschaft autowire gesetzt. In Spring gibt es vier Typen von Autowiring. Bei Autowiring byName wird die Eigenschaft von Beans über den Namen der Eigenschaft vervollständigt. Lautet der Name der Eigenschaft z. B. „book“ wird nach einem Bean mit der id „book“ gesucht. Die Vervollständigung kann auch über den Typ der Eigenschaft, also dem Klassennamen z. B. class = bib.User, erfolgen. Dies wird als Autowiring byType bezeichnet. Die dritte Variante von Autowiring ist byConstructor. Dabei wird versucht eine oder mehrere Beans auf die Parameter des Konstruktors abzubilden. Bei der vierten Variante, Autodect-Autowiring, wird Autowiring erst byType und anschließend byConstructor durchgeführt. Das 9 Kapitel 3: Spring Framework automatische Verbinden aller Eigenschaften einer Bean, kann durch Angabe von Eigenschaften über das Tag <property> überschrieben werden [Wa08, Kap. 2.4]. 3.5 Aspektorientierte Programmierung Vom Spring Framework werden in der AOP nur Joinpoints im Zusammenhang mit einem Methodenaufruf unterstützt. Andere AOP Funktionalitäten können über die Einbindung von der Java-AOP-Erweiterung AspectJ in das Spring Framework erzielt werden. Spring-Advices sind in Java-Klassen zu erstellen. Die dazugehörigen Pointcuts werden gewöhnlich in der XML-Konfigurationsdatei geschrieben. Aspekte werden in Spring zur Laufzeit in Spring-Beans eingebettet, indem sie in einer Proxy-Klasse gekapselt werden. Der Proxy fängt den Methodenaufruf ab, so dass die Methode um die Zusatzfunktionalität, den Advice, erweitert werden kann. Der Proxy wird ebenfalls in der Bean-Konfigurationsdatei definiert. Bei Spring AOP gibt es fünf verschiedene Typen von Advices, die über Einbindung des entsprechenden Interfaces implementiert werden. Dabei werden die Interfaces MethodBeforeAdvice, AfterReturningAdvice, ThrowsAdvice, MethodInterceptor und IntroductionInterceptor unterschieden [Wa08, Kap. 4.1]. package aop; public class LoggingAdvice implements MethodInterceptor { public Object invoke(MethodInvocation methodInvocation){ Log log; log = LogFactory.getLog(getClassName(methodInvocation)); log.info("Class : " + getClassName(methodInvocation)); log.info("Method : " + getMethodName(methodInvocation)); log.info("Arguments : " + getArguments(methodInvocation)); ... //ausführen der eigentlichen Methode result = methodInvocation.proceed(); ... } ... } Listing 5: LoggingAdvice Listing 5 zeigt ein typisches Beispiel für einen Advice, den LoggingAdvice. Hiermit werden zu jeder Methode, die dem Advice zugeordnet wird, Logginginformationen wie die aufgerufene Methode und deren übergebene Parameter auf der Konsole ausgegeben. Dazu wird das Interface MethodInterceptor implementiert, das die Methode invoke() beinhaltet, die implementiert werden muss. Über methodeInvocation.proceed() wird die abgefangene Methode ausgeführt. 10 Kapitel 3: Spring Framework Anschließend werden Pointcuts in der Konfigurationsdatei definiert. In Spring werden Pointcuts am häufigsten über reguläre Ausdrücke oder mit AspectJ-Ausdrücken festgelegt. Der reguläre Ausdruck legt fest, in welchen Methoden der zum Pointcut gehörende Advice ausgeführt wird. Dies erfolgt über einen Vergleich zwischen dem regulären Ausdruck und der Methodensignatur. In der XML-Konfigurationsdatei wird über das Attribut pattern der reguläre Ausdruck angegeben [Wa08, Kap. 4.1]. <!-Advice--> <bean id="loggingInterceptor" class="aop.LoggingAdvice" /> <!—Pointcut--> <bean id="loggingPointcut" class="org.springframework.aop.support.JdkRegexpMethodPointcut"> <property name="pattern" value="services.*" /> </bean> <!—Advisor--> <bean id="loggingAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"> <property name="advice" ref="loggingInterceptor" /> <property name="pointcut" ref="loggingPointcut" /> </bean> Listing 6: Konfiguration von Advice, Pointcut und Advisor In Listing 6 ist der Pointcut loggingPointcut definiert, der für jede Methode in Package services angewandt wird. Neben dem Pointcut ist noch die Verschmelzung mit dem Advice zu konfigurieren. Dieser Aspekt wird in Spring als Advisor gehandhabt. Über die <property>-Tags des Advisor werden der Pointcut und Advice in Listing 6 vereinigt. 3.6 Model View Controller Für den Aufbau einer webbasierten Anwendung wird in Spring das Modul MVC eingesetzt. Dieses beruht auf dem Entwurfsmuster Model View Controller (MVC). Stellt ein Benutzer eine Anfrage, wenn er z. B. alle Bücher der Bibliothek einsehen möchte, wird diese in Spring zunächst an das Dispatcher Servlet gesendet, welcher auch als Frontend-Controller bezeichnet wird. Dieser agiert, wie der Begriff schon vermuten lässt, als Verteiler von Aufgaben. Diese Weitergabe und Entgegennahme der Ergebnisse visualisiert Abbildung 1. 11 Kapitel 3: Spring Framework Abbildung 1: Anfrageverarbeitung über Dispatcher Servlet Wie jedes Servlet muss auch der Dispatcher in der web.xml-Datei deklariert werden. Über das <url-pattern>-Tag wird angegeben, welche URLs das Servlet behandeln soll. Die Webanfragen behandelt ein Controller im Auftrag des Dispatcher Servlets (siehe Nr. 3 in Abbildung 1). Zur Abwicklung der Anfrage verteilt dieser die Aufgaben an die Serviceschicht und führt die übermittelten Ergebnisse anschließend zusammen. Der Controller wird als normale Java-Klasse implementiert und im Anwendungskontext als Bean konfiguriert. Für die vorgestellte Bibliotheksanwendung wird eine Webseite benötigt, die alle vorhandenen Bücher anzeigt. Der BookController in Listing 7 erbt von der Klasse AbstractController. Darin wird die Methode handleRequestInternal() implementiert. In dieser wird die Methode getBooks() des BookService aufgerufen und ein ModelAndView-Objekt zurückgeben [Wa08 , Kap. 13.1]. package mvc; public class BookController extends AbstractController{ private BookService bookService; private String view = "booklist"; public BookService getBookService() { return bookService; } public void setBookService(BookService bookService) { this.bookService = bookService; } protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception { Map model = new HashMap(); model.put("books", bookService.getBooks()); return new ModelAndView(view, model); } <bean name=”/book.htm“ class=“mvc.BookController“> <property name=”bookService” ref=”bookService”/> </bean> Listing 7: Implementierung und Konfiguration eines Controllers 12 Kapitel 3: Spring Framework Des Weiteren wird der BookController in Listing 7 als Bean konfiguriert. Im Bean wird anstelle des Attributes id unter name die URL des Controllers mit vorausgesetztem Schrägstrich angegeben z. B. /book.htm. So wird jede Anfrage, die mit dieser URL an das Dispatcher Servlet gestellt wird, an diesen Controller weitergegeben. Anstelle der Verwendung der name-Eigenschaft, könnte auch ein Handler-Mapping-Bean konfiguriert werden (siehe Nr. 2 in Abbildung 1). Ein Handler-Mapping-Bean entscheidet, welcher Controller die Anfrage bearbeitet. Im Controller-Bean wird der Service, der Aufgaben ausführen soll, über Dependency Injection injiziert. Im Beispiel ist das BookService. Die Methode handleRequestInternal() zur Behandlung der Anfrage muss ein Objekt der Klasse ModelAndView zurückgeben. Dieses Objekt kapselt die View und die Modelldaten, die vom View dargestellt werden sollen. Das ModelAndView-Objekt wird vom Dispatcher Servlet an den View-Resolver weiter gegeben (siehe Nr. 5 in Abbildung 1). Der ordnet dem im ModelAndView-Objekt zurückgegebenen View-Namen eine View zu. Die View ist in den meisten Fällen eine JSP-Seite z. B. booklist.jsp. Listing 8 zeigt wie der View-Resolver über den Anwendungskontext zu konfigurieren ist. Dabei wird für JSP-Seiten InternalResourceViewResolver verwendet. Mit ihm können über die Attribute prefix und suffix an den View-Namen Werte vor- bzw. angehangen werden, so dass sich eine vollständige URL ergibt. Für das Beispiel in Listing 8 ergibt sich die URL „/WEBINF/jsp/booklist.jsp“. An diese URL leitet das Dispatcher Servlet die Anfrage weiter, um die Webseite für den Benutzer dazustellen. Die JSP-Seite die dabei aufgerufen wird findet sich in Listing 9. In der JSP-Seite wird dabei auf das Model „books“ zurückgegriffen. Somit bekommt der Benutzer eine Liste mit allen Büchern angezeigt [Wa08 , Kap. 13.1]. <bean id="viewResolver" class="org.springframework.web.servlet.view. InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/"/> <property name="suffix" value=".jsp"/> </bean> Listing 8: Konfiguration eines View-Resolvers <%@ page language="Java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html;charset=ISO-8859-1"> <title>Bücherliste</title> 13 Kapitel 3: Spring Framework </head> <body> <h1>Willkommen in der Bibliothek</h1><br> <h2>Hier sehen Sie alle verfügbaren Bücher</h2><br> <table> <tr><th>Buch</th><th>Autor</th></tr> <c:forEach items="${books}" var="book"> <tr><td><c:out value="${book.description}"/></td> <td><c:out value="${book.autor}"/></td> </tr> </c:forEach> </table> </body> </html> Listing 9: View books.jsp 3.7 Datenbankzugriff Für eine praktische Verwendung benötigt fast jede Anwendung eine Datenpersistenz. Spring integriert eine ganze Reihe an Datenzugriffs-Frameworks, die mit vielen Datenzugriffstechnologien verbunden werden können. Dies sind u. a. JDBC oder O/R Mapper wie Hibernate, iBATIS oder JPA. In diesem Unterkapitel wird die Anbindung von JDBC und als Beispiel für einen O/R Mapper JPA vorgestellt. Data Access Objects (DAO) kapseln den Datenbankzugriff in Spring. Dies geschieht unter Verwendung des Entwurfsmusters der Schablonenmethode (Template Method). Die generelle Struktur eines Datenzugriffs wird unabhängig von der verwendeten Technologie in einer abstrakten Oberklasse (Template) definiert. Bestimmte Operationen des Datenbankzugriffs können in den Unterklassen überschrieben werden, ohne die Struktur des Zugriffs zu ändern. Zu den feststehenden Teilen des Zugriffs zählen die Steuerung von Transaktionen, die Ressourcenverwaltung und die Exceptionbehandlung. Des Weiteren bietet Spring noch Supportklassen zur Vereinfachung des Zugriffs auf das Template. Den Zusammenhang zeigt Abbildung 2 [Wa08, Kap. 5.1]. Abbildung 2: Beziehung zwischen Anwendungs-DAO, DAO-Supportklasse und Schablonenklasse [Wa08, Kap. 5.1] 14 Kapitel 3: Spring Framework Diese Implementierung trennt die Datenzugriffschicht in Spring vom Rest der Anwendung. Dies hat zur Folge, dass eine konsistente Exception-Hierarchie über alle DAO-Frameworks hinweg verfügbar ist. Es werden nicht die Exceptions aus JDBC oder anderen OR-Mappern in das Spring Framework übernommen, da JDBC Exceptions universell sind und Exceptions von OR-Mapper spezifisch auf die Datenbank bzw. das Framework zu geschnitten sind. Diese Exception werden abgefangen und intern verarbeitet. Zurückgegeben werden Spring eigene Exceptions, die von RuntimeException abgeleitet sind. Für diese ist keine Behandlung notwendig. So kann sich der Programmierer den catch-Block sparen [Wa08, Kap. 5.1]. JDBC-Anbindung Grundlage für den Datenbankzugriff ist die Definition eines Datenquellen-Beans DataSource. Dafür stehen in Spring zwei Klassen zur Verfügung. DriverManagerDataSource gibt bei jeder Anfrage eine neue Verbindung zurück, während SingleConnectionDataSource immer dieselbe Verbindung wiedergibt. Da beide Verbindungen über keinen Pool zur Verbindungsverwaltung verfügen, sind sie für komplexere Anwendungen nicht geeignet. Das Beispiel in Listing 10 zeigt wie das dataSource-Bean konfiguriert wird. Das Attribut driverClassName enthält den JDBCDatendatenbanktreiber, url die Adresse der verwendete Datenbank und username und password den zugehörigen Benutzer und das Kennwort [Wa08, Kap. 5.3]. <bean id="datasource" class= "org.springframework.jdbc.datasource.SingleConnectionDataSource"> <property name=”driverClassName” value=”com.mysql.jdbc.Driver” /> <property name=”url” value=”jdbc:mysql://localhost/bib” /> <property name=”username” value=”sa” /> <property name=”password” vaue=”123” /> </bean> Listing 10: JDBC Anbindung Anschließend ist ein AnwendungsDAO zu erstellen. Dafür wird zunächst eine DAOKlasse implementiert. Diese kann von der von Spring bereitgestellten Supportklasse für JDBC-Datenbanken NamedParameterJdbcDaoSupport abgeleitet werden. Mit dieser Klasse ist neben der Bereitstellung von Standard-JDBC-Funktionen in SQL-Statements die Verwendung von benannten Parametern anstatt von indizierten Parametern möglich. Dafür werden der Methode zur Ausführung eines SQL-Statements das Statement und eine HashMap mit den Parametern des Statements übergeben. Listing 11 zeigt dies am 15 Kapitel 3: Spring Framework Beispiel für das Auslesen aller Bücher für die Bibliotheksanwendung. Diese werden mit Hilfe eines RowMappers in einer Liste von Büchern konvertiert [Wa08, Kap. 5.3]. package jdbc; public class BookDAO extends NamedParameterJdbcDaoSupport implements IBookDao{ public List getAll() { return (List) getJdbcTemplate().query( "SELECT DESCRIPTION, ISBN, AUTOR, ID FROM Book ", new BookRowMapper()); } …} Listing 11: JDBC Anfrage JPA-Anbindung Die JPA-Anbindung erfolgt sowohl in JPA direkt als auch in Spring über einen EntityManager. Des Weiteren wird für die JPA-Anbindung wieder mit einem Template gearbeitet (siehe Listing 12). Das JpaTemplate erfüllt die gleichen Aufgaben wie andere Templates für Datenbankanbindungen in Spring auch. Zudem sorgt es dafür, dass der Entity-Manager wie benötigt geschlossen und geöffnet wird und stellt weitestgehend dieselben Persistenzmethoden wie ein Entity-Manager für JPA zur Verfügung [Wa08, Kap. 5.5]. Als Entity-Manager kann ein anwendungsverwaltender oder containerverwaltender Entity-Manager eingesetzt werden. Der Unterschied zwischen beiden liegt in der Konfiguration. Diese erfolgt für anwendungsverwaltende Entity-Manager über eine Konfigurationsdatei namens persistenxe.xml, bei containerverwaltender Konfiguration im Spring-Anwendungskontext. Listing 12 zeigt wie ein containterverwaltender EntityManager in Spring mithilfe von LocalContainerEntityManagerFactoryBean konfiguriert wird. <bean id=”jpaTemplate” class=”org.springframework.orm.jpa.jpaTemplate”> <property name=”entityManagerFactory” ref=”entityManagerFactory” /> </bean> <bean id="entityManagerFactory" class="org.springframework.orm.jpa. LocalContainerEntityManagerFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="jpaVendorAdapter"> <bean class="org.springframework.orm.jpa.vendor. TopLinkJpaVendorAdapter"> <property name="showSql" value="true" /> <property name="generateDdl" value="false" /> <property name="database" value="MYSQL" /> </bean> </property> 16 Kapitel 3: Spring Framework </bean> Listing 12: Konfiguration einer containerverwaltenden EntityManagerFactory-Bean Die Konfiguration der Datenquelle, welche vom Entity-Manager benutzt wird, erfolgt über die Eigenschaft dataSource und ist vergleichbar mit der Konfiguration in Listing 10. Über die Eigenschaft jpaVendorAdapter können Details zur verwendeten JPAImplementierung angeführt werden. In diesem Fall ist das TopLink Essentials. Nach der Konfiguration des Entity-Managers wird die DAO-Klasse eingerichtet. Wie bereits bemerkt, bietet Spring dafür Support-Klassen an, von denen die selbst zu erstellende DAO-Klasse erbt. Für die JPA-Anbindung wird JpaDaoSupport verwendet. Diese Verwendung zeigt Listing 13 am Beispiel der Klasse JpaUserDAO. Für die dauerhafte Speicherung eines Objekts in der Datenbank muss die Methode persist des JpaTemplate Objekts aufgerufen werden [Wa08, Kap. 5.5]. public class JpaUserDAO extends JpaDaoSupport implements IUserDAO { public void createUser(User user) throws CreateFailureException { getJpaTemplate().persist(user); } } Listing 13: Klasse JpaUserDAO 3.8 Sicherheit Das Sicherheits-Framework in Spring stellt umfangreiche Sicherheitslösungen für die Authentifizierung und Autorisierung auf Webanfrage- und Methodenaufrufebene zur Verfügung. Spring Security nutzt dafür DI und AOP. Mit Hilfe des AuthentificationManagers erfolgt die Überprüfung des Benutzernamens und des dazugehörigen Passworts. Nach erfolgreicher Authentifizierung wird über den Access-DecisionManager kontrolliert, ob die Anfrage oder Methode von Benutzer ausgeführt werden darf [Wa08, Kap. 7.1]. Authentifizierung Der Authentication-Manager verifiziert die eingegebenen Benutzerdaten über die Methode authenticate(). Bei erfolgreicher Authentifizierung gibt die Methode ein Authentication-Objekt zurück, das Information über den Benutzer und dessen Rechte enthält. Andernfalls wird eine AuthenticationException ausgelöst. Für die meisten Fälle der Benutzerprüfung wird der ProviderManager, eine Implementierung des 17 Kapitel 3: Spring Framework AuthenticationManager, benutzt. Dabei kann der Benutzer gegenüber mehreren Quellen zur Identitätsverwaltung authentifiziert werden. Dazu werden die Benutzereingaben an Provider weitergegeben. Der ProviderManager erhält im Attribut provider eine Liste der Authentifizierungsdienste. Im Allgemeinen wird nur ein Dienst benötigt. Für die Authentifizierung mit Hilfe einer Datenbank bietet Spring Security den DaoAuthenticationProvider. Die Konfiguration des ProviderManagers findet sich im oberen Teil von Listing 14. Dieser kann über die authenticationManager referenziert werden und beinhaltet einen AuthenticationProvider, der weiter unten definiert ist [Wa08, Kap. 7.2]. <!--ProviderManager--> <bean id="authenticationManager" class="org.springframework.security.providers.ProviderManager"> <property name="providers"> <list> <ref bean="authenticationProvider" /> </list> </property> </bean> <!-- Authentication--> <bean id="authenticationProvider" class="org.springframework. security.providers.dao.DaoAuthenticationProvider"> <property name="userDetailsService" ref="authenticationDAO" /> </bean> <bean id="authenticationDAO" class="org.springframework.security.userdetails.jdbc.JdbcDaoImpl"> <property name="dataSource" ref="dataSource" /> <property name="usersByUsernameQuery"> <value> SELECT USERNAME, PASSWORD, ENABLE as enabled FROM USER WHERE USERNAME = ? </value> </property> <property name="authoritiesByUsernameQuery"> <value> SELECT u.USERNAME as username, AUTORITY as authority FROM AUTORITY a , USER u WHERE a.USERNAME = u.USERNAME AND u.USERNAME = ? </value> </property> </bean> Listing 14: Konfiguration des Authentification Managers Des Weiteren wird in Listing 14 das AuthenticationDAO konfiguriert, das eine Instanzierung von JdbcDaoImpl ist. Diese erwartet, dass die Benutzerangaben in den Tabellen Users und Authorities mit den Attributen username, password und enabled bzw. username und authority vorliegt. Falls dies nicht der Fall ist, können wie in Listing 18 Kapitel 3: Spring Framework 14 über die Eigenschaften usersByUsernameQuery und authoritiesByUsernameQuery die entsprechenden SQL-Statements angegeben werden. Autorisierung Als nächster Schritt nach der Authentifizierung erfolgt die Autorisierung. Dafür wird zunächst festgelegt, wie die Entscheidung für den Zugriff auf das angefragte Objekt getroffen werden soll. Es gibt dazu drei Implementierungen des AccessDecisionManager. Die Konfiguration für alle ist gleich. Listing 15 zeigt den Typ UnianimousBased, der den Zugriff gewährt, wenn alle Abstimmenden für den Zugriff gestimmt haben. Als nächstes werden eine oder mehrere Instanzen konfiguriert, welche kontrollieren, ob die Benutzerrechte mit den Rechten, die für den Zugriff auf das Objekt benötigt werden, übereinstimmen. Jede Instanz implementiert dabei das Interface AccessDecisionVoter. In Listing 15 wird eine Instanz roleVoter konfiguriert. Diese stimmt für den Zugriff, wenn der Benutzer zur Rolle mit dem Präfix „ROLE_“ gehört [Wa08, Kap. 7.3]. Weitere Möglichkeiten, die der Authentication-Manager bietet sind PasswortVerschlüsselung, Caching von Benutzerdaten und Authentifizierung gegenüber LDAP [Wa08, Kap. 7.2]. <!--RoleVoter--> <bean id="roleVoter" class="org.springframework.security.vote.RoleVoter"> <property name="rolePrefix" value="ROLE_" /> </bean> <!--Access-Decision-Manager--> <bean id="accessDecisionManager" class="org.springframework.security.vote.UnanimousBased"> <property name="decisionVoters"> <list> <ref bean="roleVoter"/> </list> </property> </bean> Listing 15: Konfiguration des AccessDecisionManagers Zuordnung zu Methoden <!--SecurityInterceptor--> <bean id="securityInterceptor" class="org.springframework.security. method.aopalliance.MethodSecurityInterceptor"> <property name="authenticationManager"> <ref bean="authenticationManager"/></property> 19 Kapitel 3: Spring Framework <property name="accessDecisionManager"> <ref bean="accessDecisionManager"/></property> <property name="objectDefinitionSource"> <value> services.BookService.getBooks*=ROLE_USER services.LendingService.getLendings*=ROLE_USER services.UserService.getUsers*=ROLE_ADMIN </value> </property> </bean> <!--AutoProxy--> <bean id="autoProxyCreator" class="org.springframework.aop. framework.autoproxy.BeanNameAutoProxyCreator"> <property name="interceptorNames"> <list> <value>securityInterceptor</value> </list> </property> <property name="beanNames"> <list> <value>services.BookService</value> <value>services.LendingService</value> <value>services.UserService</value> </list> </property> </bean> Listing 16: Zuordnung und Konfiguration von Zugriffrechten zu Methoden Listing 16 zeigt wie Zugriffsrechte für Methoden vergeben werden. Dazu wird ein securityInterceptor konfiguriert, dem der AuthenticationManager und AccessDecisionManager zugeordnet wird. Über die Eigenschaft objectDefinitionSource werden einzelnen Methoden Rollen zugeordnet, z. B. darf nur der Admin die Methode getUsers() aufrufen und damit alle Benutzer einsehen. Außerdem dürfen Benutzer mit der Rolle ROLE_USER Bücher und Ausleihen aufrufen ebenso wie der Admin. Neben der Zugriffrechtezuordnung muss zudem ein Proxy für den securityInterceptor definiert werden. Somit werden bei Aufruf der Beans BookService, LendingService und UserService zunächst über den Proxy die Zugriffsrechte des Benutzers überprüft. Hier findet also AOP ihre Anwendung. Wenn der Benutzer nicht authentifiziert ist, wird eine AuthentificationException ausgelöst. Eine AccessDecisionException wird geworfen, wenn der Benutzer nicht die erforderlichen Rechte aufweist [Wa08, Kap. 7.6]. 20 Kapitel 4: Vergleich Spring und Java EE 4 Vergleich Spring und Java EE Spring ist entstanden, um die Entwicklung von Java EE-Anwendungen zu vereinfachen ohne auf Vorteile der Java-EE-Umgebung verzichten zu müssen. Aus diesem Grund wird es auch als Lightweight-Framework bezeichnet. Dieses leichtgewichtige Modell baut auf POJOs (Plain Old Java Objects) auf, also auf „ganz normalen“ Objekten. Dennoch verlangt auch Spring eine gründliche Auseinandersetzung mit dem Framework damit diese angewandt werden kann. Java EE ist neben seiner Komplexität auch sehr flexibel. Deshalb werden größere Anwendungen in Java EE implementiert. Mittlerweile unterstützt auch Java EE durch die Spezifikation EJB 3.0 ein POJO-basiertes Programmiermodell, das die Anwendung schlichter macht [Wo07, Kap. 1.3]. Java EE und Spring nutzen beide Dependency Injection. In Spring erfolgt die Konfiguration meistens über Deskriptoren also XML. Dahingegen werden in Java EE meistens Annotationen verwendet. Deskriptoren können aber für das Überschreiben von Methoden verwendet werden. Ebenso ist es in Spring möglich Annotationen zu benutzen. Java EE verwendet für die Geschäftslogik EJBs, die nur zusammen mit einem Java-Server lauffähig sind. In Spring wird die Geschäftslogik unabhängig vom Server verwaltet. Ein weiteres Ziel von Spring ist die Integration anderer Frameworks zu vereinfachen [Wo07, Kap 1.3]. Das Interceptor-Konzept für EJB 3.0 erlaubt die Behandlung von Querschnittsbelangen wie Sicherheitsaspekten. Dies ist die erste und einfache Form von AOP in einer JavaEE-Anwendung. In Spring stellt AOP einer Grundfunktionalität dar. Dabei werden Methoden Pointcuts unterstützt. Für einen größeren Funktionsumfang kann AspectJ in Spring eingebunden werden. Somit bietet Spring eine umfangreichere AOP Umsetzung als Java EE an [Wo07, Kap 1.3]. Bei Java EE handelt es sich um einen Standard. Dies hat zum Vorteil, dass Herstellerunabhängigkeit erreicht wird. Des Weiteren ist viel Wissen über Java EE auf dem Markt verfügbar. Das reduziert den Einarbeitungsaufwand und es können schneller Lösungen für Probleme gefunden werden. Diese Vorteile können auch in einem Spring Projekt genutzt werden. Dazu muss Spring als Abstraktion über eine Java-EEUmgebung verwendet werden [Wo07, Kap 1.3]. 21 Kapitel 5: Zusammenfassung und Ausblick 5 Zusammenfassung und Ausblick In dieser Arbeit wurde zunächst der Begriff Framework näher erläutert. Dabei handelt es sich um ein Rahmenwerk, das für die Softwareentwicklung bestimmte Funktionalitäten zur Verfügung stellt. Diese können genutzt und erweitert werden. So wird vermieden, dass wieder verwendbare Funktionalitäten jedes Mal neu implementiert werden müssen. Anschließend wurden Aspektorientierte die Programmiertechniken Programmierung erläutert. Dependency Dieses sind Injektion die und wichtigsten Programmiertechniken, die im Spring Framework verwendet werden. In Kapitel 3 wurde dieses Framework anhand einer Bibliotheksanwendung näher dargelegt. Dabei wurden zunächst Spring-Beans und der Spring-Container anhand von Beispielen veranschaulicht. Spring-Beans stellen Anwendungsobjekte dar, die über eine Konfigurationsdatei erstellt und über den Spring-Container verwaltet werden. Im Anschluss daran wurden Dependency Injektion und AOP in Spring vorgestellt. In Spring können die Typen Setter- und Constructor-Injektion verwendet werden. Nachfolgend wurde die Konfiguration einer Datenbankanbindung über JDBC und JPA beschrieben. Außerdem wird für die Gewährleistung der Sicherheit im System eine verlässige Authentifizierung von Benutzer benötigt. Dazu werden in Spring auch Aspekte eingesetzt. Nach deren Vorstellung, schließt die Arbeit mit einem Vergleich zwischen den Frameworks Spring und Java EE. Dabei wurden herausgestellt, dass Spring als leichtgewichtige Entwicklung zu Java EE entwickelt wurde. Mittlerweile ist in Java EE durch die Spezifikation EJB 3.0 die Java-Entwicklung einfacher. In beiden Frameworks kann Dependency Injection und AOP eingesetzt werden. In dieser Arbeit wurden einige Funktionen bzw. Module des Spring Frameworks näher dargelegt. Diese können je nach Bedarf in das Framework eingebunden werden. Sie vereinfachen die Entwicklung von Java-Anwendungen und sind für den Entwickler eine gute Unterstützung. Die Funktionen bzw. Module, die in dieser Arbeit nicht vorgestellt wurden, sind in zahlreichen Büchern über das Spring Framework und auf Webseiten dokumentiert. Ausblickend lässt sich festhalten, dass aufgrund des hohen Interesses, das am Spring Framework derzeit besteht, davon ausgegangen werden kann, dass es auch zukünftig verwendet und weiterentwickelt wird. 22 Anhang A: Klassendiagramm Anhang A: Klassendiagramm Abb. 1: Klassendiagramm Bibliothek 23 Anhang B: Quellcode Anhang B: Quellcode LoggingAdvice.java package aop; import java.util.Arrays; import import import import org.aopalliance.intercept.MethodInterceptor; org.aopalliance.intercept.MethodInvocation; org.apache.commons.logging.Log; org.apache.commons.logging.LogFactory; /** * Logging Advice, den zu den zugeordneten Methoden Tracinginformationen * wie Methodenname, Parameter und Startzeit hinzufügt * @author Birgit * */ public class LoggingAdvice implements MethodInterceptor { private static long invocationID = 0; private static synchronized long getInvocationID() { return invocationID++; } /** * Methode, den zu den zugeordneten Methoden Tracinginformationen * wie Methodenname, Parameter und Startzeit hinzufügt * @param methodInvocation Methode, die erweitert wird */ public Object invoke(MethodInvocation methodInvocation) throws Throwable { Object result; long startMillis; long endMillis; long id; Log log; log = LogFactory.getLog(getClassName(methodInvocation)); id = getInvocationID(); startMillis = System.currentTimeMillis(); logMethodStart(log, methodInvocation, id, startMillis); try { result = methodInvocation.proceed(); } catch (Throwable throwable) { endMillis = System.currentTimeMillis(); logMethodException(log, methodInvocation, id, startMillis, endMillis, throwable); throw throwable; } endMillis = System.currentTimeMillis(); logMethodEnd(log, methodInvocation, id, startMillis, endMillis, result); return result; } /** 24 Anhang B: Quellcode * ermittelt die Parameterwerte der aufgerufenen Methode * @param methodInvocation Methodenaufruf * @return gibt die Parameterwerte zurück */ private static String getArguments(MethodInvocation methodInvocation) { String result; Object[] args; args = methodInvocation.getArguments(); if (args == null) { result = "[]"; } else { result = Arrays.asList(args).toString(); } return result; } /** * ermittelt den Methodennamen der aufgerufenen Methode * @param methodInvocation Methodenaufruf * @return Methodenname */ private static String getMethodName(MethodInvocation methodInvocation) { return methodInvocation.getMethod().getName(); } /** * ermittelt den Klassenname der aufgerufenen Methode * @param methodInvocation Methodenaufruf * @return Klassename */ private static String getClassName(MethodInvocation methodInvocation) { String result; Object target; target = methodInvocation.getThis(); if (target == null) { result = "null"; } else { result = target.getClass().getName(); } return result; } /** * gibt Logginginformation zur Exception des Methodenaufrufes aus * @param log Logger * @param methodInvocation Methodenaufruf * @param id Aufrufid * @param startMillis Startzeit der Methode * @param endMillis Endzeit der Methode * @param throwable Exception */ private void logMethodException(Log log, MethodInvocation methodInvocation, long id, long startMillis, long endMillis, Throwable throwable) { if (log.isInfoEnabled()) { StringBuffer sb = new StringBuffer(); sb.append("MethodInvocation " + id + " EXCEPTION"); 25 Anhang B: Quellcode sb.append("\n Class : " + getClassName(methodInvocation)); sb.append("\n Method : " + getMethodName(methodInvocation)); sb.append("\n Arguments : " + getArguments(methodInvocation)); sb.append("\n Start:" + startMillis + " ms"); sb.append("\n End:" + endMillis + " ms"); sb.append("\n Duration : " + (endMillis - startMillis) + " ms"); sb.append("\n Result : " + throwable); log.info(sb); } } /** * gibt Logginginformation zum Start des Methodenaufrufes aus * @param log Logger * @param methodInvocation Methodenaufruf * @param id Aufrufid * @param startMillis Startzeit der Methode * @param endMillis Endzeit der Methode */ private void logMethodStart(Log log, MethodInvocation methodInvocation, long id, long startMillis) { if (log.isInfoEnabled()) { StringBuffer sb = new StringBuffer(); sb.append("MethodInvocation " + id + " START"); sb.append("\n Class : " + getClassName(methodInvocation)); sb.append("\n Method : " + getMethodName(methodInvocation)); sb.append("\n Arguments : " + getArguments(methodInvocation)); sb.append("\n Start : " + startMillis + " ms"); log.info(sb); } } /** * gibt Logginginformation zum Ende des Methodenaufrufes aus * @param log Logger * @param methodInvocation Methodenaufruf * @param id Aufrufid * @param startMillis Startzeit der Methode * @param endMillis Endzeit der Methode * @param result Ergebnis des Methodenaufrufs */ private static void logMethodEnd(Log log, MethodInvocation methodInvocation, long id, long startMillis, long endMillis, Object result) { if (log.isInfoEnabled()) { StringBuffer sb = new StringBuffer(); sb.append("MethodInvocation " + id + " END"); sb.append("\n Class : " + getClassName(methodInvocation)); sb.append("\n Method : " + 26 Anhang B: Quellcode getMethodName(methodInvocation)); sb.append("\n Arguments : " + getArguments(methodInvocation)); sb.append("\n Start : " + startMillis + " ms"); sb.append("\n End:" + endMillis + " ms"); sb.append("\n Duration : " + (endMillis - startMillis) + " ms"); sb.append("\n Result : " + result); log.info(sb); } } } Book.java package bib; /** * Klasse Buch * @author Birgit * */ public class Book{ /** * BuchID */ protected int id; /** * Beschreibung/Titel des Buchs */ protected String description; /** * ISBN des Buchs */ protected String isbn; /** * Author des Buchs */ protected String autor; public Book(String description, String isbn, String autor) { this.description = description; this.isbn = isbn; this.autor = autor; } public int getId(){return id;} public void setId(int id){ this.id = id; } public String getDescription(){return description;} public void setDescription(String description){ this.description = description; } public String getISBN(){return isbn;} public void setISBN(String nr){isbn = nr;} public String getAutor(){return autor;} public void setAutor(String a){autor = a;} } 27 Anhang B: Quellcode Exemplar.java package bib; /** * Klasse Exemplar (Exemplar eines Buches) * @author Birgit * */ public class Exemplar { /** * ID des Exemplars */ protected int id; /** * Buch, das zu dem Exemplar gehört */ protected Book book; /** * mögliche Ausleihe des Exemplars */ protected Lending lending; public Exemplar(int id, Book book) { super(); this.id = id; this.book = book; } public int getId() { return id; } public void setId(int id) { this.id = id; } public Book getBook() { return book; } public void setBook(Book b) { book = b; } public Lending getLending() { return lending; } public void setLending(Lending l) { lending = l; } } Lending.java 28 Anhang B: Quellcode package bib; import java.util.Date; /** * Klasse Ausleihe * @author Birgit * */ public class Lending { /** * ID der Ausleihe */ protected int id; /** * Startdatum der Ausleihe */ protected Date date; /** * Benutzer, des das Exemplar ausgeliehen hat */ protected User user; /** * Exemplar, das ausgeliehen wurde */ protected Exemplar exemplar; public Lending(int id, Date date, User user, Exemplar exemplar) { super(); this.id = id; this.date = date; this.user = user; this.exemplar = exemplar; } public int getId(){return id;} public void setId(int id){this.id = id;} public Date getDate(){return date;} public void setDate(Date d){date = d;} public User getUser(){return user;} public void setUser(User u){ user = u; } public Exemplar getExemplar(){return exemplar;} public void setExemplar(Exemplar e){ exemplar = e; } } User.java package bib; import java.util.ArrayList; import java.util.Collection; /** * Klasse Benutzer 29 Anhang B: Quellcode * @author Birgit * */ public class User { /** * BenutzerID */ protected int id; /** * Anmeldename/User */ protected String username; /** * Vorname */ protected String firstname; /** * Nachname */ protected String lastname; /** * Password */ protected String password; protected Collection<Lending> lending = new ArrayList<Lending>(); public User (String firstname,String lastname){ this.firstname = firstname; this.lastname = lastname; } public int getId(){return id;} public void setId(int id){this.id = id;} public String getFirstname(){return firstname;} public void setFirstname(String firstname){ this.firstname = firstname; } public String getLastname(){return lastname;} public void setLastname(String lastname){ this.lastname = lastname; } public Collection<Lending> getLending(){return lending;} public void setLending(Collection<Lending> coll){ lending = coll; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } 30 Anhang B: Quellcode public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } } IBookDAO.java package dao; import java.util.List; import bib.Book; /** * Schnittstelle für DAO für Bücher * @author Birgit * */ public interface IBookDAO { /** * gibt Liste aller Bücher zurück * @return Bücherliste */ List<Book> getAll(); /** * gibt alle Bücher zur eingegeben Beschreibung zurück * @param description Beschreibung * @return Bücherliste */ List<Book> getByDescription(String description); /** * speichert das eingegebene Buch * @param book Buch * @return gespeicherte Buch */ Book save(Book book); /** * löscht alle Bücher zur eingegeben Beschreibung * @param description Beschreibung */ void deleteByDescription(String description); /** * gibt ein Buch zur eingegebenen ID zurück * @param id * @return Buch */ Book getByID(int id); /** * löscht das Bücher zur eingegeben ID * @param id */ void deleteByID(int id); /** * ändert das eingegebene Buch * @param book Buch */ void update(Book book); } 31 Anhang B: Quellcode IExemplarDAO.java package dao; import java.util.List; import bib.Exemplar; /** * Schnittstelle für DAO Exemplar * @author Birgit * */ public interface IExemplarDAO { /** * gibt Exemplare zur BuchID zurück * @param bookid BuchID * @return Exemplarliste */ List<Exemplar> getByBook(String bookid); /** * speichert ein Exemplar * @param exemplar Exemplar * @return gespeichertes Exemplar */ Exemplar save(Exemplar exemplar); /** * gibt zur ID Exemplar zurück * @param id * @return Exemplar */ Exemplar getByID(int id); /** * löscht Exemplar zur ID * @param id */ void deleteByID(int id); /** * ändert ein Exemplar * @param exemplar Exemplar */ void update(Exemplar exemplar); } ILendingDAO.java package dao; import java.util.List; import bib.Lending; /** * Schnittstelle für DAO Ausleihe * @author Birgit * */ public interface ILendingDAO { /** * speichert eine Ausleihe * @param lending Ausleihe * @return gespeicherte Ausleihe 32 Anhang B: Quellcode */ public Lending save(Lending lending); /** * gibt Ausleihen zu einer Benutzerid zurück * @param userid Benutzerid * @return Ausleihenliste */ public List getByUserID(int userid); /** * gibt alle Ausleihen zurück * @return Ausleihenliste */ public List getAll(); } IUserDAO.java package dao; import java.util.List; import bib.User; /** * Schnittstelle DAO Benutzer * @author Birgit * */ public interface IUserDAO { /** * gibt den Benutzer zum Anmeldenamen zurück * @param username Anmeldename * @return Benutzer */ User getByName(String username); /** * speichert einen Benutzer * @param user Benutzer * @return gespeicherter Benutzer */ User save(User user); /** * löscht einen Benutzer zum Anmeldenamen * @param name Anmeldename */ void deleteByName(String name); /** * gibt einen Benutzer zur ID zurück * @param id * @return Benutzer */ User getByID(int id); /** * löscht eine Benutzer zur ID * @param id */ void deleteByID(int id); /** * gibt alle Benutzer zurück * @return Benutzerliste */ List<User> getAll(); } 33 Anhang B: Quellcode bookDAO.java package jdbc; import java.sql.ResultSet; import java.sql.SQLException; import java.util.List; import javax.sql.DataSource; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSourc e; import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcDaoSupport; import bib.Book; import dao.IBookDAO; /** * DAO für Bücher * @author Birgit * */ public class BookDAO extends NamedParameterJdbcDaoSupport implements IBookDAO { /** * innere Klasse die eine RowMapper für Bücher zur Verfügung stellt * */ private static final class BookRowMapper implements RowMapper { /** * Methode zu Mappen von Bücher * @param rs Ergebnis der SQL-Abfrage * @param rowNum Anzahl Zeilen * @return Buchobjekt */ public Object mapRow(ResultSet rs, int rowNum) throws SQLException { Book Book = new Book(rs.getString(1),rs.getString(2),rs.getString(3)); Book.setId(rs.getInt(4)); return Book; } } public BookDAO(){ } /** * Konstruktor * @param dataSource Datenquelle */ public BookDAO(DataSource dataSource) { setDataSource(dataSource); } /** * gibt alle Bücher zur eingegeben Beschreibung zurück * @param description Beschreibung * @return Bücherliste 34 Anhang B: Quellcode */ public List getByDescription(String description) { MapSqlParameterSource params = new MapSqlParameterSource(); params.addValue("description", description); return getNamedParameterJdbcTemplate() .query( "SELECT DESCRIPTION, ISBN, AUTOR, ID FROM Book WHERE DESCRIPTION=:description", params, new BookRowMapper()); } /** * gibt ein Buch zur eingegebenen ID zurück * @param id * @return Buch */ public Book getByID(int id) { return (Book) getNamedParameterJdbcTemplate(). queryForObject( "SELECT DESCRIPTION, ISBN, AUTOR, ID FROM Book WHERE ID=:id", new MapSqlParameterSource("id", id), new BookRowMapper()); } /** * speichert das eingegebene Buch * @param book Buch * @return gespeicherte Buch */ public Book save(Book Book) { initTemplateConfig(); getNamedParameterJdbcTemplate().update( "INSERT INTO Book (DESCRIPTION, ISBN, AUTOR) VALUES(:description, :isbn, :autor)", new BeanPropertySqlParameterSource(Book)); return Book; } /** * löscht alle Bücher zur eingegeben Beschreibung * @param description Beschreibung */ public void deleteByDescription(String description) { getNamedParameterJdbcTemplate().update( "DELETE FROM Book WHERE DESCRIPTION=:description", new MapSqlParameterSource("description", description)); } /** * löscht das Bücher zur eingegeben ID * @param id */ public void deleteByID(int id) { getNamedParameterJdbcTemplate().update("DELETE FROM Book WHERE ID=:id", new MapSqlParameterSource("id", id)); } /** * ändert das eingegebene Buch * @param book Buch */ public void update(Book Book) { getNamedParameterJdbcTemplate() 35 Anhang B: Quellcode .update( "UPDATE Book SET DESCRIPTION=:description,ISBN=:isbn,AUTOR=:autor WHERE ID=:id", new BeanPropertySqlParameterSource(Book)); } /** * gibt Liste aller Bücher zurück * @return Bücherliste */ public List getAll() { return (List) getJdbcTemplate().query( "SELECT DESCRIPTION, ISBN, AUTOR, ID FROM Book ", new BookRowMapper()); } } ExemplarDAO.java package jdbc; import java.sql.ResultSet; import java.sql.SQLException; import java.util.List; import javax.sql.DataSource; import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcDaoSupport; import bib.Book; import bib.Exemplar; import dao.IBookDAO; /** * Klasse für DAO Exemplar * @author Birgit * */ public class ExemplarDAO extends NamedParameterJdbcDaoSupport implements dao.IExemplarDAO{ /** * BuchDAO */ private static IBookDAO bookDAO; /** * * innere Klasse, die einen RowMapper für Exemplar zur Verfügung stellt * */ private static final class ExemplarRowMapper implements RowMapper { /** * Methode zu mappen einer SQL Abfrage auf Exemplar-Objekte * @param rs Ergebnis der SQL-Abfrage * @param rowNum Anzahl Zeilen * @return Exemplarobjekt */ public Object mapRow(ResultSet rs, int rowNum) throws 36 Anhang B: Quellcode SQLException { Book book = bookDAO.getByID(rs.getInt(2)); Exemplar exemplar = new Exemplar(rs.getInt(1),book); return exemplar; } } public ExemplarDAO() { } /** * Konstruktor * @param dataSource Datenquelle */ public ExemplarDAO(DataSource dataSource) { setDataSource(dataSource); } /** * gibt Exemplare zur BuchID zurück * @param bookid BuchID * @return Exemplarliste */ public List<Exemplar> getByBook(String bookid){ MapSqlParameterSource params = new MapSqlParameterSource(); params.addValue("bookid", bookid); return getNamedParameterJdbcTemplate() .query( "SELECT ID, BOOKID FROM Exemplar WHERE BOOKID=:bookid", params, new ExemplarRowMapper()); } /** * speichert ein Exemplar * @param exemplar Exemplar * @return gespeichertes Exemplar */ public Exemplar save(Exemplar exemplar){ initTemplateConfig(); getJdbcTemplate() .update( "INSERT INTO Exemplar (ID, BOOKID) VALUES(?, ?)", new Object[] {exemplar.getId(), exemplar.getBook().getId()}); return exemplar; } /** * gibt zur ID Exemplar zurück * @param id * @return Exemplar */ public Exemplar getByID(int id){ return (Exemplar) getNamedParameterJdbcTemplate(). queryForObject( "SELECT ID, BOOKID FROM Exemplar WHERE ID=:id", new MapSqlParameterSource("id", id), new ExemplarRowMapper()); } /** * löscht Exemplar zur ID * @param id 37 Anhang B: Quellcode */ public void deleteByID(int id){ getNamedParameterJdbcTemplate().update("DELETE FROM Exemplar WHERE ID=:id", new MapSqlParameterSource("id", id)); } /** * ändert ein Exemplar * @param exemplar Exemplar */ public void update(Exemplar exemplar){ getJdbcTemplate() .update( "UPDATE Exemplar SET ID=?,BOOKID=? WHERE ID=?", new Object[] {exemplar.getId(), exemplar.getBook().getId()}); } public IBookDAO getBookDAO() { return bookDAO; } public void setBookDAO(IBookDAO bookDAO) { this.bookDAO = bookDAO; } } LendingDAO.java package jdbc; import java.sql.ResultSet; import java.sql.SQLException; import java.util.List; import javax.sql.DataSource; import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.SqlParameter; import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcDaoSupport; import bib.User; import bib.Exemplar; import bib.Lending; import dao.IExemplarDAO; import dao.IUserDAO; /** * Klasse für DAO Ausleihe * @author Birgit * */ public class LendingDAO extends NamedParameterJdbcDaoSupport implements dao.ILendingDAO { /** * Exemplar DAO 38 Anhang B: Quellcode */ private static IExemplarDAO exemplarDAO; /** * Benutzer DAO; */ private static IUserDAO userDAO; /** * innere Klasse, die RowMapper für Ausleihen zur Verfügung stellt * */ private static final class LendingRowMapper implements RowMapper { /** * Methode zum Mappen von SQL-Ergebnis auf AusleihObjekte * @param rs Ergebnis der SQL-Abfrage * @param rowNum Anzahl Zeilen * @return Ausleihobjekt */ public Object mapRow(ResultSet rs, int rowNum) throws SQLException { User user = userDAO.getByID(rs.getInt(3)); Exemplar exemplar = exemplarDAO.getByID(rs.getInt(4)); Lending lending = new Lending(rs.getInt(1),rs.getDate(2), user, exemplar); return lending; } } public LendingDAO() { super(); } public LendingDAO(DataSource dataSource, IExemplarDAO exemplarDAO, IUserDAO userDAO) { setDataSource(dataSource); this.exemplarDAO = exemplarDAO; this.userDAO = userDAO; } /** * speichert eine Ausleihe * @param lending Ausleihe * @return gespeicherte Ausleihe */ public Lending save(Lending lending) { initTemplateConfig(); getJdbcTemplate() .update( "INSERT INTO Lending (ID, DATE, USERID, EXEMPLARID) VALUES(?, ?, ?, ?)", new Object[] {lending.getId(), lending.getDate(), lending.getUser().getId(), lending.getExemplar().getId()}); return lending; } /** * gibt Ausleihen zu einer Benutzerid zurück 39 Anhang B: Quellcode * @param userid Benutzerid * @return Ausleihenliste */ public List getByUserID(int userid) { MapSqlParameterSource params = new MapSqlParameterSource(); params.addValue("userid", userid); return getNamedParameterJdbcTemplate() .query( "SELECT ID, DATE, USERID, EXEMPLARID FROM Lending WHERE USERID=:userid", params, new LendingRowMapper()); } /** * gibt alle Ausleihen zurück * @return Ausleihenliste */ public List getAll() { MapSqlParameterSource params = new MapSqlParameterSource(); return getNamedParameterJdbcTemplate() .query( "SELECT ID, DATE, USERID, EXEMPLARID FROM Lending", params, new LendingRowMapper()); } public IExemplarDAO getExemplarDAO() { return exemplarDAO; } public void setExemplarDAO(IExemplarDAO exemplarDAO) { this.exemplarDAO = exemplarDAO; } public IUserDAO getUserDAO() { return userDAO; } public void setUserDAO(IUserDAO userDAO) { this.userDAO = userDAO; } } UserDAO.java package jdbc; import import import import import java.sql.ResultSet; java.sql.SQLException; java.util.HashMap; java.util.List; java.util.Map; import javax.sql.DataSource; import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcDaoSupport; 40 Anhang B: Quellcode import bib.User; import dao.IUserDAO; /** * Klasse für DAO Benutzer * @author Birgit * */ public class UserDAO extends NamedParameterJdbcDaoSupport implements IUserDAO{ /** * innere Klasse, die RowMapper für Benutzer zur Verfügung stellt * */ private static final class UserResultSetRowMapper implements RowMapper { /** * Methode zum Mappen von SQL-Ergebnis auf BenutzerObjekte * @param rs Ergebnis der SQL-Abfrage * @param rowNum Anzahl Zeilen * @return Benutzerobjekt */ public Object mapRow(ResultSet rs, int rowNum) throws SQLException { User user = new User( rs.getString(3),rs.getString(4)); user.setId(rs.getInt(1)); user.setUsername(rs.getString(2)); return user; } } public UserDAO() { super(); } public UserDAO(DataSource dataSource) { setDataSource(dataSource); } /** * löscht eine Benutzer zur ID * @param id */ public void deleteByID(int id) { Map parameters = new HashMap(); parameters.put("id", id); getNamedParameterJdbcTemplate().update ("DELETE FROM USER WHERE ID=?", parameters); } /** * gibt einen Benutzer zur ID zurück * @param id * @return Benutzer */ public User getByID(int id) { Map parameters = new HashMap(); parameters.put("ID", id); return (User) getNamedParameterJdbcTemplate().queryForObject( "SELECT * FROM USER WHERE ID = (:ID)" , parameters 41 Anhang B: Quellcode , new UserResultSetRowMapper()); } /** * gibt den Benutzer zum Anmeldenamen zurück * @param username Anmeldename * @return Benutzer */ public User getByName(String username) { Map parameters = new HashMap(); parameters.put("USERNAME", username); return (User) getNamedParameterJdbcTemplate() .queryForObject( "SELECT * FROM USER WHERE USERNAME= (:USERNAME)", parameters, new UserResultSetRowMapper()); } /** * löscht einen Benutzer zum Anmeldenamen * @param name Anmeldename */ public void deleteByName(String name) { getJdbcTemplate().update("DELETE FROM USER WHERE USERNAME=?", new Object[] { name }); } /** * speichert einen Benutzer * @param user Benutzer * @return gespeicherter Benutzer */ public User save(User user) { Map parameters = new HashMap(); parameters.put("firstname", user.getFirstname()); parameters.put("lastname", user.getLastname()); parameters.put("username", user.getUsername()); parameters.put("password", user.getPassword()); getNamedParameterJdbcTemplate().update( "INSERT INTO USER(FIRSTNAME,LASTNAME,USERNAME,PASSWORD) " + "VALUES(:firstname,:lastname,:username,:password)", parameters); return user; } /** * gibt alle Benutzer zurück * @return Benutzerliste */ public List getAll() { List result = (List) getJdbcTemplate().query( "SELECT * FROM USER", new Object[] {}, new UserResultSetRowMapper()); return result; } } 42 Anhang B: Quellcode BookController.java package mvc; import services.BookService; import java.util.HashMap; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.AbstractController; /** * Controller, der das Modell und die View zu einer Bücheranfrage zurückgibt * @author Birgit * */ public class BookController extends AbstractController{ /** * Verweis auf Bücherservice */ private BookService bookService; /** * Viewname */ private String view = "booklist"; public BookService getBookService() { return bookService; } public void setBookService(BookService bookService) { this.bookService = bookService; } /** * gibt das Modell und die View zu einer Bücheranfrage zurückgibt * @param request HttpRequest * @param response HTTP Response */ protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception { Map model = new HashMap(); model.put("books", bookService.getBooks()); return new ModelAndView(view,model); } } LendingController.java package mvc; import java.util.HashMap; import java.util.Map; 43 Anhang B: Quellcode import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.security.context.SecurityContextHolder; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.AbstractController; import services.LendingService; /** * Controller, der das Modell und die View zu einer Ausleihanfrage zurückgibt * @author Birgit * */ public class LendingController extends AbstractController{ /** * Viewname */ private String view = "lendinglist"; /** * Service Ausleihe */ private LendingService lendingService; /** * gibt das Modell und die View zu einer Anfrage zurückgibt * @param request HttpRequest * @param response HTTP Response */ protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception { String user = (String) SecurityContextHolder.getContext(). getAuthentication().getName(); Map model = new HashMap(); model.put("lendings", lendingService.getLendings(user)); return new ModelAndView(view,model); } public LendingService getLendingService() { return lendingService; } public void setLendingService(LendingService lendingService) { this.lendingService = lendingService; } } LoginController.java package mvc; import java.util.HashMap; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; 44 Anhang B: Quellcode import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.AbstractController; /** * Controller, der das Modell und die View zu einer LoginAnfrage zurückgibt * @author Birgit * */ public class LoginController extends AbstractController{ /** * gibt die View zu einer LoginAnfrage zurückgibt * @param request HttpRequest * @param response HTTP Response */ protected ModelAndView handleRequestInternal(HttpServletRequest request,HttpServletResponse response) throws Exception { return new ModelAndView("login"); } } StartController.java package mvc; import java.util.HashMap; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import import import import org.springframework.security.Authentication; org.springframework.security.context.SecurityContextHolder; org.springframework.web.servlet.ModelAndView; org.springframework.web.servlet.mvc.AbstractController; import services.UserService; /** * Controller, der das Modell und die View zu einer Anfrage zurückgibt * @author Birgit * */ public class StartController extends AbstractController{ /** * gibt das Modell und die View zu einer Anfrage zurückgibt * @param request HttpRequest * @param response HTTP Response */ protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception { Authentication auth = SecurityContextHolder.getContext().getAuthentication(); if(SecurityContextHolder.getContext() .getAuthentication().getName().toString(). equals("anonymousUser") || ! (auth.isAuthenticated())) return new ModelAndView("login"); for (int i= 0; i<auth.getAuthorities().length;i++){ if (auth.getAuthorities()[i].getAuthority(). equals("ROLE_ADMIN")){ 45 Anhang B: Quellcode return new ModelAndView("admin/user"); } } return new ModelAndView("start"); } } Usercontroller.java package mvc; import java.util.HashMap; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.AbstractController; import services.UserService; /** * Controller gibt das Modell und die View zu einer Anfrage zurückgibt * @author Birgit * */ public class UserController extends AbstractController{ /** * Viewname */ private String view = "admin/userlist"; /** * Service User */ private UserService userService; /** * gibt das Modell und die View zu einer Anfrage zurückgibt * @param request HttpRequest * @param response HTTP Response */ protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception { Map model = new HashMap(); model.put("users", userService.getUsers()); return new ModelAndView(view,model); } public UserService getUserService() { return userService; } public void setUserService(UserService userService) { this.userService = userService; } } BookService.java package services; 46 Anhang B: Quellcode import java.util.List; import dao.IBookDAO; /** * Serivceschicht Book * @author Birgit * */ public class BookService { /** * DAO Book */ private IBookDAO bookDAO; /** * gibt alle Bücher zurück * @return Bücherliste */ public List getBooks(){ return bookDAO.getAll(); } public IBookDAO getBookDAO() { return bookDAO; } public void setBookDAO(IBookDAO bookDAO) { this.bookDAO = bookDAO; } } LendingService.java package services; import java.util.List; import dao.ILendingDAO; import dao.IUserDAO; import bib.Lending; import bib.User; /** * Serviceschicht Lending * @author Birgit * */ public class LendingService { /** * DAO Lending */ private ILendingDAO lendingDAO; /** * DAO User */ private IUserDAO userDAO; /** * gibt alle Ausleihen eines Benutzers zurück * @param username Benutzername * @return Ausleihliste */ public List<Lending> getLendings(String username){ User user = (User) userDAO.getByName(username); 47 Anhang B: Quellcode int userid = user.getId(); return lendingDAO.getByUserID(userid); } public ILendingDAO getLendingDAO() { return lendingDAO; } public void setLendingDAO(ILendingDAO lendingDAO) { this.lendingDAO = lendingDAO; } public IUserDAO getUserDAO() { return userDAO; } public void setUserDAO(IUserDAO userDAO) { this.userDAO = userDAO; } } UserService.java package services; import java.util.List; import bib.User; import dao.IUserDAO; /** * Serviceschicht User * @author Birgit * */ public class UserService { /** * User DAO */ private IUserDAO userDAO; /** * gibt alle Benutzer zurück * @return Benutzerliste */ public List getUsers(){ return userDAO.getAll(); } /** * legt einen Benutzer in der DB an * @param user Benutzer */ public void createUser(User user) { userDAO.save(user); } public IUserDAO getUserDAO() { return userDAO; } public void setUserDAO(IUserDAO userDAO) { this.userDAO = userDAO; 48 Anhang B: Quellcode } } applicationContext.xml <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:sec="http://www.springframework.org/schema/security" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security2.0.2.xsd"> <!-- =============== AOP ==============================--> <!--LoggingInterceptor --> <bean id="loggingInterceptor" class="aop.LoggingAdvice" /> <bean id="loggingPointcut" class="org.springframework.aop.support.JdkRegexpMethodPointcut"> <property name="pattern" value="services.*" /> </bean> <!-- Verbindung Adivce und Pointcut --> <bean id="loggingAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"> 49 Anhang B: Quellcode <property name="advice" ref="loggingInterceptor" /> <property name="pointcut" ref="loggingPointcut" /> </bean> <!-- =============== Datenschicht ==============================--> <!-- DataSource --> <bean id="dataSource" class="org.springframework.jdbc.datasource.SingleConnectionDataSource" > <property name="driverClassName" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost/bib" /> <property name="username" value="root" /> <property name="password" value="admin" /> </bean> <!-- JDBC Template --> <bean id="template" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource" /> <property name="nativeJdbcExtractor"> <bean class="org.springframework.jdbc.support.nativejdbc. CommonsDbcpNativeJdbcExtractor" /> </property> </bean> <!--=============== DAO ==============================--> <bean id="bookDAO" class="jdbc.BookDAO"> <property name="jdbcTemplate" ref="template" /> </bean> <bean id="userDAO" class="jdbc.UserDAO"> <property name="jdbcTemplate" ref="template" /> </bean> <bean id="exemplarDAO" class="jdbc.ExemplarDAO"> <property name="jdbcTemplate" ref="template" /> <property name="bookDAO" ref="bookDAO" /> </bean> <bean id="lendingDAO" class="jdbc.LendingDAO"> <property name="jdbcTemplate" ref="template" /> <property name="userDAO" ref="userDAO" /> <property name="exemplarDAO" ref="exemplarDAO" /> </bean> <!--=============== Beginn Sicherheitskonfiguration================--> <!-- Beans für Authentifikation und Autorisierung --> <bean id="authenticationDAO" class="org.springframework.security.userdetails.jdbc.JdbcDaoImpl"> <property name="dataSource" ref="dataSource" /> <property name="usersByUsernameQuery"> <value> SELECT USERNAME, PASSWORD, ENABLE as enabled FROM USER WHERE USERNAME = ? </value> </property> 50 Anhang B: Quellcode <property name="authoritiesByUsernameQuery"> <value> SELECT u.USERNAME , a.AUTORITY as authority FROM AUTORITY a , USER u WHERE a.USERNAME = u.USERNAME AND u.USERNAME = ? </value> </property> </bean> <bean id="authenticationProvider" class="org.springframework.security.providers.dao.DaoAuthenticationPro vider"> <property name="userDetailsService" ref="authenticationDAO" /> </bean> <!-- ProviderManager --> <bean id="authenticationManager" class="org.springframework.security.providers.ProviderManager"> <property name="providers"> <list> <ref bean="authenticationProvider" /> </list> </property> </bean> <!-- RoleVoter --> <bean id="roleVoter" class="org.springframework.security.vote.RoleVoter"> <property name="rolePrefix" value="ROLE_" /> </bean> <!-- Access-Decision-Manager --> <bean id="accessDecisionManager" class="org.springframework.security.vote.UnanimousBased"> <property name="decisionVoters"> <list> <ref bean="roleVoter"/> </list> </property> </bean> <!--=============== Absicherung Webschicht=========================--> <!--Filter für die Rechtevergabe auf der Webschicht --> <bean id="filterSecurityInterceptor" class="org.springframework.security.intercept. web.FilterSecurityInterceptor"> <property name="authenticationManager"> <ref bean="authenticationManager"/></property> <property name="accessDecisionManager"> <ref bean="accessDecisionManager"/></property> <property name="objectDefinitionSource"> <value> CONVERT_TO_LOWERCASE_BEFORE_COMPARISON PATTERN_TYPE_APACHE_ANT /book.htm=ROLE_USER /lending.htm=ROLE_USER /admin/user.htm=ROLE_ADMIN </value> 51 Anhang B: Quellcode </property> </bean> <!--bei Fehler wird authenticationEntryPoint aufgerufen --> <bean id="exceptionTranslationFilter" class="org.springframework.security.ui.ExceptionTranslationFilter"> <property name="authenticationEntryPoint" ref="authenticationEntryPoint" /> </bean> <!-- falls der Benutzer nicht authentifiziert ist, wird er nach loginFormUrl umgeleitet --> <bean id="authenticationEntryPoint" class="org.springframework.security.ui.webapp. AuthenticationProcessingFilterEntryPoint"> <property name="loginFormUrl" value="/login.htm" /> <property name="forceHttps" value="false" /> </bean> <!-- Bean zur Durchführung der Authentifizierung --> <bean id="authenticationProcessingFilter" class="org.springframework.security.ui.webapp. AuthenticationProcessingFilter"> <property name="filterProcessesUrl" value="/j_acegi_security_check" /> <property name="authenticationManager" ref="authenticationManager" /> <property name="authenticationFailureUrl" value="/login.htm" /> <property name="defaultTargetUrl" value="/" /> </bean> <!-- Benutzerdaten werden in der HTTPSession gespeichert --> <bean id="httpSessionContextIntegrationFilter" class="org.springframework.security.context. HttpSessionContextIntegrationFilter"> <property name="contextClass" value="org.springframework.security. context.SecurityContextImpl" /> </bean> <bean id="filterChainProxy" class="org.springframework.security.util.FilterChainProxy"> <property name="filterInvocationDefinitionSource"> <value> CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON PATTERN_TYPE_APACHE_ANT /**=httpSessionContextIntegrationFilter,logoutFilter, authenticationProcessingFilter,exceptionTranslationFilter, filterSecurityInterceptor </value> </property> </bean> <bean id="logoutFilter" class="org.springframework.security.ui.logout.LogoutFilter"> <constructor-arg value="/index.htm"/> <!-- URL redirected to after logout --> <constructor-arg> <list> <bean class="org.springframework.security.ui. 52 Anhang B: Quellcode logout.SecurityContextLogoutHandler"/> </list> </constructor-arg> <property name="filterProcessesUrl" value="/j_acegi_logout" /> </bean> <!--=============== Absicherung Geschäftslogik ==============================--> <bean id="securityInterceptor" class="org.springframework.security.intercept.method. aopalliance.MethodSecurityInterceptor"> <property name="authenticationManager"> <ref bean="authenticationManager"/></property> <property name="accessDecisionManager"> <ref bean="accessDecisionManager"/></property> <property name="objectDefinitionSource"> <value> services.BookService.getBooks=ROLE_USER services.LendingService.getLendings=ROLE_USER services.UserService.getUsers=ROLE_ADMIN </value> </property> </bean> <!-- AutoProxy für die Interceptoren--> <bean id="autoProxyCreator" class="org.springframework.aop.framework. autoproxy.BeanNameAutoProxyCreator"> <property name="interceptorNames"> <list> <value>securityInterceptor</value> <value>loggingInterceptor</value> </list> </property> <property name="beanNames"> <list> <value>bookService</value> <value>lendingService</value> <value>userService</value> </list> </property> </bean> <!--=============== Ende Konfiguration Sicherheit =================--> <!--=============== Services /Geschäftslogik ======================--> <bean id="lendingService" class="services.LendingService"> <property name="lendingDAO" ref="lendingDAO" /> <property name="userDAO" ref="userDAO" /> </bean> <bean id="bookService" class="services.BookService"> <property name="bookDAO" ref="bookDAO" /> </bean> <bean id="userService" class="services.UserService"> <property name="userDAO" ref="userDAO" /> </bean> </beans> 53 Anhang B: Quellcode springapp-servlet.xml <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:sec="http://www.springframework.org/schema/security" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security2.0.2.xsd"> <import resource="applicationContext.xml" /> <!--=============== Web-Konfiguration =============================--> <bean name="/login.htm" class="mvc.LoginController"/> <bean name="/lending.htm" class="mvc.LendingController"> <property name="lendingService" ref="lendingService" /> </bean> <bean name="/admin/userlist.htm" class="mvc.UserController"> <property name="userService" ref="userService" /> </bean> <bean name="/book.htm" class="mvc.BookController"> <property name="bookService" ref="bookService" /> </bean> <bean name="/start.htm" class="mvc.StartController"/> <bean id="viewResolver" class="org.springframework.web. servlet.view.InternalResourceViewResolver"> <property name="viewClass" value="org.springframework.web. servlet.view.JstlView"></property> <property name="prefix" value="/WEB-INF/jsp/" /> <property name="suffix" value=".jsp"/> </bean> </beans> web.xml <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"> <listener> <listener-class> org.springframework.web.context. 54 Anhang B: Quellcode ContextLoaderListener </listener-class> </listener> <filter> <filter-name>filterChainProxy</filter-name> <filter-class>org.springframework.security.util. FilterToBeanProxy</filter-class> <init-param> <param-name>targetClass</param-name> <param-value>org.springframework.security. util.FilterChainProxy</param-value> </init-param> </filter> <filter-mapping> <filter-name>filterChainProxy</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <display-name>SpringApp</display-name> <welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <servlet> <servlet-name>springapp</servlet-name> <servlet-class>org.springframework.web.servlet. DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springapp</servlet-name> <url-pattern>*.htm</url-pattern> </servlet-mapping> </web-app> index.jsp <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <c:redirect url="/start.htm"/> start.jsp <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ page import="org.springframework.security.context.SecurityContextHolder" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <link rel="stylesheet" href="../style/style.css" type="text/css"/> <meta http-equiv="Content-Type" content="text/html; charset=ISO8859-1"> <title>Bibliothek</title> </head> 55 Anhang B: Quellcode <body> <div id="main"> Willkommen <%= SecurityContextHolder.getContext() .getAuthentication().getName() %> auf der Webseite der Bibliothek!<br> <img src="../image/buecherei.jpg" /><br> Was möchten Sie tun?<br> <a href="book.htm">Bücherliste einsehen</a><br> <a href="lending.htm">aktuelle Ausleihen</a><br> <a href="SpringApp/j_acegi_logout">Logout</a> </div> </body> </html> login.jsp <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <link rel="stylesheet" type="text/css" href="/style/style.css" /> <meta http-equiv="Content-Type" content="text/html; charset=ISO-88591"> <title>Bibliothek: Anmeldung</title> </head> <body> <div id="main"> <h1>Willkommen der Webseite der Bibliothek!</h1><br> <img src="/image/buecherei.jpg" width="300" height="200" /><br> Sie müssen sich anmelden bevor Sie unseren Service in Anspruch nehmen können.<br> <form action="j_acegi_security_check" method="POST"> <p>Benutzer: <input type="text" name="j_username"> <p>Password: <input type="password" name="j_password"> <p><input type="submit" value="Anmelden"> </form> </div> </body> </html> lending.jsp <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib uri="http://www.springframework.org/tags" prefix="spring" %> <%@ page import="org.springframework.security.context.SecurityContextHolder" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <link rel="stylesheet" href="./style/style.css" type="text/css"/> <meta http-equiv="Content-Type" content="text/html; charset=ISO-88591"> <title>Ausleihen</title> </head> 56 Anhang B: Quellcode <body> <h1>Willkommen: <%= SecurityContextHolder.getContext().getAuthentication().getName() %></h1> <h2>Hier sehen Sie alle von Ihnen ausgeliehenen Bücher</h2> <br> <table> <tr><th>Datum</th><th>Benutzer</th><th>Exemplar</th></tr> <c:forEach items="${lendings}" var="lending"> <tr><td><c:out value="${lending.date}"/></td> <td><c:out value="${lending.user.username}"/></td> <td> <c:out value="${lending.exemplar.book.description}"/></td> </tr> </c:forEach> </table> </body> </html> booklist.jsp <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib uri="http://www.springframework.org/tags" prefix="spring" %> <%@ page import="org.springframework.security.context.SecurityContextHolder" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <link rel="stylesheet" type="text/css" href="style.css" /> <meta http-equiv="Content-Type" content="text/html; charset=ISO-88591"> <title>Bücherliste</title> </head> <body> <div id="main"> <h1>Willkommen: <%= SecurityContextHolder.getContext().getAuthentication().getName() %></h1> <h2>Hier sehen Sie alle verfügbaren Bücher</h2> <br> <table> <tr><th>Buch</th><th>Autor</th></tr> <c:forEach items="${books}" var="book"> <tr><td><c:out value="${book.description}"/></td> <td><c:out value="${book.autor}"/></td> </tr> </c:forEach> </table> </div> </body> </html> user.jsp <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 57 Anhang B: Quellcode <%@ taglib uri="http://www.springframework.org/tags" prefix="spring" %> <%@ page import="org.springframework.security.context.SecurityContextHolder" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <link rel="stylesheet" href="./style/style.css" type="text/css"/> <meta http-equiv="Content-Type" content="text/html; charset=ISO-88591"> <title>Bibliothek: Userverwaltung</title> </head> <body> <div id="main"> <h1>Willkommen: <%= SecurityContextHolder.getContext().getAuthentication().getName() %></h1> Was möchten Sie tun?<br> <a href="admin/userlist.htm">Benutzerliste einsehen</a><br> <a href="SpringApp/j_acegi_logout">Logout</a> </div> </body> </html> userlist.jsp <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib uri="http://www.springframework.org/tags" prefix="spring" %> <%@ page import="org.springframework.security.context.SecurityContextHolder" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <link rel="stylesheet" href="./style/style.css" type="text/css"/> <meta http-equiv="Content-Type" content="text/html; charset=ISO-88591"> <title>Bibliothek: Benutzer</title> </head> <body> <div id="main"> <h1>Willkommen: <%= SecurityContextHolder.getContext().getAuthentication().getName() %></h1> <h2>Hier sehen Sie eine Liste aller Benutzer</h2> <br> <table> <tr><th>ID</th><th>Username</th><th>Vorname</th> <th>Nachname</th></tr> <c:forEach items="${users}" var="user"> <tr><td><c:out value="${user.id}"/></td> <td><c:out value="${user.username}"/></td> <td><c:out value="${user.firstname}"/></td> <td><c:out value="${user.lastname}"/></td> </tr> </c:forEach> 58 Anhang B: Quellcode </table> </div> </body> </html> 59 Literaturverzeichnis [FaSc97] Fayad, Mohamed E.; Schmidt, Douglas C.: Object-Oriented Application Frameworks. ACM, 40, S. 32-38, ACM, 1997. [Fo04] Fowler, Martin: Inversion of Control Containers and the Dependency Injection Pattern. http://martinfowler.com/articles/injection.html. Abrufdatum: 28.09.2008. [GHJV04] Gamma, Erich; Helm, Richard; Johnson, Ralph; Vlissides, John: Entwurfsmuster - Elemente wiederverwendbarer objektorientierter Software. Addison Wesley Verlag, 2004. [Wa08] Walls, Craig: Spring im Einsatz. Hanser Verlag, 2008. [Wo07] Wolff, Eberhard: Spring 2. dpunkt, 2007.