Обучение Java. Сервлеты

         

Примеры 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>" + ... "</form>" + ...); out.close(); } ... }

Код генерирует HTML страницу с тегом:

<form action="http://localhost:8080/servlet/receipt" method="post">

Вы увидите эту ссылку, если просмотрите код страницы загруженный сервлетом cashier. Отправьте форму. Сервлет receipt

вернет Вам страницу, которая содержит следующий пример. Страница, загруженная сервлетом 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;">

Выполнение сервлетов


Этот урок покажет Вам несколько путей выполнения сервлетов: 



Вызов сервлета из адресной строки броузера


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



Вызов сервлета из HTML страницы


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

.) Теги, в которые можно помещать URL включают те, что начинаются с "<A..." и "<FORM...", а также мета теги.

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

Для более быстрого доступа к сервлету ShowCart, нажмите ссылку Show Cart

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


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

.

 

Для урока необходимо: 

Компьютер, localhost, с запущенным JSDK сервером (JSDK 2.1) или servletrunner (JSDK 2.0) или сервлет-совместимый веб сервер, такой как Java Web Server

на порту 8080 

Пример, Duke's Bookstore, размещенный в корневой директории для сервлетов. Для сервера из пакета JSDK 2.1 означает, что классы должны быть размещены в поддиректории WEB-INF корневого каталога сервера. Для утилиты servletrunner из пакета JSDK 2.0, что классы находятся в директории сервлета указанной в -d опции.  

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



Вызов сервлетов из адресной строки броузера


URL для сервлета имеет следующий вид, где имя-сервлета

относится к имени, которое Вы присвоили сервлету: 

http://имя-хоста:port/servlet/имя-сервлета

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

http://localhost:8080/servlet/bookstore

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

http://localhost:8080/servlet/bookdetails?bookId=203



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


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



Interface javax.servlet.SingleThreadModel


public interface SingleThreadModel

Defines a "single" thread model for servlet execution. This empty interface allows servlet implementers to specify how the system should handle concurrent calls to the same servlet.

If the target servlet is flagged with this interface, the servlet programmer is guaranteed that no two threads will execute concurrently the service method of that servlet. This guarantee is ensured by maintaining a pool of servlet instances for each such servlet, and dispatching each service call to a free servlet.

In essence, if the servlet implements this interface, the servlet will be thread safe. Note that this will not prevent synchronization problems associated with accessing shared resources (such as static class variables or classes outside the scope of the servlet).



Объекты HttpServletRequest


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

Чтобы получить данные клиента:

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

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

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

Если вы ожидаете текстовую информацию, метод getReader возвращает объект BufferedReader, чтобы Вы могли использовать, считать необработанные данные. 

Если Вы ожидаете двоичные данные, метод getInputStream возвращает объекта ServletInputStream, чтобы Вы могли использовать, считать необработанные данные. 

Замечание:

Используйте либо метод getParameter[Values] либо один из других методов для собственного разбора данных. Они не могут быть использованы вместе в одном запросе.

 



Объекты HttpServletResponse


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

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

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

 

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

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

 



Обработка GET и POST запросов


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

doGet, для обработки GET, условный GET, и HEAD запросов.  

doPost, для обработки POST запросов  

doPut, для обработке PUT запросов

doDelete, для обработки DELETE запросов  

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

Метод service также вызывает метод doOptions, когда сервлет получает запрос OPTIONS (ОПЦИИ), и метод doTrace, когда сервлет получает запрос TRACE. По умолчанию метод doOptions автоматически определяет, какие опции HTTP поддерживаются, и возвращает эту информацию. По умолчанию метод doTrace объединяет ответ с сообщением, содержащим все заголовки, отправленные в запросе трассировки. Эти методы обычно не переопределяются.

 



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


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



Получение описания сервлета


Некоторые приложения, такие как Java Web Server Administration Tool

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

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

Следующий пример демонстрирует описание сервлета BookStoreServlet:  

