Komponentenorientierte Softwareentwicklung und Hypermedia Prof. Dr. Frank Thiesing Struts Java-Framework für Webapplikationen Referenten: Jens de Witt Fabian Bartelt Inhaltsverzeichnis 1.Was ist Struts?.......................................................................................................... 3 1.1.Einführung......................................................................................................... 3 2.Vorkenntnisse........................................................................................................... 3 2.1.HTTP-Protokoll.................................................................................................. 3 Das HTTP-Protokoll und der Request/Response-Kreislauf.................................4 HTTP-Header und HTTP-Antwortcodes.............................................................. 4 2.2.JavaBeans......................................................................................................... 4 3.Vorgeschichte: Servlet & JSPs................................................................................. 5 3.1.Dynamische Webseiten und Servlets................................................................ 5 3.2.Java Server Pages (JSP).................................................................................. 6 3.3.Taglibs............................................................................................................... 6 3.4.Das Model-View-Controller-Entwurfsmuster...................................................... 7 3.5.Was bedeutet Model 1 / Model 2 bei Webapplikationen?................................. 7 3.6.Komponenten des Struts-Frameworks.............................................................. 7 4.View-Komponenten................................................................................................... 8 4.1.JSP-Seiten & Taglibs......................................................................................... 9 4.2.Internationalisierung, I18N............................................................................... 10 4.3.ActionForms.................................................................................................... 10 4.4.Daten übergeben: Attribute............................................................................. 14 5.Modell-Komponenten.............................................................................................. 14 5.1.Die Klasse Action............................................................................................ 16 5.2.Steuerung der Applikation............................................................................... 18 struts-config.xml................................................................................................ 18 6.ausführliches Beispiel: eBay© für Arme (Menschen, nicht Körperteile)..................19 6.1.Anlegen einer neuen Struts-Webapplikation in Tomcat / die Datei web.xml... 19 6.2.Die struts-config.xml........................................................................................ 20 6.3.View-Beispiel: /form/editartikel.jsp................................................................... 21 7.Weitere Konzepte in Struts..................................................................................... 22 8.Fazit........................................................................................................................ 22 9.Anhang................................................................................................................... 23 9.1.Literatur........................................................................................................... 23 9.2.Links................................................................................................................ 23 1. Was ist Struts? 1.1. Einführung Struts ist ein Framework für Webapplikationen. Struts baut auf der Java Servlet-Spezifikation 2.3 auf. Es wird als Teilprojekt des Apache-Projekts geführt. Webapplikationen werden einerseits wegen des Internetbooms stark nachgefragt (B2C, B2B). Aber auch für Unternehmen rechnet es sich, plattformabhängige Applikationen durch Webapplikationen zu ersetzen, da nur wartungsarme (daher kostengünstige) Webbrowser für solche Applikationen auf Clientseite zur Verfügung stehen müssen. Serverseitig muß andererseits ein erhöhter Wartungsaufwand in Kauf genommen werden, was sich aber durch Kostenersparnisse bei der Clientseite relativiert. (Als Beispiel ist das Mobilfunkunternehmen O2 zu nennen, die viele betriebsinterne Applikationen auf Webbasis betreiben.) Gängige Basisplattformen für Webapplikationen sind beispielsweise herkömmliche CGI-Applikationen (auf C/C++-Basis), PHP, PERL. Verstärkt durch die Popularität der Programmierplattform JAVA werden mitlwerweile für professionelle Applikationen Java-Servlets und JSP genommen, da diese problemlos mit anderen JAVA-Anwendungen kommunizieren können (J2EE mit EJBs als Referenzbeispiel genannt). Mit Struts als Framework ist eine feste Grundstruktur auf MVC-Basis bereitgestellt worden, die eine Webapplikation auf Servlet/JSP-Basis stark strukturiert, leichter verständlich macht und gleichzeitig viele lästige Standardaufgaben einer Applikationsentwicklung abnimmt. 2. Vorkenntnisse In diesem Kapitel werden Vorkenntnisse im Schnelldurchlauf vermittelt, die zum Verständnis von Struts zwingend sind. 2.1. HTTP-Protokoll Das Hypertext-Transfer-Protokioll (HTTP) ist 1989 von Tim Berners-Lee und seinen Mitstreitern am CERN Institut entwickelt worden. HTTP ist ein Protokoll auf der Anwendungsebene (7 Schichten Modell), es ist zustandslos und funktioniert nach einem simplen Anfrage/Antwort-Schema. Das HTTP-Protokoll und der Request/Response-Kreislauf Die Vorgehensweise des HTTP-Protokolls sieht wie folgt aus: Ein HTTP-Client baut eine Verbindung zum HTTP-Server über dessen TCPPort auf, i.d.R. Port-Nummer 80. Danach sendet der Client einen Request an den Server. (Z. B. GET /index.html HTTP/1.1) Der HTTP-Server sendet einen Response als Antwort, die das gewünschtes Dokument und HTTPHeader (Steuerinformationen) enthält. Der Server baut die Verbindung ab. HTTP-Header und HTTP-Antwortcodes Der HTTP-Header enthält Steuerinformationen, die zwischen einem Browser und einem HTTP-Server ausgetauscht werden. Sie stellen Informationen bereit, wie z.B. den Typ des Browsers, die Anzahl der gesendeten Zeichen, den Datentyp und den Antwortcode. Der Antwortcode teilt dem Browser den Status der Anforderung mit. Es gibt folgende Kategorien von Antwortcodes: 1xx: Zeigen Informationen über den Status der Anforderung. Wird vom HTTP 1.1 nicht verwendet. 2xx: Die Anforderungen wurden vom Server verstanden und akzeptiert. 3xx: Die angeforderte Ressource findet sich an einer anderen Stelle. 4xx: Fehler auf Seiten des Clients. 5xx: Fehler auf Seiten des Servers 2.2. JavaBeans Die JavaBeans definieren wiederverwendbare, einbettbare, modulare Software-Komponenten. Wichtige Merkmale von Beans sind: Selbstbeobachtung (Introspection): Eine Klasse lässt sich von außen auslesen. So kann ein spezielles Programm, wie das BDK oder eine visuelle Entwicklungsumgebung, eine Bean analysieren und ihre Eigenschaften abfragen. Auch umgekehrt kann eine Bean herausfinden, ob sie etwa gerade von einem grafischen Entwicklungswerkzeug modelliert wird oder in einer Applikation ohne GUI Verwendung findet. Eigenschaften (Properties): Attribute beschreiben den Zustand des Objekts. In einem Modellierungswerkzeug lassen diese sich ändern. Im BDK standen die Eigenschaften etwa in dem Property-Sheet. Da eine Bean meistens eine grafische Komponente ist, besitzt sie etwa eine Hintergrundfarbe. Diese Informationen können von außen durch bestimmte Methoden abgefragt und verändert werden. Ändern wir hier die Hintergrundfarbe im BDK, zeigt die Bean diese sofort an. Für alle Eigenschaften werden spezielle Zugriffsmethoden definiert; sie werden Property-Design-Pattern genannt. Ereignisse (Events): Wir können die Komponente Ereignisse auslösen lassen, sodass sie Zustandsänderungen und visuelle Interaktionen an andere Beans oder Dienstprogramme weiterleitet. Grundlage für die Ereignisbehandlung ist das Modell von Java 1.1 mit Auslösern und Empfängern. Anpassung (Customization): Der Bean-Entwickler kann die Eigenschaften einer Bean visuell und interaktiv anpassen Speicherung (Persistenz): Jede Bean kann ihren internen Zustand, also die Eigenschaften, durch Serialisierung speichern und wiederherstellen. So kann ein Builder-Tool die Komponenten laden und benutzen. Ein spezieller Externalisierungsmechanismus erlaubt dem Entwickler die Definition eines eigenen Speicherformats, zum Beispiel als XML-Datei Zusätzlich zu den notwendigen Grundpfeilern lässt sich durch Internationalisierung die Entwicklung internationaler Komponenten vereinfachen. Verwendet eine Bean länderspezifische Ausdrücke, wie Währungsformate oder Datumsformate, kann der Bean-Entwickler mit länderunabhängigen Bezeichnern arbeiten, die dann in die jeweilige Landessprache übersetzt werden. 3. Vorgeschichte: Servlet & JSPs 3.1. Dynamische Webseiten und Servlets In der ersten Generation von Internet-Seiten war jede Seite statisch auf dem Webserver abgelegt und durch einen eindeutigen Namen identifiziert. Jedoch kam auch der Wunsch auf, Internetseiten dynamisch zu generieren. Dies hat folgende Gründe: • Interaktion mit dem Betrachter der Website • Zugriff auf Datenbanken Aus diesen Gründen wurden serverseitig Schnittstellen definiert, wobei die bekannteste das Common Gateway Interface (kurz CGI) ist. Andere Hersteller haben für ihre Server eigene Schnittstellen definiert. Mittels der CGI-Schnittstelle kann der Browser dem Server Daten übergeben, wie etwa ein Produkt, nach dem das Programm suchen soll. Auf der Server-Seite laufen dann meist Programme, die in Skriptsprachen wie PHP oder Perl geschrieben sind, die Aufbereitung der Daten übernehmen und die Antwort an den Client zurücksenden. Servlets sind nun die Antwort auf CGI-Programme. Dabei sind Servlets aber nicht einfache Java-Programme, die über die CGI-Schnittstelle mit dem Server kommunizieren, sondern eine eigenständige Entwicklung. Wenn wir Java-Programme als normale Applikationen auf der Server-Seite nutzen würden, müsste der Webserver immer dann, wenn eine dynamische Seite generiert wird, die JVM aufrufen und dann das Programm ausführen. Eine Verbesserung würde darin bestehen, dass der Webserver eine JVM integriert, die immer läuft, und Objekte einzelne Verbindungen innerhalb der Java-Maschine bedienen. Genau das sind Servlets. Sie sind vergleichbar mit Applets. Ein Applet ist ein Java-Programm auf der Client-Seite (im Browser), und ein Servlet ist ein Programm auf der Server-Seite (im Server). Der Vorteil von Servlets liegt dabei verstärkt in der nun möglichen Interaktion mit der JAVA-Welt, nun kann man mittels Webapplikationen entwickeln, die auf JAVA-Objekte interaktiv zugreifen können. Die strenge Typisierung von JAVA-Programmen hebt zusätzlich die Qualität von Webapplikationen auf eine neue Qualität. 3.2. Java Server Pages (JSP) Servlets sind Server-Programme, die Webseiten erstellen. Das machen sie, indem sie die HTML-Anweisungen mit println() in den Ausgabestrom der ServletResponse senden. Der Aufbau einer HTML-Seite mit println()-Anweisung in einem Servlet ist daher sehr aufwändig. Außerdem fehlt eine Trennung zwischen Daten und Visualisierung. Ändert sich das Erscheinungsbild, so muss das Programm umgebaut werden. In vielen dynamischen Programmen stecken oft nur ein oder zwei Zeilen Dynamik, der Rest ist statischer HTML-Code. In der Regel ist der Programmierer auch nicht der Designer, und dieser möchte mit Webseiten-Erstellungsprogrammen wie DreamWeaver oder Microsoft FrontPage arbeiten. Eine JSP (Java Server Pages) geht das Problem genau anders herum an. Wo ein Servlet eine Java-Klasse ist, die sich um die Ausgabe des HTMLCodes kümmert, ist eine JSP eine HTML-Seite mit Java-Code ähnlich wie eine php-Seite; eine JSP-Seite ist also ein umgestülptes Servlet. Der Java-Code wird dabei eingerahmt in sogenannte Java-Scriplets. 3.3. Taglibs Eines der JSP-Entwicklung war schon immer die Neigung von Programmierern, zu viele Java-Skriptlets auf ihren Seiten zu verwenden. Dadurch sind die Seiten für einen HTML-Designer schwerer zu verstehen und zu pflegen. Die Antwort liegt in speziellen benutzerdefinierten Tags (engl. Custom tag libraries). Diese Tags sind in XML formuliert, sodass es mit ihnen erstmals möglich wird, eine Webseite ganz ohne Java-Scriptlets zu formulieren. Ein XML-Prozessor kann eine generierte Datei mit Tags dann als korrektes XML validieren. Der Autor der Tags definiert nach außen eine Funktionalität ähnlich den Beans. Den Nutzer hat es nicht zu interessieren, wie die Tags implementiert sind. Wichtige Tag-Bibliotheken, sind die Standard Tag Library (JSTL) der Apache-Gruppe und natürlich die struts-Tag-Bibliothek. Eine wichtige Technik stellt dabei die JavaBeans-Technologie dar: die Taglibs können mit JAVA-Objekten arbeiten, vorausgesetzt, dass alle benötigten Klassenmethodensignaturen nach dem JavaBeans-Standard kodiert sind. 3.4. Das Model-View-Controller-Entwurfsmuster Das MVC ist eines der bekanntesten Entwurfsmuster. Das MVC löst das Entwurfsproblem für die drei Hauptfunktionen die in vielen Applikationen auftreten: Model: Verwalten der Daten in einer Datenbank oder auf einem entfernten System View: Erstellen der Darstellungsschicht für den Endbenutzer Controller: Verwalten der Logik, die entscheidet, welche Bildschirme dem Benutzer präsentiert werden, was im Fehlerfall passieren soll, und wie und wann die Datenbank aktualisiert werden soll. MVC löst dieses Problem, indem es den Code in drei einzelene Bereiche aufteilt. Bei der Entwicklung von Webapplikationen werden die Views dabei von JSP-Seiten dargestellt, als Controller fungieren dabei die Servlets. Als Model können EJBs oder andere Applikationsobjekte verwendet werden. 3.5. Was bedeutet Model 1 / Model 2 bei Webapplikationen? Das Model 1 beschreibt die JSP-Verarbeitung, in der eine HTTPAnforderung (Request) direkt an eine JSP-Datei gesendet wird. Die gesamte Verarbeitung erfolgt direkt in der JSP-Datei. Das Model 2 ist anders. Es schickt den Request nicht zu einer JSP-Datei sondern zu einem Servlet. Das Servlet sollte die für die Anforderung erforderlichen Verarbeitungen ausführen und die Information dann in einem Bean speichern. Das Bean wurde der JSP-Datei übergeben, die die Information als Response dem Benutzer zurücksendet. Client Browser Server (1) (2) Controller (Servlet) Model (Z.B. EJBs) (3) (4) (5) View (JSP) Zeichnung 1MVC-Model-2 3.6. Komponenten des Struts-Frameworks Das Struts-Framework erweitert das MVC-Model-2-Konzept um Feste, leicht wartbare Grundstruktur Dynamische Applikationskonfiguration (struts-config.xml) Formularverarbeitung Benutzerdefinierte Custom JSP-Tags zur Erstellung von JSP-Seiten Es werden z.B. für Informationen über eine Person folgende Dateien angelegt: Person.java Diese Klasse enthält das Datenmodell für eine Person. Es stellt einfache Methoden bereit, die Daten anzugeben, abzufragen und diese persistent zu speichern und dann wieder zu lesen. PersonView.jsp Wird für die Anzeige der Personeninformation verwendet. PersonAction.java Die Action-Klasse ist der Controller, der bei der Auswertung der Personeinträge und der Auswahl der richtigen View hilft. Die folgende Grafik zeigt beispielhaft das Zusammenspiel in Struts zwischen Model, View, Controller und dem Struts-Framework. Auf die einzelnen Komponenten gehen wir in den nächsten Kapiteln ausführlicher ein. Client Server struts-config.xml ActionForm ActionServlet holt Konfiguration von der struts-config.xml (2) opt. ActionServlet Browser (1) (Controller) (3) Action (Controller) (4) (5) (6) Ausgabe View (JSP ) JSP-View holt Daten aus übergebenen JavaBeans Model (JavaBeans) Zeichnung 2Struts-MVC-Framework 4. View-Komponenten In Struts wird die View wie im MVC-Model-2 mit JSP-Seiten implementiert, zusätzlich werden sogenannte ActionForms benutzt, die mit den JSP-Seiten zusammenarbeiten. Sie sind vom Konzept her am einfachsten zu verstehen, weil sie in gewissem Ausmaß funktionieren wie die traditionellen Servlets oder reine JSPBenutzeroberflächen. 4.1. JSP-Seiten & Taglibs JSP-Seiten unter Struts sind genauso aufgebaut wie normale JSP-Seiten, vorrangig verwenden diese jedoch die Struts-Taglibs. Funktionell beinhalten diese: • erweiterte Tags zur Verarbeitung und Darstellung von Objekten • Darstellung von HTML-Formularen und Einbindung in das StrutsFramework • Formulargebundene Fehlerausgabe • Logik-Tags zur bedingten Darstellung von Informationen Zum besseren Verständnis, ziehen wir ein Beispiel aus unserem Programm heraus. (Unser Beispiel ist die Datei editUser.jsp aus unserem Programmierbeispiel.) <%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean"%> <%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html"%> <%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic"%> <jsp:useBean id="editbenutzerForm" scope="request" type="seminarstruts.form.EditbenutzerForm"/> <html> <head> <meta name = "Generator" content = "Easy Struts Xslt generator for Eclipse (http://easystruts.sf.net)."> <title> <logic:notEqual name="editbenutzerForm" property="benutzerId" value="0"> Bearbeite Benutzeraccount <bean:write name="editbenutzerForm" property="name"/>' </logic:notEqual> <logic:equal name="editbenutzerForm" property="benutzerId" value="0"> Registrierung für neuen Benutzer </logic:equal> </title> </head> <body> <html:form action="/editbenutzerSubmit"> benutzerId : <html:hidden property="benutzerId"/> <html:errors property="benutzerId"/></br> login : <html:text property="login"/> <html:errors property="login"/></br> name : <html:text property="name"/> <html:errors property="name"/></br> passwort : <html:password property="passwort"/> <html:errors property="passwort"/></br> passwort2 : <html:password property="passwort2"/> <html:errors property="passwort2"/></br> <html:submit/><html:cancel/> </html:form> <body> </html> Die JSP-Seite sieht einer HTML-Seite ähnlich, allerdings werden gleich am Anfang die Namespaces für die struts Taglibs geladen (vergleichbar mit dem import-Befehl in .java-Quellcodes). Die HTML-Tag werden zum Aufbau des HTML-Formulars verwendet. Die JSP-Tags werden serverseitig verarbeitet, bei der Ausgabe auf dem Browser (bzw. im Browser-Quelltext) sind diese Tags nicht mehr sichtbar. Interessant sind die bean- und die logic-Tags. Mit <logic:notEqual> wird überprüft, die in den ActionForm-Bean abgelegten Daten zu einem neuen oder zu einem existierenden Benutzer gehören. Existiert ein Benutzer, so wird der Benutzername in der Titelzeile des Browsers angezeigt. Anderfalls wird „Neuer Benutzer“ ausgegeben. Das <html:errors>-Tag wird zur Fehlerausgabe benutzt, die während der Validierung von Formulardaten (in ActionForm-Beans) bzw. in den ActionKlassen aufgetreten sind. Wird es mit dem Attribut verwendet, zeigt es nur die Fehler für die betreffende Eigenschaft an. Es gibt zwei spezielle Werte, die Sie in Ihre Datei ApplicationResources.properties schreiben können, um zu steuern, wie diese Fehler angezeigt werden: errors.header=<FONT COLOR="#ff0000"> errors.footer=</BR></FONT> Der Wert von errors. header wird unmittelbar vor dem Fehler ausgegeben; der f ooter wird nach dem Fehler ausgegeben. In diesem Beispiel wird der Fehler rot dargestellt. 4.2. Internationalisierung, I18N Um die Fehlermeldungen aus einer Formularauswertung internationalisieren zu können, wird das Tag bean:message in die JSP-Seite eingebaut. Dieses Tag sucht nach einem Wert in dem angegebenen Resource-Bundle (standardmäßig ApplicationResources.properties) und sendet ihn an den Browser. Auch in Verbindung mit dem <html:errors>-Tag wird dieser Mechanismus verwendet. 4.3. ActionForms Die JSP-Seiten und ActionForm-Beans arbeiten in Struts Hand in Hand: Die JSP geben die Benutzereingaben an das Bean weiter, das Bean validiert die Daten und gibt Auswertungsfehler an die JSP zurück. Um zu verstehen, wie sie zusammenarbeiten, betrachten wir ein ActionForm und die zugehörige JSP-Seite. Das Listing zeigt das Bean EditbenutzerForm. Dieses Bean wird zum Anlegen oder Bearbeiten von Benutzerdaten benutzt: package seminarstruts.form; import javax.servlet.http.HttpServletRequest; import org.apache.struts.action.ActionErrors; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionMapping; /** * EditbenutzerForm.java created by EasyStruts - XsltGen. * http://easystruts.sf.net * created on 06-04-2004 * * XDoclet definition: * @struts:form name="editbenutzerForm" */ public class EditbenutzerForm extends ActionForm { // --------------------------------------------------------Instance Variables /** passwort property */ private String passwort; /** passwort2 property */ private String passwort2; /** login property */ private String login; /** benutzerId property */ private String benutzerId; /** name property */ private String name; // --------------------------------------------------------Methods /** * Method validate * @param ActionMapping mapping * @param HttpServletRequest request * @return ActionErrors */ public ActionErrors validate( ActionMapping mapping, HttpServletRequest request) { throw new UnsupportedOperationException("Generated method 'validate(...)' not implemented."); } /** * Method reset * @param ActionMapping mapping * @param HttpServletRequest request */ public void reset(ActionMapping mapping, HttpServletRequest request) { passwort = ""; passwort2 = ""; login = ""; benutzerId = ""; name = ""; } /** * Returns the passwort. * @return String */ public String getPasswort() { return passwort; } /** * Set the passwort. * @param passwort The passwort to set */ public void setPasswort(String passwort) { this.passwort = passwort; } /** * Returns the passwort2. * @return String */ public String getPasswort2() { } return passwort2; /** * Set the passwort2. * @param passwort2 The passwort2 to set */ public void setPasswort2(String passwort2) { this.passwort2 = passwort2; } /** * Returns the login. * @return String */ public String getLogin() { return login; } /** * Set the login. * @param login The login to set */ public void setLogin(String login) { this.login = login; } /** * Returns the benutzerId. * @return String */ public String getBenutzerId() { return benutzerId; } /** * Set the benutzerId. * @param benutzerId The benutzerId to set */ public void setBenutzerId(String benutzerId) { this.benutzerId = benutzerId; } /** * Returns the name. * @return String */ public String getName() { return name; } } /** * Set the name. * @param name The name to set */ public void setName(String name) { this.name = name; } Dieses ActionForm-Bean (und die meisten anderen Struts-bezogenen Dateien in der Applikation) wurden unter Verwendung von EasyStruts für Eclipse erzeugt, der automatisch ActionForms, Actions und JSP-Dateien für Struts erzeugt. Der hintere Teil der Datei kann größtenteils ignoriert werden. Er enthält die Get-und Set-Methoden für die Bean-Eigenschaften, wie in jedem anderen JavaBean. Die beiden wichtigsten Methoden der Klasse sind reset() und validate(). Wenn ein Formular vor der Verwendung durch Struts initialisiert wird, wird die Methode reset() aufgerufen. Sie ist dafür verantwortlich, alle BeanEigenschaften auf ihre Ausgangswerte zurückzusetzen. (Es findet eine Wiederverwertung von ActionForms statt, um Ressourcen zu sparen.) Die Methode validate() wertet die vom Benutzer in dem Formular vorgenommenen Eingaben aus und stellt sicher, dass sie konsistent mit den Daten sind, die die Anwendung braucht (korrekte Syntax der Formulareingaben). Der Methode validate() werden zwei Argumente übergeben: ActionMapping für die Aktion und HttpServletRequest. Die Methode gibt ein ActionErrorsObjekt zurück, das eine Auflistung aller ActionError-Objekte darstellt, die während der Auswertung erzeugt werden. ActionError-Objekte aktivieren die <html:errors>-Tags, die die Fehlermeldung darstellt. Ein ActionError-Objekt speichert dabei einen Schlüssel, die eigentliche Meldung wird über den MessageResources-Mechanismus (also über die I18N) geholt (siehe oben). 4.4. Daten übergeben: Attribute Nun muß noch nebenbei geklärt werden, wie überhaupt die Daten innerhalb einer Webapplikation gespeichert werden und übergeben werden. Daten werden in Servlets und in JSP-Seiten zur Übergabe in sogenannte Attribute gespeichert, die sich in verschiedenen Scopes befinden können: Scope Code (Servlet) Code (JSP) request request.getAttribute("attr") request. setAttribute("attr",x) <jsp:writeBean name=“attr“ property=“prop“ scope=“request“/> response Response. getAttribute("attr") Response. setAttribute("attr",x) <jsp:writeBean name=“attr“ property=“prop“ scope=“response“/> session request.getSession(). getAttribute("attr") request.getSession(). setAttribute("attr",x) <jsp:writeBean name=“attr“ property=“prop“ scope=“session“/> application request.getSession(). getAttribute("attr") request.getSession(). setAttribute("attr",x) <jsp:writeBean name=“attr“ property=“prop“ scope=“application“/> Der Lebenszyklus und die Erreichbarkeit eines Attributes definieren sich dabei durch den Scope, d.h. Ein Session-Attribut lebt solange wie die Session existiert, kann aber nicht aus anderen Sessions heraus ausgelesen werden. Ein request-Attribut lebt nur solange, wie die einzelne Browseranforderung läuft. Auch dieses ist nicht durch andere Request auslesbar. 5. Modell-Komponenten Die Modell-Komponente wird verwendet, um den Zugriff auf die entsprechende Geschäftslogik der Anwendung zu erhalten und zu steuern. Als Model können eigene Klassen verwendet werden, es können aber auch EJBs oder andere Persistence Layers verwendet werden: Grundbedingung ist, dass die Klassen get- und set- Methoden nach der JavaBeansSpezifikation definieren. Diese werden unbedingt für die JSP-Seiten benötigt, die Taglibs (JSTL und Struts-Taglibs) arbeiten mit diesen get- und set- Methoden. In den hier verwendeten Beispiel wurde zur Generierung der Datenbankklassen der Persistence Layer Torque verwendet. Es ist Teil des apache Java-Plattform. Als Beispiel wird hier die Benutzer-Klasse verwendet. package seminarstruts.model; import org.apache.torque.om.Persistent; import java.util.List; import java.util.Vector; import org.apache.torque.TorqueException; import org.apache.torque.util.Criteria; /** * The skeleton for this class was autogenerated by Torque on: * * [Tue Jun 01 21:50:33 CEST 2004] * * You should add additional methods to this class to meet the * application requirements. This class will only be generated as * long as it does not already exist in the output directory. */ public class Benutzer extends seminarstruts.model.BaseBenutzer implements Persistent { } /** * The skeleton for this class was autogenerated by Torque on: * * [Tue Jun 01 21:50:33 CEST 2004] * * You should add additional methods to this class to meet the * application requirements. This class will only be generated as * long as it does not already exist in the output directory. */ public class BenutzerPeer extends seminarstruts.model.BaseBenutzerPeer { public static Benutzer getBenutzerByLogin(String log) { Criteria crit=new Criteria(); crit.add(LOGIN,log); List l; try { l = doSelect(crit); } catch (TorqueException e) { // TODO Auto-generated catch block e.printStackTrace(); l=new Vector(); } if (l.size()==1) return (Benutzer) l.get(0); else return null; } } Und die dazugehörige BenutzerPeer-Klasse, die die direkte Kommunikation mit der Datenbank enthält. Dazu gehören noch die BaseBenutzer-Klasse und die BaseBenutzerPeer-Klasse, die schon gewisse Grundfunktionalitäten wie Benutzer Daten speichern, löschen und editieren enthalten: dabei sind die get- und set- Methoden nach der JavaBeans-Spezifikation kodiert. In Struts wird die benutzerspezifische Controller-Logik nicht in einem Servlet implementiert, sondern in separaten 'Action'-Klassen. Das bei Struts beiliegende ActionServlet ruft diese Action-Klassen auf. Optional kann ein für die Action relevantes ActionForm-Bean vor dem Aufruf einer Action-Klasse validiert werden; schlägt diese Validierung fehl, kann automatisch eine JSP-Seite (oder auch eine andere Action) aufgerufen werden. Das Verhalten des ActionServlets wird über die Datei struts-config.xml gesteuert. 5.1. Die Klasse Action Die Aufgabe des Controllers ist es mit den Daten, die der Benutzer eingibt, etwas zu machen und zu entscheiden, was als nächstes geschehen soll. In Struts wird der Controller in zwei Teilen implementiert: in den ActionKlassen und dem eigentlichen Struts Framework. Die Action nimmt Eingaben vom Benutzer entgegen, koordiniert den Zugriff auf entfernte Systeme, implementiert die Geschäftslogik und entscheidet, welche View-Komponente dem Benutzer als nächstes angezeigt werden soll. Das Beispiel ist hier die EditbenutzerAction-Klasse: // Created by Xslt generator for Eclipse. // XSL : not found (java.io.FileNotFoundException: descriptor)) // Default XSL used : easystruts.jar$org.easystruts.xslgen.JavaClass.xsl (Bad file package seminarstruts.action; import seminarstruts.model.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import import import import org.apache.struts.action.Action; org.apache.struts.action.ActionForm; org.apache.struts.action.ActionForward; org.apache.struts.action.ActionMapping; import seminarstruts.form.EditbenutzerForm; /** * EditbenutzerAction.java created by EasyStruts - XsltGen. * http://easystruts.sf.net * created on 06-04-2004 * * XDoclet definition: * @struts:action validate="true" * @struts:action-forward name="/form/editbenutzer.jsp" path="/form/editbenutzer.jsp" */ public class EditbenutzerAction extends Action { /** * Method execute * @param ActionMapping mapping * @param ActionForm form * @param HttpServletRequest request * @param HttpServletResponse response * @return ActionForward * @throws Exception */ public ActionForward execute( ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { if (!org.apache.torque.Torque.isInit()) return mapping.findForward("noDatabase"); EditbenutzerForm bform=new EditbenutzerForm(); int log=0; try { log = Integer.parseInt( request.getSession().getAttribute("loginId"). toString()); } catch (Exception e) { } if (log!=0) { Benutzer b=BenutzerPeer.retrieveByPK(log); bform.setBenutzerId(Integer.toString(log)); bform.setLogin(b.getLogin()); bform.setName(b.getName()); bform.setPasswort(b.getPasswort()); bform.setPasswort2(b.getPasswort()); bform.setServlet(getServlet()); } } request.setAttribute("editbenutzerForm",bform); return mapping.findForward("proceed"); } Die wichtigste Methode, die die Action bereitstellt, ist die execute() Methode (vergleichbar mit der service()-Methode in Servlets). Struts ruft diese Methode auf, nachdem das Formular-Bean gesetzt und als korrekt ausgewertet wurde. Somit kann die Action-Klasse davon ausgehen, das das Formular-bean ihre Daten übergeben hat. Der Action-Klasse werden vier Parameter übergeben: ActionMapping – Die ActionMapping-Klasse stellt den Zugriff auf die Informationen bereit, die in dem Eintrag der struts-config.xml abgelegt sind, der diese Action-Klasse konfiguriert. ActionForm form – Dies ist das Formular-Bean. Es wurde die validate() Methode aufgerufen und es wurden keine Fehler zurückgeliefert. Alle vom Benutzer eingegebenen Daten stehen über das Formular-Bean zur Verfügung. HttpServletRequest request – Dies ist das Standard-JSP- oder Servlet request-Objekt. HttpServletResponse response – Dies ist das Standard-JSP- oder Servlet response-Objekt. Die execute() Methode muss ein ActionForward-Objekt zurückgeben. Dieses wird vom Controller verwendet, um festzulegen, welche Seite als Nächstes angezeigt werden soll. Statt jedoch die Seite direkt zu referenzieren, wird die übergebene ActionMapping verwendet, um den Verweis über einen Schlüsselnamen zu finden. Die Schlüsselnamen werden über die struts-config.xml konfiguriert. In der execute() Methode erzeugt der Controller jetzt eine neue ModelKomponente, setzt die Werte aus dem Formular-Bean und speichert die Daten persistent in einer Datenbank. 5.2. Steuerung der Applikation Normalerweise gibt Action-Klasse ein ActionForward zurück. Diese wird vom Controller verwendet, um festzulegen, welche Seite als Nächstes angezeigt werden soll. Aber die Action weiß nicht, wo diese ist, weil der Controller für die Weiterleitung verantwortlich ist. Sie können sogar eine Weiterleitung auf eine andere Action vornehmen, ohne über eine zwischengeschaltete JSP-Datei zu gehen. Stimmt der Pfad in der struts-config.xml für die angeforderte Weiterleitung mit dem für Actions verwendeten URI überein (das normalerweise mit .do endet), gibt Struts die Steuerung unmittelbar an die Action weiter. Dabei kann man über das request-Objekt weitere Parameter/ Objekte übergeben. spaket von Struts mehrere Plug-In-Modelle hinzuzufügen. struts-config.xml Die Datei struts-config.xml kann folgenden Elemente enthalten: Datenbankquellen (Eingeschränkt funktional, abhängig vom verwendetem Model) ActionForm-Klassen Definition aller ActionForm-Beans Globale Ausnahmen Globale Weiterleitungen Definition von globalen ActionForwards Action Mappings Referenzierung von Actions, Validierung von ActionForm-Beans sowie Definition der Rücksprungadresse bei fehlgeschlagener Validierung, Definition von ActionForwards im Action-Klassencontext (Hash-Mapping Schlüsselwort ->URI) Controller-Informationen Message-Resourcen (I18N) Plug-Ins Die Datei struts-config.xml bietet jede Menge Optionen für die Konfiguration der Applikation. Diese Konfigurationsdatei ist die zentrale Stelle, an der Aktionen, Formulare und JSP-Seiten zusammengeführt werden. In einer korrekt entworfenen Struts-Applikation weiß die JSP-Seite nichts über die Action- und die Form-Klassen, die sie unterstützen. Die Action weiß nicht, wie eine bestimmte Weiterleitungs-Anforderung tatsächlich in eine URI umwandelt wird, und die ActionForm-Klasse kann von einer oder mehreren Action-Klassen oder JSP-Seiten verwendet werden. 6. ausführliches Beispiel: eBay© für Arme (Menschen, nicht Körperteile) 6.1. Anlegen einer neuen Struts-Webapplikation in Tomcat / die Datei web.xml • Ohne easyStruts-Support: Die Datei jakarta-struts-1.1.zip von der StrutsHomepage herunterladen und entpacken. Anschließend im TomcatManager die Webapplikation mit dem Verweis auf das entpackte Verzeichnis manuell deployen. (Verweise auf andere Anleitungen) • Ansonsten bitte externe Anleitungen zur Rate ziehen: http://www-106.ibm.com/developerworks/opensource/library/osecstruts/?Open&ca=daw-co-news • Außerdem empfehlen wir, sich mit Tomcat, Servlets und den Aufbau von Webapplikationen vertraut zu machen. Die Datei /WEB-INF/web.xml (der Deployment-Descriptor von ServletApplikationen) hat bei Struts folgenden Aufbau: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/j2ee/dtds/webapp_2_3.dtd"> <web-app> <servlet> <servlet-name>action</servlet-name> <servlet-class>seminarstruts.SpecificActionServlet</servletclass> <init-param> <param-name>config</param-name> <param-value>/WEB-INF/struts-config.xml</param-value> </init-param> <init-param> <param-name>debug</param-name> <param-value>3</param-value> </init-param> <init-param> <param-name>detail</param-name> <param-value>3</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>action</servlet-name> <url-pattern>*.do</url-pattern> <servlet-name>action</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app> Zwischen den <servlet>...</servlet>-Tags wird das Servlet 'action' definiert: dieses ActionServlet wiederum ist bei Struts beigelegt und kann, wie in diesem Beispiel, überschrieben werden. Die <servlet-mapping>-Tags definieren ein Pattern-Verweis, damit alle URIs, die auf .do enden, von dem ActionServlet bearbeitet werden. 6.2. Die struts-config.xml Damit das ActionServlet weiß, was es tun soll, liest es nun die '/WEBINF/struts-config.xml' aus (gekürzter Ausschnitt): <?xml version="1.0" encoding="UTF-8"?> <!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> <!-- ========== Data Source Configuration =============================== --> <data-sources> ... </data-sources> <form-beans> <form-bean name="loginForm" type="seminarstruts.form.LoginForm"> <form-property name="passwort" type="java.lang.String" /> <form-property name="name" type="java.lang.String" /> ... </form-bean> <global-forwards> <forward name="noDatabase" path="/form/noDatabase.jsp" /> <forward name="mainpage" path="/index.do" /> <forward name="login" path="/login.do" /> </global-forwards> <action-mappings> <action attribute="editartikelForm" input="/editartikel.do" name="editartikelForm" path="/editartikelSubmit" scope="request" type="seminarstruts.action.EditartikelSubmitAction"> <forward name="login" path="/login.do" /> <forward name="success" path="/listartikel.do" /> <forward name="cancel" path="/listartikel.do" /> </action> ... </action-mappings> ... <message-resources parameter="seminarstruts.ApplicationResources" /> <message-resources key="org.apache.struts.action.MESSAGE_en" parameter="seminarstruts.ApplicationResources_en" /> </struts-config> Wird nun die URI '/editartikelSubmit.do' aufgerufen wird, schaut das ActionServlet nun nach einer Action, dessen Path-property '/editartikelSubmit' heißt, also ohne '.do'. In diesem Beispiel ist bei der Action noch ein FormBean namens 'editartikelForm' angegeben (und das Property 'validate' ist standardmäßig 'true'), daher wird das FormBean vor der Action-Ausführung validiert. Schlägt dies fehl, springt das ActionServlet auf das im Property 'input' angegebenen URI zurück. (Diese beinhaltet hier eine JSP-Seite, die von einer Action 'vorverarbeitet' wird. Außerdem wird angegeben, dass das ActionForm-Bean im request-Scope unter dem Namen 'editartikelForm' gespeichert ist/wird (die ActionForm ist sowohl über die html-Tags als auch über die bean-Tags erreichbar). 6.3. View-Beispiel: /form/editartikel.jsp <%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean"%> <%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html"%> <%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic"%> <html> <head> <title> <bean:message key="seminarstruts.locale.bearbeiteArtikel"/> '<bean:write name="editartikelForm" property="name"/>' <bean:message key="seminarstruts.locale.applicationName"/> <logic:present name="benutzer"> [<bean:message key="seminarstruts.locale.user"/>: '<bean:write name="benutzer" property="name"/>'] </logic:present> </title> </head> <body> <html:form action="/editartikelSubmit"> <html:hidden property="artikelId"/> <html:errors property="artikelId"/><br> Name : <html:text property="name"/> <html:errors property="name"/><br> Beschreibung : <html:text property="beschreibung"/> <html:errors property="beschreibung"/><br> Endedatum : <html:text property="endedatum"/> <html:errors property="endedatum"/><br> Endezeit : <html:text property="endezeit"/> <html:errors property="endezeit"/><br> Startpreis : <html:text property="startpreis"/> <html:errors property="startpreis"/><br> <html:submit/><html:cancel/> </html:form> </body> </html> <html:form> 7. Weitere Konzepte in Struts Im Folgenden existieren noch Konzepte, auf die wir nicht eingegangen sind und auch nicht zeitlich darauf eingehen können. 8. • Alle bean-, html- und logic-Tags • Nested-, template- und tiles-Tags • DynaForms (Formularverarbeitung ohne Java) • EJBs in Struts • Struts als Client von WebServices • Struts-Applikationen verteilen und testen • XML in Struts Fazit Struts löst auf den ersten Blick viele Standardaufgaben: es stellt zunächst eine MVC-Lösung bereit. Zwar unterstellen wir nicht, dass man auch sich in Struts einarbeiten muß; hat man dies aber bereits einmal gelernt, fällt auch die Einarbeitung in anderen Struts-Projekten leichter. (Herkömmliche Servlet-/JSP-Anwendungen sind weniger stark strukturiert.) Es vereinfacht die Formularverarbeitung mithilfe von ActionForms; eine erfolgreiche Validierung kann die Bedingung für eine Action darstellen. Desweiteren wird die JSP-Syntax um viele Taglibs erweitert, die das Arbeiten mit JSPs erleichtert, da weniger Skriptlets eingesetzt werden müssen. Die Trennung von Darstellung und Model/Controller wird daher weitergeführt. Nachteilig ist jedoch die gewisse Einarbeitung, bis man das StrutsFramework beherrscht. Dann jedoch kann man die Vorteile von Struts voll ausspielen. 9. Anhang 9.1. Literatur (1) Struts –JSP-Applikationen mit Struts, JBoss und Apache Axis Autoren: James Turner, Kevin Bedell Verlag: Addison-Wesley (2) Java Server und Servlets Autoren: Peter Rossbach, Hendrik Schreiber Verlag: Addison-Wesley (3) Handbuch der Java-Programmierung Autor: Guido Krüger Verlag: Addison-Wesley 9.2. Links (1) Jakarta Struts Homepage http://jakarta.apache.org/struts/ (2) Torque Persistence Layer http://db.apache.org/torque/ (3) EasyStruts http://easystruts.sourceforge.net/