Java für Fortgeschrittene Proseminar im Sommersemester 2009 Java EE 5 Eva Nieÿner Technische Universität München 21.7.2007 1 Einleitung Als 1999 die erste Version der Java Enterprise Edition veröentlicht wurde Java 2 Enterprise Edition v 1.0, war das der Anfang der Verknüpfung von Java mit dem Internet. Seitdem wurde eine Vielzahl an Technologien entwickelt, um mächtige Webanwendungen zu erstellen. Bei der Entwicklung von Webanwendungen ist zu beachten, dass die Kommunikationsbasis für die Web- und Businessanwendungen von Java EE meist das im Internet verwendete HTTPProtokoll ist. Das Webfrontend der Anwendung ist dadurch überall einsetzbar, ist jedoch auch durch dieses Internetprotokoll beschränkt. Die Technologien zur Erstellung eines Webfrontends von Java EE bieten die aus Java gewohnte objektorientierte und typsichere Implementierung, ohne eine JVM auf Clientseite zu erfordern. Im Gegensatz zu Skriptsprachen ergibt sich durch die Verwendung von Java EE eine Reihe von Vorteilen. Diese liegen vor allem in den Bereichen Skalierbarkeit, Sicherheit, Wartbarkeit, sowie eine hohe Verfügbarkeit an professionellen Bibliotheken und IDEs. Dieses Dokument soll eine Übersicht über die verschiedenen Technologien geben, die Java EE umfasst und einen besonderen Schwerpunkt auf die Techniken zur Erstellung genannter Webfrontends legen. 2 Übersicht In diesem Kapitel soll eine Übersicht über die Komponenten von Java EE gegeben werden und ihre zugehörige Schicht identiziert werden. Des Weiteren soll der allgemeine Ablauf eines Aufrufs einer Webanwendung betrachtet werden. 2.1 Zentrale Konzepte Das erste zentrale Konzept, auf dem Java EE 5 basiert, ist das Client-ServerModell. Webanwendungen werden in verteilten Systemen als Dienste eines Servers angeboten und interagieren mit ihren Clients über das Intra- bzw. Internet. 1 Die Laufzeitumgebung der Server muss natürlich die Java Virtual Machine sein. Erweitert man dieses Modell von einem Server auf mehrere Server, auf denen mehrere JVMs laufen, lässt sich die Anwendung auch auf mehreren Rechnern implementieren und ist nicht an eine einzige Virtual Machine gebunden. RMI ist ein typisches Protokoll für die transparente Kommunikation zwischen den einzelnen Servern. Daten werden zentral verwaltet und Clients können in gekapselten Teilanwendungen auf sie zugreifen. Java EE kann dabei auch als Middleware zwischen einzelnen Servern fungieren und abstrahiert so von der Hardwareebene. Um Darstellung, Anwendung und Datenhaltung trennen zu können, werden die Anwendungen als sogenannte Multi-Tier-Applications realisiert, d.h. als Mehrschichtanwendungen. Dies ist das zweite zentrale Konzept von Java EE. Auf der Darstellungsschicht werden die Daten der Anwendungsschicht grasch aufgearbeitet; sie ist die Schnittstelle zum Benutzer. Spricht man von Client Tier und Web Tier (wie in Abbildung 1), gehören diese beiden Tiers zur Darstellungsschicht. Sie unterscheiden sich lediglich darin, dass der Client Tier zur Maschine des Clients gehört und der Web Tier zum Webserver. Es lassen sich viele Arten von Oberächen implementieren, auch parallel, exibel zugeschnitten auf die Umgebung des Benutzers. Der Client basiert dabei meistens auf dem Internet Explorer oder Mozilla Firefox. Die Geschäftslogik ist in der darunterliegenden Schicht, der Anwendungsschicht, zu nden und bildet damit die zentrale Komponente der Webanwendung. In der Datenhaltungsschicht werden die Daten persistent gespeichert, das heiÿt dauerhaft. Dabei wird typischerweise auf relationale Datenbanktabellen zugegrien. Durch die Mehrschichtigkeit ist Modularität gegeben, die von Vorteil ist, wenn man einzelne Implementierungen austauschen möchte, ohne seine gesamte Anwendung umschreiben zu müssen. So kann man Oberächen von Swing bis hin zum Webportal mit der Geschäftslogik und verschiedenen Datenbanken kombinieren. Dabei sind die tatsächlichen Implementierungen der einzelnen Schichten für die jeweils darüber liegenden Schichten transparent. Insbesondere kann die Geschäftslogik auch auf Clientseite verborgen werden, was die Webanwendung vor externen Angrien sichert. Das dritte und letzte Konzept unterstützt ebenfalls die Modularität der Anwendung: Die Trennung von API und seiner Implementierung. Java stellt eine Fülle von abstrakten Klassen und Interfaces zur Verfügung, die individuell implementiert werden können. Dadurch können standardisierte Abfragen zwischen den Schichten genutzt werden, ohne spezielle Eigenarten kennen zu müssen. 2.2 Technologien und ihre Einordnung in das Schichtenmodell In diesem Kapitel sollen die verschiedenen Technologien von Java EE vorgestellt und in das Schichtenmodell eingeordnet werden. Eine grasche Übersicht bietet Abbildung 2. 2 Abbildung 1: Das Schichtenmodell von Java EE 2.2.1 Darstellungsschicht Auf der Darstellungsseite werden Servlets und Java Server Pages verwendet. Beides sind dynamische Dokumente, die es ermöglichen, Java-Code in HTML-Seiten einzubetten und so die Statik von HTML zu überwinden. Aussehen und Inhalt der Seite wird erst zur Ausführungszeit bestimmt. Obwohl beide im Grunde dieselben Funktionalitäten erfüllen, empehlt es sich jedoch sie unterschiedlich zu verwenden (siehe Kapitel 3.4). Funktionalität wird in JSPs unter anderem durch Tags erzeugt. Tag- Bibliotheken (Tag-Libraries, abgekürzt Taglibs) bieten eine Fülle an vordenierten Tags, die ermöglichen, den begrenzten Vorrat an HTML- und JSP-Tags zu erweitern. Auch eigene Tags mit spezischer Funktionalität, sogenannte Custom Tags, können implementiert werden. Im Rahmen des JCP (Java Community Process) wurden nützliche Custom Tags von Entwicklern gesammelt und schlieÿlich als Java Standard Tag Library (JSTL) veröentlicht. Servlets, JSPs und Taglibs bilden so zusammen eine echte Alternative zu CGI und PHP. Für die Erstellung von Webfrontends gibt es viele Frameworks, die die Erstellung von JSPs, Servlets und Taglibs deutlich vereinfachen und benutzerfreundlich gestalten. Die bekanntesten darunter sind Struts und JSF (Java Server Faces). 2.2.2 Anwendungsschicht Um die Geschäftslogik zu kapseln und anderen Programmteilen zur Verfügung zu stellen, werden JavaBeans bzw. Enterprise Java Beans verwendet. Beide sind portabel, leicht austauschbar und mit verschiedenen Komponenten kompatibel. JavaBeans werden jedoch im Allgemeinen für das Frontend verwendet und sind leichtgewichtiger als die Enterprise Java Beans, die einen EJB-Container im Webserver erfordern und darauf ausgerichtet sind, bereits erwähnte Verteiltheit 3 Abbildung 2: Einordnung der Technologien in das Schichtenmodell durch Remote Method Invocation zu unterstützen. Der EJB-Container übernimmt dabei unter anderem Sicherheitsfunktionalität, Transaktionsmanagement und Lastverteilung zwischen verschiedenen Servern. Sobald die Geschäftslogik der Webanwendung sehr umfangreich wird und die Anforderungen steigen, sollte man einen eigenen EJB-Container für sie verwenden. Um RMI zu realisieren, wird das Java Naming and Directory Interface benötigt. Diese Technologie ist für Namens- und Verzeichnisdienste bei verteilten Systemen zuständig. Möchte man unter Benutzung von RMI, dass Server und Client nicht direkt in Kontakt treten, kann dies auch über den Message Broker des Java Message Service (JMS) geschehen. Diese Technologie kann die normalerweise synchrone Kommunikation von RMI-Anwendungen überwinden und asynchrone Kommunikation realisieren, um Daten und Objekte auszutauschen. Dies hat viele Vorteile: Unter anderem sind Client und Server weitgehend unabhängig voneinander und die Messages werden vom Message Broker gesammelt und nacheinander abgearbeitet, ohne dass Client und Server einander kennen. Eine weitere Java EE-Technologie, die auf RMI basiert, sind die Webservices. Dies sind eindeutig identizierbare Software-Anwendungen, deren Schnittstellen über XML-Dokumente deniert werden. 2.2.3 Datenhaltungsschicht Um Daten persistent zu speichern, stellt Java die Java Persistenz API bereit. Dies ist eine Schnittstelle, die das objekt-relationale Mapping, also die Abbildung von Objekten auf Datenbankeinträge und umgekehrt, übernimmt und so dem Programmierer lange SQL-Statements abnehmen kann. 4 2.3 Kontrolluss einer Webanwendung Wie schon erwähnt, basiert der Kontrolluss einer Webanwendung auf dem Client-Server-Modell. Der Benutzer ruft in seinem Internetbrowser Seiten auf und startet damit die Webanwendung. Die Anfrage wird mit Parametern versehen vom Browser, dem Web Client, erstellt und im Rahmen des HTTPProtokolls als HTTP-Request an den Webserver versendet. Der Webserver (beispielsweise Apache Tomcat, Glasssh, JBoss etc.) nimmt den HTTP-Request an und parst ihn in ein HttpServletRequest-Objekt. Dieses leitet er an den Servlet-Container weiter, der ihn wiederum an seine zugehörige Komponente weitergibt. Zu diesen Komponenten gehören Servlets, JSPs und JavaBeans. Die aus der Ausführung der Geschäftslogik entstehenden Daten werden in das HttpServletResponse-Objekt geschrieben und an den Webserver weitergeleitet, der das Objekt wiederum in Form einer HTTP-Response an den Web Client ausgeliefert. Typischerweise handelt es sich bei der aufgerufenen Internetseite um ein HTML-Formular, das mithilfe des HTTP-Protokolls verschickt wird. Da die Seite abhängig von den Eingaben des Benutzers und anderen Rahmenbedingungen erzeugt wird, ist diese Seite eine dynamische HTML-Seite. Die eingegebenen Daten werden in Form des HTTP-Requests an den Webserver weitergeleitet. Die Komponente, die zugehörig zur Anfrage aufgerufen wird, erstellt in ihrer service()-Methode eine dynamische HTML-Seite, die als Response an service()-Methode von JSPs den Web Client ausgeliefert werden soll. In der und Servlets ndet eventuell die Weiterleitung an andere Ressourcen statt, wie z.B. die JavaBeans. Abbildung 3 beschreibt den Ablauf grasch. Aus dieser Abbildung ist auch ersichtlich, dass über die Web Components und die JavaBeans Components, falls nötig, der Zugri auf Datenbanken erfolgen kann. Betrachtet man andere Clients, wie beispielsweise PDAs oder Handys, können Servlets und JSPs neben HTML die Response auch in Form von xml-Dateien und anderen textbasierten Formaten oder in WAP-Darstellung erfolgen. Sie müssen dazu nur entsprechend konguriert werden. Eine genauere Beschreibung der Abläufe der Methodenaufrufe in Servlets soll im nachfolgenden Kapitel gegeben werden. 3 Webkomponenten Servlets und JSPs generieren die Webfrontends, die basierend auf HTML im Internetbrowser angezeigt werden. Die Hauptaufgabe der Servlets und JSPs ist es also, die Statik von HTML-Seiten zu überwinden und dynamischen Javacode in die Seiten einzubetten. Dieses Kapitel geht auf die Gemeinsamkeiten und Unterschiede, sowie Einsatzgebiete von Servlets und JSPs ein. 3.1 Servlets Die Technologie der Servlets ist älter als die der JSPs. Sie sind zunächst nichts anderes als Javaklassen, die auf einem Web Server ausgeführt wurden, Eingaben entgegennahmen und Ausgaben generierten. Im Laufe der Zeit wurden neben 5 Abbildung 3: Der Kontrolluss einer Webanwendung der Servlet-API aus dem Paket javax.servlet.* andere APIs erstellt, die Serv- lets um viele Funktionalitäten erweiterten, beispielsweise XML-Funktionalitäten und insbesondere auch die Datenbankanbindung über JDBC ermöglichten. 3.1.1 Aufbau eines Servlets Zur Betrachtung der Struktur eines Servlets soll folgende Beispielklasse TestServlet betrachtet werden. package import import import import import import import path . to . servlets ; java . util . Date ; java . io . PrintWriter ; java . io . IOException ; javax . servlet . http . HttpServlet ; javax . servlet . http . HttpServletRequest ; javax . servlet . http . HttpServletResponse ; javax . servlet . ServletException ; /* * Ein einfaches Servlet , das " Hello , World " sowie das * aktuelle Datum ausgibt . */ p u b l i c c l a s s TestServlet extends HttpServlet { p u b l i c void service ( HttpServletRequest request , HttpServletResponse response ) throws IOException , ServletException { // Setzen des zu verwendenden MIME - Types response . setContentType ( " text / html ; charset = ISO -8859 -1 " ) ; 6 // Binden der Variable out PrintWriter out = response . getWriter () ; // Erzeugen von HTML - Code ... out . println ( " < html > < body style =\" text - align : center \" > " ) ; out . println ( " Hello , World ! < br / > " ) ; // ... mit dynamischen Elementen out . println ( " Es ist jetzt genau : " + new Date () ) ; out . println ( " </ body > </ html > " ) ; } } Wie im Beispiel sichtbar vax.servlet.HttpServlet. In der ist, erbt Methode die Klasse service() Servlet wird die von im ja- Request des Clients angeforderte Geschäftslogik selbst ausgeführt oder an die zugehörige Komponente, die sie enthält, weitergeleitet und die HTML-Seite erstellt. Die service()-Methode ist vergleichbar mit der main()-Methode einer eigenständigen Applikation. Zuerst wird gemäÿ des HTTP-Headers der MIME-Type festgelegt (hier: text/html). Anschlieÿend wird ein Outputstream durch die Variable out erzeugt, der die Ausgaben in die Webseite schreibt. Sie ist vergleichbar mit der System.out - Variable, die Ausgaben auf die Kommandozeile bzw. in unserem Fall in die Protokolldatei und nicht in das HTML-File schreiben würde. Unter Verwendung der Methode println() wird nun das HTML-Dokument erstellt mit bekannter Syntax, dem Markup durch Tags. Zu beachten ist die notwendige Maskierung der Anführungszeichen im Javacode (\"). Der Servletcontainer kompiliert und führt den Javacode aus, insbesondere auch das dynamische Element new Date(), und liefert eine HTML-Seite aus, die im Browser angezeigt werden kann. 3.1.2 Lebenszyklus eines Servlets Wegen der Oberklasse in obigem Beispiel sind einige für den Lebenszyklus eines Servlets wichtige Methoden standardimplementiert worden. Dazu gehören init(), die doGet() und doPost()-Methode, sowie die destroy()-Methode, die in der folgenden Beschreibung des Lebenszyklus des Servlets erklärt werden sollen. Der Servletcontainer entscheidet dabei, welche Servlets instanziiert werden und wann. Bevor die service()-Methode aufgerufen werden kann, muss das Servlet durch Aufruf eines parameterlosen Konstruktors instanziiert werden. Danach wird die init()-Methode aufgerufen, die dem Setup des Servlets dient. Für diese Me- thode existieren zwei verschiedene Signaturen, die bei Bedarf überschrieben und implementiert werden können: Eine parameterlos, der anderen wird ein Kon- gurationsobjekt des Servlets mitgegeben (javax.servlet.ServletConfig). Die Initialisierungsparameter, sowie das ServletConfig - Objekt und andere Me- tadaten wie Kontext, Info und Name können mithilfe von entsprechenden getMethoden auch nachträglich ausgelesen werden. Es sei auf die API[3] verwiesen, 7 Abbildung 4: Klassendiagramm der Klassen HttpServlet und GenericServlet auf welche Initialisierungsparameter dieses Objekts zugegrien werden kann; die Parameter können beispielsweise die Datenbankschnittstelle denieren. Kongurationsparameter können auch in der zu jeder Webapplikation automatisch erzeugten xml-Datei web.xml eingetragen werden (siehe Kapitel 3.3). Anschlieÿend wird die eigentliche service()-Methode aufgerufen. Diese ist die Universalmethode für alle Arten von Requests eines Clients. Möchte man auf verschiedene HTTP-Request-Typen unterschiedlich reagieren, können entsprechende Methoden von folgender Signatur implementiert werden: p u b l i c void doRequestTyp ( HttpServletRequest request , HttpServletResponse response ) throws ServletException , IOException { // Java - Code } Die am häugsten verwendeten Requesttypen sind GET und POST, also Aufruf der Methoden doGet() und doPost(). Der Unterschied zwischen beiden HTTP-Request-Arten besteht darin, dass bei GET im Allgemeinen Ressourcen vom Server angefordert werden. Übermittelte Daten werden direkt als NameValue-Paare in die URL kodiert, der Request ist auf 1 KB begrenzt. Bei POSTAnfragen werden typischerweise Formularangaben übermittelt, die Parameter werden im Request-Body übertragen und sind nicht in der URL sichtbar. Die Länge des Requests ist unbegrenzt. Die Oberklasse implementiert bereits Methoden mit leeren Methodenrümpfen für alle Arten von Requests, so dass bei Modikation einer doRequestTyp()- Methode nicht alle anderen Methoden ebenfalls überschrieben werden müssen. Es ist meistens am sinnvollsten, die Methoden doGet() und doPost() gemäÿ obiger Signatur zu implementieren und die von der Superklasse implementierte service()-Methode für andere Requesttypen (HEAD, PUT, DELETE, ...) zu verwenden, da diese die häugsten Anfragetypen sind. So wie die Generierung des Servlets steuert der Servletcontainer ebenfalls die Entfernung des Servlets. 8 Soll Speicher freigegeben werden, weil beispielsweise das Servlet länger nicht aufgerufen wurde oder stoppt der Server selbst, wird die destroy()-Methode aufgerufen, um eventuell Daten zu speichern und das Servlet-Objekt anschlieÿend zu entsorgen. Ein Überblick über die Methoden der abstrakten Oberklasse HttpServlet und deren Oberklasse (GenericServlet), die alle für den Lifecycle wichtige Methoden standardimplementiert, soll das Klassendiagramm in Abbildung 4 geben. Durch die Spezikation des Lifecycles eines Servlets können diese in der Java VM wiederverwendet werden, ohne neu instanziiert werden zu müssen (spätestens sobald mindestens eine Anfrage bereits vom Servlet beantwortet wurde). Der Servletcontainer kann - je nach Konguration - die Servlets auch schon vor der ersten Anfrage instanziieren. Dies ist ein groÿer Vorteil gegenüber der CGI Scripts. Bei diesen wird bei jedem Aufruf von Geschäftslogik ein neuer Prozess gestartet. 3.1.3 Session-Management out existiert ein implizites Session-Objekt, das mittels request.getSession() gebunden werden kann. Das zurückgelieferte Objekt implementiert das Interface HttpSession. In diesem Session-Objekt werden InNeben dem Objekt formationen über die aktuelle Verbindung zwischen Client und Server über einen Request hinaus gespeichert. Diese Verbindung ist erst mal statuslos (stateless). Mit dem Session-Objekt können jede Session und ihre Attribute über eine ID identiziert werden. Die Statuslosigkeit des HTTP-Protokolls wird damit überwunden; eine statusbehaftete (stateful) Verbindung zwischen Server und Client wird simuliert. Durch die Methoden setAttribute() und getAttribute() des Session-Objekts können Client-spezische Informationen gespeichert werden. Diese Informationen werden im jeweiligen Attribut solange gespeichert, wie das Session-Objekt existiert, also über mehrere Requests eines Clients hinaus. Weitere Methoden zur Abfrage von bestimmten Eigenschaften der Session nden sich in der Java API[3]. Darüber hinaus können aktive Sessions mithilfe vom HttpSessionListener überwacht werden und damit auf enstehende bzw. verfallende Sessions beliebig reagiert werden. Ein weiterer Listener ist der HttpSessionAttributeListener, der auf die Erstellung und das Überschreiben von Attributen der Session reagieren kann. 3.2 Java Server Pages Möchte man zu seiner Webanwendung ein aufwendiges Webfrontend implementieren, sind Servlets in ihrer Handhabung eher schlecht geeignet. Dies hatte zur Folge, dass die Oberächengestaltung und damit die Erstellung vielseitiger Webanwendungen eingeschränkt war. Die Antwort auf dieses Problem sind JSPs. Sie erlauben es dem Programmierer, Java-Code direkt in den HTML-Code einzubetten und abstrahieren somit von den Servlet-Klassen. Vergleichbare Technologien sind die ASP (Active Server Pages) von Microsoft oder PHP. JSPs sind 9 .jsp-Dateien, werden aber innerhalb des Kompilierungsvorgangs automatisch in Servlets, also wieder Javaklassen, umgewandelt. 3.2.1 Syntax Der HTML-Code in einer JSP kann ohne weiteres verwendet werden. Um Bereiche zu denieren, in denen Java-Code zum Einsatz kommt, werden vier verschiedene Arten von Tags verwendet: Java-Code %> Ausdrücke: <%= Java-Ausdruck %> Deklarationen: <%! Java-Deklarationen %> Kommentare: <%-- Kommentare --%> 1. Skriptlets: 2. 3. 4. <% In Skriptlets kann beliebiger, mehrzeiliger Java-Code eingefügt werden. Um Ausgaben auf die HTML-Seite zu generieren, wird die von Servlets bekannte Variable out verwendet, die in JSPs aber bereits zur Verfügung steht, ohne sie extra binden zu müssen. Wie bei Servlets schon erwähnt, würde die System.outVariable lediglich Ausgaben im Logle des Webservers erzeugen. In Ausdrücken gewertet und wird in jeweils die eine einzelne HTML-Seite Java-Anweisung integriert, aus- beispielsweise <%= new java.util.Random().nextInt(100) %> oder eine in einem Skriptlet vorher denierte Variable name, <%= name %>. Sie ist äquivalent zum Skriptlet <% out.print(name); %> bzw. System.out.print(name); in der main()Methode einer Java-Klasse. Zu beachten ist das weggelassene Semikolon im Ausdruck. Sowohl in Skriptlets als auch in Ausdrücken kann auf vorangegangene Variablen zugegrien werden. Das Java-Programm zieht sich wie ein roter Faden durch das HTML-Dokument und setzt bei einem önenden JSP-Tag die Ausführung dort fort, wo das letzte JSP-Tag geschlossen wurde. Unter Benutzung von Skriptlets und Ausdrücken hat man sich einer Skriptsprache angenähert; nun muss man Deklarationen von globalen Variablen und ganzen Methoden zulassen, um JSPs zu vollwertigen Objekten im Sinne objektorientierter Programmierung zu machen. Dies geschieht in den Deklarationen markiert durch das Tag <%! bzw. %>. Die Sichtbarkeit der Variable kann wie in einer normalen Java-Klasse durch Modizierer festgelegt werden. Klassenvariablen und -methoden können mit static deniert werden. Auch in Deklarationen kann auf alle in vorangegangenen Deklarationen denierten Variablen und Methoden zugegrien werden. Werden deklarierte Methoden und Variablen in Skriplets und Ausdrücken verwendet, ist es sogar nicht relevant, ob die entsprechenden Deklarationsblöcke davor oder danach stehen; allerdings müssen Deklarationen immer in sich geschlossen sein, das heiÿt Methodendenitionen müssen vollständig in einem Deklarationsblock geschrieben werden. Der umgekehrte Zugri von Deklarationen auf Variablen, die in Skriptlets deniert wurden, ist nicht möglich. Die letzte Art von Tag sind 10 die Kommentare. Kommentare können innerhalb von Skriptlets und Deklarationen mithilfe der bekannten Java-Syntax (// werden. Auÿerhalb dieser wird das Tag ... bzw. /** ... */) eingetragen <%-- bzw. %> verwendet. Es ist sinnvoll, die JSP-Tags anstelle der HTML-Tags für Kommentare zu benutzen, da diese dadurch nicht im HTML-Quellcode sichtbar sind und Informationen über die Funktionsweise der Webanwendung schützen. Alle Arten von Tags können beliebig häug und an beliebigen Stellen im HTMLCode eingebettet werden; es ist jedoch aus Übersichtlichkeitsgründen empfehlenswert, sich an die Struktur einer Java-Klasse zu halten. Die Tags können auch zur Manipulation von Metainformationen zur Laufzeit verwendet werden, wie zum Beispiel zur Bestimmung von HTML-Attributwerten. Zum besseren Verständnis soll nun ein Codebeispiel betrachtet werden: <%! /* * Diese Methode berechnet die verbleibende Anzahl an * Tagen bis Weihnachten */ p r i v a t e i n t getCountdown () { Calendar cal = java . util . Calendar . getInstance () ; i n t curr = cal . get ( Calendar . DAY_OF_YEAR ) ; cal . set ( cal . get ( Calendar . YEAR ) , Calendar . DECEMBER , 24) ; i n t christmas = cal . get ( Calendar . DAY_OF_YEAR ) ; r e t u r n christmas - curr ; } %> < html > < head > < meta http - equiv = " Content - Type " content = " text / html ; charset = UTF -8 " > < title > Weihnachtsrechner </ title > </ head > < body > <h2 > Der Weihnachtscountdown </ h2 > <% i n t res = getCountdown () ; i f ( res > 0) { %> Noch <%= res % > Tage bis Weihnachten ! <% } e l s e i f ( res == 0) { %> Heute ist Weihnachten ! :) <% } e l s e i f ( res < 0 ) { % > 11 Weihnachten ist seit <%= Math . abs ( res ) % > Tagen vorbei :( <% } % > </ body > </ html > Im Codebeispiel wird zuerst die Methode getCountdown() als Deklaration de- niert. Anschlieÿend beginnt das HTML-Dokument. Nach der Überschrift wird getCountdown() aufgerufen und ihr Ergebnis res gespeichert. Danach werden die Skriptlets so eingefügt, dass je nach Wert von res entsprechender Text ausgegeben wird. In der Ausgabe des Textes wird die Variable res mithilfe von Ausdrücken eingebunden. im ersten Skriptlet die Methode in der Variable Entsprechend können natürlich auch Verzweigungen benutzt werden (switch, alle anderen while, for...). Java-Schleifen und - Abschlieÿend soll noch erwähnt werden, dass statt der JSP-Tags auch XML-Syntax verwendet werden kann, wie man an folgendem Beispiel für die Deklarationen sehen kann: <jsp:declaration> 3.2.2 Deklarationen... </jsp:declaration>. Requestdaten auswerten Neben der vordenierten Variable out gibt es noch weitere vordenierte Variablen, die dazu benutzt werden können, das Verhalten der JSP je nach Request anzupassen. Eine davon ist die request-Variable. Über sie können Daten ei- nes HTTP-Requests ausgelesen werden. Das häugste Anwendungsbeispiel ist ein HTML-Formular. Wird ein solches ausgefüllt und abgeschickt, werden alle eingetragenen Daten im request-Objekt gespeichert und können mit der Methode request.getParameter(parameterName) abgerufen werden. Folgendes Codebeispiel, Lieblingsfarbe.jsp, soll dies demonstrieren: <% String lieblingsfarbe = request . getParameter ( " farbe "); %> < html > < head > < meta http - equiv = " Content - Type " content = " text / html ; charset = ISO -8859 -1 " > < title > Lieblingsfarbe </ title > </ head > < body > < form action = " Lieblingsfarbe . jsp " > Ihre letzte Lieblingsfarbe war : <%= lieblingsfarbe % > < br / > Neue Lieblingsfarbe : < input type = " text " name = " farbe " / > < input type = " submit " value = " Eingeben " / > </ form > </ body > </ html > 12 Am Anfang des Snippets wird der letzte eingegebene Wert für den Parameter farbe ausgelesen. Danach beginnt das HTML-Dokument. Zuerst wird der davor ausgelesene Parameter-Wert ausgegeben, danach kommt das Formular zur Eingabe des neuen Parameters. Im Attribut action innerhalb des Form-Tags wird die JSP-Datei angegeben, die aufgerufen wird, wenn das Formular abgesendet wird. In unserem Fall ist es dieselbe JSP. Wird die Seite zum ersten Mal aufgerufen, ist der Parameter-Wert natürlich erstmal null. Innerhalb des Input-Tags wird der Name des Parameters angegeben, zu dem der Wert eingetragen wird, in diesem Fall Eine weitere farbe. Möglichkeit Parameter auszulesen, ist mit der Methode getParameterNames() gegeben. Diese gibt eine Aufzählung aller Parameter zu- rück. Die Methode ist insbesondere für Debuggingzwecke interessant. Analog gibt es Methoden, um den Request-Header auszulesen, in dem sich Metainformationen wie beispielsweise akzeptierte Dateiformate, Sprache und verwendeter Browser benden: 3.2.3 getHeader() bzw. getHeaderNames(). Direktiven Um Zugri auf verschiedene Kongurationen einer JSP zu haben, unterstützt JSP drei Arten von Direktiven: Page, Include und Taglib. Diese sind insbesondere für den Compiler hilfreich, da sie Informationen über den Umgang mit der JSP selbst und ihren Inhalten liefern. Innerhalb jeder Direktive können verschiedene Attribute gesetzt werden. Die allgemeine Form einer Direkti- <%@ direktive attribut1="wert1" attribut2="wert2" ... %>, ve ist die konkrete ge mit ternativ Syntax Attribut kann ist also language: wieder beispielsweise für die Direktive <%@ page language="java" %>. XML-basierte <jsp:directive.page language="java" />. Syntax verwendet PaAl- werden: Die Page-Direktive befasst sich mit der aktuellen JSP-Datei. Mittels des Attri- import können Klassen und Packages importiert werden. Es ist das einzige page, das mehrmals in einer JSP vorkommen darf. Ein weiteres Attribut ist session, das auf true oder false gesetzt, bestimmt, ob buts Attribut der Direktive das Session-Objekt verwendet werden soll oder nicht. Per Default ist dieses Attribut auf true gesetzt. Um eine Fehlerseite anzugeben, die im Fall einer ExerrorPage mit dem Namen ception aufgerufen werden soll, kann das Attribut der JSP-Datei belegt bzw. - falls es sich bei der JSP selbst um eine Errorpage handelt - isErrorPage auf true gesetzt werden. Das Exception-Objekt wird automatisch an die denierte Fehlerseite weitergereicht. Weitere modizierbare Attribute sind Tabelle 1 zu entnehmen. Über die Include-Direktive können JSPs, HTML-Seiten, XML- und Textdateien in andere JSPs eingebunden werden. Die allgemeine Syntax ist (im Falle einer eingebundenen JSP): <%@ include file="eineAndereJSP.jsp" %>. Auf diese Art und Weise wird die eingebundene Datei während der internen Übersetzung des Java-Codes integriert. Die JSPs haben wechselseitigen Zugri auf Variablen und Methoden, die in der jeweils anderen JSP deklariert wurden. Die eingebundene Datei wird also quasi per copy&paste in die 13 language="java" import="package.class" extends="package.class" verwendete Programmiersprache importierte Klassen und Packages Superklasse; muss von Servletklasse abgeleitet sein session=true|false buffer="none|8kb|sizekb" autoflush=true|false isThreadSafe=true|false Session-Management Puergröÿe für das out-Objekt Puerleerung wenn Puer voll gleichzeitige Beantwortung mehrerer Anfragen möglich info="text" errorPage="relativeURL" contentType="mimeType" isErrorPage=true|false pageEncoding="charSet|ISO-8859-1" Info über JSP Pfad zur Fehlerseite MIME-Typ Fehlerseite Zeichensatz Tabelle 1: page-Direktiven JSP eingefügt. Eine weitere Technik ist mithilfe des include-Tags realisiert. Dieses sorgt dafür, dass die eingebundene Datei zur Laufzeit als eigener Request aufgerufen wird, und nur das Ergebnis in der umgebenden JSP ge- <jsp:include file="eineAndereJSP.jsp" %> include äquivalenten Syn<jsp:directive.include file="eineAndereJSP.jsp" %>!). Dieses Tag speichert wird. Die Syntax ist (nicht zu verwechseln mit der zu der Direktive tax hat den Vorteil, dass sich häug ändernde Dateien nicht extra neu kompiliert werden müssen und immer der Originalcode aufgerufen wird, und nicht eine Kopie davon. Der Zugri auf umgebende Variablen ist damit natürlich nicht mehr möglich, das bedeutet die aufgerufene Datei muss in sich geschlossen sein. Das include-Tag ist deshalb auch nützlich, da man nun Einuss auf Para- meter hat, die für die eingebundene Datei von Bedeutung sind. Innerhalb der <jsp:include>-Tags können Parameter überschrieben und der aufgerufenen JSP mitgegeben werden: < jsp : include page = " eineAndereJSP . jsp " > < jsp : param name = " Param1 " value = " ÜberschriebenerParam1Wert " / > </ jsp : include > Die dritte Direktive ist die taglib-Direktive. Über diese Direktive können Tag- Bibliotheken eingebunden werden, die selbstdenierte Tags oder vordenierte Tags enthalten. Das oben beschriebene Include-Tag als Alternative zur IncludeDirektive ist ein solches Tag. 3.3 Verzeichnisstruktur und Deployment Bei der Erstellung von Webkomponenten sind einige Regeln bezüglich der Verzeichnisstruktur zu beachten. Im Projektverzeichnis werden alle für die We- 14 Abbildung 5: Die Verzeichnisstruktur einer Webanwendung in Eclipse banwendung relevanten Dateien bezüglich der Webkomponenten gespeichert. Diese können innerhalb des Parentfolders beliebig strukturiert werden. Dazu gehören die dynamischen Seiten, die JSPs, aber auch statische Elemente wie HTML-Seiten, Bilder, Stylesheets, etc. Der Ordner WEB-INF ist von groÿer Bedeutung, da der Servletcontainer die Kongurationsdatei web.xml genau unter diesem Ordner erwartet und nur JSPs kompiliert, die im Parentfolder von WEB-INF abgelegt sind. Der Servletcontainer untersucht die Dateien im Parentfolder nach JSP-Tags. Sobald er diese gefunden hat, wird die JSP zur Laufzeit in eine Java-Klasse umgewandelt. Diese ist nichts anderes als ein Servlet, das anstelle der service()-Methode nun eine _jspService()-Methode für al- le HTTP-Request-Arten aufweist, die in aus Servlets bekannter Syntax genau das tut, was in der JSP mit JSP-Syntax beschrieben wurde. Danach wird das nun entstandene Servlet kompiliert und der Servlet-Container bringt den JavaCode zur Ausführung. Dabei ersetzt er die dynamischen Teile des Servlets durch statischen Content. Ausgeliefert wird die nun statische HTML-Seite. Handelt es sich bei der Webkomponente ursprünglich um ein Servlet, ist der Ablauf anders deniert. Der Source-Code der Servlets kann an beliebiger Stelle gespeichert werden. Das Servlet wird zunächst in Bytecode umgewandelt. Die entstehenden .class-Dateien werden unter WEBINF im Ordner classes abgelegt. Danach wird der Javacode wie bei JSPs ausgeführt und die HTML-Seite ausgeliefert. Der Deployment Descriptor in Form der Datei web.xml ist ein integraler Bestandteil der Webkomponenten, denn in dieser XML-basierten Datei benden sich Kongurationsinformationen, ohne die der Servletcontainer die Anwendung nicht deployen, das heiÿt bereitstellen, kann. Unter anderem sind dies Servletkongurationen. Um die bereitzustellenden Dateien an den Servletcontainer 15 weiterzugeben, müssen entweder das gesamte Projektverzeichnis an die entsprechende Stelle des Webserververzeichnisses kopiert werden, oder das gesamte Projekt als Webarchiv dorthin kopiert werden. Dieses Webarchiv, eine WAR-Datei, ist der ZIP-komprimierte WEB-INF-Ordner. Alternativ kann auch in der Kongurationsdatei des Servers (i.d.R. server.xml) der zusätzliche Verzeichnisort der Webanwendungen angegeben werden. In Abbildung 5 ist ein beispielhaftes Projektverzeichnis aus Eclipse angegeben. Die Webkomponenten sind unter WEB-INF abgespeichert, die Servlets unter Java Resources: src. Der Ordner classes unter WEB-INF ist in Eclipse versteckt. Der Deployment Descriptor bendet sich sowohl als web.xml, als auch als Kopie direkt unterhalb des WebApplicationFolder. Daneben gibt es noch sun-web.xml, ein Descriptor für Sun-spezische Kongurationen. Arbeitet man mit Eclipse, werden die Servlets und Beans automatisch kompiliert und an den Webserver weitergeleitet, was die Erstellung von Webanwendungen in Eclipse besonders einfach macht. 3.4 Servlets vs. JSPs In der Einleitung von JSPs wurde gesagt, dass sich JSPs für den Webdesigner leichter programmieren lassen als die schnell unübersichtlich werdenden Servlets. Da der Servletcontainer JSPs während des Deployments intern in ein Servlet umwandelt, lässt sich leicht nachvollziehen, dass für jede JSP ein äquivalentes Servlet existiert. Umgekehrt ist dies jedoch nicht der Fall, und dies ist mitunter ein Grund, weswegen JSPs Servlets nicht vollständig ersetzen können. Eine Funktionalität, die JSPs nicht umsetzen können, ist das Senden von binären Formaten, wie PDF-Dateien oder Bildern. Der PrintWriter der JSP gehört zu den zeichenorientierten Ausgabeströmen und kann deswegen keine byteorientierten Binärformate ausgeben. Eine weitere servletspezische Funktionalität ist das sogenannte URL-Mapping. Wie im Kapitel Deployment erwähnt, müssen Servlets von Hand kompiliert und an den Webserver weitergegeben werden. Dies mag umständlich erscheinen, bedeutet jedoch mehr Kontrolle für den Entwickler. Der Vorteil des Kompilierens auÿerhalb des Webservers ist, dass bestimmte Kongurationen im Deployment Descriptor bezüglich des Servlets erfolgen können, dazu gehören die Angabe des Pfades zu den Source-Dateien der Servlets, das Binden des Servlets an einen symbolischen Namen, und das URL-Mapping, bei dem festgelegt wird, welches Servlet unter welcher URL aufzurufen ist. Dadurch können Servlets unter mehreren URLs aufgerufen werden, worüber man bei JSPs keinen Einuss hat. Es empehlt sich also, JSPs und Servlets in einer Webanwendung je nach Aufgabenbereich zu kombinieren. 4 JavaBeans Um Funktionalität in leicht austauschbare und wiederverwendbare Komponenten aufzuteilen und damit die Modularität der Anwendung einzuhalten, kommen JavaBeans zum Einsatz. Diese sind im Gegensatz zu EJBs nicht Java EE - spe- 16 zisch, eignen sich aber wegen ihrer Kompaktheit und einfachen Anwendung besonders für die Verwendung in JSPs und sollen deshalb an dieser Stelle näher behandelt werden. 4.1 Struktur einer JavaBean Beans sind Java-Klassen mit leerem Konstruktor und Methoden für die Speicherung von Variablen. Diese Variablen werden Attribute oder auch Eigenschaften genannt. Um deutlicher von den Attributen der Tags unterscheiden zu können, soll der Begri Eigenschaft für die Bean-Variable verwendet werden. Mit der setEigenschaft(), wird der Wert der Eigenschaft getEigenschaft()-Methode ausgelesen bzw. bei booleschen Variablen die Methode isEigenschaft() aufgerufen. Die Methoden müssen im Gegensatz zur privaten Eigenschaft-Variable public sein, um set-Methode, zum Beispiel gesetzt und entsprechend mit der sie von auÿen zugreifbar zu machen. Neben diesen Methoden kann die Bean auch noch andere Methoden implementieren, die auf den Eigenschaften arbeiten. Folgende Bean mit Eigenschaft email und einer zusätzlichen Validierungsmethode soll die Struktur veranschaulichen: EmailBean { String email ; p r i v a t e boolean valid ; p u b l i c void setEmail ( String email ) { t h i s . email = email ; validate () ; } p u b l i c String getEmail () { r e t u r n email ; } p u b l i c boolean isValid () { r e t u r n valid ; } p u b l i c void validate () { valid = (( email != n u l l ) && ( email . contains ( " @ " ) ) ) ; } public class private } Wie bei Servlets ist es nicht von Bedeutung, wo der Source-Code der erstellten Beans abgelegt wird; wichtig ist nur, dass die kompilierten .class-Dateien unter WEB-INF/classes zu nden sind. Alternativ kann man auch mehrere kompilierte Beans als Jar-Datei dort ablegen. 4.2 Syntax im JSP-Code Die Bean kann in einem Skriptlet per Instanziierung erzeugt werden: <% EmailBean ebean = new EmailBean(); %> 17 Einfacher ist jedoch die Einbindung in die JSP mittels des jsp:useBean-Tags: <jsp:useBean id="ebean" class="path.to.beans.EmailBean" /> Beim Aufruf müssen verschiedene Attribute des Tags deniert werden, darunter id, das angibt, unter welchem symbolischen Namen die Bean innerhalb der JSP angesprochen wird, und class, das den Pfad zum Package der Bean angibt. Ein weiteres optionales Attribut ist scope, das den Gültigkeitsbereich der Bean angibt (page, request, session, oder application). Auÿerdem kann mit dem Attribut type ein Typ speziziert werden, wenn er von dem der angegebenen Klasse abweicht. Dieser Typ kann ein Interface oder eine Superklasse sein, die die Bean implementiert. Um die Getter- und Setter-Methoden der Bean innerhalb der JSP aufzurufen, gibt es wieder sowohl die klassische Va- bean.getEmail()) und die Tag-Variante jsp:getProperty). Diese Tags haben verschiedene Atname deniert den Namen der verwendeten Bean. Die riante in Java-Syntax (beispielsweise (jsp:setProperty bzw. tribute: Das Attribut Eigenschaft der Bean, die gesetzt bzw. ausgelesen werden soll, wird durch das Attribut Attribut property gesetzt. In setProperty gibt es zwei weitere Attribute. Das value beschreibt den Wert, mit dem die Eigenschaft belegt werden soll. Eine sehr nützliche Funktionalität des Tags ist, dass ein Requestparameter direkt in die Anweisung mit eingebunden werden kann. So kann man statt des value-Attributs das Attribut param verwenden, um den Namen eines Request- Parameters anzugeben, dessen Wert der Eigenschaft der Bean zugewiesen werden soll. <jsp:setProperty name="ebean" property="email" value="[email protected]"/> email der JavaBean ebean den Wert [email protected] zuweisen. Gibt man statt eines normalen Strings für das Attribut param einen Stern (*) an, ergibt sich eine sehr nützliche Funk- würde beispielsweise der vorher denierten Eigenschaft tionalität: Die Werte der Request-Parameter werden iteriert und nacheinander den Eigenschaften der JavaBean zugewiesen unter der Bedingung, dass diese gleichnamig vorhanden sind. Ein Auszug einer JSP, die die vorher denierte EmailBean verwendet, könnte folgendermaÿen aussehen: < jsp : useBean id = " ebean " c l a s s = " beans . EmailBean " / > ... < body > <% String emailAddr = ( String ) ebean . getEmail () ; i f (! ebean . isValid () ) { %> Ihre Eingabe war leider fehlerhaft . < form action = " JSPWithEmailBean . jsp " > < input type = " text " name = " email " value = " < jsp : getProperty name = " ebean " property = " email " / > " / > < input type = " submit " value = " Email eingeben " / > 18 </ form > < br / > <%} e l s e {% > Danke für die Eingabe . Ihre Emailadresse lautet : < jsp : getProperty name = " ebean " property = " email " / > <%} % > </ body > 4.3 Einsatz von JavaBeans An obigem Beispiel wird die Wiederverwendbarkeit und Austauschbarkeit der Beans deutlich. Die Funktionalität des Speicherns einer Emailadresse und deren Validierung kann nun in mehreren JSPs verwendet werden oder beliebig durch die Implementierung einer anderen Bean ersetzt werden. Auÿerdem wird durch Einsatz von Beans das Model-View-Controller-Prinzip verwirklicht, das besagt, dass Geschäftslogik und Darstellung möglichst unabhängig voneinander programmiert werden sollen: Die EmailBean ist dem Model-Teil zugeordnet. Sie wird vom Java-Programmierer kodiert, ohne dass dieser HTML-, JSP- oder XML-Syntax verwenden muss. Die Bean arbeitet unabhängig von ihrem Einsatzort in der View. Die JSP übernimmt das Controlling, also die Vermittlung zwischen Model und View, und ist gleichzeitig die View selbst. Diese muss unter Umständen kein Java enthalten, wenn Parameter nur durch die XML-Tags bearbeitet und ausgelesen werden. Die konkrete Implementierung des Models (in unserem Beispiel unter anderem der Validierungsmechanismus), ist der View verborgen. So kann sich der Programmierer auf die Darstellung konzentrieren, ohne sich um die korrekte Ausführung der Geschäftslogik kümmern zu müssen. Da eines der zentralen Konzepte von Java EE - Webanwendungen ebenfalls die Trennung von Darstellung und Geschäftslogik im Schichtenmodell ist, lassen sich JavaBeans gut innerhalb der Java EE - Technologie einsetzen. 4.4 JavaBeans vs. Custom Tags Ähnlich wie bei JavaBeans lässt sich auch durch das Denieren eigener Tags, sogenannter Custom Tags, Funktionalität aus den JSPs auslagern. Die Funktionalität der Tags lässt sich in JSP über die XML-ähnliche Syntax in die JSPs integrieren und erlaubt unerfahrenen Webdesignern so die Anwendung der Tags, auch ohne Java-Kenntnisse. Sobald die Geschäftslogik geschrieben wurde, können die Tags zusammengefasst als Taglibs an Webentwickler weitergegeben werden, so dass diese sie verwenden können ohne sich um die konkrete Implementierung kümmern zu müssen. Dies ist ein groÿer Vorteil von Custom Tags gegenüber JavaBeans. JavaBeans sind umfangreicher; dadurch dass sie nicht wie Custom Tags in Taglibs verpackt sind, lässt sich jedoch der Bean-Code direkt zu jeder Zeit modizieren. JavaBeans und Custom Tags können parallel eingesetzt werden und es hängt letztendlich von der Entwicklungsumgebung ab, welche Technologie zum Einsatz kommen soll. 19 5 Zusammenfassung Seit 2006 ist nun die 5. Version, Java Enterprise Edition 5, auf dem Markt und ist eine Plattform geworden, die eine Fülle von Schnittstellen und Spezikationen anbietet, um vielseitige und mächtige, serverseitige Anwendungen zu erstellen. Kurz gesagt: Java EE ist das am weitesten verbreitete Betriebssystem für Webund Business-Anwendungen [6, S. 19]. Am Beispiel Google lässt sich nachvollziehen, dass dynamische, serverseitige Webanwendungen heute noch an Bedeutung gewinnen. Andere Technologien wie PHP und CGI eignen sich zwar ebenfalls für Webanwendungen, sind jedoch nicht so skalierbar wie Java EE - Anwendungen und werden bei umfangreicheren Anwendungen in ihren Komponenten schnell unübersichtlich. Durch die Objektorientiertheit und klare Aufgabenverteilung von Java und die Zusatzfunktionalitäten des EJB-Containers unterstützt Java EE die Verteiltheit und Portierbarkeit der Webanwendungen, die heutzutage eine groÿe Rolle spielen, und ist deswegen die richtige Wahl für zuverlässige, sichere und leistungsfähige Webanwendungen. 6 Demoprojekt Das Demorojekt zeigt eine typische Webanwendung, die mit Java EE programmiert wurde. Es handelt sich um einen Webshop für individuell bedruckbare diverse Artikel, u.a. T-Shirts und Tassen. Es kann zwischen verschiedenen Produkten ausgewählt und diese können einem Einkaufswagen hinzugefügt werden. Für die Weboberäche wurde dabei das Framework JSF[6] verwendet, das die Erstellung diverser JSPs und Stylesheets erleichtert und unter einem zentralen JSF-Servlet verwaltet. Die Geschäftslogik hinter dem Webshop, also der Einkaufswagen und die verschiedenen Produkt-Klassen, wurde in entsprechende JavaBeans aufgeteilt. Die Webanwendung ist beliebig ausbaubar und könnte z.B. wie in gängigen Webshops um eine Datenbank erweitert werden, um Daten verschiedener Käufer zu speichern und einen Admin-Account zu realisieren, unter dem die verschiedenen Käufe abgerufen und überwacht werden können. Literatur Eclipse Web Tools Platform. entwickler.press, 2008. Thomas Reuhl Dieter Eickstädt. J2EE mit Struts & Co. Markt+Technik [1] Kai Brüssau. [2] Verlag, 2004. [3] Sun Microsystems. JavaTM Platform Enterprise Edition, v 5.0 - API Specications, May 2006. http://java.sun.com/javaee/5/docs/api/. [4] Sun Sun Microsystems. Java System The Application Java EE Server http://java.sun.com/javaee/5/docs/tutorial/doc/. 20 5 9.1, Tutorial. October For 2008. [5] Michael Seeboerger-Weichselbaum. JSP mit Tomcat. Markt+Technik Ver- lag, 2003. [6] Thomas Stark. Java EE 5. Einstieg für Anspruchsvolle. 2007. 21 Addison-Wesley,