public class BookStoreServlet extends HttpServlet { ... public String getServletInfo() { return "The BookStore servlet returns the " + "main web page for Duke's Bookstore."; } }



Потоковый вывод


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

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

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

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

 


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

Чтобы сервлет обслуживал только одного клиента в определенный момент времени, Вам надо реализовать интерфейс SingleThreadModel

в добавление к наследованию класса HttpServlet.

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

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

public class ReceiptServlet extends HttpServlet implements SingleThreadModel {

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



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


Управление запросами GET влечет за собой переопределения метода doGet. Следующий пример показывает, как сервлет BookDetailServlet делает это. Методы, описанные в разделе , выделены жирным шрифтом

public class BookDetailServlet extends HttpServlet {

public void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ... // устанавливает заголовок "content-type" // прежде сем получить доступ к Writer

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 закрывается.

 



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


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

 



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


Управление запросами POST влечет за собой переопределения метода doPost. Следующий пример показывает, как сервлет ReceiptServlet делает это. Методы, описанные в разделе , выделены жирным шрифтом

public class ReceiptServlet extends HttpServlet {

public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ... // устанавливает заголовок "content-type" // прежде сем получить доступ к Writer

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 сервлет поддерживает запросы клиента через метод service. Метод service поддерживает стандартные HTTP запросы клиента, отправляя каждый запрос соответствующему разработанному для обработки данного запроса методу. Например, метод service вызывает метод doGet показанный ранее в simple example servlet.

 



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


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

 


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

Объект HttpServletRequest, который возвращает данные от

клиента. 

Объект HttpServletResponse, который возвращает ответ к

клиенту.

 



Использование закладок


Закладки (Cookies) это способ сервера (или сервлета, как части сервера) посылать клиенту на хранение часть информации, чтобы потом получать эту информацию от клиента. Сервлеты посылают закладки клиенту, добавляя код в ответе в HTTP заголовки. Клиенты автоматически возвращают закладки, добавляя код в запросы в HTTP заголовках.

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

Множественные закладки могут иметь одно и тоже имя. Например, сервлет может послать две закладки, имя которых BookToBuy; одна может иметь величину приведенную выше, 304qty1, когда вторая - значение 301qty3. Эти закладки будут означать, что пользователь хочет купить одну книгу с номером 304, и три книги с номером 301.

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

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

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

Закладки, которые клиент сохранил для сервера, возвращаются клиентом только этому серверу. Сервер может включать множество сервлетов; пример Duke's Bookstore

сделан из нескольких сервлетов работающих на одном сервере. Потому как закладки возвращаются серверу, сервлеты работающие на этом сервере совместно используют эти закладки. Пример в этом разделе иллюстрирует этот факт на сервлетах CatalogServlet и ShowCart, работающих с одними закладками.


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

Чтобы отправить закладку, 

объект Cookie


 

 

Чтобы извлечь информацию из закладки, 

из пользовательского запроса

Найдите закладку или закладки с именем, которое Вас интересует, используя стандартные программные операции 

, которые были найдены

 


Отправка закладки


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

Продолжая пример CatalogServlet, приводим следующий код для отправки закладок:

public void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ... //Если пользователь хочет добавить книгу, запоминаем это добавляя закладку

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);

} ... }



Отслеживание сессии


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

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

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

Чтобы использовать отслеживание сессии,

Создайте для пользователя сессию (объект HttpSession

). 

Сохраняйте или читайте данные из объекта HttpSession.

Уничтожьте сессию (необязательное).

 



Отслеживания сессии


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

 



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


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

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

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

public class CatalogServlet extends HttpServlet {

public void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // Получаем сесию пользователя и корзину для покупок

HttpSession session = request.getSession(true);

... out = response.getWriter(); ... } }



Получение значения закладки


Чтобы получить значение закладки, используйте ее метод getValue. Продолжая пример сервлета ShowCartServlet:

public void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ... //Управление удалением из корзины

