3. Вступ до Java-сервлетів та сторінок JSP
У цьому розділі наведено різні приклади сервлетів та сторінок JSP. Вони були протестовані на сервері Tomcat, який працює на порту 8080. Перейшовши за посиланнями на головній сторінці, можна отримати доступ до прикладів сервлетів та сторінок JSP. Наведені нижче приклади здебільшого взяті з прикладів Tomcat. Щоб їх протестувати, достатньо запустити Tomcat, відкрити URL http://localhost:8080 у браузері та перейти за посиланням на сервлети.
![]() | ![]() |
3.1. Java-сервлети
3.1.1. Відправлення вмісту HTML веб-клієнту
Розглянемо наведений вище приклад «Hello World». Сервлет має такий вигляд:
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class HelloWorld extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException
{
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<html>");
out.println("<head>");
out.println("<title>Hello World!</title>");
out.println("</head>");
out.println("<body>");
out.println("<h1>Hello World!</h1>");
out.println("</body>");
out.println("</html>");
}
}
Під час виконання цього сервлету на екрані з'являється таке зображення:

Слід звернути увагу на наступні моменти:
- для сервлетів потрібно імпортувати спеціальні класи:
import javax.servlet.*;
import javax.servlet.http.*;
Бібліотека javax.servlet не завжди входить до стандартного комплекту JDK. У такому випадку її можна завантажити безпосередньо з веб-сайту Sun.
- Сервлет є похідним від класу HttpServlet
public class HelloWorld extends HttpServlet {
- Запит GET, надісланий до сервлету, обробляється методом doGet
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException
- Аналогічно, запит POST, надісланий до сервлету, обробляється методом doPost
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException
- Об’єкт HttpServletRequest request — це об’єкт, який надає нам доступ до запиту, зробленого веб-клієнтом. Відповідь сервлету буде надана через об’єкт HttpServletResponse response
- Об’єкт response дозволяє нам задати заголовки HTTP, які будуть надіслані клієнту. Наприклад, заголовок Content-type: text/html тут задається таким чином:
- Для відправлення відповіді клієнту сервлет використовує вихідний потік, який йому надає об’єкт response:
- Після отримання цього вихідного потоку у нього записується код HTML, після чого він надсилається клієнту:
out.println("<html>");
out.println("<body>");
out.println("<head>");
out.println("<title>Hello World!</title>");
out.println("</head>");
out.println("<body>");
out.println("<h1>Hello World!</h1>");
out.println("</body>");
out.println("</html>");
3.1.2. Отримання параметрів, надісланих веб-клієнтом
Наступний приклад демонструє, як сервлет може отримати параметри, надіслані веб-клієнтом. Форма введення даних:

Відповідь, надіслана сервлетом:

Вихідний код сервлета такий:
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class myRequestParamExample extends HttpServlet {
String title="Récupération des paramètres d'un formulaire";
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException
{
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<html>");
out.println("<body>");
out.println("<head>");
out.println("<title>" + title + "</title>");
out.println("</head>");
out.println("<body bgcolor=\"white\">");
out.println("<h3>" + title + "</h3>");
String firstName = request.getParameter("firstname");
String lastName = request.getParameter("lastname");
if (firstName != null || lastName != null) {
out.println("firstname= " + firstName + "<br>");
out.println("lastname= " + lastName);
} else {
out.println("pas de paramètres");
}
out.println("<P>");
out.print("<form action=\"RequestParamExample\" method=\"POST\">");
out.println("firstname= <input type=text size=20 name=firstname>");
out.println("<br>");
out.println("lastname= <input type=text size=20 name=lastname>");
out.println("<br>");
out.println("<input type=submit>");
out.println("</form>");
out.println("</body>");
out.println("</html>");
}
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException
{
doGet(request, response);
}
}
Відзначимо такі нововведення порівняно з попереднім прикладом:
- Параметри, надіслані браузером, отримуються таким чином:
String firstName = request.getParameter("firstname");
String lastName = request.getParameter("lastname");
Метод request.getParameter("nomParamètre") повертає покажчик null, якщо параметр nomParamètre не входить до набору параметрів, надісланих веб-клієнтом.
- У формі вказано, що браузер повинен надіслати параметри за допомогою методу POST
- Отримані параметри будуть оброблені методом doPost сервлета. У цьому випадку цей метод просто викликає метод doGet. Таким чином, цей сервлет обробляє значення форми незалежно від того, чи були вони надіслані методом GET чи POST.
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException
{
doGet(request, response);
}
3.1.3. Отримання HTTP-заголовків, надісланих веб-клієнтом
Наведений нижче сервлет демонструє, як отримати заголовки HTTP, надіслані веб-клієнтом:

Вихідний код сервлета такий:
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class RequestHeaderExample extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException
{
response.setContentType("text/html");
PrintWriter out = response.getWriter();
Enumeration e = request.getHeaderNames();
while (e.hasMoreElements()) {
String name = (String)e.nextElement();
String value = request.getHeader(name);
out.println(name + " = " + value);
}
}
}
Важливі моменти:
- Саме об’єкт request та його метод getHeaderNames надають нам доступ до заголовків HTTP, надісланих браузером у вигляді переліку:
- Метод request.getHeader("заголовок") дозволяє отримати конкретний HTTP-заголовок. У наведеному вище прикладі наведено декілька з них. Слід пам’ятати, що наведені тут заголовки надсилаються браузером. Сервер також має власні HTTP-заголовки, які іноді повторюють заголовки браузера. HTTP-заголовки, що надсилаються браузером, мають на меті інформувати сервер про можливості браузера.
Заголовок | Значення |
ідентифікатор браузера | |
формати MIME, які підтримує браузер. Таким чином, image/gif означає, що браузер вміє обробляти зображення у форматі GIF | |
у форматі hote:port. Вказує, з яким сервером і через який порт браузер хоче встановити з’єднання. | |
формат кодування, який браузер приймає для документів, надісланих сервером. Отже, якщо сервер має документ у звичайному нестисненому вигляді та у форматі gzip, а браузер вказав, що вміє обробляти формат gzip, то сервер зможе надіслати документ у форматі gzip, щоб заощадити пропускну здатність. | |
мови, які підтримує браузер. Якщо сервер має один і той самий документ кількома мовами, він надішле той, мова якого підтримується браузером. | |
URL, який був запрошений браузером | |
режим з'єднання, запитуваний браузером. Keep-alive означає, що сервер не повинен розривати з'єднання після того, як надіслав запитувану сторінку браузеру. Якщо браузер виявить, що отримана сторінка містить, наприклад, посилання на зображення, він зможе надсилати нові запити до сервера, щоб завантажити їх, не створюючи нового з'єднання. Саме браузер візьме на себе ініціативу закрити з’єднання, коли отримає всі елементи сторінки. |
3.1.4. Отримання інформації про середовище
Наведений нижче сервлет демонструє, як отримати інформацію про середовище виконання сервлета. Деякі з цих даних надсилаються браузером у вигляді HTTP-заголовків, тому їх можна отримати за допомогою описаного вище методу.

Код сервлета такий:
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class RequestInfo extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException
{
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<html>");
out.println("<body>");
out.println("<head>");
out.println("<title>Request Information Example</title>");
out.println("</head>");
out.println("<body>");
out.println("<h3>Request Information Example</h3>");
out.println("Method: " + request.getMethod());
out.println("Request URI: " + request.getRequestURI());
out.println("Protocol: " + request.getProtocol());
out.println("PathInfo: " + request.getPathInfo());
out.println("Remote Address: " + request.getRemoteAddr());
out.println("</body>");
out.println("</html>");
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException
{
doGet(request, response);
}
}
Інформація тут отримується різними методами:
out.println("Method: " + request.getMethod());
out.println("Request URI: " + request.getRequestURI());
out.println("Protocol: " + request.getProtocol());
out.println("PathInfo: " + request.getPathInfo());
out.println("Remote Address: " + request.getRemoteAddr());
Нижче наведено перелік деяких доступних методів та їх значення:
метод | значення |
назва веб-сервера | |
робочий порт веб-сервера | |
метод GET або POST, який використовує браузер для відправлення запиту | |
ім'я клієнтського комп'ютера, з якого браузер надіслав запит | |
адреса IP цього ж комп’ютера | |
тип вмісту, надісланого браузером (заголовок HTTP Content-type) | |
кількість символів, надісланих браузером (заголовок HTTP Content-length) | |
версія протоколу HTTP, яку запитує браузер | |
URI, запитуваний браузером. Відповідає частині URL, що розміщена після ідентифікатора hote:port у http://hote:port/URI |
3.1.5. Створення сервлета з JBuilder та його розгортання за допомогою Tomcat
Тепер ми опишемо, як створити та запустити Java-сервлет. Ми використаємо два інструменти: JBuilder для компіляції сервлету та Tomcat для його виконання. Tomcat міг би вистачити сам по собі. Проте він має обмежені можливості налагодження. Ми повернемося до розробленого раніше прикладу, який виводить параметри, отримані сервером. Сервлет спочатку надсилає таку форму введення даних:

Відповідь, надіслана сервлетом:

Вихідний код сервлета такий:
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class myRequestParamExample extends HttpServlet {
String title="Récupération des paramètres d'un formulaire";
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException
{
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<html>");
out.println("<body>");
out.println("<head>");
out.println("<title>" + title + "</title>");
out.println("</head>");
out.println("<body bgcolor=\"white\">");
out.println("<h3>" + title + "</h3>");
String firstName = request.getParameter("firstname");
String lastName = request.getParameter("lastname");
if (firstName != null || lastName != null) {
out.println("firstname= " + firstName + "<br>");
out.println("lastname= " + lastName);
} else {
out.println("pas de paramètres");
}
out.println("<P>");
out.print("<form action=\"RequestParamExample\" method=\"POST\">");
out.println("firstname= <input type=text size=20 name=firstname>");
out.println("<br>");
out.println("lastname= <input type=text size=20 name=lastname>");
out.println("<br>");
out.println("<input type=submit>");
out.println("</form>");
out.println("</body>");
out.println("</html>");
}
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException
{
doGet(request, response);
}
}
- Створіть проект myRequestParamExample за допомогою JBuilder і включіть до нього попередню програму myRequestParamExample.java.
- Під час компіляції може виникнути така проблема: ваш файл JBuilder не обов’язково містить бібліотеку javax.servlet, необхідну для компіляції сервлетів. У цьому випадку вам потрібно налаштувати JBuilder так, щоб він використовував додаткові бібліотеки класів. Спосіб наведено в додатках до цього документа для JBuilder 7. Частково наводимо його тут:
- увімкніть опцію Tools/Configure JDKs або (Options/Configure JDK)

У розділі «Налаштування» (Settings) вище зазвичай у полі Name вказано версію JDK 1.3.1. Якщо у вас є новіша версія JDK, скористайтеся кнопкою Change, щоб вказати каталог, у якому вона встановлена. Вище було вказано каталог E:\Program Files\jdk14, де було встановлено JDK 1.4. Відтепер JBuilder використовуватиме цей JDK для своїх компіляцій та виконання. У розділі (Class, Source, Documentation) міститься список усіх бібліотек класів, які будуть аналізуватися JBuilder, у даному випадку — класи з JDK 1.4. Класів з цієї версії недостатньо для веб-розробки на Java. Щоб додати інші бібліотеки класів, використовується кнопка Add і вказуються додаткові файли .jar, які потрібно використовувати. Файли .jar — це бібліотеки класів. Tomcat 4.x містить усі бібліотеки класів, необхідні для веб-розробки. Вони знаходяться в <tomcat>\common\lib, де <tomcat> — це каталог інсталяції Tomcat:

За допомогою кнопки Add ми додамо ці бібліотеки, одну за одною, до списку бібліотек, які сканує JBuilder:

Відтепер можна компілювати Java-програми, що відповідають стандарту J2EE, зокрема Java-сервлети. JBuilder використовується лише для компіляції, а виконання згодом забезпечується Tomcat.
- Тепер ви можете скомпілювати програму myRequestParamExample.java і створити сервлет myRequestParamExample.class. Куди розмістити цей сервлет? Якщо початкові налаштування Tomcat не змінювалися, файли .class сервлетів слід розмістити в папці <tomcat>\webapps\examples\WEB-INF\classes (Tomcat 4.x).
- Переконайтеся, що Tomcat запущено, і за допомогою браузера відкрийте сторінку http://localhost:8080/examples/servlet/myRequestParamExample:

3.1.6. Приклади
У наведених нижче прикладах ми використовували описаний раніше метод:
- компіляція вихідного коду сервлета XX.java за допомогою JBuilder
- розгортання сервлета XX.class у <tomcat>\webapps\examples\WEB-INF\classes
- Запустити Tomcat, відкрити у браузері сторінку URL http://localhost:8080/examples/servlet/XX
3.1.6.1. Динамічне створення форми — 1
Як приклад розглянемо створення форми, що містить лише один елемент управління: список. Вміст цього списку формується динамічно на основі значень, взятих із масиву. Насправді ці значення часто беруться з бази даних. Форма має такий вигляд:

Якщо для наведеного вище прикладу виконати Envoyer, отримаємо таку відповідь:

