Skip to content

2. Ein erstes Beispiel

Die meisten unserer Beispiele beschränken sich auf die Webschicht, die mit Struts 2 implementiert wird:

Sobald die Grundlagen vermittelt sind, werden wir ein komplexeres Beispiel mit einer mehrschichtigen Architektur betrachten.

2.1. Erstellung des Beispiels

Wir erstellen unsere erste Anwendung.

  • In [1] erstellen wir ein neues Projekt
  • in [2] wählen wir den Typ „Java Web / Webanwendung“
  • In [3] geben wir dem Projekt einen Namen
  • In [4] geben wir den Speicherort des Projekts an.
  • In [5] wird das neue Projekt zum Hauptprojekt festgelegt.
  • In [6] wählen wir den Tomcat-Server aus. Bei der Installation von NetBeans 7.01 wurden zwei Server installiert: Apache Tomcat und GlassFish 3.1.
  • In [7] geben wir an, dass wir mit dem Struts-2-Framework arbeiten werden. Diese Möglichkeit ergibt sich aus der Installation des Plugins für Struts 2. Ohne das Plugin wird uns das Struts-2-Framework nicht angeboten.
  • In [8] wird die Erstellung des Beispielprojekts angefordert, das wir untersuchen werden.
  • In [9] kann man überprüfen, welche Struts-2-Bibliotheken verwendet werden.
  • In [10] sehen wir das generierte Projekt. Wir werden darauf zurückkommen.
  • In [11] befinden sich die Bibliotheken des Projekts. Sie wurden vom Struts-2-Plugin integriert. Falls das Plugin nicht vorhanden ist, finden sich diese Bibliotheken im Ordner [lib] der heruntergeladenen Struts-2-Distribution. In diesem Fall folgen wir den Schritten 12 und 13.

2.2. Das im Dateisystem generierte Projekt

  • in [1], zeigt die Registerkarte [Projects] eine „Entwickleransicht“ des Projekts
  • in [2] zeigt die Registerkarte [Files] den Projektordner im Dateisystem an
  • in [2A] wird der Zweig [Web Pages] in [2] durch den Ordner [web] [2B] dargestellt
  • In [3A] wird der Zweig [Source Packages] in [2] durch den Ordner [java] [3B] dargestellt

2.3. Die Konfigurationsdatei [META-INF/context.xml]

Diese Datei lautet wie folgt:


<?xml version="1.0" encoding="UTF-8"?>
<Context antiJARLocking="true" path="/exemple-01"/>

