Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:JAVA. Серверные приложения
.pdf
Часть 3 Серверные Java-компоненты Servlet 151
<init-parameter value="jdbc:db2:sample" name="URL"/>
<init-parameter value="itso" name="userID"/>
</servlet>
Как упоминалось выше, конфигурационный файл может быть как
простым текстовым файлом, содержащим строки параметр=значение,
так и XML-файлом с указанными парами и расширенными тегами. Вопрос об использовании XML-файла как конфигурационного отпадает
сам собой по нескольким причинам. Первая — переносимость XML, вторая — стандартизированный синтаксис: не надо для каждого нового
сервера писать новый конфигурационный файл, достаточно просто скопировать необходимый документ.
Имя XML-документа состоит из имени servletName, и после специаль-
ного расширения указывается ключевое слово servlet. Документ формата
XML обязательно должен содержать имя.class файла, содержащего servlet, описание servlet, инициализационные параметры для servlet, также
может иметь список адресов, вызываемых JSP-страниц. Этот список содержит default page, error page и одну или более целевых загружаемых
страниц, если их имя указано в запросе. Конфигурационный XML-документ располагается в той же директории, где находится класс servlet.
При поступлении запроса Application Server определяет нужный
servlet и получает параметры.servlet файла и XML-документа.
WebSphere Application Studio поставляется с несколькими помощни-
ками (wizards), один из которых генерирует servlets на основе клиентских данных. Именно этот помощник создает XML-документ, являющийся конфигурационным файлом (.servlet) для каждого созданного
servlet. Также можно создать конфигурационный файл для servlet
вручную, используя обычный текстовой редактор, но лучше, если это
будет специальный XML-редактор.
<?xml version="1.0"?>
<servlet>
<code>SimpleServlet</code>
<description>Shows how to use SimpleServlet class</description>
<init-parameter name="name1" value="value2"/>
<page-list>
<default-page>
<uri>/index.jsp</uri>
</default-page>
<error-page>
<uri>/error.jsp</uri>
</error-page>
<page>
<uri>/TemplateA.jsp</uri>
<page-name>page1</page-name>
</page>
<page>
<uri>templateB.jsp</uri>
<page-name>page2</page-name>
</page>
</page-list>
</servlet>

