SlideShare a Scribd company logo
1 of 24
Download to read offline
Основы сервлетов Java™:
Предисловие
By MageLang Institute
Январь 1999

JavaSM Developer ConnectionSM (JDC) представляет краткий курс «Основы сервлетов Java», написанный
MageLang Institute, являющимся обладателем лицензии Java Software. MageLang является ведущим
поставщиком обучающих курсов по технологии Java™ и регулярным участником JDC с 1996 года.

MageLang Institute с самого основания в 1995 году способствовал росту сообщества пользователей Java,
обеспечивая отличное обучение и действуя как независимый ресурс. Более подробную информацию по их
обучающим курсам по Java технологии можно найти на Web-сайте http://www.magelang.com/jdc.

Об этом кратком курсе
В курсе «Основы сервлетов Java» показывается, как улучшить функциональность Web-сервера, используя
безопасные, эффективные и мощные сервлеты.

Поняв концепции, содержащиеся в заметках по курсу, и выполнив упражнения, вы научитесь использовать
Java Servlet API, создавать и запускать сервлеты с JSDK, устанавливать сервлеты на Java Web Server™,
передавать параметры из HTML-документов и управлять программами, работающими на промежуточном
уровне.

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

Примечание. Этот курс создавался для JSDK 2.1 и может не работать с более новыми версиями.

Этот курс был написан несколькими членами команды MageLang Institute.

Обратная связь с читателем
Сообщите нам, что вы думаете об этом учебном пособии.

              Очень ценная информация. Ценная информация. Ничего особенного.

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

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

Для создания сервлетов, отвечающих на запросы клиентов, вы используете Java Servlet API из Java Servlet
Development Kit (JSDK). Сервлеты могут выполнять разные задачи, например, обработка HTML-документов
специальным сервлетом, или управление работой промежуточного уровня для соединения с существующим
источником данных за корпоративным брандмауэром. Кроме того, сервлеты могут управлять службами,
например, сессиями базы данных, между запросами на обслуживание ресурсов лучше, чем технологии
Common Gateway Interface (CGI).

Java Servlet API основан на нескольких интерфейсах Java, которые находятся в пакете стандартного
расширения Java (javax).

В этом курсе вы:
•   Научитесь использовать Java Servlet API
  •   Создадите и запустите сервлет при помощи JSDK
  •   Установите сервлет на Java Web Server™ от Sun
  •   Обработаете параметры, переданные из HTML-документа
  •   Будете управлять работой промежуточного уровня

Java Servlet API
  •   Место сервлетов в архитектуре
       o Работа на промежуточном уровне
       o Прокси-серверы
       o Поддержка протоколов

  •   Поддержка HTML
       o Встраивание в HTML
       o SSI
       o Замещение CGI-сценариев

  •   Установка сервлетов
       o Временные сервлеты против постоянных
       o Использование servletrunner
       o Использование Java Web Server

  •   Servlet API
        o Жизненный цикл сервлета
                  Метод init()
                  Метод service()
                  Метод destroy()
                  Пример сервлета

       o   Контекст сервлета
                Информация об инициализации сервлета
                Информация о контексте сервера
                Контекст сервлета во время запроса на обслуживание

       o   Классы утилит

       o   Поддержка HTTP
                Метод HTTP GET
                Метод HEAD
                Метод POST
                Классы поддержки HTTP
                Использование классов поддержки HTTP

       o   Резюме
                javax.servlet: Поддержка сервлетов общего назначения
                javax.servlet.http: Поддержка сервлетов HTTP

  •   Примеры сервлетов
       o Генерация встроенного содержания
       o Обработка запроса HTTP Post
       o Использование полей Cookie
       o Управление информацией о сессии
       o Соединение с базами данных

  •   Вопросы безопасности
       o «Песочница» для сервлетов
       o Списки управления доступом (ACL)
•   Потоки

    •   JSDK 1.0 и JSDK 2.0
          o Новые возможности сервлетов в JSDK 2.0

    •   Дополнительная информация

Java Servlet API
Сервлет представляет собой компонент Java™, который может быть помещен на Web-сервер,
поддерживающий Java, для обеспечения специальных услуг. Эти услуги могут включать:

    •   Новые возможности
    •   Изменения содержания во время исполнения
    •   Изменения представления во время исполнения
    •   Новые стандартные протоколы (такие как FTP)
    •   Новые специальные протоколы

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

    HTTP,
    URL,
    FTP,
    URL

или по специальному протоколу.

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

Java Servlet API включает несколько интерфейсов Java и полностью определяет связь между сервером и
сервлетами. Servlet API определяется как расширение стандартного JDK. Расширения JDK пакетируются в
библиотеке javax – корневой в дереве библиотек расширений Java. Java Servlet API содержит следующие
пакеты:

    •   Пакет javax.servlet
    •   Пакет javax.servlet.http

Сервлеты являются мощным дополнением к среде Java. Они являются быстрыми, безопасными, надежными
и на 100% написаны на Java. Поскольку сервлеты помещаются на существующий сервер, они используют
большое количество существующего кода и много технологий. Сервер управляет сетевыми соединениями,
согласованием протоколов, загрузкой классов и многим другим; вся эта работа не требует дублирования! И,
поскольку сервлеты располагаются на промежуточном уровне, они предназначены для повышения гибкости
и улучшения возможностей системы.

В этом курсе вы изучите Servlet API и кратко ознакомитесь с возможностями, которые могут реализовывать
сервлеты.

Место сервлетов в архитектуре
Из-за своей мощности и гибкости, сервлеты могут играть значительную роль в архитектуре системы. Они
могут выполнять прикладные задачи, предназначенные для промежуточного уровня, работать как прокси-
сервер для клиента и даже улучшать функциональность промежуточного уровня, добавляя поддержку новых
протоколов и других функций. Промежуточный уровень выполняет функции сервера приложений в так
называемой трехуровневой системе клиент-сервер и расположен между «легковесным2 клиентом, таким как
Web-броузер, и источником данных.
Работа на промежуточном уровне
Во многих системах промежуточный уровень работает как связь между клиентами и прикладными
службами. При использовании промежуточного уровня большая часть работы может быть перенесена и с
клиентов (делая их легче и быстрее), и с серверов (позволяя им сконцентрироваться на своих задачах).

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

Другие функции промежуточного уровня включают:

   •   Применение бизнес-правил
   •   Управление транзакциями
   •   Отображение клиентов на резервные серверы
   •   Поддержка различных типов клиентов, таких как HTML и Java клиентов.

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

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

Поддержка протокола
Servlet API обеспечивает тесную связь между сервером и сервлетами. Это позволяет сервлетам добавлять на
сервер поддержку нового протокола. (В пакет API добавлена поддержка протокола HTTP). По существу
любой протокол, который использует модель вычислений запрос-ответ, может быть реализован сервлетом.
Например:

   •   SMTP
   •   POP
   •   FTP

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

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

Поддержка HTML
HTML может обеспечить разнообразное представление информации по причине своей гибкости и широкого
диапазона поддерживаемых им объектов. Сервлеты могут участвовать в создании содержимого HTML.
Поддержка сервлетами HTML широко распространена. Пакет java.servlet.http предназначен для поддержки
протокола HTTP и генерации HTML.

Сложные Web-сайты часто требуют отображения HTML-страниц, настроенных на каждого пользователя,
или даже на каждый показ. Для обработки HTML-страниц и настройки их во время передачи клиенту могут
быть использованы сервлеты. Этот процесс может быть простым, как, например, простая подстановка на
лету, или сложным, как, например, компиляция и генерация основанного на определенной грамматике
описания страницы специального HTML-документа.

Встраивание в HTML
Некоторые Web-серверы, например Java Web Server™ (JWS), допускают непосредственное размещение
тэгов сервлетов в файлах HTML. Если сервер встречает такой тэг, он вызывает сервлет во время передачи
HTML-файла клиенту. Это позволяет сервлету вставлять свои данные прямо в исходящий поток HTML.

SSI
Еще один пример – обработка тэгов на лету, называемая SSI (server-side includes). Используя SSI, HTML-
страница может содержать специальные команды, которые обрабатываются каждый раз при запросе этой
страницы. Обычно, Web-сервер требует использования уникального расширения .shtml для HTML-
страниц, использующих SSI. Например, если HTML-страница (с расширением .shtml) включает в себя
строку: <!--#include virtual="/includes/page.html"-->, она будет воспринята Web-
сервером как запрос на отображение указанного файла. Хотя SSI поддерживаются большинством Web-
серверов, тэги SSI не стандартизованы.

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

Замещение CGI-сценариев
HTTP-сервлеты являются прямой заменой сценариев Common Gateway Interface (CGI). Сервлеты HTTP
вызываются пользователем путем ввода URL в броузер или как результат действия HTML-формы.
Например, если пользователь введет в поле адреса броузера следующий URL, броузер пошлет запрос
сервлету на передачу HTML-страницы с текущим временем:
http://localhost/servlet/DateTimeServlet. Сервлет DateTimeServlet ответит на этот
запрос передачей HTML-страницы в броузер.

Обратите внимание, что эти сервлеты не ограничиваются только генерацией Web-страниц; они могут
выполнять любую другую функцию, такую как, например, запись и извлечение информации из базы
данных, или открытие сокета на другой машине.

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

    1.   Установить сервлет на сервер.
    2.   Послать сервлету запрос на обслуживание

Существует много Web-серверов, поддерживающих сервлеты. Описание различных способов установки
сервлетов на каждый тип сервера выходит за рамки данного курса. В этом курсе рассматриваются утилита
JSDK servletrunner и JWS.

Временные сервлеты против постоянных
Сервлеты могут запускаться и останавливаться для каждого клиентского запроса. Также они могут
запускаться при старте Web-сервера и существовать до его остановки. Временные сервлеты загружаются по
требованию и предлагают хороший способ сохранения ресурсов сервера для редко используемых функций.

Постоянные сервлеты загружаются при старте Web-сервера и существуют до его остановки. Сервлеты
устанавливаются как постоянные расширения для сервера в том случае, если затраты по их запуску очень
велики (например, установка соединения с DBMS), если они предлагают постоянную функциональность на
стороне сервера (например, служба RMI), или в случаях, когда они должны отвечать на запросы клиента как
можно быстрее.

Не существует специального кода для назначения сервлета постоянным или временным; это функция
настройки сервера.
Поскольку сервлеты могут быть загружены при старте сервера, они могут использовать механизм
автозагрузки для обеспечения более легкой загрузки Java-программ на стороне сервера. Эти программы
могут обеспечивать функциональные возможности, являющиеся полностью уникальными и независимыми
от Web-сервера. Например, сервлет мог бы обеспечить R-службы (rlogin, rsh, ...) через порт TCP/IP и в
то же время использовать протокол запрос-ответ для представления и обработки HTML-страниц,
используемых для управления сервлетом.

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

Вы должны установить Java Servlet Development Kit (JSDK) как для JDK 1.1, так и для платформы Java 2.
Перед использованием servletrunner проверьте, что переменная окружения PATH указывает на каталог,
в котором он находится. Для JSDK 2.0, установленного с настройками по умолчанию, sevletrunner находится
в каталоге c:jsdk2.0bin в операционной системе Windows.

Для того, чтобы программа servletrunner получила доступ к пакетам сервлетов Java, проверьте, что
переменная окружения CLASSPATH указывает на корректный JAR-файл, а именно
c:jsdk2.0libjsdk.jar в операционной системе Windows. При работе с платформой Java 2, вместо
редактирования переменной CLASSPATH, можно просто скопировать JAR-файл в каталог ext
исполняющей среды Java. После этого пакеты сервлетов будут считаться стандартными расширениями.

После установки переменных окружения запустите программу servletrunner из командной строки.
Программа отобразит следующую информацию:

Usage: 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
  -s filename servlet property file name

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

Упражнение

   1.   Хостинг с программой servletrunner


Использование Java Web Server
Java Web Server (JWS) от Sun является полнофункциональным продуктом. К сведению разработчиков
сервлетов он обладает способностью обнаруживать обновление сервлетов. Когда новый файл класса
копируется в соответствующий каталог, это обнаруживается сервером, и он, при необходимости,
автоматически перегружает любой работающий сервлет.

JWS может быть установлен как сервис в операционной системе Windows NT. И хотя этот способ очень
удобен для запуска сервера, он не рекомендуется при разработке сервлетов. В системе Windows 95 не
существует системных сервисов, так что единственным выбором для запуска сервера является командная
строка.

Для запуска JWS из каталога c:JavaWebServer1.1bin наберите команду httpd. В терминальном
окне запустится сервер. В этом окне не будет отображаться никакой информации, пока сервлет не выполнит
команду System.out.println().

Сервлеты устанавливаются путем помещения их в каталог c:JavaWebServer1.1servlets. Как
указывалось ранее, JWS обнаружит, что в каталог были добавлены сервлеты. Хотя вы можете использовать
апплет управления JWS для установки сервлета, этот способ обычно не рекомендуется, за исключением
установки производственного сервера.

Для остановки JWS, нажмите Ctrl-C в командном окне. После остановки сервер выведет сообщение.

Упражнение

   2.   Хостинг с программой Java Web Server

Servlet API
Java Servlet API определяет интерфейс между сервлетами и сервером. Следующие API спакетированы как
стандартное расширение JDK в javax:

   •    Пакет javax.servlet
   •    Пакет javax.servlet.http

API обеспечивает поддержку в четырех категориях:

   •    Управление циклом жизни сервлета
   •    Доступ к контексту сервлета
   •    Классы утилит
   •    Классы поддержки HTTP

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

Web-сервер взаимодействует с сервлетом через простой интерфейс: javax.servlet.Servlet. Этот
интерфейс состоит из трех главных методов:

   •    init()
   •    service()
   •    destroy()

и двух вспомогательных методов:

   •    getServletConfig()
   •    getServletInfo()

Вы можете заметить сходство между этим интерфейсом и интерфейсом апплетов Java. Именно так и было
спроектировано! Сервлеты являются для Web-серверов тем же самым, чем являются апплеты для Web-
броузеров. Апплет выполняется в Web-броузере, выполняя действия по его запросу через специальный
интерфейс. Сервлет делает то же самое, работая на Web-сервере.

Метод init()

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

