Struts 2 (Basics)
Inhalt:
Konfiguration
Action "GeometricModelAction"
struts.xml
JSP
Dieses Beispiel baut auf der gleichen Logik auf wie die JSP-Beispiel, es existiert also nur eine Web-Anwendung mit minimaler Programmlogik.
Bereits durchgeführte Berechnungen werden in der Session gespeichert.
Hier gibt es das Projekt als WAR-Export-Datei: StrutsBasics.war.
Die Seite des Struts-Projekts: http://struts.apache.org/
Ein interessanter Artikel über die Unterschiede von Struts 1 und Struts 2 (und eigentlich auch eine gute Grundlage) ist dieser hier:
http://www.infoq.com/articles/migrating-struts-2-part2.
Konfiguration
Wir benötigen Struts 2.0.8.
Folgende Dateien müssen in WEB-INF\lib kopiert werden damit die simple Anwendung sich deployen läßt:
- freemarker-2.3.8.jar
- ognl-2.6.11.jar
- struts2-core-2.0.8.jar
- xwork-2.0.3.jar
ACHTUNG: das Deploy der Anwendung endet in der Konsolenausgabe so:
22:17:48,109 INFO [ObjectTypeDeterminerFactory] Detected GenericsObjectTypeDeterminer, initializing it...
Von den drei Punkten nicht irritieren lassen, das bedeutet nicht dass der Server auf irgendetwas wartet, sondern das Deploy ist wirklich abgeschlossen.
Action "GeometricModelAction"
Zuallererst fügen wir die Struts-Action "GeometricModelAction" zu die die Anwendungslogik enthält.
Ihr Klassenrumpf sieht so aus:
import org.apache.struts2.interceptor.ServletRequestAware;
import com.opensymphony.xwork2.ActionSupport;
public class GeometricModelAction extends ActionSupport implements ServletRequestAware
{
private HttpServletRequest request;
public void setServletRequest(HttpServletRequest httpServletRequest)
{
this.request = httpServletRequest;
}
...
}
Die Parentklasse com.opensymphony.xwork2.ActionSupport
markiert die Klasse als Struts-Action, d.h. eine Klasse die
bei einem Form-Submit aufgerufen wird. Anmerkung: diese Parentklasse muss nicht zwingend angegeben werden.
Das Interface import org.apache.struts2.interceptor.ServletRequestAware
wird implementiert damit der Request mittels
der Interface-Methode setServletRequest
in die Klasse geschrieben wird. Wir benutzen ihn um die Daten der Session zu speichern.
Jetzt werden der Klasse Properties zugefügt:
private double dblA = 0;
private double dblB = 0;
private double dblC = 0;
private double dblOberflaeche = 0;
private double dblVolumen = 0;
private Historie historieGesamt = null;
public double getA()
{
return dblA;
}
public void setA(double dblA)
{
this.dblA = dblA;
}
public double getB()
{
return dblB;
}
public void setB(double dblB)
{
this.dblB = dblB;
}
public double getC()
{
return dblC;
}
public void setC(double dblC)
{
this.dblC = dblC;
}
Es gibt get-Properties für die aktuell berechneten Werte sowie die Historie:
private double dblOberflaeche = 0;
private double dblVolumen = 0;
private Historie historieGesamt = new Historie();
public Historie getHistorieGesamt()
{
if (this.request == null)
{
throw new NullPointerException("request nicht gesetzt !");
}
if (this.request.getSession().getAttribute("historieGesamt") == null)
{
this.request.getSession().setAttribute("historieGesamt", new Historie() );
}
this.historieGesamt = (Historie) this.request.getSession().getAttribute("historieGesamt");
return this.historieGesamt;
}
public double getVolumen()
{
return this.dblVolumen;
}
public double getOberflaeche()
{
return this.dblOberflaeche;
}
getHistorieGesamt
hat eine Besonderheit: beim ersten Abrufen innerhalb des Requests wird die Historie aus einem Attribut im SessionContext
geholt. Ist dieses nicht vorhanden (erster Aufruf der Seite), so wird es gesetzt. Struts 2 bietet leider keine so eleganten Wege wie JSF Daten in der Session zu speichern.
Schließlich wird die Methode execute
der Basisklasse ActionSupport
überladen, die beim Klick auf "Submit" aufgerufen wird und
die Berechnung durchführt sowie die aktuelle Berechnung der Historie zufügt.
public String execute() throws Exception
{
//Ausrechnen:
this.dblVolumen = this.dblA * this.dblB * this.dblC;
this.dblOberflaeche = 2 * (this.dblA * this.dblB) + 2 * (this.dblA * this.dblC) + 2 * (this.dblB * this.dblC);
//Zufügen zur Historie !
Seitenlaengen seitenlaengeAktuell = new Seitenlaengen();
seitenlaengeAktuell.setA(this.dblA);
seitenlaengeAktuell.setB(this.dblB);
seitenlaengeAktuell.setC(this.dblC);
//Die Historie per Get-Methode abrufen damit sie bei Bedarf erzeugt wird.
this.getHistorieGesamt().addSeitenlaenge( seitenlaengeAktuell );
return "success";
}
Zur Rückgabe einer solchen Submit-Methode wird in der Config-Datei die zugehörige Zielseite deklariert.
struts.xml
Die Konfiguration des Struts-Frameworks erfolgt primär in der Datei "struts.xml". Diese liegt NICHT in WEB-INF sondern
muss ins Verzeichnis "src" (im Eclipse: "Java Resources:src") gepackt werden damit es beim Compilieren und Deploy in "classes" landet, wo Struts sie sucht.
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<package name="strutsbasics" extends="struts-default">
<action name="geometricmodel" class="de.fhw.swtvertiefung.knauf.strutsbasics.actions.GeometricModelAction">
<result>/geometricmodel.jsp</result>
</action>
</package>
</struts>
Hier wird ein Konfigurationspaket deklariert, das seine nicht explizit gesetzten Werte von einer Struts-internen Konfiguration "struts-defaults" erbt
und "strutsbasics" heißen soll.
Es wird die Action GeometricModelAction
registriert und es wird angegeben zu welcher Seite man von der execute
-Methode springen soll.
Im Element result
gibt man normalerweise den Rückgabewert der execute
-Methode an für die diese Regel gilt. In unserem Fall gibt
es nur die Rückgabe "success", dies ist gleichzeitig Default-Wert des Elements. Die Langversion würde so aussehen:
<result name="success">/geometricmodel.jsp</result>
JSP
Wir fügen eine JSP-Seite "geometricmodel.jsp" zu.
Die Seite verwendet die JSTL-Core-Library.
Sie sieht so aus:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
<title>Test für JSF</title>
</head>
<body>
<s:form action="geometricmodel">
<c:if test="${volumen > 0.0}">
Volumen: <c:out value="${volumen}"/> <br/>
Oberfläche: <c:out value="${oberflaeche}"/> <br/>
</c:if>
<s:textfield name="a" label="Kante a"></s:textfield> <br/>
<s:textfield name="b" label="Kante b"></s:textfield> <br/>
<s:textfield name="c" label="Kante c"></s:textfield> <br/>
<br/>
<s:submit value="Berechnen"></s:submit>
</s:form>
<%--Historie ausgeben: --%>
<s:if test="${historieGesamt.size > 0}">
<c:forEach var="item" items="${historieGesamt.iterator}">
a=<c:out value="${item.a}"/>, b=<c:out value="${item.b}"/>, c=<c:out value="${item.c}"/> <br/>
</c:forEach>
</s:if>
</body>
</html>
Die Elemente im Einzelnen:
- Es werden drei Taglibraries eingebunden: Struts-Tags und JSTL-Core:
<%@ taglib prefix="s" uri="/struts-tags" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
- Es wird ein HTML-Form deklariert, das beim Submit auf die Action mit dem Namen "geometricModel" verweist:
<s:form action="geometricmodel">
...
</s:form>
- Zuallererst wird geprüft ob beim aktuellen Aufruf der Seite eine Berechnung durchgeführt wurde. Hierzu wird das
JSTL-Tag
c:if
genutzt. In sämtlichen EL-Ausdrücken können wir auf die Properties der Action-Klasse zugreifen als würde es
sich um Request-Attribute handeln (scheinbar kopiert Struts alle Werte der Action-Properties in Requestattribute die genauso heißen wie die Property)
<c:if test="${volumen > 0.0}">
Volumen: <c:out value="${volumen}"/> <br/>
Oberfläche: <c:out value="${oberflaeche}"/> <br/>
</c:if>
- Jetzt folgt das eigentliche Formular. Zuerst drei Ein-/Ausgabefelder. Diese werden mit den Properties a, b und c der Action
verdrahtet, d.h. beim Erzeugen der HTML-Seite werden die aktuellen Werte in die Felder geschrieben, und beim Auswerten des Requests
werden die Request-Parameter "a", "b" und "c" (durch das Attribut
name
definiert) in die entsprechenden Felder geschrieben.
<s:textfield name="a" label="Kante a"></s:textfield> <br/>
<s:textfield name="b" label="Kante b"></s:textfield> <br/>
<s:textfield name="c" label="Kante c"></s:textfield> <br/>
Durch das Attribut "label" können wir einen Text angeben der direkt vor dem Feld ausgegeben wird (einschließlich angehängtem ":").
Der Submit-Button wird durch ein Element s:submit
abgebildet.
&<s:submit value="Berechnen"></s:submit>
- Die Ausgabe der Historie erfolgt identisch zum JSP3-Beispiel. Hier hätte sich die Möglichkeit geboten
einen Struts-Table zur Ausgabe zu verwenden, das hätte allerdings erfordert dass unsere Historie ein TableModel wird.
Stand 19.06.2007
Historie:
19.06.2007: Erstellt.