Zeile 2 gibt an, dass der Kontext der Webanwendung /exemple-01 ist. Alle URLs vom Typ [http://machine:port/exemple-01/...] werden von dieser Anwendung verarbeitet. Dieser Kontext ist in den Eigenschaften des Projekts [2] zu finden: Rechtsklick auf das Projekt / Eigenschaften / Ausführen.

2.4. Die Konfigurationsdatei [WEB-INF/web.xml]

Jede Webanwendung wird über die Datei [web.xml] im Ordner [WEB-INF] der Anwendung konfiguriert. Die generierte Datei lautet wie folgt:


<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
    <filter>
        <filter-name>struts2</filter-name>
        <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>struts2</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <session-config>
        <session-timeout>
            30
        </session-timeout>
    </session-config>
    <welcome-file-list>
        <welcome-file>example/HelloWorld.jsp</welcome-file>
    </welcome-file-list>
</web-app>
  • Die Zeilen 3–6 definieren einen Filter, der durch die Struts-2-Klasse [org.apache.struts2.dispatcher.FilterDispatcher] implementiert wird. Diese Klasse übernimmt die Rolle des Controllers C des Modells MVC.
  • Zeilen 7–10: Definieren eine Verknüpfung zwischen einem URL-Muster und dem Filter, der die URLs verarbeiten soll, die diesem Muster entsprechen. Hier wird festgelegt, dass jede URL (Muster /*) vom Filter mit dem Namen struts2 verarbeitet werden soll. Dabei handelt es sich um den in den Zeilen 3–6 definierten Filter. Somit werden hier alle URLs über den Struts-2-Controller geleitet.
  • Zeilen 11–15: Legen die Dauer einer Benutzersitzung fest, hier 30 Minuten. Bei den verschiedenen Anfragen wird ein Benutzer durch ein Sitzungstoken verfolgt, das ihm bei seiner ersten Anfrage zugewiesen wurde und das er anschließend bei jeder neuen Anfrage systematisch übermittelt. Dadurch kann der Webserver den Nutzer erkennen und ein „Gedächtnis“ für ihn verwalten, das als Sitzung bezeichnet wird. Wenn zwischen zwei Anfragen mehr als 30 Minuten vergehen, wird ein neues Sitzungstoken für den Nutzer generiert, der somit sein „Gedächtnis“ verliert und ein neues beginnt.
  • Zeilen 16–18: Legen die Datei fest, die angezeigt werden soll, wenn der Benutzer die Webanwendung aufruft, ohne ein Dokument anzufordern. Wenn also die angeforderte URL „[http://machine:port/exemple-01]“ lautet, wird die URL „[http://machine:port/exemple-01/example/HelloWord.jsp]“ zurückgegeben.

2.5. Die Konfigurationsdatei [struts.xml]

Die Datei [struts.xml] ist die Konfigurationsdatei für Struts 2. Sie kann sich an beliebiger Stelle im Verzeichnis ClassPath des Projekts befinden. Im oben genannten NetBeans-Projekt besteht das Verzeichnis ClassPath des Projekts aus den beiden Zweigen:

  • Quellpakete
  • Bibliotheken

Jeder Ordner oder jede Bibliothek, die sich in diesen beiden Zweigen befindet, ist somit Teil des ClassPath des Projekts. Es ist üblich, [struts.xml] im <default package> abzulegen. Hier hat es folgenden Inhalt:


<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>
    <include file="example.xml"/>
    <!-- Konfiguration für das Standardpaket. -->
    <package name="default" extends="struts-default">
    </package>
</struts>
  • Zeilen 5 und 10: Das Stamm-Tag des Dokuments ist das <struts>-Tag
  • Zeile 6: Die Datei „[example.xml]“ wird hier eingefügt. Sie bringt somit ihre eigene Konfiguration mit. Wir werden darauf zurückkommen.
  • Zeilen 8–9: Hier wird ein Paket mit dem Namen „default“ definiert. Ein Paket ermöglicht die Konfiguration einer Gruppe von Struts-2-Aktionen mit derselben URL. Zum Beispiel [/chemin/Action1] und [/chemin/Action2]. Für diese Aktionen kann dann ein Paket definiert werden:

<package name="employes" namespace="/employes" extends="struts-default">
... configuration
    </package>

Das oben genannte Paket heißt „employes“ und konfiguriert die URL-Aktionen /employes/Action. Das Paket kann von einem anderen Paket mit dem Schlüsselwort „extends“ erben. Im obigen Beispiel erbt das Paket employes vom Paket struts-default. Dieses Paket befindet sich in der Datei [struts-default.xml] der Bibliothek struts2-core.jar:

Das in der Datei [struts-default.xml] definierte Paket „struts-default“ konfiguriert verschiedene Aspekte, darunter eine Liste von Interceptoren, die beim Aufruf einer Aktion ausgeführt werden. Kehren wir zur Struktur MVC einer Struts-2-Anwendung zurück:

Um eine URL der Form [http://machine:port/.../Action] zu verarbeiten, instanziiert der Controller [FilterDispatcher] die Klasse, die die angeforderte Aktion implementiert, und führt eine ihrer Methoden aus – standardmäßig eine Methode namens execute. Der Aufruf dieser Methode execute durchläuft eine Reihe von Interceptoren:

Sowohl die Interceptoren als auch die Aktion verarbeiten dieselbe Anfrage. Die im Paket struts-default definierte Liste der Interceptoren ist in den meisten Fällen ausreichend. Daher werden unsere Struts-Pakete immer das Paket struts-default erweitern. Um zu erfahren, was die verschiedenen Interceptoren bewirken, lesen Sie Kapitel 4 von [ref2].

Kommen wir nun zurück zur Konfigurationsdatei struts.xml:


<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>
    <include file="example.xml"/>
    <!-- Konfiguration für das Standardpaket. -->
    <package name="default" extends="struts-default">
    </package>
</struts>
  • Zeile 8: Das Paket mit dem Namen default hat eine besondere Funktion. Es verarbeitet Aktionen, die in den anderen Paketen nicht konfiguriert wurden. Hier, in den Zeilen 8–9, ist keine Konfiguration für das Paket default vorgenommen worden. Man könnte daher die Definition dieses Pakets entfernen.

Sehen wir uns nun die Datei [example.xml] an, die in Zeile 6 der Datei [struts.xml] eingebunden ist:


<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE struts PUBLIC
        "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
        "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>
    <package name="example" namespace="/example" extends="struts-default">
        <action name="HelloWorld" class="example.HelloWorld">
            <result>/example/HelloWorld.jsp</result>
        </action>
    </package>
</struts>
  • Zeile 8: Definiert ein Paket namens example, das das Paket struts-default erweitert. Dieses Paket verwaltet URLs vom Typ /example/Action (Namespace /example).
  • Zeilen 9–11: Definieren eine Aktion mit dem Namen HelloWorld (Attribut name), die somit der URL /example/Helloworld entspricht. Diese Aktion wird von einer Instanz der Klasse example.HelloWorld (Attribut class) verarbeitet.
  • Der Controller [FilterDispatcher] lässt die Methode execute dieser Klasse ausführen.
  • Diese Methode gibt eine Zeichenkette zurück, die als Navigationsschlüssel bezeichnet wird.
  • Die verschiedenen Navigationsschlüssel müssen durch Tags <result name="Schlüssel"/> (Zeile 10) definiert werden. Fehlt das Attribut name, wird standardmäßig der Schlüssel success verwendet. Dies ist im obigen Fall der Fall. Daher muss die Methode execute der Klasse example.HelloWorld dem Controller [FilterDispatcher] den Schlüssel success zurückgeben.
  • Der Controller [FilterDispatcher] lässt dann die Seite [/example/HelloWorld.jsp] anzeigen (Zeile 10).

Wenn wir die beiden Dateien [struts.xml] und [example.xml] zusammenführen und das Paket default löschen, das unnötig erscheint, verläuft alles so, als hätten wir die Datei [struts.xml] auf die einzige Datei [example.xml] reduziert.

2.6. Die Aktion HelloWorld

Laut der untersuchten Datei [struts.xml] wird die Aktion HelloWorld ausgelöst, wenn die vom Client angeforderte URL /example/HelloWorld lautet. Daraufhin wird ihre Methode execute ausgeführt. Diese muss einen Navigationsschlüssel zurückgeben. Wir haben gesehen, dass es nur einen gab: success, und dass daraufhin die Seite /example/HelloWorld.jsp als Antwort an den Benutzer gesendet wurde.

Der Code der Aktion HelloWorld lautet wie folgt:


package example;

import com.opensymphony.xwork2.ActionSupport;

public class HelloWorld extends ActionSupport {

    public String execute() throws Exception {
        setMessage(getText(MESSAGE));
        return SUCCESS;
    }

    public static final String MESSAGE = "HelloWorld.message";

    private String message;

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}
  • Zeile 5: Die Klasse „HelloWorld“ leitet sich von der Struts-2-Klasse „ActionSupport“ ab. Dies ist fast immer der Fall. Dadurch können bestimmte Methoden genutzt werden, wie beispielsweise die Methode „getText“ in Zeile 8.
  • Zeilen 7–10: Die Methode execute, die beim Aufruf des Struts-Controllers ausgeführt wird. Es ist bekannt, dass sie eine Zeichenkette zurückgeben muss, daher ihre Signatur in Zeile 7. Hier gibt sie die Konstante SUCCESS zurück, die ebenfalls in ActionSupport definiert ist. Weitere Konstanten sind auf diese Weise für das Ergebnis der Methode execute definiert:
Constante
Valeur
SUCCESS
„success“
ERROR
"error"
INPUT
„input“
LOGIN
„login“

Hier erzeugt die Methode execute also die Zeichenfolge success. Bezieht man sich auf die Datei „[struts.xml]“, wird dem Benutzer also die Seite „/example/HelloWorld.jsp“ als Antwort zurückgegeben.

  • Zeile 8: Die Methode execute initialisiert das Feld message in Zeile 14 mit dem Wert von getText(" HelloWorld.message "). Die Methode getText gehört zur übergeordneten Klasse ActionSupport. Sie ermöglicht es, einen Text aus einer Datei entsprechend der verwendeten Sprache abzurufen. Standardmäßig wird hier die Datei package.properties verwendet, die sich im selben Paket wie die Aktion befindet. Diese gibt es in zwei Versionen:

Die Datei [package.properties] lautet wie folgt:

HelloWorld.message= Struts is up and running ...

Es handelt sich um eine Folge von Textzeilen in der Form clé=valeur. Wird diese Datei verwendet, hat getText("HelloWorld.message") den Wert „Struts is up and running ...“

Die Datei [package_es.properties] sieht wie folgt aus:

HelloWorld.message= ¡Struts está bien! ...

Wenn die vom Client-Browser verwendete Sprache Spanisch ist (Attribut „es“ in package_es.properties), hat getText („HelloWorld.message“) den Wert „¡Struts está bien!“. ... In allen anderen Fällen wird die Datei [package.properties] verwendet.

2.7. Die Ansicht HelloWorld.jsp

Dies ist das letzte Puzzleteil von Struts. Es handelt sich um die Ansicht, die angezeigt wird, wenn die URL /example/HelloWorld aufgerufen wird. Wir haben gesehen, welchen Weg die ursprüngliche Anfrage zurückgelegt hat, um schließlich diese Antwort anzuzeigen. Der Code der Seite lautet wie folgt:


<%@ page contentType="text/html; charset=UTF-8" %>

<%@ taglib prefix="s" uri="/struts-tags" %>

<html>
    <head>
        <title><s:text name="HelloWorld.message"/></title>
    </head>

    <body>
        <h2><s:property value="message"/></h2>

        <h3>Languages</h3>
        <ul>
            <li>
                <s:url id="url" action="HelloWorld">
                    <s:param name="request_locale">en</s:param>
                </s:url>
                <s:a href="%{url}">English</s:a>
            </li>

            <li>
                <s:url id="url" action="HelloWorld">
                    <s:param name="request_locale">es</s:param>
                </s:url>

                <s:a href="%{url}">Espanol</s:a>

            </li>
        </ul>
    </body>
</html>
  • Die Seite verwendet HTML-Tags (Zeilen 5, 6, …) sowie Tags aus einer Bibliothek, die in Zeile 3 definiert ist. Alle <s:xx>-Tags gehören zu dieser Bibliothek.
  • Zeile 7: Das Tag <s:text> ermöglicht die Anzeige eines unterschiedlichen Textes je nach Sprache des Client-Browsers. Das Attribut „name“ gibt den Schlüssel an, nach dem in den Nachrichtendateien gesucht werden soll. Auch hier werden die Dateien package_xx.properties verwendet. Zur Erinnerung: Diese enthalten nur eine einzige Nachricht mit dem Schlüssel HelloWorld.message.
  • Zeile 11: Mit dem Tag <s:property name="Eigenschaft"> kann der Wert einer Eigenschaft eines Objekts namens ActionContext ausgegeben werden. In diesem Objekt finden sich:
  • die Eigenschaften der ausgeführten Aktion. name="message" zeigt den Wert des Feldes „message“ der aktuellen Aktion an. Es sind die Methoden get und set, die mit dem Feld verknüpft sind, die verwendet werden, um dessen Wert abzurufen oder ihn zu initialisieren. Diese Methoden müssen daher vorhanden sein.
  • die Attribute der aktuellen Anfrage, gekennzeichnet durch <s:property name="#request['clé']">
  • die Attribute der Benutzersitzung, gekennzeichnet mit <s:property name="#session['clé']">
  • die Attribute der Anwendung selbst, gekennzeichnet mit <s:property name="#application['clé']">
  • die vom Client-Browser gesendeten Parameter, gekennzeichnet mit <s:property name="#parameters['clé']">
  • Die Notation <s:property name="#attr['clé']"> zeigt den Wert eines Objekts an, das nacheinander in der Seite, der Anfrage, der Sitzung und der Anwendung gesucht wird.
  • Zeilen 16–18: Das Tag <s:url> dient zur Definition einer URL. Das Attribut „id“ vergibt einen Namen für die zu erstellende URL. Dieser Name wird anschließend in Zeile 19 verwendet. Das Attribut „action“ gibt an, auf welche Aktion die URL verweisen soll.
  • Zeile 17: Mit dem Tag <s:param ..> können der URL Parameter in der Form ?param1=valeur1&param2=valeur2&... hinzugefügt werden. Hier lautet der hinzugefügte Parameter ?request_locale=es.

Letztendlich lautet die generierte URL wie folgt:

Um diese URL zu verstehen, muss man bedenken, dass die Seite [HelloWorld.jsp] in zwei Fällen angezeigt wird:

  • bei direkter Aufrufe der URL [/exemple-01/example/HelloWorld.jsp]
  • bei Aufruf der Aktion [/exemple-01/example/HelloWorld.action]

In beiden Fällen lautet der URL-Pfad /exemple-01/example.. Das Tag <s:url action= „...“> fügt diesem Pfad die durch das Attribut action definierte Aktion hinzu. So erhält man /beispiel-01/example/HelloWorld. Anschließend fügt sie das Suffix .action zur vorherigen URL hinzu sowie gegebenenfalls die URL-Parameter. Die resultierende URL /beispiel-01/example/HelloWorld.action?request_locale=en, ruft die in der Datei [struts.xml] definierte Aktion HelloWorld auf und übergibt dabei den Parameter request_locale=en. Letzterer wird nicht von der Aktion HelloWorld verarbeitet, sondern von einem der Struts-Interceptors, nämlich dem, der die Internationalisierung der Seiten verwaltet. Der Parameter request_locale wird erkannt und verarbeitet. Die Sprache der Seiten wird auf Englisch umgestellt (en).

  • Zeile 19: Definiert einen HTML-Link. Das href-Attribut des Tags <a> erwartet eine Zeichenkette. Hier soll der Wert der in Zeile 16 definierten URL mit der ID „url“ verwendet werden. Dazu wird href="%{url}" geschrieben. Die Variable url wird ausgewertet und ihr Wert dem Attribut href zugewiesen. In den meisten Fällen erfolgt die Auswertung von Variablen implizit. Zum Beispiel, wenn man schreibt

<s:property name= "message "/>

wird der Wert der Eigenschaft message angezeigt und nicht die Zeichenfolge „message“. In anderen Fällen muss die Auswertung von Variablen oder Eigenschaften jedoch erzwungen werden. Hätte man href= „url“ geschrieben, wäre dem Attribut href die Zeichenfolge url zugewiesen worden.

  • Zeilen 23–27: Erstellen einen HTML-Link, um die Sprache der Seiten auf Spanisch umzustellen.

2.8. Ausführung der Anwendung

Wir starten die Ausführung des Projekts:

  • In [1] wird die Ausführung des Projekts [exemple-01] gestartet. Der Tomcat-Webserver wird dann automatisch gestartet, falls er nicht bereits lief. Die URL [/exemple-01] wird aufgerufen ([3]). Die Datei [web.xml] wird dann verwendet:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
    <filter>
        <filter-name>struts2</filter-name>
        <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>struts2</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <session-config>
        <session-timeout>
            30
        </session-timeout>
    </session-config>
    <welcome-file-list>
        <welcome-file>example/HelloWorld.jsp</welcome-file>
    </welcome-file-list>
</web-app>
  • da in der angeforderten URL „[/exemple-01]“ keine Seite angegeben ist, verwendet Tomcat das Tag „<welcome_file-list>“ aus den Zeilen 16 und 18. Es wird also die URL /exemple-01/example/HelloWorld.jsp ausgeliefert.
  • Da Struts 2 alle URLs verarbeitet (Zeile 8 und 9), wird diese URL von Struts gefiltert. Da sie keiner Aktion, sondern einer JSP-Seite entspricht, wird diese angezeigt.
  • Was wir also unter [2] sehen, ist die Seite HelloWorld.jsp, die wir bereits betrachtet haben.
  • In [4] ist zu sehen, dass weder das Tag <title><s:text name="HelloWorld.message"/></title> noch das Tag <h2><s:property value="message"/></h2> ihre Wirkung entfaltet haben. Der Grund dafür ist, dass keine Aktion aufgerufen wurde. Die Liste der Interceptoren, die vor der Aktion ausgeführt werden, wurde daher nicht abgearbeitet, insbesondere derjenige, der die Internationalisierung verwaltet. Das Internationalisierungs-Tag <s:text ...> konnte nicht korrekt verarbeitet werden. Außerdem existiert die Eigenschaft message, die auf das Feld „message“ der Klasse Action1 verweist, nicht. Daher wird nichts angezeigt.

Folgen wir nun dem Link [English]. Wir gelangen auf die folgende Seite:

  • in [1], der angeforderten URL. Wir haben bereits erklärt, wie diese URL gebildet wird. Diesmal wird eine Struts-Aktion angefordert: die Aktion HelloWorld, die in [example.xml] definiert ist.

<struts>
    <package name="example" namespace="/example" extends="struts-default">
        <action name="HelloWorld" class="example.HelloWorld">
            <result>/example/HelloWorld.jsp</result>
        </action>
    </package>
</struts>
  • Die Methode execute dieser Aktion wurde ausgeführt. Wir haben gesehen, dass sie den Schlüssel success zurückgibt. Aus Zeile 4 oben lässt sich ableiten, dass die Seite /example/HelloWorld.jsp als Antwort an den Client zurückgegeben wird. Das sehen wir in [3].
  • Bei [1] sehen wir, dass die angeforderte URL durch den Parameter request_locale=en festgelegt wird. Die Sprache der Seiten ist nun Englisch. Diese Sprachauswahl wird nämlich in der Sitzung des Benutzers gespeichert, der diese Auswahl beibehält, bis er sie ändert.
  • In [2] und [3] sehen wir die Internationalisierung in Aktion. Die Tags <title><s:text name="HelloWorld.message"/></title> und <h2><s:property value="message"/></h2> haben diesmal ihre Wirkung entfaltet.

Wenn wir nun den Link [Espanol] auswählen, erhalten wir die Seite auf Spanisch:

2.9. Conclusion

Wir haben ein Beispiel untersucht, das automatisch vom Struts-2-Plugin für NetBeans generiert wurde. Dabei haben wir festgestellt, dass es recht schwierig war, die Verarbeitung einer Anfrage nachzuvollziehen. Folgende Elemente spielen dabei eine Rolle:

  • die Konfiguration [web.xml], [struts.xml]
  • die ausgeführte Aktion: Interceptoren und die Methode execute.

Anfangs mag die Funktionsweise von Struts 2 komplex erscheinen. Es dauert ein wenig, bis man sich daran gewöhnt hat. Wir werden nun eine Reihe von Beispielen vorstellen, die jeweils einen bestimmten Aspekt von Struts beleuchten.