String bookId = request.getParameter("Remove"); ... if (bookId != null) { //Находим закладку соответствующую данной книге

Cookie[] cookies = request.getCookies(); for(i=0; i cookies.length; i++) { Cookie thisCookie=cookie[i]; if (thisCookie.getName().equals("Buy") { thisCookie.getValue().equals(bookId); i++) } //Удаляем закладку устанавливая ее возраст равным нулю

thisCookie.setMaxAge(0); } } }

//прежде чем начать вывод, устанавливаем тип содержимого

response.setContentType("text/html"); PrintWriter out = response.getWriter();

//Выводим ответ

out.println("<html> <head>" + "<title>Your Shopping Cart</title>" + ...); ... }



Сохранение и получение данных сессии


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

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

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

Пример 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.getValue(session.getId());

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

if (cart == null) { cart = new ShoppingCart(); session.putValue(session.getId(), cart);

} ... } }

Потому как объект может быть ассоциирован с сессией, пример 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.getValue(session.getId()); ... // Смотрим есть ли покупки в корзине

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

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

 


Создание закладки


Конструктор класса javax.servlet.http.Cookie создает закладу с начальным именем и значением. Вы можете изменить значение закладки позже, вызвав метод setValue.

Имя закладки должно быть HTTP/1.1 токен. Токены это строки, не содержащие специальных символов перечисленных в документе RFC 2068

. (буквенно-численные строки квалифицируемые как токены.) В добавление, имена, начинающиеся со знака доллар ("$") зарезервированы документом RFC 2109
.

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

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

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

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

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

// Смотрим есть ли покупки в корзине

String bookId = request.getParameter("Buy");

//Если пользователь хочет добавить книгу, запоминаем это добавляя закладку

if (bookId != null) { Cookie getBook = new Cookie("Buy", bookId);

... }

// прежде чем начать вывод, устанавливаем тип содержимого

response.setContentType("text/html");

// теперь выводим данные ответа

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



Управление всеми броузерами


По умолчанию, прослеживание сессии использует закладки, чтобы ассоциировать идентификатор сессии с пользователем. Чтобы также поддерживать пользователей, у которых броузер не работает с закладками, или включен в режим игнорирования их, Вы должны использовать перезапись URL. (По скольку некоторые сервера поддерживают перезапись URL, утилита servletrunner являющаяся частью JSDK2.0 не поддерживает это. Для того чтобы прослеживания сессии работало, когда сервлет запущен с помощью servletrunner, клиент должен поддерживать закладки.)

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

Методом, который ассоциирует идентификатор сессии с URL, является метод HttpServletResponse.encodeUrl в пакете JSDK2.0 и HttpServletResponse.encodeURL в пакете JSDK2.1. Если Вы переадресовываете пользователя на другую страницу, методом, который ассоциирует идентификатор сессии с URL, является метод HttpServletResponse.encodeRedirectUrl в пакете JSDK2.0 и HttpServletResponse.encodeRedirectURL в пакете JSDK2.1.

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

Замечание: Здесь показан код, не являющийся частью примера Duke's Bookstore для JSDK2.1. Пример Duke's Bookstore мог бы содержать код, подобный приведенному ниже, в случае если бы использовалась перезапись URL. Поскольку перезапись URL не является частью примера Duke's Bookstore, будем считать этот код псевдокодом.

Если бы пример 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++) { ... //Выводим инвормацию для книги в две строчки

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

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

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

} } }

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

Когда пользователь жмет на ссылку переписанного URL, сервлет определяет и извлекает идентификатор сессии. Далее используется метод getSession, чтобы получить соответствующий идентификатору сессии объект HttpSession.

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


Сохранение статуса клиента


Интерфейс сервлета позволяет проследить статус клиента:



Установка атрибутов закладки


Класс Cookie обеспечивает набор методов для установки параметров закладки и ее атрибутов. Использование этих методов поступательно; они описаны в документации для класса Cookie.

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

