Bloch, bodoff руководство. сервлеты

33
Руководство: Сервлеты Авторы Cynthia Bloch и Stephanie Bodoff Данное руководство научит вас работе с сервлетами, а также с примерами кодов, исполняемых на серверах и расширющих их функциональные возможности. К примеру, сервлеты являются эффективной независящей от платформы альтернативой для CGI скриптов. Сервера, взаимодействующие с сервлетами, являются Java-серверами, которые отвечают на запросы клиентов. В «кратком обзоре сервлетов» вы узнаете, что такое сервлеты и как их можно использовать. В уроке также представлены примеры сервлетов, используемых в данном руководстве. В разделе «Взаимодействие с клиентами» будет показано, как писать сервлеты, взаимодействующие с клиентами. Сервлеты данного урока отвечают на запросы HTTP GET, HEAD и POST. Также здесь обсуждаются вопросы, связанные с многопотоковостью, и показано, как избежать различных проблем путем создания сервлета, обрабатывающего запрос одного клиента за раз. «Жизненный цикл сервлетов». Здесь обсуждаются важные события рабочего цикла сервлета, а так же рассказывается о том, как настроить инициализацию и отключение сервлета. «Сохранение состояния клиента», здесь описано, как использовать трэкинг сессии и файлы-cookies. «Коммуникация сервлета». В данном уроке показано, как связать ваш сервлет с другими сервлетами и ресурсами, такими, как CGI скрипты. В уроке «запуск сервлетов» вы научитесь тестировать свои сервлеты при помощи Tomcat и Java Servlet Development Kit (JSDK) версии 2.1. Урок «вызов сервлетов» научит вас обращаться к сервлетам из броузера и в пределах HTML страницы. Примечание: технология Java Servlet является стандартным расширением к платформе Java 2, Standart Edition. Реализации Tomcat 3.х и 4.х поддерживают технологию Java Servlet версий 2.2 и 2.3. Скачать Tomcat можно со страницы . JSDK поддерживает Java Servlet версии 2.1, скачать его можно по адресу . Информация, представленная в данных уроках применима как к Tomcat, так и к JSDK версии 2.1. Примечание для корпоративных пользователей: Глава в руководстве J2EE связана с развертыванием сервлетов на платформе J2EE SDK версии 1.3. Приложение «книжный магазин» в данном руководстве адаптировано к использованию базы данных Cloudscape, распространяемой с J2EE SDK. В главе J2EE также описаны API, представленные в спецификации сервлетов версии 2.3. 1

Transcript of Bloch, bodoff руководство. сервлеты

Page 1: Bloch, bodoff   руководство. сервлеты

Руководство: СервлетыАвторы Cynthia Bloch и Stephanie Bodoff

Данное руководство научит вас работе с сервлетами, а также с примерами кодов,исполняемых на серверах и расширющих их функциональные возможности. К примеру,сервлеты являются эффективной независящей от платформы альтернативой для CGIскриптов. Сервера, взаимодействующие с сервлетами, являются Java-серверами, которыеотвечают на запросы клиентов.

В «кратком обзоре сервлетов» вы узнаете, что такое сервлеты и как их можноиспользовать. В уроке также представлены примеры сервлетов, используемых в данномруководстве.

В разделе «Взаимодействие с клиентами» будет показано, как писать сервлеты,взаимодействующие с клиентами. Сервлеты данного урока отвечают на запросы HTTPGET, HEAD и POST. Также здесь обсуждаются вопросы, связанные с многопотоковостью,и показано, как избежать различных проблем путем создания сервлета, обрабатывающегозапрос одного клиента за раз.

«Жизненный цикл сервлетов». Здесь обсуждаются важные события рабочего цикласервлета, а так же рассказывается о том, как настроить инициализацию и отключениесервлета.

«Сохранение состояния клиента», здесь описано, как использовать трэкинг сессии ифайлы-cookies.

«Коммуникация сервлета». В данном уроке показано, как связать ваш сервлет с другимисервлетами и ресурсами, такими, как CGI скрипты.

В уроке «запуск сервлетов» вы научитесь тестировать свои сервлеты при помощи Tomcatи Java Servlet Development Kit (JSDK) версии 2.1.

Урок «вызов сервлетов» научит вас обращаться к сервлетам из броузера и в пределахHTML страницы.

Примечание: технология Java Servlet является стандартным расширением к платформеJava 2, Standart Edition. Реализации Tomcat 3.х и 4.х поддерживают технологию JavaServlet версий 2.2 и 2.3. Скачать Tomcat можно со страницы . JSDK поддерживает JavaServlet версии 2.1, скачать его можно по адресу . Информация, представленная в данныхуроках применима как к Tomcat, так и к JSDK версии 2.1.

Примечание для корпоративных пользователей: Глава в руководстве J2EE связана сразвертыванием сервлетов на платформе J2EE SDK версии 1.3. Приложение «книжныймагазин» в данном руководстве адаптировано к использованию базы данных Cloudscape,распространяемой с J2EE SDK. В главе J2EE также описаны API, представленные вспецификации сервлетов версии 2.3.

1

Page 2: Bloch, bodoff   руководство. сервлеты

Урок 1: Краткий обзор сервлетовСервлеты – это модули, которые раширяют сервера, ориентированные на системызапрос-ответ, такие, как web-сервера Java. Например, сервлет может отвечать заполучение данных в формате HTML и применение бизнес логики, используемойдля обновления базы данных компании.

Сервлеты являются для серверов тем же, что и апплеты для броузеров. Но вотличие от апплетов, у сервлетов нет графического интерфейса.

Сервлеты можно внедрять в различные сервера, так как API сервлета, который выиспользуете для его написания, ничего не «знает» ни о среде сервера, ни о егопротоколе. Все чаще стало встречаться использование сервлетов на HTTP серверах.Множество Web-серверов поддерживает технологию Java Servlet.

.

Замените CGI скрипты сервлетами!Сервлеты эффективно заменяют CGI скрипты. Они позволяют генерироватьдинамические документы гораздо проще и быстрее. Сервлеты так же решаютпроблему программирования на стороне сервера при использовании специальныхAPI, характерных только для определенных платформ. Сервлеты разрабатываютсяс Java Server API, стандартным Java расширением.

Итак, вы можете использовать сервлеты для обработки клиентских HTTP запросов.К примеру, сервлет, обрабатывающий данные, отправленные через HTTPS, гдеиспользована форма HTML, включающая бланк заказа или данные банковскойкредитной карточки. Такой сервлет может являться частью запросной иобрабатывающей системы, работающей с базами данных товаров и инвентаря, ивозможно онлайновой системы платежей.

Другие области применения сервлетовНиже представлено лишь небольшое количество приложений для сервлетов:

Взаимодействие между людьми. Сервлет одновременно обрабатываетбольшое количество запросов, а так же может синхронизировать их. Этопозволяет сервлетам поддерживать такие системы, как онлайноваяконференция.

Пересылка запросов. Сервлеты могут пересылать запросы другимсерверам и сервлетам. Таким образом, сервлеты можно применять длябалансирования нагрузки между несколькими серверами, которые

2

Page 3: Bloch, bodoff   руководство. сервлеты

отражают одинаковое содержание, а так же для распределения одногологического сервиса по нескольким серверам, в соответствии с типомпоставленной задачи или организационными границами.

Подготовка к написанию сервлетовЧтобы подготовить вас к написанию сервлетов, давайте сначала обсудимследующие темы:

Архитектура пакета Servlet

Здесь обсуждается структура основных интерфейсов и объектов пакета Servlet.

Простой сервлет

В данном параграфе представлен код простейшего сервлета.

Сервлеты, представленные в данном руководстве

Здесь рассказано о примере, используемом в данном руководстве.

Архитектура пакета ServletПакет javax.servlet обеспечивает интерфейсы и классы для написания сервлетов.Архитектура пакета описана ниже.

Интерфейс сервлета

Центральной абстракцией API сервлета является интерфейс Servlet. Все сервлетыреализуют данный интерфейс напрямую, но более распространено расширение класса,реализующего его, как HttpServlet.

Интерфейс Servlet объявляет, но не реализует методы, которые управляют сервлетом и еговзаимодействием с клиентами. Авторы сервлетов отвечают также за написание методов.

3

Page 4: Bloch, bodoff   руководство. сервлеты

Взаимодействие с клиентом