Слід зауважити, що код URL, який формує відповідь, є таким самим, як і той, що відображає форму. У цьому випадку сервлет самостійно обробляє відповідь на форму, яку він сам надіслав. Це типовий випадок. Код HTML форми має такий вигляд:
<html>
<head><title>Génération de formulaire</title></head>
<body>
<h3>Choississez un nombre</h3><hr>
<form method="POST">
<select name="cmbValeurs" size="1">
<option>zéro</option>
<option>un</option>
<option>deux</option>
<option>trois</option>
<option>quatre</option>
<option>cinq</option>
<option>six</option>
<option>sept</option>
<option>huit</option>
<option>neuf</option>
</select>
<input type="submit" value="Envoyer">
</form>
</body>
</html>
Слід зазначити, що значення, які надсилаються через форму, передаються методом POST. Код HTML у відповіді:
<html>
<head><title>Voici ma réponse</title></head>
<body>
Vous avez choisi le nombre<h2>neuf</h2>
</body>
</html>
Код сервлета, який генерує цю форму та відповідь, виглядає так:
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class gener1 extends HttpServlet{
// змінні екземпляра
private String title="Génération d'un formulaire";
private final String[] valeurs={"zéro","un","deux","trois","quatre","cinq","six",
"sept","huit","neuf"};
private final String HTML1=
"<html>" +
"<head>" +
"<title>Génération de formulaire</title>"+
"</head>" +
"<body>" +
"<h3>Choississez un nombre</h3>"+
"<hr>" +
"<form method=\"POST\">";
private final String HTML2="<input type=\"submit\" value=\"Envoyer\">";
private final String HTML3="</form>\n</body>\n</html>";
// GET
public void doGet(HttpServletRequest request,HttpServletResponse response)
throws IOException, ServletException{
// клієнту вказується тип надісланого документа
response.setContentType("text/html");
// надсилається форма
PrintWriter out=response.getWriter();
// початок
out.println(HTML1);
// комбінований список
out.println("<select name=\"cmbValeurs\" size=\"1\">");
for (int i=0;i<valeurs.length;i++){
out.println("<option>"+valeurs[i]+"</option>");
}//для
out.println("</select>");
// кінець форми
out.println(HTML2+HTML3);
}//GET
// POST
public void doPost(HttpServletRequest request,HttpServletResponse response)
throws IOException, ServletException{
// отримуємо вибір користувача
String choix=request.getParameter("cmbValeurs");
if(choix==null) doGet(request,response);
// готуємо відповідь
String réponse="<html><head><title>Voici ma réponse</title></head>";
réponse+="<body>Vous avez choisi le nombre <h2>"+choix+"</h2></body></html>";
// повідомляємо клієнту тип надісланого документа
response.setContentType("text/html");
// надсилаємо форму
PrintWriter out=response.getWriter();
out.println(réponse);
}//POST
}//класифікація
Метод doGet використовується для формування форми. У ній є динамічна частина — вміст списку, який у даному випадку береться з масиву. Метод doPost використовується для формування відповіді. Тут єдиною динамічною частиною є значення, обране користувачем у списку форми. Це значення отримується за допомогою request.getParameter("cmbValeurs"), де cmbValeurs — це ім’я списку HTML:
На завершення слід зазначити наступне:
- браузер надсилає значення форми до сервлету, який її згенерував, оскільки тег <form> не має атрибута <action>. У цьому випадку браузер надсилає дані, введені у форму, до сервлету URL, який її надав.
- тег <form> вказує, що дані форми мають надсилатися за допомогою методу POST. Саме тому ці значення отримуються методом doPost сервлета.
3.1.6.2. Динамічне формування форми — 2
Повернемося до попереднього прикладу, змінивши його наступним чином. Пропонована форма залишається незмінною:

Чи відрізняється відповідь:

У відповіді повертається форма, а під нею вказано число, обране користувачем. Крім того, саме це число відображається як обране під час показу списку. Тоді користувач може обрати інше число:

а потім виконати Envoyer. Він отримає таку відповідь:

Код сервлету з іменем gener2.java виглядає так:
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class gener2 extends HttpServlet{
// змінні екземпляру
private String title="Génération d'un formulaire";
private final String[] valeurs={"zéro","un","deux","trois","quatre","cinq","six",
"sept","huit","neuf"};
private final String HTML1=
"<html>" +
"<head>" +
"<title>Génération de formulaire</title>"+
"</head>" +
"<body>" +
"<h3>Choisissez un nombre</h3>"+
"<hr>" +
"<form method=\"POST\">";
private final String HTML2="<input type=\"submit\" value=\"Envoyer\"></form>\n";
private final String HTML3="</body>\n</html>";
// GET
public void doGet(HttpServletRequest request,HttpServletResponse response)
throws IOException, ServletException{
// отримуємо можливий вибір користувача
String choix=request.getParameter("cmbValeurs");
if(choix==null) choix="";
// повідомляємо клієнту тип надісланого документа
response.setContentType("text/html");
// надсилається форма
PrintWriter out=response.getWriter();
// початок
out.println(HTML1);
// комбінований список
out.println("<select name=\"cmbValeurs\" size=\"1\">");
String selected="";
for (int i=0;i<valeurs.length;i++){
if(valeurs[i].equals(choix)) selected="selected"; else selected="";
out.println("<option "+selected+">"+valeurs[i]+"</option>");
}//для
out.println("</select>");
// продовження форми
out.println(HTML2);
if(! choix.equals("")){
// відображається вибір користувача
out.println("<hr>Vous avez choisi le nombre <h2>"+choix+"</h2>");
}//якщо
// кінець форми
out.println(HTML3);
}//GET
// POST
public void doPost(HttpServletRequest request,HttpServletResponse response)
throws IOException, ServletException{
// перенаправлення на GET
doGet(request,response);
}//POST
}//клас
Метод doGet виконує всі операції: він формує форму, яку надсилає клієнту, та обробляє значення, які той повертає. Слід звернути увагу на наступне:
- перевіряється, чи має параметр cmbValeurs значення.
- якщо так, то під час формування вмісту списку кожен його елемент порівнюється з вибором користувача, щоб присвоїти атрибут selected елементу, обраному користувачем: <option selected>елемент</option>. Крім того, під формою відображається значення вибору.
3.1.6.3. Динамічне формування форми — 3
Ми повертаємося до тієї самої задачі, що й раніше, але цього разу значення беруться з бази даних. У нашому прикладі це база MySQL:
- база даних називається dbValeurs
- його власником є admDbValeurs, пароль якого — mdpDbValeurs
- база даних містить єдину таблицю з назвою tvaleurs
- ця таблиця має лише одне цілочисельне поле з назвою «valeur»
E:\Program Files\EasyPHP\mysql\bin>mysql --database=dbValeurs --user=admDbValeurs --password=mdpDbVa
leurs
mysql> show tables;
+---------------------+
| Tables_in_dbValeurs |
+---------------------+
| tvaleurs |
+---------------------+
1 row in set (0.00 sec)
mysql> describe tvaleurs;
+--------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------+---------+------+-----+---------+-------+
| valeur | int(11) | | | 0 | |
+--------+---------+------+-----+---------+-------+
mysql> select * from tvaleurs;
+--------+
| valeur |
+--------+
| 0 |
| 1 |
| 2 |
| 3 |
| 4 |
| 6 |
| 5 |
| 7 |
| 8 |
| 9 |
+--------+
10 rows in set (0.00 sec)
База даних MySQL dbValeurs була відкрита драйвером ODBC для MySQL. Її ім’я DSN (Data Source Name) — odbc-valeurs. Код сервлету такий:
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.sql.*;
import java.util.*;
public class gener3 extends HttpServlet{
// заголовок сторінки
private final String title="Génération d'un formulaire";
// база даних значень списку
private final String DSNValeurs="odbc-valeurs";
private final String admDbValeurs="admDbValeurs";
private final String mdpDbValeurs="mdpDbValeurs";
// значення списку
private String[] valeurs=null;
// повідомлення про помилку
private String msgErreur=null;
// код HTML
private final String HTML1=
"<html>" +
"<head>" +
"<title>Génération de formulaire</title>"+
"</head>" +
"<body>" +
"<h3>Choisissez un nombre</h3>"+
"<hr>" +
"<form method=\"POST\">";
private final String HTML2="<input type=\"submit\" value=\"Envoyer\"></form>\n";
private final String HTML3="</body>\n</html>";
// GET
public void doGet(HttpServletRequest request,HttpServletResponse response)
throws IOException, ServletException{
// клієнту вказується тип надісланого документа
response.setContentType("text/html");
// вихідний потік
PrintWriter out=response.getWriter();
// чи успішно пройшла ініціалізація сервлету?
if (msgErreur!=null){
// сталася помилка — генерується сторінка помилки
out.println("<html><head><title>"+title+"</title></head>");
out.println("<body><h3>Application indisponible ("+msgErreur+
")</h3></body></html>");
return;
}//if
// отримуємо можливий вибір користувача
String choix=request.getParameter("cmbValeurs");
if(choix==null) choix="";
// відправляється форма
// початок
out.println(HTML1);
// комбінований список
out.println("<select name=\"cmbValeurs\" size=\"1\">");
String selected="";
for (int i=0;i<valeurs.length;i++){
if(valeurs[i].equals(choix)) selected="selected"; else selected="";
out.println("<option "+selected+">"+valeurs[i]+"</option>");
}//for
out.println("</select>");
// продовження форми
out.println(HTML2);
if(! choix.equals("")){
// відображається вибір користувача
out.println("<hr>Vous avez choisi le nombre <h2>"+choix+"</h2>");
}//якщо
// кінець форми
out.println(HTML3);
}//GET
// POST
public void doPost(HttpServletRequest request,HttpServletResponse response)
throws IOException, ServletException{
// перенаправлення на GET
doGet(request,response);
}//POST
// ініціалізація сервлета
public void init(){
// заповнює масив значень з бази даних ODBC
// з іменем DSN: DSNvaleurs
Connection connexion=null;
Statement st=null;
ResultSet rs=null;
try{
// підключення до бази даних ODBC
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
connexion=DriverManager.getConnection("jdbc:odbc:"+DSNValeurs,admDbValeurs,mdpDbValeurs);
// об'єкт Statement
st=connexion.createStatement();
// виконання запиту SELECT для отримання значень
rs=st.executeQuery("select valeur from Tvaleurs");
// значення отримано та поміщено в динамічний масив
ArrayList lstValeurs=new ArrayList();
while(rs.next()){
// значення записується у список
lstValeurs.add(rs.getString("valeur"));
}//while
// перетворення списку в масив
valeurs=new String[lstValeurs.size()];
for (int i=0;i<lstValeurs.size();i++){
valeurs[i]=(String)lstValeurs.get(i);
}
}catch(Exception ex){
// проблема
msgErreur=ex.getMessage();
}finally{
try{rs.close();}catch(Exception ex){}
try{st.close();}catch(Exception ex){}
try{connexion.close();}catch(Exception ex){}
}//try
}//ініціалізація
}//клас
Слід звернути увагу на такі важливі моменти:
- Сервлет може бути ініціалізований методом, сигнатура якого повинна бути public void init(). Цей метод виконується лише під час початкового завантаження сервлета
- Після завантаження сервлет залишається в пам'яті постійно. Це означає, що після обслуговування клієнта він не вивантажується. Таким чином, він швидше відповідає на запити клієнтів.
- У нашому сервлеті потрібно знайти список значень у базі даних. Оскільки цей список не змінюється з часом, метод init є ідеальним моментом для його отримання. Таким чином, сервлет звертається до бази даних лише один раз — під час його початкового завантаження, а не при кожному запиті клієнта.
- Доступ до бази даних може завершитися невдачею. Метод init нашого сервлета встановлює повідомлення про помилку msgErreur у разі невдачі. Це повідомлення перевіряється в методі doGet, і якщо сталася помилка, doGet генерує сторінку, що повідомляє про неї.
- У коді методу init використовується стандартний доступ до бази даних за допомогою драйверів Odbc-Jdbc. За необхідності читачеві пропонується ознайомитися з методами доступу до баз даних JDBC.
Під час виконання сервлету, якщо сервер MySQL не запущено, з’являється така сторінка помилки:
![]()
Якщо тепер запустити сервер MySQL, з’явиться така сторінка:

Якщо вибрати число 6 і натиснути «Відправити»:

3.1.6.4. Отримання значень з форми
Повернемося до прикладу, з яким ми вже стикалися, — до наступної веб-форми:

Код HTML для форми balises2.htm виглядає так:
<html>
<head>
<title>balises</title>
<script language="JavaScript">
function effacer(){
alert("Vous avez cliqué sur le bouton Effacer");
}//видалити
</script>
</head>
<body background="/images/standard.jpg">
...
<form method="POST" action="http://localhost:8080/examples/servlet/parameters">
<table border="0">
<tr>
<td>Etes-vous marié(e)</td>
<td>
<input type="radio" value="Oui" name="R1">Oui
<input type="radio" name="R1" value="non" checked>Non
</td>
</tr>
<tr>
<td>Cases à cocher</td>
<td>
<input type="checkbox" name="C1" value="un">1
<input type="checkbox" name="C2" value="deux" checked>2
<input type="checkbox" name="C3" value="trois">3
</td>
</tr>
<tr>
<td>Champ de saisie</td>
<td>
<input type="text" name="txtSaisie" size="20" value="qqs mots">
</td>
</tr>
<tr>
<td>Mot de passe</td>
<td>
<input type="password" name="txtMdp" size="20" value="unMotDePasse">
</td>
</tr>
<tr>
<td>Boîte de saisie</td>
<td>
<textarea rows="2" name="areaSaisie" cols="20">
ligne1
ligne2
ligne3
</textarea>
</td>
</tr>
<tr>
<td>combo</td>
<td>
<select size="1" name="cmbValeurs">
<option>choix1</option>
<option selected>choix2</option>
<option>choix3</option>
</select>
</td>
</tr>
<tr>
<td>liste à choix simple</td>
<td>
<select size="3" name="lst1">
<option selected>liste1</option>
<option>liste2</option>
<option>liste3</option>
<option>liste4</option>
<option>liste5</option>
</select>
</td>
</tr>
<tr>
<td>liste à choix multiple</td>
<td>
<select size="3" name="lst2" multiple>
<option selected>liste1</option>
<option>liste2</option>
<option selected>liste3</option>
<option>liste4</option>
<option>liste5</option>
</select>
</td>
</tr>
<tr>
<td>bouton</td>
<td>
<input type="button" value="Effacer" name="cmdEffacer" onclick="effacer()">
</td>
</tr>
<tr>
<td>envoyer</td>
<td>
<input type="submit" value="Envoyer" name="cmdRenvoyer">
</td>
</tr>
<tr>
<td>rétablir</td>
<td>
<input type="reset" value="Rétablir" name="cmdRétablir">
</td>
</tr>
</table>
<input type="hidden" name="secret" value="uneValeur">
</form>
</body>
</html>
Тег <form> у формі було визначено наступним чином:
Браузер «відправить» значення форми на URL http://localhost:8080/examples/servlet/parameters, який є URL сервлета, що керується Tomcat і відображає значення попередньої форми. Якщо викликати сервлет parameters безпосередньо, отримаємо такі результати:

