Servlet основы. Создание приложений на Java с помощью Servlets. Использование сервлетов в WEB приложениях
Сервлеты (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 сервлета. Мы можем расширить эти классы и переопределить только необходимые методы для реализации собственных типов объектов ответов и запросов. Эти классы не используются в стандартном программировании сервлетов.
Одной из самых приятных особенностей 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 представлен .
С момента появления языка программирования Java прошло уже почти двадцать лет. За это время Java пророчили смерть и забвение, программисты на сях смеялись над ее тормознутостью и жадностью к ресурсам. Но были и те, кто поверил в Java, они разрабатывали всевозможные библиотеки, развивали сообщество, упорно доказывали, что для Java нет пределов: realtime, embedded, ИИ - возможно все. Мы решили не оставаться в стороне и сделать в этой рубрике небольшой цикл статей по Java. Поехали!
Ваш чайник выбирает Java
По заверениям самой Oracle, на сегодняшний день виртуальная машина Java установлена на более чем трех миллиардах устройств. И это не только компьютеры и смартфоны, но и фотоаппараты, телевизоры, Blue-ray-проигрыватели, принтеры, сим-карты, банковские автоматы и даже автомобили. Этот список будет неуклонно расти, а вместе с ним и предложения от работодателей для Java-программистов. Даже сейчас количество вакансий для программистов Java превышает остальные. И компании готовы платить все больше и больше, переманивая сотрудников и организуя более выгодные условия труда.
А чем же она хороша?
Программистов Java привлекает минимализмом синтаксиса. Никаких лишних модификаторов и служебных слов. Даже отсутствие множественного наследования, которое поначалу несколько смущало программистов на С++, в итоге оказывается разумным и оправданным. Простая логика, автоматическая работа с памятью, подробная документация, форумы с ответами на всевозможные вопросы, открытый код - все это позволяет быстро вникнуть в процесс разработки и значительно уменьшает количество потенциальных ошибок. Даже индийские крестьяне осваивают Java за пару месяцев, по крайней мере так говорится в их дипломах:). Кроме того, Java - интерпретируемый язык. Исходный код компилятор переводит в так называемый байт-код, который несложно преобразовать обратно, что делает Java особенно привлекательным для реверс-инжиниринга.
Ну-с, приступим
Java - объектно-ориентированный язык, это значит, что все переменные, методы, константы объявляются в рамках какого-либо класса. Кроме классов, есть еще интерфейсы - особая абстрактная конструкция, которая позволяет описать поведение объекта, не указывая конкретную реализацию. И если множественного наследования классов в Java нет, то интерфейсов класс может реализовывать любое количество, что позволяет одному объекту обладать множеством функций, но предоставлять только часть из них.
Типы данных можно разделить на две группы: простые (int, long, char и так далее) и объектные: классы, интерфейсы, массивы. Простые типы всегда и везде фиксированной размерности. К примеру, на любой архитектуре и любом устройстве int занимает четыре байта памяти. Это довольно удобно при вычислениях. Массив данных содержит специальный атрибут length, который хранит размер массива, за что отдельное спасибо разработчикам. Данные разных типов по-разному передаются в методы. Простые типы всегда передаются по значению. Объектные - всегда по ссылке для экономии памяти. Это значит, что если мы передаем int a = 10 и изменяем его значение на 5 в вызываемом методе, то в исходном методе a по-прежнему будет равно 10. Но если мы изменим свойство объекта, то оно изменится и в исходном методе.
Помни о памяти
Хотя программист Java и освобожден от необходимости выделять и освобождать память, незнание некоторых особенностей работы виртуальной машины и сборщика мусора может запросто превратить твою программу в ненасытного монстра, пожирающего процессорное время и всю доступную память.
Создавая новый массив, всегда помни, что гораздо проще создать много маленьких кусочков памяти, чем один огромный. Иначе рискуешь нарваться на ошибку Out of memory, что примерно означает, что память у тебя была, да вся вышла.
Многие программисты, когда переходят на Java и узнают об автоматической очистке памяти, начинают создавать объекты в огромных количествах, надеясь, что все это уберется само. Между тем сборщик мусора подобен машине, которая может убрать мусор, только выброшенный в урну возле дома. Если какие-то данные тебе больше не нужны, не стоит хранить их на всякий случай, как ворох старых открыток, - присвой указателю на данные null, помоги уборщику прибраться:). Также хорошим тоном будет сделать clear для списка, если он тебе уже не понадобится. Помни, объект будет храниться в памяти, пока в коде на него есть ссылки. Даже если твоя программа работает на 16 гигах памяти и вылететь с Out of memory ей не грозит, от переизбытка используемой памяти она будет становиться все более неповоротливой и тормознутой. 99% жалоб пользователей на медленную работу Java-программ связано с неэффективно написанным исходным кодом. Если тебе требуется постоянно создавать объекты, которые используются быстро и больше не нужны, например много мелких сообщений, задумайся о создании пула, в котором будет храниться некоторое количество экземпляров для многократного использования. Помни, создание и удаление объекта - операция дорогостоящая.
За дело, господа
Один пример лучше тысячи слов. Пролистать мануал и посмотреть на стандартные хеллоуворды ты можешь и без нас, поэтому будем считать, что ты это уже сделал и готов к реализации более интересного примера.
Мы с тобой займемся серверным применением Java и напишем небольшую программу для «слежки» за пользователями социальных сетей. Для этого даже не придется трудоустраиваться в АНБ - пользователи сами про себя все выкладывают, а нам останется только эту информацию получить, систематизировать и красивенько отобразить. Возьмем один из популярных социальных сервисов, к примеру foursquare, и нарисуем на карте перемещения наших друзей.
Для начала посмотрим, что мы можем вытянуть из foursquare. Пробежавшись по страничкам для разработчиков , мы обращаем свое внимание на два метода:
- https://developer.foursquare.com/docs/users/checkins - места, которые посетил пользователь. К сожалению, пока поддерживается только для зарегистрированного в программе пользователя, и ходят слухи, что из-за ограничений в реализации так оно и останется;
- https://developer.foursquare.com/docs/checkins/recent - места, которые посетили друзья зарегистрированного пользователя. Если немного поиграть с этой функцией, то выясняется печальный факт: для каждого друга возвращается ровно одно место - последнее, где он отметился.
Чтобы пользоваться foursquare API, необходимо зарегистрировать наше будущее приложение, идем по этому адресу: https://ru.foursquare.com/developers/register и заполняем поля (да, в самом foursquare тоже придется зарегистрироваться, но с этим ты прекрасно справишься и без меня).
Из важных полей тут можно отметить только «Название приложения», «Download / welcome page url» (впиши сюда произвольный веб-адрес) и «Redirect URI(s)» - это адрес, на который нас отправит сервер после регистрации. Сюда мы позже впишем нужное значение, а пока можешь просто вписать любой веб-адрес. Жмем «Сохранить», и наше приложение tracker успешно зарегистрировано.
Поднимаемся в облака
Капитан Очевидность передает, что любому серверному приложению для работы требуется сервер. Поднимать сервер самостоятельно геморройно, поэтому воспользуемся популярными сейчас облачными решениями. Спонсором облака выступит корпорация Google, потому что их Google App Engine бесплатен, довольно легок в настройке и использовании. Для начала идем сюда и скачиваем Google App Engine SDK for Java.
Теперь можно приступать к созданию проекта. Для разработки на Java я пользуюсь IntelliJ IDEA, но ты можешь воспользоваться бесплатной и не менее известной средой Eclipse.
Выберем новый проект Java. Назовем его nsa_tracker.
На следующей вкладке отметим слева Web Application и Google App Engine и укажем путь к скачанной ранее и распакованной App Engine SDK.
А теперь откинься в кресле и позволь IDE сделать свое дело. Если ты выбрал IDEA и все сделал правильно, то в результате увидишь готовый проект, который при запуске открывает окно браузера с пустым содержимым. Можно приступать к кодингу.
Начинаем искать
Итак, у нас есть папка с проектом, в которой лежит папка src. Туда мы будем складывать исходники. Исходники в Java группируются по пакетам. Пакет - это папка на диске. Пакеты нужны, чтобы не сваливать все исходники в кучу, а разделять их, руководствуясь принципами логики. Например, код, связанный с пользовательским интерфейсом, логично поместить в пакет ui, сетевые взаимодействия - в пакет network. Это значительно облегчает развитие и поддержку проекта впоследствии. Исторически сложилась практика начинать структуру пакетов с названия компании, за которым следует название программы. Это поможет легко идентифицировать наши исходники среди кучи таких же в дальнейшем. Для нашей программы мы создадим пакет org.nsa.tracker. В нем мы и будем создавать классы.
Для обработки запросов пользователей на сервере используются сервлеты. Сервлет - это класс, который наследует, как правило, HttpServlet и работает по принципу запрос - ответ. Все, что нужно, - это переопределить метод doGet. По запросу от пользователя нам нужно авторизоваться в foursquare, загрузить список чекинов друзей и перенаправить запрос на страницу с картой.
Для работы с foursquare API воспользуемся бесплатной библиотекой foursquare-api-java, которую можно взять отсюда . Библиотека Java представляет собой ZIP-архив с расширением jar, содержащий откомпилированные Java-классы, которые реализуют определенную функциональность. Для авторизации нам понадобятся ClientId и ClientSecret, полученные на этапе регистрации приложения в foursquare. Так как эти параметры не меняются в процессе выполнения программы, объявим их как константы.
Private static final String CLIENT_ID = "FAKE_CLIENT_ID"; private static final String CLIENT_SECRET = "FAKE_CLIENT_SECRET";
Final означает, что данной переменной присвоено окончательное значение, которое не дано изменить. Static делает переменную доступной для всех экземпляров данного класса. Воспользовавшись примером авторизации из библиотеки foursquare-api-java, получим примерно следующий код:
Protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
FoursquareApi foursquareApi = new FoursquareApi(CLIENT_ID, CLIENT_SECRET, CALLBACK_URL);
String code = req.getParameter("code");
if (code == null) {
// Отправляемся на страницу регистрации
resp.sendRedirect(foursquareApi.getAuthenticationUrl());
} else {
try {
foursquareApi.authenticateCode(code);
// Регистрация успешна, загрузим-ка данных
Result
Обрати внимание на «throws ServletException, IOException» в объявлении метода. Эта строка означает, что метод потенциально может бросить одно из этих исключений. Исключение в Java - это объект, который сигнализирует о возникновении исключительной ситуации. Они бывают проверяемые и непроверяемые. Проверяемые исключения нужно обрабатывать, окружив часть кода блоком try-catch, или же передавать выше. Непроверяемые исключения, как правило, не обрабатываются, потому что возникают в случаях, когда программа не в состоянии восстановить свое состояние. В данном методе мы обрабатываем только исключение FoursquareApiException.
Когда веб-сервер получает запрос для приложения, он использует дескриптор развертывания, чтобы сопоставить URL запроса с кодом, который должен обработать запрос. Дескриптор развертывания представляет собой XML-файл c названием web.xml. Добавим описание сервлета слежения.
Теперь запросы по адресу /track будет обрабатывать наш сервлет с именем TrackerServlet. Можно присвоить параметру Callback Url верное значение http://localhost:8080/track.
Для вывода результатов можно воспользоваться Static Maps API, любезно предоставленным все той же корпорацией Google (https://developers.google.com/maps/documentation/staticmaps/). Наш сервлет будет генерировать простую HTML-страницу и возвращать ее в ответ на запрос пользователя.
StringBuilder sb = new StringBuilder("
- ");
index = 1;
for (Checkin checkin: result.getResult()) {
sb.append("
Для генерации страницы используется класс StringBuilder, это обусловлено тем, что строки в Java являются неизменяемыми объектами. При конкатенации строк с помощью оператора +. создается новая строка в памяти. StringBuilder позволяет экономить память, так как использует массив char для хранения соединяемых строк. Передаем ответ пользователю:
Byte resultBytes = sb.toString().getBytes("utf-8"); resp.setContentLength(resultBytes.length); resp.getOutputStream().write(resultBytes);
…И все готово. Запускаем и видим что-то похожее на картинку с говорящей подписью «Результат работы программы».
Что дальше?
Приложение можно улучшить, например разделить сбор данных и отображение. Вынести сбор данных в отдельный сервис, который будет работать постоянно и запоминать все перемещения пользователей в базе данных. Тогда отображать можно будет уже не отдельные точки, а связный маршрут. Покопавшись немного в foursquare API, можно извлечь даже больше информации об активности пользователя.
Но надеюсь, мне удалось главное: убедить тебя в том, что Java - это просто и круто. До встречи через месяц!
Книги для Java-программера
Начать изучать язык мы советуем с книги «Java. Руководство для начинающих» (Java: A Beginner’s Guide) Герберта Шилдта. Следующий уровень - «Java. Полное руководство» от него же, а больше о сервлетах ты можешь узнать из книги «Java сервлеты и JSP: сборник рецептов» (Java Servlet and JSP Cookbook) Брюса У. Перри.
Сервлет - это Java-nporpaMMa, которая в качестве своего суперкласса использует класс HttpServiet. Сервлет используется для того, чтобы расширить возможности существующего сервера, в частности, Web-сервера. Как правило, сервлет работает на специальном сервере. Такие серверы носят название серверов Java-приложений (Java Application Server). В состав сервера Java-приложений в качестве составного блока входит Web-сервер (иногда не один, а несколько), а также серверы, работающие с серверными компонентами, серверы вспомогательных служб и т. п. Сервлет работает в окружении, которое предоставляет ему сервер. Часть сервера, предназначенная для работы с сервлетами, называется контейнером сервлетов. Спецификация сервле- тов предполагает наличие в классе сервлета стандартных методов, выполнение которых происходит на том или ином этапе жизненного цикла сервлета. Вызов этих методов осуществляется контейнером сервлетов. Имплементация спецификации сервлетов входит в набор стандартных пакетов языка Java.
В данной книге рассматривается сервер Java-приложений под названием Blazix. Сервер Blazix предоставляет полный набор возможностей для работы с сервлетами. Помимо создания класса (или классов) сервлета, а также для конфигурирования созданной программы-сервлета, установления ее на сервер, необходимо изменить файл конфигурации Web-сервера. Основные значения указываются в файле конфигурации в следующем виде: servlet.name: myservlet
servlet.myservlet.className: mypackage.MyServletClass servlet.myservlet.url: /mysrvlet
У каждого сервлета должно быть задано имя (servlet.name), по которому он идентифицируется на сервере. Это имя используется для задания свойств сервлета, в частности, для указания имени класса, в котором хранится программа сервлета (следующая строка), а также адреса, по которому происходит обращение к этому сервлету (третья строка).
Клиент запрашивает у Web-сервера адрес, по которому расположен сервлет (адрес должен быть указан в качестве значения servlet.myservlet.url в файле конфигурации Web-сервера). Сервер передает запрос и данные (если они есть) сервлету, получает ответ от сервлета и направляет его клиенту.
На этом объяснения о том, что такое сервлет, можно было бы закончить. Однако существует множество интересных и полезных деталей, на которых следует задержать внимание и изучить их подробнее.
Особенно важно иметь в виду, что путь к классу сервлета должен быть указан в переменной ciasspath или же его можно поместить в каталог C:\Blazix\classes или в каталог, который указан в файле конфигурации Web- сервера в качестве значения server.ciassDir. После того как файл конфигурации был изменен и в него вставлена информация о новом сервлете, сервер должен быть остановлен и запущен вновь. Сервлет может быть размещен и на работающем сервере при помощи утилиты администрирования, но для этого сервлет должен быть упакован в файл Web-архива WAR. Если файл класса сервлета был изменен, то останавливать сервер и запускать его вновь необязательно. По умолчанию сервер сконфигурирован так, что вызов сервлета по адресу http://localhost:81/_reload приводит к тому, что все классы будут перезагружены и измененный класс сервлета станет доступен для клиентских запросов (рис. 4.1). Посетите эту страницу после того, как будет изменен файл класса сервлета. Остановка сервера попросту не нужна.
Если читатель уже имел некоторый опыт работы с сервлетами, то он оценит ту простоту, которая свойственна серверу Blazix в сравнении с другими Java- серверами, например, с сервером Tomcat.
Чтобы сразу приступить к делу, рассмотрим простой пример, сервлет some- Servlet (листинг 4.1).
Листинг 4.1. Сервлет SomeServlet.java I
import java.io.*; import javax.servlet.*; import javax.servlet.http.*;
public class SomeServlet extends HttpServlet { public void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException { Printwriter out = response.getWriter(); out.println("Hello World");
В файле конфигурации Web-сервера запишем следующее:
servlet.name: first
servlet.first.className: SomeServlet
servlet.first.url: /dofirst
Тогда обращение к сервлету из браузера примет вид http://localhost:81 /dofirst (рис. 4.2).
Сервлеты Java предоставляют платформонезависимый метод создания Web- приложений (это не бесспорно, однако мы не будем рассматривать этот вопрос глубже, поскольку тогда нам потребуется определиться с тем, что понимать под Web-приложением, что не входит в перечень рассматриваемых тем), причем многие приложения отличаются быстротой в работе и лишены тех ограничений, которые имеются у CGI-приложений. Сервлет работает под управлением Web-сервера и, в отличие от апплета, не требует графического интерфейса пользователя. Сервлет взаимодействует с сервером, обмениваясь с ним запросами и ответами. Клиентская программа обращается к серверу с запросами. Запрос обрабатывается сервером, передается сервлету, сервлет посылает ответ через сервер клиенту. На сегодняшний день сервлеты весьма популярны при создании интерактивных Web-приложений. Существует множество Web-серверов, способных работать с сервлетами, среди них такие серверы, как Tomcat, iPlanet Web Server (ранее Netscape
Enterprise Server), Blazix. J Builder Enterprise использует сервер Borland Enterprise Server (BES), входящий в состав пакета, а также поддерживает работу с серверами Web Logic, WebSphere и iPlanet. J Builder Enterprise Server включает в себя сервер Tomcat, который используется "по умолчанию".
Рис. 4.2. Обращение к сервлету
Одним важным преимуществом сервлетов является скорость их работы. В отличие от приложений CGI сервлеты загружаются в память лишь один раз и затем выполняются непосредственно из памяти. Сервлеты по существу являются многопоточными приложениями. К тому же они платформонеза- висимы, поскольку написаны на языке Java.
Технология JSP, которой посвящена глава 3, является расширением технологии сервлетов, в JSP особое внимание уделяется работе с HTML- и XML- документами. В составе HTML- и XML-кода JSP-фрагменты находят свое наиболее частое применение. Как сделать выбор и решить, что использовать: сервлеты или серверные страницы? Сервлеты более подходят для решения задач низкоуровневого программирования и менее приспособлены для решения задач создания логики представления приложения. Серверные страницы JSP, наоборот, в основном сконцентрированы на том, как представить результат пользователю в наиболее удобном виде. Серверные страницы создаются встроенными в HTML-код, используя стиль создания HTML-документов. Технология JSP предоставляет гораздо более богатые возможности, чем простой HTML. Страницы JSP могут предоставлять возможности реализации логики приложений с использованием простых компонентов Java, а также серверных компонентов EJB, путем создания пользовательских библиотек ярлыков. Сами по себе серверные страницы Java могут являться модульными, многократно используемыми компонентами, работающими с логикой представления, которые можно использовать совместно с различными шаблонами и фильтрами. JSP-страницы преобразуются в сервлеты, поэтому теоретически можно пользоваться исключительно сервлетами. Однако технология JSP создана для того, чтобы упростить процесс создания Web-документов, отделив логику представления приложения от содержания документа. В большинстве случаев отправляемый клиенту ответ состоит как из шаблонов представления документа, так и из данных, которые генерируются автоматически, заполняя шаблон. В таких ситуациях оказывается много легче работать с JSP, чем с сервлетами.