Servlet 2.3: Новые возможности раскрыты
( Джасон Хантер )
Полный update последней спецификации Servlet API.
Обзор
В октябре 2000, Sun выпустила в свет "Proposed Final Draft" спецификации Servlet API 2.3. Эта статья объясняет различия между Servlet API 2.2 и 2.3, обсуждает причины этих изменений, и показывает, как писать сервлеты (а теперь еще и фильтры (filters)!) используя 2.3. (В оригинальной версии на английском языке 4,000 слов)
- Сервлеты в J2SE и J2EE
- Фильтры
- События времени жизни
- Выбор кодировки символов
- JAR зависимости
- Загрузчики классов (Class loaders)
- Новые атрибуты ошибок.
- Новые возможности системы безопасности
- Мелкие доработки
- Преобразование DTD
- Заключение
- Об авторе
- Ресурсы
20 Октября, 2000, Sun Microsystems опубликовал "Proposed Final Draft" спецификации Servlet API 2.3 (Смотрите ссылку на официальную спецификацию Ресурсы) Хотя спецификация была опубликована Sun, Servlet API 2.3 в действительности разрабатывался многими независимыми разработчиками и компаниями, работающими в экспертной группе JSR-053, согласуясь с Java Community Process (JCP) 2.0. Дэни Ковард (Danny Coward) из Sun Microsystems возглавляет экспертную группу по сервлетам.
Спецификация не полностью закончена. "Proposed Final Draft" — в одном шаге от официального Final Release, и технические детали еще меняются. Однако эти изменения не должны быть значительными. Фактически, компании, разработчики серверов, уже начали реализацию новых возможностей. Это значит, что пришло время начать изучать то, что придет вместе с Servlet API 2.3.
В этой статье, я опишу в деталях все, что изменилось в API 2.3 по сравнению с API 2.2. Я также объясню причины для этих изменений и продемонстрирую, как писать сервлеты, используя новые возможности. Чтобы не терять направленность статьи, я буду предполагать, что вы знакомы с классами и методами предыдущей версии Servlet API. Если нет, вы можете предварительно обратиться к ресурсам , где найдете ссылки на сайты ( и мою новую книгу!), которые помогут вам набрать скорость в их освоении.
Servlet API 2.3 , в действительности, оставляет ядро сервлетов относительно нетронутым, что показывает — сервлеты уже достигли высокого уровня завершенности. Большинство изменений было связано с добавлением новых возможностей вне ядра. В числе этих изменений:
- Сервлетам требуется наличие JDK 1.2 или выше
- Создан механизм фильтров (наконец!)
- Добавлены события времени жизни приложения
- Добавлена новая поддержка internationalization
- Формализована техника обработки внутренних зависимостей JAR
- Правила загрузки классов стали прозрачней
- Добавлены новые атрибуты ошибок и безопасности
- Класс HttpUtils признан устаревшим
- Добавлено множество новых полезных методов
- Расширены и упрощены некоторые DTD
Были сделаны и другие улучшения, но они главным образом касаются компаний разработчиков серверов, не обычных разработчиков сервлетов (за исключением факта, что программисты почувствуют улучшенную переносимость). Эти детали я опущу.
Перед тем как я начну свой обзор, позвольте обратить внимание на тот факт, что версия 2.3 была выпущена только как проект спецификации. Большинство возможностей, которые будут затронуты в этой статье, еще не работают на всех серверах. Если вы хотите проверить эти возможности, я рекомендую взять эталонную реализацию сервера, Apache Tomcat 4.0. Это открытый исходный код и вы можете взять сервер бесплатно. Tomcat 4.0 в настоящий момент в beta версии. Его поддержка API 2.3 постоянно улучшается, но все еще не завершена. Прочитайте файл NEW_SPECS.txt, который идет вместе с Tomcat 4.0, чтобы изучить в какой степени он поддерживает новые возможности спецификации. (Для получения большей информации о Tomcat смотрите Ресурсы .)
Сервлеты в J2SE и J2EE
Одна из главных деталей Servlet API 2.3, которую вы должны себе отметить, — сервлеты теперь находятся в зависимости от Java 2 Platform, Standard Edition 1.2 (также известную как J2SE 1.2 или JDK 1.2). Это маленькое, но важное изменение означает, что вы можете теперь использовать возможности J2SE 1.2 в ваших сервлетах и быть уверенными, что сервлеты будут работать во всех сервлет-контейнерах. Раньше, вы могли использовать возможности J2SE 1.2, но сервера могли не поддерживать их.
Servlet API 2.3 будет частью ядра Java 2 Platform, Enterprise Edition 1.3 (J2EE 1.3). Предыдущая версия, Servlet API 2.2, была частью J2EE 1.2. Единственное заметное различие — добавление нескольких несовсем понятных тэгов DTD, относящихся к J2EE, в файл web.xml: <resource-env-ref> для поддержки "администриуемых объектов", так как они требуются Java Messaging System (JMS); <res-ref-sharing-scope> для возможности как совместного, так и эксклюзивного доступа к ресурсам и, <run-as> для определения системой безопасности подлинности клиента, обращающегося к EJB. Большинство разработчиков сервлетов не нуждаются в использовании этих тэгов J2EE. Вы можете найти их полное описание в спецификации J2EE 1.3.
Фильтры
Наиболее значительная часть изменений API 2.3 — фильтры (filters) — объекты, которые могут преобразовывать запрос (request) или изменять ответ (response). Фильтры — не сервлеты. В действительности, они не создают ответ (response). Они — препроцессоры запроса, перед тем как он достигнет сервлет, и/или постпроцессоры ответа, отправляемого сервлетом. В известном смысле, фильтры более совершенная версия старой концепции сервлетов "servlet chaining" (связывание сервлетов). Фильтр может:
- перехватить обращение к сервлету в момент его вызова,
- проверить запрос перед вызовом сервлета,
- изменить заголовок запроса и данные запроса, подавая измененную версию объекта запроса (request), которая скрывает реальный запрос,
- изменить заголовок ответа и данные ответа, подавая измененную версию объекта ответа (response), который скрывает реальный ответ,
- перехватить обращение к сервлету после его вызова.
Вы можете сконфигурировать фильтр так, чтобы он действовал на один сервлет или группу сервлетов. Этот сервлет или группа могут не фильтроваться или фильтроваться несколькими фильтрами. Практические идеи использования фильтров: фильтры аутентификации, журналирующие и аудирующие фильтры, фильтры конверторы изображений, фильтры сжатия данных, фильтры кодировщики, фильтры анализаторы текста, фильтры приводящие в действия события доступа к ресурсам, XSLT фильтры, которые преобразуют содержание XML, или MIME-type связывающие фильтры (точно как связывание сервлетов).
Фильтр реализует javax.servlet.Filter и определяет его три метода:
- void setFilterConfig(FilterConfig config) : назначает фильтру конфигурационный объект
- FilterConfig getFilterConfig() : возвращает конфигурационный объект фильтра
- void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) : выполняет конкретные действия фильтра
Для подготовки фильтра к работе, сервер единожды вызывает setFilterConfig(), затем вызывает doFilter() любое количество раз, для различных запросов. Интерфейс FilterConfig имеет методы для получения имени фильтра, его параметров инициализации, и активного контекста сервлета. сервер передает null в setFilterConfig() , для указания, что фильтр отключается.
Каждый фильтр принимает в свой метод doFilter() текущий запрос(request) и ответ (response), также как и FilterChain, содержащий фильтры, которые должны быть выполнены. В методе doFilter(), фильтр может делать все что угодно с запросом(request) и ответом (response). (Он может обрабытывать данные сервлетов, вызывая их методы, или прятать объекты, изменяя их поведение, что будет обсуждаться далее). Для предачи контроля в следующий фильтр, фильтр вызывает chain.doFilter(). После завершения этого вызова фильтр может в конце своего собственного метода doFilter(), выполнить дополнительную работу над ответом (response). Например, он может занести в журнал информацию об ответе (response). Если в фильтре надо остановить выполнение запроса (request) и получить полный контроль над ответом (response), он может умеренно не вызывать следующий фильтр.
Фильтр может скрывать объекты запроса (request) и/или ответа (response) для обеспечения стандартного поведения приложения, изменяя истинную реализацию вызова метода, чтобы влиять на управляющие действия более поздних вызовов. Для этой цели API 2.3 содержит новые классы HttpServletRequestWrapper и HttpServletResponseWrapper. Они обеспечивают реализацию по умолчанию всех методов запроса (request) и ответа (response), и делегируют вызовы подлинному запросу(request) или ответу (response) по- умолчанию. Это значит, что изменение поведения одного метода требует только расширения wrapper и повторной реализации одного метода. Wrapper-ы дают фильтрам возможность обширного контроля над запросами и процессом генерации ответа. Ниже показан исходный текст простого журналирующего фильтра, который записывает продолжительность всех запросов:
public class LogFilter implements Filter {
FilterConfig config;
public void setFilterConfig(FilterConfig config) {
this.config = config;
}
public FilterConfig getFilterConfig() {
return config;
}
public void doFilter(ServletRequest req,
ServletResponse res,
FilterChain chain) {
ServletContext context =
getFilterConfig().getServletContext();
long bef = System.currentTimeMillis();
// никакие параметры связывания здесь не нужны
chain.doFilter(req, res);
long aft = System.currentTimeMillis();
context.log("Request to " +
req.getRequestURI() + ": "
+ (aft-bef));
}
}
Когда сервер вызывает setFilterConfig(), фильтр сохраняет ссылку на config в своей переменной , которая позже используется в методе doFilter() для получения ServletContext. Логика doFilter() проста — занести в журнал время, занимаемое запросом. Для использования этого фильтра, вы должны объявить в web.xml дескриптор развертывания, используя тег <filter>, как показано ниже
<filter> <filter-name> log </filter-name> <filter-class> LogFilter </filter-class> </filter>
Теперь сервер узнает, что фильтр, называемый log, реализован в классе LogFilter. Вы можете использовать зарегистрированный фильтр для определенных шаблонов URL или имен сервлетов, используя тэг <filter-mapping>:
<filter-mapping> <filter-name>log</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
С такой конфигурацией, фильтр будет работать со всеми запросами к серверу (статическими и динамическими). Это именно то, что мы хотим для нашего журналирующего фильтра. При соединении с простой страницей, запись в журнал может выглядеть так:
Request to /index.jsp: 10
События времени жизни
Второе из наиболее значительных изменений Servlet API 2.3 — добавление событий времени жизни приложения, которые позволяют объектам "слушателям"(listeners) знать, когда контексты сервлетов и сессии инициализированы или уничтожены, а также как когда атрибуты добавлены или удалены из контекста или сессии.
События времени жизни сервлета работают подобно событиям в Swing. Любой слушатель, заинтересованный в наблюдении за временем жизни ServletContext может реализовывать интерфейс ServletContextListener. Интерфейс имеет два метода:
- void contextInitialized(ServletContextEvent e) :вызываемый, когда Web приложение стало готовым к обработке запросов, то есть при старте Web сервера, добавлении или перегрузки контекста. Запросы не будут обрабатываться, пока не выполнился этот метод.
- void contextDestroyed(ServletContextEvent e) :вызываемый в момент, когда Web приложение почти завершило работу, то есть при выключении Web сервера, удалении, перегрузки контекста. Выполнение останавливается перед тем, как этот метод вызван.
Класс ServletContextEvent, передаваемый этим методам, имеет только метод getServletContext(), возвращающий инициализируемый или удаляемый контекст.
Слушатель, отслеживающий состояния времени жизни ServletContext, реализует интерфейс ServletContextAttributesListener, в который входит три метода:
- void attributeAdded(ServletContextAttributeEvent e) : вызываемый, когда в контекст сервлета добавляется новый атрибут
- void attributeRemoved(ServletContextAttributeEvent e) : вызываемый, когда атрибут удаляется из контекста сервлета
- void attributeReplaced(ServletContextAttributeEvent e) : вызываемый, когда атрибут в контексте сервлета заменяется другим атрибутом
Класс ServletContextAttributeEvent расширяет ServletContextEvent, и добавляет методы getName() и getValue() так, что слушатель может узнать об изменяемых атрибутах. Это полезно, когда нужно синхронизировать состояние приложения (атрибуты контекста) с другими приложениями, такими как, например, база данных.
Модель слушателя сессий аналогична модели слушателя контекста. В модели сессии есть интерфейс HttpSessionListener с двумя методами:
- void sessionCreated(HttpSessionEvent e) :вызывается при создании сессии.
- void sessionDestroyed(HttpSessionEvent e) : вызывается при уничтожении сессии
Методы принимают событие HttpSessionEvent с помощью метода getSession(), чтобы узнать создается или уничтожается сессия. Вы можете использовать все эти методы, когда реализуете интерфейс администратора, отслеживающий всех активных пользователей в Web приложении.
Модель сессии также имеет интерфейс HttpSessionAttributesListener с тремя методами. Эти методы сообщают слушателю, когда атрибуты меняются, и могут быть использованы, например, приложением, которое синхронизирует параметры сохраняемых в базе данных сессий:
- void attributeAdded(HttpSessionBindingEvent e) : вызывается при добавлении атрибута в сессии
- void attributeRemoved(HttpSessionBindingEvent e) : вызывается при удалении атрибута в сессии
- void attributeReplaced(HttpSessionBindingEvent e) : вызывается при замене одного атрибута другим в сессии
Как и следовало ожидать, класс HttpSessionBindingEvent расширяет HttpSessionEvent и добавляет методы getName() и getValue(). Единственное что здесь может показаться странным, это то, что класс называется HttpSessionBindingEvent, а не HttpSessionAttributeEvent. Просто по причине совместимости со старым API был использован уже действующий класс HttpSessionBindingEvent. Возможно, это будет изменено перед выпуском Final Release.
Одно из практических применений событий времени жизни — совместные соединения с базой данных, управляемые слушателем контекста. Вы объявляете слушателя в web.xml как показано ниже:
<listener> <listener-class> com.acme.MyConnectionManager </listener-class> </listener>
Сервер создает экземпляр класса-слушателя (listener) для приема событий, и использует интроспекцию для определения, какой интерфейс (или интерфейсы)слушателя расширяют класс. Примите во внимание, что слушатель конфигурируется в дескрипторе развертывания, поэтому вы можете добавить новых слушателей, без изменения кода. Сам слушатель можете быть написан, например, так:
public class MyConnectionManager
implements ServletContextListener {
public void contextInitialized(ServletContextEvent e) {
Connection con = // создание соединения
e.getServletContext().setAttribute("con", con);
}
public void contextDestroyed(ServletContextEvent e) {
Connection con =
(Connection) e.getServletContext().
getAttribute("con");
try { con.close(); } catch (SQLException ignored)
{ } // закрытие соединения
}
}
Этот слушатель обеспечивает доступность соединения с базой данных из каждого нового контекста сервлета и, закрытие всех соединений при закрытии контекста.
HttpSessionActivationListener, другой, новый интерфейс слушателя в API 2.3, спроектирован для управления сессиями мигрирующими от одного сервера к другому. Слушатель, реализующий HttpSessionActivationListener извещается, когда какая-либо сессия почти начала passivate (завершилась) и когда сессия почти activate (активизировалась) на втором сервере. Эти методы дают приложению возможность передавать не сериализуемые данные между различными JVM, собирать и разделять сериализованные объекты в какую-либо объектную модель до или после миграции. Интерфейс имеет два метода:
- void sessionWillPassivate(HttpSessionEvent e) : сессия почти начала перемещение. Сессия станет недоступной после вызова метода.
- void sessionDidActivate(HttpSessionEvent e) : сессия была активизирована. Сессия не будет доступна пока не вызван метод.
Вы регистрируете этот вызов также как и всегда. Однако, в отличие от обычных, вызовы passivate и activate будут, скорее всего, происходить на двух разных серверах!
Выбор кодировки символов
API 2.3 обеспечивает столь необходимую поддержку по управлению обработкой форм на иностранных языках. Вводится новый метод, request.setCharacterEncoding(String encoding) который позволяет вам сообщить серверу кодировку символов запроса. Кодировка символов (character encoding), также известная как charset, метод отображения байтов в символы. Сервер может использовать специфический charset, чтобы корректно преобразовывать параметры и данные POST. По умолчанию сервер преобразует параметры, используя обычно Latin-1 (ISO 8859-1) charset. К сожалению, он работает только с Западноевропейскими языками. Когда браузер использует другой charset, предполагается посылать информацию о кодировке в заголовок Content-Type запроса. Не каждый браузер делает это. Метод позволяет сообщать серверу, какой именно charset используется (обычно это charset страницы, содержащей форму). Обо всем остальном заботится сервер . Например, сервлет, принимающий параметры на японском языке от формы кодированной Shift_JIS, может прочитать параметры таким образом:
// установить charset — Shift_JIS
req.setCharacterEncoding("Shift_JIS");
//Читать параметр, используя charset
String name = req.getParameter("name");
Не забудьте установить кодировку перед вызовом getParameter() или getReader(). Вызов setCharacterEncoding() может вызвать исключение java.io.UnsupportedEncodingException если кодировка не поддерживается. Эта функциональность также доступна для пользователей API 2.2 и более ранних версий, в классе com.oreilly.servlet.ParameterParser. (Смотрите Ресурсы .)
JAR зависимости
Часто, WAR файл (архивный файл Web приложения, добавленный в API 2.2) требует других дополнительных JAR библиотек для своего существования на сервере и нормальной работы. Например, Web приложению, использующему класс ParameterParser, необходим cos.jar в classpath. Web приложению, использующему WebMacro необходим webmacro.jar. Перед появлением API 2.3, или эти зависимости надо было документировать (если кто вообще читает документацию!) или включать все необходимые jar файлы в каталог WEB-INF/lib, без необходимости раздувая Web приложение.
Servlet API 2.3 позволит вам описать JAR зависимости внутри WAR, используя файл META-INF/MANIFEST.MF. Это стандартный путь объявления зависимостей jar файлов, но с появлением API 2.3, WAR должны официально поддерживать тот же механизм. Если зависимость не может быть удовлетворена, сервер может вежливо отклонить Web приложение еще на этапе развертывания вместо того, чтобы вызывать непонятные ошибки на этапе выполнения. Механизм дает высокую степень модульности. Например, вы можете описать зависимость на конкретную версию необязательного пакета, и сервер должен правильно найти его с помощью поискового алгоритма. (Смотрите ресурсы с ссылкой на документацию, объясняющую в деталях, как работает manifest versioning model)
Загрузчики классов (Class loaders)
Небольшое изменение с серьезными последствиями: сервлет-контейнер (a.k.a. сервер) API 2.3, гарантирует, что классам Web приложения не позволено видеть реализации серверных классов. Другими словами, загрузчики классов должны храниться отдельно.
С виду это не серьезное изменение, но оно сокращает возможность столкновений между классами Web приложений и классами сервера. Это стало серьезной проблемой в связи с конфликтами XML парсеров. Каждому серверу нужен XML парсер для анализа web.xml файлов, и многие Web приложения в наши дни также используют XML парсер для управления чтением, манипуляцией, записью XML данных. Если парсеры поддерживаются различных версий DOM или SAX, это становится причиной неисправимого конфликта. Разделение сфер действия классов решает эту проблему.
Новые атрибуты ошибок.
Предыдущая версия API , Servlet API 2.2, добавила в запросы несколько свойств, которые используются сервлетам и JSP, через тэг <error-page>. Если вы не помните, <error-page> позволяют выводить специфические страницы на определенные коды ошибок и исключений:
<web-app> <!-- ..... --> <error-page> <error-code> 404 </error-code> <location> /404.html </location> </error-page> <error-page> <exception-type> javax.servlet.ServletException </exception-type> <location> /servlet/ErrorDisplay </location> </error-page> <!-- ..... --> </web-app>
Сервлет в <location> получает следующие три свойства из <error-page> :
- javax.servlet.error.status_code : Integer сообщающий error status code, если она произошла
- javax.servlet.error.exception_type : экземпляр Class показывающий тип исключения, ставшей причиной ошибки, если она произошла
- javax.servlet.error.message : String с сообщением исключения, переданным конструктор исключения
Таким образом, сервлет может генерировать ошибочную страницу под конкретную ошибку, как показано ниже:
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class ErrorDisplay extends HttpServlet {
public void doGet(HttpServletRequest req,
httpServletResponse res)
throws ServletException, IOException {
res.setContentType("text/html");
PrintWriter out = res.getWriter();
String code = null, message = null, type = null;
Object codeObj, messageObj, typeObj;
// Получить три возможных атрибута ошибок,
// некоторые из которых могут быть null
codeObj = req.getAttribute(
"javax.servlet.error.status_code");
messageObj = req.getAttribute(
"javax.servlet.error.message");
typeObj = req.getAttribute(
"javax.servlet.error.exception_type");
// Преобразовать атрибуты в строковые значения
// Мы делаем это таким образом,
// потому что некоторые старые сервера возвращают строковые
// тогда как новые сервера возвращают Integer, String, и Class типы.
// Это работает на всех.
if (codeObj != null) code = codeObj.toString();
if (messageObj != null) message = messageObj.toString();
if (typeObj != null) type = typeObj.toString();
// Причина ошибки код состояния или тип исключения
String reason = (code != null ? code : type);
out.println("<HTML>");
out.println("<HEAD><TITLE>" +
reason + ": " + message +
"</TITLE></HEAD>");
out.println("<BODY>");
out.println("<H1>" +
reason + "</H1>");
out.println("<H2>" +
message + "</H2>");
out.println("<HR>");
out.println("<I>Error accessing " +
req.getRequestURI() + "</I>");
out.println("</BODY></HTML>");
}
}
А может ошибочная страница содержать стек трассировки исключений или URI сервлета, который в действительности был причиной проблемы (это не всегда запрашиваемый URI)? В API 2.2 — нет. В API 2.3 эта информация доступна с помощью двух новых свойств:
- javax.servlet.error.exception : объект Throwable, отправляемый действительным исключением
- javax.servlet.error.request_uri : Строка, содержащая URI ресурса ставшего причиной проблем
Эти свойства позволяют передать в ошибочную страницу стек исключений URI ресурса, ставшего причиной проблемы. Ниже, сервлет, который был переписан для использования новых свойств.
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class ErrorDisplay extends HttpServlet {
public void doGet(
HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
res.setContentType("text/html");
PrintWriter out = res.getWriter();
String code = null, message = null, type = null, uri = null;
Object codeObj, messageObj, typeObj;
Throwable throwable;
// Получение трех возможных атрибутов ошибок,
// некоторые из которых могут быть null
codeObj = req.getAttribute(
"javax.servlet.error.status_code");
messageObj = req.getAttribute(
"javax.servlet.error.message");
typeObj = req.getAttribute(
"javax.servlet.error.exception_type");
throwable = (Throwable) req.getAttribute(
"javax.servlet.error.exception");
uri = (String) req.getAttribute(
"javax.servlet.error.request_uri");
if (uri == null) {
uri = req.getRequestURI(); // в случае если URI нет
}
// Преобразовывает атрибуты в строковые значений
if (codeObj != null) code = codeObj.toString();
if (messageObj != null) message = messageObj.toString();
if (typeObj != null) type = typeObj.toString();
// причина ошибки или код возврата или тип исключения
String reason = (code != null ? code : type);
out.println("<HTML>");
out.println("<HEAD><TITLE>" +
reason + ": " + message +
"</TITLE></HEAD>");
out.println("<BODY>");
out.println("<H1>" +
reason + "</H1>");
out.println("<H2>" +
message + "</H2>");
out.println("<PRE>");
if (throwable != null) {
throwable.printStackTrace(out);
}
out.println("</PRE>");
out.println("<HR>");
out.println("<I>Error accessing " +
uri + "</I>");
out.println("</BODY></HTML>");
}
}
Новые возможности системы безопасности
Servlet API 2.3 добавляет два новых свойства запросов(request), для принятия решения по управлению безопасными HTTPS соединениями. Новые свойства:
- javax.servlet.request.cipher_suite : Строка, представляющая код шифра HTTPS, если он есть
- javax.servlet.request.key_size : класс Integer, представляющий битовый размер алгоритма, если он есть
Сервлет может использовать эти свойства, чтобы программно решить достаточно ли безопасно соединение для работы. Приложение может отклонять соединения с недостаточной длиной ключа или не заслуживающим доверия алгоритмом. например, сервлет может использовать следующий метод для проверки, что его соединение использует по крайней мере, 128-битный ключ.
public boolean isAbove128(HttpServletRequest req) {
Integer size = (Integer) req.getAttribute(
"javax.servlet.request.key_size");
if (size == null || size.intValue() < 128) {
return false;
}
else {
return true;
}
}
Мелкие доработки
В API 2.3 сделано множество небольших доработок. Например, метод getAuthType() , возвращающий тип аутентификации, использовавшийся для идентификации клиента был определен для возврата одного из четырех новых static final String констант в класс HttpServletRequest: BASIC_AUTH, DIGEST_AUTH, CLIENT_CERT_AUTH, и FORM_AUTH. Это позволяет упростить код таким образом:
if (req.getAuthType() == req.BASIC_AUTH) {
// выполнение основной аутентификации
}
Конечно, эти четыре константы все еще имеют традиционные строковые значения, так что последующий код из API 2.2 тоже работает, правда не так быстро и элегантно. Так с помощью equals() происходит проверка getAuthType() на null, чтобы избежать NullPointerException:
if ("BASIC".equals(req.getAuthType())) {
// выполнение основной аутентификации
}
Другое изменение в API 2.3 — объявление класса HttpUtils устаревшим.HttpUtils, также известный, как "класс, который никогда не должен объявляться public", всегда выделялся как набор разрозненных статических методов, полезных, но расположенных не на своем месте. Класс имел методы для реконструкции оригинального URL из запроса (request) объекта и преобразовывал данные параметров в hashtable. В API 2.3 эта функциональность перешла в объект запроса(request), которому она больше подходит. Сам же HttpUtils объявляется устаревшим. Новые методы запроса(request):
- StringBuffer req.getRequestURL() : возвращает StringBuffer, содержащий оригинальный URL запроса, получаемый из запросной информации.
- java.util.Map req.getParameterMap() : возвращает постоянный Map параметров запроса. Имена параметров действуют как ключи, значения параметров действуют как значения. Не решено, как управлять параметрами с множественными значениями. Наиболее вероятно, что они будут возвращаться как массив String[]. Методы используют новый метод req.setCharacterEncoding() для управления перекодировками.
API 2.3 добавляет два новых метода к ServletContext позволяющие получить имя контекста и список ресурсов, которые он содержит:
- String context.getServletContextName() : возвращает имя контекста, как он объявлен в файле web.xml.
- java.util.Set context.getResourcePaths() : возвращает все пути к ресурсам доступным контексту, как набор постоянных строковых объектов. Каждая строка имеет лидирующий слеш ('/') и привязана к корню контекста.
В объект ответа (response) добавлены новые методы контроля за буфером ответов.В API 2.2 есть метод res.reset() для сброса ответа (response) и очистки тела ответа (response), заголовков, и кода состояния. API 2.3 добавляет res.resetBuffer() который очищает только тело ответа(response):
- void res.resetBuffer() : Очищает буфер ответа(response) без очистки заголовков и кодов состояния. Если ответ (response) уже был совершен, он создает исключение IllegalStateException.
Наконец, после продолжительных дебатов экспертной группы, Servlet API 2.3 раз и навсегда пролил свет на то, что происходит с сервлетом, выполняющимся не в корне контекста, при вызове res.sendRedirect("/ index.html"). Дело в том, что Servlet API 2.2 использует неполный путь, например, "/ index.html" , а сервлет-контейнер переводит его в полный, но не говорит, каким образом. Если сервлет, делает вызов из контекста с путем "/contextpath," как должен переводиться перенаправляемый URI, относительно корня контейнера (http://server:port/ index.html) или относительно корня контекста (http://server:port/contextpath/ index.html)? Для лучшей переносимости, необходимо было определить поведение. В результате долгих споров, эксперты решили транслировать URI относительно корня контейнера. Те же, кто хочет привязаться к контексту, могут использовать метод getContextPath().
Преобразование DTD
Наконец Servlet API 2.3 связывает вместе несколько потерянных концов в отношении DTD в web.xml. Например, обрезаются концевые пробелы текстовых значений в файле web.xml. (В стандартном XML, все пустые пробелы обычно сохранялись.) Это правило говорит, что следующие два варианта записи могут быть обработаны одинаково:
<servlet-name>hello</servlet-name>
<servlet-name> hello</servlet-name>
В API 2.3 для тэга <auth-constraint> вводится групповой символ "*", который может быть использован в <role-name> как символ, позволяющий все роли. Правило, подобное следующему, позволяет всем пользователем осуществлять вход так быстро, как только их роль была точно идентифицирована:
<auth-constraint> <role-name>*</role-name> < !-- позволяет все возможные роли --> </auth-constraint>
тэг <security-role> можно использовать, в качестве параметра метода isUserInRole(). анпример, опишем <security-role> в web.xml:
<servlet> <servlet-name> secret </servlet-name> <servlet-class> SalaryViewer </servlet-class> <security-role-ref> <role-name> mgr < !-- имя, используемое сервлетом --> </role-name> < !-- имя используемое в дескрипторе развертывания --> <role-link> manager </role-link> </security-role-ref> </servlet> <!-- ... --> <security-role> <role-name> manager </role-name> </security-role>
Сервлет secret может вызвать isUserInRole("mgr") или isUserInRole("manager") — результат будет одинаковый. Главным образом, security-role-ref нужен для создания алиаса, но он не обязателен. Вы вероятно так и думали, но по спецификации API 2.2 кажется можно использовать только роль, объявленную в алиасе <security-role-ref>. (Если это не имеет для вас какого-либо смысла, не беспокойтесь об этом, просто помните, что не все всегда работает, так как описано).
Заключение
Как я писал в этой статье, Servlet API 2.3 включает впечатляющий механизм фильтров, расширенную модель периода жизни, новую функциональность в поддержке интернационализации(internationalization), управления ошибками, безопасными соединениями, пользовательскими ролями. Документация на спецификацию была сжата, чтобы убрать повторения, которые могли повлиять на межплатформенное использование. В итоге,добавлены 15 новых классов (большинство для работы с моделью периода жизни, остальные для работы с фильтрами), четыре метода к уже существующим классам, четыре новых константы, и один класс объявлен устаревшим. Краткий список различий между 2.2 на 2.3, смотрите в Списке изменений .
Об авторе
Джасон Хантер (Jason Hunter) главный специалист по технологиям CollabNet, которая разрабатывает инструменты и сервисы для систем с открытым кодом. Он, автор Java Servlet Programming, 2-ая редакция (O'Reilly), издатель Servlets.com, участник Apache Tomcat (он работал над проектом, когда тот еще был внутренним, Sun-овским), член экспертной группы ответственной за Servlet/JSP и JAXP API разработку. Он также член JCP Executive Committee, наблюдающего за Java платформой, как представитель Apache Software Foundation. Совсем недавно он участвовал в совместной работе по созданию открытого исходного кода библиотеки JDOM для обеспечения возможности интеграции Java и XML.
Ресурсы
- Официальный домашний сайт рабочей группы Servlet API 2.3, JSR-053:
http://java.sun.com/aboutJava/ communityprocess/jsr/jsr_053_jspservlet.html - Официальная домашняя страница сервлетов:
http://java.sun.com/products/servlet - Документация по управлению внутренними зависимостями JAR (а теперь и WAR):
http://java.sun.com/j2se/1.3/ docs/guide/extensions/versioning.html - Страница с загружаемой спецификацией J2EE 1.3 :
http://java.sun.com/j2ee/download.html#platformspec -
Java Servlet Programming, Jason Hunter (O'Reilly & Associates, 1998) :
http://www.servlets.com/book - The com.oreilly.servlet package:
http://www.servlets.com/ resources/com.oreilly.servlet - Apache Tomcat, открытая эталонная реализация сервлетов:
http://jakarta.apache.org/ - "Introducing the New Servlet API 2.1," Jason Hunter (JavaWorld, December 1998) — описывает различия между Servlet API 2.0 и 2.1:
http://www.javaworld.com/ jw-12-1998/jw-12-servletapi.html - "What's New in Java Servlet API 2.2?" Jason Hunter (JavaWorld, October 1999) — объясняет различия между Servlet API 2.1 и 2.2:
http://www.javaworld.com/ jw-10-1999/jw-10-servletapi.html - Jason Hunter's new pet project, JDOM:
http://www.jdom.org/ - "Easy Java/XML Integration with JDOM, Part 1," Jason Hunter and Brett McLaughlin (JavaWorld, May 2000):
http://www.javaworld.com/javaworld/ jw-05-2000/jw-0518-jdom.html - "Easy Java/XML Integration with JDOM, Part 2," Jason Hunter and Brett McLaughlin (JavaWorld, July 2000):
http://www.javaworld.com/javaworld/ jw-07-2000/jw-0728-jdom2.html - Полный список статей Server-Side Java в Тематическом каталоге JavaWorld:
http://www.javaworld.com/javaworld/ topicalindex/jw-ti-ssj.html - Полный список статей по Java API в Тематическом каталоге JavaWorld':
http://www.javaworld.com/javaworld/ topicalindex/jw-ti-api.html - Обсуждение новых Servlet API в дискуссии Server-Side Java на ITworld.com:
http://forums.itworld.com/ webx?14@@.ee6bdcf/366!skip=310 - Подписка на свободно распространяемый бюллетень ITworld.com Java in the Enterprise :
http://www.itworld.com/cgi-bin/subcontent12.cgi
| Новые классы | Новые методы | Новые константы | Устаревшие классы |
|---|---|---|---|
| Filter, FilterChain, FilterConfig, ServletContextAttributeEvent, ServletContextAttributesListener, ServletContextEvent, ServletContextListener, ServletRequestWrapper, ServletResponseWrapper, HttpServletRequestWrapper, HttpServletResponseWrapper, HttpSessionActivationListener, HttpSessionAttributesListener, HttpSessionEvent, HttpSessionListener | getResourcePaths(), getServletContextName(), resetBuffer(), HttpSessionBindingEvent.getValue() | BASIC_AUTH, DIGEST_AUTH, CLIENT_CERT_AUTH, and FORM_AUTH | HttpUtils |
Reprinted with permission from the January 2001 edition of JavaWorld magazine. Copyright © ITworld.com, Inc., an IDG Communications company.
View the original article at: http://www.javaworld.com/javaworld/ jw-01-2001/jw-0126-servletapi.html