Принимая запрос от клиента, сервлет получает два объекта:

ServletRequest, который инкапсулирует связь клиента с сервером.

ServletResponse, который инкапсулирует обратную связь сервлета с клиентом.

ServletRequest и ServletResponse – это интерфейсы, определенные пакетом javax.servlet.

Интерфейс ServletRequest

Интерфейс ServletRequest дает сервлету доступ к:

Информации, такой, как имена параметров, переданных клиентом, протоколы(схемы), используемые клиентом и имена удаленного хоста, создавшего запроси сервера который их получает.

Входному потоку ServletInputStream. Сервлеты используют входной поток дляполучения данных от клиентов, которые используют протоколы приложений,такие как HTTP POST и методы PUT.

Интерфейсы, которые расширяют интерфейс ServletRequest, позволяют сервлету получатьбольше данных, характерных для протокола. К примеру, интерфейс HttpServletRequestсодержит методы для доступа к главной информации по протоколу HTTP.

Интерфейс ServletResponse

Интерфейс ServletResponse дает сервлету методы для ответа на запросы клиента. Он:

Позволяет сервлету устанавливать длину содержания и тип MIME ответа.

Обеспечивает исходящий поток ServletOutputStream и Writer, через которыесервлет может отправлять ответные данные.

Интерфейсы, которые расширяют интерфейс ServletResponse, дают сервлету большевозможнойстей для работы с протоколами. Например, интерфейс HttpServletResponseсодержит методы, которые позволяют сервлету манипулировать информацией заголовкаHTTP.

Дополнительные возможности HTTP сервлетов

Классы и интерфейсы, описанные выше, образуют основной сервлет. В HTTP сервлетахесть несколько дополнительных объектов, которые обеспечивают возможностиуправления сессией (session-tracking). Автор сервлета может использовать эти API дляподдержания состояния между сервлетом и клиентом, которое сохраняется на несколькихсоединениях в течении определенного времени. В HTTP сервлетах также есть объекты,которые обеспечивают создание файлов-cookie. Автор сервлета использует файлы-cookieAPI для сохранения данных, ассоциированных с клиентом и для получения этих данных.

Простой сервлетДанный класс полностью определяет сервлет:

4

Page 5: Bloch, bodoff   руководство. сервлеты

класс-public SimpleServlet расширяет HttpServlet { /** * Обработка метода HTTP GET путем создания простой web-страницы. */ public void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

PrintWriter out; String title = "Simple Servlet Output";

// сначала установите тип содержания и другие поля заголовковответа response.setContentType("text/html");

// затем запишите данные ответа out = response.getWriter();

out.println("<HTML><HEAD><TITLE>"); out.println(title); out.println("</TITLE></HEAD><BODY>"); out.println("<H1>" + title + "</H1>"); out.println("<P>This is output from SimpleServlet."); out.println("</BODY></HTML>"); out.close();

} }

Вот и все!

Классы упомянутые в разделе Архитектура пакета Servlet выделены впримере жирным:SimpleServlet расширяет класс HttpServlet, реализующий интерфейс Servlet: SimpleServlet преобладает над методом doGet в классе HttpServlet. Метод doGet вызывается, когдаклиент делает запрос GET (метод запроса HTTP по умолчанию), и создает простую HTMLстраницу, возвращаемую клиенту.В пределах метода doGet,

o Запрос пользователя представлен объектом HttpServletRequest.o Ответ пользователю представлен объектом HttpServletResponse.o Так как текстовые данные возвращаются клиенту, ответ отправляется, используя объект

Writer, полученный из объекта HttpServletResponse.

Примеры сервлетов

В остальных уроках данного курса будет показано, как писать HTTP сервлеты.Предполагается, что вы уже что-то знаете о HTTP протоколе. Дополнительнуюинформацию по HTTP протоколу можно получить на странице .

5

Page 6: Bloch, bodoff   руководство. сервлеты

В уроке используется пример под названием Duke's Bookstore, простой книжныйонлайн-магазин, который позволяет покупателю выполнять различные операции.Каждая функция обеспечивается при помощи сервлета:

Функция Сервлет Tomcat Сервлет JSDK2.1Просмотр книг,представленных на продажу

CatalogServlet CatalogServlet

Покупка книги, путемпомещения ее в «корзину»

CatalogServlet CatalogServlet

Получение дополнительнойинформации о заданнойкниге

BookDetailServlet BookDetailServlet

Просмотр книг, выбранных вкачестве покупки

ShowCartServlet ShowCartServlet

Удаление одной илинескольких книг из корзины

ShowCartServlet ShowCartServlet

Покупка книг, находящихсяв «корзине»

CashierServlet CashierServlet

Благодарность клиенту запокупку

ReceiptServlet ReceiptServlet

В уроках решение различных типов задач иллюстрируется на готовых сервлетах.Например, сервлет BookDetailServlet используется для демонстрации того, как управлятьHTTP GET запросами, а CatalogServlet демонстрирует управление сессией.

Данное руководство включает в себя исходные файлы, используемые в примере Duke'sBookstore. Для вашего удобства мы создали два zip-архива: один из них содержит всеисходные файлы, необходимые для создания и запуска примера, используя Tomcat, адругой содержит все исходные файлы, необходимые для создания и запуска примера,используя JSDK2.1.

Скачать zip-архив для Tomcat

Скачать zip-архив для JSDK2.1

Для запуска примера загрузите Tomcat или сервер JSDK2.1. После этого вызовите сервлетиз броузера.

Урок 2: Взаимодействие с клиентамиHTTP сервлет обрабатывает запросы клиентов методом service. Метод serviceподдерживает стандартные клиентские запросы HTTP путем отправления каждогозапроса методу, созданному для обработки этого запроса. Например, метод serviceвызывает метод doGet, показанный ранее в примере простого сервлета.

Запросы и ответы

В этом разделе обсуждается использование объектов, которые отображаютклиентский запрос (объект HttpServletRequest) и ответ сервлета (объектHttpServletResponse). Эти объекты предназначены для метода service и дляметодов, которые service вызывает для обработки HTTP запросов.

6

Page 7: Bloch, bodoff   руководство. сервлеты

Управление запросами GET и POST

Методы, которым service передает HTTP запросы, включают в себя:

doGet, для управления запросами GET, условным GET и HEAD

doPost, для управления запросами POST

doPut, для управления запросами PUT

doDelet, для управления запросами DELETE

По умолчанию эти методы возвращают ошибку BAD_REQUEST (400). Вашсервлет должен отвергать метод или методы, созданные для управлениявзаимодействиями HTTP, которые он поддерживает. В данном разделе показано,как реализовать методы, которые управляются самыми распространенными HTTP-запросами: GET и POST.

Метод HttpServlet's service также вызывает метод doOptions, когда сервлет получаетзапрос OPTIONS, и метод doTrace, когда сервлет получает запрос TRACE.Реализация по умолчанию метода doOptions автоматически определяет, какиеопции HTTP поддерживаются, и возвращает эту информацию. Реализация поумолчанию метода doTrace вызывает ответ в виде сообщения, содержащего всезаголовки, отправленные в trace-запросе. Обычно эти методы не переопределены.

Вопросы, связанные с многопотоковостью

HTTP сервлеты обычно способны обслуживать одновременно несколько клиентов.Если методы в вашем сервлете выполняют задачи клиентов путем предоставленияобщих ресурсов, вам нужно или:

Синхронизировать доступ к этому ресурсу, или

Создать сервлет, который управляет только одним запросом клиента зараз.

В данном уроке будет показано, как реализовать второй пункт условия (о первомрассказано в уроке о потоках руководства).

Описания сервлета

Кроме обработки клиентских запросов HTTP сервлеты также вызываются дляобеспечения собственных описаний. В данном параграфе показано, как обеспечитьописание путем выделения метода getServletInfo, обеспечивающего описаниесервлета.

Запросы и ответыМетоды класса HttpServlet, которые управляют клиентскими запросами принимаютдва аргумента:

1. Объект HttpServletRequest, который инкапсулирует данные от клиента

7

Page 8: Bloch, bodoff   руководство. сервлеты

2. Объект HttpServletResponse, который инкапуслирует ответ к клиенту

Объекты HttpServletRequest

Объект HttpServletRequest обеспечивает доступ к данным HTTP заголовков, таким какфайлы-cookie, найденные в запросе, и методу HTTP, при помощи которого был сделанзапрос. Объект HttpServletRequest также позволяет вам получать аргументы, которыеявляются частью запроса, отправленного клиентом.