public void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ... //Если пользователь хочет добавить книгу, запоминаем это добавляя закладку

if (values != null) { bookId = values[0]; Cookie getBook = new Cookie("Buy", bookId); getBook.setComment("User wants to buy this book " + "from the bookstore.");

} ... }

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

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

public void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ... // Управление удалением из корзины

String bookId = request.getParameter("Remove"); ... if (bookId != null) { // находим закладку которая отвечает за удаляемую книгу

... // Удаляем закладку устанавливая ее возраст равным нулю

thisCookie.setMaxAge(0);

... }

// прежде чем начать вывод, устанавливаем тип содержимого

response.setContentType("text/html"); PrintWriter out = response.getWriter();

//Выводим ответ

out.println("<html> <head>" + "<title>Your Shopping Cart</title>" + ...); ... }



Закладки


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



Запрашивание закладок


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

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

Продолжая пример сервлета ShowCartServlet:

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

//Управление удалением из корзины

String bookId = request.getParameter("Remove"); ... if (bookId != null) {

//находим закладку которая отвечает за удаляемую книгу

Cookie[] cookies = request.getCookies();

...

//Удаляем закладку устанавливая ее возраст равным нулю

thisCookie.setMaxAge(0);

}

//прежде чем начать вывод, устанавливаем тип содержимого

response.setContentType("text/html"); PrintWriter out = response.getWriter();

//Выводим ответ

out.println("<html> <head>" + "<title>Your Shopping Cart</title>" + ...); ... }



Завершение сессии


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

Чтобы вручную завершить сессию, используйте метод сессии invalidate. У некоторых программ уже есть такие точки, в которых происходит завершение сессии. Пример Duke's Bookstore

завершает сессию пользователя после того, как он осуществил покупку книг. Это происходит в сервлете ReceiptServlet:

public class ReceiptServlet extends HttpServlet {

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

... scart = (ShoppingCart)session.getValue(session.getId()); ... // Очищаем корзину завершая сессию

session.invalidate();

// прежде чем начать вывод, устанавливаем тип содержимого

response.setContentType("text/html"); out = response.getWriter(); ... } }



Договоренности именования атрибутов


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

совместно используют атрибут examples.bookstore.database.BookDBFrontEnd. Имена, начинающиеся с префиксов java.*, javax.*, и sun.* зарезервированы.



Обучение Java. Сервлеты


Получение значения атрибута также просто как вызов метода ServletContext.getAttribute. Следующий пример демонстрирует, как сервлет CatalogServlet получает значение атрибута во время инициализации:

public class CatalogServlet extends HttpServlet {

public void init() throws ServletException { BookDBFrontEnd bookDBFrontEnd = (BookDBFrontEnd)getServletContext().getAttribute( "examples.bookstore.database.BookDBFrontEnd");

if (bookDBFrontEnd == null) { getServletContext().setAttribute( "examples.bookstore.database.BookDBFrontEnd", BookDBFrontEnd.instance()); } } ... }



Использование других ресурсов сервера (JSDK 2.1)


Чтобы Ваш сервлет получил доступ к другим ресурсам сервера, таким как другой сервлет, страница JSP, или CGI скрипт, Вы можете:

Либо дать сервлету сделать HTTP запрос. (Это относится к общему программированию на Java. Для более подробной информации, обратитесь к ресурсу Working with URLs

.) 

Либо сделать запрос ресурса с помощью объекта RequestDispatcher, если ресурс доступен на сервере, на котором запущен сервлет.

Этот урок обсуждает второй из вышеуказанных пунктов:

для ресурса.

к ресурсу так, чтобы он отвечал запросу клиента.

в выходной поток сервлета.



Методы для управления атрибутами могут


Это замечание относится к сервлетам написанным под JSDK2.0: Методы для управления атрибутами могут оказаться полезными, если Вы удаляете обращения к методу getServlet. Эти методы, разработанные для сервлетов, которые вызывают метод getServlet и затем вызывают общедоступные методы отличные от метода service.

