Aplikacje internetowe i rozproszone - laboratorium Struts Celem ćwiczenia jest przygotowanie prostej aplikacji wykorzystującej środowisko aplikacyjne Apache Struts. Aplikacja umożliwi sprawdzenie poprawności zalogowania się i w zależności od wyniku walidacji wyświetli stosowny dokument. Ćwiczenie obrazuje wykorzystanie podstawowych elementów środowiska Struts: komponentów FormBean (zwykłych, dynamicznych i leniwych), deklaratywnego zarządzania nawigacją w serwisie, obsługi wielojęzykowości, walidacji poprawności danych wprowadzanych przez formularz. Do wykonania ćwiczenia potrzebne jest środowisko NetBeans 5.5 (lub 5.0). Autor ćwiczenia: Mikołaj Morzy 1. Uruchom program NetBeans i utwórz nowy projekt. Wybierz File New Project i z listy kategorii wybierz Web, z listy projektów wybierz Web Application. Kliknij na przycisk Next >, jako nazwę projektu podaj Struts-laboratorium. Upewnij się, że zaznaczono zgodność kodu źródłowego z J2EE 1.4 i że projekt będzie oznaczony jako główny projekt ( Set as Main Project ). Kliknij na przycisk Next >. Zaznacz wykorzystanie aplikacji szkieletowej Struts 1.2.9, pozostaw konfigurację bez zmian, zaznacz chęć dołączenia bibliotek znaczników ( Add Struts TLDs ). Kliknij przycisk Finish. 2. Edytuj zawartość pliku index.jsp. Umieść w nim poniższy kod. Następnie uruchom aplikację naciskając klawisz F6. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/tr/html4/loose.dtd"> <%@ page contenttype="text/html;charset=windows-1252"%> <html> <head> <title>login form</title> </head> <body> <h1>login form</h1> <form action="./login.do"> <table cellspacing="1" cellpadding="5" border="0"> <tr><td>username</td><td><input type="text" name="username"/></td></tr> <tr><td>password</td><td><input type="password" name="password"/></td></tr> <tr><td colspan="2" align="center"> <input type="submit" value="send"/> <input type="reset" value="clear"/> </td> </tr> </table> </form> </body> </html>
3. Wyświetl menu kontekstowe (prawy klawisz myszy) na ikonie reprezentującej projekt Struts-laboratorium. Wybierz New File/Folder, zaznacz kategorię Web, wybierz typ pliku Struts Action. Kliknij na przycisk Next >. Jako nazwę klasy podaj LoginAction, pakiet ai.struts, ścieżka akcji /Login. Poniższy obraz pokazuje poprawną konfigurację klasy LoginAction. Kliknij przycisk Finish. 4. W nawigatorze obiektów rozwiń gałąź Configuration Files i edytuj plik struts-config.xml. Dodaj w nim przekierowanie do strony success.jsp jako możliwy wynik zakończenia działania klasy LoginAction. <?xml version="1.0" encoding="iso-8859-1"?> <!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> <action-mappings> <action path="/login" type="ai.struts.loginaction"> <forward name="success" path="/success.jsp"/> </action> </action-mappings> </struts-config>
5. Wyświetl menu kontekstowe projektu i dodaj nową stronę JSP. Jako nazwę strony podaj success i kliknij na przycisk Finish. Umieść w pliku success.jsp poniższy kod. Uruchom projekt, wypełnij pola formularza i naciśnij przycisk send. <%@page contenttype="text/html"%> <%@page pageencoding="windows-1250"%> <html> <head> <title>congratulations</title> </head> <body> <h1>congratulations!</h1> You have logged on successfully<br/> </body> </html> 6. W kolejnym kroku utworzymy komponent ActionForm który będzie automatycznie wypełniany danymi wpisanymi do formularza. Dodaj do projektu New File/Folder, wybierz kategorię Web, typ pliku Struts ActionForm Bean. Jako nazwę klasy podaj LoginFormBean, pakiet ai.struts.beans. Kliknij na przycisk Finish. Umieść w klasie LoginFormBean poniższy kod. package ai.struts.beans; import javax.servlet.http.*; import org.apache.struts.action.*; public class LoginFormBean extends ActionForm { private String username = "scott"; private String password = "tiger"; public String getusername() { return username; public void setusername(string username) { this.username = username; public String getpassword() { return password; public void setpassword(string password) { this.password = password; 7. Obejrzyj zawartość pliku struts-config.xml. Zauważ, że automatycznie została dodana sekcja <form-beans> zawierająca deklaracje wykorzystanych komponentów ActionForm. Po powiązaniu z klasą LoginAction komponent ActionForm będzie automatycznie przekazywany do metody execute(). Zmodyfikuj definicję LoginAction w pliku struts-config.xml poprzez jej powiązanie z komponentem LoginFormBean. <action path="/login" type="ai.struts.loginaction" name="loginformbean" scope="request"> <forward name="success" path="/success.jsp"/> </action>
8. W definicji metody execute() klasy LoginAction dodaj poniższy kod oraz zaimportuj klasę LoginFormBean. Uruchom projekt i przetestuj jego działanie, zaobserwuj komunikat pojawiający się w oknie Output u dołu ekranu. Zwróć uwagę, że formularz przekazuje dane metodą GET (domyślną dla znacznika <FORM> w języku HTML). public ActionForward execute(actionmapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { LoginFormBean loginformbean = (LoginFormBean)form; String user = loginformbean.getusername(); String pass = loginformbean.getpassword(); System.out.println("User logged in as " + user + " with password " + pass); return mapping.findforward(success); 9. Dane powinny być przesyłane pomiędzy poszczególnymi warstwami aplikacji przy pomocy komponentów JavaBean. Kolejnym krokiem będzie utworzenie klasy komponentu. Dodaj do projektu nową klasę (New Java Class ), nazwij ją UserBean i umieść klasę w pakiecie ai.struts.beans. Naciśnij przycisk Finish. Umieść w klasie poniższy kod. package ai.struts.beans; public class UserBean { private String username = "scott"; private String password = "tiger"; public UserBean(String username, String password) { this.username = username; this.password = password; public String getusername() { return this.username; public String getpassword() { return this.password;
10. Zmodyfikuj metodę execute() klasy LoginAction w taki sposób, aby dane z formularza były zapisywane w komponencie JavaBean i przesyłane dalej do wynikowej strony JSP jako atrybut obiektu żądania. Nie zapomnij o zaimportowaniu klasy UserBean. public ActionForward execute(actionmapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { LoginFormBean loginformbean = (LoginFormBean)form; String user = loginformbean.getusername(); String pass = loginformbean.getpassword(); UserBean userbean = new UserBean(user,pass); request.setattribute("userdata", userbean); return mapping.findforward(success); 11. W kolejnym kroku wykorzystamy bibliotekę znaczników JSP dostarczaną wraz ze środowiskiem Struts do wyświetlania komunikatów z komponentów JavaBean. Dodaj do nagłówka strony success.jsp deklarację wykorzystania biblioteki. <%@ taglib prefix="bean" uri="http://struts.apache.org/tags-bean"%> 12. W treści strony success.jsp umieść dane odczytane z komponentu JavaBean przy użyciu znacznika <bean:write>. Uruchom i przetestuj aplikację. <h1>congratulations!</h1> You have logged on successfully<br/> as <bean:write name="userdata" property="username"/> with password <bean:write name="userdata" property="password"/>. 13. Alternatywą dla znaczników <bean> jest wykorzystanie języka wyrażeń JSP EL. Zamień powyższy kod na kod wykorzystujący wyrażenia JSP EL. Uruchom i przetestuj aplikację. <h1>congratulations!</h1> You have logged on successfully<br/> as ${userdata.username with password ${userdata.password. 14. Komponent LoginFormBean musi być ręcznie zaimplementowany w postaci klasy. Możliwe jest również wykorzystanie komponentów dynamicznych DynaActionForm. Dodaj do pliku struts-config.xml w sekcji <formbeans> poniższy wpis. <form-bean name="dynaloginformbean" type="org.apache.struts.action.dynaactionform"> <form-property name="username" type="java.lang.string" initial="scott"/> <form-property name="password" type="java.lang.string" initial="tiger"/> </form-bean>
15. Wskaż komponent DynaLoginFormBean jako komponent przejmujący zawartość formularza w trakcie wykonywania się akcji związanej z adresem /Login <action path="/login" type="ai.struts.loginaction" name="dynaloginformbean" scope="request"> <forward name="success" path="/success.jsp"/> </action> 16. Następnie, wykorzystaj zdefiniowany komponent DynaLoginFormBean do przechwycenia zawartości formularza w klasie LoginAction. Nie zapomnij o zaimportowaniu klasy org.apache.struts.action.dynaactionform. Uruchom i przetestuj aplikację. public ActionForward execute(actionmapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { DynaActionForm dynaloginformbean = (DynaActionForm)form; String user = (String)dynaLoginFormBean.get("username"); String pass = (String)dynaLoginFormBean.get("password"); UserBean userbean = new UserBean(user,pass); request.setattribute("userdata", userbean); return mapping.findforward(success); 17. Istnieje również możliwość całkowicie dynamicznej generacji komponentu obsługującego formularz. Dodaj do pliku struts-config.xml do sekcji <form-beans> deklarację wykorzystania leniwego komponentu <form-bean name="lazydynaloginformbean" type="org.apache.struts.validator.lazyvalidatorform"/> 18. Wykorzystanie komponentu LazyValidatorForm łączy się z koniecznością załadowania wtyczki (ang. plugin) obsługującej automatyczną walidację. Dodaj do pliku struts-config.xml poniższy kod jako element stanowiący bezpośrednie dziecko znacznika <struts-config>, jeśli nie został dodany automatycznie przez kreator aplikacji Struts. <plug-in classname="org.apache.struts.validator.validatorplugin"> <set-property property="pathnames" value="/web-inf/validator-rules.xml,/web-inf/validation.xml"/> </plug-in> 19. Wskaż komponent LazyDynaLoginFormBean jako komponent przejmujący zawartość formularza w trakcie wykonywania się akcji związanej z adresem /Login <action path="/login" type="ai.struts.loginaction" name="lazydynaloginformbean" scope="request"> <forward name="success" path="/success.jsp"/> </action>
20. Następnie, wykorzystaj zdefiniowany komponent DynaLoginFormBean do przechwycenia zawartości formularza w klasie LoginAction. Nie zapomnij o zaimportowaniu klasy org.apache.commons.beanutils.dynabean. Uruchom i przetestuj aplikację. public ActionForward execute(actionmapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { DynaBean lazydynaloginformbean = (DynaBean)form; String user = (String)lazyDynaLoginFormBean.get("username"); String pass = (String)lazyDynaLoginFormBean.get("password"); UserBean userbean = new UserBean(user,pass); request.setattribute("userdata", userbean); return mapping.findforward(success); 21. Kolejnym krokiem będzie dodanie pliku zasobów i wprowadzenie obsługi wielu języków przez aplikację. Dodaj do projektu nowy plik zasobów (New File/Folder), wybierz kategorię Other, typ pliku Properties File. Jako nazwę pliku podaj ApplicationResources, a jako folder podaj src/java/ai/struts/resources. Do pliku ApplicationResources wpisz poniższy kod. form.title = Success form.confirmation = Congratulations! form.message = You have logged on successfully 22. Zadeklaruj użycie pliku z zasobami. Do pliku struts-config.xml dodaj, jako bezpośredniego potomka znacznika <struts-config>, poniższy kod. Sprawdź czy kreator aplikacji Struts nie dodał wcześniej elementu <messageresources> z inną wartością atrybutu. W takim wypadku usuń wpis dokonany przez kreator. <message-resources parameter="ai.struts.resources.applicationresources"/> 23. Wpisane klucze zostaną umieszczone w wynikowej stronie success.jsp. Edytuj tę stronę JSP i umieść w niej poniższy kod. Uruchom i przetestuj aplikację. <%@page contenttype="text/html"%> <%@page pageencoding="windows-1250"%> <%@ taglib prefix="bean" uri="http://struts.apache.org/tags-bean"%> <html> <head> <title><bean:message key="form.title"/></title> </head> <body> <h1><bean:message key="form.confirmation"/></h1> <bean:message key="form.message"/><br/> as <bean:write name="userdata" property="username"/> with password <bean:write name="userdata" property="password"/>. </body> </html>
24. Kolejnym krokiem będzie obsługa wielu języków przez naszą aplikację. W nawigatorze obiektów (po lewej stronie) rozwiń gałąź Source Packages, a następnie rozwiń gałąź ai.struts.resources. Kliknij prawym klawiszem myszy na pliku ApplicationResources.properties i z menu kontekstowego wybierz Add Locale. Z listy Language Code wybierz opcję pl-polish i kliknij na przycisk OK. Uwaga: nie wybieraj Country Code ani Variant. Kliknij dwukrotnie na ikonie reprezentującej dodaną lokalizację narodową i podaj polskie odpowiedniki komunikatów. Zwróć uwagę na zastąpienie polskich znaków diakrytycznych ich kodami Unicode. form.title = Sukces form.confirmation = Gratulacje! form.message = Zalogowa\u0142a\u015b(e\u015b) si\u0119 poprawnie 25. Zmień ustawienia przeglądarki. W przeglądarce Internet Explorer wybierz Narzędzia Opcje internetowe Języki, przenieś język polski na początek listy obsługiwanych języków (jeśli języka polskiego nie ma to dodaj ten język). W przeglądarce Firefox upewnij się, że językiem domyślnym jest angielski (Narzędzia Opcje Języki). Uruchom aplikację w obu przeglądarkach i zaobserwuj działanie. 26. Ostatnim elementem aplikacji będzie automatyczna walidacja danych wprowadzonych przez formularz. W tym celu zastąpimy statyczny HTML kodem generowanym przez znacznik <html:form> w celu powiązania formularza z obsługującym go komponentem. W ten sposób będzie możliwe np. wypełnianie formularza wartościami domyślnymi oraz ponowne wyświetlenie formularza w przypadku wystąpienia błędu. Zmień kod w pliku index.jsp na poniższy. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/tr/html4/loose.dtd"> <%@ page contenttype="text/html;charset=windows-1252"%> <%@ taglib uri="http://jakarta.apache.org/struts/tags-html" prefix="html"%> <html> <head> <title>login form</title> </head> <body> <h1>login form</h1> <html:form action="login"> <html:errors/> <table cellspacing="1" cellpadding="5" border="0"> <tr> <td>username</td> <td><html:text property="username" errorkey="no.username"/></td> </tr> <tr> <td>password</td> <td><html:password property="password" errorkey="no.password"/></td> </tr> <tr> <td colspan="2" align="center"> <html:submit value="send"/> <html:reset value="clear"/> </td> </tr> </table> </html:form> </body> </html>
27. Następnie, zmodyfikuj definicję akcji Login w pliku struts-config.xml. <action path="/login" type="ai.struts.loginaction" name="loginformbean" scope="request" input="/index.jsp"> <forward name="success" path="/success.jsp"/> <forward name="failure" path="/index.jsp" redirect="true"/> </action> 28. Dodaj do klasy LoginFormBean metodę validate() która będzie odpowiedzialna za sprawdzenie poprawności danych wprowadzonych przez formularz. public ActionErrors validate(actionmapping mapping, HttpServletRequest req) { ActionErrors errors = new ActionErrors(); if (this.getusername() == "") { errors.add("username", new ActionError("no.username")); if (this.getpassword() == "") { errors.add("password", new ActionError("no.password")); return (errors); 29. W pliku struts-config.xml wskaż ponownie LoginFormBean jako komponent przejmujący zawartość formularza w trakcie wykonywania się akcji związanej z adresem /Login 30. Przywróć w metodzie akcji następującą postać kodu odczytującego przekazane parametry: LoginFormBean loginformbean = (LoginFormBean)form; String user = loginformbean.getusername(); String pass = loginformbean.getpassword(); 31. Dodaj do pliku ApplicationResources.properties poniższe linie. errors.header=<ul> errors.prefix=<li><b><font color="red"> errors.suffix=</font></b></li> errors.footer=</ul> no.username=username is required! no.password=password is required! 32. Uruchom aplikację i spróbuj wysłać pusty formularz.
33. Wypełnij formularz danymi i zatwierdź go. Zwróć uwagę, że teraz dane przesyłane są metodą POST (domyślną dla znacznika <html:form> Struts). 34. Ostatnim krokiem będzie wykorzystanie automatycznego walidatora do kontroli poprawności pól. Do pliku ApplicationResources.properties dodaj domyślne komunikatory walidatora. # -- komunikaty walidatora -- errors.invalid = {0 is invalid. errors.maxlength = {0 cannot be greater than {1 characters. errors.minlength = {0 cannot be less than {1 characters. errors.range = {0 is not in the range {1 through {2. errors.required = {0 is required. 35. Zmień klasę bazową klasy LoginFormBean. Edytuj plik, zaimportuj klasę org.apache.struts.validator.validatorform i wskaż klasę ValidatorForm jako klasę bazową dla klasy LoginFormBean. 36. Usuń z klasy LoginFormBean metodę validate(), aby nie zastąpiła metody validate() odziedziczonej z klasy ValidatorForm. 37. Rozwiń w nawigatorze obiektów zakładkę Configuration Files i umieść w pliku validation.xml poniższy kod. <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE form-validation PUBLIC "-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.1.3//EN" "http://jakarta.apache.org/commons/dtds/validator_1_1_3.dtd"> <form-validation> <formset> <form name="loginformbean"> <field property="username" depends="required"> <arg key="username" resource="false"/> </field> <field property="password" depends="required,mask"> <arg key="password" resource="false"/> <var> <var-name>mask</var-name> <var-value>^[0-9a-za-z]*[\!\@\#\$][0-9a-za-z]*$</var-value> </var> </field> </form> </formset> </form-validation> 38. Przetestuj aplikację. Sprawdź, co się stanie, jeśli spróbujesz wysłać formularz z hasłem które nie zawiera w środku znaku specjalnego. Zaobserwuj zachowanie się formularza w przypadku próby wysłania niekompletnych danych.