Для получения доступа к клиентским данным:

Метод getParameter возвращает значение названного параметра. Если у вашегопараметра может быть больше одного значения, лучше использовать методgetParameterValues. Он возвращает массив значений для названного параметра(имена праметров обеспечивает метод getParameterNames).

Для запросов HTTP GET метод getQueryString возвращает Stringпредварительных данных от клиента. Вам необходимо произвести анализ этихданных, получив при этом параметры и значения.

Для запросов HTTP, PUT и DELETE

Если вы ожидаете получение текстовых данных, метод getReader возвратитBufferedReader для чтения предварительных данных.

Если вы ожидаете получение бинарных данных, метод getInputStream возвращаетServletInputStream для чтения предварительных данных.Примечание: Используйтеили метод getParameter[Values], или один из методов, позволяющихсамостоятельно анализировать данные. Оба метода нельзя использовать вместе водном запросе.

Объекты HttpServletResponse

Объект HttpServletResponse обеспечивает два способа возвращения данных пользователю:

Метод getWriter возвращает Writer

Метод getOutputStream возвращает ServletOutputStream

Используйте метод getWriter для возвращения пользователю текстовых данных и методgetOutputStream для бинарных.

Закрытие Writer или ServletOutputStream после отправления запроса позволит серверуопределить, что ответ готов.

Данные HTTP заголовков

Вы должны установить данные заголовков HTTP прежде, чем получить доступ к Writerили OutputStream. Класс HttpServletResponse обеспечивает методы для доступа к даннымзаголовка. Например, метод setContentType устанавливает тип содержания (чаще всеговручную устанавливается только заголовок).

8

Page 9: Bloch, bodoff   руководство. сервлеты

Управление запросами GET и POSTДля управления HTTP запросами в сервлете расширьте класс HttpServlet ипереопределите методы сервлета, которые управляют HTTP запросами,поддерживаемыми сервлетом. В данном уроке проиллюстрированно управлениезапросами GET и POST. Методы, управляющие этими запросами, называютсяdoGet и doPost.

Управления запросами GET