Гарантируется, что метод init() закончится перед любым другим обращением к сервлету – таким как,
например, вызов метода service(). Обратите внимание, что init() будет вызван только один раз; он не
будет вызываться до тех пор, пока сервлет не будет выгружен и затем загружен сервером снова.
Метод init() принимает один аргумент – ссылку на объект ServletConfig, который содержит
аргументы для инициализации сервлета. Этот объект имеет метод getServletContext(),
возвращающий объект ServletContext, который содержит информацию об окружении сервлета
(смотрите раздел «Контекст инициализации сервлета»).

Метод service()

Метод service() является сердцем сервлета. Каждый запрос от клиента приводит к одному вызову
метода service(). Этот метод читает запрос и формирует ответное сообщение при помощи своих двух
аргументов:

   •   Объект ServletRequest содержит данные от клиента. Данные состоят из пар имя/значение и
       InputStream. Существует несколько методов, возвращающих информацию о параметрах
       клиента. InputStream может быть получен при помощи метода getInputStream(). Этот
       метод возвращает объект ServletInputStream, который можно использовать для получения
       дополнительных данных от клиента. Если вас интересует обработка символьных данных, а не
       двоичных, вы можете получить объект BufferedReader при помощи метода getReader().

   •   Объект ServletResponse содержит ответ сервлета клиенту. Во время подготовки ответа прежде
       всего вызывается метод setContentType() для установки типа MIME ответа. Затем могут быть
       использованы методы getOutputStream() или getWriter() для получения объектов и
       ServletOutputStream или PrintWriter соответственно для передачи данных обратно
       клиенту.

Как вы можете заметить, существуют два способа передачи информации от клиента к сервлету. Первый –
передача значений параметров, и второй – передача информации через InputStream (или Reader).
Значения параметров могут быть вставлены в URL. Как это делается - обсуждается ниже. Чтение значений
параметров сервлетом рассматривается далее.

Работа метода service() по существу проста – он создает ответ на каждый клиентский запрос,
переданный ему с сервера. Однако важно понимать, что могут существовать несколько запросов услуги,
обрабатываемых в одно и то же время. Если ваш метод требует каких-либо внешних ресурсов, таких как
файлы, базы данных, вы должны гарантировать, что доступ к ресурсам является потокозащищенным.
Создание потокозащищенных сервлетов рассматривается далее.

Метод destroy()

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

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

Пример сервлета

Приведенный ниже код реализует простой сервлет, возвращающий статическую HTML-страницу броузеру.
Этот пример полностью реализует интерфейс Servlet.

