Praktikum aus Softwareentwicklung 2, Stunde 12 Lehrziele/Inhalt 1. JavaServer Pages Dynamischer Kontext in JavaServer Pages Mit JavaServer Pages (JSP) kann man dynamischen Inhalt erzeugen. Dazu kann man: Java-Code in die Seite schreiben, Ausdrücke in der Skriptsprache Expression Language auswerten und programmierte Tags einfügen. Skriptelemente Skriptelemente sind die älteste und unübersichtlichste Art Logik in JSP einzubringen. Skriptelemente enthalten Java-Code (Scriptlet) der direkt in die Handler-Methode kopiert wird wenn der WebContainer das Servlet aus der JSP erzeugt. Der JSP-Compiler erkennt Scriptlets an dem KlammernPaar: <% und %>. Abbildung 21 zeigt ein einfaches Beispiel für Scriptlets. Man sieht, dass Scriptlets und HTML beliebig gemischt werden kann. Das erste Scriptlet beginnt mit einer Schleife, dann kommt HTML-Code und das zweite Scriptlet schließt die Schleife ab. Der JSP-Compiler kopiert dabei den HTML-Code in out.write-Anweisungen und den Scriptlet-Code direkt in die generierte Handler-Methode. Vertippt man sich im Java-Code, kann die Fehlersuche mühsam sein. Je nach verwendetem Web-Container werden Fehlermeldungen vom Java-Compiler mehr oder weniger auf die eigentliche Fehlerstelle in der JSP-Seite zurückgeführt. <%@page contentType="text/html" pageEncoding="UTF-8"%> <html> <head><title>JSP Scriptlet Page</title></head> <body> <h1>Let's Count!</h1> <% for (int i = 0; i < 10; ++i) { out.println(i); %> , <% } %> ... </body> Let's Count! </html> 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , ... Abbildung 21) Beispiel-JSP mit Scriptlet In seltenen Fällen muss man in JSP Felder und Methoden deklarieren. Das ist in DeklarationsElementen möglich, sie werden mit <%! eingeleitet und mit %> abgeschlossen. © Markus Löberbauer 2010 Seite 48 <%@page contentType="text/html" pageEncoding="UTF-8" import="java.util.Date"%> <html> <head><title>JSP Declarations Page</title></head> <body> <% String message = format("Hello World!"); out.println(message); %> </body> <%! private static final String MESSAGE_FORMAT = "%s : %s"; %> <%! private String format(String message) { return String.format(MESSAGE_FORMAT, new Date(), message); } %> </html> Fri Jun 04 08:27:27 CEST 2010 : Hello World! Abbildung 22) Beispiel-JSP mit Feld- und Methoden-Deklaration Abbildung 22 zeigt wie man in einer JSP ein Feld deklariert, hier die Konstante MESSAGE_FORMAT, und wie man Methoden deklariert, hier die Methode format. Wie bei programmierten Servlets muss man auch bei durch JSP generierten Servlets darauf achten, dass durch ein Objekt mehrere Benutzeranfragen gleichzeitig bearbeitet werden können. Also auch hier gilt, Felder sind potentiell ein Synchronisations-Problem. Im Beispiel in Abbildung 22 rufen wir eine Methode auf und geben das Ergebnis in der Web-Seite aus. Das wird in JSP mit Expression-Elementen unterstützt. Expression-Elemente werden mit <%= eingeleitet und mit %> abgeschlossen. Innerhalb eines Expression-Elements muss ein Ausdruck stehen, zB: eine Berechnung oder ein Methodenaufruf mit Rückgabewert. In Abbildung 23 werden Expression-Elemente verwendet, um das Ergebnis der Methode format und um die Zahlen 0 bis 9 in einer Schleife auszugeben. Kommentare in JSP werden in <%-- und --%> eingeschlossen, diese Kommentare kommen nur in der JSP vor. Will man Kommentare auch im generierten Servlet wiederfinden, dann muss man sie als normale Java-Kommentare in Scriptlets schreiben. Weitere Befehle in JSP: <jsp:include>: Einfügen einer anderen Seite zur Laufzeit. <jsp:forward>: Weiterleiten an eine andere Seite. <jsp:param>: Parameter an eine andere Seite weitergeben, die mit <jsp:include> oder <jsp:forward> verwendet wird. <jsp:useBean>: Verwenden von JavaBean-Komponenten in JSP. Syntax: <jsp:useBean id="Instanzname" scope="Geltungsbereich" class="Klassenname"/> <jsp:getProperty>, <jsp:setProperty>: Abfragen bzw. Setzen eines Bean-Properties. © Markus Löberbauer 2010 Seite 49 Die JSP-Syntax der Skriptelemente mit dem Prozentzeichen passt nur schlecht zum XML-Stil von HTML. Deshalb gibt es für die Skriptelemente eine XML-Syntax. Dabei heißen die Tags: <jsp:scriptlet> für Scriptlets, <jsp:expression> für Ausdrücke und <jsp:declaration> für Deklarationen. Dabei ist zu beachten, dass innerhalb einer Seite entweder konsequent die JSP-Syntax oder die XML-Syntax verwendet werden muss. Einen Überblick über die gesamte Syntax von JSP kann man im JSR 152 oder unter http://java.sun.com/products/jsp/docs.html bekommen. <%@page contentType="text/html" pageEncoding="UTF-8" import="java.util.Date"%> <html> <head><title>JSP Expressions Page</title></head> <body> <%= format("Hello World!") %><br> <% for (int i = 0; i < 10; ++i) { %> <%= i %> <% } %> </body> <%! private static final String MESSAGE_FORMAT = "%s : %s"; %> <%! private String format(String message) { return String.format(MESSAGE_FORMAT, new Date(), message); } %> </html> Fri Jun 04 08:42:31 CEST 2010 : Hello World! 0123456789 Abbildung 23) Beispiel-JSP mit Expressions Implizite Objekte in JSP In den Scriptlet-Beispielen haben wir mit out.println Text in die Web-Seite geschrieben. Aber wo kommt dieses out her? In JSP werden einige Objekte in der Handler-Methode implizit zur Verfügung gestellt: out, Klasse: JspWriter (Schreiben von Text in die Web-Seite) request, Klasse: HttpServletRequest response, Klasse: HttpServletResponse session, Klasse: HttpSession application, Klasse: ServletContext config, Klasse: ServletConfig exception, Klasse: JspException (Nur verfügbar in Error-Pages. Treten Fehler in Servlets oder JSP auf wird standardmäßig eine technische Fehlerseite erzeugt, die hilft dem Entwickler, für den Anwender wirkt sie aber unprofessionell. Daher kann man in einer JSP page-Direktive eine JSP angeben die im Fehlerfall angezeigt werden soll, zB: <%@ page errorPage="/error.jsp" %>. Auch im Deployment Descriptor (web.xml) können mit error-page-Abschnitten Fehlerbehandlungsseiten angegeben werden. Mit error-code können © Markus Löberbauer 2010 Seite 50 http-Fehlercodes abgefangen werden (zB: 404); und mit exception-type können Exceptions abgefangen werden. pageContext, Klasse: PageContext (Kapselt die Impliziten Objekte, kann zB benutzt werden, um diese an eine Methode zu übergeben. page, Klasse: Object (Verweis auf das Seiten-Objekt = this-Pointer) Expression Language Skriptelemente verleiten dazu, zu viel Java-Code in JSP einzubetten, daher möchte man gerne auf Skriptelemente verzichten. Die Verwendung von Beans ist aber häufig zu aufwändig oder zu eingeschränkt. Aus diesen Gründen hat Sun mit JSP 2.0 die Expression Language (EL) eingeführt. Ausdrücke in EL beginnen mit einem Dollar-Zeichen und sind in geschwungene Klammern eingeschlossen, zB: ${42.0 / 23}, ${person.name} oder ${car.engine.power}. EL erlaubt den Zugriff auf Properties von Properties, usw. Damit ist es mächtiger als die JSP-BeanSyntax. Und dabei wesentlich kompakter. Für jeden Property-Zugriff kommt ein Punkt gefolgt vom Namen des Properties. Existiert in einer Klasse eine Methode getName(), dann heißt das Property name. Der Zugriff erfolgt damit über ${objektname.name}. Der Aufbau von EL-Ausdrücken ist in Abbildung 24 zu sehen. ${ersterBezeichner.weitereBezeichner} Implizite Objekte Attribute aus pageScope requestScope sessionScope applicationScope pageScope requestScope sessionScope applicationScope param paramValues Objekte werden zuerst im Page-, dann im Request-, dann im Session- und wenn sie dort auch nicht gefunden werden im Application-Scope gesucht. header headerValues cookie initParam Schlüssel einer Map oder Property Muss sich an die Java Namenskonvention halten! pageContext Abbildung 24) Aufbau eines EL-Ausdrucks Muss man auf Elemente in einer Map oder Liste zugreifen, dann entsprechen die Zugriffsnamen nicht nicht immer den Java-Namenskonventionen. In diesem Fall gibt es den Klammer-Operator ([]) als Alternative zum Dot-Operator(„.“). Die Syntax sieht dann wie folgt aus: ${objektname[bezeichner]} wobei objektname eine Map, ein Bean, eine List oder ein Array sein kann; bezeichner ist ein Schlüssel © Markus Löberbauer 2010 Seite 51 in der Map, ein Property, ein Listen-Indey bzw. ein Array-Index. Der Zugriff kann auch verschachtelt sein zB: ${cars[favoiritCars[0]]}. Achtung, Property-Namen müssen unter Anführungszeichen gestellt werden, sonst wird der Name selbst als Objekt gesucht, zB: der Ausdruck im Dot-Stil ${person.name} entspricht ${person["name"]} im Klammer-Stil. Verwendet man nur ${person[name]}, dann wird zuerst name ausgewertet und das Ergebnis als Property-Name in person gesucht. Operationen in der Expression Language Mit EL kann man arithmetische und logische Operationen ausdrücken: Addition (+), Subtraktion (-), Multiplikation (*), Division (/, div) und Divisionsrest (%, mod) sowie logisches Und (&&, and), Oder (||, or) und Nicht (!, not). Vergleiche durchführen: Gleichheit (==, eq), Ungleichheit (!=, ne), Kleiner (<, lt), Grösser (>, gt), Kleiner gleich (<=, le) und Grösser gleich (>=, ge). Die folgenden Literale sind in EL definiert: true, false, null, und empty (zeigt an ob ein Attribute gesetzt ist, zB: ${not empty persons}. Das Schlüsselwort instanceof ist reserviert, hat aber noch keine Bedeutung. Java Standard Tag Libaray Mit EL kann man einfach auf einzelne Werte zugreifen. Häufig benötigt man aber auch einfache Kontrollstrukturen wie Iterationen oder Alternativen. Dazu kann die Java Standard Tab Jibrary (JSTL) genutzt werden. Seit JSP 2.1 (JavaEE 5) ist die JSTL in Version 1.2 Teil der JavaEE-Spezifikation. Damit man die JSTL in Tomcat nutzen kann muss man die Dateien jstl.jar und standard.jar entweder in das lib-Verzeichnis von Tomcat oder seiner Web-Anwendung kopieren. Die Dateien sind in der Standard-Installation von Tomcat in den Beispielanwendungen zu finden. Die JSTL ist nach Aufgaben in die Bereiche Core, Formatierung, Funktionen, SQL Datenbank-Zugriff und XML Verarbeitung geteilt. Wir werden kurzen Einblick auf den Core der JSTL geben; weitere Informationen können im JSR-52 nachgelesen werden. Wichtige Tags der Core Tag Library sind: <c:out>, <c:forEach>, <c:if>, <c:choose>, <c:when>, <c:otherwise>, <c:url> und <c:param>. <c:out> Mit dem Tag out kann man berechnete Ausgaben in die Webseite machen. Dabei wird null mit einem gegebenen Default-Wert ersetzt, ist keiner gegeben mit dem leeren String. Interessant ist auch, dass folgende HTML-Sonderzeichen ersetzt werden: < gegen &lt; > gegen &gt; & gegen &amp; ' gegen &#039; " gegen &#034; <c:forEach> Mit dem forEach-Tag kann man über Sammlungen oder Zahlenbereiche iterieren, Abbildung 25 zeigt eine Beispielanwendung. In der page-Direktive wird isELIgnored auf false gesetzt, das stellt sicher, dass die EL-Ausdrücke richtig übersetzt werden. In Tomcat 6 und Glassfish 3 ist das bereits der © Markus Löberbauer 2010 Seite 52 Standardwert. In der Beispielanwendung wird davon ausgegangen, dass eine Sammlung mit dem Namen „Cars“ in einem der Scopes verfügbar ist. <%@ page isELIgnored="false" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <html> <body> Print all cars: <c:forEach var="car" items="${Cars}"> ${car}<br /> </c:forEach> Print the numbers from 0 to 23 with a step of 5: <c:forEach begin="0" end="23" step="5" varStatus="counter"> ${counter.count}<br /> </c:forEach> </body> ... </html> Abbildung 25) JSTL Beispiel mit <c:forEach> <c:if> Der Tag if wird eingesetzt wenn optionale Teile in einer JSP vorhanden sind, Abbildung 26 zeigt ein Beispiel. Anders als in Programmiersprachen fehlt bei dem JSTL if-Tag ein else-Zweig, benötigt man eine Auswahl aus mehreren Alternativen muss man entweder jede Alternative mit einem if-Tag eindeutig beschreiben oder einen choose-Tag benutzen. <%@ page isELIgnored="false" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" <html> <body> <c:if test="${car eq 'Smart'}"> Be smart drive Smart! </c:if> <c:if test="${car eq 'SUV'}"> Real man drive hard! </c:if> </body> ... </html> %> Abbildung 26) JSTL Beispiel mit <c:if> <c:choose>, <c:when>, <c:otherwise> Der choose-Tag entspricht der switch-Anweisung oder if-else-if-Kaskade in Programmiersprachen, Abbildung 27 zeigt ein Beispiel. © Markus Löberbauer 2010 Seite 53 <%@ page isELIgnored="false" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" <html> <body> <c:choose> <c:when test="${car eq 'Smart'}"> Be smart drive Smart! </c:when> <c:when test="${car eq 'SUV'}"> Real man drive hard! </c:when> <c:otherwise> Porsche, all a lady can expect. </c:otherwise> </c:choose> </body> ... </html> %> Abbildung 27) JSTL Beispiel mit <c:choose>, <c:when> und <c:otherwise> <c:url>, <c:param> Mit <c:url> kann man Urls mit Parametern in JSP zusammensetzen, siehe Abbildung 28. Dieser Tag ermöglicht Webanwendungen ohne Cookies, dazu fügt der url-Tag eine Session-ID als Parameter an die Url, falls der Client keine Cookies unterstützt. <%@ page isELIgnored="false" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <html> <body> Please visit our <a href="<c:url value='exhibition.do'> <c:param name='color' value='${customer.favouritColor}' /> </c:url>"> car exhibition</a> to see your next vehicle! <a href="<c:url value='logout.jsp' />" >Logout</a> </body> </html> Abbildung 28) JSTL Beispiel mit <c:url> und <c:param> JSP aus Servlets Nutzen In sauber entwickelten Programmen sollte die Businesslogik aus Servlets angesprochen werden und die Oberfläche in JSP. Diese Vorgangsweise heißt in Java Model 2 Architektur und setzt das ModelView-Controller-Muster bei Webanwendungen um. Über die Klasse javax.servlet.RequestDispatcher kann man von einem Servlet auf ein anderes Servlet oder JSP umleiten. Einen RequestDispatcher kann man aus dem ServletContext über die Methode getRequestDispatcher(String absolutPath) aus einem Absoluten Pfad holen. Hier darf auch ein Pfad in das Verzeichnis WEB-INF zeigen, obwohl dieses Verzeichnis gegen Zugriffe von außen geschützt ist. Oder über die Methode getNamedDispatcher(String name) aus einem Namen erzeugen, der Name entspricht dem aus der web.xml (servlet-name). Relativ zum aktuellen Servlet kann man sich auch aus dem ServletRequest über die Methode getRequestDispatcher(String path) einen RequestDispatcher holen. © Markus Löberbauer 2010 Seite 54 Hat man einen RequestDispatcher kann man den Request über die Methoden include oder forward weiterleiten. Nutzt man die Methode include behält man die Kontrolle und kann nachdem das gerufene Servlet fertig ist noch etwas ausgeben/aufräumen. Meistens ist das aber unnötig und man verwendet forward, dabei gibt man die Kontrolle an das gerufene Servlet ab. Die aufbereiteten Daten gibt man an das gerufene Servlet als Attribute im HttpServletRequest mit. Attribute kann man in den Request über die Methode setAttribute(String name, Object o) stellen und über getAttribute(String name) abfragen. In JSP kann man Attribute über EL abfragen, wobei man den Namen des Attributes angeben muss, zB: ${name}. Abbildung 29 zeigt ein Servlet, den Parameter name aus dem Request ausliest, den gelesenen Wert in Großbuchstaben verwandelt, das Ergebnis als Attribut ablegt und den Request an eine JSP weiterleitet. Wobei die JSP im Verzeichnis WEB-INF liegt und von außen nicht direkt aufgerufen werden kann. Die zugehörige JSP ist in Abbildung 30 gezeigt. @WebServlet(name = "Greeter", urlPatterns = {"/Greeter"}) public class Greeter extends HttpServlet { public static final String NAME_PARAMETER = "name"; @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String name = request.getParameter(NAME_PARAMETER); String upperCaseName = name != null ? name.toUpperCase() : "???"; request.setAttribute(NAME_PARAMETER, upperCaseName); RequestDispatcher rd = getServletContext() .getRequestDispatcher("/WEB-INF/jsp/namePrinter.jsp"); rd.forward(request, response); } } Abbildung 29) Beispiel: Servlet mit Request-Weiterleitung <%@page contentType="text/html" pageEncoding="UTF-8"%> <%@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> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Name Printer Page</title> </head> <body> <h1>Hello <c:out value="${name}"/>!</h1> </body> </html> Abbildung 30) Beispiel: JSP das einen Namen über EL und <c:out> ausgibt. © Markus Löberbauer 2010 Seite 55 Schemadefinitionen für web.xml Der Deployment Descriptor (web.xml) ist eine XML-Datei, hier sind die Rahmen für die gängigen Versionen abgebildet. Version 2.3 wird noch über DTD beschrieben, d.h. die Elemente innerhalb der web.xml müssen in der richtigen Reihenfolge angegeben werden, zB: servlet vor servlet-mapping. Version 2.3 <?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"> <web-app> <!-- ... --> </web-app> Version 2.4 <?xml version="1.0" encoding="ISO-8859-1"?> <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <!-- ... --> </web-app> Version 2.5 <?xml version="1.0" encoding="ISO-8859-1"?> <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <!-- ... --> </web-app> Version 3.0 <?xml version="1.0" encoding="UTF-8"?> <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> <!-- ... --> </web-app> © Markus Löberbauer 2010 Seite 56