7. Додаток QuiEst
Тут ми опишемо додаток на Struts, який є дещо складнішим за попередні, що мали бути простими з навчальною метою.
7.1. Клас users
У нас є клас Java, який зберігає інформацію про користувачів комп’ютера під управлінням Unix. Ця інформація зберігається у трьох спеціальних файлах:
- /etc/passwd: список користувачів
- /etc/group: список груп
- /etc/aliases: список псевдонімів для електронної пошти
Вміст цих трьох файлів такий:
- /etc/passwd
Рядки цього файлу мають такий вигляд:
login:pwd:uid:gid:id:dir:shell
з
ім'я користувача | |
його зашифрований пароль | |
його номер користувача | |
його номер групи | |
його особисті дані | |
його каталог підключення | |
його командний інтерпретатор |
Отже, рядок користувача може виглядати так:
Попередній користувач має номер 110 і належить до групи 57. Визначення групи 57 міститься у файлі /etc/group.
- /etc/group
Рядки цього файлу мають такий вигляд:
nomGroupe:pwd:gid:membre1,membre2,....
з
назва групи | |
його зашифрований пароль — найчастіше це поле залишається порожнім | |
номер групи | |
логіни користувачів — це поле може бути порожнім |
Отже, рядок попередньої групи 57 може виглядати так:
що вказує на те, що група 57 має назву iup2-auto.
- /etc/aliases
Рядки цього файлу мають такий вигляд:
з
псевдонім | |
одну або кілька табуляцій | |
логін користувача, якому належить псевдонім |
Отже, рядок
guillaume.dupond: dupond
означає, що псевдонім guillaume.dupond належить користувачеві з логіном dupond. Нагадаємо, що псевдоніми використовуються в електронних адресах. Отже, якщо в попередньому прикладі Unix-машина називається shiva.istia.univ-angers.fr, то лист, адресований guillaume.dupond@shiva.istia.univ-angers.fr буде доставлено у поштову скриньку користувача з логіном dupond на цій машині.
Тут ми не будемо розглядати весь інтерфейс класу **users**, а лише його конструктор та кілька методів:
import java.io.*;
import java.util.*;
public class users{
// атрибути
private Hashtable usersByLogin=new Hashtable(); // логін --> логін, пароль, ..., каталог
private ArrayList erreurs=new ArrayList(); // список повідомлень про помилки
....
// конструктор
public users(String usersFileName, String groupsFileName, String aliasesFileName) throws Exception {
// usersFileName : ім’я файлу користувачів із рядками у форматі
// login:pwd:uid:gid:id:dir:shell
// groupsFileName : ім’я файлу груп із рядками у форматі
// ім'я:пароль:номер:член1,член2,..
// aliasesFileName : ім’я файлу псевдонімів із рядками у форматі
// псевдонім:[tab]login
// створює словник usersByLogin
....
}// конструктор
// список користувачів
public Hashtable getUsersByLogin(){
return usersByLogin;
}
// помилки
public ArrayList getErreurs(){
return erreurs;
}
словник (хаш-таблиця), ключами якого є логіни з файлу passwd. Значенням, пов’язаним із ключем, є масив рядків (String [7]), елементи якого — це 7 полів рядка файлу passwd, пов’язаного з логіном. Деякі поля можуть бути порожніми, якщо рядок містить менше 7 полів. | |
список повідомлень про помилки — порожній, якщо помилок немає |
7.2. Веб-додаток, який
Пропонується створити такий веб-додаток (сторінка-форма):
![]() |
№ | ім'я | тип HTML | роль |
1 | cmbLogins | <select ...>...</select> | показує список усіх логінів, щодо яких можна отримати інформацію |
2 | btnChercher | <input type="submit" ...> | для запуску пошуку |
Коли користувач натискає кнопку [Chercher] (2), запит щодо логіна (1) надсилається до об’єкта U типу users. Якщо логін існує, отримуємо таку відповідь (сторінка інформації):
![]() |
Як показано вище у URL браузера, параметри форми надсилаються на сервер за допомогою GET. Отже, можна безпосередньо передати браузеру цей налаштований URL. Саме це ми й робимо тут, щоб ввести логін, якого не існує. Отримуємо таку відповідь (сторінка помилок):
![]() |
7.3. Архітектура додатка
![]() |
У цій архітектурі ми знаходимо такі компоненти:
- представлення:
- logins.jsp, що використовується для відображення списку логінів (вигляд 1)
- infos.jsp, що використовується для відображення інформації про логін (вигляд 2)
- erreurs.jsp, що використовується для відображення списку помилок (вигляд 3)
- форми типу ActionForm, що використовуються діями:
- formLogins використовується для збору даних з форми logins.jsp
- дії:
- SetupLoginAction, яка готує вміст formulaire.jsp, а потім відображає цей вигляд
- InfosLoginAction, що обробляє вміст logins.jsp після його надсилання на сервер
- ForwardAction, який обробляє посилання [Retour vers le formulaire] з переглядів infos.jsp та erreurs.jsp
- бізнес-клас users, який використовується діями для отримання своїх даних
- модель, що забезпечується трьома текстовими файлами passwd, group та aliases
7.4. Конфігураційні файли веб-додатку
7.4.1. Файл server.xml
Контекст додатка матиме назву /strutsquiest2. Тому до файлу server.xml Tomcat додамо такий рядок:
Після цього, за необхідності, перезапустимо Tomcat, щоб він врахував новий контекст. Ми можемо перевірити його правильність, звернувшись за адресою http://localhost:8080/strutsquiest2.
7.4.2. Файл web.xml
Файл конфігурації web.xml додатка матиме такий вигляд:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN" "http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">
<web-app>
<servlet>
<servlet-name>strutsquiest2</servlet-name>
<servlet-class>istia.st.struts.quiest.Quiest2ActionServlet</servlet-class>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<init-param>
<param-name>passwdFileName</param-name>
<param-value>data/passwd</param-value>
</init-param>
<init-param>
<param-name>groupFileName</param-name>
<param-value>data/group</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>strutsquiest2</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>
Цей файл web.xml містить нову функцію. Контролер Struts більше не є org.apache.struts.action.ActionServlet, а є похідним класом, який ми тут назвали istia.st.struts.quiest.Quiest2ActionServlet. Це дозволить нам отримати два параметри ініціалізації: passwdFileName (розташування файлу passwd) та groupFileName (розташування файлу group). Файл aliases у цьому додатку не використовується.
7.4.3. Файл struts-config.xml
Файл struts-config.xml матиме такий вигляд:
<?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>
<form-beans>
<form-bean name="formLogins" type="org.apache.struts.action.DynaActionForm">
<form-property name="cmbLogins" type="java.lang.String" initial=""/>
<form-property name="tLogins" type="java.lang.String[]"/>
</form-bean>
</form-beans>
<action-mappings>
<action
path="/init"
name="formLogins"
validate="false"
scope="session"
type="istia.st.struts.quiest.SetupLoginsAction"
>
<forward name="afficherLogins" path="/vues/logins.jsp"/>
<forward name="afficherErreurs" path="/vues/erreurs.jsp"/>
</action>
<action
path="/infosLogin"
name="formLogins"
validate="false"
scope="session"
type="istia.st.struts.quiest.InfosLoginAction"
>
<forward name="afficherErreurs" path="/vues/erreurs.jsp"/>
<forward name="afficherInfos" path="/vues/infos.jsp"/>
</action>
<action
path="/retourLogins"
parameter="/vues/logins.jsp"
type="org.apache.struts.actions.ForwardAction"
/>
</action-mappings>
<message-resources
parameter="istia.st.struts.quiest.ApplicationResources"
null="false"
/>
</struts-config>
Тут містяться три основні розділи:
- опис форм у розділі <form-beans>
- описання дій у розділі <action-mappings>
- заява про файл ресурсів у <message-ressources>
7.4.4. Об'єкти (біни) форм додатка
<form-beans>
<form-bean name="formLogins" type="org.apache.struts.action.DynaActionForm">
<form-property name="cmbLogins" type="java.lang.String" initial=""/>
<form-property name="tLogins" type="java.lang.String[]"/>
</form-bean>
</form-beans>
У нашому додатку є лише один беан-форма, який називається formLogins і є похідним від DynaActionForm. Він буде використовуватися в таких ситуаціях:
- для зберігання даних, необхідних для відображення подання № 1
- отримувати значення з форми виду № 1, коли користувач підтвердить її (submit)
Структура біна formLogins пов’язана з формою подання № 1. Розглянемо її:
![]() |
№ | назва | тип HTML | роль |
1 | cmbLogins | <select ...>...</select> | показує список усіх логінів, щодо яких можна отримати інформацію |
2 | btnChercher | <input type="submit" ...> | для запуску пошуку |
Розрізнимо кілька випадків:
- від клієнта до сервера: об’єкт formLogins використовується для зберігання значень форми HTML, наведеної вище, яка буде відправлена за допомогою кнопки [Envoyer]. Тому йому потрібне поле cmbLogins, яке отримає значення поля HTML, cmbLogins, c.a.d — логін, обраний користувачем.
- При передачі даних від сервера до клієнта об’єкт formLogins використовується для надання початкового вмісту подання № 1. Його поле tLogins слугуватиме вмістом для списку 1. Його поле cmbLogins дозволить визначити елемент списку 1, який потрібно вибрати.
7.4.5. Дії додатка
Дії забезпечуються об’єктами типу Action або їх похідними. Налаштування дій здійснюється всередині тегів <action-mappings>:
<action-mappings>
<action
path="/init"
name="formLogins"
validate="false"
scope="session"
type="istia.st.struts.quiest.SetupLoginsAction"
>
<forward name="afficherLogins" path="/vues/logins.jsp"/>
<forward name="afficherErreurs" path="/vues/erreurs.jsp"/>
</action>
<action
path="/infosLogin"
name="formLogins"
validate="false"
scope="session"
type="istia.st.struts.quiest.InfosLoginAction"
>
<forward name="afficherErreurs" path="/vues/erreurs.jsp"/>
<forward name="afficherInfos" path="/vues/infos.jsp"/>
</action>
<action
path="/retourLogins"
parameter="/vues/logins.jsp"
type="org.apache.struts.actions.ForwardAction"
/>
</action-mappings>
Команда /init
<action
path="/init"
name="formLogins"
validate="false"
scope="session"
type="istia.st.struts.quiest.SetupLoginsAction"
>
<forward name="afficherLogins" path="/vues/logins.jsp"/>
<forward name="afficherErreurs" path="/vues/erreurs.jsp"/>
</action>
Опишемо принцип роботи дії /init:
- Дія /init зазвичай виконується лише один раз під час першого циклу «запит-відповідь», коли користувач надсилає запит на URL http://localhost:8080/strutsquiest2/init.do
- об’єкт formsLogins створюється або повторно використовується. Він підбирається (повторне використання) або розміщується (створення) у сесії відповідно до атрибута scope.
- Викликається його метод reset. Нагадаємо, що цей метод за замовчуванням нічого не робить у класах ActionForm та похідних від них. Він викликається безпосередньо перед копіюванням даних запиту клієнта в об’єкт ActionForm і служить для очищення об’єкта перед цим копіюванням. Який тут запит клієнта? Акція /init запускається, коли запитуваний URL-адреса — http://localhost:8080/strutsquiest2/init.do. Цей об’єкт URL може бути викликаний об’єктом GET або POST. Достатньо вказати в цьому запиті параметри з іменами полів formLogins, щоб вони були ініціалізовані, як показано в наступному прикладі:

- запит містить параметр cmbLogins (afterpak). Отже, контролер Struts скопіював значення цього параметра в поле cmbLogins з formLogins. Потім була виконана дія SetupLoginsAction, яка завершилася відображенням подання logins.jsp. Це подання містить форму, деякі поля якої отримують свої значення з formLogins. Таким чином, поле HTML типу «select», яке називається cmbLogins, отримало своє значення з поля cmbLogins (=afterpak) з formLogins. Саме тому список логінів відображається, починаючи з логіна afterpak.
- Можна також спробувати передати параметр tLogins наступним чином:
Це призвело б до ініціалізації поля tLogins з formLogins за допомогою масиву {"login1","login2"}. Однак, як ми побачимо далі, дія SetupLoginsAction присвоює значення полю tLogins і замінює створений таким чином масив новим масивом. Саме останній і з’являється у поданні logins.jsp.
- Попереднє обговорення, хоч і дещо складне, має ту перевагу, що демонструє: не можна припускати, що дія /init буде запущена без параметрів, отриманих від клієнта. Тому може бути корисним використати метод reset для очищення formLogins. У цьому випадку нам довелося б створити похідний клас від DynaActionForm. Ми цього тут не робили.
- Після виклику методу reset класу formLogins контролер копіює дані запиту клієнта в поля з відповідними іменами класу formLogins. Зазвичай дія /init викликається без параметрів від клієнта, але ми раніше показали, що ніщо не заважає клієнту викликати дію /init із довільними параметрами. Отже, наприкінці цього етапу поля cmbLogins та tLogins цілком можуть мати значення. Ми бачили, що поле cmbLogins збереже це значення, а поле tLogins — ні.
- Далі контролер перевіряє атрибут validate дії. У даному випадку він має значення «false». Метод validate для formLogins не буде викликано. Тому ми його не будемо писати.
- Об'єкт SetupLoginsAction створюється або відновлюється, якщо він уже існував, і запускається його метод execute. Його єдина роль полягає в тому, щоб присвоїти значення полю tLogins об'єкта formLogins. Цим значенням є масив логінів, який буде запитано у бізнес-класі users. Ця операція може завершитися невдачею. Саме тому за дією /init можуть слідувати два перегляди:
- погляд erreurs.jsp, якщо клас users не зміг надати масив логінів
- вигляд logins.jsp в іншому випадку
- контролер відобразить один із цих двох виглядів
- цикл запит-відповідь дії /init завершено.
Дія /infosLogin
<action
path="/infosLogin"
name="formLogins"
validate="false"
scope="session"
type="istia.st.struts.quiest.InfosLoginAction"
>
<forward name="afficherErreurs" path="/vues/erreurs.jsp"/>
<forward name="afficherInfos" path="/vues/infos.jsp"/>
</action>
Опишемо принцип роботи дії / infosLogin:
- дія /infosLogin зазвичай відбувається, коли користувач натискає кнопку [Chercher] у вікні logins.jsp. Після цього на сервер надсилається запит, визначений тегом HTML <form> у поданні:
<html:form name="formLogins" method="get" action="/infosLogin" type="org.apache.struts.action.DynaActionForm">
- бачимо, що запит надсилається на сервер методом GET. Отже, користувач може ввести його вручну:

- об’єкт formsLogins створюється або повторно використовується. Він отримується (повторне використання) або розміщується (створення) у сесії відповідно до атрибута scope.
- Його метод reset викликається безпосередньо перед копіюванням даних запиту клієнта в об’єкт ActionForm. Зазвичай він має вигляд http://localhost:8080/strutsquiest2/infosLogin.do?cmbLogins=xx, де xx — логін, обраний зі списку логінів. Але це може бути й будь-що, якщо користувач скористався попереднім об’єктом URL, передавши довільні параметри. Розглянемо таку послідовність сторінок:

- акція /infosLogin була викликана з рядком параметрів cmbLogins=xx&tLogins=login1&tLogins=login2. Отже, поля cmbLogins та tLogins класу formLogins отримають відповідно значення «xx» та {"login1","login2"}. Дія /infosLogin запитає у бізнес-класу users інформацію, пов’язану з логіном «xx». Клас users відповість, що такого логіна не існує. Звідси й з’явилося вікно, наведене вище. Тепер скористаймося посиланням [Retour au formulaire], наведеним вище:

- Це дія /retourLogins, яка запускається посиланням [Retour au formulaire]. Ця дія просто відображає вигляд logins.jsp без проміжних дій. Нагадаємо, що поле tLogins слугує для заповнення списку логінів у поданні logins.jsp. Оскільки користувач змінив це значення на {"login1","login2"}, саме ці два логіни тепер відображаються у списку. Знову ж таки, не можна не наголосити на абсолютній необхідності враховувати у роботі додатка випадки довільних параметрів, встановлених користувачем або програмою. Рішенням поставленої тут проблеми було б те, щоб посилання [Retour au formulaire] вказувало на дію /init. Таким чином ми були б впевнені, що отримаємо правильний список логінів.
- Повернемося до звичайного запиту до дії /infosLogin, наприклад:
http://localhost:8080/strutsquiest2/infosLogin.do?cmbLogins=afterpak
- Контролер Struts присвоїть значення полю cmbLogins об’єкта ActionForm. Полю tLogins значення не буде присвоєно (у надісланому запиті немає відповідного поля). Такий порядок дій нас влаштовує. Отже, нам не доведеться писати власний метод reset для formLogins.
- Після виклику методу reset об’єкта formLogins контролер копіює дані з клієнтського запиту в поля з такими самими іменами об’єкта formLogins. Поле cmbLogins отримає значення — логін, обраний користувачем (afterpak).
- Потім контролер перевіряє атрибут validate дії. У даному випадку він має значення «false». Метод validate об’єкта formLogins не буде викликано.
- Об’єкт InfosLoginAction створюється або переробляється, якщо він уже існував, і запускається його метод execute. Його роль полягає в отриманні інформації, пов’язаної з логіном cmbLogins. Ця інформація буде запитана у бізнес-класі users. Ця операція може завершитися невдачею (наприклад, логін не існує). Саме тому за дією /infosLogin можуть слідувати два перегляди:
- погляд erreurs.jsp, якщо клас users не зміг надати запитувану інформацію
- погляд infos.jsp в іншому випадку
- контролер відобразить один із цих двох виглядів
- цикл запит-відповідь дії /infosLogin завершено.
Дія /retourLogins
<action
path="/retourLogins"
parameter="/vues/logins.jsp"
type="org.apache.struts.actions.ForwardAction"
/>
- Дія /retourLogins запускається при активації посилання [Retour au formulaire] у поданнях erreurs.jsp та infos.jsp.
- Тут немає форми, пов'язаної з цією дією. Тому ми відразу переходимо до виконання методу execute об'єкта ForwardAction, який поверне об'єкт ActionForward, що вказує на вигляд /vues/logins.jsp.
7.4.6. Файл повідомлень додатка
Третій розділ файлу struts-config.xml — це файл повідомлень:
Файл ApplicationResources.properties розміщений у папці WEB-INF/classes/istia/st/struts/quiest. Його вміст такий:
errors.header=<ul>
errors.footer=</ul>
parametreManquant=<li>Le paramètre [{0}] n'a pas été initialisé</li>
usersException=<li>Erreur d'initialisation de l'application : {0}</li>
loginInconnu=<li>Le login [{0}] n'existe pas</li>
7.5. Код переглядів
Якщо читач не розуміє код представлених нижче подання, йому рекомендується ще раз перечитати урок про роботу з формами.
7.5.1. Вигляд logins.jsp
Нагадаємо, що цей вигляд відображається у двох випадках:
- під час виклику дії /init у першому циклі «запит-відповідь»
- під час виклику дії /retourLogins у наступних циклах
Код подання logins.jsp такий:
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<html>
<head>
<title>Quiest - formulaire</title>
</head>
<body background="<html:rewrite page="/images/standard.jpg"/>">
<center>
<h2>Application QuiEst</h2>
<hr>
<html:form name="formLogins" method="get" action="/infosLogin" type="org.apache.struts.action.DynaActionForm">
<table>
<tr>
<td>Login cherché</td>
<td>
<html:select name="formLogins" property="cmbLogins">
<html:options name="formLogins" property="tLogins"/>
</html:select>
</td>
<td>
<html:submit value="Chercher"/>
</td>
</tr>
</table>
</html:form>
</center>
</body>
</html>
7.5.2. Вигляд infos.jsp
Цей вигляд відображається після успішного виконання команди /infosLogin. Його код такий:
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<html>
<head>
<title><bean:write name="infosLoginBean" scope="request" property="titre"/></title>
</head>
<body background="<html:rewrite page="/images/standard.jpg"/>">
<h2><bean:write name="infosLoginBean" scope="request" property="titre"/></h2>
<hr>
<table border="1">
<tr>
<th>login</th><th>pwd</th><th>uid</th><th>gid</th><th>id</th><th>dir</th><th>shell</th>
</tr>
<tr>
<td><bean:write name="infosLoginBean" scope="request" property="infosLogin[0]"/></td>
<td><bean:write name="infosLoginBean" scope="request" property="infosLogin[1]"/></td>
<td><bean:write name="infosLoginBean" scope="request" property="infosLogin[2]"/></td>
<td><bean:write name="infosLoginBean" scope="request" property="infosLogin[3]"/></td>
<td><bean:write name="infosLoginBean" scope="request" property="infosLogin[4]"/></td>
<td><bean:write name="infosLoginBean" scope="request" property="infosLogin[5]"/></td>
<td><bean:write name="infosLoginBean" scope="request" property="infosLogin[6]"/></td>
</tr>
</table>
<br>
<html:link page="/retourLogins.do">
Retour au formulaire
</html:link>
</body>
</html>
Цей вигляд використовує об’єкт із назвою infosLoginBean, який додається до запиту за допомогою дії /infosLogin. Цей об’єкт має два поля:
String titre; // заголовок, що відображається у вікні
String[] infosLogin; // таблиця інформації, що відображається у вікні
Ми детальніше розглянемо цей клас, коли перейдемо до коду класу InfosLoginAction.
7.5.3. Вигляд erreurs.jsp
Цей вигляд відображається, коли дії /init або /infosLogin завершуються з помилкою. Його код такий:
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<html>
<head>
<title>Application QuiEst - erreurs</title>
</head>
<body background="<html:rewrite page="/images/standard.jpg"/>">
<h2 align="center">Application QuiEst - Erreurs</h2>
<hr>
<h2>Les erreurs suivantes se sont produites</h2>
<html:errors/>
<html:link page="/retourLogins.do">
Retour au formulaire
</html:link>
</body>
</html>
7.6. Класи Java
Файл web.xml посилається на клас Java:
<web-app>
<servlet>
<servlet-name>strutsquiest2</servlet-name>
<servlet-class>istia.st.struts.quiest.Quiest2ActionServlet</servlet-class>
....
</servlet>
...
</web-app>
У конфігураційному файлі struts-config.xml згадуються два класи Java:
<action
path="/infosLogin"
name="formLogins"
validate="false"
scope="session"
type="istia.st.struts.quiest.InfosLoginAction"
>
<forward name="afficherErreurs" path="/vues/erreurs.jsp"/>
<forward name="afficherInfos" path="/vues/infos.jsp"/>
</action>
<action
path="/init"
name="formLogins"
validate="false"
scope="session"
type="istia.st.struts.quiest.SetupLoginsAction"
>
<forward name="afficherLogins" path="/vues/logins.jsp"/>
<forward name="afficherErreurs" path="/vues/erreurs.jsp"/>
</action>
7.6.1. Клас Quiest2ActionServlet
Клас Quiest2ActionServlet є похідним від класу ActionServlet — класу контролера Struts. Ми створюємо похідний клас ActionServlet, щоб налаштувати його метод init. Адже цей метод, що виконується лише один раз під час початкового завантаження сервлету, дозволить нам створити бізнес-об’єкт типу users. Цей об’єкт, насправді, потрібно створити лише один раз, і метод init є ідеальним місцем для цього. Для створення об’єкта users потрібні два файли: passwd та group. Розташування цих двох файлів передається як параметри сервлету у файлі web.xml додатка:
<servlet>
<servlet-name>strutsquiest2</servlet-name>
<servlet-class>istia.st.struts.quiest.Quiest2ActionServlet</servlet-class>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<init-param>
<param-name>passwdFileName</param-name>
<param-value>data/passwd</param-value>
</init-param>
<init-param>
<param-name>groupFileName</param-name>
<param-value>data/group</param-value>
</init-param>
</servlet>
Код сервлета такий:
package istia.st.struts.quiest;
import java.util.*;
import javax.servlet.*;
import org.apache.struts.action.*;
import istia.st.users.*;
public class Quiest2ActionServlet
extends ActionServlet {
// атрибути сервлету
private users u = null;
private ActionErrors erreurs = new ActionErrors();
private String[] tLogins;
//init
public void init() throws ServletException {
// не забути ініціалізувати батьківський клас
super.init();
// локальні змінні
final String[] initParams = {"passwdFileName", "groupFileName"};
Properties params = new Properties();
// отримуємо параметри ініціалізації сервлета
ServletConfig config = getServletConfig();
String servletPath = config.getServletContext().getRealPath("/");
for (int i = 0; i < initParams.length; i++) {
String valeur = config.getInitParameter(initParams[i]);
if (valeur == null) {
erreurs.add(ActionErrors.GLOBAL_ERROR, new ActionError("parametreManquant", initParams[i]));
valeur = "";
}
// зберігаємо параметр
params.setProperty(initParams[i], valeur);
} //for
// повернення, якщо сталися помилки ініціалізації
if (erreurs.size() != 0) {
return;
}
// створюється об’єкт users
try {
u = new users(servletPath + "/" + params.getProperty("passwdFileName"),
servletPath + "/" + params.getProperty("groupFileName"), null);
}
catch (Exception ex) {
erreurs.add(ActionErrors.GLOBAL_ERROR, new ActionError("usersException", ex.getMessage()));
return;
} //catch
// отримуємо список логінів
tLogins = new String[u.getUsersByLogin().size()];
Enumeration eLogins = u.getUsersByLogin().keys();
for (int i = 0; i < tLogins.length; i++) {
tLogins[i] = (String) eLogins.nextElement();
}
// сортуємо логіни
Arrays.sort(tLogins);
} //init
// метод доступу до приватної інформації сервлета
public Object[] getInfos() {
return new Object[] {erreurs, u, tLogins};
}
}
Коротко кажучи, метод init працює наступним чином:
- спочатку викликається метод init батьківського класу (ActionServlet) для його правильної ініціалізації
- потім зчитуються параметри ініціалізації. Якщо їх не вистачає, заповнюється приватний атрибут ActionErrors «помилки».
- якщо параметри ініціалізації присутні, створюється об’єкт users. Під час створення може виникнути виняток. У цьому випадку заповнюється атрибут ActionErrors «erreurs».
- Якщо створення пройшло успішно, із створеного об’єкта витягується список усіх логінів, який сортується в масив, що поміщається у приватний атрибут String[] tLogins.
- Створений об’єкт users зберігається у приватному атрибуті users u.
- Публічний метод getInfos дозволяє отримати три приватні атрибути (u, errors, tLogins) у вигляді масиву об’єктів.
7.6.2. Клас SetupLoginsAction
Ця дія має на меті ініціалізувати об’єкт DynaActionForm formLogins. Цей об’єкт, розміщений у сесії, надалі не потребуватиме повторної ініціалізації. Отже, дія SetupLoginsAction виконується лише один раз. Її код такий:
package istia.st.struts.quiest;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import org.apache.struts.action.*;
public class SetupLoginsAction
extends Action {
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) throws IOException,ServletException {
// підготовка форми для відображення
// отримуємо інформацію з сервлета-контролера
// інформація=(ActionErrors помилки, користувачі u, String[] tLogins)
Object[] infos = ( (Quiest2ActionServlet)this.getServlet()).getInfos();
// чи були помилки під час ініціалізації?
ActionErrors erreurs = (ActionErrors) infos[0];
if (!erreurs.isEmpty()) {
this.saveErrors(request, erreurs);
return mapping.findForward("afficherErreurs");
}
// вводимо логіни у форму
DynaActionForm formLogins=(DynaActionForm) form;
formLogins.set("tLogins",infos[2]);
return mapping.findForward("afficherLogins");
}
}
Як і для всіх дій Struts, код міститься в методі execute. Цей метод:
- отримує від контролера Struts інформацію, яку той зберіг за допомогою свого методу init. Це забезпечує метод getServlet() класу Action.
- серед них є атрибут ActionErrors «Помилки контролера». Якщо цей список помилок не порожній, його додають до запиту та запитують відображення подання erreurs.jsp.
- Якщо список помилок порожній, то в поле tLogins біна formLogins записується список логінів, спочатку створений контролером. Потім викликається відображення подання logins.jsp, яке відобразить список логінів.
7.6.3. Класи InfosLoginBean та InfosLoginAction
Дія InfosLoginAction призначена для отримання інформації, пов’язаної з обраним користувачем логіном, та її відображення користувачеві. Інформація буде зібрана в об’єкті типу InfosLoginBean:
package istia.st.struts.quiest;
public class InfosLoginBean implements java.io.Serializable{
// Bean, що містить необхідну інформацію для інформаційної сторінки
private String titre;
private String[] infosLogin;
// конструктор
public InfosLoginBean(String titre, String[] infosLogin){
this.titre=titre;
this.infosLogin=infosLogin;
}
// методи-геттери
public String getTitre(){
return this.titre;
}
public String[] getInfosLogin(){
return this.infosLogin;
}
public String getInfosLogin(int i){
return this.infosLogin[i];
}
}
Попередній клас є біном c.a.d — це клас Java, у якому приватний атрибут T unAttribut автоматично пов'язаний із двома приватними методами:
- void setUnAttribut(T значення){unAttribut=значення;}
- T getUnAttribut(){ return unAttribut;}
Зверніть увагу на особливу синтаксичну будову методів get та set. Якщо атрибут є масивом T[] unAttribut, можна створити методи get та set для елементів масиву:
- void setUnAttribut(T значення, int i){unAttribut[i]=значення;}
- T getUnAttribut(int i){ return unAttribut[i];}
Щоб краще зрозуміти, розглянемо код подання infos.jsp, яке має бути надіслане після виконання дії InfosLoginAction:
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<html>
<head>
<title><bean:write name="infosLoginBean" scope="request" property="titre"/></title>
</head>
<body background="<html:rewrite page="/images/standard.jpg"/>">
<h2><bean:write name="infosLoginBean" scope="request" property="titre"/></h2>
<hr>
<table border="1">
<tr>
<th>login</th><th>pwd</th><th>uid</th><th>gid</th><th>id</th><th>dir</th><th>shell</th>
</tr>
<tr>
<td><bean:write name="infosLoginBean" scope="request" property="infosLogin[0]"/></td>
<td><bean:write name="infosLoginBean" scope="request" property="infosLogin[1]"/></td>
<td><bean:write name="infosLoginBean" scope="request" property="infosLogin[2]"/></td>
<td><bean:write name="infosLoginBean" scope="request" property="infosLogin[3]"/></td>
<td><bean:write name="infosLoginBean" scope="request" property="infosLogin[4]"/></td>
<td><bean:write name="infosLoginBean" scope="request" property="infosLogin[5]"/></td>
<td><bean:write name="infosLoginBean" scope="request" property="infosLogin[6]"/></td>
</tr>
</table>
<br>
<html:link page="/retourLogins.do">
Retour au formulaire
</html:link>
</body>
</html>
Розглянемо такий тег:
Вона вимагає записати значення поля «title» (property) об’єкта infosLoginBean (name), вказаного в запиті (scope). Значення, яке потрібно записати, буде отримано за допомогою request.getAttribute("infosLoginBean").getTitre(). Отже, метод getTitre повинен існувати в класі InfosLoginBean. Це так. Тег
вимагає записати значення елемента infosLogin[0] об’єкта infosLoginBean, вказаного у запиті. Значення, яке потрібно записати, буде отримано за допомогою request.getAttribute("infosLoginBean").getInfosLogin(0). Отже, у класі InfosLoginBean має існувати метод getInfosLogin(int i). Це так і є.
Клас InfosLoginAction призначений для створення попереднього об’єкта InfosLoginBean на основі логіна, обраного користувачем. Його код такий:
package istia.st.struts.quiest;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import org.apache.struts.action.*;
import istia.st.users.*;
public class InfosLoginAction
extends Action {
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) throws IOException,ServletException {
// повинен відображати інформацію, пов’язану з логіном
// отримуємо інформацію з сервлета-контролера
// інформація=(ActionErrors помилки, користувачі u, LoginBean[] tLogins)
Object[] infos = ( (Quiest2ActionServlet)this.getServlet()).getInfos();
// чи були помилки під час ініціалізації?
ActionErrors erreurs = (ActionErrors) infos[0];
if (!erreurs.isEmpty()) {
this.saveErrors(request, erreurs);
return mapping.findForward("afficherErreurs");
}
// спочатку отримати цей логін
String login = (String) ( (DynaActionForm) form).get("cmbLogins");
// є щось?
if (login == null) {
// Це ненормально — відправляємо форму з логінами
DynaActionForm formLogins=(DynaActionForm) form;
formLogins.set("tLogins",infos[2]);
return mapping.findForward("afficherLogins");
}
// є логін — шукаємо його
String[] infosLogin = (String[]) ( (users) infos[1]).getUsersByLogin().get(login);
// знайдено?
if (infosLogin == null) {
// логін не знайдено — відображаємо сторінку помилок
ActionErrors erreurs2=new ActionErrors();
erreurs2.add(ActionErrors.GLOBAL_ERROR, new ActionError("loginInconnu", login));
this.saveErrors(request, erreurs2);
return mapping.findForward("afficherErreurs");
}
// логін знайдено — додаємо знайдену інформацію до запиту
String titre="Application QuiEst - login["+login+"]";
InfosLoginBean infosLoginBean= new InfosLoginBean(titre,infosLogin);
request.setAttribute("infosLoginBean",infosLoginBean);
return mapping.findForward("afficherInfos");
}
}
Принцип роботи методу execute такий:
- витягується інформація, зібрана контролером Struts під час його ініціалізації. Якщо контролер зафіксував помилки, виконання зупиняється на цьому етапі з запитом на відображення цих помилок.
- перевіряється наявність логіна. Якщо користувач пройшов через форму вибору логіна, логін є. Але користувач цілком може ввести URL-адресу дії безпосередньо в браузері, не передаючи параметрів. Якщо логіна немає, знову відображається список логінів.
- Якщо логін є, запитується інформація, пов’язана з бізнес-класом users. Якщо цей клас не знаходить шуканий логін, відображається сторінка помилок. В іншому випадку створюється об’єкт InfosLoginBean для розміщення в ньому інформації, необхідної для подання infos.jsp. Цей об’єкт додається до запиту, після чого відображається сторінка infos.jsp.
7.7. Розгортання
Структура додатка виглядає наступним чином:
![]() | ![]() |
![]() | ![]() |
![]() | ![]() |
![]() |
7.8. Висновок
Ми використали Struts у реалістичному додатку, що використовує бізнес-клас. Крім того, ми показали, що слід приділяти особливу увагу запиту, надісланому клієнтом, і не робити жодних припущень щодо його характеру. Запит може бути будь-яким, і будь-який додаток повинен спочатку перевірити його валідність.