import java.io.*;
import javax.servlet.*;
public SampleServlet implements Servlet {
  private ServletConfig config;

  public void init (ServletConfig config)
    throws ServletException {
    this.config = config;
}

    public void destroy() {} // ничего не делать

    public ServletConfig getServletConfig() {
      return config;
    }

    public String getServletInfo() {
      return "A Simple Servlet";
    }

    public void service (ServletRequest req,
      ServletResponse res
    ) throws ServletException, IOException {
      res.setContentType( "text/html" );
      PrintWriter out = res.getWriter();
      out.println( "<html>" );
      out.println( "<head> );
      out.println( "<title>A Sample Servlet</title>" );
      out.println( "</head>" );
      out.println( "<body>" );
      out.println( "<h1>A Sample Servlet</h1>" );
      out.println( "</body>" );
      out.println( "</html>" );
      out.close();
    }
}

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

Информация об инициализации сервера

Информация об инициализации передается сервлету при помощи параметра ServletConfig метода
init(). Каждый Web-сервер обеспечивает свой способ передачи информации об инициализации в сервлет.
Если, например, класс сервера DatePrintServlet принимает аргумент инициализации timezone, вы
должны определить следующие свойства в файле servlets.properties:

    servlet.dateprinter.code=DatePrinterServlet
    servlet.dateprinter.timezone=PST

Эта информация также может быть предоставлена административным средством GUI. В следующем коде
сервлет получает доступ к информации timezone.

    String timezone;
    public void init(ServletConfig config) {
      timeZone = config.getInitParameter("timezone");
    }

Объект Enumeration со всеми параметрами инициализации доступен сервлету через метод
getInitParameterNames().

Информация о контексте сервера

Информация о контексте сервера доступна в любое время через объект ServletContext. Сервлет может
получить этот объект, вызывая метод getServletContext() объекта ServletConfig. Помните, что
этот объект передается сервлету во время инициализации. Грамотно написанный метод init() сохраняет
ссылку в переменной, имеющей тип доступа private.

Интерфейс ServletContext определяет несколько методов. Они перечислены ниже.


                    Гибкий способ получения информации о сервере через пары атрибутов
getAttribute()
                    имя/значение. Зависит от сервера.
GetMimeType()       Возвращает тип MIME данного файла.
                    Этот метод преобразует относительный или виртуальный путь в новый путь
getRealPath()
                    относительно месторасположения корня HTML-документов сервера.
getServerInfo(
               Возвращает имя и версию сетевой службы, в которой исполняется сервлет.
)
               Возвращает объект Servlet указанного имени. Полезен при доступе к
getServlet()
               службам других сервлетов.
getServletName Возвращает список имен сервлетов, доступных в текущем пространстве
s()            имен.
               Записывает информацию в файл регистрации сервлета. Имя файла
log()
               регистрации и его формат зависят от сервера.

Следующий пример показывает, как сервлет использует сервер для записи сообщения в свой log-файл во
время инициализации:

  private ServletConfig config;
  public void init(ServletConfig config) {
    // Сохранить config в переменной экземпляра
    this.config = config;
    ServletContext sc = config.getServletContext();
    sc.log( "Started OK!" );
  }

Контекст сервлета во время запроса на обслуживание

Каждый запрос на обслуживание может содержать информацию в форме пар параметров имя/значение, как
ServletInputStream, или BufferedReader. Эта информация доступна при помощи объекта
ServletRequest, который передается в метод service().

Следующий код показывает, как получить информацию во время работы метода service():

  BufferedReader reader;
  String         param1;
  String         param2;
  public void service (
      ServletRequest req,
      ServletResponse res) {

      reader = req.getReader();
      param1 = req.getParameter("First");
      param2 = req.getParameter("Second");
  }

Существует также дополнительная информация, доступная сервлету через объект ServletRequest. Она
приведена в следующей таблице.

getAttribute()         Возвращает значение указанного атрибута этого запроса.
getContentLength(
                       Размер запроса, если известен.
)
getContentType()       Возвращает тип MIME тела запроса.
getInputStream()       Возвращает InputStream для чтения двоичных данных из тела
запроса.
GetParameterNames
                  Возвращает массив строк с именами всех параметров.
()
getParameterValue
                  Возвращает массив значений для указанного параметра.
s()
                  Возвращает протокол и версию для запроса как строку вида
getProtocol()
                  <protocol>/<major version>.<minor version>.
getReader()       Возвращает BufferedReader для получения текста из тела запроса.
getRealPath()     Возвращает реальный путь для указанного виртуального пути.
getRemoteAddr()   IP-адрес клиента, пославшего данный запрос.
getRemoteHost()   Имя хоста клиентской машины, пославшего данный запрос.
                  Возвращает схему, используемую в URL этого запроса (например, https,
getScheme()
                  http, ftp, и т.д.).
getServerName()   Имя хоста сервера, принявшего данный запрос.
getServerPort()   Возвращает номер порта, используемого для приема этого запроса.

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

Упражнение

   3.   Получение доступа к параметрам сервлета во время обслуживания

Классы утилит
Servlet API предоставляет несколько утилит. Одной из них является интерфейс
javax.servlet.SingleThreadModel, который облегчает написание простых сервлетов. Если сервлет
реализует этот интерфейс, сервер знает, что он никогда он не должен вызывать его метод service() во
время обработки запроса. То есть, сервер обрабатывает все запросы на обслуживание в одном потоке.

Это облегчает написание сервлета, но может привести к ухудшению производительности. Полностью эта
тема будет рассмотрена ниже.

В Servlet API включены два класса исключительных ситуаций. Исключительная ситуация
javax.servlet.ServletException может быть использована при общем отказе сервлета для
информирования сервера о возникновении проблемы.

Исключительная ситуация javax.servlet.UnavailableException указывает, что сервлет
недоступен. Сервлеты могут сгенерировать эту исключительную ситуацию в любое время. Существует два
типа недоступности:

   •    Постоянная. Сервлет не может функционировать до вмешательства администратора. В этой
        ситуации сервлет должен сделать запись в log-файле с отчетом о проблеме и возможных методах ее
        решения.

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

Поддержка HTTP
Сервлеты, использующие HTTP-протокол, очень широко распространены. Нет ничего неожиданного в том,
что существует специальная помощь для разработчиков таких сервлетов. Поддержка обработки протокола
HTTP обеспечивается в пакете javax.servlet.http. Перед рассмотрением этого пакета, рассмотрим
сам протокол HTTP.

HTTP – это аббревиатура от HyperText Transfer Protocol – протокол передачи гипертекста. Он представляет
собой протокол, используемый Web-броузерами и серверами для взаимодействия друг с другом. Протокол
определяет набор текстовых запросов, называемых HTTP-методами. (Примечание: Название HTTP-методы
используется в спецификации HTTP; не смешивайте этот термин с методами Java. Думайте о методах HTTP
как о сообщениях, вызывающих определенный тип реакции). HTTP-методы включают:

   •   GET
   •   EAD
   •   POST
   •   PUT
   •   DELETE
   •   TRACE
   •   CONNECT
   •   OPTIONS

В это курсе мы будем применять только три из этих методов: GET, HEAD и POST.

Метод HTTP GET

HTTP-метод GET запрашивает информацию у Web-сервера. Этой информацией может быть файл, выходная
информация от устройства, расположенного на сервере, или выходная информация от программы
(например, сервлета или CGI-сценария).

HTTP-запрос GET выглядит так:

  GET URL <http version>
  Host: <target host>

плюс еще несколько строк информации.

Например, следующее сообщение HTTP GET запрашивает домашнюю страницу Web-сайта MageLang:

  GET / HTTP/1.1
  Connection: Keep-Alive
  User-Agent: Mozilla/4.0 (
   compatible;
   MSIE 4.01;
   Windows NT)
  Host: www.magelang.com
  Accept: image/gif, image/x-xbitmap,
   image/jpeg, image/pjpeg

На большинстве Web-серверов к сервлетам обращаются при помощи URL, начинающегося с /servlet/.
Следующий метод HTTP GET запрашивает сервлет MyServlet на хосте www.magelang.com:

  GET /servlet/MyServlet?name=Scott&
    company=MageLang%20Institute HTTP/1.1
  Connection: Keep-Alive
  User-Agent: Mozilla/4.0 (
   compatible;
   MSIE 4.01;
   Windows NT)
  Host: www.magelang.com
  Accept: image/gif, image/x-xbitmap,
   image/jpeg, image/pjpeg

URL в этом запросе GET вызывает сервлет с именем MyServlet и содержит два параметра: name и
company. Каждый параметр представляет собой пару имя/значение, имеющую формат name=value.
Параметры указываются после имени сервлета и знака вопроса (“?”), каждый параметр разделен знаком
амперсанда (“&”).

Обратите внимание на использование в поле значения параметра company символов %20. Пробел указывал
бы на конец URL в строке запроса GET, поэтому он должен быть заколирован или заменен символами %20.
Как вы увидите далее, разработчики сервлетов могут не беспокоиться о таком кодировании, поскольку эти
символы будут автоматически декодированы классом HttpServletRequest.

Запрос HTTP GET имеет одно существенное ограничение. Большинство Web-серверов ограничивают размер
данных, которые можно передавать как часть имени URL (обычно несколько сотен байт). Если между
клиентом и сервером нужно передать больше данных, необходимо использовать метод HTTP POST.

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

При ответе сервера на запрос HTTP GET им посылается ответное сообщение HTTP. Заголовок ответа HTTP
может выглядеть так:

  HTTP/1.1 200 Document follows
  Date: Tue, 14 Apr 1997 09:25:19 PST
  Server: JWS/1.1
  Last-modified: Mon, 17 Jun 1996 21:53:08 GMT
  Content-type: text/html
  Content-length: 4435

  <4435 bytes worth of data -- the document body>

Метод HEAD

Метод HTTP HEAD очень похож на метод HTTP GET. Запрос выглядит точно так же, как и запрос GET (за
исключением того, что вместо слова GET используется HEAD), но сервер возвращает только информацию о
заголовке.

HEAD часто используется для проверки:

   •   Даты последней модификации документа на сервере для целей кеширования.
   •   Размера документа перед загрузкой (чтобы броузер мог отобразить процесс загрузки)
   •   Типа сервера для того, чтобы пользователь мог оптимизировать запрос для этого сервера
   •   Типа запрашиваемого документа, чтобы пользователь был уверен, что сможет его обработать.

Метод HEAD, подобно GET, должен быть безопасным и идемпотентным.

Метод POST

Запрос HTTP POST позволяет клиенту посылать данные на сервер. Эта передача данных может
преследовать разные цели, например:

   •   Отправка сообщений в группу новостей
   •   Добавление записей в гостевую книгу Web-сайта
   •   Передача большего количества информации, чем может передать запрос GET

Обратите особое внимание на третью цель. Запрос HTTP GET передает все свои аргументы как часть URL.
Многие Web-серверы ограничивают размер информации, которую они могут принять как часть URL. Метод
POST передает всю свою информацию в потоке данных, снимая это ограничение.

Типичный запрос POST может выглядеть так:

  POST /servlet/MyServlet HTTP/1.1
  User-Agent: Mozilla/4.0 (
   compatible;
   MSIE 4.01;
   Windows NT)
  Host: www.magelang.com
  Accept: image/gif, image/x-xbitmap,
   image/jpeg, image/pjpeg, */
Content-type: application/x-www-form-urlencoded
  Content-length: 39

  name=Scott&company=MageLang%20Institute

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

Классы поддержки HTTP

Сейчас, когда вы уже ознакомлены с протоколом HTTP, рассмотрим, как пакет javax.servlet.http
может помочь в написании HTTP-сервлетов. Абстрактный класс javax.servlet.http.HttpServlet
обеспечивает реализацию интерфейса javax.servlet.Servlet и включает много полезных
функциональных возможностей. Самый простой путь написания HTTP-сервлетов – расширить класс
HttpServlet и добавить вашу собственную специальную обработку.

Класс HttpServlet обеспечивает реализацию метода service(), который распределяет HTTP-
сообщения по различным специализированным методам. Этими методами являются:

   •   doGet()
   •   doHead()
   •   doDelete()
   •   doOptions()
   •   doPost()
   •   doTrace()

Эти методы соответствуют методам протокола HTTP.

Как показано на диаграмме, приведенной ниже, метод service() интерпретирует каждый метод HTTP и
определяет, является ли он методом HTTP GET, POST, HEAD или другим методом протокола HTTP.




Класс HttpServlet действительно достаточно интеллектуален. Он не только распределяет HTTP-запросы,
но и определяет, какие методы переопределены в подклассе, и может сообщать клиенту о возможностях
сервера. (Простое переопределение метода doGet() является причиной того, что класс отвечает на метод
HTTP OPTIONS о поддержке им методов GET, HEAD, TRACK и OPTIONS. Эти возможности
поддерживаются кодом класса).

Вот другой пример поддержки, которую обеспечивает класс HttpServlet. Если переопределить метод
doGet(), метод HTTP HEAD сгенерирует автоматический ответ. (Поскольку ответ на метод HTTP HEAD
идентичен ответу на метод HTTP GET – если не принимать во внимание тело сообщения – класс
HttpServlet может генерировать соответствующий ответ на запрос HTTP HEAD из метода doGet()).
Если требуется более точный контроль, вы всегда можете переопределить метод doHead() и обеспечить
нестандартный ответ.

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

При использовании классов поддержки HTTP, вы обычно создаете новый сервлет, который расширяет класс
HttpServlet и переопределяет метод doGet(), или doPost(), или, возможно, оба. Для более точного
управления могут быть переопределены другие методы.

Методы обработки HTTP принимают два параметра: объект HttpServletRequest и
HttpServletResponse. Класс HttpServletRequest содержит несколько удобных методов для
синтаксического анализа запроса; вы можете производить этот анализ самостоятельно, просто читая текст
запроса.

Метод сервлета doGet() может:

    •     Читать данные запроса, например, входные параметры
    •     Настраивать заголовки ответа (длину, тип заголовка и тип кодировки)
    •     Писать данные ответа

Важно отметить, что обработка метода GET должна быть безопасной и идемпотентной.

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

Следуйте следующему правилу: GET должен «смотреть и ничего не трогать». Если необходима обработка,
имеющая побочные эффекты, должен быть использован другой метод HTTP, например, POST.

Если необходимо обрабатывать отправляемый HTML-документ или большое количество данных,
передаваемых клиентом, должен использоваться метод сервлета doPost(). Обработка метода HTTP POST
детально рассматривается ниже.

Запрос HEAD обрабатывается в методе doGet() класса HttpServlet. Вы можете просто реализовать
метод doGet() и все будет сделано; любые данные документа, которые вы запишете в выходной поток
ответа, не будут переданы клиенту. Однако более эффективной реализацией была бы проверка того,
является ли запрос типом GET или HEAD, и если HEAD – не записывать данные в выходной поток ответа.

Резюме
Java Servlet API является стандартным расширением. Это означает, что существует подробное определение
интерфейсов сервлетов, но оно не является частью Java Development Kit (JDK) 1.1 или платформы Java 2.
Классы сервлетов распространяются с Java Servlet Development Kit (JSDK) версии 2.0 от Sun
(http://java.sun.com/products/servlet/). Эта версия предназначена для работы и с JDK 1.1 и с платформой Java
2. Существует несколько важных отличий между JSDK 2.0 и JSDK 1.0. Детальная информация приведена в
разделе «JSDK 1.0 и JSDK 2.0». Если вы используете более раннюю версию, чем JSDK 2.0, рекомендуется
обновить ее до JSDK 2.0.

Поддержка сервлетов разделена на два пакета:

                            javax.servlet: Поддержка сервлетов общего назначения

                                   Интерфейс, определяющий взаимодействие между Web-сервером и
        Servlet                    сервлетом. Этот интерфейс определяет методы init(),
                                   service() и destroy() (и несколько других).
Интерфейс, описывающий конфигурационные параметры сервлета.
                            Они передаются сервлету при вызове Web-сервером его метода
                            init(). Обратите внимание, что сервлет должен сохранить ссылку
       ServletConfig        на объект ServletConfig, и определить метод
                            getServletConfig() для его возврата при запросе. Этот
                            интерфейс определяет, как получить параметры инициализации для
                            сервлета и контекст, в котором сервлет исполняется.
                            Интерфейс, описывающий, как сервлет может получить
                            информацию о сервере, на котором он исполняется. Она может быть
       ServletContext
                            получена при помощи метода getServletContext() объекта
                            ServletConfig.
                            Интерфейс, описывающий, как получить информацию о клиентском
       ServletRequest
                            запросе.
                            Интерфейс, описывающий, как передать ответную информацию
       ServletResponse
                            клиенту.
                            Реализация основы сервлета. Интерфейс заботится о сохранении
                            ссылки на объект ServletConfig и обеспечивает несколько
       GenericServlet       методов, делегирующих свою функциональность объекту
                            ServletConfig. Он также обеспечивает фиктивную реализацию
                            методов init() и destroy().
                            Подкласс класса InputStream, используемый для чтения части
       ServletInputStream   данных клиентского запроса. Он дополнен для удобства методом
                            readLine().
                            Поток OutputStream, в котором записываются ответные данные
       ServletOutputStream
                            для клиента.
       ServletException     Должна генерироваться при возникновении проблем с сервлетом.
                            Должна генерироваться в случаях, когда сервлет недоступен по
       UnavailableException
                            разным причинам.

                              javax.servlet.http: Поддержка сервлетов http

                           Подкласс класса ServletRequest, определяющего несколько
        HttpServletRequest методов, которые осуществляют синтаксический анализ заголовков
                           HTTP-запросов.
                           Подкласс класса ServletResponse, обеспечивающий доступ и
       HttpServletResponse
                           интерпретацию информации о кодах состояния HTTP и заголовке.
                           Подкласс класса GenericServlet, обеспечивающий
                           автоматическую сортировку HTTP-запросов по типу метода.
       HttpServlet
                           Например, запрос HTTP GET будет обработан методом service() и
                           передан методу doGet().
                           Класс, обеспечивающий поддержку для синтаксического анализа
       HttpUtils
                           запросов HTTP GET и POST.

Примеры сервлетов
Рассмотрим подробнее несколько сервлетов. А именно:

   •     Генерация встроенного контекста
   •     Обработка запросов HTTP Post
   •     Использование Cookie
   •     Управление информацией о сессии
   •     Соединение с базами данных

Генерация встроенного содержания
Иногда требуется вставить в Web-страницу только небольшой фрагмент с изменяющейся информацией. Вся
остальная информация может быть статической. Для замены только небольшого фрагмента информации
некоторые Web-серверы поддерживают концепцию SSI (Server-Side Includes).

Если Web-сервер поддерживает SSI, то при обработке файла со специальным расширением (обычно
.shtml) он должен искать теги SSI. JWS определяет специальный SSI-тег, называемый <servlet>,
например,

  <servlet code="DatePrintServlet">
    <param name=timezone value=pst>
  </servlet>

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

Сервлет DatePrintServlet работает подобно обычному сервлету, за исключением того, что он
спроектирован для очень маленького ответа, а не полной HTML-страницы. MIME-тип ответа
устанавливается в “text/plain”, а не “text/html”. Имейте в виду, что синтаксис SSI, даже если они
поддерживаются, может значительно отличаться у разных Web-серверов.

В следующем упражнении вы создадите сервлет DatePrintServlet и увидите, как использовать его в
HTML-странице.

Упражнение

    4.   Генерация встроенного содержания

Обработка запросов HTTP Post
Обработка метода HTTP POST отличается от обработки метода HTTP GET несколькими моментами. Во-
первых, поскольку POST может изменять данные на сервере, может возникнуть необходимость безопасной
обработки обновлений, приходящих от нескольких клиентов в одно и то же время. Во-вторых, так как
размер потока данных, передаваемых клиентом, может быть очень большим, метод doPost() должен
открывать InputStream (или Reader) от клиента для получения информации. В отличие от метода HTTP
GET метод HTTP POST не поддерживает передачу параметров, закодированных в URL.

Проблема поддержки одновременных обновлений от нескольких клиентов решается системой управления
базой данных (СУБД). К сожалению, протокол HTTP не достаточно хорошо работает с базами данных, а
именно из-за того, что СУБД требует поддержания постоянного соединения между собой и клиентом для
определения того, какой клиент пытается изменить данные.

Протокол HTTP не поддерживает такого типа соединений, так как основан на сообщениях и не изменяет
своего состояния. Решить эту проблему не легко, и решается она всегда не очень элегантным способом. К
счастью, Servlet API определяет средство для отслеживания сессии клиент/сервер. Этот вопрос
рассматривается далее в разделе «Управление информацией о сессии». Без отслеживания сессии можно
прибегнуть к другим стратегиям. Все они включают запись данных на клиент в скрытые поля, которые
затем посылаются назад на сервер. Простейшим способом обработки обновлений является оптимистичная
схема блокировки, основанная на временных метках. Можно использовать одну временную метку для всех
данных, или использовать отдельные метки времени для каждой «строки» информации.

После выбора стратегии обновления прием данных, переданных на сервер посредством метода HTTP POST,
становится простым. Информация из HTML-документа передается на сервер как набор параметров (пары
имя/значение) в объекте InputStream. Класс HttpUtils содержит метод parsePostData(),
принимающий необработанный InputStream от клиента и возвращающий Hashtable c уже
обработанной информацией о параметрах. Действительно хорошей функцией является то, что если параметр
с каким-то именем имеет несколько значений (как в случае имени столбца с несколькими строками), то эта
информация может быть извлечена из Hashtable как массив типа String.
В следующем упражнении рассмотрен скелетный код, реализующий пару сервлетов, которые отображают
данные в броузере как редактируемый HTML-документ. Структура данных хранится отдельно от
фактических данных. Это позволяет легко модифицировать этот код для работы с таблицами из баз данных,
поддерживающих JDBC.

Упражнение

   5.   Передача и обработка HTML-документов

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

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

  private static final String SUM_KEY = "sum";
  ...
  int sum = ...; // взять старое значение и добавить к нему
  Cookie theCookie =
    new Cookie (SUM_KEY, Integer.toString(sum));
  response.setContentType("text/html");
  response.addCookie(theCookie);

Важно помнить, что все данные полей cookie являются строками. Вы должны конвертировать информацию,
например типа int, в объект String. По умолчанию, поле cookie актуально на протяжении сессии
броузера. Для того, чтобы разрешить полям cookie жить дольше, необходимо вызвать метод
setMaxAge(interval). При положительном параметре указывается количество секунд времени жизни
поля cookie. Отрицательное значение устанавливается по умолчанию и означает, что поле cookie
разрушается при закрытии броузера. При значении ноль поле cookie удаляется немедленно.

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

  int sum = 0;
  Cookie theCookie = null;
  Cookie cookies[] = request.getCookies();
  if (cookies != null) {
    for(int i=0, n=cookies.length; i < n; i++) {
      theCookie = cookies[i];
      if (theCookie.getName().equals(SUM_KEY)) {
        try {
          sum = Integer.parseInt(theCookie.getValue());
        } catch (NumberFormatException ignored) {
          sum = 0;
        }
        break;
      }
    }
  }

Доступен полный код этого примера для тестирования.
Управление информацией о сессии
Сессия представляет собой непрерывное соединение одного и того же броузера в течение фиксированного
промежутка времени. (Это время обычно настраивается на Web-сервере. Для JWS оно по умолчанию равно
30 минутам.) Явно используя поле cookie броузера, HTTP-сервлет дает вам возможность управлять
информацией о сессии при помощи класса HttpSession. HttpServletRequest обеспечивает
управление текущей сессией в методе getSession(boolean). Если параметр boolean равен true, то
при обнаружении новой сессии создастся новая сессия. Это, обычно, и требуется. Если параметр равен
false – при обнаружении новой сессии метод возвращает null.

  public void doGet (HttpServletRequest request,
      HttpServletResponse response)
      throws ServletException, IOException {
    HttpSession session = request.getSession(true);
    // ...

Как только вы получили доступ к HttpSession, вы можете управлять набором пар ключ-значение для
сохранения специфических для сессии данных любого вида. Вы автоматически получаете доступ ко времени
создания сессии при использовании getCreationTime() и времени последнего доступа при
использовании getLastAccessedTime(), описывающего время последнего запроса сервлета для этой
сессии.

Для сохранения специфической для сессии информации используется метод putValue(key, value).
Для извлечения информации используется метод getValue(key). Следующий пример демонстрирует это,
автоматически прибавляя значение integer к параметру Addend. Если значение не является типом integer,
подсчитывается количество ошибок.

  private static final String SUM_KEY =
    "session.sum";
  private static final String ERROR_KEY =
    "session.errors";
  Integer sum = (Integer) session.getValue(SUM_KEY);
  int ival = 0;
  if (sum != null) {
    ival = sum.intValue();
  }
  try {
    String addendString =
    request.getParameter("Addend");
    int addend = Integer.parseInt (addendString);
    sum = new Integer(ival + addend);
    session.putValue (SUM_KEY, sum);
  } catch (NumberFormatException e) {
    Integer errorCount =
      (Integer)session.getValue(ERROR_KEY);
    if (errorCount == null) {
      errorCount = new Integer(1);
    } else {
      errorCount = new Integer(errorCount.intValue()+1);
    }
    session.putValue (ERROR_KEY, errorCount);
  }

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

  response.setContentType("text/html");
  PrintWriter out = response.getWriter();
  out.println("<html>" +
    "<head><title>Session Information</title></head>" +
"<body bgcolor="#FFFFFF">" +
    "<h1>Session Information</h1><table>");
  out.println ("<tr><td>Identifier</td>");
  out.println ("<td>" + session.getId() + "</td></tr>");
  out.println ("<tr><td>Created</td>");
  out.println ("<td>" + new Date(
    session.getCreationTime()) + "</td></tr>");
  out.println ("<tr><td>Last Accessed</td>");
  out.println ("<td>" + new Date(
    session.getLastAccessedTime()) + "</td></tr>");
  out.println ("<tr><td>New Session?</td>");
  out.println ("<td>" + session.isNew() + "</td></tr>");
  String names[] = session.getValueNames();
  for (int i=0, n=names.length; i<n; i++) {
    out.println ("<tr><td>" + names[i] + "</td>");
    out.println ("<td>" + session.getValue (names[i])
      + "</td></tr>");
  }
  out.println("</table></center></body></html>");
  out.close();

Доступен полный код этого примера для тестирования. Одним моментом, не показанным в примере,
является способность закрыть сессию, если следующий вызов request.getSession(true) возвратит
другую сессию. Это делается при помощи вызова invalidate().

Если пользователь запретил в своем броузере использование полей cookie, вы можете закодировать
идентификатор сессии в HttpServletResponse, вызвав его метод encodeUrl().

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

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

   •   В методе init() происходит соединение с базой данных

       Connection con = null;
       public void init (ServletConfig cfg)
         throws ServletException {
         super.init (cfg);
         // Загрузить драйвер
         String name = cfg.getInitParameter("driver");
         Class.forName(name);
         // Получить Connection
         con = DriverManager.getConnection (urlString);
       }

   •   В методе doGet() происходит извлечение информации о базе данных

       public void doGet (HttpServletRequest request,
           HttpServletResponse response)
           throws ServletException, IOException {
         response.setContentType("text/html");
// Заставить броузер проигнорировать кеш – вызвать перезагрузку
            response.setHeader ("Expires",
              "Mon, 01 Jan 1990 00:00:00 GMT");

            Statement stmt = null;
            ResultSet result = null;

            try {
              // Составить запрос
              stmt = con.createStatement();
              result = stmt.executeQuery (
                "SELECT programmer, cups " +
                "FROM JoltData ORDER BY cups DESC;");

              // Создать выходную информацию
              PrintWriter out = response.getWriter();
              while(result.next()) {
                // Сгенерировать выходную информацию из ResultSet
              }
            } finally {
              if (result != null) {
                result.close();
              }
              if (stmt != null) {
                stmt.close();
              }
            }
            out.flush();
            out.close();
        }

    •   В методе destroy() происходит отсоединение от базы данных

        public void destroy() {
          super.destroy();
          con.close();
        }

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

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

«Песочница» для сервлетов
Сервлет может появиться из различных источников. Его может написать Web-мастер, его может написать
пользователь, он может быть получен как часть какого-либо пакета или загружен с какого-либо сайта.

В зависимости от источника получения сервлета с ним должен быть связан определенный уровень доверия.
Некоторые Web-серверы обеспечивают средства для установки различных уровней доверия различным
сервлетам. Эта концепция аналогична способу управления апплетами в броузерах и известна под названием
“песочница”.

«Песочница» для сервлетов – это область, в которой данные сервлеты имеют ограниченные полномочия на
сервере. Они не могут, например, получить доступ к файловой системе или сети, или им может быть
предоставлен статус более высокого доверия. Предоставление такого статуса определенным сервлетам
является задачей администратора Web-сервера. Обратите внимание, что сервлет с полным доверием может
получить полный доступ к файловой системе и сетевым возможностям. Он может даже выполнить
System.exit(), остановив сервер…

Списки управления доступом (ACL)
Многие Web-серверы позволяют вам ограничивать доступ к определенным Web-страницам и сервлетам при
помощи списков управления доступом (ACL). ACL – это список пользователей, которые могут выполнять
определенную функцию на сервере. В этом списке указывается:

   •   Тип разрешенного доступа
   •   Объект, к которому относится этот доступ
   •   Пользователи, которым предоставлен доступ

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

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

Потоки
Web-сервер может вызвать метод сервлета service() одновременно для нескольких запросов. Эта
ситуация поднимает вопрос потокозащищенности в сервлетах.

Но сначала рассмотрим то, о чем не надо беспокоиться – о методе сервлета init(). Этот метод вызывается
только один раз во время загрузки сервлета. Web-сервер вызывает метод init() при загрузке сервлета и
не вызывает его до тех пор, пока сервлет не будет выгружен и загружен повторно. Кроме того, методы
service() и destroy() не будут вызваны до тех пор, пока метод init() не завершит свою работу.

Вопрос становится более интересным при рассмотрении метода service(). Метод service() может
быть вызван Web-сервером для нескольких клиентов одновременно. (В JSDK 2.0 вы можете установить
сервлету интерфейс SingleThreadModel. Это приведет к тому, что каждый вызов метода service()
будет обработан последовательно. Разделяемые ресурсы, такие как файлы и базы данных, и в этом случае
требуют контроля за совместным доступом.)

Если ваш метод service() использует внешние ресурсы, такие как данные экземпляра объекта сервлета,
файлы или базы данных, вам необходимо внимательно исследовать, что может произойти при нескольких
одновременных вызовах метода service(). Например, предположим, что вы определили счетчик в классе
сервлета, хранящий количество методов service(), работающих в данный момент:

  private int counter = 0;

Далее, предположим, что ваш метод service() содержит следующие строки:

  int myNumber = counter + 1;           // строка 1
  counter = myNumber;                   // строка 2

  // остальная часть кода в методе service()

  counter = counter - 1;

Что может произойти при одновременной работе двух методов service(), которые оба выполнили строку
1 перед строкой 2? Оба будут иметь одинаковое значение для myNumber и counter не будет правильно
обновлен.
Baseof servletsjava intro
Baseof servletsjava intro

More Related Content

What's hot

Azure - хранение данных в облаке
Azure - хранение данных в облакеAzure - хранение данных в облаке
Azure - хранение данных в облакеAlexander Babich
 
Azure - облачные сервисы и приложения
Azure - облачные сервисы и приложенияAzure - облачные сервисы и приложения
Azure - облачные сервисы и приложенияAlexander Babich
 
Web deployment
Web deploymentWeb deployment
Web deploymentGetDev.NET
 
В поисках магической кнопки, или как воспитать SoapUI
В поисках магической кнопки, или как воспитать SoapUIВ поисках магической кнопки, или как воспитать SoapUI
В поисках магической кнопки, или как воспитать SoapUISQALab
 
Основы и нюансы параллельного тестрования
Основы и нюансы параллельного тестрованияОсновы и нюансы параллельного тестрования
Основы и нюансы параллельного тестрованияbearoff
 
Автоматизация тестирования многопоточности
Автоматизация тестирования многопоточностиАвтоматизация тестирования многопоточности
Автоматизация тестирования многопоточностиSQALab
 
Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разрабо...
Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разрабо...Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разрабо...
Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разрабо...Andrey Rebrov
 
!2hl++2008 Restful Architechture
!2hl++2008 Restful Architechture!2hl++2008 Restful Architechture
!2hl++2008 Restful ArchitechtureOntico
 
2015-12-12 | AzovDevMeetup 2015 | Enterprise приложения на PHP | Павел Крынецкий
2015-12-12 | AzovDevMeetup 2015 | Enterprise приложения на PHP | Павел Крынецкий2015-12-12 | AzovDevMeetup 2015 | Enterprise приложения на PHP | Павел Крынецкий
2015-12-12 | AzovDevMeetup 2015 | Enterprise приложения на PHP | Павел КрынецкийJSC “Arcadia Inc”
 
Dependency injection
Dependency injectionDependency injection
Dependency injectionPavel Usachev
 
Технология создания веб-сервисов на базе стандарта JAX-WS & JAXB
Технология создания веб-сервисов на базе стандарта JAX-WS & JAXBТехнология создания веб-сервисов на базе стандарта JAX-WS & JAXB
Технология создания веб-сервисов на базе стандарта JAX-WS & JAXBFedor Malyshkin
 
The Old New ASP.NET
The Old New ASP.NETThe Old New ASP.NET
The Old New ASP.NETVitaly Baum
 
Отказоустойчивые решения SQL
Отказоустойчивые решения SQLОтказоустойчивые решения SQL
Отказоустойчивые решения SQLAndrey Korshikov
 
Maven как средство сборки проекта
Maven как средство сборки проектаMaven как средство сборки проекта
Maven как средство сборки проектаYova Stoika
 
Как devops исчерпывает себя и что будет дальше
Как devops исчерпывает себя и что будет дальшеКак devops исчерпывает себя и что будет дальше
Как devops исчерпывает себя и что будет дальшеKirill Vechera
 
Лилия Горбачик, Тестирование Web Services
Лилия Горбачик, Тестирование Web ServicesЛилия Горбачик, Тестирование Web Services
Лилия Горбачик, Тестирование Web ServicesSQADays_2009_Piter
 
Высокопроизводительные приложения на базе Windows Azure
Высокопроизводительные приложения на базе Windows AzureВысокопроизводительные приложения на базе Windows Azure
Высокопроизводительные приложения на базе Windows AzureAlexander Feschenko
 
Maven 3 : уличная магия
Maven 3 : уличная магияMaven 3 : уличная магия
Maven 3 : уличная магияAleksey Solntsev
 
Автоматизированное тестирование WEB сервисов
Автоматизированное тестирование WEB сервисовАвтоматизированное тестирование WEB сервисов
Автоматизированное тестирование WEB сервисовSQALab
 

What's hot (20)

Azure - хранение данных в облаке
Azure - хранение данных в облакеAzure - хранение данных в облаке
Azure - хранение данных в облаке
 
Azure - облачные сервисы и приложения
Azure - облачные сервисы и приложенияAzure - облачные сервисы и приложения
Azure - облачные сервисы и приложения
 
Web deployment
Web deploymentWeb deployment
Web deployment
 
В поисках магической кнопки, или как воспитать SoapUI
В поисках магической кнопки, или как воспитать SoapUIВ поисках магической кнопки, или как воспитать SoapUI
В поисках магической кнопки, или как воспитать SoapUI
 
Правильный REST API
Правильный REST APIПравильный REST API
Правильный REST API
 
Основы и нюансы параллельного тестрования
Основы и нюансы параллельного тестрованияОсновы и нюансы параллельного тестрования
Основы и нюансы параллельного тестрования
 
Автоматизация тестирования многопоточности
Автоматизация тестирования многопоточностиАвтоматизация тестирования многопоточности
Автоматизация тестирования многопоточности
 
Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разрабо...
Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разрабо...Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разрабо...
Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разрабо...
 
!2hl++2008 Restful Architechture
!2hl++2008 Restful Architechture!2hl++2008 Restful Architechture
!2hl++2008 Restful Architechture
 
2015-12-12 | AzovDevMeetup 2015 | Enterprise приложения на PHP | Павел Крынецкий
2015-12-12 | AzovDevMeetup 2015 | Enterprise приложения на PHP | Павел Крынецкий2015-12-12 | AzovDevMeetup 2015 | Enterprise приложения на PHP | Павел Крынецкий
2015-12-12 | AzovDevMeetup 2015 | Enterprise приложения на PHP | Павел Крынецкий
 
Dependency injection
Dependency injectionDependency injection
Dependency injection
 
Технология создания веб-сервисов на базе стандарта JAX-WS & JAXB
Технология создания веб-сервисов на базе стандарта JAX-WS & JAXBТехнология создания веб-сервисов на базе стандарта JAX-WS & JAXB
Технология создания веб-сервисов на базе стандарта JAX-WS & JAXB
 
The Old New ASP.NET
The Old New ASP.NETThe Old New ASP.NET
The Old New ASP.NET
 
Отказоустойчивые решения SQL
Отказоустойчивые решения SQLОтказоустойчивые решения SQL
Отказоустойчивые решения SQL
 
Maven как средство сборки проекта
Maven как средство сборки проектаMaven как средство сборки проекта
Maven как средство сборки проекта
 
Как devops исчерпывает себя и что будет дальше
Как devops исчерпывает себя и что будет дальшеКак devops исчерпывает себя и что будет дальше
Как devops исчерпывает себя и что будет дальше
 
Лилия Горбачик, Тестирование Web Services
Лилия Горбачик, Тестирование Web ServicesЛилия Горбачик, Тестирование Web Services
Лилия Горбачик, Тестирование Web Services
 
Высокопроизводительные приложения на базе Windows Azure
Высокопроизводительные приложения на базе Windows AzureВысокопроизводительные приложения на базе Windows Azure
Высокопроизводительные приложения на базе Windows Azure
 
Maven 3 : уличная магия
Maven 3 : уличная магияMaven 3 : уличная магия
Maven 3 : уличная магия
 
Автоматизированное тестирование WEB сервисов
Автоматизированное тестирование WEB сервисовАвтоматизированное тестирование WEB сервисов
Автоматизированное тестирование WEB сервисов
 

Similar to Baseof servletsjava intro

Введение в Spring
Введение в SpringВведение в Spring
Введение в SpringUnguryan Vitaliy
 
08-170327133157.pdf
08-170327133157.pdf08-170327133157.pdf
08-170327133157.pdfssuser0562f1
 
Протокол HTTP. Клиент-серверная модель взаимодействия. Servlet API
Протокол HTTP. Клиент-серверная модель взаимодействия. Servlet APIПротокол HTTP. Клиент-серверная модель взаимодействия. Servlet API
Протокол HTTP. Клиент-серверная модель взаимодействия. Servlet APIEkaterina Kuchinskaya
 
ASP.NET, MVC, ASP.NET MVC
ASP.NET, MVC, ASP.NET MVCASP.NET, MVC, ASP.NET MVC
ASP.NET, MVC, ASP.NET MVCGetDev.NET
 
C# Web. Занятие 01.
C# Web. Занятие 01.C# Web. Занятие 01.
C# Web. Занятие 01.Igor Shkulipa
 
Платформа для автоматического тестирования Erlang проектов на примере UserGat...
Платформа для автоматического тестирования Erlang проектов на примере UserGat...Платформа для автоматического тестирования Erlang проектов на примере UserGat...
Платформа для автоматического тестирования Erlang проектов на примере UserGat...DevDay
 
Meeting #4. Frameworks.
Meeting #4. Frameworks.Meeting #4. Frameworks.
Meeting #4. Frameworks.Igor Khrol
 
7 создание веб сервисов
7 создание веб сервисов7 создание веб сервисов
7 создание веб сервисовKewpaN
 
Павел Брылов, Skype
Павел Брылов, SkypeПавел Брылов, Skype
Павел Брылов, SkypeOntico
 
Enterpise&Webservices
Enterpise&WebservicesEnterpise&Webservices
Enterpise&Webservicesscassau
 
Java осень 2014 занятие 1
Java осень 2014 занятие 1Java осень 2014 занятие 1
Java осень 2014 занятие 1Technopark
 
JavaScript Базовый. Занятие 09.
JavaScript Базовый. Занятие 09.JavaScript Базовый. Занятие 09.
JavaScript Базовый. Занятие 09.Igor Shkulipa
 
Андрей Завадский "Бессерверная архитектура"
 Андрей Завадский "Бессерверная архитектура" Андрей Завадский "Бессерверная архитектура"
Андрей Завадский "Бессерверная архитектура"Fwdays
 
Повышение эффективности Java приложений (новые возможности Web Logic 12c, кон...
Повышение эффективности Java приложений (новые возможности Web Logic 12c, кон...Повышение эффективности Java приложений (новые возможности Web Logic 12c, кон...
Повышение эффективности Java приложений (новые возможности Web Logic 12c, кон...Andrey Akulov
 

Similar to Baseof servletsjava intro (20)

servlets.pdf
servlets.pdfservlets.pdf
servlets.pdf
 
servlets1.pdf
servlets1.pdfservlets1.pdf
servlets1.pdf
 
Сервлеты
СервлетыСервлеты
Сервлеты
 
Введение в Spring
Введение в SpringВведение в Spring
Введение в Spring
 
08-170327133157.pdf
08-170327133157.pdf08-170327133157.pdf
08-170327133157.pdf
 
ASP.NET MVC
ASP.NET MVCASP.NET MVC
ASP.NET MVC
 
Протокол HTTP. Клиент-серверная модель взаимодействия. Servlet API
Протокол HTTP. Клиент-серверная модель взаимодействия. Servlet APIПротокол HTTP. Клиент-серверная модель взаимодействия. Servlet API
Протокол HTTP. Клиент-серверная модель взаимодействия. Servlet API
 
ASP.NET, MVC, ASP.NET MVC
ASP.NET, MVC, ASP.NET MVCASP.NET, MVC, ASP.NET MVC
ASP.NET, MVC, ASP.NET MVC
 
C# Web. Занятие 01.
C# Web. Занятие 01.C# Web. Занятие 01.
C# Web. Занятие 01.
 
Платформа для автоматического тестирования Erlang проектов на примере UserGat...
Платформа для автоматического тестирования Erlang проектов на примере UserGat...Платформа для автоматического тестирования Erlang проектов на примере UserGat...
Платформа для автоматического тестирования Erlang проектов на примере UserGat...
 
Meeting #4. Frameworks.
Meeting #4. Frameworks.Meeting #4. Frameworks.
Meeting #4. Frameworks.
 
ASP.NET MVC: new era?
ASP.NET MVC: new era?ASP.NET MVC: new era?
ASP.NET MVC: new era?
 
7 создание веб сервисов
7 создание веб сервисов7 создание веб сервисов
7 создание веб сервисов
 
Основы Java. 4. Web
Основы Java. 4. WebОсновы Java. 4. Web
Основы Java. 4. Web
 
Павел Брылов, Skype
Павел Брылов, SkypeПавел Брылов, Skype
Павел Брылов, Skype
 
Enterpise&Webservices
Enterpise&WebservicesEnterpise&Webservices
Enterpise&Webservices
 
Java осень 2014 занятие 1
Java осень 2014 занятие 1Java осень 2014 занятие 1
Java осень 2014 занятие 1
 
JavaScript Базовый. Занятие 09.
JavaScript Базовый. Занятие 09.JavaScript Базовый. Занятие 09.
JavaScript Базовый. Занятие 09.
 
Андрей Завадский "Бессерверная архитектура"
 Андрей Завадский "Бессерверная архитектура" Андрей Завадский "Бессерверная архитектура"
Андрей Завадский "Бессерверная архитектура"
 
Повышение эффективности Java приложений (новые возможности Web Logic 12c, кон...
Повышение эффективности Java приложений (новые возможности Web Logic 12c, кон...Повышение эффективности Java приложений (новые возможности Web Logic 12c, кон...
Повышение эффективности Java приложений (новые возможности Web Logic 12c, кон...
 

Baseof servletsjava intro

  • 1. Основы сервлетов Java™: Предисловие By MageLang Institute Январь 1999 JavaSM Developer ConnectionSM (JDC) представляет краткий курс «Основы сервлетов Java», написанный MageLang Institute, являющимся обладателем лицензии Java Software. MageLang является ведущим поставщиком обучающих курсов по технологии Java™ и регулярным участником JDC с 1996 года. MageLang Institute с самого основания в 1995 году способствовал росту сообщества пользователей Java, обеспечивая отличное обучение и действуя как независимый ресурс. Более подробную информацию по их обучающим курсам по Java технологии можно найти на Web-сайте http://www.magelang.com/jdc. Об этом кратком курсе В курсе «Основы сервлетов Java» показывается, как улучшить функциональность Web-сервера, используя безопасные, эффективные и мощные сервлеты. Поняв концепции, содержащиеся в заметках по курсу, и выполнив упражнения, вы научитесь использовать Java Servlet API, создавать и запускать сервлеты с JSDK, устанавливать сервлеты на Java Web Server™, передавать параметры из HTML-документов и управлять программами, работающими на промежуточном уровне. Короче говоря, вы научитесь всему необходимому для начала построения клиент-серверных приложений с HTML-интерфейсами. Примечание. Этот курс создавался для JSDK 2.1 и может не работать с более новыми версиями. Этот курс был написан несколькими членами команды MageLang Institute. Обратная связь с читателем Сообщите нам, что вы думаете об этом учебном пособии. Очень ценная информация. Ценная информация. Ничего особенного. Если у вас есть другие комментарии или идеи для будущих статей, пожалуйста, напишите нам. Введение Сервлеты представляют собой фрагменты исходного кода Java™, которые добавляют новые функциональные возможности на Web-сервер так же как апплеты добавляют функциональные возможности в броузер. Сервлеты поддерживают модель вычислений запрос-ответ, которая широко используется Web- серверами. Согласно этой модели, клиент посылает сообщение с запросом на сервер, а сервер, в свою очередь, отвечает клиенту, посылая ответное сообщение. Для создания сервлетов, отвечающих на запросы клиентов, вы используете Java Servlet API из Java Servlet Development Kit (JSDK). Сервлеты могут выполнять разные задачи, например, обработка HTML-документов специальным сервлетом, или управление работой промежуточного уровня для соединения с существующим источником данных за корпоративным брандмауэром. Кроме того, сервлеты могут управлять службами, например, сессиями базы данных, между запросами на обслуживание ресурсов лучше, чем технологии Common Gateway Interface (CGI). Java Servlet API основан на нескольких интерфейсах Java, которые находятся в пакете стандартного расширения Java (javax). В этом курсе вы:
  • 2. Научитесь использовать Java Servlet API • Создадите и запустите сервлет при помощи JSDK • Установите сервлет на Java Web Server™ от Sun • Обработаете параметры, переданные из HTML-документа • Будете управлять работой промежуточного уровня Java Servlet API • Место сервлетов в архитектуре o Работа на промежуточном уровне o Прокси-серверы o Поддержка протоколов • Поддержка HTML o Встраивание в HTML o SSI o Замещение CGI-сценариев • Установка сервлетов o Временные сервлеты против постоянных o Использование servletrunner o Использование Java Web Server • Servlet API o Жизненный цикл сервлета Метод init() Метод service() Метод destroy() Пример сервлета o Контекст сервлета Информация об инициализации сервлета Информация о контексте сервера Контекст сервлета во время запроса на обслуживание o Классы утилит o Поддержка HTTP Метод HTTP GET Метод HEAD Метод POST Классы поддержки HTTP Использование классов поддержки HTTP o Резюме javax.servlet: Поддержка сервлетов общего назначения javax.servlet.http: Поддержка сервлетов HTTP • Примеры сервлетов o Генерация встроенного содержания o Обработка запроса HTTP Post o Использование полей Cookie o Управление информацией о сессии o Соединение с базами данных • Вопросы безопасности o «Песочница» для сервлетов o Списки управления доступом (ACL)
  • 3. Потоки • JSDK 1.0 и JSDK 2.0 o Новые возможности сервлетов в JSDK 2.0 • Дополнительная информация Java Servlet API Сервлет представляет собой компонент Java™, который может быть помещен на Web-сервер, поддерживающий Java, для обеспечения специальных услуг. Эти услуги могут включать: • Новые возможности • Изменения содержания во время исполнения • Изменения представления во время исполнения • Новые стандартные протоколы (такие как FTP) • Новые специальные протоколы Сервлеты разработаны для работы по принципу запрос-ответ. Согласно модели запрос-ответ клиент посылает сообщение с запросом на сервер, а сервер, в свою очередь, отвечает клиенту, посылая ответное сообщение. Запросы могут приходить в форме HTTP, URL, FTP, URL или по специальному протоколу. Запрос и соответствующий ответ отражают состояние клиента и сервера на момент запроса. Обычно состоянием соединения клиент-сервер нельзя управлять параллельно различными парами запрос-ответ. Однако информация о сессии может изменяться сервлетами при помощи средств, описанных ниже. Java Servlet API включает несколько интерфейсов Java и полностью определяет связь между сервером и сервлетами. Servlet API определяется как расширение стандартного JDK. Расширения JDK пакетируются в библиотеке javax – корневой в дереве библиотек расширений Java. Java Servlet API содержит следующие пакеты: • Пакет javax.servlet • Пакет javax.servlet.http Сервлеты являются мощным дополнением к среде Java. Они являются быстрыми, безопасными, надежными и на 100% написаны на Java. Поскольку сервлеты помещаются на существующий сервер, они используют большое количество существующего кода и много технологий. Сервер управляет сетевыми соединениями, согласованием протоколов, загрузкой классов и многим другим; вся эта работа не требует дублирования! И, поскольку сервлеты располагаются на промежуточном уровне, они предназначены для повышения гибкости и улучшения возможностей системы. В этом курсе вы изучите Servlet API и кратко ознакомитесь с возможностями, которые могут реализовывать сервлеты. Место сервлетов в архитектуре Из-за своей мощности и гибкости, сервлеты могут играть значительную роль в архитектуре системы. Они могут выполнять прикладные задачи, предназначенные для промежуточного уровня, работать как прокси- сервер для клиента и даже улучшать функциональность промежуточного уровня, добавляя поддержку новых протоколов и других функций. Промежуточный уровень выполняет функции сервера приложений в так называемой трехуровневой системе клиент-сервер и расположен между «легковесным2 клиентом, таким как Web-броузер, и источником данных.
  • 4. Работа на промежуточном уровне Во многих системах промежуточный уровень работает как связь между клиентами и прикладными службами. При использовании промежуточного уровня большая часть работы может быть перенесена и с клиентов (делая их легче и быстрее), и с серверов (позволяя им сконцентрироваться на своих задачах). Одним из преимуществ работы на промежуточном уровне является простое управление соединениями. Набор сервлетов может управлять соединениями с сотнями, если не с тысячами, клиентов, повторно используя набор ресурсоемких соединений с серверами баз данных. Другие функции промежуточного уровня включают: • Применение бизнес-правил • Управление транзакциями • Отображение клиентов на резервные серверы • Поддержка различных типов клиентов, таких как HTML и Java клиентов. Прокси-серверы При использовании для поддержки апплетов сервлеты могут выполнять функции их прокси-серверов. Это может быть важно, поскольку система безопасности Java позволяет апплетам соединяться только с сервером, с которого они были загружены. Если апплет нуждается в соединении с сервером баз данных, расположенном на другой машине, сервлет может создать это соединение для апплета. Поддержка протокола Servlet API обеспечивает тесную связь между сервером и сервлетами. Это позволяет сервлетам добавлять на сервер поддержку нового протокола. (В пакет API добавлена поддержка протокола HTTP). По существу любой протокол, который использует модель вычислений запрос-ответ, может быть реализован сервлетом. Например: • SMTP • POP • FTP Поддержка сервлетов в настоящее время доступна в нескольких Web-серверах, и, возможно, в ближайшем будущем начнет появляться в других типах серверов приложений. В этом курсе вы будете использовать Web-сервер для сервлетов и применять только протокол HTTP. Поскольку HTTP является одним из наиболее распространенных протоколов и может обеспечивать разнообразное представление информации, сервлеты, возможно, используются главным образом для построения основанных на протоколе HTTP систем. Поддержка HTML HTML может обеспечить разнообразное представление информации по причине своей гибкости и широкого диапазона поддерживаемых им объектов. Сервлеты могут участвовать в создании содержимого HTML. Поддержка сервлетами HTML широко распространена. Пакет java.servlet.http предназначен для поддержки протокола HTTP и генерации HTML. Сложные Web-сайты часто требуют отображения HTML-страниц, настроенных на каждого пользователя, или даже на каждый показ. Для обработки HTML-страниц и настройки их во время передачи клиенту могут быть использованы сервлеты. Этот процесс может быть простым, как, например, простая подстановка на лету, или сложным, как, например, компиляция и генерация основанного на определенной грамматике описания страницы специального HTML-документа. Встраивание в HTML
  • 5. Некоторые Web-серверы, например Java Web Server™ (JWS), допускают непосредственное размещение тэгов сервлетов в файлах HTML. Если сервер встречает такой тэг, он вызывает сервлет во время передачи HTML-файла клиенту. Это позволяет сервлету вставлять свои данные прямо в исходящий поток HTML. SSI Еще один пример – обработка тэгов на лету, называемая SSI (server-side includes). Используя SSI, HTML- страница может содержать специальные команды, которые обрабатываются каждый раз при запросе этой страницы. Обычно, Web-сервер требует использования уникального расширения .shtml для HTML- страниц, использующих SSI. Например, если HTML-страница (с расширением .shtml) включает в себя строку: <!--#include virtual="/includes/page.html"-->, она будет воспринята Web- сервером как запрос на отображение указанного файла. Хотя SSI поддерживаются большинством Web- серверов, тэги SSI не стандартизованы. Сервлеты являются хорошим способом добавить обработку SSI на Web-сервер. Поскольку все больше и больше Web-серверов поддерживают сервлеты, вполне возможно написать стандартный сервлет обработки SSI и использовать его на различных Web-серверах. Замещение CGI-сценариев HTTP-сервлеты являются прямой заменой сценариев Common Gateway Interface (CGI). Сервлеты HTTP вызываются пользователем путем ввода URL в броузер или как результат действия HTML-формы. Например, если пользователь введет в поле адреса броузера следующий URL, броузер пошлет запрос сервлету на передачу HTML-страницы с текущим временем: http://localhost/servlet/DateTimeServlet. Сервлет DateTimeServlet ответит на этот запрос передачей HTML-страницы в броузер. Обратите внимание, что эти сервлеты не ограничиваются только генерацией Web-страниц; они могут выполнять любую другую функцию, такую как, например, запись и извлечение информации из базы данных, или открытие сокета на другой машине. Установка сервлетов Сервлеты не исполняются в таком же смысле как апплеты и приложения. Сервлеты обеспечивают функциональность, расширяющую сервер. Для тестирования сервлета необходимо выполнить следующие два действия: 1. Установить сервлет на сервер. 2. Послать сервлету запрос на обслуживание Существует много Web-серверов, поддерживающих сервлеты. Описание различных способов установки сервлетов на каждый тип сервера выходит за рамки данного курса. В этом курсе рассматриваются утилита JSDK servletrunner и JWS. Временные сервлеты против постоянных Сервлеты могут запускаться и останавливаться для каждого клиентского запроса. Также они могут запускаться при старте Web-сервера и существовать до его остановки. Временные сервлеты загружаются по требованию и предлагают хороший способ сохранения ресурсов сервера для редко используемых функций. Постоянные сервлеты загружаются при старте Web-сервера и существуют до его остановки. Сервлеты устанавливаются как постоянные расширения для сервера в том случае, если затраты по их запуску очень велики (например, установка соединения с DBMS), если они предлагают постоянную функциональность на стороне сервера (например, служба RMI), или в случаях, когда они должны отвечать на запросы клиента как можно быстрее. Не существует специального кода для назначения сервлета постоянным или временным; это функция настройки сервера.
  • 6. Поскольку сервлеты могут быть загружены при старте сервера, они могут использовать механизм автозагрузки для обеспечения более легкой загрузки Java-программ на стороне сервера. Эти программы могут обеспечивать функциональные возможности, являющиеся полностью уникальными и независимыми от Web-сервера. Например, сервлет мог бы обеспечить R-службы (rlogin, rsh, ...) через порт TCP/IP и в то же время использовать протокол запрос-ответ для представления и обработки HTML-страниц, используемых для управления сервлетом. Использование servletrunner Вы должны установить Java Servlet Development Kit (JSDK) как для JDK 1.1, так и для платформы Java 2. Перед использованием servletrunner проверьте, что переменная окружения PATH указывает на каталог, в котором он находится. Для JSDK 2.0, установленного с настройками по умолчанию, sevletrunner находится в каталоге c:jsdk2.0bin в операционной системе Windows. Для того, чтобы программа servletrunner получила доступ к пакетам сервлетов Java, проверьте, что переменная окружения CLASSPATH указывает на корректный JAR-файл, а именно c:jsdk2.0libjsdk.jar в операционной системе Windows. При работе с платформой Java 2, вместо редактирования переменной CLASSPATH, можно просто скопировать JAR-файл в каталог ext исполняющей среды Java. После этого пакеты сервлетов будут считаться стандартными расширениями. После установки переменных окружения запустите программу servletrunner из командной строки. Программа отобразит следующую информацию: Usage: 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 -s filename servlet property file name Наиболее простой способ запуска этой программы – перейти в каталог, содержащий ваши сервлеты, и запустить servletrunner. Однако при этом программа автоматически не конфигурируется для загрузки сервлетов из текущего каталога. Упражнение 1. Хостинг с программой servletrunner Использование Java Web Server Java Web Server (JWS) от Sun является полнофункциональным продуктом. К сведению разработчиков сервлетов он обладает способностью обнаруживать обновление сервлетов. Когда новый файл класса копируется в соответствующий каталог, это обнаруживается сервером, и он, при необходимости, автоматически перегружает любой работающий сервлет. JWS может быть установлен как сервис в операционной системе Windows NT. И хотя этот способ очень удобен для запуска сервера, он не рекомендуется при разработке сервлетов. В системе Windows 95 не существует системных сервисов, так что единственным выбором для запуска сервера является командная строка. Для запуска JWS из каталога c:JavaWebServer1.1bin наберите команду httpd. В терминальном окне запустится сервер. В этом окне не будет отображаться никакой информации, пока сервлет не выполнит команду System.out.println(). Сервлеты устанавливаются путем помещения их в каталог c:JavaWebServer1.1servlets. Как указывалось ранее, JWS обнаружит, что в каталог были добавлены сервлеты. Хотя вы можете использовать
  • 7. апплет управления JWS для установки сервлета, этот способ обычно не рекомендуется, за исключением установки производственного сервера. Для остановки JWS, нажмите Ctrl-C в командном окне. После остановки сервер выведет сообщение. Упражнение 2. Хостинг с программой Java Web Server Servlet API Java Servlet API определяет интерфейс между сервлетами и сервером. Следующие API спакетированы как стандартное расширение JDK в javax: • Пакет javax.servlet • Пакет javax.servlet.http API обеспечивает поддержку в четырех категориях: • Управление циклом жизни сервлета • Доступ к контексту сервлета • Классы утилит • Классы поддержки HTTP Жизненный цикл сервлета Сервлеты выполняются на платформе Web-сервера как часть того же процесса, что и сам Web-сервер. Web- сервер отвечает за инициализацию, вызов и уничтожение каждого экземпляра сервлета. Web-сервер взаимодействует с сервлетом через простой интерфейс: javax.servlet.Servlet. Этот интерфейс состоит из трех главных методов: • init() • service() • destroy() и двух вспомогательных методов: • getServletConfig() • getServletInfo() Вы можете заметить сходство между этим интерфейсом и интерфейсом апплетов Java. Именно так и было спроектировано! Сервлеты являются для Web-серверов тем же самым, чем являются апплеты для Web- броузеров. Апплет выполняется в Web-броузере, выполняя действия по его запросу через специальный интерфейс. Сервлет делает то же самое, работая на Web-сервере. Метод init() При первой загрузке сервлета вызывается метод init(). Это дает возможность сервлету выполнить любую работу по установке, например, открытие файлов или установку соединений с их серверами. Если сервлет установлен на сервере постоянно, он загружается при запуске сервера. В противном случае сервер активизирует сервлет при получении первого запроса от клиента на выполнение услуги, обеспечиваемой этим сервлетом. Гарантируется, что метод init() закончится перед любым другим обращением к сервлету – таким как, например, вызов метода service(). Обратите внимание, что init() будет вызван только один раз; он не будет вызываться до тех пор, пока сервлет не будет выгружен и затем загружен сервером снова.
  • 8. Метод init() принимает один аргумент – ссылку на объект ServletConfig, который содержит аргументы для инициализации сервлета. Этот объект имеет метод getServletContext(), возвращающий объект ServletContext, который содержит информацию об окружении сервлета (смотрите раздел «Контекст инициализации сервлета»). Метод service() Метод service() является сердцем сервлета. Каждый запрос от клиента приводит к одному вызову метода service(). Этот метод читает запрос и формирует ответное сообщение при помощи своих двух аргументов: • Объект ServletRequest содержит данные от клиента. Данные состоят из пар имя/значение и InputStream. Существует несколько методов, возвращающих информацию о параметрах клиента. InputStream может быть получен при помощи метода getInputStream(). Этот метод возвращает объект ServletInputStream, который можно использовать для получения дополнительных данных от клиента. Если вас интересует обработка символьных данных, а не двоичных, вы можете получить объект BufferedReader при помощи метода getReader(). • Объект ServletResponse содержит ответ сервлета клиенту. Во время подготовки ответа прежде всего вызывается метод setContentType() для установки типа MIME ответа. Затем могут быть использованы методы getOutputStream() или getWriter() для получения объектов и ServletOutputStream или PrintWriter соответственно для передачи данных обратно клиенту. Как вы можете заметить, существуют два способа передачи информации от клиента к сервлету. Первый – передача значений параметров, и второй – передача информации через InputStream (или Reader). Значения параметров могут быть вставлены в URL. Как это делается - обсуждается ниже. Чтение значений параметров сервлетом рассматривается далее. Работа метода service() по существу проста – он создает ответ на каждый клиентский запрос, переданный ему с сервера. Однако важно понимать, что могут существовать несколько запросов услуги, обрабатываемых в одно и то же время. Если ваш метод требует каких-либо внешних ресурсов, таких как файлы, базы данных, вы должны гарантировать, что доступ к ресурсам является потокозащищенным. Создание потокозащищенных сервлетов рассматривается далее. Метод destroy() Метод destroy() вызывается для того, чтобы дать вам возможность освободить все ресурсы (например, открытые файлы и соединения с базой данных) перед выгрузкой сервлета. Этот метод может быть пустым, если вам не надо выполнять никаких завершающих операций. Перед вызовом метода destroy() сервер ждет либо завершения всех обслуживающих операций, либо истечения определенного времени. Это означает, что метод destroy() может быть вызван во время выполнения какого-либо продолжительного метода service(). Важно, чтобы вы писали метод destroy() таким образом, чтобы избежать закрытия необходимых ресурсов до тех пор, пока все вызовы service() не завершатся. Пример сервлета Приведенный ниже код реализует простой сервлет, возвращающий статическую HTML-страницу броузеру. Этот пример полностью реализует интерфейс Servlet. import java.io.*; import javax.servlet.*; public SampleServlet implements Servlet { private ServletConfig config; public void init (ServletConfig config) throws ServletException { this.config = config;
  • 9. } public void destroy() {} // ничего не делать public ServletConfig getServletConfig() { return config; } public String getServletInfo() { return "A Simple Servlet"; } public void service (ServletRequest req, ServletResponse res ) throws ServletException, IOException { res.setContentType( "text/html" ); PrintWriter out = res.getWriter(); out.println( "<html>" ); out.println( "<head> ); out.println( "<title>A Sample Servlet</title>" ); out.println( "</head>" ); out.println( "<body>" ); out.println( "<h1>A Sample Servlet</h1>" ); out.println( "</body>" ); out.println( "</html>" ); out.close(); } } Контекст сервлета Сервлет живет и умирает в пределах процесса сервера. Сервлет может получать информацию о своем окружении в различное время. Во время запуска сервлета доступна информация об инициализации; в любое время доступна информация о сервере, кроме того, любой запрос может содержать дополнительную специфическую информацию. Информация об инициализации сервера Информация об инициализации передается сервлету при помощи параметра ServletConfig метода init(). Каждый Web-сервер обеспечивает свой способ передачи информации об инициализации в сервлет. Если, например, класс сервера DatePrintServlet принимает аргумент инициализации timezone, вы должны определить следующие свойства в файле servlets.properties: servlet.dateprinter.code=DatePrinterServlet servlet.dateprinter.timezone=PST Эта информация также может быть предоставлена административным средством GUI. В следующем коде сервлет получает доступ к информации timezone. String timezone; public void init(ServletConfig config) { timeZone = config.getInitParameter("timezone"); } Объект Enumeration со всеми параметрами инициализации доступен сервлету через метод getInitParameterNames(). Информация о контексте сервера Информация о контексте сервера доступна в любое время через объект ServletContext. Сервлет может получить этот объект, вызывая метод getServletContext() объекта ServletConfig. Помните, что
  • 10. этот объект передается сервлету во время инициализации. Грамотно написанный метод init() сохраняет ссылку в переменной, имеющей тип доступа private. Интерфейс ServletContext определяет несколько методов. Они перечислены ниже. Гибкий способ получения информации о сервере через пары атрибутов getAttribute() имя/значение. Зависит от сервера. GetMimeType() Возвращает тип MIME данного файла. Этот метод преобразует относительный или виртуальный путь в новый путь getRealPath() относительно месторасположения корня HTML-документов сервера. getServerInfo( Возвращает имя и версию сетевой службы, в которой исполняется сервлет. ) Возвращает объект Servlet указанного имени. Полезен при доступе к getServlet() службам других сервлетов. getServletName Возвращает список имен сервлетов, доступных в текущем пространстве s() имен. Записывает информацию в файл регистрации сервлета. Имя файла log() регистрации и его формат зависят от сервера. Следующий пример показывает, как сервлет использует сервер для записи сообщения в свой log-файл во время инициализации: private ServletConfig config; public void init(ServletConfig config) { // Сохранить config в переменной экземпляра this.config = config; ServletContext sc = config.getServletContext(); sc.log( "Started OK!" ); } Контекст сервлета во время запроса на обслуживание Каждый запрос на обслуживание может содержать информацию в форме пар параметров имя/значение, как ServletInputStream, или BufferedReader. Эта информация доступна при помощи объекта ServletRequest, который передается в метод service(). Следующий код показывает, как получить информацию во время работы метода service(): BufferedReader reader; String param1; String param2; public void service ( ServletRequest req, ServletResponse res) { reader = req.getReader(); param1 = req.getParameter("First"); param2 = req.getParameter("Second"); } Существует также дополнительная информация, доступная сервлету через объект ServletRequest. Она приведена в следующей таблице. getAttribute() Возвращает значение указанного атрибута этого запроса. getContentLength( Размер запроса, если известен. ) getContentType() Возвращает тип MIME тела запроса. getInputStream() Возвращает InputStream для чтения двоичных данных из тела
  • 11. запроса. GetParameterNames Возвращает массив строк с именами всех параметров. () getParameterValue Возвращает массив значений для указанного параметра. s() Возвращает протокол и версию для запроса как строку вида getProtocol() <protocol>/<major version>.<minor version>. getReader() Возвращает BufferedReader для получения текста из тела запроса. getRealPath() Возвращает реальный путь для указанного виртуального пути. getRemoteAddr() IP-адрес клиента, пославшего данный запрос. getRemoteHost() Имя хоста клиентской машины, пославшего данный запрос. Возвращает схему, используемую в URL этого запроса (например, https, getScheme() http, ftp, и т.д.). getServerName() Имя хоста сервера, принявшего данный запрос. getServerPort() Возвращает номер порта, используемого для приема этого запроса. Следующее упражнение показывает, как получить параметры запроса на обслуживание. Упражнение 3. Получение доступа к параметрам сервлета во время обслуживания Классы утилит Servlet API предоставляет несколько утилит. Одной из них является интерфейс javax.servlet.SingleThreadModel, который облегчает написание простых сервлетов. Если сервлет реализует этот интерфейс, сервер знает, что он никогда он не должен вызывать его метод service() во время обработки запроса. То есть, сервер обрабатывает все запросы на обслуживание в одном потоке. Это облегчает написание сервлета, но может привести к ухудшению производительности. Полностью эта тема будет рассмотрена ниже. В Servlet API включены два класса исключительных ситуаций. Исключительная ситуация javax.servlet.ServletException может быть использована при общем отказе сервлета для информирования сервера о возникновении проблемы. Исключительная ситуация javax.servlet.UnavailableException указывает, что сервлет недоступен. Сервлеты могут сгенерировать эту исключительную ситуацию в любое время. Существует два типа недоступности: • Постоянная. Сервлет не может функционировать до вмешательства администратора. В этой ситуации сервлет должен сделать запись в log-файле с отчетом о проблеме и возможных методах ее решения. • Временная. Сервлет обнаружил (потенциально) временную проблему, например, переполнение диска, сбой сервера и т.д. Проблема может исчезнуть сама собой или может потребоваться вмешательство оператора. Поддержка HTTP Сервлеты, использующие HTTP-протокол, очень широко распространены. Нет ничего неожиданного в том, что существует специальная помощь для разработчиков таких сервлетов. Поддержка обработки протокола HTTP обеспечивается в пакете javax.servlet.http. Перед рассмотрением этого пакета, рассмотрим сам протокол HTTP. HTTP – это аббревиатура от HyperText Transfer Protocol – протокол передачи гипертекста. Он представляет собой протокол, используемый Web-броузерами и серверами для взаимодействия друг с другом. Протокол определяет набор текстовых запросов, называемых HTTP-методами. (Примечание: Название HTTP-методы
  • 12. используется в спецификации HTTP; не смешивайте этот термин с методами Java. Думайте о методах HTTP как о сообщениях, вызывающих определенный тип реакции). HTTP-методы включают: • GET • EAD • POST • PUT • DELETE • TRACE • CONNECT • OPTIONS В это курсе мы будем применять только три из этих методов: GET, HEAD и POST. Метод HTTP GET HTTP-метод GET запрашивает информацию у Web-сервера. Этой информацией может быть файл, выходная информация от устройства, расположенного на сервере, или выходная информация от программы (например, сервлета или CGI-сценария). HTTP-запрос GET выглядит так: GET URL <http version> Host: <target host> плюс еще несколько строк информации. Например, следующее сообщение HTTP GET запрашивает домашнюю страницу Web-сайта MageLang: GET / HTTP/1.1 Connection: Keep-Alive User-Agent: Mozilla/4.0 ( compatible; MSIE 4.01; Windows NT) Host: www.magelang.com Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg На большинстве Web-серверов к сервлетам обращаются при помощи URL, начинающегося с /servlet/. Следующий метод HTTP GET запрашивает сервлет MyServlet на хосте www.magelang.com: GET /servlet/MyServlet?name=Scott& company=MageLang%20Institute HTTP/1.1 Connection: Keep-Alive User-Agent: Mozilla/4.0 ( compatible; MSIE 4.01; Windows NT) Host: www.magelang.com Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg URL в этом запросе GET вызывает сервлет с именем MyServlet и содержит два параметра: name и company. Каждый параметр представляет собой пару имя/значение, имеющую формат name=value. Параметры указываются после имени сервлета и знака вопроса (“?”), каждый параметр разделен знаком амперсанда (“&”). Обратите внимание на использование в поле значения параметра company символов %20. Пробел указывал бы на конец URL в строке запроса GET, поэтому он должен быть заколирован или заменен символами %20.
  • 13. Как вы увидите далее, разработчики сервлетов могут не беспокоиться о таком кодировании, поскольку эти символы будут автоматически декодированы классом HttpServletRequest. Запрос HTTP GET имеет одно существенное ограничение. Большинство Web-серверов ограничивают размер данных, которые можно передавать как часть имени URL (обычно несколько сотен байт). Если между клиентом и сервером нужно передать больше данных, необходимо использовать метод HTTP POST. Важно отметить, что обработка сервером метода GET должна быть безопасной и идемпотентной. Это означает, что метод GET не вызовет никаких побочных действий и может выполняться повторно. При ответе сервера на запрос HTTP GET им посылается ответное сообщение HTTP. Заголовок ответа HTTP может выглядеть так: HTTP/1.1 200 Document follows Date: Tue, 14 Apr 1997 09:25:19 PST Server: JWS/1.1 Last-modified: Mon, 17 Jun 1996 21:53:08 GMT Content-type: text/html Content-length: 4435 <4435 bytes worth of data -- the document body> Метод HEAD Метод HTTP HEAD очень похож на метод HTTP GET. Запрос выглядит точно так же, как и запрос GET (за исключением того, что вместо слова GET используется HEAD), но сервер возвращает только информацию о заголовке. HEAD часто используется для проверки: • Даты последней модификации документа на сервере для целей кеширования. • Размера документа перед загрузкой (чтобы броузер мог отобразить процесс загрузки) • Типа сервера для того, чтобы пользователь мог оптимизировать запрос для этого сервера • Типа запрашиваемого документа, чтобы пользователь был уверен, что сможет его обработать. Метод HEAD, подобно GET, должен быть безопасным и идемпотентным. Метод POST Запрос HTTP POST позволяет клиенту посылать данные на сервер. Эта передача данных может преследовать разные цели, например: • Отправка сообщений в группу новостей • Добавление записей в гостевую книгу Web-сайта • Передача большего количества информации, чем может передать запрос GET Обратите особое внимание на третью цель. Запрос HTTP GET передает все свои аргументы как часть URL. Многие Web-серверы ограничивают размер информации, которую они могут принять как часть URL. Метод POST передает всю свою информацию в потоке данных, снимая это ограничение. Типичный запрос POST может выглядеть так: POST /servlet/MyServlet HTTP/1.1 User-Agent: Mozilla/4.0 ( compatible; MSIE 4.01; Windows NT) Host: www.magelang.com Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */
  • 14. Content-type: application/x-www-form-urlencoded Content-length: 39 name=Scott&company=MageLang%20Institute Обратите внимание на пустую строку – она указывает на конец заголовка запроса POST и начало дополнительной информации. В отличие от метода GET, POST может и не быть безопасным и идемпотентным; он может выполнять модификацию данных и может не быть повторяемым. Классы поддержки HTTP Сейчас, когда вы уже ознакомлены с протоколом HTTP, рассмотрим, как пакет javax.servlet.http может помочь в написании HTTP-сервлетов. Абстрактный класс javax.servlet.http.HttpServlet обеспечивает реализацию интерфейса javax.servlet.Servlet и включает много полезных функциональных возможностей. Самый простой путь написания HTTP-сервлетов – расширить класс HttpServlet и добавить вашу собственную специальную обработку. Класс HttpServlet обеспечивает реализацию метода service(), который распределяет HTTP- сообщения по различным специализированным методам. Этими методами являются: • doGet() • doHead() • doDelete() • doOptions() • doPost() • doTrace() Эти методы соответствуют методам протокола HTTP. Как показано на диаграмме, приведенной ниже, метод service() интерпретирует каждый метод HTTP и определяет, является ли он методом HTTP GET, POST, HEAD или другим методом протокола HTTP. Класс HttpServlet действительно достаточно интеллектуален. Он не только распределяет HTTP-запросы, но и определяет, какие методы переопределены в подклассе, и может сообщать клиенту о возможностях сервера. (Простое переопределение метода doGet() является причиной того, что класс отвечает на метод HTTP OPTIONS о поддержке им методов GET, HEAD, TRACK и OPTIONS. Эти возможности поддерживаются кодом класса). Вот другой пример поддержки, которую обеспечивает класс HttpServlet. Если переопределить метод doGet(), метод HTTP HEAD сгенерирует автоматический ответ. (Поскольку ответ на метод HTTP HEAD
  • 15. идентичен ответу на метод HTTP GET – если не принимать во внимание тело сообщения – класс HttpServlet может генерировать соответствующий ответ на запрос HTTP HEAD из метода doGet()). Если требуется более точный контроль, вы всегда можете переопределить метод doHead() и обеспечить нестандартный ответ. Использование классов поддержки HTTP При использовании классов поддержки HTTP, вы обычно создаете новый сервлет, который расширяет класс HttpServlet и переопределяет метод doGet(), или doPost(), или, возможно, оба. Для более точного управления могут быть переопределены другие методы. Методы обработки HTTP принимают два параметра: объект HttpServletRequest и HttpServletResponse. Класс HttpServletRequest содержит несколько удобных методов для синтаксического анализа запроса; вы можете производить этот анализ самостоятельно, просто читая текст запроса. Метод сервлета doGet() может: • Читать данные запроса, например, входные параметры • Настраивать заголовки ответа (длину, тип заголовка и тип кодировки) • Писать данные ответа Важно отметить, что обработка метода GET должна быть безопасной и идемпотентной. • Обработка считается безопасной, если нет никаких побочных эффектов, за которые несет ответственность пользователь, например, получение им доступа к базе данных или сохранение данных. • Обработка считается идемпотентной, если она может быть произведена повторно. Это дает возможность пользователю без проблем повторять запрос GET. Следуйте следующему правилу: GET должен «смотреть и ничего не трогать». Если необходима обработка, имеющая побочные эффекты, должен быть использован другой метод HTTP, например, POST. Если необходимо обрабатывать отправляемый HTML-документ или большое количество данных, передаваемых клиентом, должен использоваться метод сервлета doPost(). Обработка метода HTTP POST детально рассматривается ниже. Запрос HEAD обрабатывается в методе doGet() класса HttpServlet. Вы можете просто реализовать метод doGet() и все будет сделано; любые данные документа, которые вы запишете в выходной поток ответа, не будут переданы клиенту. Однако более эффективной реализацией была бы проверка того, является ли запрос типом GET или HEAD, и если HEAD – не записывать данные в выходной поток ответа. Резюме Java Servlet API является стандартным расширением. Это означает, что существует подробное определение интерфейсов сервлетов, но оно не является частью Java Development Kit (JDK) 1.1 или платформы Java 2. Классы сервлетов распространяются с Java Servlet Development Kit (JSDK) версии 2.0 от Sun (http://java.sun.com/products/servlet/). Эта версия предназначена для работы и с JDK 1.1 и с платформой Java 2. Существует несколько важных отличий между JSDK 2.0 и JSDK 1.0. Детальная информация приведена в разделе «JSDK 1.0 и JSDK 2.0». Если вы используете более раннюю версию, чем JSDK 2.0, рекомендуется обновить ее до JSDK 2.0. Поддержка сервлетов разделена на два пакета: javax.servlet: Поддержка сервлетов общего назначения Интерфейс, определяющий взаимодействие между Web-сервером и Servlet сервлетом. Этот интерфейс определяет методы init(), service() и destroy() (и несколько других).
  • 16. Интерфейс, описывающий конфигурационные параметры сервлета. Они передаются сервлету при вызове Web-сервером его метода init(). Обратите внимание, что сервлет должен сохранить ссылку ServletConfig на объект ServletConfig, и определить метод getServletConfig() для его возврата при запросе. Этот интерфейс определяет, как получить параметры инициализации для сервлета и контекст, в котором сервлет исполняется. Интерфейс, описывающий, как сервлет может получить информацию о сервере, на котором он исполняется. Она может быть ServletContext получена при помощи метода getServletContext() объекта ServletConfig. Интерфейс, описывающий, как получить информацию о клиентском ServletRequest запросе. Интерфейс, описывающий, как передать ответную информацию ServletResponse клиенту. Реализация основы сервлета. Интерфейс заботится о сохранении ссылки на объект ServletConfig и обеспечивает несколько GenericServlet методов, делегирующих свою функциональность объекту ServletConfig. Он также обеспечивает фиктивную реализацию методов init() и destroy(). Подкласс класса InputStream, используемый для чтения части ServletInputStream данных клиентского запроса. Он дополнен для удобства методом readLine(). Поток OutputStream, в котором записываются ответные данные ServletOutputStream для клиента. ServletException Должна генерироваться при возникновении проблем с сервлетом. Должна генерироваться в случаях, когда сервлет недоступен по UnavailableException разным причинам. javax.servlet.http: Поддержка сервлетов http Подкласс класса ServletRequest, определяющего несколько HttpServletRequest методов, которые осуществляют синтаксический анализ заголовков HTTP-запросов. Подкласс класса ServletResponse, обеспечивающий доступ и HttpServletResponse интерпретацию информации о кодах состояния HTTP и заголовке. Подкласс класса GenericServlet, обеспечивающий автоматическую сортировку HTTP-запросов по типу метода. HttpServlet Например, запрос HTTP GET будет обработан методом service() и передан методу doGet(). Класс, обеспечивающий поддержку для синтаксического анализа HttpUtils запросов HTTP GET и POST. Примеры сервлетов Рассмотрим подробнее несколько сервлетов. А именно: • Генерация встроенного контекста • Обработка запросов HTTP Post • Использование Cookie • Управление информацией о сессии • Соединение с базами данных Генерация встроенного содержания
  • 17. Иногда требуется вставить в Web-страницу только небольшой фрагмент с изменяющейся информацией. Вся остальная информация может быть статической. Для замены только небольшого фрагмента информации некоторые Web-серверы поддерживают концепцию SSI (Server-Side Includes). Если Web-сервер поддерживает SSI, то при обработке файла со специальным расширением (обычно .shtml) он должен искать теги SSI. JWS определяет специальный SSI-тег, называемый <servlet>, например, <servlet code="DatePrintServlet"> <param name=timezone value=pst> </servlet> Этот тэг вызывает сервлет с именем DatePrintServlet для генерации встроенного содержания. SSI и сервлеты дают возможность дизайнеру HTML-страницы написать шаблон страницы и использовать сервлеты для заполнения ее участков. Это очень полезно, например, для счетчиков посещений и других небольших фрагментов. Сервлет DatePrintServlet работает подобно обычному сервлету, за исключением того, что он спроектирован для очень маленького ответа, а не полной HTML-страницы. MIME-тип ответа устанавливается в “text/plain”, а не “text/html”. Имейте в виду, что синтаксис SSI, даже если они поддерживаются, может значительно отличаться у разных Web-серверов. В следующем упражнении вы создадите сервлет DatePrintServlet и увидите, как использовать его в HTML-странице. Упражнение 4. Генерация встроенного содержания Обработка запросов HTTP Post Обработка метода HTTP POST отличается от обработки метода HTTP GET несколькими моментами. Во- первых, поскольку POST может изменять данные на сервере, может возникнуть необходимость безопасной обработки обновлений, приходящих от нескольких клиентов в одно и то же время. Во-вторых, так как размер потока данных, передаваемых клиентом, может быть очень большим, метод doPost() должен открывать InputStream (или Reader) от клиента для получения информации. В отличие от метода HTTP GET метод HTTP POST не поддерживает передачу параметров, закодированных в URL. Проблема поддержки одновременных обновлений от нескольких клиентов решается системой управления базой данных (СУБД). К сожалению, протокол HTTP не достаточно хорошо работает с базами данных, а именно из-за того, что СУБД требует поддержания постоянного соединения между собой и клиентом для определения того, какой клиент пытается изменить данные. Протокол HTTP не поддерживает такого типа соединений, так как основан на сообщениях и не изменяет своего состояния. Решить эту проблему не легко, и решается она всегда не очень элегантным способом. К счастью, Servlet API определяет средство для отслеживания сессии клиент/сервер. Этот вопрос рассматривается далее в разделе «Управление информацией о сессии». Без отслеживания сессии можно прибегнуть к другим стратегиям. Все они включают запись данных на клиент в скрытые поля, которые затем посылаются назад на сервер. Простейшим способом обработки обновлений является оптимистичная схема блокировки, основанная на временных метках. Можно использовать одну временную метку для всех данных, или использовать отдельные метки времени для каждой «строки» информации. После выбора стратегии обновления прием данных, переданных на сервер посредством метода HTTP POST, становится простым. Информация из HTML-документа передается на сервер как набор параметров (пары имя/значение) в объекте InputStream. Класс HttpUtils содержит метод parsePostData(), принимающий необработанный InputStream от клиента и возвращающий Hashtable c уже обработанной информацией о параметрах. Действительно хорошей функцией является то, что если параметр с каким-то именем имеет несколько значений (как в случае имени столбца с несколькими строками), то эта информация может быть извлечена из Hashtable как массив типа String.
  • 18. В следующем упражнении рассмотрен скелетный код, реализующий пару сервлетов, которые отображают данные в броузере как редактируемый HTML-документ. Структура данных хранится отдельно от фактических данных. Это позволяет легко модифицировать этот код для работы с таблицами из баз данных, поддерживающих JDBC. Упражнение 5. Передача и обработка HTML-документов Использование полей Cookie Поля cookie представляют собой фрагменты данных, управляемые броузером, обычно с целью управления сессией. Поскольку HTTP-соединения не сохраняют состояний, можно использовать cookie() для записи постоянной информации между несколькими HTTP-соединениями. Именно в классе Cookie выполняются все действия. Класс HttpSession, рассмотренный далее, действительно легко использовать. Однако он не поддерживает сохранение информации между сессиями броузера. Для сохранения информации в поле cookie вам надо создать класс Cookie, установить тип содержимого ответа HttpServletResponse, добавить поле cookie к ответу, и затем послать выходную информацию. Вы должны добавлять поле cookie после установки типа содержимого, но перед передачей выходной информацией, потому что Cookie передается назад как часть заголовка HTTP-запроса. private static final String SUM_KEY = "sum"; ... int sum = ...; // взять старое значение и добавить к нему Cookie theCookie = new Cookie (SUM_KEY, Integer.toString(sum)); response.setContentType("text/html"); response.addCookie(theCookie); Важно помнить, что все данные полей cookie являются строками. Вы должны конвертировать информацию, например типа int, в объект String. По умолчанию, поле cookie актуально на протяжении сессии броузера. Для того, чтобы разрешить полям cookie жить дольше, необходимо вызвать метод setMaxAge(interval). При положительном параметре указывается количество секунд времени жизни поля cookie. Отрицательное значение устанавливается по умолчанию и означает, что поле cookie разрушается при закрытии броузера. При значении ноль поле cookie удаляется немедленно. Извлечение данных из поля cookie является немного неудобным. Нельзя запросить поле, используя определенный ключ. Вы должны запросить все поля cookie и найти интересующее. Кроме того, возможно существование нескольких полей cookie с одинаковым именем, так что нахождения первого значения не всегда достаточно. Следующим кодом задается установка однозначного поля cookie: int sum = 0; Cookie theCookie = null; Cookie cookies[] = request.getCookies(); if (cookies != null) { for(int i=0, n=cookies.length; i < n; i++) { theCookie = cookies[i]; if (theCookie.getName().equals(SUM_KEY)) { try { sum = Integer.parseInt(theCookie.getValue()); } catch (NumberFormatException ignored) { sum = 0; } break; } } } Доступен полный код этого примера для тестирования.
  • 19. Управление информацией о сессии Сессия представляет собой непрерывное соединение одного и того же броузера в течение фиксированного промежутка времени. (Это время обычно настраивается на Web-сервере. Для JWS оно по умолчанию равно 30 минутам.) Явно используя поле cookie броузера, HTTP-сервлет дает вам возможность управлять информацией о сессии при помощи класса HttpSession. HttpServletRequest обеспечивает управление текущей сессией в методе getSession(boolean). Если параметр boolean равен true, то при обнаружении новой сессии создастся новая сессия. Это, обычно, и требуется. Если параметр равен false – при обнаружении новой сессии метод возвращает null. public void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpSession session = request.getSession(true); // ... Как только вы получили доступ к HttpSession, вы можете управлять набором пар ключ-значение для сохранения специфических для сессии данных любого вида. Вы автоматически получаете доступ ко времени создания сессии при использовании getCreationTime() и времени последнего доступа при использовании getLastAccessedTime(), описывающего время последнего запроса сервлета для этой сессии. Для сохранения специфической для сессии информации используется метод putValue(key, value). Для извлечения информации используется метод getValue(key). Следующий пример демонстрирует это, автоматически прибавляя значение integer к параметру Addend. Если значение не является типом integer, подсчитывается количество ошибок. private static final String SUM_KEY = "session.sum"; private static final String ERROR_KEY = "session.errors"; Integer sum = (Integer) session.getValue(SUM_KEY); int ival = 0; if (sum != null) { ival = sum.intValue(); } try { String addendString = request.getParameter("Addend"); int addend = Integer.parseInt (addendString); sum = new Integer(ival + addend); session.putValue (SUM_KEY, sum); } catch (NumberFormatException e) { Integer errorCount = (Integer)session.getValue(ERROR_KEY); if (errorCount == null) { errorCount = new Integer(1); } else { errorCount = new Integer(errorCount.intValue()+1); } session.putValue (ERROR_KEY, errorCount); } Как и в любом сервлете, после выполнения необходимых операций вы должны сгенерировать какую-то выходную информацию. Если вы используете сессии, необходимо перед такой генерацией запросить сессию при помощи метода HttpServletRequest.getSession(). response.setContentType("text/html"); PrintWriter out = response.getWriter(); out.println("<html>" + "<head><title>Session Information</title></head>" +
  • 20. "<body bgcolor="#FFFFFF">" + "<h1>Session Information</h1><table>"); out.println ("<tr><td>Identifier</td>"); out.println ("<td>" + session.getId() + "</td></tr>"); out.println ("<tr><td>Created</td>"); out.println ("<td>" + new Date( session.getCreationTime()) + "</td></tr>"); out.println ("<tr><td>Last Accessed</td>"); out.println ("<td>" + new Date( session.getLastAccessedTime()) + "</td></tr>"); out.println ("<tr><td>New Session?</td>"); out.println ("<td>" + session.isNew() + "</td></tr>"); String names[] = session.getValueNames(); for (int i=0, n=names.length; i<n; i++) { out.println ("<tr><td>" + names[i] + "</td>"); out.println ("<td>" + session.getValue (names[i]) + "</td></tr>"); } out.println("</table></center></body></html>"); out.close(); Доступен полный код этого примера для тестирования. Одним моментом, не показанным в примере, является способность закрыть сессию, если следующий вызов request.getSession(true) возвратит другую сессию. Это делается при помощи вызова invalidate(). Если пользователь запретил в своем броузере использование полей cookie, вы можете закодировать идентификатор сессии в HttpServletResponse, вызвав его метод encodeUrl(). Соединение с базами данных Очень часто сервлеты соединяются с базами данных через JDBC. Это дает возможность лучше контролировать доступ к базе данных, разрешая взаимодействие с ней только на промежуточном уровне. Если ваш сервер баз данных имеет достаточное количество лицензий на одновременные соединения, вы можете даже установить соединение с базой один раз при инициализации сервлета, а затем совмещать соединения между всеми другими запросами на обслуживание. Далее демонстрируется разделение одного объекта Connection между всеми запросами на обслуживание. Для определения количества одновременных соединений, которые поддерживает драйвер, вы можете запросить его объект DatabaseMetaData , а затем создать пул объектов Connection для разделения между запросами на обслуживание. • В методе init() происходит соединение с базой данных Connection con = null; public void init (ServletConfig cfg) throws ServletException { super.init (cfg); // Загрузить драйвер String name = cfg.getInitParameter("driver"); Class.forName(name); // Получить Connection con = DriverManager.getConnection (urlString); } • В методе doGet() происходит извлечение информации о базе данных public void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html");
  • 21. // Заставить броузер проигнорировать кеш – вызвать перезагрузку response.setHeader ("Expires", "Mon, 01 Jan 1990 00:00:00 GMT"); Statement stmt = null; ResultSet result = null; try { // Составить запрос stmt = con.createStatement(); result = stmt.executeQuery ( "SELECT programmer, cups " + "FROM JoltData ORDER BY cups DESC;"); // Создать выходную информацию PrintWriter out = response.getWriter(); while(result.next()) { // Сгенерировать выходную информацию из ResultSet } } finally { if (result != null) { result.close(); } if (stmt != null) { stmt.close(); } } out.flush(); out.close(); } • В методе destroy() происходит отсоединение от базы данных public void destroy() { super.destroy(); con.close(); } Не является хорошей практикой оставлять постоянно открытыми соединения с базой данных, поэтому этот сервлет не должен устанавливаться в качестве постоянного сервлета. Устанавливая его в качестве временного сервлета, закрывающего себя после предопределенного периода неактивности, можно предоставить возможность разделения соединения с базой данных совместными запросами, уменьшая накладные расходы каждого запроса. Вы можете также сохранить некоторую информацию в HttpSession для возможного пролистывания таблицы результата запроса. Вопросы безопасности Как и в случае с апплетами Java, в сервлетах Java нужно позаботиться о безопасности. «Песочница» для сервлетов Сервлет может появиться из различных источников. Его может написать Web-мастер, его может написать пользователь, он может быть получен как часть какого-либо пакета или загружен с какого-либо сайта. В зависимости от источника получения сервлета с ним должен быть связан определенный уровень доверия. Некоторые Web-серверы обеспечивают средства для установки различных уровней доверия различным сервлетам. Эта концепция аналогична способу управления апплетами в броузерах и известна под названием “песочница”. «Песочница» для сервлетов – это область, в которой данные сервлеты имеют ограниченные полномочия на сервере. Они не могут, например, получить доступ к файловой системе или сети, или им может быть
  • 22. предоставлен статус более высокого доверия. Предоставление такого статуса определенным сервлетам является задачей администратора Web-сервера. Обратите внимание, что сервлет с полным доверием может получить полный доступ к файловой системе и сетевым возможностям. Он может даже выполнить System.exit(), остановив сервер… Списки управления доступом (ACL) Многие Web-серверы позволяют вам ограничивать доступ к определенным Web-страницам и сервлетам при помощи списков управления доступом (ACL). ACL – это список пользователей, которые могут выполнять определенную функцию на сервере. В этом списке указывается: • Тип разрешенного доступа • Объект, к которому относится этот доступ • Пользователи, которым предоставлен доступ Каждый Web-сервер имеет свои средства формирования ACL, но в общем случае, список пользователей регистрируется на сервере, и эти же имена пользователей используются в ACL. Некоторые Web-серверы также позволяют вам добавлять пользователей в логические группы, так что вы можете предоставлять доступ группе пользователей вместо указания каждого из них в ACL. ACL являются чрезвычайно важными, так как некоторые сервлеты могут отображать или модифицировать важные данные и должны тщательно контролироваться, тогда как другие отображают общедоступную информацию и не нуждаются в контроле. Потоки Web-сервер может вызвать метод сервлета service() одновременно для нескольких запросов. Эта ситуация поднимает вопрос потокозащищенности в сервлетах. Но сначала рассмотрим то, о чем не надо беспокоиться – о методе сервлета init(). Этот метод вызывается только один раз во время загрузки сервлета. Web-сервер вызывает метод init() при загрузке сервлета и не вызывает его до тех пор, пока сервлет не будет выгружен и загружен повторно. Кроме того, методы service() и destroy() не будут вызваны до тех пор, пока метод init() не завершит свою работу. Вопрос становится более интересным при рассмотрении метода service(). Метод service() может быть вызван Web-сервером для нескольких клиентов одновременно. (В JSDK 2.0 вы можете установить сервлету интерфейс SingleThreadModel. Это приведет к тому, что каждый вызов метода service() будет обработан последовательно. Разделяемые ресурсы, такие как файлы и базы данных, и в этом случае требуют контроля за совместным доступом.) Если ваш метод service() использует внешние ресурсы, такие как данные экземпляра объекта сервлета, файлы или базы данных, вам необходимо внимательно исследовать, что может произойти при нескольких одновременных вызовах метода service(). Например, предположим, что вы определили счетчик в классе сервлета, хранящий количество методов service(), работающих в данный момент: private int counter = 0; Далее, предположим, что ваш метод service() содержит следующие строки: int myNumber = counter + 1; // строка 1 counter = myNumber; // строка 2 // остальная часть кода в методе service() counter = counter - 1; Что может произойти при одновременной работе двух методов service(), которые оба выполнили строку 1 перед строкой 2? Оба будут иметь одинаковое значение для myNumber и counter не будет правильно обновлен.