Johann Mitlöhner 2010-2012
Java Server Pages sind Webseiten, die sowohl HTML als auch Java-Code enthalten können. Das geht nicht mit jedem Webserver; es ist ein JSP-fähiger Server notwendig, z.B. Tomcat. Der erzeugt aus der JSP-Seite ein Java Servlet, das bei einer Änderung der JSP-Seite neu kompiliert wird. Das Servlet wird am Server exekutiert und produziert HTML Code, der an den Browser zurückgeschickt wird.
Die Dateien liegen in einem dafür konfigurierten Verzeichnis am Server, und die Dateiendung muß .jsp sein. Java-Code wird mit <% begonnen und mit %> beendet. Ausgabe erfolgt mit out.println() statt wie in Java sonst üblich mit System.out.println(). Ein Beispiel:
<p> Eine <b>Berechnung</b>: <% out.println(1 + 1); %>
Legen Sie am Schulungsraumserver ein Verzeichnis www an (falls Sie noch keines haben) und schreiben Sie den obigen Code in eine Datei rechnung.jsp in diesem Verzeichnis. Der URL setzt sich dann zB so zusammen:
http://xmdimrill.ai.wu.ac.at:8180/j0012345/rechnung.jsp
Sehen Sie sich mit Ihrem Browser den Quellcode (page source) dieser Seite an, um zu sehen, was passiert ist: der HTML Code wird unverändert wiedergegeben, die Java-Anweisungen werden ausgeführt.
Zur Konfiguration:
Cascading Style Sheets lassen sich mit JSP ebenso integrieren wie aus HTML gewohnt:
<link rel="stylesheet" type="text/css" href="style.css" />
Ein Style Sheet dazu:
body { margin-left: 3%; margin-right: 3%; margin-top: 2%; background: Wheat; font-family: Arial; } ul { list-style-type: circle; } td { background: lightGray; }
Die Verwendung von CSS ist sehr zu empfehlen, weil sich damit Änderungen im Layout für die gesamte Applikation (die aus vielen JSP-Seiten bestehen kann) mit geringem Aufwand realisieren lassen.
Dynamische Inhalte bringen zusätzliche Funktionen, die mit statischem HTML nicht realisiert werden können.
<%@ page import="java.util.*" %> <% out.println("<p> Current local time is " + (new Date()) + "."); %>
Mit etwas Überlegung finden Sie sicher heraus, was das folgende Programm leistet. Wenn nicht, kopieren Sie den Code in eine weitere JSP Datei (z.B. babel.jsp) und probieren Sie es aus!
<%@ page import="java.util.*" %> <% String alf = "aabcdeeefghiijklmnoopqrstuuvwxyz "; Random rand = new Random(); for (int i = 0; i < 100; i++) { out.print(alf.charAt(rand.nextInt(alf.length()))); } %>
Jeder Browsersession ist ein ID zugeordnet (ein String mit 32 Zeichen); wird der Browser geschlossen und später wieder gestartet, so beginnt eine neue Session mit einem neuen ID. Auch nach Ablauf einer bestimmten (serverseitig konfigurierbaren) Zeitspanne ohne Aktivität, z.B. 30 Minuten, beginnt eine neue Session.
Beim Server können laufend Requests von verschiedenen Clients eintreffen; mit Hilfe des Session IDs können wir erkennen, daß bestimmte Requests aus derselben Session kommen und daher wahrscheinlich von derselben Person stammen. Das ist nützlich, um z.B. mehrere Warenkörbe zu füllen, bevor sich die Kunden mittels Login identifiziert haben. Auf das ID der aktuellen Session können wir mit session.getId() zugreifen:
Session ID: <p> <% out.println(session.getId()); %>
Sollen sich Benutzer einloggen, so können wir uns den username als session attribute merken. Solche Attribute werden serverseitig für jede session gespeichert und stehen uns über die Methoden session.setAttribute() und session.getAttribute() zur Verfügung.
login.jsp besteht nur aus HTML Code:
<form action=verify.jsp> <input type=text name=username> <input type=submit value=OK> </form>
In verify.jsp verwenden wir den Parameter username aus login.jsp und speichern ihn als session attribute:
<% String username = request.getParameter("username"); // check username here.. session.setAttribute("username", username ); out.println("Login successful."); %>
Ab jetzt können wir in beliebigen anderen JSP Seiten den username dieser Session abfragen:
<% out.println("username: " + session.getAttribute("username")); %>
Mit folgendem Befehl kann eine JSP-Seite header.jsp in die aktuelle Seite inkludiert werden:
<%@ include file="header.jsp" %> <p> ...
Damit können wir z.B. einheitliche Kopfzeilen, Menuleisten und ähnliches auf sehr einfache Weise implementieren. Änderungen in header.jsp wirken sich automatisch in der gesamten Applikation aus.
Möchten Sie Java-Methoden definieren, so verwenden Sie folgende Notation:
<%! declaration %>
Dabei ist declaration eine beliebige Java-Methode, die dann in dieser Seite verwendbar ist. Die beiden Konzepte können kombiniert werden, d.h. auch im obigen Beispiel in der Datei header.jsp können Methoden definiert sein, die dann in anderen JSP-Seiten zur Verfügung stehen.
Wenn eine Java-Komponente in JSP an mehreren Stellen verwendet werden soll, bietet sich die Umsetzung als Java Bean an:
Beispiel: minimale Bean Hello.java in ~/www/WEB-INF/classes/tools
package tools; public class Hello { public static String hello() { return "Hello from tools!"; } }
javac Hello.java
.jsp file:
<jsp:useBean id="h" class="tools.Hello" scope="page" /> <% out.println(h.hello()); %>
Datenbankzugriff ist natürlich eine besonders häufige Anwendung für JSP. Duplicate code vermeiden! Eine akzeptable Lösung wären declared methods und include, aber eine Java Bean bietet mehr Möglichkeiten:
DB.java in ~/www/WEB-INF/classes/tools:
package tools; import java.sql.*; import java.util.*; import javax.servlet.*; public class DB { ResultSet rset = null; Connection conn = null; public DB() throws Exception { connect("dbname", "dbuser", "pw"); } public void connect(String db, String user, String passwd) throws Exception { String url = "jdbc:postgresql:" + db; DriverManager.registerDriver(new org.postgresql.Driver()); conn = DriverManager.getConnection(url, user, passwd); } public ResultSet query(String sql) throws Exception { return rset = conn.createStatement().executeQuery(sql); } public boolean next() throws Exception { return rset.next(); } public String get(String s) throws Exception { return rset.getString(s); } public void insert(String table, ServletRequest request) throws Exception { Enumeration parm = request.getParameterNames(); String fields = "", values = ""; boolean first = true; while (parm.hasMoreElements()) { String name = (String) parm.nextElement(); if (!first) { fields += ","; values += ","; } fields += name; values += "'" + request.getParameter(name) + "'"; first = false; } update("insert into " + table + " (" + fields + ") values (" + values + ")"); } public void update(String sql) throws Exception { conn.createStatement().executeUpdate(sql); } }
Beim Kompilieren CLASSPATH auf DB driver und Servlet API setzen:
javac -cp /usr/share/java/postgresql-jdbc3.jar:/usr/share/java/servlet-api.jar DB.java
Vorteile der Bean:
Das ist zumindest die Theorie. Eine Umstellung auf ein anderes DBMS (zB von Mysql auf Postgres) kann viele Überaschungen bringen, die meisten davon unangenehm. Der Aufwand für die Umstellung sollte nicht unterschätzt werden, er wird (außer bei ganz trivialen Beispiel-Applikationen) wahrscheinlich nicht Null sein. Er hängt u.a. davon ab, wie standard-konform progammiert wurde, und wie gut DBMS und JDBC die SQL-Standards umsetzen.
<jsp:useBean id="db" class="tools.DB" scope="session"/> <b>Artikel</b> <% try { db.query("select nr, bez, preis from artikel"); out.println("<table>"); while (db.next()) { out.println("<tr>" + "<td>" + db.get("nr") + "<td>" + db.get("bez") + "<td align=right>" + db.get("preis") ); } out.println("</table>"); } catch (Exception e) { out.println(e); } %>
Die insert() Methode der DB Bean kann verwendet werden, wenn alle Request-Parameter eingefügt werden, und diese auch genauso heißen wie die entsprechenden Spalten in der Tabelle. Die Parameter sind z.B. die input Felder im HTML Formular. Ansonsten bleibt die update() Methode der Bean, die ein komplettes SQL Statement erwartet.
<jsp:useBean id="db" class="tools.DB" scope="session"/> <% try { db.insert("artikel", request); out.println("Artikelerfassung erfolgreich."); } catch (Exception e) { out.println("Fehler bei Artikelerfassung!<p>"); out.println(e); } %>