152 Часть 3 Серверные Java-компоненты Servlet
Тег <servlet> — основной элемент XML-документа, описывающего
конфигурационный файл. Каждый XML конфигурационный файл должен быть хорошо оформленным (well-formed) XMLдокументом. Тег <servlet> указывает объект, для которого осуществляется конфигурация.
Тег <code> содержит имя класса servlet (без расширения.class), указывается отдельно для каждого servlet, если таковых несколько в jar-архиве.
Тег <description> описание servlet, описывает, что делает данный
servlet.
Группа <init-parameter> содержит пару имя=значение для каждого
инициализационного параметра. При наличии нескольких параметров
для каждого параметра должен существовать собственный тег с парой
имя=значение.
Тег <page-list> представляет список JSP-страниц, вызываемых servlet
по получению запроса. Данный тег может иметь несколько наследников:
Тег <default-page>: содержит адрес страницы, если в запросе не
указана конкретная страница.
Тег <error-page>: указывает на адрес страницы, содержащей сведения об ошибках, возникающих в момент исполнения servlet.
Тег <page>: указывает адрес любых других страниц.
Первым методом, который использует Web-сервер при обращении к
servlet, является метод init().
Существует множество возможностей использования метода init(),
главное, что выполняется метод init() один раз при загрузке servlet и
работает на всем протяжении жизни servlet. Еще один маленький пример, использующий преимущества метода init():
•
private String filename;
•
public void init(ServletConfig config) throws ServletException {
•
super.init(config);
•
filename = config.getInitParameter("filename");
•
if (filename != null) {
•
try {
•
ObjectInputStream in = new ObjectInputStream(new FileInputStream(filename+ ".ser"));}
•
catch (Exception e) { e.printStacTrace(); }}
Сначала создается глобальная переменная (поле) строкового типа filename, первая строка, во второй строке вызывается метод init() с параметром config класса ServletConfig. Третья строка является стандартной операцией вызова метода суперкласса. Четвертая строка содержит
оператор присваивания полю filename значения, полученного из конфигурационного файла, либо XML-документа либо текстового файла properties, значение параметра с именем filename. И если значение параметра не пустое, строка пять, создается входящий поток с указанным
именем filename и добавленным расширением.ser, строка семь, куда
позже могут быть помещены какие-либо параметры, например переменные сессии. Восьмая строка представляет стандартный обработчик
исключительной ситуации.

Часть 3 Серверные Java-компоненты Servlet 153
Инициализация может не пройти успешно по каким-либо причинам,
тогда вызывается исключительная ситуация класса UnavailableException и сервер выгружает из памяти экземпляр servlet.
В момент краха системы некоторые используемые данные могут
быть потеряны. Для предотвращения потери данных можно критические данные записывать во внешний инициализационный файл. В момент восстановления системы, при вызове сервером метода init(), будут
восстановлены сохраненные данные. Для сохранения данных в Java используется так называемая серилизация данных. Тогда инициализационный метод можно переписать следующим образом:
private int iCount;
•
private SaveStats stats;
•
private String filename;
•
public void init(ServletConfig config) throws ServletException {
•
super.init(config);
•
iCount = 0;
•
filename = config.getInitParameter("filename");
•
stats = new SaveStats();
•
if (filename != null) {
•
try { ObjectInputStream in = new ObjectInputStream(new FileIn-
•
putStream(filename + ".ser"));
stats = (SaveServletStats) in.readObject();
•
•
in.close(); }
•
catch (Exception e) { e.printStac Trace(); }}}
•
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
•
res.setContentT pe("TEXT/HTML");
•
PrintWriter out = res.getWriter();
•
iCount++;
•
stats.iCount++;
•
synchronized (this) {
•
if (filename != null) {
•
ObjectOutputStream outstats = new ObjectOutputStream(new FileOutputStream(filename + ".ser"));
•
outstats.writeObject(stats);
•
System.out.println("Saving stats file: " + stats.iCount);
•
outstats.close(); } }
В первой строке нового примера определяется критическая переменная iCount, значение к которой и необходимо будет сохранить в случае
наступления краха системы. Вторая основная строка является тем серилизованным объектом, хранящим критическую перемененную. Класс
SaveStats, содержащий критическую переменную iCount, должен наследовать интерфейс Serializable
import java.io.*;
public class SaveStat implements Serializable {
public int iCount = 0;}

154 Часть 3 Серверные Java-компоненты Servlet
Третья строка содержит имя внешнего файла, являющегося классом
SaveState. Далее в строке перегружается стандартный метод init() с
единственным аргументом класса ServletConfig. Детально тело метода
описано выше. Здесь надо обратить внимание на восьмую строку, содержащую операторы создания нового сериализованного объекта SaveStats.
При условии существования в системе файла, содержащего класс SaveStats. Во входящий поток помещается содержимое файла, строка десять.
Главное — не забыть прописать в инициализационном файле servlet тег
filename c атрибутом имеющего значение имени файла сериализации.
Именно на данную операцию указывает строка семь. В одиннадцатой
строке полученное значение передается переменной stats. Дальнейшие
операторы будут пока опущены, они описаны ниже. Ключевой в основном
методе doGet() на данный момент является двадцать первая строка, описывающая сохранение полученного значения во внешнем файле.
Все-таки могут возникнуть ситуации, в которых не рекомендуется
использовать метод init(), тогда можно получать параметры, используя
другие методы.
Параметры из внешних источников можно получить методом getServletContext() класса ServletConfig. Получение всех инициализационных
параметров с их значениями возможно посредством метода getInitParameterNames() при использовании коллекции Enumeration. Получить
имя исполняемого servlet можно, используя getServletName(), определяющего имя servlet в виде строки.
Для получения более подробной информации о выполняемом servlet
необходимо организовать доступ к объекту SevletContext. С помощью
методов данного объекта можно получить исчерпывающую информацию о системном окружении servlet.
22.2. Обработка клиентского запроса servlet, метод service()
Но главное, для чего создан servlet, — это обработка запроса. После
инициализации, servlet готов выполнять непосредственную задачу.
Каждый запрос является объектом класса ServletRequest, а каждый ответ, в свою очередь, является объектом класса ServletResponse, описанных в Java Servlet API.
Клиент может использовать два способа отправки запроса для обработки servlet. Может посылаться непосредственно параметр, встроенный в URL, может использоваться входящий поток данных (InputStream).
•
public void service(ServletInputStream in, ServletOutputSteram res)
throws @Bullet = ServletExeption
•
{ res.setContentType("text/html");
•
ServletOutputSteram out=res.getOutputStream();
•
out.println("<jsp:include file="index.html">");
•
Out.close();}
В данном примере вместо стандартных объектов используется менее
распространенная конструкция с использованием потоков как переда-

Часть 3 Серверные Java-компоненты Servlet 155
ваемых параметров. Использование потоков оправданно при передаче
бинарных данных, например изображений, Gif- и JPEG-файлов. В случае с текстовыми данными легче воспользоваться стандартными способами. Во второй строке определяется вид выходящего документа,
text/html, что не является хорошим примером при работе с потоками.
Как указывалось выше, лучше использовать другую конструкцию, например image\\/gif. Хотя использование параметра text/html не является ошибкой. В третьей строке создается объект response, которому
присваивается значение выходящего потока OutputStream. Затем в выходной поток передается значение "jsp:include" и, наконец, освобождаются занятые ресурсы, последняя строка.
В servlet, работающих с HTTP-протоколом, определены другие методы обработки клиентского запроса. Для версии 1.0 HTTP-протокола существуют два метода doGet() и doPost(). Для версии 1.1 HTTP-протокола список методов расширен еще несколькими полезными методами:
doGet(), doPost(), doPut(), doDelete(), doOptions(),doTrace().
Эти методы, так же как метод service(), предназначены для обработки клиентского запроса, но отличаются входящими параметрами и способами обработки передачи HTTP-протоколом.
22.3. Окончание жизни servlet, метод destroy()
Для освобождения занятых ресурсов и выполнения обязательных
«предсмертных» пожеланий servlet вызывает метод destroy(): метод
destroy вызывается при выгрузке Web-сервером исполняемого servlet.
Данный метод является аналогичным методу finally() т. е. в нем определяются действия, необходимые перед завершением действия servlet.
После вызова метода destroy() и выполнения всех операторов, находящихся в нем, наступает вторая фаза освобождения ресурсов при помощи «сборщика мусора», вызываемого JVM.
Поскольку основным является метод service(), то остальные методы
«жизненного цикла» нет необходимости прописывать в тело создаваемого
servlet.
Свойства среды выполнения servlet можно получить, используя методы интерфейса ServletContext. В этом интерфейсе имеется несколько
полезных методов, определяющих внешнюю среду выполнения, такие,
как версии используемого servlet API, установка атрибутов, запись в
log-файлы, получение инициализационных параметров servlet, имени
автора и т. д.
Как уже неоднократно упоминалось, servlet является многопотоковым приложением. Каждый запрос обрабатывается в собственном потоке. Но иногда может потребоваться создавать servlet, чей метод service()
работает только в одном потоке. В принципе совместно используемые
данные можно синхронизировать, для этого используется ключевое слово synchronized, обеспечивающее синхронизацию полей или методов.
Однако для servlet существует более простое решение, а именно реализация интерфейса SingleThreadModel в создаваемом классе servlet.

156 Часть 3 Серверные Java-компоненты Servlet
public class SingleThreadServlet extends HttpServlet implements SingleThre-
adModel {
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException { //Java êîä } }
Все основные методы по обработке клиентского запроса должны полностью закончиться, прежде чем будет вызван метод уничтожения destroy(). Перед окончанием работы servlet необходимо убедиться, что все
потоки, формирующие ответ, полностью завершены. Для управления
длительностью сессии существуют методы класса HttpSession. Методы
setMaxinactiveInterval() и isNew() позволяют в какой-то мере контролировать пользовательскую сессию.
Глава 23. Использование класса ServletContext для
получения более обширной информации об исполняемом
окружении servlet
Класс ServletContext содержит методы, используемые servlet, для
получения свойств системного окружения, получения MIME типов приложения или записи в Log-файл. Обычно один объект класса ServletContext описывает одно Web-приложение на одной JVM. В данном случае Web-приложение представляет коллекцию servlet в одном архивном файле war-архиве или в одной директории виртуального хоста.
В случае распределенного Web-приложения каждый объект класса
ServletContext использует параметры одной JVM. В этой ситуации объект не может использоваться для получения глобальной информации.
Объект Servlet Context является частью специального объекта ServletConfig, создаваемого при инициализации servlet.
В ранних версиях servlet API, объект ServletContext был единым
для всех исполняемых servlet в данный момент времени. И начиная с
версии 2.1 servlet API, объект имеет дополнительные методы и работает
на уровне отдельно взятого Web-приложения.
Многие servlet — engine, использующие спецификацию 2.1, используют один объект ServletContext для множества загруженных servlet
на Web-сервере. Предвидя данную ситуацию, фирма SUN дополнила
класс ServletContext дополнительными методами идентификации выполняемого servlet. Теперь каждому исполняемому servlet можно присвоить определенные атрибуты. Существуют три метода в классе ServletContext по работе с атрибутами servlet: getAttribute(), setAttribute()
и removeAttribute(). К тому же каждый servlet engine может самостоятельно конфигурировать исполняемый servlet, а значит, и объект класса ServletContext. Данная функция полезна при инициализации servlet.
Servlet получает информацию из ServletContext при помощи объекта
ServletConfig. При использовании протокола HTTP используется метод
getServletContext() для получения системной информации. Чтобы воспользоваться методами, предоставляемыми классом ServletContext, код

Часть 3 Серверные Java-компоненты Servlet 157
должен содержать или проинициализированный объект ServletConfig,
или методы вызова этого класса. Полезным также является метод log(),
предназначенный для записи в системный файл или базу данных eventLog каких-либо сообщений. Пара методов getMajorVersion() и getMinorVersion() указывают числовое значение используемого servlet API. Для
версии API 2.2 значение будет равно 2. Метод getServerInfo(),определяет версию API servlet и имя сервера, на котором выполняется servlet.
Получение MIME — типа приложения — осуществляется при помощи метода getMimeType(), возвращающего строковое значение, или значение null, если тип не определен. Наиболее часто в Web-приложениях
используются строки"text/html" и image/gif", указывающие соответствующие типы исследуемых файлов приложений. Методы getContext(),
getResource(), getResourceAsStream(), getRealPath() необходимы для получения информации как о самом servlet, так и удаленном ресурсе. Ресурсы могут находиться как на локальной машине, так и на удаленной.
Глава 24. Переадресация из servlet — использование
объекта порожденного от интерфейса RequestDispatcher
При создании Web-приложения иногда возникает ситуация, когда
клиентский запрос должен или может быть обработан другим servlet.
Это касается не только запроса, но и ответа. Интерфейс RequestDispatcher представляет механизм распределения клиентских запросов и ответов по различным ресурсам. Такими ресурсами могут быть JSP-страница, другой servlet или CGI-скрипт.
В указанном интерфейсе содержится пара методов для осуществления
перенаправления клиентского запроса в другие источники обработки информации. Перенаправление полезно в различных ситуациях, не предусмотренных стандартным решением получения и обработки информации.
Часто перенаправление запроса используется при возникновении
исключительной ситуации, например отсутствии или перемещении запрашиваемого ресурса. Перенаправление осуществляется двумя способами: клиентский запрос направляется в другой источник, часть ответа
принимается из другого источника.
Пересылка запроса из одного servlet в другие ресурсы, такие, как
servlet, JSP, HTML, для генерации ответа использует метод forward() с
двумя параметрами: первый является наследником класса ServletRequest, другой параметр наследован от ServletResponse. Перенаправление в
динамические источники servlet и JSP необходимо для дополнительной
обработки клиентского запроса. Перенаправление в HTML-файл чаще
всего служит для информирования клиента о каком-либо событии. Метод forwad() должен использоваться до отправки клиенту ответа. Статическая HTML-страница не может обрабатывать клиентские запросы.
Ниже приведен пример использования методов диспетчеризации клиентских запросов.
•
public class HTMLFormHandlerDispatcher extends HttpServlet {
•
public void service(HttpServletRequest req, HttpServletResponse res)

158 Часть 3 Серверные Java-компоненты Servlet
throws ServletException, IOException {
•
RequestDispatcher rd = getServletContext().getRequestDispatcher
•
("/servlet/com.rrlabs.DispatcherForward");
•
rd.forward(req, res); }}
•
Как можно увидеть из примера, объект rd класса RequestDispatcher
инициализируется в основном методе servlet. Строка четыре создает
объект диспетчера.
Другой задачей перенаправления является добавление к отправляемому ответу, какой-нибудь добавочной информации, содержащейся в
другом источнике.
Для получения информации из другого источника в интерфейсе RequestDispatcher описан метод include(). Данный метод имеет те же параметры, что и метод forward(). Как обычно, первый описывает запрос
(request), а второй описывает ответ (response). Данные, полученные из
другого источника, не удаляются, а дублируются в сформированном ответе. Servlet, производящий вызов из другого источника, не вправе изменять status code или устанавливать заголовки, в любом случае любые
изменения, вносимые servlet в импортированную информацию, будут
проигнорированы.
public void service(HttpServletRequest req, HttpServletResponse res)
•
throws ServletException, IOException {
•
• res.setContentT pe("text/html");
•
PrintWriter out = res.getWriter();
•
out.println("<HTML><BODY>Start of INCLUDED request");
•
out.flush();
•
RequestDispatcher rd = getServletContext().getRequestDispatcher
•
("/servlet/com.rrlabs.DispInclude");
•
rd.include(req, res);
•
out.println("End </BODY></HTML>"); }
В данном примере показано использование метода include(). В отличие от вышеприведенного примера перед вызовом непосредственно метода include(), строка семь, данные должны быть сброшены на диск при
помощи метода flush(), строка шесть. Оба метода могут вызывать исключительные ситуации ServletException и java.io.IOException. Объект
ServletException определяет общее исключение, servlet может генерировать вызов объекта класса ServletException, когда сталкивается с невозможностью обработки внутреннего оператора. Стандартная ошибка
операций ввода-вывода обрабатывается объектом класса java.io.IOException.
Ресурсы, используемые при перенаправлении, должны быть доступны через специальный объект ServletContext, вызываемый servlet. Доступ к объектам, наследующим методы интерфейса RequestDispatcher,
организуется при помощи метода getRequestDispatcher() класса ServletContext. С помощью метода getNamedDispatcher() класса servletContext
можно получить имя ресурса, на который был перенаправлен запрос.

Часть 3 Серверные Java-компоненты Servlet 159
Глава 25. Основная структура Servlet
Структура servlet для различных протоколов передачи различна, но
в большинстве случаев имеется ряд идентичных методов последовательности их вызова. Кроме методов жизненного цикла и диспетчеризации запросов, к основным методам servlet можно отнести объекты специальных классов по работе с клиентским запросом. Такими объектами
являются request и response, опять же для каждого протокола передачи, эти объекты имеют собственные классы родителей.
25.1 Использование основных методов servlet: service(), doGet(),
doPost() и обработка данных
Описание других расширенных методов doPut(), doDelete(), doOptions(), doTrace(), doHead() будет опущено, поскольку в новых версиях
JSDK 2.1 и 2.2 обработка этих методов заменена вызовом стандартных
методов servlet.
Основные методы, используемые в servlet, имеют названия, схожие с
именами методов, используемых при передаче данных. Кроме метода
service(), все остальные методы servlet олицетворяют используемые
http-методы.
Метод doGet() используется при передаче HTTP-протоколом данных
посредством метода GET. Передача, осуществляемая при помощи
POST, обрабатывается методом doPost(), прописанным в теле servlet.
Метод POST используется для передачи клиентских запросов на сервер. DELETE и его servlet аналог doDelete() предназначены для удаления запроса с Web-страницы. Методы OPTIONS и соответственно doOptions() описывают параметры, используемые клиентской сессией в
момент отправления запроса и получения ответа. Трассировочную и
диагностическую информацию можно получить, используя пару методов, новой версий HTTP-протокола TRACE и doTrace(). Метод HEAD
очень похож на метод GET, но в отличие от оного работает только со
специальной информацией — заголовками. С помощью метода HEAD
можно получить дату изменения документа, размер пересылаемого документа, тип метода, с помощью которого осуществлялась передача документа, тип сервера, позволяющий пользователю использовать параметры непосредственно сервера.
Методы doPut(), doDelete(), doOptions(), doTrace(), doHead() используются только с протоколом HTTP версии 1.1. Методы doGet(), doPost()
рабтают с двумя версиями HTTP-протокола, с версией 1.1 и более ранней 1.0. Метод service() — независимый от протокола метод, это сердце
любого servlet. Остальные методы являются заменителями метода service() для каждой конкретной ситуации.
Каждый раз, когда клиент обращается к Web-ресурсу, используя servlet, Web-сервер обращается к одному из основных методов servlet, service() или его HTTP аналогов. При этом JVM создает еще один поток, и
затем выполняется метод service(). Для действительной работы метода
service() его необходимо переопределить. Пока метод service() выполня-

160 Часть 3 Серверные Java-компоненты Servlet
ется внутри отдельного потока, можно спокойно выполнять другие задачи. Все методы по обработке клиентских запросов, реализуемые в servlet,
обязательно используют два передаваемых параметра, ассоциированных
с запросом (request) и ответом (response). Методы service(), doGet() и doPost() создаются непосредственно для выполнения основных задач.
Тело основных методов servlet может содержать любую информацию, необходимую для обработки клиентского запроса. Чаще всего
именно в теле основных методов описываются операторы обработки и
формирования клиентской информации.
import javax.servlet.*;
•
import javax.servlet.http.*;
•
import java.util.Enumeration;
•
protected void doGet(HttpServletRequest request, HttpServletRes-
•
ponse response)
throws ServletException, IOException {
•
response.setContentType("text/html");
•
PrintWriter out = response.getWriter();
•
out.println("<HTML><HEAD><TITLE>");
•
out.println("Request info");
•
out.println("</TITLE></HEAD>");
•
out.println("<BODY><H1>Request info</H1><PRE>");
•
out.println("Parameters:");
•
Enumeration paramNames = request.getParameterNames();
•
•
while (paramNames.hasMoreElements()) {
•
String name = (String) paramNames.nextElement();
•
String[] values = request.getParameterValues(name);
•
out.println(" " + name + ":");
•
for(inti=0;i<values.length; i++) {
•
out.println(" " + values[i]); } }
•
out.println();
•
out.println("Request headers:");
•
Enumeration headerNames = request.getHeaderNames();
•
while (headerNames.hasMoreElements()) {
•
String name = (String) headerNames.nextElement();
•
String value = request.getHeader(name);
•
out.println(""+name+":"+value); }
•
out.println();
•
out.println("Cookies:");
•
Cookie[] cookies = request.getCookies();
•
for(inti=0;i<cookies.length; i++) {
•
String name = cookies[i].getName();
•
String value = cookies[i].getValue();
•
out.println(""+name+":"+value); }
•
out.println("</PRE></BODY></HTML>");
•
out.close();}
Как обычно, первыми указываются импортируемые пакеты классы,
необходимые программе для выполнения специфических функций.
Именно в четвертой строке определяется основной метод servlet do-
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