Удаление обращений к методу getServlet из этих сервлетов включает:

Замените сервлет с обращением к методу getServlet на класс не являющийся сервлетом.

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

Замените все вызовы метода getServlet вызовами значения вышеуказанного атрибута.

Замените обращения к методам объекта Servlet на обращения к методам состояния возвращенного от атрибута.


Перенаправление запроса


Как только Вы получаете объект RequestDispatcher, Вы можете дать возможность ассоциированному с ним ресурсу отвечать на запрос клиента. Перенаправление очень полезно, например, когда сервлет производит запрос, и ответ носит общий характер, так что он может быть передан другому ресурсу. Сервлет может, например, заведовать информацией кредитных карт, когда пользователь размещает заказ, и потом отправлять запрос клиента к заказу, который возвращает страницу "Спасибо за заказ". В примере Duke's Bookstore, сервлет BookStoreServlet получает сессию пользователя, а потом request dispatcher возвращает стартовую страницу магазина Duke's Bookstore:

public class BookStoreServlet extends HttpServlet {

public void service (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ... // Получить или начать новую сессию пользователя

HttpSession session = request.getSession(); // Открыть пользователю стартовую страницу

dispatcher.forward(request, response);

... } }

Запомните, что метод forward должен быть использован тогда, когда необходимо отдать другому ресурсу возможность отвечать пользователю. Если Вы уже получили доступ к объектам ServletOutputStream или PrintWriter, Вы не можете использовать этот метод; он вызовет исключение IllegalStateException.

Если Вы уже начали отвечать пользователю, используя объекты PrintWriter или ServletOutputStream, Вам необходимо использовать метод .



Получение объекта RequestDispatcher


Чтобы получить доступ к объекту RequestDispatcher, используйте метод

getRequestDispatcher класса ServletContext. Этот метод в качестве аргумента берет URL запрашиваемого ресурса. Формат этого аргумента последовательность имен директорий разбитых знаком слэш ("/"), и именем ресурса на конце. Вот следующие примеры возможных URL:

/servlet/myservlet

/servlet/tests/MyServlet.class

/myinfo.html

Например, когда сервлет BookStoreServlet получает объект RequestDispatcher для главной страницы магазина Duke's Bookstore:

public class BookStoreServlet extends HttpServlet {

public void service (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // Получаем dispatcher; он получает главную страницу для пользователя

RequestDispatcher dispatcher = getServletContext().getRequestDispatcher( "/examples/applications/bookstore/bookstore.html");

... } }

URL ресурса должен быть доступным на сервере, на котором запущен сервлет в момент обращения. Если ресурс недоступен, или у сервера не реализован объект RequestDispatcher для ресурса данного типа, этот метод вернет значение null. Сервлет должен быть готов к таким ситуациям. Сервлет BookStoreServlet делает это вот таким простым путем:

public class BookStoreServlet extends HttpServlet {

public void service (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // Получаем dispatcher; он получает главную страницу для пользователя

RequestDispatcher dispatcher = ...;

if (dispatcher == null) { // No dispatcher means the html file can not be delivered response.sendError(response.SC_NO_CONTENT);

} ... } }



RemoveAttribute - удаление атрибута


Любой сервлет может удалить атрибут из объекта ServletContext. Поскольку атрибуты являются совместно используемыми, Вам надо проявить осторожность, чтобы не удалить атрибут, который используется в этот момент другим сервлетом. Duke's Bookstore не использует метод removeAttribute.



SetAttribute - установка атрибута


Сервлеты устанавливают атрибуты, используя метод ServletContext.setAttribute; обычно это производится во время инициализации. Когда у Вас несколько сервлетов используют атрибут, каждый должен проинициализировать этот атрибут. А раз так, каждый сервлет должен проверить значение атрибута, и устанавливать его только в том случае если предыдущий сервлет не сделал этого.

