Struts 1 – Simple Login

Dies wird das zweite vollständige Beispiel werden, dass ich euch vorstellen möchte. Vollständig heißt, dass es ein “standalone”-Tutorial ist, das sich auf kein vorhergehendes Beispiel bezieht. Das habe ich für das nächste Mal geplant. Wie bei Tutorials üblich, wird es auch wieder das gesamte Beispiel zum Download geben.

Heute werden wir uns also ein kleines, ganz einfaches, Login-Form bauen. Damit ist es aber nicht getan, wir werden die Eingaben validieren und die Internationalisierung einführen.

Wir brauchen

Für die Installation des Tomcat-Servers bitte ich dann aber doch in diesen Artikel zu schauen: Struts 1 – Hello World

Für die Installation des Plugins könnt ihr hier nachschauen (an sich keine große Sache, aber die auf der verlinkten Seite werden noch zwei Vereinfachungen bei der Arbeit mit properties beschrieben, die für mich jedoch nicht notwendig waren). Der Link führt deshalb auf eine Google-Cache-Seite, da die EPFE-Seite zur Zeit der Artikelerstellung schon ne Weile einen MySQL-Fehler meldete.

Die Umsetzung

Folgendes Bild zeigt die Ordnerstruktur und passenderweise auch alle Librarys, die für diese Anwendung benötigt werden. Ich hab mir angewöhnt, immer nur die wirklich notwendigen Librarys einzubinden. Das bedingt zwar manchmal langwieriges Exceptionhopping, aber letztendlich versteht man besser, was wo liegt und was man braucht.

Kommen wir zu den Inhalten der Dateien:

[rss]Den Quellcode findet ihr direkt und hübsch aufbereitet im Blogbeitrag[/rss]

[rssless]
[java title=”web.xml” collapse=”true”]
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<display-name>StrutsSimpleLogin</display-name>

<servlet>
<servlet-name>action</servlet-name>

<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>

<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>

<load-on-startup>2</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>

<welcome-file-list>
<welcome-file>/login.jsp</welcome-file>
</welcome-file-list>

</web-app>
[/java]

[java title=”struts-config.xml” collapse=”true”]
<?xml version="1.0" encoding="ISO-8859-1"?>

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

<struts-config>
<form-beans>
<form-bean name="LoginForm" type="forms.LoginForm" />
</form-beans>

<action-mappings>
<action input="/login.jsp"
name="LoginForm"
path="/Login"
scope="request"
validate="true"
type="actions.LoginAction">
<forward name="success" path="/success.jsp" />
<forward name="failure" path="/failure.jsp" />
</action>
</action-mappings>

<message-resources parameter="ApplicationResource" />

<plug-in className="org.apache.struts.validator.ValidatorPlugIn">
<set-property property="pathnames"
value="/org/apache/struts/validator/validator-rules.xml,
/WEB-INF/validation.xml" />
</plug-in>

</struts-config>
[/java]

[java title=”login.jsp” collapse=”true”]
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%@taglib uri="http://struts.apache.org/tags-html" prefix="html" %>
<%@taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title><bean:message key="login.title" /></title>
</head>
<body>
<div style="color:red">
<html:errors />
</div>

<html:form action="/Login" >
<bean:message key="login.label.username" /> <html:text name="LoginForm" property="username" />
<bean:message key="login.label.password" /> <html:password name="LoginForm" property="password"></html:password>
<html:submit><bean:message key="login.submit" /></html:submit>
</html:form>
</body>
</html>
[/java]

[java title=”success.jsp” collapse=”true”]
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%@taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title><bean:message key="login.success.title" /></title>
</head>
<body>
<bean:message key="login.success.message" /> <bean:write name="LoginForm" property="username" />
</body>
</html>
[/java]

[java title=”failure.jsp” collapse=”true”]
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%@taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title><bean:message key="login.failure.title" /></title>
</head>
<body>
<bean:message key="login.failure.message" />
</body>
</html>
[/java]

[java title=”LoginForm.java” collapse=”true”]
package forms;

import javax.servlet.http.HttpServletRequest;

import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionMessage;

public class LoginForm extends ActionForm {

private static final long serialVersionUID = 1L;
private String username;
private String password;

public void setUsername(String username)
{
this.username = username;
}

public void setPassword(String password)
{
this.password = password;
}

public String getPassword()
{
return this.password;
}

public String getUsername()
{
return this.username;
}

public ActionErrors validate(ActionMapping mapping,
HttpServletRequest request)
{
ActionErrors errors = new ActionErrors();
if(username == null || username.length() < 1)
{
errors.add(username, new ActionMessage("error.username.required"));
}
if(password == null || password.length() < 1)
{
errors.add(password, new ActionMessage("error.password.required"));
}
return errors;
}
}
[/java]

[java title=”LoginAction.java” collapse=”true”]
package actions;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

import forms.LoginForm;