Управление запросами GET включает в себя переопределение метода doGet. В следующихпримерах показано, как это делает BookDetailServlet. Методы, описанные в параграфеЗапросы и Ответы, выделены жирным.public class BookDetailServlet extends HttpServlet {

public void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ...

// прежде чем получить доступ к методу Writer, устанавливаем заголовокcontent-type response.setContentType("text/html"); PrintWriter out = response.getWriter();

// затем выводим ответ out.println("<html>" + "<head><title>Book Description</title></head>" + ...);

// Получаем идентификатор книги для отображения String bookId = request.getParameter("bookId"); if (bookId != null) { // и информацию о книге, выводим ее ... } out.println("</body></html>"); out.close(); } ...}

Сервлет расширяет класс HttpServlet и переопределяет метод doGet.

В пределах метода doGet метод getParameter получает ожидаемый аргумент сервлета.

Чтобы ответить клиенту, метод doGet в примере использует Writer из объектаHttpServletResponse для возвращения клиенту текстовых данных. Прежде чем получитьдоступ к writer, пример устанавливает заголовок content-type. В конце метода doGet, послетого как был отправлен запрос, Writer закрывается.

Управления запросами POST

Управление запросами POST включает в себя переопределение метода doPost. Вследующих примерах показано, как это делает ReceiptServlet. Кроме того, методы,описанные в разделе Запросы и Ответы, выделены жирным.

9

Page 10: Bloch, bodoff   руководство. сервлеты

public class ReceiptServlet extends HttpServlet {

public void doPost(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException { ... // прежде чем получить доступ к методу Writer, устанавливаемзаголовок content-type response.setContentType("text/html"); PrintWriter out = response.getWriter();

// затем выводим ответ out.println("<html>" + "<head><title> Receipt </title>" + ...);

out.println("<h3>Thank you for purchasing your books from us " + request.getParameter("cardname") + ...); out.close(); } ...}

Сервлет расширяет класс HttpServlet и переопределяет метод doPost.

В пределах метода doPost, метод getParameter получает ожидаемый аргумент сервлета.

Чтобы ответить клиенту, метод doPost примера использует Writer из объектаHttpServletResponse для возвращения клиенту текстовых данных. Прежде чем получитьдоступ к writer, в примере устанавливается заголовок content-type. В конце метода doPost,после того как ответ был отправлен, Writer закрывается.

Вопросы, связанные с многопотоковостьюHTTP сервлеты обычно способны обслуживать одновременно несколько клиентов.Если методы в вашем сервлете выполняют задачи клиентов, откройте доступ кобщим ресурсам, после чего вы сможете управлять параллельностью процессапутем создания сервлета, обрабатывающего только один запрос за один раз (вымогли бы также синхронизировать доступ к ресурсу. Это описано в уроке опотоках руководства).

Для того чтобы ваш сервлет работал только с одним клиентом, необходимо, чтобыкроме расширения класса HttpServlet он реализовал SingleThreadModel.

Реализация интерфейса SingleThreadModel не включает написания каких-либодополнительных методов. Вы просто объявляете, что сервлет реализует интерфейс,а сервер гарантирует, что ваш сервлет запускает только один метод service за одинраз.

Например, метод ReceiptServlet принимает имя и номер кредитной карточкипользователя, после чего благодарит его за заказ. Если сервлет уже обновил базуданных, например, ту, что хранит записи, тогда связь с базой данных может бытьобщедоступна. Сервлет может или синхронизировать доступ к этому ресурсу, илиреализовать интерфейс SingleThreadModel. Если сервлет реализовал интерфейс,

10

Page 11: Bloch, bodoff   руководство. сервлеты

единственным отличием от кода предыдущего раздела является одна строка,выделенная жирным:

public class ReceiptServlet extends HttpServlet implements SingleThreadModel {

public void doPost(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException { ... } ... }

Создание описания сервлетаНекоторые приложения, такие как Java Web Server Administration Tool(администрирующая утилита Java Web-сервера), получают описательнуюинформацию из сервлета и отображают ее. Описанием сервлета являетсястрока, которая может описать главную задачу сервлета, его автора, номерверсии и вообще все, что автор сочтет нужным.

Метод, возвращающий данную информацию называется getServletInfo, поумолчанию у него стоит значение null. Вам не обязательно переопределятьэтот метод, но приложения не смогут обеспечить описание сервлета пока высами этого не сделаете.

В следующем примере показано описание сервлета BookStoreServlet: public class BookStoreServlet extends HttpServlet { ... public String getServletInfo() { return "The BookStore servlet returns the " + "main web page for Duke's Bookstore."; } }

Урок 3: Жизненный цикл сервлетаУ всех сервлетов одинаковый жизненный цикл:

Сначала сервер загружает и инициализирует сервлет

Затем сервлет обрабатывает ноль или несколько клиентских запросов

После этого сервер удаляет сервлет (некоторые серверы выполняют этодействие только после выключения)

Инициализация сервлета

Когда сервер загружает сервлет, он запускает метод сервлета init. Инициализациясчитается полностью завершенной еще до обработки клиентских запросов иуничтожения сервлета.

11

Page 12: Bloch, bodoff   руководство. сервлеты

Хотя большинство сервлетов исполняется на многопоточных серверах, у сервлетовнет параллельных операций во время инициализации. Сервер только один развызывает метод init, когда он загружает сервлет и больше не будет его загружатьдо следующего перезапуска сервлета. После того, как сервер уничтожил сервлет,запустив метод destroy, восстановить его невозможно.

Взаимодействие с клиентами

После инициализации сервлет может обрабатывать запросы клиентов. Эта частьжизненного цикла сервлета описана в предыдущем уроке.

Уничтожение сервлета

Сервлеты работают до тех пор, пока сервера их не уничтожат, к примеру, позапросу администратора. Когда сервер уничтожает сервлет, он запускает методсервлета destroy. Метод запускается один раз. Сервер не сможет снова запуститьего до нового запуска и инициализации сервлета.

Когда сервер вызывает метод destroy, другой поток может запустить запросслужбы. В уроке Управление потоками служб в конце работы сервлета показано,как обеспечить правильное выключение, когда могут встретиться потоки незавершившие работу со службами.

Инициализация сервлета

Метод init, обеспеченный классом HttpServlet инициализирует сервлет иследит за инициализацией. Чтобы инициализировать ваш сервлетпереопределите метод init, следуя данным правилам:Если встречается ошибка инициализации, которая указывает на неспособность сервлетаобрабатывать запросы клиентов, выдается ошибка UnavailableException.

Примером данного типа ошибки является невозможность установитьтребуемое сетевое соединение.Не вызывайте метод System.exitСохраните параметр ServletConfig так, чтобы метод getServletConfig мог возвращать значение.

Для этого проще всего сделать так, чтобы новый метод init вызывалsuper.init. Если вы самостоятельно храните объект, переопределите методgetServletConfig, для возвращения объекта обратно.

12

Page 13: Bloch, bodoff   руководство. сервлеты

Ниже представлен пример метода init: public class BookDBServlet ... {

private BookstoreDB books;

public void init(ServletConfig config) throws ServletException {

// Сохраняйте объект ServletConfig и следите за инициализацией super.init(config);

// Загрузите базу данных, для подготовки к получению запросов books = new BookstoreDB(); } ... }

Метод init достаточно прост: он вызывает метод super.init для управленияобъектом ServletConfig и слежением за инициализацией, а такжеустанавливает поле private.

Если бы сервлет BookDBServlet использовал вместо имитации базы данныхобъектом настоящую, метод init был бы более сложным. Ниже представленпсевдо-код, для которого метод init мог бы выглядеть так:

public class BookDBServlet ... {

public void init(ServletConfig config) throws ServletException {

// Сохраняйте объект ServletConfig и следите за инициализацией super.init(config);

// Откройте соединение с базой данных для подготовки к получениюзапросов try { databaseUrl = getInitParameter("databaseUrl"); ... // get user and password parameters the same way connection = DriverManager.getConnection(databaseUrl, user, password); } catch(Exception e) { throw new UnavailableException (this,

"Could not open a connection to the database"); } } ... }

Параметры инициализации

Вторая версия метода init вызывает метод getInitParameter. Этот методпринимает имя параметра как аргумент и возвращает String в виде значенияпараметра.

(Спецификация параметров инициализации соответствует серверу.Например, параметры определены при помощи свойства, когда сервлетзапускается с servlet runner. В уроке об Утилите servletrunner содержитсяобщее описание свойств и того, как она создается.)

13

Page 14: Bloch, bodoff   руководство. сервлеты

Если по каким-то причинам вам понадобится получить имена параметров,используйте метод getParameterNames.

Уничтожение сервлетаМетод destroy, который обеспечивается классом HttpServlet уничтожает сервлет ивносит записи об этом процессе. Для того, чтобы уничтожить любые ресурсыиспользуемые вашим сервлетом, переопределите метод destroy. Метод destroyдолжен стереть любую работу инициализации и синхронизировать устойчивоесостояние с текущим состоянием в памяти.

В данном примере показан метод destroy, сопровождающий псевдо-код метода initв предыдущем уроке:

public class DBServlet ... { Connection connection = null;

... // метод init

public void destroy() { // Закройте соединение connection.close(); connection = null; } }

Сервер вызывает метод destroy или после того как все вызовы сервисов завершены,или по прошествии времени определенного сервером. Если ваш сервлетобрабатывает какую-то затянувшуюся операцию, методы service могут все ещеработать во время запуска метода destroy. Вы несете ответственность завыполнение этих потоков. В следующем уроке будет показано как.

В методе destroy, показанном выше, подразумевается, что все клиентскиевзаимодействия должны быть завершены к моменту вызова метода destroy, так каку сервлета больше нет затянувшихся операций.

Управление потоками служб в конце работы сервлетаКогда сервлет удаляется, все его методы service должны быть завершены. Серверпытается гарантировать это путем вызовом метода destroy только после того, каквсе запросы служб были возвращены или после по истечению времени,определенного сервером для сервлета. Если у сервлета есть операции, выполнениекоторых требует длительного промежутка времени (то есть операции, которыемогут работать дольше, чем период работы определенный сервером), то, на моментвызова метода destroy, операции могут все еще работать. Вам необходимогарантировать, что любые потоки, которые все еще обрабатывают запросыклиентов, завершены. В конце этого параграфа описывается способ, как этосделать.

Если в вашем сервлете есть потенциально затянувшиеся запросы служб,используйте методы данного урока. Для этого:

Отследите количество потоков, запускаемых в данный момент в методеservice.

14

Page 15: Bloch, bodoff   руководство. сервлеты

Обеспечьте правильное закрытие сеанса, уведомив затянувшиеся потокио закрытии сеанса с помощью метода destroy и дождитесь их окончания.

Затянувшиеся методы должны периодически проверять, не закрываетсяли система и при необходимости заканчивать работу и возвращатьзначения.

Отслеживание запросов службДля отслеживания запросов служб включите поле в класс вашего сервлета, котороебудет отсчитывать количество запущенных методов служб. Поле должно иметьметоды доступа для увеличения, уменьшения и возвращения своего значения. Таккак многочисленные потоки будут иметь доступ к полю, а метод destroy будетожидать, когда у поля будет значение ноль, доступы к полю должны бытьсинхронизированы. Объект в поле private под названием lock является объектом,который мы синхронизируем. Например:

public ShutdownExample extends HttpServlet { private int serviceCounter = 0; private Object lock = new Object(); ... // Методы доступа для serviceCounter protected void enteringServiceMethod() { synchronized(lock) {

serviceCounter++; } } protected void leavingServiceMethod() { synchronized(lock) { serviceCounter--; if (serviceCounter == 0 && isShuttingDown()) notifyAll(); } } protected int numServices() { synchronized(lock) {

return serviceCounter; } }}

Метод service должен увеличивать значение счетчика служб при каждом вводеметода и уменьшать его при каждом возвращении метода. Это один из тех случаев,когда подкласс HttpServlet должен переопределять метод service. Новый методдолжен вызвать super.service, чтобы сохранить всю первоначальнуюфункциональность метода HttpServlet.service.

protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{ enteringServiceMethod(); try { super.service(req, resp); } finally { leavingServiceMethod(); }}

15

Page 16: Bloch, bodoff   руководство. сервлеты

Обеспечение правильного завершения работыДля того чтобы можно было правильно завершить работу сервлета, ваш методdestroy не должен уничтожать какие-либо общие ресурсы до тех пор пока всезапросы служб не будут завершены. Первой частью является проверка счетчикаслужб. Затем необходимо уведомить затянувшиеся методы о том, что насталовремя завершить работу. По этой причине кроме обычных методов доступатребуется еще одно поле. Например:

public ShutdownExample extends HttpServlet { private boolean shuttingDown; ... // Методы доступа для shuttingDown protected void setShuttingDown(boolean flag) {

shuttingDown = flag; } protected boolean isShuttingDown() {

return shuttingDown; }}

Вот пример метода destroy, использующий данные поля для обеспеченияправильного завершения сеанса работы сервлета:

public void destroy() {

synchronized(lock) { /* Проверьте, не запущены ли методы служб * если да, остановите их. */ if (numServices() > 0) { setShuttingDown(true); }

/* Подождите пока остановятся все методы служб. */ while(numServices() > 0) { try { wait(); } catch (InterruptedException e) { } } }}

Правильное создание затянувшихся методовИ наконец, последнее, что остается сделать – это урегулировать работу всехзатянувшихся методов. Такие методы должны проверять значение поля,уведомляющего их об окончании сессии и при необходимости прекратить работу.Например:

public void doPost(...) { ... for(i = 0; ((i < lotsOfStuffToDo) && !isShuttingDown()); i++) {

try { partOfLongRunningOperation(i);} catch (InterruptedException e) {

} }}

16

Page 17: Bloch, bodoff   руководство. сервлеты

Урок 4: Сохранение состояния клиентаAPI сервлета обеспечивает два способа для отслеживания состояния клиента:

Управление сессией

Управлением (трекингом) сессии называется механизм, используемый сервлетамидля поддержания состояния с несколькими запросами от одного пользователя (тоесть запросы, приходящие из одного и того же броузера) за некоторый периодвремени.

Файлы-cookie

Cookie – это механизм, который использует сервлет для того, чтобы у клиентовсохранялось некоторое количество информации состояния, ассоциируемой спользователем. Сервлеты могут использовать информацию из cookie при каждомвходе клиента на сайт (к примеру, как при входе в систему при низком уровнебезопасности), во время навигации пользователя по сайту (к примеру, какхранилище свойств пользователя) или в обоих случаях.

Управление сессиейУправлением (трекингом) сессии называется механизм,используемый сервлетами для поддержания состояния с несколькими запросами отодного пользователя (то есть запросы, приходящие из одного и того же броузера)за некоторый период времени.

Сессии доступны клиенту на сервлетах. Это удобно для приложений, собранных измножества сервлетов. Например, в Duke's Bookstore используется управлениесессией для поиска книг, которые заказал пользователь. В этом примере у всехсервлетов есть доступ к сессии пользователя.

Для того чтобы управлять сессией

Получите для пользователя сессию (объект HttpSession).

Поместите или извлеките данные из объекта HttpSession.

Сделайте сессию недействительной (необязательно).

Получение сессии

Метод getSession объекта HttpServletRequest возвращает сессию пользователя. Когда вывызываете метод и значением его аргумента create является true, реализация принеобходимости создает сессию.

Чтобы правильно сохранить сессию, вы должны вызвать метод getSession прежде чембудет выведен какой-либо ответ (Если вы отвечаете используя метод Writer, тогда выдолжны вызвать getSession до получения доступа к Writer, а не перед отправкой каких-либо ответных данных).

В примере Duke's Bookstore для поиска книг в корзине пользователя используетсяупревление сессией. Ниже представлен пример того, как сервлет CatalogServlet получаетсессию для пользователя: public class CatalogServlet extends HttpServlet {

17

Page 18: Bloch, bodoff   руководство. сервлеты

public void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // Получение сессии и корзины пользователя HttpSession session = request.getSession(true); ... out = response.getWriter(); ... } }

Хранение и извлечение данных из сессии

Интерфейс HttpSession обеспечивает методы, которые сохраняют и возвращают:

Стандартные свойства сессии, такие, как идентификатор сессии

Данные приложения, которые хранятся как пара имя-значение, где имя – этоString, а значение – это объект, написанный на языке программирования Java(это напоминает java.util.Dictionary). Так как многочисленные сервлеты имеютдоступ к сессии пользователя, вам следует принять соглашение об именах,чтобы упорядочить имена связанные с данными приложения. Это поможетизбежать случайной перезаписи значений в одной сессии. Одним из такихсоглашений является servletname.name, где servletname – это полное имясервлета, включая его пакеты. Например, com.acme.WidgetServlet.state – этофайл-cookie сервлета com.acme.WidgetServlet с именем state.

Примечание: Java Servlet API версии 2.2 и 2.3 поддерживаемый Tomcat имеетметоды [get|set]Attribute для доступа к атрибутам сессии. Java Servlet API версии2.1 поддерживаемый JSDK 2.1 имеет методы [get|set]Value для доступа к атрибутамсессии.

В примере Duke's Bookstore для поиска книг в корзине пользователя используетсяуправление сессией. Ниже представлен пример того, как CatalogServlet получаетсессионный идентификатор пользователя, а также, как он получает и устанавливаетданные приложения связанные с сессией пользователя: public class CatalogServlet extends HttpServlet {

public void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // Получите сессию и корзину пользователя HttpSession session = request.getSession(true); ShoppingCart cart = (ShoppingCart)session.getAttribute("examples.bookstore.cart");

// Если у пользователя нет корзины, создайте ее if (cart == null) { cart = new ShoppingCart(); session.putAttribute("examples.bookstore.cart", cart); } ... } }

18

Page 19: Bloch, bodoff   руководство. сервлеты

По причине того, что объект может быть связан с сессией, Duke's Bookstore отслеживаеткниги, которые заказал пользователь в объекте. Типом объекта является ShoppingCart, акаждая книга, которую заказывает пользователь, хранится в корзине в виде объектаShoppingCartItem. К примеру, вот, что происходит при использовании метода doGetсервлета CatalogServlet: public void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpSession session = request.getSession(true); ShoppingCart cart =(ShoppingCart)session.getAttribute("examples.bookstore.cart"); ... // Чек на сумму оплаты добавляется в корзину String bookId = request.getParameter("Buy");

// Если пользователь хочет добавить книгу, добавьте ее и выведитерезультат String bookToAdd = request.getParameter("Buy"); if (bookToAdd != null) { BookDetails book = database.getBookDetails(bookToAdd);

cart.add(bookToAdd, book); out.println("<p><h3>" + ...); } }

