-1Прикладное программирование в ТС Лекция 3-16 Лекция 3-16 Тема 3.3. Обработка данных на Web-сервере с использованием Java 3.3.1. Средства обработки данных на сервере в Java 3.3.2. Общие средства для работы с сервлетами в Java 3.3.2.1. Интерфейс Servlet 3.3.2.2. Интерфейс ServletConfig 3.3.2.3. Интерфейс ServletContext 3.3.2.4. Интерфейс ServletRequest и классы ServletInputStream и ServletRequestWrapper 3.3.2.5. Интерфейс ServletResponse и классы ServletOutputStream и ServletResponseWrapper 3.3.2.6. Интерфейс RequestDispatcher 3.3.2.7. Интерфейс ServletContextAttributeListener и классы ServletContextEvent и ServletContextAttributeEvent 3.3.2.8. Интерфейсы Filter, FilterChain и FilterConfig 3.3.2.9. Класс GenericServlet 3.3.2.10. Классы ServletException и UnavailableException 3.3.3. Средства для работы с сервлетами по протоколу HTTP в Java 3.3.3.1. Интерфейс HttpServletRequest и класс HttpServletRequestWrapper 3.3.3.2. Интерфейс HttpServletResponse и класс HttpServletResponseWrapper 3.3.3.3. Класс HttpServlet 3.3.4. Организация сеанса связи с сервлетом 3.3.4.1. Получение сведений о клиенте 3.3.4.2. Средства связи с клиентом в пакете javax.servlet.http 3.3.1. Средства обработки данных на сервере в Java Средства обработки данных на сервере (и, в частности на Web-сервере) представляют так называемое промежуточное (middleware) программное обеспечение, которое служит связующим звеном между клиентскими приложениями и данными, необходимыми для клиентских приложений. В языке Java для обработки данных на сервере определены следующие два компонента: Web-компоненты (Web components); Компоненты, ориентированные на бизнес-приложения (business components). Web-компоненты в Java представлены двумя тесно связанными между собой технологиями: сервлетами и серверными страницами Java – JSP (Java Server Pages). Сервлеты Java дают возможность Web-серверу обрабатывать запросы с помощью программ, написанных на языке Java. Специальным образом оформленная программа запускается в виде отдельного потока, после чего сервер передает ей данные для обработки и получает ответ. Несмотря на внешнюю схожесть с технологией CGI, сервлеты имеют существенное отличие в том, что они выполняются под управлением Web-сервера. так же, как апплеты выполняются под управлением Web-браузера. Сервлеты запускаются в виде потоков, в то время, как программы CGI запускаются как отдельный процесс. Сервлеты также позволяют хранить информацию о состоянии текущего соединения и поддерживают разделение данных между различными экземплярами программы. Кроме того, сервлеты могут пересылать запросы другим серверам и сервлетам. Таким образом, сервлеты можно применять для распределения нагрузки между несколькими серверами, которые отражают одинаковое содержание, а так же для Файл: 681459229 Создан: 09.07.2007 Модифицирован: 29.04.2016 Автор: Шонин В.А. -2Прикладное программирование в ТС Лекция 3-16 распределения одного логического сервиса по нескольким серверам, в соответствии с поставленной задачей. При использовании для поддержки апплетов сервлеты могут выполнять функции их proxy-серверов. Это может быть важно, поскольку система безопасности Java позволяет апплетам соединяться только с сервером, с которого они были загружены. Если апплет нуждается в соединении с сервером баз данных, расположенном на другой машине, сервлет может создать это соединение для апплета. Технология JSP позволяет включать в документ HTML или XML обрабатываемые на сервере сценарии на языке Java. Таким же образом функционируют и рассмотренные ранее технологии PHP и ASP, однако при использовании JSP исходные документы не интерпретируются, а компилируются в байт-коды, т.е. повторная обработка документов выполняется значительно быстрее. Компонентами, ориентированными на приложения, являются EJB (Enterprise Java Beans). Контейнеры и серверы EJB предоставляют услуги по организации взаимодействия между компонентами распределенного приложения, позволяют работать с данными и обеспечивать жизнедеятельность системы. В Java определено три типа EJB: компоненты сеанса (Session Beans), компоненты объектов (Entity Beans) и управляемые сообщениями компоненты (Message-driven Beans). Компоненты сеанса обеспечивают диалог клиента с приложением. При завершении соединения с клиентом эти компоненты удаляются из системы. Компоненты объектов содержат данные. Если клиент заканчивает работу или сервер останавливается, соответствующие службы обеспечивают сохранность компонентов объектов. Управляемые сообщениями компоненты объединяют свойства компонентов сеанса и блоков прослушивания службы сообщений Java (JMS), что позволяет компонентам EJB асинхронно получать сообщения JMS. 3.3.2. Общие средства для работы с сервлетами в Java Сервлеты представляют собой обычные программы на языке Java, но, так же как апплеты, они имеют структуру, отличную от приложений Java. Для слежения за работой сервлетов и управления ими создается специальный программный модуль, называемый контейнером сервлетов (servlet container). Контейнер сервлетов активен, он загружает сервлеты, инициализирует их, передает им запросы клиентов, принимает ответы. Чтобы сервлет мог работать, он должен быть зарегистрирован в контейнере, или, по терминологии спецификации сервлетов Java (Java Servlet Specification), установлен (deploy) в него. Установка (deployment) сервлета в контейнер включает получение уникального имени и определение начальных параметров сервлета, запись их в конфигурационные файлы, создание каталогов для хранения всех файлов сервлета и другие операции. Один контейнер может управлять работой нескольких установленных в него сервлетов. При этом один контейнер может в одно и то же время работать в нескольких виртуальных машинах Java, образуя распределенное Web-приложение. Сами же виртуальные машины Java могут работать на одном компьютере или на разных компьютерах. Контейнеры сервлетов создаются как часть Web-сервера или как встраиваемый в него модуль. Конкретное распределение обязанностей между Web-сервером и контейнером сервлетов осуществляется производителем программ для контейнеров. В стандартную поставку J2EE входит контейнер сервлетов Tomcat как составная часть сервера приложений J2EE. Интерфейс прикладного программирования для сервлетов (Servlet API) содержится в пакетах javax.servlet и javax.servlet.http. Файл: 681459229 Создан: 09.07.2007 Модифицирован: 29.04.2016 Автор: Шонин В.А. -3Прикладное программирование в ТС Лекция 3-16 Рассмотрим общие средства работы с сервлетами, содержащиеся в пакете javax.servlet. 3.3.2.1. Интерфейс Servlet Интерфейс Servlet содержит определения методов, которые должны реализовывать все сервлеты. Инициализация сервлета выполняется с помощью метода public void init(ServletConfig conf) throws ServletException В качестве параметра методу предается объект интерфейса ServletConfig (этот объект содержит информацию, необходимую для выполнения сервлета). Для обработки запроса от клиента вызывается метод public void service(ServletRequest req, ServletResponse res) throws ServletException, java.io.IOException Запрос от клиента передается через параметр req, а ответ клиенту записывается в параметр res. Следует иметь в виду, что метод service() будет параллельно выполняться несколькими подпроцессами, и поэтому при реализации метода необходимо принимать меры к синхронизации работы подпроцессов. Так, создание объектов, общих для сервлета, определение параметров, соединений с базами данных и удаленными объектами следует выносить в поля класса и в метод init(). Если параллельная работа не подходит для сервлета, то можно организовать последовательное выполнение запросов. Для этого сервлет должен реализовать интерфейс SingleThreadModel. Это пустой интерфейс без констант и методов, который служит флажком, указывающим контейнеру, что следует организовать очередь последовательно выполняющихся запросов к сервлету. Метод public void destroy() вызывается, когда сервлет выгружается. При параллельной работе сервлета сюда обычно выносятся такие действия как закрытие потоков, запись результатов на диск, закрытие соединений. Для сервлетов, как и для апплетов, определен жизненный цикл, включающий в себя следующие шаги: 1. Сервлет загружается контейнером (как правило, при первом запросе к нему или во время запуска контейнера). После выполнения запроса сервлет может оставаться в спящем состоянии, ожидая следующего запроса, или выгружаться, предварительно выполнив метод destroy(). Это зависит от реализации контейнера сервлетов. 2. Работа сервлета начинается с метода init(), выполняющего операции по инициализации апплета. 3. Затем выполняется метод service(), который выполняет требуемые операции по обработке данных. 4. При закрытии сервлета выполняется метод destroy(). Два вспомогательных метода интерфейса public ServletConfig getServletConfig() public String getServletInfo() возвращают информацию сервлете (такую как автор и версия) и о конфигурации сервлета. Файл: 681459229 Создан: 09.07.2007 Модифицирован: 29.04.2016 Автор: Шонин В.А. -4Прикладное программирование в ТС Лекция 3-16 3.3.2.2. Интерфейс ServletConfig Конфигурационный файл (deployment descriptor) описывает ресурсы, составляющие Web-приложение: сервлеты, страницы JSP, документы HTML и XML, изображения и документы других типов. Он формируется при создании Web-приложения и заполняется при установке сервлета и других ресурсов в контейнер. В новых версиях контейнеров конфигурационный файл записывается на языке XML и называется web.xml. Конфигурационный файл располагается в каталоге WEB-INF, одном из каталогов Web-приложения, и создается вручную или утилитой установки сервлета в контейнер. Каждая фирма – производитель контейнера сервлетов предоставляет свою утилиту установки или make-файл, содержащий команды установки. В стандартную поставку J2EE SDK входит графическая утилита установки Application Deployment Tool, вызываемая из командной строки набором команды deploytool. Каждый объект типа ServletConfig содержит имя сервлета, извлеченное из содержимого дескриптора <servlet-name> конфигурационного файла, набор начальных параметров, взятых из содержимого дескрипторов <init-param>, и контекст сервлета в виде объекта типа ServletContext. Эти конфигурационные параметры сервлет может с помощью следующих методов интерфейса ServletConfig public String getServletName() public Enumeration getInitParameterNames() public String getInitParameter(String name) public ServletContext getServletContext(). Первый метод получает имя сервлета, второй – список имен параметров инициализации сервлета, третий позволяет получить значение параметра с именем name, а четвертый – контекст сервлета. 3.3.2.3. Интерфейс ServletContext Для всех сервлетов, работающих в рамках одного Web-приложения, создается один контекст. Контекст (context) сервлета составляют каталоги и файлы, описывающие Webприложение. Они содержат, в частности, код сервлета, изображения, конфигурационный файл, т.е. все, относящееся к сервлету. При инициализации сервлета некоторые сведения о его контексте заносятся в объект типа ServletContext. Методы этого интерфейса позволяют сервлету получить сведения, содержащиеся в контексте. Метод public String getServerInfo() позволяет получить имя и версию J2EE, а методы public int getMajorVersion() public int getMinorVersion() возвращают номер версии и модификации Servlet API. В контексте можно определить начальные параметры, общие для всего Webприложения. Они задаются при создании Web-приложения вручную или с помощью утилиты установки и хранятся в конфигурационном файле web.xml в элементах <context-param>. Их имена и значения можно получить с помощью методов public Enumeration getInitParameterNames() public String getInitParameter(String name). Кроме строковых параметров в контексте можно определить атрибуты, значениями которых служат объекты любых типов Java. Их имена и значения можно получить с помощью методов public Enumeration getAttributeNames() public Object getAttribute(String name). Файл: 681459229 Создан: 09.07.2007 Модифицирован: 29.04.2016 Автор: Шонин В.А. -5Прикладное программирование в ТС Лекция 3-16 Установить и удалить атрибуты можно с помощью методов public void setAttribute(String name, Object value) public void removeAttribute(String name). С помощью атрибутов удобно сохранять объекты, общие для всего Webприложения, разделяемые всеми сервлетами, входящими в Web-приложение, и независимые от отдельных запросов. Методы public String getServletContextName() public ServletContext getContext(String uripath) позволяют получить имя Web-приложения либо объект типа ServletContext, соответствующий заданному URI. Метод public String getMimeType(String file) позволяет получить MIME-тип для заданного файла в контейнере. Методы public Set getResourcePaths(String path) public String getRealPath(String path) public URL getResource(String path) throws MalformedURLException public InputStream getResourceAsStream(String path) возвращают местонахождение ресурсов Web-приложения в контейнере. Первый метод возвращает все пути, соответствующие заданному в параметре path шаблону. Второй метод возвращает реальный путь к файлу, соответствующий заданному в параметре path виртуальному пути. Третий метод возвращает URL для ресурса, соответствующего параметру path, а четвертый метод возвращает ресурс для заданного параметра path в виде объекта InputStream. Два метода public RequestDispatcher getNamedDispatcher(String name) public RequestDispatcher getRequestDispatcher(String path) возвращают объект RequestDispatcher, который служит оболочкой либо для сервлета с именем name, либо для ресурса, путь к которому задан параметром path. Методы public void log(String msg) public void log(String message, Throwable throwable) записывают сообщения в журнал мониторинга. Первый метод просто записывает строку в журнал, а второй метод, помимо сообщения, выводит стек трассировки ошибок. 3.3.2.4. Интерфейс ServletRequest и классы ServletInputStream и ServletRequestWrapper В интерфейсе ServletRequest, который должен реализовать каждый контейнер сервлетов, описаны методы getXxx(), возвращающих параметры запроса или null, если параметр неизвестен. Так, методы public String getRemoteAddr() public String getRemoteHost() возвращают IP-адрес и имя отправителя запроса, а методы public String getServerName() public int getServerPort() возвращают имя и номер порта сервера, принявшего запрос. Метод public String getScheme() Файл: 681459229 Создан: 09.07.2007 Модифицирован: 29.04.2016 Автор: Шонин В.А. -6Прикладное программирование в ТС Лекция 3-16 возвращает схему запроса (например, "http:"), а метод public String getProtocol() возвращает имя протокола в виде строки, например, "НТТР/1.1". Методы public String getContentType() public int getContentLength() public String getCharacterEncoding() возвращают MIME-тип, длину тела запроса в байтах и кодировку запроса. Имена и значения параметров, пришедших с запросом, можно получить в разных формах (один параметр или все параметры в виде массива строк или отображения) с помощью методов public Enumeration getParameterNames() public String getParameter(String name) public String[] getParameterValues(String name) public Map getParameterMap(). Если у запроса есть какие-либо атрибуты, то их имена и значения можно получить с помощью методов public Enumeration getAttributeNames() public Object getAttribute(String name). Наконец, интерфейс описывает два входных потока для получения тела запроса: байтовый и символьный. Байтовый поток реализуется специально разработанным классом ServletInputStream. Класс – это абстрактный класс, расширяющий класс InputStream. Он добавляет к методам своего суперкласса только один метод public int readLine(byte[] buf, int offset, int length), читающий строку тела запроса в заранее определенный буфер buf. Чтение начинается с байта с номером offset и продолжается до достижения символа перевода строки '\n' или пока не будет прочитано length символов. Метод возвращает число прочитанных байтов или -1, если входной поток уже исчерпан. Получить байтовый поток из запроса можно с помощью метода public ServletInputStream getInputStream() throws IOException. Второй поток – это символьный поток класса BufferedReader из пакета java.io. Получить его можно с помощью метода public BufferedReader getReader()throws IOException. В пакете javax.servlet есть прямая реализация интерфейса ServletRequest – класс ServletRequestWrapper. Объект этого класса создается конструктором public ServletRequestWrapper(ServletRequest req) и реализует все методы интерфейса ServletRequest. Разработчики, желающие расширить возможности объекта, содержащего запрос, или создать фильтр, могут расширить класс ServletRequestWrapper. Методы public Locale getLocale() public Enumeration getLocales() позволяют получить предпочитаемый объект или все объекты из заголовка AcceptLanguage запроса. Метод public boolean isSecure() позволяет проверить, было ли выполнено соединение по безопасному каналу (например, с помощью протокола HTTPS). Файл: 681459229 Создан: 09.07.2007 Модифицирован: 29.04.2016 Автор: Шонин В.А. -7Прикладное программирование в ТС Лекция 3-16 Метод getRequestDispatcher() действует аналогично соответствующему методу интерфейса ServletContext. 3.3.2.5. Интерфейс ServletResponse и классы ServletOutputStream и ServletResponseWrapper Результаты своей работы метод service() интерфейса Servlet заносит в объект типа ServletResponse, ссылка на который предоставлена вторым аргументом этого метода. Методы public void setContentType(String) public void setContentLength(int len) устанавливают в заголовок ответа MIME-тип или длину тела ответа. Методы public Locale getLocale() public void setLocale(Locale loc) позволяют получить или установить объект Locale для ответа, а метод public String getCharacterEncoding() позволяет получить кодировку символов, используемую в теле ответа. Тело ответа передается через байтовый или символьный выходной поток. Методы public int getBufferSize() public void setBufferSize(int size) позволяют получить текущий размер буферной области или установить предпочитаемый размер буферной области, а методы public void flushBuffer() throws IOException public void resetBuffer() public void reset() принудительно сбрасывают содержимое буферной области или очищают буферную область (второй метод очищает буферную область, но оставляет код ответа и заголовки, а третий метод полностью очищает буферную область). Метод public boolean isCommitted() проверяет, передан ли код ответа и заголовки ответа пользователю. Байтовый поток специально разработанного класса ServletOutputStream возвращает метод public ServletOutputStream getOutputStream() throws IOException. Абстрактный класс ServletOutputStream расширяет класс OutputStream, добавляя к нему методы print(xxx) для вывода типов boolean, char, int, long, float, double и String, а также методы println(xxx) для тех же типов, добавляющие к выводимым данным символы "\r\n". Еще один метод println() без аргументов просто заносит в выходной поток символы "\r\n". Символьный поток можно получить с помощью метода public PrintWriter getWriter() throws IOException. В пакете javax.servlet есть прямая реализация интерфейса ServletResponse – класс ServletResponseWrapper. Объект этого класса создается конструктором public ServletResponseWrapper(ServletResponse resp) Файл: 681459229 Создан: 09.07.2007 Модифицирован: 29.04.2016 Автор: Шонин В.А. -8Прикладное программирование в ТС Лекция 3-16 и обладает всеми методами интерфейса ServletResponse. Разработчики, желающие расширить возможности объекта, содержащего ответ, например, для создания фильтра, могут расширить класс ServletResponseWrapper. 3.3.2.6. Интерфейс RequestDispatcher В некоторых случаях необходимо обратиться к другому сервлету, странице JSP, документу HTML, XML или другому ресурсу. Если требуемый ресурс находится в том же контексте, что и сервлет, который его вызывает, то для получения ресурса надо обратиться к описанному выше методу getRequestDispatcher(), например: RequestDispatcher rd = req.getRequestDispatcher("CourseServlet"). Если же ресурс находится в другом контексте, то надо сначала получить контекст методом getContext() интерфейса ServletContext, и уже затем воспользоваться методом getRequestDispatcher(), например: RequestDispatcher rd = conf.getServletContext(); getContext("/product").getRequestDispatcher( "/product/servlet/CourseServlet" ); Интерфейс RequestDispatcher определяет объект, который принимает запросы от клиента и пересылает их любому ресурсу (например, документу HTML, файлу или странице JSP) В этом интерфейсе определены два метода public void forward(ServletRequest, ServletResponse response) throws ServletException, IOException public void include(ServletRequest request, ServletResponse response) throws ServletException, IOException. Первый метод пересылает от объекта request, представляющему запрос клиента другому объекту response, представляющему ответ, возвращаемый сервлетом клиенту, например: if (rd != null) rd.forward(req, resp); else r esp.sendError(HttpServletResponse.SC_NO_CONTENT); Вызывающий сервлет не должен выполнять какую-либо отправку клиенту до обращения к методу forward(), иначе будет выброшено исключение класса IllegalStateException. Если же вызывающий сервлет уже что-то отправлял клиенту, то следует обратиться ко второму методу Этот метод вызывает ресурс, который на основании объекта request может изменить тело объекта response. Но вызванный ресурс не может изменить заголовки и код ответа объекта response. Это естественное ограничение, поскольку вызывающий сервлет мог уже отправить заголовки клиенту. Попытка вызванного ресурса изменить заголовок будет просто проигнорирована контейнером. 3.3.2.7. Интерфейс ServletContextAttributeListener и классы ServletContextEvent и ServletContextAttributeEvent Интерфейс ServletContextAttributeListener содержит методы public void Файл: 681459229 Создан: 09.07.2007 Модифицирован: 29.04.2016 Автор: Шонин В.А. -9Прикладное программирование в ТС Лекция 3-16 attributeAdded(ServletContextAttributeEvent scab) public void attributeReplaced(ServletContextAttributeEvent scab) public void attributeRemoved(ServletContextAttributeEvent scab) уведомляющие, что атрибут добавлен к контексту сервлета, либо атрибут замещен в контексте сервлета, либо атрибут удален из контекста сервлета. Класс ServletContextEvent является классом событий для уведомления об изменениях в контексте сервлета. Конструктор класса public (ServletContext source) создает событие типа для заданного контекста сервлета. Единственный метод этого класса public ServletContext getServletContext() возвращает измененный контекст сервлета. Класс ServletContextAttributeEvent, расширяющий класс ServletContextEvent, уведомляет о событиях изменения атрибутов в контексте сервлета. Конструктор public ServletContextAttributeEvent(ServletContext source, String name, Object value) создает новый объект для заданного контекста source, имени атрибута name и значения атрибута value. Методы класса public String getName() public Object getValue() позволяют получить имя измененного атрибута или его значение. 3.3.2.8. Интерфейсы Filter, FilterChain и FilterConfig Суть работы сервлета заключается в обработке полученного из объекта типа ServletRequest запроса и формировании ответа в виде объекта типа ServletResponse. Кроме этого сервлет может также проделывать работу по созданию объектов, обращения к их методам, организации доступа к файлам и базам данных. Эти действия усложняют изначально простую и четкую структуру сервлета. Чтобы придать стройность и упорядоченность сервлету, можно организовать цепочку фильтров — объектов, последовательно пропускающих через себя информацию, идущую от запроса к ответу, и преобразующих ее. Удобство фильтров заключается еще и в том, что один фильтр может использоваться несколькими сервлетами и даже всеми ресурсами Web-приложения. У разработчика есть возможность подготовить набор фильтров на все случаи жизни и применять их в своих сервлетах. При работе с фильтрами сразу создается цепочка фильтров, даже если в нее входит всего один фильтр. Вся работа с цепочками фильтров описывается интерфейсами Filter, FilterChain и FilterConfig. Интерфейс Filter реализуется разработчиком приложения, остальные интерфейсы должен реализовать контейнер сервлетов. Каждый фильтр в цепочке – это объект типа Filter. Структура этого объекта напоминает структуру сервлета. Интерфейс Filter описывает три метода: public void init(FilterConfig filterConfig) throws ServletException Файл: 681459229 Создан: 09.07.2007 Модифицирован: 29.04.2016 Автор: Шонин В.А. - 10 Прикладное программирование в ТС Лекция 3-16 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException public void destroy(). Как видно из этих определений, фильтр может выполнить начальные действия с помощью метода init(), причем ему передается созданный контейнером объект типа FilterConfig, который очень похож на объект типа ServletConfig. Он тоже содержит начальные параметры, которые можно получить с помощью методов public Enumeration getInitParameterNames() public String getInitParameter(String name). интерфейса FilterConfig. Остальные два метода этого интерфейса: public String getServletName() public ServletContext getServletContext(String name) позволяют получить имя или конфигурацию сервлета. Вся фильтрация производится методом doFilter(), который получает объекты request и response, изменяет их и передает управление следующему фильтру в цепочке с помощью параметра chain. Организует цепочку фильтров интерфейс FilterChain, который описывает только один метод public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException. Для того чтобы передать управление следующему фильтру в цепочке, фильтр должен просто обратиться к этому методу, передав ему измененные объекты request и response. Пример использования фильтров в сервлете: Параметры запроса идут от браузера чаще всего в MIME-типе application/xwww-form-urlencoded, использующем байтовую кодировку, принятую по умолчанию на машине клиента. Эта кодировка должна указываться в заголовке Content-Type, например: Content-Type:application/x-www-form-urlencoded; charset=windows-1251 Как правило, браузер не посылает этот заголовок Web-серверу. В таком случае возникает необходимость определить кодировку параметров запроса, чтобы правильно перевести значение параметра в Unicode. Программа, выполняющая эту задачу, приведена ниже: import java.io.*; import javax.servlet.*; public class SetCharEncFilter implements Filter { protected String enc; protected FilterConfig fc; public void init(FilterConfig conf) throws ServletException { fc = conf; enc = conf.getInitParameter("encoding"); } public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { String encoding = selectEncoding(req); Файл: 681459229 Создан: 09.07.2007 Модифицирован: 29.04.2016 Автор: Шонин В.А. - 11 Прикладное программирование в ТС Лекция 3-16 if (encoding != null) req.setCharacterEncoding(encoding); chain.doFilter(req, resp); } protected String selectEncoding(ServletRequest req) { String charEncoding = getCharsetFromContentType(req.getContentType()); return (charEncoding == null) ? enc : charEncoding; } public static String getCharsetFromContentType(String type) { if (type == null) { return null; } int semi = type.indexOf(";"); if (semi == -1) { return null; } String afterSemi = type.substring(semi + 1); int charsetLocation = afterSemi.indexOf("charset="); if (charsetLocation == -1) { return null; } String afterCharset = afterSemi.substring(charsetLocation + 8); String encoding = afterCharset.trim(); return encoding; } public void destroy() { enc = null; fc = null; } } После того как класс-фильтр написан и откомпилирован, его надо установить в контейнер и приписать (map) к одному или нескольким сервлетам. Это выполняется утилитой установки, в которой указывается имя фильтра, его начальные параметры и сервлет, к которому приписывается фильтр. Утилита установки заносит сведения о фильтре в конфигурационный файл web.xml в элемент <filter>. Эту операцию можно выполнить и вручную. Приписка фильтра к сервлету отмечается внутри элемента <filtermapping> парой вложенных элементов <filter-name> и <servlet-name>. Например: <filter-mapping> <filter-name>MyFilter</filter-name> <servlet-name>RegServlet</servlet-name> </filter-mapping> Фильтр можно приписать не только к сервлетам, но и к другим ресурсам. Для этого записывается элемент <url-pattern>, например, после строк в web.xml <filter-mapping> Файл: 681459229 Создан: 09.07.2007 Модифицирован: 29.04.2016 Автор: Шонин В.А. - 12 Прикладное программирование в ТС Лекция 3-16 <filter-name>MyFilter</filter-name> <url-pattern>*.html</url-pattern> </filter-mapping> фильтр будет применен ко всем вызовам документов HTML. Порядок фильтров в цепочке соответствует порядку элементов <filtermapping> в конфигурационном файле web.xml. 3.3.2.9. Класс GenericServlet Абстрактный класс реализует интерфейсы Servlet и ServletConfig. Этот класс, кроме реализации методов обоих интерфейсов, имеет пустой метод init() без аргументов. Этот метод выполняется автоматически после метода init(ServletConfig config), точнее говоря, последний метод реализован так: public void init(ServletConfig config) throws ServletException { this.config = config; log("init"); this.init(); } поэтому удобно всю инициализацию записывать в метод init() без аргументов, не заботясь о вызове super.init(ServletConfig config) . Класс GenericServlet оставляет нереализованным только метод service(). Удобно создавать сервлеты, расширяя этот класс и переопределяя только метод service(). Этот класс не зависит от используемого протокола обмена и является суперклассом для всех классов сервлетов, реализующих конкретные протоколы обмена. 3.3.2.10. Классы ServletException и UnavailableException Класс ServletException определяет общее исключение, которое может бросить сервлет при возникновении ошибок во время выполнения. Наиболее общим конструктором этого класса является конструктор public ServletException(String message, Throwable rootCause). где строка message содержит текст выводимого сообщения, а параметр rootCause залает исключение, которое, взаимодействуя с нормальной операцией, вызвало исключение ServletException. Два следующий конструктора определяют отдельно параметры message и rootCause, а третьим является пустой конструктор. Единственный метод класса public Throwable getRootCause() позволяет получить прерывание, ставшее причиной прерывания сервлета. Класс UnavailableException, расширяющий класс, определяет исключение, которое бросает сервлет или фильтр, когда он постоянно или временно недоступен. Для этого класса определены два конструктора public UnavailableException(String msg) public UnavailableException(String msg, int seconds) Первый создает объект с заданным текстом сообщения msg, а второй дополнительно задает оценку, сколько секунд seconds сервлет будет временно недоступен. Метод public boolean isPermanent() Файл: 681459229 Создан: 09.07.2007 Модифицирован: 29.04.2016 Автор: Шонин В.А. - 13 Прикладное программирование в ТС Лекция 3-16 проверяет, недоступен сервлет постоянно или нет, а метод public int getUnavailableSeconds() возвращает число секунд, в течение которых сервлет предполагается временно недоступным. 3.3.3. Средства для работы с сервлетами по протоколу HTTP в Java Большинство запросов к сервлетам происходит по протоколу HTTP под управлением Web-сервера. Средства для работы сервлетов с этим протоколом собраны в пакете javax.servlet.http. 3.3.3.1. Интерфейс HttpServletRequest и класс HttpServletRequestWrapper Интерфейс расширяет интерфейс ServletRequest методами анализа запросов HTTP, и, помимо методов ServletRequest, содержит свои собственные методы. Запрос HTTP начинается с одного из методов (GET, POST, или другого метода). Определить метод передачи позволяет метод интерфейса public String getMethod(). Далее в запросе, после пробела, идет адрес URI, который разбирается несколькими методами. Идентификатор URI возвращает метод public String getRequestURI(), а URL возвращает метод public StringBuffer getRequestURL(). Часть URL после вопросительного знака извлекается методом public String getQueryString(). Часть этого адреса, показывающая путь к сервлету, получается с помощью метода public String getServletPath(). Часть пути, определяющая контекст сервлета, возвращается методом public String getContextPath(). После имени сервлета может идти дополнительный путь к какому-нибудь файлу, который можно получить методом public String getPathInfo(). Этот же путь, дополненный до абсолютного пути к каталогу документов сервера, можно извлечь методом public String getPathTranslated(). После первой строки запроса могут идти заголовки запроса. Получить списки имен и значений заголовков запроса можно с помощью методов public Enumeration getHeaderNames() public Enumeration getHeaders(String name), а получить для отдельного заголовка его строковое значение, числовое значение или значение в виде даты можно с помощью методов public String getHeader(String name) public int getIntHeader(String name) public long getDateHeader(String name). Наконец, можно получить cookie, хранящиеся в Web-браузере клиента, в виде массива объектов класса Cookie с помощью метода public Cookie[] getCookies(). Использование одной из схем аутентификации для клиента реализуется с помощью методов public String getAuthType() public String getRemoteUser() public Principal getUserPrincipal() Файл: 681459229 Создан: 09.07.2007 Модифицирован: 29.04.2016 Автор: Шонин В.А. - 14 Прикладное программирование в ТС Лекция 3-16 public boolean isUserInRole(String role). Первый метод позволяет получить тип аутентификации, а второй и третий методы – имя удаленного пользователя (в виде строки или объекта Principal). Четвертый метод проверяет, включен ли аутентифицированный пользователь в какую-либо логическую роль. Другая группа методов связана с организацией сеанса. Так, методы public HttpSession getSession() public HttpSession getSession(boolean create) возвращают текущий сеанс, связанный с данным запросом или создают новый сеанс. Новый сеанс при использовании второго метода создается только в том случае, если значение параметра create равно true. Метод public String getRequestedSessionId() возвращает идентификатор текущего сеанса для запроса, а методы public boolean isRequestedSessionIdFromCookie() public boolean isRequestedSessionIdFromURL проверяют, относится ли текущий идентификатор сеанса к cookie или к URL. В пакете javax.servlet.http определен класс HttpServletRequestWrapper, который является прямой реализацией интерфейса HttpServletRequest и расширяет класс ServletRequestWrapper. Объект этого класса создается конструктором public HttpServletRequestWrapper(HttpServletRequest req) Класс HttpServletRequestWrapper реализует все методы интерфейса HttpServletRequest. Для получения дополнительных возможностей объекта, содержащего запрос HTTP, например, для написания фильтра, можно расширить класс HttpServletRequestWrapper. 3.3.3.2. Интерфейс HttpServletResponse и класс HttpServletResponseWrapper При составлении ответа по протоколу HTTP можно использовать дополнительные методы, включенные в интерфейс HttpServletResponse, расширяющий интерфейс ServletResponse. Метод public void setHeader(String name, String value) устанавливает заголовок ответа с именем name и значением value. Старое значение, если оно существовало, при этом стирается. Если надо дать несколько значений заголовку с именем name, то следует воспользоваться методом public void addHeader(String name, String value). Для заголовков с целочисленными значениями то же самое делается методами public void setIntHeader(String name, int value); public void addIntHeader(String name, int value). Заголовок с датой записывается методами public void setDateHeader(String name, long date); public void addDateHeader(String name, long date). Проверить, послан ли заголовок с именем name, можно с помощью метода public boolean containsHeader(String name). Код ответа (status code) устанавливается с использованием метода public void setStatus(int sc). Параметр этого метода sc – одна из множества констант, например, константа SC_OK, соответствующая коду ответа 200 (успешная обработка запроса), SC_BAD_REQUEST – код ответа 400 (синтаксическая ошибка в запросе клиента) и т. д. Файл: 681459229 Создан: 09.07.2007 Модифицирован: 29.04.2016 Автор: Шонин В.А. - 15 Прикладное программирование в ТС Лекция 3-16 В интерфейсе HttpServletRequest определено около сорока таких констант с модификаторами public static final int. Обычно код ответа заносится в сообщение об ошибке message, формируемое с помощью метода public void sendError(int sc, String message). Если надо послать стандартное сообщение об ошибке, например, "404 Not Found", то применяется метод public void sendError(int sc). Наконец, к запросу можно добавить cookie с помощью метода public void addCookie(Cookie cookie). Метод public void sendRedirect(String location) throws IOException посылает ответ на перенаправление запроса по заданному URL с именем location, а методы public String encodeURL(String url) public String encodeRedirectURL(String url) соответственно кодируют заданный URL с включением в него идентификатора сеанса или кодируют URL для перенаправления его с помощью метода sendRedirect(). В пакете javax.servlet.http есть прямая реализация интерфейса HttpServletResponse – класс HttpServletResponseWrapper. Этот класс расширяет класс ServletResponseWrapper в пакете javax.servlet. Объект класса HttpServletResponseWrapper создается конструктором Public HttpServletResponseWrapper( HttpServletResponse response). Класс реализует все методы интерфейса HttpServletResponse, поэтому для получения дополнительных возможностей объекта, содержащего запрос, следует создать класс, расширяющий класс HttpServletResponseWrapper. 3.3.3.3. Класс HttpServlet Для использования особенностей протокола HTTP класс GenericServlet расширен абстрактным классом HttpServlet. Главная особенность этого класса заключается в том, что, расширяя его, не надо переопределять метод service(). В начале метод public void service(ServletRequest req, ServletResponse resp) throws IOException анализирует типы аргументов req и resp. Эти типы должны быть на самом деле HttpServletRequest и HttpServletResponse. Если это не так, то метод выбрасывает исключение класса ServletException и завершается. Если аргументы req и resp подходящего типа, то с помощью метода getMethod() класса HttpServletRequestWrapper определяется метод HTTP передачи данных (если он не известен) и вызывается один из методов: protected void doXxx(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException, соответствующий этому методу. Значение Xxx должно быть одним из следующих значений: Get, Post, Put, Delete, Options или Trace. Методы doHead(), doOptions() и doTrace() уже реализованы и их редко приходится переопределять. Остальные методы просто посылают сообщение о том, что Файл: 681459229 Создан: 09.07.2007 Модифицирован: 29.04.2016 Автор: Шонин В.А. - 16 Прикладное программирование в ТС Лекция 3-16 они не реализованы. Чаще всего приходится переопределять методы doGet() и doPost(). Еще один метод protected long getLastModified(HttpServletRequest req) определяет время последней модификации файла (в миллисекундах с 1 января 1970 года). 3.3.4. Организация сеанса связи с сервлетом Сервер HTTP не сохраняет информацию о клиенте, связавшемся с ним. Хотя TCPсоединение может сохраняться вплоть до его явного закрытия (persistent HTTP connection) и за это время можно передать несколько запросов и ответов, протокол HTTP не имеет средств сохранения информации о клиенте. Многие задачи, решаемые Web-приложениями, требуют знания сведений о клиенте и поэтому обычно применяют средства, не входящие в протокол HTTP. Наиболее часто используются три средства: cookie, параметр в строке URL, и скрытое поле в HTMLформе. 3.3.4.1. Получение сведений о клиенте Принцип действия cookie следующий. Получив первый запрос, сервер составляет заголовок ответа set-cookie, в который заносит пару «имя-значение», обычно это идентификатор клиента, а также диапазон URL, для которого действует cookie, срок хранения этих сведений и другую информацию. Web-браузер, получив ответ с таким заголовком, создает небольшой, размером не более 4 Кбайт, файл cookie с этими сведениями, и сохраняет его у себя в каталоге. Посылая следующие запросы, браузер отыскивает у себя соответствующий файл cookie и заносит в заголовок cookie запроса пару «имя-значение». По этому заголовку сервер узнает клиента. В пакете javax.servlet.http есть класс Cookie, методы которого обеспечивают работу с cookie. Объект этого класса создается конструктором public Cookie(String name, String value). Метод public String getName() позволяет получить имя cookie. Получение и установка значения выполняется с помощью методов public String getValue() public void setValue(String newValue). Методы public int getVersion() public String getPath() public int getMaxAge() public String getDomain() public String getComment() позволят получить значение версии используемого протокола, путь на сервере к cookie, максимальное время жизни cookie (в секундах), набор доменных имен для cookie и комментарий, описывающий назначение данного, а методы public void setVersion(int v) public void setPath(String uri) public void setMaxAge(int expiry) public void setDomain(String pattern) public void setComment(String purpose) устанавливают перечисленные выше значения для cookie. Метод public Object clone() Файл: 681459229 Создан: 09.07.2007 Модифицирован: 29.04.2016 Автор: Шонин В.А. - 17 Прикладное программирование в ТС Лекция 3-16 возвращает копию данного cookie, а методы public boolean getSecure() public void setSecure(boolean flag) проверяют, передавался ли данный cookie с помощью протокола HTTPS или устанавливают передачу cookie по закрытому протоколу. После создания и формирования объекта cookie этот объект устанавливается в заголовок ответа методом addCookie() интерфейса HttpServletResponse. Пример создания cookie и отправки его клиенту: String value = "" + id; Cookie ck = new Cookie("studentid", value); // Cookie будет существовать полгода ck.setMaxAge(60*60*24*183); resp.addCookie(ck); Прочитать cookie из запроса клиента можно с помощью метода getCookies() интерфейса HttpServletRequest. Пример чтения cookie: int id = 0; Cookie[] cks = req.getCookies(); if (cks != null) for (int i = 0; i < cks.length; i++) if (cks[i].getName().equals("studentid")) { id = Integer.parseInt(cks[i].getValue()); break; } Использовать cookie удобно, но многие клиенты запрещают запись файлов cookie на свой жесткий диск. Второе средство – параметр в строке URL (rewriting URL) — просто записывает в первую строку запроса после имени ресурса идентификатор клиента, например: GET /some.com/InfoAppl/index.html?jid=12345678 HTTP/1.1 Это тоже хороший способ, но клиент может сам формировать строку запроса, например, просто набирая ее в поле адреса браузера и забывая об идентификаторе. Третье средство – использование скрытых полей формы HTML, например: <input type="hidden" name="studentid" value="12345678">. Для применения этого средства надо на каждой странице HTML создавать форму. В пакет javax.servlet.http внесены интерфейсы и классы для облегчения распознавания клиента. Они автоматически переключаются с одного средства на другое. Если запрещены cookie, то формируется идентификатор клиента в строке URL и т. д. 3.3.4.2. Средства связи с клиентом в пакете javax.servlet.http Основу средств создания сеанса связи с клиентом составляет интерфейс HttpSession. Объект типа HttpSession создается контейнером сервлета при получении запроса, а получить или создать его можно с помощью одного из методов getSession(), определенного в интерфейсе HttpServletRequest. Метод public ServletContext getServletContext() возвращает контекст сервлета, к которому принадлежит данный сеанс. С помощью метода public boolean isNew() интерфейса HttpSession можно узнать, новый ли это, только что созданный сеанс, или продолжающийся, уже запрошенный клиентом. Файл: 681459229 Создан: 09.07.2007 Модифицирован: 29.04.2016 Автор: Шонин В.А. - 18 Прикладное программирование в ТС Лекция 3-16 В сеансе отмечается время его создания и время последнего запроса, которые можно получить с помощью методов public long getCreationTime() public long getLastAccessedTime(). Они возвращают время в миллисекундах, прошедших с 1 января 1970 года. Создавая сеанс, контейнер дает ему уникальный идентификатор. Метод public String getId() возвращает идентификатор сеанса в виде строки. У сеанса могут быть атрибуты, любые объекты Java, которые являются удобным средством для хранения объектов, которые должны существовать на протяжении сеанса. Атрибут задается с помощью метода public void setAttribute(String name, Object value). Получить имена и значения атрибутов можно с помощью методов public Enumeration getAttributeNames() public Object getAttribute(String name), а удалить – с помощью метода public void removeAttribute(String name). Сеанс завершается вызовом метода public void invalidate() или по истечении времени ожидания очередного запроса. Это время, в секундах, задается методом public void setMaxInactiveInterval(int secs). Сервлет может узнать назначенное время ожидания с помощью метода public int getMaxInactiveInterval(). Экземпляр класса HttpSessionEvent создается при всяком изменении в активных сеансах Web-приложения, например, создании сеанса, прекращении сеанса, истечении срока ожидания запроса. Конструктор public HttpSessionEvent(HttpSession source) создает событие сеанса для заданного источника source, а единственный метод public HttpSession getSession() позволяет получить сеанс, в котором произошло событие. Блок прослушивания для события HttpSessionEvent реализуется с помощью интерфейса HttpSessionListener, в котором определены два метода public void sessionCreated(HttpSessionEvent se) public void sessionDestroyed(HttpSessionEvent se) соответственно для создания и завершения сеанса. Создание или удаление атрибута, изменение значения атрибута являются событиями класса HttpSessionBindingEvent. Этот класс является подклассом класса HttpSessionEvent. Объект этого класса создается одним из двух конструкторов public HttpSessionBindingEvent(HttpSession session, String name) public HttpSessionBindingEvent(HttpSession session, String name, Object value) Конструкторы создают событие, которое уведомляет, что объект с именем name будет связан, или, наоборот, не связан с сеансом session. Второй конструктор дополнительно задает значение объекта. Файл: 681459229 Создан: 09.07.2007 Модифицирован: 29.04.2016 Автор: Шонин В.А. - 19 Прикладное программирование в ТС Лекция 3-16 Методы public HttpSession getSession() public String getName() public Object getValue() класса HttpSessionBindingEvent позволяют получить значение сеанса (объект HttpSession), имя объекта и его значение. Блок прослушивания для события HttpSessionBindingEvent реализуется с помощью интерфейса HttpSessionBindingListener, в котором определены два метода public void valueBound(HttpSessionBindingEvent event) public void valueUnbound(HttpSessionBindingEvent event) соответственно для связывания объекта с сеансом и его идентификацией или разрыва связи объекта с сеансом. Объекты, связанные с сеансом, могут прослушивать события контейнера, уведомляющие, что сеанс переходит в пассивное состояние или, наоборот, переходит в активное состояние. Для этого используется блок прослушивания, реализованный в интерфейсе HttpSessionActivationListener. Два метода этого интерфейса public void sessionDidActivate(HttpSessionEvent se) public void sessionWillPassivate(HttpSessionEvent se) уведомляют, что сеанс только что перешел в активное состояние или готовится перейти в пассивное состояние. Для получения изменений в списке атрибутов, связанных с данным сеансом, используется блок прослушивания, реализованный в интерфейсе HttpSessionAttributeListener. Его три метода public void attributeAdded(HttpSessionBindingEvent se) public void attributeReplaced(HttpSessionBindingEvent se) public void attributeRemoved(HttpSessionBindingEvent se) должны быть заданы для добавления атрибута в сеанс, замене атрибута в сеансе и удалении атрибута из сеанса. Файл: 681459229 Создан: 09.07.2007 Модифицирован: 29.04.2016 Автор: Шонин В.А.