Java Server Pages

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:

CSS

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

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())));
  }
%>

Sessions

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()); %>

Formulare und Session Attributes

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"));
%>

Include & Declare

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.

Modularisierung mit Java Beans

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()); %>

DB Bean

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:

<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);
  }
%>