В конечном счете, обратите внимание, что сессия может быть обозначена как новая. Новаясессия вызывает метод isNew класса HttpSession, чтобы вернуть значение true, этоуказывает, например, на то, что клиент еще не знает о сессии. В новой сессии нетассоциированных данных.

Вы должны разбираться с ситуациями, включающими новые сессии. В вышеупомянутомпримере Duke's Bookstore, если у пользователя нет корзины (есть только данные,ассоциированные с сессией), сервлет ее создает. С другой стороны, если вам нужнаинформация от пользователя (такая, как имя пользователя), необходимая для началасессии, вы можете просто перенаправить пользователя на «начальную страницу», где высобираете всю необходимую информацию.

Закрытие сессии

Сессия пользователя может быть закрыта вручную, или, в зависимости от того, гдезапущен сервлет, автоматически (например, Java Web-сервер автоматически закрываетсессию, в которой в течении определенного времени не запрашивалась ни одна страница,время 30 минут ставится по-умолчанию). Закрыть сессию - значит удалить из системыобъект HttpSession и его значения.

Для того, чтобы вручную закрыть сессию, используйте метод invalidate. В некоторыхприложениях определен момент закрытия сессии. В примере Duke's Bookstore сессияпользователя закрывается после покупки книг. Это происходит в ReceiptServlet: public class ReceiptServlet extends HttpServlet {

public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

...

19

Page 20: Bloch, bodoff   руководство. сервлеты

scart =(ShoppingCart)session.getAttribute("examples.bookstore.cart"); ... // Очистите корзину путем закрытия сессии session.invalidate();

// Установите заголовок content type перед получением доступак Writer response.setContentType("text/html"); out = response.getWriter(); ... } }

Работа с броузерами

По умолчанию в управлении сессией используются файлы-cookie для того, чтобыассоциировать идентификатор сессии с пользователем. Чтобы поддержать пользователей,имеющих доступ к сервлету с помощью броузера, не поддерживающего файлы-cookie илине установленного на прием cookie, необходимо использовать перезапись URL.

Используя перезапись URL вы вызываете методы, которые при необходимости включаютв ссылку сессионный ID. Вы должны вызывать эти методы для каждой ссылки в ответесервлета.

Метод, который ассоциирует сессионный ID c URL, называетсяHttpServletResponse.encodeURL. Если вы перенаправляете пользователя на другуюстраницу, метод, предназначенный для ассоциирования сессионного ID с новым URL,называется HttpServletResponse.encodeRedirectURL.

URL-кодирование и переориентирующие методы кодирования выбирают, где необходимоперезаписать URL, и возвращают URL измененным или неизмененным (правила для URLи переориентированных URL отличаются, но в общем, если сервер определяет, чтоброузер поддерживает файлы-cookies, тогда URL не перезаписывается).

Примечание: В данном параграфе показан код, который является частью примераDuke's Bookstore для Tomcat. Перезапись URL не является частью Duke's Bookstoreдля JSDK 2.1.

Версия примера Duke's Bookstore, которая использует перезапись URL имеет в сервлетеCatalogServlet следующий код: public class CatalogServlet extends HttpServlet {

public void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // Получите сессию, корзину пользователя, Writer и т.д. ... // затем напишите данные ответа out.println("<html>" + ...); ... // Получите каталог и отправьте его, хорошо отформатировав BookDetails[] books = database.getBooksSortedByTitle(); ... for(int i=0; i < numBooks; i++) { ... // Выведите информацию по каждой книге в две строки

20

Page 21: Bloch, bodoff   руководство. сервлеты

out.println("<tr>" + ...

"<a href=\"" + response.encodeURL("/bookstore/bookdetails?bookId=" + bookId) + "\"> <strong>" + books[i].getTitle() + " </strong></a></td>" + ...

"<a href=\"" + response.encodeURL("/bookstore/catalog?Buy=" +bookId) + "\"> Add to Cart </a></td></tr>" +

} } }