public class LoginAction extends Action {

public ActionForward execute(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception
{

LoginForm loginForm = (LoginForm) form;

if (loginForm.getUsername().equals(loginForm.getPassword()))
{
return mapping.findForward("success");
}
else
{
return mapping.findForward("failure");
}
}
}

[/java]

[java title=”ApplicationResource.properties” collapse=”true”]
login.title = Login-Form
login.submit = Login
login.label.username = Username
login.label.password = Password
login.failure.message = Sry, login failed.
login.failure.title = Failure
login.success.message = Hello, nice to see you again,
login.success.title = Success

error.username.required = User Name is required.
error.password.required = Password is required.

# — standard errors —
errors.header=<UL>
errors.prefix=<LI>
errors.suffix=</LI>
errors.footer=</UL>
# — validator —
errors.invalid={0} is invalid.
errors.maxlength={0} can not be greater than {1} characters.
errors.minlength={0} can not be less than {1} characters.
errors.range={0} is not in the range {1} through {2}.
errors.required={0} is required.
errors.byte={0} must be an byte.
errors.date={0} is not a date.
errors.double={0} must be an double.
errors.float={0} must be an float.
errors.integer={0} must be an integer.
errors.long={0} must be an long.
errors.short={0} must be an short.
errors.creditcard={0} is not a valid credit card number.
errors.email={0} is an invalid e-mail address.
# — other —
errors.cancel=Operation cancelled.
errors.detail={0}
errors.general=The process did not complete. Details should follow.
errors.token=Request could not be completed. Operation is not in sequence.
[/java]

[java title=”ApplicationResource_de_DE.properties” collapse=”true”]
login.title = Anmeldung
login.submit = Anmelden
login.label.username = Benutzername
login.label.password = Passwort
login.failure.message = Oh, Anmeldung fehlgeschlagen.
login.failure.title = Fehler
login.success.message = Hallo, schön dich zu sehen,
login.success.title = Erfolg

error.username.required = Es muss ein Benutzername angegeben werden.
error.password.required = Es muss ein Passwort angegeben werden.
[/java]
[/rssless]

validation.xml

Diese Datei habe ich hier nicht aufgeführt, obwohl sie in der struts-config angegeben ist und auch auf dem Screenshot zu sehen ist. Im Download wird sie auch dabei sein. Prinzipiell gibt es zwei Wege seine Eingaben zu validieren. Einmal den Weg über das LoginForm, den ich in diesem Fall gewählt habe, als auch über die validation.xml, was vermutlich sogar die bessere Praxis wäre, im Sinne von gutem Code. Lesenswert dazu ist vll. der Artikel der tech-allies (hab mich nicht weiter damit befasst)

ApplicationResource.properties

Was mich bei vielen Frameworks stört ist das scheinbare Gezauber an manchen Stellen. Die ApplicationResource.properties gehört dazu. Ich habe meiner deutschen Version den Suffix “de_DE” angehangen und bei der Ausführung war sofort klar, dass die Buttons nun deutsche Label kriegen würden; ohne dass ich noch irgendwo etwas angegeben hätte. Nun ja; hinnehmen, freuen.

Cool ist jedenfalls, dass wir durch die Verwendung der properties und die Einbindung über bspw. <bean:message key="login.failure.message" /> unsere Inhalte total leicht wartbar gemacht haben. Wir können auf einen Schlag mehrere Sprachen auf einer Seite zur Verfügung stellen und haben bei Änderungen keinen großen Aufwand. Wir müssen die jsp.-Seiten nicht mal mehr anfassen.

Was passiert nun bei einem Aufruf der Seite?

Zum Ausführen der Applikation, klickt man im Projekt-Explorer mit Rechtsklick auf den Namen des Projektes -> Run As -> Run on Server

  • 1 und 2: Der User ruft die URL auf; da kein genauer Pfad angegeben wurde, schaut Tomcat in die web.xml und liefert die in der <welcome-file-list> definierte login.jsp als Startseite an den User zurück.
    In der login.jsp gibt es zwei Controls, deren Werte später in der LoginForm.java gespeichert werden sollen. Darum wird zu diesem Zeitpunkt die LoginForm schon instanziiert, zur Laufzeit jedoch nur ein Mal. Hierbei wird die “overridable” Funktion reset() aufgerufen. Dies ist der Konstruktor dieser Klasse und hier kann man beim initialisieren auch Defaultwerte mitgeben. Man sollte diese Funktion immer überschreiben und wichtige Eingabefelder auf null bzw “” setzen, da sonst bei mehreren Aufrufen Datenreste ungewünschte Effekte hervorrufen können.
  • 3: Der User füllt die Eingabefelder aus und schickt mit dem Drücken des submit-Buttons einen Request an den Server.
    In der login.jsp haben wir festgelegt, dass die action="/Login" ausgeführt werden soll. Beim Abschicken erhält diese die Endung *.do angehangen. Nun wird in der web.xml geschaut, was mit *do’s passieren soll und man wird über die Festlegungen im <servlet> an die struts.xml weitergereicht.
  • 4: Hier passiert ziemlich viel. In <action-mappings> steht, was passieren soll, wenn ein Request mit dem path = "/Login" hier ankommt.
    • a: zuerst einmal werden die Eingaben über die Setter in die LoginForm geschrieben
    • b: anschließend wird die validate()-Methode des LoginForms aufgerufen, da wir ja in der struts-config.xml validate="true" angegeben haben.
  • 5: Hier entscheidet sich: wurden die Daten korrekt in die Eingabefelder eingegeben (d.h. in dem Fall, sind die Felder nicht leer)?
    • Wenn nein, wird der User mit den errors an seinen Ursprung input="/login.jsp" zurückgeschickt und die Fehlermeldungen über das <html:errors /> angezeigt.
    • Sind die Eingaben valide, kommen wir in die actions.LoginAction.java und die execute()-Methode wird ausgeführt.
      Je nach Ergebnis der Prüfung der Eingaben, wird ein success oder failure zurückgeliefert
  • 6: Und je nachdem was wir in den forwards für solche Fälle definiert haben, wird dem User die Ergebnisseite präsentiert.

Downloads

BeispielProjekt als *.rar
Ausführbares *.war

Lesenswert

Beispiel A: Internationalisierung
Beispiel B: Internationalisierung
Types of Validation
Login Example with Struts 1