Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

JAVA. Серверные приложения

.pdf
Скачиваний:
0
Добавлен:
06.09.2026
Размер:
2 Мб
Скачать
Часть 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 файла, содержащего serv­let, описание 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документом. Тег <ser­vlet> указывает объект, для которого осуществляется конфигурация.
Тег <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 FileInputStre­am(filename+ ".ser"));}
catch (Exception e) { e.printStacTrace(); }}
Сначала создается глобальная переменная (поле) строкового типа fi­lename, первая строка, во второй строке вызывается метод init() с пара­метром config класса ServletConfig. Третья строка является стандарт­ной операцией вызова метода суперкласса. Четвертая строка содержит оператор присваивания полю filename значения, полученного из конфи­гурационного файла, либо XML-документа либо текстового файла pro­perties, значение параметра с именем filename. И если значение пара­метра не пустое, строка пять, создается входящий поток с указанным именем filename и добавленным расширением.ser, строка семь, куда позже могут быть помещены какие-либо параметры, например пере­менные сессии. Восьмая строка представляет стандартный обработчик исключительной ситуации.
Часть 3 Серверные Java-компоненты Servlet 153
Инициализация может не пройти успешно по каким-либо причинам, тогда вызывается исключительная ситуация класса UnavailableExcepti­on и сервер выгружает из памяти экземпляр 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 Fi­leOutputStream(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. При условии существования в системе файла, содержащего класс SaveS­tats. Во входящий поток помещается содержимое файла, строка десять. Главное — не забыть прописать в инициализационном файле servlet тег filename c атрибутом имеющего значение имени файла сериализации. Именно на данную операцию указывает строка семь. В одиннадцатой строке полученное значение передается переменной stats. Дальнейшие операторы будут пока опущены, они описаны ниже. Ключевой в основном методе doGet() на данный момент является двадцать первая строка, опи­сывающая сохранение полученного значения во внешнем файле.
Все-таки могут возникнуть ситуации, в которых не рекомендуется использовать метод init(), тогда можно получать параметры, используя другие методы.
Параметры из внешних источников можно получить методом getSer­vletContext() класса ServletConfig. Получение всех инициализационных параметров с их значениями возможно посредством метода getInitPara­meterNames() при использовании коллекции Enumeration. Получить имя исполняемого servlet можно, используя getServletName(), опреде­ляющего имя servlet в виде строки.
Для получения более подробной информации о выполняемом servlet необходимо организовать доступ к объекту SevletContext. С помощью методов данного объекта можно получить исчерпывающую информа­цию о системном окружении servlet.
22.2. Обработка клиентского запроса servlet, метод service()
Но главное, для чего создан servlet, — это обработка запроса. После инициализации, servlet готов выполнять непосредственную задачу. Каждый запрос является объектом класса ServletRequest, а каждый от­вет, в свою очередь, является объектом класса ServletResponse, описан­ных в Java Servlet API.
Клиент может использовать два способа отправки запроса для об­работки servlet. Может посылаться непосредственно параметр, встро­енный в URL, может использоваться входящий поток данных (Input­Stream).
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 êîä } }
Все основные методы по обработке клиентского запроса должны пол­ностью закончиться, прежде чем будет вызван метод уничтожения des­troy(). Перед окончанием работы servlet необходимо убедиться, что все потоки, формирующие ответ, полностью завершены. Для управления длительностью сессии существуют методы класса HttpSession. Методы setMaxinactiveInterval() и isNew() позволяют в какой-то мере контроли­ровать пользовательскую сессию.
Глава 23. Использование класса ServletContext для
получения более обширной информации об исполняемом
окружении servlet
Класс ServletContext содержит методы, используемые servlet, для получения свойств системного окружения, получения MIME типов при­ложения или записи в Log-файл. Обычно один объект класса Servlet­Context описывает одно Web-приложение на одной JVM. В данном слу­чае Web-приложение представляет коллекцию servlet в одном архив­ном файле war-архиве или в одной директории виртуального хоста.
В случае распределенного Web-приложения каждый объект класса ServletContext использует параметры одной JVM. В этой ситуации объ­ект не может использоваться для получения глобальной информации.
Объект Servlet Context является частью специального объекта Serv­letConfig, создаваемого при инициализации servlet.
В ранних версиях servlet API, объект ServletContext был единым для всех исполняемых servlet в данный момент времени. И начиная с версии 2.1 servlet API, объект имеет дополнительные методы и работает на уровне отдельно взятого Web-приложения.
Многие servlet — engine, использующие спецификацию 2.1, исполь­зуют один объект ServletContext для множества загруженных servlet на Web-сервере. Предвидя данную ситуацию, фирма SUN дополнила класс ServletContext дополнительными методами идентификации вы­полняемого servlet. Теперь каждому исполняемому servlet можно при­своить определенные атрибуты. Существуют три метода в классе Serv­letContext по работе с атрибутами servlet: getAttribute(), setAttribute() и removeAttribute(). К тому же каждый servlet engine может самостоя­тельно конфигурировать исполняемый servlet, а значит, и объект клас­са ServletContext. Данная функция полезна при инициализации servlet.
Servlet получает информацию из ServletContext при помощи объекта ServletConfig. При использовании протокола HTTP используется метод getServletContext() для получения системной информации. Чтобы воспо­льзоваться методами, предоставляемыми классом ServletContext, код
Часть 3 Серверные Java-компоненты Servlet 157
должен содержать или проинициализированный объект ServletConfig, или методы вызова этого класса. Полезным также является метод log(), предназначенный для записи в системный файл или базу данных event­Log каких-либо сообщений. Пара методов getMajorVersion() и getMinor­Version() указывают числовое значение используемого 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. Это касается не только запроса, но и ответа. Интерфейс RequestDispat­cher представляет механизм распределения клиентских запросов и от­ветов по различным ресурсам. Такими ресурсами могут быть JSP-стра­ница, другой servlet или CGI-скрипт.
В указанном интерфейсе содержится пара методов для осуществления перенаправления клиентского запроса в другие источники обработки ин­формации. Перенаправление полезно в различных ситуациях, не преду­смотренных стандартным решением получения и обработки информации.
Часто перенаправление запроса используется при возникновении исключительной ситуации, например отсутствии или перемещении за­прашиваемого ресурса. Перенаправление осуществляется двумя спосо­бами: клиентский запрос направляется в другой источник, часть ответа принимается из другого источника.
Пересылка запроса из одного servlet в другие ресурсы, такие, как servlet, JSP, HTML, для генерации ответа использует метод forward() с двумя параметрами: первый является наследником класса ServletRequ­est, другой параметр наследован от 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. Строка четыре создает объект диспетчера.
Другой задачей перенаправления является добавление к отправляе­мому ответу, какой-нибудь добавочной информации, содержащейся в другом источнике.
Для получения информации из другого источника в интерфейсе Re­questDispatcher описан метод 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.IOEx­ception.
Ресурсы, используемые при перенаправлении, должны быть доступ­ны через специальный объект ServletContext, вызываемый servlet. До­ступ к объектам, наследующим методы интерфейса RequestDispatcher, организуется при помощи метода getRequestDispatcher() класса Servlet­Context. С помощью метода getNamedDispatcher() класса servletContext можно получить имя ресурса, на который был перенаправлен запрос.
Часть 3 Серверные Java-компоненты Servlet 159
Глава 25. Основная структура Servlet
Структура servlet для различных протоколов передачи различна, но в большинстве случаев имеется ряд идентичных методов последовате­льности их вызова. Кроме методов жизненного цикла и диспетчериза­ции запросов, к основным методам servlet можно отнести объекты спе­циальных классов по работе с клиентским запросом. Такими объектами являются request и response, опять же для каждого протокола переда­чи, эти объекты имеют собственные классы родителей.
25.1 Использование основных методов servlet: service(), doGet(),
doPost() и обработка данных
Описание других расширенных методов doPut(), doDelete(), doOpti­ons(), doTrace(), doHead() будет опущено, поскольку в новых версиях JSDK 2.1 и 2.2 обработка этих методов заменена вызовом стандартных методов servlet.
Основные методы, используемые в servlet, имеют названия, схожие с именами методов, используемых при передаче данных. Кроме метода service(), все остальные методы servlet олицетворяют используемые http-методы.
Метод doGet() используется при передаче HTTP-протоколом данных посредством метода GET. Передача, осуществляемая при помощи POST, обрабатывается методом doPost(), прописанным в теле servlet. Метод POST используется для передачи клиентских запросов на сер­вер. DELETE и его servlet аналог doDelete() предназначены для удале­ния запроса с Web-страницы. Методы OPTIONS и соответственно do­Options() описывают параметры, используемые клиентской сессией в момент отправления запроса и получения ответа. Трассировочную и диагностическую информацию можно получить, используя пару мето­дов, новой версий HTTP-протокола TRACE и doTrace(). Метод HEAD очень похож на метод GET, но в отличие от оного работает только со специальной информацией — заголовками. С помощью метода HEAD можно получить дату изменения документа, размер пересылаемого до­кумента, тип метода, с помощью которого осуществлялась передача до­кумента, тип сервера, позволяющий пользователю использовать пара­метры непосредственно сервера.
Методы doPut(), doDelete(), doOptions(), doTrace(), doHead() исполь­зуются только с протоколом HTTP версии 1.1. Методы doGet(), doPost() рабтают с двумя версиями HTTP-протокола, с версией 1.1 и более ран­ней 1.0. Метод service() — независимый от протокола метод, это сердце любого servlet. Остальные методы являются заменителями метода servi­ce() для каждой конкретной ситуации.
Каждый раз, когда клиент обращается к Web-ресурсу, используя ser­vlet, Web-сервер обращается к одному из основных методов servlet, ser­vice() или его HTTP аналогов. При этом JVM создает еще один поток, и затем выполняется метод service(). Для действительной работы метода service() его необходимо переопределить. Пока метод service() выполня-
160 Часть 3 Серверные Java-компоненты Servlet
ется внутри отдельного потока, можно спокойно выполнять другие зада­чи. Все методы по обработке клиентских запросов, реализуемые в servlet, обязательно используют два передаваемых параметра, ассоциированных с запросом (request) и ответом (response). Методы service(), doGet() и do­Post() создаются непосредственно для выполнения основных задач.
Тело основных методов 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-