Обратите внимание, что CatalogServlet выдает пользователю две ссылки по каждой книге.В одной ссылке даются детали книги, а другая позволяет добавить книгу в корзину. Обессылки URL будут перезаписаны, так как, каждая ссылка, которую сервлет выдаетпользователю, должна быть перезаписана в случае использования перезаписи URL.

Если пользователь нажимает на ссылку с перезаписанным URL, сервлет распознает иизвлекает сессионный ID. Затем метод getSession использует сессионный ID дляполучения объекта HttpSession пользователя.

С другой стороны, если броузер пользователя не поддерживает файлы-cookie ипользователь нажимает на неперезаписанный URL, сессия пользователя теряется.Сервлет, связанный с этой ссылкой, создает новую сессию, но в ней нет данных,ассоциированных с предыдущей сессией. Когда сервлет теряет данные сессии, их можносчитать потерянными для всех сервлетов сессии. Если ваш сервлет должен поддерживатьклиентов, которые не подерживают или не принимают файлы-cookie, обязательноиспользуйте перезапись URL.

Использование файлов-cookiesС помощью cookie сервер (или сервлет, в качестве части сервера) отправляетклиенту для хранения определенную информацию, которую он сможет извлечьпозже. Сервлеты посылают файлы-cookie клиентам путем добавления полей взаголовки HTTP-ответов. Клиенты автоматически возвращают cookies путемдобавления полей в заголовки HTTP-запросов.

Каждый HTTP-запрос и заголовок ответа имеет название и собственное значение.Наример, файлом-cookie может быть заголовок с названием BookToBuy изначением 304qty1, указывающий приложению, что пользователь желает купитьодну книгу, инвентарный номер которой 304 (приложения имеют свойсобственный набор файлов-cookie и их значений).

У нескольких файлов-cookie может быть одно имя. Например, сервлет можетотправить два файла-cookie с одним названием BookToBuy. У одного может бытьзначение 304qty1, как в предыдущем примере, а у другого – 301qty3. Эти файлыуказывают на то, что пользователь желает купить одну книгу с инвентарнымномером 304 и три книги с номером 301.

Кроме имени и значения вы можете также обеспечить необязательные атрибуты,такие как комментарии. Современные web-броузеры не всегда правильно

21

Page 22: Bloch, bodoff   руководство. сервлеты

обрабатывают дополнительные атрибуты, так что вам не следует особо на нихполагаться.

Сервер может обеспечить клиенту один или несклько файлов-cookie. Ожидается,что программное обеспечение клиента, такое как web-броузер, поддерживаетдвадцать cookies на один хост, объем каждого, как минимум, - четыре килобайта.

Когда вы отправляете клиенту файл-cookie, стандартные кэши HTTP/1.0 не будуткэшировать страницу. На данный момент javax.servlet.http.Cookie не поддерживаетмодели управления кэшем HTTP/1.1.

Файлы-cookie, которые хранятся у клиента, предназначены для одного сервера ивыдаются только ему. В сервере может содержаться много различных сервлетов.Сервлет Duke's Bookstore состоит из множества сервлетов, запускаемых на одномсервере. Файлы-cookie возвращаются серверу, поэтому они общие для всехсервлетов, исполняемых на данном сервере. В данном параграфе этопроиллюстрированно с помощью примеров, где сервлеты CatalogServlet и ShowCartработают с одинаковыми файлами-cookie.

Примечание: В данном параграфе показан код, который не является частьюпримера Duke's Bookstore. В Duke's Bookstore можно было бы применить такой код,если бы в нем для поиска книг из заказа клиента вместо управления сессиейиспользовались файлы-cookie.Так как cookie не являются частью сервлета Duke'sBookstore, рассмотривайте примеры данного параграфа, как псевдо-код.

Для того, чтобы отправить файл-cookie

1. Создайте экземпляр объекта Cookie

2. Установите атрибуты

3. Отправьте файл-cookie

Для того, чтобы получить информацию из файла-cookie:

1. Извлеките все файлы-cookie из запроса пользователя

2. Сделайте поиск по имени одного или нескольких файлов-cookie, используядля этого стандартные методы программирования

3. Получите значения из найденных файлов-cookie

Создание файла-cookie

Конструктор для класса javax.servlet.http.Cookie создает файл-cookie, у которого уже естьначальное имя и значение. Позже вы сможете изменить значение файла-cookie припомощи метода setValue.

Имя файла-cookie должно состоять из символов, совместимых с HTTP/1.1. Именаявляются строками, в которых не должны содержатся специальные символы, описанные вдокументе (буквенно-цифровые строки допустимы). В добавок к этому, допустимыимена, начинающиеся со знака доллар ($), зарезервированые в описании .

22

Page 23: Bloch, bodoff   руководство. сервлеты

Значением файла-cookie может быть любая строка, но мы не даем гарантии того что,принимая нулевое значение, он поведет себя одинаково при работе в разных броузерах.Вдобавок, если вы отправляете файл-cookie, который компилирован согласнооригинальной спецификации Netscape, не используйте пробелов и любых изнижеперечисленных символов:

[ ] ( ) = , " / ? @ : ;

Если сервлет выдает ответ пользователю с Writer, создайте файл-cookie до того какполучить доступ к Writer (из за того, что файлы-cookie отправляются клиенту какзаголовок, заголовки должны быть написаны до получения доступа к Writer).

