FH Fulda FB AI Prof. Dr. U. Schröter Servlets und Java Server Pages (JSP) (Quelle: java Magazin 5/2000) Servlets Servlets sind zum Java Servlet-API konforme Java-Objekte, d. h. spezielle, serverseitig ausführbare Java-Klassen. Grundsätzlich lassen sich Servlets für Aufgaben der dynamischen Generierung von HTML-Seiten einsetzen. Servlets haben kein grafisches Benutzerinterface. Die Java Servlet-API definiert mit Schnittstellen Eigenschaften und Methoden, die ein Servlet zur Verfügung stellen muss. Servlets können vom Server dynamisch über ein Netzwerk geladen werden. Pakete: javax.servlet allgemeine Klassen und Schnittstellen zur Unterstützung protokollunabhängiger Servlets, javax.servlet.http von javax.servlet abgeleitete Klassen und Interfaccs mit spezieller http-Funktionalität. Die Klasse javax.servlet.http.HttpServlet stellt die Ausgangsbasis für eigene, http-spezifische Servlets zur Verfügung. Anders als bei CGI-Anwendungen läuft ein Servlet nicht in einem separaten Prozess, sondern in einem Thread innerhalb der Java Virtual Machine des Serverprozesses ab. Initialisierung von Servlets Nach dem Starten wartet der Server auf Anfragen der Clients. Zielt ein Request auf ein noch nicht geladenes Servlet, wird das Servlet geladen und genau eine Instanz erzeugt. Anschließend ruft die Servlet-Engine die init()-Metbode des Servlets auf, wobei laut APISpezifikation garantiert wird, dass diese Methode, vor der Weitergabe einer Abfrage, genau einmal vollständig ausgeführt wird. Beim Aufruf der Initialisierungsmethode wird dem Servlet als Argument eine Objekt-Referenz des Typs javax.servlet.ServletConfig übergeben. Diese Schnittstelle ServletConfig ermöglicht dem Servlet einen Zugriff auf seine Ausführungsumgebung, d. h. den Zugriff auf die Initialisierungsparameter sowie die Bereitstellung eines ServletContext-Objektes. Verarbeitung Jeder Request führt zu einem Aufruf der Methode service (ServletRequest, ServletResponse). Dabei wird dem Servlet jeweils ein Objekt vom Typ javax.servlet.ServletRequest und javax.servlet.ServletResponse übergeben. Die in diesen Schnittstellen definierten Methoden entsprechen im Wesentlichen den CGI-Umgebungsvariablen. Am wichtigsten für das Arbeiten mit Servlets sind die im Interface ServletResponse definierten Methoden getOutputStream() und getWriter(), zum Übertragen von binären Daten bzw. zur Übertragung von Zeichenketten an den aufrufenden Client. Freigabe 1 Eine Servlet-Engine kann durch den Aufruf der Methode destroy() das Servlet wieder aus dem Speicher entfernen, wodurch nicht mehr benötigte Ressourcen wieder freigeben werden. Sessions Das HTTP-Protokoll ist zustandslos. Dies ist problematisch, wenn aber eine Anwendung realisiert werden soll, für die es notwendig ist, benutzerspezifische Informationen über eine zusammenhängende Folge von Anfragen und Antworten aufzuheben. Ein Beispiel ist ein Online-Shop-System, in dem Einkaufs- und Zahlungsinformationen eines Benutzers in einem geschlossenen Ablauf hinweg im System gehalten werden müssen. Zur Unterstützung dieser Benutzerverwaltung stellt die Servlet-API das Interface HttpSession aus dem Paket java.servlet.http zur Verfügung. Sie realisiert das Konzept des Session Tracking. HttpSession stellt alle notwendigen Methoden bereit, um eine Session zu erzeugen, ihre Gültigkeit festzustellen oder wieder zu löschen. Einer Session können Objekte zugeordnet werden. Zu diesem Zweck verfügt HttpSession über die Methoden putvalue(String name, Objectvalue) zum Hinzufügen, Object getValue(String name) zum Lesen und removeValue(String name) zum Löschen eines Session-Objektes. Zudem kann mit String[] getvalueNames() festgestellt werden, welche Objektnamen verwendet werden. Intern arbeiten Webserver zur Realisierung des Session-Trackings meist mit Cookies oder URLRewriting. Beide Verfahren werden von der Servlet-API unterstützt. Beim URL Rewriting werden die in einer HTML-Seite enthaltenen URLs so verändert, dass sie eine für den Server eindeutige Identifikationsnummer, die Session-ID, enthalten. Der Vorteil dieses Verfahrens ist die Unterstützung von Browsern die keine Cookies kennen, bzw. keine Cookies akzeptieren. Parallele Client-Anforderungen Mehrere gleichzeitig eintreffende Anfragen an ein Servlet führen zu einer parallelen Ausführung der service()-Methode desselben Servlets, so kann es bei gemeinsamen Ressourcen zu Konflikten kommen. Java stellt verschiedene Möglichkeiten zur Gewährleistung der Thread-Sicherheit zur Verfügung, die hier genutzt werden müssen. Zur Synchronisation paralleler Prozesse ist in Java das Konzept des Monitors implementiert. Ein Monitor ist die Kapselung eines kritischen Bereichs, mit Hilfe einer automatisch verwalteten Sperre. Diese Sperre wird beim Betreten des Monitors gesetzt und beim Verlassen wieder zurückgenommen. Ist beim Eintritt bereits eine Sperre gesetzt, so muss gewartet werden, bis der Konkurrent die Sperre freigegeben und den Monitor verlassen hat. Das Monitor-Konzept wird durch das Voranstellen des Schlüsselworts synchronized realisiert. So besteht die Möglichkeit, entweder die komplette Methode oder einen Block innerhalb einer Methode zu schützen. Schreibende Zugriffe auf gemeinsame Ressourcen müssen synchronisiert werden. Alternativen Microsoft stellt mit den Active Server Pages (ASP) eine Kombination von HTML-Seiten mit integriertem Source-Code zur Verfügung. Der Zugriff auf Objekte außerhalb der ASP erfolgt dann mit den proprietären Techniken von Microsoft wie ODBC, COM, DCOM, OLE etc.. Damit lassen sich das Layout in den ASP-Vorlagen und der Sourcecode in den externen Objekten voneinander trennen. 2 Ähnlich geht es mit PHP, einem Hypertext-Preprozessor. Dabei werden Vorlagendateien *.php durch eine Webserver-Erweiterung interpretiert und dann ,,reines" HTML an den Client gesandt. Etwas standardisierter im Rahmen von HTML sind Server Side Indudes (SSI). Auch diese können direkt innerhalb von HTML-Dateien dynamische Information, z.B. Datum und Uhrzeit, einbinden. Sie können auch CGI-Programme starten und deren Ausgaben in die HTML-Datei einbetten. Server Side Includes werden auch serverseitig ausgewertet und natürlich nur dann, wenn der Webserver diese SSIs auch unterstützt. Damit ein Web-Server auch erkennt, dass eine HTML-Datei Server-Side-Include-Anweisungen enthält, bekommen die dazugehörigen Vorlagendateien meist die spezielle Dateinamenendung .shtml, .shtm oder .sht. Java Server Pages Während ASP integraler Bestandteil der Microsoft Plattform ist und somit als konkrete Implementierung für Webserver als Erweiterungskomponente zur Verfügung steht, hat Sun mit der Spezifikation der JavaServerPages primär eine plattform-übergreifende Lösung zur dynamischen Erzeugung von Websites definiert. Das Paket javax.servlet.jsp enthält die Basisfunktionalität zur Trennung des Content-Managements von der Präsentation, der Grundidee der Java Server Pages. Innerhalb einer JavaServerPage werden dynamische Inhalte mittels spezieller JSP-Elemente hinzugefügt, während die Formatierung der Seite durch HTML beschrieben wird. Durch Nutzung der Java Komponenten-Technologie (JavaBeans bzw. Enterprise JavaBeans) können große Teile bereits bestehender Java Komponenten mit JSP-Tags eingebunden werden. Das erlaubt eine weitestgehende Trennung der Entwicklung des Designs von der Entwicklung der Applikationslogik. Generell können Java Server Pages alle Features von Java nutzen, sie haben den vollen Zugriff auf alle Java-APIs. Damit aber eine Trennung von Layout und Code tatsächlich stattfindet, sollten JSP-Seiten auf Java nur über definierte Schnittstellen oder Beans zugreifen. Der Designer kann die JSP-Seite schreiben und der Java-Programmierer stellt die Beans und anderen Java Sourcecode zur Verfügung. Vorteil dieser Vorgehensweise ist eine bessere Wartbarkeit sowie mehr Transparenz. Komponenten von JSP´s Mit Hilfe von Direktiven übergibt eine JSP-Seite Informationen an die ausführende JSPEngine. Aktionen dienen der Manipulation des Ausgabestroms, d.h. sie geben dynamische Inhalte aus, wie z.B. den Inhalt einer Variablen oder den Wert eines beliebigen Ausdrucks. Weiterhin sind sie in der Lage, auf beliebige Objekte zuzugreifen, demzufolge kann durch Aktionen die Funktionalität einer Java Server Page erweitert werden. Dabei gibt es eine Reihe von Standardaktionen, die von allen Engines, die der JSP-Spezifikation entsprechen, unterstützt werden müssen. Während mit Deklarationen die Definition von Variablen und Methoden erfolgt, kann an beliebiger Stelle mit der % % - Markierung ein Script eingebunden werden. Diese beinhalten 3 Java Code. Durch das Hinzufügen entsprechender Import - Statements kann das gesamte Java - API innerhalb dieser Segmente genutzt werden. Wichtige Objekte, wie z.B. out für die Ausgabe oder response bzw. request für die Anfrage bzw. Antwort, werden jeder Java Server Page beim Aufruf zur Verfügung gestellt. Ausführung einer JSP Die Verarbeitung einer JSP-Seite wird von einem JSP-fähigen Webserver durch eine JSPEngine übernommen. Eine JSP Engine ist meist als Servlet implementiert. Mit Hilfe eines Aliases innerhalb des Webservers wird jede Client-Anfrage, die sich auf eine JSP-Seite bezieht, auf die JSP-Engine umgeleitet. Die als Servlet implementierte JSP-Engine öffnet die angeforderte Java Server Page und überprüft deren Syntax. Ist alles in Ordnung, erfolgt die Generierung einer ,,.java" Quelldatei, die dann mit Hilfe eines vorab konfigurierten Compilers in eine ,,.class"-Datei übersetzt wird. Das so entstandene Servlet wird dann von der Servlet-Engine geladen, um die ursprüngliche Client-Anfrage zu bearbeiten. Das Ergebnis ist somit ein aus der JSP - Seite zur Laufzeit erzeugtes Servlet, das dynamisch von der Servlet - Engine geladen wird. Die Erstellung des Servlets erfolgt nur beim ersten Aufruf der JSP - Seite. Es tritt daher eine Verzögerung bei der ersten Bearbeitung einer Anfrage, aufgrund des zur Erzeugung des Servlets benötigten Aufwands, ein. Jeder weitere Aufruf wendet sich dann über die JSP - Engine an das bereits geladene Servlet. Vor- und Nachteile Bei den Servlets sind die Möglichkeiten für Programmierer wesentlich besser, da hier ein richtiges Java-Programm geschrieben wird, mit der gewohnten Unterstützung durch Entwicklungsumgebungen. Auch die Performance ist in den meisten Fällen besser als bei JSP. Der einzige Nachteil ist die Tatsache, dass alles von Hand zusammengebastelt werden muss, was die zu generierende HTML-Seite betrifft. JSP ist bei komplexeren HTML-Seiten mit wenigen dynamischen Inhalten im Vorteil, da hier die Seiten zuerst mit einem ganz normalen Designtool für Webseiten erstellt werden können und hinterher die Java-Fragmente einfach von Hand eingetragen werden. Nachteile sind die schon erwähnte längere Dauer beim ersten Aufruf einer JSP und, bei verstärktem Einsatz von JSP, die zusätzliche Parsearbeit des Webservers. *** Beispiel Servlet import java.io.IOException; import java.io.PrintWriter; import java.util.Enumeration; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; 4 public final class Hello extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { response.setContentType("text/html"); PrintWriter writer = response.getWriter(); writer.println("<html>"); writer.println("<head>"); writer.println("<title>Sample Application Servlet Page</title>"); writer.println("</head>"); writer.println("<body bgcolor=white>"); writer.println("<table border=\"0\">"); writer.println("<tr>"); writer.println("<td>"); writer.println("<img src=\"images/tomcat.gif\">"); writer.println("</td>"); writer.println("<td>"); writer.println("<h1>Sample Application Servlet</h1>"); writer.println("This is the output of a servlet that is part of"); writer.println("the Hello, World application. It displays the"); writer.println("request headers from the request we are currently"); writer.println("processing."); writer.println("</td>"); writer.println("</tr>"); writer.println("</table>"); writer.println("<table border=\"0\" width=\"100%\">"); Enumeration names = request.getHeaderNames(); while (names.hasMoreElements()) { String name = (String) names.nextElement(); writer.println("<tr>"); writer.println(" <th align=\"right\">" + name + ":</th>"); writer.println(" <td>" + request.getHeader(name) + "</td>"); writer.println("</tr>"); } writer.println("</table>"); writer.println("</body>"); writer.println("</html>"); } } Beispiel JSP <html> <head> <title>Sample Application JSP Page</title> </head> <body bgcolor=white> <table border="0"> 5 <tr> <td align=center> <img src="images/tomcat.gif"> </td> <td> <h1>Sample Application JSP Page</h1> Beispiel - Applikation aus Tomcat </td> </tr> </table> <table border="0" border="100%"> <tr> <th align="right">Context Path:</th> <td align="left"><%= request.getContextPath() %></td> </tr> <tr> <th align="right">Path Information:</th> <td align="left"><%= request.getPathInfo() %></td> </tr> <tr> <th align="right">Query String:</th> <td align="left"><%= request.getQueryString() %></td> </tr> <tr> <th align="right">Request Method:</th> <td align="left"><%= request.getMethod() %></td> </tr> <tr> <th align="right">Servlet Path:</th> <td align="left"><%= request.getServletPath() %></td> </tr> </table> </body> </html> Datenbankzugriff mit JSP (gesamte DB) <HTML> <HEAD> <%@ page language="java" import="java.sql.*" %> <% try{ Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); Connection con = DriverManager.getConnection("jdbc:odbc:PARASERV"); Statement stm = con.createStatement(); ResultSet rs = stm.executeQuery("select * from adress"); %> <TITLE> AListe </TITLE> </HEAD> <BODY BGCOLOR="#99FFCC"> <H3><U>Adressliste als JSP</U></H3> <TABLE BORDER="1" WIDTH="80%" BGCOLOR="silver"> <TR> 6 <TD WIDTH="16%"><P ALIGN="CENTER"><B>Name</B></TD> <TD WIDTH="16%"><P ALIGN="CENTER"><B>Vorname</B></TD> <TD WIDTH="16%"><P ALIGN="CENTER"><B>Strasse</B></TD> <TD WIDTH="16%"><P ALIGN="CENTER"><B>Postleitzahl</B></TD> <TD WIDTH="16%"><P ALIGN="CENTER"><B>Ort</B></TD> <TD WIDTH="20%"><P ALIGN="CENTER"><B>Telefon</B></TD> </TR> <% rs.next(); %> <TR> <TD WIDTH="16%">&nbsp;<%= rs.getString("Name")%></TD> <TD WIDTH="16%">&nbsp;<%= rs.getString("VName")%></TD> <TD WIDTH="16%">&nbsp;<%= rs.getString("Strasse")%></TD> <TD WIDTH="16%">&nbsp;<%= rs.getString("PLZ")%></TD> <TD WIDTH="16%">&nbsp;<%= rs.getString("Ort")%></TD> <TD WIDTH="20%">&nbsp;<%= rs.getString("Telefon")%></TD> </TR> <% while(rs.next()){ %> <TR> <TD WIDTH="16%">&nbsp;<%= rs.getString("Name")%></TD> <TD WIDTH="16%">&nbsp;<%= rs.getString("VName")%></TD> <TD WIDTH="16%">&nbsp;<%= rs.getString("Strasse")%></TD> <TD WIDTH="16%">&nbsp;<%= rs.getString("PLZ")%></TD> <TD WIDTH="16%">&nbsp;<%= rs.getString("Ort")%></TD> <TD WIDTH="20%">&nbsp;<%= rs.getString("Telefon")%></TD> </TR> <% } con.close(); } catch(Exception ex){ ex.printStackTrace(); } %> </TABLE> <FORM><br><input type=button onClick="history.back()" value="zurück"></FORM> </BODY> </HTML> Datenbankzugriff mit JSP (einzelne Sätze) <HTML> <HEAD> <%@ page language="java" import="java.sql.*" %> <% try{ Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); Connection con = DriverManager.getConnection("jdbc:odbc:PARASERV"); Statement stm = con.createStatement(); ResultSet rs = stm.executeQuery("select NAME, VNAME from adress"); rs.next(); %> <TITLE> AListe </TITLE> </HEAD> <BODY BGCOLOR="#99FFCC"> <FORM action=http://localhost:8080/servlet/adrserv.AUpdel method=POST> <H3><U>Einzel-Adresse als JSP</U></H3> <P>Name :<SELECT NAME="Selection"> <OPTION><%= rs.getString("Name") %>,<%= rs.getString("VName") %></OPTION> 7 <% while(rs.next()){ %> <OPTION><%= rs.getString("Name") %>,<%= rs.getString("VName") %></OPTION> <% } con.close(); } catch(Exception ex){ ex.printStackTrace(); } %> </SELECT></P><BR> <INPUT TYPE=CHECKBOX NAME="Delete" VALUE="Entfernen">Entfernen <P><INPUT TYPE="SUBMIT" NAME="Submit" VALUE="Submit"><INPUT TYPE="RESET" NAME="Reset" VALUE="Reset"> </FORM> </BODY> </HTML> Datenbankzugriff mit Servlet (ganze Tabelle) package adrserv; import javax.servlet.*; import javax.servlet.http.*; import java.io.*; import java.util.*; import java.sql.*; public class AListe extends HttpServlet { //Globale Variablen initialisieren HtmlTable htab = null; public void init(ServletConfig config) throws ServletException { super.init(config); } //Die HTTP-Anforderung Get bearbeiten public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); PrintWriter out = new PrintWriter (response.getOutputStream()); out.println("<html>"); out.println("<head><title>Adressliste</title></head>"); out.println("<body BGCOLOR=\"#99FFCC\">"); out.println("<H3><U>Adressliste</U></H3>"); Connection con = null; Statement stm = null; ResultSet rs = null; try{ Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); con = DriverManager.getConnection("jdbc:odbc:PARASERV"); stm = con.createStatement(); rs = stm.executeQuery("select * from adress"); htab = new HtmlTable(out,rs); //html-Tabelle generieren lassen htab.doTable(true); 8 rs.close(); stm.close(); con.close(); } catch(Exception ex){} out.println("<FORM><br><input type=button onClick=\"history.back()\" value= \"zurück\"></FORM>"); out.println("</body></html>"); out.close(); } //Servlet-Information holen public String getServletInfo() { return "adrserv.AListe Information"; } } HTMLTable package adrserv; import java.io.*; import java.sql.*; public class HtmlTable { PrintWriter pout = null; ResultSet rset = null; ResultSetMetaData rsmd = null; public HtmlTable() { } public HtmlTable(PrintWriter out,ResultSet rs){ pout = out; rset = rs; } public void startTable(boolean mitRand){ if(mitRand) pout.println("<table border>"); else pout.println("<table>"); } public void endTable(){ pout.println("</table>"); } public void startRow(){ pout.print("<tr>"); } public void endRow(){ pout.println("</tr>"); } public void doTitelCell(String data){ 9 pout.print("<th>" + data + "</th>"); } public void doCell(String data){ pout.print("<td>" + data + "</td>"); } public void doRow(String[] data, boolean titel){ startRow(); for(int i = 0;i < data.length;i++){ if(titel) doTitelCell(data[i]); else doCell(data[i]); } endRow(); } public void doTable(boolean mitRand){ //datenbanktabelle als html-tabelle int feldZahl=0; try{ rsmd = rset.getMetaData(); feldZahl = rsmd.getColumnCount(); } catch(Exception ex){ex.printStackTrace();} String[] colBuff = new String[feldZahl]; startTable(mitRand); try{ rset.next(); //feldnamen -> titelzeile for(int i = 1;i<=feldZahl;i++) colBuff[i-1] = rsmd.getColumnName(i); doRow(colBuff,true); //tabellendaten holen -> html-tabelle do{ for(int col = 0;col < feldZahl;col++){ colBuff[col] = rset.getString(col+1); } doRow(colBuff,false); }while(rset.next()); } catch(Exception ex){ex.printStackTrace();} endTable(); } } 10