Skip to content

2. Een eerste voorbeeld

De meeste van onze voorbeelden zullen beperkt blijven tot de weblaag die met Struts 2 is geïmplementeerd:

Zodra de basisbeginselen onder de knie zijn, zullen we een complexer voorbeeld met een meerlaagse architectuur bestuderen.

2.1. Het voorbeeld genereren

We bouwen onze eerste applicatie.

  • in [1] maken we een nieuw project aan
  • in [2], we kiezen het type Java Web / Web Application
  • in [3] geven we het project een naam
  • in [4] geven we de locatie van het project op.
  • in [5], maak je van het nieuwe project het hoofdproject.
  • in [6], kies je de Tomcat-server. Bij de installatie van NetBeans 7.01 zijn twee servers geïnstalleerd: Apache Tomcat en GlassFish 3.1.
  • In [7] geven we aan dat we met het Struts 2-framework gaan werken. Dit is mogelijk dankzij de installatie van de Struts 2-plugin. Zonder de plugin wordt het Struts 2-framework niet aangeboden.
  • In [8] wordt gevraagd om het voorbeeldproject aan te maken dat we gaan bestuderen.
  • In [9] kunnen we controleren welke Struts 2-bibliotheken zullen worden gebruikt.
  • in [10]: het gegenereerde project. We komen hier later op terug.
  • in [11], de bibliotheken van het project. Deze zijn geïntegreerd door de Struts 2-plugin. Als je de plugin niet hebt, kun je deze bibliotheken vinden in de map [lib] van de gedownloade Struts 2-distributie. Volg dan stap 12 en 13.

2.2. Het gegenereerde project in het bestandssysteem

  • in [1], toont het tabblad [Projects] een „ontwikkelaarsweergave” van het project
  • in [2] toont het tabblad [Files] de projectmap in het bestandssysteem
  • in [2A] wordt de branch [Web Pages] weergegeven in [2] door de map [web] [2B]
  • in [3A] wordt de tak [Source Packages] in [2] weergegeven door de map [java] [3B]

2.3. Het configuratiebestand [META-INF/context.xml]

Dit bestand is als volgt:


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