Якщо введена форма balises2.htm має такий вигляд:

і натиснути кнопку «Відправити» (типу submit), сервлет parameters цього разу викликається з параметрами. Тоді він повертає таку відповідь:

У цій відповіді дійсно містяться значення, введені у форму. Код сервлету такий:
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class parameters extends HttpServlet{
// змінні екземпляра
String title="Récupération des paramètres d'un formulaire";
private String getParameter(HttpServletRequest request, String contrôle){
// повертає значення request.getParameter (перевірка) або "" якщо вона не існує
String valeur=request.getParameter(contrôle);
if(valeur==null) return ""; else return valeur;
}//getParameter
// GET
public void doGet(HttpServletRequest request,HttpServletResponse response)
throws IOException, ServletException
{
// спочатку отримуємо параметри форми
String R1=getParameter(request,"R1");
String C1=getParameter(request,"C1");
String C2=getParameter(request,"C2");
String C3=getParameter(request,"C3");
String txtSaisie=getParameter(request,"txtSaisie");
String txtMdp=getParameter(request,"txtMdp");
String areaSaisie=getParameter(request,"areaSaisie");
String[] lignes=areaSaisie.split("\\r\\n");
String cmbValeurs=getParameter(request,"cmbValeurs");
String lst1=getParameter(request,"lst1");
String[] lst2=request.getParameterValues("lst2");
String secret=getParameter(request,"secret");
// вказуємо вміст документа
response.setContentType("text/html");
// надсилаємо документ
PrintWriter out = response.getWriter();
out.println("<html>");
out.println("<body>");
out.println("<head>");
out.println("<title>" + title + "</title>");
out.println("</head>");
out.println("<body bgcolor=\"white\">");
out.println("<h3>" + title + "</h3>");
out.println("<hr>");
out.println("<table border=\"1\">");
out.println("<tr><td>R1</td><td>"+R1+"</td></tr>");
out.println("<tr><td>C1</td><td>"+C1+"</td></tr>");
out.println("<tr><td>C2</td><td>"+C2+"</td></tr>");
out.println("<tr><td>C3</td><td>"+C3+"</td></tr>");
out.println("<tr><td>txtSaisie</td><td>"+txtSaisie+"</td></tr>");
out.println("<tr><td>txtMdp</td><td>"+txtMdp+"</td></tr>");
for(int i=0;i<lignes.length;i++)
out.println("<tr><td>areaSaisie["+i+"]</td><td>"+lignes[i]+"</td></tr>");
out.println("<tr><td>cmbValeurs</td><td>"+cmbValeurs+"</td></tr>");
out.println("<tr><td>lst1</td><td>"+lst1+"</td></tr>");
if(lst2==null)
out.println("<tr><td>lst2</td><td></td></tr>");
else
for(int i=0;i<lst2.length;i++)
out.println("<tr><td>lst2</td><td>"+lst2[i]+"</td></tr>");
out.println("<tr><td>secret</td><td>"+secret+"</td></tr>");
out.println("</body>");
out.println("</html>");
}
// POST
public void doPost(HttpServletRequest request,HttpServletResponse response)
throws IOException, ServletException
{
// перенаправляє на GET
doGet(request,response);
}
}
У цьому коді використовуються техніки, представлені раніше в іншому прикладі. Слід звернути увагу на два моменти:
- елемент управління lst2 є списком із можливістю множинного вибору, отже, можна вибрати кілька елементів. Саме так і сталося в нашому прикладі, де було вибрано елементи liste1 та liste3. Значення lst2 були передані браузером на сервер у вигляді lst2=liste1&lst2=liste3. Java-сервлет може отримати ці значення у вигляді масиву за допомогою методу getParameterValues: тут request.getParameterValues("lst2") повертає масив із 2 символьних рядків ["liste1","liste3"].
- Елемент управління areaSaisie є багаторядковим полем введення. request.getParameter("areaSaisie") повертає вміст поля у вигляді єдиного символьного рядка. Якщо з нього потрібно витягти окремі рядки, що його складають, можна скористатися функцією split класу String. Наступний код
витягує рядки з поля введення. Ці рядки закінчуються символами \r\n (0D0A).
Для проведення тестів ми:
- збудували та скомпілювали сервлет parameters разом із JBuilder, як було описано раніше
- розмістили згенерований клас у <tomcat>\webapps\examples\WEB-INF\classes, де <tomcat> — це каталог інсталяції Tomcat.
- запросив URL http://localhost:81/html/balises2.htm, код якого наведено вище
- заповнив форму та натиснув кнопку Envoyer.
3.1.6.5. Отримання заголовків HTTP від веб-клієнта
Ми використовуємо той самий приклад, що й раніше, але у відповідь веб-клієнту, який надіслав значення форми, ми надсилаємо йому заголовки HTTP, які він надіслав одночасно. Ми вносимо лише одну зміну до нашої форми:
Значення з форми будуть надіслані методом GET до Java-сервлета з назвою headers, розташованого в <tomcat>\webapps\examples\WEB-INF\classes. Сервлет headers було створено та скомпільовано разом із JBuilder:
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class headers extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException
{
// визначається тип документа
response.setContentType("text/html");
// отримується потік запису
PrintWriter out = response.getWriter();
// відображення списку заголовків HTTP
Enumeration e = request.getHeaderNames();
while (e.hasMoreElements()) {
String name = (String)e.nextElement();
String value = request.getHeader(name);
out.println("<b>"+name + "</b> = " + value + "<br>");
}
}//GET
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException
{
//GET
doGet(request,response);
}//POST
}
Запитуємо URL http://localhost:81/html/balises2.htm і отримуємо Envoyer без зміни форми. Отримуємо таку відповідь:

Зверніть увагу на значення URL, яке міститься в полі Address браузера і показує спосіб (GET), який використовується для передачі параметрів. Візьмемо той самий приклад, але змінимо спосіб надсилання параметрів (POST):
Отримуємо таку нову відповідь:

Зверніть увагу на заголовки HTTP, content-type та content-length, характерні для відправлення за допомогою POST. Крім того, слід зазначити, що в полі Address браузера значення форми більше не відображаються.
3.2. Сторінки JSP
Сторінки JSP (Java Server Pages) — це ще один спосіб написання веб-серверних додатків. Фактично ці сторінки JSP перед виконанням перетворюються на сервлети, і тут знову застосовується технологія сервлетів. Сторінки JSP дозволяють краще висвітлити структуру згенерованих сторінок HTML. Нижче наведено приклади, деякі з яких можна переглянути, перейшовши за посиланням JSP на головній сторінці Tomcat:
![]() | ![]() |
3.2.1. Отримання інформації про середовище
Тут ми повернемося до прикладу, який вже розглядали у контексті сервлету: відображення змінних середовища сервлету. Це приклад snoop із набору прикладів JSP:

Вихідний код сторінки JSP знаходиться в <tomcat>\jakarta-tomcat\examples\jsp\snp\snoop.jsp (Tomcat 3.x) або <tomcat>\examples\jsp\snp\snoop.jsp (Tomcat 4.x)
<html>
<!--
Copyright (c) 1999 The Apache Software Foundation. All rights
reserved.
-->
<body bgcolor="white">
<h1> Request Information </h1>
<font size="4">
JSP Request Method: <%= request.getMethod() %>
<br>
Request URI: <%= request.getRequestURI() %>
<br>
Request Protocol: <%= request.getProtocol() %>
<br>
Servlet path: <%= request.getServletPath() %>
<br>
Path info: <%= request.getPathInfo() %>
<br>
Path translated: <%= request.getPathTranslated() %>
<br>
Query string: <%= request.getQueryString() %>
<br>
Content length: <%= request.getContentLength() %>
<br>
Content type: <%= request.getContentType() %>
<br>
Server name: <%= request.getServerName() %>
<br>
Server port: <%= request.getServerPort() %>
<br>
Remote user: <%= request.getRemoteUser() %>
<br>
Remote address: <%= request.getRemoteAddr() %>
<br>
Remote host: <%= request.getRemoteHost() %>
<br>
Authorization scheme: <%= request.getAuthType() %>
<hr>
The browser you are using is <%= request.getHeader("User-Agent") %>
<hr>
</font>
</body>
</html>
Звернемо увагу на наступні моменти:
- ми маємо код, який дуже схожий на HTML. Однак у ньому містяться теги <%= expression %>, що є характерними для мови JSP. Компілятор JSP замінює в тексті HTML весь тег на значення expression.
- У цьому прикладі використовуються методи об’єкта Java request, який є об’єктом request, з яким ми вже знайомилися під час вивчення сервлетів. Отже, це об’єкт HttpServletRequest. Таким чином, тег <%= request.getRemoteHost() %> буде замінено в коді HTML на ім’я комп’ютера веб-клієнта, який надіслав запит.
- Такого самого результату можна досягти за допомогою сервлета, але тут структура веб-сторінки є більш зрозумілою.
3.2.2. Отримання параметрів, надісланих веб-клієнтом
Тут ми повернемося до прикладу, який вже розглядали з використанням сервлета. У браузері відображається форма:

У відповідь на цей запит браузер отримує таку сторінку:

Код сторінки JSP такий:
<%
// локальні змінні головної процедури
String title="Récupération des paramètres d'un formulaire";
String firstName = request.getParameter("firstname");
String lastName = request.getParameter("lastname");
%>
<!-- код HTML -->
<html>
<head>
<title><%= title %></title>
</head>
<body bgcolor="white">
<h3><%= title %></h3>
<%
if (firstName != null || lastName != null) {
out.println("firstname= " + firstName + "<br>");
out.println("lastname= " + lastName);
} else {
out.println("pas de paramètres");
}
%>
<P>
<form method="POST">
firstname= <input type="text" size="20" name="firstname">
<br>
lastname= <input type="text" size="20" name="lastname">
<br>
<input type="submit">
</form>
</body>
</html>
- Якщо ми бачимо тег <%= вираз %>, який вже зустрічався в попередньому прикладі, то з’являється новий тег <% інструкції Java; %>. Тег <% вводить код Java. Цей код закінчується при зустрічі з тегом закриття коду %>.
- Весь попередній код (HTML + JSP) буде перетворено на Java-сервлет. Він буде розміщений в єдиному методі, який називається головним методом сторінки JSP. Саме тому змінні Java, оголошені на початку сторінки JSP, доступні в інших фрагментах коду JSP, що розкидані по коду HTML: ці змінні та фрагменти коду стануть частиною одного й того самого методу Java. Але якби наш код JSP містив методи, змінні title, firstname та lastname не були б у ньому доступні через ізольованість методів. Їх потрібно було б зробити глобальними змінними або передати як параметри методам. Ми повернемося до цього пізніше.
- Щоб включити динамічні елементи в код HTML, можна скористатися двома методами: <%= вираз %> або out.println(вираз). Об’єкт out — це потік виводу, аналогічний однойменному, що зустрічається у прикладах сервлетів, але не того самого типу: це об’єкт JspWriter, а не PrintWriter. Він дозволяє записувати в потік HTML за допомогою методів print та println.
- Сторінка JSP краще відображає структуру згенерованої сторінки HTML, ніж еквівалентний сервлет.
3.2.3. Теги JSP
Ось перелік тегів, які можна зустріти на сторінці JSP, та їхнє значення.
тег | значення |
коментар HTML. Надсилається клієнту. | |
коментар JSP. Не надсилається клієнту. | |
Оголошує глобальні змінні та методи. Змінні будуть доступні у всіх методах | |
значення виразу буде вставлено на сторінку HTML замість тегу | |
містить код Java, який стане частиною головного методу сторінки JSP | |
визначає атрибути для сторінки JSP. Наприклад: import="java.util.*,java.sql.*" для вказання бібліотек, необхідних для сторінки JSP extends="uneClasseParent" — щоб сторінка JSP успадковувалася від іншого класу |
3.2.4. Неявні об’єкти JSP
У попередніх прикладах ми зустріли два неоголошені об’єкти: request та out. Це два з об’єктів, які автоматично визначаються у сервлеті, в який перетворюється сторінка JSP. Їх називають неявними або попередньо визначеними об’єктами. Існують й інші, але найчастіше їх використовують разом з об’єктом response:
об’єкт | значення |
об’єкт, через який здійснюється доступ до запиту веб-клієнта (getParameter, getParameterNames, getParameterValues) | |
об’єкт, за допомогою якого можна сформувати відповідь веб-сервера своєму клієнту. Дозволяє задати заголовки HTTP, які будуть надіслані веб-клієнту. | |
вихідний потік, що дозволяє нам надсилати код HTML клієнту (print, println) |
3.2.5. Перетворення сторінки JSP у сервлет
Повернемося до коду JSP з myRequestParamExample.jsp:
<%
// локальні змінні головної процедури
String title="Récupération des paramètres d'un formulaire";
String firstName = request.getParameter("firstname");
String lastName = request.getParameter("lastname");
%>
<!-- код HTML -->
<html>
<head>
<title><%= title %></title>
</head>
<body bgcolor="white">
<h3><%= title %></h3>
<%
if (firstName != null || lastName != null) {
out.println("firstname= " + firstName + "<br>");
out.println("lastname= " + lastName);
} else {
out.println("pas de paramètres");
}
%>
<P>
<form method="POST">
firstname= <input type="text" size="20" name="firstname">
<br>
lastname= <input type="text" size="20" name="lastname">
<br>
<input type="submit">
</form>
</body>
</html>
Коли браузер запитує цю сторінку JSP у сервера Tomcat, той перетворює її на сервлет. Якщо запитувана сторінка URL має вигляд
http://localhost:8080/examples/jsp/perso/intro/myRequestParamExample.jsp, Tomcat 4.x розмістить згенерований сервлет у каталозі <tomcat>\work\localhost\examples\jsp\perso\intro:

