7. Anwendung QuiEst
Wir beschreiben hier eine Struts-Anwendung, die etwas komplexer ist als die vorherigen, die aus didaktischen Gründen einfach gehalten waren.
7.1. Die Klasse „users“
Es steht eine Java-Klasse zur Verfügung, die Informationen über die Benutzer eines Unix-Rechners speichert. Diese werden in drei speziellen Dateien gespeichert:
- /etc/passwd: Liste der Benutzer
- /etc/group: Liste der Gruppen
- /etc/aliases: Liste der E-Mail-Aliase
Der Inhalt dieser drei Dateien sieht wie folgt aus:
- /etc/passwd
Die Zeilen dieser Datei haben folgende Form:
login:pwd:uid:gid:id:dir:shell
mit
Benutzername | |
sein verschlüsseltes Passwort | |
seine Benutzernummer | |
seine Gruppennummer | |
seine Identität | |
sein Anmeldeverzeichnis | |
seine Shell |
So könnte die Zeile eines Benutzers wie folgt aussehen:
Der vorangegangene Benutzer hat die Nummer 110 und gehört zur Gruppe 57. Die Definition der Gruppe 57 findet sich in der Datei /etc/group.
- /etc/group
Die Zeilen dieser Datei haben folgende Form:
nomGroupe:pwd:gid:membre1,membre2,....
mit
Gruppenname | |
sein verschlüsseltes Passwort – meistens ist dieses Feld leer | |
die Gruppennummer | |
Benutzernamen – dieses Feld kann leer sein |
Somit könnte die Zeile der oben genannten Gruppe 57 wie folgt aussehen:
was bedeutet, dass die Gruppe 57 den Namen iup2-auto trägt.
- /etc/aliases
Die Zeilen dieser Datei haben folgende Form:
mit
Alias | |
einem oder mehreren Tabulatoren | |
Benutzername des Benutzers, zu dem der Alias gehört |
So sieht die Zeile
guillaume.dupond: dupond
bedeutet, dass der Alias guillaume.dupond dem Benutzer mit dem Login dupond gehört. Zur Erinnerung: Aliase werden in E-Mail-Adressen verwendet. Wenn also im vorigen Beispiel der Unix-Rechner den Namen shiva.istia.univ-angers.fr trägt, wird eine E-Mail, die an guillaume.dupond@shiva.istia.univ-angers.fr wird in das Postfach des Benutzers mit dem Login dupond auf diesem Rechner zugestellt.
Wir werden uns hier nicht mit der gesamten Schnittstelle der Klasse „users“ befassen, sondern lediglich mit ihrem Konstruktor und einigen Methoden:
import java.io.*;
import java.util.*;
public class users{
// Attribute
private Hashtable usersByLogin=new Hashtable(); // Anmeldung --> Benutzername, Passwort, ..., Verzeichnis
private ArrayList erreurs=new ArrayList(); // Liste der Fehlermeldungen
....
// Konstruktor
public users(String usersFileName, String groupsFileName, String aliasesFileName) throws Exception {
// usersFileName: Name der Benutzerdatei mit Zeilen der Form
// login:pwd:uid:gid:id:dir:shell
// groupsFileName: Name der Datei mit den Gruppen, deren Einträge die folgende Form haben
// Name:Passwort:Nummer:Mitglied1,Mitglied2,..
// aliasesFileName: Name der Datei mit den Aliasen, die Zeilen der Form
// Alias:[tab]login
// erstellt das Wörterbuch usersByLogin
....
}// Konstruktor
// Benutzerliste
public Hashtable getUsersByLogin(){
return usersByLogin;
}
// Fehler
public ArrayList getErreurs(){
return erreurs;
}
Wörterbuch (Hashtable), dessen Schlüssel die Logins aus der passwd-Datei sind. Der dem Schlüssel zugeordnete Wert ist ein Array von Zeichenketten (String [7]), dessen Elemente die 7 Felder der Zeile in der passwd-Datei sind, die dem jeweiligen Login zugeordnet ist. Einige Felder können leer sein, wenn die Zeile weniger als 7 Felder enthält. | |
Liste der Fehlermeldungen – leer, wenn keine Fehler vorliegen |
7.2. Die Webanwendung, die
Es wird vorgeschlagen, die folgende Webanwendung (Formularseite) zu erstellen:
![]() |
Nr. | Name | Typ HTML | Rolle |
1 | cmbLogins | <select ...>...</select> | zeigt die Liste aller Logins an, zu denen Informationen angefordert werden können |
2 | btnChercher | <input type="submit" ...> | um die Suche zu starten |
Wenn der Benutzer auf die Schaltfläche [Chercher] (2) klickt, wird das Login aus (1) bei einem Objekt U vom Typ „users“ abgefragt. Wenn das Login existiert, erhält man folgende Antwort (Infoseite):
![]() |
Wie der oben gezeigte URL des Browsers zeigt, werden die Formularparameter über einen GET an den Server gesendet. Man kann dem Browser also direkt diesen mit den entsprechenden Parametern versehenen URL übergeben. Genau das tun wir hier, um ein Login einzugeben, das nicht existiert. Man erhält folgende Antwort (Fehlerseite):
![]() |
7.3. Die Architektur der Anwendung
![]() |
In dieser Architektur finden wir folgende Komponenten:
- die Ansichten:
- logins.jsp, die zur Anzeige der Liste der Anmeldungen dient (Ansicht 1)
- infos.jsp, die zur Anzeige der Informationen zu einem Login dient (Ansicht 2)
- erreurs.jsp, zur Anzeige einer Fehlerliste (Ansicht 3)
- Formulare vom Typ ActionForm, die von den Aktionen verwendet werden:
- formLogins wird verwendet, um die Daten des Formulars logins.jsp zu erfassen
- die Aktionen:
- SetupLoginAction, das den Inhalt von formulaire.jsp vorbereitet und anschließend diese Ansicht anzeigt
- InfosLoginAction, das den Inhalt von logins.jsp verarbeitet, sobald dieser an den Server gesendet wurde
- ForwardAction, das den Link [Retour vers le formulaire] der Ansichten infos.jsp und erreurs.jsp verarbeitet
- die Geschäftsklasse „users“, die von den Aktionen verwendet wird, um ihre Daten abzurufen
- das Modell, das durch die drei Flatfiles „passwd“, „group“ und „aliases“ bereitgestellt wird
7.4. Die Konfigurationsdateien der Webanwendung
7.4.1. Die Datei „server.xml“
Der Anwendungskontext wird /strutsquiest2 heißen. Daher fügen wir die folgende Zeile in die Tomcat-Datei server.xml ein:
Anschließend starten wir Tomcat gegebenenfalls neu, damit der neue Kontext berücksichtigt wird. Wir können dessen Gültigkeit überprüfen, indem wir die Seite „http://localhost:8080/strutsquiest2“ aufrufen.
7.4.2. Die Datei web.xml
Die Konfigurationsdatei web.xml der Anwendung sieht wie folgt aus:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN" "http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">
<web-app>
<servlet>
<servlet-name>strutsquiest2</servlet-name>
<servlet-class>istia.st.struts.quiest.Quiest2ActionServlet</servlet-class>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<init-param>
<param-name>passwdFileName</param-name>
<param-value>data/passwd</param-value>
</init-param>
<init-param>
<param-name>groupFileName</param-name>
<param-value>data/group</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>strutsquiest2</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>
Diese Datei web.xml enthält eine Neuerung. Der Struts-Controller ist nicht mehr org.apache.struts.action.ActionServlet, sondern eine abgeleitete Klasse, die wir hier istia.st.struts.quiest.Quiest2ActionServlet genannt haben. Dadurch können wir die beiden Initialisierungsparameter passwdFileName (Speicherort der Datei „passwd“) und groupFileName (Speicherort der Datei „group“) abrufen. Die Datei „aliases“ wird in dieser Anwendung nicht benötigt.
7.4.3. Die Datei struts-config.xml
Die Datei struts-config.xml sieht wie folgt aus:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE struts-config PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 1.1//EN"
"http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">
<struts-config>
<form-beans>
<form-bean name="formLogins" type="org.apache.struts.action.DynaActionForm">
<form-property name="cmbLogins" type="java.lang.String" initial=""/>
<form-property name="tLogins" type="java.lang.String[]"/>
</form-bean>
</form-beans>
<action-mappings>
<action
path="/init"
name="formLogins"
validate="false"
scope="session"
type="istia.st.struts.quiest.SetupLoginsAction"
>
<forward name="afficherLogins" path="/vues/logins.jsp"/>
<forward name="afficherErreurs" path="/vues/erreurs.jsp"/>
</action>
<action
path="/infosLogin"
name="formLogins"
validate="false"
scope="session"
type="istia.st.struts.quiest.InfosLoginAction"
>
<forward name="afficherErreurs" path="/vues/erreurs.jsp"/>
<forward name="afficherInfos" path="/vues/infos.jsp"/>
</action>
<action
path="/retourLogins"
parameter="/vues/logins.jsp"
type="org.apache.struts.actions.ForwardAction"
/>
</action-mappings>
<message-resources
parameter="istia.st.struts.quiest.ApplicationResources"
null="false"
/>
</struts-config>
Darin finden sich die drei Hauptabschnitte:
- die Deklaration der Formulare im Abschnitt <form-beans>
- die Deklaration der Aktionen im Abschnitt <action-mappings>
- die Deklaration der Ressourcendatei in <message-ressources>
7.4.4. Die Formularobjekte (Beans) der Anwendung
<form-beans>
<form-bean name="formLogins" type="org.apache.struts.action.DynaActionForm">
<form-property name="cmbLogins" type="java.lang.String" initial=""/>
<form-property name="tLogins" type="java.lang.String[]"/>
</form-bean>
</form-beans>
In unserer Anwendung gibt es nur ein Formular-Bean namens formLogins, dessen Typ von DynaActionForm abgeleitet ist. Es wird in folgenden Situationen verwendet:
- um die für die Anzeige der Ansicht Nr. 1 erforderlichen Daten zu enthalten
- die Werte aus dem Formular der Ansicht Nr. 1 abzurufen, wenn der Benutzer das Formular absendet (submit)
Die Struktur der Bean „formLogins“ ist mit dem Formular der Ansicht Nr. 1 verknüpft. Sehen wir uns diese einmal an:
![]() |
Nr. | Name | Typ HTML | Rolle |
1 | cmbLogins | <select ...>...</select> | zeigt die Liste aller Logins an, zu denen Informationen angefordert werden können |
2 | btnChercher | <input type="submit" ...> | um die Suche zu starten |
Es sind mehrere Fälle zu unterscheiden:
- Vom Client zum Server wird das Objekt formLogins verwendet, um die Werte des oben genannten Formulars HTML zu enthalten, das über die Schaltfläche [Envoyer] übermittelt wird. Daher benötigt es ein Feld cmbLogins, das den Wert des Feldes HTML, cmbLogins und c.a.d – also den vom Benutzer gewählten Benutzernamen – erhält.
- Vom Server zum Client wird das Objekt formLogins verwendet, um den Anfangsinhalt der Ansicht Nr. 1 bereitzustellen. Sein Feld tLogins dient als Inhalt für Liste 1. Über sein Feld cmbLogins wird das auszuwählende Element in Liste 1 festgelegt.
7.4.5. Die Aktionen der Anwendung
Die Aktionen werden durch Objekte vom Typ „Action“ oder davon abgeleitete Objekte ausgeführt. Die Konfiguration der Aktionen erfolgt innerhalb der Tags <action-mappings>:
<action-mappings>
<action
path="/init"
name="formLogins"
validate="false"
scope="session"
type="istia.st.struts.quiest.SetupLoginsAction"
>
<forward name="afficherLogins" path="/vues/logins.jsp"/>
<forward name="afficherErreurs" path="/vues/erreurs.jsp"/>
</action>
<action
path="/infosLogin"
name="formLogins"
validate="false"
scope="session"
type="istia.st.struts.quiest.InfosLoginAction"
>
<forward name="afficherErreurs" path="/vues/erreurs.jsp"/>
<forward name="afficherInfos" path="/vues/infos.jsp"/>
</action>
<action
path="/retourLogins"
parameter="/vues/logins.jsp"
type="org.apache.struts.actions.ForwardAction"
/>
</action-mappings>
Die Aktion /init
<action
path="/init"
name="formLogins"
validate="false"
scope="session"
type="istia.st.struts.quiest.SetupLoginsAction"
>
<forward name="afficherLogins" path="/vues/logins.jsp"/>
<forward name="afficherErreurs" path="/vues/erreurs.jsp"/>
</action>
Beschreiben wir die Funktionsweise der Aktion /init:
- Die Aktion /init findet normalerweise nur einmal während des ersten Anfrage-Antwort-Zyklus statt, in dem der Benutzer die URL-http://localhost:8080/strutsquiest2/init.do anfordert
- das Objekt formsLogins erstellt oder wiederverwendet wird. Es wird entsprechend dem Attribut „scope“ in die Sitzung übernommen (Wiederverwendung) oder dort abgelegt (Erstellung).
- Seine Methode „reset“ wird aufgerufen. Zur Erinnerung: Diese Methode führt in den Klassen ActionForm und deren abgeleiteten Klassen standardmäßig keine Aktion aus. Sie wird unmittelbar vor dem Kopieren der Daten aus der Client-Anfrage in das Objekt ActionForm aufgerufen und dient dazu, das Objekt vor diesem Kopiervorgang zu bereinigen. Was ist hier die Anfrage des Clients? Die Aktion /init wird ausgelöst, wenn die angeforderte URL http://localhost:8080/strutsquiest2/init.do lautet. Dieses URL kann von einem GET oder einem POST aufgerufen werden. Es reicht aus, in diese Anfrage Parameter mit den Feldnamen von formLogins aufzunehmen, damit diese initialisiert werden, wie das folgende Beispiel zeigt:

- Die Anfrage enthält den Parameter cmbLogins (afterpak). Der Struts-Controller hat daher den Wert dieses Parameters in das Feld cmbLogins von formLogins kopiert. Anschließend wurde die Aktion SetupLoginsAction ausgeführt und endete mit der Anzeige der Ansicht logins.jsp. Diese Ansicht enthält ein Formular, bei dem bestimmte Felder ihre Werte aus formLogins erhalten. So erhielt das Feld HTML (ein Select-Feld mit dem Namen cmbLogins) seinen Wert aus dem Feld cmbLogins (=afterpak) von formLogins. Aus diesem Grund wird die Liste der Logins auf dem Login afterpak positioniert angezeigt.
- Man könnte auch einen Parameter tLogins wie folgt übergeben:
Dies hätte zur Folge, dass das Feld tLogins von formLogins mit einem Array {"login1", "login2"} initialisiert würde. Allerdings werden wir weiter unten sehen, dass die Aktion SetupLoginsAction dem Feld tLogins einen Wert zuweist und das so erstellte Array durch ein neues Array ersetzt. Letzteres erscheint somit in der Ansicht logins.jsp.
- Die vorangegangene Erörterung ist zwar etwas komplex, zeigt jedoch, dass man nicht davon ausgehen kann, dass die Aktion /init ohne vom Client stammende Parameter ausgelöst wird. Es kann daher sinnvoll sein, die Methode reset zu verwenden, um formLogins zu bereinigen. In diesem Fall müssten wir die Klasse DynaActionForm ableiten. Dies haben wir hier nicht getan.
- Sobald die Methode „reset“ von formLogins aufgerufen wurde, kopiert der Controller die Daten aus der Client-Anfrage in die gleichnamigen Felder von formLogins. Normalerweise wird die Aktion /init ohne Parameter vom Client aufgerufen, aber wir haben zuvor gezeigt, dass nichts den Client daran hindert, die Aktion /init mit beliebigen Parametern aufzurufen. Am Ende dieser Phase können die Felder cmbLogins und tLogins daher durchaus einen Wert haben. Wir haben gesehen, dass das Feld cmbLogins diesen Wert beibehalten würde, das Feld tLogins jedoch nicht.
- Der Controller prüft anschließend das Attribut „validate“ der Aktion. Hier hat es den Wert „false“. Die Methode „validate“ von formLogins wird nicht aufgerufen. Wir werden sie daher nicht schreiben.
- Das Objekt SetupLoginsAction wird angelegt oder, falls es bereits existierte, wiederverwendet, und seine Methode „execute“ wird aufgerufen. Seine einzige Aufgabe besteht darin, dem Feld tLogins von formLogins einen Wert zuzuweisen. Dieser Wert ist das Array der Logins, das von der Fachklasse „users“ angefordert wird. Dieser Vorgang kann fehlschlagen. Aus diesem Grund können auf die Aktion /init zwei Ansichten folgen:
- die Ansicht erreurs.jsp, wenn die Klasse „users“ das Anmeldedaten-Array nicht bereitstellen konnte
- die Ansicht logins.jsp andernfalls
- Der Controller lässt eine dieser beiden Ansichten anzeigen
- Der Anfrage-Antwort-Zyklus der Aktion /init ist abgeschlossen.
Die Aktion /infosLogin
<action
path="/infosLogin"
name="formLogins"
validate="false"
scope="session"
type="istia.st.struts.quiest.InfosLoginAction"
>
<forward name="afficherErreurs" path="/vues/erreurs.jsp"/>
<forward name="afficherInfos" path="/vues/infos.jsp"/>
</action>
Beschreiben wir die Funktionsweise der Aktion / infosLogin:
- Die Aktion /infosLogin wird normalerweise ausgeführt, wenn der Benutzer auf die Schaltfläche [Chercher] in der Ansicht logins.jsp klickt. Daraufhin wird eine Anfrage an den Server gesendet, die durch das Tag HTML <form> der Ansicht festgelegt wird:
<html:form name="formLogins" method="get" action="/infosLogin" type="org.apache.struts.action.DynaActionForm">
- Man sieht, dass die Anfrage mit der GET-Methode an den Server gesendet wird. Der Benutzer kann sie daher manuell eingeben:

- Das Objekt formsLogins wird erstellt oder wiederverwendet. Es wird entsprechend dem Attribut „scope“ in die Sitzung geladen (Wiederverwendung) oder dort abgelegt (Erstellung).
- Seine Methode „reset“ wird unmittelbar vor dem Kopieren der Daten aus der Client-Anfrage in das Objekt ActionForm aufgerufen. Sie hat normalerweise die Form „http://localhost:8080/strutsquiest2/infosLogin.do?cmbLogins=xx“, wobei „xx“ ein aus der Liste der Logins ausgewähltes Login ist. Es kann jedoch auch beliebig sein, wenn der Benutzer das vorherige Objekt URL unter Übergabe beliebiger Parameter verwendet hat. Betrachten wir die folgende Seitenfolge:

- Die Aktion /infosLogin wurde mit der Parameterzeichenfolge cmbLogins=xx&tLogins=login1&tLogins=login2 aufgerufen. Die Felder cmbLogins und tLogins von formLogins erhalten somit jeweils die Werte „xx“ und {„login1“, „login2“}. Die Aktion /infosLogin fragt bei der Fachklasse „users“ die Informationen zum Login „xx“ ab. Die Klasse „users“ antwortet, dass dieses Login nicht existiert. Daher die oben angezeigte Ansicht. Verwenden wir nun den oben genannten Link [Retour au formulaire]:

- Durch den Link [Retour au formulaire] wird die Aktion /retourLogins ausgelöst. Diese Aktion zeigt lediglich die Ansicht logins.jsp an, ohne dass zwischendurch weitere Schritte erfolgen. Zur Erinnerung: Das Feld tLogins dient dazu, die Liste der Logins in der Ansicht logins.jsp zu füllen. Da der Benutzer diesen Wert in {"login1","login2"} geändert hat, erscheinen nun diese beiden Logins in der Liste. Erneut kann nur betont werden, wie absolut notwendig es ist, bei der Funktionsweise einer Anwendung den Fall willkürlicher Parameter zu berücksichtigen, die von einem Benutzer oder einem Programm festgelegt werden. Die Lösung für das hier dargestellte Problem wäre, dass der Link [Retour au formulaire] auf die Aktion /init verweist. So wäre sichergestellt, dass die korrekte Liste der Logins abgerufen wird.
- Kehren wir zu einer normalen Anfrage an die Aktion /infosLogin zurück, etwa wie folgt:
http://localhost:8080/strutsquiest2/infosLogin.do?cmbLogins=afterpak
- Der Struts-Controller weist dem Feld cmbLogins des Objekts ActionForm einen Wert zu. Das Feld tLogins hingegen wird nicht zugewiesen (kein entsprechendes Feld in der gesendeten Anfrage). Diese Funktionsweise ist für uns in Ordnung. Wir müssen daher keine benutzerdefinierte Reset-Methode für formLogins schreiben.
- Sobald die Reset-Methode von formLogins aufgerufen wurde, überträgt der Controller die Daten aus der Client-Anfrage in die gleichnamigen Felder von formLogins. Das Feld cmbLogins erhält einen Wert, nämlich den vom Benutzer gewählten Benutzernamen (afterpak).
- Anschließend prüft der Controller das Attribut „validate“ der Aktion. Hier hat es den Wert „false“. Die Methode „validate“ von formLogins wird nicht aufgerufen.
- Das Objekt InfosLoginAction wird erstellt oder, falls es bereits existierte, wiederverwendet, und seine Methode „execute“ wird aufgerufen. Seine Aufgabe besteht darin, die mit dem Login cmbLogins verbundenen Informationen abzurufen. Diese Informationen werden von der Geschäftsklasse „users“ angefordert. Dieser Vorgang kann fehlschlagen (z. B. wenn das Login nicht existiert). Aus diesem Grund können auf die Aktion /infosLogin zwei Ansichten folgen:
- die Ansicht erreurs.jsp, wenn die Klasse „users“ die angeforderten Informationen nicht bereitstellen konnte
- die Ansicht infos.jsp andernfalls
- Der Controller lässt eine dieser beiden Ansichten anzeigen
- Der Anfrage-Antwort-Zyklus der Aktion /infosLogin ist abgeschlossen.
Die Aktion /retourLogins
<action
path="/retourLogins"
parameter="/vues/logins.jsp"
type="org.apache.struts.actions.ForwardAction"
/>
- Die Aktion /retourLogins wird durch das Anklicken des Links [Retour au formulaire] in den Ansichten erreurs.jsp und infos.jsp ausgelöst.
- Hier ist der Aktion kein Formular zugeordnet. Es wird daher sofort die Methode `execute` eines Objekts `ForwardAction` aufgerufen, die ein Objekt `ActionForward` zurückgibt, das auf die Ansicht `/vues/logins.jsp` verweist.
7.4.6. Die Meldungsdatei der Anwendung
Der dritte Abschnitt der Datei struts-config.xml ist die Meldungsdatei:
Die Datei ApplicationResources.properties befindet sich im Verzeichnis WEB-INF/classes/istia/st/struts/quiest. Ihr Inhalt lautet wie folgt:
errors.header=<ul>
errors.footer=</ul>
parametreManquant=<li>Le paramètre [{0}] n'a pas été initialisé</li>
usersException=<li>Erreur d'initialisation de l'application : {0}</li>
loginInconnu=<li>Le login [{0}] n'existe pas</li>
7.5. Der Code der Ansichten
Der Leser wird gebeten, die Lektion zur Formularverwaltung noch einmal durchzulesen, falls er den Code der unten dargestellten Ansichten nicht versteht.
7.5.1. Die Ansicht logins.jsp
Zur Erinnerung: Diese Ansicht wird in zwei Fällen angezeigt:
- beim Aufruf der Aktion /init im ersten Anfrage-Antwort-Zyklus
- beim Aufruf der Aktion /retourLogins in den folgenden Zyklen
Der Code der Ansicht logins.jsp lautet wie folgt:
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<html>
<head>
<title>Quiest - formulaire</title>
</head>
<body background="<html:rewrite page="/images/standard.jpg"/>">
<center>
<h2>Application QuiEst</h2>
<hr>
<html:form name="formLogins" method="get" action="/infosLogin" type="org.apache.struts.action.DynaActionForm">
<table>
<tr>
<td>Login cherché</td>
<td>
<html:select name="formLogins" property="cmbLogins">
<html:options name="formLogins" property="tLogins"/>
</html:select>
</td>
<td>
<html:submit value="Chercher"/>
</td>
</tr>
</table>
</html:form>
</center>
</body>
</html>
7.5.2. Die Ansicht infos.jsp
Diese Ansicht wird bei einem erfolgreichen Aufruf der Aktion /infosLogin angezeigt. Ihr Code lautet wie folgt:
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<html>
<head>
<title><bean:write name="infosLoginBean" scope="request" property="titre"/></title>
</head>
<body background="<html:rewrite page="/images/standard.jpg"/>">
<h2><bean:write name="infosLoginBean" scope="request" property="titre"/></h2>
<hr>
<table border="1">
<tr>
<th>login</th><th>pwd</th><th>uid</th><th>gid</th><th>id</th><th>dir</th><th>shell</th>
</tr>
<tr>
<td><bean:write name="infosLoginBean" scope="request" property="infosLogin[0]"/></td>
<td><bean:write name="infosLoginBean" scope="request" property="infosLogin[1]"/></td>
<td><bean:write name="infosLoginBean" scope="request" property="infosLogin[2]"/></td>
<td><bean:write name="infosLoginBean" scope="request" property="infosLogin[3]"/></td>
<td><bean:write name="infosLoginBean" scope="request" property="infosLogin[4]"/></td>
<td><bean:write name="infosLoginBean" scope="request" property="infosLogin[5]"/></td>
<td><bean:write name="infosLoginBean" scope="request" property="infosLogin[6]"/></td>
</tr>
</table>
<br>
<html:link page="/retourLogins.do">
Retour au formulaire
</html:link>
</body>
</html>
Diese Ansicht verwendet ein Objekt namens infosLoginBean, das durch die Aktion /infosLogin in die Abfrage eingefügt wurde. Dieses Objekt hat zwei Felder:
String titre; // in der Ansicht anzuzeigender Titel
String[] infosLogin; // Tabelle mit den in der Ansicht anzuzeigenden Informationen
Wir werden diese Klasse näher erläutern, wenn wir uns mit dem Code der Klasse InfosLoginAction befassen.
7.5.3. Die Ansicht erreurs.jsp
Diese Ansicht wird angezeigt, wenn die Aktionen /init oder /infosLogin mit einem Fehler enden. Ihr Code lautet wie folgt:
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<html>
<head>
<title>Application QuiEst - erreurs</title>
</head>
<body background="<html:rewrite page="/images/standard.jpg"/>">
<h2 align="center">Application QuiEst - Erreurs</h2>
<hr>
<h2>Les erreurs suivantes se sont produites</h2>
<html:errors/>
<html:link page="/retourLogins.do">
Retour au formulaire
</html:link>
</body>
</html>
7.6. Die Java-Klassen
Die Datei web.xml verweist auf eine Java-Klasse:
<web-app>
<servlet>
<servlet-name>strutsquiest2</servlet-name>
<servlet-class>istia.st.struts.quiest.Quiest2ActionServlet</servlet-class>
....
</servlet>
...
</web-app>
Die Konfigurationsdatei struts-config.xml verweist ihrerseits auf zwei Java-Klassen:
<action
path="/infosLogin"
name="formLogins"
validate="false"
scope="session"
type="istia.st.struts.quiest.InfosLoginAction"
>
<forward name="afficherErreurs" path="/vues/erreurs.jsp"/>
<forward name="afficherInfos" path="/vues/infos.jsp"/>
</action>
<action
path="/init"
name="formLogins"
validate="false"
scope="session"
type="istia.st.struts.quiest.SetupLoginsAction"
>
<forward name="afficherLogins" path="/vues/logins.jsp"/>
<forward name="afficherErreurs" path="/vues/erreurs.jsp"/>
</action>
7.6.1. Die Klasse Quiest2ActionServlet
Die Klasse Quiest2ActionServlet ist von der Klasse ActionServlet abgeleitet, der Struts-Controller-Klasse. Wir leiten die Klasse ActionServlet ab, um ihre init-Methode anzupassen. Diese Methode, die beim ersten Laden des Servlets einmalig ausgeführt wird, ermöglicht es uns nämlich, ein Geschäftsobjekt vom Typ „users“ zu erstellen. Dieses Objekt muss tatsächlich nur einmal erstellt werden, und die init-Methode ist ein geeigneter Ort, um diese Erstellung durchzuführen. Das Objekt „users“ benötigt zum Erstellen zwei Dateien: die Dateien „passwd“ und „group“. Der Speicherort dieser beiden Dateien wird in der Datei „web.xml“ der Anwendung als Parameter an das Servlet übergeben:
<servlet>
<servlet-name>strutsquiest2</servlet-name>
<servlet-class>istia.st.struts.quiest.Quiest2ActionServlet</servlet-class>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<init-param>
<param-name>passwdFileName</param-name>
<param-value>data/passwd</param-value>
</init-param>
<init-param>
<param-name>groupFileName</param-name>
<param-value>data/group</param-value>
</init-param>
</servlet>
Der Servlet-Code lautet wie folgt:
package istia.st.struts.quiest;
import java.util.*;
import javax.servlet.*;
import org.apache.struts.action.*;
import istia.st.users.*;
public class Quiest2ActionServlet
extends ActionServlet {
// Attribute des Servlets
private users u = null;
private ActionErrors erreurs = new ActionErrors();
private String[] tLogins;
//init
public void init() throws ServletException {
// Vergessen Sie nicht, die übergeordnete Klasse zu initialisieren
super.init();
// Lokale Variablen
final String[] initParams = {"passwdFileName", "groupFileName"};
Properties params = new Properties();
// Die Initialisierungsparameter des Servlets werden abgerufen
ServletConfig config = getServletConfig();
String servletPath = config.getServletContext().getRealPath("/");
for (int i = 0; i < initParams.length; i++) {
String valeur = config.getInitParameter(initParams[i]);
if (valeur == null) {
erreurs.add(ActionErrors.GLOBAL_ERROR, new ActionError("parametreManquant", initParams[i]));
valeur = "";
}
// Der Parameter wird gespeichert
params.setProperty(initParams[i], valeur);
} //for
// Rückgabe, falls Initialisierungsfehler aufgetreten sind
if (erreurs.size() != 0) {
return;
}
// ein „users“-Objekt wird erstellt
try {
u = new users(servletPath + "/" + params.getProperty("passwdFileName"),
servletPath + "/" + params.getProperty("groupFileName"), null);
}
catch (Exception ex) {
erreurs.add(ActionErrors.GLOBAL_ERROR, new ActionError("usersException", ex.getMessage()));
return;
} //catch
// Die Liste der Logins wird abgerufen
tLogins = new String[u.getUsersByLogin().size()];
Enumeration eLogins = u.getUsersByLogin().keys();
for (int i = 0; i < tLogins.length; i++) {
tLogins[i] = (String) eLogins.nextElement();
}
// die Logins werden sortiert
Arrays.sort(tLogins);
} //init
// Methode für den Zugriff auf die privaten Daten des Servlets
public Object[] getInfos() {
return new Object[] {erreurs, u, tLogins};
}
}
Zusammenfassend lässt sich die Funktionsweise der Methode „init“ wie folgt beschreiben:
- Zunächst wird die „init“-Methode der übergeordneten Klasse (ActionServlet) aufgerufen, damit diese korrekt initialisiert wird
- Anschließend werden die Initialisierungsparameter gelesen. Fehlen welche, wird das private Attribut ActionErrors „fehler“ gesetzt.
- Sind die Initialisierungsparameter vorhanden, wird ein „users“-Objekt erstellt. Bei dieser Erstellung kann eine Ausnahme ausgelöst werden. In diesem Fall wird das Attribut „ActionErrors“ mit Fehlern gefüllt.
- Wenn die Erstellung erfolgreich war, wird aus dem erstellten Objekt die Liste aller Logins abgerufen und diese in ein Array sortiert, das im privaten Attribut „String[] tLogins“ abgelegt wird.
- Das erstellte Objekt „users“ wird im privaten Attribut „users u“ gespeichert.
- Die öffentliche Methode getInfos ermöglicht es, die drei privaten Attribute (u, Fehler, tLogins) in einem Objektarray abzurufen.
7.6.2. Die Klasse SetupLoginsAction
Diese Aktion dient dazu, das Objekt DynaActionForm formLogins zu initialisieren. Dieses in der Sitzung abgelegte Objekt muss anschließend nicht mehr neu initialisiert werden. Die Aktion SetupLoginsAction wird daher nur einmal ausgeführt. Ihr Code lautet wie folgt:
package istia.st.struts.quiest;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import org.apache.struts.action.*;
public class SetupLoginsAction
extends Action {
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) throws IOException,ServletException {
// bereitet das anzuzeigende Formular vor
// die Informationen werden vom Controller-Servlet abgerufen
// infos=(ActionErrors Fehler, Benutzer u, String[] tLogins)
Object[] infos = ( (Quiest2ActionServlet)this.getServlet()).getInfos();
// Sind Initialisierungsfehler aufgetreten?
ActionErrors erreurs = (ActionErrors) infos[0];
if (!erreurs.isEmpty()) {
this.saveErrors(request, erreurs);
return mapping.findForward("afficherErreurs");
}
// Die Anmeldedaten werden in das Formular eingegeben
DynaActionForm formLogins=(DynaActionForm) form;
formLogins.set("tLogins",infos[2]);
return mapping.findForward("afficherLogins");
}
}
Wie bei allen Struts-Aktionen befindet sich der Code in der Methode „execute“. Diese:
- ruft vom Struts-Controller die Informationen ab, die dieser mithilfe seiner „init“-Methode gespeichert hat. Dies wird durch die Methode getServlet() der Klasse „Action“ ermöglicht.
- Darunter befindet sich das Attribut „ActionErrors – Fehler des Controllers“. Ist diese Fehlerliste nicht leer, wird sie in die Abfrage aufgenommen und die Anzeige der Ansicht „erreurs.jsp“ angefordert.
- Ist die Fehlerliste leer, wird dem Feld tLogins des Beans formLogins die ursprünglich vom Controller erstellte Liste der Anmeldungen zugewiesen. Anschließend wird die Anzeige der Ansicht logins.jsp angefordert, die die Liste der Anmeldungen anzeigt.
7.6.3. Die Klassen InfosLoginBean und InfosLoginAction
Die Aktion InfosLoginAction dient dazu, die Informationen zum vom Benutzer ausgewählten Login abzurufen und diesem anzuzeigen. Die Informationen werden in einem Objekt vom Typ InfosLoginBean zusammengefasst:
package istia.st.struts.quiest;
public class InfosLoginBean implements java.io.Serializable{
// Bean, das die für die Informationsseite erforderlichen Daten enthält
private String titre;
private String[] infosLogin;
// Konstruktor
public InfosLoginBean(String titre, String[] infosLogin){
this.titre=titre;
this.infosLogin=infosLogin;
}
// Getter
public String getTitre(){
return this.titre;
}
public String[] getInfosLogin(){
return this.infosLogin;
}
public String getInfosLogin(int i){
return this.infosLogin[i];
}
}
Die vorherige Klasse ist ein Bean, c.a.d. Eine Java-Klasse, in der ein privates Attribut T unAttribut automatisch zwei privaten Methoden zugeordnet wird:
- void setUnAttribut(T Wert){unAttribut=Wert;}
- T getUnAttribut(){ return unAttribut;}
Beachten Sie die spezielle Syntax der get- und set-Methoden. Wenn das Attribut ein Array T[] unAttribut ist, können get- und set-Methoden für die Elemente des Arrays erstellt werden:
- void setUnAttribut(T Wert, int i){unAttribut[i]=Wert;}
- T getUnAttribut(int i){ return unAttribut[i];}
Zum besseren Verständnis betrachten wir noch einmal den Code der Ansicht infos.jsp, die im Anschluss an die Aktion InfosLoginAction gesendet werden muss:
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<html>
<head>
<title><bean:write name="infosLoginBean" scope="request" property="titre"/></title>
</head>
<body background="<html:rewrite page="/images/standard.jpg"/>">
<h2><bean:write name="infosLoginBean" scope="request" property="titre"/></h2>
<hr>
<table border="1">
<tr>
<th>login</th><th>pwd</th><th>uid</th><th>gid</th><th>id</th><th>dir</th><th>shell</th>
</tr>
<tr>
<td><bean:write name="infosLoginBean" scope="request" property="infosLogin[0]"/></td>
<td><bean:write name="infosLoginBean" scope="request" property="infosLogin[1]"/></td>
<td><bean:write name="infosLoginBean" scope="request" property="infosLogin[2]"/></td>
<td><bean:write name="infosLoginBean" scope="request" property="infosLogin[3]"/></td>
<td><bean:write name="infosLoginBean" scope="request" property="infosLogin[4]"/></td>
<td><bean:write name="infosLoginBean" scope="request" property="infosLogin[5]"/></td>
<td><bean:write name="infosLoginBean" scope="request" property="infosLogin[6]"/></td>
</tr>
</table>
<br>
<html:link page="/retourLogins.do">
Retour au formulaire
</html:link>
</body>
</html>
Nehmen wir den folgenden Tag:
Sie fordert dazu auf, den Wert des Feldes „Titel“ (property) des Objekts infosLoginBean (name) zu schreiben, das in die Abfrage (scope) aufgenommen wurde. Der zu schreibende Wert wird über request.getAttribute("infosLoginBean") ermittelt.getTitre(). Daher muss die Methode getTitre in der Klasse InfosLoginBean vorhanden sein. Dies ist der Fall. Das Tag
fordert an, den Wert des Elements infosLogin[0] des in der Abfrage angegebenen Objekts infosLoginBean zu schreiben. Der zu schreibende Wert wird über request.getAttribute("infosLoginBean").getInfosLogin(0) ermittelt. Daher muss die Methode getInfosLogin(int i) in der Klasse InfosLoginBean vorhanden sein. Dies ist der Fall.
Die Klasse InfosLoginAction dient dazu, das vorhergehende Objekt InfosLoginBean anhand eines vom Benutzer gewählten Logins zu erstellen. Ihr Code lautet wie folgt:
package istia.st.struts.quiest;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import org.apache.struts.action.*;
import istia.st.users.*;
public class InfosLoginAction
extends Action {
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) throws IOException,ServletException {
// muss die zu einem Login gehörenden Informationen anzeigen
// Die Informationen werden vom Controller-Servlet abgerufen
// Infos=(ActionErrors Fehler, Benutzer u, LoginBean[] tLogins)
Object[] infos = ( (Quiest2ActionServlet)this.getServlet()).getInfos();
// Gab es Initialisierungsfehler?
ActionErrors erreurs = (ActionErrors) infos[0];
if (!erreurs.isEmpty()) {
this.saveErrors(request, erreurs);
return mapping.findForward("afficherErreurs");
}
// Zuerst diese Anmeldedaten abrufen
String login = (String) ( (DynaActionForm) form).get("cmbLogins");
// Gibt es da etwas?
if (login == null) {
// Das ist nicht normal – wir senden das Formular mit den Zugangsdaten zurück
DynaActionForm formLogins=(DynaActionForm) form;
formLogins.set("tLogins",infos[2]);
return mapping.findForward("afficherLogins");
}
// Wir haben ein Login – wir suchen danach
String[] infosLogin = (String[]) ( (users) infos[1]).getUsersByLogin().get(login);
// Wurde es gefunden?
if (infosLogin == null) {
// Der Login wurde nicht gefunden – die Fehlerseite wird angezeigt
ActionErrors erreurs2=new ActionErrors();
erreurs2.add(ActionErrors.GLOBAL_ERROR, new ActionError("loginInconnu", login));
this.saveErrors(request, erreurs2);
return mapping.findForward("afficherErreurs");
}
// Der Login wurde gefunden – die gefundenen Informationen werden in die Anfrage übernommen
String titre="Application QuiEst - login["+login+"]";
InfosLoginBean infosLoginBean= new InfosLoginBean(titre,infosLogin);
request.setAttribute("infosLoginBean",infosLoginBean);
return mapping.findForward("afficherInfos");
}
}
Die Methode „execute“ funktioniert wie folgt:
- Es werden die Informationen abgerufen, die der Struts-Controller bei seiner Initialisierung erfasst hat. Sollte dieser Fehler festgestellt haben, wird die Ausführung an dieser Stelle unterbrochen und die Anzeige dieser Fehler angefordert.
- Es wird überprüft, ob tatsächlich ein Login vorhanden ist. Wenn der Benutzer das Formular zur Auswahl eines Logins verwendet hat, ist das Login vorhanden. Der Benutzer kann jedoch auch die URL der Aktion direkt in seinen Browser eingeben, ohne Parameter zu übergeben. Ist kein Login vorhanden, wird die Liste der Logins erneut angezeigt.
- Wenn ein Login vorhanden ist, werden die zugehörigen Informationen von der Geschäftsklasse „users“ angefordert. Wenn diese das gesuchte Login nicht findet, wird die Fehlerseite angezeigt. Andernfalls wird ein Objekt vom Typ „InfosLoginBean“ erstellt, um die Informationen zu speichern, die die Ansicht „infos.jsp“ benötigt. Dieses Objekt wird in die Abfrage eingefügt und die Seite „infos.jsp“ wird angezeigt.
7.7. Bereitstellung
Die Struktur der Anwendung sieht wie folgt aus:
![]() | ![]() |
![]() | ![]() |
![]() | ![]() |
![]() |
7.8. Fazit
Wir haben Struts in einer realistischen Anwendung unter Verwendung einer Geschäftsklasse eingesetzt. Außerdem haben wir gezeigt, dass man der von einem Client gesendeten Anfrage besondere Aufmerksamkeit schenken und keinerlei Annahmen über deren Art treffen sollte. Eine Anfrage kann beliebig sein, und jede Anwendung muss zunächst deren Gültigkeit überprüfen.