Следующий пример демонстрирует метод init сервлета CatalogServlet, который пробует установить совместно-используемый атрибут для магазина Duke's Bookstore:

public class CatalogServlet extends HttpServlet {

public void init() throws ServletException { BookDBFrontEnd bookDBFrontEnd = ...

if (bookDBFrontEnd == null) { getServletContext().setAttribute( "examples.bookstore.database.BookDBFrontEnd", BookDBFrontEnd.instance());

} } ... }

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



Совместное использование ресурсов сервлетами (JSDK 2.1)


Сервлеты выполняемые на одном сервере иногда совместно используют ресурсы. Это справедливо для сервлетов, которые являются компонентами одного приложения, как, например, сервлеты магазина Duke's Bookstore. Сервлеты исполняемые на одном сервере могут совместно использовать ресурсы с помощью методов интерфейса ServletContext для манипулирования атрибутами: setAttribute, , и .

Отметим что использование атрибутов - один из способов из предшествующих JSDK2.1 версий сервлетов.



Связи сервлета


Чтобы удовлетворять запросам клиентов, сервлеты иногда должны получать доступ к ресурсам сети: другим сервлетам, HTML страницам, объектам используемым совместно с другими сервлетами на том же сервере, и тому подобное.

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

таких как сервлеты, HTML страницы, и так далее, с помощью объекта RequestDispatcher.

, где ресурсы это объекты Java, стало возможным через атрибуты.

с помощью объекта сервлета Servlet и вызова его общедоступных методов напрямую.



Включение ответа


Метод include интерфейса RequestDispatcher позволяет вызываемому сервлету отвечать клиенту, но использовать в качестве части ответа ресурс, ассоциированный с объектом RequestDispatcher.

Поскольку сервер вызывает метод RequestDispatcher.include подразумевается также, что он отвечет клиенту, сервлет будет использовать объекты PrintWriter и ServletOutputStream до или после вызова метода include. Вы должны запомнить, однако, что вызываемый ресурс не может устанавливать заголовки в ответе клиенту. Если ресурс попытается установить заголовки, нет гарантии, что они будут установлены.

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

public class ReceiptServlet extends HttpServlet {

public void doPut(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { // Выполняем заказ клиента

... // Благодарим за заказ

res.setContentType("text/html"); PrintWriter toClient = res.getWriter(); ... toClient.println("Thank you for your order!");

// Получаем request-dispatcher, чтобы послать список заказанного клиентом

RequestDispatcher summary = getServletContext().getRequestDispatcher("/OrderSummary");

// Опускаем список заказанного в случае ошибки

if (summary != null) try { summary.include(req, res);

} catch (IOException e) {} catch (ServletException e) {}

toClient.println("Come back soon!"); toClient.println("</html>"); toClient.close(); }



Вызов сервлетов из сервлетов (JSDK 2.0)


Чтобы Ваш сервлет вызвал другой сервлет, Вы можете:

Либо дать сервлету сделать HTTP запрос к другому сервлету. (Это относится к общему программированию на Java. Для более подробной информации, обратитесь к ресурсу Working with URLs

.)  

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

Этот урок обсуждает второй из вышеуказанных пунктов. Чтобы вызвать общедоступный метод другого сервлета напрямую, Вам надо:

Знать имя сервлета, метод которого Вы хотите вызвать.

Получить доступ к объекту сервлета Servlet. 

Вызвать общедоступный метод.

Чтобы получить доступ к объекту Servlet, используйте метод getServlet класса ServletContext. Получите объект ServletContext из объекта 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"); ... } }

Как только Вы получили объект сервлета, Вы можете вызывать любой общедоступный метод этого сервлета. Например, сервлет 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 interface, Ваш вызов может нарушить природу однопотокового сервлета. (Сервер не может вмешаться и обеспечить, чтоб вызов был в момент, когда никто не взаимодействует с сервлетом.) В таком случае, Ваш сервлет должен использовать HTTP запрос к другому сервлету вместо прямых вызовов методов.