У цій назві міститься URL http://localhost:8080/examples/jsp/perso/intro/myRequestParamExample.jsp сторінки JSP. Вище ми бачимо, що маємо доступ до Java-коду сервлету, згенерованого для сторінки JSP. У нашому прикладі він виглядає так:
package org.apache.jsp;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
import org.apache.jasper.runtime.*;
public class myRequestParamExample$jsp extends HttpJspBase {
static {
}
public myRequestParamExample$jsp( ) {
}
private static boolean _jspx_inited = false;
public final void _jspx_init() throws org.apache.jasper.runtime.JspException {
}
public void _jspService(HttpServletRequest request, HttpServletResponse response)
throws java.io.IOException, ServletException {
JspFactory _jspxFactory = null;
PageContext pageContext = null;
HttpSession session = null;
ServletContext application = null;
ServletConfig config = null;
JspWriter out = null;
Object page = this;
String _value = null;
try {
if (_jspx_inited == false) {
synchronized (this) {
if (_jspx_inited == false) {
_jspx_init();
_jspx_inited = true;
}
}
}
_jspxFactory = JspFactory.getDefaultFactory();
response.setContentType("text/html;charset=ISO-8859-1");
pageContext = _jspxFactory.getPageContext(this, request, response,
"", true, 8192, true);
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();
// локальні змінні головної процедури
String title="Récupération des paramètres d'un formulaire";
String firstName = request.getParameter("firstname");
String lastName = request.getParameter("lastname");
out.write("\r\n\r\n<!-- code HTML -->\r\n<html>\r\n <head>\r\n <title>");
out.print( title );
out.write("</title>\r\n </head>\r\n <body bgcolor=\"white\">\r\n <h3>");
out.print( title );
out.write("</h3>\r\n ");
if (firstName != null || lastName != null) {
out.println("firstname= " + firstName + "<br>");
out.println("lastname= " + lastName);
} else {
out.println("pas de paramètres");
}
out.write("\r\n <P>\r\n <form method=\"POST\">\r\n firstname= <input type=\"text\" size=\"20\" name=\"firstname\">\r\n <br>\r\n lastname= <input type=\"text\" size=\"20\" name=\"lastname\">\r\n <br>\r\n <input type=\"submit\">\r\n </form>\r\n </body>\r\n</html>\r\n");
} catch (Throwable t) {
if (out != null && out.getBufferSize() != 0)
out.clearBuffer();
if (pageContext != null) pageContext.handlePageException(t);
} finally {
if (_jspxFactory != null) _jspxFactory.releasePageContext(pageContext);
}
}
}
Згенерований код є досить складним. Ми зупинимося лише на таких моментах:
- Головний метод сервлету виглядає так:
public void _jspService(HttpServletRequest request, HttpServletResponse response)
throws java.io.IOException, ServletException {
Саме цей метод запускається при запуску сервлета. Бачимо, що він отримує два параметри: запит request від клієнта та об’єкт response для формування відповіді веб-клієнту.
- У головному методі оголошується та ініціалізується об’єкт JspWriter out. Саме він дозволить надіслати код HTML клієнту за допомогою інструкцій out.print("codeHTML").
- Код Java
<%
// локальні змінні головної процедури
String title="Récupération des paramètres d'un formulaire";
String firstName = request.getParameter("firstname");
String lastName = request.getParameter("lastname");
%>
був повністю перенесений до головного методу _jspService сервлета. Те саме стосується всього коду, розміщеного в тегах <%… %>
- Код HTML зі сторінки JSP є об’єктом інструкцій out.print("codeHTML") або out.write(...). Наприклад
out.write("</title>\r\n </head>\r\n <body bgcolor=\"white\">\r\n <h3>");
- У цьому прикладі немає інших методів, окрім головного методу _jspService.
3.2.6. Методи та глобальні змінні сторінки JSP
Розглянемо таку сторінку JSP:
<%!
// попередній тег починає розділ глобальних змінних та методів
// ця частина буде перенесена без змін у сервлет
// глобальна змінна
String prenom="inconnu";
// метод
private String sonChien(){
return "milou";
}//sonChien
// ще один метод
private void afficheAmi(JspWriter out) throws Exception{
out.println("<p>Son ami s'appelle Haddock</p>");
}//afficheAmi
// кінець глобальної частини сервлета
%>
<%
// попередній тег вказує, що наступний код буде збережено
// у головному методі сервлета
// локальна змінна головного методу
String nom="tintin";
%>
<%-- код HTML --%>
<html>
<head>
<title>Page JSP</title>
</head>
<body>
<center>
<h2>Page JSP</h2>
<p>Son nom est <%= nom %></p>
<p>Son prénom est <%= prenom %></p>
<p>Son chien s'appelle <%= sonChien() %></p>
<%
// ім’я його друга
afficheAmi(out);
%>
</center>
</body>
</html>
Ця сторінка JSP генерує таку веб-сторінку:

Давайте розглянемо, як генеруються чотири наведені вище рядки:
<p>Son nom est <%= nom %></p>
<p>Son prénom est <%= prenom %></p>
<p>Son chien s'appelle <%= sonChien() %></p>
<%
// ім'я його друга
afficheAmi(out);
%>
Наведені вище рядки знаходяться в тезі <%..%> і, отже, стануть частиною головного методу _jspService сервлету, який буде згенеровано. Як вони отримують доступ до змінних nom, prenom та методів sonChien і afficheAmi?
є локальною змінною головного методу сторінки JSP і, отже, відома в ньому | |
є глобальною змінною сторінки JSP і, отже, відома в головному методі | |
є публічним методом сторінки JSP і, отже, доступний з головного методу | |
є публічним методом сторінки JSP і, отже, доступний з головного методу. Зверніть увагу, що об’єкт out передається як параметр методу. Це є обов’язковим. Адже об’єкт out оголошується та ініціалізується в головному методі сервлету і не є глобальною змінною. |
Тепер розглянемо код Java-сервлета, згенерованого на основі цієї сторінки JSP, після видалення непотрібного коду:
package org.apache.jsp;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
import org.apache.jasper.runtime.*;
public class tintin$jsp extends HttpJspBase {
// попередній тег починає розділ глобальних змінних та методів
// ця частина буде скопійована без змін у сервлет
// глобальна змінна
String prenom="inconnu";
// метод
private String sonChien(){
return "milou";
}//sonChien
// ще один метод
private void afficheAmi(JspWriter out) throws Exception{
out.println("<p>Son ami s'appelle Haddock</p>");
}//afficheAmi
// кінець глобальної частини сервлета
static {
}
public tintin$jsp( ) {
}
private static boolean _jspx_inited = false;
public final void _jspx_init() throws org.apache.jasper.runtime.JspException {
}
public void _jspService(HttpServletRequest request, HttpServletResponse response)
throws java.io.IOException, ServletException {
JspFactory _jspxFactory = null;
PageContext pageContext = null;
HttpSession session = null;
ServletContext application = null;
ServletConfig config = null;
JspWriter out = null;
Object page = this;
String _value = null;
try {
if (_jspx_inited == false) {
synchronized (this) {
if (_jspx_inited == false) {
_jspx_init();
_jspx_inited = true;
}
}
}
_jspxFactory = JspFactory.getDefaultFactory();
response.setContentType("text/html;charset=ISO-8859-1");
pageContext = _jspxFactory.getPageContext(this, request, response,
"", true, 8192, true);
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();
out.write(" \r\n\r\n");
// попередній тег вказує, що наступний код буде збережено
// у головному методі сервлета
// локальна змінна головного методу
String nom="tintin";
out.write("\r\n\r\n\r\n");
out.write("\r\n<html>\r\n <head>\r\n <title>Page JSP</title>\r\n </head>\r\n <body>\r\n <center>\r\n <h2>Page JSP</h2>\r\n <p>Son nom est ");
out.print( nom );
out.write("</p>\r\n <p>Son prénom est ");
out.print( prenom );
out.write("</p>\r\n <p>Son chien s'appelle ");
out.print( sonChien() );
out.write("</p>\r\n ");
// ім'я його друга
afficheAmi(out);
out.write("\r\n </center>\r\n </body>\r\n</html>\r\n");
} catch (Throwable t) {
if (out != null && out.getBufferSize() != 0)
out.clearBuffer();
if (pageContext != null) pageContext.handlePageException(t);
} finally {
if (_jspxFactory != null) _jspxFactory.releasePageContext(pageContext);
}
}
}
З наведеного вище видно, що Java-код, який містився між тегами JSP <%! .. %>, був перенесений повністю і не є частиною головного методу _jspService сервлета. Отже, змінні, оголошені в цій частині, є змінними екземпляру, тобто глобальними для методів, і саме тут можна визначати методи, відмінні від _jspService.
// ця частина буде використана без змін у сервлеті
// глобальна змінна
String prenom="inconnu";
// метод
private String sonChien(){
return "milou";
}//sonChien
// ще один метод
private void afficheAmi(JspWriter out) throws Exception{
out.println("<p>Son ami s'appelle Haddock</p>");
}//afficheAmi
// кінець глобальної частини сервлета
3.2.7. Розгортання та налагодження сторінок JSP на сервері Tomcat
Коли потрібно створити сторінку JSP і використовувати її на сервері Tomcat, постає питання, де саме розмістити цю сторінку в дереві каталогів сервера. Існують різні способи, до яких ми ще повернемося. Наразі найпростішим є розміщення сторінки JSP у папці в дереві каталогів <tomcat>\webapps\examples\jsp (Tomcat 4.x), де <tomcat> — це каталог інсталяції Tomcat. Отже, URL з попереднього прикладу знаходився за адресою http://localhost:8080/examples/jsp/perso/tintin/tintin.jsp. Це означає, що сторінка tintin.jsp знаходилася у папці <tomcat>\webapps\examples\jsp\perso\tintin.
Сторінка JSP перекладається у вихідний файл Java, який потім компілюється Tomcat, коли браузер запитує файл URL зі сторінки JSP. Можуть виникнути помилки компіляції. Tomcat 4.x повідомляє про них у своїй відповіді браузеру. Зокрема, він вказує рядки файлу .java, які містять помилки. Помилки можуть мати різні причини:
- код JSP на сторінці містить помилки (наприклад, помилки у тегах jsp, що використовуються)
- код Java, включений у сторінку JSP, містить помилки
Першу причину можна усунути, перевіривши код JSP на сторінці. Другу причину можна усунути, перевіривши код Java. Це можна зробити, безпосередньо скомпілювавши файл .java, згенерований для сторінки JSP, за допомогою такого інструменту, як JBuilder, який пропонує більш розширені можливості налагодження, ніж Tomcat.
3.2.8. Приклади
Повернемося до прикладу, який ми вже розглядали, із сервлетом, де користувач вибирає число зі списку, а сервер повідомляє йому, яке саме число він вибрав, одночасно повертаючи той самий список із вибраним елементом, який обрав користувач:

Для створення цієї сторінки ми взяли код сервлета та змінили його наступним чином:
- ми залишили без змін Java-код, який не генерував код HTML
- код Java, який генерував код HTML, було перетворено на комбінацію коду HTML та коду JSP
У результаті отримуємо таку сторінку JSP:
<%@ page import="java.sql.*, java.util.*" %>
<%!
// глобальні змінні додатка
// заголовок сторінки
private final String title="Génération d'un formulaire";
// база даних значень списку
private final String DSNValeurs="odbc-valeurs";
private final String admDbValeurs="admDbValeurs";
private final String mdpDbValeurs="mdpDbValeurs";
// значення списку
private String[] valeurs=null;
// повідомлення про помилку
private String msgErreur=null;
// ініціалізація сторінки JSP — виконується лише один раз
public void jspInit(){
// заповнює масив значень з бази даних ODBC
// з іменем DSN: DSNvaleurs
Connection connexion=null;
Statement st=null;
ResultSet rs=null;
try{
// підключення до бази даних ODBC
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
connexion=DriverManager.getConnection("jdbc:odbc:"+DSNValeurs,admDbValeurs,mdpDbValeurs);
// об'єкт Statement
st=connexion.createStatement();
// виконання запиту SELECT для отримання значень
rs=st.executeQuery("select valeur from Tvaleurs");
// значення отримано та поміщено в динамічний масив
ArrayList lstValeurs=new ArrayList();
while(rs.next()){
// значення записується у список
lstValeurs.add(rs.getString("valeur"));
}//while
// перетворення списку в масив
valeurs=new String[lstValeurs.size()];
for (int i=0;i<lstValeurs.size();i++){
valeurs[i]=(String)lstValeurs.get(i);
}
}catch(Exception ex){
// проблема
msgErreur=ex.getMessage();
}finally{
try{rs.close();}catch(Exception ex){}
try{st.close();}catch(Exception ex){}
try{connexion.close();}catch(Exception ex){}
}//try
}//ініціалізація
%>
<%
// код з _jspService, що виконується при кожному запиті клієнта
// чи сталася помилка під час ініціалізації сторінки JSP?
if(msgErreur!=null){
%>
<!-- код HTML -->
<html>
<head>
<title>Erreur</title>
</head>
<body>
<h3>Application indisponible (<%= msgErreur %></h3>
</body>
</html>
<%
// кінець jspService
return;
}//if
// отримуємо можливий вибір користувача
String choix=request.getParameter("cmbValeurs");
if(choix==null) choix="";
%>
<%-- помилки немає — код HTML звичайної сторінки --%>
<html>
<head>
<title><%= title %></title>
</head>
<body>
<h3>Choisissez une valeur</h3>
<form method="POST">
<select name="cmbValeurs">
<%
// динамічне відображення значень
String selected="";
for (int i=0;i<valeurs.length;i++){
if(valeurs[i].equals(choix)) selected="selected"; else selected="";
out.println("<option "+selected+">"+valeurs[i]+"</option>");
}//для
%>
</select>
<input type="submit" value="Envoyer">
</form>
<%
// чи було обрано якесь значення?
if(! choix.equals("")){
// відображається вибір користувача
%>
<hr>Vous avez choisi le nombre<h2><%= choix %></h2>
<%
}//if
%>
</body>
</html>
Звернімо увагу на наступні моменти:
- інструкції import сервлета були обгорнуті директивою <% page import="..." %>
- тег <%! ... %> охоплює глобальні змінні та методи Java додатка
- метод init сервлета, який виконується лише один раз під час завантаження сервлета, для сторінки JSP викликається як jspInit. Ці два методи виконують одну й ту саму роль. Тому тут повністю повторено код методу init сервлета.
- Змінні екземпляру сервлета, які повинні бути доступними в декількох методах, були скопійовані без змін. В основному це змінні title, valeurs та msgErreur, які потім використовуються в коді JSP.
- Теги <% ... %> охоплюють код Java, який буде включено до методу _jspService, що виконується під час надходження запиту від клієнта.
- Як і у випадку з сервлетом, метод _jspService спочатку перевірить значення змінної msgErreur, щоб визначити, чи потрібно генерувати сторінку помилки. Якщо помилка є, вона генерує сторінку помилки та зупиняється (return).
- Якщо помилки немає, вона генерує форму зі списком значень
- після цього вона перевіряє, чи вибрав користувач число; у цьому випадку вона відображає це число на згенерованій сторінці
Що ми виграли порівняно з сервлетом? Безсумнівно, кращий вигляд згенерованого коду HTML. Але все ще залишається багато коду Java, який «забруднює» цей вигляд. Пізніше ми розглянемо інший метод, який називається делегуванням, де ми зможемо розмістити основну частину коду Java у сервлеті, а сторінка JSP збереже лише код HTML та JSP. Таким чином ми чітко відокремлюємо частину обробки від частини візуалізації.
3.3. Розгортання веб-додатку на сервері Tomcat
Тепер ми розглянемо, як розгортати веб-додатки на Java за допомогою сервера Tomcat. Хоча наведена нижче інформація стосується саме цього сервера, розгортання веб-додатка на Java в іншому контейнері J2EE матиме особливості, схожі на ті, що будуть описані далі.
3.3.1. Конфігураційні файли server.xml та web.xml
До цього моменту для тестування наших сервлетів та сторінок JSP ми розміщували
- сервлети в папку <tomcat>\webapps\examples\WEB-INF\classes. Тоді до них можна було отримати доступ через URL http://localhost:8080/examples/servlet/nomServlet
- сторінки JSP у дереві <tomcat>\webapps\examples\jsp. Тоді до них можна було отримати доступ через URL http://localhost:8080/examples/jsp/nomPageJSP
Ми ніколи не пояснювали, чому це так. Налаштування сервера Tomcat здійснюється у текстовому файлі під назвою server.xml, який знаходиться у папці <tomcat>\conf:

Цей текстовий файл насправді є файлом XML (eXtended Markup Language). Документ XML — це текстовий документ, що містить теги, так само як і документ HTML. Однак, хоча теги мови HTML чітко визначені, теги мови XML чітко визначені не є. Отже, наступний документ є документом XML:
Документ XML — це просто «розмічений» документ, який відповідає певним правилам розмітки:
- текст, розмічений у формі <xx att1="val1" att2="val2" ....>текст</xx>
- тег може бути окремим і мати вигляд <xx att1="val1" att2="val2" ..../>
Поля «atti» називаються атрибутами тегу «xx», а поля «vali» — значеннями, пов’язаними з цими атрибутами. Деякі документи HTML не є валідними документами XML. Наприклад, тег HTML <br> не є валідним тегом XML. Щоб він був валідним, його слід записати як <br/>, щоб дотриматися правила, згідно з яким кожен тег XML повинен бути закритий. Був створений варіант HTML під назвою XHTML, щоб перетворити будь-який документ XHTML на дійсний документ XML. Деякі сучасні веб-браузери здатні відображати файли XML. Отже, якщо назвати personne.xml документ XML, наведений у прикладі вище, і переглянути його за допомогою IE6, ми отримаємо таке зображення:

IE6 розпізнає теги та виділяє їх кольором. Він також розпізнає структуру документа завдяки тегам. Отже, якщо назвати personne2.xml наступний документ:
і переглянемо його за допомогою IE6, отримаємо таке саме відображення:

IE6 правильно розпізнав структуру та зміст документа. Уся цінність документа XML полягає саме в цій властивості: легко відтворити структуру та зміст документа XML. Це здійснюється за допомогою програми, яка називається парсером XML. Документи XML поступово стають стандартом у обміні документами в Інтернеті. Розглянемо комп’ютер А, який повинен надіслати документ DOC на комп’ютер Б. Документ DOC формується на основі інформації, що міститься в базі даних DB-A. Машина B, у свою чергу, повинна зберегти документ DOC у базі даних DB-B. Обмін може відбуватися наступним чином:
- машина А отримує дані з бази DB-A і інкапсулює їх у текстовий документ XML
- документ XML надсилається до машини B через мережу
- машина B аналізує отриманий документ за допомогою парсера XML та витягує з нього як структуру, так і дані (як це зробив IE6 у нашому прикладі). Після цього вона може зберегти отримані дані в базі DB-B
Ми не будемо детальніше зупинятися на мові XML, яка заслуговує на окрему книгу.
Отже, у цьому випадку Tomcat налаштовується за допомогою файлу XML server.xml. Якщо переглянути його за допомогою IE6, ми отримаємо складний документ. Ми зупинимося лише на таких рядках:

Нас тут цікавить тег <Context ...>. Він слугує для визначення веб-додатків. Слід звернути увагу на два його атрибути:
- path — це назва веб-додатка
- docBase — це папка, у якій вона знаходиться. Тут вказано відносне ім’я: examples. Відносне до якої папки? Відповідь також міститься у файлі server.xml у наступному рядку:

У наведеному вище рядку визначено веб-сервер:
- name: ім’я веб-сервера
- appBase: корінь дерева документів, які він розповсюджує. Знову маємо відносне ім’я: webapps. Воно є відносним до каталогу встановлення сервера Tomcat <tomcat>. Отже, мова йде про папку <tomcat>\webapps.
Веб-додаток examples зберігає свої документи у папці examples (див. docBase вище). Ця назва є відносною до кореня веб-дерева сервера, c.a.d. <tomcat>\webapps. Отже, це папка <tomcat>\webapps\examples. Давайте детальніше розглянемо цю папку:

Тут ми бачимо папку WEB-INF\classes, у якій ми розмістили наші сервлети для їхнього тестування. Папка WEB-INF містить файл із назвою web.xml:

Цей файл слугує для налаштування веб-додатку examples. Наразі ми не будемо заглиблюватися в деталі цього файлу, оскільки він є надто складним. Ми просто зупинимося на таких кількох рядках:
<servlet>
<servlet-name>
servletToJsp
</servlet-name>
<servlet-class>
servletToJsp
</servlet-class>
</servlet>
Тег <servlet> використовується для визначення сервлета в рамках веб-додатку. Нагадаємо, що веб-додаток, про який йдеться, має назву examples. Тег servlet містить тут ще два теги:
- <servlet-name>servletToJsp</servlet-name>: визначає ім’я сервлета
- <servlet-name>servletToJsp</servlet-name>: визначає ім’я класу, який має виконуватися при запиті на сервлет. У цьому прикладі сервлет і його клас мають однакову назву. Це не є обов’язковим.
Як сервер Tomcat отримує запит на сервлет servletToJsp від браузера?
- браузер надсилає запит на сервер Tomcat щодо сервлета URL: http://localhost:8080/examples/servlet/servletToJsp
- Tomcat аналізує шлях до сервлету /examples/servlet/servletToJsp. Він інтерпретує першу частину шляху /examples як ім’я веб-додатку та шукає у своєму конфігураційному файлі server.xml, де зберігаються документи цього додатка. Як ми вже бачили раніше, це папка <tomcat>\webapps\examples.
- Tomcat використовує решту шляху до сервлету, щоб знайти його у веб-додатку examples. Цей шлях /servlet/servletToJsp вказує, що він повинен запустити сервлет із назвою servletToJsp. Потім Tomcat прочитає файл конфігурації web.xml для додатка examples, який він знайде в <tomcat>\webapps\examples\WEB-INF. У цьому файлі він виявить, що сервлет servletToJsp пов’язаний із класом Java servletToJsp (див. файл web.xml вище). Потім він шукатиме цей клас у папці WEB-INF\classes веб-додатку examples, c.a.d. у <tomcat>\webapps\examples\WEB-INF\classes і виконає його.

3.3.2. Приклад: розгортання веб-додатку «список»
Ми повернемося до сервлета, який ми вже розглядали раніше і який показував користувачеві список чисел, з яких він обирав одне. Потім сервлет підтверджував йому число, яке він обрав:

Як показано у полі Address браузера вище, файл класу сервлета називався gener3. Згідно з поясненнями, наведеними раніше:
- URL /examples/servlet/gener3 вказує, що це сервлет із назвою gener3 у веб-додатку examples
- у файлі web.xml додатка examples не знайдемо нічого, що вказувало б на сервлет gener3. Як же тоді Tomcat її знайшов? Переглянувши весь файл web.xml, я не можу відповісти з упевненістю... Питання залишається відкритим...
Ми вирішили розгорнути сервлет gener3.class під іменем lstValeurs у веб-додатку під назвою «liste», розташованому в папці E:\data\serge\Servlets\lstValeurs:

Ми розміщуємо файл gener3.class у папці WEB-INF\classes, зазначеній вище:

Налаштовуємо веб-додаток «liste», додавши у файл server.xml такі рядки над тими, що визначають веб-додаток manager:
<!-- Особисте: lstValeurs -->
<Context path="/liste" docBase="e:/data/serge/servlets/lstValeurs" />
<!-- Контекст Tomcat Manager -->
<Context path="/manager" docBase="manager" debug="0" privileged="true" />
<!-- Контекст прикладів Tomcat -->
<Context path="/examples" docBase="examples" debug="0" reloadable="true" crossContext="true">
........
Рядок, що визначає список додатків, вказує, що він знаходиться в папці e:/data/serge/servlets/lstValeurs. Тепер нам потрібно визначити файл web.xml цього додатка. Цей файл визначатиме єдиний сервлет додатка:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<servlet>
<servlet-name>lstValeurs</servlet-name>
<servlet-class>gener3</servlet-class>
</servlet>
</web-app>
Вищезазначений файл вказує, що сервлет із назвою lstValeurs пов'язаний із файлом класу gener3.class. Цей файл web.xml потрібно створити та зберегти в папці WEB-INF додатка «liste»:

На знімку екрана вище показано папку src, у яку було поміщено вихідний файл gener3.java. Ця папка може не існувати. У даній демонстрації вона не відіграє жодної ролі. Ми готові до тестування:
- зупиніть і запустіть Tomcat, щоб він перечитав свій файл конфігурації server.xml. Тут ми працюємо в Windows. У Unix можна змусити Tomcat перечитати файл конфігурації, не зупиняючи його.
- У браузері введіть адресу URL http://localhost:8080/liste/servlet/lstValeurs

Видно, що попередній URL містить ключове слово servlet, як і всі URL сервлетів, що використовувалися до цього часу. Цього можна уникнути, пов’язавши у файлі web.xml додатка «liste» сервлет lstValeurs із шаблоном URL (url-pattern):
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<servlet>
<servlet-name>lstValeurs</servlet-name>
<servlet-class>gener3</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>lstValeurs</servlet-name>
<url-pattern>/valeurs</url-pattern>
</servlet-mapping>
</web-app>
У тезі <servlet-mapping> ми пов'язуємо шлях /valeurs із сервлетом lstValeurs, визначеним у попередніх рядках. Зберігаємо новий файл web.xml і викликаємо URL та http://localhost:8080/liste/valeurs:

3.3.3. Розгортання публічних сторінок веб-додатку
Ми щойно розглянули розгортання веб-додатка, що складається з одного сервлета. Веб-додаток може містити безліч компонентів: сервлети, сторінки JSP, файли HTML, Java-аплети тощо. Де розміщуються ці елементи додатка? Якщо <application> — це папка веб-додатка, визначена атрибутом docBase додатка у файлі конфігурації Tomcat server.xml, то ми бачили, що сервлети розміщуються в <application>\WEB-INF\classes. Інші елементи додатка можна розміщувати будь-де в дереві папок <application>, за винятком папки WEB-INF. Розглянемо вже вивчену програму JSP listvaleurs.jsp:

Ця сторінка JSP була збережена в папці <tomcat>\webapps\examples\jsp\perso\listvaleurs. Ця сторінка може бути компонентом раніше розгорнутого додатка «liste». Помістимо файл listvaleurs.jsp безпосередньо в папку цього додатка:

Нагадаємо конфігурацію додатка liste у файлі server.xml:
Будь-який файл URL, що починається зі шляху /liste, вважається частиною програми liste і буде шукатися у вказаній папці. Відкриємо файл URL http://localhost:8080/liste/listvaleurs.jsp у браузері:

Ми дійсно отримали очікувану сторінку JSP.
3.3.4. Параметри ініціалізації сервлета
Ми бачили, що сервлет налаштовується за допомогою файлу <application>\WEB-INF\web.xml, де <application> — це папка веб-додатку, до якого він належить. У цей файл можна включити параметри ініціалізації сервлета. Повернемося до нашого сервлета lstValeurs із веб-додатку liste, конфігураційний файл якого мав такий вигляд:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<servlet>
<servlet-name>lstValeurs</servlet-name>
<servlet-class>gener3</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>lstValeurs</servlet-name>
<url-pattern>/valeurs</url-pattern>
</servlet-mapping>
</web-app>
Клас, пов'язаний із сервлетом, — це клас gener3. У його вихідному коді міститься визначення кількох констант:
public class gener3 extends HttpServlet{
// заголовок сторінки
private final String title="Génération d'un formulaire";
// база даних значень списку
private final String DSNValeurs="odbc-valeurs";
private final String admDbValeurs="admDbValeurs";
private final String mdpDbValeurs="mdpDbValeurs";
Нагадаємо значення чотирьох констант, визначених вище:
заголовок документа HTML, згенерованого сервлетом | |
назва DSN бази даних ODBC, з якої сервлет отримує дані | |
ім’я користувача, який має право на читання даних з попередньої бази | |
його пароль |
Якщо адміністратор бази даних DSNValeurs змінить пароль користувача admDbValeurs, вихідний код сервлету потрібно буде змінити та перекомпілювати. Це не дуже зручно. Файл конфігурації сервлета web.xml пропонує нам альтернативу, дозволяючи визначати параметри ініціалізації сервлета за допомогою тегу <init-param>:
дозволяє задати ім'я параметра | |
визначає значення, пов'язане з попереднім параметром |
Сервлет має доступ до своїх параметрів ініціалізації за допомогою таких методів:
метод класу Servlet, від якого походить клас HttpServlet, що використовується для веб-програмування. Повертає об’єкт ServletConfig, що надає доступ до параметрів конфігурації сервлету. | |
метод класу ServletConfig, який повертає значення параметра ініціалізації «paramètre» |
Ми налаштовуємо додаток liste за допомогою такого нового файлу web.xml:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<servlet>
<servlet-name>lstValeurs</servlet-name>
<servlet-class>gener3</servlet-class>
</servlet>
<servlet>
<servlet-name>lstValeurs2</servlet-name>
<servlet-class>gener5</servlet-class>
<init-param>
<param-name>title</param-name>
<param-value>Génération d'un formulaire</param-value>
</init-param>
<init-param>
<param-name>DSNValeurs</param-name>
<param-value>odbc-valeurs</param-value>
</init-param>
<init-param>
<param-name>admDbValeurs</param-name>
<param-value>admDbValeurs</param-value>
</init-param>
<init-param>
<param-name>mdpDbValeurs</param-name>
<param-value>mdpDbValeurs</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>lstValeurs</servlet-name>
<url-pattern>/valeurs</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>lstValeurs2</servlet-name>
<url-pattern>/valeurs2</url-pattern>
</servlet-mapping>
</web-app>
У додатку liste ми визначаємо другий сервлет під назвою lstValeurs2, пов'язаний із файлом класу gener5. Останній розміщено в <application>\WEB-INF\classes:

Сервлет lstValeurs2 має чотири параметри ініціалізації: title, DSNValeurs, admDbValeurs, mdpDbValeurs. Крім того, для сервлета за допомогою тегу <servlet-mapping> було визначено псевдонім /valeurs2. Отже, до сервлету lstValeurs2 у додатку liste можна буде отримати доступ через URL та http://localhost:8080/liste/valeurs2.
Вихідний код сервлета було змінено наступним чином для отримання параметрів ініціалізації сервлета:
public class gener5 extends HttpServlet{
// заголовок сторінки
private String title=null;
// база даних значень списку
private String DSNValeurs=null;
private String admDbValeurs=null;
private String mdpDbValeurs=null;
...............
// ініціалізація сервлету
public void init(){
// отримано параметри ініціалізації сервлета
ServletConfig config=getServletConfig();
title=config.getInitParameter("title");
DSNValeurs=config.getInitParameter("DSNValeurs");
admDbValeurs=config.getInitParameter("admDbValeurs");
mdpDbValeurs=config.getInitParameter("mdpDbValeurs");
//чи отримано всі параметри?
if(title==null || DSNValeurs==null || admDbValeurs==null
|| mdpDbValeurs==null){
msgErreur="Configuration incorrecte";
return;
}
// заповнення масиву значень з бази даних ODBC
// з назвою DSN: DSNvaleurs
...............
Щоб протестувати сервлет, потрібно перезапустити Tomcat, щоб він врахував новий файл конфігурації web.xml додатка liste. У браузері потрібно завантажити URL із сервлета http://localhost:8080/liste/valeurs2:

Якщо у файлі web.xml відсутній хоча б один із параметрів ініціалізації, необхідних для сервлета, відображається така сторінка:

3.3.5. Параметри ініціалізації веб-додатку
У наведеному вище прикладі лише сервлет lstValeurs2 має доступ до параметрів title, DSNValeurs, admDbValeurs, mdpDbValeurs. Можна уявити, що інший сервлет того самого додатка liste потребує даних із тієї самої бази даних, яку використовує сервлет lstValeurs2. У такому разі потрібно перезадати параметри DSNValeurs, admDbValeurs, mdpDbValeurs у розділі конфігурації файлу web.xml нового сервлета. Іншим рішенням є визначення параметрів, спільних для декількох сервлетів, на рівні додатка, а не на рівні окремих сервлетів. Новий файл web.xml додатка матиме такий вигляд:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<context-param>
<param-name>DSNValeurs</param-name>
<param-value>odbc-valeurs</param-value>
</context-param>
<context-param>
<param-name>admDbValeurs</param-name>
<param-value>admDbValeurs</param-value>
</context-param>
<context-param>
<param-name>mdpDbValeurs</param-name>
<param-value>mdpDbValeurs</param-value>
</context-param>
<servlet>
<servlet-name>lstValeurs</servlet-name>
<servlet-class>gener3</servlet-class>
</servlet>
<servlet>
<servlet-name>lstValeurs3</servlet-name>
<servlet-class>gener6</servlet-class>
<init-param>
<param-name>title</param-name>
<param-value>Génération d'un formulaire</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>lstValeurs</servlet-name>
<url-pattern>/valeurs</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>lstValeurs3</servlet-name>
<url-pattern>/valeurs3</url-pattern>
</servlet-mapping>
</web-app>
Новий сервлет має назву lstValeurs3, пов’язаний із файлом класу gener6 і йому було присвоєно псевдонім /valeurs3 (servlet-mapping). Параметр title — єдиний параметр, який було збережено у визначенні сервлета. Решту параметрів було розміщено в конфігурації додатка у тегах <context-param>. Цей тег використовується для визначення інформації, що стосується саме додатка, а не конкретного сервлета чи сторінки JSP. Як Java-сервлет отримує доступ до цих параметрів, які часто називають параметрами контексту? Методи, доступні для отримання інформації про контекст, дуже схожі на ті, що використовуються для отримання параметрів ініціалізації, характерних для конкретного сервлета:
метод класу Servlet, від якого походить клас HttpServlet, що використовується для веб-програмування. Повертає об’єкт ServletContext, що надає доступ до параметрів конфігурації додатка | |
метод класу ServletContext, який повертає значення параметра ініціалізації «paramètre» |
Клас gener6.java вносить лише такі зміни до Java-коду gener5.java, що використовувався раніше:
// отримуємо параметри ініціалізації сервлету
ServletConfig config=getServletConfig();
title=config.getInitParameter("title");
ServletContext context=getServletContext();
DSNValeurs=context.getInitParameter("DSNValeurs");
admDbValeurs=context.getInitParameter("admDbValeurs");
mdpDbValeurs=context.getInitParameter("mdpDbValeurs");
//чи всі параметри отримано?
if(title==null || DSNValeurs==null || admDbValeurs==null
|| mdpDbValeurs==null){
msgErreur="Configuration incorrecte";
return;
}
// заповнюємо масив значень з бази даних ODBC
// з назвою DSN: DSNvaleurs
...............
Параметр title, властивий сервлету, отримується через об’єкт ServletConfig. Три інші параметри, визначені на рівні додатка, отримуються через об’єкт ServletContext. Ми компілюємо цей клас і розміщуємо його, як і інші, у папці <application>\WEB-INF\classes:

Перезапускаємо Tomcat, щоб він врахував новий файл web.xml додатка, і запитуємо URL та http://localhost:8080/liste/valeurs3:

3.3.6. Параметри ініціалізації сторінки JSP
Ми розглянули, як задати параметри ініціалізації для сервлета або веб-додатка. Чи можна зробити те саме для сторінки JSP? Повернімося до початку коду вже розглянутої сторінки listvaleurs.jsp:
<%@ page import="java.sql.*, java.util.*" %>
<%!
// глобальні змінні додатка
// заголовок сторінки
private final String title="Génération d'un formulaire";
// база даних значень списку
private final String DSNValeurs="odbc-valeurs";
private final String admDbValeurs="admDbValeurs";
private final String mdpDbValeurs="mdpDbValeurs";
.........
Чотири константи title, DSNValeurs, admDbValeurs та mdpDbValeurs визначені у файлі web.xml програми. Константи DSNValeurs, admDbValeurs та mdpDbValeurs тепер визначені на рівні додатка, тому можна припустити, що сторінка JSP, яка є частиною цього додатка, матиме до них доступ. Так і є. Ми знаємо, що сторінка JSP буде перетворена на сервлет. Цей сервлет матиме доступ до контексту через метод getServletContext(). Більш складним є випадок з константою title. Адже ми визначили її на рівні сервлету, а не на рівні додатка, таким чином:
<servlet>
<servlet-name>lstValeurs3</servlet-name>
<servlet-class>gener6</servlet-class>
<init-param>
<param-name>title</param-name>
<param-value>Génération d'un formulaire</param-value>
</init-param>
</servlet>
Для сторінки JSP попередній синтаксис більше не підходить, оскільки поняття файлу класу більше не існує. Однак синтаксис конфігурації сторінки JSP дуже схожий на синтаксис сервлета. Він виглядає так:
<servlet>
<servlet-name>JSPlstValeurs</servlet-name>
<jsp-file>/listvaleurs2.jsp</jsp-file>
...
</servlet>
Фактично, сторінка JSP розглядається як сервлет, якому присвоюється ім’я (servlet-name). Замість того, щоб пов’язувати з цим сервлетом файл класу, до нього прив’язується вихідний файл сторінки JSP, що має бути виконаний (jsp-file). Таким чином, кілька попередніх рядків визначають сервлет із назвою JSPlstvaleurs, пов’язаний зі сторінкою JSP /listvaleurs2.jsp. Шлях /listvaleurs2.jsp вимірюється відносно кореневого каталогу додатка. Отже, у випадку нашого додатка liste файл listvaleurs2.jsp знаходитиметься в папці docBase (див. server.xml) додатка liste:

Конфігурація сторінки JSP у файлі web.xml програми буде такою:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<context-param>
<param-name>DSNValeurs</param-name>
<param-value>odbc-valeurs</param-value>
</context-param>
<context-param>
<param-name>admDbValeurs</param-name>
<param-value>admDbValeurs</param-value>
</context-param>
<context-param>
<param-name>mdpDbValeurs</param-name>
<param-value>mdpDbValeurs</param-value>
</context-param>
.......
<servlet>
<servlet-name>JSPlstValeurs</servlet-name>
<jsp-file>/listvaleurs2.jsp</jsp-file>
<init-param>
<param-name>JSPtitle</param-name>
<param-value>Génération d'un formulaire</param-value>
</init-param>
</servlet>
..........
<servlet-mapping>
<servlet-name>JSPlstValeurs</servlet-name>
<url-pattern>/jspvaleurs</url-pattern>
</servlet-mapping>
<servlet-mapping>
........
</web-app>
Сторінка JSP listvaleurs2.jsp розміщена в кореневому каталозі додатка liste і пов’язана з іменем сервлета JSPlstValeurs (servlet-name), який, у свою чергу, пов’язаний з псевдонімом /jspvaleurs (servlet-mapping). Таким чином, наша сторінка JSP буде доступна через URL http://localhost:8080/liste/jspvaleurs.
Початкова сторінка JSP (listvaleurs.jsp) змінюється на listvaleurs2.jsp і отримує свої чотири параметри ініціалізації в методі jspInit():
<%!
// глобальні змінні додатка
// заголовок сторінки
private String title=null;
// база даних значень списку
private String DSNValeurs=null;
private String admDbValeurs=null;
private String mdpDbValeurs=null;
// значення списку
private String[] valeurs=null;
// повідомлення про помилку
private String msgErreur=null;
// ініціалізація сторінки JSP — виконується лише один раз
public void jspInit(){
// отримано параметри ініціалізації сервлету
ServletConfig config=getServletConfig();
title=config.getInitParameter("JSPtitle");
ServletContext context=getServletContext();
DSNValeurs=context.getInitParameter("DSNValeurs");
admDbValeurs=context.getInitParameter("admDbValeurs");
mdpDbValeurs=context.getInitParameter("mdpDbValeurs");
//чи всі параметри отримано?
if(title==null || DSNValeurs==null || admDbValeurs==null
|| mdpDbValeurs==null){
msgErreur="Configuration incorrecte";
return;
}
// заповнює масив значень з бази даних ODBC
// з назвою DSN: DSNvaleurs
..............
Сторінка JSP отримує свої параметри ініціалізації так само, як і сервлети. Попередній файл зберігається в кореневому каталозі веб-додатку liste:

Сервер Tomcat перезапускається, щоб змусити його перечитати новий файл конфігурації web.xml додатка. Після цього можна звернутися до URL http://localhost:8080/liste/jspvaleurs:

3.3.7. Взаємодія сервлетів та сторінок JSP у веб-додатку
Коли клієнт надсилає запит до веб-сервера, відповідь може формуватися кількома сервлетами та сторінками JSP. Дотепер відповідь формувалася лише одним сервлетом або однією сторінкою JSP. Ми побачили, що сторінка JSP забезпечує кращу читабельність структури згенерованого документа HTML. Однак вона, як правило, також містить багато коду Java. Ситуацію можна покращити, розмістивши
- в один або кілька сервлетів Java-код, який не генерує код HTML у відповіді
- на сторінки JSP — код, що генерує різні документи HTML, які надсилаються клієнту у відповідь
Таким чином можна сподіватися на покращення розділення коду Java та коду HTML. Ми застосуємо цю нову структуру до нашого додатка liste: сервлет Java lstValeurs4 буде відповідати за зчитування значень із бази даних під час запуску, а потім — за аналіз запитів клієнтів. Залежно від результату аналізу, запит клієнта буде перенаправлено на сторінку помилки erreur.jsp або на сторінку з переліком чисел liste.jsp. Отже, додаток liste складатиметься з сервлета та двох сторінок JSP.
Як сервлет може передати запит, отриманий від клієнта, іншому сервлету або сторінці JSP? Ми використаємо такі методи:
метод класу ServletContext, який повертає об’єкт RequestDispatcher. Параметр url — це ім’я URL, якому потрібно передати запит клієнта. Така передача запиту можлива лише в межах одного додатка. Тому параметр url є відносним шляхом до веб-дерева цього додатка. | |
метод інтерфейсу RequestDispatcher, який передає попередньому методу URL запит request від клієнта та об’єкт response, який має бути використаний для формування відповіді. | |
коли сервлет або сторінка JSP передає запит іншому сервлету або сторінці JSP, їй, як правило, потрібно передати цьому сервлету або сторінці не лише запит клієнта, а й іншу інформацію, отриману в результаті власної обробки запиту. Метод setAttribute класу ServletRequest дозволяє додавати атрибути до об’єкта request клієнта у формі, що нагадує словник пар (атрибут, значення), де attribut — це ім’я атрибута, а valeur — будь-який об’єкт, що представляє його значення. | |
дозволяє отримати значення атрибутів запиту. Цей метод буде використовуватися сервлетом або сторінкою JSP, якій було передано запит, для отримання інформації, що була додана до нього. |
Сервлет, відповідальний за обробку форми, буде налаштований у файлі web.xml наступним чином:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<context-param>
<param-name>DSNValeurs</param-name>
<param-value>odbc-valeurs</param-value>
</context-param>
<context-param>
<param-name>admDbValeurs</param-name>
<param-value>admDbValeurs</param-value>
</context-param>
<context-param>
<param-name>mdpDbValeurs</param-name>
<param-value>mdpDbValeurs</param-value>
</context-param>
............
<servlet>
<servlet-name>lstValeurs4</servlet-name>
<servlet-class>gener7</servlet-class>
<init-param>
<param-name>title</param-name>
<param-value>Génération d'un formulaire</param-value>
</init-param>
<init-param>
<param-name>JSPerreur</param-name>
<param-value>/erreur.jsp</param-value>
</init-param>
<init-param>
<param-name>JSPliste</param-name>
<param-value>/liste.jsp</param-value>
</init-param>
<init-param>
<param-name>URLservlet</param-name>
<param-value>/liste/valeurs4</param-value>
</init-param>
</servlet>
...........
<servlet-mapping>
<servlet-name>lstValeurs4</servlet-name>
<url-pattern>/valeurs4</url-pattern>
</servlet-mapping>
.......
</web-app>
Сервлет lstValeurs4 матиме чотири власні параметри ініціалізації:
заголовок документа HTML, що має бути згенерований | |
URL сторінки помилки JSP | |
URL зі сторінки JSP, що містить список чисел | |
URL, пов'язана з атрибутом action форми, представленої на сторінці JSPliste. Ця URL буде належати сервлету lstValeurs4 |
Сервлет матиме псевдонім /valeurs4 (servlet-mapping) і, отже, буде доступний через URL http://localhost:8080/liste/valeurs4. Вона пов’язана з файлом класу gener7.java, повний вихідний код якого наведено нижче:
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.sql.*;
import java.util.*;
public class gener7 extends HttpServlet{
// заголовок сторінки
private String title=null;
// база даних значень списку
private String DSNValeurs=null;
private String admDbValeurs=null;
private String mdpDbValeurs=null;
// сторінки відображення JSP
private String JSPerreur=null;
private String JSPliste=null;
// сервлет URL
private String URLservlet=null;
// значення списку
private String[] valeurs=null;
// повідомлення про помилку
private String msgErreur=null;
// -----------------------------------------------------------------
// GET
public void doGet(HttpServletRequest request,HttpServletResponse response)
throws IOException, ServletException{
// в атрибути запиту вноситься msgErreur,title
request.setAttribute("msgErreur",msgErreur);
request.setAttribute("title",title);
request.setAttribute("URLservlet",URLservlet);
// Чи сталася помилка під час завантаження сервлету?
if(msgErreur!=null){
// передаємо управління на сторінку помилки JSP
getServletContext().getRequestDispatcher(JSPerreur).forward(request,response);
// кінець
return;
}
// помилки не сталося
// список значень додається до атрибутів запиту
request.setAttribute("valeurs",valeurs);
// отримуємо можливий вибір користувача
String choix=request.getParameter("cmbValeurs");
if(choix==null) choix="";
request.setAttribute("choix",choix);
// передаємо управління сторінці JSP для відображення списку
getServletContext().getRequestDispatcher(JSPliste).forward(request,response);
// кінець
return;
}//GET
// -----------------------------------------------------------------
// POST
public void doPost(HttpServletRequest request,HttpServletResponse response)
throws IOException, ServletException{
// перенаправляє на GET
doGet(request,response);
}//POST
// -----------------------------------------------------------------
// ініціалізація сервлета
public void init(){
// отримано параметри ініціалізації сервлета
ServletConfig config=getServletConfig();
title=config.getInitParameter("title");
JSPerreur=config.getInitParameter("JSPerreur");
JSPliste=config.getInitParameter("JSPliste");
URLservlet=config.getInitParameter("URLservlet");
ServletContext context=getServletContext();
DSNValeurs=context.getInitParameter("DSNValeurs");
admDbValeurs=context.getInitParameter("admDbValeurs");
mdpDbValeurs=context.getInitParameter("mdpDbValeurs");
//чи всі параметри отримано?
if(title==null || DSNValeurs==null || admDbValeurs==null
|| mdpDbValeurs==null || JSPerreur==null || JSPliste==null || URLservlet==null){
msgErreur="Configuration incorrecte";
return;
}
// заповнення масиву значень з бази даних ODBC
// з назвою DSN: DSNvaleurs
Connection connexion=null;
Statement st=null;
ResultSet rs=null;
try{
// підключення до бази даних ODBC
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
connexion=DriverManager.getConnection("jdbc:odbc:"+DSNValeurs,admDbValeurs,mdpDbValeurs);
// об'єкт Statement
st=connexion.createStatement();
// виконання запиту SELECT для отримання значень
rs=st.executeQuery("select valeur from Tvaleurs");
// значення отримано та поміщено в динамічний масив
ArrayList lstValeurs=new ArrayList();
while(rs.next()){
// значення записується у список
lstValeurs.add(rs.getString("valeur"));
}//while
// перетворення списку в масив
valeurs=new String[lstValeurs.size()];
for (int i=0;i<lstValeurs.size();i++){
valeurs[i]=(String)lstValeurs.get(i);
}
}catch(Exception ex){
// проблема
msgErreur=ex.getMessage();
}finally{
try{rs.close();}catch(Exception ex){}
try{st.close();}catch(Exception ex){}
try{connexion.close();}catch(Exception ex){}
}//try
}//ініціалізація
}//клас
Нововведенням у цьому класі є перенаправлення запиту клієнта на сторінку JSPerreur у разі помилки, а в іншому випадку — на сторінку JSPliste. Клас сам не формує відповідь. Цим займаються сторінки JSP, JSPerreur та JSPliste. Раніше сервлет додавав атрибути (setAttribute) до запиту клієнта:
- повідомлення про помилку msgErreur у разі помилки на сторінці JSPerreur
- значення (valeurs)), що має відображатися, значення (choix), обране користувачем, заголовок (title) форми, URL (URLservlet) атрибута action форми для сторінки JSPliste
Цей клас компілюється та додається до класів додатка:

Сторінка JSP, що відображає повідомлення про помилку, налаштована наступним чином:
<servlet>
<servlet-name>JSPerreur</servlet-name>
<jsp-file>/erreur.jsp</jsp-file>
<init-param>
<param-name>mainServlet</param-name>
<param-value>/valeurs4</param-value>
</init-param>
</servlet>
.........
<servlet-mapping>
<servlet-name>JSPerreur</servlet-name>
<url-pattern>/JSPerreur</url-pattern>
</servlet-mapping>
Файл JSP, пов'язаний із сторінкою помилки, називається erreur.jsp і знаходиться в кореневому каталозі додатка:

Він має псевдонім /JSPerreur, що робить його доступним через URL http://localhost:8080/liste/JSPerreur. Вона має параметр ініціалізації з назвою mainServlet, значенням якого є псевдонім головного сервлету, описаного вище. Слід зауважити, що цей псевдонім є відносним до кореневого каталогу додатка liste,; інакше ми б мали /liste/valeurs4.. Код сторінки erreur.jsp є таким:
<%
// код з _jspService
// отримуємо параметр ініціалізації mainServlet
String servletListValeurs=config.getInitParameter("mainServlet");
// отримуємо атрибут msgErreur
String msgErreur=(String)request.getAttribute("msgErreur");
// атрибут є дійсним?
if(msgErreur!=null){
%>
<!-- код HTML -->
<html>
<head>
<title>Erreur</title>
</head>
<body>
<h3>Application indisponible (<%= msgErreur %>)</h3>
</body>
</html>
<%
} else { // атрибут msgErreur недійсний — повернення до головного сервлету
%>
<jsp:forward page="<%= servletListValeurs %>" />
<%
}
%>
Ця сторінка зазвичай має викликатися попереднім сервлетом, який повинен передати їй атрибут msgErreur. Однак ніщо не заважає викликати її безпосередньо, якщо відомий її URL. Також, якщо виявляється, що атрибут msgErreur відсутній, запит передається до головного сервлету. Тут використовується тег, характерний для сторінок JSP, синтаксис якого такий:
де URL — це URL сервлета, якому передається запит клієнта. Якщо атрибут msgErreur присутній, відображається сторінка помилки.
Сторінка JSP, що відображає список чисел, налаштована наступним чином:
<servlet>
<servlet-name>JSPliste</servlet-name>
<jsp-file>/liste.jsp</jsp-file>
<init-param>
<param-name>mainServlet</param-name>
<param-value>/valeurs4</param-value>
</init-param>
.........
<servlet-mapping>
<servlet-name>JSPliste</servlet-name>
<url-pattern>/JSPliste</url-pattern>
</servlet-mapping>
Файл JSP, пов'язаний із сторінкою помилки, називається liste.jsp і знаходиться в кореневому каталозі додатка:

Сервлет має псевдонім /JSPliste, завдяки чому до нього можна отримати доступ через URL http://localhost:8080/liste/JSPliste. Вона має параметр ініціалізації з назвою mainServlet, значенням якого є псевдонім головного сервлета. Код сторінки liste.jsp такий:
<%-- сторінка відображення списку значень --%>
<%
// код jspService
// отримуємо параметр ініціалізації
String servletListValeurs=config.getInitParameter("mainServlet");
// отримуємо атрибути запиту, що надходить від головного сервлету
String title=(String) request.getAttribute("title");
String[] valeurs=(String[]) request.getAttribute("valeurs");
String choix=(String) request.getAttribute("choix");
String URLservlet=(String) request.getAttribute("URLservlet");
// атрибути є допустимими?
if(title==null || valeurs==null || choix==null){
// є недійсний атрибут — передаємо управління сервлету
%>
<jsp:forward page="<%= servletListValeurs %>" />
<%
}//if
%>
<%-- код HTML --%>
<html>
<head>
<title><%= title %></title>
</head>
<body>
<h3>Choisissez une valeur</h3>
<form method="POST" action="<%= URLservlet %>">
<select name="cmbValeurs">
<%
// динамічне відображення значень
String selected="";
for (int i=0;i<valeurs.length;i++){
if(valeurs[i].equals(choix)) selected="selected"; else selected="";
out.println("<option "+selected+">"+valeurs[i]+"</option>");
}//for
%>
</select>
<input type="submit" value="Envoyer">
</form>
<%
// чи було обрано значення?
if(! choix.equals("")){
// відображається вибір користувача
%>
<hr>Vous avez choisi le nombre<h2><%= choix %></h2>
<%
}//if
%>
</body>
</html>
Ця сторінка працює так само, як сторінка erreur.jsp. Зазвичай її має викликати сервлет /liste/valeurs4, який передає їй атрибути title, valeurs та choix. Якщо один із цих параметрів відсутній, управління передається сервлету URLservlet (/liste/valeurs4). Якщо всі параметри присутні, відображається список чисел, а також число, обране користувачем, якщо він його обрав.
Якщо викликати сервлет URL з головного сервлету, отримаємо такий результат:

з таким вихідним кодом (View/Source):
<html>
<head>
<title>Génération d'un formulaire</title>
</head>
<body>
<h3>Choisissez une valeur</h3>
<form method="POST" action="/liste/valeurs4">
<select name="cmbValeurs">
<option >0</option>
<option >1</option>
<option >2</option>
<option >3</option>
<option >4</option>
<option >6</option>
<option >5</option>
<option >7</option>
<option >8</option>
<option >9</option>
</select>
<input type="submit" value="Envoyer">
</form>
</body>
</html>
Цей документ HTMl було створено на сторінці JSP liste.jsp. Бачимо, що атрибути title, valeurs, URLservlet були успішно вилучені.
Підсумовуючи співпрацю сервлетів та сторінок JSP, можна зауважити, що сторінки JSP тут дуже короткі й не містять Java-коду, який не бере безпосередньої участі у формуванні відповіді HTML. Таким чином, структура згенерованих документів стає більш наочною.
3.4. Життєвий цикл сервлетів та сторінок JSP
3.4.1. Життєвий цикл
Тут ми розглядаємо життєвий цикл сервлетів. Життєвий цикл сторінок JSP випливає з нього. Розглянемо сервлет, який викликається вперше. У цьому випадку веб-сервер створює екземпляр класу та завантажує його в пам’ять. Потім він обробляє запит. Після цього сервлет не вивантажується з пам’яті. Вона залишається там для обслуговування інших запитів з метою оптимізації часу відгуку сервера. Її буде вивантажено, коли мине достатньо тривалий проміжок часу, протягом якого вона не обслуговувала нових запитів. Цей час, як правило, можна налаштувати в параметрах веб-сервера.
Перебуваючи в пам’яті, сервлет може обробляти кілька запитів одночасно. Веб-сервер створює по одному потоку на кожен запит, і всі вони використовують один і той самий екземпляр сервлета:
![]() |
Усі наведені вище потоки спільно використовують змінні екземпляра сервлета. Може виникнути необхідність синхронізації потоків, щоб уникнути пошкодження даних сервлета. Ми повернемося до цього пізніше.
Під час завантаження сервлета виконується певний метод сервлета:
Для сторінки JSP це метод
public void jspInit(){
}
. Ось приклад сторінки JSP, яка використовує метод jspInit:
<html>
<head>
<title>Compteur synchronisé</title>
</head>
<body>
Compteur= <%= getCompteur() %>
</body>
</html>
<%!
// глобальні змінні та методи сторінки JSP
// змінна екземпляра
int compteur;
// метод для інкременту лічильника
public int getCompteur(){
// збільшуємо лічильник
int myCompteur=compteur;
myCompteur++;
compteur=myCompteur;
// повертається
return compteur;
}
// метод, що виконується під час початкового завантаження сторінки
public void jspInit(){
// ініціалізація лічильника
compteur=100;
}
%>
Попередня сторінка JSP ініціалізує лічильник у jspInit, присвоюючи йому значення 100. Кожен наступний запит до сервлету збільшує значення цього лічильника, а потім відображає його:
Перший раз:

Вдруге:

З наведеного вище добре видно, що між двома запитами сервлет не був вивантажений, інакше під час другого запиту лічильник мав би значення 101. Коли сервлет вивантажується, метод
виконується, якщо він існує. Для сторінок JSP це метод
public void jspDestroy(){
}
У цих методах можна, наприклад, закрити з’єднання з базами даних, які були відкриті у відповідних методах init.
3.4.2. Синхронізація запитів до сервлету
Повернімося до попередньої сторінки JSP, яка інкрементує лічильник і повертає його значення веб-клієнту. Припустимо, що надходять 2 одночасні запити. Тоді для їх виконання створюються два потоки, які використовуватимуть один і той самий екземпляр сервлету, а отже, і один і той самий лічильник. Нагадаємо код, який інкрементує лічильник:
public int getCompteur(){
// збільшуємо лічильник
int myCompteur=compteur;
myCompteur++;
compteur=myCompteur;
// повертаємо його
return compteur;
}
Збільшення лічильника було навмисно написано неточно. Припустимо, що виконання двох потоків відбувається наступним чином:
![]() |
- у момент часу T1 виконується потік TH1. Він зчитує значення лічильника (=145) у myCompteur, після чого його переривають і він втрачає доступ до процесора. Отже, він не встиг збільшити значення myCompteur і скопіювати нове значення в compteur.
- У момент часу T2 виконується потік TH2. Він зчитує лічильник (=145) з myCompteur, після чого його переривають і він втрачає доступ до процесора. Слід зауважити, що обидва потоки мають різні змінні myCompteur. Вони спільно використовують лише змінні екземпляру — ті, що є глобальними для методів.
- У момент часу T3 потік TH1 знову отримує контроль і завершується. Отже, він повертає 146 своєму клієнту.
- У момент часу T4 потік TH2 знову отримує контроль і завершується. Він також повертає своєму клієнту 146, хоча мав би повернути 147.
Тут маємо проблему синхронізації потоків. Коли TH1 хоче збільшити лічильник, потрібно запобігти тому, щоб будь-який інший потік зробив те саме. Щоб проілюструвати цю проблему, перепишемо сторінку JSP наступним чином:
<html>
<head>
<title>Compteur synchronisé</title>
</head>
<body>
Compteur= <%= getCompteur() %>
</body>
</html>
<%!
// глобальні змінні та методи сторінки JSP
// змінна екземпляра
int compteur;
// метод для інкременту лічильника
public int getCompteur(){
// зчитуємо лічильник
int myCompteur=compteur;
// зупинка на 10 секунд
try{
Thread.sleep(10000);
}catch (Exception ignored){}
// збільшуємо лічильник
compteur=myCompteur+1;
// повертаємо його
return compteur;
}
// метод, що виконується під час початкового завантаження сторінки
public void jspInit(){
// ініціалізація лічильника
compteur=100;
}
%>
Тут ми змусили потік зупинитися через 10 секунд після зчитування лічильника. Таким чином, він повинен втратити доступ до процесора, і інший потік зможе у свою чергу зчитати лічильник, який не був інкрементований. Коли ми робимо запити за допомогою браузера, ми не бачимо жодної різниці, окрім 10-секундного очікування перед отриманням результату.

Тепер, якщо ми відкриємо два вікна браузера і зробимо два запити з достатньо коротким проміжком часу:


Ми отримуємо те саме значення лічильника. Ми можемо краще проілюструвати проблему за допомогою програмного клієнта, а не ручного, яким є браузер. Далі наведено Perl-клієнт, який викликається наступним чином:
програма URL N
де
URL — це URL сервлета підрахунку
N — кількість запитів, які потрібно надіслати до цього сервлету
Ось результати, отримані для 5 запитів, які добре ілюструють проблему неправильної синхронізації потоків: усі вони отримують однакове значення лічильника.
DOS>java clientCompteurJSP http://localhost:8080/examples/jsp/perso/compteur/compteur2.jsp 5
Compteur=121
Compteur=121
Compteur=121
Compteur=121
Compteur=121
Код клієнта Java виглядає наступним чином.
import java.net.*;
import java.util.regex.*;
import java.io.*;
public class clientCompteurJSP {
public static void main(String[] params){
// дані
String syntaxe="Syntaxe : pg URL nbAppels";
// перевірка параметрів
if(params.length!=2){
System.err.println(syntaxe);
System.exit(1);
}//if
// URL
URL urlCompteur=null;
try{
urlCompteur=new URL(params[0]);
String query=urlCompteur.getQuery();
if(query!=null) throw new Exception();
}catch (Exception ex){
System.err.println(syntaxe);
System.err.println("URL ["+params[0]+" incorrecte");
System.exit(2);
}//try-catch
// кількість викликів
int nbAppels=0;
try{
nbAppels=Integer.parseInt(params[1]);
if(nbAppels<=0) throw new Exception();
}catch(Exception ex){
System.err.println(syntaxe);
System.err.println("Nombre d'appels ["+params[1]+" incorrect");
System.exit(3);
}//try-catch
// параметри правильні — можна встановлювати з’єднання з URL
try{
getCompteurs(urlCompteur,nbAppels);
}catch(Exception ex){
System.err.println(syntaxe);
System.err.println("L'erreur suivante s'est produite : "+ex.getMessage());
System.exit(4);
}//try-catch
}//main
private static void getCompteurs (URL urlCompteur, int nbAppels)
throws Exception {
// виконує nbAppels на URL urlCompteur
// щоразу відображає значення лічильника, повернуте веб-сервером
// з urlCompteur вилучаються дані, необхідні для підключення до податкового сервера
String path=urlCompteur.getPath();
if(path.equals("")) path="/";
String host=urlCompteur.getHost();
int port=urlCompteur.getPort();
if(port==-1) port=urlCompteur.getDefaultPort();
// виконуються виклики до URL
Socket[] clients=new Socket[nbAppels];
for(int i=0;i<nbAppels;i++){
// здійснюється підключення до сервера
clients[i]=new Socket(host,port);
// створюється потік запису на сервер
PrintWriter OUT=new PrintWriter(clients[i].getOutputStream(),true);
// запитується URL — надсилання заголовків HTTP
OUT.println("GET " + path + " HTTP/1.1");
OUT.println("Host: " + host + ":" + port);
OUT.println("Connection: close");
OUT.println("");
}//для
// локальні дані
String réponse=null; // відповідь сервера
// шаблон, який шукали у відповіді HTML від сервера
Pattern modèleCompteur=Pattern.compile("^\\s*Compteur= (\\d+)");
// шаблон правильної відповіді
Pattern réponseOK=Pattern.compile("^.*? 200 OK");
// результат порівняння з шаблоном
Matcher résultat=null;
for(int i=0;i<nbAppels;i++){
// кожен клієнт зчитує відповідь, яку йому надсилає сервер
// створюються потоки вводу-виводу клієнта TCP
BufferedReader IN=new BufferedReader(new InputStreamReader(clients[i].getInputStream()));
// зчитується перший рядок відповіді
réponse=IN.readLine();
// порівнюється рядок HTTP із шаблоном правильної відповіді
résultat=réponseOK.matcher(réponse);
if(! résultat.find()){
// виникає проблема з URL
throw new Exception("Client n° " + i + " - Le serveur a répondu : URL ["+ urlCompteur + "] inconnue");
}//якщо
// зчитуємо відповідь до кінця заголовків
while((réponse=IN.readLine())!=null && ! réponse.equals("")){
}//поки
// заголовки закінчилися HTTP — переходимо до коду HTML
// для отримання значення лічильника
boolean compteurTrouvé=false;
while((réponse=IN.readLine())!=null){
// порівнюємо рядок із шаблоном лічильника
if(! compteurTrouvé){
résultat=modèleCompteur.matcher(réponse);
if(résultat.find()){
// лічильник знайдено
System.out.println("Compteur="+résultat.group(1));
compteurTrouvé=true;
}//if
}//якщо
}//while
// завершено
clients[i].close();
}//for
}//getCompteurs
}//клас
Роз'яснимо попередній код:
- програма приймає два параметри:
- URL сторінки JSP лічильника
- кількість клієнтів, яких потрібно створити для цього URL
- отже, програма спочатку перевіряє правильність параметрів: чи їх дійсно два, чи перший синтаксично відповідає URL, а другий — цілому числу >0. Щоб перевірити, чи URL є синтаксично правильним, використовується клас URL та його конструктор URL (String), який створює об’єкт URL на основі такого рядка символів: http://istia.univ-angers.fr. Якщо рядок не є синтаксично правильним URL, генерується виняток. Це дозволяє нам перевірити правильність першого параметра.
- Після перевірки параметрів управління передається процедурі getCompteurs. Ця процедура створить клієнтів nbAppels, які всі підключаться одночасно (або майже одночасно) до URL urlCompteur.
- Порт та машина, до яких повинні підключитися клієнти, беруться з URL та urlCompteur: [URL].getHost() дозволяє отримати ім’я комп’ютера, а [URL].getPort() — порт.
- Перший цикл дозволяє кожному клієнту:
- підключитися до веб-сервера
- запитати у нього URL urlCompteur
У цьому циклі клієнт не чекає на відповідь сервера. Адже ми хочемо змусити сервер отримувати запити майже одночасно.
- другий цикл дозволяє кожному клієнту отримати та обробити відповідь, яку надсилає йому сервер. Обробка полягає у пошуку в відповіді рядка, що містить значення лічильника, та його відображенні.
Щоб вирішити проблему, на яку було вказано раніше (один і той самий лічильник надсилається п’ятьом клієнтам), нам потрібно синхронізувати потоки служби підрахунку за одним об’єктом перед входом у критичну ділянку читання та оновлення лічильника. Нова сторінка JSP виглядає так:
<html>
<head>
<title>Compteur synchronisé</title>
</head>
<body>
Compteur= <%= getCompteur() %>
</body>
</html>
<%!
// глобальні змінні та методи сторінки JSP
// змінні екземпляра
int compteur;
Object verrou=new Object();
// метод для інкременту лічильника
public int getCompteur(){
// синхронізується критичний фрагмент
synchronized(verrou){
// зчитування лічильника
int myCompteur=compteur;
// зупинка на 10 секунд
try{
Thread.sleep(10000);
}catch (Exception ignored){}
// збільшуємо лічильник
compteur=myCompteur+1;
}//синхронізовано
// повертається
return compteur;
}//getCompteur
// метод, що виконується під час початкового завантаження сторінки
public void jspInit(){
// ініціалізація лічильника
compteur=100;
}
%>
Під час виконання отримуємо такі результати:
dos>c:\perl\bin\perl.exe client2.pl http://localhost:8080/examples/jsp/perso/compteur/compteur3.jsp 5
Compteur= 104
Compteur= 106
Compteur= 105
Compteur= 107
Compteur= 108
У документації зазначено, що веб-сервер іноді може створювати кілька екземплярів одного й того самого сервлету. У цьому випадку попередня синхронізація більше не працює, оскільки змінна verrou є локальною для одного екземпляра і, отже, невідома іншим екземплярам. Те саме стосується змінної compteur. Щоб зробити їх глобальними для всіх екземплярів, слід написати:
// змінна класу
static int compteur;
static Object verrou=new Object();
Решта коду залишається без змін.