Regel 2 geeft aan dat de context van de webapplicatie /exemple-01 is. Alle URL-bestanden van het type [http://machine:port/exemple-01/...] worden door deze applicatie verwerkt. Deze context is te vinden in de eigenschappen van het project [2]: klik met de rechtermuisknop op het project / Properties / Run.

2.4. Het configuratiebestand [WEB-INF/web.xml]

Elke webapplicatie wordt geconfigureerd via het bestand [web.xml] in de map [WEB-INF] van de applicatie. Het gegenereerde bestand is het volgende:


<?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>
  • de regels 3-6 definiëren een filter dat wordt geïmplementeerd door de klasse [org.apache.struts2.dispatcher.FilterDispatcher] van Struts 2. Deze klasse vervult de rol van controller C van het model MVC.
  • Regels 7-10: definiëren een koppeling tussen een model van het type URL en het filter dat de URL-modellen moet verwerken die aan dit model voldoen. Hier wordt aangegeven dat elke URL (sjabloon /*) moet worden verwerkt door het filter met de naam struts2. Dit is het filter dat in de regels 3-6 is gedefinieerd. Alle URL-verzoeken zullen dus via de Struts 2-controller verlopen.
  • regels 11-15: hierin wordt de duur van een gebruikerssessie gedefinieerd, in dit geval 30 minuten. Bij verschillende verzoeken wordt een gebruiker gevolgd door een sessietoken dat bij zijn eerste verzoek aan hem is toegewezen en dat hij vervolgens bij elk nieuw verzoek systematisch meestuurt. Hierdoor kan de webserver de gebruiker herkennen en een „geheugen“ voor de gebruiker beheren, dat de sessie wordt genoemd. Als er tussen twee verzoeken meer dan 30 minuten verstrijken, wordt er een nieuw sessietoken voor de gebruiker gegenereerd, waardoor deze zijn „geheugen“ verliest en een nieuwe sessie begint.
  • regels 16-18: definiëren het bestand dat moet worden weergegeven wanneer de gebruiker de webapplicatie opvraagt zonder om een document te vragen. Dus wanneer het opgevraagde URL [http://machine:port/exemple-01] is, wordt het geleverde URL [http://machine:port/exemple-01/example/HelloWord.JSP].

2.5. Het configuratiebestand [struts.xml]

Het bestand [struts.xml] is het configuratiebestand van Struts 2. Dit kan zich overal in de map ClassPath van het project bevinden. In het bovenstaande NetBeans-project bestaat de map ClassPath van het project uit twee takken:

  • Bronpakketten
  • Bibliotheken

Elke map of bibliotheek die zich in deze twee takken bevindt, maakt dus deel uit van het project ClassPath. Het is gebruikelijk om [struts.xml] in <default package> te plaatsen. Hier is de inhoud ervan als volgt:


<!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"/>
    <!-- Configuratie voor het standaardpakket. -->
    <package name="default" extends="struts-default">
    </package>
</struts>
  • regels 5 en 10: de root-tag van het document is de tag <struts>
  • regel 6: het bestand [example.xml] wordt hier ingevoegd. Het brengt dus zijn eigen configuratie mee. We komen hier later op terug.
  • regels 8-9: definiëren een pakket dat hier "default" wordt genoemd. Met een pakket kun je een groep Struts 2-acties configureren die dezelfde URL hebben. Bijvoorbeeld [/chemin/Action1] en [/chemin/Action2]. We kunnen dan een pakket voor deze acties definiëren:

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

Het bovenstaande pakket heet "medewerkers" en configureert de acties van URL en /employes/Action. Het pakket kan een ander pakket overnemen met het trefwoord "extends". Hierboven erft het pakket employes van het pakket struts-default. Dit pakket bevindt zich in het bestand [struts-default.xml] van de bibliotheek struts2-core.jar:

Het pakket "struts-default", gedefinieerd in het bestand [struts-default.xml], configureert diverse zaken, waaronder een lijst met interceptors die worden uitgevoerd wanneer een actie wordt aangeroepen. Laten we terugkeren naar de structuur MVC van een Struts 2-toepassing:

Om een URL van het type [http://machine:port/.../Action] te verwerken, zal de controller [FilterDispatcher] de klasse instantiëren die de gevraagde actie implementeert en een van de methoden daarvan uitvoeren, standaard een methode met de naam execute. De aanroep van deze methode execute doorloopt een reeks interceptors:

Zowel de interceptors als de actie verwerken allemaal hetzelfde verzoek. De lijst met interceptors die in het pakket struts-default is gedefinieerd, volstaat in de meeste gevallen. Daarom zullen onze Struts-pakketten altijd het pakket struts-default uitbreiden. Om te weten wat de verschillende interceptors doen, raadpleeg je hoofdstuk 4 van [ref2].

Laten we teruggaan naar het configuratiebestand 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"/>
    <!-- Configuratie voor het standaardpakket. -->
    <package name="default" extends="struts-default">
    </package>
</struts>
  • regel 8: het pakket met de naam default heeft een bijzondere rol. Het verwerkt acties die niet in de andere pakketten zijn geconfigureerd. Hier, in de regels 8-9, is er geen configuratie voor het pakket default. We zouden de definitie van dit pakket dus kunnen verwijderen.

Laten we nu eens kijken naar het bestand [example.xml] dat in regel 6 van het bestand [struts.xml] is opgenomen:


<?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>
  • regel 8: definieert een pakket met de naam example dat het pakket struts-default uitbreidt. Dit pakket beheert de URL van het type /example/Action (naamruimte /example).
  • regels 9-11: definiëren een actie met de naam HelloWorld (attribuut name), die dus overeenkomt met URL /example/Helloworld. Deze actie wordt verwerkt door een instantie van de klasse example.HelloWorld (attribuut class).
    • De controller [FilterDispatcher] zorgt ervoor dat de methode execute van deze klasse wordt uitgevoerd.
    • Deze methode retourneert een tekenreeks die de navigatiesleutel wordt genoemd.
    • De verschillende navigatiesleutels moeten worden gedefinieerd met <result name="sleutel"/>-tags (regel 10). Als het attribuut name ontbreekt, wordt standaard de sleutel success gebruikt. Dit is hierboven het geval. De methode execute van de klasse example.HelloWorld moet dus de sleutel success teruggeven aan de controller [FilterDispatcher].
    • De controller [FilterDispatcher] zorgt er vervolgens voor dat de pagina [/example/HelloWorld.JSP] wordt weergegeven (regel 10).

Als we de twee bestanden [struts.xml] en [example.xml] samenvoegen en het pakket default verwijderen, dat overbodig lijkt, dan is het alsof we het bestand [struts.xml] hebben teruggebracht tot het enige bestand [example.xml].

2.6. De actie HelloWorld

Volgens het onderzochte bestand [struts.xml] wordt de actie HelloWorld geactiveerd wanneer het door de klant aangevraagde URL /example/HelloWorld is. De bijbehorende methode execute wordt dan uitgevoerd. Deze moet een navigatiesleutel retourneren. We hebben gezien dat er maar één was: success, en dat vervolgens de pagina /example/HelloWorld.JSP als antwoord naar de gebruiker werd verzonden.

De code van de actie HelloWorld is als volgt:

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;
    }
}
  • regel 5: de klasse HelloWorld is afgeleid van de Struts 2-klasse ActionSupport. Dit is vrijwel altijd het geval. Hierdoor kan gebruik worden gemaakt van bepaalde methoden, zoals de methode getText op regel 8.
  • regels 7-10: de methode execute die wordt uitgevoerd wanneer de Struts-controller wordt aangeroepen. We weten dat deze methode een tekenreeks moet retourneren, vandaar de signatuur op regel 7. Hier retourneert ze de constante SUCCESS, die eveneens is gedefinieerd in ActionSupport. Op dezelfde manier zijn andere constanten gedefinieerd voor het resultaat van de methode execute:
Constante
Valeur
SUCCESS
"success"
ERROR
"error"
INPUT
"input"
LOGIN
"login"

In dit geval levert de methode execute dus de tekenreeks success op. Als we kijken naar het bestand [struts.xml], is het dus de pagina /example/HelloWorld.JSP die als antwoord aan de gebruiker wordt teruggestuurd.

  • regel 8: de methode execute initialiseert het veld message op regel 14 met de waarde van getText(" HelloWorld.message "). De methode getText behoort tot de bovenliggende klasse ActionSupport. Hiermee kan tekst uit een bestand worden opgehaald op basis van de gebruikte taal. Standaard wordt hier het bestand package.properties gebruikt, dat zich in hetzelfde pakket bevindt als de actie. Dit bestand is er in twee versies:

Het bestand [package.properties] ziet er als volgt uit:

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

Het is een reeks tekstregels in de vorm clé=valeur. Als dit bestand wordt gebruikt, krijgt getText("HelloWorld.message") de waarde Struts is up and running ...

Het bestand [package_es.properties] ziet er als volgt uit:

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

Als de taal die door de clientbrowser wordt gebruikt Spaans is (attribuut es, in package_es.properties), zal getText("HelloWorld.message") de waarde ¡Struts está bien! hebben ... In alle andere gevallen wordt het bestand [package.properties] gebruikt.

2.7. De weergave HelloWorld.JSP

Dit is het laatste stukje van de Struts-puzzel. Dit is de weergave die wordt getoond wanneer de URL /example/HelloWorld wordt opgevraagd. We hebben gezien welk doolhof de oorspronkelijke aanvraag heeft doorlopen om uiteindelijk dit antwoord weer te geven. De code van de pagina is als volgt:


<%@ 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>
  • De pagina maakt gebruik van HTML-tags (regels 5, 6, ...) en tags uit een bibliotheek die in regel 3 wordt gedefinieerd. Alle <s:xx>-tags behoren tot deze bibliotheek.
  • regel 7: met de tag <s:text> kan een andere tekst worden weergegeven, afhankelijk van de taal van de browser van de klant. Het attribuut name geeft de sleutel aan die in de berichtbestanden moet worden gezocht. Ook hier worden de bestanden package_xx.properties gebruikt. We herinneren ons dat deze slechts één bericht met de sleutel HelloWorld.message bevatten.
  • regel 11: met de tag <s:property name="eigenschap"> kan de waarde van een eigenschap van een object met de naam ActionContext worden weergegeven. In dit object bevinden zich:
    • de eigenschappen van de actie die is uitgevoerd. name="message" geeft de waarde weer van het veld ‘message’ van de huidige actie. Het zijn de methoden get en set die aan het veld zijn gekoppeld, die worden gebruikt om de waarde ervan op te halen of te initialiseren. Deze methoden moeten dus bestaan.
    • de attributen van de huidige aanvraag, aangeduid met <s:property name="#request['clé']">
    • de attributen van de gebruikerssessie, aangeduid met <s:property name="#session['clé']">
    • de attributen van de applicatie zelf, aangeduid met <s:property name="#application['clé']">
    • de door de clientbrowser verzonden parameters, aangeduid met <s:property name="#parameters['clé']">
    • de notatie <s:property name="#attr['clé']"> geeft de waarde weer van een object dat in deze volgorde wordt gezocht in de pagina, de verzoek, de sessie en de applicatie.
  • regels 16-18: de tag <s:url > wordt gebruikt om een URL te definiëren. Het attribuut id geeft een naam aan de URL die zal worden aangemaakt. Deze naam wordt vervolgens gebruikt in regel 19. Het attribuut action geeft aan naar welke actie de URL moet verwijzen.
  • regel 17: met de tag <s:param ..> kunnen parameters aan de URL worden toegevoegd in de vorm ?param1=valeur1&param2=valeur2&.... Hier wordt de parameter ?request_locale=es toegevoegd.

Uiteindelijk zal de gegenereerde URL er als volgt uitzien:

Om deze URL te begrijpen, moet je bedenken dat de pagina [HelloWorld.JSP] in twee gevallen wordt weergegeven:

  • (vervolg)
    • op direct verzoek van de URL [/exemple-01/example/HelloWorld.JSP]
    • op verzoek van de actie [/exemple-01/example/HelloWorld.action]

In beide gevallen is het pad van URL /exemple-01/example.. De tag <s:url action= "... "> voegt de actie die is gedefinieerd door het attribuut action toe aan dit pad. Zo krijgen we /voorbeeld-01/voorbeeld/HelloWorld. Vervolgens voegt het het achtervoegsel .action toe aan het vorige URL, evenals de parameters van het URL, indien aanwezig. Het resulterende URL /voorbeeld-01/voorbeeld/HelloWorld.action?request_locale=en zal de actie HelloWorld aanroepen die is gedefinieerd in het bestand [struts.xml], waarbij de parameter request_locale=en wordt doorgegeven. Deze parameter wordt niet verwerkt door de actie HelloWorld, maar door een van de Struts-interceptors, namelijk degene die de internationalisering van de pagina’s beheert. De parameter request_locale wordt herkend en verwerkt. De taal van de pagina’s wordt Engels (en).

  • regel 19: definieert een HTML-link. Het href-attribuut van de tag <a> verwacht een tekenreeks. Hier willen we de waarde van URL gebruiken, die in regel 16 is gedefinieerd en de id URL heeft. Daarvoor schrijven we: href="%{URL}". De variabele URL wordt geëvalueerd en de waarde ervan wordt toegewezen aan het attribuut href. In de meeste gevallen gebeurt de evaluatie van variabelen impliciet. Bijvoorbeeld wanneer we schrijven
<s:property name= "message "/>

wordt de waarde van de eigenschap message weergegeven en niet de tekenreeks " message ". Maar in andere gevallen moet de evaluatie van variabelen of eigenschappen worden geforceerd. Als we href="URL" hadden geschreven, zou de tekenreeks URL aan het attribuut href zijn toegewezen.

  • regels 23-27: maken een HTML-link aan om de taal van de pagina’s naar het Spaans te wijzigen.

2.8. De applicatie uitvoeren

We starten de uitvoering van het project:

  • in [1] wordt het project [exemple-01] gestart. De Tomcat-webserver wordt dan automatisch gestart als deze nog niet actief was. Het bestand URL wordt opgevraagd, gevolgd door [/exemple-01] en [3]. Vervolgens wordt het bestand [web.xml] gebruikt:

<?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>
  • omdat in de opgevraagde URL [/exemple-01] geen pagina is gespecificeerd, zal Tomcat de tag <welcome_file-list> uit de regels 16 en 18 gebruiken. Het is dus de URL /voorbeeld-01/voorbeeld/HelloWorld.JSP die wordt weergegeven.
  • Omdat Struts 2 alle URL-tags (regels 8 en 9) verwerkt, wordt deze URL door Struts gefilterd. Aangezien deze niet overeenkomt met een actie maar met een pagina JSP, wordt deze laatste weergegeven.
  • Wat we dus zien in [2] is de pagina HelloWorld.JSP die we hebben bestudeerd.
  • In [4] zien we dat de tag <title><s:text name="HelloWorld.message"/></title> geen effect heeft gehad, evenmin als de tag <h2><s:property value="message"/></h2>. De reden hiervoor is dat er geen actie is aangeroepen. De lijst met interceptors die vóór de actie worden uitgevoerd, is dus niet uitgevoerd, met name degene die de internationalisering afhandelt. De internationalisatietag <s:text ...> kon niet correct worden verwerkt. Bovendien bestaat de eigenschap message, die verwijst naar het veld message van de klasse Action1, niet. Vandaar dat er niets wordt weergegeven.

Laten we nu de link [English] volgen. We krijgen de volgende pagina te zien:

  • in [1], de gevraagde URL. We hebben uitgelegd hoe deze URL tot stand is gekomen. Deze keer wordt een Struts-actie aangevraagd: de actie HelloWorld die is gedefinieerd in [example.xml].

<struts>
    <package name="example" namespace="/example" extends="struts-default">
        <action name="HelloWorld" class="example.HelloWorld">
            <result>/example/HelloWorld.JSP</result>
        </action>
    </package>
</struts>
  • De methode execute van deze actie is uitgevoerd. We hebben gezien dat deze de sleutel success retourneerde. Uit regel 4 hierboven kunnen we afleiden dat de pagina /example/HelloWorld.JSP als antwoord naar de client wordt teruggestuurd. Dit zien we in [3].
  • In [1] zien we dat de opgevraagde URL wordt ingesteld door de parameter request_locale=en. De taal van de pagina’s is voortaan Engels. Deze taalkeuze wordt namelijk opgeslagen in de sessie van de gebruiker, die deze keuze behoudt totdat hij deze wijzigt.
  • In [2] en [3] zien we internationalisering in de praktijk. De tags <title><s:text name="HelloWorld.message"/></title> en <h2><s:property value="message"/></h2> hebben deze keer hun werk gedaan.

Als we nu de link [Espanol] selecteren, krijgen we de pagina in het Spaans te zien:

2.9. Conclusion

We hebben een voorbeeld bestudeerd dat automatisch is gegenereerd door de Struts 2-plugin voor NetBeans. We hebben vastgesteld dat het vrij moeilijk was om de verwerking van een verzoek te volgen. De volgende elementen spelen een rol:

  • de configuratie [web.xml], [struts.xml]
  • de uitgevoerde actie: interceptors en de methode execute.

In het begin kan de werking van Struts 2 complex lijken. Het kost wat tijd om eraan te wennen. We zullen nu een reeks voorbeelden presenteren die elk een specifiek aspect van Struts verduidelijken.