Если бы в CatalogServlet для поиска книг из заказа клиента использовались файлы-cookies,сервлет мог бы создать файл-cookie подобным образом: public void doGet (HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException { // Проверьте, завершены ли добавления в корзину покупателя String bookId = request.getParameter("Buy");

// Если пользователь хочет добавить книгу, запомните ее путемдобавления файла-cookie if (bookId != null) { Cookie getBook = new Cookie("Buy", bookId); ... }

// установите заголовок content-type до получения доступа к Writer response.setContentType("text/html");

// а теперь получите writer и запишите данные ответаPrintWriter out = response.getWriter();

out.println("<html>" + "<head><title> Book Catalog </title></head>" + ...); ... }

Установка атрибутов файла-cokie

Класс Cookie обеспечивает набор методов для установки значений и атрибутов файлов-cookie. Это самый правильный способ, данные методы описаны в Java-документации вразделе, посвященном классу Cookie.

В следующем примере устанавливается поле комментариев файла-cookie сервлетаCatalogServlet. В поле комментариев описана цель файла-cookie. public void doGet (HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException { ... // Если пользователь хочет добавить книгу, запомните ее, добавив дляэтого файл-cookie if (values != null) { bookId = values[0]; Cookie getBook = new Cookie("Buy", bookId); getBook.setComment("User wants to buy this book " + "from the bookstore.");

23

Page 24: Bloch, bodoff   руководство. сервлеты

} ... }

Вы также можете установить максимальный «возраст» файла-cookie. Данный атрибутполезен, например, для удаления cookie. Итак, если бы Duke's Bookstore искал книги иззаказа клиента с помощью файлов-cookie, можно было бы использовать данный атрибутдля удаления книги из заказа. Пользователь переносит книгу из корзины вShowCartServlet, его код выглядит примерно так: public void doGet (HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

... /* Обработайте все ожидаемые удаления из корзины */ String bookId = request.getParameter("Remove"); ... if (bookId != null) { // Найдите файл-cooke, который имеет отношение к удаляемой книге ... // Удалите файл-cooke, приравняв нулю его максимальный«возраст» thisCookie.setMaxAge(0); ... } // также установите заголовок content type перед получением доступа кWriter response.setContentType("text/html"); PrintWriter out = response.getWriter();

// Выведите ответ out.println("<html> <head>" + "<title>Your Shopping Cart</title>" + ...);

Отправка файла-cookie

Cookie отправляются как заголовки ответа клиенту. Их добавляют вместе с методомaddCookie класса HttpServletResponse. Если для возвращения текстовых данных клиентувы используете Writer, то прежде чем вызывать метод getWriter сервлетаHttpServletResponse необходимо вызвать метод addCookie.

В продолжении примера CatalogServlet рассмотрите код для отправки cookie: public void doGet (HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException { ... // Если пользователь хочет добавить книгу, запомните ее при помощиcookie if (values != null) { bookId = values[0]; Cookie getBook = new Cookie("Buy", bookId); getBook.setComment("User has indicated a desire " + "to buy this book from the bookstore."); response.addCookie(getBook); } ... }

24

Page 25: Bloch, bodoff   руководство. сервлеты

Извлечение файлов-cookie

Клиенты возвращают файлы-cookie в виде полей, добавленных к заголовкам HTTPзапросов. Чтобы извлечь cookie, необходимо извлечь все файлы-cookie, используя методgetCookies класса HttpServletRequest.

Метод getCookies возвращает массив из объектов Cookie, в котором вы можетепроизводить поиск для получения желаемых файлов-cookie (запомните, что одновременномножество различных cookie может иметь одинаковые имена, узнать имя cookie можно спомощью метода getName).

Продолжение примера ShowCartServlet: public void doGet (HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

...

/* Обработайте все ожидаемые удаления из корзины */ String bookId = request.getParameter("Remove"); ... if (bookId != null) { // Найдите cookie, который относится к удаляемой книге Cookie[] cookies = request.getCookies(); ...

// Удалите cookie книги, для этого необходимо установить егомаксимальный «возраст» на «ноль» thisCookie.setMaxAge(0); } // так же до получения доступа к Writer установите content-typeзаголовок response.setContentType("text/html"); PrintWriter out = response.getWriter();

// Выведите ответ out.println("<html> <head>" + "<title>Your Shopping Cart</title>" + ...);

Извлечение значения cookie

Поиск значений файлов-cookie осуществляется с помощью метода getValue. Продолжимпример ShowCartServlet: public void doGet (HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException { ... /* Обработайте все ожидаемые удаления из корзины */ String bookId = request.getParameter("Remove"); ... if (bookId != null) { // Найдите cookie, который относится к удаляемой книге Cookie[] cookies = request.getCookies(); for(i=0; i < cookies.length; i++) {

25

Page 26: Bloch, bodoff   руководство. сервлеты

Cookie thisCookie = cookie[i]; if (thisCookie.getName().equals("Buy") && thisCookie.getValue().equals(bookId)) { // Удалите cookie книги, для этого необходимо приравнять нулю еемаксимальный «возраст» thisCookie.setMaxAge(0); } } } // так же до получения доступа к Writer установите content-typeзаголовок response.setContentType("text/html"); PrintWriter out = response.getWriter();

// Выведите ответ out.println("<html> <head>" + "<title>Your Shopping Cart</title>" + ...);

Урок 5: Утилиты servletrunnerНаписав сервлет, вы можете протестировать его функциональность спомощью утилиты servletrunner.

Srvletrunner является небольшим многопоточным процессом, которыйобрабатывает запросы, обращенные к сервлетам. Из-за своеймногопоточности, его можно использовать для одновременного запусканескольких сервлетов или для тестирования одного сервлета, которыйобрабатывает запросы клиентов, вызывая при этом другие сервлеты.

В отличие от некоторых web-серверов, у servletrunner нет автоматическогообновления сервлетов. Однако, вы можете остановить и перегрузитьservletrunner, для того чтобы запустить новую версию сервлета.

Установка свойств сервлета

Возможно, для запуска сервлета вам понадобится определить некоторыеданные. К примеру, если сервлету требуются параметры инициализации, этиданные необходимо установить еще до запуска servletrunner.

Запуск servletrunner

Запускать утилиту servletrunner можно после установки файла свойств. Вданном параграфе описано, как это сделать.

Установка свойств сервлетаСвойствами называют пары ключ-значение, используемые для настройки,создания и инициализации сервлета. Например,servlet.catalog.code=CatalogServlet – это свойство с ключомservlet.catalog.code и значением CatalogServlet.

В утилите servletrunner содержится два свойства для сервлетов:servlet.name.codeservlet.name.initargs

26

Page 27: Bloch, bodoff   руководство. сервлеты

Свойство codeЗначением свойства servlet.name.code является полное имя класса сервлета,включая его пакет. Например:

servlet.bookdb.code=database.BookDBServlet

Свойство servlet.name.code дает название вашему сервлету, ассоциируя имя(в данном примере это bookdb) с классом (database.BookDBServlet).

Свойство initargsВ значении свойства servlet.name.initArgs содержатся параметрыинициализации сервлета. Синтаксис отдельного параметра следующий:parameterName=parameterValue. Полное свойство (полная пара ключ-значение) должно быть отдельной логической строкой. Для упрощенияпрограммного кода, рекомендуется использование синтаксиса backquote, чтопозволит охватить как можно больше строк файла в свойстве. Например,если сервлет базы данных считывает данные из файла, то начальныйаргумент сервлета выглядит примерно так:

servlet.bookdb.initArgs=\ dbfile=servlets/DatabaseData

Многочисленные параметры инициализации определены, как список сразделяющими запятыми.К примеру, если сервлет базы данных соединен среальной базой данных, его начальный аргумент может выглядетьследующим образом:

servlet.bookdb.initArgs=\user=duke,\password=dukes_password,\url=fill_in_the_database_url

Файл PropertyСвойства хранятся в текстовом файле, имя которого, по умолчаниюservlet.properties (его можно изменить, запустив servletrunner). В файлесодержатся свойства для всех сервлетов, которые запускает servletrunner.Далее представлен файл свойств servlet.properties для примера Duke'sBookstore:

# В данном файле содержатся свойства сервлетов Duke's Bookstore.

# Duke's Book Store –- главная страница servlet.bookstore.code=BookStoreServlet # Сервлет, управляющий базой данных книг servlet.bookdb.code=database.BookDBServlet # Просмотр всех книг магазина servlet.catalog.code=CatalogServlet # Отображение информации о заданной книге servlet.bookdetails.code=BookDetailServlet # Просмотр выбранных для покупки книг servlet.showcart.code=ShowCartServlet # Сбор информации, необходимой для покупки выбранных книг

27

Page 28: Bloch, bodoff   руководство. сервлеты

servlet.cashier.code=CashierServlet # Предоставление квитанции пользователю, купившему книги servlet.receipt.code=ReceiptServlet

Запуск servletrunnerServletrunner расположен в директории <jsdk>/bin. Если данная директориянаходится на вашей траектории поиска, запускать данную утилиту будетгораздо проще. К примеру:% setenv PATH /usr/local/jsdk/bin: (on UNIX)

C> set PATH=C:\jsdk\bin;%PATH% (on Win32)

При запуске servletrunner с флажком -help, появится только текстовоесообщение, сама утилита запущена не будет:

% servletrunner -helpUsage: servletrunner [options]Options: -p port the port number to listen on -b backlog the listen backlog -m max maximum number of connection handlers -t timeout connection timeout in milliseconds -d dir servlet directory -r root document root directory -s filename servlet property file name -v verbose output%

Чтобы увидеть значения, установленные по умолчанию, вызовитеservletrunner с опцией –v. Утилита запустится, а вы сможете остановить ее,получив всю необходимую информацию, если вы еще не готовы к запускуили же, если вы хотите изменить значения по умолчанию. Например, всистеме Unix команда kill останавливает servletrunner.

% servletrunner -vServer settings: port = 8080 backlog = 50 max handlers = 100 timeout = 5000 servlet dir = ./examples document dir = ./examples servlet propfile = ./examples/servlet.properties

Примечание: В примерах, приведенных выше servlet.dir, document.dir и директория для servlet.propfileсодержат точку («.»). Точка указывает на текущую рабочую директорию. Текущей рабочей директориейобычно является директория, из которой были запущена программа. Однако, в данном случае точкауказывает на директорию, в которой установлен servlet development kit.

При запуске servletrunner не из инсталляционной директории, утилитапервым делом изменит текущую рабочую директорию (и, следовательно, то,что обозначалось точкой «.», – рабочая директория, тоже изменится).

Запустив servletrunner, вы уже можете использовать его для тестированиясвоих сервлетов.

Урок 6: исполнение сервлетов28

Page 29: Bloch, bodoff   руководство. сервлеты

В данном уроке показано несколько способов вызова сервлетов:

Ввод URL сервлета в адресной строке броузераСервлеты можно вызывать напрямую, набрав их URL в адресной строке броузера. Именно так вы можетепопасть на главную страницу сервлета Duke's Bookstore. В данном параграфе показан общий вид адресаURL сервлета.

Вызов сервлета из HTML страницыURL сервлетов могут использоваться в тэгах HTML, где могут быть найдены URL для CGI-скриптов илифайлов. Далее вы узнаете об использовании URL в качестве адресата ссылки, как действие, и какместоположение, которые используются в случае, когда META тэг указывает на необходимость вобновлении страницы. В данном параграфе предполагается знание HTML. Если же вы не знаете HTML, вылегко сможете изучить его по информации из различных книг, а так же, воспользовавшись спецификациейHTML.

Из другого сервлетаОдни сервлеты могут вызывать другие сервлеты. Если два сервлета запущены на различных серверах, одинможет выполнить HTTP-запрос другого. Если оба сервлета исполняются в одной сетевой среде, тогдасервлет может вызывать общие методы другого сервлета напрямую.

В данном уроке предполагается, что:Ваш компьютер, или localhost, запускает servletrunner или web-броузер с поддержкой сервлетов,такой как the Java Web Server, используя порт 8080.Duke's Bookstore расположен в корневой директории, относящейся к данному процессу. Дляservletrunner это означает, что файлы класса находятся в директории сервлета, определенной опциейby the -d option.

Если по обоим положениям у вас все совпадает, тогда вы сможете вызватьсервлет, введя URL адреса, данные в примере.

Вызов сервлетов из броузеровОбщая форма URL сервлета, где servlet-name – это имя, которое вы даливашему сервлету, выглядит так:

http://machine-name:port/servlet/servlet-name

Например, сервлет, доставляющий главную страницу Duke's Bookstoreимеет свойство servlet.bookstore.code=BookStoreServlet. Чтобы увидетьглавную страницу примера, введите в адресную строку броузераследующую информацию:

http://localhost:8080/servlet/bookstore

В URL адресах сервлетов могут содержаться запросы, такие как HTTP GETзапросы. К примеру, сервлет, с помощью которого пользователь получаетинформацию о желаемой книге, принимает инвентарный номер этой книги ввиде запроса. Именем сервлета является bookdetails. URL, позволяющийсервлету получать и отображать всю информацию о заданной книгемагазина выглядит так:

Вызов сервлетов из HTML страницыЧтобы вызвать броузер из HTML страницы, просто используйте URLсервлета в соответствующем HTML тэге (в данном разделе предполагаетсязнание HTML). Тэги, в которые входят URLs сервлета, начинаются сзакладок, форм и мета-тэгов.

29

Page 30: Bloch, bodoff   руководство. сервлеты

В данном разделе используются сервлеты ShowCart, Cashier, и Receipt изпримера Duke's Bookstore. Здесь это бланк запроса, который отображаетсясервлетом, когда вы смотрите на свою корзину и покупаете книги.

Самый прямой доступ к сервлету ShowCart можно получить путем нажатияна ссылку Show.Cart на основной странице Duke's Bookstore. Если вашservletrunner или web-броузер установлен на запуск примера, перейдите наглавную страницу магазина, как показано в предыдущем разделе. Радишутки можно попробовать добавить книгу в корзину до использованиясервлета ShowCart.

Примеры URL сервлетов в HTML тэгах

В странице, возвращенной ShowCartServlet есть набор закладок, местомназначения каждой из которых служит сервлет. Далее показан код одной изтаких закладок:

public class ShowCartServlet extends HttpServlet { public void doGet (HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException { ... out.println(... + "<a href=\"" + response.encodeUrl("/servlet/cashier") + "\">Check Out</a> " + ...); ... } ... }В результате получаем HTML страницу, в которой содержится следующая закладка: <a href="http://localhost:8080/servlet/cashier>Check Out</a>

Если страница сервлета showcart отображается в вашем броузере, вы можетеувидеть закладку, просмотрев исходный код этой страницы. Далее нажмитена ссылку. Сервлет cashier возвратит страницу, содержащую следующийпример.

Страница, отображаемая сервлетом cashier, представляет форму, в которойзапрашивается имя пользователя и номер банковской кредитной карточки.Код, выводящий тэг формы, выглядит следующим образом:

public class CashierServlet extends HttpServlet { public void doGet (HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException { ... out.println(... + "<form action=\"" + response.encodeUrl("/servlet/receipt") + "\" method=\"post\">" + ... "<td><input type=\"text\" name=\"cardname\"" + "value=\"Gwen Canigetit\" size=\"19\"></td>" + ... "<td><input type=\"submit\"" + "value=\"Submit Information\"></td>" +

30

Page 31: Bloch, bodoff   руководство. сервлеты

... "</form>" + ...); out.close(); } ... }В результате этого кода получена HTML страница, в которой есть следующий тэг, используемый в началеформы: <form action="http://localhost:8080/servlet/receipt" method="post">

Если страница сервлета cashier отображается в броузере, то в исходном кодеможно увидеть тэг, с которого начинается форма. Подтвердите форму.Сервлет receipt возвратит страницу, содержащую следующий пример.Страница сервлета быстро перегружается, поэтому, если вы хотите увидетьHTML код страницы, поторопитесь!

В странице, возвращенной сервлетом receipt есть мета тэг, использующийURL сервлета, как часть значения атрибута http-equiv. В частности, тэгуказывает странице перегрузить главную страницу Duke's Bookstore послевыдачи благодарности за покупку. Далее показан код для этого тэга:

public class ReceiptServlet extends HttpServlet { public void doPost(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException { ... out.println("<html>" + "<head><title> Receipt </title>" + "<meta http-equiv=\"refresh\" content=\"4; url=" + "http://" + request.getHeader("Host") + "/servlet/bookstore;\">" + "</head>" + ... } ... }

В результате этого кода получена HTML страница, в которой содержится следующий тэг: <meta http-equiv="refresh" content="4; url=http://localhost:8080/servlet/bookstore;">

Вызов сервлетов из сервлетовДля того, чтобы один сервлет мог вызывать другой, вам необходимо или:Чтобы сервлет мог выполнять HTTP запросы другого сервлета. Открытие соединения с URLописано в уроке «Работа с URL» данного руководства, илиЧтобы сервлет мог напрямую вызывать public-методы другого сервлета в случае, если оба сервлетаработают на одном сервере.

Данный раздел относится ко второму пункту условия. Для того чтобынапрямую вызвать public-методы другого сервлета необходимо:Знать имя вызываемого сервлетаПолучить доступ к объекту Servlet сервлетаВызвать public-метод сервлета

Чтобы получить доступ к объекту Servlet, используйте метод getServletобъекта ServletContext. Получите объект ServletContext из объекта

31

Page 32: Bloch, bodoff   руководство. сервлеты

ServletConfig, который хранится в объекте Servlet. Данный пример позволитвам это сделать. Когда сервлет BookDetail вызывает сервлет BookDB,сервлет BookDetail получает объект Servlet сервлета BookDB таким образом:

public class BookDetailServlet extends HttpServlet {

public void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ... BookDBServlet database = (BookDBServlet) getServletConfig().getServletContext().getServlet("bookdb"); ... }}

Если у вас есть объект servlet, вы можете вызвать любой из public-методовсервлета. Например, сервлет BookDetail вызывает метод getBookDetailsсервлета BookDB:

public class BookDetailServlet extends HttpServlet {

public void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ... BookDBServlet database = (BookDBServlet) getServletConfig().getServletContext().getServlet("bookdb"); BookDetails bd = database.getBookDetails(bookId); ... }}

Будьте осторожными при вызове других методов сервлета. Если сервлет, ккоторому вы хотите обратиться, реализует интерфейс SingleThreadModel,ваш вызов сервлета может нарушить его однопотоковую природу (у серверанет возможности вмешаться и гарантировать то, что вы вызываете сервлетне в тот момент, когда он взаимодействует с другим клиентом). В этомслучае вместо того, чтобы напрямую вызывать методы другого сервлета,необходимо сделать HTTP запрос.

Руковоство: сервлеты: Окончание

Trail: Servlets: End of TrailИтак, вы настигли окончания руководства по сервлетам.

32

Page 33: Bloch, bodoff   руководство. сервлеты

Передохните – выпейте чашечку горячего кофе (hot java).

Что дальше?Если вы уже успели перевести дыхание, предлагаем вам решить, что же делатьдальше. Вы можете вернуться на карту руководств и посмотреть варианты там илиперейти сразу на страницы руководств родственных тематик:Написание аплетов: Отсюда можно начать изучение написания аплетов.Безопасность в JDK 1.2: При написании взаимодействующих программ вампонадобится дополнительная информация о системах безопасности. Если вы ещепользуетесь версией 1.1, можно пойти по ссылке Безопасность в JDK 1.1.

33