Enterprise Applikationen entwickeln: Java Server

1 Java und Datenbanken

1.1 Hintergrund

Verbindung JDBC DBMS

1.2 Allgemeiner Ablauf

Vorraussetzungen:
import java.sql.*; im Quelltext der Java-Anwendung
die DB-Treiberdatei bereitstellen UND (in Netbeans) bekannt machen
1. Schritt:
DB-Treiber (in der Java Anwendung) LADEN
Class.forName("oracle.jdbc.OracleDriver");
könnte eine ClassNotFoundException werfen, für die die "catch-or-throw"-Regel zu beachten ist
2. Schritt:
Verbindung zur Datenbank herstellen (und dort anmelden)
						Connection con = DriverManager.getConnection("jdbc:oracle:thin:@10.140.130.10:1521:CDTORA","benutzername","passwort");
					
könnte eine SQLException werfen. für die AUCH die "catch-or-throw"-Regel gilt (wie auch folgende Methoden-Aufrufe)
3. Schritt:
über das erhaltene Connection-Objekt ein sog. Statement-Objekt aufbauen
Statement stm = con.createStatement();
4. Schritt:
über das erhaltene Statement-Objekt eine SQL-Anweisung festlegen und ausführen lassen
  1. SQL-Anweisungen OHNE "Ergebnis" (d.h. SQL-Anweisungen, die KEINE in der DB gespeicherten Daten abliefern)
    (für-alle DML (bzw. DDLs bzw. DCLs)

    stm.executeUpdate("Text der DML-Anweisung");

    Hinweis: obwohl KEINE in der DB GESPEICHERTEN Daten "geholt" werden, liefert diese Methode einen int-Wert ab, der der Anzahl der "betroffenen" Datensätze entspricht

    Sobald die entstandenen Objekte (d.h. Connection und Statement) nicht mehr benötigt werden, müssen diese "kontrolliert abgebaut" (d.h. "geschlossen") werden;
    IN UMGEKEHRTER Reihenfolge IHRER ENTSTEHUNG
    stm.close();
    con.close(); Abbauen der Verbindung zur DB INKLUSIVE "ORDENTLICHER" ABMELDUNG

  2. SQL-Anweisung MIT "Ergebnis", dessen Struktur zum Entwicklungszeitpunkt bekannt ist

    die SELECT-Anweisung liefert ihre "Ergebnismenge" in Form eines ResultSet-Objektes ab, dessen Adresse in einer "passenden" Referenzvariablen aufgefangen werden muss
    ResultSet rs = stm.executeQuery( "Text einer Select-Anweisung");

    anschließend die erhaltene Ergebnismenge abarbeiten, über Methoden, die über das ResultSet-Objekt aufgerufen werden

    Auswahl verfügbarer Methoden:

    (1) die "nächste" Ergebniszeile abrufen:

    										rs.next() liefert ENTWEDER true, sofern (noch) eine Ergebniszeile zur
              Verfügung steht,
              ODER false, wenn die Ergebnismenge leer ist bzw. bereits
              vollständig abgearbeitet wurde

    (2) Zugriff auf die Werte in den Ergebnis-Spalten der "aktuellen" Ergebniszeile:

    										rs.getString(SpaltenNr) liefert den Wert der entsprechenden
                            Ergebnisspalte als String ab (SpaltenNr-Zählung beginnend mit 1)
    										rs.getInt(SpaltenNr) liefert den Wert der entsprechenden
                         Ergebnisspalte als int ab (geeignet für "kleine" max. 9-stellige ganzzahlige Werte
                         aus der DB

    sobald das ResultSet-Objekt nicht mehr benötigt wird (z.B. weil es vollständig abgearbeite ist) SOLLTE/muss auch dieses "abgebaut" werden: rs.close();

1.3 Alternative zu Statement-Objekten

Hintergrund:
das zusammensetzen einer SQL-Textes aus konstanten und variablen Bestandteilen kann schnell aufwändig und unübersichtlich werden;
z.B. "siehe JDBCBsp.3a.java"
stm.executeUpdate("INSERT INTO projekt VALUES ('" + prnr + "', '" + 
                                                    prname + "', " + 
                                                    mi + ")");
in solchen Situationen sind PreparedStatement-Objekte wesentlich einfacher zu handhaben und eventuell auch (DB-seitig) effizienter
Handhabung:
(1) Schritt: ANSTELLE eines Statement-Objektes ein PreparedStatement-Objekt anlegen lassen:
PreparedStatement ps = con.prepareStatement("SQL_Anweisungs_Text in der Regel mit Platzhaltern");
also anders als bei Statement-Objekten wird hier die Struktur der SQL-Anweisung bereits VOLLSTÄNDIG angegeben, lediglich Werte fehlen in der Regel noch
(2) Schritt: Kennzeichnung von Platzhaltern geschieht durch Angabe eines oder mehrerer Fragezeichen: ?
z.B.
"INSERT INTO projekt VALUES(?,?,?)"
also auch KEINE Unterscheidung bezüglich der Typen der Werte (die noch "eingesetzt" werden müssen), d.h. Platzhalter für NUMBER-Spaltenwerte sind identisch zu Platzhaltern für DATE- oder VARCHAR2- oder CHAR-Spaltenwerten
(3) Schritt: VOR der Ausführung der SQL-Anweisung MÜSSEN ALLE Platzhalter "gefüllt" werden;
hierfür stehen verschiedene Methoden zur Verfügung, um TYP-abhängig den SQL-Anweisungs-Text syntaktisch korrekt um Werte zu vervollständigen;
insbesondere: "Setter-Methoden" für Texte-/Datumwerte und für ganzzahlige Werte, aufgerufen über das PreparedStatement-Objekt
ps.setString(Positionsnummer, Variable bzw. Ausruck);
z.B.:
ps.setString(1, prnr);
ps.setString(2, prname);

Hinweis: diese Methode bewirkt, dass der Wert in einfache Hochkommata gesetzt wird!

ps.setInt(Positionsnummer, Variable bzw. Ausruck)
z.B.:
ps.setInt(3, mi);
(4) Schritt: NACH befüllen ALLER Platzhalter SQL-Anweisung über namensgleiche (vgl. Statement-Objekte) Methoden ausführen lassem;
einziger Unterschied: Methoden der Klasse PreparedStatement benötigen KEIN Argument!
z.B.:
ps.executeUpdate();
für PreparedStatements mit Select-Anweisungen:
ps.executeQuery();

1.4 Systematische Behandlung JDBC-spezifischer Exceptions

Mögliche Exceptions:
(1) bereits beim Laden des DB-Treibers: ClassNotFoundException
(2) in Verbindung mit den "eigentlichen DB-Zugriffen: SQLException
Allgemeine Strategie:
(1) falls bereits eine ClassNotFoundException geworfen wurde: Fehlermeldung UND Ende der Anwendung
(2) für alle SQLExceptions EINEN try-Block (mit den "kritischen" Methodenaufrufen) und EINEM catch-Block (für die Bahndlung aller evtl. geworfener SQLExceptions sowie EINEM finally-Block (in dem alle erfolgreich aufgebauten Objekte wieder abgebaut bzw. geschlossen werden)
Konsequenzen:
um die üblichen Referenzvariablen sowohl im try- wie auch im finally-Block verwenden zu können, müssen sie AUßERHALB (am besten vor dem try-Block) definiert werden
um im finally-Block ZUVERLÄSSIG erkennen zu können, WELCHE Objekte erfolgreich aufgebaut werden, werden alle Referenzvariablen mit null initialisiert;
der close-Methodenaufruf darf nur dann erfolgen, wen die jeweilige Referenzvariable nicht mehr null enthält
da die close-Methodenaufrufe im finally-Block auch eine SQLException werfen könnten, müssen diese jeweils wieder in eine try-catch-Anweisung "verpackt" werden
Muster-Implementierung:
siehe JDBCBsp4.java

1.5 Auswertung von Ergebnismengen (Resultsets) mit UNBEKANNTER Struktur

Bisher:
die Struktur einer Select-Anweisung ist/war zum Entwicklungszeitpunkt vollständig bekannt;
d.h. insbesondere ist/war bekannt, WIEVIELE Ergebnisspalten geliefert werden (aus der Select-Klausel) und von welchem Typ diese sind (dies ermöglicht das "gezielte" typ-passende Auslesen über getInt() oder getDate() anstelle des "pauschalen" getString() )
Idee:
Das Ergebnis einer zur Laufzeit einzugebenden Select-Anweisung soll (zunächst stark vereinfacht) zeilen- (und spalten-)weise ausgegeben werden, z.B:
1. Zeile
    1. Spalte .....
    2. Spalte .....
2. Zeile
    1. Spalte .....
    2. Spalte .....
Zusätzlich benötigtes "Werkzeug"
zu jedem ResultSet können zusätzlich "beschreibende" Informationen abgerufen werden;
dazu wird ein ResultSetMetaData -Objekt, über das dann gezielt einzelne Informationen ermittelbar sind.
"Erzeugung" dieses Objektes:
über die parameterlose Methode getMetaData() , aufzurufen über das ResultSet-Objekt
z.B.:
...
ResultSet rs = null;
...
rs = stm.executeQuery(...);
ResultSetMetaData rsmd;
...
rsmd =  rs.getMetaData();
...
					

für dieses Objekt gibt es keine close() -Methode!

über dieses ResultSetMetaData -Objekt sind u.a. folgende Methoden aufrufbar:
(1) getColumnCount() liefert die Anzahl der Ergebnisspalten
(2) getColumnLabel(Spaltennr.) liefert als String den Spaltenname "siehe Überschrift in iSQL*Plus-Präsentation!)

2 Überblick zu Java-basierten Web-Technologien

Hintergrund:
  • neben statischen Web-Seiten, die "fertig" z.B. als HTML-Dokumente auf einem Web-Server vorliegen, können auch sogenannte dynamische Web-Seiten notwendig werden
  • hierbei wird der "Inhalt" erst zum Abruf-Zeitpunkt generiert
  • für dieses generieren (von HTML) muss AUF DEM WEB-SERVER eine entsprechende Verarbeitungslogik implementiert werden
  • diese Implementierung kann unter anderem auch in Java erfolgen
Möglichkeiten:
  • Verwendung von Java in einem Servlet:
    • eine (spezielle) Java-Klasse, die auf dem Web-Server instantiiert wird
    • beim "Aufruf" des Servlets wird eine bestimmte Methode über diese Instanz aufgerufen
    • die entscheidende Aufgabe des Servlets besteht aus dem Generieren von HTML, das an den "Aufrufer" zurückgeschickt wird
  • Verwendung von Java in einer Java Server Page(JSP):
    • ein (einfaches) HTML-Dokument mit "viel" HTML und "wenig" integrierten Java
    • über das "eingestreute" Java wird gesteuert, welche HTML-Teile wo und wie im Ergebnis landen sollen UND welche dynamischen Werte dort eingesetzt werden sollen
    • Hinweis: im Rahmen des "Bereitstellens" der JSP auf dem Web-Server wird AUTOMATISCH ein Servlet generiert;
      d.h. es wird aus dem JSP-Quelltext ein Servlet-Quelltext generiert, dieser auf dem Web-Server übersetzt und die entstandene Servlet-Klasse instantiiert;
      diese Servlet-Instanz übernimmt dann die Anfrage über die JSP

2.1 Java Server Pages

Grundsätzliches
  • Quelltexte werden in Dateien mit der Erweiterung JSP gespeichert
  • auch der "Aufruf" erfolgt letztendlich durch Angabe des Dateinamens inkl. Erweiterung
  • Quelltexte enthalten im wesentlichen HTML, in das BESONDERES GEKENNZEICHNETES Java eingebaut ist

2.1.1 Möglichkeiten der Kennzeichnung von Java in JSPs

(1) über XML-Syntax:
allg.:
<jsp:elementname attribut="Wert" ... /> d.h ein leeres XML-Element aus dem Namensraum jsp, mit
                                        1 oder mehreren Attributen
					
z.B.:
<jsp:include page="hallowelt-modul.jsp" />
Wirkung: eine sog. Standardreaktion ...
das "Ergebnis" der JSP mit dem Namen hallowelt.jsp wird an dieser Stelle in das Ergebnis der aktuellen JSP eingefügt
(2) über Skript-Elemente:
gemeinsames Merkmal: eingeleitet mit <% und abgeschlossen mit %> dazwischen wird Java eingesetzt
mögliche Ausprägungen:
  1. JSP-Kommentar: eingeleitet mit <%-- und abgeschlossen mit --%>
  2. JSP-Ausdruck: eingeleitet mit <%= und abgeschlossen mit %> dazwischen darf alles angegeben was rechts von einem Wertzuweisungs-Operator (in Java) stehen darf,
    also z.B. eine Variable, eine Konstante, ein "Rechen-Ausdruck", ein Methodenaufruf, ...
    hierbei sind Java-Syntaxregeln zu beachten

    Hinweis: KEIN Strichpunkt am Ende;

    allg.: <%= Java-Ausdruck %>
    z.B.:
    <%= new java.util.Date() %>
    die Klasse Date aus dem Package java.util wird instantiiert, über den Standardkonstruktor mit den aktuellen Systeminformationen initialisiert und das Ergebnis der automatisch aufgerufenen toString()-Methode als Wert des Ausdruck an dieser Stelle in das Ergebnis der aktuellen JSP eingefügt
  3. JSP-Skriptlet: eingeleitet mit <% und abgeschlossen mit %> dazwischen darf ENTWEDER eine (oder mehrere) vollständige Java-Anweisungen (jeweils abgeschlossen mit einem Strichpunkt) angegeben werden ODER nur ein Teil einer Java-Anweisung (z.B. if-else oder Schleife), die dann durch mindestens 1 weiteres JSP-Skriptlet vervollständigt werden muss (siehe Beispiel 4)
    allg.: <% eine Javaanweisung; %> oder <% Teil einer Javaanweisung %>
    z.B.:
    <% java.util.Date d = new java.util.Date(); %>
    eine vollständige Definitionsanweisung für eine lokale Variable inklusive Initialisierung
  4. JSP-Deklaration: eingeleitet mit <%! und abgeschlossen mit %>
    dazwischen darf angegeben werden
    ENTWEDER die Definition einer Member-Variablen, über die einer JSP eine Art "Gedächtnis" verliehen werden kann
    ODER die Definition einer "helfenden" Methode, die innerhalb der JSP (z.B. in einem JSP-Ausdruck) aufgerufen werden kann
    allg.: <%! Definition_Membervariable; %> oder <%! Definition_Methode; %>
    z.B.:
    <% Date letztes; %>
(3) über sog. "Direktive":
im Gegensatz zu Skript-Elementen (und auch der XML-Syntax:Standardaktion) KEINE Auswirkung zur Laufzeit, sondern "Aufträge", die für den Compiler gedacht sind
insbesondere: für import -Aufträge
Syntax: <%@ page import="Package_bzw_Klasse" %>
z.B.:
<%@ page import="java.util.Date" %>
führt im generierten Servlet-Quelltext zur entsprechenden import Anweisung: import java.util.Date;
Alle Beispiele zu diesem Abschnitt

2.2 Grundlagen Formularverarbeitung (in JSPs)

Hintergrund:
HTML-Formulare sind DIE Möglichkeit, innerhalb von Web-Anwendungen Benutzer-EINGABEN zu zulassen
dabei ist jedem "Formularfeld" (Steuerelement) ein eindeutiger Bezeichner zugeordnet (über das name -Attribut des Steuerelements);
sogenannte " Formular-Parameter"
beim "Abschicken" der Formular-Daten (über submit-Schaltfläche) kann gesteuert werden, ob diese "nachvollziehbar" (im vollständigen ULR-String der Abfrage/des Requests) oder "versteckt" (nicht im URL-String des Requests) weitergegeben werden;
steuerbar über das method-Attribut des Form-Tags: 'get' für "sichtbar" 'post' für "versteckt"
beim "Empfänger" ("wer" das ist, wird über das action -Attribut des form-Tags des "Senders" festgelegt) kommen diese "Formular-Parameter" als sog. "Request-Parameter" an;
Bezeichner der Request-Parameter sind identisch mit denen der Formular-Parameter
Verarbeitung der Request-Parameter:
in jeder JSP gibt es ein implizites (d.h. automatisch vorhandenes) sog. request -Objekt
über dieses kann auf die (eventuell) gelieferten Request-Parameter zugegriffen werden
Methode: getParameter(String Parametername)
im Erfolgsfall liefert diese Methode den Parameter wert als String ab
Besonderheiten:
falls der angebene Parameter NICHT mitgeschickt wird, wird null abgeliefert
falls der angegebene Parameter OHNE WERT angekommen ist, wird ein Leer-String abgeliefert

2.3 Servlets

Aufbau:
(eigene) Servlet-Klassen sind Ableitungen der Klasse HttpServlet aus dem Package javax.servlet.http
In dieser Klasse sind 2 Methoden zu implementieren (d.h. formal zu überschreiben):
  1. doGet(): für die Behandlung von "get"-Request
  2. doPost(): für die Behandlung von "post"-Request
beide Methoden besitzen 2 Parameter:
  1. vom Typ HTTPServletRequest, üblicherweise mit der Bezeichung request (darüber können evtl. mitgeschickte Request-Parameter "besorgt" werden - siehe implizites request-Objekt in JSPs)
  2. vom Typ HTTPServletResponse, üblicherweise mit der Bezeichnung response (wichtig, um das Ergebnis des Requests an den anfragenden Client zurückzuschicken)
beide Methoden könnten eine IOException oder eine ServletException werfen, für die die "catch-or-throw-Regel" zu beachten ist (hier berücksichtigt durch throws-Klausel im Kopf der Methoden)
"Vorbereitende" Anweisungen innerhalb von doGet() bzw. doPost():
(1) Art des Ergebnisses festlegen, durch Aufruf der Methode setContentType über den 2. Parameter: z.B.:
response.setContentType("text/html;charset=UTF-8");
(2) "Besorgen eines PrintWriters, über den die "Ausgabe" des generierten HTML erfolgt: z.B.:
PrintWriter out = response.getWriter();
über diese Referenz können alle von System.out bekannten Ausgabe-Methoden aufgerufen werden

Hinweis: diesen PrintWriter NICHT "schließen" (durch Aufruf der close()-Methode), da sonst anschließende "Ausgaben" in anderen Teilen der Anfrage (z.B. eine inkludierende JSP) NICHT MEHR MÖGLICH WÄREN!

Behandlung von Request-Parametern:
formal IDENTISCH zur Behandlung von Request-Parametern in JSPs, vorausgesetzt der 1. Parameter der doGet()/doPost() -Methode ist mit request bezeichnet ( siehe implizites request-Objekt in JSPs )
zum Austesten der Parameterauswertung sind KEINE "vorgeschalteten" HTML-Formulare nötig (z.B. innerhalb einer JSP);
vielmehr können Parameter (mit oder auch ohne Wert) beim "Aufruf" eines Servlets in NetBeans einfach mitgegeben werden
(1) namentlich bekannte Request-Parameter, die maximal 1 Wert besitzen können:
z.B.: Textfelder, Textareas, Optionsgruppen, Listenfelder OHNE Mehrfachauswahl
Besonderheiten: Request-Parameter für Textfelder, Textareas oder Listenfelder OHNE Mehrfachauswahl kommen beim Empfänger immer an, möglicherweise ohne Wert;
falls in einer Optionsgruppe KEINE Option ausgewählt ist, wird dieser Parameter NICHT mitgeschickt;
generell gilt, dass (z.B. im Rahmen eines 1. Abrufens) das "Angekommensein" eines jeden Parameters zu prüfen ist
"Abholen" des Wertes für einen Parameter:
										...request.getParameter("Parametername");
									
Prüfen: ist das Ergebnis null ? Parameter ist nicht angekommen
ist das Ergebnis ein Leer-String? Parameter ist OHNE WERT angekommen (denkbar bei Textfeldern oder Textareas)
(2) namentlich bekannte Request-Parameter, die eventuell mehrfach "ankommen":
z.B.: Gruppe von Kontrollkästchen, Listenfelder mit Mehrfachauswahl
die bekannte getParameter -Methode ist hier NICHT geeignet, sie würde nur 1 Wert abliefern
hier nötig:
										...request.getParameterValues("Parametername");
									
liefert im Erfolgsfall ein String-Array
Möglichkeiten:
  • null der Parameter ist NICHT angekommen (z.B: kein einziges Kontrollkästchen einer Gruppe aktiviert)
  • 1-elementiges String-Array der Parameter ist GENAU 1 mal angekommen
    (theoretisch könnte in dem 1 Array-Element ein Leer-String gelandet sein, weil der Parameter ohne Wert angekommen ist!)
Beispiel: Beispiel3.java
(3) Auswertung einer (namentlich) unbekannten Request-Parameter-Liste:
1. Schritt: "Besorgen" einer "Liste" eventuell mitgeschickter Request-Parameter-Namen:
... request.getParameterNames();
liefert im Erfolgsfall (d.h. es ist mindestens 1 Parameter angekommen) eine sog. "Auflistung von Strings" (Enumeration);
das Ergebnis dieser Methode kann bzw. muss über einer Referenzvariable vom Typ Enumeration<String> angesprochen werden;
z.B.:
Enumeration<String> pNamen = request.getParameterNames();
sollte kein einziger Parameter angekommen sein, wird eine "leere" Enumeration abgeliefert!
2. Schritt: anschließend das Ergebnis dieser Methode elementweise auslesen (ähnlich Auslesen eines ResultSet...)
... Solange noch ein weiteres Element vorhanden ...
... Hole den Element-Inhalt (also den Bezeichner eines Request-Parameters) ...
... Besorge über bekannte Methoden den/die Werte zum Request-Parameter ...
... und "bearbeite" diese Parameter-Werte ...
implementierbar über 2 Methoden, die über das "Auflistungs-Objekt" aufgerufen werden:
Prüfen, ob noch ein Element vorhanden:
pNamen.hasMoreElements(); //liefert ein boolean-Ergebnis
									
Holen des Element-Inhaltes:
pNamen.nextElement(); //liefert den Element-Inhalt als String
									
Beispiel: Beispiel4.java

2.4 Aufruf-Möglichkeiten von Komponenten einer Web-Anwendung

2.4.1 Hintergrund

eine auf einem "Web-Server" vorhandene Web-Komponente kann DIREKT über einen Browser aufgerufen werden,
z.B.:
localhost:8080/JSP-Beispiele/Beispiel4.jsp

//für einen Request der Web-Komponente Beispiel4.jsp (also eine JSP)
//innerhalb der Web-Anwendung JSPBeispiele
z.B.:
localhost:8080/ServletBeispiele/Beispiel4 

//für einen Request der Web-Komponente Beispiel4.java (also ein Servlet)
//innerhalb der Web-Anwendung ServletBeispiele
üblicherweise besteht eine Web- Anwendung aus MEHREREN Web- Komponenten;
dabei könnte es sich um
"fertige" HTML-Dokumente (für statische "Antworten"/Ergebnisse)
oder
JSPs oder Servlets (für dynamische Ergebnisse)
handeln

2.4.2 Möglichkeiten, wie eine Web-Komponente A eine (andere) Web-Komponente B "aufrufen kann:

(1) "indirekte" Aufruftechniken (d.h. durch Benutzeraktivität ausgelöst)
(a) über einen Hyper-Link innerhalb eines "fertigen" HTML-Dokumentes
z.B.:
<a href="/JSPUebungen/Uebung1.jsp"> ...   //für JSP
<a href="/ServletUebungen/Uebung2_1"> ... //für Servlet
allg.
< href="/Web_Anwendung/Web_Komponente"> ...
(b) über das action -Attribut eines form-Tags innerhalb eines HTML-Formulars (in einem HTML-Dokument oder einer JSP oder einem Servlet)

Hinweis: falls dem action -Attribut (wie in allen bisherigen Situationen) kein Wert zugeordnet ist, wird DIESELBE Web-Komponente erneut aufgerufen!

allg.
< method="" action="Web_Komponente"> ...
< method="" action="/Web_Anwendung/Web_Komponente"> ...
z.B.: Aufruf einer Web-Komponente in derselben Web-Anwendung:
<form method="..." action="Uebung5">
Aufruf einer Web-Komponente in einer anderen Web-Anwendung:
<form method="..." action="/ServletUebungen/Uebung5">

Hinweis: da die "aufgerufene" Web-Komponente ihr Ergebnis als Antwort auf den Request (im Browser) abliefert, sollte/muss sie "vollständiges" HTML erzeugen!

(2) "direkte" Aufruftechniken (d.h. der Aufruf der anderen Web-Komponente ist programmiert)
(a) innerhalb von JSPs über eine Standardaktion:
als "Inklusion":

das Ergebnis der gerufenen Web-Komponente wird in das Ergebnis der rufenden Web-Komponente eingefügt (siehe die meisten JSP-Beispiele/-Übungen)

Request-Response

d.h. das "Gesamtergebnis" entsteht durch Zusammensetzen der Teile, die von unterschiedlichen Web-Komponenten gebildet werden!

Deshalb: die "gerufene" Web-Komponente darf KEIN vollständiges HTML abliefern

Syntax: <jsp:include page="Web_Komponente" />

als "Forwarding":

das (Gesamt-) Ergebnis wird AUSSCHLIEßLICH von der gerufenen Web-Komponente gebildet (vergleichbar mit einem GOTO, also "weiterreichen" der Verantwortung an die gerufene Web-Komponente)

Request-Respons-Forwarding

deshalb: die gerufene Web-Komponente sollte/muss VOLLSTÄNDIGES HTML abliefern!

Syntax: <jsp:forward page="Web_Komponente" />

Beispielszenario
Forward
(b) innerhalb von Servlets:
da die JSP-spezifischen Standard-Aktionen hier nicht zur Verfügung stehen, muss in Servlets ein anderes "Werkzeug" benutzt werden;
Basis für "Inclusion" und "Forwarding" ist ein sog. "Request-Dispatcher";
Schritt:
  1. "Besorgen eines RequestDispatcher-Objektes (über den 1. Parameter der doGet()-/doPost-Methode:
    RequestDispatcher getRequestDispatcher(String Ziel_Web_Komponente);
    also bereits beim "Besorgen" des Objektes muss die "aufzurufende" Web-Komponente festgelegt werden
    z.B.:
    RequestDispatcher rd = request.getRequestDispatcher("Kontrolle");
  2. Über dieses Objekt können Methoden aufgerufenen werden:
    für "Inclusion":
    void include(HttpServletRequest request, HttpServletResponse response);
    für "Forwarding":
    void forward(HttpServletRequest request, HttpServletResponse response);
    also die gleichen Parameter wie bei doGet() bzw. doPost()
    z.B.:
    rd.include(request,response)
    bzw.
    rd.forward(request,response)

2.5 Browser-Sitzungen

2.5.1 Hintergrund:

ohne ausdrücklichen Auftrag erkennt ein Server NICHT, ob ein "aktueller" Request der erste eines Clients ist, oder dieser Client bereits vorher Requests geschickt hat, oder ob es sich um einen Request eines anderen Clients handelt
in gewissen Situationen ist eine solche Unterscheidung aber durchaus wichtig, z.B.: im Onlinehandel nach erfolgter Anmeldung oder zur Implementierung eines "fairen" Besucherzählers (der beim "nur" Aktualisieren NICHT hochzählt)

2.5.2 Lösung:

der Server kann eine sog. "Sitzungsverfolgung" ("Session-Tracking") vornehmen
dabei ordnet der Server jeden Client beim 1. Request eine sog. "Session-ID" zu
diese generierte Session-ID wird mit dem (1.) Response (Antwort) zusammen mit dem HTML-Ergebnis an den Client geschickt
beim nächsten Request DESSELBEN Clients (in derselben Browser-Sitzung!) wird diese Session-ID wieder an den Server geschickt

2.5.3 Handhabung

innerhalb eines JSP sind Session-Informationen nutzbar über das implizite session -Objekt;
innerhalb eines Servlets muss ein solches "Objekt" zuerst "besorgt" werden:

über den 1. Parameter der doGet()/doPost()-Methode: request.getSession();

liefert ein Ergebnis vom Typ HttpSession ab

z.B.:

HttpSession session = request.getSession
Ausgewählte Methoden, die über das Session-Objekt aufrufbar sind:
String getId() liefert die "Session-ID"
boolean isNew() liefert true, falls es sich beim "aktuellen" Request um den 1. des Clients handelt
String setAttribute(String bezeichner, Object wert) zum "sitzungslokalen" Speichern von Werten
Object getAttribute(String bezeichner) zum Auslesen eines "gemerkten" Wertes (falls Attributbezeichner nicht bekannt, wird null abgeliefert

z.B.:

...
session.setAttribute("merke1", "Ganz wichtig");
...
1. gemerkter Wert: <%= session.getAttribute("merke1") %>

2.6 Ergänzungen zu Web-Komponenten: Cookies

2.6.1 Hintergrund

2.6.2 "Setzen" eines Cookies

2.6.3 (Eventuell) empfangene Cookies auslesen

3 Format-Ausgabe für Zeit/Datum

Symbol Beschreibung
%tA, %ta vollständiger/abgekürzter Name des Wochentags
%tB, %tb vollständiger/abgekürzter Name des Monatsnamens
%tC zweistelliges Jahrhundert (00–99)
%te, %td Monatstag numerisch ohne beziehungsweise mit führenden Nullen (1–31 beziehungsweise 01–31)
%tk, %tl Stundenangabe bezogen auf 24 beziehungsweise 12 Stunden (0–23, 1–12)
%tH, %tI zweistellige Stundenangabe bezogen auf 24 beziehungsweise 12 Stunden (00–23, 01–12)
%tj Tag des Jahres (001–366)
%tM zweistellige Minutenangabe (00–59)
%tm zweistellige Monatsangabe (in der Regel 01–12)
%tS zweistellige Sekundenangabe (00–59)
%tY vierstellige Jahresangabe
%ty die letzten beiden Ziffern der Jahresangabe (00–99)
%tZ abgekürzte Zeitzone
%tZ Zeitzone mit Verschiebung zur GMT
%tR Stunden und Minuten in der Form %tH:%tM
%tT Stunden/Minuten/Sekunden: %tH:%tM:%tS
%tD Datum in der Form %tm/%td/%ty
%tF ISO-8601-Format %tY-%tm-%td
%tc komplettes Datum mit Zeit in der Form %ta %tb %td %tT %tZ %tY

3.1 Positionsangabe

System.out.format("%6s%7s seit %3$td.%3$tm.%3$tY%n",
                    rs1.getString(4),
                    rs1.getString(5),
                    rs1.getDate(6));

Das Dollarzeichen und die Ziffer davor bestimmen, welcher der letzten Parameter formatiert werden soll. Die Ziffer gibt die Position an.