Сервлеты (Servlets) - это java-программы, которые выполняются на серверной стороне Web-приложения. Точно так же, как апплеты динамически расширяют функциональные возможности Web-браузера, сервлеты динамически расширяют функциональные возможности Web-сервера. Хотя сервлеты могут обслуживать любые запросы, они обычно используются для расширения веб-серверов. Для таких приложений технология Java Servlet определяет HTTP-специфичные сервлет классы. Пакеты javax.servlet и javax.servlet.http обеспечивают интерфейсы и классы для создания сервлетов.
- Какова структура веб-проекта?
- Что такое контейнер сервлетов? Жизненный цикл сервлета.
Контейнер сервлетов - программа, управляющая жизненным циклом сервлетов.
Жизненный цикл сервлета: им управляет контейнер сервлетов, при первом обращении к сервлету происходит его загрузка в память и вызывается метод init(). На протяжении работы приложения происходят вызовы методы service() для обработки запросов клиента. По завершению работы приложения вызывается метод destroy() и сервлет выгружается из памяти.
- Каковы задачи, функциональность контейнера сервлетов?
Контейнер сервлетов может работать как полноценный самостоятельный веб-сервер, быть поставщиком страниц для другого веб-сервера, например Apache, или интегрироваться в Java EE сервер приложений. Обеспечивает обмен данными между сервлетом и клиентами, берёт на себя выполнение таких функций, как создание программной среды для функционирующего сервлета, идентификацию и авторизацию клиентов, организацию сессии для каждого из них.
- Чем отличается sendRedirect() от forward()?
Для вызова JSP по относительному пути применяется метод forward(), для обращения к JSP по абсолютному пути используется метод sendRedirect(). Отличие этих методов состоит в том, что с методом forward() передается уже существующий объект запроса request, а при вызове метода sendRedirect() формируется новый запрос. Информацию в последнем случае следует переда вать с другими объектами. К тому же метод forward() срабатывает быстрее.
- Что вы знаете о сервлет фильтрах?
Реализация интерфейса Filter позволяет создать объект, который перехватывает запрос, может трансформировать заголовок и содержимое запроса клиента. Фильтры не создают запрос или ответ, а только модифицируют их. Фильтр выполняет предварительную обработку запроса, прежде чем тот попадает в сервлет, с последующей (если необходимо) обработкой ответа, исходящего из сервлета. Фильтр может взаимодействовать с разными типами ресурсов, в частности, и с сервлетами, и с JSP-страницами. Сервлетные фильтры могут:
- перехватывать инициацию сервлета прежде, чем сервлет будет инициирован.
- определить содержание запроса прежде, чем сервлет будет инициирован.
- модифицировать заголовки и данные запроса, в которые упаковывается поступающий запрос.
- модифицировать заголовки и данные ответа, в которые упаковывается получаемый ответ.
- перехватывать инициацию сервлета после обращения к сервлету.
Сервлетный фильтр может быть сконфигурирован так, что он будет работать с одним сервлетом или группой сервлетов. Основой для формирования фильтров служит интерфейс javax.servlet.Filter, который реализует три метода:
- void init (FilterConfig config) throws ServletException;
- void destroy();
- void doFilter (ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException;
Метод init() вызывается прежде, чем фильтр начинает работать, и настраивает конфигурационный объект фильтра. Метод doFilter выполняет непосредственно работу фильтра. Таким образом, сервер вызывает init() один раз, чтобы запустить фильтр в работу, а затем вызывает doFilter() столько раз, сколько запросов будет сделано непосредственно к данному фильтру. После того, как фильтр заканчивает свою работу, вызывается метод destroy().
- Зачем нужны слушатели в сервлетах?
Слушатели контекста и сессий - это классы, которые могут следить за тем, когда контекст или сессия были инициализированы, или отслеживать время, когда они должны быть уничтожены, и когда атрибуты были добавлены или удалены из контекста или сессии. Servlet 2.4 расширяет модель слушателей запроса, позволяя отслеживать, как запрос создается и уничтожается, и, как атрибуты добавляются и удаляются из сервлета. В Servlet 2.4 добавлены следующие классы:
- ServletRequestListener
- ServletRequestEvent
- ServletRequestAttributeListener
- ServletRequestAttributeEvent
- Как обработать исключения, выброшенные другим сервлетом в приложении?
Так как браузер понимает только HTML, то когда приложение выбросит исключение контейнер сервлетов обработает исключение и создаст HTML response. Это аналогично тому что происходит при кодах ошибок вроде 404, 403 и т.д. Servlet API предоставляет поддержку собственных сервлетов для обработки исключений и ошибок, которые мы можем задать в дескрипторе развертывания. Главная задача таких сервлетов - обработать ошибку или исключение и отправить понятный HTML ответ пользователю. Например, можно предоставить ссылку на главную страницу, а так же описание некоторых деталей об ошибке.
- Что такое дескриптор развертывания?
Дескриптор развертывания - это конфигурационный файл артефакта, который будет развернут в контейнере сервлетов. В спецификации Java Platform, Enterprise Edition дескриптор развертывания описывает то, как компонент, модуль или приложение (такое, как веб-приложение или приложение предприятия) должно быть развернуто.
Этот конфигурационный файл указывает параметры развертывания для модуля или приложения с определенными настройками, параметры безопасности и описывает конкретные требования к конфигурации. Для синтаксиса файлов дескриптора развертывания используется язык XML.
- Как реализовать запуск сервлета с запуском приложения?
Контейнер сервлетов обычно загружает сервлет при первом запросе клиента, но иногда необходимо загрузить сервлет прямо на старте приложения (например если сервлет объемный и будет долго грузиться). Для этого необходимо использовать элемент load-on-startup в дескрипторе (или аннотацию loadOnStartup), который укажет необходимость загрузки сервлете при запуске.
Значение должно быть int. Если значение отрицательное, то сервлет будет загружен при запросе клиента, а если 0 и далее, то загрузится на старте приложения. Чем меньше число, тем раньше в очереди на загрузку будет сервлет.
- Что представляет собой объект ServletConfig?
Интерфейс javax.servlet.ServletConfig используется для передачи конфигурационной информации сервлету. Каждый сервлет имеет свой собственный объект ServletConfig, за создание экземпляра которого ответственен контейнер сервлетов. Для установки параметров конфигурации используются init параметры в web.xml (или аннотации WebInitParam). Для получения объекта ServletConfig данного сервлета используется метод getServletConfig().
- Что представляет собой объект ServletContext?
Интерфейс javax.servlet.ServletContext определяет ряд методов, которые сервлет использует, чтобы связаться с его контейнером сервлета, например, получить тип MIME файла, диспетчеризировать запросы, или записать в файл журнала. Объект ServletContext является уникальным и доступен всем сервлетам веб приложения. Мы можем использовать объект ServletContext, когда нам необходимо предоставить доступ одному или нескольким сервлетам к инициализированным параметрам веб приложения. Для этого используется элемент
Контейнеры сервлетов так же могут предоставлять context объекты, уникальные для группы сервлетов. Каждая из групп будет связана со своим набором URL путей хоста.
ServletContext был расширен в спецификации Servlet 3 и предоставляет программное добавление слушателей и фильтров в приложение. Так же у этого интерфейса имеются множество полезных методов вроде getMimeType(), getResourceAsStream() и т.д..
- В чем отличия ServletContext и ServletConfig?
Ниже приведены некоторые отличия:
- ServletConfig является уникальный объектом для каждого сервлета, в то время как ServletContext уникальный для всего приложения.
- ServletConfig используется для предоставления параметров инициализации сервлету, а ServletContext для предоставления параметров инициализации приложения для всех сервлетов.
- У нас нет возможности устанавливать атрибуты в объекте ServletConfig, в то время как можно установить атрибуты в объекте ServletContext, которые будут доступны другим сервлетам.
- Интерфейс ServletResponse.
Интерфейс ServletResponse - это инструмент для отправки данных клиенту. Все методы данного инструмента служат именно этой цели.
- Интерфейс ServletRequest.
Интерфейс ServletRequest - это инструмент для получения параметров HTTP запроса. Этот интерфейс имеет некоторые методы, идентичные по названию и назначению с ServletContext.
- Что такое Request Dispatcher?
Интерфейс RequestDispatcher используется для передачи запроса другому ресурсу (это может быть HTML, JSP или другой сервлет в том же приложении). Мы можем использовать это для добавления контента другого ресурса к ответу. Этот интерфейс используется для внутренней коммуникации между сервлетами в одном контексте. В интерфейса реализовано два метода:
- void forward(ServletRequest var1, ServletResponse var2) - передает запрос из сервлета к другому ресурсу (сервлету, JSP или HTML файлу) на сервере.
- void include(ServletRequest var1, ServletResponse var2) - включает контент ресурса (сервлет, JSP или HTML страница) в ответ.
Доступ к интерфейсу можно получить с помощью метода ServletContext getRequestDispatcher(String s). Путь должен начинаться с / , который будет интерпретироваться относительным текущего корневого пути контекста.
- Как можно создать блокировку (deadlock) в сервлете?
Дедлок (deadlock) можно получить реализовав зацикленный вызов метода, например вызвав метод doPost() в методе doGet() и вызвать doGet() в методе doPost().
- Как получить адрес сервлета на сервере?
Для получения актуального пути сервлета на сервере можно использовать эту конструкцию: getServletContext().getRealPath(request.getServletPath()).
- Как получить информацию о сервере из сервлета?
Информацию о сервере можно получить с использованием объекта ServletContext с помощью метода getServerInfo(). Т.е. getServletContext().getServerInfo().
- Как получить ip адрес клиента на сервере?
Использовать request.getRemoteAddr() для получения ip клиента в сервлете.
- Что вы знаете о классах обертках (wrapper) для сервлетов?
В Servlet HTTP API предоставляются два класса обертки - HttpServletRequestWrapper и HttpServletResponseWrapper. Они помогают разработчикам реализовывать собственные реализации типов request и response сервлета. Мы можем расширить эти классы и переопределить только необходимые методы для реализации собственных типов объектов ответов и запросов. Эти классы не используются в стандартном программировании сервлетов.
Пример сервлета включает сам сервлет HelloWorld.java и дескриптор приложения web.xml. Сервлет позволяет формировать страницу ответа с использованием кириллицы. Структура примера сервлета в IDE Eclipse представлена на следующем скриншоте.
Листинг примера сервлета HelloWorld.java
Приведенный ниже код реализует простой сервлет HelloWorld.java , возвращающий статическую HTML-страницу браузеру. В примере сервлет наследует свойства HttpServlet, реализующего интерфейс Servlet .
Import java.io.IOException; import java.io.OutputStream; import javax.servlet.ServletConfig; import javax.servlet.http.HttpServlet; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class HelloWorld extends HttpServlet { private ServletConfig config; @Override public void init (ServletConfig config) throws ServletException { this.config = config; } @Override public void destroy() {} @Override public ServletConfig getServletConfig() { return config; } @Override public String getServletInfo() { return "A Simple Servlet"; } public void service (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); String text = " " + "
" + " " + "Здравствуй, %s %s!
" + ""; text = String.format(text, config.getInitParameter("name"), config.getInitParameter("mname")); OutputStream outStream = response.getOutputStream(); outStream.write(text.getBytes("UTF-8")); outStream.flush(); outStream.close(); } }При обращении к сервлету вызывается метод service , который формирует страницу ответа и передает ее в браузер через HttpServletResponse. В первых строках ответа сервера определен заголовок Content-Type с указанием кодировки UTF-8. Дополнительно в шаблон ответа подставляются имя и отчество, получаемые из дескриптора приложения web.xml. То есть, выполняется чтение параметров конфигурации сервлета.
Для того, чтобы можно было бы возвращать страницу с кириллицей, используется OutputStream . В выходной поток можно вывести любую информацию (в нашем примере - простой HTML-код).
Листинг дескриптора приложения web.xml
В дискрипторе приложения web.xml определяются параметры сервлета (наименование servlet-name и класс servlet-class) и параметры инициализации (имя name, отчество mname). Данные параметры сервлет читает с использованием ServletConfig .
Для запуска примера сервлета из среды IDE Eclipse необходимо стартовать сервер приложений и в адресной строке браузера ввести следующую строку:
Http://localhost:8080/ServletHello/hello
Браузер отобразит нам приветствие с указанием основного героя произведений Ильфа и Петрова.
Для отдельной компиляции сервлета можно использовать командный файл, текст которого для Windows может иметь следующий вид.
SET TOMCAT_LIB="C:\Program Files\Apache Software Foundation\Tomcat\common\lib" SET JAVA_SDK=C:\Java\Sun\SDK\jdk\bin\ %JAVA_SDK%javac -classpath %TOMCAT_LIB%\servlet-api.jar *.java
После компиляции сервлет HelloWorld.class необходимо разместить в директории WEB-INF/classes/ WEB-приложения на сервере Tomcat и зарегистрировать в дескрипторе поставки web.xml .
Скачать пример
Исходный код примера сервлета, рассмотренного в тексте страницы, можно (191 Кб).
Пример сервлета с использованием графической библиотеки Chart.js для динамического формирования страницы с диаграммами можно посмотреть .
Одной из самых приятных особенностей Java является её многогранная природа. Конечно, создание традиционных десктопных и даже мобильных приложений - это здорово. Но что, если вы хотите уйти с проторенных дорожек и зайти на территорию разработки web приложений на Java? Для вас есть хорошая новость: в комплекте с языком идёт полноценный Servlet API, который позволяет вам создавать надёжные веб-приложения без особых хлопот.
Создание приложений на Java с помощью Servlets
Итак, мы уже создали конфигурационные файлы приложения. Однако в текущем состоянии оно буквально ничего не делает. Мы хотим, чтобы клиенты могли регистрироваться с помощью HTML-формы, поэтому следующее, что нам нужно сделать, - это создать JSP-файлы, которые будут отображать вышеупомянутую форму и данные клиента после успешного завершения регистрации. Этим мы сейчас и займёмся.
Работаем над внешним видом
Внешний вид приложения будет определяться двумя JSP-файлами - в контексте MVC они называются представлениями. Первый будет отвечать за отображение формы регистрации и возможных ошибок, вызванных после проверки введённых данных. Второй будет обычной страницей приветствия, в которой будут показаны данные, введённые клиентом, после успешного завершения процесса регистрации.
Вот первый JSP-файл:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
Регистрация
${violation}.
Файл содержит простой HTML с парочкой дополнений. Вот она, прелесть JSP в сочетании с JSTL и JEL. Обратите внимание на то, как легко проверить наличие ошибок валидации, используя такие стандартные теги, как <с:if> и
Атрибут формы регистрации action указывает на следующий URL: ${pageContext.request.contextPath}/processcustomer . Это значит, что каждый раз, когда клиент пытается зарегистрироваться, данные будут отправляться в processcustomer независимо от URL, по которому доступна форма. Это достигается за счёт функциональности объектов, доступных из JSP-файла, таких как request .
Скоро мы увидим, как сервлет связывается с URL processcustomer и как он взаимодействует с введёнными данными. А пока давайте посмотрим на JSP-файл, который отвечает за страницу приветствия:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
Спасибо за регистрацию!
Ваши введённые данные:
Имя: ${firstname}
Фамилия: ${lastname}
Email: ${email}
Теперь, когда мы разобрались с отображением страниц, следующим шагом будет создание сервлета, ответственного за сбор данных клиента из POST-запросов и подтверждение данных простым способом.
Пишем контроллер
Написать сервлет, способный получить данные из формы регистрации, проще простого. Всё, что нам нужно сделать, - это написать подкласс для класса HttpServlet и реализовать его методы doGet() или doPost() (или оба, если надо). В данном случае сервлет будет взаимодействовать с данными, поступающими из POST-запросов.
Вот как он выглядит:
@WebServlet(name = "CustomerController", urlPatterns = "/processcustomer") public class CustomerController extends HttpServlet { @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { RequestCustomer customer = RequestCustomer.fromRequestParameters(request); customer.setAsRequestAttributes(request); List violations = customer.validate(); if (!violations.isEmpty()) { request.setAttribute("violations", violations); } String url = determineUrl(violations); request.getRequestDispatcher(url).forward(request, response); } private String determineUrl(List violations) { if (!violations.isEmpty()) { return "/"; } else { return "/WEB-INF/views/customerinfo.jsp"; } } private static class RequestCustomer { private final String firstName; private final String lastName; private final String email; private RequestCustomer(String firstName, String lastName, String email) { this.firstName = firstName; this.lastName = lastName; this.email = email; } public static RequestCustomer fromRequestParameters(HttpServletRequest request) { return new RequestCustomer(request.getParameter("firstname"), request.getParameter("lastname"), request.getParameter("email")); } public void setAsRequestAttributes(HttpServletRequest request) { request.setAttribute("firstname", firstName); request.setAttribute("lastname", lastName); request.setAttribute("email", email); } public List validate() { List violations = new ArrayList<>(); if (!StringValidator.validate(firstName)) { violations.add("Имя является обязательным полем"); } if (!StringValidator.validate(lastName)) { violations.add("Фамилия является обязательным полем"); } if (!EmailValidator.validate(email)) { violations.add("Email должен быть правильно сформирован"); } return violations; } } }
Первое, на что здесь стоит обратить внимание, - использование аннотации @WebServlet(name ="CustomerController", urlPatterns = "/processcustomer") . Она говорит контейнеру сервлета использовать класс CustomerController для обработки HTTP-запросов по адресу /processcustomer . Того же эффекта можно достичь путём добавления директив сопоставления сервлетов в web.xml , как , но так как мы используем Servlet Specification 3.1 нам нет необходимости прибегать к такому способу.
Здесь мы назвали сервлет CustomerController , так как считается хорошей практикой использовать имя класса сервлета в качестве значения атрибута name аннотации @WebServlet . В противном случае некоторые контейнеры не смогут выполнить сопоставление, что приведёт к ошибке 404.
Сам класс CustomerController выполняет несколько простых задач. Во-первых, он собирает введённые в форму данные, используя реализацию интерфейса HttpServletRequest , который содержит значения, соответствующие полям firstname , lastname и email формы. Затем он устанавливает эти значения в качестве атрибутов запроса, поэтому их можно повторно отобразить либо в форме, либо на странице с результатами. Наконец, валидаторы проверяют правильность введённых данных.
Валидаторы - это простые классы, которые проверяют определённые свойства, например, является ли строка пустой и выглядит ли email как email. На GitLab автора можно посмотреть на их реализацию.
Результат валидации влияет на дальнейший ход событий: если данные не валидны, клиент перенаправляется через объект RequestDispatcher на страницу регистрации, где отображаются соответствующие ошибки. Если всё в порядке, то отображается страница приветствия.
Итак, мы создали целое веб-приложение на Java, которое позволяет зарегистрировать клиентов с помощью HTML-формы, базового сервлета и нескольких JSP-файлов. Пора его запустить.
Запускаем приложение
Для запуска приложения нужно проделать следующие шаги:
IntelliJ IDEA). Когда вы развернёте проект и запустите его, должен запуститься бразуер по умолчанию с окном регистрации.
Заключение
Итак, вы приобрели все навыки, необходимые для создания собственного веб-приложения на Java без необходимости прибегать к сложным фреймворкам. Всё, что вам нужно, - Servlet API , технология вроде JSP для отображения и встроенные средства Java. Здорово, правда?
Стоит отметить, что реализация класса CustomerController подчеркивает достоинства и недостатки сервлетов: с одной стороны, он вкратце показывает, насколько легко обрабатывать параметры запроса и отправлять ответы клиенту в разных форматах. Но эта функциональность имеет свою цену: обе реализации интерфейсов HttpServletResponse и HttpServletResponse являются обычными локаторами служб. Нельзя сказать, что это плохо, поскольку локаторы просто содержат данные. Однако нужно помнить, что эти реализации будут всегда привязаны к сервлету.
Servlet - это java-программа, которая выполняется на серверной стороне Web-приложения. Точно так же, как апплеты динамически расширяют функциональные возможности Web-браузера, сервлеты динамически расширяют функциональные возможности Web-сервера.
Работу servlet "а можно описать следующим образом: при приходе запроса от клиента Web-сервер с помощью специального конфигурационного файла может определить, какой сервлет необходимо выполнить. После этого Web-сервер запускает JVM, которая в свою очередь выполняет сервлет. Servlet обрабатывает запрос и передает содержимое Web-серверу (возможно в виде HTML-страницы). Web-сервер отправляет клиенту ответ (сформированную сервлетом HTML-страницу).
WEB-сервер по сути является неким контейнером, который загружает servlet "ы, выполняет их, и, получив от них результат, отправляет его клиенту.
Servlet в архитектуре Web-приложения
Из-за своей мощности и гибкости, servlet "ы могут играть значительную роль в архитектуре системы. Они могут выполнять прикладные задачи, предназначенные для промежуточного уровня, работать как прокси-сервер для клиента и даже улучшать функциональность промежуточного уровня, добавляя поддержку новых протоколов и других функций. Промежуточный уровень выполняет функции сервера приложений в так называемой трехуровневой системе клиент-сервер и расположен между "легковесным" клиентом, таким как Web-браузер, и источником данных.
Servlet как прокси-сервер
Для поддержки апплетов сервлеты могут выполнять функции их прокси-серверов. Это может быть важно, поскольку система безопасности Java позволяет апплетам соединяться только с сервером, с которого они были загружены. Если апплет нуждается в соединении с сервером баз данных, расположенном на другой машине, servlet может создать это соединение для апплета.
Временные и постоянные servlet "ы
Сервлеты могут запускаться и останавливаться для каждого клиентского запроса. Также они могут запускаться при старте Web-сервера и существовать до его остановки. Временные servlet "ы загружаются по требованию и предлагают хороший способ сохранения ресурсов сервера для редко используемых функций. Постоянные сервлеты загружаются при старте Web-сервера и существуют до его остановки. Сервлеты устанавливаются как постоянные расширения для сервера в том случае, если затраты по их запуску очень велики (например, установка соединения с базой данных), если они предлагают постоянную функциональность на стороне сервера (например, служба RMI), или в случаях, когда они должны отвечать на запросы клиента как можно быстрее. Не существует специального кода для назначения servlet "а постоянным или временным; это функция настройки Web-сервера.
Жизненный цикл сервлета, javax.servlet.Servlet
Сервлеты выполняются на платформе Web-сервера как часть того же процесса, что и сам Web-сервер. Web-сервер отвечает за инициализацию, вызов и уничтожение каждого экземпляра сервлета. Web-сервер взаимодействует с сервлетом через простой интерфейс: javax.servlet.Servlet .
Интерфейс javax.servlet.Servlet включает три главных методов:
- init()
- service()
- destroy()
и два вспомогательных методов:
- getServletConfig()
- getServletInfo()
Сходство между интерфейсами servlet "а и апплета Java очевидны. Именно так и было спроектировано! Java сервлеты являются для Web-серверов тем же самым, чем являются апплеты для Web-браузеров. Апплет выполняется в Web-браузере, выполняя действия по его запросу через специальный интерфейс. Сервлет делает то же самое, работая на Web-сервере.
Инициализация сервлета, метод init()
При первой загрузке сервлета вызывается метод init(). Это дает возможность сервлету выполнить любую работу по установке, например, открытие файлов или установку соединений с их серверами. Если сервлет установлен на сервере постоянно, он загружается при запуске сервера. В противном случае сервер активизирует сервлет при получении первого запроса от клиента на выполнение услуги, обеспечиваемой этим сервлетом.
Гарантируется, что метод init() закончится перед любым другим обращением к сервлету – таким как, например, вызов метода service() . Обратите внимание, что init() будет вызван только один раз; он не будет вызываться до тех пор, пока сервлет не будет выгружен и затем загружен сервером снова.
Метод init() принимает один аргумент – ссылку на объект ServletConfig , который содержит аргументы для инициализации сервлета. Этот объект имеет метод getServletContext() , возвращающий объект ServletContext , который содержит информацию об окружении сервлета.
Ядро сервлета, метод service()
Метод service() является сердцем сервлета. Каждый запрос от клиента приводит к одному вызову метода service() . Этот метод читает запрос и формирует ответное сообщение при помощи своих двух аргументов ServletRequest и ServletResponse:
Таким образом, существуют два способа передачи информации от клиента к сервлету. Первый – через передачу значений в параметрах запроса. Значения параметров могут быть вставлены в URL. Второй способ передачи информации от клиента к сервлету осуществляется через InputStream (или Reader).
Работа метода service() по существу проста – он создает ответ на каждый клиентский запрос, переданный ему с сервера. Однако необходимо помнить, что могут существовать несколько параллельных запросов, обрабатываемых в одно и то же время. Если метод service() требует каких-либо внешних ресурсов, таких как файлы, базы данных, то необходимо гарантировать, чтобы доступ к ресурсам являлся потокозащищенным.
Выгрузка сервлета, метод destroy()
Метод destroy() вызывается для освобождения всех ресурсов (например, открытые файлы и соединения с базой данных) перед выгрузкой сервлета. Этот метод может быть пустым, если нет необходимости выполнения каких-либо завершающих операций. Перед вызовом метода destroy() сервер ждет либо завершения всех обслуживающих операций, либо истечения определенного времени. Это означает, что метод destroy() может быть вызван во время выполнения какого-либо продолжительного метода service() .
Важно оформить метод destroy() таким образом, чтобы избежать закрытия необходимых ресурсов до тех пор, пока все вызовы service() не завершатся.
Конфигурация сервлета, метод getServletConfig()
Метод getServletConfig() возвращает ссылку на объект, который реализует интерфейс ServletConfig . Данный объект предоставляет доступ к информации о конфигурации сервлета, т.е. доступ к параметрам инициализации сервлета и объекту контектса сервлета ServletContext , который дает доступ к сервлету и его окружению.
Информация о сервлете, метод getServletInfo()
Метод getServletInfo() определяется программистом, создающим сервлет, для возврата строки, содержащую информацию о сервлете, например: автор и версия сервлета.
Интерфейс ServletRequest
ServletRequest предоставляет клиентскую информацию о параметрах HTTP запроса сервлету, т.е. обеспечивает данные включая название параметра и значения, атрибуты, и входной поток. Эта информация передается в метод service() .
Следующий servlet пример показывает, как получить информацию из параметра request метода service() :
BufferedReader reader; String param1; String param2; public void service(ServletRequest request, ServletResponse response) { reader = request.getReader(); param1 = request.getParameter("First"); param2 = request.getParameter("Second"); }
Дополнительная информация о запросе доступна сервлету через методы, основные из которых приведены в следующей таблице:
getAttribute () | Возвращает значение указанного атрибута этого запроса. |
getContentLength () | Размер запроса, если известен. |
getContentType () | Возвращает тип MIME тела запроса. |
getInputStream () | Возвращает InputStream для чтения двоичных данных из тела запроса. |
GetParameterNames () | Возвращает массив строк с именами всех параметров. |
getParameterValues () | Возвращает массив значений для указанного параметра. |
getProtocol () | Возвращает протокол и версию для запроса как строку вида
|
getReader () | Возвращает BufferedReader для получения текста из тела запроса. |
getRealPath () | Возвращает реальный путь для указанного виртуального пути. |
getRemoteAddr () | IP-адрес клиента, пославшего данный запрос. |
getRemoteHost () | Имя хоста клиентской машины, пославшего данный запрос. |
getScheme () | Возвращает схему, используемую в URL этого запроса (например, https, http, ftp, и т.д.). |
getServerName () | Имя хоста сервера, принявшего данный запрос. |
getServerPort () | Возвращает номер порта, используемого для приема этого запроса. |
Интерфейс ServletResponse
Интерфейс ServletResponse - это инструмент для отправки данных клиенту. Все методы данного инструмента служат именно для решения этой задачи:
Public java.lang.String getCharacterEncoding() public void setLocale(java.util.Locale loc) public java.util.Locale getLocale()
Первый метод возвращает MIME тип кодировки (к примеру - UTF8), в которой будет выдаваться информация. Вторые два метода тоже работают с charset. Они указывают на язык используемый в документе (например - русский).
Public ServletOutputStream getOutputStream() throws java.io.IOException
Метод getOutputStream возвращает поток вывода данных для сервлета. Этот поток используется, к примеру, для вывода бинарных файлов. Текстовые данные можно выводить с помощью java.io.Writer:
Public java.io.PrintWriter getWriter() throws java.io.IOException
Метод getWriter() автоматически конвертирует строки в тот charset, что указан в методе getCharacterEncoding() и getLocale().
Public void setContentLength(int len)
Методом setContentLength устанавливается значение поля HTTP заголовка "Content-Length"
Public void setContentType(String type)
Метод setContentType используется для отправки MIME типа содержимого документа. Поле HTTP заголовка "Content-Type".
Поток вывода данных является буфферизованным. Это означает, что порция данных будет выдана клиенту только после заполнения буфера.
Public void setBufferSize(int size) public int getBufferSize() public void flushBuffer() throws java.io.IOException public void resetBuffer()
Приведенные выше 4 метода позволяют, соответственно, установить размер буффера отправки, получить его размер, инициализировать отправку содержимое буффера клиенту, не дожидаясь его заполнения, а так же очистить этот буффер от данных.
Public boolean isCommitted()
Методом isCommitted можно получить флаг, начата ли уже отправка данных клиенту. Флаг будет положительным, если HTTP заголовок ответа был уже отправлен.
Public void reset()
Если HTTP заголовок ещё не отправлен, то метод reset "сбрасывает" HTTP заголовок к значениям "по умолчанию".
Диаграммы JFreeChart в сервлетах
Графическая библиотека JFreeChart может быть использована в сервлетах для создания диаграмм и их отображения на страницах сайтов в виде изображений. Подробности описания и примеры использования JFreeChart в сервлетах представлены .
Сервлет с графической библиотекой Chart.js
JNI в сервлете
В ряде случаев может потребоваться использование JNI в WEB-приложении. Пример использования JNI в сервлетах представлен .
JMS сообщения в сервлете
Сервлет может быть использован для обмена JMS сообщениями между приложениями. Пример использования сервлета для отправки и чтения JMS сообщений в контейнере JBoss представлен .
Что такое сервлеты? Сервлеты, фактически, это модули обработки HTTP и FTP запросов, используемые для построения порталов (web gates).
Основой этих порталов является собственно WEB сервер - программа, которая держит сокет сервера, принимает и передаёт данные. Чаще всего, для ускорения работы, сервер бывает написан не на Java, а на каком-либо другом языке программирования (например на C++).
В связке с сервером работает базовый сервлет. Именно ему отправляет сервер данные и от него же получает ответ, отправляемый клиенту. Фактически, базовый сервлет является "мозгом" сервера. Основная функция этого сервлета - прочитать запрос клиента, расшифровать его и, в соответствиии с расшифровкой, передать работу сервлету, отвечающему за этот тип запрашиваемой информации. Зачастую, для достижения скорости, роль базового сервлета играет сам сервер. Именно по такой схеме работает, скажем, Jacarta Tomcat.
На рисунке изображена схема передачи вызовов (request) и ответов (response) между сервером и сервлетами. Данная схема изображает работу HTTP сервера, который имеет несколько JSP страниц и два ресурса "/sample1" и "/sample2", за обработку которых отвечает два сервлета - "Sample1 Servlet" и "Sample2 Servlet" соответственно.
Разберём пошагово то, что изображено на рисунке:
- клиент подсоединяется к серверу
- сервер передаёт запрос (request) базовому сервлету ("Basic Servlet")
- базовый сервлет вычеленяет из запроса URI ресурса
- если URI указывает на "/sample1", то запрос целиком (без изменений) передаётся сервлету "Sample1 Servlet", который, в дальнейшем, и обрабатывает этот запрос
- если URI указывает на "/sample2", сервер передаёт запрос сервлету "Sample2 Servlet"
- во всех остальных случаях запрос передаётся модулю "JSP Servlet"
- сервлет, которому было передано управление, обрабатывает данные, создаёт ответ (response), после чего ответ отсылается обратно базовому сервлету.
- базовый сервлет, не обрабатывая полученные данные, тут же пересылает их обратно серверу
- сервер выдаёт данные клиенту
Таким образом достигается разбиение задачи обработки запроса на логические части, за каждую из которых отвечает свой модуль, свой "програмный кирпичик". На самом деле, ступеней в обработке запроса может быть гораздо больше. К примеру за методы "GET" и "POST" могут отвечать разные модули.
Интерфейс Servlet
Объединяет все эти модули то, что они сквозным образом связанны между собою с помощью интерфейса javax.servlet.Servlet
Посмотрим на этот интерфейс. В нём указано всего 5 методов:
Public void init(ServletConfig config) throws ServletException Этот метод вызывается, чтобы проинформировать сервлет о том, что он включён как модуль для обслуживания запросов клиента. Параметр config разделяет интерфейс javax.servlet.ServletConfig, несущий информацию об окружении сервера, имени сервлета, начальных параметрах и прочих плюшках. Об интерфейсе javax.servlet.ServletConfig будет рассказано чуть далее. Предполагается, что после вызова этой функции, сервлет аккуратно сохранит этот config у себя в переменной и будет выдавать его с помощью другого метода: public ServletConfig getServletConfig() Получив системную информацию с помощью "getServletConfig()", сервер может захотеть узнать имя автора, дату создания, прочую информацию о сервлете, что и достигается вызовом public String getServletInfo()
Чтобы обработать запрос и получить результат его обработки, используется функция
Public void service(ServletRequest request, ServletResponse response) throws ServletException, java.io.IOException В этой функции коду, который будет обрабатывать данные, передаются два инструмента: один - для получения данных от сервера, другой - для отправки результата работы сервлета. Соответственно это параметры request и response, разделяющие интерфейсы javax.servlet.ServletRequest и javax.servlet.ServletResponse Вся работа с данными ведётся именно через эти интерфейсы, так что далее поговорим о них подробнее.
После того, как сервер перестал нуждаться в этом модуле вызывается метод
Public void destroy() который и завершает все операции с объектом сервлета.
Интерфейс ServletConfig
4 метода, имена которых говорят сами за себя, составляют суть интерфейса javax.servlet.ServletConfig:
Public String getServletName() public ServletContext getServletContext() public String getInitParameter(String name) public java.util.Enumeration getInitParameterNames()
Думаю, назначение всех функция понятно, кроме
Public ServletContext getServletContext() Этот метод возвращает ссылку на очень полезный инструмент для работы с сервером:
Интерфейс ServletContext
ServletContext - интерфейс, определяющий доступ к следующим полезнейшим функциям:
Public Object getAttribute(String name) public java.util.Enumeration getAttributeNames() public void setAttribute(String name, Object object) public void removeAttribute(String name) Четыре метода для работы с аттрибутами. Роль аттрибутов выполняет любой объект любого класса. Цель данных функций - пересылать между несвязанными друг с другом сервлетами разные объекты. public String getInitParameter(String name) public java.util.Enumeration getInitParameterNames() Доступ к параметрам, с которыми был запущен сервер. Тут же могут лежать имя хоста, порт и прочие полезности. public int getMajorVersion() public int getMinorVersion() Возвращает версии Servlet API. public String getMimeType(String file) Возвращает MIME тип ассоциированный с файлом, путь до которого указан в переменной file. Вспомните, как пришлось определять MIME в программе SimpleWEBServer и оцените удобство! public java.util.Set getResourcePaths() public java.net.URL getResource(String path) throws java.net.MalformedURLException public InputStream getResourceAsStream(String path) Возвращает пути к доступным для сервера ресурсам и сами ресурсы в виде URL и в виде потоков данных. public RequestDispatcher getRequestDispatcher(path) public RequestDispatcher getNamedDispatcher(name) RequestDispatcher - это инструмент для того, чтобы переслать запрос другому ресурсу. Эти функции нужны, чтобы получить объект этого инструмента для указанных ресурсов. То бишь, скажем, для того, чтобы перенаправить запрос сервлету "sample1" из тела сервлета, можно сделать так: getServletConfig().getServletContext().getNamedDispatcher("sample1").forward(request, response);
Собственно класс RequestDispatcher включает в себя лишь два метода:
Public void forward(ServletRequest request, ServletResponse response) throws ServletException, java.io.IOException public void include(ServletRequest request, ServletResponse response) throws ServletException, java.io.IOException Причём первый - для перенаправления запроса, а второй - для включения результата работы вызываемого сервлета в результат работы текущего. К примеру, сервлет 1 печатает слово "test 1", потом вызывает include для сервлета два, после чего печатает слово "test 2". Сервлет 2 же просто печатает слово " and ". Результатом работы сервлета 1 будет строка "test 1 and test 2". public void log(String msg) Записать что-то в лог сервера. public void log(String message, Throwable throwable) Определить исключение и фразу, которая будет записываться в лог по получении этого исключения. public String getRealPath(String path) Переводит путь типа "/index.html" в "http://host/contextPath/index.html" public String getServerInfo() Возвращает имя сервера. public ServletContext getContext(String uripath) Этот метод позволяет обмениваться ServletContext между разными ресурсами одного и того же сервера. public String getServletContextName() Возвращает имя сервлета, которому принадлежит данный объект интерфейса ServletContect.
Интерфейс ServletRequest
Интерфейс ServletRequest - это инструмент для получения параметров HTTP запроса. Этот интерфейс имеет некоторые методы, идентичные по названию и назначению с ServletContext:
Public Object getAttribute(String name) public java.util.Enumeration getAttributeNames() public void setAttribute(String name, Object o) public void removeAttribute(java.lang.String name) public String getServerName() public RequestDispatcher getRequestDispatcher(String path)
Оставшися методы позволяют с удобством работать с HTTP заголовком запроса:
Public String getCharacterEncoding() public void setCharacterEncoding(String env) throws java.io.UnsupportedEncodingException Работа с кодировкой символов в полях HTTP заголовка. Функции задают метод расшифровки CGI запросов из формы %NN в обычные символы. К примеру, какой стандарт - KOI8-R, windows-1251 или UTF-8 нужно применить для расшифровки кириллических символов. public int getContentLength() public String getContentType() Читает поля "Content-Length", "Content-Type" из HTTP запроса. public jString getParameter(String name) public java.util.Enumeration getParameterNames() public String getParameterValues(String name) public java.util.Map getParameterMap() Функции для получения поля из HTTP заголовка и его значения. public ServletInputStream getInputStream() throws java.io.IOException public java.io.BufferedReader getReader() throws java.io.IOException Получить входящий поток данных или его "читатель". Reader применяется для чтения текстовой информации - он автоматически расшифрует строки в соответствии с заданным charset. Внимание! В версии J2EE 1.3 имеется существенный баг: при расшифровке символа %25 (символ % в Post и Get запросах) Reader выдаёт ошибку (баг замечен на серверах Tomcat 4 и Resign). Возможно, что схожий баг есть и с другими символами. public String getProtocol() Получить версию HTTP протокола запроса (к примеру - "HTTP/1.1"). public String getScheme() Возвращяет имя схемы запроса. Например "http", "https", или "ftp". public int getServerPort() public String getRemoteAddr() public String getRemoteHost() public boolean isSecure() Порт сервера, IP адрес клиента, имя хоста клиента и является ли соединение скретным (по протоколу HTTPS) public java.util.Locale getLocale() public java.util.Enumeration getLocales() Предпочитаемый клиентом язык документа (результат обработки поля "Accept-Language")
Интерфейс ServletResponse
Интерфейс ServletResponse - это инструмент для отправки данных клиенту. Все методы данного инструмента служат именно этой цели:
Public java.lang.String getCharacterEncoding() public void setLocale(java.util.Locale loc) public java.util.Locale getLocale() Первый метод возвращает MIME тип кодировки (к примеру - UTF8), в которой будет выдаваться информация. Вторые два метода тоже работают с charset. Они указывают на язык используемый в документе (например - русский). public ServletOutputStream getOutputStream() throws java.io.IOException Возвращает поток вывода данных для сервлета. Этот поток используется, к примеру, для вывода бинарных файлов. Текстовые данные можно выводить с помощью java.io.Writer: public java.io.PrintWriter getWriter() throws java.io.IOException Этот метод автоматически конвертирует строки в тот charset, что указан в методе getCharacterEncoding() и getLocale(). public void setContentLength(int len) Этим методом устанавливается значение поля HTTP заголовка "Content-Length" public void setContentType(String type) Метод для отправки MIME типа содержимого документа. Поле HTTP заголовка "Content-Type". public void setBufferSize(int size) public int getBufferSize() public void flushBuffer() throws java.io.IOException public void resetBuffer() Дело в том, что поток вывода данных является буфферизованным. Это означает, что следующая порция данных будет выдана клиенту только после заполнения буфера. Указанные методы позволяют, соответственно, установить размер буффера отправки, получить его размер, инициализировать отправку содержимое буффера клиенту, не дожидаясь его заполнения, а так же очистить этот буффер от данных. public boolean isCommitted() Этим методом можно получить флаг, начата ли уже отправка данных клиенту. Флаг будет положительным, если HTTP заголовок ответа был уже отправлен. public void reset() Если HTTP заголовок ещё не отправлен, то этот метод "сбрасывает" HTTP заголовок к значениям "по умолчанию".
Предопределённые типы сервлетов
Java Servlet API, кроме собственно интерфейсов, так же содержит несколько классов сервлетов, которые могут служить основой для ваших программ.
Базовым для всех этих классов является абстрактный класс javax.servlet.GenericServlet:
Public abstract class GenericServlet implements Servlet, ServletConfig, java.io.Serializable
Как видно из определения этого класса, он имеет все методы интерфейсов Servlet и ServletConfig. Не реализованным методом остался только
Public abstract void service(ServletRequest req, ServletResponse res) throws ServletException, java.io.IOException который и был объявлен абстрактным.
На базе этого класса был создан другой абстрактный класс - javax.servlet.http.HttpServlet:
Public abstract class HttpServlet extends GenericServlet implements java.io.Serializable
Создан этот класс был в соответствии с концепцией "ещё больше удобств для программиста" и имеет много полезных методов:
Protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, java.io.IOException protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, java.io.IOException protected void doHead(HttpServletRequest req, HttpServletResponse resp) throws ServletException, java.io.IOException protected void doOptions(HttpServletRequest req, HttpServletResponse resp) throws ServletException, java.io.IOException protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, java.io.IOException protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, java.io.IOException protected void doTrace(HttpServletRequest req, HttpServletResponse resp) throws ServletException, java.io.IOException protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, java.io.IOException protected void service(ServletRequest req, ServletResponse res) throws ServletException, java.io.IOException Различные варианты service(ServletRequest req, ServletResponse res) для разных HTTP методов от DELETE и GET до PUT и TRACE. А чтобы с удобством получать данные по CGI интерфейсу не расшифровывывая заголовок были созданы классы HttpServletRequest и HttpServletResponse, входящие вместе с HttpServlet в пакет javax.servlet.http protected long getLastModified(HttpServletRequest req) Этот метод возвращает время последней модификации объекта HttpServletRequest. Значение времени он берёт из поля "Date" HTTP заголовка запроса. Если же поле не обнаружено, то возвращает -1.
Соответственно разберём и интерфейсы HttpServletRequest и HttpServletResponse. Они являются наследниками соответственно ServletRequest и ServletResponse.
HttpServletRequest помимо методов, унаследованных от ServletRequest, имеет так же следующие полезнейшие методы:
Cookie getCookies() Возвращает набор куков, пересланных клиентом серверу.
Класс Cookie, входящий в тот же пакет javax.servlet.http, содержит всю возможную информацию о куке. Важнейшими методами этого класса являются
Int getMaxAge() String getName() String getValue() выдающие, соответственно, сколько ещё времени этому куку осталось жить, имя кука и его значение. Так же Cookie(String name, String value) void setValue(String newValue) void setMaxAge(int expiry) для создания кука, установки его значения и максимального возраста. long getDateHeader(String name) Возвращает дату из HTTP заголовка, если таковая есть. int getIntHeader(java.lang.String name) Возвращает численное значения поля с именем name из HTTP заголовка запроса String getMethod() Возвращает метод HTTP запроса. String getQueryString() String getRequestURI() StringBuffer getRequestURL() Возвращает строку, содержащуюся в URL документа после символа "?", URI документа и полный URL. HttpSession getSession() HttpSession getSession(boolean create) boolean isRequestedSessionIdFromCookie() boolean isRequestedSessionIdFromURL() boolean isRequestedSessionIdValid() Функции. позволяющие работать с таким важнейшим механизмом передачи данных, как сессии.
Сессии необходимы для того, чтобы таскать за пользователем данные из страницы в страницу. К примеру, пользователь заходит на страницу (1), где ему отправляются некоторые данные для страницы (2), а та сохраняет ещё какие-то вещи для страницы (3).
Впринципе, на странице (1) можно выслать данные пользователю, потом получить их на странице (2), добавить что-то, выслать пользователю... Подобным образом придётся постоянно пересылать весь набор данных от клиента серверу и обратно, причём много раз. Кроме того, что такая пересылка не всегда удобна, она ещё и пожирает траффик.
Можно так же поступить иначе - использовать механизм сессий. Механизм этот работает следующим образом: данные, присланные пользователем, сервер сохраняет в отдельном файле - файле сессии. С содержимым этого файла и будет производиться вся работа по изменению данных. Клиенту же выдаётся "ключ сессии" (он же Session key, он же Sesseion ID) - уникальный указатель на файл, содержащий данные конкретно для этого пользователя. Теперь для того, чтобы получить все данные, касающиеся этого клиента, серверу необходимо знать лишь ключ сессии. Достоинством этого метода является удобство и скорость его использования.
Вот и все основные методы интерфейса HttpServletRequest. Полный список методов читайте в документации к Java Servlet API.
Теперь об интерфейсе HttpServletRequest. Основное отличие классов, разделяющих данный интерфес, в том, что данные выводятся не сразу. Вначале происходит компановка всех данных в HTTP ответ. Ответ отправляется только после завершения работы HttpServlet.service().
И так, о методах:
Void addHeader(String name, String value) void addIntHeader(String name, int value) void addDateHeader(String name, long date) Методы добавляют в HTTP заголовок параметры. Последний метод устанавливает параметр "Date". void addCookie(Cookie cookie) Метод добавляет cookie в заголовок boolean containsHeader(String name) Позволяет узнать, содержит ли уже заголовок указанный параметр. String encodeURL(String url) String encodeRedirectURL(String url) Первый метод кодирует символы с помощью замены %NN. Второй метод делает то же самое и вызывает void sendRedirect(String location) void setStatus(int sc) void sendError(int sc) void sendError(int sc, String msg) Первый - устанавливает код возврата, вторые два - посылают сообщение об ошибке. В интерфейсе заданы следующие возможные ошибки для параметра sc, соответствующие кодам возврата протокола HTTP: SC_CONTINUE - Status code (100) SC_SWITCHING_PROTOCOLS - Status code (101) SC_OK - Status code (200) SC_CREATED - Status code (201) SC_ACCEPTED - Status code (202) SC_NON_AUTHORITATIVE_INFORMATION - Status code (203) SC_NO_CONTENT - Status code (204) SC_RESET_CONTENT - Status code (205) SC_PARTIAL_CONTENT - Status code (206) SC_MULTIPLE_CHOICES - Status code (300) SC_MOVED_PERMANENTLY - Status code (301) SC_MOVED_TEMPORARILY - Status code (302) SC_SEE_OTHER - Status code (303) SC_NOT_MODIFIED - Status code (304) SC_USE_PROXY - Status code (305) SC_BAD_REQUEST - Status code (400) SC_UNAUTHORIZED - Status code (401) SC_PAYMENT_REQUIRED - Status code (402) SC_FORBIDDEN - Status code (403) SC_NOT_FOUND - Status code (404) SC_METHOD_NOT_ALLOWED - Status code (405) SC_NOT_ACCEPTABLE - Status code (406) SC_PROXY_AUTHENTICATION_REQUIRED - Status code (407) SC_REQUEST_TIMEOUT - Status code (408) SC_CONFLICT - Status code (409) SC_GONE - Status code (410) SC_LENGTH_REQUIRED - Status code (411) SC_PRECONDITION_FAILED - Status code (412) SC_REQUEST_ENTITY_TOO_LARGE - Status code (413) SC_REQUEST_URI_TOO_LONG - Status code (414) SC_UNSUPPORTED_MEDIA_TYPE - Status code (415) SC_REQUESTED_RANGE_NOT_SATISFIABLE - Status code (416) SC_EXPECTATION_FAILED - Status code (417) SC_INTERNAL_SERVER_ERROR - Status code (500) SC_NOT_IMPLEMENTED - Status code (501) SC_BAD_GATEWAY - Status code (502) SC_SERVICE_UNAVAILABLE - Status code (503) SC_GATEWAY_TIMEOUT - Status code (504) SC_HTTP_VERSION_NOT_SUPPORTED - Status code (505)
Вот и всё, что можно рассказать о HttpServletResponse
Использование сервлетов в WEB приложениях
Поговорим теперь об использовании сервлетов в WEB приложениях. Для этого я приведу два полезных примера, которые могут на практике пригодиться.
Первый пример показывает методы работы с HttpServlet и вывод содержимого HTML страницы в сжатом виде. По идее, HTML страница в ответе браузера выводится прямым текстом, но, чтобы сократить объём пересылаемых данных, можно использовать сжатие GZIP. Современные браузеры (по крайней мере браузеры 4 поколения и выше) поддерживают такой метод пересылки текстовой информации и выведут страницу так, как будто бы её и не сжимали.
import java. io.* ; import javax. servlet.* ; import javax. servlet. http.* ; import java. util. zip.* ; // сервлет является наследником HttpServlet public class ZipServlet extends HttpServlet { // функция обработки метода GET public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // устанавливаем, что страничка является HTML документом response. setContentType("text/html" ); // берём параметр "Accept-Encoding" из HTTP заголовка String encodings = request. getHeader("Accept-Encoding" ); // берём параметр "encoding" - ранее заданная кодировка документа String encodeFlag = request. getParameter("encoding" ); // Куда будем выводить PrintWriter out; // если поле "Accept-Encoding" в запросе присутствует if (encodings != null) { // и если это поле содержит значение "gzip", а кодировка ещё не была установлена, if ((encodings. indexOf("gzip" ) != - 1 ) &&! encodeFlag. equals("none" )) { // то то, куда будем выводит, будет за одним и сжимать текст с помощью GZIP out = new PrintWriter(new GZIPOutputStream(response. getOutputStream()) , false); // и устанавливаем флаг для браузера, что документ будет сжат response. setHeader("Content-Encoding" , "gzip" ); } else out = response. getWriter(); } else // в противном случае выводить будем без сжатия out = response. getWriter(); out. println("This a test!!!" ); // пишем тело документа out. close(); // и закрываем вывод. //Всё, по завершению работы этой ф-ии, документ будет отправлен } }Второй пример показывает, как средствами сервлета можно осуществить непрерывный вывод страницы. Такой тип вывода страниц может использоваться, например, в чатах: чтобы прочитать новые сообщения, не нужно будет каждый раз обновлять страницу, новые сообщения будут просто со временем докачиваться. Нужно учесть, что некоторые прокси-серверы не поддерживают такой вид пересылки данных, но с этим - увы - ничего сделать нельзя.
import java. io.* ; import javax. servlet.* ; // программа реализует интерфейс Servlet class DoloadServlet implements Servlet { ServletConfig config; // объект ServletConfig public DoloadServlet() {} // ничего не делает // при инициализации сохраняем config public void init(ServletConfig config) throws ServletException { this. config = config;} // выдаёт сохранённый config public ServletConfig getServletConfig() { return config;} // информация о сервлете public String getServletInfo() { return "DoloadServlet" ;} public void destroy() {} // ничего не делает // обработка запроса public void service(ServletRequest request, ServletResponse response) throws ServletException, java. io. IOException { // разбирать запрос мы не будем, просто срязу // создаём HTTP заголовок: String head = "HTTP/1.0 200 OK\n" + + "Server: DoloadServlet\n" + "Content-Type: text/html; charset=UTF-8\n" + "Connection: Keep-Alive\n" + "Content-Encoding: multipart/mixed\n" + "Transfer-Encoding: chunked" + "Pragma: no-cache\n\n" ; // теперь добавляем первоначальные данные // для этого примера - 20 тэгов "" с переносом строки for (int i = 0 ; i < 20 ; i++ ) head = head + "
\n" ; // берём поток вывода ServletOutputStream os = response. getOutputStream(); // пишем туда заголовок и первоначальные данные os. print(head); // отправляем всё записаное в буффер к клиенту response. flushBuffer(); // начинаем добавлять новые строки: // эти строки будут выглядеть следующим образом: номер строки, потом "
\n" // каждая новая строка будет появляться раз в 3 секунды int i = 0 ; while (true) { // инкримент счётчика i++ ; // пишем строку os. print("" + i+ "
\n" ); // сброс буффера response. flushBuffer(); // примораживаем поток на 3 секунды try { sleep(3000 );} catch (Exception e){} } } }
Осталось сказать, что механизм сервлетов очень пластичен и позволяет творить такие вещи, которые могли бы потребовать написания отдельного WEB сервера (как, например, в случае сервлета докачки). Минусом работы сервлетов является низкая скорость первого запуска (сервлет просто компилируется JIT машиной), высокое поребление памяти и недостаток всех программ на Java - низкая скорость работы со строками. Последнее обстоятельство становится заметно при работе сервлетов, принимающих текстовые данные в POST запросах. POST запрос в HttpServlet размером в 50 кб при парсинге с помощью HttpServletRequest.getReader() может на пару минут парализавать работу сервера. То же относится и к другим программам на java.
Приведу два небольших примера:
// дана строка String text // пример 1 // идёт работа со строкой с помощью операции "+" для String String test1 = "" ; for (int i = 0 ; i < text. length(); i++ ) test1 += text. charAt(i); // пример 2 // идёт работа со строкой посредством буффера char buf = new char [ text. length()]; for (int i = 0 ; i < text. length(); i++ ) buf[ i] = text. charAt(i); String sample2 = new String(buf);Если взять небольшие строки - до 2-3 кб, то отличия в работе примеров несущественны, если же взять строку text размером хотя бы в 10 кб, то в первом случае программа будет работать со строкой значительно медленнее. Это является особенностью java и является проблемой реализации функций класса String. Так что если вы хотите написать быстрый сервлет, избегайте работу с длинными строками посредством класса String, используйте, к примеру, класс StringBuffer. Это предупреждение относится прежде всего к получению больших текстов из сети и к обработке локальных файлов (к примеру, в случае текстовой базы данных для гостевой книги при большом количестве сообщений).
Ещё одна проблема касается мультизадачности WEB системы. Не забывайте, что ваш сервлет могут одновременно запросить сразу несколько пользователей. Часто возникают проблемы синхронизации данных, обмену сведениями между разными вычислительными потоками одного и того же сервлета, а самая часто встречающаяся проблема - это проблема синхронного доступа к файлам и другим именованым ресурсам системы. К примеру, одна программа открыла файл на чтение, а другая тем временем пытается туда что-то писать. В результате вторая программа либо получает исключение, либо ждёт, пока файл освободится для записи. В связи с этим хочу обратить ваше внимание: не оставляйте за собою незакрытых потоков и закрывайте потоки, как только в них отпала необходимость. Поток, конечно, закроется позже автоматически, но это произойдёт только тогда, когда "мусорщик" до него доберётся, а меж тем вторая программа всё так же не будет иметь доступа к файлу на запись.
Дополнительно к мультизадачности хочу отметить, что с помощью методов "Object getAttribute(String name)" и "void setAttribute(String name, Object object)" интерфейса ServletContext вы можете обмениваться между сервлетами данными, в том числе и синхронизирующими.