Текст
                    Java, сервлеты и JSP.
сборник рецептов
КУДИЦ-ОБРАЗ
O’REILLY®
Брюс У. Перри

Java™ Servlet and JSP™ Cookbook™ Bruce W. Perry t O’REILLY’ Beijir g • CartTdge • Fcmham • Koh • Paris • Sebastopol • Taipei * Tokyo
1 Брюс У. Пер^и Java™ сервлеты и JSP™ сборник рецептов КУДИЦ-ОБРАЗ Москва • 2006
ББК 32.973.26-018.2 Перри Б. Java сервлеты и JSP: сборник рецептов. Изд. 2-е / Пер. с англ. - М.: КУДИЦ-ПРЕСС, 2006. — 7б8 с. Данная книга написана экспертом в области Java, имеющи и многолетний опыт практи- ческих разработок с использованием технологий Java Servlet и JSP. В книге приведены решения проблем, с которыми приходится сталкиваться web- разработчикам на Java. Рецепты даны по мере нарастания сложности и рассмотрены в свя- зи с такими популярными серверами приложений, как Tomcat и Weblogic. Книга предназначена для широкого круга читателей - от опытных Java-разработчиков, желающих усовершенствовать свое мастерство в использовании технологий Java Servlet и JSP, до студентов,-изучающих Java. Брюс У. Перри Java сервлеты и JSP: сборник рецептов Учебно-справочное издание Перевод с англ. В. В. Акимов Научный редактор Л. Б. Сиховец «ИД КУДИЦ-ОБРАЗ» Тел.: 333-82-11; E-mail: ok@kudits.ru; http://books.kudits.ru 119049, Москва, Ленинский пр-т. д. 4, стр. 1А Подписано в печать 20.08.06 \ Формат 70x90/16. Бумага газетная. Печать офсетная Усл. печ. л. 56,16. Тираж 1000. Заказ 1545 у Отпечатано в ОАО «Щербинская типография» . 117623, Москва, ул. Типографская, д. 10 Т. 659-23-27. ISBN 5-9579-0073-7 (рус.) © Перевод, макет, обложка «ИД КУДИЦ-ОБРАЗ», 2005-2006 ISBN 0-596-00572-5 © 2004 O'Reilly Media, Inc. © Kudits-Obraz 2005-2006. Authorized translation of the English edition1 © 2004 O'Reilly Media, Inv This translation is published and sold by permission of O'Reilly Media, Inc., the owner of all-rights to publish and sell the same. Все права защищены. Русское издание опубликовано издательством КУДИЦ-ОБРАЗ, © 2005-2006. Никакая часть данной книги не может быть воспроизведена в какой бы то ни было форме без письменного разрешения владельцев авторских прав. Информация, содержащаяся в данной книге, получена из источников, рассматриваемых издатель- ством как надежные. Тем не менее, имея в виду возможные человеческие или технические ошиб- ки, издательство не может гарантировать абсолютную точность и полноту приводимых сведений и не несет ответственности за возможные ошибки, связанные с использованием книги.
Предисловие На исторической оси времени сага о Java как о наилучшем инструменте для соз- дания программ, работающих на стороне сервера, началась в 1997, когда фирма Sun , Microsystems создала бета-версию «Java™ Web Server», а также Jaya Servlet Develop- l ers Kit (набор инструментов для разработчика сервлетов). Сервлет - это тип Java- класса, который выполняется на сервере. Сервлеты динамически обрабатывают сете- вые запросы и ответы, используя, главным образом, HTTP (Hypertext Transfer Proto- col - протокол передачи гипертекста). В июне 1999 Sun представила JSP (JavaServer Pages - серверные страницы Java), в которых Java-код перемешивается с JavaScript и шаблонным HTML-текстом. JSP, в их настоящем виде (начиная с версии 2.0), предназначены для инкапсуля- ции доменной логики в стандартных и пользовательских тегах и отделения этого доменного слоя от презентационной логики, реализуемой самим компонентом JSP. Презентационная логика - это действия по формированию «тех вещей, которые видят люди», когда они взаимодействуют с web-приложением, например тю формированию связанных с HTML экранных визуальных элементов. В идеале, JSP, используя теги для взаимодействия с базами данных и инкапсуляции доменных правил, статически или динамически генерирует шаблонный текст, например XML или XHTML, созда- вая визуальную страницу для пользователя. В конце 90-х я был «свободным художником», разработчиком серверной части web-приложений на разных языках программирования. Когда на сцене появилась Java для серверных приложений, я воспринял эту новое я ь с большой радостью. Java, созданная от начала и до конца как объектно-ориентированный и модульный язык, представлял собой обнадеживающую альтернативу плохо спроектированному, хотя и с самыми лучшими намерениями, web-коду, с которым я часто и г.’лкивался, когда ' '' та или иная организация бросала меня в самую середину проекта. С помощью Java вы не только можете легко создавать свои собственные повторно используемые компоненты, например, для отправки почтовых сообщений, спроектировав их и скачав для своего web-приложения один или несколько Java-клас- t сов, но в вашем распоряжении целый набор Java API для работы с базовыми, низ- коуровневыми элементами, например для обработки строк, файлового ввода-вывода .. и математических вычислений. Неплохо! 5 '
Другой сильной стороной Java является ее независимость от платформы. Web- разработчики могут создавать свди приложения, упаковывать их в специальный JAR- файл для web-компонентов, называемый Web Application Archive - архив web-приложе- ния, и затем инсталлировать эти WAR-файлы на серверах, работающих под управле- нием самых разных операционных систем (ОС). Web-компоненты Java не привязаны к какой-либо определенной ОС или к серверному ПО одного определенного производи- теля, как другие программные технологии, ориентированные на web. Перенесемся в настоящее время. К 2003 году Java приобрел статус наиболее популярной технологии для разработки программ, работающих на стороне сервера. Сервлеты и JSP были включены в Java 2 Enterprise Edition (J2EE), широко распространенную технологию основанных на сети и распределенных вычислений. Сотни тысяч разработчиков по всему миру работают на «web-уровне» технологий, основанных на J2EE, и используют сервлеты, JSP и, зачастую, специальные фреймворки, например Struts. Сейчас многие web-разработчики проводят большое количество времени за изучением различных «серверов приложений», таких, как BEA WebLogic, JBoss или WebSphere от фирмы IBM, объединяющих воедино web-уровень, бизнес-объекгы (доменные объекты) (например, компоненты, которые обрабатывают данные о погоде или финансовые счета клиентов), и информационные системы предприятия (Enterprise Information Systems - EIS). Серверы приложений представляют собой программное обеспечение, на базе которого выполняются сервлеты и JSP (хост для сервлетов и JSP). Многие web-разработчики, в том числе и я, проводят массу времени, работая над web-компонентами, хостом для которых служит Tomcat, популярная исполнительная подсистема поддержки сервлетов (servlet engine) с открытыми исходными текстами (http://www.opensource.org), являющаяся «справочной реализацией»* новых API сервлетов и JSP. Быстрое развитие и укоренившаяся природа Java естественным образом определили форму этой книги - «сборник рецептов». Сборник рецептов фокусируется на том, как решить конкретные задачи, связанные с web, средствами Java, а не на том, чтобы объяснить читателю основы работы с языком Java, или детально описать использование API сервлетов и JSP. Недостаток в руководствах по Java все еще ощущается, однако в обновленной форме или в форме переизданий, что свидетельствует о популярности Java как платформы для web-разработок. * Справочная реализация - это ПО, базирующееся на общепринятой спецификации и свободно распространяемое среди разработчиков ПО и других лиц для демонстрации того, как должна функционировать подобная программная система. / 6 | Предисловие
Что вы найдете в этой книге Создавая рецепты для этой книги, я пытался объять столько рядовых и более сложных задач, сколько способно поместиться в одной книге. Это около 230 разных рецептов. В каждом из рецептов показано, как решать определенную задачу, используя сервлеты, JSP и, во многих рецептах, один или несколько поддерживающих Java-классов. В этих рецептах показано как: • аутентифицировать web-клиентов; • работать с базой данных; • отправлять почтовые сообщения; • обрабатывать данные, полученные от web-формы; *• • читать и устанавливать cookie; • выгружать файлы от клиента; • интегрировать JavaScript с сервлетами и JSP; • встраивать в JSP и сервлеты файлы мультимедиа; например цифровое видео и музыку; • работать с web-клиентами, говорящими на разных языках (интернационализация); • протоколировать сообщения от сервлетов и JSP; • динамически включать фрагменты содержимого, как это делается в традиционном; коде включений на стороне сервера (SSI); • взаимодействовать в сервлете или JSP с компонентами Enteiprise JavaBeans (EJB); • использовать в сервлете и JSP программные интерфейсы web-сервисов сайтов Ama- zon.com и Google.com. Я также включил большое число рецептов, связанных с определенной технологией, например: • использование «сеансов» в ваших web-приложениях (концепция, представляющая отслеживание хода взаимодействия пользователя с web-сайтом); • работа с «фильтрами»; • использование инструмента ANT (open source - ПО с открытыми исходными кодами) для сборки web-приложения; • связывание Java объектов с сеансом или web-приложением, с тем, чтобы использовать их как контейнер с данными; • создание собственных пользовательских тегов для JSP; • использование библиотеки JSTL (JavaServer Pages Standard Tag Library - библиотеки стандартных тегов JSP), которая является крупным набором готовых тегов, которые вы можете использовать на своих страницах JSP. Короче говоря, книга создана, чтобы помочь Java-разработчикам в решении их повсе- дневных задач и дать быстрые ответы на типичные проблемы web-программирования. Что вы найдетеъ этой книге | 7
Рецепты, связанные с BEA WebLogic Поскольку web-разработчики, работающие на Java, обычно используют не только Tom- cat, но и какой-либо коммерческий сервер приложений, я включил ряд рецептов, демонстрирующих, как решать ряд распространенных задач, используя сервер ВЕА WebLogic. По понятным причинам я не мог охватить все существующие серверы приложе- ний, такие, как WebSphere фирмы IBM, JBoss, Jetty, сервер приложений Oracle 9i, или коммерческие исполнительные подсистемы для поддержки сервлетов, например New Atlanta ServletExec и Caucho Resin. Но мне все-таки хотелось включить рецепты, демонстрирующие, «как живут другие», и показать как для управления повседневными web-задачами можно использовать инструменты разных поставщиков. Такие решения, как развертывание или изменение web-компонентов и дескрипторов развертывания при использовании графического интерфейса, например консоли администрирования WebLogic или WebLogic Builder, могут несколько отличаться от аналогичных решений в Tomcat. В результате эта книга включает набор базовых рецептов, связанных с WebLogic, например, развертывание web-приложения на сервере WebLogic или использование сервлета для доступа к источнику данных на WebLogic. В главе 25, Использование JNDI и Enterprise JavaBeans, показано, как сервлет может взаимодействовать с компонентом EJB, инсталлированным на сервере WebLogic. Аудитория Данные рецепты создавались в расчете на опытных разработчиков, которым прихо- дится заниматься созданием, сборкой, развертыванием и изменением web-приложений на базе Java. К ним относятся разработчики JSP, сервлетов и компонентов JavaBean. Книга также подойдет опытным web-разработчикам, мигрировавшим с других платформ web-программирования (Active Server Pages, PHP или Perl) и только изучающим Java. Эти люди, как правило, хорошо разбираются в базовых механизмах, таких, как сеансы, cookie, выгрузка файлов, входная аутентификация и обработка POST-запросов HTTP, но пока еще не знают, как эти задачи можно реализовать средствами Java. Данная книга позволит им быстро вникнуть в решение проблемы, с которой они, возможно, уже сталкивались, используя другой язык. Java-разработчики, которые хотят знать, как реализовать новые возможности, зало- женные в API сервлетов версии 2.4 и в JSP 2.0 (например,, новые элементы filter- mapping файла web.xml для диспетчеров запросов и встраивание языка выражений (Expression Language - EL) в шаблонный текст JSP), также найдут в этой книге много полезного для себя. 8 | Предисловие
Что необходимо знать Читателям необходимо знать основы языка Java или учиться программированию на Java. Глава 1 Создание сервлетов и JSP включает краткую информацию по сервлетам, JSP и дескриптору развертывания для читателей, не знакомых с этими концепциями. Однако на протяжении всей книги, объяснение концентрируется на специфических задачах и не содержит длинных пояснений API сервлетов и JSP. Каждый рецепт включает введе- ние, содержащее необходимую информацию, чтобы приступить к работе с разными техно- логиями и примерами кода. Специально для читателей, которые хотят углубить свои знания по обсуждаемой теме, рецепты включают большое количество ссылок на информационные ресурсы Интернета, например страницы документации (Javadoc) и руководства. " Читатели, уже знакомые с разными областями J2EE, например с Java Database Connec- tivity (JDpC), Java Naming and Directory Interface (JNDI) и Enterprise JavaBeans (я включил один рецепт, посвященный соединению web-компонента с EJB, с использованием JNDI), будут иметь преимущество. И, наконец, полезными окажутся практические знания XML, поскольку web-разработка на Java включает дескрипторы развертывания и файлы конфигурации, базирующиеся наХМЬ. Организация Книгу открывают три главы, посвященные основам создания сервлетов и JSP, развертыванию сервлетов и JSP, именованию и регистрации сервлетов, а также исполь- зованию инструмента Ant. Затем я поднимаю несколько базовых тем web-разработки, таких, как динамическое включение содержания в web-страницу, выгрузка файлов, обработка данных, посланных из HTML-формы, чтение и установка cookie, отслеживание сеанса и интеграция JavaScript с сервлетами и JSP. Далее книга включает ряд более сложных рецептов, например протоколирование сооб- щений, аутентификация клиентов, привязка атрибутов, работа с запросом клиента и созда- ние фильтров сервлета. Глава 20 Работа с электронной почтой в сервлетах и JSP и глава 21 Доступ к базам данных посвящены двум сложным задачам web-разработки 20 различных рецептов. Глава 22 Использование пользовательской библиотеки тегов и глава 23 Использование JSTL описывают пользовательские теги и JSTL. Глава 24 Интернационализация обсуждает критическую тему интернационализации ваших web-приложений, содержащих сервлеты hJSP. Для web-разработчиков, чьи web-компоненты должны взаимодействовать с компо- нентами EJB, используя Java JNDI, глава 25 Использование JNDI и компонентов Enter- prise JavaBean показывает, как настроить JNDI на серверах Tomcat и WebLogic, а также как получить доступ к JNDI-объекту, используя эти серверы. Организация | 9
Книга завершается двумя рецептами, в которых описываются разные стратегии извлечения данных с web-сайтов web-компонентами Java. В главе 26 Пожинание информации из Web содержатся рецепты по harvesting (пожинанию) или scraping (выскабливанию) данных из web-страниц. В главе 27 Использование Web API сайтов Google и Amazon описано, как использовать интерфейсы прикладных программ web- сервисов Google и Amazon.com. Соглашения, используемые в этой книге В данной книге используются следующие соглашения по использованию шрифтов. Курсив Используется для выделения новых терминов, URL, адресов электронной почты, расширений файлов, имен, входящих в пути к файлам, каталогов, и служебных программ Unix. Моноширинный шрифт Используется для выделения команд, опций, переключателей, переменных, атрибу- тов, ключей, функций, типов, классов, пространств им^н, методов, модулей, свойств, параметров, значений, объектов, событий, обработчиков событий, XML-тегов, HTML- тегов, макросов, содержимого файлов, результата работы команд. Моноширинный полужирный шрифт Используется для выделения команд или другого текста, который должен быть напечатан пользователем дословно (буква в букву), а также для выделение кода в примерах. Моноширинный шрифт с курсивом Используется для выделения текста, который должен быть заменен, собственным значением пользователя. В некоторых случаях, там, где текст уже набран курсивом, тескт, который нужно заменить на собственные значения пользователя заключается в угловые скобки (о). Эта пиктограмма обозначает совет, указание, или общее замечание. М?' 4 - ту Эта пиктограмма обозначает предупреждение или опасность. 10 | Предисловие
Использование примеров кода Эта книга призвана помочь вам в работе. Вы можете свободно использовать код, приведенный здесь в своих программах и документации. При этом вам не требуется свя- зываться с нами, чтобы получить специальное разрешение, за исключением случая, когда вы воспроизводите значительные порции кода. К примеру, создание программу, в которой используется несколько фрагментов кода из книги, не требует получения разрешения. Для продажи или распространения CD-ROM с примерами из книг O’Reilly разрешение потребуется. Ответы на вопросы путем цитирования этой книги и приведе- ния примеров кода не требуют разрешения. Для переноса в документацию по вашему продукту значительного количества кода из книги разрешение потребуется. O'Reilly & Партнеры, а также автор будут вам признательны* если вы будете указы- вать авторство книги (но не требуют этого). Авторство обычно включает название, автора, издателя и ISBN. К примеру, «Java Servlet and JSP Cookbook, by Bruce Perry. Copyright 2004 O'Reilly & Associates, Inc., 0-596-00572-5». Если у вас возникнут сомнения по поводу правомерности вашего использования примеров кода, свяжитесь с нами по адресу permissions @ oreilly.com. Вопросы и замечания Просьба замечания и вопросы, касающиеся данной книги, присылать издателю: O'Reilly & Associates, Inc. 1005 Gravenstein Highway North Sebastopol, CA 95472 (800) 998-9938 (США или Канада) (707) 829-0515 (международный или местный) (707) 829-0104 (факс) O'Reilly поддерживает web-страницу данной книги, где вы найдете список опечаток, примеры и другую дополнительную информацию. Адрес страницы: http://www.oreilly.cofn/catalog/jsvltjspckbk Комментарии и технические вопросы присылайте по адресу: bookquestions @ oreilly. сот Дополнительную информацию о книгах издательства O'Reilly, конференциях, центрах ресурсов и сети O'Reilly ищите на web-сайте: http://www.oreilly.com Вопросы и замечания | 11
Благодарности Kaij-то ночью, более года назад, я отправил в издательство O'Reilly письмо с идеей этой книги. В то время шансы на то, что это легкомысленное письмо действительно приведет к написанию книги, казались чрезвычайно низкими. После оживленной переписки между мной и парой Java-редакторов издательства O'Reilly и нескольких месяцев мягких подталкиваний, основательного редактирования, а порой и изменения концепции, и само собой - работы, работы и еще раз работы идея написания книги переросла в реальный итог. Родилась книга! Формирование книги - это всегда результат сотрудничества нескольких людей. Воз- можно, эта книга никогда не увидела бы свет без, иногда коротких, иногда длинных, напоминаний моего редактора Брета Маклафлина (Brett McLaughlin) о том, что отличает сборник рецептов от книг другого типа. Брет также является прекрасным выпускающим редактором; Я, как автор, оценил его усилия и повысил свои познания в области редакторского дела. Брет имеет глубокие знания Java и его замечания в ряде случаев помогли мне избежать неуклюжих решений при создании кода. Я очень рад, что техническими редакторами у меня были Джейсон Хантер (Jason Hunter) и Сэнг Шин (Sang Shin). Они являются известными экспертами по Java, и книга очень выиграла благодаря тому, что они прочли текст и написали свои комментарии к значительным фрагментам книги. Их рецензия как короткий поводок и сама по себе является большой книгой. Я был поражен широтой масштаба, проявленной в сравни- тельно узких рамках. Как автор технической книги, я признателен тем, кто удержал меня от досадных ошибок! Некоторые из нас в списке людей, которых они благодарят за помощь, ставят членов своей семьи на последнее место. Возможно, это связано с тем, что последний абзац - это основа, на которой зиждется все остальное, а семья - это основа для любрго писателя, она поддерживает его и защищает от внешних раздражителей, когда он погружен в изло- жение и технологию. Эта книга не была бы создана без помощи моей жены Стейси (Stacy), дочери Рейчел (Rachel) и даже Скотта (Scott), который вдохновлял меня, хотя ему не было еще и года. Я также повторю то, что написал в другой своей книге, по AppleScript: я благодарен моим родителям Роберту и Анне Перри (Robert и Anne Perry) за то, что они привили мне Любовь к писательскому делу и книгам. 12 | Предисловие
ГЛАВА 1 Создание сервлетов и JSP 1.0 Введение Целью этой главы является краткое изложение основ создания, компиляции и упа- ковки сервлетов и JSP. Если вам до сих пор не приходилось разрабатывать сервлеты или JSP илИ вам необходимо просто освежить знания по этой технологии для начала практической работы, тогда следующие ниже рецепты предоставят вам примеры программ и краткие описания компонент, которые вам понадобится включить в поль- зовательский путь к классам (user classpath), чтобы иметь возможность ком- пилировать сервлеты. Рецепты 1.1 и 1.2 содержат краткое введение в сервлеты и JSP соответственно. Полное описание роли сервлетов и JSP для корпоративной редакции платформы Java 2 (J2EE) выходит за рамки данной книги. Однако информация, имеющая прямое отношение к технологии J2EE, такая, как базы данных и JDBC, использование JNDI (Java Naming an<^ Directory Interface - Java интерфейс именования и каталогов) и использование сервлетов с Java Mail (или электронной почтой), содержится в этой книге (и в предметном указателе!). Разделы «См. также», которыми завершается каждый рецепт, содержат ссылки на близкие по содержанию главы, оп1те-руковод<?тво от Sun Microsystems и другие книги издательства O’Reilly, углубляющие поднятые темы. I 1.1 Создание сервлетов Задача Необходимо написать сервлет, являющийся частью web-приложения. Решение Создайте класс, расширяющий класс javax. servlet .http.HttpServlet. He забудьте импортировать классы из servlet.jar (или servlet-api.jar) - они необхо- димы для компиляции вашего сервлета. 13
Обсуждение Сервлет - это класс Java, предназначенный для динамического формирования содержимого ответа на запрос клиента по сети.' Если вам зцакомы CGI-программы (Com- mon Gateway Interface - общий шлюзовой интерфейс), то сервлеты - это технология Java, которая может заменить CGI-программы. Сервлеты (как и JSP) часто называют web-компонентами, они выполняются в специальной среде исполнения, создаваемой контейнером сервлетов илй web-контейнером, таким, как Jakarta Tomcat или BEA WebLogic. Web-контейнер может быть компонентом-надстройкой (add-on) HTTP-сервера или отдельным сервером, как, например, Tomcat, который способен обслуживать НТТР- * запросы как к статичному содержимому (НТ ГР-файлы), так и к сервлетам и JSP. Сервлеты инсталлируются в web-контейнерах как часть web-приложения. Такие прило- жения по сути являются набором web-рссурсов: HTML-страниц, изображений, мультиме- диа, сервлетов, JSP, XML-файлов конфигурации, классов и библиотек поддержки Java. После развертывания web-приложения в web-контейнере контейнер создает и загружает экземпляр класса сервлета в виртуальную машину Java (JVM) для обслуживания запросов, поступающих на сервер. Сервлет обслуживает каждый запрос в отдельном потоке. Таким образом, создатели сервлетов должны решать, надо ли обеспечивать синхронизацию доступа к переменным экземпляра и переменным класса или нужно использовать разделяемые ресурсы, например соединение с базой данных, в зависимости от того, как эти ресурсы используются. Все сервлеты реализуют интерфейс javax.servlet .Servlet. Разработчики web- приложений обычно пишут сервлеты, расширяющие javax. servlet .http.Http- Servlet - абстрактный класс, который реализует интерфейс Servlet и создан специ- ально для обслуживания HTTP-запросов. При создании web-контейнером экземпляра сервлета, осуществляются следующие действия. 1. 2. Контейнер вызывает Метод init () сервлета, который предназначен для инициализа- ции ресурсов, необходимых сервлету, например сервлету-регистратору (см. главу 14). Метод ini t () вызывается только один раз за время жизни сервлета. Метод init () инициализирует объект, реализующий интерфейс javax. servlet. ServletConfig. Этот объект открывает сервлету доступ к .параметрам инициализа- ции, объявленным в дескрипторе развертывания (см. Рецепт 1.5). ServletConfig также открывает сервлету доступ к объекту javax.servlet.ServletContext, с помощью которого сервлет может протоколировать события, перенаправлять запросы к другим web-компонентам и получать доступ к прочйм web-ресурсам этого же прило- жения (см. рецепт 13.5). 14 | Глава 1. Создание сервлетов и JSP
Разработчикам не нужно реализовывать метод init () в своих подклассах класса HttpServlet. При запросе к сервлету контейнер вызывает метод service () сервлета. В терминах HttpServlet, метод service () для обработки запроса авотматически вызывает подходящий HTTP-метод сервлета: do Post () или doGet (). Например, при посылке от пользователя HTTP-запроса POST сервлет отзывается выполнением метода doPost(). При вызове двух главных методов сервлета HttpServlet (doPostO или doGet О) контейнер сервлета создает объекты javax.servlet.http.Http- Servlet Request и HttpServletResponse и передает их в качестве параметров этим методам обработки запроса. Объект HttpServletRequest представляет исходный запрос, а объект HttpServletResponse инкапсулирует ответ сервлета на этот запрос. Пример 1.1 демонстрирует типичное использование объектов запроса и ответа. Хотя большинство имен методов говорят об их назначении, стоит также ознакомиться с документацией по API сервлета (http://java.sun.com/j2ee/L4/docs/ api/javax/servlet/http/package-summary.html). 5. Не разработчик, а сам сервлет или web-контейнер управляют жизненным циклом Сервлета, то есть тем, как долго экземпляр сервлета существует в виртуальной машине Java (JVM) и обрабатывает запросы. Когда контейнер сервлета намеревается исключить сервлет из обслуживания, он вызывает метод destroy () Сервлета, в котором сервлет может освободить любые ресурсы, например соединение с базой данных. Пример 1.1 показывает типичную схему обработки сервлетом HTML-формы. Метод doGet () отображает саму форму. Метод doPost () обрабатывет введенные в форму данные, если соответствующий тег сформированной в doGet () HTML-формы задает в качестве места назначения вводимых данных этот сервлет. Данный сервлет (с именем FirstServlet) указывает, что объявляемый класс явля- ется частью пакета com.jspservletcookbook. Очень важно создавать пакеты для ваших собственных классов сервлетов и вспомогательных классов и затем сохранять эти классы в структуре каталогов, соответствующей именам пакетов, в каталоге WEB-INF. Класс FirstServlet импортирует классы, необходимые для компиляции базового сервлета - в примере 1.1 эти операторы import выделены полужирным шрифтом. Дан- ный Java-класс расширяет Класс HttpServlet. В нем объявлены всего два метода: doGet (), отображающий HTML-форму в ответ на HTTP-запрос Get; doPostO, обрабатывающий введенные в форму данные. Создание сервлетов | 15
Пример 1.1. Типичный HttpServlet для обработки HTML-формы - package com.jspservletcookbook; import j ava.io.lOException; import java.io.Printwriter; inport java.util.Enumeration; import javax.servlet.ServletExceptIon; import j avax. servlet. http. HttpServlet import javax.servlet.http.HttpServletRequest; import javax. servlet. http, HttpServletResponse; public class FirstServlet extends HttpServlet { public void doGet(HttpServletRequest request, ’ HttpServletResponse response) throws ServletException, java.io.lOException { //устанавливаем тип Mime ответа в "text/html" response.setContentType <"text/html"); //используем Printwriter для отправки данных клиенту, 1 //обратившемуся с сервлету java.io.Printwriter out = response.getWriter(); //Начало формирования HTML-содержимого out. print In (" <htmlxhead>"); out. printin (•<title>Help Page* / ti tlex /headxbody> ") ; out.printin("<h2>Please submit your information«/h2>"); //MeTOfl="post" поскольку метод сервлета service //вызывает doPost для обработки данных введенных в форму out.printlnf "«form method=\"post\" action =\"" + request.getContextPath() + "/firstservletX" >"); out.printlnf"«table border=\"0\"xtr><td valign=\"top\">"); out.printIn("Your first name: </td> <td valign=\"top\n>"); out.printlnf"«input type=\"text\" name=A"firstname\" size=\"20\">"); out.printin("</tdx/trxtrxtd valign=\"topX">"); ’ out. printing Your last name: </td> <td valign=\"topX">"); out.printlnf"«input type=\"text\" name=\"lastname\" size^\"20\">"); out.printlnf "</tdx/trxtrxtd valign=\"top\">")? out.printIn("Your email: </td> <td valign=\"top\">"); out.printlnf"«input type=\"text\* name=\"email\" size=\"20\">"); out .printlnf "</tdx/trxtrxtd valign=X"topX">") ; out.printlnf "«input type=\"submit\" value=\"Submit InfoX "x/tdx/tr>"); 16 | Глава 1. Создание сервлетов и JSP
out .printin ("</tablex/form>*); out. print In ("</bodyx/html>"); }//doGet public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.lOException { //отображаем имена и значения параметров Enumeration paramNames = request.getParameterNamesO; String parName;//здесь хранится имя параметра boolean emptyEnum = false; f % if (! paramNames.hasMoreElements()) emptyEnum = true; 11 устанавливаем тип Mime ответа в "text/html" response.setContentTypef"text/html"); // используем PrintWriter для отправки данных клиенту java.io.PrihtWriter out = response.getWriter(); // Начало формирования HTML-содержимого out .printin ("<htmlxhead>"); out.println( "<title>Submitted Parameters</titlex/headxbody>"); if (emptyEnum) { » out.println( "<h2>Sorry, the request does not contain any parameters</h2>"); ) else { out.printIn( "<h2>Here are the submitted parameter values</h2>"); } 1 while(paramNames.hasMoreElements())( parName = (String)paramNames.nextElement(); out.printIn( "<strong>" + parName + "</strong> : " + request.getParameter(parName)); out.println("<br />"); }//while out. printin (" < /bodyx/html>"); }// doPost » Создание сервлетов | 17
Вы, возможно, заметили, что и doGet (), и doPost () могут вызвать исключения ServletException и lOException. Сервлет может вызвать lOException, поскольку вызов метода response.getWriter() (как и метода Printwriter, close ()) может вызвать это исключение. Методы doGet () и doPost () могут вызвать исключение ServletException, чтобы сигнализировать о возникновении ошибки в ходе обработки запроса. Например, если сервлет определяет факт нарушения безопасно- сти или другие проблемы с запросов, в тело методов doGet () и doPost () можно включитьследующий код: //обнаружена проблема, препятствующая корректной обработке запроса throw new ServletException("Сервлет не может обработать этот запрос."); Рис. 1.1 демонстрирует отображение в браузере выходной информации метода doGet(). Рис. 1.1. Выходная информация метода doGet() См. также Рецепт 1.3 по компиляции сервлета; рецепт 1.4 по упаковке сервлетов и JSP; рецепт 1.5 по созданию дескриптора развертывания; главу 2 по развертыванию сервлетов и JSP; главу 3 по именованию сервлетов; документацию по javax. servlet .http: http://ja.va. sun.'com/j2ee/1.4/docs/api/javax/servlet/http/package-siunmary.html', руководство от Sun Micro- systems no J2EE: http://java.sun.cotn/j2ee/tutoTial/1^3~fcs/doc/J2eeTutorialTOC.html', книгу Jason Hunter «Java Servlet Programming» (O'Reilly). 18 | Глава 1. Создание сервлетов и JSP
Рис. 1.2. Выходная информация метода doPost() сервлета 1.2 Создание JSP Задача Требуется создать страницу JSP и включить ее в web-приложение. Решение Создать JSP в виде простого текстового файла, используя при необходимости шаб- лонный текст HTML. Сохранить файл с JSP на верхнем уровне иерархии данного web- приложения. ') Обсуждение Компонент JSP (JavaServlet Pages - серверные страницы Java) - тип сервлета, создан- ный для исполнения роли пользовательского интерфейса в web-приложениях Java. Разработчики пишут JSP в виде текстовых файлов, сочетающих код HTML и XHTML, XML-элементы и встроенные действия и команды JSP. Изначально JSP разрабатывалисЕ| согласно модели встроенных сценарных инструментов на стороне сервера, как и технология Создание JSP | 19
ASP от Microsoft, однако JSP развивались в сторону ориентации на XML-элементы, включая элементы, создаваемые пользователем, иди пользовательские теги, как главный метод генерирования динамического web-содержимого. Обычно имена файлов JSP имеют расширение .jsp, например mypage.jsp. При первом запросе клиентом страницы JSP или при перекомпиляции JSP, разработчиком (см. главу 5) web-контейнер транслирует этот тексто- вый документ в сервлет. Спецификация JSP 2.0 называет преобразование JSP в сервлет фазой трансляции. При этом полученный экземпляр сервлета называется объект-реализация страницы. Ж Компилятор JSP (например, компонент Jasper сервера Tomcat) автоматически преобразует исходный текстовый документ в сервлет. Web-контейнер созд. экзем- пляр полученного сервлета и делает сервлет доступным для обработки запросов. Эти задачи выполняются незаметно Для разработчика, которому совсем не нужно зани- маться транслированным исходным кодом сервлета (хотя он может проверить этот код, чтобы увидеть, что происходит за сценой, что всегда поучительно). Разработчик сосредотачивает внимание на динамическом поведении JSP и на трм, какие элементы или пользовательские теги в ней используются для генерации ответа. Создание JSP в виде текстового документа, а не в виде исходного кода на языке Java позволяет профессиональному дизайнеру работать над графикой, HTML или DHTML, оставляя XML-теги и динамическое содержимое программистам. В примере 1.2 показана JSP-страница, отображающая текущую дату'и время. Этот пример показывает, как импортировать и использовать библиотеку пользовательских тегов, детали этого описаны в главе 23. В этом коде также используется стандартное действие j.sp: us eBean, встроенный XML-элемент, который можно применять для соз- дания нового объекта Java, используемого на странице.. Ниже перечислены основные пункты написания JSP. 1. Откройте текстовый редактор или редактор среды программирования, выделяющий синтаксис JSP цветом. 2. Если вы создаете JSP для обработки HTTP-запросов, введите код HTML так, как вы это делаете для HTML-файла. 3. Включите в текст, в начало файла, необходимые директивы JSP, например директиву taglib из примера, 1.2. Директивы должны начинаться с символов <%@ 4. При необходимости введите также стандартные действия или пользовательские теги. 5. Сохраните полученный файл с расширением .jsp в каталоге, который вы отвели под JSP. Обычно это каталог верхнего уровня web-приложения, который вы создаете в своей файловой системе. Некоторые JSP создаются как XML-файлы, их еще называют JSP-документами, они состоят исключительно из корректных XML-элементов и атрибутов. Спецификация JSP 2.0 рекомендует давать таким файлам расширение .jspx. См рецепт 5.5 ’ для уточнения деталей о JSP-документах. 20 | Глава 1. Создание сервлетов и JSP
Пример 1.2. JSP-файл для отображения даты <%— исполь'зуём директиву ’taglib’ чтобы получить доступ к тегам ядра JSTL 1.0; uri библиотеки JSTL 1.1 "http://java.sun.сот/jsp/jstl/core" —%> <%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %> <%— используем стандартное действие’jsp:useBean’ для создания объекта Date; этот объект становится атрибутом с областью видимости - страница —%> <jsp:useBean id="date" class="java.util.Date" /> <html> <head>«ttitle>First JSP</title></head> <body> <. <h2>Here i-s today's date</h2> <c:out value="${date}" /> </body> </html> Чтобы увидеть результат работы этого файла в браузере, запросите файл, напечатав в поле адреса браузера http://localhost:8080/home/firstJ.jsp. Имя данного файла - firstJ.jsp. Если это первое обращение к данной странице, вы почувствуете задержку, связанную с тем, что JSP-контейнер преобразует текстовый файл в исходный код Java и затем ком- пилирует его в сервлет. ;S * • Задержки можно избежать путем предварительной компиляции (прекомпиляции) JSP Если вы запросите JSP с параметром jsp_precompile=true, Tomcat транслирует J । .?• JSP, но не будет посылать ответ на этот запрос. Вот пример* http://ldcalhost:8080/ *’ home/firstJ.jsp?jsp_precompile=true. На рис. 1.3 показан вывод JSP в браузере. Если в меню браузера вы выберете «Показать в виде HTML», чтобы увидеть исходный код страницы, то не увидите никакого специального синтаксиса JSP: ни символов ком- ментария (<%— —%>), ни директиву taglib, ни действие j sp: useBean, ни тег с: out. Сервлет посылает клиенту только шаблонный текст и сгенерированную строку с датой. См. также Рецепты 5.1—5.3 по прекомпиляции JSP; главу 2 по развертыванию JSP; рецепты 1.1-1.3 по созданию и компиляции сервлетов; рецепт 1.4 по упаковке сервлетов и JSP; рецепт 1.5 по созданию дескриптора развертывания; руководство от Sun Microsystems по J2EE: http:// java.sun.com/j2ee/tutorial/l_3-fcs/doc/J2eeTutorialTOC.html’, книгу Hans Bergsten «JavaServer Pages» (O'Reilly). Создание JSP | 21
Рис. 1.3. Вывод страницы first J.jsp в браузере 1.3 Компиляция сервлетов Задача* Сервлет написан, необходимо компилировать его в class-файл. Решение Убедитесь, что servletjar (для Tomcat 4.1.24) или servlet-api.jar (для Tomcat 5) нахо- дятся там, куда указывает ваш пользовательский путь к классам (classpath). Используйте j avac, так же, как вы это делаете для компиляции любых других исходных файлов Java. Обсуждение Для компиляции вам необходимо иметь классы для сервлета там, куда указывает путь к классам. Эти классы находятся в следующих пакетах: • javax. servlet; • javax.servlet.http. Tomcat 5 поддерживает версию 2.4 API сервлетов; J AR-файл, путь к которому вам необ- ходимо поместить в путь к классам, находится в каталоге: инсталляционный каталог Тот- cat>/common/lib/servlet-api.jar. Tomcat 4.1.24 поддерживает версию 2.3 API сервлетов. Классы сервлетов находятся в инсталляционный каталог Tomcat>/common/lib/servlet.jar. 22 | Глава 1. Создание сервлетов и JSP
Для BEA WebLogic 7.0 классы сервлетов и другие подпакеты пакета javax.(TO есть javax.ejb, javax.mail, javax.sql) размещаются в каталоге инсталляционный каталог WebLogic >/ weblogic700/server/lib/weblogic.jar. tjr— * ’ Если для компиляции классов сервлетов вы используете Ant, следуйте рецепту 4.4, не передавайте Go и не собирайте $200. Этот рецепт посвящен теме использования * f Ant для компиляции сервлетов. Если вы используете интегрированную среду разработки (IDE), следуйте ее инструкциям, чтобы поместить путь к файлу JAR в путь к классам (classpath). Следующая ниже командная строка компилирует сервлет из каталога src и размещает откомпилированный класс в каталогах, относящихся к его пакету, внутри каталога build. javac -classpath К:\tomcat5\jakarta-tomcat-5\dist\coinmon\lib\servlet-api. jar -d ./build ./src/FirstServlet.java Чтобы команда успешно выполнилась, необходимо предварительно перейти в роди- тельский каталог каталога src. Рецепт 1.4 описывает типичную структуру каталогов для разработки web- приложений, включая и каталог src. Если сервлет использует и другие библиотеки, то путь к этим JAR-файлам следует также включить в путь к каталогам. В приведенной командной строке Подключается только JAR- файл servlet-api.jar. В этой команде также необходимо заменить путь к инсталляционному каталогу Tomcat на тот, где Tomcat инсталлирован на вашем компьютере. К: \tomcat5\jakarta-tomcat-5\dist\coiranon\lib\servlet-api. jar Приведенная командная строка используется для встроенного компилятора javac, поставляемого в составе JDK (Java Software Development Kit - набора средств для разработки для Java) от Sun Microsystems. Чтобы командная строка работала правильно, необходимо в переменной окружения PATH задать путь к JDK. К примеру, для основанной на Unix системе Mac OS X 10.2 в переменную PATH следует включить путь /usr/bin. На моей машине под Windows NT переменная PATH включает h:\2sdkl. 4.1J)]\bin. СМ. также Главу 2 по развертыванию JSP; главу 3 по именованию сервлетов; рецепт 1.4 по упа- ковке сервлетов и JSP; рецепт 1.5 по созданию дескриптора развертывания; руководство от Sun Microsystems по J2EE: http://java.sun.cotn/j2ee/tutorial/l_3-fcs/doc/J2eeTutorialTOC. htmT, книгу Jason Hunter «Java Servlet Programming» (O'Reilly). Компиляция сервлетов
1.4 Упаковка .сервлетов и JSP Задача Необходимо создать структуру каталогов для упаковки и создания WAR-файла (Web ARchive) для сервлетов и JSP. Решение Создать в файловой системе необходимую струпуру каталогов, затем, используя инструмент j аг или Ant, создать W AR-файл. Обсуждение За редким исключением, сервлеты и JSP обычно создаются как часть web-приложения. Создать структуру каталогов для размещения компонентов web-приложения (HTML-фай- лов, сервлетов, JSP, графики, библиотек JAR, возможно, видео- и аудиофайлов, XML-фай- лов конфигурации (например, дескриптор развертывания, см. рецепт 1.5)) относительно легко. Простейший вариант организации такой структуры - создать точный слепок схемы web-, приложения, после чего можно использовать инструмент jar для создания WAR-файла. Структура web-приложения, включающая подкаталог WEB-INF, является стандартной для всех web-приложений Java и задана в спецификации API сервлетов (в разделе web- приложения). Эта структура выглядит следующим образом, если каталог верхнего уровня имеет имя туарр. /туарр /images /WEB-INF /classes /lib. Спецификация сервлета определяет подкаталог WEB-INF и два его дочерних ката- лога classes и lib. Подкаталог WEB-INF содержит дескриптор развертывания приложе- ния web.xml. Файлы JSP и HTML находятся в каталоге верхнего уровня (в данном случае туарр). Классы сервлетов и JavaBean и прочие вспомогательные классы размещаются в каталоге WEB-INF/classes, в структуре каталогов, соответствующей именам их паке- тов. Если полностью квалифицированное имя класса сервлета com.myorg.MyServlet, то этот класс сервлета должен находиться в WEB-INF/classes/com/myorg/MyServlet.class. Каталог WEB-INFAib содержит все необходимые приложению библиотеки JAR, такие, как драйверы баз данных, log4j.jar, а также те, что требуются для использования библио- теки стандартных тегов JSP (JavaServer Pages Standard Tag Library) (см. главу 23). 24 I Глава lf Создание сервлетов и JSP
Когда все готово для перевода приложения в формат WAR, перейдите в каталог верхнего уровня. Наберите следующую ниже команду, вводя имя WAR-файла после ката- лога верхнего уровня вашего приложения. Эта командная строка работает и для Windows, и для Unix (я применял ее для Windows NT и Mac OS X 10.2): jar cvf myapp.war He забудьте точку в конце, она указывает инструменту jar включить в WAR-файл содержимое текущего каталога и его подкаталогов. Данная команда создает файл myapp. war в текущем каталоге. Заданное имя WAR-файла становится именем приложения и подразумеваемым путем к этому web-приложению. К примеру, когда приложение./иуарр.ишг развертывается £ в web-кон геинере, с ним обычно ассоциируется путь /туарр”. Чтобы из командной строки посмотреть содержимое полученного WAR-файла, вве- дите следующее. jar tvf alpine-final.war Ниже представлен пример результата выполнения этой, команды. Н:\classes\webservices\finalproj\dist>jar tvf alpine-final.war 0 Mon Nov 18 14:10:36 EST 2002 META-INF/ 48 Mon Nov 18 14:10:36 EST 2002 META-INF/MANIFEST.MF 555 Tue Nov 05 17:08:16 EST "2002 request, jsp 914 Mon Nov 18 08:53:00 EST 2002 respbrise.jsp 0 Mon Nov 18 14:10:36 EST 2002 WEB-INF/ 4 0 Mon .Nov 18 14:10:36 EST 2002 WEB-INF/classes/ • 0 Tue Nov 05 11:09:34 EST 2002 WEB-INF/classes/com/' > 0 Tue Nov 05 11:09:34 EST 2002 WEB-INF/classes/com/parkeryiver/ CONTINUED. . . Многие команды разработчиков для компиляции сервлетов и JSP и создания W AR- файлов используют Ant. Чтобы лучше пояснить обсуждаемую тему, приведу пример структуры каталогов для всеобъемлющего web-приложения, содержащего разные сервлеты, JSP, статичные HTML-файлы, а также графические и мультимедиа компо- ненты. При создании WAR-файла из структуры каталогов подобного типа с помощью Arit, необходимо отфильтровать те каталоги, которые вы не хотите включать в итого- вый WAR-файл, например находящиеся на верхнем уровне каталоги src, dist, и meta. myapp /build /dist /lib , /meta ' /src /web /images /multimedia Упаковка сервлетов и JSP | 25*
/WEB- IMF /classes /.lib /tlds /jspf *. ’ *' Необязательные каталоги WEB-lNF/tlds и WEB-INF/jspf могут содержать, соответ- ственно, файлы дескрипторов библиотек тегов и сегменты JSP (фрагменты JSP, &' । созданные для включения в другие JSP, такие, как включения на стороне сервера). См. также Главу 2 по развертыванию JSP; главу 3 по именованию сервлетов; книгу «.The deploy- ment sections of Tomcat: The-Definitive Guide» авторов Brittain и Darwin (O'Reilly);.руко- водство от Sun Microsystems nO J2EE: htlp://java.sun.coni/j2ee/tutorial/J_3rfc$/doc/ J2eeTutorialTOC.html. ’ - 1.5 Создание дескриптора развертывания Задача Создать дескриптор развертывания для web-приложения. Решение Создайте XML-файл с именем web.xml и разместите его в каталоге WEB-INF своего web-приложения. Если у вас нет образца файла web.xml, скопируйте его из специфика- ции сервлета версии 2.3 или 2.4 и используйте как стартовую точку. Обсуждение Дескриптор развертывания - очень важная часть web-приложения. В нем, в кратком виде, содержатся требования web-приложения. Его можно прочитать в большинстве 4 XML-редакторов. Web.xml файл - это то место, где вы: ' Г. • Регистрируете и создаете отображения URL на ваши сервлеты; • Регистрируете или задаете любые фильтры и слушатели приложения; , • Задаете начальные параметры контекста в виде пар имя/значение; • Конфигурируете страницы ошибок; Указываете начальные файлы приложения; • Задаете время простоя сеанса (тайм-аут); • Задаете настройки безопасности, управляющие тем, кто к каким web-компоненТам может обращаться, 26 | Глава 1. Создание сервлетов и JSP к . . . .. .............
И эхо только часть тех настроек, которые Ложно задать в файле web.xml. В данном рецепте показаны упрощенные версии дескрипторов развертывания в соответствии со спецификацией сервлетов 2.3 и 2.4, в дальнейших главах книги содержаться более детализированные примеры web.xtnl (см. раздел «См. также»). В примере 1.3 приведено простое web-приложение с сервлетом, фильтром, слушателем и элементом session-config, а также конфигурацией страницы ошибок. Файл web.xml из примера 1.3 использует DTD (Document Type Definition), в соответствии со спецификам цией сервлета 2.3. Главное отличие между дескрипторами развертывания версий 2.3 и 2.4 в том, что 2.3 использует DTD, а 2.4 базируется на XML-схеме. Вы увидите, что ранняя (2.3) версия web.xml в начале файла содержит объявление DOCTYPE, тогда как версия 2.4 ссыла- ется на XML-схему с помощью атрибутов пространства имен элемента web-app. Соответ- ственно, XML-элементы в примере 1.3 следуют в порядке, определимом в DTD. Пример 1.3. Дескриптор развертывания в соответствии с API сервлета 2.3 <?xml version=”1.О" encodings"ISO-8859-1”?> <IDOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-application_2_3 .dtd" <web-app> <display-name»Servlet 2.3 deployment descriptor</display-name> <filter> < f i1ter-name»RequestFi1ter</ fi1ter-name> <filter-class»com.jspservletcookbook.RequestFilter</filter-class> </filter> . <filter-mapping> <filter-name>RequestFilter</filter-name> <ur 1-pa t'tem> / * </url.-pat t em> </filter-mapping> .. <listener> <listener-class»com.jspservletcookbook.ReqListener</listener-class> </listener» <servlet> < servlet -name»MyServlet < / servlet -name» <servlet-class>ccxn.d spservletcookbook.MyServlet</servlet-class> </servlet» r <servlet mapping» <servlet-name> Myservlet </servlet-name» <ur1-pattern»/myservlet</url-pattern» <I servlet-mapping» <session-config> Создание дескриптора развертывания | 27
<session-timeout>15</sessiori-timeout> </sessi.on-config> <error-page> <error-code>404</error-code> <location>/err404.jsp</location> </error-page> </web-app> В примере 1.3 показан файл web.xml для приложения, имеющего только один сервлет, на который указывает путь <путь к KOHmeKcmy>/myservlet. Время простоя cearica задано в 15 минут. Если клиент запрашивает URL, который не удается найти, web-контейнер перенаправляет запрос на страницу /err404.jsp, в соответствии с кон- фигурацией error-page (страницы ошибок). Для всех запросов, как к статическому, так и к динамическому содержимому в данном контексте, используется фильтр с име- нем RequestFilter. При запуске web-контейнер создает экземпляр класса слушателя com.j spservletcookbook.ReqListener. Все, относящееся к примеру 1.3, относится и к примеру 1.4, за исключением того, что элемент web-app (в начале файла) ссылается на XML-схему и содержит атрибуты с характеристиками ее пространства имен. Кроме того, элементы в этом дескрипторе развертывания по версии 2.4 могут следовать в произвольном порядке. К примеру, вы можете по желанию сначала поместить перечень сервлетов и отображения, а потом уже слушателей и фильтры. Пример 1:4. Дескриптор развертывания по версии 2.4 „ <?xml versions"1.0" encodings"ISO-8859-1"?> <web-app xmlns«"http://java, sun.com/xml/ns/j2ee" xmlns :xsi="http://www.w3 .org/2001/XMLSchema-instance" xsi ischemaLocation* "http: //java. sun. ccm/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" versions"2.4"> <!-оставшаяся часть та же, что и в примере 1.3 после открывающего тега web-app —> </web-app> Дескриптор развертывания по версии 2.4 также содержит определения разных элементов, не входящих в версию 2.3: jsp-config, message-destination, f Л* message-destination-ref и service-ref. Синтаксис этих элементов ' появился в спецификациях JSP версии 2.0 и J2EE версии 1.4. См, также Главу 2 по развертыванию JSP; главу 3 по именованию сервлетов; главу 9 по кон- фигурированию дескрипторов развертывания для обработки ошибок; руководство от Sun Microsystems по J2EE: http://java.sun.com/j2ee/tutorial/l__3-fcs/doc/J2eeTutorialTOC.htinl. 28 | Глава 1. Создание сервлетов и JSP
ГЛАВА2 Развертывание сервлетов и JSP 2.0 Введение Данная глава описывает, что нужно сделать, чтобы написанные сервлеты и JSP начали работать (получать запросы) под управлением контейнеров сервлетов Tom- cat или BEA WebLogic Server 7.0. Обсуждение начинается с развертывания сервле- тов и JSP, то есть включения их в работу под Tomcat и WebLogic, индивидуально или в составе web-приложения. Разработка и компиляция сервлета или JSP с помощью интегрированной среды разработки (IDE) - это одно. И совсем другое - сделать так, чтобы эти web-компо- ненты начали отвечать на HTTP-запросы. Развертывание для web-программ заключается в помещении их на обслуживание в web-контейнер, например Tomcat, или другой сервер приложений, например BEA WebLogic Server 7.0. В следующие далее рецепты подробно вписывают развертывание сервлетов и JSP на базе этих web- контейнеров, первые рецепты - индивидуальное развертывание, а последующие - развертывание в составе web-приложения. Для указанных целей обычно используется прекрасный инструмент сборки и автоматизации Jakarta Ant (открытое ПО). Он упоминается, где уместно, в следующих ниже рецептах, а глава 4 полностью посвящена инсталляции и использованию Ant 2.1 Развертывание отдельного сервлета Задача Вы хотите взять откомпилированный сервлет и инсталлировать его Tomcat, чтобы убедиться в его работоспособности. Вы делаете предварительную проверку и не собираетесь тратить время на сборку законченного web-приложения для сервлета. 29
решение Копируйте файл своего класса и вставьте его в web-приложение по умолчанию сервера Tomcat (если вы ранее инсталлировали какое-либо web-приложение, то вставьте в него), затем обратитесь к нему с запросом, используя вызывающий (invoker) сервлёт. Или используйте для временного перемещения файла с классом в приложение по умолчанию файл build.xml инструмента Ant. Обсуждение Иногда, когда вы разрабатываете сервлет, вам необходимо просто посмотреть, работает ли он. Если сервлет не зависит ют других сервлетов или компонентов всего приложения, его работоспособность можно проверить на сервере Tomcat, вставив файл класса (включая каталоги, относящиеся к его пакету) в приложение по умолчанию сервера Tomcat. Обычно это приложение находится в <инсталляционный-каталог-Тотса1 >/web-apps/ROOT. Если класс сервлета имеет полностью квалифицированное имя j spservletcook- book. CookieServlet, то можно следующими действиями, вручную запустить этот сервлет под Tomcat. 1. Остановите сервер Tomcat, выполнив командный сценарий <инсталляЦионный- каталог-Tomcat >/bin/shutdown или аналогичный сценарий, предлагаемый адми- нистратором вашего сервера. Альтернатива - остановить приложение по умолчанию, задав в браузере такой URL: http:// localhost:8080/manager/stop?path=/. 2. Создайте в каталоге <инсталляционный-каталог-Тотсш >/ webapps/ROOT/WEB- INF/classes подкаталог jspservletcookbook (если каталога classes нет, создайте его). 3. Вставьте файл класса CookieServlet в каталог <инсталляционный^каталог-Тот- cat >/ webapps/ROOT/WEB-INF/classes/ jspservletcookbook. 4. Запустите сервер Tomcat, выполнив командный сценарий <инсталляционный-ката- лог-Tomcat >/bin/startup или аналогичный от администратора вашего сервера. Альтернатива - запустите приложение по умолчанию, запросив в браузере следующий URL - http:// localhost:8080/manager/start?path=/. 5. Запросите сервлет в браузере, набрав URL ltttp://localhost:8080/servlet/jspservletcook- book. CookieServlet. Вы возможно скажете: «Должна существовать более элегантная альтернатива этой мед- ленной ручной инсталляции отдельного сервлета». Все правильно, этот процесс можно автоматизировать с помощью Jakarta Ant. Если вы установите у себя Ant, как описано в главе 4, файл build.xml из примера 2.1 выполнит тот же процесс проверки сервлета. Поместите этот сборочный файл в подходя- щий каталог. Создайте в этом же каталоге файл global.properties, настроенный в соответст- вии с вашими потребностями (см. пример 2.2). Находясь в командном окне, перейдите в этот каталог и наберите ant. Все остальное, включая запуск и остановку web-приложе- ния по умолчанию сервера Tomcat, сделает Ant. i 30 | Глава 2. Развертывание сервлетов и JSP
Пример 2.1. Инсталляция сервлета в приложение по умолчанию <project name-"Cookbook" default="deploy-servlet" basedir="."» «taskdef names"start" classname="org.apache.catalina.ant.StartTask’ /> <taskdef names"stop" classname="org*apache.catalina.ant.StopTaSk" /> <>—загрузка ряда глобальных свойств —> «property file="global.properties" /> «target name="init" descriptions"initializes some properties."> «echo message="Initializing properties."/> «property name="build" values".\buiId" /> «property names"src" values".\src* /> <1—Для корневого приложения путь к контексту - только слеш "/*; см. цели start и stop, которые уже включают слеш в состав шаблона URL «property name="context-path" values"" /> «/target» «target names "prepare", depends= " init "> • ч «echo message»"Cleaning up the build directory."/» «delete dir="${build}"/> «mkdir dir="${build}"/> «/target» <!—Устанавливаем путь к классам на.jar файлы Tomcat —» «path id="classpath"» «fileset dir="${tomcat.dir}/common/lib"» «include name="*.jar" /» «/fileset» «fileset dir="${tomcat.dir}/common/endorsed"» «include names"*.jar" /» «/fileset» «/path» <!—стартуем web-приложение по умолчанию Tomcat —» 4 «target names"start" descriptions"Starts the default Web application"» «echo messages"Starting the default application...."/» «start; url="${url}" usernames"${username}" passwords"${password}" path=" / $ {context-path}" /> Развертывание отдельного сервлета | 31
</target» <!—останавливаем web-приложение по умолчанию Tomcat —> <target name="stop" descriptions"Stops the default Web application"» <echo messages"Stopping the application...."/> <stop url="${url}" usernames"${username}" passwords"${password}" patty="/${context-path}* /> </target» <•— останавливаем web-приложение по умолчанию, компилируем сервлет, добав- ляем его к web-приложению по умолчанию, затем стартуем web-приложение по умолчанию —> ctarget name="deploy-servlet* depends= "prepare" descriptions "Compile the specified servlet, then move it into Tomcat's default Web application.’> <echo messages"Stopping the default Tomcat application...."/> <antcall targets*stop"/» <echo message="Compiling the servlet...."/» <javac srcdir="${ferc}" destdifs"${build}"» cinclude name="${conpiled.servlet}.java* /» cclasspath refid="classpath"/> </javac» <echo messages "Copying the servlet to Tomcat ROOT web application..."/» <copy todir= * ${tomcat.webapps}/WEB-INF/classes"> <fileset dir="${build}" /> </copy» <echo messages"Starting the default application...."/> <antcall- ta»get="start"/» </target» </project» Файл global.properties, размещаемый в том же каталоге, что и build.xml, выглядит так, как показано в примере 2.2. ь. 32 | Глава 2. Развертывание сервлетов и JSP
Пример 2.2. Файл global.properties для Ant tomcat .webapps=k: /jakarta-tomcat-4.1.12/webapps/R00T tomcat.dir=k:/jakarta-tomcat-4.1.12 url=http://localhost:8080/manager compiled.servlet=CookieServlet username=tomcat pas sword=tomcat Файл globaLproperties состоит из пар имя- свойства=значение. Иными словами, каждая строка файла состоит из символов, представляющих имя свойства (которое может включать символ точка ), символа *=’, за которым следуют символы, представляющие значение. Online-руководство по Jakarta Ant находится в http://jakarta.apache.org/ant/manual/ index.hml. А вот что делает файл buildxml. 1. В элементах taskDef определяет две задачи с именами start и stop. Эти задачи используются в файле buildxml позднее, в элементах target. Они позволяют исполь- зовать инструмент развертывания приложений сервера Tomcat из ваших файлов Ant. 2. Использует задачу property для загрузки набора свойств, определяемых в файле global.properties. Это означает, что свойство с именем tomcat. dir с этого момента можно использовать в файле build.xml. Элемент path использует свойство tomcat, dir, включая его значение (в данном примере «k:/jakarta-tomcat-4.1.12») как часть определения пути к классу (classpath). Получить значение этих необходимых свойств можно, используя ссылку вида: ${tomcat.dir}. В любой момент времени, когда вы захотите изменить значения этих свойств перед запуском Ant, нужно йросто напечатать новые значения в файле свойств global.properties в текстовом редакторе. 3. Создает цель init, которая выводит сообщение на консоль и создает три свойства (build, src и context-path). Значения этих свойств доступны только после завершения выполнения цели init. Например, если цель prepare не содержит «init» в качестве значения атрибута depends (зависит от), цель deploy-servlet, зависящая от prepare (depends=prepare), не сможет использовать значения свойств, определяемых в init. 4. Определяет цель под названием prepare. 5. Создает повторно используемый путь к классам (с идентификатором «classpath»), включая в него все JAR-файлы, находящиеся в паре каталогов Tomcat. 6. Создает цели start и stop. Они выдают на консоль сообщение и затем вызывают соответствующие задачи (например, stop), которые были определены в элементах taskDef в начале файла buildxml. Цели start и stop в действительности вызываются целью deploy-servlet, которая включает в себя все остальные цели. 2-1545 Развертывание отдельного сервлета | 33
7. Создает цель deploy-servlet. Эта цель выполняет всю основную работу в файле buildxml. Заметьте, что атрибут depends (зависит от) имеет значение «prepare». Это значит, что до выполнения инструкций, содержащихся з цели deploy-servlet, Ant должен сначала выполните цели init и prepare. Поскольку цель prepare зависит от цели init, deploy-servlet вызывает prepare, которая в свою очередь вызывает цель init, от которой она зависит. Таким образом, запуская цель deploy-servlet, вы инициируете цепочку целей init -э prepare deploy-servlet. Используя элемент ant call, с помощью которого цель может явно вызвать другую цель, deploy- servlet вызывает цели start и stop. Таким образом, deploy-servlet: а) останавливает приложение по умолчанию сервера Tomcat; b) компилирует сервлет с помощью задачи javac. Задача javac подключает сервлет, заданный свойством compiled, servlet, значение которого задается в файле glo- bal.properties. * , 8. Копирует откомпилированный сервлет в каталог WEB-INF/classes приложения по умол- чанию сервера Tomcat Если каталог classes не существует, задача сору создаст его. 9. Стартует web-приложение по умолчанию, после чего к нему можно обращаться из браузера. ' “ z См. также Книгу «The deployment sections of Tomcat: The Definitive Guide» авторов Brittain и Darwin (O'Reilly); рецепты 2.2,2.4, и 2.6; online-руководство no Jakarta Ant по адресу http: //jakarta.apache.org/ant/manual/index.html. 2.2 Использование элемента Context в файле server.xml для Tomcat Г Задача Требуется развернуть сервлет на Tomcat 4.1.x и затем, не перезапуская web-кон- тейнер Tomcat, развернуть его снова. Решение Разверните сервлетжак часть элемента Context файла server.xml сервера Tomcat. Обсуждение Для того чтобы вставить перекомпилированный класс сервлета вместо существующего класса сервлета и запустить этот сервлет, не перезагружая Tomcat, сделайте следущее. Найдите элемент Context вашего web-приложения или создайте новый элемент Con- text в файле <инсталляционный-каталог-Тотса1>/соп//5еп>ег.хтп1. Элемент Context должен быть вложен в элемент Host, представляющий виртуальный хост, под которым работает ваше web-приложение. 34 | Глава 2. Развертывание сервлетов и JSP
' Установите значение атрибута reloadable этого элемента Context в true. Это заставит Tomcat отслеживать изменение содержимого каталоге! WEB-INF/classes и WEB- INF/lib. При обнаружении изменений Tomcat автоматически перезагрузит web-приложение. Элемент Context файла server.xml выглядит примерно следующим образом: <Context className="org.apache.catalina.core.Standardcontext" z crossContext="false" reloadable="true" mapperClass="org.apache.catalina.core.StandardContextMapper" useNaming="true" debug=“0* swallowOutput="false" privileged="false" displayName="Home Web App" wrapperClass="org.apache.catalina.core.Standardwrapper" docBase="h:\home" cookies="true" path="/home" cachingAllowed="true" charsetMapperClass="org.apache.catalina.util.CharsetMapper" Атрибут path представляет путь к контексту приложения. Атрибут docBase уазы- вает каталог верхнего уровня данного приложения. Большинство прочих атрибутов имеют значения, которые используются совместно с другими элементами Context. К примеру,-cookie=" true" свидетельствует, что контекст для идентификации сеанса будет использовать cookie, a crossContext="false" запрещает сервлетам этого web-приложения перенаправлять запросы к другим web-приложениям, работающим на этом виртуальном хосте. ! ... Установка атрибута reloadable в true ведет к потере производительности наэтапе выполнения, поэтому рекомендуется использовать такую конфигурацию 1^—только на период разработки приложения. При такой конфигурации, когда вы вставите в web-приложение новый класс сервлета вместо старого, Tomcat спустя некоторое время отобразит на консоль сообщение. Ниже представлен пример сообщения, выдаваемого при динамической перезагрузке сервлета. WebappClassLoader: Resource 1/WEB-INF/classes/com/jspservletcookbook/ OracleTest.class1 was modified; Date is now: Sun Feb 02 22:17:41 EST 2003 WaS: Sun Feb 02 21:38:52 EST 2003 См. также Книгу «The deployment sections of Tomcat: The Definitive Guide» авторов Brittain и Darwin (O'Reilly); рецепты 2.1, 2.4, и 2.6; документацию Jakarta Tomcat по элементу Context http:// jakarta.apache:org/tomcat/tomcat-4.1-doc/config/context.html. Использование элемента Context в файле server.xml для Tomcat | 35
2.3 Развертывание отдельного сервлета в WebLogic ; Задача Требуется взять откомпилированный сервлет и инсталлировать его в BEA WebLogic Server 7.0, чтобы убедиться в его работоспособности. Решение Копируйте файл класса и вставьте его в web-приложение по умолчанию сервера WebLogic (если вы ранее инсталлировали какое-либо приложение, вставьте в него). Используйте консоль администрирования WebLogic для изменения файла web.xml и дайте сервлету узнаваемое имя, по которому будете обращаться к нему из браузера, или используйте файл сборки Ant для временного перемещения этого файла в приложе- ние по умолчанию сервера WebLogic. Обсуждение ' Приложение по умолчанию сервера WebLogic размещается в каталоге <инсталляцион- ный-каталог-WebLogio/user_projects/<Moii-doMeH>/applications/DefaultWebApp. При типич- ной инсталляции сервера WebLogic 7.0, в приложении по умолчанию мало что есть, только дескриптор развертывания web.xml и несколько файлов изображений. Чтобы к приложению по умолчанию добавить сервлет, вставьте свой класс сервлета, включая связанные с его паке- том каталоги в каталог DefaultWebApp/WEB-INF/classes. Если вы делаете это в первый раз, может потребоваться создать каталог classes. Перед повторным развертыванием web-прило- жения (см. описание из рецепта 2.4) измените файл web.xml, чтобы дать сервлету имя (это проще сделать с помощью консоли администрирования). С помощью консоли администрирования отредактируйте файл web.xml, чтобы дать новому сервлету зарегистрированное имя и поставить ему в соответствие элемент serv- let-mapping. Для этого также можно использовать и другой инструмент, например WebLogic Builder (рецепт 2.9) или текстовый редактор. На рис. 2.1 показано отображение приложения по умолчанию в консоли администрирования. Щелкните по «Edit Web Appli- cation Deployment Descriptors...». ' Будет показан экран, изображенный на рис. 2,2. Этот экран позволяет легко в графи- ческом виде отредактировать файл web.xml любого web-приложения (в данном случае приложения по умолчанию). С помощью данного графического редактора создайте элементы servlet и servlet- mapping для только что добавленного сервлета. Не забудьте щелкнуть по кнопке «Web Descriptor» в левой колонке окна на рис. 2.2 и затем сохранить внесенные в web.xml измене- ния. Эго действие приведет к перезаписи файла web.xml уже с новыми элементами serv- let и servlet Tinapping. 36 | Глава 2. Развертывание сервлетов и JSP
Рис. 2.1. Консоль администрирования WebLogic После сделанного повторно разверните web-приложение, что сводится к простому щел- канью по нескольким гипертекстовым ссылкам в консоли. В левой колонке консоли, в узле <мойдомен> -> Deployments Web Applications дерева навигации по структуре, выберите имя своего web-прйложения. Отображаемое после этого окно показано на рис. 2.3. В полученной таблице HTML щелкните по вкладке «Deploy», а затем щелкните по кнопке «Undeploy». Чтобы повторно развернуть приложение, щелкните по вкладке «Deploy», а затем щелкните по кнопке «Deploy», см. рис. 2.4. Если сервлет, над которым вы работаете, уже присутствует в web-приложении, то вы можете копировать и вставить новый класс сервлета вместо старого в каталоге WEB- INF/classes web-приложения. Новая версия сервлета станет доступна сразу после этого, без необходимости использования консоли для повторного развертывания всего web- приложения. Для компиляции сервлета и копирования его в приложение по умолчанию сервера WebLogic также можно использовать файл (сборки) Ant. Файл сборки для примера 2.3 очень похож на тот, что использовался в рецепте 2.1, но с изменениями, учитывающими использование WebLogic, а не Tomcat. Развертывание отдельного сервлета в WebLogic | 37
Рис. 2.2. Редактирование web.xml в графическом виде Пример 2.3. Использование файла Ant для сервлета на сервере WebLogic «project name="Cookbook" default="deploy-servlet" basedir=". <property file="wl.properties" /> «target name="init" description;;"Initializes some properties."> <echo message="Initializing properties."/> «property name="build" value=".\build" /> «property name=”src" value=".\src" /> «/target> '•* «target name="prepare" depends="init"> «echo message="Cleaning up the build directory."/> «delete dir="${build}“/> «mkdir dir="${build}“/> </target> «path id="classpath"> «fileset dir="${wl.dir}\server\lib"> «include name="*.jar" /> __________________________ _ ______ «. _______:_________________ 38 | Глава 2. Развертывание сервлетов и JSP
Рис. 2.3. Использование консоли для повторного развертывания web-приложения </fileset> <:/path> ctarget names"deploy-servlet" depends="prepare" descriptions"Compile the specified servlet, then move it into WL's default Web application."> <echo messages"Compiling the servlet ${compiled.servlet}...."/> •£javac srcdir="${src}" destdir="${build}"> cinclude name="${compiled.servlet}.java" /> <classpath refid="classpath"/> </javac> <echo messages "Copying the servlet to WL default web application... "/> <copy todir=" $ {wl. webapp} /WEB-INF/classes" > <fileset dir="${build}" /> </copy> </target> • </project> i Развертывание отдельного сервлета в WebLogic | 39
i^Mtchlnn CJ|Network Channels E0it.w$t- Appiication D$p|gwnt.p«picK?rs... 'Monitoring, Notes] Configuration ],Targets.1. Deploy e Ootpioymtnts ^Applications SfiilEJB a&lweb Applications ® cartreats «DsftultWsbApp ®_a₽psdrij»ma_dlr . Owe» Sanies Components , £>iconn<dor> SlstartupS Shutdown j. В Osowlcss I CJjCOM > Я CJjobc »: в CJjms В Oinosssging Bridge OxML Ojta j В OsNMP £ 5MlEC OwebLoglc Tuxedo Connect j Ojolt ZlUvirtuei Hosts Д SB £?Msll Й СУгютэ Hi jB.OSocuri»___________. . ± Deployment Status by Target: twenyserrer Server false Счриул» t.| Deploy or redeploy this component to all targets Deployment Activity: Wed Sep 03 Unprepare application rnmrw«i wed Sep 03 appsdif-hcime Jirontwoenyeerrer V ° 10:52:12 EDT 2003 Wed Sep 03 10:52:12 EDT2003 Note: To configure additional deployment targets for this component, please move to the Targets* tab . Deploy Л *E**^fHT Puc. 2.4. Развертывание сервлета в графическом режиме Данный сборочный файл Ant сначала загружает набор свойств, содержащихся в файле wLproperties, который распологается в том же каталоге, что и сам сборочный файл. Обычно файл сборки имеет имя builcLxml, однако можно вызвать и другой файл сборки из этого же каталога, использовав опцию командной строки -buildfile, например: ant -build- file wl_build. xml. Файл wLproperties для данного примера приведен в примере 2.4. Пример 2.4. Файл wLproperties для сборочного файла Ant на сервере WebLogic wl.properties for WebLogic Ant build file wl .webapp=k:/bea/user_projects/bwpdomain/applications/DefaultWebApp wl.dir=k:/bea/weblogic700 compiled.servlet=test Цель deploy-servlet зависит от цели prepare, которая также определяется в файле сборки. Цель prepare в свою очередь в атрибуте depends содержит значение «init», что означает, Что цель init выполняется до цели prepare. Таким образом, цель deploy-servlet создает цепочку выполняющихся целей: init -»prepare -»de- ploy-servlet. И вот что делает файл сборки в целом. 40 | Глава 2. Развертывание сервлетов и JSP
1. Цель init создает два свойства (build и source), указывающие на каталоги сборки и исходных файлов. , 2. Цель prepare удаляет и затем создает заново каталог сборки. Таким образом, ката- лог сборки очищается. 3. Цель deploy-servlet компилирует сервлет в каталог сборки, азатем копирует его в каталог, задаваемый свойством wl. webapp (значение которого находится в файле wl.pwperties). Элемент path создает путь к классам (classpath) из путей к JAR-файлам, найденным в каталоге k:/bea/weblogic700/server/lib. Этот каталог Ant получает, расшифровывая фразу "${wl.dir) \server\lib", то есть присоединяя к значению свойства wl.dir строку "\servef\hb". с. См. также Рецепт 2.5; рецепты 2.7-2.10; разделы по развертыванию книги «WebLogic: The Definitive Guide» авторов Mountjoy и Chugh (O'Reilly); документацию для программи- стов WebLogic Server 7.0: http://e-docs.bea.com/wls/docs70/programming.html. 2. 4 Развертывание одиночной страницы JSP под Tomcat Задача Необходимо поместить JSP в web-приложение. Решение Скопируйте вновь созданный или исправленный файл JSP в каталог верхнего уровня приложения по умолчанию сервера Tomcat или другого развернутого на сервере прило- жения. Обсуждение Простейший способ проверить работу нового файла JSP - поместить его в каталог верхнего уровня приложения по умолчанию сервера Tomcat. Это приложение находится в каталоге <инсталляцио1Шый-каталог-ТотсаГ>/ыеЬарр$/КООТ/. Сервер Tomcat 4.1.x скомпилирует (или перекомпилирует, если вы вставили новый файл JSP вместо старого) страницу JSP и отобразит ее ответ на запрос в виде web-страницы. Вам не потребуется останавливать и запускать Tomcat с помощью управляющего приложения сервера, чтобы сделать новый файл JSP доступным для вашего web-приложения. Развертывание одиночной страницы JSP под Tomcat | 41
ч4 * Простое размещение файла JSP в развернутом web-приложении не сработает если JSP зависит от каких-либо ресурсов приложения, например от сервлетов, х****»**у' пользовательских тегов hih других классов Java, поскольку нет гарантий, что web- Прияожение, которое вы временно используете в качестве хоста для JSP, имеет доступ к этцм ресурсам. Если вы развертываете JSP отдельно от его web-приложения, вы можете поместить этот файл J§P в другое развернутое на Tomcat web-приложение, необязатедьно в прило- жение по умолчанию. Это сделает страницу JSP доступной пользователям приложения, без необходимости остановки и перезапуска сервера Tomcat. Помните» что файлы JSP должны находиться в каталоге верхнего уровня web-приложения, а вообще оно имеет следующую структуру каталогов, index. html default.jsp anotherJsp.j sp imagesI logo.jpeg : WEB-INF/classes/j spservletcookbook/myservlet.class WEB-INF/lib/helperclasses.jar s WEB-INF/1ib/uti1itids,jar WEB-INF/web. xml WEB-INF/mytags.tld Иными словами, каталог верхнего уровня содержит файлы HTML и JSP, а также каталог WEB-INF. Каталог WEB-INF содержит: • дескриптор развертывания web.xml, • каталог classes, содержащий каталоги, связанные с пакетом и классы сервлетов или классы поддержки, например, JavaBeans, • каталог lib, в котором находятся любые файлы Java-архивов (JAR), содержащие слу- жебные или вспомогательные классы, используемые вашим web-приложением, • при необходимости, файлы дескрипторов библиотек тегов (файлы с расширением .tld), • при необходимости, каталоги для изображений, файлов видео, XML-файлов или иных ресурсов web. См. также 1 Книгу «The deployment sections of WebLogic: The Definitive Guide» авторов Mountjoy и Chugh (O'Reilly); рецепты 2.1,2.2 и 2.6. 42 | Глава 2. Развертывание сервлетов и JSP
2.5 Развертывание одиночной страницы JSP на WebLogic Задача Необходимо быстро проверить работоспособность JSP без развертывания ее в составе нового web-приложения. Решение Скопируйте JSP и вставьте ее в каталог верхнего уровня приложения по умолчанию сервера BEA WebLogic 7.0, а затем запросите эту JSP из браузера. Обсуждение Файл JSP можно развернуть на WebLogic в «горячем режиме», в составе web-приложе- ния по умолчанию, без необходимости создания и развертывания целого web-приложения. Приложение по умолчанию находится в каталоге <uHcmajumifuoHHbiu-Kamano2-WebLogic>/ user-projects/<uMn-eaiue2o-doMeHa>/applications/DefaultWebApp/. Если поместить файл JSP в этот каталог (DefaultWebApp), JSP сможет получать запросы без повторного развертыва- ния web-приложения по умолчанию. Если файл со страницей JSP называется newfile.jsp, то URL для запросов к данной странице будет: http://localhost:7001/neyvfile.jsp. Отметьте отсутствие в этом URL пути к контексту или имени web-приложения. Если запрос адресо- ван к web-приложению по умолчанию, имена файлов JSP следуют в URL сразу за слешем (/), после части хост:порт (то есть после localhost:7001/). Повторим предыдущее предостережение: простое размещение файла JSP в развернутом web-приложении не сработает, если JSP зависит от каких-либо ресурсов приложения, например от сервлетов, пользовательских тегов или других классов Java, поскольку нет гарантий, что web-приложение, которое вы временно используете в качестве хоста для JSP, имеет доступ к этим ресурсам. В большинство случаев JSP уже является частью некоего web-приложения, а для повторного развертывания web-приложения существует несколько инструментов, в числе которых* Ant, компоновщик (Builder) BEA WebLogic и консоль администрирова- ния WebLogic. И наконец, можно копировать и вставить файл JSP в другое web-приложение на сервере WebLogic. Однако это приложение должно быть установлено в формате «развернутых каталогов», то есть приложение не должно находиться в форме архива (WAR или EAR). Просто поместите JSP-файл в каталог верхнего уровня этого приложения. Для приложе- ния с именем «пеыарр» этот каталог будет называться <инсталляционный-каталог- WebLogic >/ user-projects/<uMn вашего-doAteHa>/appli cation.s/newapp. Развертывание одиночной страницы JSP на WebLogic | 43
См. также ; v.„ Рецепт 2.3; рецепты 2.7-2.10; документацию для программистов сервера WebLogic http://e-docs.bea.com/wls/docs70/programming.html. 2.6 Развертывание web-приложения на сервере Tomcat Задача Развернуть на сервере Tomcat целое приложение. Решение Создайте файл сборки для Jakarta Ant. Ant может автоматически откомпилировать классы сервлетов, создать файл архива web-приложения (.war) и затем развернуть этот WAR на сервере Tomcat 4.1.x.- Обсуждение Для компиляции и развертывания web-приложения рекомендуется использовать инструмент автоматизации Jakarta Ant. Если впоследствии вы что-либо измените в приложении (например, сервлет или JSP), для упаковки и повторного развертывания достаточно будет* набрать одну командную строку с вызовом Ant. Не нужно будет вручную перекомпилировать измененный сервлет, создавать новый W AR-файл, запус- кать и останавливать Tomcat и повторно развертывать web-приложение. Другой метод развертывания web-приложения на Tomcat - поместить - каталог, содержащий web-приложение, в надлежащую структуру каталогов в каталоге webapps сервера Tomcat. Имя каталога web-приложения (например, туарр) после этого становится путем к контексту или именем нового web-приложения. Этот способ развертывания может не работать с другими серверами приложений и, следовательно, неэффективен при созда- нии переносимых приложений. Кроме того, поскольку этот способ ни в какой степени не автоматизирован, трудности будут сопутствовать и внесению изменений в любой класс сервлета или JavaBeans в каталогах такого web-приложения. В завершение обсуждения опишем, как настроить файл конфигурации server .xml сервера Tomcat, чтобы путь к контексту указывал на распакованный каталог web-прило- жения, где бы на сервере он ни находился. Как альтернатива созданию архивированного приложения (WAR), этот способ развертывания во время разработки и тестирования также может использоваться разработчиками под Tomcat. 44 | Глава 2. Развертывание сервлетов и JSP
Использование Ant для развертывания web-приложения Использование Ant для компиляции и развертывания приложения включает следующие шаги. 1. Выбор каталога для файла сборки Ant - buikLxml, любых файлов свойств buildprop- erties и всего содержимого web-приложения. 2. Создание каталогов для файлов с исходным кодом (Java) сервлета, файлов JSP и HTML, архивных файлов (JAR) компонентов, например драйверов баз данных, и архивного файла web-приложения (WAR-файла). Один из способов организации необходимой структуры каталогов показан на рис. 2.5. Рис. 2.5.Структура каталогов web-приложения 3. В этом примере каталог src содержит файлы с исходным кодом (Java) сервлетов и JavaBeans. Каталог web содержит файлы, находящиеся на верхнем уровне web-прило- жения, например JSP и HTML файлы Каталог meta содержит дескрипторы развертыва- ния (как минимум, web.xml). Каталог build - то место, где Ant компилирует исходные файлы. Результаты компиляции, в конце концов, будут находиться в каталоге WEB-INF/ classes web-приложения. Каталог lib предназначен для всех JAR-файлов, используе- мых приложением, например драйверов базы данных, и/или библиотек тегов. И, наконец, каталог dist содержит WAR-файл. 4. Создание исходного кода приложения и размещение всех связанных с ним файлов (например, файлы JSP и web.xml) в предназначенных для них каталогах. 5. Создание всех необходимых значений свойств в файле build.properties, эти свойства будут использоваться в файле build.xml в процессе компиляции и развертывания. Ниже свойства будут обсуждаться подробнее. 6. Запуск файла build.xml из командной строки. Для этого необходимо перейти в ката- лог, содержащий этот файл, и напечатать в командной строке ant. В примере 2.5 показан файл build.properties, упомянутый в шаге 5. Развертывание web-приложения на сервере Tomcat | 45
Необязательно называть Этот файл именно build.properties. Это имя используется просто по взаимной договоренности Можно назвать этот файл, например, global: props Пример 2.5. Файл build.properties для развертывания web-приложения tomcat .webapps=к:/Jakarta-tomcat-4.1.12/webapps/ tomcat.dir=k:/Jakarta-tomcat-4.1.12 url=http://localhost:8080/manager _ usernaxne=t omca t password=tomcat manager.path=$(tomcat.dir}/work/standalone/localhost/manager Эти свойства можно сделать доступными (импортировать) в файле buildxml с помощью следующей строки.- <property file="build.properties" /> Данная строка представляет задачу property или XML-элемент в файле buildxml. Например, значение свойства tomcat, di г" внутри XML-файла сборки Ant равно «к:/ jakarta-tomcat-4.1.12.» В примере 2.6 файл build.xml приведен полностью. Этот файл можно использовать для компиляции классов Java, создания WAR-файла и развертывания его в Tomcat - дос- таточно только запустить в командной строке ant. Главное преимущество использова- ния Ant - автоматизация процессов, которые без этого были бы достаточно сложными. Если, к примеру, потребуется изменить или добавить сервлет в web-приложение, то, чтобы заново откомпилировать и развернуть это приложение, достаточно просто запус- тить Ant. Сам файл buildxml достаточно сложен и демонстрирует использование ряда продвинутых возможностей Ant. Пример 2.6. Файл сборки Ant для развертывания web-приложения cproject name="Deplpy Project" default="deploy-application"> <taskdef name="deploy" classname="org.apache.catalina.ant.DeployTask" /> <taskdef name="undeploy" classname="org.apache.catalina.ant.UndeployTask" /> <property file="build.properties" /> <path id="classpath"> <fileset dir="${tomcat.dir}/common/lib"> cinclude name="*.jar" /> </fileset> <fileset dir=h ${tomcat.dir}/common/endorsed"> cinclude name="*.jar" /> </fileset> » </path> 46 | Глава 2. Развертывание сервлетов и JSP
«target name="init" descriptions"Initializes some properties."> <echo message^"Initializing properties."/* «property name="build" values" .AbuiId" /> <property name="src" values".\src" /> «property name="dist" values".\dist" /> «property name="lib" values".\lib" /> > «property name="web" values".\web" /> «property name="meta" values".\meta" /> «property name= "context-path" value’s"myapp" /> «/target* «target name="prepare" depends="init"> *• «echo messages"Cleaning up the build and dist directories."/* «delete dirsn $ {build}"/* «mkdir dir="${build}"/> «delete dir="${dist}"/> «mkdir dir="${dist}"/> «/target* \ «target name="deploy" descriptions"Deploys a Web application"* «deploy url=*${url}" usernames"${username}" passwords"${password}* path="/${context-path}" war="file:${dist}/${context-path}.war" /> «/target* «target name="undeploy" descriptions"Undeploys a Web application" if="already.deployed"^ «undeploy url=" $ {url}" usemame=" $ {username}" passwords«$ {password}" paths"/ ${context-path}* /> «/target* «target name="create-war" descriptions"creates a web application archive file"* ' «war destfile="${dist}/${context-path} *war" webxml="${meta}/web.xml"> «classes dir»"${build} 7* «lib dir»"${lib}"/> «fileset dir="${web}"/* «/war* «/target* «target name="deploy-application" depends="prepare" descriptions"Compile the web application...."> «echo message»"Undeploying the application only if it's deployed..."/* - «available file="${manager.path}/${context-path}:war" property="already, deployed"/> Развертывание web-приложения на сервере Tomcat | 47
<antcall target?"undeploy"/> <echo message^"Compiling the application files..,"/> «javac srcdir="${src}" destdir="${build}"> «include name="*.java" /> «classpath refid="classpath"/> </javac> «echo messages"creating the WAR file...."/> л «antcall targetsncreate-war"/> «antcall targets"deploy"/> «/target> </project> Цель create-war использует Ant-задачу war, для генерации файла WAR на основе определенных значений атрибутов и вложенных элементов. Например, файл web.xml, который будет включен в WAR, задаётся как значение атрибута webxml задачи war. Кроме, того, классы, которые будут включены в каталог WEB-lNF/classes WAR-файла, задаются следующим вложенным элементом задачи war. «classes dir="${build}"/> Приятная новость, касающаяся этого элемента classes и вложенных элементов lib и fileset, заключается в том, что все вложенные в build, lib и web каталоги авто- матически включаются в WAR. К примеру, каталог web включает каталог images, содержащий различные GIF-файлы приложения. Каталог images включается на верхний уровень WAR-файла, вместе со всеми HTML и JSP файлами, хранимыми в каталоге web всего лишь одним вложенным элементом (см. ниже). «fileset dir="${web}"/> Рассмотрим также цель deploy-application, составляющую костяк файла build, xml. Если вы правильно установили значение переменной окружения РАТИ, чтобы она указывала на компонент Ant, цель deploy-application будет автоматически вызвана сразу после того, как вы напечатаете в командной строке ant- Сначала эта цель ищет, не было ли данное web-приложёние уже развернуто на Tomcat ранее. Данная функция включена, поскольку предполагается запускать этот файл неод- нократно, а не только при первом развертывании web-приложения. Строка, отвечающая за это, использует задачу available, которая устанавливает значение соответствующего свойства в «true», только если файл, заданный атрибутом file, существует. «available file="${manager.path}/${context-path}.war" property="already.deployed"/> «antcall targets"undeploy"/> Если такой файл существует, это означаёт, что приложение Tomcat Manager уже развернуло этот WAR-файл и значение свойства already.deployed устанавливается в «true». Это позволяет файлу build.xml по условию удалить ранее развернутое приложе- ние перед его повторным развертыванием в измененном вйде. Иными словами, web-прило- 48 | Глава 2. Развертывание сервлетов й JSP
жение удаляется с сервера, только если оно когда-то уже было развернуто (в противном случае запуск цели undeploy сгенерирует ошибку, на чем выполнение файла сборки прервется). Цель undeploy запускается, только если свойство already.deployed установлено в «true». •«target name="undeploy" descriptions"Undeploys a Web application" if = "already. deployed". > Задача ant call вызывает выполнение другой цели файла, подобно тому как вызываются методы. И, наконец, цель deploy-application использует задачу j avac' для компиляции сервлетов приложения в каталог build, а затем посредством задачи ant call создает WAR-файл и развертывает новое или измененное web-прило- жение на сервере Tomcat. При работе цель выдает на консоль сообщения, помогающие разработчику понять, что происходит. «target name="deploy-application" depends="prepare" descriptions"Compile the web application...."» <echo messages"Compiling the application files..."/» < javac srcdir="${src}" destdir="${build}"> «include name="*. java" /•» «classpath refids "classpath"/» </javac» <echo messages"creating the WAR file. <antcall target="create-war”/> <antcal1 targets"deploy"/> «/target» z- В качестве альтернативы предшествующему методу развертывания можно скон- фигурировать Tomcat так, чтобы указать на внешний каталог, содержащий корректное web-приложение. При таком подходе web-приложение будет развернуто при следующем перезапуске сервера Tomcat. Подобная стратегия применима, когда приложение находится в процессе разработки, поскольку можно сконфигурировать Tomcat для автоматической перезагрузки приложения (рецепт 2.2) при изменении сервлета или при добавлении JAR- файла в WEB-INFAib. Однако когда придет время запускать приложения на реально работающем сервере, разработчики должны создать приложения в виде WAR-файлов. Создайте файл, содержащий элемент Context, в том виде, как этот элемент должен появиться в server.xml. Дайте этому файлу расширение .xml. Можно присвоить этому файлу имя, совпадающее с путем к контексту или именем приложения (например, myapp.xml), но не обязательно. Содержимое файла будет примерно следующим: «Context className="org.apache.catalina.core.Standardcontext" crossContext="false" reloadable="true" mapperClass="org.apache.catalina.core.StandardContextMapper" useNamingsHtrue" debugs"0" swallowOutput="false" Развертывание web-приложения на сервере Tomcat | 49
privileged='" false" wrapperClass=“org.apache.cataiina.core.Standardwrapper" docBase="h:\book\cookbook\secl\secl_l\dist" cookies="true" path="7newapp" cachingAllowed=“true" y charsetMapperClass="org.apache.catalina.util.CharsetMapper"> </Context> Значение «true» атрибута reloadable настраивает Tomcat на отслеживание любых изменений классов в каталоге WEB-INF/classes и Компонентов в WEB-INF/lib. При обнаруже- нии там каких-либо изменений Tomcat автоматически перезагрузит web-приложение. Значением атрибута docBase может быть абсолютный путь к каталогу, содержащему web-приложение, или корневой каталог контекста. Его значением также может быть путь к WAR-файлу. Также атрибут docBase может содержать путь относительно каталога арр- Base объетЛлющего (родительского) элемента Host в файле server.xml, например относи- тельно каталога <инсталяционный-каталог-Тотсаг>/^сЬарр5. Атрибут path объявляет путь к контексту для нового приложения как http:/flocalhost:8080/newapp/ (где /newapp - путь к контексту). Поместите этот файл в каталог <инсталяционный-каталог-ТотсаГ>/л>еЬарр$ (или в тот каталог, который указан как appBase в объемлющем элементе Host в conf/server.xml) и перестартуйте Tomcat. Теперь web-приложение будет работать на Tomcat. - J См. также Книгу «The deployment sections of Tomcat: The Definitive Guide» авторов Brittain и Darwin (O'Reilly); рецепты 2.1, 2.2 и 2.4. 2.7 Развертывание web-приложения на WebLogic при помощи Ant Задача Необходимо, используя Jakarta Ant, развернуть web-приложение на сервере WebLogic 7.0. Решение Создайте сборочный файл для Jakarta Ant. После этого Ant сможет автоматически компилировать классы сервлетов. Создать файл архива web-прилОжения (.war) и затем развернуть этот WAR на сервере WebLogic 7.0. 50 | Глава 2. Развертывание сервлетов и JSP
Обсуждение Можно вручную компилировать и вставить web-компоненты в каталог applications сервера WebLogic (как описано ниже), а можно использовать Ant для автоматизации процесса компиляции, генерирования WAR-файла и копирования его в указанный ката- лог. Путь к каталогу applications может, например, выглядеть так: k:\bea\user_projects\ bwpdomain\applications. Последний способ подразумевает редактирование файлов build, xml и build.properties, описанных в рецепте 2.6. Развертывание web-приложения вручную Когда сервер WebLogic работает в режиме разработки, то, если поместить WAR-файл, EAR-файл (Enterprise ARchive application - заархивйрованное прило- жение предприятия) или каталог, содержащий корректное web-приложение, в каталог applications, это приложение будет автоматически развернуто и станет доступным на сервере. Корректное web-приложение содержит дескриптор развертывания WEB-INF/ web.xml, который не вызывает никаких исключений при синтаксическом разборе. Если каталог, который вы поместили в каталог applications, не содержит дескриптора развертывания, WebLogic не станет автоматически развертывать это приложение, даже если сервер функционирует в режиме разработки. WebLogic возбудит исключение и выведет на консоль, с которой его запустили, сообщение следующего вида. <Unable to activate application, _appsdir_dist_dir, from source, K:\bea\user_projects\bwpdomain\applications\dist. Reason: No-J2EE deployment descriptor found at "K:\bea\user_projects\bwpdomain\ applications\dist".> Для развертывания под WebLogic 7.0 необходимо отредактировать в build.xml цель deploy-application. <target name»"deploy-application" depends="prepare" description»"Compile the web application...."> <echo message»"Compiling the application files..."/> <javac srcdir="${src}" destdir»"${build}“> cinclude name»"*.java" /> <classpath refid»"classpath"/> </javac> <echo message»"creating the. WAR file.*.."/> <antcall target»"create-war"/> 4 <copy todir="${wl.applications}"> <fileset dir="$£dist)" /> </copy> </target> Развертывание web-приложения на WebLogic при помощи Ant | 51
Кроме того, в файле buildproperties можно определить свойство wl.applica- tions со значением типа: "k:\bed\user__projects\bwpdomain\applicationsn. После того как WAR-файл будет скопирован в этот специальный каталог, сервер WebLogic, функ- ционирующий в режиме разработки, автоматически развернет его. В каталоге \user_projecNjwpdomaih (в зависимости от имени домена вашего сервера) сценарий запуска WebLogic называется startWebLogic.cmd (для Windows) и startWebLogic.sh (для Unix). Чтобы запустить сервер в режиме разработки, соответствующая строка сценария должна выглядеть так: 8ТАКТМООЕ=(значение - пустая строка) или STARTMODE=false. Если STARTMODE=true, сервер стартует в режиме реальной работы. См. также Рецепт 2.3; рецепты 2.8-2.10; документацию для программистов сервера WebLogic 7.0 http://e-docs.bea.com/wls/docs70/programming.html. 2.8 Использование консоли администрирования WebLogic Задача Необходимо развернуть web-приложение с помощью консоли администрирования WebLogic. Решение Войдите в консоль администрирования из своего браузера щ используя ее графический интерфейс, разверните или WAR-файл, или каталог web-приложения. Обсуждение Консоль администрирования WebLogic - инструмент, опирающийся на сорвлеты и браузер, для управления ресурсами сервера WebLogic и 12ЕЕ-приложениями (J2EE - Java 2 Enterprise Edition). Чтобы использовать консоль, сервер WebLogic должен быть в работе. Во- первых, необходимо из браузера запросить URL http:/hocalhost:7001/consdle (если у вас иной адрес сервера и номер порта, то http://<adpec-Weblx)gic>:<nopm>/console). Затем введите логин и пароль. В итоге экран будет выглядеть подобно изображенному на рис. 2.6, с иерархическим списком возможностей в левой колонке и текущим выбором в правой. 52 |4 Глава 2. Развертывание сервлетов и JSP
Рис. 2.6. Консоль администрирования WebLogic В левой колонке выберите имя своего'домена, щелкнув по соответствующему знаку плюс (+) и отобразив тем самым подузлы домена. Подузлы домена включают Servers (серверы), Clusters (кластеры), Machines (машины), Network Channels (каналы сети), Deployments (развернутые программы), Services (сервисы) и Security (безопасность). После этого выберите узел «Deployments», что позволит добраться до подузла «Web applications» (web-приложения). Раскройте узел «Web applications», щелкнув по соответствующему знаку плюс. Полученный экран будет выглядеть, как показано на рис. 2.7. В окне Web applications щелкните гиперссылку «Configure a New Web Application...» (настройка нового web-приложения). Следующий экран предоставляет вам возможность выгрузить WAR-файл или EAR-файл в файловую систему сервера, не выходя из браузера (см. рис. 2.8). Инициируйте выгрузку и затем щелкните по ссылке «select», расположенную после WAR-файла. Выполните три шага,, показанные на рис. 2.9: щелкните по кнопке со стрелкой, чтобы из списка доступных серверов (колонка Available Servers) выбрать сервер, на котором вы хотите развернуть приложение (колонка Target Servers), присвойте приложению имя (оставьте то же имя, что у WAR-файла минус суффикс .war), а затем нажмите кнопку «Configure and Deploy» (настроить и развернуть). И это все, что требуется, для развертывания WAR-файла на сервере. Теперь проверьте работу развернутого приложения, обратившись с запросом из браузера к одному из сервлетов; используйте в качестве пути к контексту То имя, которое вы дали приложению. Итоговый URL может выглядеть так: http://localhost:7001/cookbook/cookieserv- let. Этот URL запрашивает сервлет, который сопоставлен имени “/cookieservlef. Путь к кон- тексту web-приложения: /cookbook. Использование консоли администрирования WebLogic | 53
Рис. 2.7. Узел «Web applications» Повторное развертывание ранее развернутого web-приложения с помощью консоли администрирования WebLogic включает следующие шаги. 1. Выберите в левой колонке консоли под узлом Web applications ваше приложение. Полученный экран будет выглядеть, как показано на рис. 2.10. 2. Щелкните по кнопке «Deploy» (развернуть) в правой части экрана, это реактивирует приложение, и оно сможет получать запросы в web-контейнере WebLogic. Если необходимо с помощью консоли администрирования удалить приложение, в левой колонке экрана консоли щелкните по имени своего домена, затем щелкните по на узлам «Deployments» и «WebApplications». Щелчок на мусорной корзине напротив приложения удалит это приложение с сервера WebLogic (см. рис. 2.11). Удаление приложения таким способом означает, что данное приложение не сможет больше получать запросы в контейнере WebLogic. См. также' Рецепты 2.3 и 2.7; рецепты 2.9 и 2.10; документацию для программистов сервера WebLogic 7.0 http://e-docs.bea.com/wls/docs70/programming.html. 54 | Глава 2. Развертывание сервлетов и JSP
3 WebLojsc Sent -i Console Mictosoft Internet t spk*er Stop Prim EdtwttXM.. ВВВВЯВВМ^В^ВН ^^еолИиип^.1(К259445 gJInriMnnFS @w£om«>Sjw>mi (gJAPlbocunmUbon ^JBatoChaptm gJGoo^e ©0wimU«v«2PMamEE vl.4) * Consol* В ФропуаопмНп В dsowon ♦ewpnryoon и Осшвого ^Mochlno* ONMwrk Channel* в uJo«* ти- и , оЗдррг^ло . St? EJB В «?VlКЛЛйХсМом •coittcot* *о*гшмтд|* •_*pp*er_hom*_W ^Wtt Son** Component* cflConnoctor* aJ Startup < Shutdown S CSsonito* -> В CJskut»». I SJDomoln LopFHor* Итмк» Locate Application or Component to configure S50Sr"ES35ii This wizard will guide you through thp process of configuring and deploying a J2EE application or module. This can be any one of the fotaMng types of files: • A Jar containing EJBs (Enterprise Java Beans) • A .war (Web Application Archive) containing JSPs and Servlets A.rar (Resource Adapter Archive) containing a JCA Connector module. • An лаг (J2EE Enterprise Application Archive)contairingeny of the above Step T. The .ear. .war. jar. or .rar file you wish to configure and deploy must be available in the Admin servers file system. It may already be there; if it is not. you can either copy it there yourself or (iioloaditl tnfQUflhJgMlf.brmsalinto #» directory selected below. Step 2. Select the .ear. .war, jar. or rar file you would Ike to configure and deploy Note that you may also configure an 'exploded* application or component by simply selecting its root directory. Cick on the [select] Ink to the left of the desired directory or file to choose it and proceed to the next step. Listing it l*c*lli**t\K:ih** 70\u**t proitct*\wrrv*—*1» [Up to parent directory] fsetect] appl .aiiQns i*eleetl bwoerryxrwr' Isaz |s<4eil] yserConfiq Puc. 2.8. Развертывание web-приложения в виде WAR или EAR файла 2.9 Использование компоновщика WebLogic Builder для развертывания web-приложения Задача Развернуть web-приложение, используя WebLogic Builder. Решение WebLogic Builder инсталлируется вместе с сервером WebLogic 7.0, таким образом, его можно запустить и, используя его графические возможности, развернуть web-приложение. Использование компоновщика WebLogic Builder для развертывания web-приложения | 55
Рис. 2.9. Заключительные шаги развертывания WAR-файла Обсуждение Компоновщик WebLogic Builder - графический инструмент, который инсталлируется вместе с сервером WebLogic 7J0. Его можно использовать для редактирования файлов дескрипторов развертывания, таких, как web.xml, а также weblogic.xml, а также для развертывания web-приложения на сервере. Используя Builder, можно открывать, редактировать и развертывать web-приложение, находящееся или в виде WAR-файла, или в распакованном формате, то есть в развернутом на каталоги виде. Формат «распакованный по каталогам» представляет собой структуру каталогов web- приложения, в том виде, в каком эта структура должна появиться в файловой системе, но не в архивированной (WAR) форме. Чтобы приложение было развернуто на WebLogic, корневой каталог web-приложения должен включать дескриптор развертывания WEB-INF/xml и все другие правильно разобранные по каталогам компоненты приложения, например каталог WEB-INF/classes, содержащий сервлеты (включая все связанные с пакетами каталоги). 56 | Глава!. Развертывание сервлетов и JSP
5 WebLogx Server Cont ok - M—пиЫ1 Irrt '-net Explorer fl. £Л y~ 1 * Ьж. mu.i; . / ~~R^n ~~ — ~i 111 ? ana in' ~ ~r । । Addtw. fe] httn:/A>e*x>rt70m/corw^»c«ontAnbMrvM8wrft«i»«*Action?t«M>«Ft«>iaJ-wl eoraole ftMM 1062S94«53223bd3«~4dt»U»red j.<oonM<fi«nw..10&5344 Hu BJIt»» -orFS ,JWafcOTwloSputawk JAPI Docu-w-uSor ,^]B«*.OMpk-t pjGeop. e]0vr-rimll«v«2PlatlanEEy'l * Console P CBpertytJomain В tOsemra ^bwpsnysbtvsr AJciustsrS i^Mschlnes ^Network Chsrmels В ^Deployments ^Applications a cPejb В bhiweb Applications «certificate *DefieuHWeoApp ® rnywebapp •_appsdir_home_dlr CJweb Senice Components о ^Connectors ta*Startup& Shutdown В ^Services E-Jjcom В ffl Cljosc В 1-ljMS В ^Messaging Bridge » t-JxML Qjta s Osnmp ; ^WLEC DJwebLoglc Tuxedo Conned 1-bolt ZpVIrtual Hosts perrvdomain* Web Applications* rnywebapp <bea ? Edit Web Application Deployment Descriptors. Configuration! Targets | Monitoring , Notes'! Deployment Status by Target: Target Type Deployed * Server false Ewfiy?eiypr |MP DentoyMi^.^ Deploy or redeploy this component to aU targets Deployment Activity: Statu* I Вея1п'П^'1”Уа^" — ДлЧЧгЦЦе ъ.'чл Description onhwoenyserver • । . n зч;?ам FfiT Art* > . imam -r am Connecter! tn localhpst 7Й01 Deploy ...a..................... Completed ^f$3°3 09:3209 Note: To configure additional deployment targets for this component, please move to the Targets' tab. Deploy, -] End Time? Wed Sep 03 09:32:09 EDT 2003 Puc. 2.10. Выбор web-приложения в консоли В Windows WebLogic Builder можно запустить с помощью меню «Пуск» или из командной строки. Сценарий запуска Builder находится в файле <ВЕА_НОМЕ>/ weblogic700/server/bin/startWLBuilder.cmd (или в файле startWLBuilder.sh в Unix). Здесь <ВЕА_НОМЕ> - каталог, в который инсталлирован сервер WebLogic 7.0. Builder позволяет легко открывать и редактировать дескриптор развертывания web- приложения. Достаточно выбрать в меню «File -♦ Open» и добраться до WAR-файла или корневого каталога приложения. В результате появится окно, изображенное на рис. 2.12. Дерево навигации в верхнем левом окне позволяет конфигурировать web-ресурсы (например, сервлеты) и элементы дескриптора развертывания (например, ограничения безопасности), а затем сохранить изменения в web.xml. Вы, например, можете добавить или удалить элементы для сервлетов, отображения (mapping) сервлетов и фильтры. Если с помощью Builder вы внесете изменения в прило- жение и сохраните их, эти изменения будут сохранены в дескрипторе развертывания. При необходимости, после этого можно подключиться к серверу (с помощью меню «Tools») и развернуть приложение. х Использование компоновщика WebLogic Builder для развертывания web-приложения | 57
Рис. 2.11. Удаление web-приложения Рис. 2.12. Открытие WAR-файла в WebLogic Builder 58 | Глава 2. Развертывание сервлетов и JSP
Окно «Deploy Module» показывает, развернуто ли уже приложение или еще нет. Это окно показано на рис. 2.13. Если ваше приложение уже развернуто» вы все еще можете внести изменения в дескриптор развертывания с помощью Builder, а затем снова развернуть приложение посредством меню «Tools». Builder специально свернет прило- жение, а после развернет заново, с изменениями, которые вы внесли в web.xml. Рис. 2.13. Окно Deploy Module Builder не показывает JSP-файлы, являющиеся частью web-приложения. Он показы- вает все отображения сервлетов, связанные с JSP-файлами. См. также Рецепты 2.3, 2.7, 2.8, и 2.10; документацию для программистов сервера WebLogic 7 0 http://e-docs.bea.com/wls/docs70/programming.htmV, встроенную справку WebLogic Builder: <BEA_HOME>\weblogic700\seiye/^buildei\index.html. Использование компоновщика WebLogic Builder для развертывания web-приложения | 59
2.10 Использование инструмента командной строки weblogic.Dep!oyer Задача Необходимо развернуть web-приложение на сервере WebLogic 7.0 из командной строки. Решение Используйте утилиту командной строки weblogic. Deployer, которая инсталлируется вместе с WebLogic Server 7.0 и работает на базе Java. Обсуждение Для. разработчиков и администраторов, предпочитающих для развертывания и переразвертывания web-приложений использовать командную строку или командные Сценарии, сервер WebLogic 7.0 предоставляет утилиту Depl oyer (работающую на платформе Java). Эта j тилита позволяет выполнять те же задачи по развертыванию и повторному развертыванию web-приложений, что и консоль администрирования, снаб- женная графическим интерфейсом. В начале данного рецепта описывается, как из командной строки с помощью утилиты Depl oyer развернуть и переразвернуть web- приложение. Азатем приводится пример командного файла Windows, выполняющего утилиту Depl oyer. Утилита Depl oyer может выполнять и другие задачи, например переразвертывание отдельных web-компонент приложения, документацию по утилите можно найти по адресу http://e-docs.bea.com/wls/docs70/programming/deploying.html^!094693. Утилита Depl oyer - это Java-программа, и для ее запуска необходимо, чтобы в пути к классам (classpath) был прописан следующий JAR-файл: <BEA_HOME>\servei\ lib\weblogic.jar. Здесь <ВЕА_НОМЕ> - каталог, где инсталлирован WebLogic Server 7.0. На машине с Windows NT следующий командный сценарии повторно развернет web- приложение cookbook.war на сервере с именем bwpserver. java -Ср k:\bea\weblogic700\server\lib\weblogic.jar; %CLASSPATH% weblogic.Deployer -adminurl http://localhost:7001 * -user bwperry -name cookbook -source .\dist\cookbpok.war -targets bwpserver -activate 60 | Глава 2. Развертывание сервлетов и JSP
Этот сцёнарий развёртывает web-приложение, представленное файлом архива cookbook, war, после чего это приложение может получать запросы с путем к контексту /cookbook. При запуске из командной строки сценарий выдаёт приглашение на ввод пароля (если вы не включили пароль в сценарий с помощью опции -password). Опция -source задает местоположение WAR-файла или каталогов web-приложения. Опция -targets задает один или несколько серверов, на которых необходимо развернуть web-приложение. Заключительная команда для развертывания web-приложение -activate. Приводимые ниже строки деактивируют (делают недоступным) web-приложение, находящёеся на сервере bwpserver. Если вы не добавите опцию -password с паролем, в начале выполнения будет запрашиваться пароль. java -ср k: \bea\weblogic7.00\server\lib\weblogic,jar; %CLASSPATH% weblogic.Deployer -adminurl http://localhost:7001 -user bwperry -name cookbook -targets bwpserver -deactivate Опция -ср задает путь к классам, используемый для запуска утилиты Deployer, и этот путь должен включать путь к JAR-файлу weblogic.jar. Переключатель -admin- url задает административный сервер (по умолчанию http://localhost:7001, то есть это значение не обязательно было включать в сценарий). Опция -name задает имя деакти- вйзируемого приложения, и опция -targets указывает имя сервера, на котором это приложение работает. Приводимые ниже строки переразвертывают все то же приложе- ние «cookbook». ( java -ср k:\bea\weblogic700\server\lib\weblogic.jar; %CLASSPATH% weblogic.Deployer -user кмрёггу -name cookbook -activate На этот раз опции -adminurl и -targets опущены. По умолчанию эти переключатели имеют значения, соответственно, http://localhost:700J и все текущие серверы (если повторно развертывается уже существующее приложение). Если же приложение развертывается впервые, значением по умолчанию для -targets является административный сервер. , Проще задавать эти команды в составе командного файла, поскольку это требует меньше усилий по печатанию сложных командных строк и к тому же позволяет сохранять сценарии на будущее. Пример 2.7 - первый пример, оформленный в виде командного файла Windows NT. Пример 2.7. Развертывание приложения set WL_HOME=*K: \bea\weblogic700 set BEA_CLASSPATH=%WL_HOME%\server\lib\weblogic.jar;%CLASSPATH% java -cp %BEA_CLASSPATH% weblogic.Deployer -adminurl http://localhost:70.01 - user bwperry. -name cookbook -source .XdistXcookbook.war -targets bwpserver - activate Использование инструмента командной строки weblogic.Deployer | 61
В этом командном файле задаются две переменные окружения: WL_HOME и ВЕА_ CLASSPATH. Они используются, чтобы включить в путь к классам путь к файлу weblogic, jar, в котором содержится утилита Deployer Если этот сценарий сохраню ь под именем deploy.bat, то его можно будет запускать из следующей командной строки. Н:\book\cookbook>deploy После запуска на консоль будет выводиться примерно следующая информация. Enter a password for the user "bwperry":bwpserver_19G8 Operation .started, waiting for notifications... #TaskID Action Status Target 15 Activate Success bwpserver book\cookbook\.\dist\cook Type Application Source Server cookbook* ~ H:\ См. также Рецепты 2.3 и 2.5; рецепты 2.7-2.9; документацию для программистов сервера WebLogic 7.0 http://e-docs.bea.com/wls/docs70/programmingJitml. 62 | Глава 2. Развертывание сервлетов и JSP
z ГЛАВА3 Присвоение имен сервлетам 3.0 Введение Одной из важных задач конфигурации web-приложения является создание пути, с помощью которого пользователи сети будут обращаться к сервлету. Это то,, что пользователь вводит в поле адреса браузера, чтобы обратиться к сервлету. Иногда в этом качестве используют полное имя сервлета, что приводит к появлению невообразимых URI. Пусть, к примеру, на сайте имеется сервлет, динамически создающий страницу «Resources», вместо статичной страницы resources.html. При использовании полного имени сервлета URL запроса может выглядеть так: http: //www.myorganization.com/servlet/com.organizAtion.ser\iets.resources.ResourceServlet. Не каждому человеку под силу набрать такой адрес, поэтому имеет смысл .поста- вить сервлету в соответствие некоторый путь к сервлету (servlet path), который будет являться псевдонимом данного сервлета. Новый адрес динамической страницы (с использованием пути к сервлету) может быть таким: http://www.myorga- nization.com/resources. В данном случае /resources - путь к сервлету. Путь к сервлету также является идентификатором, используемым другими сервле- тами или JSP, для переадресации запросов на этот сервлет, он также является адресом, используемым в атрибуте action тегов HTML-форм для передачи сервлету имен и значений параметров. Спецификация сервлетов предлагает интуи- тивно понятный и гибкий способ: отображение HTTP-запросов на сервлеты с помощью дескриптора развертывания web.xml. В данной главе описано, как использовать дескриптор развертывания web.xml для создания одного или нескольких псевдонимов сервлета (путей к сервлету). Здесь также обсуждается, как вызвать сервлет с помощью URL других типов, в частности такого, который выглядит как обращение к странице JSP (например, info.jsp) или как обращение к HTML-странице (например, info.html). В рецепте 3.5 также описано, как обратиться к сервлету без применения отображения в web.xml, например, если разработчик хочет отладить сервлет, не изменяя файл web.xml. И, наконец, в рецептах 3.7,3.9 и 3.10 показано, как отобразить все запросы на один «управляющий» сервлет (3.7), ограничить доступ к определенным сервлетам только пользователями с соответствующими полномочиями (3.9) и блокировать все запросы к некоторым сервлетам, 'за исключением запросов, переадресуемых к ним от управляющего сервлета (контроллера) (3.10). 63
3.1 Отображение сервлета на определенное имя с помощью файла web.xml Задача Необходимо создать псевдоним сервлета (путь к сервлету). Решение Создайте в web.xml эдементы servlet; и servlet-mapping. Обсуждение Создание псевдонима сервлета осуществляется с помощью элемента servlet-map- ping находящегося в дескрипторе развертывания. Согласно API сервлетов версии 2.3, все элементы servlet в дескрипторе должны идти раньше любого из элементов servlet- mapping. Элемент servlet-mapping ссылается на имя сервлета, появляющееся в элементе servlet-name, например на следующее. <servlet><servlet-jname>inyservleC</servlet-name></servlet> Это зарегистрированное имя сервлета. Затем в servlet-mapping приводится имя или шаблон URL, .которое пользователи web-приложения могут вводить в браузере для доступа к данному сервлету. Пример 3.1 показывает файл web.xml с элементами servlet и servlet-mapping. В данном случае зарегистрированное имя сервлета: «CookieServlet». Пример 3.1. Элементы servlet и servlet-mapping <?xml versions"1.0" encodings"ISO-8859-1"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-application_2_3.dtd" <web-app> <servlet> •» t <serVlet-name>CookieServlet</servlet-name> <servlet-class>com.jspservletcookbook.CookieServlet</servlet-class> </servlet> 'lb < servlet-mapping> <servlet-name>CookieServlet</servlet-name> <ur1-pattern>/cookieservlet</иг1-pattern> <I servlet-mapping> </web-app> 64 | Глава 3. Присвоение имен сервлетам
В этом примере в элементе servlet регистрируется имя «CookieServlet» (путем зада- ния его в servlet-name). Имя класса указывается в элементе servlet-class. Этот класс сервлета может находиться в каталоге WEB-INF/classes/com/jspseryletcookbook/ или в JAR-файле, расположенном в WEB-INF/lib. Имя «CookieServlet» становится заре- гистрированным именем, под которым сервлет выступает в оставшейся части файла webjanl. А теперь создадим путь к сервлету, по которому пользователи web-приложения будут обращаться к сервлету из браузеров. Это назначение псевдонима выполняется с помощью элемента servlet-mapping. Итак, servlet-name определяет зарегистрированное имя, по которому можно ссылаться на сервлет в файле web.xml, а элемент ur 1-pattern создает URL для доступа к этому сервлету. Символ «/>> в шаблоне /cookieservlet означает «начиная с корневого каталога web-приложения». К примеру, если «cookbook» - путь к контексту для сайта http://www.mysite.org, то полный адрес доступа к сервлету Cook- ieServlet - http://www.mysite.org/cookbook/cookieservlet. Часть /cookbook этого URL является путем к контексту web-приложения. Затем, внутри этого контекста, сервлет иден- тифицируется шаблоном /cookservlet. Обобщая, любому сервлету соответствует следующий URL. http://<хост>:<порт>/<путь-к-контексту>/<путь-к-сервлету> ' *’ Большинство контейнеров сервлетов допускает использование контекста j по умолчанию, для которого путь, к сервлету /. В этом случае URL имеет * следующую форму. http://<хост>:<порт>/<путь-к-сервлету> Например, если вы используете на своем компьютере сервер Tomcat 4.1 и у вас соз- дано приложение «туарр», а шаблон URL сервлета - /myservlet, то полный адрес сервлета выглядит примерно так: http://localhost:8080/myapp/myservlet. Вы можете обращаться к сервлету и по URL следующего вида. http; //хост : порт /путь -к -контексту/servl е t /зарегистрированно -имя-сервлета. Если зарегистрированное имя сервлета «MyServIet», то запрос выглядит так: http:// localhost:8080/myapp/servlet/MySerylet. Некоторые исполнительные подсистемы (servlet engine) используют путь к сервлетам, отличный от/servler, другие позволяют администратору изменить этот путь. Чтобы узнать, какой путь для установки является корректным у вас, ознакомьтесь с документацией по используемому контейнеру сервлетов. Какой, к примеру, элемент servlet-mapping должен появиться в файле web.xml приложения по умолчанию, у которого путь к контек- сту - «Л>? В этом случае пользователи должны обращаться к сервлету CookieServlet, используя адрес http://www.mysite.org/cookieservlet. 3-1S4S Отображение сервлета на определенное имя с помощью файла webjanl | 65
.... В Tomcat и WebLogic url-pattern, который вы создаете для сервлета в элементе iCSlS servlet-mapping, является чувствительным к регистру символов. Согласно главе х*»*****у SRV.11.1 спецификации сервлетов версии 2.3 и предлагаемой окончательной редакции версии 2.4, «контейнер должен использовать чувствительный к регистру символов способ сравнения строк». Если вместо http://www.mysite.org/cookbook/ cookieservlet пользователь введет http://www.mysite.org/co0kbook/cook1eSERVLET, запрос не будет направлен к отображаемому сервлету (CookieServlet). В Tomcat 4.1.x и WebLogic 7.0, в ответ на такой запрос, сервером будет возвращен кдд ошибки HTTP 404 («Файл не найден»). Элемент url-pattern в элементе servlet-mapping может иметь разные формы; это обсуждается в последующих рецептах. См. также Главу 1 по web.xml; рецепту 3.2-3.8; главу 11 спецификации сервлетов Версии 2.3 и 2.4 в части отображения запросов на сервлеты. i ' т 3.2 Создание более одного отображения на сервлет Задача Необходимо создать несколько имен или шаблонов URL, используя которые, пользо- » . ватели смогут обращаться к одному и тому же сервлету. i Решение В дескрипторе развертывания создать элемент servlet с несколькими элементами servlet-mapping. ’ i Обсуждение Как показано в примере 3.2, для одного сервлета можно создать несколько элемен- тов servlet-mapping. Пользователь может обращаться к этому сервлету, используя»: Один из двух адресов: http://www.mysite.org/cookbook/cookieservlet и http://www.mysite. > org/cookbook/mycookie. Пример3.2. Два тега servlet-mapping <?xml version="l.0" encoding="ISO-8859-1"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.suh.com/dtd/web-applicatipn_2_3.dtd" 66 I Глава 3. Присвоение имен сервлетам '
<web-app> <servlet> <servlet-name>C?pokieSerVlet< I servlet-name> <servlet-class>cpm.parkerriver.cookbook.CookieServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>CookieServlet</servlet-name> <ur1-pattern>/cpokieservlet</иг1-pattern> </sepvlet-mapping> <servlet-mapping> <servlet-name>CookieServlet</servlet-name> f. <url-pattem>/mycookie</url-pattern> ' </servlet-mapping> </web-app> И помните, что в дескрипторе развертывания элементы servlet-mapping должны появляться только после всех элементов servlet (согласно версии 2.3). Сработает только точное совпадение с шаблонов URI Ес [и -тодьтоватрль вместо . /cookieservlet запросит /cookieservlet/ (со слешем на конце), он получит, вместо ^**ш*у' ожидаемой сгенерированной страницы, ошибку HTTP. Для расширения шаблона отображения можно использовать симврл подстановки (*). Отображения в примере 3.3 приведут к выводу сервлета CookieServlet для всех URL. которые начинаются с /cookie/ и затем, возможно, содержат некоторое имя после слеша. Например, используя этот дескриптор, сервлет CookieServlet можно вызвать с URL http:/ /www.mysite.org/cookbook/cookie/. И все потому, что шаблону (url-pattern) соответ- ствует любой запрос с любой строкой после /cookie/. Пример 3.3. Использование звездочки в шаблоне URL <?xml version="1.О" encodingsмISO-8859-1"?> <!DOCTYPE web-app ... . PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "https//java.sun.com/dtcl/web-application_2_3 .dtd" > ;n' - <serylet> /л ..* ' <servlet-name>CookieServlet</servlet-name> . ' <servlet-class>com.jspservletcookbobk.CookieServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>CookieServlet</servlet-name> <url-pattern>/cookie/*</url-pattern> </servlet-mapping> Создание более одного отоЗражениг на сервлет | 67 3*
Не допускается использование символа (*) в качестве символа подстановки в элементе servlet-name. Звездочку в этом качестве можно использовать только в элементе 74; url-pattem, например <url-pattem»/cookie/*</url-pattem>, или даже в шаблонах, указывающих на все файлы с некоторым расширение^ (<url- pattern>*. jsp</url-pattern>> Последний вариант шаблона называют отображение на расширение (extension mapping) См. также Главу 1 по web.xml; рецепт 3.1; рецепты 3.3-3.8; главу И спецификации сервлетов версии 2.3 и 2.4 в части отображения запросов на сервлеты. г • > > 3.3 Создание для сервлетов URL как у JSP Задача Требуется создать для сервлета шаблон URL, выглядящий как запрос к JSP-файлу. Решение Создайте элемент servlet-mapping, содержащий шаблон в стиле JSP. Обсуждение Как уже говорилось, имеется масса вариантов создания псевдонимов, указывающих на сервлет. Например, можно легко отобразить на сервлет запрос, выглядящий как запрос JSP-файла. Дескриптор развертывания из примера 3.4 ставит сервлету Jspinfо в соответствие шаблон URL /info.jsp. Пример 3.4. Пример дескриптора развертывания с URL в стиле JSR <?xml version="1.0’ encoding="ISO-8859-1"?> <! DOCTYPE web-app > PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http:11 java.sun.com/dtd/web-application_2_3.dtd" > <web-app> <servlet> <servlet-name>Jspinfo</servlet-name> <servlet-class>com.parkerriver.cookbook.Jspinfo</servlet-qlass» > </servlet» <servlet-mapping» <servlet-name»Jsplnfo</servlet-name» 68 | Глава 3. Присвоение имен сервлетам s
<url-pattern:*/info. jsp</url-pattern* <7servlet-mapping* </web-app> " • ‘ )# Прямой слеш, с которого начинается шаблон URL /info.jsp, означает «начиная с корневого каталога web-приложения, использующего данный дескриптор развертыва- ния». В результате полный URL сервлета Jsplnf о приложения cookbook выглядит так: http://www.mysite.org/cookbook/info.jsp. Можно отобразить все ссылки на все страницы JSP на единственный сервлет (Пример 3.5). I- > . < i . ’!-• : Пример 3.5. Отображение URL всех страниц JSP на один сервлет <?xml version="1.0" encoding="ISO-8859-1"?> '• < -doctype web-ад?.., .u .. .-J . PtfBLlt "-//Sun Microsystems, Inc./,DTD Web Application 2.37/EN" "http: / /java. sun. com/dtd/web-application_2_3. dtd" <web-app* <ser*vlet> <servlet-name>Jsplnfo</servlet-name> ' <serviet-class>com.parkerriver.cookbook.Jsplnfo<I servletr-olass* </servlet* <servlet-mapping> <servlet-name*Jsplnfo</servlet-name> , <url-pattern**.jsp</url-pattern* 4- - s </servlet-mapping: </web-app> Убедитесь, что вы исключили слеш (/) из этого шаблона URL. поскольку отображе- ние на расширение файла всегда задается так: звездочка, точка и расширение файла (например, <url-pattern** . jsp</иг 1-pattern*). Такой тип отображения может быть полезен при переходе 6т старой версии приложения, использующей т.шогб страниц JSP, к новой версии, целиком построенной на сервлетах. Это т- забота о пользователях, сохранивших закладки с URL, указывающие на страницы JSP. << * *' Сервер Tomcat 4’ 1.x включает неявное отображение на собственный сервлет компиляции и выполнения JSP-страниц. Если вы включите отображение, приведенное £ в предыдущем фрагменте web-app, то ваше отображение перезапишет собой неявное отображение сервера Tomcat. См. также Главу 1 по web.xml; рецепты 3.1 и 3.2; рецепты 3.4-3.8; главу 11 спецификации сервле- тов версии 2.3 и 2.4 в части отображения запросов на сервлеты. Создание для сервлетов URL как у JSP | 69
3.4 Отображение на сервлет обращений к статичному содержимому Задача Требуется, чтобы запросы на статичный контент (например, URL в HTML-стиле) направлялись к сервлету. Решение Используйте элемент servlet-ntapping файла web.xml, отображающий имя сервлета на статичное содержимое. Обсуждение Вы можете сделать так, чтобы ответы на запросы с URL, выглядящими как запросы к статичному содержимому (например, к HTML-файлам), формировал сервлет, хотя это зачастую излишне. В примере 3.5 сервлет JitrflServlet отображается на все URL, заканчивающиеся суффиксом .html. Любой запрос внутри web-приложения, содержащего этот дескриптор развертывания и ? заканчивающийся на .hfml. будет' направлен к HtmlServlet. Пример 3.6. Отображение статичного контента на сервлет в web.xml <?xml versions"1.0" encodings"ISO-8859-1"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http: //java, suh.com/dtd/web-application__2_3 .dtd" > - <web-app> ,. <servlet> <servlet-name>HtmlServlet</servlet-name> <servlet-class*com.jspservletcookbook.HtmlServlet</servlet-class* </servlet> "* <servl e t -mapping* <servlet-name*HtmlServlet</servlet-name> <url-pattern*w.html</url-pattern* </servlet-mapping* </web-app* 70 | Глава 3. Присвоение имен Сервлетам
На этом, листинге ^eM^HT^^ryl^t^ppin^i содержиттлабпон UR^ отображающий сервлет на расширение .html, но начинаетсяяСо звездочки и заканчиваемся символами .html. Если необходимо отобразись, СВрвлет гол?, и ндбдиН HTML-файл, чело, тьСуйте XML следующего вида. <url-pattern>myfile.html</url-pattern». Г( л При таком шаблоне к сервлету HtmlServlet будут направляться только запросы к файлу inyfilc.lttMl, | Создавая шаблон URL с отображением на расширение файла, помнит». что он никогда не начинается со слеша (/). L См. также Главу 1 по web.xml; рецепты 3.5-3.8; главу 11 спецификации сервлетов версии 2,3 д 2.4 в части отображения запросов .на сервлеты ‘ ' !Ь. .. 5 ’ ИС1 ’’ ' • / И.’* 3.5 Вызов сервлетов, не имеющих >и. отображения в web.xml Задача з * Необходимо обратиться к сервлету, для которого не существует элемента servlet- mapping в дескрипторе развертывания web.xml. Решение Используйте для обращения к сервлету URL в стиле invoker: http://www.mysite.org/ mywebapp/servlet/com.jspservletcookbook.MyServlet. Обсуждение Некоторые сервлеты могут не иметь отображения на путь в дескрипторе развертыва- ния web-приложенйя. Каким образом пользователь может обратиться к этому сервлету? Какое имя и URL он должен использовать? Tomcat и другие контейнеры сервлетов предоставляют способ обращения к сервле- там, не отображенным в web.xml. Для этого используется URL следующей формы. http://www. ту site, org/mywefrapp/servlet/<полностыо квалифицированное имя класса сервлета>. Вызов сервлетов не имеющих отображении b web.xml | 71
. К сервлету с именем класса и пакета jspservletcookbookMyServlet можно обратиться с запросом с помощью URL http://www.mysite.org/mywebapp/servlet/jspservletcookbook. MyServlet. Убедитесь, что сегмент пути после имени web-приложения-Zserv/ei/, а не/serv- lets/. Если сервлет находится в web-приложении по умолчанию (обычно на верхнем уровне контейнера сервлетов), то URL для обращения к нему - http:// www.mysite.org/servlet/jsps- ervletcookbook.MyServlet. Файл web.xml, размещаемый в каталоге <инсталяционный-каталог-Тотсм>/соп/, включает следующее определение и отображение сервлета invoker (вызывающий). <servlet> <servlet-name»invoker</servlet-name» <servlet-class>org.apache.catalina.servlets.lnvokerServlet</servlet- class> < init-param» <param-name»debug</param-name» <param-value>0< /param-value» </init-param> <load-on-startup>2</load-on-startup» </servlet» <servlet-mapping> <servlet-name»invoker-q/servlet-name» <url-pattem»/servlet/*</url-pattern» ’ . </servlet-mapping> Сервлет invoker (вызывающий) можно использовать и для обращения к сервлетам, зарегистрированным в web.xml. Для этого используется URL вида http://www.mysite.org/ cookbook/servlet/oapezucmpupoeaHHoe имя сервлета>. Допустим у вас есть следующий элемент servlet- >. <servlet> к <serylet-name»myservlet</servlet-niim<!> । < <servlet-class»jspservletcookbook.MyServlet</servlet-class» </servlet> . Предположим также, что путь к контексту web-приложения -/cookbook. Если для этого приложения доступен сервлет invoker сервера Tomcat, то ваш сервлет можеп быть вызван по зарегистрированному имени, посредством URL http://www.mysite.orz/cookbOok/ servlet/myservlet. На сервере Tomcat 4.1.x, в фыле'<инсталяционный-каталог-ТотсШ>/соп//меЬ.хт1, отображение сервлета invoker можно закомментировать. В результате этот сервлет будет недоступен и обратиться к другим сервлетам можно будет только * с помощью путей, задаваемых в элементах servlet-mapping файла web.xml. 72 | Глава 3* Присвоение имен сервлетам^
Если к сервлету обращаются не по его зарегистрированному имени, а используя форму http://ww.mysite.ofg/mywebapp/servlet/<nonHOcmwo квалифицированное имя класса сервлета>, любые параметры инициализации, заданные в файле web.xml, недоступны. В примере 3.7 приводится зарегистрированный Сервлет с параметрами инициализации. Пример 3.7. Зарегистрированный сервлет с параметрами инициализации <servlet> <servlet-name>Weather</servlet-name> <servlet-class>home.Weather</servlet-class> <init-param> <param-name>reqion</param-name> <param-value>New England</param-value> , </init-param> </servlet> Поскольку параметр с названием региона (region) связан с конкретным зарегистрированным именем, то только обращение с использованием этого имени (или пути к сервлету, отображенного на это имя) задействует параметр region. При обращении по полностью квалифицированному имени параметр region не будет передан сервлету Weather. , См. также Главу 1 по web.xml\ рецепты 3.1-3.4; рецепты 3.6-3.8; главу 11 спецификации сервле- тов версии 2.3 и 2.4 по отображению запросов на сервлеты. 3.6 Отображение на сервлет всех запросов к web-приложению Задача Требуется, чтобы все запросы к web-,приложению направлялись к одному управляющему сервлету (контроллеру). Решение Необходимо использовать в дескрипторе развертывания элемент servlet-mapping, содержащий следующий элемент: ur 1 -pattern: <ur 1-pattern:*/*</игl-pattern>. Отображение на сервлет всех запросов к web-приложению | 73
Обсуждение В некоторых случаях требуется, чтобы все запросы к web-приложению направлялись к одному сервлету. Этот сервлет-контроллер может протоколировать запросы, реализо- вывать политику безопасности или анализировать и, возможно, изменять объект-запрос перед его переадресацией в другое место (обычно к сервлету или JSP). Ознакомьтесь с описанием от Sun Microsystems шаблона проектирования фрон- МЛ’ л контР°ллеРа как способа использования сервлета в качестве центральной точки 4 L “ цЛ* обработки. См. голубые Страницы по ядру J2EE http://java.sun.coin/blueprints/ corej2eepattems/Pattems/FrontController.htptL - - ; •'»' ; Повторяясь, webjanl - это место настройки сервлета на получение всех запросов web-лриложения В примере 3.8 показано, как использовать шаблон URL для. направле- ния всех запросов к сервлету-контроллеру. <4, Пример 3.8. Направление всех запросов К сервлету-контроллеру > <?xml version="1.0" encoding="ISO-8859-1"?> <! DOCTYPE web-a^ PUBLIC "-//Sun Microsystems^ Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-application_2_3.dtd" <web-app> <servlst> S servlet -name»Intarcaptor < I servlet -name» <ssrvlet-class»com.jspservletcookbook.Interceptor</servlet-class> </servlet> < 1— The mappings for the Interceptor servlet —> < servlet -mapping» <servlet-name»Interceptor</eervlet-name>:’ <url-pattern»/.*</url-pattSrfa» v ' •’3 v> < / servlet -mapping» 3 •'* r ’' ~ A v 3s’ < servlet -mapping» 1 ”' ' ' <servlet -name» Interceptor < / Servlet -name» <url -pattern» I servlet / * < /иг 1 -pattern» < / servlet -mapping» </web-app> Вы можете переопределить любой заданный по умолчанию вызывающий сервлет (invoker) собственным отображением; -Ь. <url-pattern>/servlet/*</urJj-pattem> 4 --х;1 Отобразите сервлет, который должен получать все вызовы приложения на этот шаб- лон URL. Если вы сохраните вызывающий сервлет в неизменном виде, пользователи смогут обойти сервлет-контроллер, используя URL вида http://www.mysite.org/myapp/ serX'let/com.jspservletcookbook.CookieServlet. Т4 | Глава Присвоение им£н сервлетам v
На сервере Tomcat вызывающий (invoker) сервлет также можно сделать недоступным, закомментировав элемент servlet-mapping в файле web.xml верхнего уровня (в каталоге <инсталяционный-каталог-Тотса1>/соп/). Это повлияет ‘ на все web-приложения, запущенные под этим экземпляром Nomcat, однако такое решение необходимо принимать совместно с администратором, развертывающим приложения на этом сервере. Вам также необходимо удалить, изменить или закомментировать Другие элементы servlet-mapping, которые позволяют обращаться к сервлетам в обход сервлета- контроллера. Если в web.xml включено какое-либо специфическое отображение (как в примере 3.9), запросы к сервлету CookieServlet будут идти в обход сервлета- контроллера Interceptor. € Пример 3.9. Специфическое отображение переопределяет отображение, использующее символы подстановки <?xml version="1.О" encoding="ISO-8859-1"?> <!DOCTYPE web-app PUBLIC "-//Sun Micrgsystems, Inc.//DTD Web Application 2.3//EN" "ht tp: 7/j ava. sun. com/dtd/web-applicat ion_2_3. dtd" <web-app> <servlet> <servlet-name>Interceptor*/servlet-name> <servlet-class>jspservletcookbook.Interceptor*/servlet-class» </servlet> <servlet» < servlet-name»CookieServlet</servlet-name> <servlet-class» com.j spservletcookbook.CookieServlet </servlet-class» </servlet» <servlet-mapping» <servlet-name»Interceptor</servlet-name> ' <url-pattern»/**/url-pattern» </servlet-mapping» <servlet-mapping» <servlet-name»CookieServlet</servlet-name» <url-pattern»/CookieServlet</url-pattern» </servlet-mapping» </web-app> \ Отображение на сервлет всех запросов к web-приложению | 75
В этом примере элемент servlet-mapping для CookieServlet позволяет исполь- зовать путь /CookieServlet к данному сервлету, в обход сервлета Interceptor,- поскольку путь /CookieServlet (как часть запроса вада http://xocm:nopm/context-path/CookieServlet) точнее соответствует шаблону VRL/CookieServlet, нежели шаблону У*. Запросы к статичному содержимому, типа начальных файлов (Например, index html), тоже перехватываются шаблоном /♦. Эти запросы также направляются । ч • сервлету-контроллеру. См. также Главу 1 по web.xml-, рецепты 3.1-3.4; рецепты 3.6-3.8; главу 11 Спецификации сервле- тов версии 2.3 и 2.4 по отображению запросов на сервлеты; голубые страницы по ядру J2EE http://java.sun.com/blueprinis/corej2eepattems/Patterns/FrontController.html. 3.7 Отображение запросов на контроллер с сохранением отображений сервлетов Задача Требуется направить все запросы единственному сервлету-контроллеру, сохранив при этом, в безопасном режиме, отображения для других сервлетов. Решение Для предотвращения возможности обращений пользователей с запросами к осталь- ным сервлетам (всем, кроме контроллера) используйте в файле web.xml элементы secu- rity-constraint. Обсуждение Что, если сервлет-кднтроллер, получающий все запросы,, захочет при определенных условиях направить поступивший запрос на специализированную обработку другому сервлету? Если все остальные отображения на сервлеты удалены из web.xml, а шаблон URL в стиле invoker (/servlet/*) также отображен на контроллер, то даже сам сервлет- контроллер не сможет передать запрос другому сервлету! Как обойти это ограничение? Решение заключается во введении в файле web.xml Персональных отображений на сервлеты. Для этого используются элементы security-constraint, предот- вращающие возможность обращения с запросами к сервлетамгнеконтроллерам со. стороны пользователей. Когда сервлет-контроллер хочет перенаправить запрос к другому сервлету, он 76 | Глава 3. Присвоение «мен сервлетам •.<» лпре • ; И?
* исцользует объект, реализующий интерфейс javax.servlet.RequestDispatcher (диспетчер запросов). Диспетчеры запросов (RequestDispatcher) не ограничены в воз- можности передавать запросы (с помощью метода RequestDispatcher. for- ward (request, response)) на шаблоны URL, заданные в элементах security- constraint. Пример 3.10 демонстрирует сервлет с именем Controller, использующий диспетчер запросов (RequestDispatcher) для передачи запроса другому сервлету. Рецепт 3.9 описывает, как с помощью элемента защитить security-constraint сервлеты от получения любых запросов от web-пользователей, поэтому я не привожу эту информацию здесь. Пример3.10. Использование RequestDispatcher для перенаправления запросов import javax. servlet. *; .*• import javax.servlet.http.*; public class Controller extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response!/ - s. " " * Г ’ ‘ > /J’ throws ServletException, java.io.IOException { RequestDispatcher dispatcher = thill? i String param = request.getParameter("go"); if (param == null) throw new ServletException("Missing parameter in Controller.");. else if (param.equals("weather")) dispatcher = request. getRequestDispatcher /weather") ; else if (param.equals("maps")) dispatcher = request.getRequestDispatcher("/maps"); else throw new ServletException( "Improper parameter passed to Controller."); //if we get this far, dispatch the request to the correct URL if (dispatcher != null) dispatcher.forward(request,response); else throw hew ServletException( "ConttOll^r received a null dispatcher from request object."); } . r-ir.. • } Этот сервлет проверяет значение параметра go. Запрос к нему может выглядеть так: http://localhost:8080/home?go-weather. Сервлет Controller из приведенного примера, с помощью отображения, настроен на получение всех запросов к \уе1>приложению «home», Другими Словами, его элемент set viet-mapping в web.xml содержит шаблон URL /*. Отображение запросов на контроллер с сохранением отображений сервлетов | 77
Для передачи запроса в зависимости от параметра до, сервлет Controller создает объект RequestDispatcher с тем или иным URL назначения. Сначала сервлет подучает объект RequestDispatcher, для чего вызывает метод getRequestDis- patcher (String path) объекта request. Значение параметра path может быть задано относительно корневого каталога контекста web-приложения, но оно не должно выходить за пределы текущего контекста сервлета. Предположим, шаблон URL /weather отображен на зарегистрированное имя сервлета «Weather». <servlet-mapping> <servlet-name>Weather< / servlet-name> <url -pat tem> /weather< /url -pat tem> </servlet-mapping> В этом случае путь, передаваемый методу getRequestDispatcher, выглядит так: getReques t Di spa t cher ("/weather"). Если параметр go задан неверно или опущен, Controller вызывает исключение ServletException с соответствующим сообщением. Сервлет Weather не доступен web-пользователям* поскольку доступ к нему ограничен элементом security-constraint* однако на метод RequestDis- patcher . forward (request, response) эти ограничения не распространяются. Для получения объекта RequestDispatcher можно также использовать метод javax.servlet.ServletContext.getNamedDispatcher(String name) При использований этого метода не нужно включать никаких элементов servlet-mapping для сервлетов назначения. Метод getNamedDispatcher () принимает в качестве параметра зарегистрированное в web.xml имя сервлета. В примере 3.11 показан сервлет из предыдущего примера, но с использованием метода getNamedDispatcher (Weather) и зарегис- трированного имени сервлета. Пример 3.11. Использование getNamedDispatcher () для передачи Запроса inrport javax. servlet. *; import j avax.servlet.http.*; public class Controller extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.lOException { RequestDispatcher dispatcher = null; String param = request.getParameter("go*); if (param == null) ,, . throw new ServletException("Missing parameter in Controller."); else if (param.equals("weather")) dispatcher = getServletContext(). getNamedDispatcher("Weather"); else if (param.equals("maps")) 78 | Глава 3. Присвоение имен сервлетам ; -.щ. • ns
dispatcher *, getServletContext() . j; • v!' : ggtNamedpispatcher ("Maps") ; else *• * ( thrpw new ServletException! ( "improper parameter passed to Controller."); /*Check for a null dispatcher, then dispatch the request to the correct URL*/ if (dispatcher != null) dispatcher.forward(request,response); else throw new ServletException( "Controller received a null dispatcher.*); i j. ‘ Здесь метод doGet О изменен и в нем используется получение объекта Request- Dispatcher с помощью метода ServletContext. getNamedDispatcher (String «зарегистрированное-имя-сервлета»). Вместо пути к сервлету объект Dis- patcher использует зарегистрированное в webjanl имя сервлета Weather. «servlet» • , < servlet-name»Weathpr</servlet-name» <servlet-class»com. jspservletcookbook.Weather «/servlet-class» .«/servlet? Если ServletContext вернет null из-за того, что кто-то пропустил в web.xml требуемый XML-элемент, то doGet () вызовет исключение ServletException с объясне- нием, что объект dispatcher пуст. Альтернативная стратегия - использовать слушатель (listener) для проверки i запроса до того как он найдет путь к сервлету. Глава 11 описывает, как д использовать слушатель для проверки НТТР-запроса. См. также Главу 1 по web.xml; рецепты З.Ь-3,5; рецепт 3.8; главу 19 по использованию слуша- теля для проверки запроса; главу 11 спецификации сервлетов версии 2.3 и 2.4 по отображению запросов на сервлеты; голубые страницы по ядру J2EE http://java.sun. com/blueprints/corej2eepattems/Pattems/FrontController.html. г ' 4 Отображение запросов на контроллер с сохранением отображений сервлетов | 79
3.8 Создание начальных (welcome) файлов web-приложения Задача Необходимо сконфигурировать один или несколько начальных файдов web-приложения. Решение Используйте элемент welcome-file-list дескриптора развертывания. Обсуждение Начальный, или стартовый, файл - традиция столь же старая, как и сам гипертекстовый Интернет. На многих сайтах имеются домашние страницы или начальные файлы, призван- ные быть заглавной страницей, или входной дверью этих web-сайтов. Такие страницы обычно носят имя index.html wclcome.html, или default.html. Можно сконфигурировать web- приложение, чтобы обеспечить передачу запросов к таким страницам, для этого нужно добавить в дескриптор развертывания web-приложения элемент welcome-f ile-list. - Создайте в web.xml список начальных файлов, как показано в примере 3.12. Элемент wel- come-file-list в дескрипторе развертывания должен находиться после любого из элементов error-page или taglib (по версии 2.3 спецификации сервлетов). Пример 3.12. Задание начальных файлов в web.xml <?xml versions"1.0" encodings-'ISO-8859-1 "?> >. <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD, Web Application 2.3//EN" ' " http: / / j ava. sun. com/dtd/web-applicat ion_2_3. dtd" <web-app> <i— Define servlets and servlet-mappings here —> <welcome-file-list> J <welcome-file>index.html</welcome-file> f <welcome-f ile>default. jsp</welcome-files* </welcome-file-list> н </web-app> - Когда контейнер сервлетов сталкивается с URL web-приложения,, задающим только п каталог, а не конкретное имя файла или сервлет, он ищет в дескрипторе развертывания >ч элемент welcome-file-list (со списком начальных файлов). В спецификации сервле- тов версии 2.3 такой тип URL называется valid partial request (корректный неполный запрос) Контейнер сервлетов прикрепляет к запросу любой найденный в web.xml началь- ный файл (в порядке их перечисления в web.xml) и возвращает,этот файл клиенту. 80 | Глава 3. Присвоение имен сервлетам
Предположим, Tomcat получил запрос к http://www.mysite.org/cookbook/. И пусть также файл weh.xml приложения cookbook содержит элемент welcome-file-list, показан- ный в примере 3.12. В этом случае Tomcat вернет файл http://www.mysite.org/cookbook/ index.html, если такой существует, если нет, Tomcat ищет в каталоге cookbook файл default, jsp и возвращает его. Контейнер сервлетов инициирует такой поиск в ответ на любой URL с запросом каталога, который он получает (например, http://www.mysite.org/cookbook/bookinfo/). Иными словами, если index.html или default.jsp (или другой выбранный вами файл) суще- ствует в корневом каталоге web-приложения и разработчик правильно настроил элемент welcome-file-list, то эти файлы автоматически возвращаются в ответ на запросы каталога. С. См. также Главу 1 по web.xml’, рецепты 3.1-3.6; рецепт 3.9; главу 11 спецификации сервлетов версии 2.3 и 2.4 по отображению запросов на сервлеты; 3.9 Ограничение запросов к определенным сервлетам Задача Предоставить доступ к определенным сервлетам только для аутентифицированных пользователей. Решение Используйте элемент security-constraint дескриптора развертывания web.xml. Обсуждение Некоторые web-приложения включают сервлеты, которые не должны вызываться непосредственно пользователями, поскольку они обрабатывают важные данные или имеют специальное назначение (например, администрирование сервера или web-прило- жения). К примеру, вы могли разработать сервлет, предназначенный только для систем- ных администраторов. Как защитить подобные сервлеты от непосредственного вызова неавторизованных пользователей? Ограничение запросов к определенным сервлетам | 81
В крайнем случае, можно использовать декларативную безопасность (declarative secu- rity) или безопасность управляемую контейнером. Эта стратегия включает конфигурирова- ние дескриптора развертывания web.xml на основе информации по безопасности приложения и, таким образом, вынос информации по безопасности из кода сервлета. Любые последующие изменения политики безопасности приложения могут осущедтв- ляться редактированием XML-файлов конфигурации (или с помощью консоли адми- нистрирования сервера WebLogic 7.0) без вмешательства в исходный код сервлетов. Далее контейнер сервлета загружает и реализует созданную конфигурацию безопасности. Также можно использовать программируемую безопасность, которая предполагает включение в сервлет кода, связанного с реализацией политики безопасности, например для контроля объекта HttpServletRequest на предмет принадлежности пользова- теля к числу допущенных к определенному web-pecypcy. Для Tomcat использование элемента security-constraint в web.xm/ требует заведения имени пользователя и пароля в XML-файле, находящемся по адресу <инстал- ляционный-каталог-Тотса1>/соп//1отса!-ихегх.хт1. Этот файл предназначен для хране- ния имен и паролей пользователей. Он выглядит так, как показано в примере 3.13. Пример 3.13. Файл tomcat-users.xml <?xml versions * 1.0' encoding=*utf-8’?> <tomcat-users> <role rolename="manager"/> н * <role rolename= * tomcat"7 > »’• <role rolename="developer"/> , «user Usernames" tomcat" passwords "tomcat* rdlfefe» "tomcat, manager "/> cuser usernames"bruce" passwords"brucel957* 1 ’* roles="tomcat,manager,developer"/> ’ iii <user usernames"stacy" passwords"stacyl986" roles="tomcat"/> t</tomcat-users> . Показанный фрагмент XML включает корневой элемент tomcat-users, содержащий один или несколько элементов role (роль), и user (пользователь), в зави- 3 симости от числа пользователей web-приложений на сервере Tomcat. Этот файл кон- фигурации tomcat-users.xml доступен всем web-приложениям сервера. ' ; f, Далее, в дескрипторе развертывания приложения (web.xml), необходимо создать элементы security-constraint, login-config и security-role. ‘ч* 1 —' » ......... Если вы не используете дескриптор развертывания по версий 2.4 спецификации ЛиО сервлетов, элементы, связанные с безопасностью, должны следовать именно в этом порядке, после большинства других элементов, которые могут присутствовать в web.xml, в противном случае дескриптор развертывания не. будет являться корректным XML-файлом. После элемента security-г ole могут следовать п,......только элементы env-entry, ejb-ref и е jb-local-r^fj. 82 | Глава 3. Присвоение имен сервлетам
Элемент security-constraint выглядит так, как показано в примере 3.14; защи- щаемый шаблон URL в данном случае - <url-pattern>/CookieServlet</url- -* pattern*. Пример3.14. Элемент security-constraint «security-constraint» <web-resource-collection» ' <web-resource-name»CookieInfo</web-resource-name> <url-pattern»/CookieServlet</url-pattern» <http-method»GET< /http-method» <http-method»POST</http-method» </web-resource-collection> f <auth-constraint} <description»This applies only to the "developer" security role</description> <role-name>developer</role-name» </autH-constraint> <user-data-sonstraint> ' <transport-guarantee»NONE</transport-guarantee» </user-data-constraint> </securi ty-cons traint» »Л-’% I Элемент security-constraint должен содержать один или более элементов web-resource-collection. Элемент4 web-resource-collection описывает, какие ресурсы web-приложения защищаются указанным ограничением безопасности. Другими словами, запрос web-pecypca через Интернет (например, сервлета) активизирует каждое из ограничений безопасности, относящихся к этому ресурсу. В рассматриваемом примере ограничения безопасности распространяются на любой запрос, удовлетворяющий шаблону URL <каталог-у^еЬ-приложения>/Соок1е8еп>1е1. Элементы http-method задают методы HTTP, на которые действуют данные ограничения безопасности. В этом примере GET и POST с запросом к /CookieServlet активизируют механизм безопасности. Если в, элементы security-constraint не включено никаких элементов http- method, ограничения будут применяться ко всем методам HTTP (таким, как PUT и DELETE, в дополнение к GET и POST). Объекты, реализующие интерфейс javax.servlet.RequestDispatcher, могут передавать HTTP-запросы от сервлета к защищаемому сервлету без активизации ограничений безопасности. М-' 1 Элемент auth-constraint предназначен для описания ролей безопасности, R которым разрешен доступ * к web-компоненту. Роль безопасности - это имя, л представляющее пользователю или группе пользователей полномочия доступа к опреде- ленному ресурсу, например сервлету. К примерам ролей безопасности относятся: admin Ограничение запросов к определенным сервлетам | 83
(администратор), manager (менеджер) или developer (разработчик). Эти роли назначаются пользовагелям в файле tomcat-users.xml. В приведенном в примере элементе secur ity- f constraint доступ к CookieServlet открыт только для пользователей, которым в файле tomcat-users.xml назначена роль developer. Как web-приложение аутентифицирует пользователя? Как, например, web-приложение Ц. находит имя и пароль обращающегося с запросом пользователя, таким образом определяя, имеет ли он доступ к сервлету? При безопасности, управляемой контейнером, для этого предназначен элемент login-config. В файле web.xml этот элемент следует сразу после |V элемента Security-constraint. В дескрипторе развертывания web-приложейия все | это выглядит примерно так, как показано в примере 3.15. Пример 3.15. Использование элемента login-config с элементом security-constraint «security-constraint» <web-resource-collection> <web-resource-name»Cookielnfо*/web-resource-name> <ur 1 -pattern» /CookieServlet* /иг 1 -pat tem> t <http-method»GET</http-method» , » , <http-method>POST</http-method> i </web-resource-collection> <auth-constraint> <description>This applies only to the "developer" security role*/description» <role-name:>developer< /role-name» <Iauth-conStraint> <user-data-constraint> <transport-guarantee>NONE*/transport-guarantee> </user-data-constraint> ^/security-constraint» -Ж <login-config> <auth-method>BASIC*Iauth-method> <I login-config> «security-role» <role-name»deyeloper*/role-name> Элемент login-config задает способ, используемый для аутентификации любог^ пользователя, запрашивающего защищаемый web-pecypc. Защищаемыми являются web- ресурсы, указанные в элементе web-resource-collection, внутри элемента secu- rity-constraint. В приведенном примере для ^любого запроса, соответствующего шаблону URL /CookieServlet используется способ аутентификации BASIC (базовый). BASIC - знакомая вам форма web-аутентификации, при которой браузер открывает перед пользователями диалоговое окно для ввода имени и, пароля пользователя. Tomcat сравнивает полученные имя и пароль с информацией о пользователях, заданной в файле зав 84 | Глава 3, Присвоение имен сервлетам
tomcat'-users.jmil, и затем, Используя настройку ограничений для web-приложения из соответствующего Элемента ty—cohstr efint, определяет, Имеет ли Данный пользовательправо доступа к зашишаемдМу ресурсу. ...... ' * • ДочернийДЛя lbgin-ebn¥'ig элемент, aut hornet hod также может принимать значенияFORM, CLIENT*CERT ИЛи DIGEST. ” LZJJ* л я-.’ -И' ' - Н .. ... •' i -i • Для 1авершенности пой конфигурации.безопасности требуется еще один ингредиент: элемент security-role (роль бе .опасности). В примере 3.15 создается роль безопасно- сти developer (разработчик). Значение developer также присутствует в дочернем элементе auth-constraint элемента security-constraint. Это означает, что пользова- тели, которым назначена роль безопасности developer, имеют доступ к web-ресурсам, защищенным данными ограничениями (то есть ресурсам, перечисленным в элементе web- resource-collection, дочернем по отношению к security-constraint). Иными словами, описываемый способ аутентификации состоит из двух шагов. 1. Проверка корректности получаемых имени и пароля пользователя. 2. Определение, присвоена ли этому пользователю требуемая роль безопасности. К примеру, пользователь может ввести корректны? имя и пароль, однако требуемая роль безопасности ему не присвоена. В этом случае он не допускается к использо- ванию запрошенного web-pecypca. На сервере Tomcat присвоение пользователям ролей безопасности производится с помощью файла tomcat-users.xml. Вот пример того, как может выглядеть элемент user (пользователь) файла tomcat-users.xml. <username="bwperry" password="bruce2002"' roles="developer,standard,manager" /> Пользователь назначены три разные роли: developer,standard и manager. Контейнер сервлетов Tomcat использует эти XML-элементЫ из файла tomcat-users.xml для определе- ния, назначена ли некоторой комбинации имя/пароЛь определенная роль. На рис. 3.1 изображен процесс прохода по этим перекрестным ссылкам. Считайте роли безопасности просто дополнительным способом деления пользователей приложений на группы, в дан- ном случае - в соответствии с их привилегиями. XML-текст с конфигурацией безопасности, приведенный в примере 3,15, может использоваться и. для WebLogic, но там существует свой специфичный файл кон- фигураций w^blogi^bbiil.!! г о .( т . щис л‘- ' I Файл weblagiCfXml,. как и дескриптор развертывания web-xml, находится £ каталог WEB-INF web-приложения. п В примере 3.16 приведен XML из дескриптора развертывания weblogic.xml. ’ э» -л ’-.ь;.....* 1 ' . ; j Ограничение iiinffoltiB й определенный егрвЛетям | 85
web. xml deployment descriptor <security-constraint> <web-resource-collection> <web-resource-name>MyServtet </web-resource-name> <url-pattem>/myservlet</ur1-pattem> <http-method>GET</http-method> <A№b-resource-collecfion> GET /myservlet Q (?) Check servlet configuration Web container Клиент ® BASIC аи1*,еп®с?®оп (3) Check security role I tomcat-users.xml <userrame~"b.vperry password="bruce2002' role$a’"developer,star ftnin’aiager7> Рис. 3.1. Использование элемента ограничения доступа Пример 3.16. Роль безопасности в weblogic.xml <!— weblogic.xml security role mapping —> <securi ty-role-assignment> <role-name>developer</role-name> ’ <principal-name>bwperry</principal-name> </security-role-assignment> В WebLogic 7.0 можно задать пользователей, группы и роли безопасности, глобально по отношению^ определенному серверу WebLogic, с помощью консоли администрирования. В этом рецепте было описано, как ограничить "запросы к определенным сервлетам. В следующем рецепте приведен способ запретить все запросы, за исключением тех, что передаются от сервлета-контроллера. См. также Главу 1 no web.xml-, рецепты 3.1-3.8; главу И спецификации сервлетов версии 2.3 и 2.4 по отображению запросов на сервлеты; голубые страницы по ядру J2EE http://java.sun.com/ blueprints/corej2eepattems/Pattems/FrontController.html. 86 | Глава 3. Присвоение имен сервлетам
3,10 Предоставление доступа к определённым сервлетам только для контроллера Задача Необходимо установит^ web-приложение так, чтобы к определенным сервлетам имел доступ только сервлет-контроллер. Решение » • Создайте роль безопасности, которая не назначена никакому иЗ пользователей, затем в элементе security-constraint укажите сервлеты, к которым может обращаться только контроллер. —........ Обсуждение Этот рецепт показывает, как создать элемент security-constraint, запрещающий все запросы к указанным шаблонам URL, Сервлеты, отображаемые на эти шаблоны URL, получают запросы только через один или несколько сервлетов-контроллеров, использующих объект, реализующий интерфейс javax.servlet.RequestDispatcher. Рецепт 3.7 включает пример сервлета-контроллера, направляющего запросы с помощью RequestDis- patcher к другому сервлету. Пример 3.17 показывает, как задать элемент security- constraint для примера сервлета с зарегистрированным именем «Weather*. Пример3.17. Элемент security-constraint, пропускающий только запросы, нанрасленные посредством RequestDispatcher <?xml versions"1.0" encodings" ISO-8859-1 "?,> <!DOCTYPE web-app- : - PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application. 2.3/7ENj; "http://java.sun.com/dtd/web-application_2_3.dtd"> <*!— Configure the Weather servlet; '.n it receives- .requests from a jJiiPtiocqy-\ 07- -Contrail®] s.srvlfet J .UT <servlet> ‘' <servlet-name>Weather</servlet-name> <servlet-class> M com.j spservletcookbook.Weather £ < . ; c/sclt.]et-class». _ </servlet>..< l;?r, -у. <servlet-mapping> Предоставление доступа к определенным сервлетам Только Д. 1я контроллера |: 87
<servlet-name>Weather</servlet-name» curl-pattern» /weatherurl -pa t tem> <I servlet-mapping» <!— this element prevents the Weather servlet from directly receiving requests from users, •because no users are mapped to the ‘nullrole’ role—> <securi ty-cons traint» cweb-resource-collection» , <web-resource-name»Weather </web-reSource-name» « * <url-pattern»/weather</url-pattern» <http-method»GET</http-method» <http-method»POST< /http-method» </web-resource-collection» 3 <auth-constraint» <role-name»nullrole</role-name> </auth-constraint> cuser-data-constraint» < transport -guar ant ee»NONE </transport-guarantee» Г ' ) </user-data-constraint» c/security-Constraint» clogin-config» cauth-method»BASIC< /auth-method> с/login-confxg> • csecurity-role» ' crole-name»nullrolec/role-name> с/security-role» c/web-app» Следующий шаг по защите сервлета Weather - убедиться, что в файле tomcat-users, xml роль «nullrole» не назначена никому из пользователей. Элемент security-role выглядит примерно следующим образом: csecurity-role» • crole-name»nullrolec/role-name> с/security-role» А вот так выглядит типичный файл <инсталляционный-каталог-Тотса1>/соп//Ют- cat-users.xml. c?xml version='1.0' encodings’utf-8'?> ctomcat-usets» crole rolename="manager"/» crole rolename="tomcat"/» crole rolename="developer"/» 88 | Глава 3. Присвоение имен сервлетам
<user usernames"tomcat" passwords"tomcat" roles="tomcat,manager"/> <user username="bruce" passwords"brucel957" roles=" tomcat, ihanager, developer" /> </tomcat-users> В web-приложениях, сконфигурированных как в примере 3.17, на любой прямой запрос к шаблону URL /weather будет возвращен ответ типа «HTTP статус 403 - в доступе к запрашиваемому ресурсу отказано». Однако сервлет-контроллер, используя метод RequestDispatcher. forward (request, response), по-прежнему может направлять запросы к URL /weather на обработку. В рецепте 3.7 и примере 3.10 показан сервлет, использующий этот способ передачи, поэтому я не стану повторять этот код здесь. --- е- < '' Позаботьтесь о создании доброжелательных страниц с сообщением об ошибке, отправляемых пользователям, обращающимся к защищенным сервлетам. Описание 4 * £ того, как с помощью дескриптора развертывания web-приложения, для определенных кодов возврата HTTP, назначить страницы с сообщениями об ошибках, содержится в главе 9. Вам может потребоваться из страницы с сообщением об ошибке, после небольшого интервала, делать автоматические обновления контроллера или каких- либо login-страниц. См. также Главу 1 по web.xml-, рецепты 3.1-3.9; главу 11 спецификации сервлетов версии 2.3 и 2.4 по отображению запросов на сервлеты; голубые страницы по ядру J2EE http://java.sun.com/ blueprints/corej2eepattems/Pattems/FrontController.html. Предоставление доступа к определенным сервлетам только для контроллера | 89
h'A ЛШЮШТ/ H <тил’ЛлОП яГЛАВД 4 Использование ApacheAnt / f г, 41! .J й. ; 4.0,; ..Введение" , Apache Ant (http://ant.apache.org/) - средство автоматизации, созданное нЙ баз^ Java и XML и распространяемое Apache Software Foundation как ПО с открытым исходным кодом (open source software). Ant эволюционировал в инструмецт^компо) новки, используемый для автоматизации работы над программными проектами JaVa, тоесть для Построений Этих проектов от начала ио конца. Сюда относится койгп (Линия классов Java; создание JAR или WAR файлов и выполнение задач, свя* зайнЫх 0 файловой системой,' например Создание каталогов и перемещение или копирование файлов. Выполнение этих1 задач для определенного проекта управляв ется в Ant файлом компоновки. Файл компоновки Апг* это XML-Лайл. запускаемый на обработку из команд- ной ст^оки'й’ выполняющий Javaknaccbi, йахЬдяЩиеся’за сценой. Ant является расширяемым инструментом; вы можете настроить его для выполнения собствен- f ( ны* зед?Чь А, кроме того, Дп( -независим от платформы и комцтктец поскольку осцоврн на XML и Java. Однажды позрркомивщи;сг с этим удобным и мощныэд инструментом,,web-разработчики быстро оценят то насколько он облегчает выпо^ нение задач по компиляции, упаковке, внесению изменений и переразвертыванвдф V. u.,/.;,. M/.sUv . Л.." йбэб В начале главы Списывается, как скачать Ant и установить его в системе, Затем для новичков объясняются понятия Ant ^цельь и «задача» В заключительной части описано, как создать путь к классам (classpath), включающий необходимы JAR-файлы Tomcat, как создать WAR и JAR файлы и как использовать Ant для выполнения приложения Менеджер. Т01 neat. уk • Sfe %- H. !9O?jZ'
4.1 Как получить и установить Ant Задача Требуется скачать и установить Apache Ant на компьютер. ✓ Решение Направьте браузер по адресу http://ant.apache.org/, скачайте дистрибутив Ant (двоичный код или исходный файл) и затем следуйте инструкциям этого рецепта и сайта поддержки Ant. Обсуждение я. Двоичный дистрибутив Ant можно скачать с http://ant.apache.org/bindownload.cgi. Можно скачать, дистрибутив в виде исходного кода, который содержит исходные Java- файлы, с http://ant.apache.org/srcdownload.cgi. Для инсталляции вам необходимо иметь установленный Java SDK (Software Development Kit - набор для разработчика ПО). ЧЛ- Ant версии 1.5.3 будет последней версией, поддерживающей JDK 1.1. Ant версии 1.5.1 может работать с ЛЖ 1.1, хотя отдельные задачи работают только с JDK 1.2. v- Для использования Ant необходимо иметь синтаксический анализатор XML, совмести- мый с Java API для обработки XML (Java API for XML Processing - JAXP), и путь к классам, указывающий на него. Двоичный дистрибутив Ant включает Apache Xerces2 - син- таксический анализатор XML. Если вы хотите использовать другой JAXP-совместимый син- таксический анализатор, удалите из каталога Ant верхнего уровня /lib (например, jakarta-ant- 1.5.1/lib) файлы xerceslmpl.jar и xmlParserAPIs.jar и поместите в Aib JAR-файлы другого син- таксического анализатора. Их можно добавить и по-другому, напрямую в ваш пользователь- ский путь к классам. Пользовательский путь к классам -г это путь к классам, представленный на вашей машине переменной окружения CLASSPATH. Этот путь к классам замещает значение принятое по умолчанию (., или текущий каталог). В инструменте командной строки * java переключатели -ср и -classpath замешают значение переменной окружения CLASSPATH. Пользовательский путь к классам также можно установить с помощью опции -jar средства java, если путь включает JAR-файлы. Этот способ, в свою очередь, замещает значение пути к классам, заданное другим способом. В общем, проще поместить выбранный вами синтаксический анализатор в каталог jakarta-ant-1.5.1Aib и не связываться с суетой вокруг пути к классам. Как получить и установить Ant | 91
Полное руководство по установке* Ад^ссы^ web-страницы находятся р http://ani.apache.org/mdnual/iridex.ntml. Для запуска Ant на вашей машине необходимо сделать следующее. 1. Распакуйте сжатый файл (в формате ZIP или TAR), содержащий инструмент Ant Для Ant * версии. L5.1 после распаковки дистрибутива будет создан хата тог jakarta-ant-1.5.1.< '' 2. Установите значением переменной окружения ANT_HOME каталог» в который инсталлируется Ant. В Unix это можно сделать, набрав в командной строке следующее: export ANT_HOME=/usr/local/jakarta-ant-1.5.1 В Windows наберите следующее: set ANT_HOME=h:\jakarta-ant-1.5.1 I 3. К переменной окружения PATH добавьте каталог <инсталляционный-каталог-Ап1>/ bin. Это позволит разработчику перейти в любой рабочий каталог* содержащий файл build.xml, и, напечатав ant, выполнить этот файл (выполнение файла build.xml опи- , сано в следующем рецепте). Каталог <‘нстйлляционныи-каталог-Ап1>/Ь1псодержит сценарии, которые запускают классы Java, формирующие основу Ant. Г 4. При необходимости, в переменной окружения tTAVA^_HOME задайте каталог где устач норлер JDK Если,это .сделать, при использовании вами задач javac или rmic# сценарии из каталога/Ъот, входящие в поставку Ant, смогут автоматически присоеди- нить необходимые классы из JDK. Задачи - это XML-элементы, делающие в файла^ Ant основную работу, например, задача war создает war-файлы, а задача javac ком- пилирует классы Java. 5. Проверьте правильность установки, напечатав ant-version. Если все Пройдет хорошо, эта команда дозвратит значение версии,-как доказано ниже; < ' 1 -.\Л .. г K:\>ant -version Apache Ant version 1.5.1 compiled on October 2 2002 См. также Рёцепт 4.2 по использованию целей Aht, рецепт 4.3 ito включений) JAR-файлбв Tomcat в путь к классам Ant; рецепт 4.4 по коМпйЛЯцйй сервлетой’с помощью Ant, рецепт 4.^ по созданию с помощ^о Ant WAR-файлов, рецеп г 4.6 созданию с помощью An( JAR-$arf- Лов; рецепты14.7 и 4.8 по запуску и остановке TOmcdf с помощью ’Arib, рецепты 2.1 й 2„d по развертыванию созданию с помощью Ant web-приложений; руководство по Apache АпЬ http'//ant.apache.org/manual/index.htm!; проек*. Apache Ant: http://ant.apache.org. < '-цэ -лэ 92 | Глава 4. Использование Apache Ant
4.2 Использование целей Aht Задача Для разрабатывающегося приложения создать в файле сборки необходимые элементы target (цель). Решение 1 Создайте один или несколько элементов target в качестве дочерних элементов элемента project. Проследите, чтобы элементы target имели необходимый атрибут name и значение. Обсуждение Файл сборки -г это XML-файл, то есть простой текстовый файл, состоящий из элемен- тов и атрибутов.- В примере 4.1 показан файл сборки Ant (далее - просто файл Ant), выдающий эхо-сообщение на консоль. Как уже говорилось, файлы Ant выполняют Java-код, находящийся за сценой. Тем, какие именно действия необходимо выполнить, вы управляете, помещая в корневой элемент pro j ес t один или несколько элементов target. Пример 4.1. Сборочный файл Ant, выдающий сообщения на консоль <project name="Cookbook" default»"echo-message" basedir,» । <target nanu.«"echo-messagu" description»"Echoing a message to the console•> <echo message»"Hello from the first Ant file"/> </target> </project> Файлы Ant имеют единственный корневой элемент project, который должен содержать атрибут default и значение. Атрибут default задает цель, кстгора.'. ы шолня ^гся, если в командной строке не обнаружено других целей. Атрибуты name и basedir можно не указывать. Атрибут name присваивает элементу project описательное имя. ^трибут basedir задает каталог, относительно которого вычисляются пути,, указанные ^файле. По умолчанию его Значением является каталог, в котором расположен данный сборочный файл. * Что такое цель? Это группа задач, представленная в Ant элементом target. Цели объ- единяют одну или несколько задач (которые в свою очередь представлены элементами task) в логические поименованные единицы управления, наподобие методов Java. Задачи выполняют: компиляцию файлов Java (задача javac), копирование файлов с места на место (задача сору), создание JAR и WAR файлов (задачи jar и war) и кое- что еще. К примеру, цель echo-message из примера 4.1 вызывает задачу echo. ’ Использование целей Ant | 93
Имя echo-message цели из примера 4.1 - это просто некоторое придуманное мною имя. Атрибут description не является обязательным, как и три чругих атрибута: depends, if и unless. Я уже вкратце объяснял назначение атрибута depends; атрибуты if и unless позволяют задавать условия выполнения целей. После того как Ant корректно установлен на компьютер, можно выполнить приве- денной выше файл сборки build.xml с помощью следующей командной строки. Н:\book\cookbook\secl\secl_3»ant Buildfile: build.xml. echo-message: [echo] Hello from the first Ant file.-4 BUILD SUCCESSFUL . , . .„j Total time: 3 seconds ,r Но вначале этот XML-текст с корневым (.цементом project сохраняется в фдмле с именем build.xml. Затем необходимо перейти в каталог, содержащий данный файл сборки, и напечатать ant, без каких-либо опций. После этого Ant ищет в текущем ката- логе файл build.xml и запускает цель по умолчанию (default) проекта (в примере 4.1 это3 цель echo-message). г*^г Можно дать сборочному файлу другое имя (не build.xml)i но тогда Ant необходимо запускать с переключателем -bui Idf i le. ant -buildfile dev.xml Большинство сборочных файлов включают несколько целей, которые в определенной' последовательности инициируют задачи разработки. Пример 4.2 демонстрирует использо- вание атрибута depends (зависит от). В нем показано, как выполнить несколько целей в определенной последовательности. ’ р Пример 4.2. Использование атрибута depends элементов target для выполнения целей в определенной последовательности «project name®"Cookbook" default="echo-message" basedir®"»"> «target name®‘'init"» <*•' * lV «property name®"name" value®"Bruce Perry"/» o- «/target» «target name®"show-props" depends®"init"» «echo message® \ "The 'name' property value is: $[name}"7» «echo message® "OS name and version is: $‘{os.name} $ {os. version} "/> «echo message® < < n- "Your Java home is: ${java.home} "/> , r «/target» 94 | Глава 4. Испст зов~ние Apache Ant
«target name='’ccho'message,, depends="show-props“> • '< j «echo message? ar, ‘и. . ., "Helld from: the first Ant file in directory: ${basedir}"/> </targe t> «/projects» j Здесь в элемент project вложен ие один, а несколько элементов target. Цель echo-message все еще является целью по умолчанию, но ее поведение меняется из-за значения атрибута depends. Этот необязательный атрибут задает имя одной или несколь- ких целей Ant, которые должны быть выполнены до данной цели. Другими словами, цель echo-message говорит: «я завишу от цели show-props, сначала выполните ее». Цель show-props, в свою очередь, тоже имеет атрибут depends, свидетельствующий, что она зависит от цели init. В результате сборочный файл устанавливает такую последова- тельность выполнения своих целей: init -»show-props -»echo-message. Результат выполнения файла сборки из командной строки показан ниже.. < Н: \bdoX\cookbook\secl\seclT_3>ant. Buildfile: build.xml init: show-props: / «[echo] Thu 'name* property value is: Bruce Perry [echo] OS name and version is: Windows NT 4.0 ’ [echo] Your Java home is: h:\jdkl.3.1_02\jre echo-message:- , j •<,„ , r. [echo} Hello from the first Ant file id directory: л \book\ccokbook\secl\secL_J3 ‘ i BUILD SUCCESSFUL ~ ( Tgtal time; 2 seconds Вот что делает этот файл. 1. Сначала цель init создает свойство name, содержащее значение «Bruce Perry». , Для этого цель использует задачу property. Повторюсь, реальную работу 'в Ant выполняют задачи; цели же просто 1гуппируют элементй, вызывающие.одну или несколько задач. , , 2. Затем цель show-props выводит на консоль значение свойства name (созданного целью init) и трех встроенных свойств: os .name, os. version и java.home. 3. Цель echo-message выводит на консоль сообщение и значение свойства basedir. Для вывода на консоль все цели используют задачу echo. Заметьте, что свойство name не будет установлено, если цель init не будет выпол- нена. И если цель show-props определить так, как показано ниже, появятся проблемы. «target name="show-props"> . . . </target> '• Использование целей Ant j iV *
Однако эта цель объявлена правильно. <target name="show-props” depends="init"> . . . </target» Без атрибута depends цель init никогда не будет выполнена, поскольку в этом случае цепочка выполнения сборочного файла будет такой: show-props -*echo-message. Свойство name никогда не получит значение. В реальности, сборочные файлы Ant намного сложнее, чем приведенные примеры, которые являются скорее демонстрацией широких возможностей Ant, чем примерами плохого проектирования. В главе 2 показано, как развертывать отдельные сервлеты и web-приложение с помощью более развитых файлов Ant См. также Рецепт 4.1 по скачиванию и установке Ant; рецепт 4.3 по включению JAR-файлов Tomcat в путь к классам для Ant; рецепт 4.4 по компиляции сервлета с помощью Ant; рецепт 4.5 по созданию с помощью Ant WAR-файла; рецепты 4.7 и 4.8 по запуску и остановке Tomcat с помощью Ant; рецепты 2.1 и 2.6 по развертыванию с помощью Ant web-приложения; руководство по Ant, раздел по задаче property: http://ant.apache.org/ manual/CoreTasks/property.htmh руководство по Ant, сегмент цо целям: http://ant.apache. org/manual/using.html#targets; индексную страницу руководства по Ant: http://ant.apache. org/manual/index.html; проект Apache Ant: http://ant.apache.org. 4.3 Включение JAR-файлов Tomcat в путь к классам сборочного файла Задача Требуется установить для Ant путь к классам, включающий различные JAR-файлы Tomcat. Решение Для определения пути к классам создайте структуру, отражающую путь, и затем, при необходимости, ссылайтесь на нее. Задайте каталоги, в которых размещаются необ- ходимые JAR-файлы с Помощью внешнего файла свойств. 96 | Глава 4. Использование Apache Ant
Обсуждение Перед началом компиляции сервлета с помощью Ant, необходимо убедиться, что в путь к классам, используемый при компиляции в сборочном файле, включены ссылки на классы API сервлетов. К примеру, каталог <инсталляционный-каталог-Тотса1>/соттопЛ1Ь содержит servletjar, включающий классы, необходимые для компиляции сервлета.. А для компиляции сервлета, использующего JavaMaii API, потребуется включить файл mailjar, находящийся в том же каталоге. В другом каталоге, <инсталляционный-каталог- Totncat>/common/endorsed, содержится файл xmlParserAPIs.jar, который необходимо ука- зать в пути к классам, если требуется использовать классы, связанные с синтаксическими анализаторами SAX или DOM для XML. В примере 4.3 путь к классам задается с помощью XML-элемента path. Ниже в этом XML-файле, в цели compile-servlet, этот путь к классам используется для компи- ляции сервлета. Пример 4.3. Определение пути к классам, содержащего JAR-файлы Tomcat «project name="Cookbook" default="compile-servlet-" basedir="."» <!—включаем свойства compiled-servlet и tomcat-dir —> <property file="global.properties" /> «path id*"servlet-classpath"> «fileset dir*"${tomcat.dir}/сопвпоп/11Ь"> «include name»"*.jar" /> «/fileset» «fileset dir="${tomcat.dirI/common/endorsed"» <include name"”*.jar" /> </fileset> «/path» <target name= "compile-servlet"». «echo message^"Compiling the servlet....“/> <javac srcdir="${src}" destdir="$-{build} "> •«include name="${compiled.servlet}.java" /> «classpath refid»"servlet-classpath "/> </javac> </target» «/project» С помощью элемента path можно определить путь к классам и затем использовать его повсюду в сборочном файле, наподобие экземпляра переменной Java-класса. К преимуще- ствам такого подхода относится то, что можно создать очень сложный путь к классам, при этом определив его всего один раз. В тех местах сборочного файла, где потребуется данный путь к классам, его можно подтянуть с помощью элемента classpath'c атрибу- том refid. В примере 4.3 элементу path присвоен уникальный идентификатор serv- let-classpath. Разработчик создает это имя для однозначной идентификации структуры, хранящей путь. Включение JAR-файлов Tomcat в путь к классам сборочного файла | 97
Другим базовым типом задачи Ant является задача fileset (набор файлов), fileset -г это элемент, представляющий группу файлов. Два вложенных элемента fileset в примере имеют атрибут dir и задают два подкаталога инсталляционного каталога Tomcat: ./common/lib и ./common/endorsed. В этих подкаталогах находятся мно- гие важные библиотеки Java, например servlet.jar и mailjar. Вложенные в fileset элементы include создают шаблон (в атрибуте name), задающий тип файлов, включаемых в данную группу файлов. В примере в группу файлов включаются все файлы указанных каталогов, имеющие расширение «.jar». Если в дальнейшем потребуется отсеять некоторые виды JAR-файлов из группы фай- лов, можно использовать вложенный в fileset элемент exclude. <fileset dir="${tomcat.dir}/c<xnmon/lib"> ' •«include name="*.jar" /> «exclude name="commons *.j ar"/> </fileset> Шаблон commons* .jar исключает из пути к классам все JAR-файлы, начинающиеся на «common», за которыми следует ноль и более символов и расширение «.jar». Цель compile, servlet из примера 4.3 выдает эхо-сообщение на консоль, после чего использует задачу javac для компиляции сервлета. Следующий код из примера 4.3 делает доступными в сборочном файле Ant два свой- ства, которые определены в другом файле. «property file="global.properties" /> Файл global .properties выглядит примерно следующим образом: tomcat.dir=k:/jakarta-tomcat-4.1.12 compiled.servlet=MyTask src=.\src build=.\build Свойство compiled, servlet содержит имя исходного Java-файла, подлежащего компиляции. А свойство tomcat. dir - путь к корневому каталогу Tomcat. В примере 4.3 элемент classpath вложен в задачу javac. «javac srcdir="${src}" destdir= "${build} "> «include names-${compiled.servlet}.java" /> «classpath refid="servlet-classpath"/> </javac> Атрибут refid элемента classpath подтягивает путь к классам, определенный ранее в этом же сборочном файле (путь включает все JAR-файлы Tomcat из каталогов ./ сопипон/ИЬ и ./common/endorsed}. Значением атрибута refid является идентификатор элемента path (“servlet-classpath”). Короче говоря, элемент path из прймера 4.3 представляет путь к классам; идентификатор или имя этого элемента - servlet-class- path. 98 | Глава 4. Использование Apache Ant • <
Если в путь к классам, определенный в файле Ant, потребуется добавить другие классы или JAR, просто добавьте в элемент path дополнительные вложенные элементы fileset. В примере 4.4, с помощью третьего вложенного fileset, к пути к классам из примера 4.3 (с относящимися к Tomcat JAR-файлами) добавляется все содержимое каталога build. Пример 4.4. Структура пути из трех вложенных групп файлов «path id="servlet-classpath"> <fileset dir=“${tomcat.dir}/common/lib"> «include name="*.jar4 /> </fileset> <fileset dir="${tomcat.dir}/common/endorsedn> r «include name="*.jar" /> / «/fileset> «fileset dir="./build"/> </path> Идиома **, часто появляющаяся в шаблонах пути, означает ноль или более каталогов. Например, следующий тег fileset включает все файлы, содержащиеся в любом и? вложенных каталогов images независимо от того, насколько глубоко они вложены (src - имя свойства, указывающего исходный каталог для данного fileset). «fileset dir="${src}"> «include name="**/images/*"/> </fileset> См. также Рецепт 4.1 по скачиванию и установке Ant; рецепт 4.2 по созданию целей Ant; рецепт 4.4 по компиляции сервлета с помощью Ant; рецепт 4.5 по созданию с помощью Ant WAR- файла; рецепт 4.6 по использованию Ant для создания JAR-файлов; рецепты 4.7 и 4.8 - по запуску и Остановке Tomcat с помощью Ant; рецепты 2.1 и 2.6 по развертыванию с помощью Ant web-приложения; руководство по Ant, раздел по задаче property: http://ant. apache.org/manual/CoreTasks/property.hti1il; руководство по Ant, сегмент по целям: http://ant. apache.org/manual/using.htmlkhargets; индексную страницу руководства по Ant: http://ant. apache.org/manual/index.html; проект Apache Ant: http://ant.apache.org. Включение JAR-файлов Tomcat в путь к классам сборочного файла | 99
4.4 Компиляция сервлета с помощью сборочного файла Ant Задача Создать простой сборочный файл для компиляции отдельных сервлетов, избавляющий от утомительного ручного ввода имен сервлетов. Решение Спроектировать сборочный файл Ant так, чтобы имя Java-класса, подлежащего ком- пиляции. можно было задавать во внешнем файле свойств или в командной строке. Обсуждение Если для разработки и компиляции сервлетов вы не используете IDE, то сборочный файл Ant может помочь автоматизировать процесс компиляции исходных файлов. Для того чтобы этот файл можно было использовать неоднократно, его необходимо спроектировать так, чтобы он получал имя исходного файла из внешнего файла свойств или из командной ' строки. Преимущества Ant особенно заметны при использовании его для автоматизации всех аспектов сборки, архивирования и развертывания web-приложения. Однако Ant также можно использовать и как командный процессор. В данном рецепте Ant используется для динамического выбора Java-файла на компиляцию. Файл build.xml из примера 4.5 импортирует ряд свойств из файла buildproperties, включая имя сервлета подлежащего компиляции. Единственный' способ выбрать Java- файл для компиляции, не изменяя сборочный файл, - изменить в файле свойств значение свойства compiled, servlet. tomcat.dir=/users/bruceper/java/jakarta-tomcat-4.1.12 compiled. servlet=MyServlet Чтобы выполнить пример 4.5, необходимо перейти в каталог с файлом build.xml и напечатать ant без всяких параметров. Если вы выполняете сборочный файл с другим именем - не buildxml, а, например, ant_.compiler.xml, то в командной строке необходимо набрать следующее. _____ЪЛ’ ant -buildfile ant_compiler.xml Сначала сборочный файл импортирует свойства tomcat .dir и compiled, serv- let из файла build.properties. Этот файл находится в том же каталоге, что и сборочный файл. Свойство tomcat .dir используется для создания пути к классам, указывающего на JAR-файлы из двух каталогов, являющихся частью дерева’Тошса! (см. рецепт 4.2). 100 | Глава 4. Использование Apache Ant
Пример 4.5. Компиляция сервлета с помощью сборочного файла Ant , » «project name="servlet compiler" default*"compile" basedir=*."> <property file="build.properties" /> «path id>->" servlet-classpath"» <fileset dir*"${tomcat.dir}/common/lib"> «include name""*.jar" /> «/fileset» < fileset dir"" $ {tomcat.dir}/coumon/endorsed"> «include name""*.jar" /> «/fileset» c. </path> / «target name*4init" descriptions"Initializes some properties."» «echo messages"Initializing properties."/» «property name="build" value="./build" /> «property name="src" values".Isrc" /> «/target» «target name="prepare" depends="init"> «echo messages"Cleaning up the build directory."/» «delete dir="${build}"/> «mkdir dir="${build}"/» «/target» «target name«"coiqpile" depends*"prepare" description*"Compile the servlet"» «echo message*"Compiling the Java file "/» «echo message""${compiled.servlet}.java..."/» «javac srcdir-"${src}" destdir«"${build}"» «include name*"${compiled,servlet}.java" /» «classpath refid*"servlet-classpath "/» «/javac» «/target» «/project» Цель init создает два свойства, представляющие исходный каталог (src) и каталог для записи результата компиляции (build). Java-файл, подлежащий компиляции, нахо- дится в каталоге src. Типичный сборочный файл также содержит цель init, но обычно она инициализирует несколько большее количество свойств. Поскольку цель compile имеет атрибут depends, указывающий на цель prepare, а цель prepare, в свою очередь, зависит от init, то последовательность сборки выглядит так: init ->pre- pare -> compile. , \ Компиляция сервлета с помощью сборочного файла Ant | 101
Цель prepare просто очищает каталог build, чтобы удостовериться, что в итоге он будет содержать только результат последней компиляции. Цель compile для компиляции Java-файла использует задачу javac. Элемент javac имеет атрибуты, задающие исходный каталог и каталог результата для Java-файлов, подле- жащих компиляции. В примере 4.5 значения для этих атрибутов берутся из свойств src и build. Два вложенных в задачу javac элемента компилируют заданный файл сервлета и формируют путь к классам, используемый задачей j avac (см. рецепт 4.2). » Вот что остается на консоли после выполнения данного сборочного файла (с некоторыми изменениями для удобочитаемости). init: [echo] Initializing properties. prepare: [echo] Cleaning up the build directory. [delete! Deleting directory /Users/bruceper/books/cookbook/seel/secl_3/buiId [mkdir] Created dir: /Users/bruceper/books/cookbopk/secl/secl_3/build ' compile: [echo] Compiling the Java file MyServlet.java... [javac] Compiling 1 source file to /Users/bruceper/books/cookbook/secl/secl_3/build BUILD SUCCESSFUL Total time: 6 seconds f Использование командной строки для задания сервлета, подлежащего компиляции Что делать, если необходимо откомпилировать уже другой сервлет, но вы не хотите набирать новое имя Java-файла в файле build.properties! Выполнение сборочного файла build.xml с помощью командной строки, приведенной ниже, перезапишет значение импортируемого свойства compiled. servlet. * ; ant -Dcompiled.servlet=AnotherServlet Здесь AnotherServlet.java - имя файла, ожидающего компиляцию в каталоге src. Вот фрагмент выдачи на консоль, подтверждающий, что значение свойства, переданное из командной строки, перезаписывает значение свойства с таким же име- нем, импортируемого из внешнего файла или создаваемого в самом сборочном файле. compile: [echo] Compiling the Java file AnotherServlet.java... [javac] Compiling 1 source file to /Users/bruceper/books/cookbook/seelIsecl_3/build 102 | Глава 4. Использование Apache Ant
, Задача javac компилирует только те Java-файлы из каталога src, для которых нет соот- ветствующего файла класса или когда файл, класса более старый, ием исходный Java-файл. И, как всегда, чтобы узнать все возможные варианты использования и атрибуты javac, обращайтесь к руководству по Ant: http://ant.apache.ofg/ntanuaI/CoreTasks/javac.html. Если необходимо скопировать откомпилированный класс сервлета в каталог web- приложения, можно добавить цель deploy-servlet, использующую задачу сору. Л*.< target names: * deploy- servlet" depends=" compile"» <echo message= "Copying the servlet to Tomcat web app"/> <copy todir="${tomcat.webapps}/WEB-INF/classes"» <fileset dir="${build} /> </copy> </target» Задача copy, используя в качестве копируемых файлов вложенный в нее fileset, представляющий содержимое каталога, имя которого берется из свойства build, копирует эти файлы классов в каталог WEB-INF/classes приложения по умолчанию Tomcat. См. также Рецепт 4.1 по скачиванию и установке Ant; рецепт 4.2 по созданию целей Ant; рецепт 4.3 по созданию пути к классам для файла Ant; рецепт 4.5 по созданию с помощью Ant WAR- файла; рецепт 4.6 по использованию Ant для создания JAR-файлов; рецепты 4.7 и 4.8 пр запуску и остановке Tomcat с помощью Ant; рецепты 2.1 и 2.6 по развертыванию с помощью Ant web-прилРжения; руководство по Ant раздел по задаче property: httpr// ant.apache.org/manual/CoreTasks/property.html-, руководство по Apt, сегмент пр целям: http:// ant.apache.org/manual/using.htnil#targets\ индексную страницу руководства по Ant: http://ant. apache.org/manual/index.html-, проект Apache Ant: http://ant.apache.org. 4.5 Создание WAR-файла с помощью Ant Задача . Используя Ant создать файл web-архива (WAR). I Решение Воспользуйтесь задачей war. 4 Создание WAR-файла с помощью Ant | 103
Обсуждение WAR-файл - это архив web-приложения, содержащий классы сёрвлетов, JSP-файлы, HTML-файлы, каталоги с изображениями, JAR-файлы, XML-файлы конфигурации и другие ресурсы, от которых зависит web-приложение. Для того чтобы сделать wtjb-приложение доступным пользователям, WAR развертывается в web-контейнере, например в Tomcat. Для облегчения генерирования WAR из структуры каталогов, содержащих необходимые файлы web-приложения, Ant включает задачу war. В примере 4.6 показан отдельный сборочный файл, предназначенный только для создания WAR-файла. Это проще, чем изучать одну цель в составе сложного сборочного файла, который компилирует Java-файлы, создает WAR и развертывает приложение (см. рецепт 2.6). В данном примере задается следующая последовательность сборки. init-» pre- pare -»create -»war. Цель init создает несколько свойств, указывающих на ката- логи, например, каталог build, содержащий файлы классов сервлета. Свойство context- path представляет путь к контексту для web-приложения и, в данно^ случае, имя WAR- файла (myapp.war). При выполнении этого сборочного файла из командной строки теку- щим каталогом должен быть корневой каталог web-приложения. Пример 4.6. Файл Ant, использующий задачу war «project names"war-task" defaults"create-war" basedir="."> «target name="init" descriptions"Initializes some properties."> «echo messages"Initializing properties."/* «property name="build" values".\buiId" /> «property name=src" values".\src" /> «property names"dist" values".\dist" /> «property names"lib" values".\lib" /> «property name=“web" values".\web" /> «property names"meta" values".\meta" /> «property name="context-path" values"myapp" /> «/target* «target name="prepare" depends="init"> «echo messages "Cleaning up the build and dist directories."/* «delete dir="${build}"/> «mkdir dir="${build}"/> «delete dir="$(dist}"/> «mkdir dir="${dist}"/> «/target* «target names"create-war" descriptions 104 | Глава 4. Использование Apache Ant
"creates a web application archive file" depends"prepare"> <war destfilea"${dist}/${context-path} .war" webxml" " $ {meta} /web.xml"> <classes dir«"${build}"/> <lib dir""${lib}"/> ( <fileset dir«"${web}"/> </war> </target> i </project> Если сборочный файл имеет имя war-task.xml, то для его г выполнения требуется следующая командная строка. ant -buildfile war-task.xml Цель create-war вызывает задачу war. Атрибут destfile задачи war необходим - он указывает местоположение итого- вого WAR-файла. В примере 4.6 WAR-файл создается в каталоге dist. Атрибут webxml указывает местоположение дескриптора развертывания web-приложения. В данном примере файл web.xml находится в каталоге meta. В примере задача war имеет гри вложенных элемента: classes, lib и fileset. Атрибут dir элемента classes указывает на каталог, содержащий Java-классы, которые будут размещаться в каталоге WEB-INF/classes. Задача war автоматически создает в WAR- файле каталог WEB-INF/classes. При его создании эта задача также автоматически воспроизводит все связанные с пакетом каталоги из каталога build. Иными словами, если каталог build включает структуру каталогов com/jspservletcookbook, то и WAR-файл будет иметь такую же структуру каталогов в WEB-INF/classes. Элемент lib выбирает и сохраняет все JAR-файлы, которые будут размещаться в ката- логе WEB-INF/lib WAR-файла. И, наконец, вдоженнь/й элемент fileset, в данном случае, выбирает из каталога /web все статичные файлы и любые вложенные каталоги image и поме- щает все это на верхний уровень дерева каталогов файла WAR. Вот так выглядит выдача на консоль данного сборочного файла (с некоторыми правками для удобства чтения) init: [echo] Initializing properties. prepare: [echo] Cleaning up the build and dist directories. [delete] Deleting directory /Users/bruceper/books/cookbook/buiId [mkdir] Created dir: /Users/bruceper/books/cookbook/build [delete] Deleting directory /Users/bruceper/books/cookbook/dist Создание WAR-файла с помощью Ant | 105
[mkdir] Created dir: » ‘ •' /Users/bruceper/books/cookbook/dist create-war: [war] Building war: , /Users/bruceper/books/cookbook/dist/туарр.war Задача war имеет много других, необязательных атрибутов, все они описаны в руко- водстве по Ant: http.//ant.apache.org/manual/CoreTasks/war.html. См. также Рецепт 4.1 по скачиванию и установке Ant; рецепт 4.2 по созданию целей Ant; рецепт 4.3 по созданию пути к классам для файла Ant; рецепт 4.4 по компиляции сервлета при помощи Ant; рецепт 4.6 по использованию Ant для создания JAR-файлов; рецепты 4.7 и 4.8 по запуску и остановке Tomcat с помощью Ant; рецепты 2.1 и 2.6 по развертыванию с помощью Ant web-приложения; руководство по Ant, раздел по задаче property: http:// ant.apache.org/manual/CoreTasks/property.html", руководство по Ant, сегмент по целям: http:// ant.apache.org/manual/usmg.htn-ilthargets', индексную страницу руководства по Ant: http://ant. apache.org/manual/index.html", проект Apache Ant: http://ant.apache.org. 4.6 Создание JAR-файла с помощью Ant Задача Создать JAR-файл при помощи Ant. Решение Используйте встроенную задачу jar. Обсуждение Задача jar автоматизирует создание JAR-файлов. Так же как задача war для W AR- файлов, задача j аг позволяет автоматизировать процесс набора командных строк, необ- ходимых для создания JAR-файла. Таким образом, сборочные файлы используют задачу jar наподобие сценария или командного файла. Спецификацию Sun Microsystems на JAR-файл можно найти в http://java.sun.coni/j2se/1.4/docs/guide/jar/jar.html. В web-приложениях JAR-файлы используются как хранилище отдельных библиотек с кодом, необходимых для работы приложения (например, JAR-файл с драйвером базы данных) Они содержаться е каталоге WEB-INFAib приложения. В примере 4.7 показана цель Ant, использующая задачу j аг для создания JAR-файла и последующего копирова- ния его в каталог lib web-приложения. Эти действия предшествуют архивированию web- приложения в WAR-файл, которое можно включить в этот же сборочный файл, для более полной автоматизации (см. рецепт 4.5 по созданию WAR-файла). 106 | Глава 4. Использование Apache Ant
Пример 4.7. Создание JAR-файла с помощью Ant f «project name="jar-task" defaults"create-jar" basedir="."> <target name="init" descriptions"Initializes some properties,"» <echo messages"Initializing properties."/» <property name="dist" values"dist" /» <property name="web" values"web" /> «property name="meta" values"meta" /> «property names'!jar-name" values"myutils" /> «/target» «target name="prepare" depends="init"> «echo messages" •Cleaning up the build and dist directories."/» «delete dir="${dist}"/» «mkdir dir="${dist}"/» «/target:» «target name*"create-jar" descriptIon*"creates a JAR archive file" depends*"prepare"» «jar destfile*"${diet}/${jar-name}.jar" basedir*"../.,/" includes*"**/".class **/${wub}/*.html"> «fileset dir*"../../images”/» </jar> «/target» «/project» Этот сборочный файл включает три цели, выполняемые в следующем порядке: ini t->prepare->create- j аг. Эти цели создают ряд свойств и очищают каталог dist, предназначенный для итогового JAR-файла. Цель create-jar вызывает задачу jar, которая выглядит следующим образом. «jar destfile="${dist}/${jar-name}.jar" basedir="../../" includes=•**/*.class **/$fweb}/*.htrnl"» «fileset dirs"../../images"/> «/jar» Создание JAR-фдйла с помощью Ant | 107
Атрибут destf lie элемента jar задает местоположение и имя создаваемого JAR- файла. Здесь используется свойство jar-name, чтобы пользователь мог выполнить этот файл Ant из командной строки и при необходимости передать новое имя JAR-файла в качестве параметра. ant -Djar-name=mynew jar.j ar Помните, что значения свойств, задаваемые с помощью переключателя -D, заменяют значения этих же свойств, заданные внутри сборочного файла. Атрибут basedir задачи jar определяет каталог верхнего уровня файлов, которые будут включены в JAR. Например шаблон ../../ означает «подняться вверх на два ката- лога относительно базового каталога данного проекта»; другими словами, подняться на два каталога вверх от каталога, в котором находится данный сборочный файл Ant. Атрибут includes имеет два шаблона, разделенных пробелом (для разделения можно использовать и запятую). Эти шаблоны предназначены для отбора файлов для включения в JAR. Первый шаблон определяет включение всех файлов с расширением .class, находя- щихся в нуле или более каталогов, внутри каталога, заданного в basedir. Таким образом, JAR-файл будет содержать все файлы классов Java из всех подкаталогов каталога из basedir; JAR включит любые вложенные каталоги, в которых будут найдены файлы классов. Второй шаблон (**/${web}/* .html) берет все подкаталоги каталога, задан- ного свойством web и включает в JAR все файлы с расширением .html. И еще раз повторю, что в JAR-файл будут включены все вложенные директории, в которых будут найдены HTML-файлы. И наконец, элемент fileset, вложенный в задачу jar, берет все содержимое ката- лога ../../ и включает его в JAR, но не включает туда сам каталог ./images. Чтобы включить не только содержимое, но сам каталог images на верхний уровень JAR, необ- ходимо переписать задачу j аг следующим образом: <jar destfile="${dist}/${jar-name}.jar" basedir="../../" includes=“**/*.class **7${web}/*.html **/imagee/*.gif*/> Здесь в атрибут includes добавлен третий шаблон (**/images/* .gif), который выбирает из всех каталогов images, вложенных в каталог, заданный в basedir, все GIF- файлы. Сами каталоги images также включается в JAR. — Тку Шаблон ** часто используется в элементах Ant, он означает ноль или более каталогов. 108 | Глава 4. Использование Apache Ant
Манифест , Если в задаче jar отсутствует атрибут manifest, то в JAR-файле будет сформирован файл META-INF/MANIFEST.MF. Этот манифест по умолчанию выглядит следующим образом: Manifest-Version: 1.0 Created-By: Apache Ant 1.5.1 Если вы хотите задать местоположение собственного файла манифеста (например для подписи JAR-файла или указания файла, содержащего метод main() в исполняемом JAR), используйте атрибут manifest задачи jar. Значением необязательного атрибута manifest может быть местоположение файла манифеста или имя другого JAR-файла, который юбавлен с помощью вложенного элемента fileset. Если это J AR-файл, дан- ная задача ищет манифест META-INF/MANIFEST.MF в нем. См. также Рецепт 4.1 по скачиванию и установке Ant; рецепт 4.2 по созданию целей Ant; рецепт 43 по созданию пути к классам для файла Ant; рецепт 4.4 по компиляции сервлета при п< »мощи Ant; рецепты 4.7 и 4.8 по запуску и остановке Tomcat с помощью Ant; рецепты 2.1 и 2.6 ло развертыванию с помощью Ant web-приложения; руководство по Ant, раздел по задаче property: http://ant.opache.org/manual/CoreTasks/property.html', руководство по Ant, сег- мент по целям: http://ant.apache.Org/manual/using.html#targets', индексную страницу руково- дства по Ant: http://ant.apache.org/nianual/index.html', проект Apache Ant: http://ant.apache.org. I 4.7 Запуск с помощью Ant приложения на Tomcat Задача Создать файл Ant для запуска web-приложения на Tomcat. Решение Используйте поддерживаемую Tomcat задачу StartTask, с помощью которой Ant может управлять сервером Tomcat. Обсуждение Tomcat, являющийся контейнером для сервлетов и JSP, включает встроенное web- приложение «Менеджер», которое можно использовать для запуска, остановки, развертывания и выполнения других административных задач в отношении web-приложе- ний. Путь к контексту этого приложения: /manager. ___________________ i__________________'____, Запуск с помощью Ant приложения на Tomcat | 109
Tomcat версии 4 (и более поздних) включает Java-классы, позволяющие разработчикам Использовать приложение «Менеджер» из сборочных файлов Ant. Преимущество исполь- зования из Ant приложения «Менеджер» в том, что в этом случае не требуется кон- фигурировать файл conf/server.xml, чтобы сделать web-приложение перезагружаемым динамически (см. рецепт 2.2). Кроме того, можно запускать и останавливать отдельное web-приложение, не затрагивая других приложений Tomcat. Документация по «Менеджеру» находится в http://jakarta.apache.org/tomcat/tomcat-4.l- doc/printer/manager-howto.html. Ниже представлена последовательность запуска Tomcat из Ant. 1. Убедитесь в наличии JAR-файла, необходимого для задачи запуска Tomcat: <Инстал- ляционный-католог-Anr^/lib/catalina-ant.jar. При необходимости скопируйте этот файл из каталога <инсталляц1юнный-каталог-Тотса1>/зегуегЛ1Ь в свой каталог <Инсталля- ционный-катсъ1ог-Апг>/НЬ (известный K3X.ANT_H0MEAib). 2. Убедитесь, что база данных пользователей Tomcat включает имя пользователя, которому назначена роль manager. Запускать и останавливать web-приложение с помощью инструмента «Менеджер» могут лишь пользователи-администраторы. Отображение пользователей и паролей на рбли задается в файле conf/tomcat-users.xml. Только пользова- • тель с ролью manager может использовать инструмент «Менеджер». Вот пример соответ- ствующей записи в файле conf/tomcat-users.xml. «user usemame="doug" passwords "_1968dgw" roles="manager,dbadmin"/> 3. В файле Ant, используя элемент taskdef, определите пользовательскую задачу и присвойте ей имя. В примере 4.8 этой задаче присваивается имя start, и это имя затем используется в цели, предназначенной для запуска Tomcat. 4. Выполните этот файл Ant из командной строки, для чего перейдите в каталог с этим файлом и напечатайте ant. Пример 4.8 показывает элемент taskdef, определяющий задачу start, за которым следует цель, запускающая заданное приложение Tomcat. Пример 4.8. Запуск Tomcat при помощи файла Ant •«project name="My Project" defaults"start-tomcat" basedir="."> «taskdef names"start” classnames"org.apache.catalina.ant.StartTask" /> <1— импортируем свойства задающие имя и пароль пользователя, url, и путь к контексту —> «property file*"global.properties" /> «target name-*” start -tomcat"' descriptions"Starts the Web application"» «echo messages"Starting the default application ${ context-path}..."/> «start 110 | Глава 4. Использование Apache Ant
url»"${url}" userndst-" >" $ {usernams) " password»"$(password)" path»"/$(context-path}" /> , </target> </project» Задача start имеет четыре атрибута, значения которых в примере 4.8 устанавливаются в файле global.properties. Ниже приведен этот текстовый файл, содержащий четыре пары ймя/значение, которые импортируются в файл Ant с помощью задачи property. <property file="global.properties- /> Файл globaLproperties размещается в том же каталоге, что и сборочный файл Ant. Вот его содержимое. url=http://localhost:8080/manager usemame=bruce f password=brucel957 context-path=home Свойство url задает URL «Менеджера» Tomcat, username и password определяют пользователя, которому в базе данных пользователей присвоена роль manager, свойство context-path задает путь к контексту web-приложения, которое вы запускаете, и сам файл Ant добавляет к этому пути символ слеш (Г). Другой способ передачи значений свойств в файл Ant - указать их в командной строке. —ЦА* ant -Dusername=bruce -Dpassword=brucel957 -Durl=http://localhost:8080/manager -□context -path=home Значения свойств из командной строки замещают те значения, что были заданы в задаче property. Выполните данный файл Ant, для чего перейдите в каталог, где он находится, и напечатайте в командной строке ant или ant -buidfile <имя командного файла». Ниже приведена такая командная строка и вывод результатов ее работы. Н:\book\cookbook\code\chap4>ant .-buildfile start.xml Buildfile: start.xml start-tomcat: [echo] Starting the default application home... [start] О1Г- Started application'at context path /home BUILD SUCCESSFUL Total. time: 4 seconds Запуск с помощью Ant приложения на Tomcat | 111
Когда приложение остановлено, оно недоступно для web-пользователей (см. рецепт 4.8). Когда приложение запущено вновь, оно может нормально принимать запросы. Приложение «Менеджер» Tomcat может инициировать многие другие обще- yJV 4 ж административные задачи, например развертывание приложений (см. рецепт 2.6). См. также Описание приложения «Менеджер» Tomcat hftp://jakarta.apache.org/tomcat/tomcat-4.1- doc/manager-howto.html', рецепт 4.1 по скачиванию и установке Ant; рецепт 4.2 по соз- данию целей Ant; рецепт 4.3 по созданию пути к классам для файла Ant; рецепт 4.4 по ком- пиляции сервлета при помощи Ant; рецепты 4.5 и 4.6 по созданию WAR и JAR файлов; рецепт 4.8 по остановке Tomcat с помощью Ant; рецепты 2.1 и 2.6 по развертыванию с помощью Ant web-приложения; руководство по Ant, раздел дю задаче property: http:// ant.apache.org/manual/CoreTasks/property.html-, руководство по Ant, сегмент по целям: http:/ /ant.apache.org/manual/using.html^argets', индексную страницу руководства по Ant: http:// ant.apache.org/manual/index.htmh, проект Apache Ant: http://ant.apache.org. \ 4.8 Остановка Tomcat с помощью Ant Задача С помощью Ant остановить определенное web-приложение. Решение В файле Ant, с помощью элемента taskdef определите соответствующую задачу и Java-класс org. apache. catalina. ant. StopTask. / Обсуждение В ходе разработки может потребоваться остановить web-приложение, работающее на Tomcat, например, для того чтобы добавить новые сервлеты или записи в дескрипторе развертывания, а затем снова запустить его. чтобы внесенные изменения начали дейст- вовать. В случае отсутствия настройки conf/server.xml, делающей данное приложение динамически перезагружаемым (см. рецепт 2.2), для остановки одного web-приложения без ущерба для остальных, можно использовать соответствующую цель Ant. Этот процесс - противоположность процессу запуска приложения (рецепт 4,7), при этом приложение прекращает работу До тех пор, пока его не запустят вновь. 112 | Глава 4. Использование Apache Ant
Класс org.apache;catalina.ant.StopTask обеспечивает связь между Ant и приложением «Менеджер» Tomcat. «Менеджер» - встроенное web-приложение (путь к контексту /manager), которое можно использовать для администрирования, других web- приложений. Следующие четыре шага, знакомые вам по рецепту 4.7, необходимы для использова- ния задачи stop. . 1. Убедитесь в наличии JAR-файла, необходимого для остановки Tomcat: <Инсталля- ционный-каталог-Ап1>/ИЬ/са1аНпа-ап1./аг. При необходимости скопируйте этот файл , из каталога <инсталляционный-каталог-Тотса1>/зегуег/Ш> в свой каталог <Инстал- ляционный-каталог-Ап1>Л1Ь (известный KaxANT^HOMEAib). 2. Убедитесь, что база данных пользователей Tomcat включает имя пользователя, которому назначена роль manage г (см. шаг 2 рецепта 4.7, если Требуются детали). 3. В примере 4.9, с помощью элемента taskclef этой задаче присваивается имя stop и это имя затем используется в цели, предназначенной для остановки Tomcat 4. Выполните файл Ant из командной строки, для чего перейдите в каталог с этим фай- лом и напечатайте ant или ant -buildfile «имя сборочного файла>.' Пример 4.9. Использование Ant для остановки web-приложения «project name="My Project” default""stop-tomcat* basedir-"."> «taskdef naiue»"stop" classname»"erg.apache.catalina.ant.StopTark" /> <! — импортируем свойства задающие имя и пароль пользователя, url, и путь к контексту —> «property file""global.properties" /> «target name"”stop-tomcat" description""Stops the Web application”» «echo message""Stopping the application $(context-path}..."/> «stop url»"$(url}" username" " $ {username} " password""${password}" path"”/${context-path}" /> «/target» ' «/project» Элемент taskdef определяет для сборочного файла задачу под названием stop. Эта задача используется далее в сборочном файле. «stop url=" $ {url}" usemame=" $ {username}" password"" $ {password}" path""/${context-path}" /> Остановка Tomcat с помощью Ant | 113
Значения свойств в примере 4.9 извлекаются в задаче property, из файла global, properties (файл свойств, находящийся в том же каталоге, что и сборочный файл). Эти свойства представляют: • имя и пароль пользователя, которому в conf/tomcat-users.xml присвоена роль manager* • URL приложения «Менеджер» (например, http://localhost:8080/manager); • путь к контексту останавливаемого web-приложения. Приложение «Менеджер» Tomcat может инициировать множество других административных задач, например развертывание приложений (см. рецепт 2.6). См. также Описание приложения «Менеджер» Tomcat http://jakarta.apache.org/tomcat/tomcat-4.l- doc/manager-howto.html; рецепт 4.1 по скачиванию и установке Ant; рецепт 4.2 по соз- данию целей Ant; рецепт 4.3 по созданию пути к классам для файла Ant; рецепт 4.4 по ком- пиляции сервлета при помощи Ant; рецепты 4.5 и 4.6 по созданию WAR и JAR файлов; рецепт 4.7 по запуску Tomcat с помощью Ant; рецепты 2.1 и 2.6 по развертыванию с помощью Ant web-приложения; руководство по Ant, раздел по задаче property: http:// ant.apache.org/manual/CoreTasks/property.html; руководство по Ant, сегмент по целям: http:/ /ant.apache.org/manual/using.htmVhargets; индексную страницу руководства по Ant http:// ant.apache.org/manual/index.html; проект Apache Ant: http://ant.apache.org. 114 | Глава 4. Использование Apache Ant
. . , i: .. . . ГЛАВА5 Различные форматы страниц JSP • = "j f 5.0 Введение Данная глава описывает два способа работы с JSP, которые слегка выходят за рамки обычного подхода. Первый способ - прекомпиляция JSP для превращения ее в исходный код сервлета* Второй - разработка JSP в формате XML-документа. Прекомпиляция JSP Прекомпиляция (предварительная компиляция) JSP включает использование предоставляемого сервером инструмента командной строки для преобразования страницы JSP в файл с классом сервлета. Страница JSP преобразуется в сервлет, часто называемый классом реализации JSP (JavaServer Page implementation class), до того как она начнет обрабатывать какие-либо НТГР-вапросы В спецификации JSP стадия, на которой контейнер JSP преобразует синтаксис страницы JSP в сервлет, называется фазой трансляции (translation phase). В Tomcat, если вы хотите посмотреть, как выглядит класс реализации JSP, получаемый после преобразования, обратитесь к каталогу <инсталчяционный-каталог-ТотсаГ>/ыогк/51апс1а1опе/<имя хоста>/<имя ыеЬ-приложения>, где <имя хоста> - localhost или другое имя хоста, где работает Tomcat, <имя у\>еЬ-приложения> - это также и имя контекста, которое обычно выглядит так: examples, ROOT или storefront. Указанный каталог содержит файлы с расширением Java, такие, как default-jsp. java. Это файлы с исходным Java-кодом, которые затем компилируются в файл классов и после выполняются как сервлеты, и отвечают на запросы. К причинам, побуждающим разработчиков прекомпилировать страницы JSP, относятся: 1. желание избежать ощутимой задержки при поступлении первого запроса от web- . контейнера, во время которой компилятор JSP преобразует исходный код JSP в сервлет; 2. возможность посмотреть исходный Java-код класса реализации JSP и, воз- можно,, поработать над ним в редакторе интегрированной среды разработки сервлетов (IDE). ( И в Tomcat, и в WebLogic для прекомпиляции JSP может использоваться инструмент, запускаемый из командной строки. В рецепте 5.4 показано, как в web. xml странице JSP поставить в соответствие ее класс реализации. 115 \
JSP в формате XML-документа Заключительные рецепты данной главы описывают создание JSP в виде XML-файлов. Спецификация JSP, версии 1.2 и версии 2.0 описывает создание и использование JSP в формате XML-документов. Это значит, что помимо создания JSP в обычном синтаксисе их можно кодировать в виде правильных XML-документов. Согласно спецификации, JSP- документ - это XML-документ с определенным пространством имен. Контейнер JSP определяет, что имеет дело с JSP-документом, а не с обычной страницей JSP, по крайней мере, одним из трех способов. 1, В файле web.xml элемент j sp-property-group имеет дочерний элемент is-xml. (Элемент j sp-property-group является одним из элементов конфигурирования JSP, которые предлагает добавить в web.xml спецификация JSP версии 2.0.) 2. Сам файл имеет расширение Jspx. 3. Страница имеет корневой элемент j sp: root. В рецепте 5.5 показано, как выглядят эти файлы. Спецификация JSP назывет представление страницы JSP в виде XML-документа XML- представлением (XML view). XML-представление генерирует контейнер JSP во время фазы трансляции. XML-представление может использоваться подклассом javax.servlet, jsp. tagext.TagLibraryValidator для синтаксического анализа JSP с целью проверки правильности использования пользовательских тегов, перед тем как контейнер наконец преобразует JSP в класс реализации страницы (в сервлет). Процесс генерации XML- представления и сохранения полученного XML-файла описан в рецепте 5.6. Существуют, в частности, следующие причины создавать JSP в виде XML-файлов (JSP-документов). 1. Web-контейнеры способны взаимодействовать с JSP-документами, входящими в web- приложение, то есть web-приложение может включать XML-файлы вместо страниц JSP с традиционным синтаксисом. Таким образом, JSP-документы можно интегрировать с прочим XML-содержимым (XHTML-файлами, масштабируемой векторной графикой (SVG) и XML-файлами, связанными с транзакциями web-сервисов). 2. Для работы с JSP-документами можно использовать редакторы XML. 3. С JSP-документами можно использовать другие XML-технологии, например XSLT, SOAP, SAX и DOM. 116 | Глава 5. Различные форматы страниц JSP
5.1 Прекомпиляция JSP в Tomcat Задача Используя Tomcat 4.1.x, преобразовать JSP в сервлет. Решение Воспользуйтесь инструментом командной строки JspC, находящимся в <инсталляци- оннъ'й-каталог-ТотсаГ>/Ьй1. Обсуждение Использование инструмента JspC, запускаемого из командной строки - первый шаг в прекомпиляции JSP на Tomcat. Для Unix этот инструмент предоставляется в форме сценария оболочки - jspc.sh, для Windows - в форме командного файла jspc.bat, он создает исходные Java-файла с классами реализации страниц JSP. Затем эти итоговые файлы .java компилировать в классы сервлетов, используя javac или другой компилятор с Java. Таким образом, прекомпиляция JSP - процесс, состоящий из двух шагов, и имеет смысл автоматизировать его с помощью командного файла. Однако разберемся сначала с тем, как использовать утилиту JspC. Командному файлу запуска JspC под Windows \<инсталляционный-каталог-Тотсаг>/ bin/jspc.bat) требуется, чтобы переменная окружения JASPER_HOME содержала путь к инсталляционному каталогу Tomcat. Значение этой переменной устанавливается с помощью следующей командной строки. set JASPER_HOME=k:\jakarta-tomcat-4.1.12 Для запуска утилиты JspC перейдите в каталог %JASPER±HOMEc/tJ>in и напечатайте следующую команду (указав свои собственные значения путей). jspc -d H:\book\cookbook -webinc H:\book\cCokbook\map.xml -webapp h:\book\cookbook\dist Здесь переключатель -d задает каталог, в который необходимо поместить сгенерирован- ные файлы с исходным кодом, переключатель -webinc задает имя автоматически, генерируемого файла, в котором JspC создает элементы servlet и servlet-mapping для получаемых файлов сервлетов. Если вы компилируете страницу с именем precomp.jsp, эти элементы для отображения будут выглядеть так, как показано в примере 5.1. Пример 5.1. Отображение сервлета для прекомпилированной страницы JSP <servlet> <servlet-naine>org. apache. j sp .precorap_j sp</servlet-name> <servlet-class>org.apache.jsp.precomp_j sp</servlet-class> </servlet> Прекомпиляция JSP в Tomcat | 117
<servlet-mapping> <servlet-name>org.apache.jsp.precomp_j sp</serVlet-name> <url-pat tem> /precomp. j sp< /url -pattern> </servlet-mapping> После этого данные элементы можно скопировать и вставить в дескриптор развертывания web.xml своего приложения. Переключатель -webapp задает каталог web-приложения, который должен иметь под- каталог /WEB-INF, содержащий файл web.xml вашего приложения. JspC находит в указан- ном каталоге верхнего уровня web-приложения все файлы .jsp и транслирует их в файлы с исходным кодом сервлетов, вместе со страницами JSP из вложенных подкаталогов. Полученные в итоге файлы Java размещаются в каталоге, заданном в переключателе -d. В отличие от -webinc, переключатель -webxml создает полный файл web.xml, в который включены вновь созданные сервлеты и отображения для этих сервлетов. Другие возмож- ности JspC описаны в http://cvs.apache.Org/viewcvs/-checkout~/jakarta-tonicat-4.0/jasper/doc/ jspc.html. Далее необходимо откомпилировать сгенерированные исходные файлы. Для того чтобы выполнить оба шага за один раз, рекомендую использовать командный файл. В примере 5.2 приведен командный файл Windows, генерирующий исходные файлы сервлетов и затем, с помощью javac, компилирующий их. Пример 5.2. Использование командного файла для прекомпиляции JSP под Tomcat @echo off jspc -d H:\book\cookbook\classes -webinc H:\book\cookbook\map.xml -webapp h:\book\cookbook\dist set PRECLASSPATH=%CATALINA_HOME%\common\lib\servlet.jar; %CATALINA_HOME%\common\lib\jasper-runtime.jar;%CLASSPATH% javac -classpath %PRECLASSPATH% -d ./classes *.java Сохраните этот текст в текстовом файле, например с именем precomp.bat. Перейдите в каталог, содержащий данный командный файл, и напечатайте precomp. Файл выполнит команду JspC для всех .jsp файлов, находящихся ниже каталога web-приложения h:\bookx cookboolddist. Поскольку используется переключатель -webinc, команда создает фрагмент XML, содержащий элементы servlet и servlet-mapping. Если не было ошибок, преобразованные файлы сохраняются в каталоге h:\boohcookbooldclasses. Затем создается переменная окружения PRECLASSPATH, подключающая компо- ненты servlet.jar и jasper-runtime.jar, вместе с любыми каталогами или JAR, являющимися частью существующей переменной окружения CLASSPATH. Компонент servlet.jar необходим во время компиляции для импорта следующих пакетов Java: • javax.servler; • javax.servlet.http; ' • javax.servlet.jsp. 118 | Глава 5. Различные форматы страниц JSP
Добавление jasper-runtime.jar необходимо для импорта пакета org. apache. j asper. runtime. Под Windows, для успешного выполнения данного командного файла, необхо- димо значением переменной окружения JASPER-HOME сделать инсталляционный каталог Tomcat. Сценарий, выполняющий те же задачи в Unix, приведен в примере 5.3. Данный сценарий запускает находящийся в каталоге /Ып сервера Tomcat файл jspc.sh, преком- пилирующий все файлы JSP, которые JspC найдет в текущем каталоге. Полученные файлы .java сценарий сохраняет в каталоге ./classes. Пример 5.3. Сценарий оболочки для прекомпиляции JSP #!/bin/sh.. $CATALINA_JiOME/bin/jspc.sh -d ./classes -webinc ./map.xifii -webapp ./; PRECLASSPATH=$CATALINA_HOME/common/lib/servlet. jar: $CATALINA_HOME/common/ lib/jasper-runtime.jar; export PRECLASSPATH; javac -classpath $PRECLASSPATH -d ./classes ./classes/*.java См. также Рецепт 5.3 по протоколу прекомпиляции; рецепт 5.4 по отображению компилирован- ных JSP в web.xml\ раздел по прекомпиляции JSP книги Hans Bergsten «JavaServer Pages» (O'Reilly); главу JSP. 11.4 спецификаций JSP версии 2.0. 5.2 Прекомпиляция JSP в WebLogic Задача Прекомпилировать JSP на сервере WebLogic. Решение Воспользуйтесь Java-утилитой weblogic.jspc, инсталлируемой вместе с сервером WebLogic. Обсуждение В WebLogic имеется собственная утилита для прекомпиляции JSP: weblogic.jspc. Эта утилита - часть файла JAR, находящегося в <UHcmajuixyuoHHbiu-Kamaxo2-WebLogic>/ weblogic700/serverflib/weblogic.jar. При прекомпиляции страниц JSP с помощью weblogic, jspc эта утилита помещает итоговые файлы классов в указанный ей каталог. В примере 5.4 показан простой командный файл для Windows NT, прекомпилирующий страницу example, jsp в класс реализации. Прекомпиляция JSP в WebLogic | 119
Пример 5.4. Прекомпиляция JSP с помощью weblogic.jspc @echo off , ' set WLCLASSJ?ATH=k: \bea\weblogic700\server\lib\weblogic.jar;%CLASSPATH% java -cp %WLCLASSPATH% weblogic.jspc -d .\classes example.jsp Во второй строке формируется значение переменной окружения WLCLASSPATH. Это значение включает путь к weblogic.jspc и значение переменной CLASSPATH. В следующей строке полученный комбинированный путь используется для запуска weblogic.jspc. Переключателе -d определяет место сохранения итоговых файлов классов, в данном случае это каталог classes находящийся в каталоге с командным файлом и файлом example.jsp. В результате генерируется файл jsp_servlet._ ^example.class с Java-классом (включается имя ' пакета). Если вы не указали пакет для компилируемого сервлета, то по умолчанию в качестве имени пакета используется j sp_serviet (см. пример 5.6). В примере 5.5 пока- зан сценарий оболочки, написанный для Mac OS, который прекомпилирует JSP с помощью среды WebLogic. Пример 5.5. Прекомпиляция страниц JSP с помощью weblogic.jspc и сценария оболочки #!/bin/sh WLCLASSPATH=/Users/bruceper/java/weblogic_jar/weblogic. jar: $CLASSPATH; export WLCLASSPATH; java -cp $WLCLASSPATH weblogic.jspc -d /Users/bruceper/books/cookbook/code/chap5/classes newfile.jsp ' В отличие от утилиты JspC в Tomcat, weblogic.jspc компилирует файл с синтаксисом страницы JSP в файл класса за одну операцию. Тогда как использование JspC ** f £ из командной строки требует последующего запуска компилятора (например, j avac) для компиляции файлов .java в файлы классов. При использовании weblOgic. jspc компиляция выполняется автоматически. Командный файл для Windows, приведенный в примере 5.6, для всех страниц JSP, найденных в web-приложении, указанном переключателем -webapp, задает пакет jsps- ervletcookbook. Пример 5.6. Использование weblogic.jspc для прекомпиляции всех страниц JSP web-приложения 6echo off set WLCLASSPATH=k i^beaXweblogic?00\server\lib\weblogic.jar;%CLASSPATH% java -cp %WLCLASSPATH% weblogic.jspc -d .\classes -package jspservletcook- book -compileAll -webapp h:/home В примере 5-7 показан сценарий оболочки, выполняющий ту же работу в Unix. Пример 5.7. Прекомпиляция всех страниц JSP web-приложения с помощью сценария оболочки #1/bin/sh WLCLASSPATH=7Users/bruceper/java/weblogic_j ar/weblogic. jar: $CLASSPATH; export WLCLASSPATH; 120 | Глава 5. Различные форматы страниц JSP
Обратите внимание на следующий набор инструкций из приведенного примера. -compileAll -webapp h:/home Переключатель —compileAll вместе с аргументом -webapp просит weblogic.jspc декомпилировать все файлы JSP, найденные в web-приложении, размещенном в каталоге h:/home, включая JSP-файлы из вложенных подкаталогов. Это web-приложение находится r формате «развернутых каталогов» (не архивировано в WAR-файле). В примере 5.6 откомпилированные классы записываются в K3Lrasior\classes^spservletcookbook. См. также Рецепт 5.3 по протоколу прекомпиляции; рецепт 5.4 по отображению компилирован- ных JSP в web.xml-, раздел по прекомпиляции JSP книги Hans Bergsten «JavaServer Pages» (O'Reilly); главу JSP.l 1.4 спецификации JSP версии 2.0. 5.3 Прекомпиляция JSP с помощью протокола прекомпиляции Задача Вы хотите использовать «протокол прекомпиляции», являющийся частью специфи- кации JSP, для прекомпиляции одного или нескольких файлов JSP. Решение Направьте контейнеру JSP запрос, включающий параметр jsp_precompile. * ' Обсуждение Спецификация JSP версий 1.2 и 2.0 требует от контейнеров JSP поддержки параметра запроса jsp_precompile. Данный параметр подразумевает прекомпиляцию запраши- ваемой страницы JSP. В Tomcat это работает следующим образом: 1. Запросите страницу JSP, которую хотите декомпилировать, добавив к URL параметр jsp_precompile, например http://localhost:8080/home/url_rewrite.jsp?jsp_ргесот- pile=true. Контейнер JSP не станет выполнять запрошенную страницу, он только преком- пилируетее В результе запроса, сделанного из браузера, в браузере отобразится пустая страница. Прекомпиляция JSP с помощью протокола прекомпиляции | 121
2. Если JSP-файл с синтаксисом страницы JSP еще не был скомпилирован или если после компиляции в него были внесены изменения, Tomcat создаст для этой страницы в каталоге <инсталляционный-каталог-Тотса1>Л^огк новые файлы - один с исходным текстом на Java, другой -* с Java-классрм. Для JSP-файла с именем url_rewrite.jsp Tomcat создаст файлы с именами url_rewriteJsp.java и url_rewrite_jsp.class. Подразумевается, что запрос (С параметром isp_precompile (без части «=true») эквивалентен запросу с параметром j sp_precompile=true. * •' В Tomcat протокол прекомпиляции создает как файл java, так и результат его л компиляции - класс реализации страницы. Инструмент JspC (см. рецепт 5.1) ' '* f $ позволяет создать лишь файл Java/ Описываемый протокол лучше всего использовать совместно с каким-либо инструмен- том автоматизации, способным создавать HTTP-запросы, например с компонентом Jakarta Commons HttpClient. Это позволит автоматизировать процесс прекомпиляции множе- ства JSP страниц, послав несколько НТТРтапросов из единственной Java-программы. См. также Рецепты 5.1 по использованию Tomcat-утилиты JspC\ рецепт 5.2 по прекомпиляции на сервере WebLogic; рецепт 5.4 по отображению откомпилированных страниц в web.xml' 1 лаву 7 по отправлению HTTP-запросов из сервлетов или JSP; домашнюю страницу компо- нента Jakarta Commons HttpClient: http://jakarta.apache.org/commons^ttpdrcnt/; раздел по прекомпиляции JSP книги Hans Bergsten «JavaServer Pages» (O’Reilly); главу JSP. 11.4 спецификации JSP версии 2.0. 5.4 Отображение страницы JSP на соответствующий ей класс реализации страницы Задача Имеется прекомпилированная страница JSP, необходимо в дескрипторе развертывания задать отображение обращений к этой странице на соответствующей ей класс реализации. Решение V Скопируйте элементы servlet и servlet-mapping, автоматически сгенерирован- ные утилитой JspC, и вставьте их в web.xml. Создайте в каталоге WEB-INF/classes своего приложения необходимые каталоги, связанные с пакетом и поместите в этот же каталог . прекомпилированные станины JSP. 122 | Глава 5. Различные форматы страниц JSP
Обсуждение Прекомпиляция JSP позволяет убрать из web-приложения все файлы с синтаксисом JSP и Оставить только итоговые файлы с классами сервлетов. После этого, используя элемент servlet-mapping файла web.xml, .ложно отобразить URL в стиле ссылки нгг JSP (например, default.jsp) на откомпилированные классы сервлетов. Вот как решается эта задача. 1. Прекомпилируйте страницы JSP (см. рецепты 5.1 и 5.2), включая компиляцию исход- ных Java-файлов в файлы классов с помощью javac или другого компилятора. 2. Скопируйте автоматически сгенерированные утититой JspC элементы servlet- mapping и servlet-mapping в дескриптор развертывания (если используется Tomcat) или добавьте эти элементы в web.xml вручную (если используется WebLogic или иной контейнер). 3. Удостоверьтесь, что элемент url-pattern в элементе servlet-mapping содержит имя файла в сти те JSP, например default.jsp, или отображение расширений, например *.jsp. 4. Поместите класс или классы, включая связанные с пакетом каталоги, в WEB-INF/ classes или в JAR-файл, находящийся в WEB-INF/lib. Когда web-пользователь запросит URL, заданный в элементе servlet-mapping класса реализации страницы, web-контейнер направит этот запрос к данному классу сервлета. Пример 5.8 демонстрирует конфигурацию сервлета для прекомпилированнои страницы JSP. Пример 5.8. Записи web.xml, относящиеся к прекомпилированнои JSP <servlet> <servlet-hame>org.apache.j sp.precomp_j sp</servlet-name> <servlet-class>org. apache, jsp-. precomp_jsp</servlet-ciassV </servlet> <servlet-mapping> <servlet-name>org. apache. j sp .precomp_j sp< Zserviet-name> <url-pattern>/precomp.j sp</urlxpattern> </servlet-mapping> В web-приложении структура каталогов для этого’ класса будет выглядеть примерно так: /WEB-INF/classes/org/apache/jsp/precompJsp.class. Если путь к контексту web-прило- жения /home, то пользователи moi у г обращаться к этому классу реализации JSP (скрытому сервлету) с помощью (JRL типа http://localhostr8080/home/precomp.jsp. См. также Рецепты 5.1-5.3; главу JSP.11.4 спецификации JSP версии 2.0. Отображение страницы JSP на соответствующий ей класс реализации страницы | 123
„ 5.5 Создание JSP в виде JSP-документа «с нуля» Задача Создать JSP-документ с помощью редактора XML. Решение Откройте редактор XML и создайте JSP используя только XML-элементы. Обсуждение JSP-документ - это ссылающийся на определенное пространство имен, корректно сформированный XML-файл, содержащий стандартные действия JSP (например, jsp: include и jsp: useBean), пользовательские действия (например, пользовательские теги JSTL) и XML-эквиваленты директив JSP. В табл. 5.1 приведены XML-эквиваленты общих директив JSP. Для создания JSP используйте такой редактор XML, с помощью которого можно проверить корректность полученного документа. Для того чтобы JSP-документ можно было поместить на выполнение в контейнер JSP, он должен быть корректным XML-документом. Табл. 5.1. XML-эквиваленты директив JSP Директива Пример page <%@ page import="java. util.Date" %> Эквивалент в JSP-документе <jsp:directive, page import="java. util.Date" /> include <%& include file="footer.html" %> <j sp: direc tive. incl ude file-"footer.html" /> taglib <%0 taglib uri="WEB- TNF/tlds/xml gen.tld" prefix=”t" .%> <jsp:root jsp:id="0" xmlns: jsp="http://java.sun.com/JSP/Page" version-"2.0” xmlns:t="urn:jsptld: /WEB-INF/tlds/xml_gen.tld"> В JSP 1.2 Единственным способом определения того, что страница JSP сформирована в XML-формате, было наличие корневого элемента j sp: root. Однако в JSP 2.0 2 добавилось несколько дополнительных опций - теперь JSP-документ можно * отличить от страницы JSP не XML-формата по наличию в дескрипторе развертывания элемента jsp-property-group, по расширению файла .jspx или по корневому элементу j sp: root. В данном рецепте сравнивается простая страница JSP и ее XML-эквивалент, затем сравнение повторяется после добавления пользовательского тега и выражения, вычисляемого на этапе выполнения (в атрибут JSP-элёмента). В примере 5.9 приведен простой файл с синтаксисом страницы JSP, показывающий местное время сервера. 124 | Глава 5. Различные форматы страниц JSP
Пример 5.9. Простой файл с синтаксисом страницы JSP <%@page contentType="text/html"%> <%@page import="java.util.Date"%> <html> <headxtitle>Welcome to the Web</titlex/head> <body> <h2>Welcome to the Web</h2> The' server's local time is <%=new Date() %>. </body> </html> Данная страница JSP содержит две директивы и JSP-выражение, Отображающее строку с датой и временем в браузере. Копируя код из этой страницы и вставляя его в редакторе XML в новый файл, с заменой не XML конструкций на элементы XML, можно преобразо- вать эту страницу в JSP-документ. В примере 5.10 приведен JSP-документ, эквивалентный странице из примера 5.9. Рис. 5.1. Простая страница JSP до преобразования в XML Пример 5.10. Простой JSP-документ с корректным XML-форматом < j sp:root xmlns:jsp="http: / / java.sun.com/JSP/Page" version="2.0"> <jsp:directive.page contentType="text/html"/> 4 < j sp:directive.page import="j ava.util.Date"/> <html> • <head><title>Welcome to the Web</titlex/head> <body> <h2>Welcome to the Web</h2> ' The server's local time is <jsp:expression>new Date()</jsp:expression>. </body> </html> </jsp:root> Создание JSP в виде JSP-документа «с нуля» | 125
Код из примера 5.10, вместо традиционных директив JSP, имеющих недопустимый в XML синтаксис элементов с <%@, включает элементы j sp: directive .page. Все, «го в странице JSP, имеет ограничители в стиле <%, не может использоваться в JSP-документе, в противном случае документ не пройде г Проверку на корректность формата XML. В примере 5.11 приведена более сложная .страница JSP, с директивой taglib, задающей библиотеку тегов ядра из JSTL 1.0; на этой странице также использован код на языке выражений (Expression language - EL). Кроме того,.здесь имеется элемент jsp: useBean, устанавливающий областью видимости переменной dateString из java, util. Date данную страницу. .. Пример 5.11. Страница JSP, требующая сложного преобразования в XML <%@page contentType="text/html"S> у <%@ .taglib uri="http://java. sun. сот/jstl'/core” prefix="c" %> <html> ' I-? . i5 <head>s;title>Welcbrne to the Web</title></head> <body> <h2>Welcome to the VJeb</h2> Hello, <c:out value="$ {param, firstName} ${param. last Name}"/><br><br> <jsp:useBean id="dateString" class="java.util-Date"/> The time is <c:out value="${dateString}" />.<brxbr> The value of 10 + 24 +35 = <c:out value="${10 + 24 + 35}" /> </body> </html> В примере 5.12 представлена та же страница, преобразованная в JSP-документ. Пример 5.12. JSP-документ, ссылающийся на библиотеки тегов (taglib) _ <j sp:root xmlns:j sp="http://java.sun.com/JSP/Page" xmlns:c="http://java.sun.com/jstl/gore" version="2.0"> <jsp:directive.page contentType="text/html"/> , <html> *r <head><title>Welcome to the Web</titlex/head> <body> <h2>Welcome to the Web</h2> <jsp:text>HeJlo </jsp:text> <c:out value="$ {param. firstName} $ {param. lastName}"/><brx/brxbrx/br>! <jsp:useBean id="dateString" class-?java.util.Date"/> <jsp:text>The time is </jsp: text xc: out value="${dateString}" />. <brx/brxbr ></br> <jsp:text>The value of 10 + 24 + 35 = </jsp:text> <^c:out value="${10 + 24 + 35}" /> ) </body> </html> </jsp:root>> ’ , 126 | Глава 5, Различные форматы стрг ниц JSP t
В JSP-документе любые библиотеки тегов можно подключать как атрибуты пространства имен, как это сделано в элементе jsp: root, приведенном ниже. < j sp:root xmlns:j sp="http://java.sun.com/JSP/Page * xmlns :c?=*http: //java.sun.com/jstl/core" version="2.0"> Элемент j sp: text можно использовать для хранения в JSP-документе любых шаб- лонных данных. Стандартные действия JSP (например, jsp:useBean) и пользователь- ские теги (например, с: out) можно использовать в JSP-документе с тем же синтаксисом, что и для традиционной страницы JSP. j На рис. 5.2 показано отображение JSP-документа из примера 5,12 в браузере. К этой странице мОжно обратиться, используя URL - http:/flocalhost:8080/home/example_xml2. jsp ?firstName=Bruce&lastName=Perry. t Рис. 5.2. Вид JSP-документа из примера 5.11 в браузере А вот как выглядит исходный код HTML, если в меню браузера выбрать «Вид -> в виде HTML» (с некоторыми корректировками для улучшения читаемости). <htmlxheadxtitle>Welcome to the Web</titlex/head> <body> <h2>Welcome to the Web</h2> Hello Binice Perry<br/xbr/> The time is Mon Feb 10 16:20:05 EST 2003 .<br/xbr/> The value of 10 + 24 + 35 = 69 </body></html> См. также Рецепт 5.6 по генерированию XML-представления страницы JSP; главу JSP.6 (JSP-доку- менты) спецификации JSP версии 2.0, главу JSP.10 (XML-представление) спецификации JSP версии 2.0.' Создание JSP в виде JSP-документа <с нуля» | 127
5.6 Генерирование XML-представления страницы JSP Задача Автоматически сгенерировать XML-представление страницы JSP. Решение Создайте пользовательский тег и класс TagLibraryValidator, с помощью которых можно вывести XML-представление страницы JSP в файл. Обсуждение XML-представление - это представление страницы JSP в формате XML, которое генерируется контейнером JSP в фазе трансляции, промежуточная стадия перед тем, как контейнер преобразует JSP в класс реализации страницы (в сервлет). Класс TagLibrary- Validator может использовать XML-представление для проверки корректности исполь- зования в JSP пользфвательских тегов, перед тем как JSP превратится в сервлет. XML- представление очень похоже на JSP-документ (страницу JSP в формате XML), создавае- мый разработчиком вручную для включения в web-приложение. Согласно спецификации JSP 2.0 различия между этими двумя XML-файлами заключаются в следующем. • В XML-представлении развернуты все директивы include, а в соответствующих фрагментах JSP-документа эти директивы могут присутствовать. • В XML-представлении каждый XML-элемент снабжен атрибутом j sp: id. • В XML-представлении элемент jsp;root добавляется в качестве корневого Элемента документа, если документ еще не имеет элемента j sp: root. • В XML-представлении добавляется элемент jsp:directive,раде (если его не было) t атрибутом pageEncoding и значение атрибута pageEncoding устанав- ливается в «UTF-8». • В XML-представлении добавляется элемент j sp: directive. раде (если его не было) с атрибутом contentType и значение атрибута contentType устанавли- вается в соответствии с главой JSP.4.2 «Кодировки символов в ответе не запрос» спе- цификации JSP 2.0 («text/xml» для JSP-документа). Разработчика на Java могут добавлять к web-приложениям подклассы класса j avax. servlet.jsp.tagext.TagLibraryValidator в качестве инструментов проверки использования в приложении пользовательских тегов. Контейнер JSP (в Tomcat такой кон- тейнер называется Jasper) создает XML-представленйе страницы JSP и передает его в TagLibraryValidator для синтаксического анализа и верификации XML-элементов. 128 | Глава 5. Различные форматы страниц JSP
Полезно проверить XML-представление страницы JSP, чтобы отладить класс TagL- ibraryValidator, используемый вами в библиотеке пользовательских тегов, или чтобы открыть JSP в редакторе XML и оценить ее синтаксис с точки зрения XML. Вот хороший способ (с примесью хакерства) автоматической генерации файла с XML- представлением страницы JSP. Он заключается в использовании объекта javax. serv- let . jsp.tagext.PageData, который автоматически возвращает XML-представле- ние страницы JSP в виде объекта java. io. Inputstream. Необходимо следующее. 1. Создайте класс, расширяющий класс javax.servlet.jsp.tagext.TagLib- raryValidator. Подобные классы верификации используются для проверки корректности применения пользовательских тегов и детально описаны в главе 23. 2. Переопределите метод TagLibraryValidator. validate (String prefix, String uri, PageData page) так, чтобы записать XML-представление из параметра PageData в файл. 3. Создайте простейший пользовательский тег, расширяя javax.servlet.jsp tagext. TagSupport. Этот тег «пометит» страницу JSP, чтобы ее XML-представ- ление можно было вывести в файл. Тег должен включать атрибут «filename», из которого созданный вами класс верификации возьмет Имя файла для записи XML- представления. Этот тег выглядит примерно следующим образом: <t:toxml filenames"myxml_view" /> 4. Создайте для этой библиотеки тегор файл дескриптора библиотеки тегов (Tag Library Descriptor - TLD), задав в качестве верификатора этой библиотеки созданный вами класс верификации. <validator> <validator-class> com.j spservletcookbook.ToXmlValidator </validator-class> <description> Saves XML views of JSP pages to the specified directory. < /description </validator> 5. Поместите оба класса (TagLibraryValidator и TagSupport) в каталог 'WEB-INF/ classes web-приложения или в JAR-файл, находящийся в WEB-INF/lib (примеры из данного рецепта подразумевают первый из форматов, а не размещение классов в JAR). 6. Поместите файл TLD в каталог WEB-INF/tlds. 7. Добавьте в дескриптор развертывания WEB-INF/web.xml элемент taglib, соответ- ствующий вашим тегу и TLD. Наличие в web.xml элемента taglib не является необходимым для JSP версий 1.2 и 2.0, поскольку контейнер JSP автоматически проводит поиск файлов с расширением .tld в каталоге WEB-INF, как и в каталоге МЕТА -INF JAR-файлов приложений. 5-1545 Генерирование XML-представления страницы JSP | 129
8. Создайте файл свойств, содержащий путь к каталогу, который вы хотите использо- вать для автоматически генерируемого XML-представления. Сохраните этот файл свойств в WEB-INF/classes, используя соответствующие имена пакетов. Этот файл свойств используется, чтобы избежать трудоемкой записи абсолютного пути к ката- логу в коде класса верификации. 9. Поместите созданный пользовательский тег в файл JSP, XML-представление которого требуется получить в файле. В примере 5.13 приведена страница JSP с пользовательским тегом. Пример 5.13. Генерирование XML-представления страницы <%@ taglib uri="/toxml_view" prefix="tH %>, <html> , / <head> <title>Test tld</title> </head> <body bgcolor="#ffffff"> Hello, this page is using the toxml tag to look at its XML View. <t:toxml filename=,'iny_xmlview"/> </body> </html> Элемент с тегом t: toxml - пустой элемент, сигнализирующий классу верификации о необходимости генерировать файл, содержащий XML-представление. Итоговый файл будет назван my_xmlview.xml (класс верификации добавит расширение .xml). Этот тег не оказывает никакого влияния на внешний вид или поведение страницы JSP. Следующий ниже фрагмент дескриптора развертывания показывает элемент taglib, задающий URI, используемый в директиве taglib. Элемент taglib дескриптора развертывания также задает местоположение файла TLD (WEB-INF-/tlds/xml_gen .tld). <taglib> <taglib-uri>/toxml_view</taglib-uri> <taglib-location>/WEB-INF/tlds/xml_gen.tld</taglib-location> </taglib> В примере 5.14 показан файл TLD для нашей библиотеки тегов, задающий класс верификации и простейший пользовательский тег (маркер), используемый в примере 5.13. Я не привожу код тега toxml, поскольку в нем нет ничего интересного, кроме того, что он содержит переменную-член типа string, с названием filename. Основное назначение этого тега - инициировать работу класса верификации Контейнер JSP создает по одному экземпляру класса верификации для каждой библиотеки тегов, подключающее класс верификации. 130 | Глава 5. Различные форматы страниц JSP
Пример 5.14. Файл TLD для пользовательского тега, используемого для получения XML-представ- ления <?xml version="1.0" encodings“ISO-8859-1" ?> <!DOCTYPE taglib PUBLIC "—//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN" "http://java.sun.com/dtd/web-jsptaglibrary_l_2.dtd"> <taglib> ^«tlib-version*1.0</tlib-version* <jsp-version*l.2</jsp-version* <short-name*Validator test</short-name> <description>Validator test«/description* «validator* <validator-class> com.j spservletcookbook.ToXmlValidator «/validator-class* «description* Saves XML views of JSP pages to the specified directory.. <Jdescription* </validator* <tag> <name> toxml</name* <tag-class*c6m.jspservletcookbook.ToXml</tag-class* <body-content>EMPTY</body-content* «description* This tag demonstrates the production of JSP XML view files. </description* <attribute> <name> f i lename< /nartie* «required*true«/required> <rtexprvalue*false</rtexprvalue* «description* This attribute provides the filename.«/description* x. «/attribute* «/tag* «/taglib* 5* Генерирование XML-представления страницы JSP I 131
Класс верификации com. jspservletcookioook. ToXmlValidator выполняет метод validate когда загружена страница JSP использующая тег toxml. Как класс верификации узнает, где сохранить файл, содержащие XML-представление страницы? Путь к каталогу хранения он извлекает из файла свойств, который приводится ниже. Такой подход позволяет лицам, использующим пользовательский тег, изменить каталог хранения XML-представления, не вмешиваясь в исходный код класса верификации. Файл свойств размещается в том же каталоге, что и класс верификации. Путь к этому файлу свойств - 'WEB-INF/classes/comJjspservletcookbook/vahdator.properties. directory=h: /home/kmlviews Имя файла для хранения берется из самого тега. <t: toxml filenames "my_xml view" /> Таким образом, полный путь к файлу с XML-представлением выглядит так: h:/home/ xmlviews/my_xmlview.xml. При создании файла с XML-представлением класс верификации добавляет к имени расширение xml. Перед этим верификатор извлекает имя файла из тега toxml. используя для синтаксического разбора входного потока из объекта javax. servlet .jsp. tagext. PageData синтаксический анализатор SAX. Теперь у вас есть все необходимое, за исключением очень важного класса верификации, он приводится в примере 5.15. Метод validate для чтения значения свойства direc- tory (каталог) использует объект/ java.util.ResourceBundle. Метод validate получает имя файла используя вспомогательный класс,'приведенный в примере 5.16. Затем этот метод генерирует XML-представление страницы JSP, используя java.io.Input- Stream, возвращаемый из PageData. getlnputStream (). Пример 5.15. Класс верификации, предназначенный для генерирования XML-представления import j avax.servlet.jsp.tagext.TagLibraryValidator; import javax.servlet.jsp.tagext.ValidationMessage; irrport j avax. servlet .jsp. tagext. PageData ; import j ava.io.*; import java.util.ResourceBundle; import java.util.MissingResourceExceptiOn; inport java.util.Date; public class ToXmlValidator extends TagLibraryValidator { /** Creates new ToXmlValidator */ public ToXmlValidator() { } public ValidationMessage[] validatefjava.lang.String prefix, java.lang.String uri,PageData page){ ValidationMessage(] vam = null; 132 | Глава 5. Различные форматы страниц JSP
try{ ResourceBuridle bundle = ResourceBunble.getBundle("com.j spservletcookbook.Validator"); String director^ = bundle.getString("directory"); String fileName = getFilename(page); //throw an Exception if the directory is invalid if (directory.== null). throw new Exception ( "Received a null directory for the XML view file."); //throw an Exception if the filename is invalid if (fileName -= null) c. throw new IOException( "Received a null filename for the XML view file."); File file = new File(directory + ”/" + fileName + ".xml"); FileWriter writer = new FileWriter(file); BufferedReader in ч new BufferedReader ( new InputStreamReader(page.getInputStrearn())); String line = ""; //write the XML view to the specified file while ((line = in.readLineO) != null ){ writer.write(line); } in.closeO; < writer.close(); } catch (lOException io){ f //return a validation message ValidationMessage vmsg = new ValidationMessage(null,io.getMessage()); vam = new ValidationMessage[1]; vam[0].= vmsg; return vam; } catch (MissingResourceExdeption mre){ //return a validation message ValidatioriMessage vmsg = hew ValidationMessage (null, mre. getMessage ()) ; vam = new ValidationMessage[1J; vam[0] = vmsg; return vam; } catch (Exception e){ //return a validation message ValidationMessage vmsg = new Генерирование XML-представления страницы JSP | 133
ValidationMessage(null,e.getMessage()>; vam = new ValidationMessage[1] ; vam[0] = vmsg; return vam; } //return empty array vam = new ValidationMessage[0]; return vam; } private String getFilename(PageData pacje) throws Exception { try{ < ValidateHandler handler = new ValidateHandler(); return handler.getFilename(page); } catch (Exception e){ throw e; } } } В примере 5.16 приведен вспомогательный класс ValidateHandler, который используется нашим верификатором для получения из пользовательского тега имени файла. Этот класс делает один проход по XML-представлению (до того как оно записы- вается в файл) и извлекает из атрибута filename элемента toxml находящееся там имя файла. Вся работа по синтаксическому разбору XML производится этим классом за сценой, а верификатор получает имя файла Простым вызовом метода. ValidateHandler handler = new ValidateHandler(); return handler.getFilename(page); Класс ValidateHandler для разбора XML, получаемого ,c помощью javax. servlet -jsp. tagext. PageData. getlnputStream () использует следующие интерфейсы прикладных программ: Java API для обработки XML (Java API for XML processing - JAXP) и простой API для XML (Simple API for XML - SAX). Класс Vali- dateHandler необходимо поместить в каталог WEB-INF/classes (или в JAR-файл, в WEB-INF/lib), чтобы web-приложение (класс ToXmlValidator) смогло его найти. Вместо SAX можно использовать любые другие.компоненты, обеспечивающие ана- логичную функциональность (синтаксический разбор XML). Если вы решили использо- вать JAXP, а ваш web-контейнер еще не содержит необходимые компоненты JAXP, то для полной установки JAXP добавьте в каталог V/EB-INF/lib следующие JAR-файлы: jaxp-api.jar, dom.jar, sax.jar, xalan.jar, xerceslmpl.jar и xsltc.jar. Эти компоненты можно скачать как часть Java Web Services Developer Pack (http://java.sun.com/webservices/web- servicespack.htmiy, библиотеки JAXP включены и в Java 1.4.x. 134 | Глава 5. Различные форматы страниц JSP
Пример 5.16. Вспомогательный класс, расширяющий класс defaultHandler, для извлечения имени файла из атрибута пользовательского тега import org.xml.sax.Attributes; import org.xml.sax.SAXParseException; import org.xml.sax.SAXExcept ion; import org.xml.sax.helpers.DefaultHandler; _mport javax.xml.parsers.SAXParserFactory; import j avax.xml.parsers.FactoryConfigurationError; import j avax.xml.parsers.ParserConfigurationExcfeption; import javax.xml.parsers.SAXParser;. import j ava.io.JOException; import javax.servlet.jsp.tagext.PageData; public class VaiidateHandler extends DefaultHandler { private String fileName = public void startElement(String nameSpaceuri, String sname, String qname, Attributes attrs)( forfint i=0; i<attrs. getLength (.); i++) if("filename".equals(attrs.getLpcalName(i))) this.fileName=attrs.getValue(i); } public String getFilename(PageData page) throws FactoryConfigurationError, ParserConfigurationException, SAXException, lOException { try{ f SAXParserFactory factory = SAXParserFactory.newlnstance() ; factory.setNamespaceAware(true); SAXParser saxparser = factory.newSAXParser(); saxparser.parse(page.getInputStream(),this); } catch (FactoryConfigurationError fe){ throw fe; } catch (ParserConfigurationException pee){ throw pee; x } qatch ( SAXExcepztion se) { / throw se; } catch( java.io.IOException io){ t ' throw io; } finally { return this.fileName; } } Генерирование XML-представления < границы JSP | 135
public void error(SAXParseException e) throws SAXParseException { throw e; } } XML-представление, генерируемое для страницы JSP из примера 5.13, показано в примере 5.17 (добавлено несколько переносов строк для удобочитаемости). Смотрится не очень хорошо, зато теперь вы знаете, как выглядит XML-предствление! 'Весь HTML-код рассматривается как шаблонные данные, и заключен г элемент J sp: text и секции CDATA. Два XML-элемента jsp:root и t:toxml получили в XML-представлении последовательные идентификационные номера, указанные в атрибуте isp: id. Эти иден- тификаторы могут использоваться классом TagLibraryValidator для выдачи деталь- ных сообщений, относящихся к XML верифицируемой страницы. Пример 5.17. XML-представление JSP из примера 5.13 I V <jsp:root jsp:id=*0' xmlns:jsp="http://java.sun.com/JSP/Page' version="1.2' xmlns: t=" / toxml_view" > < jsp: textx![CDATA[]]></jsp:text> <jsp:text><! [CDATA[<html>] ]></jsp: text:> <jsp:te^t><![CDATA[<head> ]]></jsp:text> <jsp: textx! [CDATA [<title>Test tld] ] ></jsp: text> <jsp: textx! [CDATA[</title>] ]x/jsp: text> <jsp:textx! [CDATA[</head>] ]x/jsp:text> <jsp:textx! [CDATA[<body bgcolor="#ffffff">Hello, this page is using the toxml tag to look at its XML View.]]></jsp:text> <t:toxml jsp:id="l" filenames"my_xmlview"/> <jsp:text><! [CDATAf] ]x/jsp:text> < j sp: textx! [ CDATA [ < /body> ] ] >< / j sp: text> 4 <jsp: textx! [CDATA[</html>] ]x/jsp: text> </jsp:root> у См. также Рецепт 5.5 по созданию страницы JSP в форме JSP-документа «с нуля»; главу JSP.6 (JSP-документы) спецификации JSP версии 2.0; главу JSP.10 (XML-представление) спе- цификации JSP версии 2.0. 136 | Глава 5. Различные форматы страниц JSP
ГЛ\ВА6 Динамически подключаемое содержимое страниц JSP и сервлетов 6.0 Введение Сервлеты и JSP часто содержат общие для некоторой организации фрагменты информации - логотип, товарный знак, панель навигации и т. п. Для подключения такой информации web-приложения используют механизм импорта, позволяющий централизованно хранить и изменять содержимое, без необходимости вносить измене- ния во всех фрагментах кода, где эта информация используется. Часть подобной информации постоянна и редко подвергается изменениям (например, логотип организа- ции). Другая часть более динамична, меняется часто и непредсказуемо, например текст приветствия, который необходимо локализовать для каждого отдельного пользователя. В обоих случаях вам необходима уверенность, что сервлет или JSP могут развиваться независимо от подключаемого ими содержимого и что данная реализация сервлета или JSP при необходимости корректно обновит подключаемое содержимое. В этой главе приводятся рецепты включения содержимого в сервлеты и JSP, которые можно использовать в следующих условиях. • Когда подключаемая информация обновляется при каждом запросе пользователя. • Когда подключаемая информация содержит два и более уровней вложенности, например, когда включаемый файл в Свою очередь подключает другой фрагмент информации и т. д. • Когда для смены подключаемого сервлетом элемента информации желательно использовать дескриптор развертывания - аккуратный и не подверженный ошибкам способ подключения содержимого, которое довольно часто прихо- дится настраивать и изменять. • Когда требуется импортировать ресурсы, находящиеся за пределами web-прило- жения. Рецепт 6.1 описывает, как импортировать ресурс каждый раз, когда сервлет обрабатывает запрос. 137
Подключение ресурса при каждой обработке запроса Задача Требуется включать в сервлет Информацию из внешнего файла каждый раз, когда сервлет обрабатывает запрос. Решение Используйте в методе doGet() сервлета метод javax.servlet.RequestDis- patcher . include (request, response), подключающий внешний файл,, Обсуждение . J 1 • Метод doGet () класса javax.servlet .http.HttpServlet инициирует меха- низм подключения, когда web-контейнер получает запрос GET для данного сервлета. -*Л- При использовании данного подхода реализуйте метод doPost () сервлета так, чтобы он вызывал doGet (request, response). • В примере 6.1 приведен сервлет, импортирующий в методе doGet () шаблон с авторским правом (copyright) с помощью метода javax. servlet .RequestDis- patcher .includO(). Пример 6.1. Включение содержимого в методе init () сервлета, расширяющего класс HttpServ- let package com.j spservletcookbook; import j avax.servlet.*; import jaVax.servlet.http.*; public class IncludeServlet extends HttpServlet { public void doGet(fittpServletRequest request, HttpServletResponse response) throws ServletException, java.io.IOException { response.setContentType("text/html"); j ava.io.PrintWri ter out = response.getWriter(); out ’. printin (" <htrftl> H) ; out.printIn("<head>"); out.printin("<title>Include Servlet</title>“) ; out.printIn("</head>"); 138 | Глава 6. Динамически подключаемое содержимое страниц JSP и сервлетов
•d out.printin("<body>"); out.printIn("<hl>Welcome To Our Universe</hl>"); out.println("Imagine the rest of the page here.<br><br>"); //Включаем информацию об авторском праве RequootDicpatcher dispatcher и request.getRequestDispatcher( */copyright"); dispatcher.Include(request, response); out.printlp("</body>"); out.printlnft</html>n); }//doGet ) В примере 6.1 сервлет получает объект Request Dispatched'путем вызова метода javax.servlet.ServletRequest.getRequestDispatcher(). В данном случае методу getRequestDispatcher()в качестве параметра передается путь к сервлету, содержащему импортируемый ресурс: /cvpyright. Этот путь в web.xml отображается на сервлет Copyright, показанный в примере 6.2. Пример 6.2. Импортируемый сервлет Copyright public class Copyright extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.lOException { java.io.Printwriter out = response.getWriterO; out.printin("Copyright&copy; 2003-2004 EmbraceAndExtend Corp."); } } Сервлет Copyright выводит строку текста, включающую код символа авторского права (&сору), чтобы этот символ корректно отображался в итоговом HTML. Когда импортирующий сервлет вызывает метод include (), данный текст с авторским правом вставляется в место, определяемое положением этого вызова. I .....Т"1 Сервлет может импортировать и HTML-страницу, а не' только результат вывода JSP или сервлета. Если вы импортируете описанным выше способом HTML, убедитесь, что импортируемый текст не нарушит вашу страницу HTML, например повторением HTML-тегов или из-за ошибки закрытия определенного тега. На рис. 6.1 показано отображение в браузере страницы, сгенерированной сервлетом IncludeServlet. Рецепт 6.2 описывает, как конфигурировать импортируемые ресурсы с помощью внешнего файла конфигурации, например web.xml. Подключение ресурса при каждой обработке запроса | 139
Вас* Refresr Home Welcome To Our Universe Imagine the rest of the page here Copyright© 2003-2004 EmbraceAndExtend Corp Local intranet iather.com L 5g] Welcome toSparhawkJ 'Search Favorites Рис. 6.1. Страница сервлета IncludeServlet в браузере Джейсон Хантер, написавший технический обзор этой книги, указывает, что многие прибегают к созданию предварительно формируемых (в offline) статичных f v (HTML) файлов, тогда как содержимое большинства сайтов использует включения, например импорт заголовков и нижних колонтитулов для страниц этих сайтов. В большинстве случаев сервер обрабатывает запросы статичных файлов намного эффективнее, чем запросы к динамическим страницам (таким, как JSP, подключающим другие ресурсы). См. главу 3 «Servlet Best Practices» книги «Java Enterprise Best Practices» (O’Reilly) См. также % Рецепты 6.2 и 6.3 по включению ресурсов в сервлеты; рецепты 6.4 и 6.7 по использо- ванию jsp:include, директивы include, а также по включению ресурсов в JSP- документы или XML-файлы; главу SRV. 14.2.5 спецификации сервлетов версии 2.4; главу JSP.5.4 спецификации JSP версии 2.0 по j sp: include; главу JSP. 1.10.3 специфи- кации JSP версии 2.0 по директиве include. 140 | Глава 6. Динамически подключаемое содержимое страниц JSP и сервлетов
6.2 Использование внешней настройки включения ресурса в сервлет Задача Требуется для настройки ресурсов, включаемых в сервлет, использовать внешний файл конфигурации (например, web.xml). Решение Используйте параметры инициализации включающего сервлета'это позволит осущест- вить внешнюю настройку механизма подключения, а затем подключайте ресурсы с помощью метода javax. servlet. RequestDispatcher. include (request, response). Обсуждение Может потребоваться периодически Менять ресурс, подключаемый сервлетом, без необ- ходимости изменять и перекомпилировать код сервлета. Подобные изменения можно произ- водить, изменяя параметры инициализации сервлета в web.xml. С помощью этой стратегии можно менять как местоположение файла с ресурсом, так и способ извлечения ресурсов (Например, из базы данных). Меняя содержимое элемента param-value можно заставить сервлет импортировать требуемый ресурс. В примере 6.3 показан сервлет, скон- фигурированный на подключение файла privacy.jspf. Этот файл содержит стандартное зая- ление, часто включаемое в web-приложения, о неразглашении информации, предос- тавляемой пользователями. Пример 6.3. Задание подключаемого ресурса с помощью элемента ini t-param сервлета <servlet> <servlet-name>PrivacyServlet</servlet-name> <servlet-class>com.jspservletcookbook.IncludeServlet-</servlet-class> < ini t -param> <param-name>included-resource</paranr-name> <param-yalue>privacy. j spf</param-value> <init-param> </servlet> В примере 6.4 представлен метод doGet () сервлета PrivacyServlet. Метод прлучает значение параметра инициализации included-resource (privacy.jspf) и затем подключает необходимый JSP-сегмент. Использование внешней настройки включения ресурса в сервлет | 141
Пример 6.4. Подключение ресурса, заданного параметром инициализации public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.IOException { response.setContentType("text/html"); java.io.Printwriter out = response.getWriter(); out.printIn("<html>"); out.printIn("<head>"); out.printlnf"<title>Include Servlet</title>"); out.printIn("</head>"); out.printIn("<body>"); \ out.printlnf"<hl>Welcome To Our Universe</hl>"); out.printIn("Imagine the rest of the page here.<br><br>"); //Подключаем информацию о конфиденциальности //на основе значения init-param String includeRes • (String) getInitParameter( "included-resource"); //Получаем объект RequestDispatcher //на основе значения init-param RequestDispatcher dispatcher request» getRequestDispatcher(includeRes); dispatcher.include(request, response); out.printIn("</body>*); out.printIn("</html>*); } J В примере 6.4 сервлет получает объект RequestDispatcher на основе значение элемента init-param. Ниже представлен этот код. //Переменная includeRes содержит значение "privacy.jspf" // полученное из init-param RequestDispatcher dispatcher = request.getRequestDispatcher(includeRes); Затем с помощью метода dispatcher, include (request, response) в ответ на запрос подставляется выводная информация из privac.jspf. JSP-сегмент, подключаемый серйлетом PrivacyServlet показан в примере 6.5. Содержимое этого сегмента JSP содержит ряд тегов H'tML, не противоречащих HTML, сформированному включающей страницей. Пример 6.5. Сегмент JSP, включаемый в сервлет с помощью RequestDispatcher <%@page errorPage="/error.j sp"%> <pxstrong>Parker River Net Solutions Privacy Policy</strongx/p> <p>Any personal information you provide to us regarding Web- or software- development services or shareware software, such as your name, address,- tele- phone number, and e-mail address, will not be released, sold, or rented to any entities dr individuals outside of Parker River Net Solutions.</p> 142 | Глава 6. Динамически подключаемое содержимое страниц JSP и сервлетов
Включаемые сегменты или страницы не должны устанавливать или менять заголовки ответа на запрос, и любые попытки установить тип содержимого из подключаемого сервлета или JSP, например <%@ page content*Type=" text/xml'* %> будут игнорироваться. Все включаемые JSP задают страницу ошибки (error page), состоящую из тегов форматирования HTML и текста. На рис, 6.2 показано, как выглядит страница, формируемая сервлетом PrivacyServlet в браузере. Рис. 6.2. Web-страница с включенным сегментом JSP Можно несколько изменить пример 6.4, чтобы задать ресурс, подключаемый по умолчанию, на тот случай, если необходимый параметр инициализации сервлета по ошибке пропущен в дескрипторе развертывания (web.xml). В случае такой ошибки метод getlnitParameter возвращает null и в случае совпадения использует в операторе включения значение по умолчанию. См. также Рецепт 6.3 по включению ресурсов, имеющих вложенные включения; рецепты 6.4 - 6.8 по включению ресурсов в страницы JSP; главу SRV. 14.2.5 спецификации сервлета 2.4; главу JSP. 1.10.3 спецификации JSP 2.0 по включению файлов в JSP. Использование внешней настройки включения ресурса в сервлет | 143
6.3 Включение в сервлет ресурсов с несколькими уровнями вложенности Задача Включить в сервлет ресурсы, которые в свою очередь включают сервлеты, JSP или HTML-страницы. Решение Для подключения файла ресурсов верхнего уровня воспользуйтесь методом javax. servlet. RequestDispatcher.include (reQuest, response). Убедитесь, что в web.xml правильно сконфигурированы страницы ошибок - на случай возникновения исключительной ситуации в одном из импортируемых файлов «глубокого залегания». Обсуждение Хотя это не является наилучшим архитектурным решением, можно включить в сервлет ресурс, который в свою очередь включает другой ресурс и т. д. Это напоминает матрешку. Вы открываете верхнюю половинку матрешки и находите там матрешку меньших размеров, вложенную в первую. И здесь уже не кажутся странными мысли об очень слож- ных web-страницах, использующих HTML-фреймы, табличные теги, содержащие заго- ловки и нижние колонтитулы, и где все эти сегменты страницы содержат сторонний специализированный контент, получаемый с помощью механизма включения. Один из подключаемых файлов, находящийся на некоторой глубине вложенности, может возбу- дить исключение или некоторым образом испортить цепочку включений. Поэтому необхо- димо позаботиться о том, чтобы web-приложение имело соответствующим образом сконфигурированную страницу ошибки, которая отображала бы информацию о ресурсе, по вине которого возникла проблема, хотя это скорее помогает в отладке приложения, чем является профессиональным способом защиты от возникновения ошибок. В данном рецепте приводится пример сервлета, имеющего три уровня вложенности включаемых ресурсов, ^тот сервлет включает другой сервлет - Level2, который включает JSP level3.jsp, а эта страница, в свою очередь, включает сервлет Level4. На рис. 6.3 пока- зано, что увидит в браузере пользователь, обратившийся к этому сервлету com. j spserv- letcookbook. Multipleinc. Код сервлета приведен в примере 6.6. Этот сервлет отвечает за вывод текста первого уровня («Hello from Level 1»), а затем каждый из включаемых ресурсов дополняет содержимое ответа на запрос. 144 | Глава б. Динамически подключаемое содержимое страниц JSP и сервлетов
' Рис. 6.3. Три файла, подключаемых к одной web-странице Пример 6.6. Внешний сервлет package com.j spservletcookbook; Import j avax.servlet.*; import j avax.servlet.http.*; public class Multipleinc extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, / j ava.io.lOException { response.setContentType("text/html") ; java.io.Printwriter out = response.getWriter()? out.printIn(<html>"); ' out;. print In (" <head>") ; out.printin("<title>Multiple Includes</title>"); out.printin("</head>"); out.println("<body>"); out.printin("<hl>Hello from Level l</hl>"); out.printin("This text is displayed at Level 1."); RequestDispatcher dispatcher request. getRequestDispatcher("ZIevel2" ) ; Включение в сервлет ресурсов с несколькими уровнями вложенности | 145
I dispatcher.include(request, response); out.printIn(“</body>"); out.printIn("</html>"); } } Код RequestDispatcher dispatcher = request.getRequestDispatcher("/level2"); dispatcher.include(request, response); включает вывод сервлета, отображаемого на путь Aevel2, его код показан в примере 6.7 (только метод doGet ()). Пример 6.7. Сервлет первого уровня включения public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IGException { java.io.Printwriter out = response.getWriter(); out.println("<h2>Hello from Level 2</h2>"); out.println("This text is included at Level 2:"); //Include the JSP file named "level3.jsp" try{ RequestDispatcher dispatcher request.getRequestDispatcher( •/level3.jsp"); dispatcher.include(request, response); } catch (Exception se){ String context_path * (String) request.getAttribute( "javax. servlet. include. context_path"); String servlet_path (String) request.getAttribute( "javax.servlet.include.servlet_path"); String errMessage new StringBuffer( "Exception raised during Level2 servlet include:<br>"). append("Context path: "+context_path+"<br>"). append("Servlet path: "+servlet_path).toString(); throw njw ServletException (errMessage) i ) ) Сервлет из примера 6.7 дописывает в формируемый ответ на запрос свой текст, а затем подключает level3.jsp, как и внешний сервлет, используя для этого объект j avax. serv- let .RequestDispatcher. Сервлет Level2, кроме этого, использует блок try/catch и атрибуты объекта request (запрос), предназначенные для обработки исключений, которые могут возникнуть во время операций включения. 146 | Глава 6. Динамически подключаемое содержимое страниц JSP и сервлетов
В соответствии со спецификацией JSP API, включаемые ресурсы имеют доступ к пяти атрибутам запроса (объекта request): • javax.servlet.include.request_uri • javax.servlet.include.context_path • javax.servlet.include.sen let_path • javax.servlet.include.path_info • javax.servlet.include.query_string В блоке catch сервлет Level2 получает значения двух из этих атрибутов запроса. String context_path = (String) request.getAttribute(•javax.servlet.include.pontext_path"); String servlet_path = (String) request.getAttribute("javax.servlet.include.servlet_path"); Затем в этом же блоке catch сервлет Level2 возбуждает новое исключение Servlet- Exception и включает в состав сообщения об ошибке полученные значения атрибутов. Эту информацию об исключении, возникшем в ходе операции включения, отображает страница ошибок, сконфигурированная для данного web-приложения. Конфигурация с границы ошибок в web.xml выглядит примерно следующим образом: * \ <error-page> <except ion-t,ype> javax.servlet.ServletException </exception-type> <location>/error</lpcation> </error-page> Путь “/error” отображается на" сервлет, выводящий информацию, связанную с исключением. В примере 6.7 путь к контексту был пустым, а путь к сервлету - /level2. Отображение страницы ошибок в браузере показано на рис. 6.4. В качестве сервлета, генерирующего исключение, указан сервлет верхнего уровня (Multipleinc), поскольку именно он ини- циировал механизм включения, который привел к исключению ServletException. В примере 6.8 показан файл JSP (level3.jsp), импортируемый сервлетом начального уровня вложенности. &awilevel3.jsp представляет собой следующий уровень включаемого содержимого. Включение в сервлет ресурсов с несколькими уровнями Вложенности | 147
Рис. 6.4. Отображение информации об исключении, возникшем в ходе включения Включаемые сервлеты не вызывают метод java.io.PrintWriter.close(), поскольку это не позволит внешнему сервлету поместить в состав ответа на запрос что-то еще после того как отработает цепочка включений. Внешний сервлет (Multipleinc из примера 6.6) вызывает Printwriter. close () в самом конце, после окончания включения вложенных ресурсов. Пример 6.8. Включаемый JSP-файл level3.jsp <%@page errorPage="/error"%> <h3>Hello from Level 3</h3> This text is included at Level 3. <jsp:include page="/level4"/> И, наконец, файл JSP с помощью стандартного действия j sp: include импортирует текст, возвращаемый сервлетом, который отображен на путь flevel4. Сервлет Level 4 делает то же, что остальные сервлеты - пишет символьные данные в объект Print- Writer, поэтому его код не приводится. Причина всей этой пирамиды - проде- монстрировать, как файлы ресурсов разных, типов могут образовывать цепочки вложенных включений. Внешний сервлет включает сервлет номер два, тот включает JSP-файл, который в свою очередь включает текст, возвращаемый третьим сервлетом. Первый включаемый сервлет заключает код подключения своих ресурсов в блок try, чтобы перехватить любые прерывания, возбуждаемые включаемым файлом JSP. 148 | Глава 6. Динамически подключаемое содержимое страниц JSP и сервлетов
См. также Рецепт 6.1 по использованию механизма включения RequestDispatcher; рецепт 6.2 по заданию включаемого ресурса с помощью файла конфигурации; рецепты 6-4 - 6.8 по включению ресурсов в страницы JSP; главу SRV. 14.2.5 спецификации сервлета 2.4; главу JSP.1.1O.3 спецификации JSP 2.0 по включению файлов в JSP; главу 9 по заданию страниц ошибок B^web-приложениях. 6.4 Включение в JSP редко изменяемых ресурсов Задача ' Требуется включить в JSP ресурсы, которые не подвержены частым изменениям (например, фрагменты страницы, представляющие заголовок или нижний колонтитул). Решение Поместите на страницу JSP директиву include и присвойте включаемому сегменту JSP расширение .jspf. Обсуждение Страницы JSP часто составляются из фрагментов, представляющих панель навигации, заголовок (элементы страницы, находящиеся в верхней части web-страницы), нижний колонтитул (элементы, появляющиеся в нижней части) и тело с основным содержанием. Поскольку все страницы одного web-приложения или сайта могут использовать единую панель навигации, панель помещается в отдельный файл, и используется всеми web-компо- нентами, которым она требуется. Если вам необходимо импортировать на страницу JSP некоторый статичный (неизменяемый) сеТмент JSP, используйте директиву include. <%@ include file="/WEB-INF/jspf/navbar.jspf" %> Если ваша JSP-страница является JSP-документом, то есть выполнена в синтаксисе XML (см. главу 5), употребите следующую форму директивы include. <jsp:directive.include file="/WEB-INF/jspf/navbar.. jspf" /> Если значение атрибута file начинается с «/», то это путь относительно контекста, то есть путь относительно каталога web-приложения, включающего страницу JSP, использующую эту директиву. Для приведенных выше директив это означает «из корневого каталога web-приложения взять файл/WEB-INF/jspf/navbar.jspf». Включение в JSP редко изменяемых ресурсов | 144
Если значение атрибута file элемента include не начинается с «/», то это путь относительно страницы, то есть путь относительно страницы JSP, использующей данную директиву include. Приведенная ниже директива include включает файл из каталога segments, расположенного в том же каталоге, что и включающая страница JSP. <%@ include file="segments/navbar.jspf" %> Директива include включает текст или код включаемого сегмента во время фазы трансляции, когда JSP преобразуется в сервлет. Этот механизм включения - более эффективный способ импорта текста или кода (например, HTML-тегов или директив taglib), которые в противном случае вам' пришлось бы печатать на странице JSP до ее преобразования в сервлет. •» Разница между директивой include и стандартным действием jsp:include в том, что директива include импортирует реальный текст или байты * । включаемого сегмента, тогда как стандартное действие j sp: include посылает запрос к включаемой странице и затем включает уже динамически формируемый ответ на запрос. См. рецепт 6.5. Пример 6.9. Включение сегмента JSP в страницу JSP во время трансляции <%враде contentType="text/html"%>• <%е include file«"/WEB-INF/jepf/taglib-inc.jepf" %> <html> <head> <title>Main Content</title> </head> <body> <hl>Here is the main content</hl> This web application is using the following Servlet API: <c:out value="${pageContext.servletContext.majorVersion}"/>.<c:out value= * $ {pageContext. servletContext .minorversion}" /xbrxbr» <jsp:useBean id="timeValues" class="java.util.Date"/> <c:set targets"${timeValues}" values * ${pageContext.session.creationTime}* property^"time"/> The session creation time: <fmt:formatDate'value="${timeValues}" type="both" dateStyle= "medium" /xbrxbr> The toxml tag will create an XML view of this page. <t; toXml filenames * include-xmlview" /> </body> </html> 150 | Глава 6. Динамически подключаемое содержимое страниц JSP и сервлетов
Во второй стоке примера 6.9 подключается сегмент JSP с именем taglib-inc.jspf. Этот сегмент содержит директивы taglib, ответственные за доступность JSTL и возмож- ность использования на исходной странице пользовательских (нестандартных) тегов. Страница taglib-inc.jspf приведена в примере 6.10. Пример 6.10. Сегмент JSP, содержащий директивы taglib <%@ taglib uri="http://jaya.sun.com/jstl/core" prefix="c" %> <%@ taglib uri="http://java. sun. com/jst'l/fmt" prefix="fmt" %> <%(a taglib un,= "/toxml_view" prefix="t" %> Директива include включает эти три директивы taglib в исходную страницу, как если бы вы напечатали их на странице вручную, после чего контейнер JSP преобразует исходную страницу в сервлет. Три директивы taglib делают возможным использова- ние следующих тегов из примера 6-9. • c:out • c:set • fmt:formatDate • t:toXml ' *’ Спецификация JSP 2.0 рекомендует файлам с незаконченным кодом JSP, предназначенным для включения в другие файлы, присваивать расширение .jspf, ' f $ что означает «фрагмент JSP». Однако спецификация 2.0 называет эти фрагменты «сегментами JSP», чтобы не путать их с интерфейсом javax.servlet.jsp. tagext. JspFragment. Этот интерфейс — часть API расширения тегов. На рис. 6.5 показано, как данная страница JSP выглядит в браузере. Эта страница показывает версию API сервлетов, используемую web-контейнером, время создания javax. servlet.http.HttpSession (отформатированное с помощью JSTL-тега frnbf ormatDate) и пользовательский тег, описанный в рецепте 5.6. Этот тег вызывает генерирование XML-представления содержащей его страницы и сохранение соз- данного XML-файла под именем, указанным в атрибуте filename. Тег вставлен, чтобы показать способы включения разных видов директивы taglib. I См. также Рецепт 6.5 по использованию стандартного действия jsp:include; рецепт 6.7 по включению сегментов JSP в XML-файлы; рецепт 6.8 по включению содержимого, находящегося вне контекста JSP; главу JSP. 1.10.3 спецификации JSP 2.0 по включению файлов в JSP; главу 23 по тегам JSTL. -----------д------1-------j---------------- Включение в JSP редко изменяемых ресурсов | 151
Here is the main content This web application is using the"following Servlet API: 2.3 The session Creation time Mar 21.2003 2 36:47 PM The toXml tag wi II create an XML vi ew of thi s page. I : ' F], Puc. 6.5. Страница JSP с включенным сегментом JSP, содержащим директивы taglib 6.5 Включение содержимого в JSP при каждой обработке запроса Задача Требуется включать содержимое в страницу JSP каждый раз, когда она получает запрос, а не тогда, когда JSP преобразуется в сервлет. Решение Используйте стандартное действие j sp: include. Обсуждение Действие jsp:include включает ресурс в страницу JSP каждый раз, когда та получает запрос, что делает j sp: include гораздо более динамичным механизмом, чем директива include (см. рецепт 6.4). При использовании jsp:include включаемые сегменты JSP имеют доступ к объектам request, session и application исход- ной страницы и ко всем атрибутам, которые имеют эти объекты. Используйте действие jsp:include в любом месте файла, где необходимо импортировать ресурсы (например сегменты JSP) из этого же web-приложения. 152 | Глава 6. Динамически подключаемое содержимое страниц JSP и сервлетов
* *' Пользовательское действие import, являющееся частью ядра JSTL, может импортировать ресурсы из других web-приложений или даже из другого места t £ Интернета. См. рецепт 6.8. Пример 6.10 показывает страницу JSP, получающую информацию, введенную в форму, от другой страницы того же web-приложения. Получающая страница, для подключения сегментов, содержащих заголовок и нижний колонтитул, в свои верхнюю и нижнюю части, использует j sp: include. Чтобы продемонстрировать возможности подключаемого сегмента по доступу к информации о запросе и сеансе родительской страницы, сегмент с заголовком отображает (с помощью приветственного HTML-тега title) введенное в форму имя пользователя, которое хранится в параметрах запроса fname и Iname. В подвале отображается идентификатор сеанса, первое имя пользователя (first name) и последнее имя пользователя (last name). HTML-страница с формой для ввода первого имени, последнего имени и адреса электронной почты пользователя, показана на рис. 6.6. Рис. 6.6. HTML-форма Предположим, что страница с формой содержит код JavaScript, проверяющий правильность введенной информации. Когда пользователь нажимает кнопку Submit, информация, введенная в форму, передается к странице /solutions.jsp с помощью следующего HTML-тега. <form method=post action="/solutions.jsp"> Включение содержимого в JSP при каждой обработке запроса | 153
В примере 6.11 показана страница solutions.jsp, включающая два сегмента JSP: header.jspf (заголовок) и footer.jspf (нижний колонтитул). Сегмент header.jspf содержит HTML-тег head и помещает введенное в форму имя пользователя в тег title, вложен* ный в тег head. Сегмент footer.jspf для примера, повторяет имя пользователя и показы- вает идентификатор его сеанса, получаемый от JSP-объекта session. Спецификация JSP 2.0 рекомендует держать эти файлы в каталоге WEB INF/jspf. Пример 6.11. Подключение двух сегментов и отображение введенных в форму данных <%@page contentType="text/html"%> <html> <jsp: include page-"/WEB-INF/jspf/header,jspf" /> <body bgcdlor="white"> <table widths"660" border="0" summary="A,two-column table in which resides a logo and navigation bar"> <trxtd valign="top"> Organization image goes here...<p> <u>Main</u> </td> ) <td aligns"right" valign="top"> Navbar goes here... </tdx/trx trx td valign="top" aligns"center" colspan="2"> <table border="0" summary= "A nested table for aligning body content"> <trxtdxh2>Thanks for registering at this site</h2x/tdx/tr> <trxtd>Here is the info you submitted:</tdx/tr> <trxtd>Name: ' <%= request.getParameter("fname") %> <%= request.getParameter("lname") %></tdx/tr> <trxtd>Email : <%= request. getParameter (" eaddress") %>- < / tdx / trx /1 able> < / tdx / tr>< trx td>< / tdx / tr> </table> > <table widths"660” border="0" summary= "A table containing a footer navigation bar."> <tr><td valign="top" aligns"center"> <jsp:include pages "/WEB-INF/ jspf /footer, jsp" /> ( ««. * if </tdx/tr> 1 </table> </body> </html> / 154 | Глава 6. Динамически подключаемое содержимое страниц JSP и сервлетов
В примере 6.12 приведен сегмент header.jspf. Пример 6.12. Сегмент, содержащий заголовок страницы, включаемый директивой jsp: include <HEAD> t <META name="author" content= "Bruce W. Perry, author®jspservletcookbook.com"> <META name="keywords" content= "Java, JSP, servlets, databases, MySQL, Oracle, web development*» <TITLE>Parker River: Thanks For Visiting <H" request.gotParamstor("fnama") \> roquest.getParameter("Inane") H> </TITLE» <. </HEAD> Все, что делает этот сегмент, - включает имя пользователя в тег title. В примере 6.13 приведен импортируемый сегмент footer.jspf. $тот сегмент также отображает имя пользо- вателя, а кроме того, отображает идентификатор (ID) сеанса, причем перед обращением к методу HttpSession.getld() проверяется, что объект javax. servlet .http. HttpSession не null. Пример 6.13. Сегмент JSP с нижней частью страницы (подвалом), подключаемый с помощью jsp: include Thanks for visiting request.gotParameter("fname") 4> <4» roquest.getParameter("Inane") %><br> Session ID: <4 if (request. getSessionO ! null) <%» <4" request.getSession().getXd() %> <H } else (*> Unknown <4 } %> <brxbr> <a href="/index.html">Main</a» | <a href="/service.html">Services</a> | <a hrefs*/sitemap.html"»Site Map</a» | <a href®"/resources.html"»Resources</a> | <a href«"/contacts.html">Contact Us</a»| <a href="/prns_privacy.html">Privacy</a> На рис. 6.7 показан вид страницы solutionsjsp в браузере. ** • При использовании jsp:include изменения во включаемых файлах сразу же отражаются во включающей странице. А если вы измените страницу, включаемую * f директивой include, эти изменения не отразятся на включающей странице до тех пор, пока вы не внесете какие-либо изменения во включающую страницу, что заставит контейнер JSP перекомпилировать ее. \ Включение содержимого в JSP при каждой обработке запроса | 155
Рис. 6.7. Отображение в браузере сегментов с заголовком и нижней частью страницы См. также Рецепт 6.4 по директиве include; рецепт 6.7 по включению сегментов JSP в XML- файлы; рецепт 6.8 по включению содержимого, находящегося вне контекста JSP; главу JSP. 1.10.3 спецификации JSP 2.0 по включению файлов в JSP; главу 23 по тегам JSTL. 6.6 Использование внешнего файла конфигурации для включения ресурсов bJSP Задача Требуется динамически включать в JSP тот или иной файл, основываясь на значении из файла конфигурации. 156 | Глава 6. Динамически подключаемое содержимое страниц JSP и сервлетов
Решение Используйте стандартное действие jsp:include. Файл, который необходимо включить, задайте как значение свойства во внешнем файле свойств или как параметр конфигурации в дескрипторе развертывания. Обсуждение Использование внешней настройки для задания файла, включаемого в JSP. позволяет изменять имя и/или путь включаемого файла, не трогая код JSP. Кроме того, при исполь- зовании jsp:include включающую страйицу не нужно перекомпилировать, чтобы отразить какие-либо изменения во включаемом файле - web-pecjypc включается в JSP при обработке каждого запроса. Если в файле конфигурации вы укажете другой файл, ответ от включаемого ресурса добавится к ответу включающей JSP во время следующего запроса. Различие между стандартным действием j sp: include и директивой include *4» д в том, что последняя включает байты или содержимое импортируемого файла *• ? £ до того, как JSP будет откомпилирована (во время фазы трансляции). Если * . впоследствии включаемый сегмент изменится, изменения не отразятся на JSP, пока сама JSP не будет изменена, что заставит контейнер JSP (например, JSP-контейнер Jasper из Tomcat) перекомпилировать JSP. В примере 6.14 показана JSP, использующая для задания включаемого файла внеш- ний файл свойств.. Пример6.14. Использование java. util.ResourceBundle.getBundleO для включения файла, заданного с помощью внешнего файла конфигурации <%@page contentType="text/html"%> <%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %> <html> <% java-util.ResourceBundle bundle = java.util.ResourceBundle. get Bundle ("com. j spservletcookbook. include") j String segment bundle.getString("external~include");%> <jsp:include page="<%=segment %>"'/> <body> <h2>Welcome to our Portal Home <c:out value="${param.fname}" /> <c:out value="${param.Iname)" /></h2> <jsp:useBean id="datestring" class="java,.util.Date"/> The time is <c:out value="${datestring}" />.<br><br> </body> </html> Использование внешнего файла конфигурации для включения ресурсов в JSP | 157
В примере 6.14 включается сегмент JSP, путь к которому задан в свойстве exter- nal-include. Это свойство описано в обычном текстовом файле include.properties и выглядит примерно следующим образом: external-include=WEB-INF/ j spf/header_tag.j sp Файл include.properties хранится в WEB-INF/classes/com/jspservletcookbook. Когда ваш сервлет или JSP пытается добраться до списка свойств, вызывая статический метод ! £ j ava .util. ReseourceBundle. getBundle (" cbm. j spservletcookbook. '* include"), getBundle автоматически заменяет символы точка «.» символом «/» и добавляет к концу этой строки «.properties» (в нашем примере будет осуществлен поиск com/jspservletcookbook/include.properties). Код примера сохраняет полученные значения в переменной segment типа String. String segment = bundle.getString("external-include"); После этого переменная segment, задающая путь к файлу с подключаемым сегментом, содержит значение WEB-INF/jspf/header_tag.jsp. Включение выполняется с помощью JSP- выражения <%=segment %> на месте значения атрибута раде элемента j sp: include. <jsp:include page="<%=segment %>"/> 9 Во время работы этой страницы JSP, ответ от включаемого файла включается в ту часть страницы, где находится стандартное действие jsp:include. В примере 6.15 показано содержимое включаемого файла header_tag.jsp. Пример 6.15. Содержимое сегмента header_tag.jsp , <%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %> <HEAD> <МЕТА name="author" contents "Bruce W. Perry, author®jspservletcookbook.com"> <META name="keywords" contents "Java, JSP, servlets, databases, MySQL, Oracle, web development"> <TITLE>Parker River: Thanks For Visiting <c:out values"${param.fname}"/> <c:out values«${param.Iname}"/> </TITLE> </HEAD> Это законченный HTML-тег HEAD, включающий два вложенных тега - МЕТА и TITLE. Если вы обратитесь к включающей странице с запросом следующего вида: http://localhost: 8080/home/externalIncludeJsp?fname=Mister&lname=Beaii, то возвращаемое от сегмента JSP содержимое - реальный текст, который контейнер JSP подставит вместо тега j sp: include в вывод - выглядит примерно так, как показано в примере 6.16. 158 | Глава 6. Динамически подключаемое содержимое страниц JSP и сервлетов /
Пример 6.16. Вывод, выдаваемый при использовании jsp:include <HEAD> <МЁТА name—'1 author" content= "Bruce W. Perry, author©jspservletcookbook.com"> <META name="keywords" content= "Java, JSP, servlets, databases, MySQL, Oracle, web development"> <TITLE>Parker River: Thanks For Visiting Mister Bean </TITLE> </HEAD> Включаемый сегмент обрабатывает параметры запроса fnanfe и Iname из строки запроса: fname=Mister&lname=Bean и включает их значение в тег TITLE. Вид страницы extemallnclude.jsp в браузере показан на рис. 6.8. dfflE 3 Parker River Thanks Foi Visiting Mister Bean - Microsoft Internet Explorer Refresh^ Back hltp://localhost8080/home/externalfnclude.isp?fname«Misterttlnafne=Bean Welcome to our Portal Home Mister Bean The time is Mon Mar 31 12:43:33 EST 2003. I intranet' Puc. 6.8. Вид в браузере страницы JSP, использующей для включения другого сегмента JSP стандартное действие jsp: incl ude Использование внешнего файла конфигурации для включения ресурсов в JSP | 15
• •’ В данном примере подключаемый сегмент содержит такую директиву taglib, что необходимо использовать теги с: out из JSTL 1.0. Если вы используете JSTL 1.1, " f д* то атрибут URI должен иметь значение http://java.sun.com/jsp/jstl/core. <%@ taglib ' uri«"http://java.sun.com/jstl/core" prefix="c" %> Вы также можете передать параметры на обработку во включаемый сегмент* <jsp:include page="<%=segment %>"> <jsp:param name=*role" value="comedian"/> </jsp:include> ' Если вместо указания пути к включаемому файлу вы хотите использовать параметр контекста в дескрипторе развертывания, добавьте о web.xml элемент context-param (см. пример 6.17). Пример 6.17. Элемент context-рагат, задающий путь к включаемому файлу <context-param> <param-narae>external-include</param-name> <param-value>WEB- INF/ jspf /header_tag. j sp</param-value> </context-param> > . Во включающей странице JSP моркно получить значение контекстного параметра следующим образом. <jsp:include page="<%=application.get!nitParameter("external-include")%>"/> В результате JSP подставит в качестве значения атрибута раде элемента jsp: include путь WEB-INF/jspf/header_tag.jsp. См. также Рецепт 6.4 по директиве include; рецепт 6.7 По включению сегментов JSP в XML- файлы; рецепт 6.8 по включению содержимого, находящегося вне контекста JSP; главу JSP. 1.10.3 спецификации JSP 2.0 по включению файлов в JSP; главу 23 по тегам JSTL; следующую web-страницу о том, как метод getBundle возвращает определенные типы ResourceBundles: http://java.sun.eom/j2se/l.4.1/docs/api/java/util/ResourceBundle. html#getBundle(java. lang.String, java.util.Locale, jaya.lang. ClassLoader). 160 | Глава 6. Динамически подключаемое содержимое страниц JSP и сервлетов
6.7 Включение фрагмента XML в JSP-документ Задача Необходимо включить в JSP-документ файл с фрагментом XML, то есть включить страницу JSP, выполненную в синтаксисе XML. Решение Если необходимо, чтобы включение выполнялось при каждом запросе JSP, исполь- зуйте стандартное действие j sp: include. Если необходимо осуществить включение во время фазы трансляции, используйте элемент j sp: directive. include. Обсуждение Поскольку JSP-документ является правильным XML-файлом, для включения в него сегментов JSP можно использовать два XML-элемента: jsp:include и jsp:direc- tive. include. JSP-докуменг - это страница JSP, выполненная в синтаксисе XML, с соблюдением всех правил XML, иными словами, страница состоит из элементов и атрибутов XML, а также содержимого отдельных элементов XML. Такой JSP-документ затем размещается в корневом каталоге web-приложения (это стандартное местоположе- ние, но можно самостоятельно задать другое место для страниц JSP), и JSP-контсйнер транслирует этот XML-файл в сервлет. Одной из причин использования JSP-документов является интеграция JSP с другими XML-технологиями (XHTML, SVG, SOAP и т. п.). Рецепт 5.5 описывает JSP-документы подробнее. В примере 6.18 показана страница JSP из примера 6.14, выполненная в виде JSP-документа, здесь для включения файла WEB-INF/ ispf/header_tagjspf используемся директива j sp: include. Пример 6.18. JSP-документ, использующий для включения файла директиву jsp:include <jsp:root xmlns:jsp="http?//java.sun.com/JSP/Page" xmlns:c="http://java.sun.com/j s11/core" xmlns= “http: / /www. w3. org/1 99 9/xhtml" vers ion=" 2.0" > <j sp:directive.page contentType="text/html"/> <html> ' । / <jвр:include page«"WEB-INF/jspf/header_tag.jspf" /> <body> <h2>Welcome to our Portal <c:out value=" $ {param, f name} " /><jsp:text> </jsp:text> <c:out value="${param.Iname}• /></h2> *-1545 Включение фрагмент? XML в JSP-документ | 161
<jsp:useBean id="dateString" class="java.util.Date"/> <jsp:text>The time is </jsp:text> <c:out value="${dateString}" <br /><br /> </body> </html> </jsp:root> В примере 6.18 страница JSP включает текст, возвращаемый от headerjag.jspf. При использовании jsp: include сам включаемый файл не обязательно должен быть правильным XML-документом, главное, чтобы он возвращал текст, являющийся правиль- ным XML и корректно состыкующийся с включающим JSP-документом. Спецификация JSP 2.0 рекомендует давать сегментам JSP, включаемым в страницы JSP или JSP-доку^енты, расширение 'jspf. Кроме того, одним из способов отделить JSP-документы от обычных страниц JSP является присвоение JSP-документам расширения .jspx (при использовании web.xml по версии 2.4 спецификации сервлетов). Tomcat 4.1.x будет .компилировать и выполнять файлы с таким расширением как страницы JSP, если вы добавите в conf/web.xml следующие элементы servlet-mapping. < s er vl e t -mappijig> <servlet-name>jsp</servlet-name> <ur1-pattern> *.j spf</url-pa11ern> </servlet-mapping> <servlet-mapping> <servlet-name>jsp</servlet-name> <url-pattern>*. jspx</url-pattern:» </servlet-mapping> В примере 6.49 приведен включаемый файл header_tag.jspf. Он имеет собственную директиву taglib, чтобы теги, включенные в TITLE и связанные с JSTL, вырабатывали правильные значения из параметров запроса fname и lname. Когда к включающей странице поступает запрос http://localhost:8080/home/x617.jspx?fname=Bruce&lname=Perry текст, возвращаемый данцым сегментом JSP, включаемым посредством j sp: include, показывает комментарии, приведенные в нижней части примера 6.19. Пример 6.19. Включаемый файл header_tag.jspf <%@ taglib uri="http://java.sun.com/jstl/core" ptefix="c" %> <HEAD> <META name="author" content= > "Bruce W. Perry, author®jspservletcookbook.com"/> <META name="keywords" content- "Java, JSP, servlets, databases, MySQL, Oracle, web development"/> <TITLE>Parker, River: Thanks For Visiting <c:out value="${param.fname}"/> <c:0ut value="${param.lname}"/> 162 | Глава 6. Динамически подключаемое содержимое страниц JSP и сервлетов
</TITLE> </HEAD> <!--исходный тексту возвращаемый от header_tag.jspf <HEAD> <META name="author" content= "Bruce W. Perry, author©jspservletcookbook.com"/> <META name="keywords" content= "Java, JSP, servlets, databases, MySQL, Oracle, web development"/> <TITLE>Parker River: Thanks For Visiting / Bruce Perry </TITLE> </HEAD> —> На рис. 6.9 показано, как итоговый документ выглядит в браузере. Рис. 6.9. Вид JSP-документа в браузере Для включения можно также использовать элемент j sp: directive. include, включающий содержимое до того, как JSP-документ будет преобразован в сервлет, тогда как jsp:include производит включение уже во время выполнения. Чтобы изменить пример 6.17 для использования jsp:directive.include, замените элемент jsp: include. <jsp:directive.include f ile=“WEB-INF/jspf/header_tag.jspf" /> В данном случае включаемое содержимое не должно иметь синтаксических форм, не принадлежащих языку XML, например директив taglib, поскольку включаемый код включается напрямую в JSP-документ, когда последний преобразовывается в сервлет. Включение фрагмента XML в JSP-документ | 163 6*
Альтернативный подход заключается в использовании в XML секций CDATA, чтобы обернуть слова и символы. Которые в противном случае нарушили бы ** f jfr корректность синтаксиса XML-документа. Секции CDATA выглядят следующим ’ образом: <![CDATA[ ...]]> При использовании jsp:directive.include, для корректной компиляции JSP, необходимо удалить из верхней части кода из примера 6.19 директиву taglib. А для того чтобы теги с: out из включаемого сегмента все еще работали, необходимо проследить за наличием во включающем JSP-документе атрибута xmlns, делающего доступным элементы ядра JSTL, как это сделано в верхней части примера 6.18. Основное правило использования двух механизмов Включения заключается в следующем: если включаемый сегмент будет часто изменяться, причем включающая Страница должна оперативно реагировать на эти изменения, используйте jsp: include. Если включаемый сегмент относительно постоянен, используйте j sp: directive. include. См. также Рецепт 6.4 по директиве include; рецепт 6.5 по стандартному действию jsp: include; рецепт 6.8 по включению содержимого, находящегося вне контекста JSP; главу JSP. 1.10.3 спецификации JSP 2.0 по включению файлов в JSP; главу 23 по тегам JSTL. 6.8 Включение содержимого, находящегося за пределами контекста JSP Задача Требуется включить сегмент JSP, находящийся за пределами контекста включающего файла. Решение Используйте тег ядра JSTL - с: import. Обсуждение Тег с: import дает авторам страницы JSP большую гибкость в подключении ресурсов, находящихся как внутри, так и вне создаваемого web-приложения. Тег с: import позво- ляет странице импортировать ресурсы: J64 | Глава 6. Динамически подключаемое содержимое страниц JSP и сервлетов
• из-за границ web-контейнера, при помощи абсолютного URL (например, http://java. sun.com/api)’, • из другого контекста в пределах того же web-контейнера. Допустим, ваш домен имеет центральный репозиторий подключаемого содержимого, находящийся по адресу http:// www.mydomain.com/warehouse. Тогда страница JSP, инсталлированная в контекст /cus- tomer может импортировать ресурс из контекста /warehouse при помощи тега <с: import ur'l="7cataiog_header. jspf" context="/warehouse" />; • из собственного контекста, наподобие j sp: include. Данный рецепт содержит примеры импорта ресурсов из-за пределов контекста импортирующей страницы JSP. В примере 6.20 импортируется сегмент JSP с именем header_tag.jsp из контекста /dbproj. Атрибут url задает вкЯючае^ый ресурс, атрибут context объявляет контекст из которого JSP импортирует этот ресурс. Чтобы исполь- зовать тег с: import, JSP должна содержать директиву taglib следующего вида. <%в taglib uri="http://java.sun.com/jstl/core" prefix="c" %> В примере 6.20, во второй строке, включается сегмент taglib-inc.jspf с группой тегов taglib. Пример 6.20. Использование тега с: import для импорта по внешнему URL <%t?page contentType=" text/html" %> <%@ include file="/WEB-INF/jspf/tagljb-iric.jspf" %> <html> ' r - <c:import url«"/header_tag.jspf* context-"/dbproj" /> <bpdy> , . ; , . <h2>welcome to our Portal <c:but valuer" ${param.fname}" 7o <c:out yalue^“$(param.Iname}" /> </h2> <jsp:useBean id="datestring" class="java.util.Date"/> The time is <c:out value="${datestring}" />. <br /><br /> </body> </html> ' Тег c: import вставляет текст, генерируемый /dbproj/headerjag.jsp в ту часть кода, где располагается этот тег. Путь к контекрту /dbproj представляет другое (по отношению к импортирующей JSP) web-приложение (другой контекст). Верхняя часть импортирующей страницы выглядит теперь, благодаря HTML, созданному в импортируемом файле, следующим образом: <html> <HEAD> <МЕТА name="author"‘content= "Bruce W. Perry, author@jspservletcookbook.com"/> <META name="keywords" content= Включение co зержимого, находящегося за пределами контекста JSP | 165
"Java, JSP, servlets, databases, MySQL, Oracle, web development"/> <TITLE>Parker River: Thanks For Visiting Mister Bean </TITLE> </HEAD> <body> <!—далее идет продолжение страницы... --> При использовании Tomcat, во избежание исключения при попытке импорта ресурсов из другого контейнера для страницы JSP, использующей с:import, в файле conf/server.xml должен находиться элемент context со следующей парой атрибут/значение. ; crossContext="true" <!—по -умолчанию "false"—> В примере 6.21 импортируется описание протокола НТТР/1.1 (предложения для обсуж- дения (RFC) 2Q68). В этом примере тип содержимого указан как «text/plain», поэтому браузер не будет пытаться отобразить этот текст как HTML, что может сделать текстовые файлы неудобочитаемыми. Затем в примере 6.21 используется директива taglib, для того, чтобы в JSP можно было использовать тег с: import. Тег с: import задает местополо- жение импортируемого текстового файла с помощью абсолютного URL: http://www.ietf. org/rfc/rfc2068. txt. Пример 6.21. Использование с: import для импорта текстового ресурса, заданного с помощью абсолютного URL <%@page contentType="text/plain"%> <%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %> <c:import url=;"http://www.ietf.org/rfc/rfc2068.txt" /> Если JSP использует c: import для доступа к сомнительному ресурсу (когда сервер, получивший запрос на ресурс, отвечает HTTP-кодом возврата 403), тег с: import вызовет исключение и компиляция JSP завершится неудачно. С тегом с:import можно использовать вложенные в него теги с:param. В примере 6.22 импортируется файл header_tag.jspf и ему передаются на обработку два параметра запроса: fname и Iname. Директива taglib в верхней части примера 6.22 позволяет использовать в остальной части кода теги с: import и с: param. Пример 6.22. Передача значений параметров с помощью с: param <%@ taglib uri="http://java.jsun.com/jstl/core" prefix=“c" %> <html> <c:import url="WEB-INF/jspf/header_tag.jspf" > <c:param name=nfhame" value»"Mister"/> <c:param name»"Iname" value»"Bean"/> 166 | Глава 6. Динамически подключаемое содержимое страниц JSP и сервлетов
</с:import* <body> <h2> Далее идет продолжение страницы...</h2> </body> </html> Файл header_tag.jspf берет значения переданных параметров и помещает их в привет- ствие, формируемое в теге TITLE. HTML, получаемый в результате этого импорта пока- зан в примере 6-23. Пример 6.23. Значения параметров запроса отображаются в выходном HTML <html> » <HEAD> <• <МЕТА name="author" contents "Bruce W. Perry, author@jspservletcookbodk.com"/> <META name=" keywords" contents "Java; JSP,.servlets, databases, MySQL, Oracle, web development"/> <TITLE>Parker River: Thanks For Visiting Mister Bean </TITLE* </HEAD> <body> <h2> Далее идет продолжение страницы ...</h2> </body> </Html> См. также . Рецепты 6.1-6.3 по включению ресурсов в сервлет; рецепты 6.4-6.7 по использованию include и jsp:include, и включению ресурсов в JSP-документы или XML-файлы; главу 23 по использовании > JSTL 1.0; главу JSP.5.4 спецификации JSP 2.0 по j sp: include; главу JSP1.10.3 спецификации JSP 2.0 по директиве include. Включение содержимого, находящегося за пределами контекста JSP | 167
ГЛАВА 7 Обработка данных от web- формы в сервлетах и JSP 7.0 Введение Каждому разработчику знаком сценарий, когда клиент заполняет web-форму и отсылает введенную информацию на обработку в программу, работающую на стороне сервера. В ряде случаев для отправки НТТР-зацроса программе, работающей на стороне сервера, используется метод POST. В методе POST данные пересылаются на сервер в теле запроса^ а не в строке запроса, «прицепом» к URL (как в методе GET). Рассмотрим HTML-тег form из примера 7.1. _ Пример 7.1. HTML-тег form для пересылки данных методом POST <form method=POST асt£on="/project/controller"> <b>User Name:</b> <input type="text" name="username" size="20"> <br><br> <b>Department:</b> <input type="text" name="department" size=" 15" ><brxbr> <b>Email:</b> <input type=*'text* name=" email" size= »15"><br><br> <input type="submit" values"Submit"> </form> Когда клиент нажимает на кнопку «submit», чтобы отослать введенную информацию, формируется текст запроса, начало которого выглядит примерно следующим образом: POST /project/OOntroller HTTP/1.1 Accept: image/gif, image/х-xbitmap, image/jpeg, image/pjpeg, applica- tion/msword, application/vnd.ms-powerpoint, application/vnd.ms-excel, application/pdf, */* Referer: http://Iqcalhost:8080/project/login.jsp Accept-Language: en-us Content-Type: application/x-www-form-urlencoded 168
Немного далее, спустя несколько заголовков, следует тело запроса, содержащее вве- денные данные. username=Bruce+W+Perry&password=bw^pl9 б 8 JSP и сервлеты осуществляют лексический разбор данных, переданных с помощью POST, совершенно незаметно для разработчика. И это - тема нескольких следующих рецептов. Затем мы обсудим, как использовать сервлеты и JSP для отправки данных методом POST, когда они фактически исполняют роль клиента, а не выступают в роли программы на стороне сервера. 7.1 Обработка HTTP-запроса POST в сервлете Задача Требуется обработать данные, являющиеся частью запроса POST. Решение Используйте в |методе doPost сервлета методы ServletRequest.getParame- ter(String nar^e), getParameterMap(), getParameterNames () или get- ParamterValues(String name). Обсуждение Когда клиент посылает HTTP-запрос POST, метод service сервлета вызывает метод doPost сервлета. Далее у разработчика сервлета есть четыре разных метода, которые она может вызвать, чтобы добраться до присланных данных, что делает процесс обработки довольно простым. Если клиентское приложение использует метод GET, чтобы переслать сервлету данные в составе строки запроса, сервлету потребуется в методе doGet () осуществить вызов. doPost(request,response); Пример 7.2 демонстрирует обработку данных, посланных посредством POST, с помощью метода get Parameter (String name), а также с помощью метода getPa- rameterMap (), возвращающего java .util .Мар (карту). Этот объект содержит ключи и значения параметров. Метод getParameterNames!) возвращает java.util .Enu- meration - перечисление с именами параметров. Вы можете по очереди перебирать имена из этого перечисления, передавая их в метод getParameter(String name). Другой метод объекта ServletRequest - getParameterValues (String name) - воз- вращает массив строк (String), содержащий все значения, присланные в параметре с указанным именем (если значение всего одно, возвращается массив, содержащий всего одну строку). На рис. 7.1 показан вид в браузере вывода сервлета PostHandler после того, как пользователь заполнил и отослал форму из примера 7.1. Обработка HTTP-запроса POST в сервлете | 169
Рис. 7.1. Сервлет отображает пары имя/значение параметров, переданных от формы Пример 7.2. Использование методов ServletRequest.getParameter и getParameterMap для обработки присланных данных import j avax.servlet.*; import javax.servlet.http.*; import java.util.Map; import j ava.util.Iterator; import java.util.Map.Entry; public class PostHandler extends HttpServlet { public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.IOException { /* Использование ServletRequest.getParameter(String name), getParameterMap(), getParameterNames(), или getParameterValues() в методе сервлета doPost */ String name = request.getParameter("username"); String depart request.getParameter("department"); String email = request.getParameter("email"); response.setContentType("text/html"); java.io.Printwriter out = response.getWriter(); out.printin("<html>"); out.printIn("<head>"); 170 | Глава 7. Обработка данных от web-формы в сервлетах и JSP
I out .printin ("<title>Welcome</title>’'); out.printin("</head>M); out.printIn(“<body>"); out.printIn("<hl>Your Identity</hl>"); out.printIn( "Your name is: " + ( (name null || name, equal s ("'*)) ? "Unknown" : name)); out. print In (" <brxbr> ") ; out.printIn( "Your department is: " + ( (depart null || depart.equals("")) ? "Unknown" : depart)); »• out. printin (" <brxbr> *) ; out.printIn( "Your email address is: " + ( (email = null || email.equals("")) ? "Unknown" : email)); out.printIn("<h2>Using ServletRequest.getParameterMap</h2>"); Map param_map request. getParameterMapO ; if (param_map -= null) throw new ServletException( "getParameterMap returned null in: ".+ getClass().getName()); //перебор java.util.Map и отображение присланных //значении параметров //ключи объектов Map.Entry имеют тип String; значения //имеют тип String[], //то есть массив сорок Iterator iterator param_map.entryset().iterator(); * while(iterator.hasNext()){ Map.Entry roe» (Map.Entry) iterator .next (); out.println(me.getKey() + ": "); String (J arr (String!)) me.getValueO; for(int i=0;i<arr.length;i++){ out.printIn(arr[i]); ' //в случае нескольких значений, после значения //ставится запятая (кроме последнего) if (i > 0 && i I" arr.length-1) out.printin(", "); }//end for out .printin ("<brxbr>"); Обработка HTTP-запроса POST в сервлете | 171
}//end while out.printin("</body>"); out.printIn("</html>"); } public void doGet(HttpServletRequeSt request, HttpServletResponse response) throws ServletException, java*.io.lOException { doPost(request,response); } } ' ‘.r/i '•> it Получение значения параметра сводится к вызову метода request .getParame- ter(<имя параметра»). Затем, как в коде примера 7.2, можно осуществить проверку значения, чтобы напечатать корректные данные. out.printIn("Your name is: " + ( (name c= null || name.equals(""J) ? "Unknown" : name)); Если переменная name равна null или пустой строке, сервлет печатает «Unknown», в противном случае он печатает значение имени. Существует несколько шаблонов проектирования для проверки данных, введенных в форму, которыми вы можете вос- пользоваться, в том числе серверный сценарий JavaScript и компонент JavaBean для проверки. Обработка типа java. util .Мар (карта) сложнее и требует больше кода. Сервлет получает карту параметров, вызывая метод ServletRequest. Map, param_map = request.getParameterMap() Далее код получает java.util, Iterator от java.util .Set, возвращаемого от Map. entrySet (). Объект Set содержит объекты Map. Entry, являющиеся парами ключ/значение и представляющие имя и значение параметров. Сервлет, для организации цикла по именам и значениям параметров, использует итератор. Iterator iterator = param_jnap. entrySet().iterator(); while(iterator.hasNext()){ Map.Entry me = (Map.Entry) iterator.next(); out.printIn(me.getKey() + ": "); // Возвращаемое значение-массив строк String(] arr = (String[]) me.getValue(); for(int i=0;i<arr.length;i++){ out.printin(arr[i]); // в случае нескольких значений, после значения Г! Ставится запятая (кроме последнего) if (i > 0 && i !.= arr.length-1) out.printIn(", "); 172 | Глава 7. Обработка данных от web-формы в сервлетах и JSP
,}//end for out.printIn("<br><br>"); }//end while Если такой способ обработки присланных данных покажется слишком замысловатым, то оставьте getParameterMap() для приложений, созданных для работы с ниц, например для работы с компонентом JavaBean проверки, принимающим карту (Мар) в качестве параметра конструктора или метода. См. также Рецепт 7.2 по обработке запроса POST в JSP; рецепт 7.5 по передаче (POST) данных от сервлета; рецепт 7.7 по использованию сервлета для добавления параметра в строку запроса; документацию по ServletRequest API: http://java.sun.eom/j2ee/l.4/docs/api/ index.html. 7.2 Обработка HTTP-запроса POST в JSP Задача > ' • Требуется создать страницу JSP, обрабатывающую данные, передаваемые от прило- жения клиента с помощью метода POST. Решение Для перебора имен и значений параметров используйте JSTL-тег с: f orEach. Обсуждение JSTL делает процесс получения данных, присланных методом POST, достаточно простым. В примере 7.3 приведена страница JSP, содержащая некоторый шаблонный текст и JSTL-теги для отображения присланной информации. JSTL-тег с: f orEach перебирает присланные данные, используя неявно созданный JSTL-объект param. Объект param содержит типы java.util .Map.Entry, каждый из которых содержит пару ключ/ значение, представляющую имя и значение параметров, например «department=Develop- ment». Использующие язык выражений (Expression Language (EL)) конструкции * $ {map_ entry, key}" или " ${map_entry. value)" эквивалентны вызову методов Map. Entry. getKey () и getValue (). Возвращаемые ими значения передаются тегу с: out для отображения на HTML-странице. На рис. 7.2 показано, как эта страница будет выгля- деть в браузере, если в JSP переданы данные, введенные в форму из примера 7.1. Чтобы иметь возможность использовать JSTL 1.1, в элементе taglib атрибут uri должен иметь значение http://java.sun.com/jsp/jstl/core. Обработка HTTP-запроса POST в JSP | 173
Пример 7.3. Перебор присланных данных с помощью JSTL <%@page contentType="text/html"%> <%® taglib uri="http://java.sun.com/jstl/core" prefix="c" %> <html> <headxti tie» Post Data Viewer</titlex/head> <body> <h2>Here is your posted data</h2> <c:forEach var=,,map_lentryn items'- "$ {param} "> <strong»<c:out value="${map_entry.key}" /»</strong»: <c:out value= "${map_entry.value} " /xbrxbr» </c:forEach» </body> , </html> \ i При использовании тегов JSTL .обязательно удостоверьтесь в наличии соответ- ствующей директивы taglib. В примере 7.3 taglib обеспечивает работу всех пользова- тельских тегов с префиксом «с» (например, с: forEach). В главе 23 объясняется, как установить JSTL в web-приложение, создавать различные пользовательские теги и использовать EL. Рис. 7.2. JSP отображает присланные пары имя/значение 174 | Глава 7. Обработка данных от web-формы в сервлетах и JSP
Если вы хощте получить значения параметров без использования тега с: f orEach* используйте фрагмент кода, приведенный в примере 7.4. Этот код отображает значения параметров в случае, когда имена параметров известны разработчику (так обычно и бывает). Пример 7.4. Отображение значений отдельных параметров с Ломощью с: out <h2>Here is your posted data</h2> <strong>User name:</strong>: <c:out value=“${param.username}"/> <brxbr> <strong>Department:</strong>: <c:out value="${param.department}"/> <strong>Email:</strong>: <c:out value="${param.email}"/> Подстановка этого кода в JSP приведет к такому же отображению в браузере, как и то, что приведено на рис. 7.2. *• Спецификация JSP 2,0 позволяет использовать EL в шаблонном тексте, то есть без необходимости использовать JSTL-тег с: out. См. также Рецепт 7.2 по обработке запроса POST в JSP; рецепт 7.3 по заданию значений свойств компонента JavaBean из данных, введенных в форму; рецепт 7.4 по присвоению ' значения параметра атрибуту, принадлежащему определенной области видимости; рецепт 7.6 по передаче данных (POST) от JSP; главу 23 по использованию JSTL. 7.3 Установка свойств компонента JavaBean в JSP Задача Требуется установить значения свойств компонента JavaBean из данных, введенных в форму. Решение Используйте стандартное действие jsp:setProperty, с атрибутом property, установленным в «*», и атрибутом класс, установленным в полностью квалифицирован- ное имя класса компонента JavaBean. Установка свойств компонента JavaBean в JSP | 175
Обсуждение Стандартное действие j sp: setproperty имеет встроенный метод для авто- матического отображения значений, введенных в форму на поля (переменные) компо- нента JavaBean. Имена введенных параметро в должны корреспондироваться с Именами методов-установщиков значений свойств (setter) компонента JavaBean. В примере 7.5 показана страница setBean.jsp, получающая данные от HTML-формы. <fonp method=post action="http://localhost:8080/home/setBean.jsp"> i Сначала страница JSP создает объект типа com. jspservletcookbook.UserBean. Затем с помощью jsp:setProperty устанавливаются значения свойств компонента JavaBeap. Значение атрибута name элемента j sp: SetProperty соответствует значению атрибута id элемента jsp:useBean. Атрибут'property элемента jsp: setProp- erty просто устанавливается в «*». Пример 7.5. Страница setBean.jsp, присваивающая свойствам компонента UserBean значения, введенные в форму <%@page contentType= " text/h>tml"%> <%@ taglib uri="http://java.sun.com/jstl/cbrfe" prefix="c" %> <jsp:uscBoan id="userB" class""com. jspservletcookbook.UserBean" > <jsp:setProperty name" "userB" property»"*" /> </jsp:useBean> <html> <headxtitle>Post Data Viewer</title></head> <body> <h2>H'ere is your posted data</h2> <strong>User name</strong>: <d:out value= "${userB. username}" /><brxbr> <strong>Department</strong>: <c: out value=" $ {userB. department}" /><brxbr> <strong>Email</strong>: <c:out valufe="${userB.email)" /> </body> •> </html> В примере 7.5, для отображения на странице в браузере значений свойств компо- нента JavaBean, используется JSTL-элемент с: out. Атрибут value элемента с: out для получения значения свойства использует EL, например ’’${userB.email)". Такой синтаксис эквивалентен вызову метода getEmail () объекта UserBean. В примере 7.6 показан компонент UserBean, использующий соглашение по имено- ванию JavaBean, чтобы убедиться, что значения его свойств можно корректно устанав- ливать и извлекать. Отображение этих значений в браузере показано на рис. 7.3. 176 | Глава 7. Обработка данных от web-формы в сервлетах и JSP
Put. 7.3. Отображение введенных в форму данных посредством Java Bean t , - f I Действие jsp:setProperty, так, как оно используется в данном рецепте, устанавливает значения свойств компонента JavaBean, определяя соответствие имен параметров именам методов-установщиков значений свойств компонента JavaBean (setter). Если у компонента JavaBean имеется поле с именем «Username», то должен существовать параметр с точно таким же именем «Username», а метод- установщик значения поля должен называться "setUsername (String name)" (если поле имеет тип String) и никак иначе. Будьте осторожны, имена чувствительны к регистру символов! Пример 7.6. Инкапсуляция присланных данных в компоненте JavaBean package com.jspservletcookbook; public class UserBean implements java.io.Serializable! String username; String email; String department; public UserBeanOf) public void setUsername(String .username){ if(_usemame != null && „username. length () > 0) username = „username; else username = "Unknown"; ) Установка свойств компонента JavaBean в JSP | 177
public String getUsername(){ if(username != null) return username; v else return "Unknown";} public void setEmail(String _email){ if(„email != null && „email.length() >0) , email = „email; else email = "Unknown"; } ; public String getEmail(){ if(_email != null) return email; / else return "Unknown";} public void setDepartment(String „department){ if(„department != null && „department.length() > 0) department = „department; x else department = "Unknown"; } public String getDepartment(){ if(department J= null) return department; else return "Unknown"; } } В рецепте 7.2 показано, как использовать компонент JavaBean для проверки коррект- ности введенных в форму данных См. также Рецепт 7.2 по обработке запроса POST в JSP; рецепт 7.4 по присвоению значения параметра атрибуту, принадлежащему определенной области видимости; рецепт 7.6 по передаче данных (POST) от JSP; главу 23 по использованию JSTL. 178 | Глава 7. Обработка данных от web-формы в сервлетах и JSP
7 А Присвоение в JSP значения параметра формы атрибуту, принадлежащему определенной Области видимости Задача Требуется атрибуту, имеющему областью видимости запрос, сеанс или приложение, присвоить одно из значений, введенных в форму. Решение Сначала для присвоения свойствам компонента JavaBean значений, введенных в форму, используйте стандартные действия j sp: useBean и j sp: setProperty. После этого с помощью пользовательского JSTL-тега с: set присвойте атрибуту проверенное в компо- ненте JavaBean введенное значение. Обсуждение Некоторые web-приложения могут проверять корректность введенных в форму дан- ных, например комбинацию e-mail/пароль, после чего проверенное значение присваива- ется атрибуту, имеющему областью видимости запрос, сеанс или приложение. Эффективным способом обработки введенных в форму данных является использование компонента JavaBean, чьей целью является проверка этих данных на соблюдение ряда бизнес-правил или соответствие внешнему источнику данных, например базе данных. Если данные корректны, приложение создает, например, атрибут сеанса, инициа- лизируемый этим значением. Если введенные данные некорректны, то логическая переменная в компоненте JavaBean устанавливается в false. Страница JSP, которой посылаются введенные в форму данные, мойсет проверить эти значения, Перед тем как обрабатывать их как корректные. В примере 7.7 показан компонент JavaBean ClientValidator, имеющий три поля: email, password и valid. Этот компонент используется страницей JSP для проверки введенных в форму данных, перед тем/ к^к страница присвоит эти данные атрибутам с областью видимости - запрос. Пример 7.7. Компонент JavaBean ClientValidator package com.j spservletcookbook; public class ClientValidator implements java.io.Serielizable{ String email; String password; boolean valid; Присвоение в JSP значения параметра формы атрибуту | 179
public ClientValidator(){ this.valid=filse;} public boolean isValid(){ f /* С помощью объекта доступа к данным (Data Access Object)проверяется email и пароль. Если данные корректны, this.valid устанавливается в true*/ this.valid=true; return valid; } t public void setEmail(String „email){ , if(„email != null && „ernai 1.length()'> 0) email = „email;, else / email = "Unknown"; } public String getEmail(){ return email; ' } public void setPasswOrd(String „password){ if («password != null && „password.length() >0) password = „password; else- paSsword = "Unknown"; } public String getPassword(){ return password; } 7 .} Страница JSP, использующая компонент ClientValidator приведена в примере 7.8. Сначала с помощью j^sp: useBean создается экземпляр компонента. Затем полям (свойст- вам) компонента присваиваются присланные значения (e-mail и пароль). После этого значение свойства isValid проверяется на true с помощью следующего JSTL-кода. <o:if test="${isValid)"> JSP присваивает значения двум атрибутам с областью видимости - запрос. Теперь эти атрибуты доступны странице, на которую переадресуется данный запрос. Атрибуты с областью видимости сеанс доступны из сервлетов и JSP, связанных с этим же сеансом (см. главу 11). Область видимости «приложение» распространяется на контекст (web- Ттриложение). 180 | Глава 7. Обработка данных от web-формы в сервлетах и JSP
Если требуете» задать значения атрибутам с областью видимости сеанс или 4 приложение, замените код примера 7.8 на следующий код. —__<с: set var=" email" value= ** $ {chk. email}". 'scope»"session" /> <c:set var="password" value="${chk.password}" scope»"session" J> , или: <c:set var="email" value="${chk.email}" scope»"application" /> <c:set var="password" value="${chk.password}" scope="application" /> Пример 7.8. Страница validChk.jsp, использующая компонент JavaBean для верификации данных, введенных в форму <%@page contentType="text/html"%> <%© taglib uri="http://java.sun.com/jstl/core" prefix="c" %> <jsp:useBean id«-"chk" class» "com. j spservletcookbook. Cl ientValidator" > <jsp:setProperty name»"chk" property»"*" /> </jsp:useBean> <%— получаем корректные значения от компонента ClientValidator —%> <с:set.var»"isValid" value»"${chk.valid}" /> <c:if test»"§{isValid}"> <c:set var»"email" value»"${chk.email}" scope»"request" /> <c:set var»"password" value»"${chk.password}" scope»"request" /> </c:if> <html> <head><title>Client Checker</titlex/head> <body> f <h2>Welcome</h2> <strong>Email</strong>: <c:out value»"${email}" /><brxbr> <strong>Password</strong>:i. <c:out value»"${password}" /> </body> </html> См. также . .t Рецепт 7-2 пр обработке запроса POST в JSP; рецепт 7.3 по заданию значений свойств компонента JavaBean из данных, введенных в форму; рецепт 7.6 по передаче данных (POST) от JSP; главу 23 по использованию JSTL. Присвоение в JSP значения параметра формы атрибуту | 181
7.5 Передача данных (POST) от сервлета Задача Требуется переслать из сервлета параметры и их значения в виде запроса POST. Решение Для автоматизации отправки данных (POST) другим программам используйте компо- нент HttpClient (из Jakarta Commons) и его класс PostMethod. \ * Обсуждение Компонент HttpClient из Jakarta Commons позволяет разработчику замаскировать в своем JAVA-коде особенности работы web-браузеров, например отправки GET или POST HTTP-запросов, а также ^использование HTTPS для защищенного обмена. Как написано в домашней странице этого полезного компонента, HttpClient «обеспечивает эффек- тивную, отвечающую современным требованиям, со множеством возможностей, реали- зацию клиентской стороны для большинства самых последних стандартов и рекомендаций по HTTP» (http://jakarta.apache.org/commons/httpclient/). HttpClient распространяется под лицензией Apache Software. В данном рецепте описано использование HttpClient для отправки данных другой работающей на стороне сервера программе с помощью HTTP-метода POST. Для начала скачайте HttpClient с сайта Jakarta (http://jakarta.apache.org/commons/httpclient/down- loads.html). Затем распакуйте дистрибутив и поместите содержащийся там JAR-файл в каталог WEB-INF/lib своего web-приложения. Во время написания этих строк был дос- тупен JAR-файл Release 2.0 Alpha 3: commons-httpclient-2.0-alpha2.jar. После инсталля- ции вы можете начать использовать классы этого компонента в своих сервлетах и компонентах JavaBean. В примере 7.9 приведен сервлет, посылающий данные странице JSP: http://localhost: 8080/home/viewPost.jsp. Файл viewPost.jsp со страницей, отображающей присланные дан- ные, показан в примере 7.3. Обратите внимание на классы из пакета org.apache, commons. httpclierit, которые импортируются в верхней части сервлета. » Пример 7.9. Сервлет, посылающий с помощью HttpClient данные странице JSP package com.jspservletcookbook; import javax.servlet.*; import j avax. servlet. http. * ; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpStatus; import org. apache. commons. httpclient. methods. PostMethod; 182 | Глава 7. Обработка данных от web-формы в сервлетах и JSP
import org.apache.commons.httpclient.NameValuePair; public class ClientPost extends HttpServlet { public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.lOException { HttpClient httpClient « new HttpClient( ) ; PostMethod postMethod » new PostMethod ( "http://localhost:8080/home/viewPost.jsp"); NameValuePair[] postData { new NameValuePair("username", "devgal"), < new NameValuePair ("department", ' development”), new NameValuePair("email", "devgalGyahoo.com") }; //в версии 2.0 betal появился метод //PostMethod.setRequestBody(NameValuePair[]) i //а метод addParameters удален postMethod.addParameters(postData); httpClient.executeMethod(postMethod); , //отображаем ответ на метод POST z 1 response.setContentType("text/html"); java.io.Printwriter out = response.getWriterO; //Код возврата (HTTP Status Code) "200 OK" if (postMethod.getStatusCode() HttpStatus.SC_OK) { out.printIn(postMethod.getResponseBodyAsString()); } else ( n out.printIn("The POST action raised an error: " + postMethpd.getStatus- LineO); }' //освобождаем соединение, использованное методом postMethodireleaseConnection(); } '• public void doGet(HttpServletRequest iequest, HttpServletResponse response) throws ServletException, java.io.lOException { doPost(request,response); i } } Передача данных (POST) от сервлета | 183
Приведенный код посылает странице JSP, которая будет обрабатывать данные, три пары имя/значение (с именами username, department и email). HttpClient принимает возвращаемый в результате выполнения метода POST текст, и его можно отобразить в этом же сервлете. Если в ответ на запрос POST ожидается текст большого объема, то вместо метода getResponseBodyAsString () нужно использовать метод HttpMe):hodBase. getResponseBodyAsStreamf). Последний (метод getResponseBodyAsStream ()) возвращает объект java, io. Inputstream. Пример 7.9 создан на основе примеров кода, приведенных на web-сайте компонента HttpClient. Рис. 7.4 показывает результат запроса сервлета ClientPost в браузере. Рис. 7.4. Отображение текста, возвращаемого в ответ на отправку сервлетом данных См. также Рецепт 7.1 по обработке запроса POST в сервлете; рецепт 7.7 по использованию сервлета для добавления параметра в строку запроса; страницу компонента HttpClient (Jakarta Commons): http://jakartajapache org/commons/httpclient. 184 | Глава 7. Обработка данных от web-формы в сервлетах и JSP
7.6 Пересылка данных методом POST из JSP Задача Требуется переслать параметры и их значения в составе HTTP-запроса POST из JSP. Решение Самым простым является старый способ - использовать HTML-тег form и кнопку Sub- mit (представить). Если требуется послать данные динамически (не полагаясь на нажатие пользователем кнопки), используйте компонент JavaBean, инкапсулирующий код работы с HttpClient, описанный ь рецепте 7.5. Обсуждение Самый простой способ инициировать метод POST из JSP - задать шаблонный HTML- текст, приведенный в примере 7.1, с HTML-тегом form, когда пользователь заполняет форму данными и отсылает их, нажав кнопку Submit Поскольку типичная форма была уже приведена в примере 7.1, здесь я покажу компонент JavaBean, позволяющий странице JSP динамически посылать данные другому процессу, также выполняющемуся на стороне сервера. Пример 7.10 демонстрирует страницу jspPost.jsp, использующую вспомогательный класс PostBean для отправки набора параметры/значения другой странице JSP. Получающая страница, viewPost.jsp, обрабатывает параметры, посылаемые ей объектом PostBean, и возвращает некоторый тек^т, который отображает JSP из примера 7.10. Исходная страница передает классу PostBean параметры, которые необходимо отправить, в виде java.util .Мар. Свойство url объекта PostBean задает место назначения отправляемых данных (адрес, которой в ином случае вы поместили бы в атрибут action HTML-тега form). Сначала код. <jsp:setproperty name="postBean" property="parameters" value="<%= request. getParameterMap() %>" /> с помощью метода HttpServletRequest. getParameterMap () получает карту параметров (Map), которые были переданы странице jspPost.jsp, а затем передает эту карту классу PostBean для дальнейшей пересылки. Пример 7.10. Страница JSP, передающая параметры и значения динамически < %@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %> < %— создаем экземпляр класса PostBean, если он еще не создан —%> <jsp:useBean id="postBean" class="com.jspservletcookbook.PostBean" /> < %— присваиваем свойству parameters объекта PostBean значение типа Map (карта) - -%> Пересылка данных методом POST из JSP | 185
<jsp:setProperty name*"postBean" property*"parameters* value*"<%= request, getParameterMap()4>" /> <jsp:setProperty name*"postBeanM property*"url" value*"http://localhost: 8080/home/viewPost.jsp" /> <%— Отсылаем параметры и отображаем ответный текст —%> <jsp:getProperty name*"postBean" property*"post"/> В примере 7.11 показан класс Post Bean, используемый страницей JSP для посылки данных. Этот компонент JavaBean, для отправки HTTP-запроса POST, использует компо- нент HttpClient (из Jakarta Commons). Сама отправка происходит в методе PostBean, get Post (), который посылает параметры и возвращает текстовый ответ от получающего сервлета (в данном случае от viewPost.jspY Поскольку данный метод компонента JavaBean носит имя get Post (), то в соответствии с соглашением JavaBean об именовании методов, возвращающих значения свойств, из JSP к нему можно обращаться следующим образом: сjsp:getProperty name="postBean" property="post"/> Вышеприведенная строка кода затем заменяется возвращаемым значением типа String. Пример 7.11. Компонент JavaBean, пересылающий данные методом POST, для использования в сервлетах и JSP package com.j spservletcookbook; Import java.util.Map; import java.util.Iterator; import java.util.Map.Entry; T import org.apache.commons.httpclient.HttpClient; irnnort org.apache.commons.httpclient.HttpStatus; import org.apache.commons.httpclient.methods.PostMOthod; , import org. apache. commons. httpclient. MameValuePair; import org.apache.commons.httpclient.HttpException; public class PostBean implements java.io.Serializable { private Map parameters; private String url; public PostBean(){ } public void setparameters(Map param){ if (param != null) parameters = param; } public Map getParameters(){ 186 | Глава 7. Обработка данных от web-формы в сервлетах и JSP
return parameters; } ' public void setUrl(String url) ( if (url != null && !(url.equals(""))) this.url=url; ) public String getUrl(){ return url; } public String getPost() throws java.io.IOException,HttpException{ c. if (url == null || url.equals("") || parameters == null) throw new IllegalStateExceptionf "Invalid url or parameters in PostBean.getPost method."); String retumData = "" ; HttpClient httpClient new HttpClient(); PostMethod postMethod » new PostMethod(url); //преобразуем Map, переданный ж компонент к типу NameValuePair[] NameValuePair!] postData * getParams(parameters); //в версии 2.0 betal•появился метод //PostMethod.setRequestBody(NameValuePair(]) //а метод addParameters удален postMethod.addParameters(postData); httpClient.executeMethod(postMethod); //Код возврата (HTTP Status Code) "200 OK” if (postMethod.getStatusCode() HttpStatus.SCJDK) ( retumData» postMethod. getResponaeBodyAsString (); } else { retumData» "The POST action raised an error: " + postMethod.getStatus£ine(); } //освобождаем соединение, использованное методом postMethod.гeleaseConnection(); return retumData; }//end getPost private NameValuePair[] getParams(Map map){ Пересылка данных методом POST нз JSP | 187
NaxneValuePair [] pairs new NaroeValuePair[map.size() ]; //Используем итератор, чтобы поместить пары имя/значенио //из карты (Мар) в массив Iterator iter map.entryset().iterator(); • int 1 0; while (iter.hasNoxt()){ Map.Entry me (Map.Entry) iter.next(); //Map.Entry.getValue() возвращает массив String[] pairs[i] new NamaValuePair( ' (String)me.getKey()<((String[]) me.getValue())[0]); i++; } return pairs; }//end getParams } Отображаемый результат выглядит так, как показано на рис. 7.5, поскольку здесь для отображения пар имя/значение, передаваемых в JSP, также используется viewPosj.jsp. Повторюсь, если вы хотите использовать JSP для динамической имитации HTML-формы, очень неплохая идея - делегировать механизм отправки данных компоненту JavaBean, и тогда JSP, как ей и положено, останется компонентом презентации, и к тому же создан- ный компонент JavaBean можно использовать повторно в других приложениях. См. также у . , •» г Рецепт 7.2 по обработке запроса POST в JSP; рецепт 7.3 по заданию значений свойств компонента JavaBean из данных, введенных в форму; рецепт 7.8 по использованию JSP для добавления параметра в строку запроса. 188 | Глава 7. Обработка данных от web-формы в сервлетах и JSP
Рис. 7.5. Отображение параметров, добавленных перенаправляющей страницей JSP 7.7 Использование сервлета для добавления параметра в строку запроса Задача Вы хотите использовать сервлет, чтобы добавить один или несколько параметров к строке запроса и затем перенаправить этот запрос к месту конечного назначения. Решение , / Для получения существующей строки запроса используйте API HttpServletRe- quest. Затем добавьте в нее несколько новых параметров и перенаправьте запрос с помощью javax. servlet .RequestDispatcher. Обсуадение Сервлет из примера 7.12 просто берет любую существующую строку запроса и добавляет к ней дополнительные параметры. Затем он посылает расширенную строку запроса, вызывая RequestDispatcher. forward. Использование сервлета для добавления параметра в строку запроса | 189
Пример 7.12. Добавление параметра к строке запроса с помощью сервлета import j avax.servlet.*; > import javax.servlet.http.*; public class QueryModifier extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.lOException { //returns null if the URL does not contain a query string String querystr = request.getQueryString(); if (querystr != null){ t querystr querystr + "&inspector-name«Jen&inspedtor-emailnJenniferqOyahoo.com”; } else { T querystr = " inspector-name®Jen&inspector-emaileJenniferqdyahoo. cbm" ; } RoquestDispatcher dispatcher « request.getRequestDispatcher("/viewPost.jsp?"+querystr); dispatcher.forward(request,response); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.lOException { doGet(request,response); } } • • Метод HttpServletRequest.getQueryString() возвращает строку запроса без открывающего «?». - iff.* first=Bruce&last=Perry&zipcode=01922 Если вы хотите получить URL запроса, предшествующий строке запроса, без символа «?», используйте HttpServletRequest.getRequestuRL(), который возвращает результат типа java. lang. StringBuf f еУ. ч См. также < Рецепт 7.1 по обработке запроса POST в сервлете; рецепт 7.5 по посылке (POST) дан- ных от сервлета. 190 | Глава 7. Обработка данных от web-формы в сервлетах и JSP
7.8 Использование JSP для добавления параметра к строке запроса Задача Вы хотите использовать JSP, чтобы добавить один или нескольких параметров к строке запроса и затем перенаправить запрос к месту назначения. Решение Используйте стандартные действия j sp: forward и j sp: papain. Обсуждение Чтобы добавить один или нескольких параметров и перенаправить запрос другому компоненту, требуется просто добавить в JSP четыре строки. Действие j sp: forward добавляет любые j sp: param к существующим параметрам, когда перенаправляет этот текст обрабатывающему компоненту, см. пример 7.13. Пример 7.13. Добавление параметров и перенаправление в JSP <jsp:forward page="/viewPost.jsp” > ' <jsp:param name="inspector-name" value="Jen"/> <jsp:param name="inspectdr-email" value="jenniferq@yahoo.com"/> </j sp:forward> Если обратиться к этой странице с помощью следующего URL: http://localhost:8080/home/addParam.jsp?first=Bruce&last=Perry&zip=01922 то три оригинальных параметра (first, last и zip) сохраняются, когда действие jsp: forward добавляет два дополнительных параметра (inspector-name, inspec- tor-email) и перенаправляет страницу. В данном примере страница обрабатывается страницей viewPost.jsp, приведенной в примере 7.3. Запрошенная из браузера страница addParam.jsp перенаправляет запрос с пятью параметрами к странице viewPost.jsp. Резуль- тат отображается в браузере так, как показано на рис. 7.5. См. также Рецепт 7.2 по обработке запроса POST в JSP; рецепт 7.3 по заданию значений свойств компонента JavaBean из данных, введенных в форму; рецепт 7.6 по передаче данных (POST) от JSP. Использование JSP для добавления параметра к строке запроса | 191
7.9 Использование фильтра для чтений значений параметров - Задача Вы хотите использовать фильтр для перехвата и чтения данных, введенных в форму. Решение Для получения значения параметров в фильтре, используйте различные методы get- Parameter программного интерфейса (API) ServletRequest. Обсуждение Класс фильтра, создаваемого для сервлета, должен реализовывать интерфейс javax. servlet .Filter. Это означает, что этот класс Filter реализует методы doFilter (request, response) и destroy () этого интерфейса. Метод doFil- ter содержит «тропинку» к значениям параметров фильтруемого сервлета. Параметр ServletRequest метода doFil ter имеет методы get Parameter, getParame- terMap, getParameterNames и getParameterValues, позволяющие фильтру подсмотреть параметры (и их значения) запроса к этому сервлета. Но сначала необходимо отобразить созданный фильтр на требуемый сервлет. Приве- денная ниже цепочка из web.xinl отображает объект Filter на сервлет по имени Viewer. <•—здесь идут элементы context-param —> <filter> <filter-name>ParamSnoop</filter-name> <filter-class>com.jspservletcookbook.ParamSnoop</filter-class> </filter> < f i 1 ter-mapping» < fi1ter-name>ParamSnoop</fi1ter-name> <servlet-name>Viewer</servlet-name> </filter-mapping» <!—продолжение web.xml —> Поместите класс фильтра в каталог WEB-INF/classes своего web-приложения или внутрь JAR-файла находящегося в WEB-INFAib. Во время своего старта контейнер сервлетов создает по экземпляру каждого фильтра, объявленного в web.xml. Затем, при запросе пользователя к любому из сервлетов, на которые отображен данный фильтр, контейнер выполняет фильтр (вызывая его метод doFilter). Таким образом, фильтр ParamSnoop может проинспектировать запрос к сервлету Viewer до того, как сервлет обработает этот запрос. 192 | Глава?. Обработка данных от web-формы в сервлетах и JSP
Согласно спецификации сервлетов версии 2.4, глава SRV.6.2.1: «для каждой , * виртуальной Java-мащиньг, выполняемой на контейнере, создается только 4 t по одному экземпляру фильтра, объявленного в дескрипторе развертывания». В примере 7.14 доступ к параметрам перехватываемого запроса осуществляется с помощью вызова метода ServletRequest .getParameterMap!). Однако вы можете использовать для этого и другие методы ServletRequest API, например getParam- eterStringName. Метод getParameterMap!) возвращает карту java, util .Мар имен и значений параметров, которые можно извлечь из карты с помощью итератора j ava. ut i 1.1 terator и его метода next (). •Метод Map.entrySet () возвращает java.util .Set, из которого можно j получить Iterator, вызвав метод Set .iterator (•). В этом случае метод It era tor. next () возвращает объекты Map. Entry, содержащие пары ключ/ значение, представляющие имя и значение параметров. В примере 7.14 также показано, как извлечь из карты параметров пары имя/значение параметра и запротоколировать их с помощью метода ServletContext. log (). Пример 7.14. Подглядывание за значениями параметров запроса сервлета package com.j spservletcookbook; import j avax.servlet.*; k import j avax.servlet.http.*; import java.util.Map; import java.util.Iterator; import java.util.Map.Entry; public class ParamSnoop implements Filter { private FilterConfig config; /** Создание нового экземпляра ParamSnoop */ public ParamSnoop!) { < } public void init(FilterConfig filterCoiifig) throws ServletException! this, config = ijil terConf ig(; ) public void doFilter! ServletRequest request, ServletResponse response^, Filterchain chain) throws java.io.IOException, ServletException { Map paramMap request.getParameterMap!); ServletContext context « config.getServletContext!); /* Используем метод ServletContext.log для протоколирования 7 Использование фильтра для чтения значений параметров | 193
имен/значений параметров */ , context.log("doFilter called in: " + config.getFilterNameO + " on " + (new java.util.Date())); context.log("Snooping the parameters in request: " + ((HttpServletRequest) request).getRequestURI()); i Iterator iter paramMap.entrySet().iterator(); while (iter.hasNext()){> Map.Entry me (Map.Entry) iter.nextO; cont ext. log ((String) me. getKey() + + ((String[]) me.getValue())[0]); } ; //послать запрос дальше, к Следующему фильтру //или к сервлету назначения chain.doFilter(request,response); } public void destroy(){ /♦вызывается перед удалением экземпляра фильтра из обслуживания в web-контейнере*/ } } Единственной причиной использования метода ServletContext. log () было желание отобразить результат инспекции параметров запроса. Ниже приводится пример протокола Tomcat, находящегося в каталоге <Tomcat-mstallation-directory>/logs, в который выведены два параметра, переданные в запросе к сервлету (last и first). Иными словами, из браузера был сделан запрос http:/Aocalhost:8080/home/ viewer?first-Bruce&last=Perry. 2003-04-13 17:13:33 doFilter called in: ParamSnoop on Sun Apr 13 17:13:33 EDT 2003 2003-04-13 17:13:33 Snooping the parameters in request: /home/viewer 2003-04-13 17:13:33 last: Perry 2003-04-13 17:13:33 first: Bruce • I См. также Рецепт 7.1 по обработке запроса POST в сервлете; рецепт 7.7 по использованию сервлета для добавления параметра в строку запроса; главу 19 по фильтрации запросов и ответов; главу SRV.6 спецификации сервлетов версии 2.4 по фильтрам. 194 | Глава 7. Обработка данных от web-формы в сервлетах и JSP
ГЛАВА8 , I Выгрузка файлов 8.0 Введение Чтобы дать возможность пользователю выгрузить файл из своей файловой системы для обработки на сервере, на web-сайтах, используется HTML-тег form. Элемент input, вложенный в тег form, имеет атрибут type, которому для выгрузки файла необходимо присвоить значение file. Теги form и input имеют синтаксис, описан- ный в рецепте 8.1. HTTP-запрос на выгрузку файлов использует тип контента «multipart/form-data». HTTP-сообщение, которое пользователь посылает на сервер, нажав на vfreb-странице клавишу подтверждения ввода, включает в себя описательные заголовки и тело каж- дого из выгружаемых файлов. Каждый из выгружаемых файлов отделяется от других заданным шаблонным ограничителем (см. значение заголовка Content-Туре в примере 8.1). В примере 8.1 показан, в урезанном виде, запрос с содержимым типа "multipart/form-data", включающий с себя три очень небольших выгружаемых файла. Чтобы сделать пример компактнее, я удалил некоторые значения из заголовка Accept этого запроса. Пример 8.1. Сообщение с HTTP-запросом, включающим три выгружаемых файла POST /home/upload.jsp HTTP/1.1 Accept: image/gif, image/х-xbitmap, ima^e/jpeg, image/pjpeg ... Referer:.http://localhost:8080/home/interact.html Accept-Language: en-us Content-Type: multipart/form-data; boundary^----------------------- 7d33cllc6018e Accept-Encoding: gzip, deflate ' User-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 4.0) Host: localhost:9000 Content-Length: 541 Connection: Keep-Alive Cache-Control: no-cache Cookie: JSESSIONID=7F6154184FFF3DiAE345ElF2FFFlA22E -----------------------------7d33cllc6018e 195 7*
' Content-Disposition: form-data; name="filel"; filename»nH:\home\filel.txt" Content-Type: text/plain This is file 1. ----------------------------7d33cllc6018e Content-Disposition: form-data; name="file2"; filename*"H:\home\file2.txt" Content-Type: text/plain This is file 2. \ ----------------------i-----7d33cllc6018e Content-Disposition: form-data; name="file3"; filename*"!!:\home\file3.txt” Content-Type: text/plain This is file 3. ' _ ----------------------------7d33cllc6018e— В HTTP-запросе каждый из выгружаемых файлов отделен шаблонным ограничителем. -------------__---7d33cllc6018e Каждый файл имеет заголовки Content-Disposition и Content-Туре., Эти простые текстовые файлы, выгружаемые на сервер, состоят лишь из одной строки, чтобы дать вам представление о том, как выглядит данный вид HTTP-запроса. Более подробная информация о механизме выгрузки файлов содержится в RFC 1867: http:// www.ietf.org/rfc/rfcl867. txt. 8.1 Подготовка HTML-страницы для выгрузки файлов Задача Неоходимо создать HTML-страницу, позволяющую пользователю указать файл своей файловой системы, который требуется выгрузить на сервер. / Решение Используйте НТЭДЬ-тег form с атрибутом enptype, установленным в «multipart/ form-data». Используйте тег input, вложенный в тег form, с атрибутом type, установ- ленным в «file».. Обсуадение Код HTML, предназначенный для выгрузки файлов, должен включать в себя ряд необ- ходимых вещей. Тег form задает сервлет (или другой компонент, работающий на стороне сервера), который будет обрабатывать выгружаемый файл, указанный в атрибуте action этого тега. Чтобы выгрузка работала, атрибут method должен быть установлен в POST (не GET). Атрибут enc type тега form должен иметь значение «multipart/form-data». 196 | Глава 8. Выгрузка файлов »
Пользователь вводит файл, предназначенный для выгрузкй с помощью тега input с атрибутом type равным «file», который на форме выглядит как текстовое поле. Атрибут name однозначно идентифицирует каждый конкретный тег input, что приобретает важ- ное значение, когда HTML-код предполагает выгрузку более одного файла (см. замечание в конце данного рецепта). Без какого-либо дополнительного вмешательства сервер сохраняет у себя выгруженные файлы под их исходными именами. Атрибут accept предназначен для ограничения типов файлов, которые пользователь может выбрать для выгрузки (например, только MIME-типы «application/pdf»), однако этот атрибут плохо поддерживается некоторыми браузерами. При отображении HTML-кода из примера 8.2 браузеры автоматически показывают кнопку Browse. При нажатии на эту кнопку браузер открывает стандартное для файловой системы окно выбора файла, с помощью которого пользователь < Может выбрать файл для выгрузки. Пример 8.2. Простой НТМЪкод для выгрузки файла <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head> <title>Please Choose The File</title> </head> <body bgcolor="#ffffff"> <table border»"0"><tr> <form actions"/home/servlet/com.j spservletcookbook.UploadServlet" method»"post" enctype*"multipart/form-data"> <td valign="top"xstrong>Please choose your document:</strongxbrx/td> <td> <input type="file" name=”filel"> <brxbr> </tdx/tr> <trxtdxinput type»"submit" value»'Upload File"x/tdx/tr> </form> <7table> </body> f </html> /• После выбора файла, в текстовом поле выводится полный путь к выбранному файлу. Рис. 8.1 показывает внешний вид этой HTML-страницы в браузере. На рис. 8.1 показано поле input после того, как пользователь выбрал файл. Браузер автоматически заполняет это текстовое поле значением полного пути к выбранному файлу. Чтобы можно было выгрузить более одного файла, включите несколько* тегов ' input с разными значениями атрибута name. Браузер свяжет кнопку Browse * с каждым из них. Подготовка HTML-страницы для выгрузки файлов | 197
Рис. 8.1. HTML-страница для выгрузки файла в сервлет См. также Рецепт 8.2 по использованию для выгрузки файлов библиотеки com.oreilly. serv- let; рецепт 8.3 по выгрузке одного файла; рецепт 8.4 по выгрузке нескольких файлов; рецепт 8.5 по управлению именами файлов; рецепт 8.6 по использованию JSP для выгрузки файлов; домашнюю страницу com.oreilly.servlet: http://www.servlets.com/cos/index. html, RFC 1867 по выгрузке файлов с помощью формы: http://www.ietf.org/rfc/rfcl867.txt. 8.2 Использование библиотеки com.oreilly.servlet Задача Для выгрузки файлов вы хотите воспользоваться классами com.oreilly.servlet, разработанными постоянным автором издательства O’Reilly Джейсоном Хантером. * ♦’ Библиотека Джейсона Хантера берет на себя всю основную работу по выгрузке файлов. Я использую здесь эту библиотеку (конечно, с разрешения автора), поскольку она делает работу по выгрузке красиво, а кроме того, нет необходимости * заново изобретать и без того совершенное колесо. Решение Скачайте дистрибутивный ZIP-файл с http://www.servlets.com/cos/index.html. Добавьте входящий в дистрибутив файл cos.jar в каталог WEB-INF/lib своего web-приложения. Убедитесь, что не нарушаете лицензионное соглашение, используя данную библиотеку. 198 | Глава 8. Выгрузка файлов
Обсуждение JAR-файл cos.jar включает в себя пакеты com.oreilly. servlet и срт. oreilly. servlet .multipart. Эти пакеты содержат несколько классов, в том числе классы, имя которых начинается с «Multipart», которые можно использовать в сервлете для выгрузки файла. * *' Архив cos.jar также включает ряд других интересных и полезных классов, предназначенных для использования в сервлетах, однако приводимые здесь * f рецепты посвящены только выгрузке файлов. Загрузите с сайта http://www.servlets.com/cos/index.html последнюю версию ZIP-файла с дистрибутивом. Этот ZIP-файл содержит cos.jar, который необходимо поместить в каталог V'EB-INF/classes ваше] о приложения. Впоследствии вы можете импортировать необходимые классы в свой сервлет. inport com.oreilly.servlet.MuitipartRequest; import com. oreilly. servlet .multipart. FiTeRenamePolicy; Но перед тем как вы интегрируете эти классы в свой код, внимательно прочитайте лицензионное соглашение по их использованию: http://www.servlets.com/cos/license.html. В последующих рецептах данной главы предполагается, что cos.jar у вас уже есть и содержащиеся в нем классы доступны web-приложению. Если вы не сделаете необходимые шаги по обеспечению доступности этих классов в ваших web- приложениях, ни один из приведенных в главе примеров не будет работать правильно. См. также Рецепт 8.1 по подготовке HTML-кода для выгрузки файлов; рецепт 8.3 по выгрузке одного файла; рецепт 8.4 по выгрузке нескольких файлов; ’ рецепт 8.5 по управлению именами файлов; рецепт 8.6 по использованию JSP для выгрузки файлов; домашнюю страницу com,.oreilly, servlet: http://www.servlets.com/cos/index.html’, RFC 1867 по выгрузке файлов с помощью формы: http://www.ietf.org/rfc/rfcl867.txt. Использование библиотеки com.oreilly.servlet | 199
8.3 Выгрузка одного файла Задача Требуется создать компонент, способный принимать выгружаемый клиентом файл и сохранять его в локальном каталоге. Решение Создайте сервлет, использующий класс com.preilly.servlet.MultipartRe- quest из созданного Джейсоном Хантером архива cos.jar. Обсуждение Класс MultipartRequest включает несколько перегружаемых конструкторов. Один из них, используемый в примере 8.3, принимает в качестве параметров объект javax. servlet .http.HttpServletRequest, путь к каталогу, где вы хотите сохранить выгружаемые файлы, и предельно допустимый размер этих файлов. В примере 8.3, если клиент выгружает файл размером более 5 Мб, сервлет UploadServlet возбуждает исключение java. io. lOException. Вы можете сделать так, как сделано в примере 8.3: позволить обработать это исключение в элементе error-page файла web.xml, созданном для исключений lOException, или использовать для обработки ошибок в сервлете блок ~ try/catch. Объявление страниц ошибок в web-приложении описано в гл^ве 9. После создания экземпляра класса MultipartRequest, этот объект сразу присту- пает к выгрузке файла, то ест^ь для выгрузки вам не придется вызывать какие-либо методы. Сервлет из примера 8.3 инициирует выгрузку файла и затем отображает имя выгружаемого файла (файлов). Пример8.3. Сервлет, использующий классMultipartRequest package com.j spservletcookbook; import javax.servlet.*; import j avax.servlet.http.*; import com.oreilly.servlet-MultipartRequest; import java.util.Enumeration; public class UploadServlet extends HttpServlet { private String webTempPath; 200 | Глава 8. Выгрузка файлов
public void init(){ , webTempPath = getServletContext().getRealPathf"/") + “data"; } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.IOException { //предельный размер файла - 5Мбайт MultipartRequest mpr new MultipartRequest( ? request,webTempPath,5 * 1024 * 1024); Enumeration enum mpr.getFileNames(); response,petContentType("text/html"); ,. java.io.PrintWriter out•= response.getWriter(); out.printIn("<html>"); out.printIn("<head>"); out.printin("<title>Servlet upload</title>"); out.println("</head>"); out.printIn("<body>"); 4 for (int i 1; enum.hasMoreElements();!++) out.printIn("The name of uploaded file " + 1 + " is: ” + mpr.getFilesystemName((String) enum.nextElement()) + "<brxbr>"); out.printin("</body>"); out.printlnf"</html>"); } public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.IOException { throw new ServletException ("GET method used with " + getClassO ,getName()+"; POST method required."); ' } } Путь к каталогу сохранения файла генерируется с помощью вызова j avax. servlet. ServletContext.getRealPath("/"), который дает абсолютный путь к корневому каталогу web-приложения (например, h:\home\). После этого к нему добавляется имя ката- лога, в котором будет сохранен файл (data). Имя каталога сохранения также можно указывать, Используя внешний файл конфигурации, например с помощью элемента context-param a web.xmL Подробности см. в рецепте 8.6. 1 ...""3 ' " "" ---- Выгрузка одного файла | 201 I
Метод MultipartRequest. getFilesystemName (StringNaxne) возвращает имя выгружаемого файла в файловой системе клиента. На сервере его можно сохранить под этим же именем, а можно переименовать, использовав другой конструктор класса MultipartRequest, принимающий в качестве параметра объект FileRenamePol- icy. Этот конструктор выглядит следующим образом: MultipartRequest(javax.servlet.http.HttpServletRequest request, java.lang.String saveDirectory, int maxPostSize, \ ' FileRenamePolicy policy) Существует несколько версий конструктора класса MultipartRequest, имеющих параметр FileRenamePolicy, все они используются для переименования выгружаемых файлов (см. рецепт 8.5). Сервлет из примера 8.3 возбуждает исключение ServletExcep- tion, если к нему обратились с помощью метода GET, который неприменим для выгрузки файлов. См. также Рецепт 8.1 по подготовке HTML-кода для выгрузки файлов; рецепт 8.2 по использо- ванию библиотеки com.oreilly.servlet; рецепт 8.4 по выгрузке нескольких фай- лов; рецепт 8.5 по управлению именами файлов; рецепт 8.6 по использованию JSP для выгрузки файлов; домашнюю страницу com.oreilly.servlet: http://www.serv- lets.com/cos/index.html-, RFC 1867 по выгрузке файлов с помощью формы: http://www.ietf. org/rfc/tfcl867.txt. 8.4 Выгрузка нескольких файлов Задача Требуется выгрузить сразу несколько файлов клиента и обработать каждый из полученных файлов. Решение Используйте класс Mui tipart Parser из созданного Джейсоном Хантером архива cos.jar. Обсуждение Класс Mui tipart Parser позволяет сервлету последовательно обрабатывать каж- дый раздел (содержащий один файл) из составного (multipart) HTTP-запроса с выгружае- мыми файлами, получаемого сервером. 202 | Глава 8. Выгрузка файлов
Класс Mui tipart Parser также можно использовать для обработки нескольких файлов как единого целого. Однако Mui tipartParser дает возможность I* £ обрабатывать каждую часть в отдельности (например, сохраняя ее в базе даннух) * в ходе разбора многофайловой выгрузки. Кроме того, при обработке запроса сервлет может узнать тип содержимого, размер и имя файла. С помощью данного класса сервлет может произвести основные проверки, например подсчитать количество выгруженных файлов, а также узнать, заполнил ли пользователь все предложенные ему для загрузки файлов поля input. HTML-файл из рецепта 8.5 был изменен таким образом, чтобы позволить пользова- телю выгрузить из своей файловой системы три разных файла (см. рис. 8.2). Рис. 8.2. HTML-форма для выгрузки трех файлов Эта форма создана путем включения в HTML трех тегов input с атрибутом type="file". <input type="file" name="filel"xbrxbr> <input type="file" name=" file2 "><brxbr> <input type=“file" name="file3"> Сервлет из примера 8.4 обрабатывает загрузку нескольких файлов, импортируя из архива cos.jar три класса. Класс MultipartParser в примере 8.4 ограничивает размер выгружае- мых файлов 5 Мб, однако вы можете установить другое значение, передав в конструктор соответствующее значение параметра, и уменьшить или увеличить наибольший размер принимаемого файла, или оставить принятое по умолчанию значение - 1 Мб. Выгрузка нескольких файлов | 203
Документация по этому классу находится по адресу http://www.servlets.com/cos/ javadoc/com/oreilly/servlet/multipart/MultipartParser.html. --- В случае превышения одним из выгружаемых файлов заданного наибольшего размера, объект Mui tipart Parser возбуждает исключение java. io. lOException. Метод MultipartParser. readNextPart () возвращает тип Part (раздел) или null, если вход- ной поток не. содержит других разделов. Объект Part может быть объектом типа FilePart или объектом типа ParamPart, в зависимости от того, что он реально содержит. Объект ParamPart является оберткой для других параметров, которые может содержать HTML- форма, например «username» (имя пользователя). Объект FilePart имеет несколько мето- дов, предоставляющих информацию о выгружаемом файле, например, тип содержимого и имя файла. Метод FilePart. writeTo (java, io. File dir) сохраняет файл в задан- ном параметром dir каталоге и возвращает размер файла (тип long). Кроме того, объект FilePart может записывать в Outputstream, например: writeTo(jaya.io.Out- putstream out). Пример 8.4. Сервлет, обрабатывающий загрузку нескольких файлов package com.jspservletcookbook; import javax.servlet.*; import javax.servlet.http.*; . import com.oreilly.servlet.multipart.MultipartParser; import com.oreilly.servlet.multipart.Part; import com.oreilly.servlet.multipart.FilePart/" public class ParserServlet extends HttpServlet {. private String fileSavePath; , public void init(){ // save uploaded files to a 'data* directory in the web app fileSavePath “ getServletContext().getRealPath("/") .+ "data"; } ’ public void doPost(HttpServletRequest request, HttpServletRespbnse response) throws ServletException, java,io.lOException { response.setContentType("text/html"); java.io.Printwriter out = response.getWriter(); out.printIn("<html>"); out.printing"<head>"); out.printin("<title>File uploads</title>"); out.printIn(“</head>"); out.printin("<body>"); 204 | Глава 8. Выгрузка файлов
out.printIn("<h2>Here is information about any uploaded files</h2>"); try{ // наибольший размер файла - 5 MB MultipartParser parser new MuitipartParser( request,5 * 1024 * 1024); Part _part null; while ((_part parser.readNextPart()) ! null) { if (_part.ioFile()) { // получаем некоторую информацию о файле File Part fPart ж (FilePart) _part; * String name fPart.getFileName(); if (name ! null) { long fileSize fPart.writeTo( new java.io.File(fileSavePath)); out.printIn("The user's file path for the file: " + fPart.getFilePath() + "<br>"); out.printin("The content type of the file: " * fPart.getContentType()+ "<br>"); out. print In ("The file size: " +fileSize+ " bytes<brxbr>"); //переходим к следующему файлу, если он есть } else { out.printIn( "The user did not upload a file for this part."); } } else if (_part.isPuram()) { // если это не файл, а другой параметр, //например имя пользователя, делаем что-то иное } }// end while out. print In t" < /body> "); out.println("</html>") ; } catch (java.io.lOException ioe){ Z/error-page в дескрипторе развертывания //отображен на java.io.lOException throw new java.io.lOException( "lOException occurred in: " + getClass().getName()); Выгрузка нескольких файлов | 205
} }//doPost public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.lOException { throw new ServletException( "GET method used with " + getClass().getName()+. POST method required."); } На рис. 8.3 приведена страница, созданная сервлетом по итогам выгрузки файлов. Рис. 8.3. Сервлет отображает информацию о выгруженных файлах См. также I Рецепт 8.1 по подготовке HTML-кода для выгрузки файлов; рецепт 8.2 по использо- ванию библиотеки com.oreilly.servlet; рецепт 8.3 по выгрузке одного файла; рецепт 8.5 по управлению именами файлов; рецепт 8.6 по использованию JSP для выгрузки файлов; домашнюю страницу com.oreilly.servlet: http://www.servlets.com/cos/iiidex. html', RFC 1867 по выгрузке файлов с помощью формы: http://www.ietf.org/rfc/rfcl867.txt. 206 | Глава 8. Выгрузка файлов
8.5 Изменение имен файлов Задача Вы хотите изменять имена выгружаемых файлов, в соответствии с некоторой приня- той у вас политикой, во избежание конфликтов имен с существующими файлами. Решение Создайте класс, реализующий интерфейс com. oreilly. servlet .multipart. FileRenamePolicy, или воспользуйтесь ютассом DefaultFileRenamePolicy. • • Обсуждение Пакет com. oreilly. servlet .multipart содержит интерфейс FileRename- Policy, который можно использовать для проведения политики изменения имен выгружаемых файлов. Класс DefaultFileRenamePolicy переименовывает выгружаемые файлы, имена которых вступают в конфликт с существующими файлами, добавляя к их имени число. Например, если уже существует файл index.txt, то класс DefaultFileRenamePolicy присвоит первому выгружаемому файлу с таким же именем имя indcxl.txt, следующему выгружаемому файлу с этим именем - index2.txt и т. д. .Если вы хотите проводить собственную политику переименования, создайте класс, реализующий интерфейс FileRenamePolicy, затем реализуйте в этом классе метод rename (java. io. File f ile), инициирующий акт переименования. Приведенный ниже образец кода показывает использование конструктора класса MultipartRequest из примера 8.3. Здесь конструктору в качестве параметра переда- ется объект DefaultFileRenamePolicy. MultipartRequest mpr = new MultipartRequest( request,webTempPath,(5 * 1024 * 1024),new DefaultFileRenamePolicy()); He забудьте добавить в класс сервлета следующие операторы импорта. import com.oreilly.servlet.MultipartRequest; import com.oreilly.servlet.multipart.DefaultFileRenamePolicy; Как уже отмечалось, вы можете создать собственный механизм переименования, самостоятельно реализовав интерфейс FileRenamePolicy. В примере 8.5 приведен класс MyFileRenamePolicy, который переименовывает каждый выгружаемый файл, добавляя к концу существующего имени отметку времени. Отметка времени вычисля- ется следующим образом. ,// столько секунд прошло с 00:00:00 часов 1 января 1970 new java.util .Date () .getTimeO /1000 Изменение имен файлов | 207
Этот код переименовывает файл, сначала убирая расширение файла (если оно есть), затем добавляя строку (String) (с вычисленной числовой последовательностью) и снова возвращая прежнее расширение файла. Пример 8.5. Переименование выгружаемых файлов с помощью собственного Java-класса package com.j spservletcookbook; import java.io.File; import j ava.ut i1.Date; import com.oreilly.servlet.multipart.FileRenamePolicy; public class MyFileRenamePolicy implements FileRenamcPolicy { //* соответствии с контрактом интерфейса FiloRenamePolicy // реализуем метод rename(File f) public File rename(File f){ //Получаем путь к родительскому каталогу // например, h:/home/user или /home/user String parentDir f.getParent(); //Получаем имя файла без пути, например 1index.txt* String fname f.getNameO; //Получаем расширение файла, если оно есть String fileExt int i -1; if(( i fname.indexOf(".")) ! -1){ fileExt fname. substring (i); fname * fname. substring CO, i) j f~ } //добавляем временную метку fname fname + (""+( new Date() .getTimeO / 1000)); //собираем имя файла fname parentDir + System. getProperty( "file.separator") + fname + fileExt; File temp » new File(fname); return temp; ' ) } 208 | Глава 8. Выгрузка файлов
Из-за того, что новый класс, реализующий интерфейс FileRenamePolicy, называ- ется com. jspservletcookbook.MyFileRenamePolicy, вызов конструктора объ- екта MultipartRequest выглядит следующим образом: MultipartRequest mpr = new MultipartRequest( request,webTempPath,(5 * 1624 * 1024), new com.jspservletcookbook.MyFileRenamePolicy()); Сохраните новый класс в каталоге WEB-INF/classes своего web-приложения, исполь- зуя структуру каталогов, соответствующую имени пакета класса (например, WEB-INF/ classes/coni/jspservletcookbook/MyFileRenamePolicy. class). Пакет com.oreilly.servlet также включает класс Mui tipartFi Iter. Согласно статье Джейсона (http://www.servlets.com/soapbox/filters.htmr), «MultipartFilter 7$ просматривает входящие запросы, и при обнаружении запроса на выгрузку файлов * (с типом контента multipart/form-data) заключает объект request в специальную ‘обертку’, которая‘знает’как разбирать формат такого типа содержимого». См. также ' Рецепт. 8.1 по подготовке HTML-кода для выгрузки файлов; рецепт 8.2 по использо- ванию библиотеки com.oreilly.servlet; рецепты 8.3 и 8.4 по выгрузке одного и нескольких файлов; рецепт 8.6 по использованию JSP для выгрузки файлов; домашнюю страницу com.oreilly.servlet: http://www.servlets.com/cos/index.html', RFC 1867 по выгрузке файлов с помощью формы: http://www.ietf.org/rfc/rfcl867.txt. 8.6 Использование JSP для обработки выгрузки файлов Задача Требуется для выгрузки файлов использовать JSP. 1 Решение Г Создайте компонент JavaBean, обертывающий функциональность класса cont. oreilly.servlet.MultipartRequest из библиотеки Джейсона Хантера cos.jar. Затем, на странице, с помощью стандартного действия j sp: useBean создайте экзем- пляр этого компонента JavaBean для выгрузки файлов. Использование JSP для обработки выгрузки файлов | 209
Обсуждение В этом рецепте описывается компонент JavaBean, использующий для управления выгрузкой файлов класс com.oreilly.servlet.MultipartRequest. Вначале мы увидим, как создается этот компонент, обертывающий функциональность класса сот. oreilly.servlet.MultipartRequest, а затем рассмотрим страницу JSP, исполь- зующую его для выгрузки файлов. В примере 8.6 показан компонент UploadBean, используемый страницей JSP, приведенной в примере 8.7. Пример 8.6. Компонент JavaBean для выгрузки файлов package com. jspservletcookbook; import java.util.Enumeration; import javax.servlet.http.HttpSetvletRequest; inport j avax.servlet.ServletRequest; r import com.oreilly.servlet.MultipartRequest; import com.oreilly.servlet.multipart.DefaultFileRenamePolicy; z public class UploadBean { private String webTempEath; private HttpServletRequest req; private String dir; public UploadBean() {) public void setDir(String dirName) { if (dirName == null || dirName.equals("")) throw new IllegalArgumentException( "invalid value passed to " + getClassO .getName() + " .setDir"); webTempPath = dirName; } public void setReq(ServletRequest request) { if (request != null && request instanceof HttpServletRequest){ req = (HttpServletRequest) request; } else { throw new IllegalArgumentException( "Invalid value passed to " + getClassO.getName()+".setReq"); J } public String getUploadedFiles() throws,java.io.lOException{ 210 | Глава 8. Выгрузка файлов
//file limit size of 5 MB MultipartRequest mpr » new MultipartRequest( reg, webTempPath, 5 * 1024 * 1024,new DefaultFileRenamePolicyO ) ; Enumeration enum * mpr.getFileNames(); StringBuffer buff = new StringBuffer(""); for (int i = 1; enum.hasMoreElements();i++){ buff.append("The name of uploaded file ").append(i). append(" is: "). append(mpr.getFilesystemName((String)enum. nextElument())). append("<br><br>"); }//for //возвращаем в виде строки return buff.toString(); } // getUploadedFiles Г ' ' Этот код импортирует классы, необходимые, наряду с классом MultipartRequest для выгрузки файлов. Класс DefaultFileRenamePolicy используется конструктором класса MultipartRequest для разрешения конфликтов имен, возникающих при выгрузке файлов с именами, совпадающими с именами уже хранящихся файлов. При возникновении конфликта имен, класс DefaultFileRenamePolicy автоматически добавляет число в конец имени выгружаемого файла, например, имя index.txt будет изменено на indexl.txt. В примере 8.6 для методов компонента JavaBean используется соглашение по имено- ванию JavaBean, что позволяет, например, вызывать методы setDxr () и getUploaded- Fi les () с помощью стандартных действий j sp: set Proper ty и jsp: get Property. В примере 8.7 показано использование этих действий в странице JSP, выполняющей выгрузку файлов и отображающей информацию о выгружаемых файлах. JSP использует д ля выгрузки приведенный выше класс UploadBean. Сначала JSP соз- дает экземпляр компонента JavaBean с помощью стандартного действия j sp: useBean, затем с помощью jsp:setProperty задает имя каталога, куда будут записаны выгружаемые файлы; сохранение файлов в заданном каталоге осуществляется посредст- вом стандартного действия j sp: getProperty. / Пример 8.7. Страница JSP, выгружающая файлы и отображающая информацию о них <jsp:useBean id="uploader" class="com. jspservletcookbook.UploadBean" /> <jsp:setProperty name»"uploader" property="dir" value="<ss=application.getInitParameter(\"save-dir\"/> <jspssetProperty name*"uploader" property*"req" value*"<%* request %>" /> <html> <head>< title»file uploads<71it lex /head» Использование JSP для обработки выгрузки файлов | 211
<body> <h2>Here is information about-the uploaded files</h2> <jsp:getProperty name="uploader" property»"uploadedFilea" /> </body> </html> JSP из примера 8.7 создает экземпляр компонента UploadBean с помощью следующего кода. <jsp:useBean id» "up loader." class="com.jspservletcookbook.UploadBean" /> Класс com. j spservletcookbook. UploadBean должен быть помещен в каталог WEB-INF/classes web-приложения (внутри каталога WEB-INF/classes/convjspservlcf/ jf $ cookbook) или в JAR-файл, в каталог WEB-INFAib. Затем JSP передаёт компоненту JavaBean объект HttpServletRequest. <jsp:setProperty name="uploader" property»"req" value="<%= request %>" /> В JSP по версии спецификации 2.0, значение request можно передать посредством следующего кода. <jsp:setProperty nante="uploader" property» "req" value»"${pageContext.request}" /> Спецификация JSP 2.0 позволяет в атрибуте value элемента jsp: setProperty использовать синтаксис EL. Объект request необходимо передать в конструктор MultipartRequest, который выполняет в компоненте JavaBean основную работу по выгрузке файлов. JSP также передает в компонент JavaBean имя каталога, в котором требуется сохранить выгружаемые файлы. <jsp: setProperty name="uploader" property»'"dir" value»"<%=application.getlnitParameter(\"save-dir\")%>" /> Выражение application. getlnitParameter (\" save-dir\ ") возвращает зна- чение параметра контекста save-dir, которое равно имени каталога для сохранения файла. Вот как выглядит этот элемент файла web.xml. <!— начало дескриптора развертывания —> <context-param> <param-name>Save-dir</param-name> <param-value>h: \home \ da ta< /param-value> </context-param> <!— продолжение дескриптора развертывания '—> 212 | Глава 8. Выгрузка файлов
Заключительный шаг - вызов метода компонента JavaBean getUploadedFiles (). JSP выполняет эту задачу следующим образом. <jsp:getProperty name="uploader" property^"uploadedFiles" /> -- * • JSP вызвает метод компонента JavaBean так, как она обращается к свойствам компонента, для этого я дал методу имя со стандартным префиксом «get»: f Д» getUploadedFiles (). Трюк! Итоговая web-страница, выдаваемая после заполнения и отправки пользователем HTML-формы, приведена на рис. 8.4. Рис. 8.4. JSP, выгружающая файлы Чтобы воспользоваться данной JSP для выгрузки файлов, необходимо в HTML-теге form задать атрибут action следующего вида. <form action="http://localhost:9000/home/upload.jsp" method="post" enctype="mul- tipart/form-data"> См. также Рецепт 8.1 по подготовке HTML-кода для выгрузки файлов; рецепт 8 2 по использо- ванию библиотеки com.oreilly. servlet; рецепты 8.3 и 8.4 по выгрузке одного и нескольких файлов; рецепт 8.5 по переименованию выгружаемых файлов; домашнюю страницу com.oreilly.servlet: http://www.servlets.corn/cos/index.html\ RFC 1867 по выгрузке файлов с помощью формы: http://www.ietf.org/rfc/rfcl867.txt. Использование JSP для обработки выгрузки файлов | 213
ГЛАВА9 Обработка исключений в web-приложениях 9.0 "Введение В некоторых случаях web-притожения выдают сообщения об ошибках, которые хотелось бы скрыть от пользователей. Пользователь обращается к вашему сайту, ожи- дая получить полезную, наполненную информацией страницу, а вместо этою видит в окне браузера непонятное, отталкивающее сообщение «HTTP Status 500», чтр может отбить у него желание посетить ваш сайт еще раз! Все web-сайты обрабатывают нештатные коды состояния HTTP (такие, как «404 Not Found», или «403 Forbidden»), выдавая дружественные и информативные сообщения об ошибках, но у вас может воз- никнуть' желание скрыть эти сообщения от пользователей. В распоряжении разработчиков имеются средства, позволяющие обрабатывать как ошибки времени исполнения Java-кода, так и непредвиденные коды состояния HTTP, и рецепты данной главы показывают, как использовать.их эффективно. 9.1 Объявление обработчиков ошибок в web.xml Задача При возбуждении web-компонентом Java-исключения или генерировании непредвиденного кода возврата сервера вы хотите активизировать определенные сервлеты или JSP. I Решение Для вызова сервлетов или JSP в ответ на определенные исключения или коды состояния HTTP используйте элементы error-page в файле web xml. 214
Обсуждение Java-разработчикам в wetj-приложениях необходимо обрабатывать следующие типы исключительных ситуаций. * / • Код возврата сервера «404 Not Found», говорящий о том, что пользователь совершил ошибку при наборе URL или что запрашиваемая страница более не существует. • Код «500 Internal Server Error», который может возбуждаться сервлетом, при вызове им метода sendError(500) для объекта HttpServletResponse. • Исключения времени исполнения, возбуждаемые web-прилржением и не перехваты- ваемые фильтром, сервлетом или JSP. Настройка обработки исключений и реакции на коды возврата сервера производится с помощью элементов error-page в дескрипторе развертывания. Элементы error- page располагаются в web.xinl после всех элементов servlet, servlet-mapping, session-config, mime-mapping и welcome-file-list, но перед элементами taglib, resource-eny-ref,' resource-ref или security-constraint. Элемент error-page определяет соответствие между кодом состояния, или типом исключения, и путем к web-pecypcy. Таким web-ресурсом может быть сервлет, JSP или HTML-страница, информирующие пользователя о том, что произошло, и предос- тавляющие ссылки к другим частям web-сайта, в зависимости от существа ошибки. В примере 9.1 показан дескриптор развертывания (по версии 2.3 API сервлетов), с настройкой страниц ошибок. Пример 9.1. Настройка страниц ошибок в web.xml <?xml version*"1.0* encoding="ISO-8859-1"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "ht tp: / / j ava. sun. com/dtd/web-application_2_3. dtd" <web-app> <!— верхняя часть дескриптора развертывания с элементами filter, servlet, servlet-mapping, session-config, welcome-file й др,—> <servlet> <servlet-name>Error404</servlet-name> <servlet-class>com.j spservletcookbook.Error404</servlet-class> </servlet> <servlet> <servlet-name>Error403</servlet-name> <servlet-class>com.jspservletcookbook.Error403</servlet-class> </servlet> <servlet> . <servlet-name>ErrorIo</servlet-name> Объявление обработчиков ошибок в web.xml | 215
<servlet-clasfe>com.jspservletcookbook.ErrorIo</servlet-class> </servlet> <servlet> <servlet-namesErrorServlet</servlet-names <servlet-class>com.jspservletcookbook.ErrorServlet</servlet-dlasss </servlets <servlets <servlet-name>ErrorGen</servlet-nartie> <servlet-classscom.jspservletcookbook.ErrorGenc/servlet-classs </servlets <!—отображения сервлетов —> <servlet-mappings <servlet-namesError4 04</servlet-pames <^url-pattern>/err404</url-pattern> </servlet-mappings <servlet-mappings ' <servlet-namesError403</servlet-names <url-patterns/err403</url-patterns <Iservle t-mappings 4 <servlet-mappings <servlet-namesErrorIo</servlet-name> <ur1-patterns/errIo</url-patterns </servlet-mappings f <servlet-mappings <servlet-namesErrorServlet</servlet-names <ur1-patternsI errServ</url-patterns < I servlet-mappings <servlet-mappings <servl e t -namesEr r orGen< / servl e t -names <url-patterns/errGen</url-pattems < I servlet-mappings < •— страницы ошибок, соответствующие кодам ошибок —> <!— Not Found —s <error-pages <error-codes404</error-codes <locations/err404</locations </error-pages <!— Forbidden —> <error-pages <error-codes403</error-codes 216 | Глава 9. Обработка исключений в web-приложениях
<location»/err4G3</location» <I error-page> <!— страницы ошибок, соответствующие типам исключений —> <error-page> <exception-type»javax.servlet.ServletException</exception-type > <location>/errServ</location> </error-page> <terror-page> < except ion-typei* java. io. IOException</exception-type > <location>/errIo</location» </error-page> • <! — все остальные типы —> <error-page> <exception-type>java.lang.Throwable</exception-type > <location>/errGen</location> </error-page» <!— продолжение web.xml; элементы tag-lib, resource-ref, security-constraint, и др. —> </web-app> Когда сервлет возбуждает какое-либо исключение, web-контеййер ищет в web.xml кон- фигурации, использующие элемент exception-type, соответствующий данному типу исключения. В примере 9.1, если web-приложение возбуждает ServletException, то web-контейнер вызовет сервлет /errServ. Web-контейнер всегда вызывает класс, наиболее близкий по иерархии классов. Например, если сервлет возбудит исключение lOExcep- tion, контейнер вызовет сервлет /errlo, который поставлен в соответствие этому типу исключений, а не компонент, поставленный в соответствие java.lang.Throwable, даже несмотря на то что lOException находится на том же уровне иерархии, что и Throwable. Если это приложение возбудит IllegalStateException, контейнер вызовет сервлет /errGen (поставленный в соответствие для Throwable), поскольку не задана страница ошибки, предназначенная именно для IllegalStateException. Если будет возвращен HTTP-код возврата 403 или 404, контейнер вызовет web-ком- поненты или HTML-страницы, поставленные (с помощью элемента location) в соот- ветствие в точности этим кодам. * *. z В соответствии со спецификацией API сервлетов, при возникновении исключения, л котоРое не обрабатывается с помощью механизма error-page, web-контейнер *•* f Л* должен сформировать код возврата 500. Объявление обработчиков ошибок в web.xml | 217
См. также Рецепт 9.2 по созданию обработчика ошибок сервлета; рецепт 9.3 по отправке сервле- том ошибки; рецепт 9:4 по отправке ошибки из JSP; рецепт 9.5 по использованию JSP для обработки ошибок; рецепт 9.6 по объявлению в странице JSP другой страницы JSP, где будут обрабатываться ошибки, возникшие в первой; главу 1 по дескриптору развертывания; спецификацию по сервлетам, где в главе SRV.9.9 описывается обработка ошибок: http://java.sun.com/products/servlet/index.html. 9.2 Создание сервлета, обрабатывающего исключения Задача Требуется создать сервлет, генерирующий страницу с сообщением об ошибке. Решение Создайте сервлет, отображающий некоторую информацию об ошибке, а затем, в дескрипторе развертывания, установите соответствие между типами исключений и/или кодами ошибок и этим сервлетом. Г Обсуждение Сервлет-обработчик ошибок имеет доступ к ряду атрибутов запроса, которые он может использовать для описания ошибки. Страница ошибок также имеет доступ к объ- ектам request и response, связанным, со страницей, вызвавшей ошибку. К примеру, объект java. lang.Throwable, связанный с любыми объектами exception, можно получить следующим образом. .Throwable throwable = (Throwable) » request.getAttribute ("javax.sexrvlet.error.exception"); Код возврата сервера можно получить следующим образом: String status_code = ((Integer) request.getAttribute("j avax.servlet.error.status_code")).toString(); Атрибуты запроса, к которым имеет доступ сервлет, обрабатывающий ошибку, све- дены в табл. 9.1. В примере 9.2 приведен сервлет ErrorGen. Согласно конфигурации из примера 9.1, web-контейнер вызывает этот сервлет,. когда другой сервлет (или JSP) возбуждает необработанное исключение Throwable. I 218 | Глава 9. Обработка исключений в web-приложениях
Табл. 9.1. Атрибуты запроса, доступные сервлету-обработчик. Атрибут запроса Тип Java javax.servlet.error.status_code javaJang.Integer javax.servlet.error.exception_type java.Iang.Class javax.servlet.error.message java.lang.String javax.servlet.error.exception java.lang.Throwable javax. servlet.error.request._uri java.lang.String javax.servlet.error.servlet_name java.lang.String Пример 9.2. Сервлет для обработки ошибок package com.j spservletcookbook; import javax.servle^.*; import javax.servlet.http.*; public class ErrorGen extends HttpServlet { public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.lOException { //проверяем исключение Throwable throwable (Throwable) request.getAttribute("javax.servlet.error.exception"); String servletName “ (String) request.getAttribute("javax.servlot.error.servlet_name"); if (servletName == null) servletName = "Unknown"; String requestUri (String) request.getAttribute("javax.servlet.error.request_uri"); if (requestUri == null) requestUri = "Unknown"; response.setContentType("text/html"); java.io.Printwriter out = response.getWriter(); out.printIn("<html>"); out.printing " <head>"); out.printin("<title>Error page</title>"); out. print In (" < /head> "); out.printIn("<body>"); if (throwable == null){ out.printin("<h2>The error information is not available</h2>"); Создание, сервлета, обрабатывающего исключения | 219
out.printIn("Please return to the <a href=\"" + response.encodeURL("http://localhost:8080/home") + "\">home page</a>."); } else{ out.printIn("<h2>Here is the error information*:/h2>"); ou t. print In ( "The servlet name associated with throwing the exception: "+ servletName + "<br><br>"); out .printIn ("The type of exception: " + throwable.getClass() .getNameO * "<br><br>"); out. print In ("The request URI: " + requestor! + "<brxbr>"); out.printIn("The exception message: " + throwable. getMessageO); } out.printIn("</body>"); out.println("</html>"); z } public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.lOException { doPost(request,response); ) ) Данный сервлет получает ссылку на exception (возникшее исключение) и затем Отображает информацию о нем, например имя класса исключения и сообщение исключения. URI запроса представляет собой частичный путь (например, /home/errGen.jsp) к компоненту, вызвавшему исключение, что очень полезно при отладке и в информацион- ных целях. Внешний вид страницы, выдаваемой при возбуждении исключения сервлетом, выполняющимся в Tomcat, приведен на рис. 9.2. На рис. 9.2 показана страница ошибки, отображаемая сервлетом из рассматривае- мого примера, когда страница JSP из того же web-приложения возбудит исключение j ava. 1 ang. Ari thme t i cExcept i on. См. также Рецепт 9.1 по объявлению обработчиков исключений в дескрипторе развертывания; рецепт 9.3 по отйравке сервлетом ошибки; рецепт 9.4 по отправке ошибки из JSP; рецепт 9.5 по использованию JSP для обработки ошибок; рецепт 9.6 по объявлению в странице JSP другой страницы JSP, где будут обрабатываться ошибки, возникшие в первой; главу 1 по дескриптору развертывания; спецификацию по сервлетам, где в главе SRV.9.9 описывается обработка ошибок: http://java.sun.coni/products/sen4et/ijidex.html. 220 | Глава 9. Обработка исключений в web-приложениях
Рис. 9.1. Страница ошибки, отображаемая сервлетом-обработчиком ошибки 9.3 Возврат из сервлета сообщения об ошибке Задача Вы хотите использовать сервлет для того, чтобы «вручную» сгенерировать ошибку. Решение Используйте метод javax. servlet. HttpServletResponse. sendError (). Обсуждение Класс javax. servlet .HttpServletResponse имеет две версии метода sendEr- ror (): один принимает в качестве параметра целое число (int), представляющее НТТР- код возврата (например, 500), другой принимает два параметра - то же целое чисйо и строку (String) с сообщением об ошибке. Второй параметр используется для отображения кли- енту сообщения в том Случае, когда нет страницы ошибок, сконфигурированной для дан- Возврат из сервлета сообщения об ошибке | 221
5 Error page - Microsoft Internet Explorer .FdfWafdi Address |£] http://localhost:8080/home/errGen.jsp Home file tdit View Favorites look Help 3 Here is the error information The servlet name associated with throwing the exception jsp The type of exception: java.Iang.ArithmeticException В The request URI: /home/errGen.jsp . The exception message: / by zero К Рис. 9.2. Страница ошибки, отображаемая сервлетом из Примера 9.1, когда JSP вызывает ошибку ного кода возврата. В примере 9.3 показан скелетон сервлета, в секциях комментариев которого описаны разные сценарии отправки кода возврата. ---------- Используйте версию метода, принимающую два параметра, в этом случае осмысленное сообщение будет отображено даже в случае, когда в приложении *’ f вч ♦ не сконфигурирована страница ошибки для данного конкретного кода ошибки. Пример 9.3. Отправка кода возврата из сервлета package com.jspservletcookbook; import j avax.servlet.*; import javax.servlet.http.*; public class Sender extends HttpServlet { public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.lOException { 222 | Глава 9. Обработка исключений в web-приложениях
/* если сервлет пытается получить доступ к ресурсу и обнаруживает, что кли- ент не имеет прав на доступ к нему - код "401 unauthorized" */ //response.sendError(401, // "You are not authorized to view the requested component"); /* если сервлет пытается получить доступ к ресурсу, который запрещен для данного клиента и более подробная информация об этом отсутствует - код "403 Forbidden" */ //response.sendError(403, 11 "You are forbidden from, viewing the requested component; no //further information"); /* если сервлет пытается получить доступ к ресурсу по URL, переданному Клим- ентом и не находит его - код "404 Not Found" */ I . sendError (404, //"The server could not find the requested component"); } i public void doGet(HttpServletRequest request,. HttpServletResponse response) throws ServletException, java.io.lOException { doPost(request,response); ) ) Если для кода ошибки, заданного вами в методе sendError (), сконфигурирована страница ошибок, web-контейнер вызывает эту страницу. Если же в web.xml для данного кода не сконфигурирована страница ошибок, web-контейнер генерирует стандартную HTML-страницу, содержащую сообщение, которое вы задали в строковом параметре метода sendError (), см. рис. 9.3. Когда сервер возвращает эту HTML-страницу кли- енту, он оставляет без изменений cookie и другие заголовки ответа. Если вы вызовете sendError О после того, как уже сформировали ответ клиенту (например, когда буфер ответа [память предназначенная для временного хранения ответа] заполнен, и при этом он настроен на автоматическую отправку [«auto- flushed»]), этот метод вызовет исключение java, lahg. IllegalStateException. .Размер буфера ответа можнб установить с помощью метода javax.servlet. ServletResponse.setBuf ferSize(). См. также Рецепт 9.1 по объявлению обработчиков исключений в дескрипторе развертывания; рецепт 9.2 по созданию обработчика ошибок сервлета; рецепт 9.4 по отправке ошибки из JSP; рецепт 9.5 по использованию JSP для обработки ошибок; рецепт 9.6 по объявлению в странице JSP другой страницы JSP, где будут обрабатываться ошибки, возникшие в первой; главу 1 rio дескриптору развертывания; спецификацию по сервлетам, где в главе SRV.9.9 описывается обработка ошибок: http://java.sun.com/products/servlet/index.html. Возврат из сервлета сообщения об ошибке | 223
Рис. 9.3. Ответ, формируемый сервером при вызове HttpServletResponse. sendError, когда для переданного кода ошибки не сконфигурирована страница ошибок 9.4 Возврат сообщения об ошибке из JSP Задача Вы хотите использовать JSP для того, чтобы «вручную» сгенерировать сообщение об ошибке. * Решение Используйте в скриптлете JSP неявный объект response и метод sendError. Обсуждение Если требуется вернуть из JSP сообщение об ошибке, просто получите в скриптлете неявный объект response и вызовите для него метод sendError (). Убедитесь, что вы не вызываете sendError () уже после того, как ответ направлен клиенту, то есть, что этот метод це вызовет исключение java.lang.IllegalStateException. Код JSP, приведенный в примере 9.4, может быть самостоятельной страницей или фрагментом более крупной страницы; при обращении к нему со строкой запроса «?client-unautho- rized=true», он отобразит страницу, показанную на рис. 9.3. 224 | Глава 9. Обработка исключений в web-приложениях
Пример 9.4. Использование неявного объекта response для возврата ошибки из JSP <%@ taglib uri= "http://java.sun.com/jstl/core" pre£ix="c" %> <c:if test="${param.client-unauthorited}" > <% response.sendError(401, "You are not authorized to view the requested component"); </c:if> См. также Рецепт 9.1 по объявлению обработчиков исключений в дескрипторе' развертывания; рецепт 9.2 по созданию обработчика ошибок сервлета; рецепт 9.3 по отправке сервлетом ошибки; рецепт 9.5 по использованию JSP для обработки ошибок; рецепт 9.6 по объявлению в странице JSP другой страницы JSP, где будут обрабатываться ошибки, возникшие i первой; главу 1 по дескриптору развертывания; спецификацию по сервлетам, где в главе SRV.9.9 описывается обработка ошибок: http://java.sun.com/products/servlet/index.html. 9.5 Создание страницы JSP, обрабатывающей ошибки Задача Вы хотите использовать JSP в качестве страницы ошибок Как для сервлетов, так и для JSP. Решение Создайте JSP, которая бы отображала информацию об объекте java. lang. Throw- able, передаваемую в определенных атрибутах запроса (объекта request), например javax. servlet .error. exception. С помощью атрибута error-page в web.xml отобразите на эту станицу JSP требуемое типы исключений. Обсуждение JSP может отображать информацию об ошибке таким же способом, как сервлет из рецепта 9.2. Страница JSP, приведенная в примере 9.5, может быть использована в качестве страницы ошибок как для сервлетов, так и для JSP. Для отображения различных характеристик возникшего исключения, например, полностью квалифицирован- ного имени класса исключения, в ней используются JSTL и EL. В-1545 Создание страницы JSP, обрабатывающей ошибки | 225
Пример 9.5. Использование JSP в качестве страницы ошибок <%@page ieErrorPage«»"true" %> <%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %> <html> <head><title>Sorry about the error</title></head> <body> <h2>Sorry, We Erred Handling Your Request</h2> <strong>Here is information about the error :<7strong> <brxbr> The servlet name associated with throwing the exception; <c:out value«"${requestscope[\njavax.servlet.efror.servlet_name\"]}" /> <brxbr> / The type of exception: <csout valuer **${ request Scope [\и javax. servlet, error. exception\"] .class, name}" /> <brxbr> The request URI: <c:out value«"${requestScope[\,'javax.servlet.error.request_uri\n]}" /> <brxbr> The exception message: <c:out value" "${requestscope[\"javax.servlet.error.exception\"J.message}" /> </body> </html> Данная страница ошибок получает универсальный индикатор ресурса (URI), представляющий собой путь к сервлету, начинающийся с пути к контексту и не вклю- чающий строку запроса, с помощью кода. <с: out value= •• $ { requestScope [ \" j avax. servlet. error. request_uri \" ]}" /> Этот код передает значение атрибута запроса с Именем javax.servlet.error, request_uri JSTL-тегу c:out, в результате чего значение этого атрибута отобража- ется в итоговой HTML-странице. Не пропустите двойные кавычки в фразе на языке EL. *${requestScope[\"javax.servlet.error.request_uri\"]" Приведенный код получает информацию об исключении (exception) из атрибутов запроса (request), которые автоматически создаются web-контейнером когда сервлет или JSP возбуждают исключение. Например, для добавления к выдаваемой информации возвращаемого кода состояния его значение можно получить из атрибута javax. servlet. error. status_code запроса. 226 | Глава 9. Обработка исключений в web-приложениях
Полный список таких атрибутов приведен в табл. 9.1. Та часть примера 9.5, где определяется имя класса исключение использует для этого метод getClass () . getName () объекта Throwable. На рис. 9.4 показано отображе- ние данной страницы ошибок в браузере, после того как JSP с именем errGen.jsp сгенерировала ошибку. Sorry, We Erred Handling Your Request Here is information about the error: f The servlet name associated with throwing the exception: jsp The type of exception: java .io.lOException The request URL /home/errGen jsp "J The exception message: The system cannot find the path specified Puc. 9.4. JSP-страница ошибок отображает информацию о возникшем исключении Для того чтобы задать страницу ощйбок из примера 9.5 в качестве обработчика всех исключений java. io. lOExceptions, необходимо поместить в дескриптор развертыва- ния web.xml следующий элемент. <error-page> <exception-type>java.io.lOExceptionc/exception-type > <location>/errHandler.j sp</location> </error-page> Создание страницы JSP, обрабатывающей ошибки | 227
В примере 9,.6 показана страница JSP, возбуждающая данное исключение. Пример9.6. JSP, возбуждающая исключение java. io. lOException <html> <head><title>Exceptiori Thrower</titlex/head> <body> <h2>Throw an lOExceptxon </h2> <4 java.io.File file = new java.io.File( *zs" + System.getProperty("file.separator") + "ten®"); file.createNewFile();%> </body> </html> • I См. также Рецепт 9.1 по объявлению обработчиков, исключений в дескрипторе развертывания; рецепт 9.2 по созданию обработчика ошибок сервлета; рецепт 9.3 по отправке сервлетом ошибки; рецепт 9.4 по отправке ошибкй из JSP; рецепт 9.5 по использованию JSP для обработки ошибок; рецепт 9.6 по объявлению в странице JSP другой страницы JSP, где будут обрабатываться ошибки, возникшие в первой; главу 23 по использованию JSTL; главу JSP.1.4 спецификации JSP 2.0 по обработке ошибок http://java.sun.com/products/jsp/. 9.6 Объявление специальной JSP, обрабатывающей ошибки, возникающие в других JSP Задача Требуется внутри JSP объявить, что другая, внешняя страница JSP, будет обрабаты- вать любые возникающие в ней ошибки. Решение В директиве раде своей страницы задайте атрибут errorPage и присвойте ему значение - путь к специальной странице web-приложения, обрабатывающей ошибки. В самой обрабатывающей странице JSP, в директиве раде, задайте атрибут isEr- rorPage со значением true. 228 | Глава 9. Обработка исключений в web-приложениях
Обсуждение Спецификация JSP позволяет в верхней части страницы JSP объявлять, что все ошибки, возникающие в ней, должны обрабатываться специальной JSP, предна- значенной для обработки ошибок. Такая конструкция инкапсулирует обработку оши- бок в специально создаваемой странице JSP. Для того чтобы в своей странице объявить страницу-обработчик, необходимо в директиву раде своей страницы поместить атрибут errorPage, значением которого является местоположение в web-приложении страницы-обработчика. В примере 9.7 приведена страница JSP, с директивой раде, объявляющей в качестве страницы, обраба- тывающей ее ошибки, страницу errHandler.jsp. Объявление обработчика в директиве раде перезаписывает любые подходящи^ конфигурации, заданные с помощью элементов error-page в web.xml. Если JSP возбуждает исключение java.io.lOException и в web.xml имеется элемент error-page с атрибутом exception-type, указывающим на это исключение, web-контейнер все равно вызовет страницу, заданную в директиве раде, а не URI, приведенный в web.xml. Пример 9.7. Страница JSP. объявляющая в качестве страницы ошибок другую JSP <%@page errorPage="/errHandler.jsp" %> <html> <head>< t i tle>Exception Thrower</1 i 11 ex/head> <body> <h2>Throw an lOExceptnon </h2> <% java.io.File file n new java.io.File("z:" ♦ System.getProperty("file.eep- aratorn) + "ten©"); file.createNewFile(); %> </body> </html> ' Страница ошибок имеет доступ к неявному объекту exception, представляющему объект j ava. lang. Throwable, связанный с ошибкой. Код из примера 9.8 для показа информации об исключении использует JSTL и EL. Если вы не слишком сильны в использовании JSTL и EL, обратитесь к главе 23. Пример 9.8. JSP для обработки ошибок по имени errHandler.jsp <%@page isErrorPage="true" %> <%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %> <html> <headxtitle>Sorry about the error</titlex/head> <body> Объявление специальной JSP, обрабатывающей ошибки, возникающие в других JSP | 229
<h2>Sorry, We Erred Handling Your Request</h2> <strong>Here is information about the error:</strong> <br><br> The servlet name associated with throwing the exception: <%— JSP 2.0 usage only! <c:out value»"${pageContext.errorData.servletName}" /> —%> <brxbr> The type of exception: <c:cut value* "${pageContext. except ion. class, name}" /> <brxbr> The request URI: <%— JSP 2.0 usage only! <c:out value="${pageContext.errorData.requestURI}" /> —%> <br><br> The exception message: <c:out value»"${pageContext.exception.message}" /> </body> </html> Отображение страницы errHandler.jsp в браузере, после того как JSP из примера 9.7 вызвала исключение java.io.lOException при попытке создать файл на несуще- ствующем диске, приведено ца рис. 9.5. Закомментированные разделы кода из примера 9.8 показывают использование класса javax.servlet. jsp.ErrorData, позволяющего с помощью EL получить дополнительную информацию об ошибке. Можно, к примеру, получить URI (типа /Jtome/errGen.jsp) страницы, вызвавшей исключение, с помощью следующего синтаксиса. ${pageContext.errorData.requestURI} Однако такой синтаксис был введен только в JSP 2.0, и в контейнере, ориентирован- ном на версию 1.2 (например, в Tomcat 4.1.12) он не работает. Именно по этой причине на странице браузера после слов «The request URI:.» ничего нет. Аналот^ичный синтаксис можно использовать и для получения доступа из страницы ошибок к объекту j ava. lang. Throwable. Д.*.‘ <c:out value»"${requestScope[ \"javax.servlet.j sp.j spException\"]}" /> См. также Рецепт 9.1 по объявлению обработчиков исключений в дескрипторе развертывания; рецепт 9.2 по созданию обработчика ошибок сервлета; рецепт 9.3 по отправке сервле- том ошибки; рецепт 9.4 по отправке ошибки из JSP; рецепт 9.5 по использованию JSP для обработки ошибок; главу 23_по использованию JSTL; главу JSP.1.4 спецификации JSP 2.0 по обработке ошибок http://java.sun.com/products/jsp/. 230 | Глава 9. Обработка исключений в web-приложениях
Рис. 9.5. JSP-страница ошибок, использующая атрибуты директивы page: errorPage иisErrorPage Объявление специальной JSP, обрабатывающей ошибки, возникающие в других JSP | 231
_______________ГЛАВА 10 Чтение и запись Cookie 10.0 Введение f Во время типичного посещения web-сййта пользователь направляет к web- серверу множество запросов на ресурсы. Если web-страница содержит много изображений (а чаще всего это так!), то обращение к одной единственной web- странице включает один запрос HTML-кода и прочего шаблонного текста (например, заголовков и фраз), за которым следуют отдельные запросы каждого из изображений, содержащихся на странице. Последующие обращения к той же самой странице, для повышения эффективности, зачастую возвращают версии этого текста и изображений, сохраненные в кеше на компьютере клиента, но это зависит от того, допускают ли данные ресурсы кэширование. В любом случае, сервер рассматривает запросы к таким web-ресурсам, как отдельные и не связан- ные с остальными запросами. Без использования дополнительных протоколов у сервера нет механизма управления состоянием клиента, например, тем, в какой стадии находится заполнение анкеты или просмотр виртуальной витрины. Cookie как раз и предназначены для обеспечения логической связи между несколькими запросами, объединения их в один сеанс работы с пользователем. Cookie - небольшой кусочек информации, располагающийся на компьютере пользователя, который web-сервер может использовать для идентификации этого пользователя при следующем посещении им сайта. При первом посещении сайта, поддерживающего работу с cookie, сервер включает в ответ на запрос дополнитель- ный заголовок, который выглядит примерно следующим образом: Set-Cookie: mycookie=1051565332678; Domain=. myorg.com; Expires=Tue, 29-Apr-2003 07:42:12 GMT Далее, при повторном посещении сайта этим же пользователем, его браузер посылает в составе запроса дополнительный заголовок, содержащий cookie, установ- ленный данным сайтом. Вот как выглядят заголовки запроса, посылаемого, когда клиент повторно посещает сайт, установивший у него cookie mycookie’, если исполь- зуемый контейнер сервлетов - Tomcat 4.1.12, то заголовок запроса Cookie, кроме того, содержит пару имя/значение cookie, связанного с сеансом (JSESSIONID) 232
GET /home/cookie HTTP/1.1 Accept, image/gif, image/х-xbitmap, image/jpeg, image/pjpeg, application/msword, application/vnd.ms-^iowerpoint, application/vnd.ms-excel, application/pdf, */* Accept-Language: en-us Accept-Encoding: gzip, deflates User-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 4.0) Host: localhost:9000 ' Connection: Keep-Alive Cookie: JSESSIONID»F80F0F571FDE4873CFF3FF0B842D4938; mycookie-1051610231064 Любой cookie имеет имя и значение, он также может включать ряд дополнительных пар атрибут/значение, которые отделяются друг от друга точкрй с запятой. Domain Определяет домен, которому данный cookie будет посылаться при последующих запросах. Форма записи: Domain=. jspservletcookbook. com. По умолчанию значение данного необязательного атрибута равно имени хоста того домена, который посылал заголовок Set-Cookie. ' Path Более подробно уточняет часть web-сайта, при обращении к которой, будет послан cookie от клиента. В большинстве cookie значение этого атрибута равно /. Если же, к примеру, cookie должен передаваться только на путь к контексту customer, то заго- ловок Set-Cookie будет включать такую пару атрибут значение: path= /customer. Клиент не будет посылать значения cookie при любых запросах к домену, не включающему путь к контексту /customer. Expires Задает максимальный промежуток времени, в течении которого браузер пользова- теля должен хранить данный cookie. Это атрибут является строкой с некоторой буду- щей датой. Если Expires содержит прошедшую дату, cookie у халяется. Для управления значением Этого атрибута Java Cookie API включает метод setMax- Age () объекта Cookie (см. рецепт 10.1). Version Необязательный атрибут. Содержит 0 для предварительной спецификации Netscape, и 1 для документа RFC 2109, Secure Содержит true, если данный cookie можно постлать только по защищенному соеди- нению, например посредством HTTPS. Введение | 233
Comment f Может содержать описание назначения данного cookie. ж*, ' Согласно документации javax.servlet.http.Cookie API, браузер должен поддерживать не менее 20 cookie для каждого web-сервера, 300 cookie всего; . 4 7k».* размер каждого cookie не должен превышать 4 Кб. Согласно предварительной спецификации Netscape, ограничение в 4 Кб устанавливается на комбинацию имя/ значение .cookie. На практике, cookie обычно меньше 4 Кб. Пользователь имеет возможность запретить браузеру принимать cookie, в результате его браузер не будет сохранять cookie, посылаемые в ответе web-ссрвера на обращение. Например, в Netscape 7.1 комбинация пунктов меню Edit -* Preferences -> Privacy & Secu- rity Cookies открывает окно, в котором, установив переключатель «Disable cookies» вы можете отказаться от приема cookie. В таком случае для работы со всеми клиентами, отказывающимися от приема cookie, web-разработчик, использует метод «перезапись URL» (см. рецепты 11.7 и 11.8). В API сервлетов cookie представлен объектом типа j avax. servlet. http. Cookie. Рецепты данной главы демонстрируют, как создаются новые cookie и как читаются или изменяются значения существующих cookie, в сервлетах и в JSP. 10.1 Установка cookie из сервлета Задача Требуется установить cookie из сервлета. Решение • Создайте в сервлете объект j avax. servlet. http. Cookie, а затем с помощью мето- да javax.servlet.http.HttpServletResponse.addCookie(Cookie cookie) установите этот cookie на машине пользователя. Обсуждение Создайте в сервлете cookie, породив новый экземпляр класса Cookie и затем вызвав методы-установщики (setter) его свойств. Конструктор Cookie принимает в качестве параметров имя и значение cookie. Cookie cookie = new Cookie("mycookie","thelcookie"); примере 10.1 создается cookie, и его атрибуту path присваивается' значение (cookie.setPath(String path)) равное пути к контексту (например, /home). В результате, клиент не будет посылать данный cookie серверу, за исключением случаев 234 | Глава 10. Чтение и запись Cookie
обращения клиента с запросом к ресурсам, находящимся в пределах заданного пути к кон- тексту. Значение, помещаемое в атрибут path, сервлет получает с помощью метода HttpServletRequest.getContextPath(). Пример 10.1. Сервлет, устанавливающий cookie и отображающий некоторую информацию из cookie package com.j spservletcookbook; import javax.servlet.*; import j avax.servlet.http.*; public class CookieServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.lOException { - Cookie cookie = null; \ //Получаем массив Cookies связанных с данным доменом Cookie[] cookies’» request.getCookies()f I boolean newCookie = false; //Получаем Cookie по имени 'mycookie’, если существует if (cookies != null){ for (int i = 0; i < cookies.length; i++)< if (cookies [i].getName().equals ("xnycookie")) { cookie = cookies[i]; } }//end for }//end if if (cookie == null){ newCookie=true; //Значение Max-Age берем из элемента context-param //если значение параметра •'cookie-age* задано некорректно, //задаем для cookie значение по умолчанию -1, 'хранить вечно* int maxAge; try{ . хпuxAge new lnteger( getServletContext (). getlpitParameter ( * "cookie-uge")).intValue() ; } catch (Exception e) { maxAge -1; //Создаем объект Cookie cookie = new Cookie ("xnycookie"," "+getNextCookieValue ()); cookie.setPath(request.getContextPath()); Установка cookie из сервлета | 235
cookie. setMaxAge (maxAge); response.addCookie(cookie); }//end if // получаем некоторую информацию о cookie response.setContentType("text/html"); java.io.Printwriter out = response.getWriter(); out.printin("<html>"); out.printIn("<head>"); out.printin("<titie>Cookie info</title>"); out .printin ("</head>") ; out.printIn("<body>"); , out.printlnf "<h2> Information about the cookie named \nmycookie\"</h2>"); out.printlnf"Cookie value: "+cookie.getValue()+"<br>"); £f (newCookie){ out.printlnf"Cookie Max-Age: "+cookie.getMaxAge()+"<br>"); out.printlnf"Cookie Path: "+cookie.getPath()+"<br>"); } out.printin("</body>"); out.printin("</html>"); ) private long getNextCbokieValue(){ //Возвращаем Кол-во милисекунд прошедших с 1 янв.1970г. return new java.util.Date().getTimef); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.lOException { doGet(request,response); } . } I В примере 10.1 для задания времени, когда cookie может бьпъ удален браузером, используется метод Cookie. setMaxAge (int age), Параметр этого метода задает мак- симальное количество секунд, которое cookie может провести на машине клиента, после того, как' он был создан. В коде примера это значение берется из элемента context- par ат дескриптора развертывания web.xml, что позволяет разработчику изменять это значение, внося изменения только в дескриптор развёртывания. Ниже приведен пример элемента context-param, содержащего значение продолжительности хранения cookie. <context-param> 236 | Глава 10. Чтение и запись Cookie
<param-name>cookie-age</param-name> <param-value>315360 0 0</param-value> </context-param> К примеру, если вы хотите, чтобы cookie задержался на один год (365x24x60x60 Секунд), то можете использовать следующий код. cookie.setMaxAge(31536000); ) Пользователь может удалить cookie со своей машины не обращая внимания на срок хранения, установленный вами. Ряд браузеров предоставляют окно, отображающее все установленные на компьютере cookie, и позволяют пользователю удалить один или несколько из них. Поэтому не стройте планов, основанных на том, что cookie будет находиться на машине пользователя на протяжении всего срока хранения В примере 10.1 также проверяется существование cookie с тем же именем, которое планируется присвоить вновь создаваемому cookie (mypookie). Если пользователь не прислал cookie с именем mycookie, то сервлет устанавливает новый cookie с этим име- нем, а впоследствии отображает некоторые из значений этого cookie. Выходная информация сервлета показана на рис. 10.1. Information about the cookie named "mycookie" Cookie value: 1051737069432 Я Cookie Max-Age: 31536000 № Cookie Path /home к Л. _ _______ --- .. ’11 л • Л»--.!™.®* \ л. Н- I ».», SWje six г Ji w Рис. 10.1. Сервлет отображает информацию о новом cookie В данном случае значение cookie произвольно устанавливается равным строке, содержащей некоторое большое число, чтобы продемонстрировать, как задается значение cookie. Поскольку многим cookie требуются уникальные значения, вы можете использо- вать метод создания уникальных значений, отталкивающийся от адреса электронной почты пользователя, или уникального идентификатора базы данных. Установка cookie из сервлета | 237
См. также Рецепт 10.3 по установке cookie из JSP; рецепт 10.4 по использованию сервлета для чтения cookie; рецепт 10.5 по чтению значения cookie из JSP; рецепт 10.6 по изме- нению значения или удалению существующего cookie; документ RFC 2109, посвященный cookie ftp://ftp.rfc-editor.org/in-notes/rfc2109.txt:, предварительную спецификацию Netscape по cookies http://wp.netscape.com/newsref/std/cookie_spec.html\ Java Cookie API http://jci.va. sun.com/j2ee/1.4/docs/opi/javax/servlet/http/Cookie.html. 10.2 Создание массива всех cookie, переданных в запросе Задача > Требуется сохранить все cookie, содержащиеся в запросе пользователя в массиве объек- , тов Cookie. Решение Воспользуйтесь методом HttpServletRequest. getCookies (), возвращающим массив объектов javax. servlet. http. Cookie. Обсуждение Для создания массива объектов Cookie, представляющих все cookie, включенные в запрос, используйте метод HttpServletRequest .getCookies (). Добраться до имен и значений cookie можно с помощью методов getName () и getValue () класса Cookie. Код получения массива Cookie приведен в примере 10.2. Пример 10.2. Создание массива объектов класса Cookie //метод doGet сервлета public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.lOException { Cookie cookie = null; //Получаем массив Cookie, связанных с этим доменом Cookie[] cookies = request.getCookies(); //Проверяем на null, затем что-либо делаем с Cookie if (cookies !.= null){ //читаем значение каждого Cookie } //остальной коД сервлета 238 | Глава 10. Чтение и запись Cookie
После того как cookie был создан, при следующем посещении, пользователь посы- лает cookie в составе заголовка запроса, и единственная информация, которую вы можете извлечь тогда из объекта Cookie - это его имя и значение. Вы не получите из заголовка запроса время хранения, поскольку все что содержит заголовок - это имя заголовка и пару имя/значение cookie, которая и попадает в объект Cookie. См. также Рецепт 10.1 по установке cookie из сервлета; рецепт 10.3 rfo установке cookie из JSP; рецепт 10.4 по использованию сервлета для. чтения cookie; рецепт 10.5 по чтению значения cookie из JSP; рецепт 10.6 по. изменению значения или удалению суще- ствующего cookie; документ RFC 2109, посвященный cookie ftp://ftp.rfc-editor.org/in- notes/rfc2109.txt; предварительную спецификацию Netscapejio cookies http.7/wp.netscape. com/newsref/std/cookie_spec.html‘, Java Cookie API http://java.sun.eom/j2ee/l.4/docs/api/ javax/servlet/http/Cookie.html. 10.3 Установка cookie из JSP I . ; Задача Требуется установить cookie у клиента с помощью JSP. Решение Создайте компонент JavaBean, «обертывающий» API сервлетов для создания cookie. Обсуждение Для создания cookie и установки его у клиента можно использовать компонент Java- Bean. В примере 10.3 с помощью стандартного действия j sp,: useBean создается экзем- >1ляр компонента JavaBean, имеющего тип com. jspservletcookbook.CookieBean. Затем- JSP задает ряд свойств этого компонента JavaBean. Компонент JavaBean передает значения этих свойств в cookie, который он генерирует для JSP. JSP задает с помощью j sp: setProperty значения следующих свойств cookie. • Имя cookie (B данном случае - bakedcookie). • Наибольшее количество секунд, а течение которых браузер будет хранить cookie (в примере 10.2 - примерно год). Для атрибута cookie Expires это значение преобразуется в удобочитаемую дату (конечный срок). • Путь на сервере, связанный с данным cookie. После того как JSP пошлет cookie кли- енту, клиент при последующих запросах будет возвращать cookie в заголовке только тех запросов, которые содержат указанный путь к контексту (например, /home). Установка cookie из JSP | 239
К примеру, если JSP находится в файле /home/cookieSet.jsp, то только запросы к ресурсам из каталога /home будут включать заголовок Cookie. Пример 10.3.. JSP, посылающая клиенту cookie <jsp:useBean id«"cookieBean" Gjlu3S«"ccm. jspservletcookbook.CookieBean" /> <jsp:setProperty nama*”cookieBean” property""шипе" value-» "bakedcookie" /> <%-- устанавливаем атрибут 'Expires* примерно на год от текущего момента --%> <jsp:setProperty name*"cook!eBean" property*"maxAge" value* "<4* (365*24*60*60) *>" /> <jsp: setProperty name*”cookieBean” property* "path" vt.luer»"<%* regues t. get Con- text Path () y>" /> <jsp: setProperty name*"cookieBean" property* "cookieHeader" valuer"<k* response %> /> ' <html> \ <head><title>Cookie Maker</titlex/head>. <body> <h2>Here is information about the new -cookie</h2> Maine: < jsp: get Property name*"cookieBean" property* "name” /><br> Value: <jsp:getProperty name*"cookieBean" property*"value" /><br> Path: < jsp: get Property name*"cookieBean" property* "path" 7> </body> </html> Данная страница JSP передает объект HttpServletResponse компоненту Java- Bean, чтобы тот смог вызвать метод response.addCookie(Cookie cookie) ‘ для отправки нового cookie клиенту. Объект response передается компоненту Java- Bean с помощью следующего кода (см. метод setCookieHeader () из примера 10.4). <jsp:setProperty name*"cookieBean" property*"cookieHeader" value* "<%= response %>" /> В нижней части страницы JSP отображаются некоторые из значений cookie. Вид дан- ной страницы в браузере приведен на рис. 10.2. Повторные запросы к этой странице JSP приведут к перезаписи существующего cookie новым cookie. Код самого компонента JavaBean приведен в примере 10.4, который получился сравни- тельно длинным из-за всех его методов-установщиков (setter) и получателей (setter). Данный класс JavaBean необходимо поместить в каталог WEB-INF/classes web- приложения (включая структуру каталогов, отвечающую имени пакета компонента JavaBean), чтобы web-контейнер смог загрузить этот класс. Компонент JavaBean также можно заархивировать в JAR-файл и поместить в каталог WEB-INFAib\ однако этоГ JAR-файл все равно должен содержать структуру каталогов, соответствующую имени пакета данного компонента. 240 | Глава 10. Чтение и запись Cookie
Рис. 10.2. JSP отображает информацию о вновь созванном cookie Значение созданного cookie можно задать в JSP (в примере 10.3 это не сделано), для этого нужно вызвать метод setvalue (String value) компонента JavaBean с помощью j sp: setProperty. f <jsp:setProperty name="cookieBean" property="value" value="newvalue" /> • Компонент JavaBean импортирует классы Cookie и HttpServletResponse, поскольку он использует их для создания нового cookie и отправки его клиенту. Код из примера 10.4 обертывает методы класса Cookie в собственные методы, например setvalue () и setMaxAge (). Пример 10.4. Компонент JavaBean для создания cookie package com.jspservletcookbook; import j avax. servlet. http. HttpServletResponse ; x import javax.servlet.http.Cookie; public class Cooki.eBean { л private (Rookie cookie null; public CookieBean(){} //Установка имени cookie public void setName( String name){ if (name null || (name.equals(""))) throw new IllegalArgumentException( "Invalid cookie name set in: M+getClass().getNameO); Установка cookie из JSP | 241
cookie a new Cookie(name,""+new java.util.Date().getlime ()); } //Установка значения cookie public void setValue(String value){ if (value яв null || (value.e<juals(""))) throw new IllegalArguinentExcept ion ( "Invalid cookie value set in* "+getClass() .getNameO ) ; if (cookie ! null) cookie.setValue(value); > public void setMaxAge(int maxAge){ if (cookie ! null) cookie.setMaxAge(maxAge); public void setPath(String path){ if (path null || (path.equals(""))) throw new XllegalArguxnentException( "Invalid cookie path set in: n+getClass().getName()); if (cookie ! null) cookie.setPath(path); public void setCookieHeader(HttpServletResponse response) { if (response «« null ) throw new IllegalArguinentException ( "Invalid HttpServletResponse set in: n+getClass() .getNameO); if (cookie 1» null) response.addCookie(cookie); } public String getNameO { if (cookie != null) return cookie.getName(); else return "unavailable“; } public String getValue(){ if (cookie != null) return cookie.getValue(); else 242 | Глава 10. Чтение и запись Cookie
return "unavailable"; } public String getPathf){ if (cookie !- null) return cookie.getPath(); else return "unavailable"; } } Если JSP потерпит неудачу при использовании jsp:setProperty для вызова метода setCookieHeader (HttpServletResponse response), то cookie будет создан, но не будет включен в заголовки ответа, посылаемого клиенту. В таком варианте пользователь компонента может установить некоторые дополнительные параметры cookie, перед тем как реально отправить cookie в составе ответа клиенту. См. также Рецепт 10’1 по установке cookie из сервлета; рецепт 10.2 по созданию массива всех cookie, переданных в запросе; рецепт 10.4 по использованию сервлета для чтения cookie; рецепт 10.5 по чтению значения cookie из JSP; рецепт 10.6 по изменению значения или удалению существующего cookie; документ RFC 2109, посвященный cookie ftp://ftp.ifc- editor.org/in-notes/rfc2109.txt, предварительную спецификацию Netscape по cookies http:// wp.netscape.com/newsref/std/cookie_spec.html\ Java Cookie API http://java.sun.eom/j2ee/l.4/ docs/api/javax/servlet/http/Cookie.html. 10.4 Чтение значения cookie в сервлете Задача Требуется в сервлете прочитать значение cookie, полученного от клиента. Решение С помощью метода HttpServletRequest .getCookies () создайте массив объ- ектов javax. servlet .http.Cookie. Затем, перебирая в цикле все cookie из мас- сива, выбирайте значения интересующих вас cookie. Обсуждение Web-пользователь будет посылать cookie на конкретный web-сайт только в том случае, если он (пользователь) в свое время получил заголовок Set-Cookie именно из этого домена. Кроме тогй, если cookie был установлен с атрибутом path, задающим некоторый Чтение значения cookie в сервлете | 243
путь к контексту, то сервлет сможет получить этот cookie, только если сам сервлет также связан с данным путем к контексту. Вывод - всегда проверяйте возвращаемое методом request.getCookies () значение (массив объектов Cookie) на равенство null, что свидетельствует 9 том, что пользователь не выслал ни одного cookie, прежде чем начнете производить над возвращаемым значением какие-либо действия. Сервлет из примера 10.5 отображает в браузере значения всех принятых cookie. Для получения этой информации класс CookieReader использует методы j avax. serv- let .http.Cookie. getName () и getValue (). Пример 10.5. Сервлет, читающий cookie package com.jspservletcookbook; , import javax.servlet.*; / import javax.servlet.http.*; public class CookieReader extends HttpServlet { public void doGet (HttpServletRequest request., HttpServletResponse response) throws ServletException, java.io.lOException { Cookie cookie = null; //Получаем массив Cookie, связанных с этим доменом Cookie[] cookies request.getCookies(); boolean hasCookies = false; //если массив не пуст, //отображаем информации) о находящихся в нем cookie. if (cookies ! null) hasCookies true; .// Отображаем имя/значенир каждого cookie response.setContentType("text/html"); java.io.Printwriter out = response.getWriter(); out.printIn("<html>"); out.printIn("<head>"); out.printin( "<title>Cookie information</title>");' out.printIn("</head>"); out.printIn("<body>"); if (hasCookies){ out.printIn( "<h2> The name and value of'each found cookie</h2>"); 'for (int i = 0; i < cookies.length; i++){ cookie = cookies[i]; out.printIn( "Name of cookie #"+(i + l)+"s "+cookie.getName()+"<br>"); 244 I Глава 10. Чтение и запись Cookie
out.printIn( " r "Value e£ cookie #"+(i + l)+*s "+ cookie. getValue ()+" <brxbr> "); }//for } else { out.println( "<h2> This request did not include any cookies</h2>"); } out.printIn("</body>"); out.println("</html^");} public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, j ava.io.lOException { doGet(request,response); } } Класс javax. servlet .http.Cookie представляет собой абстракцию реального cookie, у него есть методы установщики (setter) и получатели (getter), позволяющие задать й получить значения атрибутов cookie, например имя, значение, путь и атрибут безопасности. Однако при получении cookie обратно от клиента, вы можете извлечь только имя и значение, поскольку только эта информация включается в заголовок запроса на стороне клиента. Заголовок запроса Cookie выглядит следующим образом: Cookie: JSESSIONID=F80F0F571FDE4873CFF3FF0B842D4938; mycookie=1051610231064 Поэтому, например, вызов метода Cookie.getPath() для cookie, полученного от клиента, возвратит null, даже если для данного cookie в свое время был задан атрибут path (например, /mypath}. Эти значения в Сервлете Или JSP можно получить лишь для созданных здесь же, мгновением раньше, cookie (см. рецепты 10.1 и 10.3). На рис. 10.3 показано отображение в 'браузере информации, выводимой данным сервлетом. См. также Рецепт 10.1 по установке cookie из сервлета; рецепт 10.2 по созданию массива всех cookie, переданных в запросе; рецепт 10.3 по установке cookie из JSP; рецепт 10;5 по чтению значения cookie из JSP; рецепт 10.6 по изменению значения или удалению существующего cookie; документ RFC 2109, посвященный cookie ftp://ftp.rfc-editor.org/ in-notes/rfc2109.txt, предварительную спецификацию Netscape по cookies http://wp. netscape;com/newsref/std/cookiejspec.html\ Java Cookie API http://java.sun.com/j2ee/L4/ docs/api/javOx/servlet/hltp/Cookie.html. Чтение значений cookie в сервлете | 245
иЧ, gWtaxFS ^Wdc^loS|»W. В] •«« >.-= » The name and value of each found cookie f Name of cookie #1: bakedcookie ’ ® Value of cookie #1:1051637247029 g Name of cookie #2: mynewcookie . f Value of cookie #2'1051641964712 f Name of cookie #3: acookie q» Value of cookie #3: A1 new i Puc. 10.3. Сервлет отображает информацию о cookie 10.5 Чтение значения cookie в JSP Задача Требуется в JSP получить значение cookie. Решение Для отображения имен и значений всех cookie, найденных в запросе, воспользуйтесь библиотекой JSTL и ее неявным объектом cookie. Обсуждение Библиотека JSTL и ее язык EL имеют неявно создаваемый объект cookie (переменную, которая автоматически доступна для JSP или EL кода), который вы можете использовать в JSP для отображения имен и значений всех cookie. Более подробную информацию о JSTL и EL можно получить в главе 23. 246 | Глава 10. Чтение и запись Cookie
Доступ к неявно создаваемому объекту cookie можно получить следующим образом. ${cookie} Этот неявно создаваемый объект приводится к тип^ java.util.Мар, чьи значения можно перебирать с помощью JSTL-тега c:forEach. Каждая итерация c:forEach возвращает объект java.util .Map. Entry, который инкапсулирует пары ключ/значение. В данном случае ключ представляет имя cookie, а значение - объект javax.servlet , http. Cookie. В примере 10.6 для извлечения очередного объекта Cookie из карты (Мар> доступ- ных cookie используется следующий код. <c:forEach var="cookieVal" items»*${cookie}"» Атрибут var элемента с: forEach содержит объект Map. Entry, ключ которого представляет имя cookie, а значение - объект Cookie. Для отображения в JSP имен и значений cookie используется тег с:out. Приведенный ниже сдвоенный синтаксис ' отображает значение каждого cookie. <c:out value="${cookieVal.value.value)" /> Код cookieVal.value вычисляет объект javax.servlet.http.Cookie. А полная фраза $ { cookieVal. value. value} эквивалентна вызову метода Cookie. getValue ().. Пример 10.6. Страница JSP, читающая имена и значения cookie <ЭД taglib uri»"http://java.sun.com/jstl/core" prefix»"c" %> <html> <body> • Проверяем, содержит ли запрос Cookie —%> <с:choose» I I <c:when test»"${empty cookie}" > <h2»We did not find any cookies in the request</h2» </с:when» <c:otherwise» <h2»The name and value of each found cookie</h2» .. <c:forEach var» "cookieVal" items-:" $ {cookie}"» <strong»Cookie name:</strong» <c:6ut value»"${cookieVal.key}" /xbr» <strong»Cookie value:</strong» <C:out value» "${cookieVal.value.value}" /»<br»<br» </сsforEach» </с:otherwise» </c:choose» </body> </html> Отображение страницы JSP ^информацией о доступных cookie показано на рис. 10.4. Чтение значения cookie в JSP | 247 1
Рис. 10.4. Выходная информация страницы cookieReader.jsp Не забудьте в верхнюю часть страницы включить директиву taglib для библиотеки ядра JSTL, чтобы иметь возможность использовать JSTL-теги для просмотра значений cookie. <%@ taglib uri= "http://java.sun.com/jstl/core" prefix="c" %> При использовании JSTL версии 1.1 задайте uri=http://java.sun.com/ jsp/jstl/core См. также Рецепт 10.1 по установке cookie из сервлета; рецепт 10.2 по созданию массива всех cookie, переданных в запросе; рецепт 10.3 по установке cookie из JSP; рецепт 10.4 по использованию сервлета для чтения cookie; рецепт 10.6 по изменению значения или удалению существующего cookie; документ RFC 2109, посвященный cookie ftp://jtp rfc- editor.org/in-notes/rfc2109.txt\ предварительную спецификацию Netscape по cookies http:// wp.netscape.com/newsref/std/cookie_spec.html-, Java Cookie API http://java.sun.eom/j2ee/l.4/ docs/api/javax/servlet/http/Cookie.html. 248 | Глава 10. Чтение и запись^СооИе
10.6 Изменение значения или удаление существующего cookie Задача Вы хотите изменить или удалить существующий cookie. Решение Чтобы перезаписать существующий cookie, необходимо послать клиенту cookie с тем же именем и путем (Path). Обсуждение Вы можете перезаписать cookie и, возможно, изменить значение его атрибутов (например, значение cookie). Для этого нужно включить в заголовок ответа cookie, имеющий то же имя и путь, что и изменяемый cookie. Допустим, сервлет в свое время установил у клиента cookie посредством заголовка ответа. Set-Cookie: newcookie=lt)51642031398; Expires=Wed, 28-Apr-2004 18:47:11 GMT; Path»/home Тогда этот cookie можно перезаписать у клиента, изменив его значение, но не имя и путь. Set-Cookie': newcookie=Allnew; Expires=Wed, 28-Apr-2004 18:52:50 GMT; Path=/home Данный заголовок ответа заменит newcookie на cookie с этим же именем. Новая версия имеет другое значение (Allnew), а также новое значение атрибута Expires. Удаление cookie Чтобы удалить cookie, требуется послать клиенту заголовок ответа, содержащий такое же имя cookie, такое же значение пути (Path), но со значением атрибута Expires, равным уже прошедшей дате. Используя Java Cookie API, просто вызовите метод javax.serv- let. http.Cookie.setMaxAgeO co-значением аргумента равным 0. В примере 10.7 приведена страница JSP из рецепта 10.2. На этот раз JSP удаляет mycookie, устанавливая с помощью j sp: setProperty значение свойства maxAge в 0. Пример 10.7. Удаление существующего cookie <jsp:useBean id="cookieBean" class»"com.jspservletcookbook.CookieBean" /> <jsp:setProperty name="cookieBean" property-""name" value»"mycookie” /> <%— удаляем cookie, вызывая Cookie.setMaxAge(O) —%> <jsp:setProperty name»"cookieBean” property»"maxAge" value»”0" /> <jspssetProperty name="cookieBean" property»"value" value»"finished" /> Изменение значения или удаление существующего cookie | 249
<jsp:SetProperty name="cookieBean" £roperty="path" value= "<%= request.getContextPath() %>" /> <jsp:setProperty name="cookieBean" property^ 'cookieHeader" value= "<%= response %>" /> <%— продолжение JSP —%> Cookie может быть удален только с помощью отправки заголовка ответа, исходя- щего из того же домена, который создал этот cookie, с тем же именем cookie и с таким же значением атрибута Path. Вот как выглядит заголовок ответа клиенту от JSP, удаляющей cookie, НТТР/1.1 200 ОК Content-Type: text/html;charset=ISO-8859-fl Set-Cookie: mycookieefinished; ExpireseThu, 01-Jan-1970 00:00:10 GMT; Path=/home Transfer-Encoding: chunked Date: Tue, 29 Apr 2003 19:18:59 GMT ” Server: Apache Coyote/1.0 Обратите внимание, что значение атрибута Expires равно прошедшей дате. В результате клиент больше не будет посылать cookie mycookie в заголовках запросов, Обращенных к этому домену и пути к контексту /home. Однако, естественно, он будет продолжать посылать туда другие cookie, если токовые были установлены во время предыдущих посещений этого домена и этого пути к контексту. В любом случае исходите из того, что пользователь может удалить cookie в любое время, когда захочет. См. также Рецепт 10; 1 по установке cookie из сервлета; рецепт 10.2 по созданию массива всех cookie, переданных в запросе; рецепт 10.3 по установке cookie, из JSP; рецепт 10.4 по использованию сервлета для чтения cookie; рецепт 10.5 по чтению значения cookie из JSP; документ RFC 2109, посвященный cookie ftp://ftp.Tfc-editor.orgfin-notes/rfc2109.txt\ Предварительную спецификацию Netscape по cooMes http://wp.netscape.com/newsref/std/ cookie_spec.html', Java Cookie API http://java.sun.eom/j2ee/l.4/docs/api/javax/servlet/http/ Cpokie.html. i 250 | Глава 10. Чтение и запись Cookie . -
ГЛАВА И Отслеживание сеанса 11.0 Введение В этой главе описано, как в сервлетах и JSP следить за ходом сеанса. Сеансом называют процесс взаимодействия, протекающий между web-пользователем и web- приложением. Протокол HTTP является протоколом без состояний, это означает, что он не рассчитан на работу с состояниями, то есть стадиями прохождения взаимодей- ствия отдельного пользователя с web-сервером путем обмена HTTP запросами и отве- тами. Каждый конкретный запрос к сервлету или JSP, по крайней мере с точки зрения web-сервера, рассматривается в отрыве от всех остальных запросов и никак не связывается с тем же самым пользователем. Однако многим web-приложениям требуется шаг за шагом отслеживать весь ход взаимодействия пользователя с прило- жением, чтобы быть в курсе сделанных ею покупок и/или ее предпочтений. К примеру, когда пользователь покупает книгу на сайте Amazon.com, сайт отсле- живает, что он добавил или удалил в покупательскую корзину и использует эту информацию в процессе оформления покупки и платежа. Кроме того, Amazon.com показывает пользователю, какие книги он смотрел в ходе текущего сеанса. На подоб- ных сайтах электронной торговли последовательные посещения конкретного пользо- вателя считаются частью одного сеанса. Для реализации сеанса web-приложения обычно используют cookie. Все кон- тейнеры сервлетов поддерживают использование cookie для отслеживания сеансов, в соответствии со спецификацией сервлета версий 2.3 и 2.4. Cookie - это небольшой кусочек информации, сохраняемый браузером клиента, когда заголовок ответа от web- сервера включает соответствующую информацию. Cookie детально обсуждались в главе 10, однако поскольку они являются ключевым звеном в концепции сеанса, я приведу здесь краткое описание применения cookie для отслеживания хода сеанса. Когда пользователь запрашивает у web-сервера какую-либо страницу, сервер в составе HTTP-ответа посылает и набор пар имя/значение, называемых заголовки ответа. Среди этих заголовков может находиться заголовок с именем Set-Cookie (установить cookie), который просит клиентскую сторону сохранить у себя некоторую информацию о состоянии. Необходимыми элементами заголовка Set- Cookie являются лишь имя и значение cookie. Однако cookie может включать и другие кусочки информации, отделяемые друг от друга точкой с запятой. Для отслеживания хода сеанса web-контейнеры Java устанавливают cookie 251
следующего вида: j sessionid=значение-cookie, где значение-cookie обычно - строка байт с длинным числом, записанным в шестнадцатеричном виде. Согласно специ- фикации сервлетов версии 2.3, такой cookie должен иметь имя JSESSIONID. Однако ряд web-контейнеров, включая и Tomcat, генерируют это имя в нижнем регистре. Cookie для отслеживания сеанса обычно выглядит примерно следующим образом. jsessionid=3CAF7CD0A0BFF5076B390CCD24FD8F0D Значение такого cookie представляет идентификатор сеанса. Этот идентификатор одно- значно определяет конкретного пользователя на то Время, пока он направляет запросы к web-серверу. Если, к примеру, в одно и то же время с некоторым web-приложением взаи- модействуют 10 пользователей, web-сервер выделит им 10 уникальных идентификаторов. Кроме того, если некто сидел за ПК и обращался,к web-приложению с помощью Internet Explorer, а затем переместился к подключенному "лэптопу и открыл браузер Safari, чтобы подключиться к тому же web-приложению, каждый из его двух браузеров будет иметь соб- ственный, не совпадающий с другим идентификатор сеанса. У web-сервера нет никакой возможности определить, что один и тот же пользователь взаимодействует с web-приложе- нйем, пользуясь одновременно двумя разными браузерами, особенно если тот подключился через прокси-сервер. Однако, если пользователь работает за одним браузером и его Сеанс еще не истек (time out), web-сервер может отслеживать действия пользователя и ассоциировать их с одним сеансом. Отказ от cookie Что, если пользователь блокировал cookie? Обычно web-браузеры предоставляют воз- мо'.кность с помощью настроек отказаться от приема cookie. Серверы могут использовать защищенный обмен с помощью SSL (Secure Sockets Layer - уровень защищенных соке- тов), имеющего встроенный механизм отслеживания сеанса. Кроме того, существует распространенный запасной способ отслеживания сеанса - перезапись URL. Перезапись URL - способ, основанный на включении идентификатора сеанса в URL в качестве параметра пути, при переходе с одной страницы на следующую; таким образом, следующая страница имеет доступ к идентификатору сеанса безо всяких cookie. Формируемый таким образом URL выглядит примерно следующим образом. /home/default.jsp;jsessionid=3CAF7CD0A0BFF5076B390CCD24FD8F0D ' Поскольку большинство запросов к страницам обычно7 производятся без использова- ния SSL, в web-компоненте, которому требуется отслеживанйе сеансов, вам придется реализовывать механизм перезаписи URL. 252 | Глава 11. Отслеживание сеанса
11.1 Установка в web.xml времени простоя сеанса (time out) Задача I Требуется в дескрипторе развертывания задать время простоя сеанса web-приложения. Решение Создайте в web.xml элемент session-config. Обсуждение ' Одним из важных компонентов web-приложения является время простоя (time out), в течение которого сеанс все еще продолжается, до того как сервер закроет сеанс иорвободит все объекты, связанные с сеансом. В Tomcat 4.1.x, по умолчанию, время простоя сеанса составляет 30 минут. Если в течение этого времени не поступит ни одного запроса, связанного с данным сеансом, сейнс завершится. Если пользователь решит после 30 минут отсутствия вернуться к этому же web-приложению, используя тот же браузер, для него будет создан новый сеанс. В примере 11.1 показано, как можно задать собственное время простоя сеанса. Пример ll.t. Настройка времени простоя <?xml version="l.О" encoding="ISO-8859“l"?> <!DOCTYPE web-app PUBLIC '"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-application_2_3.dtd" <web-app> <!— пёред элементом session-config располагаются элементы filter, listener, servlet, и servlet-mapping —> v <session-config> <session-timeout>15</session-t,rmeout> </session-config> </web-app> В элемент session-config вложите элемент session-timeout. Время простоя задается в минутах, оно перезаписывает значение, принятое по умолчанию (которое, к примеру, в Tomcat составляет 30 минут). Однако метод HttpSession.getMaxI- nactivelnterval(), используемый в сервдетах для получения значения времени простоя сеанса, возвращает время в секунддх; если । в web.xml задано время простоя 15 минут, getMaxlhactivelriterval () вернет 900. Установка в web.xml времени простоя сеанса (time out) | 253
Другой способ задать время простоя для сервлета - использовать элемент init- param в web.xml, как показано в примере 11.2. Пример 11.2. Добавление параметра инициализации сервлета (init-param) для установки времени простоя сеанса ( <servlet> <servlet-name>Cart</servlet-name> <servlet-class>com.jspservletcookbook.TimeoutSession</servlet-class> <init-param> <param-name>timeout</parAm-name> <param-value>60 0</param-value> </init-param> </servlet> i В файле web.xml этого web-приложения элемент servlet имеет вложенный элемент init-param, который создает параметр с именем timeout. Данный сервлет Cart берет значение этого параметра (600 секунд, что равно 10 минутам) и .передает его методу session.setMaxinactivelnterval(int seconds). В примере 11.3 приведен метод doGet () рассматриваемого сервлета, где устанавливается значение переменной, хранящей время простоя, которое берется из этого параметра. Пример 11.3. Использованце параметров инициализации для задания времени простоя сеанса в сервлете public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java»io.lOException { response.setContentType("text/html"); java.io.Printwriter out = response.getWriter(); HttpSession session request.getSession(); //Сначала присваиваем значение времени простоя, принятое по умолчанию int —default з session.getMaxInactiveXnterval()г int timeout ш —default; try{ I timeout - new Integer (get Init Paramet er ("timeout")) .intValue (); } catch(NumberFormatException nfe){ //Рапортуем о проблемах co значением параметра из web.xml log("Problem with configuring session timeout in: " + getClass().getName()) ; }//try //Устанавливаем новое время простоя сеанса if(timeout Iя —default && timeout > -2) session.setMaxInactivelnterval(timeout); out.printIn("<html>"); 254 | Глава 11. Отслеживание сеанса
out .print Jn (‘'<head>"); out.printin("<title>Cart Servlet</title>"); out.printing"</head>"); out.printin("<body>") ; out.printIn("The timeout interval is: ". + session.getMaxInactivelnterval());' out.printIn("</body>"); out.printin("</html>"); Результат запуска этого сервлета, отображаемый в окне браузера, показан на рис. 11.1. Рис. 11.1. Динамическое изменение времени простоя сеанса Время простоя здесь меняется, только если новое значение отличается от принят» по умолчанию и при этом оно больше чем -2. if(timeout != „default && timeout > -2) session.setMaxlnactivelnterval(timeout); Время простоя можно задать равным -1, что, согласно спецификации сервлета версии 2.4, означает - ждать вечно. В некоторых серверах такое поведение может быть не реализовано. Как уже отмечалось, сеансы, по большей части, реализуются с помощью cookie. Работа с cookie в сервлетах и JSP описана в главе 10. См. также Рецепты .11.2 и 11.3 по настройке времени простоя для web-приложений, работающих на Tomcat; главу 1 по web.xml', главу 7 спецификации сервлета версий 2.3 и 2.4 по сеан- сам; разделы, посвященные отслеживанию сеансов книг «Java' Servlet Programming», автора Jason Hunter (O’Reilly), и «JavaServer Pages», автора Hans Bergsten (O'Reilly). Установка в web.xml времени простоя сеанса (time out) | 255
11.2 Установка времени простоя для всех web-приложений, работающих На Tomcat Задача * Требуется задать время простоя сразу для всех web-приложений, работающих под управлением некоторого экземпляра Tomcat. Решение Установите время простоя в элементе session-config в файле <инсталляцион- ный-каталог-Тотса^/conf/web.xml. / Обсуждение Настроив значение в файле дескриптора развертывания по умолчанию conf/web.xml сервера Tomcat, можно задать время простоя для всех web-приложений сразу. Если дескриптор развертывания конкретного web-приложения не содержит элемента session- config, то это приложение использует значение, заданное в файле conf/web.xml. Значение элемента session-timeout (вложенного в session-config) представляет время в минутах до истечения неактивного сеанса. В примере 11.4 показан элемент session-config дескриптора развертывания по умолчанию conf/web.xml сервера Tomcat с необходимыми XML-комментариями. Пример 11.4. Элемент session-conf ig файла по умолчанию conf/web.xml сервера Tomcat <i—================== Default Session Configuration ================—> <i— Вы можете задать значение по умолчанию времени простоя (в минутах)—> <!— для всех Вновь создаваемых сеансов, изменив это значение. —> <session-config> ' <sessipn-timeout>30</session-timeout> </session-config> При развертывании приложения Tomcat сначала обрабатывает свой файл web.xml, со значениями по умолчанию, а затем - дескрипторы развертывания каждого из web- приложений. В результате, задаваемый вами элемент session-config перезаписы- вает такой же элемент, заданный в conf/web.xml. Обычно желательно настраивать время простоя сеансов для каждого web-rtpwio>KCHHa в отдельности, особенно если требуется обеспечить переносимость приложения. См. также Рецепт 11.1 по настройке времени простоя сеанса; рецепт 11.3 по настройке времени простоя из программы; рецепт 11.4 по проверке корректности сеанса; главу 1 по web.xml', главу 7 спецификации сервлета версий 2.3 и 2.4 по сеансам; разделы, посвященные отеле- 256 | Глава 11. Отслеживание сеанса
жйванию сеансов книг' «Java( Servlet Programming», автора Jason Hunter (O'Reilly), и «JavaServer Pages», автора Hans Bergsten (O'Reilly). 113 Программная установка времени простоя сеанса Задача / Требуется задать время простоя сеанса в коде сервлета. Решение ; Для получения ссылки на объект HttpSession используйте метод getSes- sion() объекта HttpServletRequest. Затем с помощью метода HttpSession. setMaxinactivelnterval (int seconds) измените время простоя сеанса. Обсуждение Метод HttpSession.setMaxinactivelnterval(int seconds) задает время простоя сеанса для отдельно взятого сеанса, то есть с его помощью можно уста- новить тайм-аут только для данного конкретного объекта HttpSession. Прочие сервлеты, которые выполняют в web-приложении отслеживание сеансов, будут продол- жать использовать значение времени простоя, взятое из элемента session-timeout из web.xml или, если его там нет, то из session-timeout дескриптора по умолчанию сервера. Сервлет из примера 11.5 проверяет существующее значение времени простоя сеанса, а затем устанавливает это время в 20 минут. Пример 11.5. Изменение времени простоя, принятого по умолчанию Resetting a default timeout period z package com.jspservletcookbook; . . ., J ' unport java.util.Date; import j ava.text.DateFormat; ? import javax.servlet.*; import javax.servlet.http.*; public class SimpleSession extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) , throws ServletException, java.io.lOException { response.setContentType("text/html"); 9-1545 Программная установка времени простоя сеанса | 257
java.io.Printwriter out = response.getWriter(); HttpSession session = request.getSession(); out.printIn("<html>"); out .printin ("<head>")'; out.printin("<title>Simple Session Tracker</title»"); out.printIn("</head»"); out.printIn("<body»"); out.printing"<h2»Session Info</h2»"); out.printin("session ID: " + session.getld() + "<br»<br>"); out.println( "The SESSION TIMEOUT,'period is " + session. getMaxInactivelntervai ()' + " seconds. <br»<br>") ; ou t. print In ( "Now changing it to 20 minutes. <brxbr>"); session.setMaxInactivelnterval(20 * 60); out.printIn("The SESSION TIMEOUT period is now " + session.getMaxInactivelntervai() + seconds."); out.printIn("</body»"); out.printin("</html>"); } } Результат обращения к этому сервлету из браузера показан на рис. 11.2. Рис. 11.2. Получение информации о времени простоя сеанса Сервлет получает объект HttpSession с помощью метода getSession () класса HttpServletRequest. 258 | Глава 11. Отслеживание сеанса
Если по умолчанию время простоя для сервлета было установлено, скажем, в 30 минут, сервлет из примера 11.5 изменит его на 20 минут. ? session.setMaxInactivelnterval(20 * 60); Запомните, что данный метод изменяет время простоя только для сеанса, связанного с пользователем, обратившимся к данному сервлету. Но почему одни пользователи получают одно время простоя, а другие - другое? Предположим, в ходе изучения web- пользователей, проведенное вашей организацией, было установлено, что продолжитель- ность простоя сеанса в пять минут подходит для сервлетов, связанным с покупатель- ской корзиной, тогда как для сервлетов, создающих диаграммы или карты, более приемлемым является время простоя в 30 минут или больше, поскольку пользователи последних дольше изучают сложные изображения, появляющиеся в браузере. Для большинства web-приложений время простоя задается (или изменяется) с помощью дескриптора развертывания, и вам не придется динамически изменять его в коде сервлета. См. также Рецепт 11.1 по настройке времени простоя сеанса; рецепт 11.2 по установке времени простоя для всех приложений сервера Tomcat сразу; рецепт 11.4 по проверке корректно- сти сеанса; главу 1 по web.xml', главу 7 спецификации сервлета версий 2.3 и 2.4 по сеан- сам; разделы, посвященные отслеживанию сеансов книг «Java Servlet Programming», автора Jason Hunter (O'Reilly) и «JavaServer Pages», автора Hans Bergsten (O'Reilly). 11.4 Проверка запроса HttpServletRequest - существует ли для него сеанс Задача Требуется проверить, связан ли с пользователем web-приложения корректный сеанс. Решение > Чтобы проверить, пуст ли объект HttpSession (равен ли null), используйте метод getSession(false) объектаHttpServletRequest. Обсуждение Некоторые web-компоненты создаются для проверки корректности сеанса и последующей переадресации пользователя к другому web-компоненту, в зависимости ot результата проверки. Допустим, пользователь обращается к компоненту, для работы ---------------------------------------------------X,----------- Проверка запроса HttpServletRequest - существует ли для него сеанс | 259 9* .
которого требуется существование в объекте HttpSession (сеансе) некоторого пользова- тельского (custom) объекта, например «корзины покупателя». Вам, следовательно, необхо- димо проверить, существует ли соответствующий корректный сеанс; а если корректного сеанса еще нет, не создавать его здесь же для данного запроса, поскольку в цепочке компо- нентов приложения (в ее начале) есть другой web-компонент, который и отвечает за созда- ние нового сеанса и заполнение ого элементами для формирования корзины покупателя. Пользователь мог случайно попасть сразу на Шаг 3 web-приложения, вместо Шага 1. В этом случае, когда сеанс некорректен, запрос переадресуется другой точке входа прило- жения (например, на экран регистрации). Если при вызове метода getSession (false) текущего объекта HttpServletRequest (запроса) этот метод вернул null, пользова- тель не находится в рамках корректного сеанса -и для запроса пользователя создается новый сеанс. J При обращении с другим параметром HttpServletRequest. getSession (true) (или просто getSession ()) метод будет пытаться создать новый сеанс. В примере 11.6 приведен сервлет, проверяющий существование сеанса пользователя и, если объект HttpSession (сеанс) равен null, перенаправляющий пользователя к другому web-компоненту. , । Пример 11.6. Проверяем, корректен ли сеанс import j avax.servlet.*; 1 inport j avax.servlet.http.*; public class Sessioncheck extends HttpServlet { • public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.lOException { HttpSession session request.getSession(false); if (session жж null){ response. sendRedirect (" /myproj /login, j sp"); } else { response. sendRedirect (" /myproj /menu .jsp"); Если в примере 11.6 сеанс некорректен (session == null), сервлет направляет запрос к странице loginjsp, имеющей путь к контексту /myproj. Если сеанс нормальный, запрос направляется к компоненту /myproj/menu.jsp. 260 | Глава 11. Отслеживание сеанса
Метод HttpServletResponse.sendRedirect(String location) посылает £ клиенту HTTP-ответ, который выглядит примерно следующим образом. _д]л* НТТР/1.1 302 Moved Temporarily Location: Http://localhost:9000/dbproj/login.j sp Content-Type: text/html;charset=TSO-8859-l После чего клиент, посылает другой запрос, уже на URL, указанный в заголовке Location этого НТТР-ответа. См. также Рецепты 11.1 и 11.3 по настройке времени простоя, сеанса; главу 1 по шеЬ.хтГ, главу 7 спецификации сервлета версий 2.3 и 2.4 по сеансам; разделы, посвященные отслеживанию сеансов книг «Java Servlet Programming», автора Jason Hunter (O'Reilly) и «JavaServer Pages», автора Hans Bergsten (O'Reilly). 11.5 Отслеживание активности сеанса в сервлете Задача Вы хотите использовать сервлет для слежения за параметрами сеанса: временем соз- дания и временем последнего обращения Решение Для получения ссылки на объект HttpSession (сеанс) воспользуйтесь методом get- Session () объекта, HttpServletRequest (запрос). После этого вызовите для полученного объекта методы HttpSession.getCreationTimeO и HttpSession. getLastAccessedTime(). Обсуждение В этом рецепте описано, как с помощью HttpSession API получить время созда- ния и время последнего обращения для некоторого сеанса. Как web-приложение может использовать эту информацию? Во-первых, вам может потребоваться проанализировать активность обращений к web-приложению, путем сопоставления времени создания сеанса, времени последнего доступа и текущего времени. К примеру, разница между временем создания и текущим временем (измеряемая в секундах) покажет, как долго длится сеанс конкретного пользователя. Отслеживание активности сеанса в сервлете | 261 / '
Метод HttpSession.getLastAccessedTime () возвращает время (тип данных long), когда пользователь уделал последний в рамках продолжающегося сеанса запрос. Сервлет, вызывавший getLastAccessedTime (), представляет текущий, самый последний из запросов, связанных с сеансом. Другими словами, время, когда пользователь обратился к сервлету, вызвавшему метод getLastAccessedTime () становится временем последнего обращения. Сервлет из примера 11.7 отображает текущее время, а также время создания сеанса и время последнего доступа. Вызов HttpServletRequest.getSessionO свяжет с текущим запросом новый сеанс, если с этим запросом не связан никакой сеанс. Вызов HttpServletRequest. getSession (false) Просто вернет null, если с запросом не связан никакой сеанс, и не станет создавать для пользователя новый сеанс. См. рецепт 11.4. Пример 11.7. Вызов в сервлете методов объекта HttpSession package com.j spservletcookbook; import javax.servlet.*; import j avax.servlet.http.*; inport java.util.Date; import j ava.text.DateFormat; import java.util.Enumeration; public class SessionDisplay extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.lOException { response.setContentType("text/html"); java.io.Printwriter out = response.getWiriter () ; HttpSession session =.request.getSession(); Date creationTime = new Date(session.getCreationTime()); Date lastAccessed n new Date(session.getLastAccessedTime()); Date now = new Date () ; DatfeFormat formatter = DateFormat.getDateTimelnstande(DateFormat .MEDIUM, DateFormat. MEDIUM) ; out.printin("<html>"); out.printin("<head>"); out.println( "<title>Displaying the Session Creation and "+ 262 | Глава 11. Отслеживание сеанса т
"Last-Accessed Time</title>"); out.printIn("</head>"); out.printIn("<body>"); out.printIn("<h2>Session Creation and Last-Accessed Time</h2>"); out.printIn( "The time and date now is: " + formatter.format(now) + "<br><br>"); out.printin("The session creation time: "+ / "HttpSession.getCreationTimeO: " + formatter, format (creat ionTime) + "<brxbr>"); out.printing"The last time the session was accessed: " + HttpSes sion.getLastAccessedTime(): " + formatter.format(lastAccessed) ); out.printIn("</body>"); out.printin("</html>"); }//doGet public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.lOException { doGet(request,response) }//doPost Отображение результатов работы сервлета в браузере приведено на рис. 11.3. Рис. 11.3. Вывод времени создания сеанса и времени последнего обращения в рамках этого сеанса Отслеживание активности сеанса в сервлете | 263
Как и в предыдущем примере, здесь форматирование строк с датами для отображения в браузере выполняется с помощью объекта java, text .DateFormat. Значение (дата), возвращаемое методами getCreationTime () и getLastAccessedTime () объекта HttpSession, имеет тип данных long, из которого можно создать объект javautil. Date. Date creationTime = new Date( session.getCreationTime() ); Время создания сеанса можно отобразить, используя метод объекта Date: for- mat (Date _date). В следующем рецепте мы рассмотрим, как отслеживать активность сеанса в JSP. См. также Рецепты 11.5 и 11.8; главу 1 tio web.xml', главу 7 спецификации сервлета версий 2.3 и 2.4 по сеансам; javax.servlet.http.HttpSession API на странице http://java.sun.com/ j2ee/sdk_1.3/techdocs/api/javax/servlet/http/HttpSession.html-, разделы, посвященные отслежи- ванию сеансов книг «Java Servlet Programming», автора Jason Hunter (O’Reilly) и «JavaServer Pages», автора Hans Bergsten (O'Reilly). 11.6 Отслеживание активности сеанса в JSP Задача Требуется с помощью страницы JSP определить время создания сеанса и время последнего обращения в рамках сеанса. Решение Для получения доступа к объекту HttpSession (сеанс), связанному со страницей JSP, воспользуйтесь JSTL. А затем вызовите методы HttpSession.getCreation- Time () и HttpSession. getLastAccessedTime () для этого объекта. Обсуждение Следить за ходом сеанса в JSP очень легко: здесь используются лишь слегка видоизме- ненные, по сравнению с теми, что мы использовали в сервлетах, методы и инструменты. В примере 11.8 для отображения информации о текущем сеансе используется пользова- тельское действие out из JSTL 1.0. Более детально описание JSTL и связанного с ней языка EL приводится в главе 24. 264 | Глава 11. Отслеживание сеанса
Пример 11.8. Отслеживание сеанса в JSTL <%@page contentType="text/html”%> taglib uri="http://java.sun.com/jstl/core" prefix»"c" %> <html> <head><title>View Session JSP </titlex/head> <body> <h2>Session Info From A JSP</h2> The session id: <c:out value="${pageContext.session.id}"/><brxbr> The session creation time as a long value: <c:out value»"${pageContext.session.creationTimo}"/xbr><br> The last accessed time as a long value: <c: out value» " $ {pageContext. session.lastAccessedTime} "/><brxbr> </body> </html> На этой странице JSP для выполнения необходимых пользовательских действий, являющихся частью библиотеки тегов ядра, используется директива taglib. По согла- шению, атрибут uri для тегов ядра равен http://java.sun.com/jstl/core, а префикс - с (вы можете создать собственный префикс в директиве taglib). Для JSTL версии 1.1 значение uri равно http://java.sun.com/jsp/jstl/core. Для отображения идентификатора теку- щего сеанса (возвращается методом HttpSession.getld()), времени создания сеанса (тип long) и времени последнего обращения в рамках сеанса, используется тег out из биб- лиотеки тегов ядра JSTL. Отображение этих значений в браузере показано на рис. 11.4. Рис. 11.4. Отображение информации о сеансе из JSP Отслеживание активности сеанса в JSP | 265
Элемент out записывает значение, заданное в его атрибуте value в поток ответа страницы JSP. Само же значение задается с помощью языка EL. Вот как, к примеру, выглядит выражение на языке EL, получающее значение времени создания сеанса. ${pageContext.session.treationTime} • •' Ссылка pageContext - это ссылка На один из неявно создаваемых объектов, *4; . к которым можно обращаться из EL. Это эквивалент неявно создаваемого объекта * t Л* JSP с тем же именем. Здесь способ получения времени создания сеанса отличается от привычного (вызова метода); для получения свойства session (сеанс) объекта pageContext мы исполь- зуем оператор точка (.), а затем еще раз используем оператор точка для получения свой- ства creationTime объекта session. Таким образом, полная фраза выглядит следующим образом: pageContext.session.creationTime. И, наконец, в EL для разыменования всех переменных и свойств (с целью получить их значение) их необходимо заключить в символы $ { }. Остальные значения, относящиеся к сеансу, получаются аналогично. К примеру, время последнего обращения (значение типа long, возвращаемое методом HttpSes- sion . getLastAccessedTime ()) извлекается следующим образом: $ {pageContext. session. lastAccessedTime} В примере 11.8 время последнего обращения в рамках сеанса отображается как боль- шое «непричесанное» число. Это значение станет более понятным, если его отобразить в виде даты. В примере 11.9 показано, как использовать пользовательские действия JSTL для форматирования в виде строки с датой. 1Пример11.9. Форматирование с помощью JSTL времени создания сеанса и времени последнего доступа <%@page contentType="text/html"%> taglib uri»"http://java.sun.com/jstl/core" prefix="c" %> taglib uri»"http://java.sun.com/jstl/fmt" prefix»"f mt" %> <html> <headxtitle>Viev Session JSP </title></head> <body> < < h2>Session Info From A JSP</h2> The session ID: <c:out value="${pageContext.session.id)"/> < h3>Session date values formatted as Dates</h3> <jsp:useBean id»"time Values" class»"java.util.Date"/> < c:set target»"${time Values}" value» "${pageContext.session.creationTime}" property»"time"/> 266 | Глава 11. Отслеживание сеанса
The creation time: <fmt:formatDate value»"${timeValues}" type="both" datestyle» "medium" /><brxbr> <c:set target»"${time Values}" value» "${pageContext.session.lastAccessedTime}" property»"time"/> The last accessed time: <fmt:formatDate value»"${timeValues}" type» "both" datestyle»"short" /> </body> </html> Отображение данной страницы JSP в браузере приведено на рис. 11 5. 3 View Session JSP - Microsoft Internet Explorer Search'®!^ Favorite* RefreshЧ Home Htp://locaF»*t fi.Whome.'session format i >р &1п*гхи^ ^аг^огтЕ^Т ^weather.com ^Wefcome to Spartak ® ' DoconentaLor Session Info From A JSP ! The session id: 2E64D9F4A98C21611B52525ABB487BEF S Session date values formatted as Dates The creation time: Feb 24,2003 11:01:04 AM The last accessed time: 2/24/03 11:10:28 AM Mon Feb 24 11 10:28 EST 2003 § §0^——.............?-Z Puc. 11.5. Форматирование помощью JSTL времени создания сеанса и времени последнего доступа Данная страница JSP берет два значения: дату/время создания сеанса и дату/время последнего запроса к web-приложению в рамках сеанса и отображает их в браузере. Как уже говорилось, эти значения возвращаются от объекта HttpSession в виде Java-типа long. Вам необходимо создать объект Date и присвоить его свойству time полученное значение типа long. А затем использовать пользовательские действия по форматированию из биб- лиотеки JSTL, чтобы создать удобочитаемые строки (String). Но для начала сделаем биб- лиотеку с тегами форматирования доступной для нашей страницы JSP с помощью следующей директивы. <%@ taglib uri="http://java.sun.com/jstl/fmt" prefix="fmt" %> Объект java.util .Date, используемый для создания дат из значений типа long генерируется с помощью стандартного действия JSP j sp: useBean, например, следующим образом. ч <jsp:useBean id="timeValues" class="java.util.Date"/> Отслеживание активности сеанса в JSP | 267
В этой строке создается новый объект Date и сохраняется в переменной timeValues. что делает его доступным с помощью синтаксиса EL: $,{ timeValues}. Затем bJSP используется пользовательское действие set, для того чтобы задать значение свойства time объекта Date. <c:set target="${timeValues}" value="${pageContext.session.creationTime}" property="time“/> В атрибуте target элемента set задается компонент JavaBean, значение свойства которого требуется установить. Имя свойства задается в атрибуте property. Данному свойству присваивается значение типа long, возвращаемое следующим JSTL-выражением. ${pageContext.session.creationTime} Другими словами, подобное использование Пользовательского действия эквивалентно вызову метода java.util.Date.setTime(long secs) для объекта timeValues класса Date. Этот прием повторяется дважды - для времени создания сеанса и для времени последнего обращения. В примере 11.10 приведен фрагмент кода, отвечающий за получение и отображение значений, включая пользовательское действие fmt: f ormatDate. Пример 11.10. Отображение времени создания сеанса и времени последнего обращения <c:set target="${timeValues}" values"${pageContext.session.creationTime}« property="time"/> The creation timei <fmt:formatDate values"${timeValues}" type="both" dateStyle= "medium" /><brxbr> <c:set targets"${timeValues}"values "${pageContext.session.lastAccessedTime}" property="time"/> The last accessed time: <fmt:formatDate values"${timeValues}“ type="both" datestyles"short" /> Элемент formatDate - одно из действий по форматированию из библиотеки JSTL, которая описывается в главе 24. В этом примере действие formatDate работает следующим образом: приведенный ниже код заменяется отформатированным значением времени, например «Jan 21,2003 1:57:39 РМ». <fmt:formatDate values"${timeValues}" type="both" dateStyle="short" /> Чтобы проиллюстрировать разницу, в примере 11.10 приводятся два разных значения атрибута dateStyle (medium (средняя) и short (сокращенная)). См. также Рецепт 11.4 по проверке корректности сеанса; главу 1 по webjanl; главу 7 специфика- ции сервлета версий 2.3 и 2.4 по сеансам; javax. servlet .http.HttpSession API на странице http://java.sun.eom/j2ee/sdk_l.3/techdocs/api/javax/servlet/http/HttpSession.hTinl; разделы, посвященные отслеживанию сеансов книг «Java Servlet Programming», автора Jason Hunter (O’Reilly) и «JavaServer Pages», автора Hans Bergsten (O'Reilly). 268 | Глава 11. Отслеживание сеанса
11.7 Использование в JSP метода перезаписи URL Задача * • I Вы хотите быть уверенным, что в JSP используется перезапись URL на тот случай, если какие-либо пользователи запретили браузеру принимать cookie. Решение Для создания URL, в< который автоматически включается в качестве параметра иден- тификатор сеанса, воспользуйтесь пользовательским действием url из JSTL. Обсуждение Некоторые из пользователей могли настроить свои браузеры на запрет приема cookie. А поскольку cookie - стандартный для JSP способ отслеживания сеанса, как запрет cookie повлияет на общение этих пользователей с web-приложением? Я рекомендую все страницы JSP, где необходимо отслеживать сеанс, разрабатывать с использованием переза- писи URL, чтобы пользователи-противники cookie могли нормально работать с вашим web-приложением. Одно из решений данной проблемы - использовать пользовательское действие url, являющееся частью JSTL. Элемент url автоматически добавляет идентификатор сеанса в качестве параметра в состав URL, который будет использоваться, например, в тегах href, form и frameset. Это позволит страницам, на которые указывают такие ссылки (например, сервлетам или JSP), отслеживать сеанс, не прибегая к cookie. Одна из приятных сторон использования элемента url - то, что он добавляет иденти- фикатор сеанса к URL в качестве параметра, когда это нужно, без всякого вмешательства автора страницы. Порядок использования url продемонстрирован в примере 11.11. Пример 11.11. Использование тега ядра url для перезаписи URL <%браде contentType="text/html"%> <%@ taglib uri="http://java.sun.дот/jstl/core" prefix=’,c" %> <html> <head><title>URL Rewriter</title></head> <body> <hl>This page will use URL rewriting if necessary*:/h2> <c:url value""/default.jap" var«"goToDefault" escapeXinl""false"/> Go to the default.jsp page <a href="<c:out value="${goToDefault}"/>">here</a>. </body> </html> Использование в JSP метода перезаписи URL | 269
Для того чтобы сделать библиотеку тегов ядра доступной, в примере используется директива taglib. <%@ taglib uri=’http://java.sun.com/jstl/core" prefix="c" %> Элемент url из этой библиотеки тегов создает URL, представляющий web-компо- нент default.jsp, находящийся на верхнем уровне данного web-приложения. Созданный URL, с помощью атрибута var тега url сохраняется в переменной goToDefault. При этом атрибуту escapeXml присваивается false (по умолчанию - true) во избе- жание замены в URL таких символов, как амперсанты и угловые скобки на коды их сим- вольных сущностей. Полностью элемент url выглядит следующим образом. <c:url value="/default.jsp" var="goToDefaplt" escapeXml="false"/> URL, созданный этим пользовательским действием, используется затем в качестве значения атрибута href следующим образом. <а href="<с:out valuer"${goToDefault}"/>">here</а> В этом коде для создания гиперссылки используется пользовательское действие out и выражение на языке EL (${goToDefault)). После запроса обсуждаемой страницы, если браузеру запрещено принимать cookie, возвращаемый данной строкой HTML выглядит следующим образом. <а href="/home/default.jsp;jsessionid=3CAF7CD0A0BFF5076B390CCD24FD8F0D">here</a> Бросаются в глаза два отдичия между URL, созданным здесь: <c:url value="/default.jsp" var-"goToDefault” escapeXml="false"/> и URL, сгенерированным пользовательским действием: /home/default.jsp;jsessionid=3CAF7CD0A0BFF5076B390CCD24FD8F0D Во-первых то что пользовательское действие url автоматически добавило перед /default, jsp путь к контексту (в данном случае Люте). Во-вторых, к URL, в качестве параметра пути с именем jsessionid, добавился идентификатор сеанса, и, таким образом, страница, на которую указывает такая ссылка, получает доступ к идентификатору сеанса, связанного с данным пользователем, и может отслеживать сеанс. Параметр пути начинается с точки с запятой и представляет собой пару имя/ значение, например ; jsessionid=3CAF7CD0A0B. URL, создаваемый на странице JSP с помощью элемента out, может иметь и другие параметры. Пример 11.12 почти полностью повторяет первый пример рецепта, за исключением параметров, добавляющихся к URL в пользовательском действии url. 270 | Глава 11. Отслеживание сеанса
Пример 11.12. Добавление параметров с помощью пользовательского действия url <%@page contentType="text/html"%> <%® taglib uri=nhttp://java.sun.com/jstl/core" prefix="c" %> <html> <head><title>JSP Page</titlex/head> <body> <hl>This page will use URL rewriting if necessary</h2> <c surl valuer"/default.jsp?n=${param.first}&!=${param.last}" var«"goToDefault" /> Go to the default.jsp page <a href= "<c:out value="${goToDefault}" escapeXml=nfalse" />">here</a>. </body> </html> Теперь URL выглядит следующим образом. /default. jsp?n=${param.first}&1=${param.last} Здесь, для доступа к двум параметрам запроса с именами first и last, использу- ется встроенный синтаксис EL, Так, например, для доступа к параметру first исполь- зуется неявно создаваемый объект языка EL param, за которым следует оператор точка и имя параметра: $ {param, first}. Предположим, что k JSP из этого примера обращаются следующим образом. http://localhost:8080/home/url_rewrite.jsp?first=Bruce&last=Perry Тогда атрибут value обсуждаемого элемента uri будет разрешен в следующий код. <c:url value-"/default.jsp?n=Bruce&l=Perry" var="goToDefault" /> Следующее далее в примере пользовательское действие out включает атрибут escapeXml со значением false. Если оставить значение этого атрибута, принятое по умолчанию (true), то символ амперсант, (&) будет заменен на код символьной сущ- HOCTH(&dmp) и строка запроса в URL при выполнении JSP будет выглядеть следующим образом. <а href="Zhome/default.jsp;jsessionid=D37AF592DACABD?n=Bruce&amp;l=Perry"> here</a> * Во избежание таких неприятностей при генерации URL ссылок с помощью элемента out не забудьте задать атрибуту escapeXml значение false. При использовании перезаписи URL с помощью элемента url не забывайте пользоваться относительной формой URL (например, /default.jsp). Если в атрибуте value элемента url используется абсолютная форма URL (например, hpp://www. mysite.com/home/default.jsp), перезарись URL работать не будет. Использование в JSP метода перезаписи URL | 271
Табл. 11.1. Специальные символы.и запись кодов сущностей. Символ Коды сущностей < &lt; / > &gt; & &amp; 1 &#039; II &#034; См. также ; Рецепт 11.6 по отслеживанию активности сеанса из JSP; рецепт 11.8 по использо- ванию перезаписи URL в сервлетах; раздел по конфигурации JSP спецификации JSP версии 2.0; главу 23 по JSTL; разделы, посвященные отслеживанию сеансов книги «Jav-4 aServer Pages», автора Hans Bergsten (O'Reilly). 11.8 Использование перезаписи URL в сервлетах Задача Требуется создать сервлет, использующий перезапись URL, если браузер пользова- теля не принимает cookie. Решение Для кодирования всех URL, которые используются для ссылок на другие страницы, используйте метод HttpServletResponse. encodeURL (String url). Обсуждение Класс javax.servlet.HttpServletResponse включает отличный метод, который в случае, если запрос к сервлету сделал пользователь, запретивший у себя cookie, будет дополнять URL идентификатором текущего сеанса. Если для кодирования URL, используемых в сервлете, вы используете метод Http- ServletResponse. encodeURL (String url), этот метод, при необходимости, берет на себя перезапись URL, и вам уже не надо беспокоиться о том, принимает ли пользователь cookie. При использовании данного метода вы должны добросовестно кодировать URL каж- дой ссылки, имеющейся в сервлете. В примере 11.13 приведена модифицированная версия сервлета из рецепта 11.6. 272 | Глава 11. Отслеживание сеанса
Пример 11.13. Использование перезаписи URL в сервлете import javax.servlet.*; import javax.servlet.http.*; public class-UrlRewrite extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.lOException { response.setContentType("text/html"); java.io.Printwriter out = response.getWriter(); String contextPath = request.getContextPath(); String encodedUrl response.encodeURL(contextPath + "/default.jsp"); out.printIn("<html>"); out.printIn("<head>"); out.println("<title>URL Rewriter</title>"); out.printIn("</head>"); out.printIn(*<body>•); out.printIn( “<h3->This page will use URL rewriting if necessary</h2>"); out.printIn("Go to the default.jsp page <a href^X"" + encodedUrl + "X">here</a>."); out.printin("</body>"); out.printIn("</html>"); public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io«lOException { doGet(request,response); } } На странице, посылаемой браузеру; не сохраняющему cookie, URL выглядит примерно так: /home/default.jsp;jsessionid=3CAF7CD0A0BFF5076B390CCD24FD8F0D. Использование перезаписи URL в сервлетах | 273
Одно из отличий между использованием метода encodeURL и тем решением, которое применялось для JSP в рецепте 11.7 (пользовательское действие url из JSTL) состоит в том, что последнее автоматически добавляет в начало URL путь к контексту. Если /home - путь к контексту, a /default.jsp - URL, то перезаписанный с помощью пользовательского действия URL будет примерно таким: /home/default. jsp;jsessiohid=3CAF7CD0A0BFF5076B390CCD24FD8F0D. На самом деле автома- тическое добавление пути к контексту - это отдельная, не связанная с перезаписью URL операциями метод encodeURL ее не выполняет. Если вы хотите, чтобы она все же выполнялась в сервлете, добавляйте к URL путь к контексту перед вызовом метода encodeURL. String contextPath =request.getContextPath(); - String encodedUrl =! response.encodeURL(contextPath + "/default.jsp") / * Для инициации перезаписи URL при вызове метода HttpServletResponse.sen- dRedirect (String url) (перенаправление на другую страницу) можно также использо- вать метод HttpServletResponse. encodeRedirectURL (String url). В при- мере 11 14 приведен сервлет, в методе doGet'() которого используется обращение к методу encodeRedirectURL для того, чтобы страницы с URL назначения также имели доступ к идентификатору сеанса перенаправленного к ним пользователя. Пример 11.14. Использование encodeRedirectURL в методе doGet public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io..lOException { //перенаправляем пользователя в зависимости от параметра до String destination = request.getParameterU'go"); String contextPath = request.getContextPath(); if(destination == null || destination.equals("")) throw new ServletException( "Missing or invalid 'go’ parameter in * + getClass().getName()); if(destination.equals("weather")){ //обеспечиваем перезапись URL response.sendRedirect( respopse.encodeRedirectURL( contextPath + "/weather") );} if(destination.equals("maps")){ // обеспечиваем/перезапись URL response.sendRedirect( response.encodeRedirectURL( contextPath+ "/maps") );} } 274 | Глава 11. Отслеживание сеанса
Метод response. sendRedirect {'String url.) перенаправляет запрос по назна- чению, задаваемому efo параметром url. Сервер посылает клиенту ответное сообщение с кодом состояния НТГР. НТТР/1.1 302 Moved Temporarily Кроме того, заголовок ответа Location снабжает клиента новым URL, по которому следует искать запрошенный им файл. При необходимости, метод response. encodeRe- directURL (String url) добавляет к URL с новым местом назначения идентификатор сеанса. Приведенный в примере сервлет получает имя сервлета, к которому следует направить клиента, из параметра запроса до. String destination = request.getParameter("go"); Если параметр отсутствует или его значение - пустая строка, сервлет вызывает исключение ServletException. Если же до имеет корректное значение, сервлет перенаправляет запрос к одному из двух сервлетов с путями /weather или /maps (путь к контексту в данном примере /home)/. Следующий ниже код, при правильной реализации на сервере, добавит к URL идентификатор сеанса, если обратившаяся с запросом сторона не принимает cookie, что позволит сервлету назначения следить за сеансом. response.sendRedirect( response.encodeRedirectURL(contextPath + "/weather") ); См. также Рецепт 11.4 по проверке корректности сеанса, рецепт 11.7 по использованию переза- писи URL в JSP; главу 1 по web.xml\ главу 7 спецификации сервлета версий 2.3 и 2.4 по сеансам; javax. servlet .http .HttpSession API на странице http://java.sun.com/ j2ee/sdk_1.3/techdocs/api/javax/servlet/http/HttpSession.html; разделы, посвященные отслежи- ванию сеансов книг «Java Servlet Programming», автора Jason Hunter (O'Reilly) и «JavaSer- ver Pages», автора Hans Bergsten (O'Reilly). 11.9 Использование слушателя для отслеживания жизненного цикла сеанса f Задача Вы хотите создать объект, реализующий интерфейс HttpSessionListener, вызываемый, когда сеанс создается и когда сеанс разрушается. Решение Создайте класс слушателя (listener), реализующий интерфейс HttpSessionLis- tener, и зарегистрируйте этот класс в дескрипторе развертывания своего приложения. Использование слушателя для отслеживания жизненного цикла сеанса | 275
Обсуждение API сервлетов предоставляет интерфейс javax.servlet.http.HttpSession- Listener для выполнение каких-либо действий* в ответ на создание сеанса и на уничтожение сеанса. Класс, реализующий этот интерфейс, может выполнять какие- либо собственные действия в моменты возникновения одного из этих событий (или при обоих событиях). Процесс создания и объявления слушателя сеанса в web-приложении включает следующие пункты. 1. Создайте класс, реализующий интерфейс HttpSessionListener. В этом интерфейсе определены два метода: sessionCreated() и sessionDe- stroyed(), каждый из которых принимает один параметр - HttpSessionEvent. 2. Убедитесь, что конструктор создаваемого класса не принимает аргументов 3. Поместите откомпилированный класс в каталог WEB-INF/classes своего web-прило- жения (включая все связанные с пакетом каталоги) или сохраните класс в виде JAR, в каталоге WEB-INFAib. 4. Объявите этот слушатель в дескрипторе развертывания webjcnil. 5. Перезапустите web-контейнер (если необходимо), который создаст экземпляр вашего класса слушателя и зарегистрирует его в качестве слушателя для всех событий созда- ния новых сеансов и всех событий удаления сеансов в вашем web-приложении. Объекты, которые привязываются к сеансам, должны реализовывать интерфейс Ht- tpSessionBindingListener. Такой слушатель не нужно настраивать * 7 Л* в дескрипторе развертывания. Однако привязанные объекты должны реализовывать HttpSessionBindingListener, а также методы valueBound (), valueUnbound(), и init(). Слушатель HttpSessionActivationListener предназначен для сеансов, которые мигрируют между виртуальными машинами Java (JVM - Java Virtual Machine). Объекты, привязанные к таким сеансам, должны реализовывать интерфейс HttpSessionActivationListener и два его метода: sessionDidActivate()и sessionWillActivate(). Вот запись в web.xml для класса слушателя из нашего примера. <listener> <listener-class>com.jspservletcookbook.SessionListen</listener-class> </listener> Класс HttpSessionListener из примера 11.15 хранит количество «живых» сеан- сов web-приложения и записывает на консоль сообщение при каждом создании и уничтожении сеанса. Было бы лучше протоколировать сообщения с помощью подхо- дящего компонента, например log4j, который мы будем обсуждать в главе 14. .Л 276 | Глава 11. Отслеживание сеанса •• 1
Пример 11.15. Отслеживание активности сеанса с помощью класса слушателя package com.j spservletcookbook; import java.util.Date; import javax.servletr*; inport javax.servlet.http.*; public class S.esskonListen implements HttpSessionListener { private int sessioncount; public SessionListen() { this.sessioncount = 0; } - public void sessionCreated(HttpSessionEvent se){ HttpSession session = se.getSession(У; session. setMaxinactivelnterval< 6 0); //увеличиваем счетчик сеансов sessionCount++; ' String id = session.getldf); Date now = new Date () ; String message = new StringBuffer( "New Session created on "). append(now.toString()).append("\nID: "). append(id)*append("\n").append("There are now "). append(""+sessionCount).append( " live sessions in the application."). toStringO; System.out.printIn(message); public void sessionDestroyed(£ttpSessionEvent se){ HttpSession session = se.getSession(); String id = session.getldl); —sessionCount;//уменьшаем счетчик сеансов String message = new StringBuffer("Session destroyed" + "\nValue of destroyed session ID is"). append("" +id) . append(" \n"),.append( "There are now ").append(""+sessionCount).append! " live sessions in the application."). toStringO ; System.out.printin(message); } } Использование слушателя для отслеживания жизненного цикла сеанса | 277
, Каждый класс слушателя должен иметь конструктор без параметров. Наш класс SessionListen содержит единственную переменную экземпляра, имеющую тип int, предназначенную для хранения числа сеансов. В методе sessionCreated () (сеанс создан) с помощью метода HttpSessionEvent.getSession() код получает дос- туп к вновь созданному сеансу. После этого время простоя для этого сеанса устанавлива- ется в 60 секунд, в результате чего создание и разрушение сеансов можно наблюдать на консоли без больших задержек; ‘ ‘ Класс HttpSessionListener (слушатель) извещается только о тех запросах j к страницам, которые приводят к созданию нового сеанса, получая информацию, * например, с помощью метода request .getSession(). Он также извещается в тех ^случаях, когда сервлет или JSP заканчивают существующий сеанс, такие события приводят к вызову метода sessionDestroyed() этого класса. Если какие-либо сервлеты и JSP доступны для пользователя, но не занимаются отслеживанием сеанса, то слушатель не извещается об их активности; то' же справедливо и когда сеанс уже создан, web-приложение продолжает работу в рамках сеанса и удаление еще не происходит. Аналогичная отправка сообщений и доступ к объекту HttpSession имеет место в методе sessionDestroyed(). Вывод на консоль, показанный на рис. 11.6, демонстрирует, что йы имеете доступ к информации об объекте HttpSession из обоих методов слушателя. Используя интерфейс HttpSessionListener можно создать класс, следящий затем, сколько сеансов создается за определенный период времени и как долго они длятся, прежде чем станут ненужными и прекратятся по превышению времени простоя. См. также Главу 14 по использованию слушателей для протоколирования сообщений; рецепт U.4 по проверке корректности сеанса; главу 1 по web.xml', главу 7 спецификации сервлета версий 2.3 и 2.4 по сеансам; javax. servlet .http.HttpSession API на странице http://java.sun.com/j2ee/sdk_l.3/techdocs/api/javax/servlet/http/HttpSession.htnil. 278 | Глава 11. Отслеживание сеанса
T omcat ______ _____________________________L=JSE? INFO: Creating new Registry instance Sep 23, 2003 12:55:45 PM org.apache.commons.modeler.Registry getServe INFO: Creating MBeanServer Sep 23, 2003 12:55:48,PM org.apache.coyote.httpll.HttpllProtocol init INFO: Initializing Coyote HTTP/1.1 on port 8080 Sep 23, 2003 12:55:49 PM org.apache.coyote.httpll.HttpllProtocol init INFO:. Initializing Coyote HTTP/1.1 on port 8443 Starting service Tomcat-Standalone Apache Tomcat/4.1.24 Sep 23, 2003 12:56:07 PM org.apache.coyote.httpll.HttpllProtocol star INFO: Starting Coyote HTTP/1.1 on port 8080 Sep 23, 2003 12:56:07 PM org.apache.coyote.httpll.HttpllProtocol star INFO: Starting Coyote HTTP/1.1 on port 8443 Sep 23, 2003 12:56:07 PM org.apache.jk.common.ChannelSocket init INFO: JK2: ajpl3 listening on /0.0.0.0:8009 Sep 23, 2003 12:56:07 PM org.apache.jk.server.JkMain start INFO: Jк running ID=0 time=10/111 config=K:\Tomcat24\jakarta-tomcat- Session destroyed /alue of destroyed session ID is87C2CD4E3125983D0702B84A35475A24 There are now 0 live sessions in the application. New Session created on Tue Sep 23 13:38:18 EDT 2003 ID: 2 3C53BCD7C35D15834B417C41F86DE47 There are now 1 live sessions in the application. Session destroyed value of destroyed session ID is23C53BCD7C35D15834B417C41F86DE47 There are now 0 live sessions in the application. Puc. 11.6. Извещения о создании и прекращении сеансов 11.10 Использование слушателя для наблюдения за атрибутами сеанса Задача Вы хотите использовать класс слупфтеля, чтобы получать уведомления при добавле- нии, удалении'или замене атрибутов сеанса. Решение Создайте Ja'va-класс, реализующйй интерфейс HttpSessionAttributeListener. Зарегистрируйте его с помощью дескриптора развертывания web-приложения. Использование слушателя для наблюдения за атрибутами сеанса | 279
Обсуждение Интерфейс HttpSessionAttributeListener имеет три метода: attributeAd- ded (), a^tributeReinoved () и attributeReplaced (); все принимают в качестве параметра тип HttpSessionBindingEvent. Этот слушатель уведомляется в случаях, когда устанавливается, удаляется или меняется какой-либо атрибут сеанса. Таким образом, кдасс HttpSessionAttributeListener'получает извещения, когда где-либо в дан- ном web-приложении вызываются следующие методы. HttpSession. seztAttribute {String пате, Object value). HttpSession. removeAttribute (String name). Вызов HttpSession.setAttributeO в Случае, когда атрибут с таким именем ранее уже был привязан к сеансу. Первоначалоныи атрибут заменяется, что порождает вызов метода attributeReplaced (). Код примера 11.16, когда к сеансу привязывается объект (атрибут), выводит на кон- соль сообщение, содержащее в том числе и значение атрибута (в данном случае это строка [String]). Сообщения выводятся гакже при удалении или замене атрибута. Чтобы данный слушатель был доступен в приложении, необходимо следующее. • Класс слушателя должен иметь конструктор без параметров. • Класс нужно поместить в каталог WEB-INF/classes приложения, или каталог lib (если он находится в виде JAR). • Класс необходимо объявить в дескрипторе развертывания приложения. • Перезапустите web-контейнер (если нужно), чтобы он мог создать экземпляр слушателя. Пример 11.16. Прослушивание привязки и отсоединения объектов к сеансу package com.j spservletcookbook; import j avax.servlet.*; import j avax.servlet.http.*; public class SessionAttribListen implements HttpSessionAttributeListener { //Создает новый объект SessionAttribListen public SessionAttribListen() { System.out.printin(getClass().getName());• } public void attributeAdded(HttpSessionBindingEvent se) { HttpSession session = se.getSession(lu- string id = session.getld(); String name = se.getName(); String value = (String) se.getValue(); String source = se.getSource().getClass().getName(); String message = new StringBuffer( "Attribute bound to session in ").append(source). 280 | Глава 11. Отслеживание сеанса
- ♦I ' ‘'-К J • append("\nThe attribute name: -) .append(name). append(*\n").append("The attribute value:"). append(value).append("\n"). append("The session ID: "). append(id).toString(); System.out.println(message); public void attributeRemoved(HttpSessionBindingEvent se) { HttpSession session = se.getSessionO ; String id = session.getld(); String name = se. getName (); < ' if(name == null) name = "Unknown"; r " • , String value = (String) se.getValue(); v String source = se.getSource() .getClassO .getNameO ; String message = new StringBuffer( "Attribute unbound from session in ").append(source). append("\nThe attribute name: ").append(name). append("\n").append("The attribute value: "). append(value).append("\n").append( "The session ID: ").append(id).toString(); System.out.printIn(message); } f public void attributeReplaced(HttpSessionBindingEvent se) { String source = se.getSource() .getClassO .getNameO ; String message = new StringBuffer( "Attribute replaced- in session "). append(source).toString(); System.out.printin(message); } } > Когда в web-приложении происходит добавление, замена или удаление атрибута сеанса, этот класс печатает информацию о данном атрибуте и о сеансе на консоли web-контейнера. К объекту HttpSession (сеанс), к которому происходит привязка атрибута, можно получать доступ с помощью метода getSession () класса HttpSessionBindingEv- ent. Во всех трех методах слушателя вы можете получить идентификатор этого сеанса, а.также имя и значение атрибута. В конструктор класса слушателя помещена строка кода для печати на консоль, при создании экземпляра слушателя, имени класса слушателя. System, out. print In (getClassO .getNameO ) ; Использование слушателя для наблюдения за атрибутами сеанса | 281
ЭтО сообщение информирует разработчика о том, что слушатель корректно объявлен в web.xml и что web-контейнер создал экземпляр слушателя. Работающий класс слушателя выводит на консоль сообщения о классе HttpSession (сеанс), ставшем источником произошедшего события. Для получения ссылки на источник события привязки аргумента слушатель использует метод j ava. util.EventObject.getSourceO (который наследуется объектом HttpSessionBindingEvent). String source = se. getSource ()•. getClass (). getName (); Переменная se - это объект класса HttpSessionBindingEvent. Ниже приве- дена информация, печатаемая на консоли Tomcat. Attribute bound to session in org.apache.catalina.session.Standardsession The attribute name; session-attribute ! The attribute value: Hello The session ID: 9ED2C34964778265A34F7AB0DEA4B884 Attribute replaced in session org.apache.catalina..session.Standardsession Attribute unbound from session in org.apache.catalina.session.StandardSessiori The attribute name: session-attribute The attribute value: Hello there. The session ID: 9ED2C34964778265A34F7AB0DEA4B884 Слушатель позволяет получить идентификатор сеанса, а также имя й значение атрибута сеанса в ходе удаления атрибута из сеанса. Метод HttpSessionBindingEvent. getValue () возвращает значение атрибута в виде объекта класса java. lang. Object. Таким образом, если вам в слушателе потребуется доступ к атрибуту, то придется приво- дить тип Object к подходящему типу во время добавления, удаления или замены атрибута. Допустим, у вас в сеансе хранится карта (объект класса java.util.Map) и вы хотите в методе attributeRemoved слушателя проверять содержимое этой карты. Если переменная be имеет тип HttpSessionBindingEvent, то код приведения типов для значения атрибута, возвращаемого методом be. getValue () будет выглядеть следующим образом. java.util.Map map = null; Object value = null; if((value = be.getValue()) instanceof java.util.Map){ map = (java.util.Map) value; Systern.out.printIn("HashMap value: " + map.get("key")); } Этот метод возвращает значение удаляемого аргумента, который был привязан к сеансу. Если реальный возвращаемый тип - Мар, локальная переменная приводится к типу j ava. util .Мар, а затем для полученного объекта вызывается метод get () класса Мар (считаем, что данный экземпляр класса Мар содержит ключ с именем «key»). 282 | Глава 11. Отслеживание сеанса
A См. также Главу 14 по использованию слушателей для протоколирования сообщений; рецепт 11.4 по проверке корректности сеанса; рецепт 11.9 по использованию слушателя для отслежива- ния жизненного цикла сеанса; главу 1 по web.xml', главу 7 спецификации сервлета версий 2.3 и 2.4 пр сеансам; javax. servlet.http. HttpSession API на странице http://java.sun. com/j2ee/sdk_1.3/techdocs/api/javax/servlet/http/HttpSession.html. i 11.11 Использование фильтра для наблюдения за атрибутами сеанса Задача Вы хотите использовать фильтр для проверки атрибута сеанса перед тем, как запрос попадет к сервлету. Решение Создайте Java-класс, реализующий интерфейс javax. servlet ..Filter, в метод doFilter() этого класса поместите код, работающий с сеансом, а затем скон- фигурируйте этот фильтр в дескрипторе развертывания своего приложения. Обсуждение Как следует из названия, фильтры - частично проницаемые барьеры, через которые должны пройти все запросы к web-приложению, прежде чем они достигнут сервлетов, JSP или даже статичного содержимого. Технически фильтры являются Java-классами, реализующими интерфейс javax. servlet'.Filter. Фильтр может осмотреть объекты ServletRequest (запрос) и ServletResponse (ответ) перед тем, как эти объекты попадут на обработку в методы сервлета (например, в serviced, doGet () или doPost ()). Фильтры могут иницийровать аутентификацию, протоколирование, шифрова- ние, обращение к базе данных, кэширование и практически любые другие задачи, касающиеся запросов и ответов. Настройка фильтров осуществляется в web.xml. Фильтр, приведенный в примере 11.17, проверяет некоторый атрибут logged-in (прошел регистрацию) объекта HttpSession (сеанс) и протоколирует его активность с помощью метода log () объекта ServletCon- text. Этот фильтр отображен в web.xml на сервлет, зарегистрированной там под именем MyServlet. Таким образом, любой запрос к сервлету MyServlet вызовет обращение к методу SessionFilter .doFilter () данного фильтра. В примере 11.17 показаны необходимые для этого фильтра записи в web.xml. Использование фильтра для наблюдения за атрибутами сеанса | 283
Пример 11.17. Настройка фильтра в web.xml <!— здесь должно идти начало web.xml —> <filter> < f i11er-name>SessionFi11er</fi11er~name> <filter-class>com.jspservletcookbook.SessionFilter</filter-class> </filter> <filter-mapping> < f i 11 er -namo Ses s ionFi 11 er < / f i 11 er-hame> <servlet-name>MyServlet</servlet-name> </fi1ter-mapping> <!— остальные необходимыеклассы фильтров и слушателей помещаем сюда —> <servlet> <servlet-name>biyServlet< / servlet-name> <servlet-class>com.jspservletcookbook.MyServlet</servlet-class> </servlet> <!— продолжение дескриптора развертывания ...—> Элемент filter задает имя регистрируемого фильтра и его полностью квали- фицированный Java-класс. Вы присоединяете класс фильтра к web-приложению, помещая его в каталог WEB-INF/classes приложения или в виде JAR-файла в каталог WEB-INFAib. Элемент filter-mapping отображает (ставит в соответствие) фильтр на сервлет, зарегистрированный в дескрипторе развертывания под именем MyServlet. Фильтры также можно отображать на шаблоны URL (детально этот синтаксис описан в главе 20). Исходный код сервлета com.jspservletcookbook.SessifcnF-iIter приведен в примере 11.18. Пример 11.18.Фильтр, который следит за информацией сеанса package сот.j spservletcookbook; import j avax.servlet. *; inport j avax.servlet.http.*; public class SessionFilter implements Filter { private FilterConfig config; //Создаем новый экземпляр класса SessionFilter public SessionFilter() {} public void init(FilterConfig filterConfig) throws ServletException{ Sys tern.out.printIn("Instance created of "+getClass().getNameO); this.config = filterConfig; : i public void doFilter(ServletRequest request, ServletResponSe response, Filterchain chain) throws java.io.lOException",''ServletException { 284 | Глава 11. Отслеживание сеанса
HttpSession session = ((HttpServletRequest) request).getSession(); ServletContext context = config.getServletContext(); /* используем метод ServletContext.log для протоколирования сообщений ,фильра */ context.log("doFilter called in: " + config.getFilterNameO. + " on " + (new java.util.Date())); // протоколируем идентификатор сеанса context.log("session ID: " + session.getld()); 11 Смотрим, установлен ли атрибут сеанса logged-in String logged = (String) session.getAttribute("logged-in"); if (logged «ч null) session.setAttribute("logged-in","no"); ' //заносим в протокол сообщение о состоянии процесса регистрации context.log("log-in status: + (String)session.getAttribute("logged-in”)); context.log(""); chain.doFilter(request,response); } public void destroy(){ /* вызывается перед удалением экземпляра фильтра из обслуживания в web-контейнере */ } } Любой фильтр должен иметь конструктор без параметров, как и классы слушателей. При создании экземпляра фильтра web-контейнером метод init() отображает на консоль свое сообщение. Объект javax. servlet. FilterConfig используется для получения объекта ServletContext для этого фильтра (вызовом метода Filter- Config . getServletContext ()). Метод ServletContext. log () используется для протоколирования сообщений фильтра. Затем эти сообщения можно будет прочитать в протоколах сервера. При использовании Tomcat ищите файлы протоколов в каталоге <uHcmajuinifUOHHbiu-Kamcuioz-Tonicat>Aogst где они хранятся с именами вроде localhost_ home_log.2003-01-24.txt. Ниже представлен пример записей протокола для данного фильтра. 2003-01-24 11:56:09 doFilter called in: Ses?ionFilter on Fri Jan 24 11:56:09 EST 2003 2003-01-24 11:56:09 session ID: E04DE93D9B88A974ED2350BCF7945F34 2003-01-24 11:56:09 log-in states: no Использование фильтра для наблюдения за атрибутами сеанса | 285
Фильтр получает доступ к сеансу с помощью следующего кода. HttpSession session = ((HttpServletRequest) request).getSession(); Поскольку параметр, представляющий запрос, метода doFilterO имеет тип ServletRequest, а не HttpServletRequest, его необходимо привести к послед- нему, чтобы получить возможность вызвать метод request. getSession (). Остерегайтесь делать это вслепую в среде, относительно которой нет уверенности, что все сервлеты дтносятся к классу HttpServlet. Если нет полной уверенности, проблему решит простая проверка класса перед приведением типов. После получения фильтром доступа к объекту, представляющему сеанс, он ищет требуемый атрибут сеанса (logged-in (прошел регистрацию)). Если вызов session. getAttribute (" logged-in") возвращает null, этот атрибут добавляется к сеансу и ему присваивается значение «по». Далее в коде метода doFilterO вызывается метод chain. doFi Iter (request, response). Этот метод вызывается для объекта Filterchain (цепочка фильтров), чтобы обеспечить передачу запроса и ответа далее, следующему фильтру в цепочке фильтров, или, если фильтров больше нет, то самому web-pecypcy назначения. В примере 11.19 показан метод doGet () сервлета MyServlet, на который отображен фильтр из примера 11.18. • Пример 11.19. Метод doGet () сервлета, на который отображен фильтр public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.lOException { response.setContentType("text/html"); java.io.Printwriter out = response.getWriter(); HttpSession session = request.getSession(); String logged = (String) session.getAttribute("logged-in"); out.printIn("<html>*); out.printin("<head>); out.printin("<title>Filter Servlet</title>"); out.printIn("</head>"); 4 out.printIn("<body>") > ' out.printin("<h2>Session Logged in Info</h2>"); out. print In ("logged in : " + logged* "<brxbr>"); out.println("</body>"); out.printin("</html>"); } Этот сервлет проверяет значение атрибута logged-in сеанса и отображает это значение, см. рис. 11.7. 286 | Глава 11. Отслеживание сеанса
Фильтр отображается на зарегистрированное имя сервлета следующим образом. < f111er-mapping> <filter-name>SessionFilter</filter-nanie> <servlet-name>MyServlet</servlet-name> </f i11er-mapp ing> Однако запросы к сервлету, с URL в стиле «invoker» (http://localhost:8080/servlet/ com.jspservletcookbook.MyServlet), не проходят через этот фильтр. Для решения данной проблемы рассмотрите запрет или перезапись в вашем web-приложении отображения на URL /servlet/*. В рецепте 3.6 описано, как это делается. Рис. 11.7. Проверка объекта, представляющего сеанс, после того, как он был обработан фильтром До того как запрос достигнет сервлета или JSP, выполняющих отслеживание сеанса, фильтр может произвести целый ряд действий над объектом, представляющим сеанс, например добавить, удалить или изменить атрибуты сеанса. Он также может изменить время простоя сеанса (с помощью метода "HttpSession. setMaxInactivelnter- val (int seconds)) в зависимости от атрибута сеанса или запроса. См. также Главу 19 по использованию фильтров; рецепт 11.4 по проверке корректности сеанса; главу 1 по web.xml; главу 7 спецификации сервлета версий 2.3 и 2.4 по сеансам; j avax. servlet .http.HttpSession API на странице http://java.sun.com/j2ee/sdk_L3/tech- docs/api/javax/servlet/http/HttpSession.html; главу 1 no web.xml; главу 6 спецификации сервлетов версии 2.3 и 2.4 по фильтрам. Использование фильтра для наблюдения за атрибутами сеанса | 287
ГЛАВА 12 Интеграция JavaScript с сервлетами и JSP 12.0 Введение JavaScript - система сценариев для web-страниц, самостоятельных приложений и серверов. Она была разработана корпорацией Netscape и быстро стала Столь популярным и полезным инструментом программирования, что в настоящее время ее поддержку включают почти все браузеры. В отличие от Java-кода, рассматриваемого и этой книге, JavaScnpt выполняется, главным образом, в браузере, как система сценариев, действующая на стороне клиента, а не на стороне сервера. Большинство активно действующих web-сайтов используют JavaScript для прида- ния странице динамического поведений, например для проверки корректности дан- ных вводимых в форму, или для создания нового окна в браузере (к большому неудовольствию пользователей, которых часто заваливают безответственными динамически генерируемыми всплывающими окнами). Выберите в меню браузера команду «Просмотр HTML-кода» для типичной web-страницы, и часто первое, что бросается в глаза, - многочисленные строки JavaScript. JavaScript используют для решения продвинутых задач, например для анимации изображений в браузере (динамический HTML), создания летающих объектов и инициализации поведения встроенных видеороликов. Разработчики, преобразующие статичные страницы в JSP или сервлеты, имеют возможность интегрировать код JavaScript в свой исходный Java-код. Именно об этом пойдёт речь в рецептах данной главы. Руководство по JavaScript находится по адресу http://devedge.netscape.com/. 288
12.1 Включение модулей JavaScript в сервлет Задача Требуется импортировать модуль или файл, содержащий код JavaScript, с тем чтобы этот код мог быть включен в выходной HTML сервлета. / Решение Для импорта кода JavaScript в свою страницу воспользуйтесь методом javax. servlet.RequestDispatch.include(). Обсуждение \ Эффективным приемом работы c JavaScript в web-проекте является хранение кода JavaScript в отдельных файлах или модулях. Сервлеты, которым потребуются функции JavaScript, могут затем импортировать эти модули JavaScript. Вы не станете хранить файлы созданных вами Java-классов в своей файловой системе где попало, без их упорядочения в соответствии с целями данного кода. Точно так же не стоит хранить код JavaScript иначе, как в хорошо определенных модулях. JavaScript включается в выходной HTML сервлета с помощью тегов script и, при необходимости, выполняется в браузере. В примере 12.1 приведен код модуля JavaS- cript с именем functions. js. Этот модуль хранится в каталоге WEB-lNF/javascript web- приложения. Логично хранить все модули JavaScript в специальном отдельном каталоге, с тем чтобы их можно было легко найти и чтобы не загромождать каталог верхнего уровня web-приложения. Каталог для JavaScript может быть подкаталогом WEB-INF, как в данном рецепте. Пример 12.1. Модуль JavaScript <script language="JavaScript"> function CheckEmai1(emai1) { var firstchunk,indx,secondchunk if (email == ""){ alert("Please make sure you have entered.a valid " + "email before submitting the info.") return false } //получаем индекс символа (считая от нуля) indx = email.indexOf("@") 10-1545 Включение модулей JavaScript в сервлет | 289
//если символа @ нет в строке, возвращаем false if (indx == -1 ){ alert("Please make sure you have entered a valid " + "email before submitting the info.") return false } //если первая часть email< 2 символов и вторая < 7 символов //(волюнтаристский, но работающий критерий), адрес отметаем firstchunk = email.substr(0,indx)’ //до символа "6" не включая его' //от "@" 'Хне включая)до конца адреса secondchunk = email, substr (indx + 1) »' //если во второй части нет точки то //также возвращаем false if ((firstchunk.length < 2 ) || (secondchunk.length < 7) •(secondchunk.indexQf(".") == -1)){ alert("Please make sure you have entered a valid " + "email before submitting the info.") r.eturn false J //корректный email; в нем есть символ @, имя пользователя длиннее 1 //символа, более б символов после "@" //и подстрока после "@" содержит точку ".* return true }//CheckEmail function CreateWindow(uri) { var newWin = window.open(uri,•newwinl•, ’width=500,height=400,resizable,1 + 'scrollable,scrollbars=yes'); newWin.focus(); v > </scripts Модуль из примера 12.1 содержит блок script с определением двух функций Java- Script. Функция CheckEmail проверяет введенный пользователем в HTML-форму адрес электронной почты - содержит ли он символ @, два символа перед ним и семь символов после него, включая точку (.). Функция CreateWindow создает новое окно браузера для переданного ей URL , В примере 12.2 показан сервлет, который импортирует данный файл JavaScript с помощью метода javax. servlet. RequestDispatcher. include (). 290 | Глава 12. Интеграция JavaScript с сервлетами и JSP
Пример 12.2. Сервлет, включающий файл JavaScript package com.j spservletcookbook; import j avax.servlet.*; import javax.servlet.http.*; public class ModuleServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse respons e) throws ServletException, java.io.lOException { response.setContentType("text/html*); java.ioiPrintwriter but = response.getWriter(); out. printin (" <jhtmlxhead>") ; RequestDispatcher dispatcher я request.getRequestDispatcherf "/WEB-INF/ javascript/functions.j s"); dispatcher.include(request, response); out .printin ("<title>Client Forms</titlex/head><body>“) ; out.printlnf"<h2>Enter Your Name and Email</h2>"); out.printInf"<form action» ' \"/home/displayHeaders.jsp\" name»\"entryForm\" onSqbmit* \" return CheckEmailfthis.email.value)\">"); out .println( "<table border=\"0\"xtrxtd valign=\"top\">") ; out.printlnf "First and last name: </td> <td valign=\"top\"xinput type= \"text\" name»\" name\" size=\"20\"x/tdx/tr>") ; •' out. print Inf "< trx td valign=\"top\">"); out.printIn("Email: </td> <td valign=\"top\"xinput type=\"text\" name= \"email\" size-\"20\"x/td>"); out .printin ("<trxtd valign=\ "top\"xinput type= \"submit\" value»\"Submit\" x/td>") ,- out. print In (" < / trx / tablex / f orm>") ; out. print Inf" </bodyx/html>'') ; } I/doGet } Включение модулей JavaScript в сервлет | 291 10*
Сервлет из примера 12.2 для включения кода, содержащегося в модуле functions.js, использует Request Dispatcher внутри генерируемого сервлетом HTML-тега head. Генерируемая страница включает также HTML-тег form. Когда пользователь щелкает кнопку Submit, обработчик собьпгия onSubmit тега form проверяет адрес e-mail, введен- ный пользователем в форму, используя импортируемую функцию JavaScript CheckEmail. Эта функция возвращает false, отменяющее отправку данных формы, если адрес не отвечает простым критериям, заданным в функции. Внешний вид web-страницы при заполнении пользователем поля адреса, показан на рис. 12.1. Рис. 12.1. Сервлет генерирует web-страницу, содержащую JavaScript Также для импорта модуля JavaScript можно использовать встроенный атрибут src HTML-тега script. 4 • <script src=" functions. js">- См. также Сайт DevEdge фирмы Netscape http://devedge.netscape.com/’, рецепты 12.2, 12.4, и 12.6 по использованию JavaScript с JSP; рецепт 12.3 по использованию JavaScript в сервлетах для создания новых окон браузера; рецепт 12.5 по верификации данных, вводимых в форму, с сервлетом и JavaScript; рецепт 18.3 по использованию фильтра с НТТР-запросами. 292 | Глава 12. Интеграция JavaScript с сервлетами и JSP
12.2 Включение модулей JavaScript в JSP Задача i Требуется включить модуль или файл, содержащий код JavaScript в вывод страницы JSP. Решение Используйте тег ядра JSTL с: import. Обсуждение В предшествующем рецепте мы обсудили, как включить файл с кодом JavaScript (пример 12.1) в выходной HTML сервлета. Эту же задачу очень легко выполнить и в JSP, и такая страница importModjsp показана в примере 12.3. В нее, с помощью тега ядра JSTL с:import, включается файл functions.js. Модуль fiuictions.js содержит тег script с определениями двух функций (см. пример 12.1 рецепта 12.1). Из HTML, генерируемого этой страницей JSP, видно, что действие с: import помещает тег script в тег head JSP. Страница также генерирует HTML-форму, вид которой ранее был показан на рис. 12.1. Пример 12.3. Использование тега с: import для импорта JavaScript <%@ taglib uri»"http://java.sun.com/jstl/core" prefix»"c" %> <html> <head> <c:import url»"/WEB-INF/javascript/functions.js" /> <title>Client Forms</t itlex/headxbody> <h2>Enter Your Name and Email</h2> <form action="/home/displayHeaders.jsp" name»"entryForm" onSubmit»"return CheckEmail(this.email.value)"> <table border="0"xtrxtd valign="top"> First and last name: </td> <td vallgn="top"xinput type="text" name="name" size=* 20" х/tdx/tr> <trxtd valign="top"> Email: </td> <td valign="top"xinput type="text" name="email" size="20**x/ tdx/tr> ctrxtd valign="top"xinput type="submit" value="Submit"></td> </trx/table> </form> </bodyx/html> Включение модулей JavaScript в JSP | 293
Когда пользователь заполняет эту HTML-форму, ее действие прерывается обработчиком события onSubmit тега form, который производит простую проверку синтаксиса адреса электронной почты, введенного пользователем в форму. Передача данных, введенных в форму и предназначенных для страницы Люте/displayHeaders.jsp, отменяется, если вве- денный адрес имеет ошибочный синтаксис. Код this. email .value (из JavaScript) возвращает строку, введенную пользователем в текстовое поле с именем email. Ключевое слово this ссылается f у на объект form', содержащий обработчик события onSubmit. С помощью приведенного кода JavaScript эта строка передается в качестве параметра обработчику события. / См. также Сайт DevEdge фирмы Netscape http://devedge.netscape.com/., рецепт 12.4 по использо- ванию JavaScript для создания нового окна из JSP; рецепт 12.6 по верификации с помощью JavaScript данных, вводимых в форму в JSP; рецепт 12.3 по использованию JavaScript в сервлетах для создания новых окон браузера; рецепт 12.5 по проверке дан- ных формы в сервлетах с помощью JavaScript; рецепт 18.3 по использованию фильтра с HTTP-запросами. ' _ 12.3 Создание из сервлета, с помощью JavaScript, нового окна Задача Вы хотите включить в сервлет код JavaScript для генерирования нового окна браузера. Решение Для включения необходимой функции JavaScript в сервлет используйте javax. servlet. RequestDispatcher. Сама функция JavaScript вызывает метод open объ- екта window. / Обсуждение В данном рецепте импортируется тот же самый модуль, что и в первых двух, но на этот раз сервлетом используется вторая из функций модуля (CreateWindow). В примере 12.4 создается HTML-кнопка. Когда пользователь щелкает по этой кнопке, JavaScript генерирует небольшое окно (называемое всплывающим окном или windo id). URL для загрузки информации в это окно, сервлет динамически извлекает из параметра init, чего нельзя сделать со статической HTML-страницей. 294 | Глава 12. Интеграция JavaScript с сервлетами и JSP
Пример 12.4. Сервлет, загружающий код JavaScript для создания окна package com.j spservletcookbook; import javax.servlet.*; - import javax.servlet.http.*; public class WindowServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.lOException { //Получаем URL для всплывающего окна String url » getlnitParameter("popup-url"); //если параметр не задан if (url == null || url.equals("")) url = "/displayHeaders.jsp"; //добавляем путь к контексту в начало URL, натфимер /home •url request.getContextPath() + url; response.setContentType("text/html"); java.io.Printwriter out = response.getWriter(); out. print In ("<htmlxhead>") ; RequestDispatcher dispatcher request.getRequestDispatcher( "/WEB-INF/javascript/functions.js"); di spatcher.include(request, response); out.printin("<title>Help Page</titlex/headxbody>"); out.printin("<h2>Cookie Info</h2>"); out.println("<form action =\"\" onSubmit=\" return false\">"); out. print In ("Kt^ble border=\"0\"xtrxtd valign=\"top,\">"); out.printIn( "Click on the button to get more info on cookies: </td>"); Out.printlnf"<td valign=\"top\">"); out.printIn("<input type=\"button\" name»\"buttonl\" " + "value»\"MOre Info\" onClick»\"CroateWindow(• + url + "') \"x/tdx/tr>"); out. printin ("</tablex/form>") ; out.printin("</bodyx/html>") ; } //end doGet } Создание из сервлета, с помощью JavaScript, нового окна | 295
Этот сервлет требует некоторой настройки в дескрипторе развертывания. А именно - ука- зания в параметре init-param значения URL для загрузки информации во вновь создавае- мое окно. Переменная url с этим значением является параметром функции CreateWindow (см. определение функций JavaScript в примере 12.1). Сервлет генерирует необходимый HTML и динамически задает URL, указывающий, какую информацию необходимо загрузить в новое окнд. Вот как выглядит определение HTML-кнопки в выходном HTML сервлета. <input type="button" name="buttonl" value="More Info" • onClick="CreateWindow('/home/cookieReader.jsp’)"> • ’ Если web-приложение является динамически перезагружаемым (web-контейнер следит за дескриптором развертывания и'при любом его изменении перезагружает *•’ контекст), то разработчик может изменять значение параметра init-param в дескрипторе развертывания ни о чем не беспокоясь. Во всплывающее окно, создаваемое сервлетом, информация будет загружаться в соответствии с новым URL, безо всякой перекомпиляции сервлета, или остановки сервера. Вот как выглядит настройка данного сервлета в web.xml. <servlet> <servlet-name>windowservlet</servlet-name> <servlet-class>com.jspservletcookbook.WindowServlet</servlet-class> <init-param> <param-name>popup-ur1< /param-name> <param-value>/cookieReader.j sp</param-value> </init-param> </servlet> Сервлет загружает определение функции CreateWindow с помощью следующего кода. RequestDispatcher dispatcher = request.getRequestDispatcher( "/WEB-INF/javascript/functions.js"); В HTML-коде, генерируемом сервлетом, тег script, содержащий код JavaScript, появля- ется внутри HTML-тега head. Когда пользователь щелкает по кнопке формы, находящейся на сгенерированной сервлетом странице, создается новое окно и в него загружается страница, в соответствии с URL, заданным в параметре init-param в web.xml (cookieReader.jsp). Вывод сервлета показан на рис. 12.2. А на рис. 12.3 показано само всплывающее окно. См. также Сайт DevEdge фирмы Netscape http://devedge.netscape.com/., рецепты 12.2, 12.4 и 12.6 по использованию JavaScript с JSP; рецепт 12.5 по верификации данных, вводимых в форму, с сервлетом и JavaScript; рецепт 18.3 по использованию фильтра с H lTF-Запросами. 296 | Глава 12. Интеграция JavaScript с сервлетами и JSP
Рис. 12.2. Сервлет, создающий всплывающее окно http://localhost 8080/home/cookieReader jsp - Microsoft Internet Ex... asG The name and value of each found cookie Cookie name: JSESSIONID Cookie value: 6AE2A572D8B67815834C03958FAE4627 Puc. 12.3. Вновь созданное окно загружает значение URL из параметра ini t-param Создание из сервлета, с помощью JavaScript, нового окна | 297
12.4 Создание нового окна из JSP при помощи JavaScript Задача Вы хотите из JSP при помощи JavaScript создать новое окно. Решение Для импорта кода JavaScript в JSP воспользуйтесь JSTL-тегом с:import. Затем, с помощью неявно создаваемого JSTL-объекта initParam, динамически задайте URL для окна, Генерируемого кодом JavaScript. Обсуждение JSP из примера 12.5 (yvindowJ.jsp) использует JSTL-тег с: import для импорта опреде- ления функции JavaScript, создающей новое окно. Затем JSP вызывает эту функцию (Cre- ateWindow) в обработчике события onClick кнопки web-страницы. Функция CreateWindow загружает URL, заданный ее параметром, в новое окно браузера. Для динамического извлечения этого значения URL из параметра контекста в примере 12.5 используется тег ядра с: out и синтаксис EL. Тег с: out выглядит следующим образом. <c:out value= 1 "${pageContext.request.contextPath}${initParam[\"j sp-url\"]}"/> В атрибуте value заданы два EL-выражения. Первое указывает путь к контексту, а второе - значение параметра контекста jsp-url, заданное в элементе context-param. Полный URL, получающийся в результате сцепления значений этих выражений EL, равен / home/cookieReader.jsp. Пример 12.5. Использование JSTL для импорта кода JavaScript в JSP <W taglib uri»"http://java.sun.com/jstl/core" prefix»"с" %> <html> <head> <c:import url»"/WEB-INF/javascript/functions.js" /> < t i 11 e>Help Page< /1 i 11 e>< /headxbody» <h2>Cookie Info</h2> <form action ="" onSubmit=" return false*» <table border="0"xtrxtd valign="top"> Click on the button to get more info on cookies: </td> <td valign="top"> -«input type» "button" name» "buttonl" value» "MOre Info" onClick» 298 | Глава 12. Интеграция JavaScript с сервлетами и JSP
"CreateWindow( * <с: out values 1 ,• 4 "${pageContext.request.contextPath}${initParam[\"jsp-url\"J}"/>')"> </tdx/tr> < /table></form> < /bodyx / html > Эта страница JSP использует следующий элемент context-param в файле web.xml. <context-param> <param-name>j sp-url</param-name> <param-value>/cookieReader.j sp</param-value> < /context-param> Неявно создаваемый EL-объект init Par am приводится к объекту типа java, util .Map, содержащему имена и значения всех элементов context-param, задан- ных для этого web- приложения. Неявно создаваемый объект - это переменная, которую JSTL автоматически создает и делает доступной для кода JSP. В примере 12.5 используется EL-синтаксис initParam[\"jsp-url\"], вместо initParam. jsp-url, чтобы вернуть необходимое значение в Tomcat 5 (на момент написания книги - альфа-версия). Цель кода - избежать символа дефиса (-) в «jsp-url». См. также Сайт DevEdge фирмы Netscape http://devedge.netscape.com/; рецепты 12.2 и 12.6 по использованию JavaScript в JSP, импорту кода JavaScript и проверке данных вводи- мых в форму; рецепт 12.3 по использованию JavaScript в сервлетах для создания новых окон браузера; рецепт 12.5 по проверке данных формы в сервлетах с помощью JavaS- cript; рецепт 18.3 по использованию фильтра с НТТР-запросами. 12.5 Использование JavaScript в сервлете для проверки данных, введенных в форму Задача Вы хотите в сервлете использовать JavaScnpt для проверки данных, введенных в форму. Решение Для включения в сервлет кода функции JavaScript, осуществляющей проверку, используйте ’javax. servlet .RequestDispatcher. Затем вызывайте- функцию проверки в обработчике события onSubmi t формы’ Использование JavaScript в сервлете для проверки данных, введенных в форму | 299
Обсуждение В примере 12.6 приведен модуль JavaScript с именем validate.js. Этот файл должен находится в WEB-INF/javascript/validate.js. Файл содержит тег script, включающий определение одной единственной функции: validate. Эта функция JavaScript перебирает в цикле элементы формы (такие, как теги input, чьи атрибуты type содержат значение text, то есть текстовые поля формы), определяя, нет ли среди них незаполненных Функ- ция validate принимает в качестве параметра объект form. Если пользователь оставил какие-либо поля незаполненными, функция отображает окно С предупреждением и отменяет передачу данных формы. В реальных функциях проверки реализуется более сложная бизнес-логика, но я сделал этот пример как можно более простым, чтобы сосредоточить внимание на механизме включения функции в сервлет. Пример 12.6. Модуль JavaScript validate.js для проверки ввода в форму <script languages"JavaScript"> function validate(forml) ( for (i =0; i < forml.length; i++){ if ( (forml.elements[i].value == "")){ alert("You must provide a value for the field named: " + forml.elements[i].name) return false } } return true } </script> .Сервлет из примера 12.7 включает файл validate.js с помощью RequestDispatcher. Этот RequestDispatcher помещает JavaScript-тег script внутрь HTML-тега head в выходном HTML-коде, генерируемом сервлетом. Тег form страницы, формируемой сервлетом, имеет атрибут, который складывается из пути к контексту (значение которого возвращается методом request. getCoritextPath ()), сцепленного с файлом JSP: /dis- playHeaders.jsp. Если поля формы заполнены правильно, браузер посылает данные, введен- ные в форму этой странице JSP (/home/displayHeaders.jsp). И, наконец, обработчик события onSubmit вызывает включенную JavaScript- функцию validate, передавая ей в качестве параметра ключевое слово JavaScript this. Параметр this приводится к объекту типа form. Если пользователь ошибется при заполнении полей name и email, функция validate отменит пересылку данных браузером, возвратив false. V 300 | Глава 12. Интеграция JavaScript с сервлетами и JSP
Пример 12.7. Сервлет, импортирующий код JavaScript для проверки данных, введенных в форму package com.j spservletcookbook; import j avax-. servlet. *; import j avax.servlet.http. *; public class FormServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.lOException { response.setContentType("text/html"); java.io.Printwriter out = response.getWriter(); out. printin (" <htmlxhead> " ) ; RequestDispatcher dispatcher request.getRequestDispatcher( "/WEB-INF/javascript/validate.js"); > dispatcher.include(request, response); out.printin("<title>Help Page</title></head><body>"); out.printin("<h2>Please submit your information</h2>"); out.printin( x "<form action =\"" + request.getContextPath() + "/displayHeaders.jsp\" onSubmit=\" return validate(this)\">"); out .printin ("<table border=\"0'\”xtrxtd valign=\"top\">"); out. printing Your name: </td> <tdvalign=\“top\">"); out.pr±ntln("<input type=\"text\" name=\"username\" size=\"20\">"); out.println( "</td></tr><trxtd valign=\"top\">"); out.printIn("Your email: </td> <td valign=\"top\">"); out.printlnf"<input type=\"text\" name=\"email\" size=\"20\">"); out.printin("</tdx/tr><trxtd valign=\"top\">"); out.println( "cinput type=\"submit\ " value*\"Submit Info\"x/tdx/tr>") ; out.println("</tablex/forro>"); out.£>rintln( "</bodyx/html>4) ; } //doGet * r } Вид страницы с формой в браузере показан на рис. 12.4. Окно с предупреждением, генерируемое функцией JavaScript, приведено на рис. 12.5. Другую возможность проверить данные, введенные в форму, предоставляет исполь- зование фильтра для проверки значений параметров, после чего можно вернуть пользо- вателя на страницу с исходной формой или на новую страницу, если во введенных данных есть ошибка. Разработчикам больше подойдет именно этот вариант, поскольку Использование JavaScript в сервлете для проверки данных, введенных в форму | 301
Рис. 12.4. HTML-форма, генерируемая на выходе сервлета Рис. 12.5. JavaScript-функция проверки выдает окно с предупреждением фильтр позволяет для разбора значений параметров использовать Java-код и дает вам большие возможности по контролю над настройкой страницы, формируемой в ответе, в случае, если при заполнении формы допущена ошибка. Использование фильтра с сервлетом для работы с запросами клиента описано в рецепте 18.3. См. также Сайт DevEdge фирмы Netscape http://devedge.netscape.com/-, рецепты 12.2 и 12.6 по использованию JavaScript в JSP, импорту кода JavaScript и проверке данных вводи- мых в форму; рецепт 12.3 по использованию JavaScript в сервлетах для создания новых окон браузера, рецепт 12.5 по проверке данных формы в сервлетах с помощью JavaS- cript; рецепт 18 3 по использованию фильтра с НТТР-запросами. 302 | Глава 12. Интеграция JavaScript с сервлетами и JSP
12.6 Использование JavaScript в JSP для проверки значений, введенных в форму Задача Вы хотите импортировать код JavaScript в JSP для проверки значений, введенных в HTML-форму. Решение Для импорта определений функций JavaScript используйте тер ядра JSTL с: import. Затем проверьте введенные в форму данные с помощью обработчика события onSubmit тега form. Обсуждение Страница JSP из примера 12.8 использует тег ядра JSTL с: import для включения содержимого файла /WEB-INF/javascript/validate.js. Содержимое файла validate.js - опреде- ление функции validate - приведено в примере 12.6. Эта функция выясняет, не оставил ли пользователь какие-либо поля незаполненными. Остальная часть JSP очевидна: обработчик события onSubmit вызывает функцию validate и передает ей в качестве параметра объект form (используя для этого ключевое слово JavaScript this). Возвращая false (в случае если на форме остались незаполненные поля), функция validate отменяй отправку данных, введенных в форму. Пример 12.8. Страница JSP, использующая JavaScript для проверки данных, введенных в HTML- форму <%@ taglib uri="http://java.sun.com/jst1/core" prefix*"c" %> -<html> <head> <c:import url»"/WEB-INF/javascript/validate,js" /> <title>Help Page</titlex/headxboay> <h2>Please submit your informationsZh2> <form action ""/hcme/displayHeaders.jsp" onSubmit"" return validate(this)"> <table border="0"xtrxtd valign=Htop"> Your name: </td> <td valign="top"> , <input type="text" name="username" size="20"> </tdx/tr*trxtd valign="top"> Your email: </td> <td valign="top"> Использование JavaScript в JSP для проверки значений, введенных в форму | 303 г
<input type=“text" name="email" size=*20"> </tdx/tr> <tr><td valign="top"> » ; л .. . • <input type="submit" value="Submit Info"></tdx/tr> < / tablex / f orm> </bodyx/html> Вад web-страницы с формой в браузере показан на рис. 12.4. Окно с предупреждением, генерируемое, если пользователь оставил одно или несколько текстовых полей пустыми, приведено на рис. 12.5. См. также ; Сайт DevEdge фирмы Netscape http://devedge.netscape.com/:, рецепты 12.2 и 12.4 по использованию JavaScript в JSP, импорту определений функций JavaScript и соз- данию нового окна браузера; рецепт 12.3 до использованию JavaScript в сервлетах для создания новых окон браузера; рецепт 12.5 по проверке данных формы в сервлетах с помощью JavaScript; рецепт 18.3 по использованию фильтра с НТТР-запросами. 304 | Глава 12. Интеграция JavaScript с сервлетами и JSP
ГЛАВА 13 Отправка не-HTML содержимого 13.0 Введение Большинство web-сайтов предлагают посетителям информацию, представленную в самых разных форматах. Типичный набор наших дней включает файлы PDF (Porta- ble Document Format - формат для перемещения документов), документы, созданные в текстовых редакторах, аудиофайлы, фильмы и XML-файлы (Extensible Markup Lan- guage - расширяемый язык разметки). В ряде случаев эти альтернативные типы фай- лов хранятся в базах данных в двоичном виде, то есть как вербница некодированных байтов. Web-разработчики не всегда могут обеспечить пользователей простыми гиперссылками для загрузки этих файлов. Пользователь выбирает ссылку или вводит URL в поле адреса браузера и сервлет, или другой web-компонент начинает отправку двоичных данных клиенту. В большинстве случаев клиент сохраняет эти данные в виде файла, для последующего просмотра или использования в соответствующих приложениях. Приведенные здесь рецепты описывают, как инициировать такую отправку дан- ных. В типичных случаях сервлет задает операцию загрузки, в результате чего браузер открывает перед пользователем диалоговое окно «Сохранить как», позволяя ему сохранить файлы в своей файловой системе. Здесь, однако, не гарантируется 100% совместимость поведения всех браузеров. Некоторые из них позволяют пользо- вателю точно настроить то, как именно он хочет обработать определенные типы фай- лов (например, PDF-документы). Так Opera 5 предоставляет пользователю все виды настроек загрузки файлов, в том числе открытие во внешнем вспомогательном прило- жении, отображение файла с помощью лацстройки (plug-in) или немедленную загрузку файла в специальный каталог, без предварительного появления окна «Сохранить как». Всегда тестируйте работу своего web-приложения с различными браузерами, при разных настройках одного браузера, а также на разных платформах, и вы X Д‘ будете знать о вариантах реакции браузера на ваш сервлет. 305
Альтернатива этим стратегиям - просто обеспечить ссылку на PDF-файл, если, к примеру, данные существуют в виде файла. Этот метод, однако, также в значительной степени зависим от возможностей браузера клиента и пользователя, который настраивал этот браузер для работы с этим форматом данных. И если продвинутым пользователям такой подход может понравиться, он вряд ли устроит менее искушенных пользователей. 13.1 Отправка PDF-файла Задача Требуется отправить пользователю двоичные данные, представляющие PDF-файл. Решение Для чтения байтов из исходного файла используйте сервлет и объект java.io. BufferedlnputStream. Для отправки содержимого файла пользователю воспользуй- тесь объектом javax. servlet.ServletOutputStream, полученным от объекта j avax. servlet. http. HttpServletResponse. Обсуждение PDF-файлы широко распространены в Web, поскольку большинство пользователей обладают приложением Adobe Reader, позволяющим их читать. Сервлет из примера 13.1 1 получает имя файла из строки запроса, являющейся частью запроса к сервлету, и отвечает двоичными данными, представляющими PDF-файл. Сервлет с помощью заго- <, ловка ответа Content-Туре определяет файл, посылаемый клиенту как имеющий МШЕ-тип application/pdf. MIME (Multipurpose Internet Mail Extensions - многоцелевые расширения Интернет-почты) служат для обозначения различных типов данных, которые отправляются по электронной почте или в составе HTTP-ответов в Web. Тип MIME состоит из Типа верхнего уровня и подтипа, разделяемых прямым слэшем, например text/html, application/pdf или audio/mpeg. Заголовок ответа Content- Туре - способ, используемый в ответе сервера Для уведомления о типе и формате данных, посылаемых сервером сетевому клиенту, например браузеру. Для бодее подробной информации о MIME обращайтесь к соответствующим техническим документам (RFC): ftp://ftp.rfc-editor.org/in-notes/rfc2045.txt и ftp://ftp.rfc-editor.org/ in-notes/rfc2046. txt. В табл. 13.1 приведен ряд типов MIME, которые могут потребоваться web- разработчику. 306 | Глава 13. Отправка не-HTML содержимого
Табл. 13.1. Наиболее распространенные MIME-типы Файл MIME-тип Расширение XML text/xml .xml HTML ( text/html .html Текстовый файл без форматирования (plaintext) text/plain .txt PDF application/pdf .pdf GIF image/gif •gif JPEG image/jpeg •jpeg PNG image/png •png Музыка MP3 audio/mpeg .mp3 Анимация Shockwave Flash application/futuresplash или application/x-shockwave-flash .pdf Документ Microsoft Word application/msword .swf Таблица Excel application/vnd.ms-excel ,xls Документ PowerPoint application/vnd.ms-powerpoint .ppt Запрос к сервлету выглядит примерно следующим образом: http://localhost:8080/home/sendpdf?file=chapter5 Сервлет из примера 13.1 проверяет, корректен ли параметр запроса file, и затем Добавляет к имени файла расширение .pdf, если его там не было. Это имя файла предла- гается в HTTP-ответе браузеру (оно появится в качестве предлагаемого по умолчанию в окне «Сохранить как»). Джейсон Хантер в книге «Java Enterprise Best Practices» (O'Reilly) указывает, что зачастую полезно включать это имя файла (то, которое появляется в диалоге «Сохранить как» браузера) непосредственно в URL, в качестве дополнительной информации о пути. Браузер опознает это имя как запрошенный ресурс и сможет задать его в Диалоговом окне «Сохранить как». URL в этом случае будет выглядеть примерно следующим образом: http://localhost:8080/home/chapter5.pdf?file=ch5 Пример 13.1. Отправка PDF-файла в виде двоичных данных package com.jspservletcookbook; import\java.io.Fileinputstream; import java.io.BufferedlnputStream; import java.io.File; import j ava.io.lOException; Отправка PDF-файла | 307
import javax.servlet. *; import j avax.servlet.http.*; public class SendPdf extends HttpServlet { publid void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, lOException { //получаем имя файла из параметра "file" String fileName = (String) request.getParameter("file") ; if (fileName == null || fileName.equals("")) throw new ServletException( ' "Invalid or non-existent file parameter in SendPdf servlet."); // добавляем расширение .pdf если его нет ’ if (fileName.indexOf("-pdf") == -1) fileName = fileName + ".pdf"; * //где хранятся PDF-файлы? String pdfDir « getServletContext().getlnitParameter("pdf-dir"); if (pdfDir « null || pdfDir.equals("")) throw new ServletException ( "Invalid or non-existent 'pdfDir' context-param."); ServletOutputStream stream => null; BufferedlnputStream buf « null; try{ stream response.getOutputStream(); File pdf new File(pdfDir + "/" + fileName); //задаем заголовки ответа response.setContentType("application/pdf"); response.addHeader( , "Content-Disposition","attachment; filename="+fileName ); response.setContentLength( (int) pdf.length() ); FileinputStream input new FileinputStream(pdf); buf в new BufferedlnputStream(input); int readBytes "0; / • ' //читаем из файла; пишем в ServletOutputStream while((readBytes “ buf.read(J) ! -1) Stream.write(readBytes); } catch (lOException ioe){ throw new ServletException(ioe.getMessage()); } finally { 308 | Глава 13. Отправка не-HTML содержимого
//закрываем входной/выходной потоки if (stream ! null) stream.close(); if (buf Is null) buf.close();. 1 } //end doGet public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, lOException { doGet(request,response); } } В примере 13.1 каталог, где хранятся PDF-файлы, определяется из содержимого I элемента context-param в дескрипторе развертывания. xcontext-param» <param-name>pdf-dir< /param-name» <param-value»h:Zbook/distribute</param-value» </context-param> Помните, что элементы context-param в web.xml, по версии 2.3 API сервлетов, должны располагаться перед элементами filter, filter-mapping, listener ? f и servlet. Затем код получает объект ServletOutputStream (поток) из объекта HttpServ- letResponse. В этот поток записываются двоичные данные, представляющие PDF-файл. stream = response.getOutputStreamO ; В сервлете не используется объект java.io. Printwriter, например response. getWriter (), поскольку Printwriter предназначен для возврата символьных дан- ных (например, HTML), которые браузер отображает на экране компьютера. Сервлет из примера 13.1 добавляет заголовки ответа, помогающие удержать браузер от попытки отобразить посылаемые байты в качестве содержимого окне браузера. response.setContentType("application/pdf"); response.addHeader( "Content-Disposition","attachmentf filenames"+fileName ); response.setContentLength( (int) .pdf.length() j; Поле заголовка Content-Disposition предлагает клиенту рассматривать получаемое содержимое как вложение, а не как символы для отображения в браузере. Этот дополнитель- ный заголовок ответа также содержит рекомендуемое имя файла, которое браузер может включить в качестве имени, предлагаемом по умолчанию в окне «Сохранить как». Отправка PDF-файла | 309
За подробной информацией по заголовку Content-Disposition обращайтесь к RFC 2183: ftp://ftp.rfc-ed.itor org/in-notes/rfc2183.txt. Браузер клиента может использовать значение длины передаваемого содержимого, из заголовка ответа Cont ent-Length, для демонстрации пользователю соотношения объема уже принятой информации к общему ее объему, с помощью соответствующего визуального компонента. В сервлете также используется объект java. io. Bufferedln- putStream - для буферизации читаемой из файла информации в байтовом массиве (byte [ ]), что ускоряет пересылку данных от сервера клиенту. За примером использования java'net. URLConnection (как альтернативы использованию Fileinput Stream) для'получения входного потока, связанного с web-ресурсом, обращайтесь к примеру 13.5. URLConnection полезен, когда вы хотите получать двоичные данные из PDF-файла, который доступен только по web-адресу, начинающемуся с http://. Код закрывает ServletOutputStream и BufferedlnputStream в блоке finally, освобождая все системные ресурсы, используемые этими объектами. Код блока finally выполняется всегда, независимо от того, произошли ли какие-либо исключения. При обращении к сервлету этого рецепта, из Internet Explorer 5.5, в нем обычно возникает исключение, которое отражается в файле журнала (log) Tomcat. Это запротоколированное исключение не нарушает работы программы, оно не возникает при запросе сервлета из’ Opera 5 или Internet Explorer 5.2 и браузеров Safari (Macintosh). Сообщение об исключении включает текст «Connection reset by peer: socket write error» (Соединение разорвано обратившейся стороной: ошибка записи сокета). Это сообщение вызвало предположение в разных списках рассылки, связанных с серверами, о том, что этот Windows-браузер IE приводит к возникновению исключения, разрывая соединение с Tomcat после передачи данных. Никто пока не придумал более подходящего решения по поводу этого, по- видимому, безобидного исключения, чем предложение настроить механизм протоколирования контейнера сервлетов на игнорирование исключений этого типа. См. также Рецепты 13.2-13.4 по отправке файлов WORD, XML и MP3 в виде двоичных данных; , рецепт 13.5 по получению входного потока,, представляющего web-pecypc, например web. xtnb, технические документы (RFC) по MIME: ftp://ftp.rfc-editor.org/in-notes/rfc2045.txt и ftp: //ftp.rfc-editor.org/in-notes/rfc2046.txt.', RFC 2183 ftp://ftp.rfc-editor.org/in-notes/rfc2183.txt с базовой информацией по заголовку Content-Disposition; раздел, посвященный форматам данных «HTTP Pocket Reference» Клинтона Вонга (Clinton Wong) (O'Reilly). 310 | Глава 13. Отправка не-HTML содержимого
13,2 Отправка файла, созданного в Word Задача Требуется послать файл Microsoft Word в виде двоичных данных. Решение Используйте ту же настройку сервлета, что описана в рецепте 13.1, но включите другое расширение файла, а также задайте в заголовке Content-Туре тип application/msword. Обсуждение У вас может быть несколько документов Microsoft Word, которые вы хотите распространять в двоичном виде с помощью сервлета. Сервлет в примере 13.2 имеет ту же базовую структуру, что сервлет из примера 13.1, с некоторыми изменениями для отправки документов Microsoft Word. К ним относятся: доступ к другому элементу context- param (однако вы можете держать все файлы, предназначенные для загруЗки в том же каталоге) и использование другого MIME-типа в качестве параметра метода setCon- tentType () ( response. setContentType ("application/msword")). Пример 13.2. Отправка файла Word в двоичном виде package com.jspservletcookbook; import java.io.Fileinputstream; import j ava.io.BufferedlnputStream; import java.io.File; import j ava.io.lOException; import javax.servlet.*; import javax.servlet.http.*; public class SendWord extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, lOException { //получаем имя файла из параметра "file" String fileName = (String) request.getParameter("file") ;z if (fileName == null || fileName.equals("")) throw new ServletException( "Invalid or non-existent file parameter in'SendWord.“); // добавляем суффикс .doc если его нет if (fileName.indexOf(".doc") == -1) Отправка файла, созданного в Word | 311
* , fileName = fileName + ".doc"; //где хранятся файлы Word? String wordDir getServletContext().getInitParameter("word-dir"); if (wordDir null || wordDir.equals("")) throw new ServletException ( "Invalid or non-existent wordDir context-param."); ServletOutputStream stream = null; BufferedlnputStream buf = null; try{ stream = response.getOutputStream()/ File doc new File(wordDir + "/" +/fileName); //задаем заголовки ответа response.setContentType("application/msword"); response.addHeader( "Content-Disposition","attachment; filename="+fileName ); response.setContentLength( (int) doc.length() ); FilelhputStream input = new FilelnputStream(doc); buf = new BufferedlnputStream(input); int readBytes =* 0; //читаем из файла; пишем в ServletOutputStream while((readBytes = buf.read()) != -1) stream.write(readBytes); } catch (lOException ioe){ throw new ServletException(ioe.getMessage()); } finally { //закрываем входной и выходной потоки if(stream != null) stream.close О; if(buf != null) buf.close(); } } //end doGet public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, lOException { doGet(request,response); 312 ] Глава 13. Отправка не-HTML содержимого ' м
Объекты ServletOutputStream (информация посылается как ответ сервлета) и BufferedlnputStream (из которого сервлет получает данные из файла, который необходимо отослать) закрываются в блоке finally, чтобы гарантировать освобожде- ние всех используемых ими системных ресурсов. Описание остальной части кода читайте в соответствующей части обсуждения в рецепте 13.1, включая предупреждение, касающееся исключения, вызываемого Internet Explorer. См. также Рецепт 13.1 по отправке PDF-файла; рецепты 13.3 и 13.4 по отправке файлов XML и MP3 в виде двоичных данных; рецепт 13.5 по получению входного потока, представляющего web-pecypc, например web.xml’, технические документы (RFC) по MIME: ftp://ftp.rfc-editor.org/in-notes/ifc2045.txt и ftp://ftp.rfc-editor.org/in^iwtes/rfc2046.txt.; RFC 2183 ftp://ftp.rfc-editor.org/in-notes/rfc2183.txl с базовой информацией по заголовку Content- Disposition; раздел, посвященный форматам данных «HTTP Pocket Reference» Клин- тона Вонга (Clinton Wong) (O'Reilly); главу 1, вводящую в разработку сервлетов. ) J 13.3 Отправка XML-файла Задача { Требуется из сервлета отправить XML-файл в двоичном виде. Решение Для отправки клиенту XML-файла в двоичном виде используйте объект j avax. serv- let. ServletOutputStream, получаемый из объекта javax. servlet .http. HttpServletResponse. Обсуждение В данном рецепте описывается, как послать XML-файл в двоичном виде с помощью ServletOutputStream, чтобы пользователь мог обработать этот файл как загружаемый XML-файл. Пример 13.3 получает байты XML-файла с помощью объекта Buf f e-redln- putStream, обертывающего Fileinputstream. Этот код очень похож на код примера 13.1 из рецепта 13.1, за исключением того, что здесь используется MIME-тип text/ XML. Вы можете преобразовать XML в HTML или в другую форму с помощью XSLT (Extensible Stylesheet Language Transformations - преобразований языка и,** расширяемой таблицы стилей). Если целью является генерирование содержимого (с помощью XSLT), отображаемого в браузере, не включайте заголовок ответа Consent-Disposition, так как он предназначен для работы с XML как с загружаемым файлом, который будет сохранен в файловой системе пользователя. См. главу 23 по использованию JSTL-тега х: transform. Отправка XML-файла | 313
Пример 13.3. Отправка XML-файла из сервлета package com.jspservletcookbook; import java.io.Fileinputstream; import j ava.io.Buf feredlnputStream; import java.io.File; import java.io.lOException; import javax.servlet.*; import javax.servlet.http.*; public class SendXml extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws.'ServletException, lOException { 4 // получаем имя файла из параметра "file" String fileName = (String) request.getParameter("file")r if (fileName == null || fileName.equals("")) throw new ServletException( "Invalid or non-existent file parameter in SendXml servlet.*); // добавляем суффикс .xml если его нет if (fileName.indexOf(".xml") == -1) fileName = fileName + ".xml"; // где хранятся XML-файлы? String xmlDir getServletContext().getInitParameter("xml-dir"); if (xmlDir null || xmlDir.equals("")) throw new ServletException( "Invalid or non-existent xmlDir context-param."); ServletOutputStream stream = null; BufferedlnputStream buf = null; try{ - stream = response.getOutputStream(); File xmj = new File(xmlDir + "/" + fileName); //задаем заголовки ответа response.setContentType("text/xml"); response.addHeader( "Content-Disposition","attachment; filenames"+fileName ); response.setContentLength( (int) xml.length() ); Fileinputstream input = new FilelnputStream(xml); buf = new BufferedlnputStream(input); int readBytes = 0; // читаем/из файла; пишем в ServletOutputStream while((readBytes = buf .readO) != -1) stream.write(readBytes) ; 314 | Глава 13. Отправка не-HTML содержимого
} catch (lOException ioe){ throw new ServletException(ioe.getMessage()); } finally { // закрываем входной и выходной потоки if(stream != null) stream.close(); if(buf != null) buf.close(); }//finally } //end doGet public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, lOException { doGet(request,response); } } ' ' • Чтобы context-param работал в этом коде корректно, вам требуется включить в web.xml элемент, который выглядит примерно следующим образом. <context-param> <param-qame>xml-dir</param-name> <param-value>h:/home/xml</param-value> </context-param> Если вам необходимо освежить в памяти (или ознакомиться) сведения о дескрипторе развертывания web.rml, обратитесь к главе 1. • Основные принципы работы этого кода можно найти в обсуждении из рецепта 13.1, и я не стану повторять это здесь. Также обратите внимание на заключительную часть рецепта 13.1, о прерывании, вызываемом в сервлетах этого типа, браузером Internet Explorer. См. также / Рецепт 13.1 по отправке PDF-файла; рецепт 13.2 по отправке файлов Word в двоичном виде; рецепт 13.4 по отправке МРЗ-файлов в виде двоичных данных; рецепт 13.5 по получению входного потока, представляющего wety-pecypc, например web.xml', технические документы (RFC) по MIME: ftp://ftp rfc-editor.org/in-iwtes/rfc2045.tlct и ftp://ftp. rfc-editor.org/in-notes/rfc2046.txt.‘, RFC 2183 ftp://ftp.ifc-editor.org/in-notes/rfc2183.txt с базовой информацией по заголовку Content-Disposition; раздел, посвященный форматам данных «HTTP Pocket Reference» Клинтона Вонга (Clinton Wong) (O'Reilly); главу 1, вводящую в разработку сервлетов; руководство по XSLT: http://java.sun.com/web- services/docs/l.l/tutorial/doc/JAXPXSLT.html#wp68287 Отправка XML-файла | 315
13.4 Отправка аудиофайлов Задача Требуется отправить аудиофайл, например MP3. Решение Для извлечения данных из исходного аудиофайла используйте java.io.Buff- 1 eredlnputStream, а для отправки данных'клиенту - объект javax.servlet. ServletOutputStream, получаемый из объекта javax.servlet.http»HttpS- ervletResponse. Обсуждение В коде из примера 13.4 используется тот же подход, что и в предшествующих рецеп- тах, только MIME-тип на этот раз задается как audio/mpeg. Браузеры связывают с файлами MP3 и ряд других MIME-типов, включая audio/x- mpegi, audio/трЗ и audio/x-трЗ. Пользователь передает в составе URL имя запрашиваемого файла. http://localhost:8080/home/sendmp3 ?file=song_name Дескриптор развертывания (web.xml) отображает путь к сервлету /sendmp3 на класс сервлета из примера 13.4: com. jspservletcookbook.SendMp3. Если имя запраши- ваемого файла еще йе содержит расширения .mp3, сервлет добавляет его. В дескриптору развертывания, в элементе context-param, задается каталог, где хранятся все аудиофайлы. <context-param> <param-name>mp3-dir</param-name> <param-value>h:/home/mp3s</param-value> </context-param> Сервлет из примера 13.4 использует имя этого каталога плюс имя необходимого файла в качестве параметра конструктора для создания нового объекта java. io. File, являющегося источником для java.io.Fileinputstream. Объект Bufferedln- putStream буферизует байты аудиофайла, которые затем записываются в выходной поток с помощью объекта ServletOutputStream. 316 | Глава 13. Отправка не-HTML содержимого
Пример 13.4. Отправка МРЗ-файла package com.jspservletcookbook; л import java.io.FileinputStream; import j ava.io.BufferedlnputStream; import java.io.File; import j ava.io.lOException; import j avax.servlet.*; import javax. servlet, http.*; public class SendMp3 extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response); throws ServletException, lOException { // получаем имя файла из параметра “file" String fileName = (String) request.getParameter("file"); if (fileName == null || fileName.equals("")) throw new ServletException( "Invalid or non-existent file parameter in SendMp3 servlet."); // добавляем суффикс .mp3 если его нет if (fileName.indexOf<•• .mp3") = -1) fileName fileName + ".mp3"; 11 где хранятся MP3-файлы? String mp3Dir n getServletContext().getlnitParameter("mp3-dir"); if (mp3Dir == null || np3Dir.equals("")) throw new ServletException( "Invalid or non-existent mp3-Dir context-param.); ServletOutputStream stream = null; BufferedlnputStream buf <= null; try{ stream = response.getOutputStream(); File mp3 = new File(mp3Dir + "/" + fileName); ’ // задаем заголовки ответа response.setContentType("audio/mpeg"); response.addHeader( "Content-Disposition","attachment; filehame="+fileName ); response.setContenfLength( (int) mp3.length() ); Fileinputstream input - new FileInputStream(mp3); buf = new BufferedlnputStream(input); int readBytes = 0; // читаем из файла; пишем в ServletOutputStream Отправка аудиофайлов | 317
while ((readBytes = buf.readO) != -1) stream.write(readBytes); } catch (lOException ioe){ throw new ServletException(ioe.getMessage())t } finally { IJ закрываем входной и выходной'потоки if(stream != null) stream.close(); if(buf != null) buf.close(); / } } //doGet public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, lOException { doGet(request, response); } } > Дальнейшее объяснение этого кода ищите в рецепте 13.1, в том числе замечание по поводу записи в журнале о прерывании, вызываемом в сервлетах этого типа, , браузером Internet Explorer. См. также Рецепт 13.1 по отправке PDF-файла; рецепты 13.2 и 12.3 по отправке файлов Word и XML । двоичном виде; рецепт 13.5 по получению входного потока, представляющего web-pecypc, например, web.xml; технические документы (RFC) по MIME: ftp://ftp.rfc-edi- tor.org/in-notes/rfc2045.txt и ftp://ftp.rfc-editor.org/in-iiotes/rfc2046.txt.\ RFC 2183 ftp://ftp. ifc-editor.org/in-notes/rfc2183.txt с базовой информацией по заголовку Content-Dis- position; раздел, посвященный форматам данных «HTTP Pocket Reference» Клинтона Вонга (Clinton Wong) (O'Reilly); главу 1, вводящую в разработку сервлетов. ч £( 318 | Глава 13. Отправка не-HTML содержимого
13.5 Просмотр внутренних ресурсов в сервлете Задача Вы хотите использовать сервлет для извлечения внутренних ресурсов web-приложе- ния, для их просмотра аутентифицированными пользователями. Решение Для генерирования из web-ресурсов входного потока воспбльзуйтесь методом javax.servlet.ServletContext.getResource(String path). Обсуждение Пока web-приложение находится на стадии разработки, сервлет можно использовать для просмотра дескриптора развертывания. Web-разработчики часто осуществляют удвоенный контроль файла web.xml, следя за значениями элементов context-param, зарегистрированным именем сервлета и другой информацией. Как было бы удобно просмотреть файл web.xml, просто обратившись к сервлету из браузера! Сервлет из примера 13.5 открывает web.xml с помощью метода ServletContext. getResource(), который возвращает объект java.net.URL, представляющий дескриптор развертывания, находящийся в WEB-INF/web.xml. Приводимый код открывает соединение с этим XML-файлом, вызывая метод ореп- ConnectionO объекта URL, который возвращает объект java.net.URLConnec- tion. Затем код буферизует входной поток, связанный с ресурсом, обертывая его в BufferedlnputStream. buf = new BufferedInputStream(urlConn.getInputStream()); I Переменная urlConn ссылается на URLConnection. ' •' Если браузер не може^ отображать XML-файлы в удобочитаемом виде (Netscape 7.1 и Internet Explorer способны Отображать эти файлы правильно), перед тем как *• ТУ? послать их браузеру, вы можете использовать XSLT для преобразования XML * в HTML. Пример 13.5. Отображение дескриптора развертывания с помощью сервлета package com.jspservletcookbook; i import java.io.BufferedlnputStream; import java.io.Printwriter; import java.io.lOException; import java.net.URL; . Просмотр внутренних ресурсов в сервлете | 319
import j ava. net. URLConnection; imoort java.net.HalformedURLException; import javax.servlet. *; import j avax.servlet.http.*; public class ResourceServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, lOException { //получаем web.xml для отображения сервлетом String file "/WEB-INF/web.xml"; URL url null; URLConnection urlConn null; Printwriter out w null; BufferedlnputStream buf null; . \ try{ out response.getWriter(); //доступ к web-ресурсу в пределах этого же web-приложения // как к объекту URL url • getServletContext ().getResource(file); //задаем заголовок ответа response.setContentType(text/xml"); ' urlConn url.openConnection(); //устанавливаем соединение c URL, представляющим web.xml urlConn.connect(); buf new BufferedInputStream(urlConn.getXnputStream()); int readBytes « 0; //читаем из файла; пишем в Printwriter while ((readBytes buf.readO) ! -1) 4 out.write(readBytes); } catch (MaiformedURLException mue){ throw new ServletException(mue.getMessage()); ' } catch (lOException ioej{ throw new ServletException(ioe.getMessage()); } finally { //закрываем входной и выходной потоки if(out 1“ null) out.close(); 320 | Глава 13. Отправка не-HTML содержимого
if(buf ! null) buf.close(); } } //doGet public void doPost(HttpServletRequest request, HttpServletResponse response) thrqws ServletException, lOException { doGet(request,response); ) Этот сервлет предназначен для разработчиков; если посторонний получит возможность изучить дескриптор развертывания, он сможет нарушить ч>м*****у безопасность приложения. Таким образом, этот сервлет следует удалить из промышленной версии web-приложения или использовать аутентификацию, чтобы позволять просматривать выходную информацию сервлета только пользователям с необходимыми полномочиями (детали см. в главе 15). Для записи байтов, полученных из входного потока здесь используется объект Print- Writer, поскольку сервлет предполагает отображение своего ответа в символьном виде (вместо передачи клиенту данных как загружаемого ресурса). Метод ServletContext. getResource(String path) принимает путь, начинающийся с символа/. Этот путь интерпретируется как начинающийся от корневого каталога контекста, или каталога верхнего уровня web-приложения. Поэтому сервлет получает web.xml с помощью следующего кода. String file = "/WEB-INF/web. xml"; url = getServletContextf).getResource(file); Метод ServletContext.getResouce() возвращает null, если вернуть кор- ректный ресурс, представленный параметром path, невозможно. См. также Рецепты 13.1-13.4 по отправке файлов PDF, Word, XML и аудиофайлов в двоичном виде; технические документы (RFC) по MIME: ftp://ftp.rfc-editor.org/in-notes/rfc2045.txt и ftp://ftp.rfc-editor.org/in-notes/rfc2046.txt.', RFC 2183 ftp://ftp.rfc-editor.org/in-notes/ rfc2183.txt с базовой информацией по заголовку Content-Disposition; раздел, посвященный форматам данных «HTTP Pocket Reference» Клинтона Вонга (Clinton Wong) (O'Reilly); главу 1, вводящую в разработку сервлетов. Просмотр внутренних ресурсов в сервлете | 321
ГЛАВА 14 Протоколирование сообщений сервлетов и JSP 14.0 Введение Протоколирование (logging) включает в £ебя отправку сообщений из приложе- ния и отображение различными способами этой информации для разработчиков и администраторов. Сообщение можно направить на консоль, а можно - в файл или базу данных - на постоянное хранение. Протоколирование можно использовать для выдачи отладочных сообщений в период разработки приложения, его используют и в процессе промышленной эксплуатации - для выдачи предупрежде- ний и сообщений об ошибках. В данной главе описывается чрезвычайно мощный инструмент протоколирования log4j (с открытым исходным кодом - open source). Он распространяется в виде JAR- файла (Java-архив) log4j-1.2.8.jar, который вы можете добавить к своему web-прило- жению, поместив »каталог WEB-INFAib. Это сделает его доступным для всех сервле- тов и компонентов JavaBean приложения, из которых вы хотите выдавать сообщения. В этом разделе приводится только краткое введение в возможности log4j, поскольку его мощь повлекла за собой неизбежную сложность. С log4j связаны три основных понятия: logger (регистратор), appender (присое- динитель) и layout (разметка). Для настройки этих трех элементов, протоколирова- ния в log4j используется внешний файл конфигурации, напоминающий дескриптор развертывания. В следующих ниже рецептах приведены примеры таких файлов кон- фигурации - это обычные текстовые файлы, включающие список пар имя/значение. Преимущество использования внешнего файла в том, что для изменения настройки (например, формата сообщений) не нужно перекомпилировать код сервлета. • ♦* Чтобы изменения были восприняты, вам может потребоваться перезагрузить J сервлет или другой компонент инициализирующий систему протоколирования * f £• для приложения. 322
Logger Logger (регистратор) - это сущность, используемая сервлетом для протоколирования сообщений. Чтобы его использовать» импортируйте классы log4j в сервлет, создайте экземпляр logger (а именно, org.apache. 1од4j.Logger), а затем вызывайте его методы. Эти методы имеют имена, соответствующие уровню выдаваемого сообщения. К примеру, для выдачи информационного сообщения (уровень INFO) необходимо вызвать следующий метод. logger.info("HttpServlet init method called."); Iog4j имеет пять разных уровней сообщений: DEBUG, INFO, WARN, ERROR и FATAL. Эти уровни выстроены по возрастанию, начиная с низшего - DEBUG; logger, настроенный на уровень INFO, протоколирует только сообщения уровня INFO, WARN, ERROR и FATAL (но не сообщения уровня DEBUG, поскольку, по иерархии, сообщения этого уровня нахо- дятся ниже INFO и прочих уровней). Ниже представлено краткое описание каждого из уровней. • DEBUG включает сообщения, выдаваемые для целей первоначальной разработки и отладки приложения. • INFO включает сообщения, помогающие отслеживать ход работы приложения. • WARN включает сообщения, предупреждающие о возникновении потенциально опас- ных ситуаций. • ERROR включает сообщения об ошибках, которые, возможно, устранимы. • FATAL включает сообщения об ошибках, которые вынуждают приложение закончиться аварийно. Для каждого logger в свойствах log4j или в файле конфигурации (см. рецепт 14.4) задается свой уровень сообщений (например, DEBUG). При выдаче сообщений, log4j связывает с каждым сообщением его уровень, и уровень сообщения определяется име- нем метода, с помощью которого оно выдается, в результате пользоваться этой систе- мой легко. Причем logger отправляет сообщение, только если уровень этого сообщения (например, logger .debug (Object message)) равен или выше, чем тот уровень, на который он настроен. Допустим, вы настроили logger на уровень DEBUG, а зат?м создали сервлет, содержащий ряд вызовов logger. debug (Object message). Предположим, позднее вы изменили файл конфигурации, присвоив logger уровень INFO. Это «выключит» протоколирование сообщений с уровнем DEBUG в вашем сервлете, так что Эти сообщения не будут более заноситься в файлы журналов, базы дан- ных или иные репозитории сообщений. И все потому, что в иерархии сообщений DEBUG не больше или равен INFO. Аналогично, вы можете вернуть обратно уровень DEBUG, просто заменив в файле конфигурации INFO на DEBUG*. В результате отладочные (DEBUG) сообщения переста- нут отфильтровываться. Программисты, включившие в программу несколько вызовов методов протоколирова- ния уровня DEBUG, затем, при переходе приложения на следующую стадию разработки или в промышленную эксплуатацию, смогут легко переключиться на уровень WARN или ERROR. п* Введение | 323
Appender log4j также предоставляет широкий выбор способов выдачи сообщения, С его помощью вы можете направить сообщения на консоль, в файл, в чередующийся файл (файл, ^ля которого автоматически создается архивная копия при увеличении его до заданного размера), в базу данных, на почтовый сервер, а также в ряд других репо- зиториев сообщений. В log4j каждый из этих механизмов протоколирования называется appender (присоединитель). В рецепте 14.4 представлен файл конфигурации, в котором вы можете описывать appender. Layout Как будет выглядеть выданное сообщение? Какую информацию оно будет включать? Iog4j отличился и в этой области. С помощью файла конфигурации вы можете задать большое количество разных layout (разметок). Iog4j позволяет задавать очень сложные (или простые) разметкй, с помощью шаблонов преобразования, которые напоминают регулярные выражения. Чтобы воспользоваться самой разовой настройкой, нужно задать org.apache.Iog4j .SimpleLayout. В соответствии с этим форматом, запись в журнале содержит название уровня, за которым следует дефис (-) и текст сообщения. INFO - HttpServlet init method called. Большие возможности предоставляет использование org.apache.Iog4j .Pat- ternLayout, и в рецепте 14.5 приведены некоторые примеры различных разметок для протоколируемых сообщений. 14.1 Протоколирование без Log4j Задача Вы хотите занести сообщение в журнал сервера. Решение Вызовите в сервлете метод ServletContext. log (). Обсуждение Если вы хотите просто занести сообщение в журнал контейнера сервлетов и вам совсем не требуются мощные возможности log4j, воспользуйтесь методом Servlet- Context . log (). В примере 14.1 показаны две версии метода log (). Первая прини- мает в качестве параметра строку с сообщением, а вторая имеет два параметра: строка с сообщением и объект Throwable. Если вы используетет эту форму метода, запись в журнале для данного сервлета будет содержать распечатку стека Throwable. 324 | Глава 14. Протоколирование сообщений сервлетов и JSP
Пример 14.1. Сервлет, использующий метод ServletContext. log() package com.j spservletcookbook; import javax.servlet.*; import j avax,servlet.http.*; public class Contextbog extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.lOException { String yourMessage = request.getParameterC'mine"); //Вызываем два метода ServletContext.log ServletContext context « getServletContext(); « if (yourMessage == null || yourMessage.equalsf"")) //версия log()c параметром Throwable context.log("No message received:", new IllegalStateException("Missing parameter")); else context.log("Here is the visitor's message: " + yourMessage); response.setContentTypel"text/html"); java.io.Printwriter out = response.getWriter(); //возможно требуется дополнительно выдать некоторый HTML out.printIn( "<htmlxheadxtitle>ServletContext logging</title></head><body>,,) ; out.println("<h2>Messages sent</h2>"); out .printin ( "</bodyx/html>") ; } /-/doGet ) Объект ServletContext заносит свои текстовые сообщения в файл журнала кон- тейнера сервлетов. В Tomcat эти журналы располагаются в каталоге < инсталляцион- ный-каталог-Toincat >/logs. Ниже приведен вывод сервлета из примера 14.1, второй формы метода ServletContext .'log(), в которой печатаются само сообщение и распечатка стека объекта Throwable (показаны только два уровня стека методов). Здесь вы видите, что запись в журнале включает также дату и время ее занесения, и текст сообщения. 2003-05-08 14:42:43 No message received: java.lang.IllegalStateException: Missing parameter at com.j spservletcookbook.ContextLog.doGet(Unknown Source) at javax.servlet.http.HttpServlet.service(HttpServlet.java:740) Форма метода, где принимается один параметр, просто отображает дату, время и текст сообщения, как в первой строке предшествующего примера записи в журнале. Каждый вызов метода log () начинает свою запись с новой строки файла журнала сервера. Протоколирование без Log4j | 325
См. также Рецепты 14.2-14.8 по использованию log4j для разработки собственного механизма протоколирования; главу SRV.3 API сервлетов по контексту сервлета; ссылки на послед- ние спецификации сервлета: http://java.sun.com/products/servlet/index.html. 14.2 Установка Log4j Задача Вы хотите установить у себя log4j, чтобы использовать егоъ своем, web-приложении, г ’ Решение Загрузите дистрибутив log4j из проекта Apache Jakarta и поместите полученный файл log4j-1.2.8.jar (для разных версий log4j имя будет разным) в каталог WEB-INF/lih своего web-приложения. Обсуждение Пакет log4j распространяется под лицензией Apache Software, которая включена в дистрибутив. Ниже приведены шаги по установке log4j в web-приложении. 1. Зайдите на web-сайт log4j и скачайте дистрибутив в формате ZIP (для Windows) или GZ (для Unix-систем): http://jakarta.apache.orgdog4j/docs/download.html. Загруженный файл будет называться примерно так: jakarta-log4j-1.2.8.zip или jakarta-log4j-1.2.8.tar.gz. 2. Распакуйте дистрибутив, что приведет к созданию каталога jakarta-log4j-1.2.8 (для log4j версии 1.2.8). В подкаталоге distr находится файл log4j-1.2.8.jar. Скопируйте этот JAR-файл в каталог WEB-INF/lib своего приложения. 3. Создайте файл свойств log4j и поместите его в каталог WEB-INF/classes своего приложения. Это, обычно, текстовый файл, содержащий пары имя/значение для настраиваемых элементов lpg4j, например для logger, appender, и layout. Пример такого файла приведен в рецепте 14.4. 4. В сервлет или компонент JavaBean (где необходимо использовать протоколирование) включите необходимые операторы import. В примере 14.2 приведен «скелетный» сервлет, демонстрирующий классы, которые обычно используются в таких случаях. Пример 14.2. Импорт пакетов, связанных с l'og4j import org.apache.Iog4 j.Logger; import org.apache.Iog4 j.Propertyconfigurator; import javax.servlet.*; import j avax.servlet.http.*; 326 | Глава 14. Протоколирование сообщений сервлетов и JSP
public class LoggerSkel extends HttpServlet { private Logger log; public void init(){ //logdj будет искать файл log4j.properties //в WEB-lNF/classes log Logger.getLogger(LoggerSkel.class); //просто пример использования logger log. debug ("Instance created of: " + getClassO .getNameO); } public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.lOException { //если необходимо, выдавайте сообщения и здесь } //doGet } Если log4j-1.2.8.jar находится в каталоге WEB-INF/Iib, ваш сервлет может использо- вать необходимые ему классы пакетов org. apache. Iog4 j . *. См. также Рецепты 14.3-14.8 по использованию log4j для разработки собственного механизма протоколирования; сайт для загрузки log4j: http://jakarta.apache.org/log4j/docs/download.html', страницу с документацией по проекту log4j: http://jakarta.apache.org/log4j/docs/documenta- tion.html. 14.3 Использование logger без файла конфигурации Задача - ; Вы хотите использовать в сервлете logger, не создавая собственного файла кон- фигурации. Решение Создайте в сервлете logger, а для его настройки используйте класс org. apache. Iog4j.BasicConfigurator. Использование logger без файла конфигурации | 327
Обсуждение log4j позволяет настроить logger не создавая файл настройки (файл свойств). Сервлет из примера 14.3 создает экземпляр logger в методе init (), который контейнер сервлетов вызывает при создании экземпляра сервлета. В статическом методе BasicConfigura- tor.configure!) создается ConsoleAppender, иными словами, logger будет выда- вать свои сообщения на консоль, используя формат сообщений принятый по умолчанию. Пример 14.3. Сервлет, использующий BasicConfigurator для настройки logger package com.jspservletcookbook; import org.apache.Iog4 j.Logger; import org.apache.Iog4j.BasicConfigurator; ; inport j avax.servlet.*; import javax.servlet.http.*; public class LoggerNconfig extends HttpServlet { private Logger log null; public void init(){ //используем корневой logger log Logger.getRootLoggerO; //этот logger отправляет сообщения в формате по умолчанию на консоль BasicConfigurator.configure(); ) public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.TOException { //выдаем сообщение уровня DEBUG log.debug("Sending a DEBUG message"); // выдаем сообщение уровня INFO log.info("Sending an INFO message"); //а лучше выдать и некоторый HTML response.setContentType("text/html"); java.io.Printwriter out = response.getWriter(); out.println( “<htmlxheadxtitle>Servlet logging</titlex/headxbody>") ; out.printin("<h2>Hello from a Logger with no Config file</h2>"); //Родитель этого logger - корневой logger out.printIn( "Your logger name is: " + log.getNaxne()+"<br>"); out.println( 328 | Глава 14. Протоколирование сообщений сервлетов и JSP
"Your logger parent is: " + log.getParent().getName()+"<br>");? out. print In (" </bodyx/html> “ ) ; } //doGet public void doPost(HttpServletRequest request, y*'' HttpServletResponse response) throws ServletException, java.io.lOException { doGet(request,response); } '} Вид сообщений от этого сервлета приведен в примере 14.4. Эти сообщения выдаются в соответствии с форматом, принятым по умолчанию, который включает имя потока (Thread-5), название уровня (DEBUG), имя logger (com. jspservletcookbopk.Log- gerNconf ig) и текст coo6iueHwr(«Sending a DEBUG message»). В рецепте 14.5 показано, как создать шаблон формата для протоколируемых сообщений, то есть вы сами можете настроить то, какую информацию будет посылать logger. Пример 14.4. Пример выдачи сообщений с использованием Bas icCon figure tor 4061660 [Thread-5] DEBUG com. jspservletcookbook.LoggerNconfig - Sending a DEBUG message 4061660 [Thread-5] INFO com.jspservletcookbook.LoggerNconfig - Sending an INFO meshage Вот шаблон, используемый для layout (разметки), связанной с BasicConf igurator: %-4r [%t] %-5p %c %x - %m%n Детали использования класса org.apache.Iog4j .PatternLayout приведены в рецепте 14.5. См. также Рецепт 14.2 по скачиванию и установке log4j; рецепты 14.4—14.8 по использованию log4j для разработки собственного механизма протоколирования; сайт для загрузки log4j : http://jakai1a.apache.org/log4j/docs/download.hbnl; страница Javadoc по log4j : http://jakartcL apache.org/log4j/docs/api/index.html; страницу с документацией по проекту log4j; http:// jakarta.apache.org/log4j/docs/documentation.html. Использование logger без файла конфигурации | 329
14.4 Добавление Appender к корневому Logger Задача s,. Вы хотите настроить appender (выбрать место, куда будут направляться сообщения) для корневого logger. Решение Создайте файл конфигурации с именем loglj.properties и поместите его в каталог WEB-INF/classes своего web-приложения. Обсуждение Наконец наше обсуждение подошло к конфигурационному файлу log4j, с помощью которого разработчики могут настраивать logger, appender и layout. Ниже приведена последовательность использования примеров рецепта. 1. Создайте файл свойств с именем log4j.properties (его содержимое выглядит примерно . так, как показано в примере 14.5). 2. Поместите этот файл в каталог WEB-INF/classes своего приложения. 3. Импортируйте в свой сервлет класс org. apache.1од4 j. Logger. 4. В коде сервлета получите ссыпку на корневой logger с помощью статического метода Logger. getRootLogger () и начинайте протоколирование. В примере 14.5 для приложения настраивается корневой logger - своего рода супер- logger, он настраивается на уровень DEBUG. Этот корневой logger использует appender с именем cons. Этот appender имеет тип org.apache. Iog4j .ConsoleAppender, что означает, что он доставляет сообщения на консоль. Пример 14.5. Файл log4j.properties создающий appender для корневого logger log4 j.rootLogger=DEBUG, cons log4 j.appender.cons=org.apache.Iog4 j.ConsoleAppender log4 j.appender.cons.layout=org.apache.Iog4 j.SimpleLayout Третья строка файла устанавливает, что этот logger для форматирования сообщений будет использовать разметку SimpleLayout, то есть будет выдаваться название уровня (DEBUG), тире (-) и сам текст сообщения. Сервлет, использующий этот logger, приведен в примере 14.6. Iog4j автоматически будет искать в каталоге WEB-INF/classes файл log4j.properties, поскольку сервлет не настраивал logger с помощью метода Bas- icConf igurator. configure (), как показано в рецепте 14.3. 330 | Глава 14. Протоколирование сообщений сервлетов и JSP
Пример 14.6. Использование корневого logger, настроенного с помощью файла log4j.propirties package com.jspservletcookbook; import org.apache.Iog4j.Logger; import j avax.servlet - *; import javax.servlet.http.*; public class LoggerWconfig extends HttpServlet { private Logger log = null; public void init(){ //Корневой logger настраивается с помощью файла //WEB-lNF/classes/log4j.properties log Logger.getRootLogger(); log. info (" LoggerWconfig started."); ) public void c^oGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.lOException { //посылаем сообщение уровня DEBUG log. debug ("Sending a DEBUG message*1); //посылаем сообщение уровня INFO log.info("Sending an INFO message"); //а еще’ лучше добавить и HTML response.setContentType("text/html"); java.io.Printwriter out = response.getWriter(); out.printIn( 4 "<html><headxtitle>$ervlet logging</titlex/headxbody>"); out.printIn( "<h2>Hello from a Logger with a log4j.properties file</h2>"); out.printIn("Your logger name is: " + log.getName()+"<br>"); out. printin (" < /bodyx /html>") ; } //end doGet } / Сервлет из примера 14.6 протоколирует одно сообщение INFO в методе initО, а затем еще два сообщенйя. в методе doGet (). Logger протоколирует все эти сообще- ния на консоль, поскольку именно так в файле log4j.properties настроен appender для корневого logger: На консоли это выглядит примерно следующим образом. INFO - LoggerWconfig started. DEBUG - Sending a DEBUG message INFO - Sending an INFO message Информация, выдаваемая данным сервлетом в браузере, выглядит так, как показано на рис. 14.1. Добавление Appender к корневому Logger | 331
Servlet logging - Microsoft Internet Explorer ига o £ilc Edit View Favorites lools ikr Back , Forward L2J Stop FHresh Home Search http://locaihost:8080/home/servlet/com.ispservletcookbook.LoggeiWconfig it^Go Links gjlnstitutjonFS @ Welcome to Spwhawkl^^] API Documentation ^5] Beta Chapters if] Google x> Hello from a Logger with a Iog4j.properties file Your logger name is: root Done. cal intranet Puc. 14.1. Сервлет отображает имя, которое имеет используемый им logger См. также Рецепт 14.2 по скачиванию и установке log4j\ рецепт 14.3 по использованию logger без файла свойств; рецепты 14.5 - 14.8 по использованию log4j для разработки собствен- ного механизма протоколирования; сайт для загрузки log4j : http://jakarta.apache.org/ log4j/docs/download.html; страница Javadoc по log4j : http://jakarta.apache.orgAog4j/docs/ api/index.html-, страницу с документацией по проекту log4j\ http://jakarta.apache.org/log4j/ dOcs/docutnentation. html. 14.5 Использование шаблона для appender Задача Вы хотите создать для сервлета свой собственный logger и назначить ему appender. Решение Включите в файл log4j.properties настройку для appender. Обсуждение В данном рецепте создается новый logger, что заставляет нас обсудить структуру насле- дования в log4j. Корневой logger - это «супер-logger», и от него производятся (наследуются) все другие logger, это сравнимо с тем, какое положение занимает java.lang.Object в объектно-ориентированном программировании на языке Java. В примере 14.7 создается 332 | Глава 14. Протоколирование сообщений сервлетов и JSP
новый logger с именем com. j spservletcookbook, который наследует от корневого log- ger уровень сообщений (DEBUG) и консольный appender (с именем cons). В примере 14.7 также создается appender специально для logger com. jspservletcookbook. Помес- тите этот файл log4j.properties в каталог WEB-INF/classes. Пример 14.7. Настройка для logger с именем сот. jspservletcookbook log4j.rootLogger=DEBUG, cons log4j.logger.com.jspservletcookbook=, inyAppender #appender для корневого logger log4 j.appender.cons=org.apache.Iog4 j.ConsoleAppender ttappender для com.jspservletcookbook log4j.appender .myAppender=org.apache.Iog4j.RollingFileAppender log4 j. appender .inyAppender. File=h: /home/example. log log4j. appender. inyAppender. MaxBackupIndex=l log4 j. appender .inyAppender .MaxFileSizeslMB #layout (разметка) для корневого logger log4 j.appender.cons.layout=org.apache.Iog4 j.SimpleLayout # layout (разметка) для com.jspservletcookbook log4 j. appender .inyAppender. layout=org. apache. Iog4 j. PatternLayout log4j.appender.inyAppender.layout.ConversionPattern»%-5p Logger:%c{l) Date: i|&d{ISO8601} - Win Вы, возможно, заметили сходство между именами пакета и именем нового logger в примере 14.7: com. jspservletcookbook. В log4j используется схема именования, базирующаяся на схеме Java. Вот краткое изложение основ этой схемы. • Каждый logger производится (наследует) от корневого logger. • Каждый logger, чье имя содержит префикс, совпадающий с именем некоторого настроенного logger (например, coms jspservletcookbook), также наследует йот него. Таким образом, logger с именем com. jspservletcookbook.Log- gerWconfig наследует характеристики от com. j spservletcookbook. В примере 14.7 logger с именем com. j spservletcookbook настроен на использо- вание appender с именем inyAppender^ Это appender вывода в чередующийся файл (rolling file appender), то есть в файл, для которого автоматически создается архивная копия при достижении им некоторого заданного объема. Этот appender базируется на Java-классе org. apache. Iog4j .RollingFileAppender, находящемся в наборе классов, которые использует log4j. Использование шаблона для appender | 333
Если вы заглянете в описание (Javadoc) для этого класса, то увидите, что он имеет множество методов, которые выглядят так: getXXXO, где XXX. - одно из свойств appender. Вы задаете эти свойства в файле конфигурации, давая каждому свойству значение. Так, чтобы задать свойство File для appender, необходимо использовать следующий Синтаксис. Iog4 j.appender.myAppender.File=h:/home/example.log Этот элемент настройки задает местоположение файла, в который appender будет протоколировать сообщения. Когда этот файл достигнет размера в 1 Мб (заданного в свойстве MaxFileSize), log4j переименует1 этот файл в example.log.! и создаст Новый файл example.log, в который и продолжит запись сообщений. Свойство MaxBdckupIn- dex.означает, что log4j будет создавать только один архивный файл. Описание (Javadoc) RollingFileAppender можно найти по адресу http:// J jakarta.apache.org/log4j/docs/api/org/apacheAog4j/RollingF'ileAppender.htnil. ---IS# В примере 14.7 для logger com. jspservletcookbook Также задается разметка (layout), и выглядит это следующим образом. 1од4 j .appender .myAppender. layout=org.-apache. Iog4j . PatternLayout log4 j . appender.myAppender. layout. ConversionPattern=%-5p Logger: %c{. 1} Date: %d{ISO8601) - %m%n В первой Строке указывается, что эта разметка будет использовать шаблон org. apache. Iog4j .PatternLayout, который, согласно его описанию (Javadoc), основан на шаблонах преобразования функции print f (язык С). Используемый язык шаблонов для генерирования отформатированного сообщения сочетает литеральный текст и специ- фикаторы преобразования. Спецификаторы преобразования - это буквы (например, с), имеющие специальное значение при заполнения полей строки. Например, эти буквы могут представлять дату или имя logger. Перед символами шаблона преобразования ставится знак %. Рассмотрим, например, следующий шаблон. Logger:%с{1} Он преобразуется к литеральному тексту «Logger:» за которым будет следовать имя log- ger. Цифра 1 в фигурных скобках ({1}), идущая после %с, - это спецификатор точности, который означает «отобразить только один сегмент имени, начиная справа». Если logger имеет имя com. jspservletcookbook.LoggerServlet, то шаблон %с{1) отобразит в тексте сообщения слово «LoggerServlet». И это потому, что спецификатор преобразования с - заполнитель имени logger. 334 | Глава 14. Протоколирование сообщений сервлетов и JSP
Буква m заполняет строку протокола самим текстом сообщения, п - воспроизводит специфичный для данной платформы символ-разделитель строк (конец строки), a d представляет дату. Строка %d{ISO8601} в log4j форматирует дату в детализирован- ном виде. Подробности см. в http://jakarta.apache.org/log4j/docs/api/org/apache/log4j/help- ers/ISO8601DateFormat. html. В примере 14.8 приведен сервлет, использующий logger, наследующий свои характеристики от двух настроенных logger: корневого и com. j spservletcookbook. Пример 14.8. Сервлет, который использует logger-потомок package сот. jspservletcookbook; import org.apache.Iog4j.Logger; ( import javax.servlet.*; import j avax. servlet,, http. *; public class LoggerNewConfig extends HttpServlet { private Logger log = null; public void init(){ //logger имеет имя,совпадающее с именем класса: //сот.jspservletcookbook.LoggerNewConfig log в Logger.getLogger(LoggerNewConfig.class); log,info("LoggerNewConfig started. ); } public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.lOException { t //выдаем сообщение уровня DEBUG log.debug("Sending a DEBUG message"); „ // выдаец сообщение уровня INFO log .'info ("Sending an INFO message"); //а лучше выдать еще и HTML response. set.ContentType (" text /html "); j ava.io.PrintWriter out =;response.getWriter(); out.printlnf "<html><headxtitle>Servlet logging</title></headxbody>") ; out.printing "<h2>Hello from a Logger with its own configuration in the " + log4j.properties file</h2>"); out.println("Your logger name is: “ + log.getName()+"<br>"); out.printIn( Использование шаблона для appender | 335
"Your logger parent is: " + log.getParent().getName()+"<br>"); out. print In ( " < /bodyx /html> ") ; } //end doGet ) Статический метод org. apache. Iog4 j . Logger. getLogger (Class class- Name) создает logger с именем com. jspservletcookbook. LoggerNewConfig, сов- падающим с именем класса сервлета. А поскольку этот logger содержит в имени префикс com. jspservletcookbook, то он наследует тот appender, который в файле свойств (пример 14.7) назначен logger com. jspservletcookbook. Фактически, все заданные там свойства унаследует любой logger, созданный в классах, являющихся частью пакета com. jspservletcookbook, если разработчик придерживается порядка именования своих logger по названиям классов, в которых они созданы. Ниже приведен пример внешнего вида записей протокола, генерируемых при исполь- зовании шаблона из файла конфигурации примера 14.7. INFO Logger:LoggerNewConfig bate: 2003-07-10 17:16:22,713 - LoggerNewConfig started ' DEBUG Logger:LoggerNewConfig Date: 2003-07-10 17:16:34,530 - Sending a DEBUG message INFO Logger:LoggerNewConfig Date: 2003-07-10 17:16:34,530 - Sending an INFO message Если вас интересуют детали шаблонов, используемых в разметках (layout), посетите http://jakarta.apache.org/log4j/docs/api/org/apacheAog4j/PatteniLayout.html. Из-за установок в конфигурационном файле и структуры наследования сообщения сервлета из примера 14.8 также будут протоколироваться на консоль. См. также Рецепт 14.2 по скачиванию и установке log4j\ рецепт 14.3 по использованию logger без файла свойств; рецепт 14.4 по добавлению appender к корневому logger; рецепт 14.6 по использованию looger в JSP; рецепты 14.7 и 14.8 по использованию log4j со слушателями событий приложения; сайт для загрузки log4j : http://jakarta.apache.orgAog4j/docs/doyvnload.^ html’, страница Javadoc по log4j : http://jakarta.apache.orgAog4j/docs/api/index.html; страницу с документацией по проекту log4j: http://jakarta.apache.orgAog4j/docs/documentation.html. 336 | Глава 14. Протоколирование сообщений сервлетов и JSP
14.6 Использование log4j в JSP Задача Вы хотите включить протоколирование в JSP. Решение Разработайте пользовательский тег, использующий log4j для инициации прото- колирования сообщений. Обсуждение Пользовательский (custom) тег - это XML-элемент, который вы сами разрабатываете для использования на страницах JSP. Иными словами, контейнер JSP не имеет встроен- ной поддержки такого пользовательского действия; web-разработчик самостоятельно разрабатывает Java-классы, реализующие функциональность данного тега. Пользова- тельский тег (пользовательское действие) можно использовать для реализации функцио- нальности log4j по протоколированию на страницах JSP. В этом рецепте вы найдете: • Java-класс, содержащий обработчик тега для пользовательского тега cbck: log; • дескриптор библиотеки тегов (Tag Library Descriptor - TLD), обеспечивающий web- приложение информацией об этом теге; • страницу JSP, использующую тег cbck: log. В примере 14.9 приведен Java-класс LoggerTag, на котором построен тег cbck: log. Любое пользовательское действие управляется за сценой одним или несколькими Java-классами. В данном случае класс LoggerTag напоминает компонент JavaBean, обертывающий классы log4j, которые мы импортируем в самом начале этого класса. Пользовательские теги JSP - отдельная сложная тема, поэтому, объясняя этот тег, я сосредоточусь главным образом на тех его особенностях, что связаны с log4j. Пробелы в ваших знаниях о создании пользовательских тегов поможет заполнить глава 22. Пример 14.9. Пользовательский тег, использующий log4j package com.j spservletcookbook; import org.apache.Iog4 j.Logger; import org.apache.Iog4 J.Propertyconfigurator; import java.lang.reflect.Method; import javax.servlet.jsp.*; import javax.servlet.jsp.tagext.*; public class LoggerTag extends BodyTagSupport { Использование log4j в JSP | 337
private Logger log « null; private String configFile » null; private String level = null; private static final String[] LEVELS = { "debug","info","warn","error","fatal"}; .public void setConfigFile(String fileName){ this.configFile = fileName; } public void setLevel(String level){ 4 this.level = level; } public int doEndTag () throws JspExcept ion { ' String realPath pageContext.getServletContext().getRealPath("/"); String fileSep System, get Proper ty("file.separator"); if (realPath ! null && (1realPath.endsWith(fileSep))){ realPath * realPath + fileSep;} //настраиваем logger если пользователь задал дополнительные атрибуты if (configFile ! null) PropertyConf igurator.configure(realPath + "WEB-INF/classes/" + configFile); //если пользователь тега задал неверный .уровень, //не DEBUG, INFO, WARN, ERROR, или FATAL, вызываем исключение level e level.toLowerCase(); if (1 contains(level)) throw new JspException( "The value given for the level attribute is invalid."); //logger имеет то же имя, что и сам класс: //com. jspservletcookbook.LoggerTag. То есть он наследует //appender'ы от logger'а определенного в файле конфигурации: //com. jspservletcookbook log = Logger.getLogger(LoggerTag.class); String message getBodyContent () .getStringO .trim(); Method method « null; try{ method ж log.getClassO . getMethod(level,new Class[ ] { Object.class }); method.invoke(log,new String[]{message}); 338 | Глава 14. Протоколирование сообщений сервлетов и JSP
} catch (Exception e){} return, EVAL_PAGE; , • } // doEndTag public void release(){ //освобождаем ресурсы, используемые переменными экземпляра log = null; configFile = null; level = null; }// release private boolean contains(String str)Г for (int i = 0; i < LEVELS.length; i++){ if(LEVELS[i].equals(str)) return true; return false; }// contains ) Класс LoggerTag расширяет класс javax.servlet.jsp.tagext.BodyTag- Support, который был создан для пользовательских действий, обрабатывающих содержимое тела тега, то есть текст, который может появляться между открывающим и закрывающим тегами. Атрибуты тега - обязательный атрибут level и необязательный атрибут config- File - обрабатываются так же, как свойства компонентов JavaBean: с помощью методов- «установщиков» (setter) (например, public void set Level (String level)). Метод doEndTag () выполняет для тега почти всю важную работу. 1. Пытается настроить logger, если пользователь задал в атрибуте configFile имя файла конфигурации. 2. Проверяет, корректно ли задан уровень сообщений (только DEBUG, INFO, WARN, > ERROR ИЛИ FATAL). 3. Протоколирует сообщение. В примере 14.10 приведен дескриптор библиотеки тегов (TLD), предоставляющий контейнеру JSP сведения о данном теге, например, Какие атрибуты обязательны, а какие опциональны. В библиотеке тегов, связанной с эти TLD один единственный тег cbck: log. Файлы TLD должны находится в каталоге WEB-INF или его подкаталоге, или в каталоге META-INF JAR-файла, размещенного в WEB-INF/lib. Использование log4j в JSP | 339
Пример 14.10. TLD для пользовательского тега протоколирования <?хш1 versions"1.0" encodings"ISO-8859-1" ?> <!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN" "http: / / j ava.sun.com/dtd/web-j sptaglibrary_l_2.dtd"> «taglib» <tlib-version>l.0«/tlib-version> <jsp-version»1.2</jsp-version» <short-name»cbck«/short-name» «uri>jspservletcookbook.com.tags«/uri», <description>Cookbook custom tags</desbription> <tag> <name»log</name> <tag-class»com.jspservletcookbook.LoggerTag</tag-class> <body-content>JSP</body-content» «description»This tag uses log4j to log a message.</description» «attribute» <name»con£igFile</name» <required»false</required» <rtexprvalue»false</rtexprvalue» «description» This attribute provides any configuration filename for the logger. The file must be located in WEB-INF/classes. «/description» «/attribute» «attribute» «name > level* /name» «required»true</required» <rtexprvalue»false«/rtexprvalue> <description»This attribute provides the level for the log request. «/description» «/attribute» «/tag» «/taglib» В примере 14.11 показана страница logger.jsp и то, как может использоваться наше пользовательское действие. 34и | Глава 14. Протоколирование сообщений сервлетов и JSP
Пример 14.11. Страница JSP, использующая для доступа к возможностям log4j пользователь? ское действие > <%@page contentType="text/html"%> <9j0 taglib uri="jspservletcookbook.com.tags" prefix»"cbck" %> <html> <headxtitle>A logging JSP</titl(ex/head> <body> <h2>Here is the logging statement</h2> <cbck:log level»"debug"> Debug message from logger.jsp </cbck:log> Debug message from logger.jsp </body> </html> Сначала на странице используется директива taglib - для объявления библиотеки тегов, содержащей наше пользовательское действие. В примере 14.10 показан файл TLD - XML-файл, описывающий свойства разных тегов из библиотеки тегов. Более подробно TLD описывается в главе 22. Пользовательское действие cbck:log позволяет разработчику протоколировать сообщение от JSP путем вкладывания текста сообщения в тег cbck: log (то есть сооб- щение составляет содержимое тела тега). Часть тега cbck - это префикс, объявленный в директиве taglib. Часть log - имя тега. Тег позволяет пользователю объявить уровень протоколируемых сообщений с помощью атрибута level. I Обычно существует компонент, инициализирующий систему протоколирования log4j при запуске web-приложения, например инициализирующий сервлет. Пользовательское действие, описываемое здесь, само не инициализирует log4j. Однако я добавил необязательный атрибут configFile, который позволяет задать имя файла конфигурации log4j, в котором будут настраиваться уровень logger, appender и разметка. Допустим, вы решили, какого уровня сообщение вы хотите выдать и ^поместили Это значение в атрибут level. Класс, реализующий тег, заранее не знает, какого уровня сообщение ему предстоит обработать; DEBUG, INFO, WARN, ERROR или FATAL. Но поскольку в log4j методы logger носят имена, совпадающие с названиями уровней, мы можем динамически вызывать подходящий метод, основываясь на значении атрибута level. Именно для этого предназначен код. method = log.getClass(). getMethod(level,new Class(]{ Object.class }); method.invoke(log,new String[]{message}); Здесь мы получаем объект java. lang. reflect.Method (метод), который имеет имя DEBUG, или INFO, или WARN, или ERROR, или FATAL, и затем активизируем этот метод, вызывая me thqd. invoke и передавая ему протоколируемое сообщение из страницы JSP. Использование log4j в JSP I 341
Для этого тега не обязательно задавать имя файла конфигурации, однако как log4j узнает, как и куда посылать записи протокола для сообщений? Данный тег предпола- гает, что некоторый сервлет уже инициализировал систему log4j для web-приложения, как это обычно происходит при использовании log4j в web-окружении. Конфигурацион- ный файл тот же, что был описан в рецепте 14.4 и показан в примере 14 5. Вы также можете использовать сервлет, подобный приведенному в примере 14.6, в качестве сервлета, инициализирующего log4j. Этот файл конфигурации создает механизм протоколирования, направляющий сообще- ния на консоль и в файл, и именно туда будут направляться сообщения из пользователь- ского тега. К примеру, запуск «границы logger.jsp приведет к выдаче на консоль сообщения. DEBUG - Debug message from logger.jsp Logger тега запишет в файл example.log сообщение. DEBUG Logger:LoggerTag Date: 2003-05-12 12:53:13,750 - Debug message from logger.jsp Если вы хотите подключить собственный файл конфигурации, вы можете включить в пользовательский тег атрибут configFile. И тогда тег настроит logger в соответст- вии с вашим файлом, вместо того, который был инициализирован до этого. if (configFilp != null) PropertyConfigurator.configure( I pageContext.getServletContext().getRealPath(”/") + "WEB-INF/classes/" + configFile); Метод Propertyconfigurator.configure() позволяет задать имя файла свойств log4j, когда вы инициализируете систему протоколирования, если это имя отличается от log4j.properties. Данный метод (в log4j версии 1.2.8) не возбуждает исключений, которые можно было бы отлавливать в классе тега. Вы можете проверять существование значения атрибута configFile (представляющего путь к файлу в web-приложении) непосредственно в коде, с помощью java, io API, И затем, если в атрибуте configFile задан некорректный путь, вызывать исключение. См. также Рецепт 14.2 по скачиванию и установке log4j’, рецепт 14.3 по использованию logger без файла свойств; рецепт 14 4 по добавлению appender к корневому logger; рецепт 14.5 по использованию шаблонов для appender; рецепты 14.7 и 14.8 по использованию log4j со слушателями событий приложения; сайт для загрузки log4j : http://jakarta.apache.org/ log4j/docs/download.html\ страница Javadoc по log4j: http://jakarta.apache.orgAog4j/docs/api/ hidex.html-, страницу с документацией по проекту log4j: http://jakarta.apache.org/log4j/docs/ documentation, html. 342 | Глава 14. Протоколирование сообщений сервлетов и JSP
14.7 Протоколирование сообщений слушателя событий контекста сервлета Задача Вы хотите с помощью log4j протоколировать сообщения, в моменты, когда контекст сервлета создается и когда он отключается. Решение Используйте log4j и слушатель событий контекста. Обсуждение API сервлетов включает интерфейс слушателя, носящий имя javax.servlet. Service t Con text Listener, который можно использовать для получения извещений в Java-классе при создании контекста сервлета и при его отключении. Этот извещаемый класс может протоколировать создание или отключение контекста сервлета или сохране- ние атрибута объекта в контексте сервлета, и те .действия, которые этот Java-класс (слу- шатель) предпринимает при получении поступающих к нему извещений. Слушатель контекста сервлета - это слушатель событий приложения - категория клас- сов, к которой также относятся слушатели событий сеанса (см. главу 11 или рецепт 14.8) и слушатели событий запроса. К примеру, слушатель событий сеанса получает извещения, когда контейнер сервлета создает новые объекты HTTP-сеанса, и это позволяет отслежи- вать прохождение пользователем разных этапов работы с web-приложением. Контейнер сервлетов извещает слушателя событий запроса, когда пользователь делает запрос к web- приложению, так что слушатель может выполнить какие-либо связанные с этим действия, например запротоколировать IP-адрес пользователя. Объект javax. servlet. ServletContext используется для хранения атрибутов (то есть доступа к параметрам контекста, общим для всего приложения), получения объ- ектов RequestDispatcher дйя перенаправления или включения файлов (см. главу 6), или получения информации, такой, как абсолютный путь, связанный с web-ресурсом. С каждым web-приложением связан всего один контекст сервлета. * ' Согласно документации (Javadoc) по ServletContext (http://java.sun.com/j2ee/ 1.4/docs/api/javax/servlet/ServletContext.html), для каждого web-приложения (или для каждой виртуальной машины Java (JVM), в случае распределенного ? приложения) существует только один экземпляр контекста сервлета. Iog4j является хорошим выбором для случая, когда требуется генерировать собствен- ные протокольные сообщения из класса, реализующего интерфейс ServletContex- tListener. В примере 14.12 приведен класс ContextLogger, использующий log4j для отправки сообщений из двух своих методов. Протоколирование сообщений слушателя событий контекста сервлета 1 343
Пример 14.12. Слушатель контекста сервлета, посылающий протоколируемые сообщения package com.jspservletcookbook; ' import org.apache.Iog4 j.Logger; import org.apache.Iog4 j.Propertyconfigurator; import javax.servlet. *; ’ import j avax.servlet.http. *; public class ContextLogger implements ServletContextListener { private Logger log; public ContextLogger () {} „• public void contextDestroyed(ServletCont.extEvent see) { String name B sce.getServletContextO .get Servlet Cont extNameO; //ведаем сообщение уровня info log.info("ServletContext shut down: " + (name жп null ? "" : name )); //выполняем необходимые действия, например освобождение ресурсов //используемых wob-приложением } public void contextXnitialized(ServletContextEvent see) { ServletContext context " sce.getServletContextO; String realPath context.getRealPath("/"); String fileSep “ System.getProperty("file.separator"); //Убедимся, что путь завершается символом-разделителем файлов (1/') if (realPath ! null && (I realPath.endsWith(fileSep))){ realPath realPath + fileSep;} //Здесь инициализируем logger; имя файла свойств для log4j //задано в параметре контекста "logger-config" PropertyConf igurator.configure(realPath + "WEB-INF/classes/" + context.getInitParameter("logger-config")); log Logger.getLogger(ContextLogger.class); String name context.getServletContextName(); //запрос на протоколирование факта инициализации контекста сервлета log.info("ServletContext ready: " + (name null ? "" : name )); } } 344 | Глава 14. Протоколирований сообщений сервлетов и JSP
Создайте для этого класса конструктор без параметров, поместите класс в каталог WEB- INF/classes или в JAR-файл, распо. южснный в каталоге WEB-INFAib, и зарегистрируйте его в web.xml. <listener> <listener-class> com.j spservletcookbook.ContextLogger </listener-class> *i </listener> Данный слушатель контекста отслеживает жизненный цикл контекста сервлета с помощью двух методов: contextlnitialized() и cont ext Destroyed (). Кон- тейнер сервлетов вызывает первый из методов, когда контекст сервлета создан и web- приложениё готово получить свой первый запрос. Контейнер сервлетов извещает дан- ный класс слушателя и вызывает метод contextDestroyed (), когда прекращает работу контекста сервлетов, например, когда web-приложение останавливается, перед тем как оно будет перезагружено. Tomcat 4.1.24' инициализирует слушателя контекста сервлета до создания экзем- пляров сервлета, даже если в этом приложении сервлет сконфигурирован как заранее загружаемый В сервлете из примера 14.12 система log4j инициализируется в методе contextlnitialized(). В дескрипторе развертывания можно сообщить контейнеру сервлетов о необходимости, при запуске, загружать экземпляр сервлета и вызывать его метод init (). Для этого следует включить в соответствующий элемент servlet элемент load-on-startup. <servlet> <servlet-name>logger</servlet-name> <servlet-class> com.j spservletcookbook.LoggerServlet </servlet-class> <1oad-on-startup>1</1oad-on-startup> </servlet> Значением элемента load-on-startup является целое число, определяющее порядок, в котором контейнер загружает данный сервлет. В методе contextlnitialized/) слушатель настраивает log4j, используя файл, заданный элементом context-pararti в web.xml. ccontext-param> <param-name>logger-config</param-name> <param-va'lue>servletLog.properties</param-value> </context-param> Файл конфигурации log4j (servletLog.properties) размещается в каталоге WEB-INF/ classes. Затем, когда web-приложение запускается или останавливается, слушатель протоколирует свои сообщения на консоль и в файл. Файл конфигурации, используе- мый слушателем для настройки log4j, показан в примере 14.13. Протоколирование сообщений слушателя событий контекста сервлета | 345
Пример 14.13. Файл конфигурации log4j, используемый слушателем контекста сервлета log4j.rootLogger=DEBUG, cons log4j.logger.com.jspservletcookbook=, myAppender log4j.appender.cons=org.apache.Iog4j.ConsoleAppender #configure the 'myAppender' appender log4 j.appender.myAppender=org.apache.Iog4 j.RollingFileAppender log4 j.appender. myAppender.File=h:/home/example.log log4 j.appender.myAppender.MaxBackupIndex=1 log4 j.appender. myAppender.MaxFileSi ze=lMB log4 j.appender.cons.layout=org.apache.Iog4 j.SimpleLayout log4 j.appender.myAppender.layout=org.apache.Iog4 j.PatternLayout log4 j.appender.myAppehder.layout.ConversionPattern= %-5p Logger:%c{l} Date: %d{ISO8601) - %m%n Слушатель получает logger с помощью следующего кода. log = Logger.getLogger(ContextLogger.class); В результате logger, вслед за использующим его классом, получает имя com. jsps- ervletcookbook. ContextLogger. Таким образом, в соответствии со схемой имено- вания, используемой в log4j, logger, используемый слушателем, наследует appender, который в примере 14.13 определен для logger класса com.jspservletcokbook. Это происходит вследствие того, что в данной конфигурации не определяется непосредственно logger класса com. jspservletcookbook.ContextLogger; следовательно, этот log- ger слушателя наследует свои характеристики от ближайшего по иерархии определенного logger: com. jspservletcokbook. A.logger com. jspservletcokbook имеет кон- сольный appender и файловый appender. z В итоге, слушатель контекста сервлета посылает сообщения протокола на консоль и в файл h.7home/example.log. В примере 14.13 для консольного и файлового appender определены разные разметки (layout). Сообщения слушателя на консоли выглядят следующим образом. INFO - ServletContext shut down: The home web application INFO - ServletContext ready: The home web application Сообщения, направляемые в файл, имеют другой формат. INFO Logger:ContextLogger Date: 2003-05-12 16:45:20.398 - ServletContext shut down: The home web application INFO Logger:ContextLogger Date: 2003-05-12 16:45:20,999 - ServletContext ready: The home web application Формат этих сообщений состоит из имени уровня протоколирования (например, INFO), имени logger, даты запроса на занесение сообщения в протокол и самого сообщения. 346 | Глава 14. Протоколирование сообщений сервлетов и JSP
См. также Рецепт 14.2 по скачиванию и установке log4j\ рецепт 14.3 по использованию logger без файла свойств; рецепт 14.4 по добавлению appender к корневому logger; рецепт 14.5 по использованию шаблонов для appender; рецепт 14.6 по использованию logger в JSP; рецепт 14.8 по использованию log4j со слушателями событий сеанса; сайт для загрузки lug4j : http://jakarta.apache.orgAog4j/docs/download.html\ страница Javadoc по log4j : http:// jakarta.apache.orgAog4j/docs/api/index.html\ страницу с документацией по проекту log4j'. http://jakaria.apache.org/log4j/docs/docunientation.html. 14.8 Протоколирование сообщений слушателя событий сеанса Задача Вы хотите протоколировать сообщения от слушателя событий сеанса, имея возможность настраивать формат выдачи этих сообщений в соответствии со своими потребностями. Решение Создайте слушатель событий сеанса, который использует механизм протоколирова- ния log4j. Обсуждение Контейнер сервлетов извещает класс слушателя событий сеанса, когда он создает новый объект HttpSession (сеанс), а также когда закрывает сеанс. Web-приложения используют сеансы для отслеживания этапов работы пользователя с web-приложением, обычно идентифицируя сеансы с помощью cookie с именем JSESSIONID. За детальными сведениями о cookie обращайтесь к главе 10, а описание сеансов приведено в главе 11. Сервлет из примера 14.14 реализует интерфейс javax. servlet .http.HttpSes- sionListener, в частности два метода этого интерфейса: sessionCreated() и sessionDestx;oyed(). Сообщение касающиеся создания нового сеанса, выводятся в протокол в методе sessionCreated (), а сообщения, связанные с закрытием сеанс^, - в методе sessionDes.troyed (). Пример 14.14. Использование log4j в слушателе событий сеанса- package com.jspservletcookbook; import org.apache.Iog4j.Logger; import org.apache.Iog4j.PropertyConfigurator; import javax.servlet.*; Протоколирование сообщений слушателя событий сеанса | 347
import javax.servlet.http. *; public class SessionLogger implements HttpSessionListener { private Logger log; f public SessionLogger(){ /* logger обычно инициализируют в специальном слушателе инициализации или специальном сервлете. Если в данном случае 'это не так, инициализируем logger прямо здесь. java.util.ResourceBundle bundle = java.util.ResourceBundle.getBundle C "com.j spservletcookbook.global"); Propertyconfigurator.configure(bundle.getString( "log-configure-path")); */ log Logger.getLogger(SessionLogger.class); - } public void sessionCreated(HttpSessionEvent se) { //запрос на протоколирование сообщения уровня INFO log.info("HttpSession created: " + se.getSession().getldO); } - public void sessionDestroyed(HttpSessionEvent se) { // запрос на протоколирование сообщения о закрывающихся сеансах log.info("HttpSession invalidated: " ♦ se.getSession().getldO); } ) Создайте для этого класса конструктор без параметров, поместите класс в каталог WEB- INF/classes или в JAR-файл, расположенный в каталоге WEB-INF/lib, и зарегистрируйте его в web.xml. <listener> <listener-class>. com.j spservletcookbook.SessionLogger </listener-class> </listener>. Класс SessionLogger получает logger в конструкторе, а вообще это зависит оттого, был ли ранее в данном приложении (в каком-либо сервлете, или в слушателе событий контекста, как в рецепте 14.4) уже инициализирован механизм log4j. 348 | Глава 14. Протоколирование сообщений сервлетов и JSP
Web-приложение может настраивать механизм log4j в специально предназначенном для этого сервлете, или слушателе, и тогда другим классам или компонентам JavaBean, протоколирующим сообщения, нет необходимости включать в себя стадию настройки log4j. Вы можете инициализировать механизм log4j, используя сервлет, подобный приведенному в рецепте 14.6. Закомментированный код в конструкторе иллюстрирует еще один способ того, как данный класс слушателя может настроить свой собственный logger в случае, если меха- низм log4j не был настроен ранее в этом приложении. Ниже приведено сообщение, выводимое на консоль Tomcat после первого запроса к сервлету или JSP, участвующим в отслеживании сеанса. INFO - HttpSession created: A65481C53B92F869BD18961D635BBF52 Когда сеанс закрывается, на консоль выводится следующий текст. INFO - HttpSession invalidated: A65481C53B92F869BD18961D635BBF52 Как и в случае со слушателем, описанным в рецепте 14.7, здесь logger наследует спо- собы протоколирования (appender) от logger класса com. jspservletcookbook. Файл конфигурации из примера 14.13 показывает, как этот logger настраивается на выдачу сооб- щений и на консоль, и в файл example.log. Формат или разметка (layout) сообщений, выво- димых в журнал на базе файла, задается с помощью шаблона PatternLayout и отличается от разметки сообщений, выводимых с помощью appender консоли. Ниже приведен пример текста, заносимого в протокол на базе файла, когда контейнер сервлета закрывает сеанс. INFO Logger:SessionLogger Date: 2003-05-12 20:41:05,367 - HttpSession invalidated: A65481C53B92F869BD18961D635BBF52 См. также Рецепт 14.2 по скачиванию и установке. Iog4y, рецепт 14.3 по использованию logger без файла свойств; рецепт 14.4 по добавлению appender к корневому logger; рецепт 14.5 по йспользованию шаблонов для appender; рецепт 14.6 по использованию logger в JSP; рецепт 14.8 по использованию log4j со слушателями событий сеанса; сайт для загрузки log4j : http://jukarta.apache.org/log4j/doqs/downlQad.html-, страница Javadoc по log4j : http:/ /jakarta.apache.org/log4j/docs/api/index.html-, страницу с документацией по проекту log4j: http://jakarta.apache.orgAog4j/docs/documentation.html. Протоколирование сообщений слушателя событий сеанса | 349
ГЛАВА 15 Аутентификация клиентов 15.0 Введение Из-за роста электронной коммерции и связанного с этим увеличения потребно- сти в передаче и хранении конфиденциальных данных (например, номеров кредит- ных карт и финансовых счетов) обеспечение безопасности становится одной из важнейших задач, решаемых при создании web-приложений средствами Java. Рецепты данной главы посвящены решению вопросов, связанных с аутентифи- кацией, то есть с ответом на вопрос «действительно ли ты тот, за кого себя выда- ешь». Аутентификация обычно включает взаимодействие между клиентом или пользователем и кодом, работающим на стороне сервера и сравнивающим имя и пароль, введенные пользователем (а иногда - цифровой сертификат, био- метрические данные или другие доказательства), с теми данными, что хранятся, например, в базе данных пользователей. Приводимые здесь рецепты описывают, как установить защищенное соедине- ние (SSL - Socket Secure Level - уровень защищенных сокетов), а также как использовать BASIC-аутентификацию и аутентификацию на основе формы в. Apache Tomcat. В заключительных рецептах описано, как использовать в сервле- тах и JSP мощную инфраструктуру безопасности JAAS (Java Authentication and Authorization Service - Java-сервис для аутентификации и авторизации). 15.1 Создание пользователей и паролей в Tomcat Задача Вы. хотите завести имена и пароли пользователей для аутентификации запросов к определенным web-компонентам. Решение Добавьте необходимые имена, пароли и роли в файл tomcat users.xml. 350
Обсуждение Самый простой способ аутентификации пользователей в Tomcat включает заведение имен, паролей и ролей пользователей в файле tomcat-users.xml. Этот файл хранится в каталоге <Tomcat-installation-directory>/conf. Понятия «имя пользователя» и «пароль» знакомы каждому, но что такое роль? Роли - это логический способ объединения пользователей с одинаковыми зонами ответственности в группы, можно создать, например, группу manager (менеджеров) или группу databaseAd- ntin (администраторов базы данных). В примере 15.1 показан файл tomcat-users.xml, создающий две роли и двух пользователей посредством XML-элементов role и user. Пример 15.1. XML-файл tomcat-users <?xml version='1.0' encodings'utf-8'?> <tomcat-users> <role rolename="dbadmin"/> <role rolename="manager"/> , <user usernames"BruaeP" passwords"bwperry" roles="dbadmin,manager"/> <user usernames"Ji11H" passwords"jhayward" roles="manager"/> </tomcat-users> В примере 15.1 пользователю BruceP назначены две роли (dbadmin и manager), а пользователю JillH назначена только одна роль - manager. Tomcat использует этот файл при BASIC-аутентификации и аутентификации на основе формы, см. рецепты 15.3 и 15.4. См. также Документацию по Tomcat и рецепт 15.2 по установке SSL-соединения с аутентифика- цией: http://jakarta.apache.org/tomcat/tomcat-4.l-doc/ssl-howto.html', рецепт 3.9 по огра- ничению запросов к определенному сервлету; рецепт 15.3 по использованию BASIC-аутен- тификации; рецепт 15.4 по использованию аутентификации на основе формы; рецепт 15.5 по завершению работы с аутентифицированным Пользователем; рецепты 15.6-15.9 по использованию JAAS. Создание пользователей и паролей в Tomcat | 351
15.2 Установка SSL-соединения в Tomcat Задача Вы хотите в Tomcat установить SSL-соединение с тем, чтобы можно было переда- вать имя и пароль пользователя в зашифрованной форме. Решение Создайте для сервера Tomcat цифровой сертификат с помощью утилиты $JAVA_ HOME\bin\keytool, затем раскомментируйте в файле conf/server.xml элемент Connec- tor, связанный с установкой SSL-соединения. Обсуждение Для передачи имени и пароля пользователя, при работе на базе протокола HTTP, на Tomcat или на другом сервере приложений, необходимо использовать SSL. Этот протокол обеспечивает передачу по сети имени и пароля в зашифрованном виде, тем самым защищая их от хакеров и других злоумышленников. На Tomcat 4 установка SSL-соединения производится за два шага. 1. С помощью утилиты key tool создайте файл keystore, инкапсулирующий цифровой сертификат, используемый сервером для защищенных соединений. 2. В файле Tomcat conf/server.xml раскомментируйте элемент Connector, связанный с установкой SSL-соединения и, при необходимости, измените его атрибуты. Утилита keytool находится в подкаталоге bin того каталога, где у вас установлен JSDK. Следующая ниже строка создает на сервере Tomcat, в файле для хранения ключа с именем .keystore, один самоподписанный цифровой сертификат. Этот файл создается в домашнем каталоге пользователя, запустившего данную команду. %JAVA_HOME%\bin\keytool -genkey -alias tomcat -keyalg RSA В Unix эта команда будет выглядеть следующим образом. $JAVA_HOME\bin\keytool -genkey -alias tomcat -keyalg RSA ^r Чтобы команда выполнилась успешно, переменная окружения JAVA_HOME должна содержать путь к каталогу, в котором инсталлирован Java 2 SDK, например h:\ J# j2sdkl.4.1_01. 352 | Глава 15. Аутентификация клиентов
В примере 15.2 показаны сообщения, выводимые на консоль при выполнении команды keytool. Утилита запрашивает некоторые сведения о вас и вашей организации, но вы можете в ответ просто нажимать Enter, и будут приняты значения по умолчанию. Эта информация встраивается в сертификат сервера и показывается пользователю (в web- браузере), когда он запрашивает какие-либо компоненты посредством URL, начинающимся на https://. При установлении SSL-соединения в Tomcat, вы должны использовать один и тот же пароль и для файла keystore, и для сертификата, хранящегося в этом файле (см. пример 15.2). По умолчанию, в Tomcat используется пароль «changeit»: http://jakarta.apache.org/ tomcat/tomcat-4.1 -do c/ssi-howto.html. Пример 15.2. Вывод на консоль утилиты key tool Enter keystore password: changeit What is your first and last name? [Unknown]: Bruce Perry What is the name of your organizational unit? [Unknown]: What is the name of your organization? [Unknown]: What is the name of your City or Locality? [Unknown]: What is the name of your State or Province? [Unknown]: What is the two-letter country code for this unit? [Unknown]: Is CN=Bruce Perry, OU=Unknown, 0=Unknown, L=Unknowh, ST=Unknown, C=Unknown correct? [no]: yes Enter key password for <tomcat> (RETURN if same as keystore password): И, наконец, раскомментируйте элемент Connector в файле conf/server.xml (см. пример 15.3), удаляя обрамляющие его Символы комментария (<! — —>). После этого перезапустите Tomcat. Пример 15.3. Элемент Connector в файле server.xml <!— Define a SSL Coyote HTTP/1.1 Connector on port 8443 —> <Connector className= "org.apache.coyote.tomcat4.CoyoteConnector" pprt= • "8443" minProcessors=”5" maxProcessors="75" enableLookups= 12-1545 Установка SSL-соединения в Tomcat | 353
"true" acceptCount="100" debugs"0" schemes“https" secure="true" useURIValidationHack="false" disableUploadTimeout="true"> <Factory className= "org.apache.coyote.tomcat4.CoyoteServerSocketFactory" clientAuth= "false" protocol="TLS" /> «/Connector> В этом элементе используется не тот номер порта, который обычно используется для установки незащищенных HTTP-соединений (в Tomcat это 8080), а другой (8443). После того как вы перезапустите Tomcat, вы можете установить защищенное соединение с web- компонентом приложения home, применяя URL, который выглядит следующим образом, https://localhost:8443/home/sqlJsp.jsp / При наборе этой web-ссылки не забудьте про https (вместо http)! См. также Документацию по Tomcat и рецепт 15.2 по установке SSL-соединения с аутентифика- цией: http://jakarta.apache.org/tomcat/tomcat-4.l-doc/ssl-lwwtp.html', рецепт 15.1 по заведению в Tomcat имени и пароля пользователя; рецепт 15.3 по использованию BASIC-аутенгйфика- ции; рецепт 15-4 по использований аутентификации на основе формы; рецепт 15.5 по завершению работы с аутентифицированным пользователем; рецепты 15.6-15.9 по использованию JAAS. <_ I 15.3 Использование BASIC-аутентификации Задача Вы хотите для компонентов web-приложения в Tomcat использовать BASIC (базовую) аутентификацию. Решение Для защиты одного или более URL используйте в дескрипторе развертывания элементы security-constraint, login-config и security-role. 354 | Глава 15. Аутентификация клиентов
Обсуждение BASIC (базовая) конфигурация - это метод аутентификации, который используется с web-ресурсами уже несколько лет, и все распространенные браузеры его поддерживают. Этот метод аутентификации включает пересылку по сети имени и пароля пользователя, закодированных при помощи алгоритма кодирования содержимого - Base64._Base64 легко декодируется и совершенно не может служить защитой от просмотра. Выход заключается в использовании BASIC-аутентификации совместно с SSL, протоколом, шифрующим дан- ные перед их отправкой в сеть (см. рецепт 15.2). Вот как работает BASIC-аутентификация для web-приложений, установленных в Tomcat. 1. Задайте в файле conf/tomcat-users.xml, описанном в рецепте 15.1 имена, пароли и роли. 2. Создайте в дескрипторе развертывания (web.xml) элемент securi ty-constraint, задающий web-ресурсы, для которых вам требуется аутентификация. 3. Включите в web.xml элемент login-config; этот элемент имеет вложенный элемент auth-method, содержащий текст «BASIC». Когда пользователь обращается к какому-либо из защищенных ресурсов, сервер 4 °состав ответа на запрос включает заголовок, который выглядит примерно , * f следующим образом. WWW-Authenticate: BASIC Realm="MyRealm" Возможно, вам знакомо то, что происходит потом: браузер отображает стандартное диалоговое окно с просьбой ввести имя и пароль пользователя (рис. 15 1). Если имя и пароль неправильны, браузер или предоставляет пользователю еще один шанс ввести их, или просто возвращает код состояния сервера «401: Unauthorized». . Имена и пароли пользователей, задаваемые в файле conf/tomcat-users.xml, л чувствительны к регистру символов. И пользователь должен вводить их заглавными * f и строковыми буквами в полном соответствии с оригиналом, хранящимся в файле conf/tomcat-users.xml. В примере 15.4 показаны элементы webjanl, предназначенные для организации BASIC-аутентификации для шаблона URL /sqlJsp.jsp. Пример 15.4. Элементы, необходимые для организации аутентификации для файла JSP <!— Начало дескриптора развертывания web.xml —> <securi ty-cons traint> <web-resource-collection> <web-resource-name>JSP database component</web-resource-name> <ur1-pattern>/sqlJsp.j sp</url-pattern> <http-method>GET< /http-method> Использование BASIC-аутентификации | 355
<ht tp-method>POST< /http-raethod> </web-resource-collection> <auth-constraint> <role-name>dbadmin</role-name> </auth-cons traint> <user-data-constraint> < transport -.guarant ee>CONFIDENTIAL< / transpor t-guarant ee> </user-data-constraint> v </security-constraint> <login-config> <auth-method>BASlC</auth-method> / <I login-config> <security-role> <role-name>dbadmin</role-name> </security-role> <!— Продолжение дескриптора развертывания web.xml —> Элемент и security-constraint из примера 15.4 включает в себя элемент web- resource-collection. Этот элемент задает следующие ограничения, которые применяются для любого запроса к /sqUsp.jsp. • Ограничения касаются любых GET и POST запросов (это задается элементами http-method) • Элемент auth-constraint, вложенный в security-constraint, содержит элемент role-name со значением dbadmin. Таким образом, обращающийся с запросом должен ввести правильные имя и пароль пользователя (в том виде, как они заданы в файле tomcatusers.xml) и при этом ему должна была быть назначена роль dbadmin. Только те, кому была назначена роль dbadmin, могут получить доступ к данному защищенному web-pecypcy, то есть недостаточно только ввести корректные имя и пароль. На рис. 15.1 показано диалоговое окно, выдаваемое браузером Netscape 7.1, когда Tomcat использует BASIC-аутентификацию. Используемый URL! https://localhost:8443/ home/sqlJsp.jsp. Заметим, что этот URL использует в Tomcat защищенное соединение для запросов к данной странице JSP: протокол HTTPS и порт 8443. На рис. 15.2 показано окно браузера пользователя, не выдержавшего проверку. См. также Документацию по Tomcat и рецепт 15.2 по установке SSL-соединения с аутентифика- цией: http://jakarta.apache.org/tomcat/tomcat-4.1-doc/ssi-howto.html', рецепт 3.9 по ограниче- нию запросов к определенному сервлету; рецепт 15.5 по завершению работы с аутенти- фицированным пользователем; рецепты 15.6-15.9 по использованию JAAS. 356 | Глава 15. Аутентификация клиентов
Prompt Password: a Enter userrtame and password for '"Iocalho$t8443" at localiost8443 . User Name: О Use Password Manager to remember these values. г ок* i - Puc. 15.1. Диалоговое окно браузера для ввода имени и пароля пользователя •S^SMai ДА1М Ц Home Radio Netscape litl [ Apache Tomcat/4.1.24 - Error report Eie Edit yiew .Go Bookmarks Joels У/indow Help lockmarks I [% https://localioS21 I CL Search' _ «ы»; .............— ЗИП Ф9 Apache Tomcat/4.1 24 - Error report Netscape HTTP Status 401 - а® Status report This request requires HTTP authentication Q ache Tomcat/4.1.24 Puc. 15.2. Отображение в браузере страницы с кодом состояния сервера 401 Использование ВА$1С«аутентификации 357
15.4 Аутентификация с использованием формы Задача Вы хотите создать собственную форму для получения имени и пароля пользователя во время BASIC-аутентификации. Решение f Используйте в дескрипторе развертывания элемент login-config и вложенному в него элементу auth+method задайте значение «FORM». Обсуждение API сервлет'ов предлагает в качестве альтернативы BASIC-аутентификации использо- вать аутентификацию на основе формы (fonn-based authentication) Этот метод позво- ляет создать собственную форму для получения имени и пардля пользователя, а также задать содержательную страницу, которую сервер будет отсылать клиенту, если аутен- тификация пользователя завершилась отказом в доступе. Это дает возможность создать более дружественный и настраиваемый интерфейс для приложений, включающих BASIC-аутентификацию. '' Метод на основе формы все равно должен использоваться в сочетании с SSL * и протоколом HTTPS, чтобы имя и пароль передавались по сети в зашифрованном fl*/ виде' В примере 15.5 показаны установки, задаваемые дескрипторе развертывания web- приложения, для аутентификации на основе формы. Они отличаются от тех, что приво- дились в рецепте 15.3 в одном: в элементе login-config, который выделен в этом фрагменте полужирным шрифтом. Пример 15.5. Элементы файла web.xml, предназначенные для аутентификации на основе формы <!— Начало дескриптора развертывания web.xml —> • <security-constraint> <web-resource-collection> <web-resource-name>JSP database component</web-resource-name> <url-pattern>/sqlJsp.jsp</url-pattern> <http-method>GET</http-method> <http-method>POST</http-method> </web-resource-collection> 358 | Глава 15. Аутентификация клиентов
<auth-cons traint> <role-name>dbadmin</role- name> </auth-constraint^ л <user-data-constraint> <transport-guarantee>CONFIDENTIAL</transport-guarantee> </user-data-constraint> </security-constraint» <login-config> <• <auth-method>FORM</auth-method> <fonn-login-conf ig> <form-login-page>/login.html< /form-login-page> ' < form-error ~page>/ loginError. j sp< / f orm-error-page> </form-login-config> </login-config> <security-role> <role-name>dbadmin</role-name> </security-role> <!— Продолжение дескриптора развертывания web.xml —> Элемент auth-method содержит текст «FORM». Элемент form-login-config задаст страницу с формой для аутентификации (flogin.html) и страницу, информирующую о неудаче прохождения аутентификации (/loginError.html), используемыми в данном приложении. Слэш, предшествующий именам файлов, означает, что местоположение страниц отсчитывается относительно корневого каталога web-приложения. Почти по волшебству, если пользователь обращается к защищенному ресурсу вашего приложения, сервер посылает ему страницу login.html (в данном случае), вместо инициа- ции обычного поведения, при котором браузер отображает свое собственное диалоговое окно для ввода имени и пароля. Если имя и пароль, введенные пользователем, оказались некорректными, сервер направит его запрос к странице loginError.html. В примере 15.6 для справки приводится страница login.html. Пример 15.6. Форма для аутентификации <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head> < t i 11 e>Wel come< /1 i t le> </head> <body bgcolor="#ffffff•> <h2>Please Login to the Application</h2> Аутентификация с использованием формы | 359
<form method»"POST” action»"j_eecurity_check"> ctable border="0"><tr> <td>Enter the username: </tdxtd> <input type»"text" name»" j_usemame" size="15"> </td> </tr> <tr> <td>Enter the password: </tdxtd> <input type«"password" name»"j_password" size»"15"> </td> </tr> <tr> <td> <input type»"submit" value»"Submit"» </td> </tr> </table» </£orm> </body> </html> На рис. 15.3 показано, как эта форма выглядит в браузере. Рис. 15.3. Форма, предназначенная для аутентификации на основе формы 360 | Глава 15. Аутентификация клиентов
При аутентификации на основе формы атрибут action тега form должен иметь значение «j_security_check». Атрибуты name элементов input, предназначенных для ввода имени и пароля, должны иметь значения «j_user_name» и «j_password» соответственно. На рис. 15.4 показана HTML-страница, которую сервер посылает пользователю, если его аутентификация завершилась отказом в доступе. Рис. 15.4. Аутентификация на основе формы позволяет задать вашу собственную страницу, информирующую об отказе в доступе Исходный текст данной страницы приведен в примере 15.7. Подход на основе формы более предсказуем и дружественен, чем полагаться на методы, используемые различными браузерами, для BASIC-аутентификации. Пример 15.7. Сервер отображает данную страницу loginError.jsp, если пользователь не прошел проверку и ему отказано в доступе <html> <head> <title>Login Error</title> </head> <body bgcolor="#ffffff"> <h2>We Apologize, A Login Error 0ccurred</h2> Please click <a href="http://localhost:8080/home/sqlJsp.jsp">here</a> for another try. <%— Или динамически перечислите здесь гиперссылки на свои защищенные ресурсы, возможно извлекая их из базы данных или файла конфигурации, вместо того, чтобы вручную кодировать ссылку на странице ошибок. —%> </body> </html> Аутентификация с использованием формы | 361
См. также Документацию по Tomcat и рецепт 15.2 по установке SSL-соединения с аутентифика- цией: httpi//jakarta.apache.org/tomcat/tomcat-4.1-doc/ssl-howto.html\ рецепт 3.9 по ограни- чению запросов к определенному сервлету; рецепт 15.5 по завершению работы с аутенти- фицированным пользователем; рецепты 15.6-15.9 по использованию JAAS. 15.5 Завершение работы с аутентифицированным пользователем * / г Задача Вы хотите завершить работу аутентифицированного пользователя в системе, использующей аутентификацию на основе формы. Решение Вызовите метод invalidate!) объекта HttpSession (сеанс), связанного с дан- ным пользователем. Обсуждение Вызов метода invalidate () для объекта HttpSession (сеанса), связанного с поль- зователем, приведет к прекращению сеанса работы этого пользователя в приложений, использующем аутентификацию на основе формы. Сервлет из примера 15.8 отображает некоторую информацию об аутентифицированном пользователе, а затем прекращает работу этого, пользователя, завершая его сеанс. И при следующем запросе данного пользо- вггеля к защищенному ресурсу web-приложение направит его к странице, заданной в качестве страницы для аутентификации, поскольку работа этого пользователя с приложе- нием была прекращена. Пример 15.8. Прекращение работы пользователя package com. jspservletcookbook; import javax.servlet.*; import j avax.servlet.http.*; public class LogoutServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, j ava.io.lOException { 362 | Глава 15. Аутентификация клиентов
HttpSession session 5 request.getSession(); response.setContentType(“text/html"); java.io.Printwriter out = response.getWriter(); out.printIn( "<htmlxheadxtitle>Adthenticated User Info</titlex/headxbody>") ; out.println(“<h2>L6gging out a user</h2>"); out.println("request.getRemoteUser() returns: "); //Получаем имя, под которым зарегистрировался пользователь - String remUser в request. getRemoteUser ().; //Если request.getRemoteUser() возвращает null,- то //пользователь не был аутентифицирован (зарегистрирован) out.println(remUser == null 7 "Not authenticated." : remUser ); out.println("<br>"); out .println("request. isUserInRole (\ " dbadmin \ ") returns: "); / /Определяем, назначена ли пользователю роль dbadmin .boolean isInRole request.isUserInRole("dbadmin!'); out.println(isInRole); out.printIn("<br>"); //Прекращаем работу пользователя, закрывая сеанс (HttpSession) session.invalidate(); out. println (" </bodyx/html>") ; } I/doGet public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.lOException { doGet(request,response); } //doPost } //LogoutServlet Если пользователь, прошедший регистрацию (аутентифицированный), обратится к данному сервлету, он увидит информацию, приведенную на рис. 15.5. Сервлет отображает значения, возвращаемые методом HttpServletRequest.getRemo- teUser () (имя пользователя) и методом HttpServletRequest. isUserInRole (). Последний возвращает значение типа boolean, показывающее, связана ли с данным поль- зователем роль, указанная в качестве параметра String метода. Затем сервлет завершает'сеанс пользователя, прекращая ее работу. Если вслед за этим произойдет повторно? обращение того же пользователе к сервлету, она увидит информацию, приведенную на рис. 15.6. Завершение работы с Аутентифицированным пользователем | 363
I) Authenticated User Info - Netscape л fie £dfc yiew fio Bookmarks loots Window HdP EHE3 чВNewr fS^wwzent4sw*E... | Zi‘'HttpSess>on(Java2F1adormEhL . VAi<henficated User Info Logging out a user requestgetRemoteUserQ returns: BruceP' request.isUserInRole(“dbadmm") returns, true S О? □ 6one ¥ Puc. 15.5. Сервлет, перед тем как прекращает работу пользователя, отображает некоторую информацию о нем (§) Authenticated User Info - Netscape ж file £tft yiew fio .Bookmarks loots Window Help ‘ I http://localwst8080A»me/servlet/com.jspst,s31 Search^ ж Home fie, Radio Search uBookmarks v Amazon.com--..' ‘-^Java Authentic.:: у Java 2 Logging out a user requestgetRemoteUserQ returns: Not authenticated. request.isUserIhRole("dbadmin") returns: false © Q Q? □ 0°ne EE -u. Puc. 15.6. Выходная информация сервлета, свидетельствующая, что сеанс пользователя был ранее прекращен1 См. также Документацию по Tomcat и рецепт 15.2 по установке SSL-соединения с аутентифика- цией: http://jakarta.apache.org/tomcat/tomcat-4.l-doc/ssl-howto.html', рецепт 3.9 по ограни- чению запросов к определенному сервлету; рецепты 15.6-15.9 по использованию JAAS. 364 | Глава 15. Аутентификация клиентов
15.6 Использование JAAS для создания аутентифицирующего модуля Задача Вы хотите использовать JAAS (Java Authentication and Authorization Service - Java- службу по аутентификации и авторизации) для создания модуля аутентификации, который мог бы использоваться сервлетом или JSP. Решение Создайте в своем приложении класс javax.security.auth.spi.LoginModule и сохраните этот класс в, каталоге WEB-INF/classes или в каталоге WEB-INFAib (в JAR- файле). Обсуждение JAAS представляет собой API для реализации функций безопасности, и может исполь- зоваться для создания автономных, подключаемых инструментов аутентификации или авторизации для Java-приложений. Термин подключаемый (pluggable) - означает, что код безопасности JAAS не привязан к конкретному приложению; он хранится в JAR-файле и может быть погружен в.или подключен к web-приложению или Java-программе другого типа. *'' JAAS - это Java-версия инфраструктуры, носящей имя РАМ (Pluggable Authentication Module - подключаемый модуль аутентификации). Подробнее об этом читайте в http://java.sun.coin/security/jaas/doc/pam.html. Итак, в рецептах 15.5-15.6 описывается простой пример JAAS-аутентификации, в которой задействованы два класса и один сервлет, использующий JAAS API. В нашем примере эти классы хранятся в каталоге WEB-INF/classes. Однако во многих организациях имеется более сложная архитектура безопасности, которая реализует более всестороннюю модель аутентификации и авторизации и, следовательно, включает в себя больше Java-кода и объектов. В этом случае вам потребуется создать для JAAS-кода собственное имя пакета, архивировать его в JAR-файл и помещать этот файл в каталог WEB-INFAib для использова- ния web-приложением. При использовании JAAS для аутентификации web-клиентов необходимо сделать следующее. 1. Убедитесь, что у вас инсталлированы пакеты JAAS и, таким образом, они доступны для использования в web-приложениях. JAAS интегрирован в Java 2 SDK версии 1.4, и вы можете использовать JAAS, если ваш сервер приложений (Tomcat или другой) использует эту версию Java. Если же вы используете Java 1.3, вам потребуется само- стоятельно скачать и установить JAAS как расширение Java, для этого обратитесь на сайт http://java.sun.com/products/jaas/index-IO.html. Использование JAAS для создания аутентифицирующего модуля | 365
2. Создайте класс4 Log inModule, осуществляющий аутентифик щии. Этот класс нужно сохранить в каталоге WEB-INF/classes или в виде JAR-фашта в WEB-INFAib. 3. Создайте класс CallbackHandler, который отвечает за взаимодействие с клиен- том для получения его имени и пароля. Сохраните этот класс вместе с вашим классом - LoginModule. 4. Создайте файл конфигурации JAAS, который указывает, какой(ие) класс(ы) LoginMod- ule вы используете для аутентификаций. Поместите файл в такое место, где его сможет прочитать код, связанный с JAAS (описание файла конфигурации см. в рецепте 15.7). 5. Включите в код сервлета объект LoginContext и вызовите его метод login (). ' •' Аутентификация - это проверка того, входит ли данный пользователь или клиент в круг лиц, имеющих допуск, и/ обычно она заключается в проверке f/.* принадлежности введенных имени и пароля к некоторому множеству имен и паролей. JAAS также можно использовать для авторизации - определения пределов доступа к данным для пользователя, успешно прошедшего аутентификацию. В этом рецепте мы сосредоточимся на аутентификации. Чтобы понятнее объяснить этот сложный материал, я распределил эти шаги по трем рецептам. • В данном рецепте описаны шаги 1-3. • В рецепте 15.6 показано, как создать файл конфигурации JAAS. • ' В рецепте 15.7 показано использование классов аутентификации JAAS в сервлете. В примере 15.9 приведен класс, реализующий интерфейс javax. security.auth. spi.LoginModule. Он выполняет большую часть работы по идентификации клиен- тов и использует пакеты, являющиеся частью JAAS API (выделены в коде полужирным). Чтобы сделать этот класс доступным исполнительной подсистеме, запускающей сервлеты на выполнение, поместите его в каталог WEB-INF/classes или в виде JAR- файла в каталог WEB-INFAib. Пример 15.9. Класс LoginModule для web-аутентификации package com.j spservletcookbook; import java.util.Map; import j ava.sql.*; import javax.naming.Context; import j avax.naming.Initialcontext; inport j avax. nami ng. NamingException; inport j avax.security.auth.spi.LoginModule; inport javax.security.auth.*; inport javax.security.auth.callback.*; inport javax.security.auth.login.*; import j avax.sql.*; 366 | Глава 15. Аутентификация клиентов
public class DataSourceLoginModule implements LoginModule { I/Эти переменнее экземпляра будут инициализированы в //методе initialize() CallbackHandler handler; Subject subject; Map sharedState; Map options; private boolean loginPassed false; public DataSourceLoginModule(){}//конструктор без аргументов public void initialize(Subject.subject, CallbackHandler handler, Map sharedState, Map options){ this.subject = subject; this.handler «handler; » this.sharedState « sharedState; this.options = options; } public boolean login() throws LoginException { String name = x String pass = "" ; Context env = null; Connection conn = null; Statement stmt = null; ResultSet rs = null; <• DataSource pool = null; boolean passec} = false; try{ . //Создаем массив CallBack для передачи //в метод CallbackHandler.handle() Callback[] callbacks » new Callback[2]; //He используйте конструктор NameCallback без аргументов! callbacks[0J ® new NameCallback("Username:"); //He используйте без аргументов Passwordcallback! callbacks[1] = new Passwordcallback("Password:", false); 1 * handler.handle(callbacks); //Получаем имя и пароль NameCallback nameCall « (NameCallback) callbacks[0]; name nameCall. getName (); Использование JAAS для создания аутентифицирующего модуля | 367
Passwordcallback разпСаИ (Passwordcallback) callbacks [1]; pass = new String (passCall.getPassword() ); //Ищем наш DataSource чтобы иметь возможность проверить Нуойя и пароль env (Context) new Initialcontext().lookup("java:comn/env"); pool я (DataSource) env.lookup("jdbc/oracle-8i-athletes"); if (pool == null) throw new LoginExcept ion ( "Initializing the DataSource failed."); // SQL-запросы для проверки имени и пароля в таблице Z/с именем athlete String sql "select * from athlete where name*»’"+nama+"' String sqlpass и "select * from athlete where passwrd"*"+pass+ //Получаем Connection (соединение)- из пула (pool) соединений conn = pool.getConnection(); i stmt = conn.createStatementO ;• //Проверяем имя пользователя rs s tint, execut eQuery ( sql) //Если полученный набор содержит строки, то имя корректное J/и функция next() вернет true passed ж rs.nextO; rs. close О; if (1 passed){ loginPassed false; 4 throw new FailedLoginException( "The username was not successfully authenticated"); } //Проверяем пароль rs stmt. executeQuery( sqlpass);’ passed rs.nextO; 4 if (1 passed){ loginPassed false; • throw new FailedIioginException( "The password was not successfully authenticated"); } else { loginPassed true; 368 | Глава 15. Аутентификация клиентов
return true; } . } catch (Exception e) ( throw new LoginException(e.getMessage()); } finally { * try{ //закрываем Statement (оператор) stmt.close() ; -I //Возвращаем Connection (соединение) в пул conn.close О; } catch (SQLException sqle){ } } //finally } //login public boolean consult() throws LoginException { //Здесь мы нечего не делаем, поскольку этот класс //представляет простой пример аутентификации с помощью JAAS. //Просто возвращаем то, что вернет функция login(). return loginPassed; } public boolean abort() throws LoginException { //Очищаем состояние boolean bool = loginPassed; loginPassed = false; return bool; } public boolean logout() throws LoginException { //Очищаем состояние • loginPassed = false; ", return true; } //logout J //DataSourceLoginModule Использование JAAS для создания аутентифицирующего модуля | 369
Класс, реализующий интерфейс LoginModule, реализует пять объявленных в этом интерфейсе методов: initialize(), login(), commit(), abort() и logout(). Метод login () инициирует главную задачу проверки имени и пароля и определения того, успешно ли клиент прошел аутентификацию. Поскольку это всего лишь простой пример, в классе DataSourceLoginModule упор сделан на методе login (). Другие методы из примера 15.9 просто очищают состояние объекта, чтобы он мог выполнить другую аутентификацию, хотя более сложные процессы регистрации пользователя включают и иные задачи, например настройку для аутентифицированного пользователя объектов, связанных с авторизацией. — JAAS - это достаточно всеобъемлющая инфраструктура. Если вы хотите разрабатывать более продвинутые 1АА2?-программы,.чем те, что описаны в данном г рецепте, обратитесь к документации от Sun Microsystems: http7/javp.sun.com/ products/jaas/index- 14.html. JAAS разделяет взаимодействие с клиентом (например, получение имени и пароля) и выполнение аутентификации на CallbackHandler и LoginModule соответст- венно. LoginModule из примера 15.9 использует CallbackHandler для получения имени и пароля, затем эта информация проверяется посредством доступа к Таблице из базы данных Oracle 8i. Для получения доступа к базе данных модуль использует поиск JNDI, см. главу 21, где описываются детали этого процесса. Класс LoginModule занимает Connection (соединение) из пула соединений с базой данных, использует SQL-операторы SELECT для проверки имени и пароля кли- ента, а затем возвращает соединение (закрывая его) в совместно используемый пул. Класс CallbackHandler, приведенный в примере 15.10, получает имя и пароль кли- ента из параметров HTTP-запроса, Конструктор класса включает аргумент ServletRe- quest, из которого класс может получить параметры запроса, вызвав метод getParameter () класса ServletRequest. Этот процесс станет намного понятнее, если вы посмотрите, как сервлет (см. пример 15.11 в рецепте 15.7) использует эти классы для выполнения аутентификации. Пример 15.10. Класс CallbackHandlerдля web-аутентификации package com.j spservletcookbook; import j avax.security.auth.calIback.*; import j avax. servlet*. ServletRequest; public class WebCallbackHandler implements CallbackHandler { private String userName; private String password; public WebCallbackHandler(ServletRequest request){ userName = request. getParameter ("userName"); password = request.getParameter("password"); ) 370 | Глава 15. Аутентификация клиентов
public void handle(Callback[] callbacks) throws java.io.lOException, UnsupportedCallbackException { //Добавляем имя и пароль из параметров запроса к //Callbacks for (int i = 0; i < callbacks.length; i++){ if (callbacks[i] instanceof NameCallback){ NameCallback nameCall (NameCallback) callbacks [i]; nameCall. setName (userName); } else if (callbacks[i] instanceof Passwordcallback){ Passwordcallback passCall (Passwordcallback) callbacks[i]; passCall.setPassword(password.toCharArray()); } else{ . throw new UnsupportedCallbackException (callbacks[i], "The CallBacks are unrecognized .in class: n+getClass(). getName()); / } } //for } //handle } Подытожим то, как LoginModule и CallbackHandler действуют вместе, перед тем как вы перейдете к следующим двум рецептам: один из конструкторов класса Log- inContext принимает CallbackHandler в качестве второго параметра, как пока- зано в следующем коде. WebCallbackHandler webcallback = new WebCallbackHandler(request); LoginContext Icontext = null;*. try{ Icontext = new LoginContext( "WebLogin",webcallback ); } catch (LoginException le) { //ответ- на исключение...} Рецепт 15.7 показывает, как создать файл конфигурации JAAS, который определяет класс(ы) LoginModule, который определенное приложение будет использовать при аутентификации. См. также Руководство по JAAS для разработчика от Sun Microsystems: http://java.sun.eom/j2se/l.4. 2/docs/guide/security/jaas/JAASLMDevGuide.html; список руководств и примеров программ по JAAS: http://java.sun.coni/j2se/L4.2/docs/guide/security/jaas/JAASRefGuide.html', докумен- тацию (JavaDoc) по файлам конфигурации JAAS: http://java.sun.eom/j2se/l.4.l/docs/api/ javax/secmity/authAogin/Configuration.html: рецепт 15.9 по использованию JAAS с JSP. Использование JAAS для создания аутентифицирующего модуля | 371
15.7 Создание файла конфигурации JAAS Задача Требуется создать файл конфигурации JAAS. Решение Создайте файл конфигурации, затем задайте местоположение этой конфигурации в вашей файловой системе с помощью файла ${java.homf]/jre/lib/security/java.security. 1 Обсуждение , Использование JAAS также включает создание файла конфигурации, необходимого для определения того, какой класс(ы) LoginModule (модуль аутентификации) будет использоваться для аутентификации в конкретном приложении. Файл конфигурации цз примера 15.11 описывает приложение с именем «WebLogin». Пример 15.11.. Файл конфигурации JAAS WebLogin { com.jspservletcookbook.DataSourceLoginModule requisite; Хотя в этом рецепте задан всего один модуль атентификации, одной из мощных воз- можностей конструкции безопасности JAAS является возможность использования для аутентификации пользователей нескольких модулей аутентификации (классов Log- inModule) или слоев. Пользователь, прежде чем он получит доступ к web-компоненту и данным, может быть аутентифицирован несколькими способами (например, сначала сканирование радужной оболочки глаза, а затем ввод имени и пароля). Файл конфигурации задает: • полностью квалифицированное имя класса(ов) LoginModule; • значение флага, являющееся константным выражением, например «required» или «requisite». В данном примере используется «requisite». Возможные значения флага приведены в табл. 15.1; • Одну или более опций (в примере 15.11 опции не заданы). Опции представляют собой список пар имя/значение, разделенный пробелами, например debug=«true» । (можно использовать любые необходимее вам пары). Опции позволяют файлу кон- фигурации передавать свойства и значения в соответствующий LoginModule. 372 | Глава 15. Аутентификация клиентов
Табл. 15.1. Возможные значения флага для файлов конфигурации JAAS Имя флага Описание Required Обязательный. Для успешной полной аутентификации обязательно успешное завершение данного модуля аутентификации (LoginMod- ule); процесс полной аутентификации (куда этот модуль входит как этап) завершится отказом в доступе, если LoginModule, помеченный флагом «required», завершится отказом. Однако даже в случае отказа, процесс аутентификации будет продолжен в соответствии со списком модулей аутентификации Requisite Необходимый. Для успешной полной аутентификации необходимо успешное завершение данного модуля аутентификации (LoginMod- ule); если аутентификация в данном модуле завершится отказом, управление будет передано в само приложение (вместо продолжения выполнения оставшейся части списка модулей аутентификации) Sufficient Достаточный. Если в данном модуле аутентификация завершилась успешно, управление передается в приложение, при этом оставшиеся модули аутентификации не выполняются. Если здесь аутентификация завершится отказом, то процесс аутентификации будет продолжен в соответствии со списком модулей. Иными словами, отказ в данном модуле не означает окончательного отказа в доступе, как в случае с флагами «required» и «requisite» Optional Дополнительный. Успех аутентификации в данном модуле не обя- зателен для окончательного ответа. Независимо от успеха или отказа, аутентификация продолжается в остальных перечисленных модулях аутентификации Основная структура файла конфигурации выглядит примерно следующим образом. ИмяПриложения { ИмяМодуля Флаг Опции; ИмяМодуля Флаг Опции; ИмяМодуля Флаг Опции; } ; ИмяДругогоПриложения{ ИмяМодуля Флаг Опции; ИмяМодуля Флаг Опции; }; Еще раз отметим, что вам не обязательно использовать несколько модулей аутенти- фикации. Создание файла конфигурации JAAS | 373
Детали конфигурирования можно найти по адресу: http://java.sun.eom/j2se/l.4.l/ docs/api/javax/security/authdogin/Configuration.html. Но как реализация JAAS находит файл конфигурации? Каталог ${java.home}/jreAib/ security содержит файл с именем java.security. В терминах безопасности Java это файл «свойств» или файл «политики» - текстовый файл, содержащий пары имя/значение. Приводимая ниже строка' задает файл конфигурации JAAS для сервлета аутентифика- ции из примера 15.11. login.config.url.l=file:h:/home/.j ava.login.config Если у вас есть другие файлы конфигурации JAAS, которые вы хотите ком- бинировать с данным, используйте синтаксис следующего вида: login. config. url. 2=file:h: /home/ .my.config (обратите внимание на увеличение числа: 2), эту строку также поместите в файл java.security. Вы можете использовать любой способ именования файла; имя файла конфигурации не обязано начинаться с точки. В одном файл конфигурации JAAS можно задать модули аутентификации для нескольких имен приложений. В рецепте 15.8 приведен сервлет, использующий модуль аутентификации (LoginModule), описанный в рецепте 15.5. “i См. также Руководство по JAAS для разработчика от Sun Microsystems: http://java.sun.com/j2se/ l:4.2/docs/guide/security/jaas/JAASLMDevGuide.html’, список руководств и примеров программ по JAAS: http://javfl.sun.eom/j2se/l.4.2/docs/guide/security/jaas/JAASRefGuide. Iinnl', документацию (JavaDoc) по файлам конфигурации JAAS: http://java.sun.eom/j2se/l. 4.1/docs/apt/javax/security/auth/login/Configuration.html; рецепт 15.8 по использованию JAAS с сервлетами; рецепт 15.9 по использованию JAAS с JSP. 374 | Глава 15., Аутентификация клиентов
15.8 Использование JAAS в сервлетах Задача Вы хотите аутентифицировать клиентов сервлета с помощью JAAS. Решение Создайте компонент JavaBean, обертывающий функциональность классов JAAS API, которые вы включаете в свое приложение. Обсуждение Использование JAAS в сервлетах предполагает, что в вашем приложении установлен LoginModule (или в каталоге WEB-INF/classes, или в виде JAR-файла, в WEB-INFAib). В примере 15.12 показан сервлет с именем LoginServlet, реализующий JAAS- аутентификацию. Этот сервлет использует класс CallbackHandler, описанный в рецепте 15.5. Этот CallbackHandler также необходимо поместить в каталог WEB- INF/classes или включен в JAR-файл, в каталоге WEB-INFAib. Запрос из браузера к дан- ному сервлету выглядит примерно так: http:/Aocalhast:8080/hoine/servlet/com.jspservlet- cookbookLoginServlet?userName-Bruce%20W%20Perry&password~bwpl968. Если вы хотите использовать более безопасную стратегию и сделать так, чтобы имена и пароли не были видны в URL, используйте из HTML-формы запрос POST совместно с SSL (рецепт 15.2). Пример 15.12. Сервлет для аутентификации и входа клиента package com.jspservletcookbook; import javax.servlet.*; import j avax.servlet.http.* ; import javax.security.auth.login.LoginContext; import javax.security.auth.login.LoginException; import j avax. security. auth. cal Iback. Cal IbackHandler ; public class LoginServlet extends HttpServlet { public void dpGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.lOException { //CallbackHandler получает имя и пароль из параметров //запроса в URL; поэтому, ServletRequest (запрос) //передается в конструктор класса CallbackHandler WebCallbackHandler webcallback new WebCallbackHandler(request); LoginContext Icontext null; boolean loglnSuccess true; Использование JAAS в сервлетах | 375
try{ Icontext » new LoginContext! "WebLogin",webcallback ); //этот метод выбрасывает исключение LoginException //если аутентификация завершилась отказом Icontext.login()3 } catch (LoginException lge){ ч loginSuccess “ false; ' > response.setContentType("text/html"); java.io.Printwriter out = response.getWriter(); out.println( "<htmlxheadxtitle>Thanks for logging in</title>"+ x "</headxbody>M) ; out.printIn("<h2>Your logged in status</h2>"); out.printin(""+ ( loginSuccess ? "Logged in" : "Failed Login" )); out.println( "</bodyx/html>"); } //doGet public void doPost(HttpServletRequest request, ' z HttpServletResponse response) throws ServletException, java.io.lOException { doGet(request,response); } //doPost } //LoginServlet Этот сервлет делает следующее. 1. Создает объект WebCallbackHandler (пример 15.10) и передает объект Servle- tRequest (запрос) в конструктор (из него CallbackHandler получает имя и пароль клиента). 2. Создает объект LoginContext, передавая конструктору два параметра: имя прило- жения, осуществляющего аутентификацию (из нашего файла конфигурации в рецепте 15.6, «WebLogin») и объект WebCallbackHandler. 3. Вызывает метод login О объекта LoginContext, который на нижнем уровне вызывает метод login () объекта DataSourceLoginModule (из примера 15.9), для выполнения аутентификации. На рис. 15.7 показано окно браузера после успешной попытки войти с использова- нием данного сервлета. 376 | Глава 15. Аутентификация клиентов
jjS Thanks for logging in Netscape j file £cft yiew go Bookmarks Jools Window Це|р HBG S> Я Mail AIM -в-Home QJ Radio W Netscape Sea ch L2 Bookmat । Your logged in status Logged in Puc. 15.7. Сервлет LoginServlet сообщает об удачной регистрации См. также Рецепт 15.6 по созданию JAAS-класса LoginModule; рецепт 15.7 по созданию файла конфигурациих JAAS; главу 21 по доступу к базе данных из сервлетов; руководство по JAAS для разработчика от Sun Microsystems: http://java.sun.eom/j2se/l.4.2/docs/guide/ security/jaas/JAASLMDevGuide.html; список руководств и примеров программ по JAAS: http://java.sun.eom/j2se/l.4.2/ddcs/guide/security/jaas/JAASRefGuide.html; документацию (Jav- aDoc) по файлам конфигурации JAAS: http://java.sun.eom/j2se/l.4.l/docs/api/javax/security/ auth/login/Configuration.html; рецепт 15.9 по использованию JAAS с JSP. 15.9 Использование JAAS в JSP Задача Вы хотите для аутентификации клиентов использовать JSP и JAAS. Решение Создайте компонент JavaBean, обертывающий функциональность классов JAAS API, которые вы включаете в свое приложение. Обсуждение Рецепты 15.5-15.7 приоткрывают основы JAAS, а данный рецепт фокусируется на адаптации JSP к API безопасности JAAS. Для выполнения регистрации (входа в приложение) страница JSP из данного рецепта использует компонент JavaBean. Использование JAAS в JSP | 377
Компонент JavaBean, приведенный в примере 15.13, имеет два свойства (в форме переменных экземпляра): объект ServletRequest и значение типа boolean, сигна- лизирующее о том, прошли ли имя и пароль Данную проверку на наличие допуска. Компо- нент JavaBean передает объект ServletRequest (запрос) в конструктор объекта WebCallbackHandler; а объект WebCallbackHandler извлекает из параметров запроса введенные имя и пароль. Пример 15.13. Компонент JavaBean, использующий для выполнения аутентификации JAAS API package com.j spservletcookbook; import javax.servlet.ServletRequest; import j avax. security. auth. login. LoginContext; inroort javax.security.auth.login.LoginException; public class LoginBean { //закрытые переменные экземпляра компонента JavaBean (свойства) private ServletRequest req; boolean loginsuccess; public LoginBean(){ }//конструктор без аргументов public boolean getLoginSuccess() throws LoginException { //Свойство ServletRequest должно быть установлено до ? //Вызова данного метода, чтобы мы могли здесь извлечь из него //имя и пароль if (req null) thrcyw new IllegalStateException ( "The ServletRequest cannot be null in getLogin() WebCallbackHandler webcallback e new WebCallbackHandler (req) ; try{ LoginContext Icontext new LoginContext(- "WebLogin", webcallback); //Вызываем метод login() объекта LoginContext; если //не будет выброшено исключение, метод вернет true Icontext.login(); return true; } catch (LoginException Ige){ //в доступе отказано, поскольку метод LoginContext.login() //выбросил исключение LoginException return false; } } //getLoginSuccess 378 | Глава 15. Аутентификация клиентов
public void setReq(ServletRequest request) { if (request == null) throw -new 11legalArgumentException( "ServletRequest argument was null in: "•+ getClass().getName()); this.req = request; } //setReq } // LoginBean Работа компонента JavaBean зависит от правильной установки его свойства Serv- letRequest, до того, как будет вызван метод getLoginSuccess (). Данный метод выполняет регистрацию пользователя, используя знакомый нам класс LoginContext и его метод login () (также знакомый нам по рецепту 15.5!). Java-объект, использующий данный компонент JavaBean, узнает о успехе или отказе в регистрации по значению типа boolean, возвращаемому методом getLoginSuc- cess (). В нашем случае объектом, использующим компонент является экземпляр сервлета, полученный из страницы JSP (см. пример 15 Л4). Страница JSP для создания экземпляра объекта LoginBean (в переменной jaas- Веап) использует стандартное действие jsp:useBean. Затем в ней используются JSTL-теги для следующего. 1. Установки значения свойства ServletRequest компонента JavaBean (с именем req) равным текущему запросу (используется тег с: set). 2. Определения, успешно ли прошла регистрация; при этом для вызова метода getLog- • inSuccess () компонента JavaBean используется синтаксис EL. В этом рецепте сведены вместе многие Java-технологии. За описанием JSTL и связанного с этим синтаксиса EL обращайтесь к главе 23. --- Пример 15.14. Страница JSP, регистрирующая пользователей с помощью JAAS API и компо- нента JavaBean <%<? taglib uri»nhttp://java.sun.com/jstl/core" prefix»"c" %> <html> <head><title>Authenticating JSP</title></head> <body> <h2>Here is your login status...</h2> <jsp:useBean id«"jaasBean" class«"com.jspservletcookbook.LoginBean" /> <%— Значение свойства 'req* компонента JavaBean устанавливается с помощью свойства 'request* неявно создаваемого объекта pageContext языка EL —%> Использование JAAS в JSP | 379
<с:set target""${jaasBean}" value""$(pageContext.request}" property""req"/> <c:choose» <c:when test""${jaasBean.loginsuccess}"> Logged in successfully. </с:when» <c:otherwise» Login failed. </c:otherwise» </c:choose» f </body> </html> Компонент LoginBean имеет метод getLoginSuccess (), возвращающий false, если регистрация не пройдена, и true, если пройдена. С помощью EL вы можете вызывать любые из методов доступа к свойствам компонента JavaBean, исполь- зуя следующую терминологию, имя компонента.имя свойства компонента Здесь часть имя свойства компонента представляет действительное имя свойства, а не имя метода, несмотря на то что в результате использования данного синтаксиса будет вызван метод доступа, связанный с данным свойством. Таким образом, в примере 15.14 значение, возвращаемое методом getLoginSuccess () получается с помощью следующего кода. ${j aasBean.loginsuccess} Если это выражение вернет true, JSP отобразит текст «Logged in successfully». В противном случае будет показана надпись «Login failed». ' На рис. 15.8 показано окно браузера, отображаемое при отказе в регистрации. См. также Рецепт 15.6 по созданию JAAS-класса LoginModule; рецепт 15.7 по созданию файла конфигурации JAAS; рецепт 15.8 по использованию JAAS с сервлетами; главу 23 по JSTL; Руководство по JAAS для разработчика от Sun Microsystems: http://java.sun.eom/j2se/l.4.2/ docs/guide/security/jaas/JAASLMDevGuide.htmh, список руководств и примеров программ по JAAS: http://java.sun.eom/j2se/14.2/docs/guide/security/jaas/JAASRefGuide.htmh, докумен- тацию (JavaDoc) по файлам конфигурации JAAS: http://java.sun.eom/j2se/l.4.l/docs/api/ javax/security/auth/login/Configuration.html. 380 | Глава 15. Аутентификация клиентов
hO| [-.\ Search ] ’л] 0е ЕЛ yiew fio Bookmark* Tool* window НФ Q2) Authenticating JSP Netscape Mail ^JAIM # Home Q'Rwlo F*r| Netscape Search QBooknw - — - -z--- - - — ” - - - ._____2> • _______fe. 5__________________ Here is your login status... Login failed. . Puc. 15.8. Страница JSP сигнализирует о неудаче регистрации Использование JAAS в JSP | 381
ГЛАВА 16 Привязка, доступ и удаление атрибутов в web-приложениях 16.0 Введение Атрибут - это Java-объект, который код еервлета может привязать к (сохранить в) определенной области видимости, например к контексту сервлета (ServletCon- text), сеансу или запросу. В этом объекте, способом, которому нет равноценной для разработчиков замены, может временно храниться, и совместно использоваться разными компонентами, небольшой кусочек данных. Затем, когда приложению этот объект больше не нужен, ваш код может удалить» или отвязать его, и web-контейнер сделает данный объект доступным для «сборки мусора». Эта глава описывает порядок работы с атрибутами для всех трех областей види- мости: контекста сервлета, сеанса и запроса. Если вам требуется, чтобы некоторый объект был доступен для всех сервлетов и JSP одного контекста, необходимо прйвя- зать этот объект к контексту сервлета (ServletContext). Если же приложению требуется связать некоторый объект, такой, например, как «корзина покупателя» с конкретным сеансом (см. главу 11), вы можете задать этот объект в качестве атрибута сеанса. И, наконец, если приложению требуется, чтобы два сервлета, взаи- модействующие между собой через RequestDispatcher, совместно использо- вали один объект, то эти сервлеты могут использовать атрибут объекта привязанный к области видимости - запрос. Поскольку в сильно загруженных web-приложениях одновременно существует большое количество сеансов и запросов, связанных с большим числом f пользователей, разработчикам необходимо уделять особое внимание размеру ’ объектов, привязанных в качестве атрибутов к запросам или сеансам, и используемым ими ресурсам. 382
16.1 Задание в сервлетах атрибутов контекста сервлета Задача Некоторый объект необходимо сделать доступным Для всех сервлетов, принадлежа- щих одному контексту (то есть одному web-приложению). Решение Привяжите этот объект к объекту ServletContext (контекст сервлета) с помощью метода j avax.servlet.ServletContext.setAttribute(). / » Обсуждение Атрибут объекта ServletContext доступен всем сервлетам и JSP из одного контек- ста (web-приложения). Ниже приведены шаги по связыванию объекта с ServletContext. 1. Создайте Java-класс, объект которого вы хотите привязать к контексту сервлета. 2. Поместите этот класс в каталог 'WEB-INF/classes, включая туда все необходимые каталоги, связанные с пакетом. Этот класс также можно сохранить в JAR-файле в каталоге WEB-INFAib. 3. Создайте сервлет, который привязывает объект этого класса к объекту ServletCon- text, используя метод javax. servlet. ServletContext. setAttribute (). 4. Доступ к этому объекту из других сервлетов осуществляйте с помощью метода ServletContext.getAttribute О. Вначале мы рассмотрим объект, который в данном рецепте будет привязан к Serv- letContext. А затем - сервлет, который сохраняет этот объект в качестве атрибута объ- екта ServletContext. В примере 16.1 показан простой объект, обертывающий тип java.util .Мар (отображение). Используйте это отображение для хранения характеристик каждого из запросов к web-приложению, В данном примере каждый ключ отображения - это IP-адрес клиента, Сделавшего запрос. А значение отображения - дата, когда был сделан запрос. Пример 16.1. Объект, который сервлет связывает с ServletContext package com.j spservletcookbook; import java.util.Collections; import j ava.util.HashMap; import java.util.Iterator; import j ava.ut 11.Map; import java.util.Set; Задание в сервлетах атрибутов контекста сервлета | 383
f public class Contextobject { private Map map; public Contextobject(){ map = Collections.synchronizedMap(new HashMapO); } public void put(Object key, Object value){ if (key ==xnull 11 value ==' null) throw new IllegalArgumentException( "Invalid parameters passed to Contextobject.put"); map.put(key,value); * / } public String getValues(){ StringBuffer buf = new StringBuffer(""); Set set = map.keyset(); //при использовании итератора требуется явная синхронизация synchronized(тар) { Iterator i = set.iterator(); while (i.hasNext()) buf.append((String) i.next() + "<br>"); } / /{synchronized return buf.toString(); J 1 public String toString(){ return getClass().getName() + •[ " +map+ " ]"; }//toString } Класс Contextobject имеет метод для добавления к отображению (Мар) ключей и значений (put (Object key, Object val ue)) и метод для выдачи текущих значений ключей (getValues ()). Отображение синхронизировано для обеспечения корректной многопоточной работы; оно создается в конструкторе объекта Contextobject сле- дующим образом. map = Collections.synchronizedMap(new HashMapO); 384 | Глава 16. Привязка, доступ и удаление атрибутов в web-приложениях
Когда вы генерируете отображение с помощью статического метода java.util. Collections. synchronizedMap (), To в любой момент времени только один поток может вызывать методы отображения (объекта Мар), Это особенно важно для атрибутов объекта ServletContext. к которому одновременно могут обратиться несколько сервлетов и/или несколько потоков. В примере 16.2 показан скелетон сервлета Cont ext Binder, который привязывает экземпляр класса Contextobject из примера 16.1 к ServletContext. Пример 16.2. Сервлет, привязывающий объект к ServletContext package com.j spservletcookbook; import javax.servlet.*; import javax.servlet.http.*; public class ContextBinder extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.lOException p //привязываем объект к ServletContext getServletContext().setAttribute( "com.j spservletcookbook.ContextObject", new ContextObj ect()); //отображаем некоторый HTML-текст } //end doGet } Метод getServletContext () этого сервлета возвращает экземпляр класса javax. servlet. ServletContext. После этого можно вызывать метод setAttribute () этого экземпляра, передавая ему в качестве параметров имя нового атрибута (тип String) и сам привязываемый объект. По соглашению об именовании атрибутов, имя атрибута должно совпадать с пблностью квалифицированным именем класса привязываемого объ- екта, в данном случае - com. jspservletcookbook. ContextObj ect. См. также I Рецепт 16.2 по установке в JSP атрибутов контекста сервлета (ServletContext); рецепт 16.3 по доступу к, или удалению атрибута контекста сервлета; рецепты 16.5-16.8 по обработке атрибутов сеанса в сервлетах и JSP; рецепты 16.9-16.12 по обработке атрибу- - тов запроса в сервлетах и JSP; рецепт 14.5 по использованию слушателя событий контекста сервлета (ServletContext); документацию по javax.servlet.Servlet£ontex- tAttributeListener: http://java.sun.com/j2ee/L4/docs/api/javax/servlet/ServletCoiitexrAt- tributeListener. html. 1з^545 Задание в сервлетах атрибутов контекста сервлета | 385
16.2 Установка атрибутов контекста сервлета (объекта ServletContext) с помощью JSP Задача Требуется из страницы JSP сохранить атрибут объекта ServletContext. Решение Для привязки объекта к области видимости приложение воспользуйтесь JSTL-тегом с: set. Для представления контекста сервлета (ServletContext) в JSTL использу- ется неявно создаваемый объект application, который также является областью видимости для атрибутов объекта, обсуждаемых в предшествующем рецепте. Обсуждение i Для реализации в JSP той же функциональности, что и в рецепте 16.1, разработчики могут использовать теги ядра JSTL и стандартное действие j sp: useBean. В Представ- ленной ниже странице JSP, как и в программе предыдущего рецепта, в контексте сервлета сохраняется атрибут объекта, содержащий тип java.util .Мар (отображение). В этом отображении хранятся пары ключ/значение, к которым должны обращаться другие сервлеты или JSP из этого же контекста. Ниже приведены шаги, необходимые для привязки атрибута к объекту Servlet- Context из страницы JSP. 1. Создайте Java-класс, экземпляр которого вы собираетесь привязать к контексту сервлета. 2. Поместите этот класс в каталог WEB-INF/classes, включая туда все каталоги, связан- ные с пакетом (если этот класс имеет имя com. jspservletcookbook.Contex- tobject, поместите класс в каталог WEB-INF/classes/conFjspservletcookbook), или, если класс хранится в виде JAR-файла, то в каталог WEB-INFAib. 3. Создайте страницу JSP, которая будет привязывать атрибут объекта к ServletCon- text. Сохраните JSP в каталоге верхнего уровня web-приложения. 4. Если web-контейнер еще не обеспечивает возможности работы с компонентами, свя- занными с JSTL, включите эти компоненты в каталог WEB-INFAib (см. главу 23), чтобы страница JSP могла использовать необходимые библиотеки тегов. Сначала рассмотрим атрибут объекта, который JSP привязывает к ServletContext. В примере 16.3 приведен тот же Java-класс, что и в примере 16.1, за исключением метода getMap (), возвращающего тип мар (отображение), который объект этого класса исполь- зует для хранения информации. Я добавил данный метод, чтобы сделать это отображение доступным для тега ядра с:set (см. пример 16.4). Поскольку два этих образца кода идентичны во всем, кроме метода getMap (), пример 16.3 сокращен до показа только соз- дания синхронизируемого отображения и метода для извлечения его значения (getter), остальные части класса смотрите в примере 161). 386 | Глава 16. Привязка, доступ и удаление атрибутов в web-приложениях
Пример 16.3. Атрибут объекта, привязываемый к контексту сервлета в JSP package com.j spservletcookbook; import java.util.Collections; import j ava.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; public class Contextobject { x private Map.map; public Contextobject(){ map = Col lections. synchronizedMap (new HashMapO); ) public Map getMap(){ return map; } // см.пример 16.1, где находятся остальные части класса } В примере 16.4 производится работа по созданию экземпляра атрибута объекта и затем этот атрибут привязывается к контексту сервлета (ServletContext). Код создает Экзем- пляр класса ContextObj (который будет сохраняться в контексте сервлета) с помощью стандартного действия j sp: useBean. Затем, с помощью тега ядра JSTL с: set этот объ- ект сохраняется в области видимости application (приложение), которая является псевдонимом контекста сервлета. Класс ContextObj хранит информацию с помощью входящего в него типа Мар (отображение). Следующий код из примера 16.4 сохраняет дан- ные в атрибуте контекста сервлета. \ <c:set target= "${applicationscope[\"com.j spservletcookbook.ContextObj ect\"J.map}" value="${date}" property=“${pageContext.reques t.remoteAddr}"/> Присвоение значения атрибуту target приводит к вызову метода getMap () объ- екта ContextObj. Затем в коде создается новая, пара ключ-значение для данного отображения (Мар), состоящая из удаленного IP-адреса клиента, сделавшего запрос (ключ), и текущей даты (значение). Я выбрал эту информацию произвольно, просто для демонстрации того, как с помощью JSTL и JSP можно сохранить кусочки данных в атрибуте контекста сервлета. В своем коде вы можете сохранять данные, необходимые для вашего приложения, например уникальный идентификатор покупателя и товар, который он приобрел. Остановка атрибутов контекста сервлета (объекта ServletContext) с помощью JSP | 387 13*
Пример 16.4. Файл contextBind.jsp <%® taglib uris"http://java.sun.com/jstl/core" prefix="c" Ч> <html> <head><title>Context binding JSP</titlex/head> <body> <h2>Here is the bound ContextObject</h2> //создаем экземпляр класса Contextobject; сохраняем его в качестве атрибута // области видимости Раде (странйцс) <jsp:useBean id*"contextObj" class* "com.jspservletcookbook.Contextobject",/> // создаем экземпляр класса Date; сохраняем его в качестве атрибута // области видимости Раде (страница) <jsp:useBean id®"date" class*"java.util.Date" /> //привязываем объект к контексту сервлета, представленному //неявно создаваемым объектом 'application1 <c:set var* I "com.jspservletcookbook.Contextobject" value="${contextObj}" scopes "application" /> //создаем новую пару ключ/значение в отображении привязанного объекта <c:set target" "$(applicationscope[\"com.j spservletcookbook.ContextObject\"].map}" values"${date}" property*"${pageContext.request.remoteAddr}"/> </body> </html> Посмотрев на этот код, вы можете удивиться, почему переменная Contextobject именуется дважды, один раз в jsp:useBean, при создании объекта (с присвоением объекту идентификатора или имени contextObj), и второй раз в с: set, когда объект привязывается к контексту сервлета (и создается имя объекта - com. jspservlet- cookbook . ContextObj ec t). По соглашению, вы должны называть атрибут по полностью квалифицированному имени его класса. Однако вы не можете использовать этот формат имени в j sp: use- Веап, поскольку это действие создает Java-переменную в сервлете, реализующем JSP. Эта переменная имеет имя contextObj. Для реализации любой страницы JSP, JSP-контейнер за сценой создает соответствующий сервлет. Вы не можете использовать точку (.) при именовании Java-переменных, поэтому код, при привязывании объекта к контексту сервлета, переименовывает данный объект в атрибуте var тега с: set. »’ 388 | Глава 16. Привязка, доступ и удаление атрибутов в web-приложениях
См. также Главу 23 по использованию JSTL; рецепт 16.1 по заданию в сервлете атрибута контек- ста Сервлета; рецепт 16.4 по доступу к и удалению атрибута контекста сервлета в JSP; рецепты 16.5-16.8 по обработке атрибутов сеанса в сервлетах и JSP; рецепты 16.9-16.12 по обработке атрибутов запроса в сервлетах и JSP; рецепт 14.5 по использованию слуша- теля событий контекста сервлета (ServletContext); документацию по javax.serv- let . ServletContextAttributeListener: http://java.sun.coni/j2ee/L4/docs/api/javax/ servlet/ServletContextAttributeUstener.html. 16.3 Доступ и удаление в сервлете атрибутов контекста сервлета Задача Требуется в коде сервлета получить доступ к атрибуту контекста сервлета для работы с ним или для его полного удаления. Решение Для доступа к атрибуту воспользуйтесь методом ServletContext. get At- tribute (String attributeName). Для удаления атрибута контекста сервлета исполь- зуйте метод ServletContext. removeAttribute (String attributeName). Обсуждение Код примера 16.5 получает атрибут контекста сервлета и сохраняет его в локальной переменной. Затем код добавляет к атрибуту- (в котором для хранения ключей и значений используется тип java.util .Мар (отображение)) новую пару ключ/значение. После этого сервлет печатает список ключей атрибута, которые являются IP-адресами ини- циаторов запросов к сервлету. Пример 16.5. Доступ из сервлета к атрибургу контекста сервлета package com.jspservletcookbook; import javax.servlet.*; import j avax.servlet.http.*; public class ContextAccessor extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.lOException { Доступ и удаление в сервлете атрибутов контекста сервлета | 389
//получаем атрибут контекста сервлета ContextObject ContextObj = (Contextobject) getServletContext().getAttribute( "com.jspservletcookbook.ContextObject"); if (ContextObj 1= null) contextObj.put(request.getRemoteAddr(),""+ new java.util.Date()); //отображаем значения атрибута контекста response.setContentType("text/html"); java.10.Printwriter out = response.getWriter(); out.printlnf f "<htmlxheadxtitle>Context Attribute</title></headxbody>"); if (contextObj != null){ out.printin("<h2>ServletContext Attribute Values</h2>"); out.printIn(contextObj.getValues()); } else { out.printlnf"<h2>ServletContext Attribute is Null</h2>"*) ; ) out .printlnf "</bodyx/html>") ; } //end doGet } В примере 16.1 рецепта 16.1 приведен исходный код класса Contextobject. Из него видно, что метод put () класса ContextObj ect просто передает ключ и значение, приня- тые им в качестве Параметров, одноименному методу класса Мар, единственное, метод put () класса ContextObj ect не допускает, чтобы ключ или значение были равны nul 1. Если вы хотите удалить атрибут, который был привязан в этом рецепте, вызовите метод ServletContext. removeAttribute (), передав ему в качестве параметра имя атрибута. getServletContext () . гetnoveAttribute (' "com.j spservletcookbook.ContextObj ect"); После того как код удаления атрибута выполнится, все последующие вызовы метода ServletContext.getAttribute() с этим же именем атрибута будут возвращать null. См. также Рецепты 16.1-16.2 по установке атрибутов контекста сервлета (ServletContext) в сервлетах и JSP; рецепты 16.5-16.8 по обработке атрибутов сеанса в сервлетах и JSP, рецепты 16.9-16.12 по обработке атрибутов запроса в сервлетах и JSP; рецепт 14.5 по использованию слушателя событий контекста сервлета (ServletContext); докумен- тацию по зavax. servlet. ServletContextAttгibuteListener: http://java.sun. com/j2ee/l. 4/docs/api/javax/servlet/ServletContextAttribute Listener. html. 390 | Глава16. Привязка, доступ и удаление атрибутов в web-приложениях
16.4 Доступ из jSP к атрибуту контекста сервлета или его удаление Задача J Требуется в коде JSP получить доступ к атрибуту контекста сервлета для работы с ним, или для его полного удаления. Решение Для отображения значения атрибута используйте тег ядра JSTL с: out, а для удале- ния атрибута из контекста сервлета - тег с: remove. Обсуждение К этому моменту вы, вероятно, уже знакомы с атрибутом объекта, который в предше- ствующих рецептах сохранялся в контексте сервлета под именем com. j spservlet- cookbook. Con textobject. Если нет, в рецептах 16.1 и 16.2 показан исходной код этого класса и то, как в сервлете и JSP привязать объект этого класса в качестве атрибута. В данном рецепте показаны теги JSTL, которые можно использовать в коде JSP для доступа к атрибуту и его удаления (отвязывания). Пример 16.6 включает директиву taglib, необходимую для использования на странице JSP тегов JSTL. Затем с помощью атрибута value тега с:out мы получаем доступ к атрибуту контекста сервлета. Этот тег получает значение атрибута контекста с помощью неявно создаваемого объекта applicationscope, который имеет тип java. util. Map. Пример 16.6. Доступ из JSP к атрибуту приложения <%@ taglib uri=“http://java.sun.com/jstl/core" prefix="c" %> //Здесь находится HTML или другой код внешнего представления... <c:o\it value= "${applicationscope[\“com.j spservletcookbook.ContextObj ect\"].values}" escapeXml =;"false" /> Неявно создаваемый объект - это объект, который JSTL автоматически делает доступным разработчику В обрамленйи символов ${...} используется терм f b-v applicationscope, этот терм приводит к типу java.util .Мар любой из атрибутов объекта, привязанных к контексту сервлета. Доступ из JSP к атрибуту контекста сервлета или его удаление | 391
В коде ${applicationscope[\"com,jspservletcookbook.ContextObject\"].values} используется синтаксис EL для доступа к атрибуту контекста сервлета с именем сот. jspservletcookbook.Contextobject и получения его свойства values, то есть фактически для вызова метода getValues () объекта ContextObj ect. Этот метод отображает все ключи отображения (Мар), содержащегося в объекте Contextobject, разделяя их - HTML-тегом конца строки (<br>). Атрибут escapeXml="false" предотвращает исчезновение из тега <br> символов < и > (их замену на символы &lt; и &gt; соответственно), для корректного отображения текста в браузере. Если потребуется сделать класс ContextObj ect более универсальным, можно создать свойство компонента JavaBean, позволяющее пользователю класса задавать разделитель строк, чтобы вывод метода getValues () можно было использовать в разных контекстах, а не только в HTML. Отображение в браузере результатов доступа к атрибуту из JSP, полученных при использовании этого кода, приведено на рис. 16.1. Рис. 16.1. Доступ к атрибуту, привязанному к контексту сервлета Для удаления атрибута из контекста сервлета (ServletContext) используйте JSTL-тег с: remove Этот тег удаляет заданную по имени переменную из указанной области видимости. <с:remove var= / "com.jspservletcookbook.ContextObject" scope="application" /> 392 | Глава 16. Привязка, доступ и удаление атрибутов в web-приложениях
Здесь application - псевдоним контекста сервлета. После выполнения JSP, содержащей такой тег, любые последующие попытки доступа к атрибуту контекста сервлета с этим именем будут приводить к возврату null. См. также Главу 23 по использованию JSTL; рецепты 16.1 и 16.2 по заданию в сервлетах и JSP атрибута контекста сервлета; рецепт 16.3 по доступу из сервлета и удалению атрибута контекста сервлета; рецепты 16.5-16.8 по обработке атрибутов сеанса в сервлетах и JSP рецепты 16.9-16.12 по обработке атрибутов запроса в сервлетах и JSP; рецепт 14.5 по использованию слушателя событий контекста сервлета (ServletContext); докумен- тацию по javax. servlet. ServletContextAttributeListener: http://java.sun. con7j2ee/1.4/docs/api/javax/servlet/ServletContextAttributeListener.html. 16.5 Установка из сервлета атрибутов сеанса Задача Требуется сохранить атрибут объекта в сеансе. Решение Используйте метод setAttribute () классу javax. servlet. http. HttpSession. Обсуждение Механизм помещения атрибутов объектов в сеансы очень похож на сохранение объ- . ектов в контексте сервлета, описанный в рецепте 16.1. Различие заключается лишь в области видимости этих объектов; иными словами, в том, какие пользователи, и сколько пользователей одновременно могут иметь доступ к привязанным объектам. Сеанс (session) представляет процесс взаимодействия некоторого пользователя с web- сайтом. Последовательность web-страцйц или компонентов, которые этот пользователь запросил с web-сайта, представляют один сеанс (подробности в главе 11). Таким образом, когда вы сохраняете объект в атрибуте сеанса, каждый из пользователей, с которыми установлен сеанс, будет взаимодействовать со своим собственным экзем- пляром этого атрибута. Что касается, атрибута контекста сервлета, то все пользователи одного приложения взаимодействуют с одним и тем же экземпляром атрибута, поскольку каждое web-приложение имеет только один контекст сервлета (Servlet- Context), и каждый контекст связан с одним экземпляром атрибута. Установка из сервлета атрибутов сеанса | 393
•»<Г Распределенные web-приложения имеют по одному контексту сервлета на каждую виртуальную машину Java (JVM). И для хранения информации, глобальной для всего приложения, вместо контекста сервлета, документация по ServletContext советует использовать базу данных, что гарантирует сервлетам распределенного приложения доступ к одним и тем же. данным. Читайте документацию на http://java. sun.eom/j2ee/l.4/docs/api/javax/servlet/ServletContexi.html Типичным примером объекта, сохраняемого в атрибуте сеанса, является покупатель- ская корзина, хранящая выбранные клиентом покупки. Фрагмент кода сервлета, демонстрирующего сохранение объекта в сеансе, приведен в примере 16.7. Пример 16.7. Сохранение атрибута объекта в сеансе <!—этот код размещается в методах doGet или'doPost сервлета., по ситуации —> //если сеанс не создан, создаем его HttpSession session = request.getSession(); ' //привязываем атрибут к сеансу if (session != null) session.setAttribute( " com. j spservletcookbook.ContextObj ect", new ContextObj ect()); Доступ к сеансу получаем, используя метод getSession () объекта j avax. servlet. http.HttpServletRequest. Затем, -вызывая HttpSession.setAttributeO, (передаем имя атрибута и экземпляр атрибута объекта. В коде примера 16.7 используется тот же класс Contextobject, который был приведен в примере 16.1 (рецепт 16.1). В классе Contextobject, для обеспечения одновременной работы с атрибутом нескольких пото>* ков, используется синхронизируемый тип java. util. Map. Необходимо внимательно отнестись к обеспечению возможности одновременного , доступа к атрибутам 'сеанса из нескольких потоков. Согласно спецификации Л* сервлетов версии 2.4 (глава SRV.7.7.1), «Несколько сервлетов, обрабатывающих * потоки запросов, могут в одно и то же время активно обращаться к одному и тому же объекту session (сеанс). Разработчик отвечает за необходимую синхронизацию доступа к ресурсам сеанса». См. также Рецепты 16.1-16.4 по. обработке атрибутов контекста сервлета (ServletContext) в сервлетах и JSP; рецепт 16.7 по доступу и удалению атрибутов сеанса в сервлете; рецепты 16.6 и 16.8 пр обработке атрибутов сеанса в JSP; рецепты 16.9-16.12 по обработке атрибутов запроса в сервлетах и JSP; рецепт 14.6 по использованию слушателя событий сеанса; документацию по javax. servlet .HttpSessionAttributeListener: http:/ /java.sun.coiii/j2ee/1.4/docs/api/javax/servlet/http/HttpSessionAttributeListener.html. 394 | Глава 16. Привязка, доступ и удаление атрибутов в web-приложениях
16.6 Установка атрибутов сеанса из JSP Задача Требуется на странице JSP привязать объект к сеансу. Решение Используйте теги j sp: useBean и с: set для создания объекта и назначения его в качестве атрибута сеанса. Обсуждение » Для работы с атрибутами сеанса в JSP используются теги ядра JSTL и стандартное действие j/sp: useBean. В примере 16.8 к сеансу привязывается атрибут объекта, отображается значение из этого объекта, а затем демонстрируется идентификатор сеанса клиента, обратившегося к данной странице. Здесь в качестве атрибута привязывается объект все того же класса Contextobject, который используется на протяжении всей главы. Он содержит тип java.util .Мар для хранения IP-адресов пользователей,' обращающихся к данной странице JSP (см. пример 16.1 и описание этого кода). Пример 16.8. Установка атрибута сеанса из JSP taglib uri="http://java.sun.com/jstl/core" prefix="c" %> <html> <head><title>Context binding JSP«7title></head> <body> <h2>Here are the values from the bound ContextObj ect</h2i> <%— Создаем экземпляры классов Contextobject и Date —%> <jsp:useBean id»"contextObj" class» "com.j spservletcookbook.ContextObj ect" /> <jsp:useBean id»"date" class»"java.util.Date" /> привязываем атрибут к области видимости сеанс —%> <c:set var= ? "com.j spservletcookbook.ContextObj ect" value*" $ {contextObj}" scope» "session" /> <%— помещаем в объект значение, затем отображаем это значение —%> <с:set target» "${sessionscope[\"com.j spservletcookbook.ContextObject\"].map}" value» 11 $ (date } " property» " $ (pageContext. request. remoteAddr} " / > <c:out value»"${sessionScope[\"com.jspservletcookbook.ContextObject\"]. values}" escapeXml»"false” /> Установка атрибутов сеанса из JSP | 395
<h2>Here 'is the session ID</h2> <c:out values"${pageContext.session.id}” /> </body> </html> Следующий код из примера 16.8 привязывает объект к сеансу: <с:set var= "coin.j spservletcookbook.ContextObj ect* value="${ContextObj}" scope= "session" /> Отличие примера 16.8 от JSP из рецепта 16.2, где объект привязывался к контексту сервлета, заключается только в значении атрибута scope тега с: set (в данном случае это session). Аналогичным образом тег с: set устанавливает значение в атрибуте сеанса, ссылаясь на неявно создаваемую переменную sessionScope. <c:set targets "${sessionScope[\"com.jspservletcookbook.ContextObject\"].map}" value= "${date)" property="${pageContext.request.remoteAddrJ"/> Механизм EL автоматически делает доступной неявно создаваемую переменную sessionScope, представляющую rari-java. util. Мар, где хранятся любые перемен- ные объекта области видимости - сеанс. Если имя вашего атрибута не содержит в своем, составе символы точка, вы можете задать имя атрибута без дополнительного уточнения контекста, и EL произведет поиск атрибута с этим именем в областях видимости page, request, session, и application. К примеру, следующий синтаксис EL возвращает атрибут сеансу с именем contextObj (йлИ null, если атрибута сеанса с таким именем не существует) без использования неявно создаваемой переменной для допол- нительной квалификации имени ${contextObj} См. также Главу 23 по использованию JSTL; рецепты 16.1-16.4 по обработке атрибутов контекста сервлета (ServletContext) в сервлетах и JSP; рецепт 16.7 по доступу и удалению атрибутов сеанса в сервлете; рецепт 16.8 по доступу к и удалению, атрибутов сеанса в JSP; ‘рецепты 1.6.9-16.12 по обработке атрибутов запроса в сервлетах и JSP; рецепт 14.6 по использованию слушателя событий сеанса; документацию по javax. servlet. Ht tpSess i onAt t r ibu teLi s t ener: http://java.sun.com/j2ee/L4/docs/api/javax/servlet/ http/HttpSessionAttributeListener.html. ' 396 | Глава 16,- Привязка, доступ и удаление атрибутов в web-приложениях .'л- • .1
16.7 Доступ из сервлета к атрибутам сеанса и их удаление Задача Требуется из сервлета получить доступ к атрибуту сеанса или удалить атрибут Решение Для доступа к атрибуту используйте метод javax. servlet .http.HttpSession. getAttribute (String a t tribu teName). Для удаления атрибута сеанса используйте метод removeAttribute (String at tributeName). Обсуждение 7 Для дос гуна к атрибуту сеанса вы сначала должны привязать атрибут к сеансу, как показано в рецепте 16.5. После этого атрибут объекта становится доступным для пользова- теля, Связанного с данным сеансом. Сервлет из примера 16.9 работает с атрибутом, нося- щим имя com. jspservletcookbook.ContextObject. В примере показан только код, связанный с доступом из сеанса к атрибуту. Весь сервлет полностью, включая метод doGet () сводом доступа к атрибуту, приведен в примере 16.5 рецепта 16.3. < — ’* Метод HttpSession.getAttribute() возвращает тип Object, таким л образом, возвращаемое значение необходимо привести к требуемому типу перед ч' 1передачей его в качестве параметра другому Методу. Пример 16.9. Получение из сервлета доступа к атрибуту сеанса package com.jspservletcookbook; <J—этот код помещается в методы doGet или doPost сервлета, в соответствии с ситуацией. Класс CpntextObject хранится в каталоге WEB-INF/classes/com/ jspservletcookbook/ --> //создаем сеанс, если онещр не создан HttpSession session = request.getSession.(); //данная локальная переменная будет содержать атрибут ContextObject contextObj “ null; //получаем доступ к атрибуту сеанса if (session != null) contextObj = (ContextObject) session.getAttribute! "Com. j spservletcookbook.ContextObject") ; Доступ из сервлета к атрибутам сеанса и их удаление | 397
//перед вызовом каких-либо методов убедимся, что contextObj не null if (contextObj != null) out.printIn( contextObj.getValues() );> <!— остальная часть класса сервлета и метода doGet или doPost —> . Перед тем как бы начнете работать с атрибутом сеанса, необходимо сделать следующие шаги. 1. Откомпилируйте класс, объект которого будет сохраняться в сеансе. 2. Поместите этот класс в каталоге WEB-INF/classes (или в WEB-INF/lib, если он хранится в виде JAR-файла). 3. Убедитесь, что сервлет, JSP, или прочие web-компоненты устанавливают этот атрибут в сеансе с помощью метода HttpSession. setAttribute (). Удаление атрибута сеанса при помощи сервлета Для удаления атрибута вызовите метод HttpSession.removeAttribute(), передав в качестве параметра имя атрибута. Для удаления атрибута, с которым мы имеем дело в данной главе, необходим следующий код. HttpSession session = request.getSession(); <!— Метод HttpSession.removeAttribute не делает ничего, если атрибута с таким именем не существует —> if (session != null) session.removeAttribute("com.j spservletcookbook.ContextObj ect"); С этого момента атрибут недоступен в сеансе, связанном с пользователем, вызвав- шим этот сервлет. Но атрибут сеанса все еще доступен в других сеансах, где он может храниться (хотя это будут другие экземпляры). С каждым пользователем связан свой сеанс, и каждый сеанс может нести свой собственный экземпляр атрибута. С другой стороны, когда вы удаляете атрибут из контекста сервлета, он становится недоступен для всех пользователей, поскольку в каждом нераспределенном приложении существует только один , экземпляр, контекста сервлета (ServletContext). См. также Ренеты 16.1-16.4 по обработке атрибутов контекста сервлета (ServletContext) в сервлетах и JSP; рецепт 16.5 по установке из сервлета атрибута сеанса; рецепт 16.6 по установке из JSP атрибута сеанса; рецепт 16.8 по доступу к и . удалению атрибутов сеанса в JSP; рецепты 16.9-16.12 по обработке атрибутов запроса в сервлетах и JSP; рецепт 14.6 по использованию слушателя событий сеанса; документацию по javax.servlet. HttpSessionAttributeListener: http://java.sun.eom/j2ee/l.4/docs/api/javax/servlet/ http/HttpSessionAttributeListener.html. 398 | Глава 16. Привязка, доступ и удаление атрибутов в web-приложениях
16.8 Доступ и удаление атрибута сеанса с помощью JSP Задача Требуется в JSP поработать с атрибутом сеанса и затем удалить его. Решение Для отображения значения атрибута используется тег ядра JSTL с: out, а для удале- ния атрибута из сеанса - тег с: remove. Обсуждение Вот что необходимо сделать для доступа или удаления переменной с областью види- - мости сеанс с помощью JSTL и JSP. 1. Убедитесь, что ваше web-приложение может использовать JSTL (то есть в каталоге WEB-INFAib имеются необходимые JAR-файлщ, такие, как jstLjar и standardjar, за инструкциями обращайтесь к главе 23). 2. Включите в текст страницы директиву taglib, которая делает JSTL доступным для использования в JSP (см. код, приведенный ниже). 3. Убедитесь, что атрибут объекта ранее был привязан к сеансу этой же страницей или другим web-компонентом (например, сервлетом). Код, приводимый в этом рецепте, показывает, в чем отличие привязки атрибута к сеансу, от его привязки к контексту сервлета (показанной в рецепте 16.4). В этом коде используется неявно создаваемый EL-объект sessionScope, который является авто- матически доступной переменной в EL-формате, содержащей все атрибуты с областью видимости сеанс. Этот код - фрагмент JSP, который отображает значения, содержащиеся в атрибуте с именем com. j spservletcookbook. ContextObj ect. *?. В примере 16.4 рецепта 16.2 страница JSP, обращающаяся к атрибутам приведена полностью. Однако там осуществляется доступ к атрибуту контекста сервлета, г а не к атрибуту с областью видимости сеанс. taglib uri="http://java.sun.com/jstl/core" prefix="c" %> //HTML or other presentation code here... <c:out value= "${sessionScope[\"com.j spservletcookbook.ContextObj ect\"].values}" esqapeXml=11 false " / > Доступ и удаление атрибута сеанса с помощью JSP | 399
Часть escapeXml="false" теп c:out говорит тегу о необходимости оставить символы, являющиеся частью вывода этого тега, такие, как < и > без изменений (то есть не заменять их на символьные сущности &lt; и &gt;). Следующий код JSP, с помощью тега ядра с: remove удаляет переменную с обла- стью видимости сеанс. <с:remove var= "com.j spservletcookbook.ContextObject" scope="session” I> С этого момента этот атрибут объекта более не доступен в данном сеансе, связанном с пользователем, обратившемся к этой странице JSP. Иными словами, тег с: remove уда- ляет не все атрибуты сеансов, имеющие заданное/имя, а только те из них, что связаны с сеансами пользователей, обратившихся к странице JSP, содержащей тег с: remove. См. также Главу 23 по использованию JSTL; рецепты 16.1-16.4 по обработке атрибутов контекста сервлета (ServletContext) в сервлетах и JSP; рецепт 16.5 по устайовке из сервлета атрибута сеанса; рецепт 16.7 по доступу и удалению атрибутов сеанса в сервлете; рецепты 16.9-16.12 по обработке атрибутов запроса в сервлетах и JSP; рецепт 14.6 по использованию слушателя событий сеанса; документацию по javax. servlet. HttpSessionAttributeListener: http://java.sun.com/j2ee/L4/docs/api/javax/servlet/ http/HttpSessioiiAttributeListener.html. 16.9 Установка атрибутов запроса с помощью сервлета Задача Требуется с помощью сервлета сохранить атрибут в запросе. Решение Используйте метод javax. servlet. ServletRequest. setAttribute (). Обсуждение Метод javax. servlet. ServletRequest. setAttribute () часто использу- ется в коде, который динамически перенаправляет запросы или подключает содержимое с помощью javax. servlet .RequestDispatcher. 400 | Глава 16. Привязка, доступ и удаление атрибутов в web-приложениях
Web-приложения, использующие RequestDispatcher для совместной работы нескольких web-компонентов с одним запросом, могут осуществлять взаимодействие между этими компонентами с помощью атрибутов запроса. И адресат метода Request- Di spatcher. forward (), и файл или страница, включаемые с помощью метода RequestDispatcher. include (), имеют доступ к исходному запросу. Таким образом, эти web-компоненты также могут получить доступ к любому из атрибутов, сохраненных в этом запросе. Сервлет из примера 16.10 создает экземпляр класса Contextobject, сохраняет в этом объекте некоторую информацию, вызывая его метод put (), и затем помещает этот объект в HttpServletRequest (запрос} под именем «com.jspservletcookbook.ContextObject». Затем сервлет использует RequestDispatcher для перенаправления запроса (вместе с атрибутом) компоненту, отображенному на путь к сервлету /displayAttr. Web-компонент, отображенный на этот путь, теперь имеет доступ к созданному до него атрибуту запроса. Пример 16.10. Привязка объекта к запросу package com.j spservletcookbook; import javax.servlet.*; import j avax.servlet.http.*; public class RequestBinder extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.lOException { //привязываем объект к запросу Contextobject contextObj new Contextobject(); contextObj.put( request.getRemoteAddr(), ""+new java.util.Datff())l request.setAttribute( "com.jspservletcookbook.Contextobject",contextObj ); //используя RequestDispatcher перенаправляем запрос другому сервлету // отображенному на путь '/displayAttr* RequestDispatcher dispatcher request.getRequestDispatcberf "/displayAttr"); dispatcher.forward(request,response); } //doGet } / Сервлет, который получает переадресованный запрос, приведен в примере 16.11. Этот сервлет (Request Di splay) отображен с помощью web.xml на путь к сервлету /displayAttr. Он (сервлет) получает атрибут запроса из объекта HttpServletRequest, вызывая метод getAttribute!) и передавая ему в качестве параметра имя атрибута: com.jspserv- letcookboojc - ContextObj ect. Поскольку значение, возвращаемое методом getAt- tribute (), имеет тип Obj ect, необходимо привести его к типу ContextObj ect. „ - Установка атрибутов запроса с помощью сервлета | 401
Пример 16.11. Адресат метода RequestDispatcher.forward имеет доступ к атрибуту запроса package com.j spservletcookbook; import javax.servlet.*; import javax.servlet.http.*; public class RequestDisplay extends HttpServlet {* public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.lOException { Contextobject obj « (Contextobject) request.getAttribute( "com. j spservletcookbook.RequestObject"); response. setCohtentType ("text/html") java.io.PrintWriter out = response.g6tWriter (.); out.printIn( "<html><head><title>Request Attribute</titlex/headxbody>"); out.printin("<h2>Request attribute values<7h2>"); //отображаем ключи отображения (java.util.Map), хранимого //ж атрибуте запроса if (obj ! null) out.println( obj.getValues() ); out .println("</bodyx/htmi>"); x } //end doGet ) Перед вызовом каких-либо методов объекта-атрибута не забудьте убедиться, что значение, возвращаемое методом ServletRequest. getAttribute (), не равно null. Метод getAttribute () возвращает null если запрос не содержит атрибута с указан- ным именем. ! См. также ' Рецепты 16.1-16.4 по обработке атрибутов контекста сервлета (ServletContext) в сервлетах и JSP; рецепты 16.5-16.8 по обработке атрибутов сеанса в сервдетах и JSP; рецепт 16.10 по установке с помощью JSP атрибутов запроса; рецепты 16.11 и 16.12 по доступу и удалению атрибутов запроса в сервлетах и JSP; документацию по j avax. servlet. ServletRequestAttributeListener: http://java.sun.com/j2ee/L4/docs/ api/javax/servlet/ServletRequestAttributeListener.html. 402 11 Глава 16. Привязка, доступ и удаление атрибутов в web-приложениях
16.10 Установка атрибутов запроса с помощью JSP Задача Требуется с помощью JSP установить атрибут запроса. Решение Используйте теги ядра JSTL и стандартное действие jsp:useBean для создания объекта и привязки его к запросу. ч Обсуадение Страница JSP, приведенная в примере 16.12 сохраняет объект com. jspservletcook- book. Contextobject в'области видимости запрос, предварительно создав этот объект с помощью j sp: useBean. Как и в рецептах 16.2 и 16.6 для привязки объекта используется тег с: set, но на этот раз в атрибуте scope этого тега зацаегся значение request. *» Классы объектов, которые JSP использует в качестве атрибутов запроса, j необходимо поместить в каталог WEB-INF/classes, или, если класс - часть JAR- * f файла, то - в каталог WEB-INFAib. Страница JSP, приведенная в примере 16.12 в точности похожа на код JSP из рецеп- тов 16.2 и 16.6, только на этот раз для получения доступа к атрибуту и присвоения ему нового значения (value) и свойства (property) используется неявно создаваемый объект requestscope. Объект requestscope используется по правилам синтаксиса EL для доступа к атрибутам запроса (см. главу 23). Пример 16.12. Установка из страницы JSP атрибута запроса и переназначение запроса taglib uri="http://java.sun.com/jstl/core" prefix="c" %> <jsp:useBean id="contextObj" class"' "com. j spservletcookbook. ContextObj ect" / >. <jsp:useBean id="date" class""java.util.Date" /> <c:set var="com.jspservletcookbook.ContextObject" value" "$ {cont ext Ob j'}" scope""request" /> <c:set target" "${requestScope[\"com.jspservletcookbook.ContextObject\"].map}" value" "${date}" property""$ {pageContext.request.remoteAddr}" / > <jsp:forward page""/displayAttr" /> Установка атрибутов запроса с помощью JSP | 403
После создания атрибута запроса и присвоения ему некоторых значений ZJSP переадресует запрос к компоненту, отображенному на путь к сервлету /displayAttr. JSP или сервлет, отображенный на этот путь, имеет доступ к данному, вновь созданному атрибуту. См. также Главу 23 по использованию JSTL; рецепты 16.1-16.4 по обработке атрибутов контекста сервлета (ServletContext). в сервлетах и JSP; рецепты 16.5-16.8 по обработке атрибу- тов сеанса в сервлетах и JSP; рецепт 16.10 по установке с помощью JSP атрибутов запроса; рецепты 16.11 и 16.12 по'доступу й удалению атрибутов запроса в сервлетах и JSP; доку- ментацию по javax.servlet.ServletRequestAttributeListener: http://java. suii.coiii/j2ee/1.4/docs/api/javax/servlet/ServletReque$tAttribuieListener.htmi r' 16.11 Использование сервлета для доступа к атрибутам запроса и их удаления Задача Требуется из сервлета получить доступ к атрибуту запроса или удалить атрибут запроса. Решение Используйте методы javax.servlet.ServletRequest.getAttributeO и javax. servlet. ServletRequest. гemoveAttribute (), передавая им в качестве параметра имя атрибута. Обсуждение Пример 16.13 произведен из метода doGet () из примера 16.11 рецепта 16.9 (если необходимо освежить, в памяти полный код сервлета, обрабатывающего атрибуты запроса, обращайтесь к этому классу). Код из примера 16.13 получает атрибут объекта от объекта HttpServletRequest, передаваемого методу doGet () в качестве первого параметра. **’’ Контейнер сервлетов создает объект HttpServletRequest и передает его . в первого параметра всем сервисным методам класса HttpServlet, в том числе \ j $ методам doGet () и doPost (). В примере 16.13 вызывается один из методов атрибута запроса, а затем этот атрибут удаляется. i 404 | Глава 16. (Тривязка, доступ и удаление атрибутов в web-приложениях
Пример 16.13, Сервлет, работающий с атрибутом запроса и затем удаляющий его public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.lOException { ContextObject obj = (Contextobject) request.getAttribute( "com.j spservletcookbook.ContextObj ect" ) ; response.setdontentType("text/html"); java.io.Printwriter ou.t = response.getWriter(); out.printin( ”^html><head><title>Request Attribute</titlex/headxbody>") ; //отображаем ключи отображения -(Map) out.printIn("<h2>Request attribute values</h2>"); if (obj I» Hull) out.printin( obj.getValues() ); //Вызов этого метода не обязателен, поскольку атрибуты запроса //существуют ровно столько, сколько длится обработка запроса, //это отмечено в документации по ServletRequest API. request.remove Attribute("con.jspservletcookbook.ContextObject"); out. printin (" < /bodyx /html>"); } //doGet Если у запроса нет атрибута с указанным именем (поскольку атрибут не был предварительно привязан к1 запросу), метод ServletRequest.getAttribute() возвратит null. Убедитесь, что код сервлета проверяет возвращаемое значение на null перед обращением к методам этогр объекта. Кроме того, значение, возвращаемое методом ServletRequest. getAttribute (), имеет тип Object, так что не забудьте привести его к требуемому типу, прежде чем начнете вызывать методы, присущие этому типу. См. также Рецепты 16.1-16.4 по обработке атрибутов контекста сервлета (ServletContext) в сервлетах и JSP; рецепты 16.5-16.8 по< обработке атрибутов сеанса в сервлетах и JSP; рецепт 16.12 по доступу с помощью JS>P к атрибутам запроса и их удалению; главу 6 по включению содержимого с помощью сервлетов и JSP; документацию по javax. servlet. ServletRequestAttributeListener: http://java.sun.com/j2ee/L4/docs/api/ javax/servlet/ServletRequestAttributeListener.html. Использование сервлета для доступа к атрибутам запроса и их удаления | 405
16.12 Использование JSP для доступа к атрибутам запроса и их удаления Задача Требуется из JSP получить доступ к атрибуту запроса или удалить атрибут запроса. Решение Для доступа и удаления атрибута запроса используйте теги ядра JSTL с: out и с: remove. Обсуждение Страница JSP, приведенная в примере 16.14 обращается к атрибуту объекта, привя- занному к HttpServletRequest (запросу). Доступ к атрибуту осуществляется путем использования внутри тега с: out синтаксиса EL. Страница JSP из примера 16.12 (рецепт 16.10) переадресует атрибут запроса сервлету, используя стандартное действие j sp: forward. JSP из данного примера может перенаправить свой атрибут запроса к JSP из примера 16.14, используя следующий код. <jsp:forward page®"/requestDisplay.jsp" /> Выходе "${requestScope[\"com.jspservletcookbook.ContextObject\"]. values}" используется неявно создаваемый JSTL-объект requestscope. Эта переменная, которую JSTL автоматически делает доступной EL-коду, представляет собой тип j ava. util .Мар, содержащий все атрибуты, привязанные к области видимости запрос. Затем код отображает значения, содержащиеся в атрибуте, обращаясь к свойству values этого атрибута объекта (см. рецепт 16.1, где приводится описание объекта, используе- мого для сохранения в качестве атрибута во всех рецептах данной главы). Пример 16.14. Доступ к атрибуту запроса и его удаление с помощью JSTL <%@ taglib uri»"http://java.sun.com/jstl/core" prefix="c" %> <html> <headxtitle>Request reading JSP</titlex/head> <body> <h2>Here are the values from the bound Request0bject</h2> I <c:out value® "${requestScope[\"com.j spservletcookbook.ContextObj ect\"]. values}" escapeXml®"false" /> <%— c:remove использовать не обязательно, поскольку атрибут запроса суще- ствует ровно столько времени, сколько обрабатывается запрос —%> 406 | Глава 16. Привязка, доступ и удаление атрибутов в web-приложениях
<br>Reraoving request attribute with c:remove ... <c:remove var= "com.jspservletcookbook.ContextObj ect" scopes"request" /> </body> </html> Тег c: remove удаляет атрибут, имя которого указано в атрибуте var этого тега из ука- занной области видимости. Здесь указана область видимости scope="request", поскольку мы удаляем атрибут из области видимости: запрос к JSP. Отображение в браузере информации, выводимой страницей displayRequest.jsp, приводится на рис. 16.2. Рис. 16.2. Отображение в браузере, формируемое страницей JSP в результате доступа к атрибуту запроса и его последующего удаления Страница JSP, чье имя набрано в поле адреса браузера - requestBind.jsp, в дей- ствительности только создает атрибут и переадресует запрос (см. рецепт 16.10). , £ При выполнении j sp: forward (переназначения запроса), в поле адреса браузера * по-прежнему остается адрес первоначальной страницы, даже если браузер на самом деле отображает вывод той страницы JSP, куда был переадресован запрос. См. также Главу 23 по использованию JSTL; рецепты 16.1-16.4 по обработке атрибутов контек- ста сервлета (ServletContext) в сервлетах и JSP; рецепты 16.5-16.8 по обработке атрибутов сеанса в сервлетах и JSP; рецепт 16.11 по доступу с помощью сервлета к атрибутам запроса и их удалению; документацию по javax. servlet. ServletRe- questAttributeListener: http://java.sun.com/j2ee/1.4/docs/api/javax/servlet/Servle- tRequestA ttributeListener. html. Использование JSP для доступа к атрибутам запроса и их удаления | 407
ГЛАВА 17 Внедрение мультимедиа в страницы JSP 17.0 Введение Большинство web-сайтов включают в * себя те или иные типы мультимедиа и интерактивные программы, это, например, цифровое видео, цифровые аудиофайлы, видео ролики Macromedia Flash, и Java-апплеты. Таким образом, web-сайты, разрабо- танные с использованием Java, часто интегрируют содержимое этого типа с сервлетами и страницами JSP (JavaServer Pages). В данной главе объясняются основы внедрения мультимедиа в web-компоненты Java. Этот процесс подразумевает включение в выход- ной HTML, генерируемый web-компонентами, тегов obj ect и embed. Страница JSP предоставляет отличные возможности для комбинирования муль- тимедиа с динамически формируемым содержимым, поскольку позволяет сделать теги, используемые Для внедрения мультимедиа, частью ее шаблонного HTML-тек- ста. Можно включить мультимедиа и в выходной HTML-текст, генерируемый сервлетом, об этом рассказывается в рецепте 17.5. Если страница, содержащая мультимедиа не нуждается во включении других типов динамического вывода, сделайте ее обычной статичной HTML- страницей и не связывайтесь с сервлетами и JSP. Статичная страница обычно требует от сервера меньших ресурсов для ответа на запрос страницы. 17.1 Внедрение апплета в JSP с использованием jsp:plugin Задача Требуется, используя стандартное действие jsp:plugin, выполнять Java- апплет с помощью подключаемого модуля Java (Java Plug-in software). Решение Разместите стандартное действие jsp:plugin в той части JSP, где должен появляться апплет. \ - 408
Обсуждение Спецификация JSP предусматривает стандартное действие jsp:plugin, выраба- тывающее теги object и embed, предназначенные для того, чтобы браузеры могли загрузить Java-апплет. Это действие приводит к запуску апплета с помощью Java Plug-in (подключаемого модуля) от Sun Microsystems или инициирует загрузку этого подключаемого модуля, если он до сих пор не был установлен. _ < <**. — ’ * Подключаемый модуль (Java Plug-in) предназначен для выполнения апплетов л в среде исполнения Java 2 Runtime Environment фирмы Sun Microsystems, вместо tV? их выполнения в среде исполнения данного браузера. Установка Java JRE или набора для разработки программного обеспечения (Software Development Kit) автоматически приводит к установке Java Plug-in. Для передачи параметров запускаемому апплету (в виде пар имя/значение) предна- значены вложенные элементы jsp:param. Эти элементы должны быть вложены в единственный элемент j sp: params. Файл JSP, использующий j sp: plugin для внедрения апплета с именем Clock.class, приведен в примере 17.1. В данном случае файл Clock.class находится в том же ката- логе, что и сама страница JSP из примера 17.1. Этот апплет заимствован из примеров апплетов от Sun Microsystems: http://java.sun. com/products/plugin/1.4.1/demos/plugin/applets/Clock/examplel .html. Пример 17.1. Внедрение Java-апплета с помощью jsp:plugin <%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %> <jsp:useBean id="date" class»"java.util.Date" /> . <html> , <head><title>A Clock in a JSP</titlex/head> <body> <h2 >The time...</h2 > <jsp:plugin type»"applet" code»"Clpck.class" codebase» "http://localhost:8080/home/applets" jreversion»"1.4.1"> <j sp:params» <jsp:param name»"scriptable" value»"false"/> </jsp:params> <jsp:fallback» Sorry, we are unable to start the Java plugin <br /» </jsp:fallback» Внедрение апплета в JSP с использованием jsp:plugin | 409
</jep:plugin> <br /><c:out value=*${date)"/> </body> </html> < Для пользователей, у которых установлен Internet Explorer для Windows, задание браузеру инструкций по загрузке апплета, осуществляется посредством HTML-тега object. В браузерах, поддерживающих подключаемый модуль в стиле Netscape, в HTML используется тег embed. Стандартное действие jsp.-plugin генерирует HTML, который должен работать в браузерах обоих типов (однако вам все равно необходимо протестировать созданную страницу JSP). , В примере 17.2 показаны HTML-теги, генерируемые действием jsp:plugin при запросе страницы JSP из примера 17.1 браузерами Internet Explorer 5.5 и Netscape. Пример 17,2. HTML-теги, генерируемые действием jsp:plugin для загрузки Java-апплета OBJECT classid= clsid:8AD9C840-044E-11D1-B3E9-00805F499D93 codebase= " http: / / j ava. suh. com/products /plugin/1.2.2 / j install -1_2_2 -win. cab# Version=l,2,2,0"> <PARAM name="java_code" value="Clock.class"> <PARAM name="java_codebase" value="http://localhost:8080/home/applets"> <PARAM name="type" value="application/x-java-applet;version=l.4.1"> <PARAM name="scriptable" value="false"> <COMMENT> <EMBED type="application/x-java-applet;version=l.4.1" pluginspage= "http://java.sun.com/products/plugin/" java_code= "Clock.class" java_codebase= "http://localhost;8080/home/applets" scriptable="false"/> <N0EMBED> Sorry, we are unable to start the Java plugin <br /> </N0EMBED> </C0MMENT> </OBJECT> На рис. 17.1 показана страница JSP, в которую встроен апплет. См. также Страницу, посвященную технологии подключаемого модуля Java: http://java.sun.com/ products/plugin/, рецепт 17.2 по внедрению апплета с помощью HTML-конвертера Sun Microsystems. 410 | Глава 17. Внедрение мультимедиа в страницы JSP
Рис. 17.1. Страница JSP, в которую встроен апплет 17.2 Внедрение апплета в страницу JSP с помощью HTML-конвертера Задача Вы хотите использовать подключаемый модуль Java - HTML-конвертер - для генерирования тегов для внедрения апплета. Решение Используйте инструмент HTML-конвертер, находящийся в архиве htmlconverter.jar, размещающемся подкаталоге lib каталога, в котором у вас установлен Java SDK. Внедрение апплета в страницу JSP с помощью HTML-конвертера | 411
Обсуждение Занятый разработчик может возложить заботу по выработке HTML-тегов, предна- значенных для загрузки Java-апплетов, на подключаемый модуль Java: HTML-конвертер. Подключаемый модуль Java (Java Plug-in) - это инструмент, созданный на базе Java, позволяющий запускать апплеты в среде исполнения Java 2 от Sun Microsystems, вместо среды исполнения Java web-браузера. Подключаемый модуль Java устанавливается на вашем компьютере, когда вы инсталлируете JRE, включая установку SDK. Инструмент HTML-кОнвертер преобразует указанный JSP-файл, содержащий HTML- тег applet, заменяя тег applet на более сложный набор тегов, позволяющий боль- шинству браузеров загрузить Java-апплет. Остальную часть кода JSP конвертер остав- ляет без изменений; он заменяет только тег applet. Вот как используется инструмент HTML-конвертер. 1. Напишите JSP-файл, включающий тег applet. В примере 17.3 показана JSP, в которую внедряется ссылка на апплет Clock.class. Эта страница JSP, довольно избыточно, динамически выводит под апплетом строку в текущим временем. Я включил этот код, чтобы показать, что конвертер не. меняет код JSP; он только изменяет шаблонный текст тега applet, включенный в JSP. Пример 17.3. Страница JSP с тегом applet <%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %> <jsp:useBean id="date" class="java.util.Date" /> <html> <headxtitle>A Clock in a JSP</titlex/head> <body> <h2>The time...</h2> <applet code="Clock.class" codebase="http://localhost:8080/home/applets"> </applet> <br 7><c:out value="${date}"/> </body> </html> 2. Откройте окно с командной строкой и сделайте текущим подкаталог lib каталога, в котором у вас установлен SDK, например H:\j2sdkl.4-.1 -OPdib. 3. Напечатайте java -jar htmlconverter, jar -gui. Эта команда запустит Swing-версию HTML-конвертера. На рис. 17.2 показано, как выглядит этот инструмент. HTML-конвертор также можно запускать в режиме командной строки. Чтобы узнать о поддерживаемых опциях, читайте руководство разработчика по подключаемому f модулю Java: http://java.sun.eom/j2se/l.4.1/docs/guide/plugin/. 412 | Глава 17. Внедрение мультимедиа в страницы JSP
4. Если вы хотите вобрать каталог для архивирования, в который HTML-конвертор сохраняет старые версии JSP-файлов (с тегом applet), для выбора этого каталога используйте окно пользовательского интерфейса конвертера. 5. Указав в верхнем текстовом поле JSP-файл, щелкните по кнопке «Convert...», и конвертер перезапишет исходный файл, поместив в него дополнительные теги object и embed. Рис. 17.2. ЦТМЬ-конвертер (версия с пользовательским интерфейсом) Код, которым конвертер заменил тег applet в JSP из примера 17.3 (выделен полужирным), а также код этой страницы, оставленный без изменений, показаны в примере 17.4. Пример 17.4. Теги object и embed, сгенерированные HTML-конвертером <%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %> <jsp:useBean id="date" class="java.util.Date" /> <html> <headxtitle>A Clock in a JSP</titlex/head> <body> <h2>The time...</h2> Внедрение апплета в страницу JSP с помощью HTML-конвертера | 413
<1 — "CONVERTEDAPPLET" — » <1— HTML CONVERTER —> <OBJECT classid = "clsid:8AD9C840-044E-11D1-B3E9-00805F49SD93" codebase “ "https /-/ j ava. sun. com/product s /plugin/autodl / jins tall-1_4 - windows - i586.cab#Version=l,4,0,0" <PARAM NAME “ CODE VALUE "Clock.class" > <PARAM NAME ж CODEBASE VALUE « "http://localhosts8080/home/applets" > <PARAM NAME « "type" VALUE = "application/x- java-applet; vers ion»» 1.4 "> <PARAM NAME « "scriptable" VALUE » "false"> «COMMENT» «EMBEDч type “ "application/x-java-applet;version=l.4" CODE "Clock.class" JAVA-CODEBASE » "https//localhoSt:8080/home/applets" scriptable ° false pluginspage = "http s / / j ava. sun. com/productв/plugin/ index. html#download" > «NOEMBED» «/NOEMBED» «/EMBED» «/COMMENT» «/OBJECT» <! — «APPLET CODE » "Clock.Class" JAVA-CODEBASE "https//localhost s 8080/home/applet s"» </APPLET» <! — "END_CONVERTED_APPLET"—» <br /><c:out value=“${date}"/> </body> </html> 414 | Глава 17. Внедрение мультимедиа в страницы JSP
Пользователи могут потерпеть неудачу при загрузке апплета в свои браузеры, если у них инсталлировано несколько версий подключаемого модуля Java. Такая ситуация возникает, если пользователь постоянно обновляет установленные версии JRE или Java SDK, что приводит к установке соответствующих версий подключаемого модуля Java. В этом случае самое простое решение - деинсталлировать старые версии подключаемого модуля Java. См. также Страницу, посвященную технологии подключаемого модуля Java: http://java.sun.com/ products/pluginf, рецепт 17.1 по внедрению Java-апплета в JSP с помощью стандартного действия j sp: plugin. 17.3 Автоматическое создание шаблона HTML для включения Flash-файлов Задача Автоматически сгенерировать HTML, необходимый для внедрения Flash-файла в web-компонент. Решение Находясь в программе Macromedia Flash 6, выполните команды меню File -»Publish», чтобы получить HTML-файл с Необходимыми тегами obj ect и embed. Обсуждение Откройте в Macromedia Flash 6 свой файл .swf, а затем с помощью команды меню «File Publish» создайте HTML-файл. Этот файл включает теги, необходимые для внедрения созданного вами ролика Flash р web-компонент. Затем скопируйте эти теги и атрибуты и вставьте их в свою страницу JSP. В примере 17.5 результат выполнения этой команды меню над файлом .swf с именем example.swf. Пример 17.5. Шаблонный текст, автоматически генерируемый приложением Flash <HTML> <HEAD> cjneta http-equiv!=Content-Type content="text/html; charset=ISO-8859-l"> <TlTbE>example</TITLE> </HEAD> <BODY bgcolor="#FFFFFF"> Автоматическое создание шаблона HTML для включения Flash-файлов | 415
< 1— URL's used in the movie—> < !— text used in the movie—> < !—DeductionsPaycheckSDIAnnual SalaryMedicareSocial securityESPP401k$#%FederalStateMarriedSingleactions—> «OBJECT clasBid-"clsid:D27CDB6E-AE6D-llcf-96B8-444553540000" codebase- "http: / /download. macromedia. ост/pub/ shockwave/cabs 1flash/ swf lash. cab#versioh-6,0,0,0"WIDTH»"550" HEIGHT="400" id-"example" ALIGN-"" «РАНАМ NAME-movie VALUE-"exa^qple.swf"> «PARAM NAME=quality VALUE-high> «PARAM ^AME-bgcolor VALUE-#FFFFFF> <EMBED src-"example.swf" quality-high bgcolor-#FFFFFF WIDTH-'550" HEIGHT "400" NAME-'example* ALIGN-"" TYPE-"application/x-shockwave-flash" PLUGZNSPAGE- "ht tp : / /www. macromedia. ccm/go/getflashplayer" > «/EMBED» «/OBJECT» «/BODY» «/HTML» Ваша JSP-страница возможно уже содержит какие-то HTML-заготовки, например тег body; так что вам остается лишь скопировать и вставить не закомментированный, выде- ленный полужирным текст, из примера 17.5. *»’ В данном примере файл example.swf находится в том же каталоге, что и данный А HTML-файл. --- В примере 17.6 из следующего рецепта показан JSP-файл с тегами object и embed, которые, как и теги из этого рецепта, связаны с файлами Hash. На рис. 17.3 показано отображение автоматически сгенерированного HTML-файла из примера 17.5. Данный ролик Hash создан на основе одного из примеров, сопровождающих приложение Hash 6: Paycheck_calculator.swf , См. также Страницу с техническими замечаниями Macromedia: http://www.macromedia.com/sup- port/flash/technotes.html; статью об альтернативах использования тега embed: http://www. macromedia.com/devnet/mx/dreamweaver/articles/flash_satay.html. 416 | Глава 17. Внедрение мультимедиа в страницы JSP
Рис. 17.3. Шаблонный HTML-текст во встроенным файлом Flash Автоматическое создание шаблона HTML для включения Flash-файлов | 417 14-1545
17.4 Собственноручное создание шаблона HTML для внедрения файла Flash Задача Вы хотите самостоятельно создать шаблонный HTML-текст для внедрения файла Flash в страницу JSP. , Решение Используйте теги object и embed, чтобы созданный HTML корректно читался как браузерами, поддерживающими один тег, так и браузерами, поддерживающими другой тег. Обсуждение У вас может не быть приложения Macromedia Flash, которое автоматически генерирует HTML, необходимый для внедрения роликов Flash в страницу JSP (рецепт 17.4). В этом случае вам придется написать такой HTML-текст вручную. В примере 17.6 показана страница JSP, в которую встроен файл Hash (внедряемый файл имеет расширение .swf). В этом примере использован тот же подход, что и в других рецептах: тег object предназначен для Windows-браузера Internet Explorer, который внедряет медиафайлы как элемент ActiveX, а не как подключаемый модуль в стиле Netscape. Тег embed, вложенный в тег object предназначен для внедрения файлов Hash в Netscape и другие браузеры, поддерживающие подключаемые модули в стиле Netscape. Пример 17.6 взят из технических комментариев на http://www.rnacromedia.com/sup- port/flash/technotes.html. Пример 17.6. Страница JSP, содержащая встроенный файл <%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %> <jsp:useBean id="date" class="java.util.Date" /> <html> <headxtitle>Flash in a JSP</title></head> <body> <h2>Enjoy the Flash Movie</h2> «OBJECT CLASSip= "clsid:D27CDB6E-AE6D-llcf-96B8-444553540000" CODEBASE= "http: / /download.macromedia.com/pub/shockwave/ccdDS/flash/swflash.cab# version=6,040,0" width="293" height="423" <PARAM name="movie" VALUE="coolFlashMov.swf"> <PARAM name="quality" VALUE="high"> <PARAM name="bgcolor" VALUE="#FFFFFF"> 418 | Глава 17. Внедрение мультимедиа в страницы JSP
<EMBED SRC= "coolFlashMov.swf" qua!ity="high" width='l293" height="423" bgcolor="#FFFFFF" type="application/x-shockwave-flash" PLUGINSPAGE- "http://www.macromedia.com/go/getflashplayer" </EMBED> </OBJECT> <br /><c:out value="${date}"/> </body> </html> Оба тега - embed и object - предлагают конечному пользователю скачать, соответ- ственно, необходимую версию подключаемого модуля Flash или элемента управления ActiveX, если таковые у пользователя еще не установлены. См, также Страницу с техническими замечаниями Macromedia: http://www.macromedia.com/sup- port/flash/technotes.html', статью об альтернативах использования тега embed: http://www. macromedia.com/devnet/mx/dreamweaver/articles/flash_satay.html. 17.5 Внедрение файлов Flash в сервлет Задача Требуется встроить файл Flash в вывод сервлета. Решение В методе doGet () сервлета используйтр метод javax.servlet .RequestDis- patcher . include (request, response), который включает необходимый шаблон- ный HTML-текст. / t Обсуждение Сервлет с помощью RequestDispatcher может подключить HTML-фрагмент, загружающий ролик Flash на страницу. Этот процесс похож на включения на стороне сервера в традиционных CGI-программах (Common Gateway Interface - общий шлюзовой интерфейс). Когда сервлет получает запрос, он включает в состав HTML ответа текстовый фрагмент, содержащий теги, необходимые для внедрения файла Flash. Такой подход позво- ляет отделить сервлет от тегов и параметров, предназначенных для загрузки ролика Flash, позволяя развивать каждую из этих двух частей независимо друг от друга. К примеру, вы можете изменить имя файла ролика Flash или изменить какой-либо из параметров тегов object и embed, без необходимости перекомпилировать код сервлета Внедрение файлов Flash в сервлет | 419 14*
В примере 17.7 приведен сервлет, использующий Request Dispatcher для включения текста из примера 17.8. Текст находится в файле flash, txt, расположенном в каталоге верхнего уровня web-приложения. Обычно RequestDispatcher используют для включения выхода | сервлета и JSP, а не для влючения фрагментов текста. Подробнее об этом можно прочитать в рецептах главы 6, посвященных RequestDispatcher. Пример 17.7. Сервлет, использующий RequestDispatcher для включения тегов object и embed package com.j spservletcookbook; inport javax.servlet.*; import j avax.servlet.http.*;, public class FlashServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.lOException { response•setContentType("text/html"); java.io.Printwriter out = response.getWriterO out.printIn( "<html><head><title>Embedded Flash content</title></head><body>"); RequestDispatcher dispatcher = request.getRequestDispatcher( "/flash.txt"); dispatcher.include(request/ response); out.println( "</bodyx/html>") ; } //doGet public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.lOException { doGet(request,response); }//doPost ’ ) Текстовый фрагмент, включаемый сервлетом из примера 17.7, приведен в примере 17.8. Пример 17.8. Текстовый фрагмент (flash.txt), включаемый сервлетом для внедрения файла Flash OBJECT classid=“clsid:D27CDB6E-AEбD-llcf-96B8-444553540000,, codebase= "http: / /download, macromedia. com/pub/shockwave/cabs/f lash/swf lash, cab# version=6,0,0,0" WIDTH-"550" HEIGHT="4()0" id="example" ALIGN=“" <PARAM NAME=movie VALUE="/home/example.swf"> 420 | Глава 17. Внедрение мультимедиа в страницы JSP
<PARAM NAME=quality VALUE=hign> <PARAM NAME=bgcolor VALUE=#FFFFFF> <EMBED src="/home/example.swf" quality=high bgcolor=#FFFFFF WIDTH= "550" HEIGHT="400" NAME="example" ALIGN="" TYPE= " appl ication/x-shockwave- flash * P1»UGINSPAGE= "http://www.macromedia.com/go/getflashplayer"> </EMBED> </OBJECT> Отображение в браузере результата работы сервлета приведено на рис. 17.3. См. также Главу 6 по динамическому включению содержимого в сервлеты; страницу с техническими замечаниями Macromedia: http://www.macromedia.com/support/flash/technotes. html', статью об альтернативах использования тега embed: http://www.macromedia.com/devnet/ mx/drcqmweaver/articles/flash_satay.html. 17.6 Внедрение в JSP фильмов в формате QuickTime Задача Требуется встроить в JSP фильм в формате QuickTime. Решение Используйте тег embed, вложенный в тег object. Тег object должен содержать атрибут CLASSID с соответствующим значением. Обсуждение Как при использовании подключаемого модуля Java, для загрузки фильма в формате QuickTime (от Apple Computer) в JSP используется тег embed, вложенный в HTML-тег object. При этом в атрибуте CLASSID необходимо задать в точности такое значение, какое для него задано в примере 17.9. Для атрибута CODEBASE также необходимо задать значение как в этом примере. Если у пользователя есть Windows-браузер Internet Explorer, но не инсталлирован элемент управления ActiveX: QuickTime, значение атрибута CODE- BASE указывает, откуда пользователь может скачать этот элемент. Внедрение в JSP фильмов в формате QuickTime | 421
Пример 17.9. Внедрение фильма в формате QuickTime в JSP <%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" •%> <jsp:useBean id="date" class»"java.util.Date" /> <html> <head><title>QuickTime in a JSP</titlex/head> <body> <h2>Ladies and Gentlemen, The Who</h2> <OBJECT CLASSID» "clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B" WIDTH»"320" HEIGHT»"256" CODEBASE»"http://www.apple.com/qtactivex/qtp lugin.cab"> <PARAM name-"SRC" VALUE="http://www.parkerriver.com/films/who_bene2.mov"> <PARAM name»"AUTOPLAY" VALUE»"true"> <PARAM name»"CONTROLLER" VALUE»"true"> <EMBED SRC» "http://www.parkerriver.com/films/who_bene2 .mov" WIDTH»"240" HEIGHT»"196" AUTOPLAY»“true" CONTROLLER» "true" PLUGINSPAGE»"http://www.apple.com/quicktime/download/"> </EMBED» </OBJECT» <br /><c:out value»"${date}"/> </body> </html>' Если браузер использует подключаемые модули в стиле Netscape, то тег embed ини- циирует для него загрузку фильма в формате QuickTime. Страница JSP из примера 17.6, к примеру, корректно загружает фильм в браузер Safari на моем лэптопе фирмы Macintosh. Одним из преимуществ тега embed заключается в том, что вы можете использовать ряд специфических атрибутов, которые понимает внедряемый объект, например QuickTime. В примере 17.9 указывается, что воспроизведение фильма должно начаться как только браузер загрузит необходимое количество данных (AUTOPLAY=" true"), а также то, чтЬ браузер должен отображать элементы управления ходом просмотра, позволяющие пользо- вателю останавливать и снова запускать фильм (CONTROLLER»" true"). На рис. 17-4 показано, как выглядит отображение фильма, внедренного в JSP из примера 17.9. 422 | Глава 17. Внедрение мультимедиа в страницы JSP
Ladies and Gentlemen, тпе wno J5] http://www.parkemve' x>m/filmi/whc berie2J; 1 | LW Local into Puc. 17.4. Фильм в формате QuickTime, внедренный в JSP См. также Руководство по внедрению QuickTime в web-страницы от Apple Computer: http://www. apple.com/quicktime/authoring/embed.html\ рецепты 17.3-17.4 по внедрению файлов Flash; рецепт 17.7 по внедрению SVG-файла в JSP; рецепт 17.8 по внедрению фоновой звуко- вой дорожки. Внедрение в JSP фильмов в формате QuickTime | 423
17.7 Внедрение SVG-файла в JSP Задача Вы хотите внутри страницы JSP отобразить рисунок в формате SVG (Scalable Vector Graphics - масштабируемая векторная графика). Решение Для позиционирования SVG на странице JSP используйте НТМ1.гЭлемент embed. 1 Обсуждение Разработчики часто используют тег embed для размещения SVG-файла в HTML- файле или JSP. SVG - это основанная на XML графическая технология, вооружающая разработчиков и дизайнеров инструментом для создания и отображения интерактивной графики, Для интерпретации внедренных SVG-файлов браузеры используют специальные приложения - просмотрщики SVG. Просмотрщик, созданный фирмой Adobe System, можно скачать с сайта http.7Avww.adobe.com/svg/viewer/install/. г Просмотрщик от фирмы Corel можно скачать с сайта http://www.core! com/svgviewer/. В страницу JSP, приведенную в примере 17.10, внедряется SVG-файл testLogo.svg\ - если у пользователя еще не установлен какой-либо просмотрщик SVG, он направляется на сайт, которого можно скачать просмотрщик Adobe SVG Viewer. * * ** Файлы SVG имеют расширение .svg или .svgz, несмотря на то что фактически это ** XML-файлы. . _2_дЛ‘ Пример 17.10. Внедрение в JSP графического файла SVG <%@ taglib uri="http://java,sun.com/^stl/core” prefix="c" %> <jsp:useBean id="date" class="java.util.Date" /> <html> <headxtitle>SVG in a JSP</titlex/head> <body> <h2>A Scalable Vector Graphics example</h2> <embed src= '<c:out values"${param.svg_source}"/>.svg’ widths "200" heights"200" type="image/svg-xml" pluginspage= "http://www.adobe.com/svg/viewer/install/" 424 | Глава 17. Внедрение мультимедиа в страницы JSP
<br /><c:out value=“${date} "/> </body> </html> „ В примере 17.10 показано, как разместить SVG внутри других элементов кода JSP, например, в директиве taglib и стандартном действии jsp:useBean. Пример 17.10 также примечателен тем, что здесь файл SVG загружается динамически, в зависимости от параметра запроса svg_source. В коде страницы для вывода значения параметра используется JSTL-тег с: out и неявно создаваемый EL-объект param (см. ?лаву 23 по использованию JSTL). Результат запроса к странице JSP из примера 17.10, включающего в качестве параметра запроса имя SVG-файла, показан на рис. 17.5. URL запроса выглядит примерно следующим образом. http://localhost:8080/home/svg.jsp?svg_source=testLogo SVG-файл, изображенный на рис. 17.5, заимствован у фирмы Adobe Systems, силами которой были созданы Adobe SVG Viewer и программа для работы с векторной графи- кой, в том числе SVG, Adobe Illustrator. • t См. также Спецификацию SVG от консорциума W3: http://www.w3.org/Graphics/SVG/Overview. htm8\ страницу для инсталляции просмотрщика SVG фирмы Adobe: http://www.adobe. сот/svg/viewer/install/, рецепты 17.3-17.5 по внедрению файлов Flash в сервлеты и JSP; рецепт 17.6 по внедрению фильмов в формате QuickTime в JSP. 17.8 Внедрение фонового звука в JSP Задача Вы хотите внедрить аудиофайл в страницу JSP. Решение ч Используйте в JSP тег embed. Если вы хотите, чтобы элементы управления звуком были скрыты, используйте атрибут hidden; в противном случае задайте атрибуты width и height, управляющие отображением элементов.управления звуком. Внедрение фонового звука в JSP | 425
Рис. 17.5. Страница JSP с векторной графикой (SVG) Обсуждение Тег embed используется и для включения в JSP аудиофайлов, таким образом, когда пользователь обратится к странице, браузер начнет воспроизводить музыку. Браузер определяет MIME тип внедренного файла, а затем активизирует вспомогательное прило- жение, например QuickTime или RealAudio для обработки внедренного файла и воспроиз- ведения музыки. В примере 17.11 показана страница JSPb которую внедрен MPEG аудио-файл уровня 3 (MP3). Страница также отображает некоторую информацию об исполнителе на основе значения параметра запроса; эта случайная информация включена, чтобы показать, как код JSP сочетается с тегом embed. Тег embed включает атрибуты width и height, для отображения элементов управления воспроизведением звука на web-странице. Эти элементы управления позволяют пользователю уменьшать громкость или выключать звук, если звук ему мешает. 426 | Глава 17. Внедрение мультимедиа в страницы JSP
Пример 17.11. Страница JSP с внедренным аудио файлом <%@ taglib uri="http://java.sun.com/jstl/core" prefix»"c" %> <c:set var="artist" value="${param.artist}" /> <html> <headxtitle>Choose Your Tunes</titlex/head> <body> <h2>You chose music from the artist <c:out value="${artist}" /x/h2> <embed src="Constantcraving.mp3" width="240" height="160"> </embed> </body> </html> Вид страницы из примера 17.11 в браузере показан на рис. 17.6. Рис. 17.6. Внедрение элементов управления звуковым файлом в JSP См. также Рецепты 17.1 и 17.2 по внедрению Java-апплета в JSP; рецепты 17.3-17.5 по внедрению файлов Flash в сервлеты и JSP; рецепт 17.6 по внедрению фильмов в формате QuickTime в JSP; рецепт 17.7 по внедрению SVG-файла. Внедрение фонового звука в JSP | 427
ГЛАВА 18 Работа с запросом клиента 18.0 Введение Некоторым web-приложениям необходимо проанализировать запрос от клиента, прежде чем послать ответ на него. Примером может служить сервлет, определяющий тип браузера (обычно на основе заголовка User-Agent). Сервлеты, или другие web- компоненты, получают информацию о запросе, анализируя заголовки НТТР-запроса. Эти заголовки состоят из имени заголовка, за которым следует символ двоеточия и значение, например Accept-Language: еп. Заголовки предшествуют телу любого запроса, посылаемого клиентом серверу, например запросу, содержащему текст, посланный из HTML-формы. Ниже приведен пример группы заголовков, посланных в составе запроса к странице JSP с именем cOntextBindJsp. GET /home/contextBind.jsfc> HTTP/1.1 User-Agent: Opera/5.02 (Windows NT 4.0; U) [en] Host: localhost:9000 Accept: text/html, image/png, image/jpeg, image/gif, image/x-xbitmap, */* Accept-Language: en Accept-Encoding: deflate, gzip, x-gzip, identity, *;q=0 Cookie: mycookie=1051567248639; JSES- SIONID=1D51575F3FOB17D26537338B5A29DB1D Connection: Keep-Alive Рецепты данной главы показывают, как в сервлете или JSP проанализировать заголовки запроса, как испбльзовать фильтры для модификации запроса, как авто- матически обновлять сервлеты и JSP и как подсчитать количество запросов к приложению. 428
18.1 Анализ заголовков HTTP-запроса в сервлете Задача Вы хотите в сервлете проанализировать заголовки НТТР-запроса. •S Решение Для доступа к именам и значениям заголовков запроса используйте методы javax. servlet. http. HttpServletRequest.getHeaderNames () и getHeader (). Обсуждение Метод HttpServletRequest. getHeaderNames () возвращает имена всех заголов- ков пришедшего запроса. После этого можно получить значение конкретного заголовка, передав его имя методу HttpServletRequest. getHeader (). Сервлет из.примера 18.1 получает в методе doGet () перечисление (Enumeration) с именами заголовков, а затем отображает каждый заголовок и его значение в отдельной строке генерируемой HTML- страницы. Пример 18.1. Сервлет, отображающий имена и значения заголовков package com.jspservletcookbook; ? import java.util.Enumeration; import javax.servlet.*; import javax.servlet.http.*; public class RequestHeaderView extends HttpServlet { public; void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.lOException { //получаем перечисление co всеми именами заголовков Enumeration enum = request .'getHeaderNames (); response.setContentType("text/html"); java.io.Printwriter out = response.getWriter(); out.printIn( "<htmlxheadxtitle>Request Header View</title></headxbody>") ; out .printin (J,<h2>Request Headers</h2>") ; String header = null; //отображаем имя и значение каждого заголовка Анализ заголовков НТТР-запроса в сервлете | 429
while (enum. hasMoreElements()){ header = (String) enum.nextElement(); //getHeader возвращает null если заголовка с указанным именем // в запросе не существует out.printIn("<strong*"+header+"</strong*"+": *+ request.getHeader(header)+"<br>" ); } out. println (" </bodyx/html>") ; } //doGet } Отображение выходной информации сервлета RequestHeaderView в браузере приводится на рис. 18.1. Addfess http://localhost:8080/home/requestheaders Refresh,, iHome] ВНИ Request Header View - Microsoft Internet Explorer Request Headers £ в accept: image/gif, image/x-xbitmap. image/jpeg. image/pjpeg, application/msword, application/vnd.ms-powerpoint, application/vnd.ms- excel, application/pdf, 7* accept-language en-us § accept-encoding: gzip, deflate user-agent Mozilla/4.0 (compatible; MSE 5.5; Windows NT4.0) host localhost:8080 4 connection: Keep-Alive Puc. 18.1. Сервлет, отображающий имена и значения заголовков запроса См. также Рецепт 18.2 по анализу заголовков запроса в JSP; рецепт 18.3 по использованию фильтра для обертывания запроса и перенаправлению запроса по цепочке фильтров; рецепт 18.6 по использованию слушателя для отслеживания запросов; главу 7 по работе с параметрами запроса и свойствами компонента JavaBean в сервлетах, JSP и фильтрах. 430 | Глава 18. Работа с запросом клиента
18.2 Анализ заголовков HTTP-запроса в JSP Задача С помощью JSP отобразить имена и значения заголовков запроса. Решение Для просмотра имен и значений запроса используйте JSTL-теги с: f orEach и с: out. Обсуждение ' JSTL версии 1.0 позволяет получить заголовки запроса посредством неявно создавае- мого объекта header. JSTL?автоматически делает эту переменную доступной для JSP; объект header приводится к типу j ava .util. Мар (отображение). В примере 18.2 тег с: forEach перебирает значения отображения (Мар) заголовков и заносит каждое имя и значение заголовка в переменную цикла, заданную атрибутом var тега с: f orEach (в примере 18.2 она называется req). Атрибут var реализован как тип java.util .Map.Entry тип данных для хранения ключей и значений. Тег с:out отображает имя каждого заголовка, используя EL-формат: $ {req. key}. Тег с: out После- довательно отображает значения, получаемые с помощью конструкции $ {req. value} . Пример 18.2. Просмотр имен и значений заголовков запроса в JSP <%© taglib uri®"https//java.sun.com/jstl/cora" prefix«"c" %> <html> <head><title>Request Headers</titlex/head> <body> <h2>Here are the Request Header names and values</h2> <c:forEach var="req" items®"${header}"> -4 <strongxc:out value® i "${req.key}"/x/strong>s <csout value®"${req.value}"/xbr> </c:forEach> </body> </html> Результат запроса страницы displayHeaders.jsp из браузера приведен на рис. 18.2. См. также Главу 23 по использованию JSTL; рецепт 18.1 по анализу заголовков запроса в сервле- тах; рецепт 18 3 по использованию фильтра для обертывания запроса и перенаправлению запроса по цепочке фильтров; рецепт 18.6 по использованию слушателя для отслеживания запросов; главу 7 по работе с параметрами запроса и свойствами компонента JavaBean в сервлетах, JSP и фильтрах. Анализ заголовков HTTP-запроса в JSP | 431
□HG 3 Request Headers - Microsoft Internet Explorer Forward Si arch Ete Е<Й yiew Fivorrtei Tools Help Refresh Home „„fr Back Across |£] http://localhosl:80807home/di$pla}iHeadefs.jsp Here are the Request Header names and values Ж accept-encoding: gzip. deflate connection: Keep-Alive host: localhost:8080 accept-language: en-us user-agent: Mozilla/4.0 (compatible; MSIE'5.5; Windows NT4.0) cookie: JSESSIONID=CA63DD08E1CAEA895F638DFB667F9AB0 accept: */* — Puc. 18.2. Страница JSP, показывающая заголовки запросов с помощью JSTL 18.3 Использование фильтра для модификации заголовков запроса Задача Вы хотите использовать фильтр для изменения заголовков запроса, перед тем как этот запрос получит сервлет или JSP. Решение Оберните запрос в созданный вами пользовательский класс запроса. Передайте этот обертывающий (декорирующий) класс методу FiIteiChain.doFilter () вместо первоначального адресата запроса. 432 | Глава 18. Работа с запросом клиента
Обсуадение Класс j avax. servlet. http. HttpServletRequestWrapper - это самый подхо- дящий для данного случая класс, который можно расширить для придания дополнитель- ной функциональности HTTP-запросу. Вот как с помощью фильтра можно модифицировать и перенаправить запрос. 1. Создайте класс, расширяющий класс HttpServletRequestWrapper. 2. Поместите этот класс в каталог WEB-INF/classes web-приложения (включая туда все каталоги, связанные с пакетом) или в каталог WEB-INFAib, если клаЬс является частью JAR-файла. 3. Создайте класс, реализующий javax. servlet .Filter (фильтр), типа того, что приведен в примере 18.3. Этот класс использует созданный вами пользовательский класс-обертку запроса, чтобы обернуть им параметр ServletRequest метода Filter.doFilter(). 4. Сохраните получившийся класс фильтра в каталог! WEB-INF/classes или WEB-INFAib (если он находится в JAR) web-приложения. 5. Зарегистрируйте этот фильтр в web.xml. В данном рецепте фильтр отображается на все запросы к web-приложению, поскольку используется отображение на URL /*. В примере 18.3 приведен класс фильтра, передающий класс, обертывающий запрос, по цепочке фильтров. Файл с фильтром называется RequestFilter; класс-обертка называется ReqWrapper. Пример 18.3. Фильтр, обертывающий HttpServletRequest package com.jspservletcookbook; import j avax.servlet.*; import javax.servlet.http.*; public class RequestFilter implements Filter { private FilterConfig config; /♦♦ Создаем новый RequestFilter */ public RequestFilter!) {} public void init(FilterConfig filterConfig) throws ServletException! this.config = filterConfig; } public void doFilter(ServletRequest request, ServletResponse response, Filterchain chain) throws java.io.lOException, ServletException { ReqWrapper wrapper = null; ServletContext context = null; //создаем объект, обертывающий запрос - экземпляр класса //Reqwrapper. Запрос клиента передается //в конструктор класса ReqWrapper Использование фильтра для модификации заголовков запроса | 433
if (request instanceof HttpServletRequest) wrapper = new ReqWrapper((HttpServletRequest)request)/ //используем метод ServletContext.log для протоколирования // имен/значений параметров if (wrapper != null){ context = config.getServletContext(); context.log("Query: " + wrapper.getQueryString());} //продолжаем обработку запроса в другом фильтре //или в сервлете назначения 1 if (wrapper null) chain.doFilter(wrapper,response); else chain.doFilter(request,response); }//doFilter public void destroy (.) { /*вызывается перед удалением экземпляра фильтра х из обслуживания web-контейнером*/ }//destroy ) В примере 18.3 для протоколирования строки запроса ReqWrapper используется контекст сервлета. В данном случае класс ReqWrapper добавляет в строку запроса еще один параметр, однако вы можете изменить реализацию этого класса в своем web-прило- жении по своему усмотрению. В примере 18.4 приведены записи отображения фильтра в дескрипторе развертывания (web.xml), гарантирующие, что любой запрос к прило- жению будет проходить через этот фильтр. Пример 18.4. Отображение фильтра в web.xml <filter> < f ilter-name>RequestFilter</fi1ter-name> <filter-class>com.jspservletcookbook.RequestFilter</filter-class> </filter> <f i1ter-mapping> < f i1ter-name>RequestFi1ter</filter-name» curl-pattern>/*</иг1-pattern» </fi1ter-mapping> ReqWrapper является простым примером подкласса класса HttpServletRe- questWrapper, который инкапсулирует исходный запрос. Этот класс переопределяет метод getQueryString (), чтобы добавить к строке запроса один параметр. 434 | Гляви 18. Работа с запросом клиента
Чтобы получить доступ к новому параметру filter, вы должны вызвать метод getQueryString() запроса, когда запрос достигнет сервлета, которому он * * и был адресован, затем необходимо разобрать значение, которое вернет этот метод ' на отдельные параметры. Использование EL не работает для «обернутого запроса», для которого был-переопределен метод getQueryString (). //значение нового параметра //добавляемого в методе getQueryString //так не получишь , ${param.filter} Запрос, проходящий через фильтр, посылается в качестве параметра в конструктор класса ReqWrapper, таким образом, фильтр (из примера 18.3) обертывает запрос с помощью следующего кода. wrapper = new ReqWrapper((HttpServletRequest)request); URL, посылаемый приложению содержит строку запроса name=Bruce, что приво- дит к появлению в протоколе (log) сервлета следующего текста (в результате работы метода ServletContext. log). Query: name=Bruce&filter=com.j spservletcookbook.ReqWrapper. Код класса ReqWrapper приведен в примере 18.5. Пример 18.5. Класс ReqWrapper для инкапсуляции HttpServletRequest (запроса) package -com.j spservletcookbook; import javax.servlet.*; import j avax.servlet.http.HttpServletRequestWrapper; import javax.servlet.http.HttpServletRequest; public class ReqWrapper extends HttpServletRequestWrarrer{ private static final String AMP = public ReqWrapper(HttpServletRequest request){ super(request); public String getQueryString()f String query = null; //получаем строку запроса из обертываемого объекта (запроса) query « ((HttpServletRequest)getRequest()).getQueryString(); //добавляем к строке запроса параметр 'filter' //присваивая ему в качестве значения имя данного класса if (query ! null) / return query +AMP+"filter*"+getClass().getName(); Использование фильтра для модификации заголовков запроса | 435
else '.return " filter» " +getClass (). getName (); }I/getQueryString , } Вызов метода chain.doFilter(wrapper,response) в конце примера 18.3 передает запрос (уже обернутый в созданный вами класс) и ответ следующему фильтру или, если других фильтров не зарегистрированно, то сервлету (или JSP), которому был адресован запрос. См. также Рецепты 18.Р и 18.2 по анализу заголовков.запроса в сервлетах и JSP; рецепт 18.3 по использованию фильтра для обертывания запроса и перенаправлению запроса - по цепочке фильтров; рецепт 18.6 по использованию слушателя для отслеживания запросов; главу 7 по работе с параметрами запроса и свойствами компонента JavaBean в сервлетах, JSP и фильтрах. 18.4 Автоматическое обновление сервлета Задача Требуется через определенные интервалы времени автоматически обновлять генерируемую сервлетом страницу. Решение Используя объект j avax. servlet. http. HttpServletResponse, добавьте в ответ заголовок ответа Refresh. \ Обсуждение Предположим, ваш сервлет отслеживает бейсбольный матч между командами Red Sox и Yankees. И вы хотите, чтобы пользователь имел возможность следить за ходом игры «по горячим следам», и чтобы ваше web-приложение постоянно обновляло информацию о состоянии игры. Если к ответу клиенту добавить заголовок ответа Refresh, браузер будет периодически обновлять отображаемую страницу, в соответст- вии с заданным интервалом. Сервлет из примера 18.6 добавляет заголовок ответа, отсылаемого web-контейнером клиенту, в формате Refresh: 60, что означает «еще раз запросите эту страницу через 60 секунд». 436 | Глава 18. Работа с запросом клиента
Пример 18.6. Обновление сервлета через каждые 60 секунд package com.jspservletcookbook; import javax.servlet.*; import javax.servlet.http.*; public class AutoServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.lOException { //браузер клиента будет обращаться к этой странице // через каждые 60 секунд response.addHeader("Refresh","60"); response.setContentType("text/html"); java.io.Printwriter out = response.getWriter(); out.printIn( "<html><head><title>Client Refresh</title></head><body>"); out.println("<h2>Welcome to the Red Sox - Yankees serj.es.. ,</h2>"); //Остальной HTML или динамическое содержимое out.printIn("</body></html>"); } //doGet } Относительно этого подхода необходимо сделать несколько замечаний - если пользова- тель отойдет от своего стола, ее браузер будет упорно продолжать запрашивать данную страницу. Если ваш сервлет не выводит никаких элементов управления, позволяющих управлять процессом обновления, на ваше приложение может лечь немалая неоправданная нагрузка. Одно из решений данной проблемы заключается в сохранении в атрибуте сеанса количества произведенных обновлений сервлета (см. главу 16). Если это количество превы- сит некоторый предел, вы прекращаете добавлять заголовок к ответу. Фрагмент метода doPost (), в котором учитывается количество обновлений, приведен в примере 18.7. Пример 18.7. Отслеживание количества обновлений страницы у клиента //фрагмент метода doPost (or doGet?) HttpSession session = request,getSession(); Long times = (Long) session.getAttribute("times"); //создаем атрибут сеанса, если он еще не создан if (times == null) ‘ session.setAttribute("times",new Long(0)); //значение атрибута сеанса хранится в локальной переменной 'temp' long temp = 1,- Автоматическое обновление сервлета | 437
//увеличиваем.значение атрибута, чтобы учесть текущий запрос if (times != null) temp = times.longValueO + 1; if (temp < 60) //не более 60 обновлений; то есть около часа response.addHeader("Refresh","60"); //изменяем значение атрибута сеанса session.setAttribute(!'times" ,new Long.(temp)) ; Этот код также работает и внутри метода doGet (). . .fo* См. также Рецепт 18.5 по автоматическому обновлению JSP; рецепты 18.1 и 18.2 по анализу заго- ловков запроса в сервлетах и JSP; рецепт 18.3 по использованию фильтра для обертывания запроса и перенаправлению запроса дю цепочке фильтров; рецепт 18.6 по использованию слушателя для отслеживания запросов. 18.5 Автоматическое обновление JSP Задача Вы хотите, чтобы запросы к JSP повторялись через определенный интервал. Решение Используйте скриптлет JSP, добавляющий заголовок Refresh ответа к ответу на запрос. Обсуждение Следующий ниже код скриптлета добавляет заголовок Refresh, задающий 60-секунд- ный интервал обновления JSP. Поместите этот код в начале JSP перед содержимым страницы. <% response.addHeader("Refresh","60"); %> Если вы хотите, чтобы JSP обновлялась содержимым другого web компонента или страницы, используйте следующий синтаксис. <% response.addHeader("Refresh","10; http://localhost:8080/home/thanks.jsp"); %> 438 | Глава 18. Работа с запросом клиента I
См. также Пример 18.6 из рецепта 18.4 по обновлению сервлета; пример 18.7 JJ3 рецепта 18.4 по ограничению количества автоматических обновлений сервлета; рецепты 18.1 и 18.2 по анализу заголовков запроса в сервлетах и JSP; рецепт 18.3 по использованию фильтра для обертывания запроса и перенаправлению запроса по цепочке фильтров; рецепт 18.6 по использованию слушателя для отслеживания запросов. 18.6 Подсчет числа запросов к web-приложению Задача Требуется подсчитать число запросов, обработанных web-приложением. Решение Для получения уведомления об инициализации HTTP-запроса используйте javax. servlet. ServletRequestListener (слушатель запросов к сервлету). Обсуждение Хорошая кандидатура для отслеживания запросов - слушатель запросов, поскольку web-контейнер извещает слушателя запросов обо всех новых запросах, вызывая его метод requestlnitialized(). В примере 18.8 ведется учет количества поступающих запросов и это количество сохраняется в статической переменной класса reqCount. Программа увеличивает значение данной переменной в синхронизированном блоке в методе requestlnitialized(). С помощью ServletContext (контекста сервлета) сообщения о запросах прото- колируются, что позволяет наблюдать за работой слушателя. Однако действующее и активно используемое приложение, протоколирующее информацию о каждом запросе очень неэффективно использует ресурсы web-контейнера. Такой тип протоколирования активности следует оставить лишь для приложений, находящихся в стадии отладки. Пример 18.8. Класс слушателя запросов, подсчитывающий количество запросов к приложению package com.jspservletcookbook; import javax.servlet.*; import javax.servlet.http.*; f public class ReqListener implements ServletRequestListener { private static long reqCount; public vbid reqUestInitialized(ServletRequestEvent sre){ Подсчет числа запросов к web-приложению | 439
//используется ServletContext для протоколирования context sre.getServletContext(); ,//используется ServletRequest для получения информации о новом запросе request = sre.getServletRequest(); //статическая переменная класса reqCount увеличивается в этом блоке; //приращение значения этой переменной синхронизируется для того, // чтобы один поток не мог читать значение этой переменной //в то время, как другой увеличивает его synchronized (context){ context.log( "Request for "+ (request instanceof HttpServletRequest ? ((HttpServletRequest) request).getRequestURI() : Unknown")+ "; Count""+ ++reqCount); } / /synchronized public void requestDestroyed(ServletRequestEvent sre){ //Вызывается когда запрос к сервлету всходит из области видимости. }//requestDestroyed Вы можете получить доступ к новому запросу (ServletRequest) в обоих методах слушателя, вызывая метод Servlet Request Event. getServletRequest (). f Возвращаемое им значение ServletRequest необходимо привести к типу * HttpServletRequest, чтобы получить возможность вызывать ' методы последнего. В примере 18.8 вызывается метод getRequestURI () нового запроса HttpServletRequest, предоставляющий часть информации, включаемой в сообщение протокола. Слушатель запроса (ServletRequestListener) необходимо зарегистрировать в web.xml. <listener> <listener-class>com.jspservletcookbook.ReqListener</listener-class> </listener> После этого, во время своего запуска, web-контейнер создает экземпляр этого слуша- теля. Ниже приведен пример записи протокола (журнала) сервера, созданной в резуль- тате запроса к приложению, в котором зарегистрирован слушатель запросов. 2003-05-30 07:22:21 Request for /home/servlet/сот.jspservletcookbook.Ses- sionDisplay; Count=2 440 | Глава 18. Работа с запросом клиента
Для сервера Tomcat данная строка будет помещена в файл протокола (журнал), находящийся в каталоге <инсталляционный-каталог-Тотса1>Лс^$. См. также Рецепты 18.1 и 18.2 по анализу заголовков запроса в сервлетах и JSP; рецепт 18.3 по использованию фильтра для обертывания запроса и перенаправлению запроса по цепочке фильтров; главу 7 по работе с параметрами запроса и свойствами компонента JavaBean в сервлетах, JSP и фильтрах. Подсчет числа запросов к web-приложению | 441
ГЛАВА 19 Фильтрация запросов и ответов 19.0 Введение Впервые фильтрация сервлетов появилась в API сервлета версии 2.3 в 2001 году. Фильтрация - это мощная технология, предназначенная для разработчиков сервлетов, позволяющая генерировать цепочки JavarKiiaccoB, последовательно выполняющихся в ответ на запрос клиента. Сначала разработчики создают один или несколько Java-классов, реализующих интерфейс javax. servlet. Filter. Эти классы могут выполнять ряд действий до того, как запрос к сервлету будет обработан, образуя, таким образом, цепочку действий, выполняемых перед тем как запрос достигнет точки своего назначения (в числе этих действий может быть и полное блокирование данного запроса). Согласно документации по Filter API, к таким действиям относятся: • аутентификация запроса; • шифрование данных; • сжатие данных; • протоколирование; • XSLT-фильтрация; • преобразование изображения. Документация по интерфейсу Filter находится по адресу: http://java.sun. com/j2ee/I 4/docs/api/javax/servlet/Filtei httnl Зарегистрируйте фильтр в дескрипторе развертывания и затем отобразите зарегистрированный фильтр на имена сервлетов или шаблоны URL там же4 в дескрипторе развертывания вашего web-приложения. Когда web-контейнер запус- кает вашего приложение, он создает по одному экземпляру каждого фильтра, объяв- ленного вами в дескрипторе развертывания. Фильтры выполняются в том порядке, в каком они объявлены в дескрипторе развертывания. 442
19.1 Отображение фильтра на сервлет Задача Требуется отобразить фильтр на отдельный сервлет. Решение Для связи фильтра с сервлетом используйте элементы filter и filter-mapping в web.xml. у I Обсуя(дение Web-контейнер определяет, какие фильтры вы хотите применить к сервлету, используя информацию из дескриптора развертывания. Элемент filter связывает имя фильтра с конкретным Java-классом, реализующим интерфейс javax. servlet. Filter. Элемент filter-mapping затем связывает отдельные фильтры с URL или путями, подобно тому, как работает элемент servlet-mapping, который вы ранее уже использовали в web.xml. В примере 19.1 показан дескриптор развертывания из API сервлетов версии 2.3, включающий отображение фильтра с именем LogFilter на путь к сервлету /requestheaders. Пример 19.1. Отображение фильтра на сервлет <?xml versions"1.О" encodings"ISO-8859-1"?> <!DOCTYPE web-app' PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-application_2_3.dtd" <web-app> <1— регистрируем фильтр --> <filter> < f il t er-naxne >LogFilt er < / filter-name > <filter-clase>com.jspservletcookbook.LogFilter</filter-class> </filter> ' <fi1ter-mapping> <filter-name>LogFilter</filter-name> <ur 1 -pattern» / requestheaders < /url -patt em> </filter-mapping» <1— регистрируем сервлет, на который отображается фильтр —> <servlet> < sexvlet-name>requestheaders</servlet-name> Отображение фильтра на сервлет | 443
<servlet-class»com.jspservletcookbook.RequestHeaderViewc/servlet-class» </servlet> <1— Это отображение на URL сервлета requestheaders —> <servlet-mapping» < servlet-name»requestheaders</servlet-name» <url-pattera»/requestheaders</url-pattern» </servlet-mapping» , </web-app> Когда клиент посылает запрос, содержащий путь к сервлету /requestheaders, web-кон- тейнер применяет к этому запросу фильтр LogFilter. Этот путь, как, например, в строке / http://localhost:8080/home/requestheaders является единственным путем к сервлету, к которому применяется данный фильтр. Как можно догадаться, фильтр LogFilter протоколирует некоторую информацию о запросе перед тем, как запрос продолжит движение к сервлету назначения. Класс, реализующий фильтр LogFilter, приведен в примере 19.2. Этот фильтр также демонстрирует то, как внутри фильтра можно протоколировать сообщения. Что необходимо помнить следующее. • Создавайте фильтр с конструктором, не имеющим параметров. • Присваивайте классу фильтру имя пакета. • Сохраняйте фильтр в каталоге V^EB-INF/classes web-приложения, включая туда все каталоги, связанные с его пакетом. • Отображайте фильтр на сервлет в web.xml, как показано в примере 19.1. Пример 19.2. Фильтр, протоколирующий информацию package com.jspservletcookbook; import javax.servlet.*; inport, javax.servlet.http.*; import org.apache.Iog4 j.Logger; inport org.apache.Iog4 j.Propertyconfigurator; public class LogFilter implements Filter { private FilterConfig confxg; private Logger log; 7/ Создаем новый LogFilter public LogFilter() {) 444 | Глава 19. Фильтрация запросов и ответов
public void init(FilterConfig filterConfig) throws ServletException{ this.config = filterConfig; //загружаем конфигурацию logger'ов данного приложения // используя файл servletLog.properties Propertyconfigurator. configure (config. getServletContext () . getRealPath(" /") + "WEB-INF/classes/servletLog.properties"); log = Logger.getLogger(LogFi1ter.class); log.info("Logger instantiated in "+ getClass().getName{)) ; }//init public void doFilter(ServletRequest request, ServletResponse response, Filterchain chain) throws java.io.lOException, ServletException { HttpServletRequest req = null; if (log != null && (request instanceof HttpServletRequest))( req = (HttpServletRequest) request; log.info( "Request received from: " + req.getRemoteHost() + " for: " + req.getRequestURL()); } //передаем запрос далее по цепочке фильтров chain.doFilter(request,response); }// doFilter public void destroy()( /♦вызывается перед тем, как данный экземпляр фильтра удаляется из обслуживания web-контейнером*/ log = null; } } Данный фильтр протоколирует адрес удаленного хоста, от которого поступил запрос, и запрошенный клиентом URL. Ниже представлен пример записи протокола. INFO - Request received ffom: lodalhost for: http://localhost:8080/home/ requestheaders Фильтр использует библиотеку log4j (см. главу 14) от Apache Software Foundation. Поскольку первый параметр метода dofilterO фильтра имеет тип javax. servlet .ServletRequest, этот параметр необходимо привести кHttpServletRequest, чтобы получить возможность вызывать такие методы, как HttpServletRequest.getRempteHost(). Отображение фильтра на сервлет | 445
См. также Рецепт 7.9 по использованию фильтра для чтения значений параметров запроса; рецепт 11.11 по использованию фильтра для отслеживания атрибутов сеанса; рецепт 18.3 по использованию фильтра для модификации запроса; рецепты 19.2-19.4 по отображению фильтра на web-компоненты; рецепт 19.5 по настройке параметров инициализации фильтра; рецепт 19.6 по блокированию запросов; рецепт 19.7 по фильтрации HttpServ- letResponse (ответа); рецепт 19.9 по использованию фильтров для проверки параметров запроса; рецепт 19.10 по использованию фильтров д ля запрета запросов с определенных IP-адресов. / 19.2 Отображение фильтра на JSP Задача Вы хотите, чтобы web-контейнер применял фильтр для всех запросов к определен-, ной странице JSP. , Решение Для отображения фильтра на JSP используйте дочерний элемент url-pattern элемента filter-mapping в дескрипторе развертывания. Обсуждение Отобразите фильтр нд JSP, задав путь к данной странице JSP в элементе url-pat- tern, входящем в элемент filter-mapping. В примере 19.3 показана конфигурация web.xml, отображающая фильтр из примера 19.2 на страницу re.questHeadeis.jsp. Пример 19.3. Отображение фильтра на JSP <!— начало дескриптора развертывания web.xml —> <filter> < fi1ter-name>LogFi1ter</f i1ter-name> <filter-class>com.jspservletcookbdok.LogFilter</filter-class> </filter> < f i11er-mapping> < f i11er-name>LogFi11er</f i11er-name> » <url-pattern>/displayHeaders.jsp</url-pattern> </f i11er-mapping> <!— продолжение дескриптора развертывания —> 446 | Глава 19. Фильтрация запросов и ответов
' *' Для одного фильтра можно создать несколько отображений, при этом каждое U*’ 4 отображение может иметь свой тип шаблона URL. — При конфигурации, приведенной в примере 19.3, любые запросы к /requestheaders будут проходить через фильтр с именем LogFilter. Исходный код класса, реализующего данный фильтр, приведен в примере 19.2. Этот код протоколирует сооб- щение о запросе, перед тем как запрос отправится дальше по цепочке фильтров к странице JSP. Протоколируемое сообщение выглядит примерно следующим образом. INFO - Request received from:'localhost for: http://localhost:8080/home/ displayHeaders.jsp Саму страницу JSP никак не нужно специально настраивать для работы под фильтром. С помощью приведенной ниже конфигурации можно применить фильтр ко всем страни- ' цам JSP. < f i11er-mapping> < f i1ter-name>LogFi1ter</f i1ter-name> <url-pattern>*.jsp</url-pattern> </filter-mapping> Шаблон URL вида *.jsp представляет собой расширенное отображение, связывающее фильтр LogFilter с любыми компонентами web-приложения, имеющими расширение .jsp. См. также Рецепт 7.9 по использованию фильтра для чтения значений параметров запроса; рецепт 11.11 по использованию фильтра для отслеживания атрибутов сеанса; рецепт 18.3 по использованию фильтра для модификации запроса; рецепт 19.3 по отображению на сервлет более одного фильтра; рецепты 19.4 по изменению порядка применения фильтров к сервлету; рецепт 19.5 по настройке параметров инициализации фильтра; рецепт 19.6 по блокированию запросов; рецепт 19.7 по фильтрации HttpServletRe- sponse (ответа); рецепт 19.8 по использованию фильтров с RequestDispatcher; рецепт 19.9 по использованию фильтров для проверки параметров запроса; рецепт 19.10 по использованию фильтров для запрета запросов с определенных IP-адресов. Отображение фильтра на JSP | 447
19.3 Отображение на сервлет более одного фильтра Задача Требуется, чтобы запросы к сервлету или JSP проходили более одного фильтра. Решение < Отобразите каждый из фильтров на сервлет или JSP с помощью элементов filter- mapping в дескрипторе развертывания. Фильтры применяются к сервлету в том порядке, в каком они появляются в дескрипторе развертывания. Обсуждение Web-приложение может определить несколько разных фильтров - каждый для определенной цели. К примеру, один из фильтров может протоколировать сообщения, а другой - аутентифицировать пользователей. Организовать цепочку фильтров, в которой каждый фильтр выполняется в определенном порядке, довольно просто. С помощью элемента filter-mapping каждый из фильтров отображается на сервлет (или JSP). Затем web-контейнер применяет фильтры к этому сервлету в том порядке, в котором элементы filter-mapping определены в дескрипторе развертывания. В примере 19.4 конфигурируются два. фильтра: AuthenFilter и LogFilter. Затем с помощью элементов filter-mapping сервлет с именем requestheaders отобража- ется на каждый из этих фильтров. Порядок появления элементов filter-mapping в примере 19.4 задает, что фильтр для аутентификации (AuthenFilter) должен применяться к сервлету г equest headers первым, а за ним будет применен фильтр LogFilter. Чтобы можно было отобразить фильтр на имя сервлета, сервлет должен быть зарегистрирован в web.xml. В примере 19.4 регистрация сервлета requestheaders следует после элементов filter и filter-mapping. Пример 19.4. Отображение на сервлет более одного фильтра <!— начало дескриптора развертывания web.xml —> <filter> < f i11er-name>AuthenFi1ter</fi1ter-name> <filter-class>com.jspservletcookbook.AuthenticateFilter</filter-class> </filter> <filter> < fi11er-name>LogFi11er</fi11er-name> <filter-class>com.jspservletcookbook.LogFilter</filter-class> 448 | Глава 19. Фильтрация запросов и ответов
</filter> i <fliter-mapping» <filter-name»AuthanFilter<I filter-name» <servlet-name»requestheaders</servlet-name» </filter-mapping» <filter-mapping» <filter-name»LogFilter</filter-name» < servlet -name»requestheaders</ servlet -name» </filter-mapping» <1— определения сервлета —> <servlet» <servlet-name»requestheaders</servlet-name» < servlet-class»com.jspservletcookbook.ReguestHeaderView</servlet- class» </^ervlet> <J— раздел web.xml с элементами servlet-mapping —» <servlet-mapping» < servlet-name>reguestheaders</servlet-name» <ur1-pattern»/requestheaders</url-pattern» </servlet-mapping> <!— продолжение дескриптора развертывания —> Когда пользователь обращается к сервлету requesthead.ers, используя путь к сервлету /requestheaders, который указан в элементе servlet-mapping, запрос прохо- дит через фильтры AuthenFil ter и LogFilter прежде, чем достигнет сервлета. ' ’’ Аналогичный процесс применяется в отношении элементов filter-mapping, в которых вместо servlet-name используется элемент url-pattern. Порядок к размещения элементов filter-mapping в дескрипторе развертывания определяет порядок применения фильтров к web-компонентам, которые соответствуют шаблону, заданному в url-pattern. См. также Рецепт 7.9 по использованию фильтра для чтения значений параметров запроса; рецепт 11.11 по использованию фильтра для отслеживания атрибутов сеансд; рецепт 18.3 по использованию фильтра для модификации запроса; рецепты 19.4 по изменению порядка применения фильтров к сервлету; рецепт 19.5 по настройке параметров инициализации фильтра; рецепт 19.6 по блокированию запросов; рецепт 19.7 по фильтрации HttpServ- letResponse (ответа); рецепт 19.8 по использованию фильтров с RequestDis- patcher; рецепт 19.9 по использованию фильтров для проверки параметров запроса; рецепт 19.10 по использованию фильтров для запрета запросов с определенных IP-адресов. 15-1545 Отображение на сервлет более одного фильтра | 449
19.4 Изменение порядка, в котором фильтры применяются к сервлетам Задача Требуется изменить порядок, в котором фильтры применяются к web-компонентам. Решение Измените порядок следования элементов filter-mapping в дескрипторе развертывания. Обсуждение Порядок следования элементов filter-mapping в web.xml определяет пбрядок, в котором web-контейнер применяет фильтры к сервлету. В примере 19.5 порядок следо- вания элементов filter-mapping, отображающих два фильтра на сервлет request- headers, изменяется на обратный (по сравнению с рецептом 19.3). Таким образом, фильтр LogFilter применяется к сервлету раньше, чем фильтр AuthehFilter. Любые запросы к данному сервлету проходят по цепочке: LogFilter -»AuthenFilter -> сервлет requestheaders. Пример 19.5. Изменение порядка следования элементов fil ter-mapping <!— Фильтр LogFilter применяется к сервлету requestheaders до фильтра AuthenFilter —> \ < f i11er-mapping> < f i11er-name>LogFi11er</filter-name> <servlet-name>requestheaders</servlet-name> </fi1ter-mapping> <filter-mapping>' < f i11er-name>AuthenFi1ter</fi11er-name> <servlet-name>requestheaders</servlet-name> </f i1ter-mapping> См. также Рецепт 7.9 по использованию фильтра для чтения значений параметров запроса; рецепт 11.11 по использованию фильтра для отслеживания атрибутов сеанса; рецепт 18.3 по использованию фильтра для модификации запроса; рецепты 19.1-19.3 по отображению фильтров на web-компоненты; рецепт 19.5 по настройке параметров инициализации фильтра; рецепт 19.6 по блокированию запросов; рецепт 19.7 по фильтрации HttpServ- letResponse (ответа); рецепт 19.8 по использованию фильтров с RequestDis- patcher; рецепт (19.9 по использованию фильтров для проверки параметров запроса; рецепт 19.10 по использованию фильтров для запрета запросов с определенных IP-адресов. 450 | Глава 19. Фильтрация запросов и ответов
19.5 Настройка параметров инициализации для фильтра Задача Вы хотите создать параметр инициализации (init), доступный для фильтра. Решение Для объявления параметра инициализации и его значения используйте дочерний элемент init-param элемента filter. Из фильтра обращайтесь к параметру инициали- зации, с помощью метода getlnitParameter объекта FilterConfig. Обсуждение В примере 19.6 приведен фильтр, объявленный в дескрипторе развертывания. Фильтр включает параметр инициализации с именем log-id. Пример 19.6. Фильтр, объявленный в дескрипторе развертывания с параметром инициализации <filter> < f i11er-name>LogFi11er</filter-name> <filter-class>com.jspservletcookbook.LogFilter</filter-class> <init-param> <param-name>log-id</param-name> <param-value>A102003 < /param-value> < / init -param> </filter> В примере 19.7 показан код, который вы будете использовать в фильтре для доступа к параметру инициализации и его значению. В методе init этого кода инициализируется объект FilterConfig, данный метод вызывается всего один раз - когда web-контейнер создает экземпляр фильтра. Затем код получает значение параметра инициализации фильтра. String id = config.getlnitParameter("log-id"); He забудьте проверить значение,* возвращаемое методом get Ini tParameter, на null, прежде чем будете что-либо делать с этим объектом. Пример 19.7. Доступ из фильтра к параметру инициализации package com.jspservletcookbook; import javax.servlet.*; import javax.servlet.http.*; i import org.apache.Iog4j.Logger; Настройка параметров инициализации для фильтра | 451 15*
import org.apache.Iog4 j.Propertyconfigurator; public class LogFilter implements Filter { private FilterConfig config; private Logger log; // Создаем новый экземпляр LogFilter public LogFilter() {} public void init(FilterConfig filterConfig) throws ServletException{ this.config filterConfig; //загружаем конфигурацию logger'ов данного приложения //используя файл servletLbg.propertied PropertyConfiguratOr.configure(config.getServletContext(). getRealPath("/") + " WEB-INF/classes/servletLog.properties"); log = Logger.getLogger(LogFilter.class); log.info("Logger instantiated in "t getClass().getName()); } • public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws java.io.lOException, ServletException { HttpServletRequest req = null; String id = config.getlnitParameter("log-id"); if (id null) id "unknown"; if (log I* null && (request instanceof HttpServletRequest)){ req = (HttpServletRequest) request; log.infо("Log id:" + id + ": Request received from: n + req.getRemoteHost() + " for " + req.getRequestURLO ); } chain.doFilter(request,response); }// doFilter public void destroy(){ • /♦вызывается перед удалением экземпляра фильтра из обслуживания web-контейнером*/ Iqg = null; ) } Вот как выглядит выходные записи протокола. INFO - Log id:A102003: Request received from: localhost for http://local- host :8080/home/requestheaders 452 | Глава 19. Фильтрация запросов и ответов
Для получения всех параметров инициализации в виде перечисления (объект java. 1 util .Enumeration) вы можете использовать метод getlnitParameterNames объекта FilterConfig. См. также Рецепт 7.9 по использованию фильтра для чтения значений параметров запроса; рецепт Ц.И по использованию фильтра для отслеживания атрибутов сеанса; рецепт 18.3 по использованию фильтра для модификации запроса; рецепты 19.1-19.4 по отображению фильтров на web-компоненты, рецепт 19.6 по блокированию запросов, рецепт 19.7 по фильтрации HttpServletResponse (ответа); рецепт 19.8 по использованию фильтров с RequestDispatcher; рецепт 19.9 по использованию фильтров для проверки параметров запроса; рецепт 19.10 по использованию фильтров для запрета запросов с определенных IP-адресов. 19.6 Возможное блокирование запроса с помощью фильтра Задача Вам требуется возможность блокировать запрос с помощью фильтра. Решение Не вызывайте в фильтре метод doFilter () объекта Filterchain. Вместо этого выдавайте ответ клиенту прямо из метода doFilter () фильтра. Обсуждение Фильтр может блокировать запрос к web-компоненту, например сервлету, JSP или Н*ГМЬ-странице, если не будет вызывать метод Fi 1 terChain.doFilter () в своем методе doFi 1 ter (). ' Класс BlockFilter из примера 19.8 пытается аутентифицировать пользователя, основываясь на параметрах запроса. Если аутентификация проваливается, фильтр исполь- зует объект response (ответ) чтобы самостоятельно выдать клиенту ответ на его запрос, при этом сам запрос не передается дальше к сервлету назначения. Фильтр может выдать клиенту окончательный ответ, даже не выполняя своих задач по фильтрации. Возможное блокирование запроса с помощью фильтра | 453
Пример 19.8. Фильтр имеет возможность блокировать запрос и сформировать ответ самостоятельно package com кj spservletcookbook; import java.io.Printwriter; import java.io.lOException; import j avax.servlet.*; import j avax,servlet.http.*; public class BlockFilter implements Filter { private FilterConfig config; /** Создается новый BlockFilter */ < public-BlockFilter() {} ’ ' public void init(FilterConfig filterConfig) throws ServletException{ this.config = filterConfig; } public void' doFilter(ServletRequest request, ServletResponse response, Filterchain chain) throws lOException, ServletException { HttpServletRequest req = null,- boolean authenticated “ false; Printwriter out = null; if (request instanceof HttpServletRequest){ req = (HttpServletRequest) request; String user » req.getParameter("user");//получаем имя пользователя authenticated authenticateUser(user);//аутентифицируем его ) if (authenticated){ //аутентифицирован, поэтому передаем запрос дальше chain.doFilter(request,response); else { //фильтр формирует ответ на запрос самостоятельно response.setContentType("text/html"); out response.getWriterO; out.printIn( "<htmlxhead»<title»Authentication Response</title»"); out.printin("</head»<body>"); out.printIn("<h2»Sorry your authentication attempt failed</h2>"); out .printIn ("</bodyx/html» " ) ; 454 | Глава 19. Фильтрация запросов и ответов
} }// doFilter public void destroy(){ /* вызывается перед удалением экземпляра фильтра из обслуживания web-контейнером */ } private boolean authenticateUser(String userName){ //аутентификация пользователя с использованием JNDI и базы данных, //но для данного примера просто возвратим false return false; }// authenticateUser } Данный код аутентифицирует пользователя, получая гипотетическое имя пользователя в качестве параметра запроса. Это имя передается в качестве параметра методу аутентифи- кации authenticateUser (), который в данном случае просто возвращает false, для демонстрации самостоятельного ответа фильтра клиенту. В фильтре используется метод Printwriter объекта javax. servlet.ServletResponse (этот объект передается в качестве параметра методу doFilter ()). Метод Printwriter посылает HTML обратно клиенту. Отображение ответа фильтра в браузере приведено на рис. 19.1. 3 Authentication Response Microsoft Internet Explorer He £dit View Favorites Доой .Help Back ill taT Refresh Home Search, Address |^] http://l^caiTost:8080/home/requestheaders Unks £]lns»utionFS Welcome to Sparhawkl £]API DocumerKaonr Beto Chaprers ё|6оо* Sorry your authentication attempt failed Puc. 19.1. HTML-страница, возвращаемая блокирующим фильтром л* ’' Если вы регулярно используете фильтры для отправки ответа клиенту, рассмотрите возможность создания компонента JavaBean для настройки ответа. Сохраните этот f компонент в его пакете в каталоге WEB-INF/classes и используйте в фильтрах. Возможное блокирование запроса с помощью фильтра | 455
См. также Рецепт 7.9 по использованию фильтра для чтения значений параметров запроса; рецепт 11.11 по использованию фильтра для отслеживания атрибутов сеанса; рецепт 18.3 по использованию фильтра для модификации запроса; рецепты 19.1-19.4 по отображению фильтров на web-компоненты; рецепт 19.5 по настройке параметров инициализации для фильтра; рецепт 19.7 по фильтрации HttpServletResponse (ответа); рецепт 19.8 по использованию фильтров с RequestDispatcher; рецепт 19.9 по использованию фильтров для проверки параметров запроса; рецепт 19.10 по использованию фильтров для запрета запросов с определенных IP-адресов. 19.7 Фильтрация НТТР-ответа Задача Требуется с помощью фильтра изменить ответ в процессе движения запроса клиента к сервлету назначения. Решение В методе doFilter () фильтра измените объект javax.s,ervlet .ServletRe- sponse ,(ответ), обернув его вашим собственным Ьбъектом. Затем передайте обернутый ответ в качестве параметра методу FilterChain.doFilter (). Обсуждение Ниже приведены шаги по изменению ответа в фильтре, а также класс-обертка. 1. .Создайте Java-класс, расширяющий класс j avax. servlet. http. HttpServlet- ResponseWrapper. 2. Поместите этот класс, включая все каталоги, связанные с пакетом, в каталог WEB- INF/classes. 3. Используйте в фильтре класс-обертку для обертывания объекта ServletResponse (ответа), передающегося в метод doFilter (} фильтра в качестве параметра. 4. Вызовите метод chain.doFilter(), передав ему в качестве параметра этот обернутый ответ. В примере 19.9 показан Java-класс, который мы будем использовать для обертыва- ния объекта ServletResponse (ответ). 456 | Глава 19. Фильтрация запросов и ответов
* <’ Если вы собираетесь внести в ответ лишь незначительные изменения, не стоит * •. связываться с использованием класса HttpServletResponseWrapper. к’ f Приведенный ниже код метода фильтра добавляет к ответу заголовок, а затем * вызывает метод chain.doFilter (), передавая ему в качестве параметра уже измененный ответ. if(response instanceof HttpServletResponse){ //приводим к HttpServletResponse чтобы //вызвать метод addHeader myHttpResponse = ((HttpServletResponse)response); myHttpResponse.addHeader("WWW-Authenticate", "BASIC realm-\"Admin\""); chain.doFi1ter(reques t,response); } Класс Responsewrapper содержит только каркас нового метода с именем getWe- bResource. Моя цель показать механизм обертывания ответа в фильтре и поэтому я сде- лал этот класс как можно более простым. Вызовы остальных методов, унаследованных от HttpServletResponse, делеги- руются в оборачиваемый объект, в этом и ’ заключается удобство расширения класса HttpServletResponseWrapper. Пример 19.9. Класс HttpServletResponseWrapper, предназначенный для использования в фильтре package com.jspservletcookbook; import javax.servlet.*; import javax.servlet.http.HttpServletResponseWrapper; import javax.servlet.http.HttpServletResponse; public class Responsewrapper extends HttpServletResponseWrapper{ public Responsewrapper(HttpServletResponse response){ super(response); } / public String getWebResource(String resourceName){ //Реализует метод возвращающий строку, //представляющую собой выход web-pecypca //См. Рецепт 13.5 return "resource"; //для компилятора... }// getWebResource } Фильтрация HTTP-ответа | 457
В примере 19.10 показан метод doFilter (), принадлежащий фильтру, использующему класс HttpServletResponseWrapper. Класс, расширяющий HttpServletResponseWrapper, необходимо поместить в каталог WEB-INF/classes, включая туда структуру каталога, соответствующую имени пакета. Пример 19.10. Метод doFilter() фильтра, использующего класс HttpServletResponse- Wrapper public void doFilter(ServletRequest request, ServletResponse response, Filterchain chain) throws java .'io. lOException, ServletException { if(response instanceof HttpServletResponse){ chain.doFilter(request, new Responsewrapper((HttpServletResponse)response)); } else { chain.doFilter(request,response); } }//doFilter Этот код вызывает метод chain. doFilter () и передает ему в качестве параметра обернутый ответ. В итоге web-pecypc. находящийся в конце цепочки фильтров, имеет доступ к измененному объекту, представляющему отве;т, и может вызывать дополни- тельный метод, объявленный в классе-обертке ответа. Вызовы остальных методов, унас- ледованных от объекта HttpServletResponse (ответ), например getWriterO или getOutputStream (), осуществляются через объект-обертку. См. также Рецепт 7.9 по использованию фильтра для чтения значений параметров запроса; рецепт 11.11 по использованию фильтра для отслеживания атрибутов сеанса; рецепт 18.3 по использованию фильтра для модификации запроса; рецепты 19.1-19.4 по отображению фильтров на web-компоненты; рецепт 19.5 по настройке параметров инициализации для фильтра; рецепт 19.6 по блокированию запроса; рецепт 19.8 по использованию фильтров с RequestDispatcher; рецепт 19.9 по использованию фильтров для проверки параметров запроса;, рецепт 19.10 по использованию фильтров для запрета запросов с определенных IP-адресов. 458 | Глава 19. Фильтрация запросов и ответов
19.8 Использование фильтров с объектами RequestDispatcher Задача Вы хотите применить фильтр к сервлету, чья выходная информация включается в другой сервлет. Решение Для включения выходной информации от сервлета используйте объект j avax. serv- let .RequestDispatcher. Настройте фильтр в web.xml, Включив туда элемент dis- patcher, содержащий значение «INCLUDE» (только для API сервлетов версии 2.4 и выше!). Обсуждение В API сервлетов версии 2.4 появился новый трюк, предназначенный для работы с дис- петчерами запросов (RequestDispatcher). С помощью элемента filter-mapping в дескрипторе запросов вы можете указать, что фильтр, применяемый к сервлету, - есть часть действия диспетчера запросов по включению (include) или перенаправлению (forward). В примере 19.11 приведен файл web.xml с настройкой фильтра. Пример 19.11. Применение фильтра к сервлету, при использовании диспетчера запросов ( Requ estDispatch er) <?xml versions"1.О" encodings"ISO-8859-1"?> <web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation= "http://java.sun.com/xml/ns/j 2 ее http://j^va.sun.com/xml/ns/j 2ee/web-app_2_4.xsd" versions"2.4"> «filter» <filter-name>LogFilter«/filter-name» <filter-class>com.jspservletcookbook.LogFilter«/filter-class» «/filter» I «filter-mapping» <filter-name»LogFilter«/filter-name» <url -pattern» / reguestheaders < /url -pat t em» «dispatcher>REQUEST«/dispatcher» <dispatcher>TNCLUDE</dispatcher» </filter-mapping» Использование фильтров с объектами RequestDispatcher | 459
В этом примере элементы dispatcher указывают, что фильтр LogFilter приме- няется к запросам, адресуемым на путь к сервлету /requestheaders, а также к любым дис- петчерам запросов, включающим выходную информацию от сервлета, отображенного на путь /requestheaders. — ------- ’ ’ Аналогично, если вы хотите инициировать работу фильтра когда вы используете диспетчер запросов (RequestDispatcher) для перенаправления запроса |]Л* Другому компоненту, используйте в элементе dispatcher значение FORWARD. <fi1ter-mapping> <fi1ter-name>LogFi1ter</fi1ter-name> <url-pattern>/requestheaders</url-pattern>. <dispatcher>REQUEST</dispatcher> <dispatcher>FORWARD</dispatcher> </fi1ter-mapping> В примере 19.12 приведен метод doGet сервлета, создающий диспетчер запросов для пути /requestheaders. Этот код включает выходную информацию сервлета, представ- ленного данным путем к сервлету. Однако вследствие конфигурации, заданной в web.xml в примере 19.11, web-контейнер применяет фильтр LogFilter до того, как сервлет, отображенный на путь /requestlieaders, будет выполняться. Пример 19.12. Сервлет, включающий выход другого сервлета, при включении происходит запуск фильтра public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.lOException { 7* Выход сервлета, отображенного на путь "/requestheaders" будет включен в выход данного сервлета, но перед тем, как запрос, будет послан сервлету '7гequestheaders", он проходит через фильтр LogFilter */ RequestDispatcher dispatch = request.getRequestDispatcher( "/requestheaders"); dispatch.include(request,response); ) Процесс работы фильтров и диспетчеров запросов проиллюстрирован на рис. 19.2. На рис. 19.2 web-клиент посылает запрос сервлету с путем /home/servletl, где Люте - путь к контексту. Компонент servletl использует диспетчер запросов (ReguestDis- patcher) для включения в свою выходную информацию выходной информации сервлета servlet2. В соответствии с элементом filter-mapping в web.xml, любой запрос Kservlet2, включая действие диспетчера запросов по включению генерируемого этим сервлетом выхода, должен сначала пройти через фильтр LogFilter. Этот фильтр настроен с помощью элемента filter в web.xml. (Эта конфигурация не показана на рис. 19.2; см. пример 19.11.) 460 | Глава 19. Фильтрация запросов и ответов
web.xml <filter-mapplng> <filter-name>LogFilter</filter-name> <url-pattern>/servlet2</url-pattern> <dispatcher>REQUEST</dispatcher> <dispatcher>INCLUDE</dispatcher> </filter-mapping> Puc. 19.2. Фильтр вмешивается перед тем, как сервлет включит выходную информацию от другого сервлета Такой вид настройки диспетчера запросов поддерживается лишь начиная с версии 2.4 АИ.сервлетов. ! См. также Главу 6 по включению содержимого с помощью диспетчера запросов; рецепт 7.9 по использованию фильтра для чтения значений параметров запроса; рецепт 11.11 по использованию фильтра тля отслеживания атрибутов сеанса; рецепт 18.3 по использо- ванию фильтра для модификации запроса; рецепты 19.1-19.4 по отображению фильтров на web-компоненты; рецепт 19.5 по настройке параметров инициализации для фильтра; рецепт 19.6 по блокированию запроса; рецепт 19.7 по фильтрации HTTP-ответа; рецепт 19.9 по использованию фильтров для проверки параметров, запроса; рецепт 19.10 по использо- ванию фильтров для запрета запросов с определенных IP-адресов. Использование фильтров с объектами RequestDispatcher | 461
19.9 Проверка параметров формы с помощью фильтра Задача Вы хотите использовать фильтр для проверки значений, которые пользователь ввЬл в форму. Решение С Помощью дескриптора развертывания отобразите фильтр на сервлет или JSP, куда передаются данные из формы. Обсуждение В проверке корректности значений, введенных пользователем в поля HTML-формы, фильтры являются альтернативой JavaScript и другим языкам для программ, выполняе- мых на стороне сервера. Фильтр, приведенный в этом рецепте, осуществляет базовую проверку параметров запроса на null или пустую строку. В примере 19.13 показана страница JSP, содержащая HTML-форму. Эта страница включает ряд JSTL-тегОв, которые заполняют текстовые поля корректными значениями, введенными при предыдущей попытке заполнения, если форма возвращается пользова- телю на Исправление ошибок. Как правило, пользователь заполняет большую часть полей правильно и допускает ошибку лишь в одном-двух из них. И не стоит заставлять его заново заполнять абсолютно все поля. Пример 19.13. Страница JSP, содержащая форму для заполнения пользователями <%& taglib uri="http://java.sun.com/jstl/core" prefix»"с" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitibnal//EN"> <html> <head> ,<title>Personal Information</title> </head> <body bgcolor="#ffffff"> <c:if test«"${! (empty errorMsg)}"> <font color="red"> <c:out value»"${errorMsg}"/> </font> </csif> <h2>Please enter your name and email address</h2> <table> 462 | Глава 19.' Фильтрация запросов и ответов
<fопц action»n/home/thanks.j sp"> <trxtd valign="top">First name: </td> <td valign="top"> <input types"text" name«"first" size="15" valuer: '<c:out value="${first}" />*> </td> <td valign="top">Middle initial: </td> 1 <td valign="top"> <input type="text" name="middle" size="2" values •<c:out value="${middle}"/>•> </td> </tr> <tr> 4 <td valign="top">Last name: </td> <td valign="top"> <input type="text" name="last" size»"20" values <c:out value»"${last}"/>*> </td></tr> <tr> <td valign="top">Your email: </td> <td v^lign='e top"> <input type-"text" name®"email" size="20" value" '<csout value»"${email}"/>'> </td></tr> ctrxtd valign="top"xinput type="submit" value="Submit"> </td> < tdx / tdx / tr> ** </table> </body> </html> Когда пользователь нажимает кнопку подтверждения ввода в форму из примера 19.13, браузер посылает информацию, введенную форму, JSP-странице thanksjsp, в соответст- вии с URL, заданным в атрибуте action тега form. В дескрипторе развертывания на URL thanks.jsp отображен фильтр, код которого приведен в примере 19.14. Этот фильтр предна- значен для проверки значений, введенных в поля формы, чтобы удостовериться, что поль- зователь не оставил какие-то из полей незаполненными, в последнем случае пользователь снова возвращается к форме (с именем fomtjsp). Проверка параметров формы с помощью фильтра | 463
Помните, что конструктор любого фильтра не должен иметь параметров. Пример 19.14. Филыпр, проверяющий значения параметров package coni. j spservletcookbook; import java.io.lOException; import java.util.Enumeration; import j avax.servlet.*; import javax.servlet.http.*; , public class CheckFilter implements Filter { private FilterConfig config; public CheckFilter() {} public void init(FilterConfig filterConfig) throws ServletException { this.config = filterConfig; } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws lOException, ServletException { //Получаем имена всех параметров, связанных с полями формы. Enumeration params « request.getParameterNamesO; boolean rojected false; //Цикл по <зсем параметрам; если какой-то из них пуст //вызываем метод 'reject' while (params .hasMoreElements ()) { if (isEmpty( request.getParameter( (String) params. nextElement()))){ rejected true; I reject(request,response); \ }//if }//while //Если все хорошо, 7/ передаем запрос по назначению if (1 rejected) chain.doFilter(request,response); )// doFilter private boolean isEmpty(String param){ 464 | Глава 19. Фильтрация запросов и ответов
if (param.=« null || param.length() < 1){ return true; } return false; } private void reject(ServletRequest request, ServletResponse response) throws lOException, ServletException { //Создаем сообщение об.ошибке; сохраняем его в атрибуте запроса request.setAttribute("errorMsg", "Please make sure to provide a valid value for all of the text "+ "fields."); Enumeration params a reduest.cetParamsterNames(); String paramN null; //Создаем атрибуты запроса, которые JSP с формой будет //использовать для заполнения полей, ранее заполненных правильно //Та»им образом, пользователю не придется повторно //заполнять всю форму целиком. while (params. hasMoreElements ()) { paranN (String) params.nextElement(); request.setAttribute( paramN, request.getParameter(paramN)); } //Используем RequestDispatcher чтобы вернуть пользователя к форме //для заполнения незаполненных ранее полей. RequestDispatcher dispatcher request. getRequestDispatcher (" /form, j sp"); di spatcher. forward (request , response); ' }//rxiject public void destroy(){ /‘вызывается перед тем, к£к экземпляр фильтра удаляется из обслуживания в - web-контейнере*/ } } Программные комментарии в примере 19.14 поясняют, что происходит в фильтре. Если какой-либо из параметров запроса пуст, фильтр возвращает, пользователя к форме, при этом на форме отображается сообщение об ошибке. В примере 19.15 показано, как фильтр CheckFilter отображен в web.xml. Если пользователь заполнил форму корректно, этот запрос посылается странице thanks.jsp без препятствий со стороны фильтра. Проверка параметров формы с помощью фильтра | 465
Пример 19.15. Фильтр CheckFil ter регистрируется и отображается на шаблон URL в web.xml <!— начало web.xml... —> <filter> <filter-name>CheckFilter</filter-name> <filter-class>com.jspservletcookbook.CheckFilter</filter-class>' </filter> < f i1ter-mapping> <filter-name>CheckFilter</filter-name> <url-pattem>/thanks. j sp</url-pattern> < /filter-mapping> < !— продолжение web.xml... —> На рис. 19.3 показана HTML-форма, которая была отослана заполненной лишь частично. Фильтр посылает форму обратно пользователю, сопровождая ее сообщением (выделенным красным шрифтом). Рис. 19.3. Фильтр направляет странице JSP сообщение об ошибке 466 | Глава 19. Фильтрация запросов и ответов
См. также Главу 6 по включению срдержимого с помощью диспетчера запросов; рецепт 19.8 по использованию фильтров с диспетчером запросов; рецепт 7.9 по использованию фильтра для чтения значений параметров запроса; рецепт 18.3 по использованию фильтра для модификации запроса; рецепты 19.1-19.4 по отображению фильтров на web-компо- ненйы; рецепт 19.5 по настройке параметров инициализации для фильтра; рецепт 19.6 по блокированию запроса; рецепт 19.7 по фильтрации НТТР-ответа. 19.10 Блокирование IP-адреса с помощью фильтра Задача ✓ Вы хотите использовать фильтр, проверяющий IP-адрес, связанный с запросом. Решение Используйте фильтр, вызывающий внутри своего метода doFilter () метод getRemoteAddr() класса HttpServletRequest и блокирующий запрос тем, что не вызывает метода chain. doFilter (). Обсуждение Фильтр в web-приложении обычно используется для проверки приемлемости запроса. Предположим, отдел безопасности вашей организации обнаружил, что определенный диа- пазон IP-адресов принадлежит недоброжелательно настроенным клиентам, и вы хотите отвечать на исходящие оггуда запросы НТТР-ютветом «403 Forbidden» (запрещено). Сервлет, приведенный в примере 19.16, блокирует любые запросы клиентов с адресов, начинающихся с «192.168». Пример 19.16 Фильтр для блокирования определенного диапазона адресов package com.j spservletcookbook; import j ava.io.lOException; inport java.util.StringTokenizer; import javax.servlet.*; import javax.servlet.http.*; public class IPFilter implements Filter { private FilterConfig config; Блокирование IP-адреса с помощью фильтра | 467
public final static String IP_RANGE "192.168"; public IPFilterO {} public void init(FilterConfig filterCopfig) throws ServletException { this.config - filterConfig; } public void doFilter(ServletRequest request, ServletResponse response, Filterchain chain)- throws lOException, ServletException { String ip request. getRemoteAddr (); HttpServletResponse httpResp = null; if (response instanceof HttpServletResponse) httpResp - (HttpServletResponse) response; //Разделяем IP-адрес на фрагменты, представляющие его отдельные байты StringTokenizer toke » new StringTokenizer(ip,"."); int dots 0; String bytel ""; String byte2 ""; String client » ""; 11 while (toke.hasMoreTokens()){ ++dots; //Данная лексема представляет первый байт if (dots == 1){ bytel toke.nextToken(); } else { f 11 Данная лексема представляет второй байт byte2 “ tоке.nextToken(); break;//only interested in first two bytes } }//while //Соединяем части IP-адреса клиента вместе, чтобы можно было // сравнить с запрещенным диапазоном, представленным //IPFilter.IP_RANGE client bytel+"."+byte2; //если IP-адрес клиента принадлежит запрещенному диапазону... 468 | Глава 19. Фильтрация запросов и ответов
if (IP_RANGE.equals(client)){ httpResp. sendError (HttpServletResponse. SC. .FORBIDDEN r "That means goodbye forever!" ); } else { //Клиент нормальный; посылаем запрос дальше chain.doFilter(request,response); } }// doFilter public void destroy(){ /* вызывается перед тем, как экземпляр фильтра удаляется из обслуживания в web-контейнере */ } } Фильтр получает IP-адрес клиента с помощью метода getRemoteAddr () объекта ServletRequest. Затем фильтр осуществляет разбор возвращенного методом адреса, чтобы определить, попадает ли он в диапазон «192.168». Если IP-адрес принадлежит этому диапазону, то код вызывает метод sendError () объекта HttpServletRe- sponse с кодом состояния HTTP «403 Forbidden». httpResp.sendError(HttpServletResponse.SC__FORBIDDEN, "That means goodbye forever!" ); Вызов этого метода эффективно, сокращает область перемещения запроса, предотвращая проникновение пользователя туда, куда он стремился. Если же IP-адрес приемлем, код вызывает метод chain. doFilter (); который передает объекты, представляющие запрос и ответ, дальше по цепочке фильтров. В данном случае, поскольку приложение не отображает на thanks.jsp никаких других фильтров, web-контейнер акти- визирует саму эту страницу. В примере 19.17 показано отображение данного фильтра в web.xml. Фильтр отображен на URL «/*», то есть на все запросы. Пример 19.17. Отображение для фильтра блокирующего IP-адреса <filter> < f i 1 ter-name>IPFi 11 er< / f i 1ter-name» , <filter-class>com.jspservletcookbook.IPFilter</filter-class> </filter> <filter-mapping» <filter-name>IPFilter</filter-name> <url -pattern» / * < /url -pattern» </filter-mapping» На рис. 19.4 показана страница, отображаемая web-браузером клиента, чей адрес блокируется. ( Блокирование IP-адреса с помощью фильтра | 469
Рис. 19.4. Фильтруемый IP-адрес получает сообщение с кодом состояния HTTP 403 См. также Главу 9 по обработке ошибок в web-приложении; рецепт 19.8 по использованию фильтров с диспетчером запросов; рецепт 18.3 по использованию фильтра для модифика- ции запроса; рецепты 19.1-19.4 по отображению фильтров на web-компоненты; рецепт 19.5 по настройке параметров инициализации для фильтра; рецепт 19.7 по фильтрации НТТР- ответа; рецепт 19.9 по проверке параметров формы с помощью фильтра. 470 | Глава 19. Фильтрация запросов и ответов
_________________________________________ГЛАВА 20 Работа с электронной почтой в сервлетах и JSP 20.0 Введение Данная глава описывает порядок использования программных интерфейсов Java- Mail и JAF (JavaBeans Activation Framework-фреймворк активации компонентов Java- Bean) для работы с электронной почтой в сервлетах. JavaMail предоставляет Java- классы, имеющие отношение к большинству аспектов, связанных с созданием, отправкой и получением электронной почты. JAF - это отдельный программный интерфейс для работы с типами данных и MIME-типами (Multipurpose Internet Mail Extension - многоцелевые расширения электронной почты в сети Интернет), который вы можете использовать при генерировании писем, например при создании вложений в виде файлов разных типов. Оба этих АЙ являются частью платформы J2EE (Java 2 Enterprise Edition). JavaMail моделирует систему электронной почты с помощью классов, представляющих сеанс почтовой связи (класс javax.mail.Session), банки сооб- щений (message stores) (класс javax. mail .Store), папки (класс javax. mail. Folder, например, для представления папки «Входящие»), почтовые сообщения (класс javax.mail .Message) и адреса электронной почты (класс javax.mail. internet. InternetAddress). К примеру, почтовое сообщение, подобно компо- ненту JavaBean, имеет методы-установщики для создания различных компонентов сообщения (методы setFromf), setRecipients (), setSubject () ит. д.). В следующих ниже рецептах показано, как управлять базовыми возможностями работы с почтовыми сообщениями в одном сервлете, а также как разделить зоны ответственности по работе с почтой и обработке HTTP-запросов между компонен- тами JavaBean и сервлетами. 471
20.1 Размещение пути к классам, имеющим отношение к электронной почте, в вашем пути к классам Задача Вы хотите в сервлете для работы с электронной почтой использовать javax.mail и другие необходимые пакеты. ' f Решение Скачайте ZIP-файлы, содержащие архивы mailjar и activationjar. Добавьте эти файлы в совместно используемый каталог JAR-файлов, чье содержимое загружается web-кон- тейнером. Если каталог такого типа недоступен, добавьте файлы mail.jar и activationjar в каталог WEB-INFAib вашего web-приложения. Обсуждение Если ваш путь к классам (classpath), используемый при компиляции сервлетов, уже включает путь к JAR-файлам, доступным в вашем web-контейнере (например, JAR-файлы из каталога соттоп/НЬ в Tomcat), проверьте, успешно ли компилируется сервлет, работающий с электронной почтой, например сервлет из примера 20.1. Если компилятор сообщает, чт(Гпакеты javax.mail и javax. mai 1. internet отсутствуют, вам необ- ходимо добавить в путь к классам необходимые JAR-файлы. См. рецепт 4.3 по использованию Ant для включения JAR-файлов Tomcat в ваш путь к классам. Скачайте компонент mail.jar, расположенный по адресу http://java.sun.coin/prod- ucts/javamail/. Скачиваемый файл представляет собой ZIP-архив, содержащий архив mail .jar. Этот файл включает пакеты, необходимые для работы с электронной почтой в сервлетах, например javax.mail и javax.mail. internet. После этого скачайте JAF, расположенный по адресу http://java.sun.com/products/javQ- beans/glasgow/jaf.html. Сервлеты могут использовать эти классы как часть пакета javax. activation для работы с различными типами данных, которые могут перено- ситься в составе почтовых сообщений в виде вложенных файлов. 472 | Глава 20. Работа с электронной почтой в сервлетах и JSP
С основными типами вложенных файлов можно работать, используя только * JavaMail API (без JAF), как это сделано в рецептах 20.5 л 20.6. Примеры * 7 ft* использования некоторых классов из пакета javax.activation для добавления к почтовому сообщению вложенных файлов приведены в рецепте 20.7. Добавьте архивы mail. jar и activation, jar в каталог WEB-INFAib своего web- приложения, чтобы сделать пакеты JavaMail и JAF доступными для сервлетов. См. также Страницу Sun Microsystems по JavaMail API: http://javasun.com/products/javamail/; страницу по JAF: http://javasun.com/products/javabeans/glasgow/jaf.html; рецепт 20.2 по отправке почтового сообщения из сервлета; рецепт 20.3 по отправке почтового сообще- ' ния с использованием JavaBean; рецепт 20.4 открывающий, как получить доступ к почтовому сообщению из сервлета; рецепт 20.5 по доступу к почтовому сообщению ' с использованием JavaBean; рецепт 20.6 по обработке вложений в сервлете; рецепт 20.7 по добавлению вложений к почтовому сообщению; рецепт 20.8 по чтению заголовков почтового сообщения. 20.2 Отправка почтового сообщения из сервлета Задача Отправить из сервлета почтовое сообщение. Решение В верхней части исходного кода сервлета поместите операторы импорта пакетов javax.mail и javax.mai 1.internet. Создайте метод sendMessage() (метод можно назвать и по-другому), который может быть вызван из методов doGet ( ) или' doPost () сервлета. Обсуждение Метод sendMessage () из примера 20.1, используя JavaMail API, подключается к почтовому серверу, конструирует почтовое сообщение, а затем отправляет это сообще- ние одному или нескольким адресатам. Данный сервлет получает отдельные компоненты почтового сообщения - адрес получателя, адрес отправителя, поле темы и содержимое тела почтового сообщения - из параметров запроса. Сервлет может обрабатывать данные формы, заполненной клиентом с помощью web-браузера. Отправка почтового сообщения из сервлета | 473
Ter form может выглядеть следующим образом. <form method="POST" action= "/home/servlet/com.j spservletcookbook.EmailServlet"> Сервлет из примера 20.1 вызывает метод sendMessagef) из метода doPost(). Параметры метода sendMessagef) включают'части почтового сообщения: сервер SMTP, получателя сообщения (переменная to), адрес отправителя, тему сообщения и содержимое сообщения. Пример 20.1. Сервлет отправляет почтовое сообщение, создаваемое на основе значений параметров запроса package com.j spservletcookbook; inport j ava.io.lOException; import j ava.io.Printwriter; import java.util.Properties; import javax.mail.*; inport javax.mail.internet.*; inport javax.servlet.*; inport j avax.servlet.http.*; public class EmailServlet extends HttpServlet { / //значение по умолчанию адреса почтового сервера //на случай, если пользователь не укажет такого адреса private final static String DEFAULT—SERVER = "mail.attbi.com"; public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.lOException { , //получаем значения компонентов сообщения //из параметров запроса String smtpServ = request.getParameter("smtp"); if (smtpServ « null || smtpServ.equals("")) smtpServ DEFAULT-SERVER; String from request.getParameter("from"); String to » request.getParameter("to"); String subject request.getParameter("subject"); String emailContent request.getParameter("emailcontent"); response.setContentType("text/html"); java.io.Printwriter out = response.getWriter(); out.printlnf f "<htmlxheadxtitle>Email message sender</titlex/headxbody>") ; try { 474 | Глава 20. Работа с электронной почтой в сервлетах и JSP
sendMessage(smtpServ, to, from, subject, emailcontent); } catch (Exception e)- { throw new ServletException(e.getMessage()); } out.printIn( "<h2>The message was sent successfully</h2>"); out. printin (" </bodyx/html> "•) ; } //doPost private void sendMessage(String smtpServer, String to. String from. String subject,String emailcontent) throws Exception { Properties properties «= System.getProperties() ; //заполняем объект 'Properties1 адресом почтового сервера //чтобы объект по умолчанию типа 'Session* //' мог его использовать. properties.put("mail.smtp.host", smtpServer); Session session Session.getDefaultInstance(properties); Message mailMsg « new MimeMessage(session);//новое почтовое сообщение IntemetAddress[] addresses » null; try { if (to != null) { //выбрасываем исключение 'AddressException* //если адрес получателя //нарушает синтаксис, установленный в RFC822 addresses я InternetAddress.parse(to, false); mailMsg.setRecipients(Message.RecipiontTypo.TO, addresses); } else { throw new MessagingException( "The mail message requires a 'To* address."); } •/ if (from != null) { mailMsg.setFrom(new TntemetAddress (from)); } else { throw new MessagingException( "The mail message requires a valid 'From' address."); } if (subject I® null) mailMsg.setSubject(subject); Отправка почтового сообщения из сервлета | 475
if (emailcontent !ч null) mailMsg.setText(emailcontent); //Наконец посылаем сообщение; если для какого-либо из получателей // задан некорректный адрес, выбрасывается исключение //* SendFailedException' Transport. send(mailMsg); } catch (Exception exc) { throw exc; } }//sendMessage public void doGet(HttpServletRequest reQuest, HttpServletResponse response) throws ServletException, java.io.lOException '{ 4 //doGet() вызывает dORost()... doPost(request, response); }//doGet }//EmailServlet Данный сервлет взаимодействует с почтовым сервером следующим образом. 1. В коде создается объект javax.mail. Session, который содержит различные значения, принятые по умолчанию, а также значения 'свойств (например mail, smtp.host), все это используется другими объектами JavaMail. Вы можете исполь- зовать один объект Session всеми компонентами приложения. 2. Код создает объект MimeMessage (передавая конструктору в качестве параметра объект Session). 3. Затем сервлет заполняет MimeMessage различными компонентами почтового сооб- щения, например адресами отправителя и получателя, темой, а также содержимым сообщения. 4. Код отправляет почтовое сообщение испбльзуя статический метод send () класса j avax. mai 1. Transport. См. также Страницу Sun Microsystems по JavaMail API: http://java.sun.com/products/javamail/', страницу no JAF: http://java.sun.com/products/javabeans/glasgow/jqf.html', рецепт 20.1 по добавлению к web-приложению JAR-файлов, связанных с JavaMail; рецепт 20.3 по отправке почтового сообщения с использованием JavaBean; рецепт 20.4 открывающий, как получить доступ к почтовому сообщению из сервлета; рецепт 20.5 по доступу к почтовому сообщению с использованием JavaBean; рецепт 20.6 по обработке вложений в сервлете; рецепт 20.7 по добавлению вложений к почтовому сообщению; рецепт 20.8 по чтению заголовков почтового сообщения. 476 | Глава 20. Работа с электронной почтой в сервлетах и JSP
20.3 Отправка из сервлета почтового сообщения с использованием компонента JavaBean Задача Вы хотите для отправки сообщения из сервлета использовать компонент JavaBean (вспомогательный класс). Решение Создайте Java-класс реализующий метод sendMessage () (имя метода выбрано мною произвольно) для конструирования почтового сообщения и его отправки. Сохраните этот класс в каталоге WEB-INF/classes своего web-приложения, включив туда все каталоги, относящиеся к пакету класса. Обсуждение Можно отделить функциональность, связанную с обработкой HTTP-запросов, от функ- циональности, связанной с обработкой почтовых сообщений, инкапсулировав их в отдель- ных классах. Здесь мы рассмотрим компонент JavaBean, обеспечивающий основные возможности по отправке почтовых сообщений. В рецептах 20.5 и 20.6 показаны компоненты JavaBean, предназначенные для доступа к почтовому сообщению и обработки вложений. Компонент JavaBean. 4 * Л* выполняющий все виды работ с электронной почтой, получился бы слишком большим, и разумнее разделить эти задачи по нескольким разным компонентам JavaBean (вспомогательным классам), которые можно использовать в сервлетах. Создайте компонент JavaBean и сохраните его в каталоге WEB-INF/classes. В примере 20.3 показан метод doGet () сервлета, в котором компонент JavaBean использу- ется для отправки почты. Сам класс компонента JavaBean приведен в примере 20.2. Разница между методом sendMessagef) из примера 20.1 и таким же методом из примера 20.2 заключается в способе, которым компонент JavaBean получает различные части почтового сообщения, такие, как адрес получателя. Компонент JavaBean хранит эти части в виде свойств и использует для задания значений свойств методы-установщики (setter). С другой стороны, в примере 20.1 для задания этих значений используются параметры запроса и аргументы метода. Отправка из сервлета почтового сообщения с использованием компонента JavaBean | 477
Пример 20.2. Компонент JavaBean для отправки электронной почты package com.jspservletcookbook; \ import,java.io.lOException; . import java.io.Printwriter;, import j ava.util.Properties; import javax.mail.*; import javax.mail.internet.*; public class EmailBean { public EmailBean(){} //set defaults private final static String DEFAULT_CONTENT = "Unknown content"; private final static String DEFAULT_SUBJECT= "Unknown subject"; private static String DEFAULT_SERVER = null; private static String DEFAULTJTO = null; x private static String DEFAULT_FROM = null; static{ //устанавливаем значения по умолчанию //из файла свойств java.util.ResourceBundle bundle = j ava.util.ResourceBundle. getBundle("com.jspservletcookbook.mailDefaults"); ' DEFAULT_SERVER = bundle.getString("DEFAULT_SERVER"); DEFAULT_TO'•= bundle.getString("DEFAULT_TO"); DEFAULTJFROM = bundle.getString("DEFAULT_FROM"); }//static //Свойства компонента JavaBean private String smtpHost; private String to; private String from; private String content; private String subject; public void sendMessage() throws Exception { Properties properties System.getProperties(); //заполняем объект 'Properties* адресом почтового сервера //чтобы объект по умолчанию типа * Session* //мог его использовать. properties. put ("mail. smtp. host" , smtpHost); Session session Session.getDef aultInstance (properties); Message mailMsg « new MimeMessage(session);//a new email message 478 | Глава 20. Работа с электронной почтой в сервлетах и JSP
InternetAddress[] addressee « null; try { if (to •!= null) { 11 выбрасываем исключение ‘AddressException’ // если адрес получателя // нарушает синтаксис, установленный в RFC822 addresses InternetAddress.parse(to, false); mailMsg.setRecipients(Message.RecipientType.TO, addresses); } else { throw new MessagingException( "The mail message requires a 'To' address."); } if (from != null) { । mailMsg. setFrom(new IntemetAddress (from)); ) else { throw new MessagingException( "The mail message requires a valid 'From' address."); } if (subject l.« null) mailMsg.setSubject(subject); if (content Is null) mailMsg.setText(content); // Наконец посылаем сообщение; если для какого-либо из получателей // задан некорректный адрес, выбрасывается исключение //1SendFailedException* Transport. send (mailMsg); } catch (Exception exc) {• throw exc; } }//sendMessage //Все методы-установщики имеют одинаковую структуру, * //поэтому рассмотрим только два из них public void setSmtpHost(String host){ if (check(host)){ this.smtpHost = host; } else { Отправка из сервлета почтового сообщения с Использованием компонента JavaBean | 479
this.smtpHost = DEFAULT_SERVER; } } / /setSmtpHost public void setTo(String to){ if (check(to)){ this.to = to; } else { 1 this.to = DEFAULT_TO; } }//setTo /* — He похаваны: методы-установщики для' from', 'subject1, и 'content', которые имеют такую же структуру — */ private boolean check(String value){ if(value == null || value.equals("")) return false; return true; }//check } В примере 20.3 установка значений свойств по умолчанию для переменных, таких, как имя сервера, производится с использованием класса java.util.ResourceBundle. Файл свойств mailDefaults.properties хранится в каталоге WEB-INF/classes/coin/jspservlet- cookbook. Вот пример содержимого этого файла. DEFAULT_SERVER=smtp•Comcast.net DEFAULT_TO=author@j spservletcookbook.com DEFAULT_FROM=author@j spservletcookbook.com Компонент JavaBean позволяет задавать значения разных частей почтового сообщения р помощью следующих методов: setSmtpHost(), IsetTof), setFrom(), setSub- j ect () и setcontent () (в примере 20.3 показаны не все из них). Сервлет из примера 20.3 создает экземпляр класса EmailBean, задает значения разных частей почтового сообщения и затем вызывает метод sendMessage (). В примере 20.3 приведен только метод doGet (). Метод doPost () сервлета может вызывать метод doGet () следующим образом: doGet (request, response). Пример 2Q.3. Сервлет, использующий компонент JavaBean для отправки почтового сообщения public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.lOException { response.setContentType("text/html"); java.io.Printwriter out = response.getWriter(); 480 | Глава 20. Работа с электронной почтой в сервлетах и JSP т - , • \ -......
out.println( *’<htrnl><head><title>Email message sender</titlex/head><body>"); EmailBean emailer ж new EmailBean(); emailer.setSmtpHost("mail.attbi.can"); emailer.setTo("myfriend@yahoo.con"); emailer.setFrom("author@jspservletcookbook.com"); emailer.setSubject("This is not spam!”); emailer.setcontent("Please call ASAP."); try{ emailer.sendMessage(); } catch (Exception e) {throw new ServletException(e);} out .println ("</bodyx/html>"); i } //doGet Компонент JavaBean может выбрасывать исключение MessagingExceptions в случае, если, например, адрес получателя, заданный пользователем, имеет неверный формат. Компонент повторно выбрасывает любое исключение, пойманное им в ходе созда- ния и отправки почтового сообщения. См. также Рецепт 20.4 открывающий, как получить доступ к почтовому сообщению из сервлета; рецепт 20.5 по доступу к почтовому сообщению с использованием JavaBean; рецепт 20.6 по обработке вложений в сервлете; рецепт 20.7 по добавлению вложений к почтовому сообщению; рецепт 20.8 по чтению заголовков почтового сообщения, 20.4 Доступ к почтовому сообщению из сервлета Задача Требуется из сервлета получить доступ к почтовому сообщению и отобразить его содержание. Решение Используйте JavaMail API и создайте метод сервлета, обрабатывающий и отображающий содержание почтовых сообщений. 16-1545 Доступ к почтовому сообщению из сервлета | 481
Обсуждение Процесс получения почтовых сообщений с помощью JavaMail и сервлета достаточно Прост. 1. В верхней части исходного кода сервлета импортируйте пакеты javax.mail и javax.mail. internet.' 2. В методе, получающем почту, создайте объект класса javax.mail.Session для работы с данным почтовым сеансом. 3. Получите от объекта Session объект javax.mail . Store (хранилище почтовых сообщений), который будет представлять учетную запись для работы с сервером вхо- дящих почтовых сообщений POP3. . , 4. Подключитесь к хранилищу (Store) используя метод connect (String host, String user, String password) объекта Store (хранилища) (существует несколько перегруженных версий этого метода). Класс Store разработан для аутен- тификации пользователя и подключения к почтовому серверу. 5. С помощью объекта Store (хранилища почтовых сообщений) получите доступ к папке INBOX (входящие). 6. Получите все сообщения, хранящиеся в этой папке, в виде массива типа Message [ ] и затем делайте с каждым из этих сообщений все, что угодно, организовав цикл по этому массиву. Сервлет в примере 20.4 получает почтовые сообщения, вызывая свой метод han- dleMessages () в методе doGet (). Пример 20.4. Сервлет, получающий почтовые сообщения package com.j spservletcookbook; import java.io.lOException; import java.io.Printwriter; import java. util, Properties.; F import javax.mail.*/ import javax .mail. internet. * ; import javax.servlet.*; inport javax.servlet.http.*; public class MailAccessor extends HttpServlet { private final static String DEFAULT_SERVER = "mail.attbi.com*; public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, j ava.io.lOException { response.setContentType(•text/html"); java.io.Printwriter out = response.getWriter(); out.printlnl"<html><head><title>Email Reader</title></head><body>"); 482 | Глава 20. Работа e электронной почтой в сервлетах и JSP
//Данный метод получает все почтовые сообщения и отображает //их содержимое. handleMessages(request, out); out.printIn(*</body></html>"); )//doGet private void handleMessages(HttpServletRequest request, Printwriter out) throws lOException, ServletException { //Получаем информацию для аутентификации пользователя на РОР-сервере, //используемом для получения почты. Эта информация хранится //в объекте HttpSession. HttpSession httpSession request.getSessionf); String user (String) httpSession.getAttribute("user"); String password (String) httpSession.getAttributt("pass")г String popAddr (String) httpSession.getAttribute("pop"); Store popStore null; Folder folder null; if (I check(popAddr)) popAddr MailAccessor.DEFAULT_SERVER; try { //проверка имени и пароля пользователя на null if ((I check(user)) || (1 check(password))) throw new ServletException ( "A valid username and password is required."); Properties properties System. getPropertiesO; //Получаем объект'Session*(сеанс) co значениями no умолчанию //для данного взаимодействия с почтовым сервером Session session Seesion.getDefaultInstance(properties); //Получаем хранилище сообщений(store) (то есть, учетную запись //для работы с сервером входящих сообщений POP3) //от объекта Session. popStore session.getstore("pop3"); //подключаемся к хранилищу, предъявляя имя и пароль пользователя popStore.connect(popAddr, user, password); //Получаем доступ к папке INBOX (входящие), открываем ее //и извлекаем оттуда все почтовые сообщения 1 folder popStore.getFolder("INBOX"); if (I folder.exists()) throw new ServletExcept ion ( < "An 'INBOX* folder does not exist for the user."); Доступ к почтовому сообщению из сервлета | 483 16*
folder, open (Folder. READ-ONLY) ; Message[] messages folder.getMessagesQг int msgLen messages.length; if (msgLen 0)f out .println( c < .4 "<h2>The INBOX folder doesn't contain any email "+ "messages.</h2>”);} • .. -:.Vf i .f\ . . . ' • //щт каждого полученного сообщения, с помощью //метода displayMessage, отображаем //его содержание for (int i 0; i < msgLen; i++H displayMessage (messages [i]-, out); out.printIn("<br /xbr />"); } } catch (Exception exc) { out.println( "<h2>Sorry, an error -occurred while accessing the email" + • messages.</h2>"); out.println(exc.toString()) ; ) finally { try{ // блоке finally закрываем папку и хранилища //если в качестве параметра передать 'true', //сообщения, которые надо удалить, будут удалены из папки if (folder I* null) folder.close(false); if (popstore ! null) popStore.close(); } catch (Exception e) { } ) J//printMessages private void displayMessage(Message msg,.Printwriter out) throws MessagingException, lOException{ if (msg ! null && msg.getContent() instanceof String){ if (msg.getFrom()[0] instanceof InternetAddress){ out.println( "Massage received from: " + ((InternetAddress)msg.getFrom()[0]).getAddress() + "<br />"); 484 | Глава 20. Работа с электронной почтой в сервлетах и JSP
out. print In ("Message content type: * + msg.getContentTypeO + <br />")> out.printIn("Message body content: " * (String) msg.getContent()); } elee{ ’ ' out.println( j "<h2>The received email message was not of a text " ♦ "content type.</h2>"); }//outer if }//displayMessage private boolean check(String value){ if(value == null || value.equals(•’)) return false; return true; } I /check } Метод displayMessage () для каждого сообщения отображает адрес отправителя, тип содержимого сообщения (например MIME-тип text/plairi) и содержимое сообщения. Простейшее почтовое Сообщение, содержащее только заголовки и текстовое послание, можно получить в виде строки (String), вызвав метод Message.getContent(). Получение адреса отправителя несколько залутайнее. out.printing"Message received from: " + ((InternetAddress)msg.getFrdm()[ 0 ]).getAddress() +<br />"); Метод Message.getFrom() возвращает массив объектов класса javax.mail. Address. Вышеприведенный код предназначен для получения первого адреса электронной почты, поскольку письмо обычно отправляется одной стороной другой стороне (не считая, конечно, разных спамеров). Код получает первый элемент массива, приводит полученное значение к типу javax.mail. InternetAddress и затем вызывает метод getAddress ()этого объ- екта, который возвращает адрес электронной почты в виде строки. На рис. 20.1 показано отображение в браузере выходной информации обсуждаемого сервлета. Поскольку сервлет получает аутентифицирующую информацию из атрибутов сеанса, первый запрос обращен к странице JSP; устанавливающей атрибуты сеанса. Затем эта страница перенаправляет запрос сервлету Mail Accessor. Сервлет отображает каж- дое полученное почтовое сообщение, отделяя его от предыдущего двумя пустыми строками. Иными словами, информация, которую сервлет отображает о каждом письме, включает адрес отправителя, тип содержимого письма и сам текст сообщения. Доступ к почтовому сообщению из сервлета | 485
Message received from: brucewperry@attbi.com Message content type: text/plain; charset="iso-8,859-1 Message body content This is Message 1 text/ Message received from: brucewperry@attbi.com Message content type: text/plain; charset-'iso-8859-1 Message body content This is message 2 text. k£ Local intranet Puc. 20.1. Сервлет получает и отображает два почтовых сообщения См. также Главу 16 по установке атрибутов сеанса; главу 25 по доступу к JNDI-объекту javax. mail.Session на сервере BEA WebLogic; страницу Sun Microsystems no JavaMail API: http://java.sun.coni/products/javamail/; рецепт 20.1 по добавлению к web-приложению JAR- файлов, связанных с JavaMail; рецепт 20.2 по отправке почтового сообщения с помощью сервлета; рецепт 20.3 по отправке почтового сообщения с использованием JavaBean; рецепт 20.5 по доступу к почтовому сообщению с использованием JavaBean; рецепт 20.6 по обработке вложений в сервлете; рецепт 20.7 по добавлению вложений к почтовому сообщению; рецепт 20.8 по чтению заголовков почтового сообщения. 486 | Глава 20. Работа с электронной почтой в сервлетах и JSP
20.5 Доступ из сервлета к электронной почте с использованием компонента JavaBean Задача Для доступа к почтовым сообщениям и отображения их содержимого вы хотите использовать компонент JavaBean (вспомогательный класс). , ‘ ' - I Решение | Добавьте в класс компонента JavaBean, определенный в примере 20.2, методы han- - 1 dleMessages () и displayMessage () из примера 20.4, Затем обращайтесь к этому компоненту JavaBean из метода doGet () или doPost () сервлета. Обсуждение В предыдущей редакции (в примере 20.2) компонент JavaBean EmailBean содержал метод sendMessage () и несколько методов-установщиков (например, метод setSmt*- pHost (String host)). Если к этому же классу добавить методы handleMessages () и displayMessage () из примера 20.4, данный компонент можно использовать и для отправки, и для получения почты. - * ’ V/ > Необходимо слегка изменить код метода handleMessages () из примера 20.4, включив в него имя класса EmailBean. ЛР-' //статическая ссылка .на постоянное значение if,(! check(popAddr)) popAddr = EmailBean.DEFAULT_SERVER; Однако в результате добавления двух новых методов класс EmailBean заметно увеличится в размере, вместо этого вы можете создать два разных компонента JavaBean - один для отправки почты, а другой для получения. Сервлет из примера 20.5 создает и использует экземпляр компонента JavaBean, созданный для работы с электронной почтой. Класс, реализующий этот компонент JavaBean, необходимо сохранить в ката- логе WEB-INF/classes или в виде JAR-файла в каталоге WEB-INFAib. В примере 20.6 приведен также компонент JavaBean, в котором определены методы handleMessages () и displayMessage () для работы с вложениями f в почтовые сообщения. Доступ из сервлета к электронной почтё с использованием компонента JavaBean | 487
Пример 20.5. Сервлет, использующий компонент JavaBean для получения почтовых сообщений public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.lOException { response.setContentType("text/html“); java.io.Printwriter out = response.getWriter(); out.println( "<htmlxheadxtitle>Email message sender</titlex/headxbody>") ; EmailBean emailer = new EmailBeanO; emailer.setSmtpHost("mail.attbi.com"); „ emailer.handleMessages(request,out); , out»println (“ < /bodyx/html>"); }//doGet i См. также Страницу Sun Microsystems no JavaMail API: http://java.sun.com/products/javcunail/; рецепт 20.1 по добавлению к web-приложению JAR-файлов, связанных c JavaMail; рецепт 20.2 по отправке почтового сообщения с помощью сервлета; рецепт 20.3 по отправке почтового сообщения с использованием JavaBean; рецепт 20.4 по доступу к почтовому сообщению из сервлета; рецепт 20.6 по обработке вложений в сервлете; рецепт 20.7 по добавлению вложений к почтовому сообщению; рецепт 20.8 по чтению заголовков почтового сообщения. 20.6 Работа с вложениями в почтовые сообщения, полученные сервлетом Задача Вы хотите из сервлета прочитать почтовое сообщение и сохранить все вложенные в него файлы. Решение Для сохранения информации из вложенных файлов (представленной объектами Inputstream (входные потоки) в заданной папке используйте JavaMail API и специ- альный компонент JavaBean 488 | Глава 20. Работа с электронной почтой в сервлетах и JSP »
Обсуждение Доступ к электронной почте обычно включает в себя аутентификацию пользователя с использованием учетной записи- POP, а затем установление соединения с почтовым сервером и выгрузку всех почтовых сообщений. Компонент, приведенный в примере 20.6, для выгрузки массива сообщений (Messages) из некоторой учетной записи пользователя, использует классы Session, Store, Folder и Message из JavaMail API. Что отличает его от сервлета из рецепта 20.4, который работает только с сообщениями (Messages), чье содержимое имеет тип String (значение именно этого типа возвращает метод Message. getContent ()). Если содержимое сообщения (Message) имеет тип Multipart (из нескольких частей), то процесс обработки вложений становится похож на очистку лука - он включает еще больше кода. Код взаимодействия с электронной почтой выделен в примере 20.6 в отдельный компонент JavaBean, который можно использовать в сервлете. Метод dis- playMessage () этого компонента проверяет содержимое каждого сообщения (Mes- sage). Если тип содержимого Multipart, то код проверяет отдельно каждую из частей (BodyPart) такого составного сообщения. к Представляйте себе тип Multipart как контейнер. Этот контейнер имеет такие же заголовки, что и другие почтовые сообщения (но с иными значениями). Контейнер заключает в себе отдельные части тела сообщения (BodyPart), напоминающие сообщения внутри сообщения. Некоторые из этих частей (BodyPart) представляют собой текстовые сообщения, сопровождающие составное почтовое сообщение. Другие части представляют собой вложенные в сообщение файлы, например файлы Microsoft Word или изображения в формате JPEG. Если содержанием части (BodyPart) является строка (String), то компонент Java- Bean отображает это текстовое сообщение. В противном случае он предполагает, что дан- ная часть (BodyPart) - это вложенный файл и сохраняет этот файл в специальной папке attachments. Возможно вы уже знакомы с кодом метода handleMessages (), тогда вы можете его пропустить и переключиться на метод displayMessageO, который занимается сохранением файловых вложений. Пример 20.6. Компонент JavaBean, работающий с вложениями и отображающий в браузере сообщения / package com. jspservletcookbook; import j ava.io.*; import java.util.Properties; import javax.mail.*; import javax.mail.internet.*; import j avax.servlet.*; import j avax.servlet.http.*; public class AttachBe^n { Работа с вложениями в почтовые сообщения, полученные сервлетом | 489
/* НЕ ПОКАЗАНЫ: закрытые поля компонента (свойства); переменные со значениями по умолчанию и метод sendMessage; ‘ см. Пример 20.2 */ public AttachBean(){} private void handleMessages(HttpServletRequest request. Printwriter out) throws lOException, ServletException ( //Получаем информацию для аутентификации пользователя на POP-сервере, //используемом, для получения почты. Эта информация хранится //в объекте HttpSession. HttpSession httpSession = request.getSession(J; String user = (String) httpSessionrgetAttribute("user"); String password = (String) httpSession.getAttribute("pass"); String popAddr = (String) httpSession.getAttribute("pop"); Store popStore = null; Folder folder null; if (L check(popAddr)) popAddr = AttachBean.DEFAULT_SERVER; try { if ((! check(user)) || (! check(password))) throw new ServletException( "A valid username and password is required to check email."); Properties properties System.gutProperties(); Session session Session.getDefaultlnstance(properties); popstore = session.getStore("pop3"); popStore.connect(popAddr, user, password); folder popstore.getFolder("INBOX"); if (I folder.exists()) throw new ServletException ( "An 'INBOX* folder does not exist for the user."); folder.open(Folder.READ-ONLY) ; . .... Message[] messages folder.getMessagesO; int msgLen = messages.length; if (msgLen == 0) out.println( ”<h2>The INBOX folder does not yet contain any " + " email messages.</h2>"); for (int i 0; i < msgLen; i++){ displayMessage(messages[i], out); ' out.printIn("<br /xbr />"); t « 49(1 | Глава 20. Работа с электронной почтой в сервлетах H.JSP
} catch (Exception exc) { out.printin( "<h2>Sorry, an error occurred^while accessing * + "the email messages.</h2>"); out.printIn(exc.toString()) ; } finally { try{ t if (folder ! null) folder.close(false)j if (popStore ! null) POPStore.close(); } catch (Exception e) { } ) ) / /handleMessages private void displayMessage(Message nrg, Printwriter out) throws MessagingException, lOException{ if (msg 1" null) { /* получаем содержимое сообщения; сообщение может быть письмом без вложений, или письмом с вложениями. Метод getContent() в >сг.рсщает объект, имеющий тип 'Multipart* если msg (сообщение) имеет вложения */ Object о msg.getContent(); if ( о instanceof String){ //просто отображаем некоторую информацию ,//о содержании сообщения handleStringMessaao(msg,(String) о, out); } else if ( p instanceof Multipart ) { //сохраняем вложение(я) ь папке Multipart mpart » (Multipart) о; Part part . null; File file null; FileOutputStream stream null; InputStream input “ null; String fileName 11 Multipart (составное сообщение) состоит // из частей (*BodyParts*) каждая Работа с вложениямй в почтовые сообщения, полученные сервлетом | 491
//из которых имеет тип 'Fart* for (int i * 0; i < xnpart ~utCount(); i++){ part apart.g< itBt> iyPart (i); Object partcontent part.getContent(); if (partcontent instanceof String){ handleStringMeebagr(msg, (String) part Cont ent, out); • <7 / £* 7 L. 4 ) elee {//обрабатываем как вложенный файл fileName part.getFileName(); if (1 check(fileName))файла no умолчанию fileName "file"+ new java.util .Dat^().gttTime(); } //записываем InputStream (входной поток) //вложения в файл file hew File( attachFolder+ Syntem.getPrpperty("fiJ,e.n. parator") + fileName); stream nw». FileOutputstream(file); input « part.getInputStream(); int ch; j while ( (ch input.read()) ! -1){ stream.write (ch); } input.close(); out. print In ( "Handled attachment named s ’+ fileName*"<br /xbr />"); }// if )//for )//else if instanceof multipart } else( out.printIn( "<h2>The received email nj sage returned null.</h2>"); }// if msg ! null }//displayMestage private void handleStringMessage(Part part, String emailcontent. Printwriter out) throw* MenvagingException { if (part instanceof Message){ Message msg * (Message) part; ь. • V'', if (msg.getFromO [OJ instanceof Tntornet/xidro“c) { 492 | Глава 20. Работа с электронной почтой в сервлетах н JSP >
out.printIn("Mesьади received from:" + ((Intwrn^tAddress) msg.getFrotn() [0]) .get Address() + "<br />"); } ....... t(:. out.printing 'K1 s«gt content type: " + msg.getContentType() + "<br/>"); out.printIn("Message content: " + emailcontent +"<br />"); J л?.£ Xrt-a? * ‘ Г ♦ jr /11 < \ i J , • * private. boolean- ch_=ck(Strihg value) { i f(value ==;nul1 |14value,equals("")) return false; V • V 1 return true; }7/check /* rib ПОКАЗАНЫ: методы-установщики свойств компонента JavaBean см. Пример 20.2 *7 }// AttachBean Если код метода displayMessage () определяет что рассматриваемая часть сооб- щения (BodyPart) - вложенный файл, он получает байты;, составляющие этот файл как InputStream (входной поток). BodyPart реализует интерфейс Part, в котором определен метод get Input Stream(). Код сохраняет файл, используя этот Input- Stream и класс j ava. io. FileOutputStream. В примере 20.7 приведен метод doGet () сервлета, использующего компонент сот. j spservletcookbook.AttachBean. Пример 20.7. Метод doGet () сервлета, использующего компонент JavaBean для работы с вло- жениями в почтовое сообщение public void doGet(HttpServletRequest request» HttpServletResponse response) throws ServletException, java.io.lOException { •• ; ' - *.-> response.setContentType("text/html"); java.io.Printwriter out = response.getWriter(); out.println( ”chtmlxheadxtitlesiEmail message sender</titlex/headxbody>“); AttachBean email: z * new AttachBean (); , emailer, set SmtpHost ("mail.attbi.com") ; > emailer.setAttachFolder(getServletContext().getRealPath("/") * "attachments"); exnailer.handleMessages(request,out); Работа с вложениями в почтовые сообщения, полученные сервлетом | 493
out .println ("</bo^yx/html>"); }//doGet На рис. 20.2 показаны сообщения, которые этот сервлет отображает в браузере (используя компонент JavaBean). Первое почтовое сообщение - просто текстовое посла- ние без всяких вложений. Второе письмо содержит два вложения; оно имеет М1МЕ-тип nudtipart/mixed. Рис. 20.2. Сервлет отображает информацию о полученных вложениях и сообщениях См. также Страницу Sun Microsystems по JavaMail API: http://java.sun.com/products/javamail/', рецепт 20.1 по добавлению к web-приложению JAR-файлов, связанных с JavaMail; рецепт 20.2 по отправке почтового сообщения с помощью сервлета; рецепт 20.3 по отправке почтового сообщения с использованием JavaBean; рецепт 20.4 по доступу к почтовому сооб- щению из сервлета; рецепт 20.5 по получению почты с помощью компонента JavaBean; рецепт 20.7 по добавлению вложений к почтовому сообщению; рецепт 20.8 по чтению заго- ловков почтового сообщения. 494 | Глава 20. Работа с электронной почтой в сервлетах и JSP
20.7 Добавление вложения к почтовому сообщению в сервлете Задача Вы хотите в сервлете создать почтовое сообщение с вложением. Решение Для реализации основных действий по созданию почтового сообщения используйте JavaMail A.PI, а для генерирования файловых вложений - JAF (JavaBeans Activation Framework - инфраструктура активации компонентов JavaBean). Обсуждение z Классы JAF обеспечивают детальное управление процессом вложения файлов в почтовое сообщение. * *’, Если вы используете и JavaMail API и JAF, не забудьте в классе сервлета * импортировать все необходимые пакеты. — import javax.activation.*; import j avax.mai1.*; • import javax.mail.internet.*; //продолжение определения класса Метод sendMessage () из примера 20.8 создает новое почтовое сообщение (аточнее - новое сообщение javax. mail, internet. MimeMessage), добавляет в него текстовое послание и вкладывает файловое вложение. Затем метод отсылает это сообщение, используя код, который вы уже видели в рецептах 20.2 и 20.3. Transport.send(maiIMsg); Для выполнения своих задач код создает контейнер (объект javax.mail .Multi- part) и две части сообщения (javax.mail .BodyParts), составляющие содержимое этого контейнера. Первая часть (Bodypart) - текстовое послание (обычно используемое, чтобы описать получателю файловые вложения), а вторая - файловое вложение (в данном случае это файл Microsoft Word). Затем код устанавливает в качестве содержимого почтового сообщения (MimeMessage) контейнер Multipart. Короче говоря, MimeMes- sage (почтовое сообщение) содержит Multipart, а он складывается из двух Body- Parts (частей): текстового послания и прикрепляемого к письму файла. Добавление вложения к почтовому сообщению в сервлете | 495
Если вам требуется посмотреть на заголовки почтового сообщения (MimeMessage), содержащего вложения, вызовите метод getAllHeaders () этого объекта MimeMessage. Детали смотрите в рецепте 20.8. Пример 20.8. Создание в сервлете вложения к почтовому сообщению package com.jspservletcookbook; v inport j ava.io.*; import java.util.Properties; import javax.activation.*; import javax.mail.*; ; import javax.mail.internet.*; import javax.servlet.*; import j avax.servlet.ht tp.*; public class EmailAttachServlet extends HttpServlet { //адрес почтового сервера по умолчанию, на случай, //если пользователь не задал таковой. private final static String DEFAULT_SERVER = "mail.attbi.com"; public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.lOException { response.setContentType("text/html"); java.io.Printwriter out = response.getWriter(); out.printlnf "<htmlxheadxtitle>Email message sender</titlex/head><body>"); String smtpServ = request.getParameter("smtp"); if (smtpServ == null || smtpServ.equals("")) smtpServ = DEFAULT_SERVER; String from = request.getParameter ("from") ; String to = request.getParameter("to"); String subject = request.getParameter (/subject"); try { sendMessage(smtpServ, to, from, subject); } catch (Exception e) { throw new ServletException(e.getMessage()); ) out.printIn( "<H2>Your attachment has been sent.</H2>"); out.printlnf "</bodyx/html>"); , - }//doPost 496 | Глава 20. Работа с электронной почтой в сервлетах и JSP
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.lOException { doPost(request,response); }//doGet private void nendMessaqe(String smtpServ, String to. String from, String subject) throws Exception { Multipart multipart « null; BodyPart bpart1 null; BodyPart bpart2 " null; Properties properties = System.getProperties(); //заносим в объект 'Properties' адрес почтового сервера //чтобы экземпляр 'Session' со значениями по умолчанию //мог использовать его. properties.put("mail.smtp.host", smtpServ); Session session = Session.getDefaultlnstance(properties); Message mailMsg = new MimeMessage(session);//новое почтовое сообщение InternetAddress[] addresses = null; try { if (to != null) { //если адрес получателя нарушает //синтаксис RFC822, выбрасывается //исключение 'AddressException' addresses = InternetAddress.parse(to, false); mailMsg.setRecipients(Message.RecipientType.TO, addresses); } else { throw new MessagingException( , "The mail message requires a 'To' address."); } 4 if (from J= null) { .* mailMsg /setFrom(new IntemetAddress (from)); } else { throw new MessagingException( "The mail message requires a valid 'From' address."); } if (subject != null) mailMsg.setSubj ect(subj ect); Добавление вложения к почтовому сообщению в сервлете | 497
//Содержимое данного письма имеет тип 'Multipart* //MIME-тип этого содержимого - 'multipart/mixed* multipart = new MimeMu.lt ip art (); //Текстовая часть составного почтового сообщения bpart1 в new MimeBodyPart(); String textPart "Hello, just thought you'd be interested in this Word file."; //создаем объект DataHandler для текстовбй части DataHandler data new DataHandler(textPart* "text/plain"); //помещаем DataHandler a BodyPart (часть почтового сообщения) bpart1.setDataHandler(data); » » //добавляем текстовую часть (BodyPart) в контейнер Multipart multipart.addBodyPart( bpart1); //создаем часть (BodyPart) представляющую вложенный файл Word bpart2 new MimeBodyPart()? //создаем Datasource указывающий на файл FileDataSource fds new FileDataSource( new File( "h:/book/chapters/chapl/chapl.doc") ); //Позаботимся, чтобы вкладываемый файл обрабатывался //как соответствующий MIME-тип: //в данном случае application/msword MimetypesFileTypeMap ftm = new MimetypesFile!typeMap (); //no синтаксису здесь указывается MIME-тип, за которым //через пробел следуют расширения файла ftm. addMimeTypes("application/msword doc DOC" ); fds. setFileTypeMup (ftm); // создается DataHandler на основе // созданного ранее FileDataSource DataHandler fileData new DataHandler( fds ); //данная часть (BodyPart) будет содержать файл Word bpart2.setDataHandler(fileData); //добавляем вторую часть (BodyPprt), содержащую вложение //к объекту Multipart multipart.addBodyPart( bpart2 ); //и, наконец, задаем объект Multipart в качестве //содержимого сообщения (MimeMessage) mailMsg.setContent( multipart ); //отправляем почтовое сообщение; » 498 | | Глава 20. Работа с электронной почтой в сервлетах и JSP । \
//если кто-либо из получателей имеет некорректный адрес, //возбуждается исключение 'SendFailedException' Transport.send(mailMsg)г } catch (Exception exc) { throw exc; }//try }//sendMessage }//EmailAttachServlet Комментарии в примере 20.8 поясняют, что происходит, когда вы используете классы javax.activation для создания файлового вложения определенного MIME-типа. Наи- более, запутанная часть - создание объекта javax.activation.FileDataSource (файловый источник данных), который указывает на файл, который вы хотите вложить в почтовое сообщение. Код использует этот источник данных (FileDataSource) для соз- дания объекта javax.activation.DataHandler (обработчик данных), отвечающего за содержимое файлового вложения. //создаем DataSource указывающий на файл FileDataSource fds = new FileDataSource( new File( "h:/book/chapters/chapl/chapl.doc") ); Необходимо удостовериться, что MimeMessage (сообщение) идентифицирует вло- женный файл как имеющий MIME-тип application/msword, чтобы почтовое приложение пользователя смогло обработать это вложение как файл Microsoft Word Задаем File- ТуреМар (отображение типа файла) для файлового источника данных (FileData- Source) с помощью следующего кода. //Позаботимся, чтобы вкладываемый файл обрабатывался //как соответствующий М1МЕ-тип: //в данном случае application/msword MimetypesFileTypeMap ftm = new MimetypesFileTypeMap(); //по синтаксису здесь указывается MIME-тип, за которым //через пробел следуют расширения файла ftm.addMimeTypes("application/msword doc DOC" ); fds. setFileTypeMap (ftm)’; MimetypesFileTypeMap - это класс, который связывает MIME-типы (например, application/msword) с соответствующими им расширениями файлов (например, .doc). Убедитесь, что вы связываете с файлом, отправляемым как вложение, правильный MIME-тип, поскольку вы явно задаете эту связь в коде. Детали приведены на сайте: http://java. sun. com/j2ee/1.4/docs/api/javax/activation/MimetypesFileTypeMap. html. Добавление вложения к почтовому сообщению в сервлете | 499
Затем в коде выполняются следующие шаги. . 1. Создается обработчик данных (DataHandler), при этом его конструктору в качестве параметра передается файловый источник*данных (FileDataSource). 2. Этот обработчик данных (DataHandler) задается как источник содержимого составной части сообщения (BodyPart). 3. Часть сообщения (BodyPart) добавляется к объекту Multipart (который в свою очередь представляет содержимое почтового сообщения). См. также Страницу Sun Microsystems по JavaMail API: http://java.sun.com/products/javainail/; web- страцица по JAF: http://java.sun.com/products/javabeans/glasgow/jaf.html; рецепт 20.1 по добавлению к web-приложению JAR-файлов, связанных с JavaMail; рецепт 20.2 по отправке почтового сообщения с помощью сервлета; рецепт 20.3 по отправке почтового сообщения с использованием JavaBean; рецепт 20.4 по доступу к почтовому сообщению из сервлета; рецепт 20.5 по получению почты с помощью компонента JavaBean; рецепт 20.6 по обработке вложений в сервлете; рецепт 20.8 по чтению заголовков почтового сообщения. 20.8 Чтение в сервлете заголовков полученного почтового сообщения Задача Прочитать из сервлета заголовки почтового сообщения. Решение Для доступа к каждому почтовому сообщению используйте JavaMail API. Вызовите метод getAllHeaders () интерфейса Part, затем, перебирая в цикле перечисление (Enumeration), возвращенное этим методом, можно получить имя и значение каж- дого из заголовков. Обсуждение Некоторые развитые почтовые программы, такие, как фильтр спама, предназначены для анализа заголовков почтовых сообщений, а не только для чтения их текста и файло- вых вложений. Заголовок состоит из имени, символа двоеточия (1) и значения. Заголовки содержат детали, связанные с почтовым сообщением, например информацию об отправителе f д* сообщения, информацию о почтовом сервере (серверах), который обрабатывал сообщение во время его путешествия по сети. Ниже представлен пример заголовка. То: <bwperry@parkerriver.com> 500 | Глава 20. Работа с электронной почтой в сервлетах и JSP
JavaMail API упрощает работу с заголовками почтовых сообщений. Объект класса Message (сообщение) имеет метод getAHHeaders () (относящийся к интерфейсу Part, который реализуется классом Message), Этот метод возвращает перечисление (java .util. Enumeration), содержащее набор Объектов javax.mail .Header (заго- ловок). Для получения имени и значения некоторого заголовка достаточно вызвать методы getName () и getValue () объекта Header. Интерфейс Part также включает метод getHeader(String headerName), который вы можете использовать для получения значения конкретного заголовка. Этот метод возвращает массив строк, содержащий значение (значения) заголовка с указанным в качестве параметра именем. В примере 20.9 показан тот же сервлет, что и в рецепте 20.4, с изменениями, позволяющими выдать и содержимое сообщений и значения заголовков. Код, работающий с заголовками, находится в методе displayMessage (). Пример 20.9. Сервлет, отображающий имена и значения заголовков почтового сообщения package com.j spservletcookbook; import java.io.lOException; import java.io.Printwriter; import java.util.Properties; import java.util.Enumeration; import javax.mail.*; import javax.mall.internet.*; import javax.servlet.*; import j av£x.servlet.http.*; public class HeaderAOcessor extends HttpServlet { private final static String DEFAULT_SERVER = "mail.attbi.com''; public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.lOException { response.setContentType("text/html"); java.io.Printwriter out = response.getWriter()(); out.println("<html><headxtitle>Email Reader</titlex/head><body>") ; handleMessages(request, out); out.println("</bodyx/html>"); } //doGet private void handleMessages(HttpServletRequest request, Printwriter out) throws lOException, ServletException { HttpSession httpSession = request .getSession () ,- String user = (String)- httpSession.getAttribute("user1 ); String password = (String) httpSession.getAttribute("pass"); Чтение в сервлете заголовков полученного почтового сообщения | 501
String popAddr = (String) httpSession.getAttribute(*pop") ; Store popStore « null; Folder folder = null; , if (! check(popAddr)) popAddr = Header Accessor.DEFAULT_SERVER; try { if ((! check(user)) || (! check(password))) throw new ServletException( , "A valid username and password is required to check email."); Properties properties » System.getPropertiesO; Session session = Session.getDefaultlnstance(properties); popStore » session.getStore("рорЗ"); popstore.connect(popAddr, user, password); folder = pops tore. getFolder ("INBOX*’); if (! folder.exists()) throw new ServletException( "An ‘INBOX’ folder does not exist for the user."); folder.open(Folder.READ_ONLY); Message[] messages « folder.getMessages(); int msgLen = messages.length; if (msgLen == 0) out.printIn( "<h2>The INBOX folder does not yet contain any " + "email messages.</h2>"); for (int i = 0; i < msgLen; i++){ displayNSssags(messages [i], out) ; out.printIn("<br I><br />"); )//for , } catch (Exception exc) {' out.printlnf "<h2>Sorry, an error occurred while accessing the " + "email messages.</h2>"); out.printin(exc.toString()); i } finally { ' try{ if (folder != null) folder.close(false); if (popStore != null) popStore.close(); } catch (Exception e) ( } 502 | Глина 20, Рабогu с элей тройной почтой в сервлетах н JSP
} }//handleMessages private void displayMessage(Message msg, Printwriter out) throws MessagingException, ZOException{ if (msg != null && msg.getContent() instanceof String){ if (msg. get From (•) [Q] instanceof InternetAddress) { out.printin(• "Message received from: " + ((InternetAddress)msg.getFrom()[0]).getAddress() +"<br />"); } out.printin("Message content type:." + msg.getContentType() + "<br />"); out.printIn( "Message body content: " + (String) msg.getContent()); //Отображаем каждый из заголовков письма с помощью тега ul out.println("<ul>"); Header head ж null; Enumeration headers msg.getAllHeadersO; while ( headers.hasMoreElements() ){ head (Header) headers.nextElement(); z out.printIn( r<li>J ♦ head.getNameO + "s " + head.getValueO* "</li>"); }//while out.println("</ul>"); } else{ out.printIn( "<h2>The received email message was not " + "a text content type.</h2>"); } }//displayMessage private boolean check(String value){ if(value -= null || value.equals("")) return false; return true; ) } На рис. 20.3 показано отображение в браузере работы сервлета из примера 20.9. Каж- дому из заголовков предшествует буллит, за которым следует имя заголовка, двоеточие и значение заголовка. Чтение В сервлете ыгхз товков полученного почтового сообщения | 503
Рис. 20.3. Сервлет, получающий почтовые сообщения и отображающий их заголовки См. также Страницу Sun. Microsystems по JavaMail API: http://java.sun.com/products/javamail/; рецепт 20.1 по добавлению к web-приложению JAR-файлов, связанных с JavaMail; рецепт 20.2 по отправке почтового сообщения с помощью сервлета; рецепт 20.3 по отправке почтового сообщения с использованием JavaBean; рецепт 20.4 по доступу к почтовому сообщению из сервлета; рецепт'20.5 по получению почты с помощью компо- нента JavaBean; рецепт 20.6 по обработке вложений в сервлете; рецепт 20.7 по добавлению вложений в почтовое сообщение. 504. |. Глава 20. Работа с электронной почтой в сервлетах и JSP
________,______ГЛАВА 21 Доступ к базам данных 21.0 Введение , Если вы являетесь Java-разработчиком, никогда не писавшим код, связанный с базами данных, я могу дать совет: не затаивайте дыхание, когда будете получать поручение заняться этой работой! Приведенные здесь рецепты демонстрируют приемы доступа к ресурсам, находя- щимся в базах данных, с использование поиска на основе JNDI (Java Naming and Direc- tory Interface - интерфейс именования и каталогов), который является наиболее эффективным (и, возможно, наиболее широко используемым) независимым от платформы способом доступа к ресурсам баз данных. JNDI - это программный интерфейс (Java API), созданный для хранения объектов в иерархической древовидной структуре, напоминающей файловую систему, складывающуюся из каталогов, подката- логов и файлов. Сервлеты и JSP могут использовать методы JNDI API (приведенные в нескольких примерах данной главы) для получения ссылок из Java-объектов, таких, как компоненты JavaBean, чтобы использовать полученные ссылки в своем коде. Для кода обработки базы данных это обычно означает использование объектов j avax. sql. DataSource, являющихся объектами-фабриками (factories) «произво- дящими» соединения с базами данных. Объекты DataSource обеспечивают «пулы соединений» - другой очень важный инструмент баз данных в web. Пулы соедине- ний - это группы соединений с базами данных, используемые совместно сервлетами, страницами JSP и другими классами. Серверы приложений, такие, как WebLogic, обычно позволяют вам задать, сколько соединений хранится в пуле, какая таблица базы данных может быть использована сервером для автоматического тестирования соединения, чтобы определить подходит ли оно для. того, чтобы вернуть его в совме- стно используемый пул, а также другие свойства пула. Данные рецепты объясняют основы того, как устанавливается пул соединений как для сервера Tomcat, так и для WebLogic. Рецепты также освещают ряд других практических вопросов работы с базами данных, например, как из сервлета или JSP вызвать хранимые процедуры или как включить в транзакцию более одного оператора SQL (Structured Query Language - структурированный язык запросов). ' 505
21.1 Доступ из сервлета к базе данных без использования DataSource Задача Вы хотите обращаться из сервлета к базе данных, не прибегая к настройке объекта DataSource для этой базы данных. Решение Используйте для доступа к объекту java„sqJ^Gonuection,’ который соединяет сервлет с базой данных, программный интерфейс JDJ3C API :(Java Database Connectivity - соединение с базами данных на платформе Java). ... —>' Обсуждение Иногда разработчикам требуется быстрое, пусть и не очень элегантное решение вопроса доступа к базе данных. В данном рецепте объясняется, как использовать для доступа к базе данных класс java.sql.DriverManager. Этот класс взаимодей- ствует с драйвером базы данных - программным обеспечением, позволяющим Java-коду работать с конкретной базой данных, например, MySQL или Oracle. На самом деле более предпочтительным является Использование класса javax. sql. Datasource, позволяющего получит^ соединение с базой данных из пула ** fjfr соединений, см. рецепты 21.2-2 L.& у™ v. 4 В примере 21.1 задачи по работе с базой данных выполняются в методе doGet (). Пример 21.1. Сервлет, обращающийся! к базе данных идпШъзуя JDBC API package com..jspservletcookbook; z import java.sql.*; import javax.servlet.*; import javax.servlet.http.*; public class DatabaseServlet extends HttpServlet'{ public void doGet(HttpServletRequest request, •- ; HttpServletResponse response) throws-ServletException, java.io.lOException { String sql = "select * from athlete"; j г f Connection conn null; Statement stmt null; 506 | Глава 21. Доступ к базам данных
ResultSet rs null» ResultSetNetaData rsn null» response.setContentType(* text/html*); j ava.io.PrintWri ter out = response.getWriter(); out.printIn( <html><head><title>Servlet Database Access*/titje>*/head>*body>"); out.printIn("<h2 >Database info*/h2 >"); out.printin("<table border='1•><tx>*)i try{ //загружаем драйвер базы данных Class.forNane ("oracle.jdbc.driver.OracleDriver*); //JDBC URL для данной базы данных Oracle String url в *jdbc:oraclexthinxS192.168.0.2>1521:ORCL"» //Создаем java.sql.Connection (соединение) для этой базы данных //используя правильные иия и пароль пользователя conn DrivexManager.getConnection(url,"scott", "tiger")» //Создаем statement (оператор) для выполнения SQL-запроса stat conn.createStatenentO» //Выполняем SQL-оператор rs stMt.executeQuery (sql)» //получаем информацию о возвращенном значении в форме //обмята ResultSetNotaData (метаданные //о результирующем множестве) ran в rs.getNetaDataO» int colCount • rsM.getColwmCountO» //печатаем имена полей в ячейках заголовка таблицы for (int i в 1» i <«colCount» ++i){ out. print In ("<th>" + rsm.getColunmName(i) + "</th>")» out.println("</tr>")» while (, rs.next()){ . out.println(b<tr>")» //печатаем значения каждого из полей for (int i в 1» i <«colCount» ++i) out.printin("<td>" * rs.getString(i) ♦ "</td>")» out.printIn("</tr>")» Доступ из сервлета к базе данных без использования DataSoui < | 507
} } catch.{Exception e){ throw new ServletException(e.getMessage()); } finally { try{ //вдесъ закрываются все соответствующие 11 Result Set в (результирующие множества) if(ntmt 1* null) stmt •close() ; if (conn I" null) conn.close(); } catch (SQLException sqle){ ) x }//finally out.println("</table><br><br>"); out.println(*</body>"); out.println!"</html>"); } //doGet Ниже приведены шаги, необходимые для запуска сервлета и проиллюстрированные примером 21.1. Добудьте JAR-файл, содержащий драйвер вашей базы данных, и сохраните его или в общедоступном каталоге сервера (в Tomcat это каталог <Tomcat-root>/commonAib\ или в каталоге WEB-INFAib вашего приложения. Измените расширение файла JDBC-драйвера Oracle (например, dassesl2.zip) на .jar, чтобы хранящиеся в нем Java-классы могли быть нормально загружены в JVM. Найдите в литературе производителя драйвера URL базы данных, а у адми- нистратора базы данных (им можете быть и вы!) узнайте имя и пароль пользователя или что-то другое, что нужно для получения доступа к базе данных. Код не сможет получить доступ к базе данных без правильных имени и пароля пользователя. Оборотная сторона данного подхода заключается в том, что вы помещаете конфиден- циальную информацию в код сервлета. Более приемлемым может быть применение стратегий, приведенных в пяти последующих рецептах, начиная с рецепта 21.2 «Настройка DataSource в Tomcat». Результат запуска сервлета показан на рис. 21.1. 508 | Глава 21. Доступ к базам данных
Рис. 21.1. Сервлет, отображающий информацию из базы данных В главе 23, посвященной JSTL, показано, как в JSP получить доступ к базе данных, без необходимости настройки DataSource. См. также Спецификацию JDBC: http://java.sun.com/products/jdbc/download.html', рецепты 21.2-21.6 по настройке и использованию DataSource в Tomcat и WebLogic: рецепты 21.7 и 21.8 по обращению из сервлетов и JSP к хранимым процедурам; рецепт 21.9 по преобразо- ванию объекта j ava. sql. ResultSet в j avax. servlet. j sp. j stl. sql Result; рецепты 21.10 и 21.11 по использованию транзакций в сервлетах и JSP; рецепт 21.12 по выяснению информации о ResultSet. 21.2 Настройка DataSource в Tomcat Задача Требуется настроить j avax. sql. DataSource (источник данных) для его исполь- зования в сервлете, выполняющемся в web-контейнере Tomcat. Решение Создайте в файле server.xml сервера Tomcat элемент resource, а в дескрипторе развертывания - связанный с ним элемент resource-ref. Настройка DataSource в Tomcat | 509
Обсуждение i Tomcat облегчает создание пула соединений, предназначенного для Того, чтобы сервлеты и JSP могли совместно эффективно использовать соединения с базой данных. На web-сайтах, имеющих большое число одновременно обращающихся пользователей, пул соединений повышает эффективность работы, позволяя совместно использовать суще- ствующие соединения с базой данных, вместо того чтобы каждый раз, когда приложению требуется использовать базу данных, создавать новое соединение и разрывать его. \ Другим преимуществом использования пула соединений является то, что вы можете сменить СУБД, используемую сервлетом или JSP, не изменяя их Java-кода, поскольку ресурс - база данных - настраивается за пределами сервлета или JSP. Ниже приведены шаги по настройке DataSource в Tomcat. 1. Создайте в server.xml (в XML-файле, находящемся в каталоге webapps сервера Tomcat) элементы Resource и ResourceParams. Эти элементы описывают JNDI-объект, который вы создаете, чтобы обеспечить свои сервлеты или JSP источником данных (Datasource). 2. Добавьте в web.xml элемент resource-ref, позволяющий компонентам данного web- приложения получить доступ к настроенному DataSource (источнику данных). В примере 21.2 показаны элементы Resource и ResourceParams в файле server.xml. В данном примере описывается DataSource (источник данных), подключаемый к базе данных Oracle 8i. Пример 2L2. Элемент Resource в файле server.xml «Resource name="jdbc/oracle-8i-athletes" scope= "Shareable" type="javax.sql.DataSource" auth= , "Container" descriptions"Home Oracle 8i Personal Edition"/>. «ResourceParams name="jdbc/oracle-8i-athletes"> «parameter» <name>driverClas sName</name> <value>oracle.jdbc.driver.OracleDriver«/value> -</parameter> <parameter> <name»ur1</name» <value>jdbc:oracle:thin:@192.168.0.2:152leORCL*/value» «/parameter» «parameter» <name»username</name» «value»scott</value» «/parameter» «parameter» 4 < 510 | Глава 21. Доступ к базам данных
<name>password</name> <value>tiger</value> </parametef> </ResourceParams> Создайте элементы Resource и ResourceParams для каждой базы данных, исполь- зуемой вашим приложением. В примере 21.3 показан элемент resource-ref. связанный с элементом Resource из примера 21.2. Пример21.3. Элемент resource-ref из файла web.xml, задающий источник данных (Data- Source) <!—начало файла web.xml —> <resource-ref> > <res-ref-name>jdbc/oracle-4Ji-athletes</res-ref-name> <res-type>javax.sql.DataSource</res-type> <res-auth>Container</res-auth> </resource-ref> <!— продолжение файла web.xml —> JNDI-путь к этому источнику данных (DataSource), используемый в JNDI-поиске: jdbc/oracle-8i-athletes. ' *’ API сервлетов версии 2.4 не требует, чтобы элементы в файле web.xml (в том числе • и resource-ref) следовали в определенном порядке. Спецификация версии 2.3 *•' f наоборот, требуе^, чтобы все элементы располагались в порядке, заданном в DTD- схеме (Document Type Definition - определение типа документа). См. главу 1. См. также Спецификацию JDBC; http://java.sun.cofn/products/jdbc/download.html',. рецепт 21.3 по использованию DataSource в сервлетах на сервере Tomcat; рецепты 21.4-21.6 по настройке и использованию DataSource сервлетами и JSP в WebLogic; рецепты 21.7 и 21.8 по обращению из сервлетов и JSP к хранимым процедурам; рецепт 21.9 по преобразованию объекта java, sql .‘ResultSet в javax. servlet, jsp. jdtl. sql Result; рецепты 21.10 и 21.11 по использованию транзакций в сервлетах и JSP; рецепт 21.12 по выяснению информации о ResultSet- Настройка DataSource в Tomcat | 511
21.3 Использование DataSource в сервлетах на сервере Tomcat Задача Вы хотите использовать источник данных (DataSource), который вы настроили на сервере Tomcat. Решение Для получения DataSource используйте классы JNDI API, а затем с помощью этого DataSource осуществите доступ к соединению с базой данных. Обсуждение • Для доступа к сконфигурированному источнику данных (DataSource) используйте классы из пакета javax.naming. Например, объект javax.naming. Initialcontext, для поиска источника данных (DataSource), который был привязан как JNDI-объект. Пакет javax.naming - часть стандартной редакции платформы Java версий 1.3 Сервлет из примера 21.4 создает в своем методе init () переменную-экземпляр класса javax.sql.DataSource, метод init () вызывает контейнер сервлетов, когда он соз- дает экземпляр сервлета. В Tomcat JNDI-объекты хранятся на корневом уровне, заданном строкой «java:comp/env». Пример 21.4. Использование Da taSource в сервлете package com.j spservletcookbook; import java.sql.*; import javax. naming. Cont ext; import javax.naming.Initialcontext; import javax.naming.NamingException; import javax.sql.*; import javax.servlet.*; import javax.servlet.http.*; public class DbServlet extends HttpServlet { DataSource pool; ; / public void init() throws ServletException { Context env null; 512 | Глава 21. Доступ к базам данных
try{ env (Context) new Initialcontext().lookup("java:c<w>/env"j; //Ищем DataSource, который представляет пул соединений pool (DataSource.) env. lookup (*jdb з /oracle-8i-athletes"); if (pool и" null) throw nuw ServletException( "'oracle-8i-athletes' is an unknown DataSource”); } catch (NamingException ne) { throw new ServletException(ne.getMessage()); }//try } ♦ public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.lOException { String sql "select * from athlete”; Connection conn null; Statement stmt B null; ResultSet rs null; ResultSetMetaData rsm null; //Начинаем создание ЛТМЬ-страницы response.setContentType("text/html"); j ava.io.Printwriter out = response.getWriter(); out.printlnf л . "<html><head><title>Typical Database Access</titlex/headxbody>"); out.printin("<h2>Database info</h2>"); out.printlnf "<table border ='l 'xtr»") j try{ //Получаем соединение из пула соединении conn pool.getConnection(); //Создаем объект Statement, который можно использовать // для выполнения * SQL-запроса stmt conn.createStatemant(); //выполняем простой запрос SELECT rs stmt.executeQuery(sql); //Получаем объект ResultSetMetaData, чтобы можно было динамически //отобразить имена столбцов (полей) |7 Использование DataSource в сервлетах на сервере Tomcat | 513
//ResultSet (результирующего множества) 1 ±sm rs.getMetaData()j int colCount rsm.getColumnCount (); //печатаем имена полей в ячейках заголовка таблицы for (int i 1; i <"ColCount; ++i)( out.printin("<th>" + rsm.getColvmnName(i) + "</th>"); > out.printin("</tr>"); • //пока в ResultSet есть еще строки...,- while( rs.next()){ out.printin("<tr>*); //Печатаем значение каждого поля каждой строки (записи) //с помощью метода Resul t Set. get String () for (int i • 1; i <«colCount; ++i) out.printIn("<td>" -I- rs.getString(i) + "</td>"); out.printin("</tr>")i }//while } catch (Exception e) ( . throw new ServletException(6.getMessage()); } finally { ; • > try{ //При закрытии об*ьекта Statement (оператор), закрываются //все связанные с ним ResultSet (результирующие множества) if (stmt ! null) stmt.closeOr . ±.’V j‘ //ОЧЕНЬ ВАЖНО! Этот код возвращает соединенно //в пул if (conn ! null) conn.close() i } catch (SQLException sqle){ } ) Л I out.printin("</table></bbdyx/html>"); }//doGet <.р .;1 514 | Глава 21. Доступ к базам данных ’ /
Сервлет из примера 21.4 получает DataSource, используя в JNDI-поиске адрес, сконфигурированный в Tomcat (рецепт 21.2; jdbc/oracle-8i-athletes). Этот код выглядит примерно следующим образом. //Поиск DataSource, который представляет пул соединений pool = (DataSource) env.lookup("jdbc/oracle-8i-athletes"); Затем код, из пула соединений, получает соединение с базой данных, для чего вызывает метод getConnection () объекта DataSource. Очень важно, когда сервлет покончит со всем, вызвать метод close () объекта Connection, поскольку этот метод возвращает совместно используемое соединение в пул. Обращение из браузера к сервлету примера 21.4 приводит к созданию выходной страницы, которая выглядит так, как показано на рис. 21.1. «г* ------ *5' Л « ___ В главе 23 по JSTL показано как использовать JSP для доступа к базе данных с помощью сконфигурированного DataSource. См. также Спецификацию JDBC: http://java.sun.com/products/jdbc/doyvnload.html', рецепт 21.1 по доступу из сервлета к базе данных без обращения к пулу соединений; рецепт 21.2 по конфигурированию DataSource на сервере Tomcat; рецепты 21.4-21.6 по настройке и использованию DataSource сервлетами и JSP в WebLogic; рецепты 21.7 и 21.8 ' по обращению из сервлетов и JSP к хранимым процедурам; рецепт 21.9 по преобразо- ванию объекта java. sql. ResultSet в javax. servlet -jsp. jstl. sql Result; рецепты 21.10 и 21.11 по использованию транзакций в сервлетах и JSP; рецепт 21.12 по выяснению информации b ResultSet. 21.4 Создание DataSource на сервере WebLogic Задача * Требуется на сервере WebLogic создать j avax. sql. DataSource для последующего использования сервлетами. Решение Используя консоль WebLogic, создайте новый пул соединений, а затем настройте новый DataSource (источник данных), связанный с этим пулом. Создание DataSource на сервере WebLogic | 513
Обсуждение ,Настройка DataSource в WebLogic включает в себя следующие шаги. 1. Войдите в консоль WebLogic, позволяющую управлять сервером WebLogic из браузера. URL консоли обычно выглядит так: http://<localhost:7001>/ (подставьте имя вашего хоста вместо localhost, а также измените номер порта, в соответствии с настройкой вашего сервера WebLogic). 2. В левой колонке окна консоли, в дереве щелкните <имя вашего домена> -> Ser- vices -»JDBC -* Connnection Pools. Затем щелкните по «Configure a new JDBC Connec- tion Pool...». / 3. В полученном окне введите имя для данного пула соединений, JDBC URL (например, jdbc:oracle:thin:@192.168.0.2:1521:ORCL'), имя класса драйвера (например, oracle, j dbc. driver. OracleDriver), а также имя и пароль пользователя в текстовом поле Properties. На рис. 21.2 показан настроенный пул соединений с именем «OraclePool». Запомните это имя - далее в рамках этого же процесса оно понадобится для настройки DataSource. Рис. 21.2. Создание пула соединений с помощью консоли WebLogic 516 | Глава 21. Доступ к базам данных
4. Щелкните по кнопке «Create» на этом окне, чтобы создать данный пул соединений, после чего выберите вкладку «Targets». Полученный экран позволит вам выбрать сервер, для которого будет применяться этот пул соединений. После выбора сервера щелкните по кнопке «Apply» на экране «Targets». Имя нового пула должно появиться в оконном меню слева. 5. Щелкните <имя вашего домена> -> Services -»JDBC Data Source и щелкните на URL «Configure New JDBC Data Source...». На рис. 31.3 показано окно настройки DataSource (источник данных), включающее имя этого источника данных («oracie- 8i-athletes»),. под которым WebLogic будет привязывать его как JNDI-объект. Окно также содержит текстовое поле, в которое вам необходимо ввести имя пула соедине- ний, который вы только что настроили: «OracIePool». Щелкните кнопку «Create», чтобы создать новый источник данных (DataSource). Окно JDBC Data Sources' (источники данных JDBC) показано на рис. 21.3. 6. Выполните те же самые действия, что и в шаге 4, с экраном «Targets», чтобы применить этот источник данных (DataSource) к необходимому серверу. Все прошло безболез- ненно, не так ли? Рис. 21.3. Создание Da taSource с помощью приложения консоль WebLogic Создание DataSource на сервере WebLogic | 517
Рис. 21.4. Сервер WebLogic, Внешний вид JNDI-дерева, содержащего DataSource См. также Спецификацию JDBC: hrtp://java.sun.coni/products/jdbc/download.html', главу 2 по развер- тыванию сервлетов и JSP на WebLogic; рецепт 21.2 и 21.3 по использованию DataSource на сервере Tomcat; рецепты 21.5 и 21.6 по использованию DataSource сервлетами и JSP в WebLogic. 21.5 Использование JNDI-поиска для доступа к DataSource jn WebLogic Задача Требуется использовать JNDI-поиск для доступа к DataSource на сервере WebLogic. Решение Для получения JNDI-объекта, который у вас привязан на сервере WebLogic, исполь- зуйте JNDI API и классы из пакета j avax. naming. 518 | Глава 21. Доступ к базам данных
Обсуждение ' • . - . .4- ч... । - Доступ к Connection (сое. мнению) посредством DataSource (источника данных) сервера WebLogic и пула соединений осуществляется при помощи такого же Java-кода, что на сервере Tomcat. 1. Создайте пул соединений и источник данных (DataSource) следуя инструкциям рецепта 21.4. 2. В коде сервлета с помощью JNDI-поиска по.1учит« источник данных (DataSource). Этот процесс включает создание экземпляра класса javax. naming. Initial- . Context (первоначальный контекст) и последующий вызов его метода lookup () с именем, которое вы присвоили своему источнику данных, в качестве параметра (рецепт 21.4). 3. Получите соединение (Connection), вызвав для этого метод getConnection () объекта DataSource. В примере 21.5 создается экземпляр класса Initialcontext, для чего конструктору этого класса передается объект Hashtable (хеш-таблица), свойства которого содержат ряд необходимых, значений. Пример 21.5. Сервлет, использующий пул соединений сервера WebLogic package com.jspservletcookbook; import java.util.Hashtable; import java.sql.*; import javax.naming.Context; import javax.naming.Initialcontext; import javax.naming.NamingException; import javax.sql.*; inport javax.servlet.*; import javax.servlet.http.*; ' . , public class WeblogicDbServlet extends HttpServlet { DataSource pool; public void init() throws ServletException { Context env null; Hashtable ht = new HashtableO; //Создаем имена/значения свойств //которые потом будут переданы //конструктору класса Initialcontext ht.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory"); // t3s//localhost:7001 - это значение, принимаемое no умолчанию. Использование JNDI-поиска для доступа к DataSource из WebLogic | 519
//При необходимости задайте собственные значения: // ht.put(Context.PROVIDER_URL,"t3://localhost:7001”); try { env > new Initialcontext(ht); pool (javax.sql.DataSource) env.lookup ( "oracle-8i-athlttes"); if (pool »" null) throw new ServletException( 'oracle-Si-athletesis an unknown DataSource11); } catch (NamingException ne) { throw new ServletException(ne); } ) public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.lOException { String sql = "select * from athlete"; Connection conn null; Statement stmt null; Remit Set rs null; ResultSetMetaData rsm null; response.setContentType("text/html"); java.io.Printwriter out = response.getWr iter(); out.printIn( "<htmlxheadxtitle>Weblogic Database Access</titlex/headxbody>") ; out.println("<h2>Database info</h2>"); Out.println("<table border='1'><tr>"); try { conn pool.getConnection(); stmt conn. Greatest at ementO ; rs stmt.executSQuery(sql); rsm rs.getMetaDataO; int colCount rsm.getColumnCount()j //печатаем имена столбцов for (int i 1; i <«colCount; ++i){ ----------_----------------------------.---.----------------------------- 520 | Глава 21. Доступ к базам данных
out.println("<th>" + rsm.getColumnName (i) + "</th>"); out.println("</tr>"); while( rs.nextOH out.printing"<tr>")j for (int i 1; i <=colCount; ++i) out.printIn("<td>" + rs.getString(i) + "</td>"); out.println("</tr>”); } catch (Exception e){ throw new ServletException(e,getMessage()); } finally { try{ if (stmt Iе null) stmt.close(); //ВОЗВРАЩАЕМ СОЕДИНЕНИЕ В ПУЛI if (conn ! null) conn.closeO; } catch (SQLException sqle){ } ) out .println("</table></bodyx/html>"); } //doGet После того как вы получили соединение (Connection) из пула соединений WebLogic, можно выполнять разные операторы SQL, взаимодействующие с соответ- ствующей базой данных. Никогда не забывайте вызывать метод close () объекта Con- nection, после того как соединение вам больше не нужно, поскольку этот метод возвращает данное совместно используемое соединение в пул. Пример 21.5 не заработает без правильно настроенных пула соединений и источника данных, что очень легко сделать с помощью консоли WebLogic (см. рецепт 21.4). Отображение данного сервлета в браузере выглядит примерно так, как показано на рис. 21.1, за исключением другого URL в поле адреса браузера (http.7/localhost:7001/ dbServlef). Использование JNDI-поиска для доступа к DataSource из WebLogic | 521
См. также (Спецификацию JDBC: http://java.sun.cdm/products/jdbc/download.html-, рецепт 21.1 по доступу из сервлета к базе данных без обращения к пулу соединений; рецепт 21.2 по конфигурированию DataSource на сервере Tomcat, рецепт 21 3 по использованию DataSource на сервере Tomcat; рецепт 21.6 по использованию DataSource страни- цами JSP в WebLogic; рецепты 21.7 и 21.8 по обращению из сервлетов и JSP к хранимым процедурам; рецепт 21.9 по преобразованию объекта java. sql. Result Set в javax. servlet. jsp. jstl. sql Result; рецепты 21.10 и 21.11 по использованию транзак- ций в сервлетах и JSP; рецепт 21.12 по выяснению информации о ResultSet. 21.6 Использование DataSource страницами JSP на сервере WebLogic Задача Вы хотите на странице JSP использовать источник данных (javax. sql .Data- Source), настроенный вами на Сервере WebLogic. / Решение На странице JSP используйте скриптлеты для доступа к DataSource с помощью JNDI-поиска, а затем, опять-таки в скриптлетах, используйте JDBC API для доступа f к базе данных. Обсуждение Страница JSP, приведенная в примере 21.6 трансплантирует код из сервлета в шаб- лонный HTML-текст. На странице используются скриптлеты, которые содержат Java- код, помещенный между символами «<% %>». * *’ На страницах JSP предпочтительнее использовать не скриптлеты, а SQL-теги JSTL; U*»' А однако та PeajlH3aiWa JSTL, которую я использую в примерах данной книги, * f ч; не позволяет обращаться к DataSource с помощью реализации JNDI-поиска сервера WebLogic. В рецепте 23.6 приведен пример использования SQL-тегов JSTL для работы с DataSource на сервере Tomcat В верхней части страницы JSP из примера 21.6 с помощью директивы раде и ее атрибута import импортируются необходимые классы. В остальных отношениях данная страница выполняет все то же самое, что и сервлет из предыдущего примера, включая идентичное отображение выходной информации в браузере (см. рис. 21.1 рецепта 21.1). 522 | Глава 21. Доступ к базам данных
Пример 21.6. Использование скриптлета JSP для доступа к Da taSource на WebLogic page imports" java.util.Hashtable, java.sql.*, javax.naming. *, javax.sql.*" 4> <html> <headxtitle>Database Query in WebLogic</titlex/head> <body> <h2>Querying a database with a JSP in WebLogic</h2> <% Context env null; DataSource pool null; Hashtable ht new HashtableO; ht.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory"); ht.put(Context.PROVIDER_DRL,"t3 s//localhost:7001"); env new Initialcontext(ht); //Ищем данный DataSouce на верхнем уровне JNDI-дерева WebLogic pool (DataSource) env.lookup ("oracle-8i-athletes"); String sql "select * from athlete"; Connection conn null; Statement stmt null; ResultSet rs null; ResultSetMetaData rsm null; Я&> <table borders' 1 ’ xtr> <4 try{ //получаем java.sql.Connection (соединение)из пула conn pool.getConnection(); stmt conn.createStatement();//create a java.sql.Statement //выполняем оператор SQL генерирующий ResultSet rs ctmt.executeQuery(sql); < rsm rs.getMetaData(); int colCount rsnugetColumnCount(); //печатаем имена столбцов for (int i « 1; i <=colCount; ++i) { Tj> <thx%=rsm.getColumnName (i)%> </th> <% } %> </tr> Использование DataSource страницами JSP на сервере WebLogic | 523
<4 while( rs.nextO ){ Ч> <tr> <% for (int i 1; 1 ocolCount; ++i) { 4> <td> <%= rs.getString(i) %> </td> <4}//for %> •</tr> <%} //while } catch (Exception e) { throw new JspException(e.getMessage()); } finally { try{ stmt.close(); conn.closeО; } catch (SQLException sqle){ } } *> < /body> </html> ПосДе того как вы с помощью консоли WebLogic правильно настроили пул соединений и источник данных, можно посмотреть на результаты, генерируемые данной стрМшцей, для этого скопируйте страницу в приложение по умолчанию сервера WebLogic, а затем из браузера запросите URL следующего вида: http:/AocaUwst:7001/sqlWeblogic.jsp: См. также Спецификацию JDBC: http://java.sun.com/products/jdbc/download.htmk, рецепт 21.1 по доступу из сервлета к базе данных без обращения к пулу соединений; рецепт 21.2 и 21.3 по использованию DataSource на сервере Tomcat; рецепты 21.4 и 21.5 по использо- ванию DataSource сервлетами на сервере WebLogic; рецепты 21.7 и 21.8 по обращению из сервлетов и JSP к хранимым процедурам; рецепт 21.9 по преобразованию объекта java.sql.ResultSet в javax.servlet, jsp. jstl.sql Result; рецепты 21.10 и 21.11 по использованию транзакций в Сервлетах и JSP; рецепт 21.12 по выяснению информации о ResultSet. 524 | Глава 21. Доступ к базам данных
21.7 Вызов хранимых процедур из сервлета Задача Требуется из сервлета вызвать хранимую процедуру. v. Решение В одном из методов сервлета (doGet () или doPost ()) используйте класс java. • sql.CallableStatement. Обсуждение Обычно хранимые процедуры создаются разработчиками баз данных для оформления SQL-кода, который, по их замыслу, должен выполняться на регулярной основе, то есть по тем же соображениям, по которым Java-разработчики оформляют некоторый код в виде метода. Хранимая процедура - это фрагмент SQL, который прекомпилируется системой управления базами данных под определенным именем. Хранимая процедура, которую я,использую в данном рецепте, называется addEvent. - Вполне естественно, что web-разработчик, работающий с базой данных, хочет иметй возможность вызывать к ранимые процедуры. Класс java. sql. Cal lableS tatement инкапсулирует некоторую хранимую процедуру, позволяя вам использовать денные инструменты (хранимые процедуры) в JDBC-коде. В табл. 21.1 приведена схема таблицы, котирую использует процедура addEvent. Дан- ная таблица имеет четыре столбца (поля): EVENT_ID, NAME, LOCATION и RACEDATE. Табл. 2L1. Схема таблицы RACEEVENT Имя Может быть пустым? Тип / EVENT.ID нс пустое NUMBER NAME не пустое VARCHAR2(30) LOCATION непустое VARCHAR2(30) RACEDATE DATE В примере 217 показано определение процедуры addEvent с использованием синтак- сиса Oracle 8/. Эта хранимая процедура принимает в качестве аргументов имя события (NAME), местонахождение (LOCATION) и дату (RACEDATE). Затем Она добавляет эти значения в качестве новой строки (записи) в таблицу RACEEVENT. Вызов хранимых процедур из сервлета | 525
w Значение для поля EVENTJD вновь создаваемой строки обеспечивает фрагмент кода, называемый последовательность (sequence), с именем log_seq. В СУБД Oracle последовательность может отслеживать длинную последовательность порядковых чисел. Разработчики баз данных создают последовательности точно так же, как они создают хранимые процедуры. Пример 21.7. Хранимая процедура, добавляющая строку в таблицу RACEEVENT create or replace procedure addEvent(eventname in varchar2, location^ in varchar2,date_ in date) as — need to do inserts in raceevent begin < insert into raceevent values(log_seq.hextval, eventname,location_,date_); end; Если вы используете какой-либо инструмент, предназначенный для работы с базами данных, например SQL PLUS, из командной строки, то вы можете вызвать процедуру addEvent следующим образом. exec addEvent('Falmouth Triathlon',‘Falmouth MA', '26-Jul-2003'); В примере 21.8 показано, как можно вызвать хранимую процедуру из сервлета. В приведенном сервлете хранимая процедура вызывается из метода doGet () в методе addRaceEvent. Этот метод принимает качестве аргумента java. util. List (список). Этот список содержит значения, которые код использует как аргументы при вызове храни- мой процедуры addEvent. Пример 21.8. Сервлет, использующий CallableStatement для вызова хранимой процедуры package com.j spservletcookbook; import import import import import import import import import import public DataSource pool; java.sql.*; java.util.ArrayLlst; java.util.List; java.util.Iterator; javax.naming.Context; javax.naming.InitialContext; j avax. naming. NamingExcept ion; javax.sql.*; javax.servlet.*; j avax.servlet.http.*; class StoredProcServlet extends HttpServlet { 526 | Глава 21. Доступ к базам данных <
public void init() throws ServletException { i Context env - null; try{ env = (Context) new Initialcontext().lookup(“java:comp/env") ; pool = (DataSource) env.lookup(*jdbc/oracle-8i-athletes") ; if (pool == null) throw new ServletException( "'oracle-8i-athletes' is an unknown DataSource"); , ) catch (NamingException ne) { throw new ServletException(ne); ) ) public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.iQ.lOException { , String eventName request.getParametar("eName"); String location request.getParam*ter("eLocation"); String date request.getParameter("eDate"); List paramList new ArrayListO; paramList. add (eventName); paramList. add (location).; paramList.add(date)t try{ addRaceEvent(paramList); } catch (SQLException sqle){ throw new ServletException(sqle.getMessage{)); }//try response.setContentType("text/html"); java.10.Printwriter out = response.getWriter(); out.println( "<htmlxhead><title>Add an Event</title></'headxbody>") ; out.printIn( ”<h2>The Event named "+ eventName + " has been added to the database</h2>"); out.printIn("</body>“); out.printin{"</html>"); } //doGet Вызов хранимых процедур из сервлета | 527
public Connection getConnection(){ ^Connection conn- = null; try{ conn = pool.getConnection!); } catch ,(SQLException sqle) { throw new ServletException(sqle.getMessagfe()); } finally { return conn; } } public void addRaceEvent(List valUob) throws SQLException! if (values == null) throw new SQLException( "Invalid parameter in addRaceEvent method."); Connection conn = null; conn = getConnection(); if (Conn == null ) throw new SQLException ( "Invalid Connection ip addRaceEvent method"); Iterator it = values.iterator(); CallableStatement cs null; //Создаем экземпляр CallableStatement cs conn.prepareCai1( "(call addEvent (?,?,?)}" ); for (int i 1; i < values.size(); i++) cs.setString(i,(String)Xit.next()); //Вызываем унаследованный метод PreparedStatement.executeVpdateO cs.executeupdate(); //возвращаем соединение в пул conn.close(); }//addRaceEvent } Сервлет из примера 21.8 получает соединение из пула соединений используя технику, описанную в предыдущих рецептах. Это соединение используется для создания объекта CallableStatement, который можно использовать для вызова хранимой процедуры. cs = conn.prepareCall( "{call addEvent (?,?,?)}" ); 528 | Глава 21. Доступ к базам данных
' Аргумент (имеющий тип String) метода prepareCall объекта Connection включает вопросительные знаки (?) на том месте, куда будут помещены параметры храни- мой процедуры. Затем код вызывает метод setstring () объекта CallableStatement для подстановки значений вместо этих знаков вопроса. И, наконец, для выполнения храни- мой процедуры addEvent вызывается метод executeUpdate() объекта Callable- Statement. Если выполнение хранимой процедуры вызовет ошибку базы данных, метод addRaceEvent выбросит исключение SQLException. Сервлет получает значения для новой строки (записи) из параметров запроса. Приве- денный ниже URL вызывает сервлет с тремя параметрами: eName, eLocation и eDate. http://localhost:8080/home/servlet/com.j spservletcookbook. StoredProcServlet ?eName= Falmouth%2 0Triathlon&eLocation=Falmouth%2 0MA&eDate=2 6-July-2 003 Отображение в браузере возвращаемой сервлетом информации приведено на рис. 21.5. Рис. 21.5. Вид в браузере выходной информации сервлета StoredProcServlet См. также Спецификацию JDBC: http://java.sun.com/products/jdbc/download.html', рецепт 21.1 по доступу из сервлета к базе данных без обращения к пулу соединений; рецепт 21.2 и 21.3 По использованию DataSource на сервере Tomcat; рецепты 21.4-21.6 по использованию DataSource сервлетами и JSP на сервере WebLogic; рецепт 21.8 по обращению из JSP к хранимым процедурам; рецепт 21.9 по преобразованию объекта java. sql. ResultSet в javax.servlet. jsp. jstl.sql Result; рецепты 21.10 и 21.11 по использованию транзакций в сервлетах и JSP; рецепт 21.12 по выяснению информации о ResultSet. Вызов хранимых процедур из сервлета | 529
21.8 Вызов хранимой процедуры из JSP Задача Требуется из страницы JSP вызвать хранимую процедуру. Решение Используя контейнер, поддерживающий JSP версии 2.0, разработайте EL-функцию (Expression Language - язык выражений), которая будет вызывать для вас хранимую процедуру. Обсуждение В JSP версии 2.0 введены функции - статические методы, которые можно вызывать из операторов EL. Если вам необходимо познакомится с языком EL, обратитесь к главе 23. В данном рецепте разъясняются этапы создания функции, которая вызывает хранимую процедуру. * ' 1. Создайте в своей СУБД хранимую процедуру.. 2. Напишите Java-класс, реализующий необходимую функцию в виде статического метода (метода класса). 3. Определите эту функцию в дескрипторе библиотеки тегов (TLD - Tag Library Descriptor), представляющем собой XML-файл конфигурации, который необходимо включить в состав web-приложения. 4. В самой JSP с помощью директивы taglib объявите библиотеку тегов, содержащую данную функцию 5. В JSP вызовите , функцию, используя префикс, соответствующий вашей библиотеке тегов. Вызов функции, которую я использую в данном рецепте выглядит примерно следующим образом. <cbck:addRaceEvent("Му Race", "Anytown USA", "ll-Dec-2003") /> В примере 21.9 приведен Java-класс, реализующий эту функцию. Пример 21.9. Java-класс, реализующий EL-функцию package com.jspservletcookbook; I import j ava.sql.*; import javax.naming.Context; import javax.naming.Initialcontext; import javax.naming.Namin^Exception; 530 | Глава 21. Доступ к базам данных
import javax.sql.*; public class StoredProcUtil { private static DataSource pool; .private static Context env; static { //статическая инициализация Context и DataSource try{ env (Context) new Initialcontext().lookup("javaгcomp/env"); z pool (DataSource) env.lookup("jdbc/oracle-8i-athletes"); if.(pool null) throw new Exception ( "'oracle-8i-athletes' is an unknown DataSource”); j catch (Exception e) { System, out.printin(e); } }//jt»*tic • /* Этот статический метод будет сконфигурирован в TLD-файле, он обеспечивает реализацию EL-функции. Пример использования функции: <cbck:addRaceEvent("Му Race","Anytown USA","ll-Dec-2003") /> */ public static void addRaceEvent(String лшпа. String location,String date) { iff (! check(name)) || (! check(location)) || (! check(date))) throw new IllegalArgumentException( "Invalid param values passed to addRaceEvent()"); Connection conn null;. try{ conn pool.getConnection(); if (conn null ) throw new SQLException( "Invalid Connection in addRaceEvent method”); • t CallableStatument cs null; //Создание'экземпляра CallableStatement cs conn.prepareCall( "{call addEvent (?,?,?)} ); cs.setString(1,name); cs.setstring(2,location); cs.setString(3,date); //Вызов унаследованного метода PreparedStatement.executeUpdateO Вызов хранимой процедуры из JSP | 531
cs. executeupdate (); // возврат соединения в пул conn.close(); } catch (SQLException sqle) { } }//addRaceEvent private static boolean check(String value){ if(value == null || value.equals("")) return false; return true; } \ f } Метод addRaceEvent () создает объект класса java. sql.CallableStatement, который вызывает хранимую процедуру addEvent. Этот процесс пояснен в рецепте 21.7. Java-метод, реализующий функцию для JSP должен быть определен как static. Данный Java-класс необходимо сохранить в каталоге WEB-INF/classes web-приложения (соблюдая структуру каталогов, соответствующую имени пакета) или в виде JAR-файла в каталоге WEB-INFAib. Так Java-класс из примера 21.9 необходимо сохранить в WEB-1NF/ classes/com/jspservletcookbook/StoredProcUtiLclass. TLD-файл, определяющий EL-функцию показан в примере 21 10 TLD-файл имеет расширение .tld и размещается в подкаталоге каталога WEB-INF web-приложения, например в WEB-INF/tlds. Пример 21.10. TLD-файл для конфигурирования EL-функции <taglib xmlns="http://java.sun.com/xml/ns/J2ee" xmlns:xsi= "http: //www.w3.org/2001/XMLSchema-instance" xsi:sKchemaLocation= " ht tp :7 / java.sun.com/xml/ns/j2ее http://java.sun.com/xml/ns/j2ee/ web-j sptaglibrary_2_0.xsd" version="2.0" <tlib-version>l. 0</tlib-version> <jsp-veraion>2.0</jsp-version> <short-name>cbck</short-name> <uri>jspservletcookbook.com.tags</uri> <description>Cookbook custom tags</description» 532 | Глава 21. Доступ к базам данных
<function> <name>addRaceEvent</name> <function-class» Com. j spservletcookbook.StoredProcUtil «/function-class» < function-signature» void addRaceEvent(java.lang.String, j ava.lang.String,j ava.lang.String) </function-signature> </function» I <tag> <!— определяйте пользовательский тег здесь, если необходимо —> , </tag> </taglib» В примере 21.10 показано определение функции с помощью тега function, и его атрибутов name, function-class, и function-signature. Убедитесь в том, что в элементе function-class вы указали полностью квалифицированное имя класса. Контейнер JSP узнает, как вызвать функцию, анализируя,элемент function-signa- ture. Этот элемент содержит сигнатуру функции, включая тип возвращаемого значения (в данном случае «void»), имя функции, и все ее параметры, заданные полностью квалг фицированными именами классов. В примере 21.11 приводится страница JSP, которая вызывает определенную нами функцию. Сначала идет директива taglib, которая определяет библиотеку классов и префикс («cbck»), используемый функцией. Пример 21.11. Страница JSP, использующая EL-функцию для вызова хранимой процедуры taglib uri«"jspservletcookbook.com.tags" prefix»"cbck" 4> <html> <headxtitle>Calling a Stored procedure</titlex/head> <body> <h2>This JSP calls a stored procedure with a JSP 2.0 function</h2> ( ${cbck:addRaceEvent("Falmouth Triathlon","Falmouth MA","26-Jul-2003")} </body> </html>' В соответствии c EL, синтаксис инкапсулирует вызов функции внутри строки «${ }». За открывающей скобкой идет префикс («cbck»), используемый данной функцией, двоеточие и собственно вызов функции. ${cbck:addRaceEvent("Falmouth Triathlon","Falmouth MA","2 6-Jul-2003")} Вызов хранимой процедуры из JSP | 533
Поначалу этот процесс может показаться несколько сложным, но после того как вы сде- лаете свою первую функцию JSP 2.0, все пойдет гораздо легче! Этот процесс не требует чего-то большего, чем создание статического Java-метода, конфигурирования функции подходящими значениями в XML-файле и затем вызова функции из JSP. Это отличный способ вызова хранимых процедур! См. также Спецификацию JDBC: http://javcLsun.com^roducts/jdbc/downloa(Lhtml‘, главу 23 по JSTL; главу 22 по созданию библиотек пользовательских тегов; рецепт 21.1 по доступу из сервлета к базе данных без обращения к пулу соединений; рецепт 21.2 и 21.3.по исполь- зованию DataSource на сервере Tomcat; рецепты 21.5 и 21.6 по использованию Data- Source сервлетами и JSP на сервере WebLogic; рецепт 21.7 по обращению из сервлета к хранимым процедурам; рецепт 21.9 по преобразованию объекта j ava. sql. Resul tSet в javax.servlet. jsp. jstl.sql Result; рецепты 21.10 и 21.11 по использованию транзакций в сервлетах и JSP; рецепт 21.12 по выяснению информации о Resul tSet. 21.9 Преобразование объекта java.sql. ResultSet к javax.servlet.jsp.jstLsql.Result Задача Требуется преобразовать объект java.sql.ResultSet к объекту javax.serv- let . jsp. j st 1. sql.Result, поскольку последний можно использовать с JSTL. Решение Исполвзуйте метод javax. servlet. jsp. j stl. sql. ResultSupport. toRe- sult(). i \ Обсуждение Интерфейс Result позволяет коду работать с объектами класса ResultSet как с массивами (array) или с картами (j ava. util.Мар). Теги JSTL часто используют мас- сивы или карты для перебора содержащихся в них значений (именно поэтому в специфи- кацию JSTL включен интерфейс Result). Таким образом, у вас может возникнуть необходимость преобразовать ResultSet к Result и затем работать с Result на странице JSP, использующей JSTL-теги. 534 | Глава 21. Доступ к базам данных
В примере 21.12 приведен сервлет; который: 1 .' в результате запроса к базе данных создаёт ResultSet; 2 - "‘ преобразует ResultSet KResult; 3; передает Result дальше к JSP, сохраняя Result в качестве атрибута сеанса. Пример 21.12. Сервлет, преобразующий ResultSet к Result package com.j spservletcookbook; import java.sql.*; import javax.naming.Context; import javax.naming.InitialContext; > import javax.naming.NamingException; import javax.sql.*; | M import javax.servlet.jsp.jstl.sql.Result; import j avax.servlet.j sp»j stl.sql.Resultsupport; import j avax.servlet.*; import j avax.servlet. http.*; public class DbServletResult extends HttpServlet { DataSourc^ pool; \ r .. л public void iftit()() throws ServletException { Context env = null; try{ env = (Context) new InitialContextO .lookup("java:comp/env"); pool = (DataSource) env.lookup("jdbc/oracle-8i-athletes"); if (pool == null) throw new ServletException( "'oracle-8i~athletes* is,an unknown DataSource"); } catch (NamingExcdption ne) { / throw new ServletException(ne); -4 i } }7/init public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.lOException { String sql = "select * from athlete"; try{ //Получаем объект Result, представляющий результат выполнения Преобразование объекта iava.sql.ResultSet к javax.servlet.jspjstl.sql.ResuIt | 535 .
11 SQL-оператора 'select * from athlete' Result jspResult select(sql); HttpSession session « request .getSession(); //сохраняем Result в атрибуте сеанса, //чтобы передать его //в JSP и использовать с JSTL-тегами session.setAttribute( "javax.servlet.jsp.jstl.sql.Result",jspResult); RequestDispatcher dispatcher « request-getRequestDispatcher( " /useResult. j sp'*); di spatcher.forward(request,response); } catch (SQLException sqle){ A thtow new ServletException(sqle.getMesSage()).;} } I/doGet private Result select(String sql) throws SQLExceptionf if (sql == null || sql.equalsC")) throw new SQLException("Invalid parameter1 in select method"); ResultSet rs null; * Connection conn null; Result res = null; //Получаем соединение (Connection) из пула conn pool.getConnection(); if (conn null ) throw new SQLException("Invalid Connection in select method"); PreparedStatement stmt “ conn.preparestatement (sql) ; //Создаем ResultSet rs stmt. execut eQuery (); //Преобразуем объект ResultSet к объекту //Result который можно использовать с тегами JSTL res*ResultSupport.toResult(rs); stmt.close();//3To закрывает любые связанные с запросом ResultSet conn.close();//возвращаем соединение(Connection) в пул return res;//возвращаем объект Result }//select ) 536 | Глава 21. Доступ к базам данных
Сервлет из примера 21.12 импортирует необходимые Java-классы, включая Result и Resultsupport. * import javax.servlet.j sp.j stl.sql.Result; import javax.servlet.jsp.jstl.sql.Resultsupport; Метод select () делает важную работу: создает Result Set, преобразует этот объ- ект к Result и возвращает объект Result. Ниже представлен код, выполняющий преобразование. res=ResultSupport.toResult(rs); Статический метод toResult () класса Result Support принимает в качестве аргумента Result Set и возвращает Result. Затем в методе doGet () сервлета из объекта Result создается атрибут сеанса и с помощью диспетчера запроса (RequestDispatcher) запрос переадресуется странице JSP. JSP с именем useResultjsp. «г* г*Л— %. Сначала пользователь обращается из браузера с запросом к сервлету, а сервлет уже передает запрос странице JSP. После ' чего пользователь видит в браузере * * £ выходную информацию этой страницы JSP. Код, работающий с диспетчером запросов, выглядит следующим образом. RequestDispatcher dispatcher = request.getRequestDispatchert "/useResult.jsp"); dispatcher, forward (request, response) Страница JSP, приведенная в примере 2143, использует теги ядра JSTL (с префиксом «с»). Тег с: set получает доступ к атрибуту сеанса и сохраняет значение атрибута в переменной resultObj. Затем Jc помощью тегов с: forEach и с:out страница JSP отображает значения из базы данных. Пример 21.13. Страница JSP, использующая объект Resul t, сохраненный в атрибуте сеанса <%® taglib uri«"http://java.sun.com/jstl/core" prefix="c" 4> taglib uri""http://java.sun.com/jstl/sql" prefixj"sql" %> <html> <HEAD> \ <TITLE>Using a Result object</TITLE;* . '</HEAD> <body bgcolor="white"> <h2>View Database Data</h2> <%—сохраняем атрибут сеанса .(объект Result) в переменной 'resultObj'—Яь> <c:set var="resultObj" values "${sessionscope[\"javax.servlet.jsp,.jstl.sql.Result\"]}" /> xtable border="l" cellspacing="2"> --------------------,--:_________________I____________________________________ Преобразование объекта java.sql.ResultSet к javax.servlet.jsp.jstl.sql.Result | 537
<%— для каждой строки (записи) из Result ...—%> <c:forEach items»"${resultObj.rows}" var*wrown> <%— для каждого поля строки ... —%> <c:forEach items»"${row}" var«"column"> <tr> <td align="right"> v <b> <c:out value»"${column.key}" /> </b> </td> <td> <c:out value»"${column.value}" /> </tdx/tr> </c s forEach> </c:forEach> </table> </body> </html> Синтаксис «${sessionScope[\”javax.servlet.jsp.jstl.sql.Result\"]}» необходим из-за того, что имя атрибута сеанса включает в себя точки (.). В ином случае, если бы, к примеру, f X; атрибут носил имя myAt tribute, EL-синтаксис для доступа к нему был бы проще ${myAttribute} На рис. 21.6 показано, как браузер отображает выходную информацию этой страницы JSP. См. также Спецификацию JDBC: http://java.sun.com/products/jdbc/download.html; главу 23 по JSTL; главу 16 по использованию атрибутов сеанса; рецепт 21.1 по доступу из сервлета к базе дан- ных без обращения к пулу соединений; рецепт 2L2.H 21.3 по использованию DataSource на сервере Tomcat; рецепты 21.5 и 21.6 по использованию DataSource сервлетами и JSP на сервере WebLogic; рецепт 21.7 и 21.8 по обращению к хранимым процедурам из сервле- тов и JSP; рецепт 21.9 по преобразованию объекта java.sql.ResultSet в javax. servlet. j sp. j stl. sql Result; рецепты 21.10 и 21.11 по использованию транзакций в сервлетах и JSP; рецепт 21.12 по выяснению информации о ResultSet. I 1 а 538 | Глава 21. Доступ кбазаМ данных ।
Рис. 21.6. Вид в браузере выходной информации страницы JSP 21.10 Выполнение нескольких SQL- операторов в рамках одной транзакции Задача Требуется в рамках одной транзакции выполнить более одного оператора SQL. Решение Для создания транзакции используйте программный интерфейс java.sql .Con- nection и методы setAutoCommit (), commit () и rollback (). Обсуждение Некоторые операторы SQL, например те, что изменяют информацию о клиенте в двух разных таблицах базы данных, должны выполняться только как единое действие, как группа. Если один из таких операторов выполняется с ошибкой, база данных возвращается в состояние, предшествующее выполнению всей группы операторов. Именно в этом Выполнение нескольких SQL-операторов в рамках одной транзакции | 539
заключается цель использования транзакций в Java-коде. Транзакция - логическое объеди- нение нескольких операций над базой данных, для которого можно произвести «откат» (roll back), то есть отменить результаты выполнения всей группы, если одна из входящих в нее операций выполнилась с ошибкой. После того как вы получили соединение с базой данных (экземпляр класса j ava. sql. Connection), вы можете использовать различные методы этого класса для созда- ния транзакции. Ниже приведены шаги по созданию транзакции. 1. Вызовите метод setAutoCommit () объекта класса Connection, передав ему в качестве параметра false. Это отключит поведение JDBC-кода, принятое по умолчанию, которое заключается в фиксации (commit) каждого SQL-оператор в отдельности, вместо того, чтобы автоматически группировать последовательно выполняемые операторы в одну транзакцию.’ 2. Вслед за методом setAutoCommit () выполните весь код, работающий с базой дан- ных, который вы хотите сделать единой транзакцией. 3. Вызовите метод commit () объекта Connection, чтобы фиксировать в соответ- ствующем файле базы данных выполненные SQL-операторы, которые произвели какие-либо изменения в базе данных (например операторы DELETE или UPDATE). 4. В той части Java-кода, что предназначена для обработки ошибок или нештатных ситуаций, например в блоке catch, вызовите метод rollback () объекта Connec- tion, который произведет откат назад всех операторов SQL, включенных в данную транзакцию. В примере 21.14 приведен сервлет, иллюстрирующий этот процесс. Пример 21.14. Сервлет, использующий SQL-транзакцию package com.jspservletcookbook; import Java.sql.*; import javax. naming. Cont ext; import javax.naming.Initialcontext; import javax. naming. NamingExcept ion; import javax.sql.*; import javax.servlet.*; import j avax.servlet. http.*; public class DbServletTrans extends HttpServlet { DataSource pool; /•Инициализируем DataSource в методе iriit() сервлета этот метод контейнер сервлетов вызывает всего один раз когда он создает экземпляр этого сервлета */ public void init() throws ServletException { Context env = null; try{ 540 | Глава 21. Доступ к базам данных
env = (Context) new InitialContext().lookup("java:comp/env"); pool = (DataSource) enV.lookup(" jdbc/oracle-8i-athletes") ; if (pool == null) > throw new ServletException( "'pracle-8i-athletes1 is an unknown DataSource"); } catch (NamingException ne) { throw new ServletException(ne); } }//init public void doGet(HttpServletRequest request,. HttpServletResponse response) throws ServletException, java.io.lOException { Connection conn = null; Statement stmt = null; response.setContentType("text/html"); java.io.Printwriter out = response.getWriter(); out.printin( "<htmlxheadxtitle>Using transactions</titlex/headxbody>") ; out.println( "<h2>These SQL statements are part of a transaction/h2>"); out.prihtln("CallableStatement.executeUpdate()"); out. print In (" <brxbr>"); out.printIn("Statement.executeUpdate()"); out. print In (" <brxbr>"); try{ //Получаем соединение из пула conn = pool.getConnection(); //Отображаем значения по умолчанию для setAutoCommit() //уровень изоляции ’ out. print In ("AutoCoramit before setAutoCcsnmit (): " + corm, get AutoConnnit () + > "<brxbr>" ) ; out.printIn("Transaction isolation level: "); //просто из любопытства, отображаем текущий уровень // изоляции транзакции switch(conn.getTransactionIsolation()){ case 0 : out.println("TRANSACTION_NONE<br><br>"); break; " .. ' ' 1 ' ' 11 ' ' 1 , 1 т. . I I I" I p Выполнение нескольких SQL-операторов в рамках одной транзакции | 541
case 1 : out.printIn( "TRANSACTION_READ_UMCOMMITTED<brxbr>") ; break; case 2 : ou t. print In ( "TRANSACTION_BEAD_COMMITTED<brxbr> " ) ; break; case 4 : out.println( ,,TRANSACTION_REPEATABLE_READ<br><br>") ; break; * case 8 : out.println( < "TRANSACTION_SERIALIZABLE<brxbr>"); break; default: out .println ( "UNKNOWN<brxbr>" ) ; }//switch . ' //установите Autoconmit я false, чтобы отдельные SQL-операторы не //фиксировались, до тех пор пока //не будет вызван метод Connection.commit() conn. netAutoCommit ( false); //Начинается SQL, связанный с транзакцией... CallableStatement cs = null; //Создаем экземпляр CallableStatement ' cs = conn.prepareCall( "{call addEvent (?,?,?)}" I; cs.setStringd, "Salisbury Beach 5-Miler"); cs.setstring(2,"Salisbury MA"); cs.setStringd, " 14-Aug-2003") ; //Вызываем унаследованный метод PreparedStatement.executeUpdate() cs.executeUpdate(); String(sql = "update raceevent set racedate=113-Aug-2003’ "+ ."where name=•Salisbury Beach 5-Miler1"; int res = 0; stmt conn.createStatement <); res = stmt.executeUpdate(sql); //фиксируем эти два оператора SQL conn.commit(); } catch (Exception e){ t try{ //"откат" транзакции в случае возникновения проблем conn. rollback (•); } catch (SQLException sqle){ } throw new ServletException(e.getMessage()); 542 | Глава 21. Доступ к базам данных
} finally { try{ if (stmt != null) stmt.close(); if (conn != null) conn.close(); } catch (SQLException sqle){ } } out.println (" </ tablex/bodyx/html> "); } //doGet } Метод doGet () из примера 21.14 отображает значения, принятые по умолчанию для «автоматической фиксации» (Autocommit) SQL-операторов и уровень изоляции транзакции (уровень блокирования базы данных, устанавливающийся, когда начинается выполнение транзакций в вашем Java-коде). Если, к примеру, ваши SQL-операторы изменяют некоторые поля базы данных, могут ли остальные пользователи базы данных просматривать новые значения столбцов до того, как ваша транзакция будет фиксирована? Если это разрешается, такой тип поведения называется «черновое чтение» (dirty read). В табл. 21.2 приведены различные типы уровней изоляции транзакции, от наименее строгого до наиболее строгого. Но прежде чем вы начнете знакомиться с этой таблицей, необходимо ввести два новых понятия. Невоспроизводимое чтение (non-repeatable read) - ситуация, когда одна транзакция читает некоторую строку, другая транзакция изменяет ту же самую строку и первая транзакция читает ту же строку и получает другое значение. Фантомное чтение (phantom read) - ситуация, когда одна транзакция получает результирующее множество данных на основе WHERE-условия, а вторая транзакция добавляет новую строку, удовлетворяющую этому WHERE-условию. Затем первая транзакция снова делает выборку из той же таблицы базы данных с тем же WHERE- условием и извлекает новую «фантомную строку». ' *’ Ознакомьтесь со спецификацией от поставщика вашей базы данных или с соответствующей литературой и выясните, как используемая вами база данных __Д_1 $ обрабатывает уровни изоляции транзакции. С помощью метода getTrans- actionlsolation() объекта Connection определите, какое значение связано с конкретным драйвером базы данных, используемым кодом, работающим с JDBC. Этот метод возвращает целое число. Например, значение «2» означает, что соединение (Connection) связано с уровнем изоляции транзакции TRANSACTION_READ COMMITTED. _______________._____________:________X—___________________ Выполнение нескольких SQL-операторОЬ в рамках одной транзакции | 543
Табл. 21.2. Уровни изоляции транзакции Уровень изоля- ции транзакции Значение, возвращае- мое методом java^ql. Connection. getTrans- actionlsoIationO Определение TRANSACTION. NONE 0 Драйвер базы данных не поддерживает транзакции TRANSACTION. READ. UNCOMMITTED 1 • Другие транзакции могут видеть нефиксированные изменения; допуска- ется «черновое чтение» TRANSACTION. READ COMMITTED 2 Нефиксированные изменения невидны другим транзакциям TRANSACTION. REPEATABLE. READ 4 Нефиксированные изменения не видны другим транзакциям; невоспроизводимое чтение также запрещено TRANSACTION. SERIALIZABLE 8 Нефиксированные изменения не видны другим транзакциям; невоспроизводимое чтение и фантомное чтение запрещены I В примере 21.14 в одну транзакцию входят два оператора SQL: выполняется хранимая процедура и оператор UPDATE. Затем в коде вызывается метод commit () объекта Con- nection для фиксации изменений, произведенных в базе данных. Если этот SQL-код выбрасывает исключение, осуществляется откат транзакции с помощью вызова метода rollback () объекта Connection. Вызов этого метода аннулирует работу предше- ствующих операторов SQL. На рис. 21.7 показано отображение в браузере выходной информации сервлета из примера 21.14. См. также Спецификацию JDBC: http://java.sim.coni/products/jdbc/download.htmP, рецепт 21.1 по доступу из сервлета к базе данных без обращения к пулу соединений; рецепт 21.2 и 21.3 по использованию DataSource на сервере Tomcat; рецепты 21.4-21.6 по использованию DataSource сервлетами и JSP на сервере WebLogic; рецепт 21.7 и 21.8 по обращению к хранимым процедурам из сервлетов и JSP; рецепт 21.9 по преобразованию объекта java. sql. Resul tSet в javax. servlet. j sp. j stl. sql Result; рецепт 21.11 по исполь- зованию транзакций в JSP; рецепт 21.12 по выяснению информации О ResultSet. Л 544 | Глава 21. Доступ к базам данных
Рис. 21.7. Вид в браузере выходной информации сервлета, выполняющего транзакцию 21.11 Использование транзакций в JSP Задача Требуется из страницы JSP выполнить операторы SQL в составе транзакции. Решение Используйте JSTL-тег sql: transaction. Обсуждение JSTL включает тег' sql: transaction, который выполняет любые вложенные в него SQL-действия (например, sql: update) как одну транзакцию. Тег sql:transaction использует те же методы java.sql.Connection, которые вы бы использовали в сервлете, работающем с транзакциями (рецепт 21.10): f setAutoCommit (false), commit () и rollback (). 1S-154S Использование транзакций в JSP | 545
Страница JSP из примера 21.15 использует источник данных (DataSource), настроенный ъыеЬ.хтЦ таким образом, никакая информация, связанная с базой данных, р JSP нс присутствует. Описание того, как настроить источник данных в дескрипторе развертывания, приведено в рецепте 23.6. SQL-операторы INSERT и SELECT, вложен- ные в тег sql: transaction, составляют одну транзакцию и при возникновении каких-либо проблем при выполнении этой транзакции, внесенные ими обоими измене- ния будут аннулированы (произойдет откат). Пример 21.15. Страница JSP, выполняющая SQL-операторы INSERT и SELECT в одной транзакции <Яб@ taglib uri»"http://java-sun.com/jstl/core" prefix‘>"c" Ч> taglib uri»"http://java.eun.com/jstl/-ql" prefix="sql" %> <html> > <HEAD> <TITLE»Using a Transaction with a JSP</TITLE> </HEAD> <body bgcolor="white•> <h2>View Athlete Data</h2> <sql: transaction» <sql:Update> insert into athlete values(2, 'Rachel Perry,’rlpbwpl996', '24-Feb-1996','F') </uql:update» <sql: query var« 'resultObj * > select * from athlete </sql:query> </sql:transaction» <table> <c:forEach items="${resultObj.rows}" var=*row"» <C:forEach items="${row}" var="column"> <tr> <td aligns"right"» <b»<c:out value="${column.key}" /></b> </td> <td> <c:out values"${column.value}" /> </td></tr> </c:forEach» t </c:forEach» </table> </body> </html> 546 | Глава 21. Доступ к базам данных
После выполнения SQL-операторов* входящих в транзакцию, JSP отображает изменен- ные значения таблицы базы данных. Содержимым тегов sql:update и sqlrquery являются обычные операторы SQL. Не забудьте включить правильную директиву taglib, чтобы можно было использовать библиотеку sql-тегов JSTL 1.0. Тег sql: transaction также имеет атрибут isolation, в котором вы можете задать уровень изоляции для транзакции (см. рецепт 21.10). <sql:transaction isolation="TRANSACTION_READ_COMMITTED"> SQL-операторы и теги располагаются здесь... —%> </sql: transaction:* На рис. 21.8 показана выходная информация файла sqlTrans.jsp. Рис. 21.8. Страница JSP, отображающая измененную таблицу базы данных Использование транзакций в JSP | 547 18*
См. также Спецификацию JDBC: http://java.sun.com/products/jdbc/download.html-, главу 23 по JSTL; рецепт 21.1 по доступу из сервлета к базе данных без обращения к пулу соединений; рецепт 21.2 и 21.3 по использованию DataSource на сервере Tomcat; рецепты 21.4-21.6 по использованию DataSource сервлетами и JSP на сервере WebLogic; рецепт 21.7 и 21.8 по обращению к хранимым процедурам из сервлетов и JSP; рецепт 21.10 по использо- ванию транзакций в сервлетах; рецепт 21.12 по выяснению информации о ResultSet. 21.12 Получение Информации о ResultSet Задача Требуется динамически получить информацию о строках (записях) и столбцах (Полях) результирующего множества данных (j ava. sql. ResultSet). X Решение Используйте класс ResultSetMetaData, получаемый в результате вызова метода java.sql.ResultSet's getMetaData(). Обсуждение Web-разработчикам иногда приходится работать с таблицами баз данных, имена и типы полей которых им заранее не известны. Пакет java.sql включает очень полезный интерфейс Result SetMetaData, который определяет методы, предназначенные для получения информации о java.sql .ResultSet. Объект ResultSet инкап- сулирует строки (записи), возвращаемые в результате выполнения SQL-оператора SELECT. В примере 21.16 приведен сервлет, получающий ResultSet из базы данных Oracle 8/ и затем отображающий имена столбцов, индекс столбца, SQL-тип столбца и количество символов, необходимых для отображения значений каждого столбца. Пример21.16. Сервлет, использующий класс ResultSetMetaData package com.j spservletcookbook; import j ava. sql. import j avax.naming.Context; import j avax.naming.Initialcontext; import javax.naming.NamingException; import j avax.sql.*; import j avax.servlet.*; import javax.servlet.http.*; 548 | Глава 21. Доступ к базам данных
public class DbMetaServlet extends HttpServlet { DataSource pool; , I /♦Инициализация исФочниха данных (DataSource) в методе init() этот метод контейнер сервлетов вызывает всего один раз, когда он создает экземпляр этого сервлета */ public void init() throws ServletException { Context env = null; try{ env = (Context) new Initialcontext().lookup(“java:comp/env"); pool = (DataSource) env.lookup("jdbc/oracle-8i-athletes"); if (pool == null) throw new ServletException( "'oracle-8i-athletes' is an unknown DataSource"); } catch (NamingException ne) { 4 throw new ServletException(ne); } }//init public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.lOException { String sql = "select * from athlete"; Connection conn = null; Statement stmt = null; ResultSat rs = null; ResultSetMetaData rsm null; response.setContentType("text/html"); java.io.Printwriter out = response.getWriterO ; out.printlnf "<htmlxheadxtitle>Discover a ResultSet</titlex/headxbody>") ; out.printIn("<h2>Here is Info about the returned ResultSet</h2>"); out.printIn("<table border=•1'><tr>"); try{ //Получаем соединение из пула conn = pool.getConnection(); ( ! //Создаем объект Statement (оператор) для выполнения SQL stmt = conn.createStatement(); Получение информации о ResultSet | 549
11 Выполняем SQL-оператор rs = stmt.executeQuery(sql); //Получаем объект ResultSetMetaData от ResultSet rsm rs.getMetaData(); int colCount rim.getCplumnCount(); //печатаем имена столбцов printMeta (rsm, "name", out, colCount:); //печатаем индекс каждого столбца printMeta (rsm, " index" , out, colCount) j ’ // печатаем тип каждого столбца > printMeta(rsm, "column type",out,colCount) l H печатаем размер поля, необходимого // для отображения каждого столбца printMeta (rsm, "column display*, out, colCount).; } catch (Exception e){ throw new ServletException(e.getMessage()); } finally { try{ stmt.close(); conn.close(); } catch (SQLException sqle) { -} } out.println (" < /tablex/bodyx/html> ") ; } //doGet private void printMeta (ResultSetMetaData metaData, String type, java.io.Printwriter out, int colCount) throws SQLException { if (metaData == null || type == null || out == null) throw new IllegalArgumentException( "Illegal args passed to printMeta ()"'); out.println("<tr>"); if (type.equals("table")){ out. print In ("<tdxstrong>Table name</strongx/td>"); for (int i 1; i <«colCount; ++i){ out.println("<td>" + metaData.getTableName(!) + "</td>"); } 550 | Глава 21. Доступ к базам данных
} else if (type.equals ("'name")) { out .prxntln("<tdxetrong>Coluinn name</strongx/td>"); for (int i = 1; i <=colCount; ++i){ out.printIn("<td>" + metaDatu.getColunmName(i) + "</td>"); } } else if (type.equals("index")){ out .printin("<tdxstrong>Column index</strongx/td>"); for (int i a 1; i <«=colCount; ++i){ out.printIn("<td>" + i + "</tdJ"); } } else if (type.equals("column type")){ out .printIn("<tdxstrong>Coluinn type</strongx/td>"); for (int i a 1; i <=colCount; ++i){ out.printIn("<td>" + metaData.getColumnTypeName(i) + "</td>"); } } else if (type.equals("column display")){ put .printin ("<tdxstrong>Column display size</strongx/td>"); for (int i 1; i <=colCount; ++i){ out.printin("<td>" + xnetaData.getColumnDisplaySize(i) + "</td>"); } out.printIn("</tr>"); }//printMeta } В сервлете из примера' 21.16 для получения информации о каждом из столбцов результирующего множества (ResultSet) используются методы класса ResultSet- MetaData. В коде эти методы вызываются внутри метода printMeta (). К примеру, код. metaData.getColumnName(1) возвращает имя первого столбца, определяемое схемой таблицы, например «USER_ID». На рис.21.9 показан вид в браузере генерируемого этим сервлетом HTML-кода. Получение информации о ResultSet | 551
.s—i. Puc. 21.9. Сервлет, отображающий метаданные о ResultSet «а ' •’ Для получения большого количества информации о СУБД, с которой связано уа’4 соединение (java.sql.Connection), используемое в коде, используйте f ft интерфейс java.sql.DatabaseMetaData. Объект, реализующий интерфейс DatabaseMetaData, возвращает метод getMetaData () объекта Connection. См. также Спецификацию JDBC: http://java.sun.com/products/jdbc/doyvnload.html-, Класс Result- SetMetaData: http://java.sun.eom/j2se/l.4.1/docs/api/java/sql/ResultSetMetaData.html-, рецепт 21.1 по доступу из сервлета к базе данных без обращения к пулу соединений; рецепт 21.2 и 21.3 по использованию DataSource на сервере Tomcat; рецепты 21.4-21.6 по использованию DataSource сервлетами и JSP на сервере WebLogic; рецепт 21.7 и 21.8 по обращению к хранимым процедурам из сервлетов и JSP; рецепт 21.10 и 21.11 по исполь- зованию транзакций в сервлетах и JSP. 552 | Глава 21. Доступ к базам данных
ГЛАВА 22 Использование пользовательской библиотеки тегов 22.0 Введение Исключительно мощной особенностью технологии JavaServer Pages является воз- можность создания собственных XML-тегов, которые можно использовать на страни- цах JSP. Пользовательские теги стали частью спецификации JSP, начиная с версии 1.1. В спецификации JSP версии 2.0 процесс создания пользовательских тегов стал проще по сравнению с прошлыми версиями. Заметной частью курса на упрощение стало вве- дение в версии 2.0 упрощенных обработчиков тегов и файлов тегов (tag files), с которыми мы познакомимся в рецептах 22.8-22.14. Но перед тем как мы перейдем к рецептам, Связанным с пользовательскими тегами, определим ряд терминов. Тег - это экземпляр XML-элемента и член опреде- ленного пространства имен. Так, например, все теги, связанные с данной книгой, имеют префикс cbck. Страница JSP ссылается на отдельный тег, связанный с пространством имен cbck (например, шуТад), следующим образом. <cbck:myTag>whatever this tag does...</cbck:myTag> / Теги - это XML-элементы; таким образом, имена и атрибуты тегов чувствительны к регистру символов. Набор тегов, обеспечивающих сходную функциональность или логически связанных друг с другом, называют библиотекой тегов. Разработчики могут инсталлировать в web-приложение одну или более библиотек тегов. Функциональность тега в JSP обеспечивает Java-класс, называемый обработчик тега. Пользовательское действие - это тег, который вы создали для применения в JSP и который поддерживается за сценой объектом-обработчиком тега, этот объект web-контейнер держит в памяти. Классический обработчик тега * использует дополнительный программный интерфейс (API) для работы е тегами, который развивался от версии 1.1 до версии 1.2 спецификации JSP. Упрощенный обработчик тега - это Java-класс, реализующий интерфейс SimpleTag, введенный в JSP 2.0. Файл тега определяет пользовательский тег, используя для этого синтаксис JSP. Он придуман для облегчения жизни разработчиков тегов. Web-контейнер генерирует из этого файла тега Java-класс, реализующий интерфейс SimpleTag, а затем соз- дает объект этого класса, для интерпретации использования тега на страницах JSP. 553
И, наконец, дескриптор библиотеки тегов (TLD) - это XML-файл, обеспечивающий отображение между ссылками на библиотеки тегов в JSP (с помощью директивы taglib) и классами библиотеки тегов, которые вы инсталлировали в web-приложении. TLD - это файл конфигурации, наподобие дескриптора развертывания web-приложения. В рецептах данной главы приведены примеры создания классических обработчиков тегов, упрощен- ных обработчиков тегов и файлов тегов. Здесь также показано, как поместить эти компо- ненты в web-приложения. 22.1 Создание классического обработчика тега Задача Вы хотите создать классический (в стиле JSP версии 1.2) обработчик для пользова- тельского действия. Решение Создайте Java-класс, расширяющий один из классов поддержки создания тегов из пакета javax. servlet .jsp. tagext, например BodyTagSupport. Обсуждение Существует множество типов пользовательских тегов, которые вы можете создать для JSP, например действия без тела (пустые теги), действия, вложенные в другие поль- зовательские действия, и пользовательские действия, использующие Содержимое тела тега. На самом деле, теме создания пользовательских тегов посвящены целые книги! В этой книге я ограничусь демонстрацией простого классического тега, который добав- ляет на страницу JSP изображение и текстовое сообщение, составляющие логотип Изучив детали этого примера, вы сможете решать свои собственные задачи. Тег, предлагаемый в качестве примера, предназначен для того, чтобы дизайнер мог задать изображение логотипа, его ширину и высоту, а также сопроводительную над- пись, размещаемую рядом с изображением. В примере 22.1 приведен классический обработчик тега для этого пользовательского действия. Этот Java-класс расширяет класс BodyTagSupport, так как он использует содержимое, вложенное в тег (в качестве надписи для логотипа). Пример 22.1. Классический обработчик тега, добавляющий логотип и надпись package com.jspservletcookbook; import j avax.servlet.*; import javax.servlet.http.*; f import javax.servlet.jвр.* ; inport javax.servlet.jsjp.tagext.*; 554 | Глава 22. Использование пользовательской библиотеки тегов
/** Этот тег генерирует уменьшенное изображение, используя HTML-тег img, и вслед за ним - текстовое сообщение. Пользователь задает текст сообщения и уровень заголовка(то есть, <Н1>-<Н6>) */ public class LogoTag extends BodyTagSupport { //Эти переменные представляют атрибуты тега private String heading null; private String image =null; private String width =null; *** private String height =null; //этот метод предполагает, что значения атрибутов заданы. public int doStartTagO throws JspException{ try { • int h new Integer(heading).intValue()(); if(I (h > 0 && h < 7)) throw new JspException( "The 'heading' attribute value must between 1 and 6 inclusive."); } catch (Exception e) { throw new JspException(e.getMessage()); } return EVAL_BODY. BUFFERED; } public int doEndTag() throws JspException { JspWriter out = pageContext.getOutO; //каталог 'images' находится в корневом каталоге //web-приложения String imgDir ((HttpServletRequest) pageContext. getRequestO) .getContextPath() + "/images/"; //получаем текст, расположенный между открывающим //и закрывающим тегами данного пользовательского действия String message « getBodyContent().getString().trim(); try{ //build the HTML img tag > x out.println("<img src=\""+ imgDir + image + "\" width«\"" + width ♦ "\" height=\"" + height + -\" aligna\"left\">" ♦ "<H" ♦ heading +' ">" + message + "</H" + heading* ">"); } catch (java.io.lOException io) {} return EVAL_PAGE; } //doEndTag , Создание классического обработчика тега | 555
//методы, предназначенные для установки значений атрибутов public void setHeadingtString level){ this.heading= level; ) public void setImage(String name) { , . i this.image = name; }. public void setwidth(String width){ this, width = width; } public void setHeight(String height){ this.height = height; / } //контейнер JSP может кэшировать и использовать повторно //объекты - обработчики тегов. Этот метод освобождает //переменные экземпляра обработчика с тем, чтобы // обработчик тега мог быть использован повторно, public void release(){ heading = null; image =null; width =null; height =null; }// releaoe } Классические обработчики тегов подобны компонентам JavaBean. Вы объявляете атрибуты пользовательского тега как переменные экземпляра, или свойства, и опреде- ляете для каждого из атрибутов метод-установщик (setter). Если вам требуется только обработать тело пользовательского тега, определите метод doEndTag () (). Когда кон- тейнер JSP вызывает doEndTag () содержимое тела тега уже известно и разработчики могут использовать этот метод для получения содержимого тела, которое пользователь тега поместил между открывающим и закрывающим тегами. В примере 22.1 также определен метод doStartTag (), который в данном случае проверяет, присвоил ли пользователь тега корректное значение атрибуту header (число от одного до шести). В момент, когда вызывается метод doStartTag (), значения атрибутов, установленные пользователем, уже доступны, а вот содержимое тела тега - еще нет. 556 | Глава 22. Использование пользовательской библиотеки тегов
Метод doEndTag () использует значения разных атрибутов тега для построение тегов img и Н, которые приводят к отображению на странице JSP (на которой использу- ется наш пользовательский тег) простого логотипа. Ниже приводится пример того, как действие, определенное этим обработчиком тега, можно использовать в JSP. <%— импортируем библиотеку тегов с помощью директивы 'taglib' —%> taglib uri="jspservletcookbook.com.tags" prefix="cbck" %> <%— продолжение страницы JSP... —%> <%— Use the 'logo' tag —%> <cbck:logo heading="l" images"stamp.gif" widths"42" heights"54">Thanks for visiting</cbck: logo На рис. 22.1 приводится страница JSP, использующая тег, определенный в примере 22,1. Рис. 22.1. Страница JSP, использующая пользовательский тег, отображающий изображение и заголовок Страница JSP, использующая этот тег, генерирует примерно такой HTML. <img src="/home/images/stamp, gif width="42" height="54" align="left"> <H1> Thanks for visiting</Hl> . Вы можете возразить: «но дизайнер может ввести эти HTML-теги и вручную, зачем ему связываться с синтаксисом нестандартного тега»! Это так, однако данный тег берет на себя заботу о принимаемом по умолчанию местоположении каталога изображений, о позиционировании и выравнивании изображения и контролирует корректность ввода атрибутов. Другими словами, он выполняет большую часть рутинной работы и устраняет возможность опечаток. Создание классического обработчика тега | 557
Примите во внимание и то, что это всего лишь простой пример; а что, если вместо изображения будет файл в формате Flash? Пользовательский тег берет, на себя заботу обо всех сложных деталях встраивания файла Flash в HTML-страницу и генерирования необходимых значений атрибутов, оставляя пользователю тега лишь графическое пози- ционирование этого медиа-файла. Золотое правило работы с пользовательскими тегами: оставляйте механическую, сложную или утомительную работу обработчику тега, резервируя детали настройки за атрибутами тега. См. также Страницу со спецификацией JSP 2.0 http://jcp.org/en/jsr/detail?id=152-, рецепты 22.2 и 22.3 по созданию TLD-файлов для библиотек тегов; рецепты 22.4 и 22.5 включению биб- лиотек тегов в web-приложение; рецепт 22.6 по использованию пользовательских тегов в JSP; рецепт 22.7 по обработке исключений в тегах;- рецепты 22.8 и 22.9 По созданию упрощенного обработчика тега; рецепт 22.10 по использованию упрощенного обработчика тега в JSP; рецепты 22.11-22.14 по использованию файла тега JSP; рецепт 22.15 по добав- лению к библиотеке тегов класса слушателя; разделы, посвященные пользовательским тегам, книги «JavaServer Pages» Ханса Бергстена (Hans Bergsten), издание третье (O'Reilly). 22.2 Создание TLD (по версии JSP 1.2) - для классического обработчика тега Задача Требуется создать TLD-файл (по версии JSP 1.2) для одного или более пользователь- ских тегов. Решение Создайте XML-файл, используя соответствующее TLD по версии JSP 1.2 объявление DOCTYPE. Обсуждение TLD (дескриптор библиотеки тегов) - это XML-файл, описывающий ваши пользова- тельские теги, атрибуты тегов (если есть), а также Java-классы, обеспечивающие функцио- нальность тегов. Контейнер JSP использует этот файл конфигурации, когда интерпретирует пользовательские теги, встреченные на страницах JSP. Если используется контейнер JSP по версии 1.2, дескриптор библиотеки тегов имеет объявление DOCTYPE, приведенное в примере 22.2. Приведенный в этом примере TLD описывает обработчик тега из предыдущего рецепта. 558 | Глава 22. Использование пользовательской библиотеки тегов
Пример 22.2. TLD файл для классического (JSP 1.2) обработчика тега <?xml version*"1.0" encoding*"ISO-8859-1" ?> <1DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN" "http://java.sun.com/dtcl/web-jsptaglibrary_l_2 .dtd"» «taglib» <tlib-version>l.0</tlib-version» <jsp-version>l.2</jsp-version» <short-name»cbck</short-name> <!-- Это URI, который вы будете использовать в директиве ‘taglib’ в JSP —> <uri >com.j spservletcookbook.tags</uri > <description»Cookbook custom tags*/description» <tag> *name»logo« /name» if- <1— обязательно используйте здесь полностью Кввлифицироаанное имя класса —> *tag-class»com.j spservletcookbook.LogoTag</tag-class» <body-cont ept » JSP< /body-content > <description»This tag writes a logo inside the JSP.</description» «attribute» «name > heading < /name» <1-- Для тега logo этот атрибут является обязательным —г» «required»true</required» <!—атрибут может принимать в качестве значения JSP-выражение —» <rtexprvalue»true< /rtexprvalue» «description»The heading level for the logo; 1 through 6. </description» «/attribute» «attribute» <name»image</name» «required»true*/required» ><rtexprvalue»true«/rtexprvalue» <description»The image name for the logo.«/description» «/attribute» «attribute» <name»width< /name» «required»true*/required* <rtexprvalue»true</rtexprvalue» «description»The image width for the logo.«/description» Создание TLD (no версии JSP 1.2) для классического обработчика тега | 559
</attribute» ». * «attribute» «name»height</name> <reguired»true</required» <rtexprvalue»true</rtexprvalue» «description»The image height for the logo.</description» «/attribute» ( </tag> </taglib» / В JSP 1.2 и 2.0 контейнер JSP автоматически просматривает каталог WEB-INF, а также каталог META-1NF JAR-файлов вашего приложения в поисках файлов с расширением .tld. Необходимо, чтобы имена файлов^ содержащих дескрипторы библиотек тегов, имели расширение tld Затем контейнер использует информацию из TLD для интерпретации пользовательских тегов, используемых в web-приложении. К примеру, контейнер отображает элементы uri, найденные в TLD, на URI, заданные в директивах taglib в JSP-файлах. В примере 22.2 для библиотеки тегов, содержащей тег logo, задан uri: com. jspservletcookbook. tags. Директива taglib страницы JSP, на которой используется эта библиотека тегов, выглядит следующим образом. <%@ taglib uri="com.jspservletcookbook.tags" prefix="cbck" %> Когда ниже на странице контейнер JSP встретит тег logo, он будет знать, что этот тег принадлежит библиотеке тегов, значение uri которой равно com.jspservletcookbook.tags, и контейнер сможет правильно интерпретировать данный тег, основываясь на спецификации класса тега, взятой из TLD, и атрибутах тега. Основываясь на TLD, контейнер JSP знает, что все атрибуты тега logo являются обязательными, таким образом, если JSP использует тег logo без какого-либо параметра, для этой страницы будет выдана ошибка компиляции. См. также Страницу со спецификацией JSP 2.0 http://jcp.org/en/jsr/detail7id~152-, рецепт 22.3 по созданию TLD-файлов для библиотек тегов по версии JSP 2.0; рецепты 22.4 и 22.5 включению библиотек тегов в web-приложение; рецепт 22.6 по использованию пользова- тельских тегов в JSP; рецепт 22.7 по обработке исключений в тегах; рецепты 22.8 и 22.9 по созданию упрощенного обработчика тега; рецепт 22.10 по использованию упрощенного обработчика тега в JSP; рецепты 22,11-22.14 по использованию файла тега JSP; рецепт 22.15 по добавлению к библиотеке тегов класса слушателя; разделы, посвященные пользователь- ским тегам, книги «JavaServer Pages» Ханса Бергстена (Hans Bergsten), издание третье (O'Reilly). 560 | Глава 22. Использование пользовательской библиотеки тегов
22.3 Создание TLD-файла (по версии JSP 2.0) для классического обработчика тега Задача Требуется создать TLD-файл (по версии JSP 2.0) для библиотеки тегов. Решение Создайте дескриптор библиотеки тегов с соответствующим корневым элементом tagl ib, включая необходимые атрибуты xmlns и их значения. Обсуждение Если вы используете в библиотеке тегов и TLD какие-либо особенности JSP версии 2.0, например функцию или элемент tag-file, вам необходимо использовать TLD в стиле 2.0, см. пример 22.3. TLD по версии JSP 2.0 имеет обратную совместимость с элементами, определенными в DTD JSP 1.2. Это означает, что, когда вы обновляете свой TLD-файл до версии JSP 2.0, вы можете продолжать использовать элементы taglib и tag в том виде, как они исполь- зовались в TLD по версии JSP 1.2. К примеру, единственное отличие между TLD из примера 22.3 и TLD по версии 1.2 из примера 22.2, в начальном теге taglib, который должен иметь в точности то содержимое, которое показано ниже. Пример 22.3. TLD-файл по версии JSP 2.0 для нашего классического обработчика тега <taglib xmlns ="http://java.sun.сспп/xml/ns/j2ee" xmlns: xs i® "http: //www.w3 .org/2001/XMLSchema-instance" xsi:schemaLocationx "http://java.sun.com/xml/ns/j2ee http: / / j ava. sun. com/xml /ns / j 2ee/web- j sptaglibrary_2_0. xsd" version""2.0" <1— ОСТАЛЬНОЕ СОДЕРЖИМОЕ В ТОЧНОСТИ ТАКОЕ ЖЕ, ЧТО И В TLD ПО ВЕРСИИ JSP 1. 2 КРОМЕ <jsp-version>2.0</jsp-version> И <body-content>scriptless</body- content>. Значение "scriptless" означает, что содержимым тега может быть шаблонный текст (например HTML-код), EL-код, или элементы с действиями, но не "сценарные" элементы, такие как JSP-код, заключенный в скобки <% Ч> —> <tlib-version>l.0</tlib-version> <jsp-version>2.0</jsp-version> <short-name>cbck<I short-name> <uri>com.j spservletcookbook.tags</uri> Создание TLD-файла (по версии JSP 2.0) для классического обработчика тега | 561
<description»Cookbook custom tags</description» <tag> , v (name»logo</name > <tag-class >con.j spservletcookbook.LogoTag<Itag-class» (body-content»scriptless(/body-content» (description»This tag writes a logo inside the JSP.(/description» *• (attribute» (name»heading( /name > (required»true(/required» 1 <rtexprvalue»true</rtexprvalue» ; (description» The heading level for the logo; 1 through 6. </description» •(/attribute» (attribute» <name»image</name» <required»true</required» (rtexprvalue»true(/rtexprvalue» <description»The image name for the logo.(/description» < (/attribute» (attribute» (name»width( /name» (required»true(/required» (rtexprvalue»true(/rtexprvalue» (description»The image width for the logo.(/description» (/attribute» (attribute» (name»height ( /name» (required»true(/required» (rtexprvalue»true</rtexprvalue» (description»The image height for the logo.(/description» (/attribute» (/tag» '(/taglib» TLD по версии 2.0 базируется на XML-Схеме, а не на DTD (файл с соответствующей XML-Схемой находится по адресу http://java.sun.corn/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd). 562 | Глава 22. Использование пользовательской библиотеки тегов
• •' XML-Схемы позволяют проектировщикам XML-документов создавать более сложные элементы и атрибуты, по сравнению с элементами, которые можно определить с помощью DTD. XML-Схемы спроектированы так, что они сами * являются корректными XML-документами, что облегчает их интеграцию с приложениями, базирующимися на XML. В примере 22.3 элемент taglib имеет четыре атрибута. Атрибут xmlns указывает, что данный TLD имеет то же пространство имен по умолчанию, что и все дескрипторы развертывания J2EE: http://java.sun.com/xml/ns/j2ee. Пространство имен - уникальный идентификатор, позволяющий избежать I , коллизии имен между двумя XML-элементами с одним и тем же именем, f К примеру, элемент с именем taglib, являющийся частью пространства имен * http //java.sun.com/xml/ns/j2ee отличается от элемента с именем taglib, который определен как часть пространства имен http://acme.com. Пространство имен должно быть уникальным только в пределах своего домена (например, URL Интервента); оно не обязано представлять фактический web-документ. Атрибут xmlns :xsi задаёт пространство имен для набора XML-элементов, связан- ных с экземплярами XML-Схемы. Атрибут xsi: schemaLocation задает местополо- жение XML-Схемы, на которой базируется данный XML-документ. *<- XML-Схема описывает определенный набор элементов и атрибутов. Экземпляр XML-Схемы - это XML-документ, в котором используются ранее определенные f элементы и атрибуты. Это Похоже на Java-класс и экземпляры этого класса * (объекты). И, наконец, атрибут version элемента taglib задает версию спецификации JSP, на которой базируется библиотека тегов, например, JSP 2.0. См. также Файл с XML-схемой для TLD по версии JSP 2.0: http://java.sun.com/xml/ns/j2ee/web- jsptaglibrary_2J).xsd\ рецепт 22.2 по созданию TLD-файлов для библиотек тегов по версии JSP 1.2; рецепты 22.4 и 22.5 упаковке библиотек тегов в web-приложение; рецепт 22.6 по использованию пользовательских тегов в JSP; рецепт 22.7 по обработке исключений в тегах; рецепты 22.8 и 22.9 по созданию упрощенного обработчика тега; рецепт 22.10 по использованию упрощенного обработчика тега в JSP; рецепты 22.11-22.14 по использо- ванию файла тега JSP; рецепт 22.15 по добавлении!) к библиотеке тегов класса слушателя; разделы, посвященные пользовательским тегам, книги «JavaServer Pages» Ханса Бергстена (Hans Bergsten), издание третье (O’Reilly). Создание TLD-файла (по версии JSP 2.0) для классического обработчика тега | 563
22.4 Как включить библиотеку тегов в web- приложение Задача Сделать библиотеку тегов доступной в web-приложении. Решение Поместите свой TLD-файл в каталог WEB-INF или в подкаталог каталога WEB-JNF (за исключением подкаталогов WEB-INFAib и WEB-INF/classes). Поместите класс (или классы) обработчика тегов в каталог WEB-INF/classes. Обсуждение Размещение библиотеки тегов вне JAR-файла - это' процесс, обычно укла- дывающийся в два шага. 1. Сохраните TLD-файл в каталоге WEB-INF или подкаталоге каталога WEB-INF, и кон- тейнер JSP (совместимый с версиями 1.2 и 2.0) автоматически настроит вашу биб- лиотеку тегов. TLD-файл должен иметь расширение .tld. К примеру, если вы сохраняете mytags.tld в WEB-INF/tlds, то контейнер JSP автоматически найдет ваш TLD-файл и настроит вашу библиотеку тегов. | .... Спецификация JSP 2.0 устанавливает, что дескрипторы библиотеки тегов (TLD) не могут размещаться в каталогах WEB-INF/lib или WEB-INF/classes. Контейнер jgp не сганет искать tld в этих каталогах. 2. Убедитесь, что классы обработчиков тегов для вашей библиотеки тегов имеют имя пакета (например, com. jspservletcookbook) и расположены в каталоге WEB- INF/classes или в JAR-файле в каталоге WEB-INFAib. В следующем рецепте показано, как упаковать вашу библиотеку тегов, включая и TLD, в JAR-файл. См. также Файл с XML-схемой для TLD по версии JSP 2.0: http://java.sun.com/xml/ns/j2ee/web- jsptaglibrary_2_O.xsd-, рецепты 22.2 и 22.3 по созданию TLD-файлов для библиотек тегов; "рецепт 22.5 по упаковке библиотеки тегов в JAR-файл; рецепт 22.6 по использованию пользовательских тегов в JSP; рецепт 22 7 по обработке исключений в тегах; рецепты 22.8 и 22.9 по созданию упрощенного обработчика тега; рецепт 22.10 по использованию упрощенного обработчика тега в JSP; рецепты 22.11-22.14 по использованию файла тега JSP; рецепт 22.15 по добавлению к библиотеке тегов класса слушателя; разделы, посвящен- ные пользовательским тегам, книги «JavaServer Pages» Ханса Бергстена (Hans Bergsten), издание третье (O’Reilly). 564 | Глава 22. Использование пользовательской библиотеки тегов
22.5 Упаковка библиотеки тегов в JAR-файл Задача Вы хотите, чтобы библиотека тегов была доступна приложению в виде JAR-файла. Решение Создайте JAR-файл, содержащий класс (классы) обработчика тега в корректной структуре каталогов (с именами подкаталогов, соответствующими именам пакета). Поместите файл дескриптора библиотеки тегов в каталог МЕТА-INF этого JAR-файла. Затем поместите этот JAR-файл в каталог WEB-INFAib своего web-приложения. Обсуждение Чтобы сделать библиотеку тегов легко лереносимой, сохраните все свои классы обработчиков тегов и файлы тегов в JAR-файле. '»' В JAR-файле сохраните все файлы тегов в каталоге META-INF/tags или в подкаталоге каталога META-INF/tags. Если этого не сделать, контейнер JSP у не распознает в них легитимные файлы тегов. Детали смотрите в рецепте 22.11. Вы можете сгенерировать этот JAR-файл из каталога, который содержит классы вашей библиотеки тегов, включая и подкаталоги, связанные с их именами. Например, тег logo, разработанный мною в этой главе, имеет имя пакета com. jspservletcookbook, следова- тельно, относительный путь к этому файлу будет com/jspservletcookbook/LogoTag.class. Включите каталог МЕТА-INF на верхний уровень того каталога, где хранятся классы (то есть в тот же каталог, где находится подкаталог сот). Поместите файл дескриптора библиотеки тегов в каталог МЕТА-INF или в подкаталог каталога МЕТА-INF. Если ваша библиотека включает какие-либо файлы тегов, поместите их в каталог META-INF/tags или в подкаталог этого каталога. Перейдите в каталог, содержащий все эти подкаталоги, и напечатайте следующую командную строку, подставив вместо cookbooktags.jar имя своего файла. jar cvf cookbooktags.jar . ‘ Не забудьте поставить в конце символ точки (.). Это говорит инструменту j аг о необ- ходимости включить в ар^ив все файлы и каталога, которые содержит текущий каталог. Убедитесь, что переменная окружения PATH вашего компьютера включает путь к подкаталогу Ып каталога, где у вас установлен Java SDK, например h:\2sdkl.4.1_ { 01\Ып. Это позволит для запуска инструмента jar напечатать в командной строке * только j аг. Упаковка библиотеки тегов в JAR-файл | 565
Для инсталляции библиотеки тегов просто возьмите получившийся JAR-файл и поместите его в каталог WEB-INFAib своего web-приложения. Контейнер JSP (в JSP 1.2 и 2.0) автоматически найдет в каталоге META-1NF JAR- файла TLD-файл, вам не требуется включать элемент taglib в дескриптор развертыва- ния web.xml. См. также Web-страницу со спецификацией JSP 2.0 http://jcp.org/en/jsr/detail7id~152-, рецепты 22.2 и 22.3 по созданию TLD-файлов для библиотек тегов; рецепт 22.4 по помещению библио- теки тегов в web-приложение без использования JAR-файла; рецепт 22.6 по использованию пользовательских тегов в JSP; рецепт 22.7 по обработке исключений в тегах; рецепты 22.8 и 22.9 по созданию упрощенного обработчика тега; рецепт 22.10 по использованию упрощенного обработчика тега в JSP; рецепты 22.11 и 22.14 по использованию файла тега JSP; рецепт 22.15 по добавлению к библиотеке тегов класса слушателя; разделы, посвящен- ные пользовательским тегам, книги «JavaServer Pages» Ханса Бергстена (Hans Bergsten), издание третье (O'Reilly). 22.6 Использование пользовательского тега bJSP Задача Вы хотите использовать пользовательский тег, который вы разработали и инсталлировали. Решение Включите в начало страницы JSP директиву taglib. Эта директива должна указать uri вашей библиотеки тегов тот самый uri, что указан в TLD-файле. Обсуждение Для того чтобы можно было применять пользовательские теги из вашей библиотеки тегов на странице JSP, в JSP включается директива taglib, как в.примере 22.4. Атрибут uri совпадает с u;ri, указанном в TLD-файле (см. рецепт 22.5). Атрибут prefix (префикс) указывает пространство имен для ваших тегов. В примере 22.4 указан префикс cbck; таким образом, на странице JSP тег logo из библиотеки тегов используется следующим образом: <cbck: ldgo>...</cbck: logo>. 566 | Глава 22. Использование пользовательской библиотеки тегов
Если в тег заключено, некоторое содержимое (это не пустой тег), убедитесь, что корректно закрываете тег, например </cbck: logp> вместо </logo>. Если JSP использует тег не так, как определено в TLD (например, пропущен обяза- тельный атрибут), при первом запросе к этой странице компиляция страница JSP завершится неудачно. Пример 22.4. Использование тега logo на странице JSP taglib uri="jspservletcookbook.com.tags" prefixs"cbck" %> <html> <headxtitle>Mi casa.es su casa</titlex/head> <body> <cbck:logo headings "<%=request. getParameter ("level") %>" images" stamp .gif" width="42" heights"54">Thanks for visit ing</cbck: logo Here's all the other stuff this-page contains... </body> r </html> В примере 22.4 атрибут heading тега logo в качестве значения принимает выраже- ние, вычисляемое на этапе выполнения, то есть пользователь тега может динамически формировать значение атрибута, например, с помощью следующего URL: http://local- host:8080/home/logoTest.jsp ?level=l. На рис. 22.2 показано отображение этой страницы JSP в браузере. Рис. 22.2. Отображение в браузере результатов работы пользовательского тега Использование пользовательского тега в JSP | 567
См. также Web-страницу со спецификацией JSP 2.0 http://jcp.org/en/jsr/detail?id=152', рецепты 22.2 и 22.3 по созданию TLD-файлов для библиотек тегов; рецепты 22.4 и 22.5 по включению библиотеки тегов в web-приложение; рецепт 22.7 по обработке исключений в тегах; рецепты 22.8 и 22.9 по созданию упрощенного обработчика тега; рецепт 22.10 по использо- ванию упрощенного обработчика тега в 1SP; рецепты 22.11-22.14 по использованию файла тега JSP; рецепт 22.15 по добавлению к библиотеке тегов класса слушателя; разделы, посвя- щенные пользовательским тегам, книги «JavaServer Pages» Ханса Бергстена (Hans Berg - sten), издание третье (O'Reilly). 22.7 Обработка исключений в классе пользовательского тега Задача Вы хотите, чтобы ваш обработчик пользовательского тега имел дело с любыми исключениями, выброшенными внутри тега. Решение Реализуйте в обработчике тега интерфейс TryCatchFinally. Обсуждение API расширения тегов предоставляет интерфейс TryCatchFinally, который вы можете реализовать в своем классе обработчика тега, чтобы написать код, имеющий дело с любыми исключениями, которые этот обработчик кода может выбрасывать. Если класс реализует интерфейс TryCatchFinally, он должен включать методы doCatch () и doFinally (). К примеру, в методе doCatch () крд имеет доступ к любому объекту Throwable, выбрасываемому методом doStartTagO или doEndTag ().. В методе doFinally () код закрывает любые ресурсы, используемые тегом, например, соедине- ние с базой данных. Вообще, этот интерфейс позволяет обработчику тега самостоятельно перехватывать и обрабатывать любые исключения, которые не влияют на выходную информацию, генерируемую страницей JSP, содержащей данный тег. В примере 22.5 используется тот же код, что и в примере 22.1, но с добавлением новым методов для реализации интерфейса TryCatchFinal 1у. 568 | Глава 22. Использование пользовательской библиотеки тегов
Пример 22.5. Обработчик тега logo, перехватывающий любые исключения package com.j spservletcookbook; import javax.servlet.*; ' import javax.servlet.http.*; import javax.servlet.jsp.*; import javax.servlet.jsp.tagext.*; /**. Этот тег генерирует уменьшенное изображение, используя HTML-тег img, и вслед за ним - текстовое сообщение. Пользователь задает текст сообщения и уровень заголовка(то есть, <Н1>-<Нб>) */ public class LogoTag extends BodyTagSupport implements TryCatchFinallyf private String heading = null; private String image =null; private String width =null; private String height =hull; //этот метод предполагает, что значения атрибутов заданы. public int doStartTagO throws JspException{ try { int h s new Integer (heading) .in tValueO ; if(! (h > 0 && h < 7)) throw new JspException( "The 'heading' attribute value must between 1 and 6"+ " inclusive."); } catch (Exception e) { throw new JspException(e.getMessage()); } return EVAL_BODY_BUFFERED; } . public int doEndTagO throws JspException { JspWriter out = pageContext.getOut(); String imgDir = ((HttpServletRequest) pageContext.getRequest ())'. getContextPath() + •/images/•; String message = getBodyContent{).getString().trim(); try{ out.println(4"<img src=\""+ imgDir + image + ^\" width=\"" + width + "\" height=\"" + height + "\" a'lign=\"left\">" + "<H" + heading + ">" + message + "</H" + heading* ">"); Обрабо гка исключений в классе пользовательского тега | 569
•} catch (java.io.lOException io).{) •» return EVAL_PAGE; } //doEndTag /* Следующие два метода обязательно дол.хны присутствовать в классе, если этот класс реализует интерфейс TryCatchFinally */ public void doCatch(Throwable t){ try{ //печатаем сообщение об исключении » том месте страницы JSP где //находится тег pageContext.getout().println(t.getMessage()+"<br />"); } catch (java.io.lOException io) {} J public void doFinally(){ //ничего не делаем, поскольку у нас нет никаких открытых //ресурсов, таких как соединение с базой данных } public void setHeading(String level){ this.headings level; } /* ПРОДОЛЖЕНИЕ ОСТАВШЕЙСЯ ЧАСТИ КОДА ПРИМЕРА 22-1... */. } Если тег выбрасывает исключение, то web-контейнер вызывает метод doCatch () и обработчик тега печатает сообщение об этом исключении в том месте вывода страницы JSP, где в ином случае было бы изображение формируемое тегом. В нашем случае метод doFinally () не делает ничего, поскольку не было никаких открытых ресурсов, таких/ как FilelnputStream. См. также Web-страницу со спецификацией JSP 2.0 http://jcp.org/en/jsr/detail7id~152-, рецепты 22.2 и 22.3 по созданию TLD-файлов для библиотек тегов; рецепты 22.4 и 22.5 по включению библиотеки тегов в web-приложение; рецепт 22.6 по использованию Пользовательского тега, в JSP; рецепты 22.8 и 22.9 по созданию упрощенного обработчика тега; рецепт 22.10’ по использованию упрощенного обработчика тега в JSP; рецепты 22.11-22.14 по использо-1 ванию файла тега JSP; рецепт 22.15 по добавлению к библиотеке тегов класса слушателя^ разделы, посвященные пользовательским тегам, книги «JavaServer Pages» Ханса Бергстена (Hans Bergsten), издание третье (O’Reilly). 570 | Глава 22. Использование пользовательской библиотеки тегов
22.8 Создание упрощенного обработчика тега Задача Вы хотите создать упрощенный обработчик тега по версии JSP 2.0. Решение Создайте Java-класс, который или реализует интерфейс SimpleTag, или расширяет класс SimpleTagSupport. Обсуждение Для упрощения разработки пользовательских тегов в спецификацию JSP версии 2.0 были добавлены интерфейс j avax. servlet .jsp. tagext. SimpleTag и класс Sim- pleTagSupport. Класс SimpleTagSupport создан в качестве базового класса для обработчиков тегов, реализующих интерфейс SimpleTag. В этих обработчиках тегов необходимо реализовать только один метод - doTag (). Спецификация JSP 2.0 указывает, что поставщики ПО, разрабатываемого л в соответсвии с данной спецификацией, не должны кешировать упрощенные f „ч; обработчики тегов, так что разработчикам не следует беспокоиться о повторном использовании объектов обработчиков запросов и освобождении срстояния объекта в своем коде. Пример 22.6 имитирует обработчик тега logo, созданный в предшествующих рецеп- тах, но в своей работе использует класс SimpleTagSupport из API JSP 2-0- Пример 22.6. Упрощенный обработчик тега, отображающий логотип package com.jspservletcookbook; import java.io.lOException; import javax.servlet.*; 'import j avax. servlet.http.*; import javax.servlet.jsp.*; import javax.servlet.jsp.tagext.*; /** Этот тег генерирует уменьшенное изображение, используя HTML-тег img, и вслед за ним - текстовое сообщение. Пользователь задает текст ^.сообщения и уровень заголовка(то есть, <Н1>-<Нб>) */ public class SimpleLogoTag extends SimpleTagSupport { private String heading = null; private String image =null; i Создание упрощенного обработчика тега | 571
private String width =null; private String height =null; public void doTagO throws JspException, lOException{ //JspContext обеспечивает доступ к JspWriter для генерирования //текста из тега. С помощью JspContext вы также можете получить //любые хранящиеся значения'атрибутов JspContext JspContext « getJspContext(); //этот метод предполагает, что значения атрибутов заданы, try { int h и new Integer(heading) .intValueO; if(I (h > 0 && h < 7)) throw new JspException ( "The 'heading1 attribute value must between 1 and б"* " inclusive."); } catch (Exception e) { throw new JspException(e.getNessage()); } //Получаем JspWriter для формирования вывода тега JspWriter-out JspContext.getOut(); //значение атрибута 'imgDir1 - каталог /images web-приложения //путь к каталогу хранится в атрибуте сеанса String imgDir в (String) JspContext.findAttribute("imgDir"); if (imgDir °e null || "*.equals(imgDir)) throw new JspException ( "No attribute provided specifying the application's " + "image directory."); //отображаем HTML-те™ img и H out.printIn(new StringBuffer("<img erc»\**).append(imgDir). append(image) .append("\" width«\"") .append(width). append(”\" height»\*").Append(height).append("\" align«\"left\">"). append("<H").append(heading).append(”>").toString()); // grJspBodyO возвращает объект ' JspFragment'; вызов метода //'invoke()'этого объекта с параметром'null' приведет к выводу //с помощью JspWriter содержимого, вложенного в тег. getJspBody().invoke(null); out.printIn(new StringBuffer("</H").append(heading). append(">").toString()); }//doTag //Методы-установщики, работающие с атрибутами public void setHeading(String level){ 572 | Глава 22 Использование пользоваз ельской библиотеки Тегов
this.heading= level; } public Void setimage(String name){ this.image = name; k } public void setwidth(String width){ this.width = width; } public void setHeight(String height){ this.height = height; } }// SimploLogoTug Этот упрощенный обработчик тега получает доступ к объекту JspContext, для чего вызывает метод get JspContext () класса SimpleTagSupport. Код использует Jsp- Context для получения значения атрибута, сохраненного в сеансе, а также для доступа к JspWriter - для генерирования выходной информации тега. JspContext JspContext = getJspContext(); //другие операторы... JspWriter out = jspContext.getOut(); //значение атрибута 'imgDir' - путь к каталогу /images web-приложения //путь к каталогу хранится в атрибуте сеанса String imgDir = (String) jspContext.f indAttribute("imgDir"); //продолжение кода... Вызов метода getJspBody() класса SirrtpleTagSupport возвращает объект Jsp- Fr^gment, который представляет фрагмент кода JSP в качестве объекта. Вызов метода invoke () этого объекта с параметром null направляет выходную информацию представляемого объектом фрагмента в JspWriter, доступный обработчику тега. //Получаем содержимое тела тега и выводим его, используя JspWriter //который доступен благодаря вызову JspContext.getOut() getJ spBody().invoke(null); Этот код отображает содержимое тега - тот текст, который разработчик страницы JSP поместил между открывающим и закрывающим тегом данного пользовательского действия. Обработчик тега использует содержимое тела тега для выдачи текстового сообщения в составе логотипа. Вид страницы JSP, включающей этот тег, в браузере приведен на рис. 22.1 рецепта 22.1. Создание упрощенного обработчика тега | 573
См. также > Web-страницу со спецификацией JSP 2.0 http://jcp.org/en/jsr/detail?id=152- рецепты 22.2 и 22.3 по созданию TLD-файлов для библиотек тегов; рецепты 22.4 и 22.5 по включению библиотеки тегов в web-приложение; рецепт 22.6 по использованию пользовательского тега bJSP; рецепт 22.7 по обработке исключений в теге; рецепт 22.9 по созданию TLD для упрощенного обработчика тега; рецепт 22.10 по использованию упрощенного обра- ботчика тега в JSP; рецепты 22.11-22.14 по использованию файла тега JSP; рецепт 22.15 по добавлению к библиотеке тегов класса слушателя; разделы, посвященные пользователь- ским тегам, книги «JavaServer Pages» Ханса Бергстена (Hans Bergsten), издание третье (O’Reilly). 22.9 Создание TLD для упрощенного обработчика тега Задача , Требуется создать TLD для упрощенного обработчика тега. Решение Для упрощенного обработчика тега создайте TLD в стиле JSP 2.0. Обсуждение Упрощенный обработчик тега наследуется от API JSP 2.0, поэтому вы можете использо- вать версию TLD также соответствующую JSP 2.0. В примере 22.7 показан начальный тег taglib и различные атрибуты xmlns, которые в точности должны быть воспроизведены в вашем TLD. Затем, если вы не используете какие-либо особенности TLD по версии JSP 2.0, например элемент tag-file, вы можете задать элемент tag и вложенные в него элементы, используя тот же синтаксис, что использовался в предшествующих версиях TLD. Пример 22.7. TLD-файл (по версии JSP 2.О) для упрощенного обработчика тега <tagllb xmlnsл"http://java.sun.com/xml/ns/j2ee" xmlns: xs 1= "http: //www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation= "http://java.sun.com/xml/ns/j2ee http: / /java. sun. com/xml/ns/j 2ее/web- j sptaglibrary_2_0 .xsd" version»"2.0" <!—ОСТАЛЬНАЯ ЧАСТЬ XML-СОДЕРЖИМОГО ТА ЖЕ, ЧТО И ДЛЯ TLD ПО ВЕРСИИ JSP 1.2, КРОМЕ <jsp-version>2.0</jsp-version> —> 574 | Глава 22. Использование пользовательской библиотеки те/ов
<tlib-version>l.0</tlibvversion> <jsp-version>2.0</jsp-version> <short~name»cbck</short-name» <uri»com.jspservletcookbook.tags</uri> <description>Cookbook custom tags</description» <tag> <name»siinplelogo</name» <tag-class»CQm.j spservletcookbook.SimpleLogoTag</tag-class> <body-content>JSP< /body-content» <description»This tag writes a logo inside the JSP.</description» «attribute» <name»heading</name» <required»true</required» <rtexprvalue»true</rtexprvalue» <description> The heading level for the logo; 1 through 6. </description> < /attribute» <attribute» <name»image< /name» •«required» true< / required» <rtexprvalue»true</rtexprvalue» <description»The image name for the logo.</description» < /attribute» <attribute» <naroe»width< /name» <required»true</required» ' <rtexprvalue»true</rtexprvalue» <description»The image width for the logo.</description» < /attribute» <attribute» <name»height</name» <required»true</required» <rtexprvalue»true</rtexprvalue» <descriptidn»The image height for the logo.</description» < /attribute» </tag> /taglib» Создание TLD для упрошенного обработчика тега | 575
Чтобы использовать упрощенный обработчик тега в web-приложении, поместите TLD в подкаталог каталога WEB-INF, например в WEB-INF/tlds. Или сохраните f TLD в каталоге META-INF JAR-файла или в его подкаталоге. Затем поместите этот * JAR-файл в каталог WEB-INFAib. См. также Web-страницу со спецификацией JSP 2.0 http://jcp.org/en/jsr/detail?id=152\ рецепты 22.2 и 22.3 по созданию TLD-файлов для библиотек тегов; рецепты 22.4 и 22.5 по включению библиотеки тегов в web-приложение; рецепт 22.6 по использованию пользовательского тега в JSP; рецепт 22.7 по обработке исключений в теге; рецепт 22.8 по созданию упрощенного обработчика тега; рецепт 22.10 по использованию упрощенного обработчика тега в JSP; рецепты 22.11-22.14 по использованию файла тега JSP; рецепт 22.15 по добавлению к биб- лиотеке тегов класса слушателя; разделы, посвященные пользовательским тегам, книги «JavaServer Pages» Ханса Бергстена (Hans Bergsten), издание третье (O'Reilly). 22.10 Использование упрощенного обработчика тега в JSP Задача Вы хотите использовать пользовательский тег, базирующийся на упрощенном обработчике тега. Решение Используйте в JSP директиву taglib, задав атрибут uri, соответствующий исполь- зуемой библиотеке тегов. Обсуждение Разместите библиотеку тегов, упрощенный обработчик тегов и связанный с ним TLD так, как показано в рецептах 22.4 и 22.5 и замечании из рецепта 22.9. В примере 22.8 пока- зана остальная часть установки, необходимая для использования тега в JSP. Упрощенные обработчики тегов предназначены для облегчения процесса разработки (поскольку вам требуется реализовать всего один метод: void doTag ()). Связанные с ними теги используются на страницах JSP точно так же, как теги, связанные с классическими обработчиками тегов. 576 | Глава 22. Использование пользовательской библиотеки теГов
Пример 22.8. Страница JSP, использующая тег, определенный упрощенным обработчиком тегов <%Э taglib uri="jspservletcookbook.com.tags" prefix»"cbck" %> <html> <head><title>Me Casa Su Casa</title></head> <body> <% session.setAttribute("imgDir",(request,getContextPath() + "/images/")); %> <cbck:simplelogo heading» "<%»request.getParameter(\"level\") %>" image* "stamp.gif" width»"42" height»"54"> Thanks for visiting here</cbck:simplelogo> Here's all the other stuff this page contains... </body> </html> Страница JSP из примера 22.8 получает значение атрибута heading тега logo с помощью JSP-выражения. Пользователь страницы обеспечивает это значение в URL. http://localhost:8080/home/logoTest.jsp?level=l Выходная информация этой страницы отображается так же, как выходная информация страницы, показанная на рис. 22.1 рецепта 22.1. См. также Web-страницу со спецификацией JSP 2.0 http://jcp.org/en/jsr/detail7id-152; рецепты 22.2 и 22.3 по созданию TLD-файлов для библиотек тегов; рецепты 22.4 и 22.5 по включению библиотеки тегов в web-приложение; рецепт 22.6 по использованию пользовательского тега в JSP; рецепт 22.7 по обработке исключений в теге; рецепты 22.8 и 22.9 по созданию упрощенного обработчика тега; рецепты 22.11-22.14 по использованию файла тега JSP; рецепт 22.15 по добавлению к библиотеке тегбв класса слушателя; разделы, посвященные пользовательским тегам, книги «JavaServer Pages» Ханса Бергстена (Hans Bergsten), изда- ние третье (O’Reilly). Использование упрощенного обработчика тега в JSP | 577
22.11 Создание файла тега JSP Задача Создать пользовательский тег в форме файла тега. Решение Создайте файл тега, используя синтаксис JSP, присвоив имени файла расширение .tag или .tagx. Поместите файл тега в каталог WEB-INF/tags, или в каталог META-INF/tags внутри JAR-файла, или в подкаталог одного из этих каталогов. Обсуждение В JSP версии 2.0 были введены файлы тегов, представляющие собой пользовательские теги, которые пишутся с использованием синтаксиса JSP. Файлы тегов предназначены для разработчиков с небольшим (или вовсе без) опытом работы с Java, они позволяют соз- дать простой тег, используя только JSP- и XML-элементы. Кроме того, файлы тегов не требуют TLD, хотя вы можете описать файл тега в TLD (см. рецепт 22.12). Если вы соз- дали файл тега, поместите его в каталог WEB-INF/tags, и контейнер JSP откомпилирует этот файл в класс обработчика Тега при первом использовании связанного с ним тега на странице JSP. * • Контейнер JSP преобразует файл тега в класс* расширяющий класс javax. servlet. jsp. tagext.SimpleTagSupport. За более подробной информацией , ** f по этому классу обращайтесь к рецепту 22.8. Файлы тегов привели к введению ряда новых директив и стандартных действий, например, директив tag и attribute, а также действия j sp: doBody. В примере 22.9 показаны эти новые синтаксические элементы. В этом примере создается тот же тег logo, с которым мы работали на протяжении всей главы, но в данном случае используется формат файла тегов. ., I В рецепте 22.14 показано, как получившийся пользовательский тег использовать на I странице JSP. 4 В примере 22.9 директива tag используется, чтобы указать, что содержимое тела тега (текст, появляющийся между тегами начала и конца) не может содержать сценарных элементов (scriptless). Это значит, что содержимое тела может содержать только шаблонный текст, EL-код и действия JSP. 578 | Глава 22. Использование пользовательской библиотеки тегов
** «*, 1 • ’ Если вы определяете пустой тег, значение body-content равно «empty». Если * * тег принимает в качестве содержимого тела код JSP, используется значение «JSP». * * f Четвертое возможное значение body-content равно «tagdependent», что означает, что тег сам интерпретирует код, находящийся в его теле (например, операторы SQL). Поскольку тег file может использовать нормальный синтаксис JSP, в примере 22.9 используется директива taglib, чтобы можно было применять JSTL (см. главу 23). Затем в примере определяется каждый из атрибутов тега. Помните, что и tag и attribute являются директивами, поэтому их код начинается с «<%@». Пример 22.9. Файл тега, генерирующий пользовательский тег, который помещает логотип на страницу JSP tag body-content»"scriptless" description="Writes the HTML code for inserting a logo.” %> < %0 taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> < W attribute name»"heading" required»"true" rtexprvalue» "true" description»"The heading level for tho logo."%> < %0 attribute name»"image" required»"true" rtexprvalue» "true” description»"The image name for the logo."%> < W attribute name»"width" required»"true" rtexprvalue» "true" description»"The image width for the logo."%> attribute name»"height" required»"true" rtexprvalue» "true" description»"The image height for the logo."%> <img src»"<c:out value»"${imgDir}${image}"/>" width» "<csout value»"${width}"/»" height»"<c:out value» "${height}"/>" align«"left"> <H<c: out value» " $ {heading} " / »< j sp s doBody/ > < /H<c: out value»" $ {heading} " / » У директивы attribute такие же атрибуты, как атрибуты, которые мы использовали для TLD по версии JSP 1.2 (см. рецепт 22.2). Поскольку файл тега принимает простой шаб- лонный текст, то именно в таком виде мы задаем HTML-тег img, для генерирования которого и предназначен данный файл тега. Атрибуты тега img получают значения при помощи JSTL-тега с:out и EL (см. главу 23). К примеру, выражение «${ irngDir}» возвращает значение хранимого атрибута объ- екта с этим именем, устанавливающего каталог, содержащий изображение, используемое в логотипе. Выражение «${image}» возвращает значение атрибута image данного тега, которое уже должно быть установлено пользователем. Л 1 Создание файла тега JSP | 579
Стандартное действие jsp:doBody - отличный способ вывести (подставить выходную информацию) текст, расположенный между открывающим и закрывающим тегами данного пользовательского действия. Действие jsp:doBody, так же* как директивы tag, attribute и variable (в данном рецепте не используется) можно использовать только в файлах тегов. См. также Web-страницу со спецификацией JSP 2.0 http:/(jcp.org/en/jsr/detail?id=152; рецепты 22.2 и 22.3 по созданию TLD-файлов для библиотек тегов; рецепты 22.4 и 22.5 по включению библиотеки тегов в web-приложение; рецепт 22.6 по использованию пользовательского тега в JSP; рецепт 22.7 по обработке исключений в теге; рецепты 22.8 и 22.9 по созданию упрощенного обработчика тега; рецепт 22.10 по использованию упрощенного обработчика тега в JSP; рецепты 22.12-22.14 по использованию файла тега JSP; рецепт 22.15 по добав- лению к библиотеке тегов класса слушателя; разделы, посвященные пользовательским тегам, книги «JavaServer Pages» Ханса Бергстена (Hans Bergsten), издание третье (O’Reilly). 22.12 Упаковка файла тега JSP в web-приложение Задача Требуется сохранить файл тега так, чтобы его можно было использовать в web- приложении. Решение Поместите файл тега в каталог WEB-INF/tags web-приложения, или в каталог МЕТА- INF/tags JAR-файла, или в какой-либо подкаталог этих каталогов. Если вы это сделаете, у вас не будет необходимости описывать этот тег в TLD-файле. Обсуждение Контейнер JSP ищет файл тега, используя атрибут tagDir директивы taglib. Иными словами, атрибут tagDir содержит путь к каталогу web-приложения, в котором находится данный файл тега. <%@ taglib prefix="cbck" tagdir="/WEB-INF/tags" %> 580 | Глава 22. Использование пользовательской библиотеки тегов
Если вы поместили файл тега, имеющий расширение .tag (или .tagx, если файл тега выполнен в синтаксисе XML) в каталог /WEB-INF/tags, JSP может использовать тег, свя- занный с этим файлом тега. На странице JSP директива taglib размещается до того, как используется соответствующий пользовательский тег. Файл тега также можно описать в TLD (версии JSP 2.0). <tag-file> <name>dbSelect</name> <раth>/WEB- INFI tags/dbtags</path> </tag-file> Данная запись TLD определяет файл тега с именем dbSelect.tag, который находится в каталоге /WEB-INF/tags/dbtags. Значение атрибута path Должно начинаться с «/META- INF/tags», если файл тега находится в JAR, если же файл тега находится в Web-архиве (в WAR-файле) или в неархивированном web-приложении, то с «/WEB-INF/tags». См. также Web-страницу со спецификацией JSP 2.0 http://jcp.org/en/jsr/detail?id=I52-, рецепты 22.2 и 22.3 по созданию TLD-файлов для библиотек тегов; рецепты 22.4 и 22.5 по включению библиотеки тегов в web-приложение; рецепт 22.6 по использованию пользовательского тега в JSP; рецепт 22.7 по обработке исключений в теге; рецепты 22.8 и 22.9 по созданию упрощенного обработчика тега; рецепт 22.10 по использованию упрощенного обработчика тега в JSP; рецепты 22.13 и 22.14 по упаковке файла тега и его использованию в JSP; рецепт 22.15 по добавлению к библиотеке тегов класса слушателя; разделы, посвященные пользовательским тегам, книги «JavaServer Pages» Ханса Бергстена (Hans Bergsten), изда- ние третье (O'Reilly). 22.13 Упаковка файла тега JSP в JAR Задача Вы хотите сохранить файл тега в JAR-файле. Решение Поместите файл тега в каталог META-INF/tags JAR-файла или в какой-либо его под- каталог. Упаковка файла тега JSP в JAR | 581
Обсуждение Разработчики обычно распространяют библиотеки тегов в виде JAR-файлов, осо- бенно если они создают переносимую библиотеку тегов. Для библиотек тегов в стиле JSP 2.0, использующих файлы тегов, размещайте эти файлы тегов в каталоге МЕТА-INF/ tags или его подкаталоге. Файл тега должен иметь расширений .tag, или .tagx, если файл тега выполнен в синтаксисе XML. Затем этот JAR-файл необходимо поместить в каталог WEB-INFAib web-приложения, содержащего страницу JSP, использующую этот тег. См. также Web-страницу со спецификацией JSP 2.0 http://jcp.org/eii/jsr/detail?id=152', рецепты 22.2 и 22.3 по созданию TLD-файлов для библиотек тегов; рецепты 22.4 и 22.5 по включению библиотеки тегов в web-приложение; рецепт 22.6 по использованию пользовательского тега bJSP; рецепт 22.7 по обработке исключений в теге; рецепты 22.8 и 22.9 по созданию упрощенного обработчика тега; рецепт 22.10 по использованию упрощенного обработчика тега в JSP; рецепт 22.14 по использованию тега, базирующегося на файле тегов; рецепт 22.15 по добавлению к библиотеке тегов класса слушателя, разделы, посвященные пользователь- ским тегам, книги «JavaServer Pages» Ханса Бергстена (Hans Bergsten), издание третье (O'Reilly). 22.14 Использование пользовательского тега, связанного с файлом тега Задача Вы хотите использовать пользовательский тег, связанный с'файлом тега. Решение Используйте в JSP соответствующую директиву taglib, размещая ее на странице до кода, в котором используется тег, связанный с файлом тега. Обсуждение Директива taglib, с помощью атрибута tagdir ставит тегу в соответствие путь к каталогу tags web-приложения. В примере 22.10 используется тег, из файла тега, храня- щегося в /WEB-INF/tagsAogo.tag .. 582 | Глава 22. Использование пользовательской библиотеки тегов
Имя тега, используемое a JSP, совпадает с именем файла. тега, взятым без расширения .tag. Атрибут prefix представляет пространство имен пользователь- ского тега, так что полностью тег выглядит в JSP так: <«cbck: logo heading=. .. > . ... содержимое тега. . . </cbck: logo»». • Пример 22.10. Страница. JSP, использующая тег, определенный в файле тега <%& taglib prefix="cbck" tagdir»"/WEB-INF/tags" %> <html> <headxtitle>Me Casa Su Casa</title></head> <body> <% session.setAttributeCimgDir", (request.getContextPath() + “/images/")); %> <cbck:logo headings"<%srequest.getParameter(\“level\") %>” images "stair®, gif" widths"42" heights"54"> Thanks for visiting here ... </cbck: logo Здесь располагается остальное содержимое страницы... </body> </html> Г*^Г-- ' * На протяжении всей главы я использую один и тот же базовый тег logo, чтобы проиллюстрировать различия в синтаксисе между разными вариантами поль- зовательского тега. См. также Web-страницу со спецификацией JSP 2.0 http://jcp.org/en/jsr/detail?id=152\ рецепты 22.2 / и 22.3 по созданию TLD-файлов для библиотек тегов; рецепты 22.4 и 22.5 по включению библиотеки тегов в web-приложение; рецепт 22.6 по использованию пользовательского тега в JSP; рецепт 22.7 по обработке исключений в теге; рецепты 22.8 и 22.9 по созданию упрощенного обработчика тега; рецепт 22.10 цо использованию упрощенного обработчика ' тега в JSP; рецепты 22.11-22.13 по установке файла тегов JSP; Рецепт 22.15 по добавлению к библиотеке тегов класса слушателя; разделы, посвященные пользовательским тегам, книги «JavaServer Pages» Ханса Бергстецй (Hans Bergsten), издание третье (O'Reilly). 1 Использование пользовательского тега, связанного с файлом тега | 583
22.15 Добавление к библиотеке тегов класса слушателя Задача Вы хотите включить класс слушателя в свою библиотеку тегов. Решение Добавьте элемент listener (слушатель) в свой TLD-файл. Обсуждение API сервлета включает «слушателей событий приложения», являющихся специаль- ными Java-классами, которые извещаются web-контейнером при возникновении опреде- ленного события, например при создании нового сеанса пользователя (см. главу 11). Вы можете включать классы слушателей в свои библиотеки тегов. К примеру, у вас может быть тег, связанный с сеансом, которому необходимо знать, когда сеансы создаются и когда разрушаются. Элемент listener TLD-файла имеет точно такой же синтаксис, как аналогичный элемент из дескриптора развертывания web.xml. В примере 22.11 показан элемент lis- tener, включенный в TLD-файл по версии JSP 2.0. Пример 22.11. Добавление элемента listener в TLD-файл по версии JSP 2.0 <!-- начало TLD-файла. Элемент listener, вложенный в элемент taglib. ЗА ПРИМЕРАМИ КОДА СЛУШАТЕЛЯ ОБРАЩАЙТЕСЬ К ГЛАВЕ 11 ИЛИ 14 — > <taglib xmlns="http://java,sun.corrt7xml/ns/j2ee" xmlns: xs i =" ht tp: //www.w3.org/2001/XMLSchema-ins tance" xsi:schemaLocation= "http://java.sun.com/xml/ns/з 2ee http: / /java. sun. com/xml /ns I j 2ee/web-j sptaglibrary_2_0. xsd" versions"2.0" <tlib-version>l.0</tlib-version < jsp-version>2.0</jsp-version> < short -name>cbck< / short -riame> < uri>com.jspservletcookbook.tags</uri> < description>Cookbook custom tags</description <listener> <listener-class> 584 | Глава 22. Использование пользовательской библиотеки тегов
com. jspservletcookbook.ReqListSner <I listener-class» </listener» <tag> у/ <!— объявляйте тег здесь. См. примеры 22-2 (Рецепт 22.2), 22-3 ,£Р6ц.елт 22.3), или 22-7 (Рецепт 22.9) —> </tag> </taglib» Спецификация JSP требует, чтобы контейнер JSP автоматически создавал и регис- трировал экземпляр слушателя, связанного с библиотеками тегов. Слушатель может исполь- зоваться с библиотекой тегов, чтобы отслеживать количество запросов, получаемых web- приложением, как показано в ServletRequestListener из примера 18.8 (рецепт 18.6). TLD-файл по версии 1.2 использует XML DTD. Следовательно, элементы в TLD- файле должны следовать в строго определенной последовательности. Элемент listener вложен в элемент taglib; в нем listener следует после всех остальных вложенных элементов, кроме элемента tag. Вы можете поставить элемент listener перед своими элементами tag. С другой стороны, в TLD- файле по версии JSP 2.0 вы можете разместить элемент listener сразу после корневого элемента taglib. Сохраните все классы слушателей в том же JAR-файле, в котором хранятся классы обработчиков тегов. Убедитесь, что в элементе listener-class вы задали полностью квалифи- цированное имя класса слушателя, в противном случа» контейнер JSP может не найти этот класс. См. также Пример 18.8 из рецепта 18.6 где приводится класс, реализующий интерфейс javax. servlet.ServletRequestListener; главу 11 и главу 12, содержащие несколько рецептов касающихся слушателей; рецепт 22.2 по созданию TLD-файла по версии JSP 1.2; рецепт 22.3 по созданию TLD-файла по' версии JSP 2.0; рецепт 22.9 по созданию TLD для упрощенного обработчика та а; разделы, посвященные пользовательским тегам, книги «Jav- aServer Pages* Ханса Бергстена (Hans Bergsten), издание третье (O'Reilly). Добавление к библиотеке тегов класса слушателя | 585
•_______ГЛАВА 23 Использование JSTL 23.0 Введение Пользовательские теги JSP и обработчики тегов предназначены для того, чтобы дать вам возможность создавать собственные теги. Это очень мощный инструмент в руках Java-разработчиков, однако, разработка пользовательских тегов требует дополнительных знаний и может отнять много времени. К счастью, инициативная группа разработчиков программ, в результате напряженной работы, создала для вас набор исключительно полезных тегов. Этот набор тегов носит название библиотека стандартных тегов JSP (JavaServer Pages Standard Tag Library - JSTL). Спецификация JSTL появилась в результате коллективного процесса Java-разработчиков (Java Com- munity Process (JSR-052)), а участники проекта Apache Jakarta Project разработали первую реализацию JSTL - библиотеку тегов версии 1.0 (Standard 1.0 taglib). JSTL имеет очень широкую функциональность. Она включает теги, которые: 1. устанавливают атрибуты объектов для web-приложений (с: set); 2, выводят текст на web-страницы (с: out и х: out); 3. перебирают элементы набора данных (с: forEach и х: forEach); 4. форматируют числа, даты, и валюты, йспользуя разные интернациональные стили (например, fmt: formatDate, fmt: forma tNumber); 5. выполняют преобразование XML (x: trans form); 6. взаимодействуют с базами данных используя SQL (например, sql: query, sql: update); 7. позволяют вам встраивать вызовы функций в код JSP и шаблонный текст (например, fn:substring О). Эта функциональность доступна только для JSP 2.0 и JSTL 1.1 (см. рецепт 23.14). JSTL породила новую, очень важную технологию - язык выражений (Expression Language - EL). Это язык сценариев (базирующийся главным образом на JavaScript и других инструментах сценариев), который, начиная с JSP 2.0, можно встраивать в шаблонный HTML-текст. 586
Сначала EL был частью JSTL 1 0, но теперь он перекочевал в спецификацию JSP. Поддержка EL должна быть реализована в JSP-контейнерах, отвечающих * jc спецификации JSP версии 2.0, таких, как Tomcat 5. Данная глава позволит вам оперативно освоить библиотеку JSTL, которую можно скачать и установить в 'web-приложении. Многие web-контейнеры, в конце концов, интегрируют, или уже интегрировали реализацию JSTL в свои исполнительные подсис- темы, обеспечивающие работу сервлетов и JSP. 23.1 Скачивание JSTL 1.0 и использование тегов JSTL на страницах JSP Задача Вы хотите скачать JSTL на свой компьютер и пользоваться этой библиотекой. Решение Скачайте дистрибутив JSTL в виде ZIP или TAR файла с сайта Apache Jakarta Project. Обсуждение Сайт Apache Jakarta Project содержит справочную реализацию (reference implementation) библиотеки JSTL. Справочная реализация - это программное обеспечение, реализующее спецификацию определенной Java-технологии, с целью демонстрации того, как такое программное обеспечение должно функционировать. Справочная реализация может сво- бодно использоваться разработчиками и продавцами программ. Дистрибутив JSTL в двоичном виде или в виде исходных кодов можно скачать с http://jakarta.apache.org/ taglibs/doc/standard-doc/intro.html. Распакуйте ZIP или TAR файл в выбранный по вашему усмотрению каталоп Это приведет к созданию каталога jakarta-taglibs. В этом рецепте используется Standard Taglib версии 1.0.3 - реализация JSTL 1.0. Однако к тому времени как выбудете это читать, на сайте Jakarta Taglibs появится Standard Taglib версии 1.1, которая является реализацией JSTL 1.1. Новая версия будет включать ряд новых возможностей, например функции, которые описаны в рецепте 23.14. В каталоге standard-1.0.3 находится подкаталог lib. Он содержит ряд JAR-файлов, в том числе jstl.jar и standardjar. В файле jstl.jar хранятся классы JSTL 1.0 API.; а в файле stan- dardjar - набор классов реализации JSTL 1.0. Добавьте все JAR-файлы, находящиеся в каталоге lib дистрибутива JSTL (например, в каталоге jakarta-taglibs/standard-1.0.3/lib) в каталог WEB-INF/lib. Скачивание JSTL 1.0 и использование тегов JSTL на страницах JSP | 587
JSTL 1.1 требует установки в каталог WEB-INFAib только jstl jar и standard jar (при условии, что вы используете J2SE версии 1.4.2 или выше, а также Servlet 2.4 и JSP 2.0). В табл. 23.1 описан каждый из JAR-файлов, находящихся в каталоге lib дистрибу- тива (информация заимствована из документации по Standard Taglib 1.0). Табл. 23.1. Содержимое каталога lib справочной реализации JSTL 1.0 Имя файла Назначение jstl.jar standard .jar jaxen_full.jar jdbc2_0-stdext ,jar >, saxpath.jar xalan.jar dom.jar, jaxp-api.jar, sax.jar, xerceslmpl.jar Классы API JSTL 1.0 Классы реализации JSTL 1.0 Классы исполнительной подсистемы Xpath Классы реализации JDBC (также включенные в J2SE 1.4) Простой API для синтаксического разбора Xpath Процессор XSLT-преобразования Apache Xalan Библиотеки Java API для обработки XML (JAXP); API версии 1.2 На странице JSP, где вы хотите использовать теги JSTL, необходимо поместить соот- ветствующую директиву taglib, из тех, что показаны в табл. 23.2. Если, например, вы используете все возможные JSTL-функции (функции ядра, XML, форматирования, и SQL), ваша страница должна содержать все из приведенных директив taglib, причем жела- тельно в начале страницы (онй должны следовать до тегов, которые их используют). Табл. 23.2. Директивы taglib для разных JSTL-функций, версия 1.0 Библиотека JSTL Директива taglib Ядро Обработка XML Форматирование данных (например, дат и валют) для интернациональных пользователей <%@ taglib uri='java.sun.com/jstl/core" prefix="c" %> <%@ taglib uri="java.sun.com/jstl/xml" prefix=”x" %> <%@ taglib uri="java.sun.coni/jstl/fmt" prefix="fmt" %> I SQL и доступ к базам данных <%@ taglib uri="java.sun.com/jstl/sqr prefix="sqr %> 588 | Глава 23, Использование JSTL
ijava-сообщсство сейчас работает над JSTL версии 1.1, для которой потребуется контейнер JSP совместимый с JSP 2.0. Для JSTL 1.1 в директиве taglib будут £ использоваться другие uri. * http://java.sun.com/jsp/jstl/core, так что полностью директива будет выглядеть так: <%@ taglib uri="java.sun.com/jsp/jstl/core" prefix="c" %> * http://java.sun.Com/jsp/jstl/xml, директива taglib: <%@ taglib uri<="java. sun.com/jsp/jstl/xml" prefix="x" %> * http://java.sun.com/jsp/jstl/jmt, директива taglib: <%@ taglib uri="java. sun.com/jsp/jstl/fmt" prefix="fmt" %> * http://java.sun.cont/jsp/jstl/sql, директива taglib: <%@ taglib uri="java. sun.com/jsp/jstl/sql" prefix="sql" %> См. также Сайт проекта Jakarta с библиотеками тегов: http://jakarta.apache.org/taglibs/index.html; информационная страница Sun Microsystem по JSTL: http://java.sun.com/products/jsp/jstl/;, рецепт 23,3 по использованию тегов ядра; рецепты 23.4 и 23.5 по использованию тегов, свя- занных с обработкой XML; рецепт 23.6 по использованию тегов форматирования; рецепты 23.7 и 23.8 по SQL-возможностям JSTL; рецепты 23.9-23.14 по использованию EL для доступа к переменным с различными областями видимости, к cookies, и к свойствам компонентов JavaBean. 23.2 Скачивание пакета разработчика web-серйисов Java Задача Вы хотите скачать пакет разработчика web-сервисов Java (Java Web Services Developer Pack - WSDP), что позволит вам использовать справочную реализацию JSTL 1.1. Решение Посетите сайт Sun Microsystems, содержащий Java WSDP: htip://java.sun.com/webser- vices/jwsdp. Обсуждение Java WSDP версии 1.2 включает в себя справочную реализацию JSTL 1.1, а кроме того - ряд других web-технологий, ? том числе API сервлетов 2.4 и JSP 2:0. Пакет WSDP интегрирован с Tomcat 5, так что после того, как вы установите этот пакет, вы сможете начать экспериментировать с различными технологиями, включая JavaServer Faces, Java Architecture for XML Binding (JAXB), Java API for XML Processing и Java API for XML- based RPC (Jax-RPC). Скачивание пакета разработчика web-сервисов Java | 589
Пакет WSDP 1.2 инсталлируется и под Windows и под раз^чные Unix-системы, такие, как Solaris и Linux. В рецепте 23.15, посвященном использованию функций JSTL 1.1, я пользовался пакетом WSDP. Использование Tomcat 5 и новых возможностей JSTL 1.1, таких, как функции и встраивание EL-кода в шаблонный текст, требует, чтобы вы использовали web. xml, соответствующий API сервлета версии 2.4. Подробности см. в рецепте 23.15. См. также Сайт Sun Microsystems для скачивания WSDP: http://java.sun.com/webservices/jwsdp', рецепт 23.15 по использованию функций JSTL 1.1} информационная страница Sun Microsys- tem по JSTL: http://java.sun.com/products/jsp/jstl/, рецепт 23.3 по использованию тегов ядра; рецепты 23.4 и 23.5 по использованию тегов, связанных с обработкой XML; рецепт 23.6 по использованию тегов форматирования; рецепты 23.7 и 23.8 по SQL-возможностям JSTL; рецепты 23.9-23.14 по использованию EL для доступа к переменным с различными облас- тями видимости, к cookies, и к свойствам компонентов JavaBean. 23.3 Использование тегов ядра JSTL Задача I Вы хотите на странице JSP использовать 1еги ядра JSTL. Решение Чтобы сделать эти теги доступными на странице JSP, используйте директиву taglib со значением атрибута uri равным core. Обсуждение В данном рецепте показано несколько JSTL-тегов, которыми мы пользовались все это время: с: set, с: out, с: f orEach, и с: i f. Ниже представлено их краткое описание • Тег с:set устанавливает атрибуты объекта для областей видимости раде (страница), request (запрос), session (сеанс), или application (приложение). • Тег с: out отображает на странице JSP текстовые литералы, или значения перемен- ных, или свойства компонента JavaBean. • Тег с: forEach перебирает значения отображений (Мар), наборов (Collection), и массивов (array). • Тег с:if проверяет значения выражений на истину (true) или ложь (false) и в зависимости от результата проверки выполняет код, вложенный в тело тега с: if. 590 | Глава 23. Использование JSTL
Не забывайте указывать префикс определенной функциональной области JSTL, например с, за ним - двоеточие и потом уже имя тега, например, с: f orEach. В примере 23.1 приведен вспомогательный класс, который я посчитал нужным соз- дать для того, чтобы на странице JSP в примере 23.2 правильно возвращать массив строк с идентификаторами часовых поясов. Пример 23.1. Вспомогательный класс для отображения идентификаторов часдвых пдясов package com.jspservletcookbook; import java.util.Timezone; public class Zonewrapper { public ZoneWrapper()<} public String[] getAvailablelDs(){ return TimeZone.getAvailablelDs() (); } 1 В примере 23.2 показано использование ряда тегов ядра JSTL. Для создания объектов ZoneWrapper (пример 23.1) и java.util .Date, с которыми работают теги, в коде используется стандартное действие j sp: useBean. Пример 23.2. Использование тегов ядра JSTL 1.0 на странице JSP <%@ taglib uri="http://java.sun.com/jstl/core" prefix*"c" %> <jsp:useBean id="zonr" class»"com.jspservletcookbook.ZoneWrapper" /> <jsp:useBean id="date" class»"java.util.Date" /> <html> <headxtitle>Using the Core JSTL tags</titlex/head> <body> <h2>Here are the available Time Zone IDs on your system</h2> <c:if test*"${date.time I* 0}" > , <c:out value* "Phew, time has not stopped yet...<br /xbr />" escapeXml*"false"/> </c:if> <%— Переменная 'zones" содержит массив строк с идентификаторами часовых поя- сов; она сохраняется как атрибут объекта 'session'(сеанс). Выражение '${zone. availableZDs}• эквивалентно вызову метода ZoneWrapper.getAvailablelDsO —%> <c:set var»-"zones" value*" $ {zone. availablelDs}" scope* "session" /> Использование тегов ядра JSTL | 591
<c:forEach var*"id" items*"${zones}"> r <cxout value*"${id}<br />" escapeXml*"false" /> </с:forEach» </body> </html> Тег c: if использует фразу на языке EL для проверки того, что метод getTime () объ- екта Date возвращает ненулевое значение (значение естественно ненулевое, я просто демонстрирую как пользоваться тегом с: if). ${date.time != 0} Этот код представляет собой логическое выражение (boolean), возвращающее значение true если Date. getTime () больше нуля. Если значение true, выполняется вложенный тег с: out, который пишет сообщение, отображаемое браузером клиента. Код escapeXml="false" приводит к корректному отображению символов <Ьг /><Ьг /> в выходном HTML, генерируемом тегом с: out. CM. табл. 23.3. Пример 23.2 устанавливает атрибут объекта области видимости сеанс (session). Этот объект имеет тип String!] и содержит идентификаторы часовых поясов, например «Pacific/Tahiti». Затем с помощью тега с: forEach мы в цикле перебираем элементы дан- ного массива, отображая каждый посредством тега с: out. <с:forEach var="id" items*"${zones}•> <c:out value*"${id}<br />" escapeXml*"false" /> </c:forEach» Во время перебора массива текущий член массива сохраняется в атрибуте var тега с: forEach. Для доступа к значению текущего члена массива в теге с: out используется EL-выражение. <c:out value*"${id}<br /> escapeXml*"false" /> Если при использовании тега с: out 'атрибуту escapeXml не присвоить значение false, то вместо символов, которые выводятся с помощью escape-последовательностей, будут отображаться соответствующие им коды символьных сущностей, приведенные в табл. 23.3. На рис. 23.1 показана часть страницы JSP, использующей код из примера 23.2. 592 | Глава 23. Использование JSTL
Табл. 23.3. Заменяемые символы тега c:out Символ из атрибута value тега c:out Код символьной сущности < &lt; > & с &gt; &amp; &#039; « &#034; Рис. 23.1. Страница JSP, использующая различные теги ядра для отображения идентификаторов часовых поясов Использование тегов ядра JSTL | 593
См. также Рецепт 6.8 по включению в содержимого в JSP с помощью тега с :url; сайт с библио- теками тегов проекта Jakarta Project http://jakarta.apache.org/taglibs/index.html', информаци- онная страница Sun Microsystem по JSTL: http://java.sun.com/products/jsp/jstl/’, рецепты 23.4 и 23 5 по использованию тегов, связанных с обработкой XML; рецепт 23.6 по использо- ванию тегов форматирования; рецепты 23.7 и 23.8 по SQL-возможностям JSTL; рецепты 23.9-23-14 по использованию EL для доступа к переменным с различными облас- тями видимости, к cookies, и к свойствам компонентов JavaBean. 23.4 Использование тегов JSTL, связанных с обработкой XML Задача Вы хотите использовать на странице JSP XML-теги библиотеки JSTL. Решение Различные XML-теги можно использовать после объявления библиотеки тегов с помощью соответствующей директивы taglib (для JSTL 1.0 атрибут uri равен http:/ /java.sun.com/jstl/xml, для JSTL 1.1 - http://java.sun.com/jsp/jstl/xml). ' Обсуждение Многим web-разработчиком приходится писать программы, разбирающие или читающие XML, в поисках определенной информации, или код, отображающий информацию, нахо- дящуюся в XML-документе, в удобочитаемом виде. Теги JSTL для работы с XML - самый подходящий инструмент для такого рода задач. В примере 23.3 отображается некоторая информация из файла buid. xml программы Ant. (Если вы еще не знакомы с инструментом Ant, обратитесь к главе 4). Я использую этот XML-файл только для того, чтобы показать, как применяются JSTL-теги, связанные с обработкой XML. Обратите внимание, что директивы taglib, расположенные в верхней части страницы, позволяют использовать на странице теги обработки XML и теги ядра библиотеки JSTL. Пример 23.3. Страница JSP, осуществляющая синтаксический разбор файла build.xml <%в taglib uri="http://java.sun.com/jstl/xml1* prefix="x" %> taglib urip"http://java.sun.com/jstl/core" prefix="c" %> <html> <head><title>Using the Core XML tags</titlex/head> <body> <h2>Here are the target and property Values from the XML file</h2> 594 | Глава 23. Использование JSTL
<c:import url= "http://localhost:8080/home/build.xml" var«"buildXml" /> <x: parse xml«"${buildXml}" var»"antDoc" /> <h3>First the target names.. </h3> <x:forEach select="$antDoc/project/target" > <x:out select="®name"/> <x:if selects"©depends"» • depends»<x:out select*"@depends"/x/x:ifxbr /> </x:forEach> <h3>Then property names and values...</h3> <x:forEach select«"$antDoc/project/target/property" > <x:out select»"@name"/>: value» <x:out select»"©value"/><br /> </x: forEach» </body> </html> В примере 23.3 для импорта сборочного файла (buildxml) и сохранения его в перемен- ной с именем buildXml используется тег с: import. Затем тег х: parse осуществляет синтаксический разбор импортированного документа и создание на его основе конструкции (объекта), с которой могут работать остальные теги обработки XML. Резуль- тат разбора сохраняется в другой переменной - с именем antDoc. Некоторые из JSTL-тегов для XML имеют такие же имена, что и теги из библиотеки ядра, но у них другой префикс («х»). Тег х: f orEach в примере 23.3 использует XPath- выражение в качестве значения своего атрибута select. । XPath - это XML-технология, предназначенная для поиска и выбора порции или «набора узлов» из иерархического дерева, представляющего XML-документ. Изучение XPath находится далеко за пределами данного рецепта (это по существу небольшой язык программирования), существует достаточное количество У руководств и книг по этому предмету. Вы можете начать с руководства Sun Microsystems по web-сервисам, которое включает обсуждение XPath; http://java. sun.eom/webservices/docs/l.3/tutohal/doc/. Тег х: for Each дает возможность вложенным в него элементам, например xjout, добираться до любого узла, из числа выбранных XPath-выражением. Код из примера 23.3 отображает имя каждой цели (target) Ant из файла buiklxml; для этого вначале из файла выбираются все элементы target. <%— это XPath-выражение означает "начать с корневого элемента 'project' и получить все вложенные в него элементы 'target1 " —%> <x:forEach select»"$antDoc/project/target" > Имя каждой цели (target) выводится при помощи следующего кода. <x:out select»"©name"/> Использование тегов JSTL, связанных с обработкой XML | 595
Тег х: forEach, охватывающий этот код, подставляет в тег х: out содержимое XPath-выражения. Приведенный ниже код указывает «вёрнуть true, если текущий узел имеет коррект- ный атрибут depends». <x:if select="©depends"> Если это выражение возвращает true, вложенный тег x:out выводит значение атрибута depends. Результат запроса браузером страницы JSP из примера 23.3 показан на рис. 23.2. Я преобразовал XML-информацию в более удобный для чтения в браузере формат. Для преобразования информации из XML-формата в HTML или иной удобочитае- мый формат можно использовать XSLT-преобразования (Extensible Stylesheet Language Transformation - преобразование на основе расширяемого языка стилевых таблиц). Это тема следующего рецепта. Рис. 23.2. Страница JSP отображает результат синтаксического разбора XML-файла 596 | Глава 23. Использование JSTL
См. также Сайт с библиотеками тегов проекта Jakarta Project http://jakarta.apache.org/taglibs/index. html', информационная страница Sun Microsystem по JSTL: http://java.sun.com/products/jsp/ jstl/; рецепт 23.3 по использованию тегов ядра; рецепт 23.5 пр использованию тегов преобразования XML; Рецепт 23.6 по использованию тегов форматирования; рецепты 23.7 и 23.8 по SQL-возможностям JSTL; рецепты 23.9-23.14 по использованию EL для доступа к переменным с различными областями видимости, к cookies, и к свойствам компонентов JavaBean. 23.5 Использование тегов преобразования XML Задача Вы хотите использовать JSTL-теги, предназначенные для работы с XML и XSLT. . Решение Объявите библиотеку тегов при помощи соответствующей директивы taglib (атрибут uri равен http://java.sun.com/jstl/xml для JSTL 1.0 или http://java.sun.com/jsp/jstl/ xml для JSTL 1.1) и используйте необходимые теги обработки XML. Обсуждение Ряд групп, разрабатывающих web-сайты, имеют созданные ранее таблицы стилей для преобразования XML в HTML. Кроме тою, у вас может возникнуть желание выне- сти действия, связанные с преобразованием XML из страниц JSP, с тем чюбы JSP фокусировались только на представлении уже преобразованной информации. JSTL включает теги, связанные с обработкой XML и предназначенные для облегчения интеграции таблиц стилей в JSP. В примере 23.4 приведен XSL-документ (Extensible Stylesheet Language - расширяемый язык стилевых таблиц), который преобразует XML в HTML. Эта таблица стилей обеспечивает преобразование сборочного файла Ant к виду, описанному в рецепте 23.3. Пример 23.4. Таблица стилей для преобразования XML-файла <?xml versions"!.0" encodings"ISO-8859-1"?> j <xsl: stylesheet xmlns: xsl= "http: I /мм. w3. org/1999/XSL/Transform" versions * 1.0" > <xsl:output methods"html"/> <xsl:template match="/"> <htmlxhead><title>List of build.xml targets </title></headxbody bgcolor="white"xh2>Build.xml targets</h2> Использование тегов преобразования XML | 597
<xsl:apply-templates /> * < /bodyx /html > </xsl:template> <xsl:template match="/proj ect"> <dl> <xsl:for-each select="./target"> r s<dtxb> , <xsl:value-of select""©name" /></b»&#xA0;</dt> <xsl:if test="©depends"> ♦ <dd>depends=<xsl:value-of select" ".©depends" /»&#xA0,'</dd> </xsl:if> </xsl:for-each><!—end fqr-each —> </dl> </xsl:template» <xsl:template match"•text()"> <xsl:value-of select="normalize-space()" /> </xsl:template» </xsl:stylesheet» Как применить этот XSL-файл, чтобы получить buikLxml (сборочный файл) в.удобочитаемом формате? В примере 23.5 для связи таблицы Стилей и XML-файла исполь- зуется тег х: transform. Но сначала JSP, с помощью тега с:import, импортирует таб- лицу стилей из предыдущего примера, а также XML-файл, который будет преобразовываться с помощью этой таблицы стилей. Тег с: import импортирует ресурс, заданный с помощью атрибута url, и сохраняет его в пёременной (например, в buildXml), с которой далее может раоотаги тегх: transform. Пример 23.5. Страница JSP, отображающая результат XSL-преобразования <W taglib uri»"http://java.sun.com/jstl/xml" prefix»"к" %» <%& taglib uri="http://java.sun.com/jstl/core" prefix»"c" %> <c:import url»"http://localhoLt:8080/home/bulld.xml” var»"buildXml" /» ^c:import url»"/WEB-INF/xslt/chap23.xsl" var»"xslt" /> <x: trans form xml»"${buildXml}" xslt»"${xslt}" /» Ter x: transform делает процесс преобразования очень простым, достаточно лишь иметь файл с правильной таблицей стилей. Атрибут xml тега х: trans form задает XML- файл, подлежащий преобразованию. А атрибут xs It задает таблицу стилей, приме- няемую для преобразования. 598 | Глава 23. Использование JSTL
Атрибуты xml и -xslt тега х: transform декларируют использование переменных, представляющие таблицу стилей и XML-файл, используя EL. ${buildxml} Результат запуска JSP из примера 23.5 показан на рис. 23.3. Резюмируя, тег х: trans- form дает вам возможность использовать в JSP свой собственный XSLT-процессор. Build.xml targets init prepare depehds=init start deploy-servlet depends=prepare compile depends=;prepare lg-етеж^'.!.---------------------S Puc. 23.3. Страница JSP, отображающая преобразованное XML-содержимое См. также Сайт с библиотеками тегов проекта Jakarta Project http://jakarta.apache.org/taglibs/index. html; информационная страница Sun Microsystem по JSTL: http://java.sun.com/products/jsp/ jstl/; рецепт 23.3 по использованию тегов ядра; рецепт 23.4 по использованию различных тегов для работы с XML; рецепт 23.6 по использованию тегов форматирования; рецепты 23.7 и 23.8 по SQL-возможностям JSTL; рецепты 23.9-23.14 по использованию EL для доступа к переменным с различными областями видимости, к cookies, и к свойствам компонентов JavaBean. Использование тегов преобразования XML | 599
23.6 Использование JSTL-тегов форматирования Задача Вы хотите с помощью JSTL отформатировать дату или число. Решение Используйте действия fmt: f ormatDate и fmt: f ormatNumber. Обсуждение - Интернационализация или «i!8n» - процесс, при котором web-разработчики разраба- тывают web-сайты, учитывая интересы посетителей, говорящих на разных языках. Термин «i!8n», означает internationalization (интернационализация) - начинается с «i», за которой следуют 18 букв и буква «п» на конце Он придуман, чтобы заменить написание такого длинного слова более коротким вариантом. Локализация означает добавление к web-сайту специфических ресурсов с тем, чтобы информация, например приветствия на web-странице, могла быть правильно переведена на язык посетителя. К примеру, вы можете локализовать сайт для посетителей из Японии, добавив ресурсы, которые содержит перевод текстов web-страниц на японский язык (более широко Java-код, связанный с локализацией представлен в главе 24). Страница JSP из примера 23.6 использует библиотеку JSTL с тегами форматирования для отображения текущей даты и некоторого большого числа в соответствии со стилями, принятыми в Швейцарии и США. Пример 23.6. Отображение даты и числа для аудитории из США и Швейцарии <%0 taglib uri»"http://java.sun.com/jstl/core" prefix="c" %> <%—include this taglib for il8n related actions —%> <ЗД taglib uri»"http://java.sun.com/jstl/fmt" prefix»"fmt" %> <html> <headxtitle>Formatting numbers and dates</titlex/head> <body> <h2>Dates and numbers in Swiss and US style formats</h2> <%-- создаем объект, представляющий текущую дату —%> <jsp:useBean id«"now" class»"java.util.Date"/> <%— устанавливаем locale: немецкий язык и код страны - Швейцария —%> 600 | Глава 23. Использование JSTL
< fmt:setLocale value=de_CH" / > < s trong> Swi s s-s ty1e date:</s trong> < %— выводим дату —%> <fmt:formatDate type= "both" values"${now}" dateStyle="full" timeStyle="short" /> <br /> <strong>Swiss-style number:</strong> < %— выводим java.util.Date.getTimeO чтобы показать, как форматируются числа —%> <fmt:formatNumber values"${now.time}" /> <br /><br /> < %— устанавливаем locale: английский язык и код страны - США —%> <fmt:setLocale values"en_US"/> <strong>US-style date:</strong> < %— выводим дату —%> <fmt :formatDate type="both" values"${now}" dateStyle= "full" timeStyle="short" /> <br /> <strong>US-style number:</strong> <fmt:formatNumber values"${now.time}" /> <br /><br /> </body> </html> В примере 23.6 для установки контекста дкя форматирования дат и чисел используется тег fmt: setLocale;. сначала устанавливается Швейцария-немецкий язык («de_CH»), а затем США-английский язык («en_US»). л \ rtt**- Термин «locale» представляет определенный культурный, географический, или политический регион. Этот регион обычно задается, строкой, в которой находится * fv код языка, символ подчеркивание.(_), и код страны. Более широкое обсуждение il8n находится в главе 24. Оба тега - и fmt: formateDate и fmt: f оrmatNumber - для форматирования своей информации используют текущую локалы Тег fmt: f ormateDate имеет несколько атрибу- тов, предназначенных для настройки формата даты. Атрибут both (и то, и другое) указы- вает, надо ли выводить только дату, только время, или и то, и другое, как в примере 23.6. Атрибуты datestyle и timestyle имеют настройки, которые наследуются то класса java.text.DateFormat. В примере 23.6 задано полное («full») отображение даты, включающее показ дня недели, месяца и года. В этом коде также задано сокращенное («full») отображение времени (например, «8:07»). Использование JSTL-тегов форматирования | 601 /
Изображение страницы JSP, на которой форматируются дата и время для клиентов «Швейцария'-немецкий» и «США-английский», приведено На рис. 23.4. Существует несколько других JSTL-тегов, имеющих отношение к форматированию, которые будут обсуждаться детально в рецептах главы 24. З Formatting numbers and dates - Microsoft Internet Explorer ИН 131 Dates and numbers in Swiss and US style formats Swiss-style date: Samstag, 5. Juli 2003 08:07 Swiss-style number: ТОбУ^Об'ббв'ЮТ US-style date: Saturday, July 5,2003 8:07 AM 4 US-stvIe number: 1 057 406 868 101 К Рис. 23.4. Теги frat: forraateDate и frat: forraatNumber, выполняющие волшебное превращение на странице JSP См. также Главу 24 по использованию нескольких JSTL-тегов, относящихся к i 18п; Сайт с библио- теками тегов проекта Jakarta Project http://jakarta.apache.org/taglibs/index.html', информаци- онная страница Sun Microsystem по JSTL: http://java.sun.com/products/jsp/jstl/; рецепт 23.3 по использованию тегов ядра; рецепт 23.4 по использованию различных тегов для работы с XML; рецепт 23.5 по использованию тегов преобразования XML; рецепты 23.7 и 23.8 по SQL-возможностям JSTL; рецепты 23.9-23.14 по использованию EL для доступа к переменным с различными областями видимости, к cookies, и к свойствам компонентов JavaBean. 602 | Глава 23. Использование JSTL
23Л Использование SQL-тегов JSTL с настройкой DataSource Задача Вы хотите работать с реляционной базой данных, настроив для этого javax.sql. DataSource в дескрипторе развертывания. Решение Добавьте в web.xml элемент context-param, создав параметр с именем javax. servlet. j sp. j stl. sql. datasource (источник данных), связанный с определен- ной базой данных. Обсуждение / Библиотека JSTL с тегами для работы с SQL позволяет страницам JSP взаимодейство- вать с базой посредством пользовательских тегов. Работу этой технологии обеспечивает JDBC и классы пакета javax. sql. Но для начала необходимо настроить источник дан- ных (DataSource), который теги будут использовать для доступа к базе данных. DataSource - это фабрика для объектов java.sql.Connection, которые представляют сокетное соединение с некотором сервером базы данных, наприцер MySQL или Oracle. В примере 23.7 показан элемент context-param (параметр контекста) из файла web. xml. Чтобы JSTL-теги, предназначенные для работы с SQL, могли автоматически получить соединения (Connection), настроенные с помощью этого элемента, данный параметр должен иметь имя. javax.servlet-jsp. jstl.sql.datasource. Значение этого параметра представляет собой фразу, разделенную запятыми, со следующим содержанием. [JDBC URL],[имя драйвера], [имя пользователя],[пароль] Обычно разработчики извлекают JDBC URL и имя драйвера из документации постав- щика базы данных (а зачастую из списков рассылки, поскольку отладка JDBC-соединений с базой данных может оказаться делом'сложным!). В приведенном здесь коде использу- ется JDBC URL для Oracle8i Personal Edition. Пример 23.7. Пример настройки javax. sql. Da tasource в файле web.xml <!— начало файла web.xml —> <context-param> <param-name> javax. servlet. j sp. j st 1. sql. dataSource< /param-name> <param-value> jdbc: oracle: thin: @192.168.0.2:1521 :ORCL, Использование SQL-тегов JSTL с настройкой DataSource | 603
oracle.jdbc.driver.OracleDriverf scott,tiger</param-value» 4/context-param» <!— продолжение файла web.xml —> JSTL-программы используют эти значения для генерирования источника данных (Datasource) для тегов, работающих с SQL. Преимущество внешней настройки источника данных заключается в возможности переключения на другую базу данных без изменения кода страницы JSP, а только изменяя значение параметра (context- param) и настраивая его на новую базу данных. С помощью SQL-тего; и настройки источника данных осуществляется «прозрачная» работа JSP с базой данных. Теперь обратимся к JSP. Помните, что SQL-теги (теги использующие префикс «sql») работают с тем источником данных (DataSource), который вы настроили с помощью элемента context-param в файле web.xml. В web-приложениях файл web.xml всегда размещается в каталоге WEB-INF. За подробностями обращайтесь к главе 1. Для того чтобы на странице JSP можно было использовать теги ядра и SQL-теги биб- лиотек JSTL 1.0, в начало страницы WEB-INF поместить соответствующие директивы taglib. Пример 23.8. Страница JSP, использующая JSTL-теги, предназначенные для работы с SQL, для отображения информации из базы данных taglib uri-"http://java.sun.com/jstl/core" prefix-"с" %> taglib uri-"http://java.sun.com/jstl/sql" prefix-"sql" %> <html> <headxt i t le>Database Query</1itle></head» <body> <h2>Querying a database from a JSTL tag</h2> , <sql:query var-"athletes"» SELECT * FROM athlete </sql: query» «table border="1"» <c:forEach var-"row" items-"${athletes.rows}"» <tr> < th>user_id</th> < th>name< / th> < th»bi rthdate</th» < th>pas swrd</th» < th»gender< / th>< / tr > 604 | Глава 23. Использование JSTL
<tr> > <tdxc:out value s"${row.user_ld}"/></td> <td»<c sout value*" $ (row.name} "/></td> <td><c:out value*"${row.birthdate} n/x/ta> ' <td><c&9ut value="${rbw.passwrd} "/></td> <td><c:out value* "${ row.gender}"/></td> </tr> </c:forEach» </table» </body> </html> Ter sql: query использует вложенное в него содержимое, чтобы послать к базе дан- .ных SQL-оператор «select * from athlete». Соединение с базой данных (connection) производится от источника данных, который вы уже настроили. Этот оператор выбирает все строки (записи ) из таблицы «athlete». Тег sql: query сохраняет результирующий набор в объекте javax. servlet. j sp. j stl. sql. Result, в переменной athletes. . иг* Объекты' Result получаются в результате преобразования из объектов java. * . sql- Result Set. У объектов Result имеются методы, предназначенные (V? для взаимодействия с SQL-тегами JSTL. Код ${athletes.rows} представляет собой EL-фразу, вызывающую метод getRows () объекта Result. Этот метод возвращает тип java.util.SortedMap[]. to есть массив объектов Sorted- Мар. Для перебора элементов этого массива и создания для каждой из строк (записей) базы данных - отдельной строки HTML-таблицы, используется тег с: f orEach. Для отображения имен столбцов результирующего множества (этот набор хранится в переменной «athletes») можно использовать такой код. <с:forEach var="col" i tems= "$(athletes.columnNames}"> <c:out value*"${col}"/> </с:forEach» В следующем рецепте показано, как JSP может выполнить эту же задачу, не прибегая к настройке источника данных с помощью параметра контекста (context-param). Использование SQL-тегов JSTL с настройкой DataSource | 60S '
Старайтесь придерживаться стратегии, предполагающей настройку источника данных в web.xml, поскольку такой подход является более предпочтительным, нежели определение источника данных внутри самой страницы JSP. На рис. 23.5 показана страница JSP, отображающая в браузере информацию из строк базы данных. Рис. 23.5. Отображение информации из базы данных с помощью JSP См. также Главу 21 по работе с базами данных; Сайт с библиотеками тегой проекта Jakarta Project http://jakarta.apache.org/taglibs/index.html', информационная страница Sun Microsystem по JSTL: http://java.sun.cotn/products/jsp/jstU', рецепт 23.3 по использованию тегов ядра; рецепт 23.5 по использованию тегов преобразования XML; рецепт 23.6 по использованию тегов форматирования; рецепт 23.8 по использованию SQL-тегов JSTL без настройки DataSource; рецепты 23.9-23.14 по использованию EL для доступа к переменным с различными областями видимости, к cookies, и к свойствам компонентов JavaBean. 606 | Глава 23. Использование JSTL
23.8 Использование SQL-тегов библиотеки JSTL без предварительной настройки DataSource Задача Вы хотите задать источник данных * (DataSource) непосредственно внутри страницы JSP. Решение Для того чтобы назначить источник данных для остальных SQL-тегов, таких, как sql: query, используйте тег sql: setDataSoufrce. Обсуждение Источник данных, который будет использоваться SQL-тегаМи JSTL, можно задать непосредственно на странице JSP, для этого используется тег sql: setDataSource и его атрибут datasource. JSP из примера 23.9 создает такай же источник данных, как в рецепте 23.6, и сохраняет его в переменной dSource. Затем именно этот источник дан- ных указывается в атрибуте datasource тега sql:query. В коде решаются те же задачи, что и в примере 23.8: JSP посылает системе управления базой данных SQL- оператор SELECT и затем отображает результаты. Пример 23.9. Использование тега sql:setDataSource <ЭД taglib uri»"http://java.sun.com/jstl/core" prefix»"c" %> <%0 taglib uri«"http://java.sun.'com/jstl/sql" pr<fix»"sql" 4> <html> <head><title>Database Query</titlex/head> <body> <h2>Query.ing a database from a JSTL tag</h2> <sql:setDataSource dataSource» "jdbc:oracle:thin:0192.168.0.2:1521:ORCL,oracle.jdbc.driver.Ora- cleDriver,scott,tiger” var«"dSource" scope»"application"/» <sql:query vur"athletes" datasource»"dSource"> SELECT * FROM athlete </sql:query> <table border»"1"> <c:forEach var»"row" iterns»"${athletes.rows}"> < <tr> Использование SQL-тегов библиотеки JSTL | 607
<th>user_id</th> < th>name< / th> < th>birthdate< I th> < th>passwrd<Ith> < th>gender</th></tr> <tr> <tdxc:out value-"$ {row.user_id} "/x/td> <tdxc:out value-"${row.name}"/></td> <tdxc:out value-"$ {row. birthdate} "/x/td> <tdxc:out value*-"${row.passwrd} "/></td> < td> <c: out value* " $ { row. gender }"/></ td> </tr> </c:forEach> </table> </body> </html> Код сохраняет DataSource (источник данных) в переменной, имеющей область видимости «приложение», так что другая страница'JSP может обращаться к этому же источнику данных, посредством следующего кода. . <sql:query var="athletes" dataSource="${dSource}"> SELECT7 * FROM athlete </sql:query> Единственная разница между таким использованием тега sql: query и применением этого тега в примере 23.9 в том, что здесь значение атрибута вычисляется с помощью EL; тег пытается найти и получить значение переменной с именем «dSource» из области види- мости приложение (то есть значение атрибута контекста сервлета). / В SQL-тегах JSTL источник данных можно задавать, используя строку для интерфейса JNDI (Java Naming and Directory Interface - интерфейс именования и каталогов), но мы отложим обсуждение этой темы до главы 21, в которой рассматривается использование баз данных в сервлетах и JSP. См. также Главу 21 по работе с базами данных; Сайт с библиотеками тегов проекта Jakarta Project http://jakarta.apache.org/taglibs/index.html\ информационная страница Sun Microsystem по JSTL: http://jaya.suti.com/products/jsp/jstl/-, рецепт 23.3 по использованию тегов ядра; рецепты 23.4 и 23.5 по использованию XML-тегов; рецепт 23.6 по использованию тегов форматирования; рецепты 23.7 и 23.8 по использованию SQL-тегов JSTL; рецепты 23.9-23.14 по использованию EL для доступа к переменным с различными областями видимости, к cookies, и к свойствам компонентов JavaBean. 608 | Глава 23. Использование JSTL
23.9 Доступ к переменным, имеющим различные области видимости, с помощью EL Задача Вы хотите, используя пользовательский тег JSTL, получить и отобразить значение атрибута объекта. Решение Для получения значения атрибута, сохраненного в определенной области видимости, используйте EL и тег с: out. Обсуждение Такие объекты, как java.util.Date, java.lang.Integer, а также объекты, созданные вами, можно сохранять в четырех различных областях видимости: • раде (страница), тогда объект доступен только в том сервлете или той JSP, где он был создан; \ • request (запрос), тогда объект доступен всем страницам, которые с помощью RequestDispatcher (диспетчера запросов) взаимодействуют с данной страни- цей JSP, например, когда запрос перенаправляется от одной страницы к другой; • session (сеанс), тогда объект доступен любым сервлетам или JSP, участвующим в том же сеансе (см. главу 11); • application (приложение), эта область видимости представляет весь контекст сервлета, для одного web-приложения. В примере 23.10 для установки переменной com. jspservletcookbook. Session- Object в области видимости session (сеанс) применяется JSTL-тег с:set. Затем тег с: out получает и отображает значение этой переменной. Пример 23.10. Доступ к значению объекта^ хранящегося в области видимости session (сеанс) <%@ taglib uri="http://java.sun.com/jstl/core" prefix^c" %> <html> <headxtitle>Accessing a Scoped Value</titlex/head> <body> "* <h2>Here is the value of the Session-Scoped Attribu£e</h2> <c:set var= "com.j spservletcookbook.SessionObject" valuer Доступ к переменным, имеющим различные области видимости, с помощью EL , | 609 20-1545
"My object attribute.<br />" scope»"session" /> <c:out value» "${sessionScope[\"ccm.jspservletcookbook.SessionObjectX])" escapeXml»"false" /> </body> ' </html> , По соглашению атрибутам объестов присваивают имена, совпадающие с полностью квалифицированными именами их Java-классов (обычно по Java-типу сохраненного объ- екта). Таким образом, имена атрибутов содержат в своем составе символ точка (.). Именно поэтому используется синтаксис ${sessionScope,[\"com. jspservletcookbook. SessionObjectX"]}. Если имя атрибута не содержит точки, то для доступа к этому атрибуту объекта вы можете использовать EL-выражение, состоящее только из имени переменной, без неявного JSTL-объекта sessionScope, <c:out value» "${SessionObjectJ" escapeXml»"false" /> Если вы включите только имя, как в предшествующем фрагменте кода, то JSTL будет искать атрибут с этим именем последовательно в областях видимости страница, запрос, сеанс и приложение и вернет null, если не найдет. Вы должны использовать все полагающиеся в EL-выражении символы (знак доллара, и фигурные скобки, обрамляющие выражение: «$ { ... }»). В противном случае тег с: out выведет только строковый литерал, например «SessionObject». См. также Сайт с библиотеками тегов проекта Jakarta Project http://jakarra.apache.org/taglibs/index. htrnh информационная страница Sun Microsystem по JSTL: http://java.sun.com/products/jsp/ jstl/\ рецепт 23.3 по использованйю тегов ядра; рецепты 23.4 и 23.5 по использованию XML-тегов; рецепт 23.6 по использованию тегов форматирования; рецепты 23.7 и 23.8 по использованию SQL-тегов JSTL; рецепты 23.10-23.14 по использованию EL для доступа к параметрам запроса, к cookies, и к свойствам компонентов JavaBean. 610 | Глава23 Использование JSTL
23.10 Доступ к параметрам запроса с помощью EL Задача Вы хотите с помощью EL получить доступ к параметрам запроса на странице JSP. Решение ( Используйте в коде JSP неявный объект param. Обсуждение JSTL обеспечивает неявный объект param, который вы можете использовать для получения параметров запроса. Достаточно поставить за термом «рагат» точку и имя параметра. Этот синтаксис можно использовать в EL-выражениях при выводе значений параметров запроса с помощь» > тега с: out. Страница из примера 23.11 отображает привет- ствие, включая в него имя посетителя. Запрос страницы может выглядеть следующим образом. ht tp: / / localhos t: 80 80/home/welcome. j sp?name=Bruce%’2 OPerry , Если URL не включает в себя параметр «пате», данная страница JSP отображает сообщение: «Hello Esteemed Visitor». Пример 23.11. Использование JSTL в JSP для отображения параметров запроса <%0 taglib uri""http://java.sun.com/jstl/core" prefix»"с" \> <html> <head><title»Accessing a Scoped Value</title»</head> <body> <h2>Hello _ J <c:choose» <c:when test""${empty param.name}"» Esteemed Visitor </c:when» <c{otherwise»/ <c:out value""${purom.name}" /> < /с: otherwise» </cichoose> </h2> </body> </html> i, Доступ к параметрам запроса с помощью EL | 611 20*
Сначала код проверяет, содержит ли запрос значение параметра паше, для этого используется ключевое слово EL empty. <c:when test="${empty param, name} "> Теги c: choose, c:when, и c: otherwise подобны операторам if/then/else в Java-коде. Если у параметра запроса name нет значения, браузер отбразит строку «Esteemed Visitor». В противном случае он отобразит значение параметра name. На рис. 23.6 показана страница JSP, отображающая сообщение, включающее значение параметра Рис. 23.6. Скромное приветствие от страницы JSP, использующей неявный JSTL-объект См. также Главу 18 о работе с запросами клиентов; сайт с библиотеками тегов проекта Jakarta Project http://jakarta.apache.org/taglibs/index.html,, информационная страница Sun Microsys- tem по JSTL: http://java.sun.coin/products/jsp/jstl/', рецепт 23.3 по использованию тегов ядра; рецепты 23.4 и 23.5 по использованию XML-тегов; рецепт 23.6 по использованию тегов форматирования; .рецепты 23.7 и 23.§ по использованию SQL-тегов JSTL; рецепты 23.12-23.14 по использованию EL для доступа к заголовку запроса, к cookies, и к свойствам компонентов JavaBean. 612 | Глава 23. Использование JSTL
23.11 Использование EL для доступа к заголовкам запроса Задача Вы хотите использовать EL для получения значений заголовков НТТР-запроса. Решение Используйте неявный объект header, который EL делает доступным для пользова- тельских тегов. Обсуждение Неявно создаваемый объект header имеет тип j ava. util. Мар и содержит значения заголовков запросов, отображенные на ключи (которые в данном случае являются име- нами заголовков, например, «accept» или «user-agent»). Web-клиенты (обычно браузеры) посылают эти заголовки (пары имя/значение) вместе с web-гадресом с границы, которая их интересует. В примере 23.12 для циклического перебора заголовков запросов, хранящихся в объ- екте header, используется тег с: f orEach. Текущая пара заголовок/значение сохраня- ется при этом в переменной reqHead. Для отображения имени заголовка и его значения используется EL: выражение «${reqHead.key}» выводит имя заголовка, а «${ reqHead. value}») - значение. Пример 23.12. Использование JSTL для отображения заголовков запроса <%9 taglib uri и "http://java. sun. com/ jstl/core" prefix«"c" %> <htnil> <headxtitle>Request header display</titlex/head> <body> <h2>Here are all the Request Headers</h2> <4— 'header* имеет тип java.util.Map и хранит имена и значения заголовков запроса —%> <с:forEach-var-"reqHead" items""${header}"> <strongxc:out value" "$ {reqHead. key} "/x/strong>: <c:out value""${reqHead.value}"/><br / </c:forEach> </body> </html> Результат запроса этой страницы JSP из браузера показан на рис. 23.7. Использование EL для доступа к заголовкам запроса | 613
J Request header display - Microsoft Internet Explorer НПЯЕЗ file Edit yiew Favorites Jodis Hdp л:Р?Ж’’- . у1 y I T i J Back Stop Refresh Home ...... .. ...... .J ......... Search Address |& Htp://localhost:8080/home/headerChap23.jsp __________.________,---------------.-----------₽----t---------.----.--------,-,----------------—-------------------- Links @ InstitutionFS Welcome to Sparhawk! igjARi Documentation ST1 Beta Chapters Here are all the Request Headers accept-encoding: gzip, deflate connection: Keep-Alive host localhost:8080 accept-language: en-us user-agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows NT4.0) cookie: JSESSIONID=B39E7E176FEE53F6C47A635A8C19A098 accept 7* — ^Local Puc. 23.7. Отображение заголовков запроса на странице JSP См. также Главу 18 о работе с запросами клиентов; сайт с библиотеками тегов проекта Jakarta Project http://jakarta.apache.org/taglibs/index.html", информационная страница Sun Microsys- tem по JSTL: http://java.sun.com/products/jsp/jstl/", рецепт 23.3 по использованию тегов ядра; рецепты 23.4 и 23.5 по использованию XML-тегов; рецепт 23.6 по использованию тегов форматирования; рецепты 23.7 и 23.8 по использованию SQL-тегов JSTL; рецепты 23.12- 23.14 по использованию EL для доступа к заголовку запроса, к cookies и к свойствам ком- понентов JavaBean. 614 | Глава 23. Использование JSTL
23.12 Использование EL для доступа к конкретному заголовку запроса Задача Вы хотите использовать EL для доступа к значению одного конкретного заголовка НТТР-запроса. Решение Используйте неявный объект headervalues, который EL делает доступным для пользовательских тегов. Обсуждение Неявный объект headervalues имеет тип java.util .Мар и содержит по массиву строк (тип String[}) для каждого имени заголовка. В примере 23.13 отображается только значение заголовка запроса «user-agent» (этот заголовок указывает тип браузера, используемого клиентом). Пример 23.13. Использование JSTL для отображения заголовков запроса taglib uri="http://java.sun.com/jstl/core" prefix="c" %> <html> <head><title>User Agent</title></head> <body> <h2>Here is your user agents</h2> <%— 'headervalues’ имеет тип java.util.Map и хранит для каждого заголовка запроса массив сорок (тип String[]) —г%> <strong»<с:out value" "${headervalues[\"user-agent\"J10 ]}"/> </strong> </body> </html> В коде извлекается значение только первого члена массива строк (поскольку, скорее всего, этот заголовок запроса содержит всего одно значение). Выражение $ {headervalues [ \ "user-agent\ ]) возвращает целый массив, а полное исходное выражение включает оператор «[0}», применяемый к массиву и возвращающий тип браузера, например «Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 4.0)». Использование EL для доступа к конкретному заголовку запроса | 615
См. также Главу 18 о работе с запросами клиентов; рецепт 23.11 о том, как использовать EL для дос- тупа ко всем доступным заголовкам запроса; сайт с библиотеками тегов проекта Jakarta Project http://jakarta.apache.org/taglibs/iiulex.html', информационная страница Sun Microsys- tem по JSTL: http://java.sun.com/products/jsp/jstl/', рецепт 23.3 по использованию тегов ядра; рецепты 23.4 и 23.5 по использованию XML-тегов; рецепт 23.6 по использованию тегов форматирования; рецепты 23.7 и 23.8 по использованию SQL-тегов JSTL; рецепт 23.10 по использованию EL для доступа к параметрам запроса; рецепты 23.13 и 23.14 по использо- ванию EL для доступа к cookies, и к свойствам компонентов JavaBean. 23.13 Доступ к cookie с помощью EL Задача Используя EL посмотреть имена и значения всех cookie. Решение Для отображения имен и значений cookie используйте в JSP неявный EL-объект cookie. Обсуждение Неявный объект cookie имеет тип java.util .Мар и содержит отображение имен cookie (например, «JSESSIONID») на объекты javax. servlet .Cookie. Поскольку в данном случае cookie хранятся в объекте с типом Мар, то для отображения имени fH значения каждого cookie с помощью тега с: out, можно использовать их циклический перебор тегом с,: forEach. (См. пример 23.14). Не забудьте в начало страницы JSP включить директиву taglib, с тем чтобы можно было на этой странице использовать теги JSTL. Пример 23.14. Использование EL для отображения на странице JSP имени и значения каждого cookie / taglib uri-"http://java.Bun.com/jstl/core" prefix="c" %> <html> <head><title>Cookie display</titlex/head> <body> <h2>Here are all the Available Cookies</h2> <Яб— ${cookies.key}дает имя cookie; ${cookies.value} дает объект класса Cookie; ${cookies.value.value} возвращает значение cookie —%> <c:forEach var»"cookies" items""${cookie}"> t>16 j Глава 23. Использование JSTL
<strong> <c:out value»"${cookies.key}"/> </strong»: Object" <c:out value""${cookies.value}"/>, value» <c:out value»”${cookies.value.value}"/xbr /> </сsforEach» </body> </html> Тег c: f orEach для каждого cookie сохраняет запись из объекта типа Мар в перемен- ной с именем cookies. Для доступа к имени каждого cookie используется EL-фраза «${cookies.key}». Вы можете Подумать, что выражение «${cookies.value}» возвращает значение cookie, однако это не так, это выражение возвращает сам объект Cookie. Значение cookie возвращается при помощи загадочного синтаксиса «${cookies.value.value}». То, в каком виде страница JSP отображает эту информацию, показано на рис. 23.8. Рис. 23.8. Отображение объекта cookie и значения cookie с помощью JSTL См. также Главу 10 о чтении и установке cookie; сайт с библиотеками тегов проекта Jakarta Project http://jakarta.apache.org/taglibs/index.html’, информационная страница Sun Micro- system по JSTL: http://java.sun.com/products/jsp/jstl/; рецепт 23.3 по использованию тегов ядра; рецепты 23.4 и 23.5 по использованию XML-тегов; рецепт 23.6 по использованию тегов форматирования; рецепты 23.7 и 23.8 по использованию SQL-тегов JSTL; рецепты 23.9-23.12 по использованию EL для доступа к переменным различных облас- тей видимости, параметрам запроса и заголовкам запроса; рецепт 23.14 по использо- ванию EL для доступа к свойствам компонентов JavaBean. Доступ к cookie с помощью EL | 617
23.14 Использование EL для доступа к свойствам компонентов JavaBean Задача Вы хотите использовать EL для доступа к свойствам компонентов JavaBean на странице JSP. Решение Для создания или доступа к экземпляру компонента JavaBean используйте стандартное действие j sp: useBean, а затем используйте EL для доступа к свойствам этого экземпляра. Обсуждение Для отображения на странице JSP значений свойств компонента JavaBean вы можете использовать тег ядра JSTL c:out и EL. В примере 23.15 показан скелет компонента JavaBean, предназначенного для работы с электронной почтой. Я использовал этот ком- понент в главе 20, где подробно описаны его методы для отправки и получения почтового сообщения. Пример 23.15. Компонент JavaBean, экземпляр которого будет создаваться и использоваться на странице JSP package com.jspservletcookbook; inport java.io.lOException; import java.ib.Printwriter; import java.util.Properties; import j avax.mail.*; 4 import javax.mail.internet.*; import j avax.servlet.*; import j avax.servlet.http.*; public class EmailBean { //значения, принятые по умолчанию private final static String DEFAULT_SERVER = "smtp.comcast.net"; private final static String DEFAULT_TO = * "author©j spservletcookbook.com"; private final static String DEFAULT_FROM = "author©j spservletcookbook.com"; private final static String DEFAULT_CONTENT - "Unknown content"; 618 | Глава 23. Использование JSTL
private final static String DEFAULT_SUBJECT= "Unknown subject"; //Свойства JdvaBean private String smtpHost; private String to; private String from; private String content; private String subject; //конструктор компонента, не принимающий аргументов public EmailBean () {} //настраиваем почтовое сообщение с помощью //параметров запроса и отправляем его public void sendMessage(HttpServletRequest request, Printwriter out) throws lOException { //ДЕТАЛИ ЭТОГО МЕТОДА КОМПОНЕНТА ИЩИТЕ В РЕЦЕПТАХ 20.3 И 20.6 }//sendMessage //получаем почтовое сообщение, используя учетную запись РОР-сервера private void handleMessages(HttpServletRequest request. Printwriter out) throws lOException, ServletException { 11 ДЕТАЛИ ЭТОГО МЕТОДА КОМПОНЕНТА ИЩИТЕ В РЕЦЕПТАХ 20.3 И 20.6 }//handleMessages //отображаем информацию о полученных почтовых сообщениях private void displayMessage(Message msg, Printwriter out) throws MessagingException, IOException{ // ДЕТАЛИ ЭТОГО МЕТОДА КОМПОНЕНТА ИЩИТЕ В РЕЦЕПТАХ 20.3 И'20.6 }//displayMessage //методы для доступа к значениям свойств public String getSmtpHost() ( return (smtpHost == null || smtpHost.equals("")) ? EmailBean,DEFAULT_SERVER : smtpHost; }//getSmtpHost public String getTo(){ return to; }//getTo public String getFrom(){ • return from; }//getFrom Использование EL для доступа к свойствам компонентов JavaBean | 619
public String getContent'() { return content; }//getContent public String getSubject(){ return subject; }//getSubject //методы для установки значения свойств , public void setSmtpHoct(String host){ if (check(host)){ this.smtpHost = host; } else { this.smtpHost = EmailBean,DEFAULT_SERVER; } }Z/setSmtpHost public void setTo(String to){ if (check(to)){ this, to = to:; } else { this.to = EmailBean.DEFAULT_TO; } }//setTo public void setFrom( String from){ -if (check(from)) { this, from = from; } else { this.from = EmailBean.DEFAULT_FROM; } }//setFrom public void setcontent(String content){ i f (check(content)){ this.content = content; } else { this.content = EmailBean.DEFAULT_CONTENT; ’ } }//setcontent public void setSubject(String subject){ i f (check(subj ect)){ this.subject = subject; } else { 620 | Глава 23. Использование JSTL
this, subject = EmailBean. DEFAULT_SUBJECT; } }//setSubject private boolean check(String value){ if(value == null || value.equals("")) return false; return true; } } В примере 23.16 показана с граница JSP, которая создает экземпляр этого компойента JavaBean, используя для этого стандартное действие j$p: useBean. В атрибуте id дейст- вия jsp:useBean в качестве имени компонента JavaBean указано значение «emailer». Именно это имя код использует в EL-выражениях для доступа к значениям свойств экзем- пляра компонента. Пример 23.16. Создание экземпляра компонента JavaBean и использование JSTL для отображе- ния значений его свойств taglib uri»"http://java.sun.com/jstl/core" prefix="c" %> •^jsp:useBean id="emailer" class»"com.jspservletcookbook.EmailBean"/» <jsp:setProperty name» "emailer" property»"*" /> <html> <head><title>Bean property dispiay</titlex/head» <body> <h2>Here are the EmailBean properties</h2> <strong>SMTP host: </strong> <csout value»"${emailer.smtpHost}" /xbr /> <sttong>Email recipient: </strong> <ctout value»${emailer.to}" /xbr /> <strong>Etnail sender: </strong> <d:out value»"${emailer.from}" /xbr /> <strong»Email subject: </strong> <csout value»"${emailer.subject}" /Xbr’/» <strong>Email content: </strong> <c:out value»'${emailer.content}" /xbr /> </body> , </html> Когда в коде используется выражение типа «${emailer.smtpHost}», то вызывается метод getSmtpHost() компонента EmailBean (возвращающий SMTP-сервер, с которого вы получили почтовое сообщение, например «smtp.comcast.net»). Переменная emailer ссы- лается на созданный экземпляр компонента EmailBean. Использование EL для доступа к свойствам компонентов JavaBean | 621
В примере 23.16 значения всех доступных для установки свойств компонента EmailBean, задаются из значений параметров запроса, имеющих такие же имена. Эту задачу решает следующий код. <jsp:setProperty name= "emailer" property="*" /> Указывая это выражение в качестве значения атрибута value тега с .-out, мы отображаем значение соответствующего свойства экземпляра компонента JavaBean. На рис. 23.16 показан вид этой страницы JSP в браузере. Рис. 23.9. Отображение свойств компонента JavaBean с помощью JSTL-тега с: out См. также Главу 20 по использованию JavaBean для работы с электронной почтой; сайт с библио- теками тегов проекта Jakarta Project http://jakarta.apache.org/taglibs/index.html', информаци- онная страница Sun Microsystem по JSTL: http://java.sun.com/products/jsp/jstl/; рецепт 23.3 по использованию тегов ядра; рецепты 23.4 и 23.5 по использованию XML-тегов; рецепт 23.6 по использованию тегов форматирования; рецепты 23.7 и 23.8 по использо- ванию SQL-тегов JSTL; рецепты 23.9-23.13 по использованию EL для доступа кперемен- ным различных областей видимости, параметрам запроса, заголовкам запроса и к cookies. 622 | I лава 23. Использование JSTL
23.15 Использование функций JSTL Задача Вы хотите использовать встроенные функции, включенные в JSTL 1.1. Решение Используйте на странице JSP соответствующую директиву taglib (со значением uri равным «http://java.sun.com/jsp/jstl/functions») и требуемый префикс (например, fп: В fn: contains). Обсуждение JSTL 1.1 и связанный с этой версией EL включает прекрасную новую библиотеку функций (functions). Теги этой библиотеки позволяют разработчикам JSP вызывать встроенные функции, способные обрабатывать и возвращать значения объектов типа String, array, Мар, и Collection. Природа этих.функций будет понятна каждому, Кто работал с объектами j ava. lang. String (строками) и их многочисленными мето- дами (см. табл. 23.4). Функции отражают эволюцию JSTL ot простого включения наборов пользовательских тегов до предоставления вам возможности встраивать вызовы функций в шаблонный текст. Вот что потребуется, чтобы получить возможность использовать функции JSTL в ваших JSP. 1. Контейнер JSP по версии JSP 2.0. 2. Какая-либо реализация JSTL 1.1 (в этом рецепте я использую Java Web Services . Developer Pack 1.2). 3. Приведение вашего файла web.xml в соответствие требованиям API сервлетов версии 2.4 (см. далее в этом рецепте). В примере 23.17 показаны новые значения атрибутов uri и prefix тега taglib, которые необходимо использовать для работы с библиотекой функции. На этой странице JSP на вход четырех из. доступных функций (fn:length(), fn:contains(), fn: toUpperCase (), и fn:split ()) подается строка «I am a test Strings. Пример 23 17. Страница JSP, использующая функции JSTL 1.1 taglib uri="http://java.sun.com/jep/jstl/functions" prefix="fn" %> taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <html> <headxtitle>Using the JSTL functions</title></head> <body> <h2>Using various JSTL 1.1 functions</h2> Использование функций JSTL | 023
<c:set var="texnpStr" value»"I am a test String*/> < %— длина тестовой строки: —%> The length of the test String: $(fn:length(tempStr)}<br /> — содержит ли тестовая строка слово "test": —%> Does the test String contain "test"? ${fn:contains(tempStr,"test”)}<br /> < %— перевод строки в верхний регистр —%> Putting the String into upper case using fn:toUpperCase(): ${fn:toUpper- Case(teinpStr) }<br /> i < *— разбиваем строку на подстроки и заносим их в массив, возвращаем размер мас- сива: —%> Splitting the String into a String array using fn:split(), and returning the array length: ${fn;length!fn:split(tempStr," *'))} </body> </html> Функции JSTL 1.1 можно смешивать с шаблонным текстом, как это показано в примере 23.17. Просто обрамляйте вызовы функций ограничителями EL (${...}) и не забывайте использовать префикс fn:, например ${fn:toUpperCase(tempStr)}. В примере 23.18 показано, как изменить web.xml с учетом требований API сервлета версии 2.4, с тем чтобы контейнер JSP 2.0 мог интерпретировать EL-функции в вашем коде. Главное отличие между JSTL 1.0 и JSTL 1.1 в том, что теперь ответственность за EL возложена на, спецификацию JSP 2.0. Таким образом, теперь контейнер * JSP 2.0, а не библиотеки JSTL, занимается вычислением EL-синтаксиса. Если ваш дескриптор развертывания соответствует версии 2.3 API сервлета, то кон- тейнер JSP 2.0 не станет вычислять EL-выражения и вызовы функций. Использование старого (по версии 2.3) дескриптора развертывания «выключит» EL-вычисления кон- тейнером JSP; следовательно, вы не сможете использовать библиотеку функций или включать EL-синтаксис в шаблонный текст. Такое автоматическое игнорирование EL- выражений контейнером JSP предназначено для облегчения миграции существующих страниц JSP к JSP 2.0. Короче говоря, страница JSP, использующая JSTL .1.0 и связанная с дескриптором развертывания по версии 2.3 API сервлета, под управлением контейнера JSP 2.0 будет работать так же, как и раньше. Однако у вас может возникнуть желание использовать новые функции! Пример 23.18 показывает, как перейти к web.xml по версии 2.4 API сервлета. 624 | Глава 23. Использование JSTL
Пример 23.18. Приводим web.xml к версии 2;4 API сервлете, чтобы воспользоваться преимуществ вами JSTL 1.1 <?эап1 versions"1.0" encodings"ISO-8859-1"?> <web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns :xsi«"http: //www.w3 .org/2001/XMLSchema-instance" xsi:echemaLocation= "http: / / j ava. sun. com/?<ml/ns/j2«e http: //java. sun. com/xml/ns/ j2ee/web-app_2_4 .xsd" version»"2.4" <! — ОСТАЛЬНЫЕ ЭЛЕМЕНТЫ ДЕСКРИПТОРА РАЗВЕРТЫВАНИЯ —> </web-app> В примере 23.18 изменен элемент web-app, в него включены атрибуты, необходимые для дескриптора развертывания по версии 2.4 API сервлета (см. главу 1). Остальные элементы webjanl можно оставить так, как это было раньше, при использовании DTD версии 2.3 API сервлета. В табл. 23.4 описано назначение каждой из функций, которые JSTL, 1.1 включила в библиотеку функций. Табл. 23.4. XML-эквиваленты директив JSP Имя Функции Аргументы Тип результата Назначение fnxontains String, String boolean Ищет в строке (первый аргумент) опреде- ленную подстроку (второй аргумент) fnxontain- slgnoreCase String, String boolean Ищет в строке (первый аргумент) опреде- ленную подстроку (второй аргумент), сравнение осуществляется без учета регистра символов fn:endsWith String, String boolean , Определяет, заканчивается ли строка (первый аргумент) определенной подстрокой (второй аргумент) fn;- escapeXML String String Убирает символы, которые могут быть интерпретированы, как XML-разметка, например «>» fn:indexOf Siring,String int Возвращает индекс (позицию) подстроки (второй аргумент) внутри строки (первый аргумент) fn join Stringf], String String Объединяет все элементы массива строк в одну строку, используя для отделения в этой строке одного элемента от другого заданный разделитель (второй аргумент) Использование функций JSTL ] 625
Табл. 23.4. XML-эквиваленты директив JSP (Продолжение) Имя функции Аргументы Тип результата Назначение fn:length Map, array, Collection, Iterator, Enumeration, или String int Определяет длину массива, набора или строк fnrreplace String, String, String String В исходной строке (первый аргумент) заменяет все вхождения подстроки (второй аргумент) на другую подстроку (третий аргумент) fhrsplit String, String / Stringf] Расщепляет исходную строку на под- строки и заносит эти подстроки в массив. В качестве разделителя (разделителей) используется второй аргумент fn: startsWith String, String boolean Определяет, начинается ли строка (первый аргумент) с определенной подстроки (второй аргумент) fmsubstring String, int, int String Из* исходной строки (первый аргумент) выделяет подстроку, начинающуюся с символа С индексом, задаваемым вторым аргументом (включительно), и до символа с. Индексом, задаваемым третьим аргумен- том (не включая его) fn:substrin- gAfter String, String String Возвращает часть строки (первый аргумент) после заданной подстроки (второй аргумент) fn:sub- stringBefore String, String 1 String Возвращает часть строки (первый аргумент) перед заданной подстрокой (второй аргумент), начиная с первого сим- вола fn:toLower- Case String String Возвращает строку, в которой все сим- волы исходной строки переведены в ниж- ний регистр. fnztoUppcr- Case Stnng String Возвращает строку, в которой все сим- волы исходной строки переведены в ниж- ний регистр. fn:trim String String Удаляет пробельные символы из начала и конца исходной строки. 626 | Глава 23. Использование JSTL
На рис. 23.10 показано отображение в браузере страницы JSP, использующей разные члены библиотеки функций JSTL 1.1. 23.10. Страница JSP, отображающая результаты использования некоторых функций JSTL 1. См. также Информационная страница Sun Microsystem по JSTL: http://java.sun.com/products/jsp/ jstl/; рецепт 23.3 по использованию тегов ядра; рецепты 23.4 и 23.5 по использованию XML-тегов; рецепт 23.6 по использованию тегов форматирования; рецепты 23.7 и 23.8 по использованию SQL-тегов JSTL; рецепт 23.9 по использованию EL для доступа к переменным различных областей видимости; рецепт 23.10 по использованию EL с параметрами запроса; рецепты 23.11 и 23.12 ио использованию EL с заголовками запроса; рецепт 23.13 по поиску информации о cookies; рецепт 23 14 по использованию EL для дос- тупа к свойствам JavaBean. Использование функций JSTL | 627
ГЛАВА 24 Интернационализация 24.0 Введение Почти все web-сайты имеют глобальную аудиторию. На многих сайтах, по крайней мере, часть содержания необходимо адаптировать к языку и национальным особенно- стям посетителей, с тем, чтобы браузер посетителя правильно форматировал числа и даты, и транслировал текст на нужный язык. Очевидным примером здесь является web-сайт с документацией по продукту/сайт поддержки). Что, если многие из покупа- телей данного продукта (или просто интересующихся) говорят не на английском, а на < других языках? Java предоставляет web-разработчикам инструменты для интернацио- нализации web-сайтов. Перед тем как мы начнем обсуждение этих инструментов, проясним ряд терминов, которые обычно присутствуют* в разговорах о переводах web-сайтов. 1. Интернационализация, или сокращенно П8п, означает придание web-сайту или другим Java-программам возможности обеспечивать разные версии содержимого, в соответствии с языком или национальностью посетителя. Этот термин означает придание сайту глобального характера. 2. Локализация, или И On, означает добавление к web-сайту ресурсов, с тем чтобы адаптировать его к определенному географическому или культурному региону. Примером ПОп может послужить добавление к web-сайту переводов на корейский язык. Web-разработчикоВ, отвечающих за эту работу обычно называют локали- заторы (localizers). 3. Locale* - это один определенный культурный или географический регион. Как правило, мы будем обозначать локаль строкой, состоящей из обозначения языка и обозначения страны, разделенных символом подчеркивание: «en_US» для англий- ской локали, «de_.DE» для говорящих на немецком жителей Германии, «de_CII» для говорящих на немецком жителей Швейцарии, или «fr_CH» для жителей Швейцарии говорящих на французском. Локаль также может представлять только язык, например, «ja» для японского, или «it» для итальянского. И наконец, локаль может * Английское слово «locale» происходит от французского «local» - произносится «локбль». Носителями русского языка такое звучание воспринимается как более естественное, чем английское «лоукбл». Поэтому в дальнейшем вместо «locale» будет использоваться слово «локбль» - Примеч. науч ред. 628
иметь третий сегмент, или «вариант», отражающий определенный тип браузера или поставщика, например «МАС» для Macintosh. Пример локали для английского с Wndows — «en_US_ WIN». • Элемент, отражающий язык, задается кодом языка ISO (International Standards Organization - организация по международным стандартам) (http://www.ics.uci.edu/ ' f gfr pub/ietf/http/related/iso639.txt)\ страна кодируется согласно ISO-3166 (http://www. chemie.fii-berlin.de/diverse/doc/ISO_J166.html). Итак, как же выполнить интернационализацию или локализацию web-сайта на базе Java? Это необъятный предмет, который может стать темой нескольких книг. В следующих ниже рецептах приводятся основы создания файлов свойств (называемый ResourceBundle (наборы ресурсов)).. Эти файлы (они также могут быть реализованы в виде Java-классов) обеспечивают перевод на другой язык фраз, используемых на вашей web-странице. Затем сервлет может получать эти ресурсы и формировать различные версии текста, в соответствии от локали человека, обратившегося с запросом. В рецептах этой главы также показано, как с помощью тегов JSTL адаптировать страницы JSP к посетителям, говорящим на разных языках. Но начнем мы с того, как определить локаль запроса в сервлете или JSP. 24.1 Определение локали клиента в сервлете Задача Требуется в сервлете определить локаль клиента. Решение Используйте метод ServletRequest. getLocale (). Обсуждение Локаль представляется посредством класса java.util .Locale. Наиболее предпочтительный для данного пользователя вариант локали можно определить, вызвав метод getLocale () объекта ServletRequest (запроса) Этот метод возвращает объ- ект Locale. Определение локали клиента в сервлете | 629
* •’ Наиболее предпочтительным считается вариант локали, который более других подходит для данного пользователя. К примеру, пользователь в настройках своего 4 lift браузера может указать, что он Предпочитает локаль с испанским языком («es_ES»). Java-код имеет доступ к списку из нескольких локален, этот список пользователь настраивает в своем браузере; для доступа используется метод getLocales () объекта ServletRequest, который возвращает перечисление (объект Enumeration). Этот объ- ект содержит наиболее предпочтительный и менее предпочтительные варианты локали. Чтобы настроить список предпочитаемых языков в Netscape 7.1, перейдите J в ^re^erences Netscape -> Languages». В браузере Macintosh Safari откройте * tV; System Preferences и Перетащите предпочитаемые языки в верхнюю часть списка (затем перезапустите Safari); В Internet Explorer 5.5 перейдите в «Tools -»Internet Options -»Languages». В примере 24.1 сервлет получает наиболее предпочтительную для пользователя локаль, вызывая метод request. getLocale(). Затем сервлет отображает информацию о локали, вызывая некоторые из методов объекта Locale. Сервлет также выводит информацию о менее предпочтительных вариантах локали, используя метод request. getLocales (). Пример 24.1. Доступ из сервлета к объекту Locale package com.jspservletcookbook; import java.util.Enumeration; import java.util.Locale; inport javax.servlet.*; import javax.servlet.http.*; public class LocaleDisplay extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.lOException { //Получаем все локали клиента Enumeration enum request.getLocalesO; //Получаем наиболее предпочтительный вариант локали Locale preferred request.getLocale(); String prefDisplay if (preferred ! null) prefDisplay и preferred.gatDisplayName(); //Отображаем предпочтительный и остальные варианты локали response.setContentType("text/html"); 630 | Глава 24. Интернационализация /
java.io.Printwriter out = response.getWriter(); out.printin( "<htjnl><head><title>Loca.ie Display</titlex/headxbody>") ; out.printin("<h2>Here is your Locale info...</h2>"); out.printIn("<b>Preferred Locale:</b> "); out. print In ( pref Display ); . out.printin("<br />"); out.printin("Locale country: "); if (preferred != null) out.printIn( preferred.getDisplayCountry() ); out. print In (**<br />"); 1 out.printIn("Locale language: "); if (preferred != null) out. print In ( preferred. getDisplayLanguageO ); out.printlnf”<br / xbr />*); out.printIn("<h3>Lower priority Locales...</h3>"); Locale.loc null; while (enuia.hasMoreElemehts ()) { loc (Locale)enum.nextElement(); if (I (loc.getDisplayNamef).equals( prefDisplay ))){ out.printin("Locale: "); out.printlnf loc.getDisplayNameO ); out .printIn("<br />'")} out.println("Locale country: "); ou t. print In ( loc.getDisplayCountryO ); out.printlnf"<br />"); out.printIn("Locale language: "); > out.printlnf loc.getDisplayLanguageO ); out. print In ("<br /xbr />"); }//if }//while out. printin (" < /bodyx /html> ") ; } //doGet На рис. 24.1 показано отображение в браузере выходной информации этого сервлета в ответ на запрос посетителя с предпочитаемым вариантом локали «enJUS». Определение локалц клиента в сервлете | 631
1_дел4 Displdf Neucape L * P« здм fr "'«at- _ Jota «Дож HQi 1l=ID Ырс/Лом>^<0ЯИм1И/и^1/вот|1и^»*с<к*Ьоак1оедЬВйНу iZ'En^w». on .гЕ'.и.Ц -ВЙ loMhOw' twite.. j Jw»2hl » fhwgt n Vwwp>._ [«‘‘SoMrfiopZitUwoaVj» tnttdvl , - - Я Here is your Locale info... Preferred Local*: Sigfah (United Stnter) Locale county: United Statea Locale language: EngBh Lower priority Locolei... Locale: Engtyb (United Kingdom) . Locale county: United Kingdom Locate language Ец * » Locje. Japaneat I Locale county Locale lltyuage: Japaneae Locate: German (Germany) Locale county Germany Locale language: German Locate: Spamrh (Spain) Locale county Spain Locate language: Spanish Locale: French (France) Locate county France Locate language: French 1 Locale: Italian Locale county: Locate language: Italian '.£эадедв.|ра^"~7' Рис. 24.1. Сервлет, отображающий наиболее предпочтительный и менее предпочтительные варианты локали Этот пользователь настроил свой браузер на несколько локален. Как вы уже заметили, метод locale.getDisplayName () возвращает локаль в более удобочитаемой форме (по сравнению с «де_СЙ»). Библиотека com.oreilly.servlet включает в себя класс LocaleNegdtiator, который использует запрос клиента, чтобы определить, какие набор символов, локаль, и набор ресурсов использовать для ответа. Советы по использованию библиотеки com.oreilly.servlet см. в рецепте 8.4. Я, в частности, не обсуждаю здесь класс LocaleNegotiator, но о нем можно прочитать в документации. См. http://www. servlets.com/cos/javadoc/com/oreilly/servlet/LocaleNegotiaior.html. 632 | Глава 24. Интернационализация
См. также Документацию, описывающую класс Locale; http://java.sun.eom/j2se/l.4.l/docs/api/ java/util/Locale.html; рецепт 24.2 по определению локали в JSP. 24.2 Определение всех локалей клиента bJSP Задача Вы хотите определить наиболее предпочтительный вариант локали запроса, а также менее предпочтительные варианты и отобразить эту информацию на странице JSP. Решение Извлеките наиболее предпочтительный вариант локали с помощью выражения «${pageContext.request.locale}». Получите доступ ко всем локалям с помощью выраже- ния «${pageContext.request.locales}». Обсуждение JSTL-теги облегчают адаптацию JSP к посетителям, говорящим на разных языках. В примере 24.2 с помощью EL создается переменная с именем clientLocale, представляющая предпочитаемый вариант локали запроса. Затем страница JSP отображает имя, язык и страну этогой локали. В примере 24.2 также отображается информация о менее подходящих для клиента вариантах локали. Пример 24.2. Доступ к локали запроса из JSP taglib uri="http://java.sun.com/jsp/jstl/core" prefix»"c" %> <html> <headxtitle>Locale Display</titlex/head> <body> <h2>Here is your preferred locale info...</h2> <c:set var="clientLocale" valuer"${pageContext.request.locale}" /> <c:set vars"clientLocales" value="${pageContext.request.locales}" /> Preferred locale: ${ clientLocale. displayName} <br /> Preferred locale country: ${clientLocale.displaycountry} <br /> Preferred locale language: ${clientLocale.displayLanguage} Определение всех локалей клиента в JSP I 633
<h3>Lower priority locales 1..</h3> <c:forEach var»"loc" items""${clientLocales}" begin""1"> Locale: ${loc.displayName} <br /> Locale country: $ {loc.displaycountry} <br /> Locale language: ${loc.displayLanguage} <br /xbr /> </с:forEach» </body> </html> Выражение «${pageContext.request.locale}>> от неявно создаваемого объекта pageCon- text получает объект request, а от него - объект Locale, представляющий предпочтительный вариант локали данного запроса. Красиво? Затем следуют другие выраже- ния, например <<${clientLocaIe.displayName}>>, что эквивалентно вызову метода getDis- playNamef) объект^ Locale. EL-фраза «${pageContext.request.locales}>> представляет собой эквивалент вызова метода getLocales () объекта ServletRequest Этот метод возвращает тип j ava. util.Enumeration (перечисление). Это перечисление содержит все локали, настроен^ ные клиентом, начиная с наиболее предпочтительного варианта. Пример 24.2 использует реализацию JSTL 1.1 и JSP 2.0. Не забудьте включить в начало файла JSP соответствующую директиву taglib, чтобы страница могла использовать EL и теги ядра. Если вся эта терминология для вас непривычна и непонятна, прочитайте главу 23, посвященную JSTL. Пример 24.2 перебирает в цикле все локали используя тег с: forEach. <c:forEach var="loc" items="${clientLocales}" begin="l"> Используя атрибут begin=«l» мы начинаем цикл с:forEach со второго объекта locale, поскольку информацию о первом объект locale - наиболее предпочитаемом пользо- вателем варианте, мы уже отобразили. Чтобы получить первый элемент перечисления, атрибуту begin необходимо присвоить значение «0». Вид данной страницы JSP в браузере приведен на рис. 24.2. Отображенная там информация относится к посетителю, чей браузер указал, что наиболее Предпочтительной локалью является «es_ES». См. также Главу 23 по JSTL; документацию, описывающую класс Locale: http://java.sun.com/ j2se/1.4.1/docs/api/java/util/Locale.html. 634 | Глава 24. Интернационализация
Рис. 24.2. Браузер запросил страницу JSP, реагирующую на локаль посетителя 24.3 Создание ResourceBundle в виде файла свойств Задача Мы хотим сохранить в web-приложении ресурсы, необходимые для i 18п. Решение Создайте текстовый файл, состоящий из пар имя/значение, представляющих ваши ресурсы il8n. Назовите этот файл именем вашего глобального ресурса и сохраните в каталоге WEB-INF. х Создание ResourceBundle в виде файла свойств j 635
Обсуждение i Добавление ресурсов il8n к web-приложению включает или создание файла свойств, или создание классов ResourceBundle (рецепт 244). Набор ресурсов, выполненный в виде файла свойств, представляет собой просто список ключей и значений, который можно создать в любом текстовом редакторе. В нем ключи - слова, которые необходимо перевести, а значения - переводы. Эти файлы и являются теми ресурсами, которые web- приложения используют для динамического перевода текста на необходимый язык. Допустим, вы создали некоторые ресурсы с глобальным (базовым) именем «Welcome- Bundle». В примере 24.3 показан подкласс этих ресурсов, предназначенный для посетителей имеющих локаль «es_ES», то есть для людей из Испании, говорящих на испанском языке. К примеру, ключ «Welcome» ассоциирован с его испанским эквивалентом «Но1а у recepciyn». В рецепте 24.5 показано, как сервлет использует подобный ResourceBun- dle (набор ресурсов) для динамической трансляции слова «Welcome» на язык посетителя. Пример 24.3. Содержимое файла с набором ресурсов, имеющего имя WelcomeBundle_es_ES.prop- erties ♦ресурсы для испанского языка Welcome = Hola у recepciyn Здесь только ключи и значения, разделенные символом перехода на новую строку, комментарии начинаются с символа решетка (#). Этот файл необходимо сохранить в том месте, где остальные web-компоненты смо- гут его найти, подобно тому, как в web-приложение устанавливаются Java-классы. Ниже приведен Путь к данному файлу свойств, который имеет полностью квалифицированное имя il8n.WelcomeBundle_esJES.properties. Обязательной деталью является расширение имени файла .properties. WEB-INF/il8n/WelcomeBundle_es_ES.properties Размещение ресурсов i!8n в отдельном подкаталоге каталога WEB-INF - разумный способ организации этой информации и борьбы с беспорядком. См. также Рецепт 24.4 по созданию ResourceBundle (набора ресурсов) в виде Java-класса; документацию по PropertyResourceBundle: http://java.sun.eom/j2se/l:4.l/docs/api/ java/util/PropertyResourceBundle.html. 636 | Глава 24. Интернационализация
24.4 Создание ResourceBundle в виде Java-класса Задача Вы хотите создать ResourceBundle (набор ресурсов) в виде Java-класса. Решение Создайте класс, расширяющий класс java.util.ListResourceBundle. Обсуждение Если вашему приложению требуется большая функциональность, чем та, что может предоставить статичный файл свойств (Рецепт 24.3), вы можете создать набор ресурсов в виде Java-классов: типов java.util.ListResourceBundle. К примеру, может оказаться, что для некоторого ресурса необходимо извлекать соответствующую ему переведенную информацию из базы данных. Пример 24.4 включает ту же информацию, что и файл свойств из предшествующего рецепта. Однако в данном случае пары ключ/значение хранятся в форме двумерного массива объектов Object. Этот класс хранится в том же месте, что и файл .properties - в каталоге WEB-INF/il8n. Пример 24.4. Хранение перевода в ListResourceBundle package com.j spservletcookbook; import java.util.ListResourceBundle; public class WelcomeBundle_es_ES extends ListResourceBundle { static final Object[][] contents = { {"Welcome"* "Hola у recepciyn") }; public Object[][] getContepts() { return contents; } } Приведенный ниже фрагмент сервлета показывает, как этот класс можно использовать. Создание ResourceBundle в виде Java-класса | 637
Пример24.5. Вызов метода ListResourceBundle из ResourceBundle (набора ресурсов), созданного в виде Java-класса \<!— например, в методах goGetO или doPost () сервлета —> ResourceBundle bundle = ResourceBundle.getBundle( "il8n.WelcomeBundle_es_ES"); //Вызываем наследуемый метод getKeysO класса ListResourceBundle java.util.Enumeration enum = bundle.getKeys(); while (enum.hasMoreElements()) { //Печатаем ключ: "Welcome" out.println((String) enum.nextElement()); out.println("<br /xbr />"); }//while Статический метод ResourceBundle. getBundle () сначала пытается найти Java-класс с полностью квалифицированным именем «i!8n.WelcomeBundle_es_ES» (в данном примере). Если это не удается, он ищет файл свойств с этим же именем (не считая расширение .properties): H8n.Wel‘jorneBundle_es_ES.properties. См. также Документацию по ListResourceBundle: http://javasun.eom/j2se/l.4.l/ddcs/api/java/ util/ListResourceBundle.html\ рецепт 24.3 по созданию ResourceBundle в виде файла свойств. 24.5 Использование ResourceBundle в сервлете Задача Вы хотите, чтобы сервлет динамически отображал приветствие «Welcome» на языке посетителя (в зависимости от локали). л Решение Используйте сервлет для динамического доступа к переведенному тексту из ResourceBundle (набора ресурсов). Обсуждение После того как вы добавили наборы ресурсов к web-приложению, сервлеты могут' использовать их для динамического отображения текста, в зависимости от локали поль- зователя. 638 | Глава 24. Интернационализация
Помните, что web-приложения хранят наборы ресурсов (ResourceBundle) в виде файлов .properties (текстовых) или в виде Java-классов. В примере 24.6 используется набор ресурсов (ResourceBundle) с базовым именем «WelcomeBundle». Он хранится в каталоге WEB-INF/il8n, таким образом, его полно- стью квалифицированное имя П8п. WelcomeBundle. Пример 24.6. Сервлет, использующий ResourceBundle для динамического отображения переведенного текста package com.j spservletcookbook; import java.util.Locale; import j ava.util.ResourceBundle; inport javax.servlet.*; import j avax.servlet.http.*; public class WelcomeServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.10.lOException { //Получаем’локаль клиента Locale locale = request.getLocale()} ResourceBundle bundle ResourceBundle.getBundle( "il8n.WelcomeBundle",locale); String welcome * bundle.getString("Welcome"); //Отображаем локаль response.setContentType("text/html"); j ava.io.PrintWri ter out = response.getWriter(); out .println( "<html><headxtitle>"+welcome+"</title></head><body>") ; out.printIn("<h2>"+welcome+"</h2>"); out.printin("Locale: ") ; out.printin( locale.getLanguage()++locale.getCpuntry() ); out. print In (" < /bodyx /html > !*); } //end doGet // метод doPost... }//WelcomeServlet Использование ResourceBundle в сервлете | 639
Вот как приложение использует этот ресурс для ответа посетителю с локалью «es_ES» (Испания). 1. Сервлет получает доступ к локали как к объекту java. util. Locale. 2. Он передает локаль в метод ResourceBundle. getBundle (), этот метод исполь- зует локаль для поиска набора ресурсов с именем ilSn. WelcomeBundle_es_ES. Метод формирует данный терм поиска, добавляя имя локали текущего запроса вконец базового имени требуемого набора ресурсов. В данном случае набор хранится в виде Java-класса (рецепт 24.4). 3. Затем набор выдает искомое сообщение, обращаясь к ключу «Welcome», который находится в наборе ресурсов. Иногда браузер посылает информацию о локали в виде одного только кодь языка, например «es» - для Испании (вместо «es_ES», где и код языка и код страны). Если в приложении установлен лишь ресурс с именем WelcomeBundle_es_ES, но нет ресурса с именем WelcomeBundle_es, то метод getBundle О по умолчанию обратится к ресурсу с именем WelcomeBundle (что может быть не вполне оптимальным решением) и, таким образом, не сможет отобразить переведенный текст. Для учета этих случаев не забывайте включать в приложение ресурс Welcome Bundle_es. На рис. 24.3 показана выходная информация сервлета, выдаваемая в ответ на запрос из Испании. recepcion Netscape -ULUJE3 I Holay recepcion Locale: es_ES I ' Puc. 24.3. Клиент-испанец обратился с запросом к сервлету LocaleServlet На рис. 24.4 показан ответ сервлета на запрос с локалью, для которой в приложении не предусмотрен соответствующий ресурс. В данном случае браузер настроен на японский язык, но для этой локали в приложении еще нет ресурса. 640 | Глава 24. Интернационализация
Hello and Welcome Locale: ja_ Puc. 24.4. Браузер, настроенный на японский язык получает сообщение, принятое по умолчанию Текст, который отображает браузер, берется из,файла свойств, принятого по умолчанию: WelcomeBundle.properties (заметьте, в имени этого файла нет какого-либо суффикса, ука- зывающего на конкретную локаль). См. также Рецепт 24.6 по использованию ResourceBundle; документацию по Resource- Bundle: http://java.sun.eom/j2se/l.4.l/docs/api/java/util/ResourceBundle.html. 24.6 Использование ResourceBundle в JSP Задача Вы хотите динамически отображать текст на странице JSP, в зависимости от локали запроса. Решение Используйте теги JSTL из библиотеки форматирования. 21-1545 Использование ResourceBundle в JSP | 641
Обсуждение Теги форматирования JSTL облегчают динамическое отображение текста в зависимо- сти от языковых настроек браузера. В примере 24.7 использованы директивы taglib, позволяющие использовать на этой странице JSTL-теги ядра и форматирования. Далее на странице используется тег fmt: setBtindle, задающий И8п-ресурсы, которые будут использоваться страницей (контекст локализации). Пример 24.7. Использование тегов форматирования для отображения на странице JSP сообще- ния, чувствительного к локали пользователя <%& taglib uri="http://java.sun.com/jstl/core" prefix="c" %> taglib uri="http://java.sun.com/jstl/fmt" prefix="fmt" %> <fmt:setBundle basename=nil8n.WelcomeBundle" /> <html> <head><title> <fmt:message key» "Welcome" /></titlex/head> <body> <h2xfmt:message key= "Welcome" /></h2> Locale: <c:out value" "${pageContext.request.locale.language} " />_<c:out value» "${pageContext.request.locale.country}" /> </body> </html> Так же, как и в коде сервлета из предыдущего рецепта, тег динамически использует ресурс WelcomeBundl.e в зависимости от локали запроса. Иными словами, если локаль браузера - «es_ES» (Испания), то теги fmt:message используют ключи и значения из файла свойств (или Java-класса) WelcomeBundle_es_JBS (если таковой реализован). , ’*•’ Если задать контекст локализации с помощью элемента context-param ' в дескрипторе развертывания, то в JSP не нужно будет использовать тег fmt: f setBundle. См. рецепт 24.13. В JSP код <fmt:message key="Welcome" /> заменяется значением ключа. «Welcome» из выбранного файла с Набором ресурсов («Но1а у recepciyn»). Результат запроса этой страницы JSP выглядит примерно так, как показано на рис. 24.3 в рецепте 24.5. См. также Рецепт 24.5 по использованию ResourceBundle в сервлете; главу 23 по JSTL; документацию по ResourceBundle: http://java.sun.eom/j2se/l.4.l/docs/api/java/util/ ResourceBundle. html. (_________2______________ 642 | Глава 24. Интернационализация /
24.7 Форматирование дат в сервлете Задача В сервлете вывести дату в формате, соответствующем локали запроса. Решение Воспользуйтесь классом j ava. text. DateFormat. Обсуждение В разных странах приняты разные способы записи даты и времени. Класс DateFormat, подобно многим другим классам пакета java.text, «чувствителен к локали». Код отображает дату в том или ином виде, в зависимости от языковых настроек браузера. Все, чТо вам необходимо сделать, - передать объект класса Locale статическому методу Date- Format . ge t Dei t eTime Instance (), как в примере 24.8. Пример 24.8. Отображение сроки с датой чувствительным к локали способом package com.j spservletcookbook; * import java.text.DateFormat; import java.util.Date; import java.util.Locale; import java.util.ResourceBundle; ' import j avax.servlet.*; import javax1 servlet.ht tp.*; public class DateLocaleServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws''ServletException, java.io.lOException { //Получаем локаль клиента Locale locale request.getLocale(); ResourceBundle bundle = ResourceBundle.getBundle( "il8n.WelcomeBundle",locale) ; String welcome = bundle, get String ("Welcome"); String date » DateFormat .get Di. t eTime Instance (DateFormat .FULL, DateFormat-. SHORT, locale). format (new Date ()) r //Отображаем локаль response.setContentType(”text/html"); j ava. io.PriritWri ter out = response.getWri ter();• Форматирование дат в сервлете | 643
out .printin( "<html><head><title>H+welcome+,,</title></head><body>") ; out.println("<h2>"+bundle.getString("Hello") + " " + bundle.getString("and") +""+ welcome+"</h2>"); out .print In (date + "<br /xbr />"); out.printIn("Locale: "); out.printin( locale.getLanguage()++locale.getCountry() ); out .printIn ("</bodyx/html>") ; } //doGet //реализация doPost и вызов doGet(request, response); } Метод DateFormat. getDateTimelnstance () включает также параметры в форме констант (например, DateFormat. FULL), которые позволяют настраивать формат даты. Сервлет из примера 24.8 отображает дату в виде, включающем название дня недели, и использует сокращенную форму записи времени. Вы можете поэкспериментировать с этими константами, чтобы познакомится с тем, как все это отображается в браузере. На рис. 24.5 показано, как даТа отображается в ответ на запрос с локалью «de_DE» (немец- кий язык). Рис. 24.5. Отображение сервлетом даты в соответствии с локалью запроса 644 | Глава 24. Интернационализация
См. также Документацию по классу DateFormat: http://java.sun.eom/j2se/l.4.l/docs/api/java/ text/DateFormat.html\ рецепт 24.8 по форматированию Дат в JSP. 24.8 Форматирование дат в JSP Задача Требуется на странице JSP отобразить дату, настроенную ца локаль пользователя. Решение Используйте JSTL-тег fmt: formatDate. Обсуждение JSTL включает библиотеку «форматирования», позволяющую коду JSP по-разному отображать даты, в зависимости от локали пользователя. В примере 24.9 используется тег fmt: formatDate. Для создания объекта java. util. Date, представляющего текущую дату и время, код использует стандартное действие j sp: useBean. Код передает этот объ- ект в атрибут value тега fmt: formatDate. Когда пользователь запрашивает JSP, тег fmt: formatDate заменяется на текст, отображающий отформатированную дату. Пример 24.9. Форматирование даты с помощью тега fmt: formatDate <%® taglib uri«"http://java.sun.сот/jstl/core" prefix»*c" <%9 taglib uri»"http://java.sun.com/jstl/fmt" prefix»"fmt" %> <jsp:useBean id»"date" class»"java.util.Date" /> <html> <head><title> <fmt:message key» "Welcome" /> </title></head> <body> <h2> <fmt:message key="Hello" /> <fmt:message key="and" /> <fmt:message key="Welcome" /> </h2> <fmt:formatDate value'"${date}" type»"both" dateStyle» "full" timeStyle»"short" /> <br /> Locale: <c:out value» "${pageContext.request.locale.language} />_<c:out value» "${pageContext.request.locale.country}" /> </body> </html> Форматирование дат в JSf | 645
Здесь теги fmt:message зависят от параметра конфигурации (элемента context-paj/am) из дескриптора развертывания. Этот элемент context-param задает ресурсы, связанные с i!8n. См рецепт 24.13. Обсуждаемый элемент имеет атрибуты datestyle и timeStyle, позволяющие коду настраивать формат строк с датой и временем. Детальное описание класса DateFormat см.: http://java.sun.eom/j2se/l.4.l/docs/api/ java/text/DateFormathtml. 1 Выходная информация данной страницы JSP выглядит в браузере примерно так, как изображено на рис. 24.5 из предыдущего рецепта. См. также Документацию по классу DateFormat: http://java.sun.eom/j2se/l.4.l/docs/api/java/ text/DateFormat.html\ главу 23 по JSTL; рецепт 24.7 по форматированию дат в сервлете. 24.9 Форматирование денежных сумм в сервлете Задача Требуется отобразить денежную сумму в виде, соответствующем локали запроса. Решение Воспользуйтесь классом j ava. text. NumberFormat. Обсуждение Класс java. text .NumberFormat может форматировать числа, умеющие тип long или double, в виде процентов. Этот класс имеет статический метод getCurrencyIn- stance (). Этот метод может принимать в качестве параметра объект класса j ava. util. Locale, чтобы отображать валюту в виде, соответствующем коду страны и языка текущего пользователя. В примере 24.10 приведен сервлет, демонстрирующий отображение денежных сумм, зависящее от локали, этот сервлет выводит денежную сумму, а Также код страны и языка, в соответствии с текущей локалью. 646 | Глава 24. Интернационализация
Пример 24.LQ. Форматирование денежных сумм в сервлете ' package com.j spservletcookbook; inport java.text.NumberFormat; import j ava.util.Locale; import j ava.util.ResourceBundle; import javax.servlet.*; import javax.servlet.http.*; public class CurrLocaleServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws SfervletException, java.io.lOException ( //Получаем локаль клиента Locale locale *= request .getLocale () > ResourceBundle bundle = ResourceBundle.getBundle( "il8n.WelcomeBundle",locale); String welcome = bundle.getString("Welcome"); NwnberFormat nit • NumberFormat.getCurrencylnstance(locale); String iormattedCurr в nit.format(1000000); //Отображаем локаль response.setContentType("text/html");‘ java.io.PrintWriter out = response.getWriter(); out.printin ("<html><headxti tle>" +welcome+"</titlex/headxbody>") ; out.printin("<h2>"+bundle.getString("Hello") + " " + bundle.getString("and") + " " + welcome+"</h2>"); out.printIn("Locale:- "-); out.printin( locale.getLanguage()+"_"+locale.getCountry() ); out. print In (" <br I xbr / >") ; out.printIn(formattedCurr); out. print In (" < /bodyx /html>") ; } //doGet //реализуем doPost() с вызовом doGet()... } ’ Форматирование денежных сумм в сервлете | 647
Метод format () класса NumberFormat возвращает строку (String), представ- ляющую отформатированную денежную сумму. На рис. 24.6 показано, что выводит этот сервлет, когда получает запрос от браузера, пользователь которого настроил его на «еп_ GB» (английский язык, Великобритания). & Welcome N etscape 'Ll Fie Edit View Go Bookmarks Tools Window Heb Hello and Welcome Locale: en_GB £1,000,000.00 Puc. 24.6. Посетитель из Великобритании видит ртображение суммы в один миллион фунтов См. также Документацию по классу NumberFormat: http://java.sun.eom/j2se/l.4.l/docs/api/java/ text/Number Format.html', рецепт 24.10 по форматированию валют в JSP. 24.10 Форматирование денежных сумм в JSP Задача Требуется отформатировать денежные суммы на странице JSP. Решение Используйте JSTL-тег fmt: formatNumber. 648 | Глава 24. Интернационализация
Обсуждение Тег fmt: formatNumber предназначен для отображения денежных сумм в зависи- мости от локали посетителя. В примере 24.11, в самом начале, располагается директива taglib, открывающая доступ к библиотеке форматирования JSTL 1.0. Пример 24.11. Форматирование чисел при помощи тегов JSTL 1.0 <%& taglib uri«"https//java.sun.com/jstl/core" prefix-"с" %> <%— библиотека форматирования включает в себя тег fmt: format Numb* г --%> <%0 taglib uri-"http://java.sun.com/jstl/fmt" prefix-"fmt" %> x <html> <headxtitle> <fmt:message key= "Welcome* /x/titlex/head> <body> <h2> <fmt:message key="Hello" /> <fmt:message key="and" /> <frnt:message key="Welcome" /x/h2> Locale: <c:out value="$(pageContext.request.locale.language}" />_<c:out value="${pageContext.request.locale.country}" /> <br /xbr /> <fmt:formatNumber value«"1000000" type“"currency" /> </body> </html> j Ter fmt:formatNumber довольно прозрачен. Атрибут value принимает число, которое вы хотите отформатировать в виде денежной суммы, а атрибут type необходимо установить в «currency». Когда браузер станет отображать страницу JSP, данный тег будет заменен на текст, представляющий отформатированное число. Страница JSP из примера 24.11 отображает в браузере ту же информацию, что приведена на рис. 24.6 предыдущего рецепта. См. также Документацию по классу NumberFormat: http://java.sun.eom/j2se/l.4.l/docs/api/java/ text/NumberFormat.html\ рецепт 24.9 по форматированию валют в сервлете. Форматирование денежных сумм в J$P | 649
24.11 Форматирование процентов в сервлете Задача Вы хотите отформатировать числа в виде процентов и отобразить их в сервлете. Решение Используйте класс java.txt.NumberFormat и его статический метод getPer- centlnstance(). Обсуждение В примере 24.12, чтобы получить тип NumberFormat для отображения числа в виде процентов, используется метод NumberFormat.getPercentInstance(), которому в качестве аргумента передается локаль пользователя. Затем вызывается метод format () класса NumberFormat с форматируемым числом в качестве аргумента. Метод format () отображает число 51 в виде «5100%»; чтобы получить более приемлемый результат (например, 51%), необходимо передать число с десятичной точкой, имеющее тип double, в данном случае 0.51. Пример 24.12.. Использование NumberFormat для отображения процентов в сервлете package com.j spservletcookbook; import j ava. text. NumberFormat; import java.util.Locale; import java « util.ResdurceBundle; import j avax.servlet.*; import javax.servlet.http.*; public class PerLocaleServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.lOException { //Получаем локаль клиента Locale locale = request.getLocale(); ResourceBundle bundle = ResourceBundle.getBundle( "il8n.WelcomeBundle",locale); String welcome = • bundle. get Str ing ("Welcome") ; NumberFormat nft = NumberFormat.getPercentInstance(locale); String formatted « nft.format(0.51); 650 | Глава 24, Интернационализация
//Отображаем локаль response.setContentType("text/html"); java.io.Printwriter out = response.getWriter(); out .printin ("<htmlxheadxtitle>"+welcome+"</titlex/headxbody>") ; out.printlnf"<h2>"+bundle.getString("Hello") +"" + bundle.getString("and") +""+ welcome+"</h2>") ; out.printIn("Locale: "); Out.printIn( locale.getLanguage()++locale.getCountry() ); out.printlnf"<br /xbr />"); out .printIn ("NumberFormat.getPercentInstance (): "^-formatted) ; out.printin("</body></html>"); } //doGet //implement doPost() to call doGet()... } Вид выходной информации сервлета в браузере приведен на рис. 24.7. Рис. 24.7. Браузер отображает проценты, в соответствии с определенной локалью См. также Документацию по классу NumberFormat: http://java.sun.eom/j2se/l.4.1/docs/api/java/ text/NumberFormat.html-, рецепт 24.12 по форматированию процентов в JSP. Форматирование процентов в сервлете | 651
24.12 Форматирование процентов в JSP Задача Вы хотите на странице JSP отобразить число в виде процента. Решение . Используйте тег fmt: f ormatNumber. f Обсуждение JSTL-тег fmt: formatNumber может отображать в виде процента число, заданное в его атрибуте value. Для этого значение атрибута type должно быть равно «percent» (а не «percentage»). В примере 24.13 атрибуту value пёредается строка «.51». А код отображает в браузере текст «51%». Пример 24.13. Использование в JSP тега fmt: formatNumber для отображения процентов <%@ taglib uri="http://java.sun.com/jstlZcore" prefix="c" %> taglib uri»"http://java.sun.cotn/jstl/fmt" prefix»"fmt" %> <html>- cheadxtitlexfmt .'message key= "Welcome" /x/titlex/head> <body> <h2xfmt:message key=*Hello" /> <fmt:message key="and" /> <fmt:message key="Welcome" /x/h2> Locale: <c:out value="${pageContext.request.locale.language}" />_<c:out value="${pageContext.request.locale.country}" /> <br /xbr /> <fmt:formatNumber value»".51" type»"percent" /> </body> </html> Отображение этой страницы JSP приведено на рис. 24.8. См. также Документацию по классу NumberFormat: http.7/java.sun.coin/j2se/1.4.1/docs/api/java/text/ NumberFormat.html-, главу 23 по JSTL; рецепт 24.11 по форматированию процентов в JSP. 652 | Глава 24. Интернационализация
^Welcome - Netscape Q fie Edit Jfiew Go Bookmarks Took ^findow НФ Bsaj| ' Д | http:/Ax:a>K>st 8080/home/localePer.isp Ci| Search j 'J», td, El Mail Д AIM C Home (j Racfc W Netscape ^Search Bookmarks Amazon.com-.. ($B{Weft Q. :ome Hello and Welcome I J Locale: en_GB | 51% Puc. 24.8. Страница JSP отображающая число, отформатированное в виде процента 24.13 Установка контекста локализации в дескрипторе развертывания Задача Вы хотите настроить контекст локализации для JSTL-тегов, используемых в прило- жении. Решение Используйте элемент context-param в дескрипторе развертывания приложения. Обсуждение Контекст локализации - это совокупность ресурсов, таких как ResourceBundle (наборы ресурсов), используемых в web-нриложении для обеспечения зависящей от локали информации для страниц JSP. Если на .странице JSP требуется отображать тот или иной вариант перевода текста, можно использовать JSTL-теги (такие, как fmt: fmtMessage), определяющие локаль пользователя, а затем Ищущие в пределах контекста локализации требуемый вариант перевода для его отображения на странице. Контекст локализации устанавливается с помощью элемента context-param (параметр контекста) в web.xml. Этот параметр должен иметь имя javax.servlet. jsp.jstl.fmt.localizationcontext. Значением этого параметра должно быть полностью квалифицированное базовое имя набора ресурсов (ResourceBundle), который установлен вами в web-приложении. В примере 24.14 показан элемент соп- Установка контекста локализации в дескрипторе развертывания | 653
text-param, который указывает на набор ресурсов (ResourceBundle), который мы использовали на протяжении всей этой главы. • Добавление элемента этого типа в дескриптор развертывания является альтернати- вой использованию внутри страницы JSP JSTL-тега fmt: setBundle, задающего набор ресурсов (ResourceBundle). Пример 24.14. Установка контекста локализации для JSTL-тегов <!— Начало web.xml —> <context-param> <param-name> j avax.servlet.j sp.jstl.fmt.localizationcontext </param-name> 1 <param-value>il8n.WelcomeBundle</param-value> <I context-param> <!— Продолжение web.xml *-> См. также । Главу 23 no JSTL; рецепт 24.2 по определению в JSP "локали клиента; рецепт 24.6 по использованию в JSP набора ресурсов (ResourceBundle); рецепт 24.8 по форма- тированию дат; рецепт 24.10 по форматированию валют; рецепт 24.12 по форматированию процентов. 654 | Глава 24. Интернационализация
ГЛАВА 25 Использование JNDI и компонентов Enterprise JavaBean 25.0 Введение JNDI (Java Naming and Directory Interface - интерфейс наименований и каталогов) - это программный интерфейс (API), который Java-разработчики используют для дос- тупа к службе именования и службе каталогов. Эти службы (сервисы) используются Java-программами для хранения или привязки (bipd) объектов с тем, чтобы эти объ- екты можно были впоследствии использовать, а также для поиска ссылки на требуе- мый объект. Целью создания JNDI являлось отделение задач, связанных с обслуживанием репозитория объектов общего пользования, от большого количества Java-классов, использующих эти объекты, в том числе от сервлетов и JSP. Примерами JNDI-служб являются реестр RMI (Remote Method Invocation registry - реестр вызова методов удаленных объектов) и протокол LDAP (Lightweight Directory Access Protocol - облегченный протокол доступа к каталогам). JNDI API, представлен- ный пакетом javax.naming, обеспечивает общую реализацию доступа к объектам, которые привязаны к этим службам. Пакет j avax. naming является частью наборов инструментальных средств разработчика ПО (SDK) Java 2 1.3 и 1.4. Каждая из этих технологий имеет свою схему именования, позволяющую находить требуемые «JNDI-объекты». Структура таких схем обычно.является иерархической; вы начинаете поиск с вершины JNDI-дерева, а затем движетесь по соответствующим вет- вям и наконец находите то, что искали. Используя JNDI, Java-программы начинают с «начального контекста», в Unix его аналогом является прямой слеш С/), с которого начинается описание размещения любого файла. Символ / представляет корневой ката- лог носителя информации (жесткого диска); а чтобы найти каталог Users, хранящийся на верхнем уровне этого диска, необходимо набрать /Users. В Tomcat, во встроенной в йего реализации JNDI, начальный контекст - это адрес java:comp/env, любой поиск начинается отсюда. В главе 21 описано, .как, используя реализацию JNDI, получить доступ к источнику данных (j avax. sql. Data- source), начав с начального контекста java:comp/env й затем разыскивая источник данных по адресу jdbc/MyDataSource. Все источники данных (Datasource) 655
хранятся под jdbc, и именно так Java-код получает доступ к источнику данных с именем «MyDataSource». Используя аналогию с файловой системой, корневым каталогом JNDI- структуры сервера Tomcat является «java:comp/env», а конкретный источник данных хранится в его подкаталоге jdbc. Вы можете использовать JNDI для доступа к любому Java-объекту, а не только к источнику данных. В данной главе описывается, как сохранить компонент JavaBean, используя реализацию JNDI сервера Tomcat, и как впоследствии найти этот компонент из сервлета или JSP. В этой главе также описывается, как настроить почтовый сеанс (спомощью объекта javax.mail.Session), используя реализацию JNDI сервера BEA WebLogic, и как затем получить доступ к этому почтовому сеансу, предоставляя сервлету или JSP возможность отправлять почтовое сообщение. Используя JNDI, сервлет также может получиТь доступ к компонентам Enterprise Java- Bean (EJBj. ' ’’ EJB - это Java-класс, находящийся на «бизнес-уровне» многоуровневой . архитектуры J2EE (Java 2 Enterprise Editon). Также J2EE включает web-уровень, 4 Уд* содержащий знакомые нам сервлеты и JSP, и уровень информационной системы предприятия (Enterprise Information System - EIS), включающий системы управления базами данных. Смотрите раздел «См. также» рецепта 25.8. Сервлеты и JSP могут реализовывать логику представления (отображения информации) системы, использующей компоненты EJB для доступа к базе данных и решения специфических задач бизнеса или управления. В заключительном рецепте показано, как, используя в качестве сервера приложений BEA WebLogic, получить из сервлета доступ к компоненту EJB. 25.1 Конфигурирование JNDI-объекта на Tomcat Задача Вы хотите сконфигурировать компонент JavaBean в качестве JNDI-объекта, исполь- зуя Tomcat 4. Решение л Создайте элементы Resource и ResourceParam в файле server.xml или в XML- файле, представляющем ваше web-приложение (он размещается в каталоге webapps сервера Tomcat). Затем добавьте в web.xml элемент resource-env-ref. 656 | Глава 25. Использование JNDI и компонентов Enterprise JavaBean
Обсуждение На Tomcat установка JNDI-объекта происходит с помощью файла conf/server.xml. Если же ваше web-приложение настроено посредством отдельного XML-файла, находящегося в каталоге webapps сервера Tomcat, то конфигурировать JNDI ресурс следует именно в этом файле. В примере 25.1 показана привязка компонента JavaBean в качестве JNDI-объ- екта. Этот компонент имеет имя-сот. 7spservletcookbook. StockPriceBean. Пример 25.1. Элемент из файла server.xml, предназначенный для настройки JNDl-Ьбъекта «Resource name="bean/pricebean" type= "сот.jspservletcookbook.StockPriceBean" auth="Container" descriptions "A web harvesting bean"/> <ResourceParams name="bean/pricebean"> «parameter» , <name> f ac tory< /пате» <value>org.apache.naming.factory.BeanFactory</value> «/parameter» </ResourceParams» Пример 25.1 включает элемент Resource, а также ссылающийся на него по имени («Ьеап/рпсеЬеап») элемент ResourceParam. Это имя - тот самый адрес, по которому Java-код осуществляет доступ к экземпляру компонента JavaBean, используя JNDI API. В примере 25.2 показан элемент resource-env-ref, который необходимо помес- тить в дескриптор развертывания (web.xm!), чтобы код web-приложения получил доступ к данному JNDI-объекту Сохраните сам класс com. jspservletcookbook. Stock- PriceBean в каталоге WEB-INF/classes или в JAR-файле в WEB-INFAib. Пример 25.2. Поместите этот элемент в дескриптор развертывания web.xml <!— начало дескриптора развертывания —> <resource-env-ref> «description» A factory for StockPriceBean «/description» <resource-env-ref-name> bean/pricebean </resource-env-ref-name> <resource-env-ref-type> com.j spservletcookbook.StockPriceBean «/resource- env-ref-type» </resource-env-ref> <!— продолжение дескриптора развертывания т-> Конфигурирование JNDI-объекта на Tomcat | 657
В примере 25.3 показан фрагмент, в котором используется JNDI API, демонстрирующий как данная конфигурация используется кодом, связанным с JNDI API. Пример 25.3. Фрагмент кода для доступа KJNDI-pecypcy Tomcat import javax. naming. Content; import javax.naming.Initialcontext; ixqport javax. naming. NamingExcept ion; ‘ //Этот код может находиться в методе init() или, возможно, //в doGet() или doPost() Context env = null; StockPriceBean spbean = null; try{ env = (Context) new Initialcontext().lookup("java:comp/env"); spbean (StockPriceBean) env.lookup("bean/pricebean"); if (spbean == null) throw new ServletException( "bean/pricebean is an unknown JNDI object"); //закрываем начальный контекст (Initialcontext) env.close(); t • } catch (NamingException ne) { ч //закрываем данный контекст, если он больше не нужен try{ env.closeO; } catch(NamingException nex) {} г.. throw new ServletException (ne) ; } Пример 25.3 импортирует необходимые классы из пакета j avax. naming. Затем для получения ссылки на компонент JavaBean, который был привязан к JNDI-реализации, подряд идут два поиска (lookup). Первый поиск обеспечивает начальный контекст. env = (Context) new Initialcontext () .lookup’( "java:comp/env"); Второй поиск пытается вернуть объект StockPriceBean. spbean = (StockPriceBean) env.lookup("bean/pricebean"); После этого, если код не собирается использовать начальный контекст (InitialCon- text) для другого поиска, он закрывает контекст, чтобы освободить ресурсы, используе- мые этим объектом. В следующих рецептах код, подобный этому, используется в сервлетах hJSP. См. также Рецепт 25.2 по доступу из сервлета к JNDI-объекту на сервере Tomcat; рецепт 25.3 по доступу из JSP к JNDI-объекту на сервере Tomcat; главу 21 по доступу к источникам данных (DataSource) с помощью JNDI. 1 • ' 1 658 | Глава 25. Использование JNDI и компонентов Enterprise JavaBean
25.2 Доступ из сервлета к JNDI-pecypcy Tomcat Задача Вы хотите из сервлета получить доступ к JNDI-объекту, используя реализацию JNDI сервера Tomcat. Решение Для поиска JNDI-объекта используйте в методе init() сервлета классы пакета j avax.naming. Обсуждение Сервлет может получить доступ к компоненту JavaBean как к зарегистрированному JNDI-pecypcy только после того, как вы сделаете следующее. 1. Разработаете класс компонента JavaBean и сохраните его в каталоге WEB-INF/classes или в JAR-файле в WEB-INFAib. 2. Внесете изменения в файл конфигурации сервера и в файл web.xml, как это описано в рецепте 25.1, чтобы привязать этот объект к JNDI-дереву сервера Tomcat. В примере 25.4, в методе ini,t (), создается объект javax.naming. Initialcon- text (начальный контекст), а затем осуществляется поиск компонента JavaBean: com. jspservletcookbook.StockPriceBean. Этот компонент привязан к реализации JNDI под именем «Ьеап/рпсеЬеап». Метод init () вызывается всего один pai - когда кон- тейнер сервлетов создает экземпляр данного сервлета, таким образом, сервлет получает доступ всего к одному экземпляру компонента StockPriceBean. Пример 25.4. Использование JNDI объекта Tomcat из сервлета package com.jspservletcookbook; import java.io.lOException; import java.io.Printwriter; import javax,naming.Context; import javax.naming.Initialcontext; import javax.naming.NamingException; import javax.servlet.*; import javafc. servlet. http. *; public class BeanServlet extends HttpServlet { private StockPriceBean spbean; public void init() throws ServletException { Context env = null; Доступ из сервлета к JNDI-pecypcy Tomcat | 659
try{ env (Context) new Initialcontext().lookup(" java:comp/env"); spbean • (StockPriceBean) env.lookup("bean/pricebean"); //закрываем начальный контекст, если он не нужен //для другого поиска env.closeO; if (spbean == null) throw new ServletException( "bean/pricebean is an unknown JNDI object"),; } catch (NamingException ne)' { try{ env.closeO;} catch (NamingException nex) { } throw new ServletException(ne); }//try }//init public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, jaya.io.lOException { //задаем MIME-тип of ответа, "text/html" response.setCOntehtType("text/html"); //используем Printwriter для отправки текстовых данных клиенту java.io.Printwriter out = response-.getWriter (); //Начинаем формирование HTML-содержимого out .println ("<htmil><head>") 7 out.println("<title>Stock Price Fetcher</title></headxbddy>"); out.println("<h2>Please submit a valid stock symbol</h2>")7 //убедитесь, что methods"POST* чтобы метод service сервлета //вызвал doPost в ответ на передачу данных этой формы out.println( "<form method=\"POST\" action \"n + request.getContextPath() + "/namingbean\" >"); out.println("<table border=\"0\"><tr><td valign=\"top\">"); out.println(“Stock symbol: </td> <td valign=\"top\">"); out.println("<input type=\"text\" name=\"symbol\" size=\"10\">"); out.println("</td></tr><tr><td valign=\"top\”>"); out.println( "<input type=\"submit\" value=\"Submit Info\"></tdx/tr>") ; out .println( "</tablex/fdrm>") ; 660 I Глава 25. Использование JNDI и компонентов Enterprise JavaBean
out. printing " </bodyx/html>") ; } /./doGet public void doPost(HttpServletRequest request, HttpServletResponse response) throws java.io.lOException{ f String symbol;//здесь будет храниться символ акции float price = Of; symbol = request.getParameter("symbol"); boolean isValid = (symbol -== null || symbol.length() < 1} ? false : true; //устанавливаем MIME-тип ответа, "text/html" response.setContentType("text/html"); //используем Printwriter для отпрарки текстовых данных клиенту java.io.Printwriter out = response.getWriter(); //Начинаем формирование • HTML-содержимого Out.ptintln("<html><head>"); out.printin("<title>Latest stock value</title></head><body>"); if ((! isValid) || spbean == null){ out.println( "<h2>Sorry, the stock symbol parameter was either "+ "empty or null</h2>"); } else { out.printIn("<h2>Here is the latest value of "♦ symbol +"</h2>"); spbean. set Symbol (symbol); price * spbean.getLatestPrice(); out.printlnf (price«O? "The symbol is probably invalid." : ”"+price) ); ) put .printin ("</bodyx/html>") ; }//doPost }//BeanServlet В примере 25.4 вызывается метод close О объекта Initialcontext (начальный контекст), чтобы освободить все ресурсы, используемые данным объектом, поскольку этот начальный контекст больше не нужен - другой поиск не требуется. Затем сервлет исполь- зует найденный компонент JavaBean в методе doGet () для доступа к текущим котировкам акций. Сначала сервлет вызывает метод-установщик свойства данного компо- нента setSymbol (), чтобы известить компонент о том, какая акция его интересует (зада- вая символ акции). Доступ из серВлета к JNDI-ресурсу Tomcat | 661
В примере 25.5 приведен компонент JavaBean, который хранится на сервере Tomcat как JNDI-объект (это тот самый компонент, который используется в примере 25.4). Обсужде- ние этого компонента, извлекающего с web-страницы цену акции, приводится в главе 26. Там обсуждаются детали его работы; сервлет данной главы использует методы setSym- bol () и getLatestPrice () данного компонента. Компонент реализует все детали, свя- занные с загрузкой цены акции. Пример 25.5. Компонент JavaBean, который хранится как JNDI-объект package com.j spservletcookbook; import j ava.io.Buf feredReader; import java .do.lOException; import java.io.InputstreamReader; import java.net.URL; v i import java.net.MaiformedURLException; import javax.swing.text.html .HTMLEditorKit.Parsercallback; import j avax. swing. text. Mutable At tributeset; import j avax. swing. text. html. parser. ParserDelegator; public class StockPriceBean { private static final String urlBase = "http://finance.yahoo.com/q?d=tts="; private BufferedReader webPageStream = null; private URL stockSite = null; private ParserDelegator htmlParser = null; private MyParserCallback callback = null; private String htmlText = private String symbol = • "; private float stockVal = Of; public StockPriceBean() {}//конструктор компонента JavaBean //не хфинимает аргументов public void setSymbol(String symbol){ this.symbol = symbol; } public String getSymbol(){ return symbol; } //Внутренний класс, предназначенный для обратного вызова class. MyParserCallback extends Purs-urCallback { private boolean lastTradeFlag = false; private boolean boldFlag = false; public MyParserCallback(){ 662 | Глава 25. Использование JNDI и компонентов Enterprise JavaBean
> if (stockVal ,’е 0) stockVal = Of;. } public void handleStartTag(javax.swing.text.html.HTML.Tag t, MutableAttributeSet a,int pos) { if (lastTradeFlag && (t ” javax.swing.text.html.HTML.Tag.В )){ boldFlag = true; } }//handleStartTag , public void handleText(char(] data, int pos) { htmlText = new String(data); if (htmlText.indexOf("No such ticker symbol.") != -1){ throw new IllegalStateException( "Invalid ticker symbol in handleTextO method."); } else if (htmlText.equals ("Last Trade:")){ lastTradeFlag = true;,: } else if (boldFlag){ try{ stockVal = new Float (htmlText). f loatValue (); } catch (NumberFormatException ne) { try{ //вычесываем из этого числа любые запятые //используя NumberFormat java.text.NumberFormat nf a java.text.NumberFormat. getlnstance(); Double f = (Double) nf.parse(htmlText)г stockVal « (float) £.doubleValueO ; } catch (java. text. Par seExcept ion pe){ throw new IllegalStateException( "The extracted text * + htmlText + ” cannot be parsed as a number!"); }//вложенный try }//внешний try lastTradeFlag = false; boldFlag = false; }//if } //handleText } / /MyParserCal Iback public float getLatestPrice() throws lOException,HalformedURLException { stockSite = new URL(urlBase + symbol); webPageStream = new BufferedReader(new InputStreamReader(stocksite. Доступ из сервлета к JNDI-ресурсу Tomcat | 663 i
openStream())); htmlParser = new ParserDelegator(); tailback = new MyParserCallbackO;//Parsercallback synchronized (htmlParser) { htmlParser. parse (webPageStream, callback, true); }//синхронизация , //стираем символ setSymbol(""); return stockVal; }//getLatestPrice }//StockPriceBean Метод ParserDelegator .parse () синхронизируется, таким образом, в любой момент времени только один поток может осуществлять синтаксический разбор web-страницы и извлекать из нее.котировку акции. На рис. 25.1 показана web-форма, генерируемая методом doGet () сервлета. Пользо- ватель вводит в форму символ акции, нажимает на кнопку подтверждения и данные передаются методу doPost () сервлета. Рис. 251. Ввод символа акции для выяснения текущей котировки этой акции Биржевая информация, найденная данным JNDI-объектом для сервлета, показана на рис. 25.2. 664 | Глава 25. Использование JNDI и компонентов Enterprise JavaBean
Рис. 25.2. Метод doPost () сервлета генерирует текущую котировку акции, используя JNDI-объект См. также Рецепт 25.1 по конфигурированию JNDI-объекта на сервере Tomcat; рецепт 25.3 по доступу из JSP к JNDI-объекту на сервере Tomcat; главу 21 по доступу к источникам данных (DataSource) с помощью JNDI; главу 26 пр использованию web-информации. 25.3 Доступ к JNDI-pecypcy сервера Tomcat из JSP Задача Вам требуется доступ из JSP к JNDI-pecypcy. Решение Используйте фильтр для того, чтобы поместить этот объект (хранящийся как JNDI- ресурс) в область видимости запрос или сеанс. Йа странице JSP для доступа к объекту используйте JSTL-тегй с: set и с: out; i Обсуждение Получение доступа к JNDI-объекту - самая подходящая работа для фильтра, после чего, в том же фильтре, ссылку на этот объект можно поместить в область видимости сеанс, чтобы объект можно было использовать на странице JSP. Более подробную информацию, относящуюся к фильтрам, вы найдете в главе 19. Доступ к JNDI-pecypcy сервера Tomcat из JSP | 665
Вот шаги, которые необходимо сделать, чтобы использовать фильтр с JNDI и JSP. 1. Создайте и откомпилируйте фильтр, включая конструктор не принимающий аргументов. 2. В фильтре используйте JNDI API и пакет j avaxnaming, чтобы найти требуемый JNDI-объект и установить его в качестве атрибута сеанса. 3. Поместите фильтр в каталог WEB-INF/classes или в JAR-файл в WEB-INFAib. 4. Добавьте в web.xml элементы filter и filter-mapping; отобразите фильтр ' на страницу JSP, которая будет использовать данный JNDI-объект (пример 25.7). 5. Создайте JSP, в которой используется данный атрибут сеанса. Фильтр показан в примере 25.6. В методе init () этого фильтра инициализируется тцп javax.naming.Context (когда контейнер сервлетов создает экземпляр фильтра).. Метод doFilter ( ) берет JNDI-объект и сохраняет его в качестве атрибута сеанса. Цепочка фильтров заканчивается страницей JSP. Ha которую отображен данный фильтр; таким образом, JSP имеет доступ к атрибуту сеанса (то есть JNDI-объекту). Пример 25.6. Фильтр, который получает доступ к JNDI-объекту и устанавливает этот объект в качестве атрибута сеанса package com.j spservletcookbook; import java.io.lOException; import javax.naming.Context; import javaX.naming.Initialcontext; import javax.naming.NamingException; import j avax.servlet.*; import javax.servlet.http.*; public class JndiTFilter implements Filter { private FilterConfig config; private Context env; * ‘//Для* фильтра требуется конструктор без аргументов; здесь мы //задаем его в явном виде, несмотря на то, что компилятор может //сам создать конструктор, если обнаружит, что его/нет. public JndiTFilter() {} public void init (FilterConfig filterConfig). throws ServletException { this.config « filterConfig; try { env (Context) new Initialcontext().lookup("java:comp/env"); env.closeO; } catch (NamingException ne) { try{ env.closeO; } ce^tch (NamingException nex) {} throw new ServletException(ne); } ) public void doFilter(ServletRequest request, ServletResponse response. Filterchain chain) throws lOException, 666 | Глава 25. Использование JNDI и компонентов Enterprise JavaBean
ServletException { StockPriceBean spbean null; try { spbean (StockPriceBean) env.lookup("bean/pricebean"); } catch (NamingException ne) , { } HttpServletRequest hRequest = null; if (request instanceof HttpServletRequest) hRequest = (HttpServletRequest) request; HttpSession hSession « hRequest.getSession(); if (hSession 1" null) л hSession.setAttribute("MyBean",spbean); chain.doFilter(request,response); }// doFilter public void destroy(){ /♦вызывается перед тем, как этот экземпляр фильтра удаляется из обслуживания web-контейнером*/ } . }//Filter Метод doFilter () фильтра вызывается каждый раз, когда какой-либо клиент обращается с запросом к странице JSP, в результате с каждым клиентом связан отдельный экземпляр компонента JavaBean. Иными словами, в каждом сеансе хранится собственный экземпляр компонента. • •' Затем JSP может удалить этот атрибут сеанса (есйи не собирается использовать его вновь), чтобы сберечь ресурсы сервера. См. главу 16, где приведены рецепты ч’ по установке и удалению атрибутов сеанса. В примере 25.7 показаны элементы filter и filter-mapping, которые вы можете добавить в дескриптор развертывания. Это заставит контейнер сервлетов создать экземпляр данного фильтра (вызывая метод init() фильтра). Затем, каждый раз при получении запроса, удовлетворяющего шаблону (шаблонам) URL, связанному с фильтром при помощи элемента filter-mapping, контейнер вызывает метод doFilter () данного фильтра. Пример 25.7. Элемент filter и другие элементы для фильтра, работающего с JNDI <!— начало web.xml —> <filter> Я <fi1ter-name>JridiTFi1ter</fi1ter-name> <filter-class>com.jspservletcookbook.JndiTFilter</filter-class> </filter> <fi1ter-mapping> i < f i11er-name>JndiTFi11er</filter-name> Доступ к JNDI-ресурсу сервера Tomcat из JSP | 667
curl -pa t tem> / j ndiJsp. j sp< /url -pa t tem> </fi1ter-mapping» < !-- продолжение web.xml —> В фрагменте, приведенном в примере 25.7, фильтр JndiTFilter отображается на web- компонент с URL/jndUsp.jsp. В примере 25.8 показана страница JSP, использующая создан- ный атрибут сеанса - «МуВеап» - для отображения котировки акции. Пример 25.8. Страница JSP использует атрибут сеанса, являющийся по происхождению JNDI- объектом taglib tiri»"http://java.sun.com/jstl/core" prefix»"c" %> <html> <head><title>Jndi Bean</titlex/head> <body> < h2>Getting a StockPriceBean object via JNDI—</h2> <c:set var»"priceBwan" value»"${MyBean}"/> < 4— свойству •symbol* присваиваем символ интересующей акции—%> < c:set target»"${priceBean} property»"symbol" value»"${param.symbol}"/» — получаем последнюю цену, вызывая метод getLatestPrice() объекта JavaBean — The latest price: <c:out value»"${priceBean.latestPrice}" /> </body> </html> Выходная информация этой страницы JSP показана на рис. 25.3. Код компонента Java- Bean, используемого страницей, приведен в примере 25.5 рецепта 25.2. Рис. 25.3. Страница JSP, использующая атрибут сеанса, полученный при помощи JND1, для отображения цены акции См. также Рецепт 25.1 по конфигурированию JNDI-объекта на сервере Tomcat; рецепт 25.2 по дос- тупу из сервлета к JNDI-объекгу на сервере Tomcat; главу 21 по доступу к источникам дан- ных (DataSource) с помощью JNDI. 668 | Глава 25. Использование JNDI и компонентов Enterprise JavaBean
25.4 Конфигурирование JNDI-ресурса в WebLogic Задача Вы хотите привязать объект к реализации JNDI сервера WebLogic. Решение Воспользуйтесь консолью администрирования WebLogic. Обсуждение Здесь приводятся шаги, необходимые для привязки объекта javax.mail.Session (который я йспрльзую в этом рецепте в качестве примера) к реализации JNDI сервера WebLogic. К преимуществам такого способа использования объекта javax.mail .Ses- sion (почтовый сеанс) относится то, что в этом случае к моменту его использования ока- зываются настроены все необходимые элементы, такие как SMTP-хост (см. табл. 25.1). То есть почтовый сеанс (Session) полностью готов к использованию тем кодом, который время от времени будет находить (с помощью JNDI-поиска) и использовать его. 1. Войдите в консоль администрирования WebLogic, для чего наберите в браузере примерно такой URL: http://localhost:700I/console. 2. В меню слева выберите Имя-вашего-домена Services -»Mail. 3. Щелкните по «Configure a new Mail Session...». Появится окно, показанное на рис. 25.4. 4. Заполните текстовые поля этого окна. Дайте объекту Session JNDI-имя (поле «Jndi- Name»), то есть то имя, по которому этот объект будут искать. 5. Введите значения для любых свойств объекта Session, напечатав для каждого имя свойства, знак равно (=), и значение свойства. См. табл. 25.1. 6. Щелкните по кнопке «Apply», затем выберите вкладку «Targets». Открывшийся в результате экран позволит вам связать данный JNDI-объект с одним или более серверов. С этого момента JNDI-объект доступен Java-программам, использующим JNDI API, по имени, которое ему было присвоено при привязке. В рецепте 25.5 показано, как уви- деть графическое изображение JNDI-дерева, чтобы убедиться, что объект был привязан правильно. Конфигурирование JNDI-pecypca в WebLogic | 669
Рис. 25.4. Использование консоли администрирования WebLogic для конфигурирования типа javax.mail. Session в качестве JNDI-объекта Табл. 25.1. Набор свойств JavaMail для JNDI-объекта Session из этого рецепта Имя. свойства Описание Пример mail.host Почтовый сервер по умолчанию mail.comcast.net mail.smtp.host Почтовый хост, связанный с определенным прото- колом; значение по умолчанию mail. host mail.comcast.net mail.user Имя пользователя д ля подключения к почтовому серверу bruceperry mail.from Обратный адрес, который нужно указать при отправке почты author@jspservlet- cookbook.com См. также Рецепт 25.6 по доступу из сервлета к JNDI-объекту на сервере WebLogic; рецепт 25.7 по доступу из JSP к JNDI-объекту на сервере WebLogic; главу 2 цо развертыванию web- компонентов на WebLogic. ' 670 | Глава 25. Использование JNDI и компонентов Enterprise JavaBean
25.5 Просмотр JNDI-дерева на сервере WebLogic Задача Вы хотите увидеть графическое изображение JNDI-дерева на сервере WebLogic. Решение Находясь в консоли администрирования WebLogic, щелкните правой кнопкой по имени сервера и выберите «View JNDI Tree». Обсуадение После того как вы привязали некоторый объект к JNDI, используя консоль адми- нистрирования, можно посмотреть, как выглядит JNDI-дерево, чтобы убедиться, чтб WebLogic привязал ваш объект так, как вы хотели. В меню слева щелкните правой кноп- кой по «Имя-вашего-доме на -»Servers -»Имя-сервера». и выберите «View JNDI Tree». Будет создано новое окно браузера, показанное на рис. 25.5. Рис. 25.5. Графическое изображение JNDl-depeea сервера WebLogic См. также Рецепт 25.4 по конфигурированию JNDI-обьекта на сервере WebLogic; рецепт 25.6 по доступу из сервлета к JNDI-объекту на сервере WebLogic; рецепт 25.7 по доступу из JSP к JNDI-объекту на сервере WebLogic; главу 2 по развертыванию web-компонентов на WebLogic. Просмотр JNDI-дерева на сервере WebLogic | 671
25.6 Доступ из сервлета к JNDI-pecypcy на сервере WebLogic Задача Вы хотите получить доступ к JNDI-объекту, созданному и привязанному на сервере WebLogic. Решение Чтобы получить доступ к ссылке на привязанный объект, используйте в своем сервлете JNDI API. Обсуждение В примере 25.9 приведен сервлет (HttpServlet), который получает объект javax. mail*Session от JNDI-реализации сервера WebLogic. Сервлет использует этот объект для создания почтового сообщения. Сервлет в методе init() инициирует JNDI-поиск объекта, привязанного под именем «МуЕгпаП» (рецепт 25.4). Контейнер сервлетов вызы- вает метод init () всего один раз - когда контейнер создает экземпляр данного сервлета. Пример 25.9. Сервлет, получающий объект javax.mail. Session от JNDI-реализации сервера WebLogic, и создающий почтовое сообщение package com.j spservletcookbook; import java.io.lOException; import j ava.io.PrintWri ter; import javax.naming.Context; import javax.naming.Initialcontext ; import javax.naming.NamingException; import javax.mail.*; import javax.mail.internet.*; import javax.servlet.*; import javax.servlet.http.*; public class EmailJndiServlet extends HttpServlet { private Session mailSession; public void init() throws ServletException { Context env = null; try! ' env (Context) new Initialcontext(); 672 | Глава 25. Использование JNDI и компонентов Enterprise JavaBean
mailSession (Session) env.lookup("MyEmail"); if (mailSession null) • throw new ServletException( "MyEmail is an unknown JNDI object"); //закрываем начальный контекст env.cldse(); } catch (NamingException ne) { try{ env.closeO;} catch (NamingException nex) ( } throw new ServletException (ne); } }//init public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, / java.io.lOException { response.setContentType("text/html"); java.io.Printwriter out = response.getWriter(); out.println( "<html><head><title>Email message sender</titlex/headxbody>"); String to request.getParameter("to"); String from request.getParameter("from"); String subject = request.getParameter("subject"); String emailcontent ° request.getParameter(”emailContent"); try{ sendMessage (to, from, subject, emailcontent); } catch(Exception exc){ throw new ServletException(exc.getMessage()); ' } out.printIn( "<h2>The message was sent successfully</h2x/bodyx/html>"); > out.println("</bodyx/html>"); } //doPost public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.lOException { I/doGet() calls doPost() doPost(request,response); } private void sendMessage(String to. String from, String subject. String bodycontent) throws Exception { 1545 Доступ из сервлета к JNDI-pecypcy на сервере WebLogic | 673
Message mailMsg null; mailMsg new MimeMessage (mailSession);//a new email message InternetAddress[] addresses null; try { л if (to !- null) { //выбрасываем 'AddressException* если поле 'to* почтового //адреса ие соответствует синтаксису RFC822 addresses InternetAddress.parse(to, false); mailMsg.setRecipients(Message.RecipientType.TO, addresses); } else { throw new MeosagingExcbptioh( "The mail message requires a 'To* address."); } if (from ! null) mailMsg. setFrom( from); if (subject ! null) mailMsg.setSubject(subject); if (bodyContent !« null) mailMsg.setText(bodyContent); //Наконец отправляем сообщение; выбрасываем *SendFailedException* //если кто-либо из получателей имеет неправильный адрес Transport.send(mailMsg); } catch (Exception exc) { throw exc; }//sendMessage , }//EmailJndiServlet В методе doPost () вызывается метод sendMessage () сервлета, и ему передаются необходимые части почтового сообщения, например адрес получателя и содержание сооб- щения. Сервлет извлекает эту информацию из параметров запроса, поступившего от поль- зователя. Типичный запрос к этому сервлету выглядит примерно следующим образом. http://localhos t:7001/email?to=author@jspservletcookbook.com& ftom=bwperry@parkerriver.com&subj ect=hello& emailContent=A web message Пользователь также м(?жет послать (Post) информацию сервлету с помощью HTML- формы. Метод sendMessage () сервлета использует JNDI-объект в конструкторе j avax. mail. internet. MimeMessage - когда создает новое почтовое сообщение. На рис. 25.6 показано краткое сообщение, отображаемое сервлетом в окне браузера. 674 | Глава 25. Использование JNDI и компонентов Enterprise JavaBean
Рис. 25.6. Сервлет успешно отправил почтовое сообщение См. также Рецепт 25.4 по конфигурированию JNDI-объекта на сервере WebLogic; рецепт 25.5 по просмотру JNDI-дерева с помощью консоли администрирования сервера WebLogic; рецепт 25.7 по доступу из JSP к JNDI-объекту на сервере WebLogic; главу 2 по развертыванию web-компонентов на WebLogic. 25.7 Доступ к JNDI-pecypcy WebLogic из JSP Задача Вы хотите на странице JSP использовать JNDI-объект WebLogic. Решение Создайте фильтр, осуществляющий доступ к JNDI-объекту и устанавливающий этот объект в качестве атрибута сеанса. Обсуждение Возникает некоторое ощущение deja Vu, вызванное тем, что несколько рецептов назад мы использовали фильтр для передачи JNDI-объекта странице JSP на сервере Tomcat. Единственные отличия заключаются в том, что на этот раз в качестве сервера приложений используется WebLogic, а JNDI-объектом является объект Session (почтовый сеанс) из JavaMail, а не компонент JavaBean. Фильтр получает доступ к этому объекту, используя JNDI API на WebLogic. Затем фильтр устанавливает этот объект (javax.mail.Session) в качестве атрибута сеанса для того, чтобы страница JSP могла добраться до него. Код фильтра, используемого в дан- ном рецепте на сервере WebLogic, приведен в примере 25.10. Доступ к JNDI-pecypcy WebLogic из JSP | 675 22*
Пример 25.10. Фильтр, сохраняющий JNDI-объект WebLogic в атрибуте сеанса .package com.j spservletcookbook; import java.io.lOException; import javax.naming.Context; import javax.naming.Initialcontext; import javax. naming. NamingExcept ion; import javax.Servlet.*; import j avax.servlet.http.*; Ъ 1 public class JndiFilter implements Filter { private FilterConfig config; private Context env; public JndiFilter() {} public void init(FilterConfig filterConfig) throws ServletException ( this.config filterConfig; try { ' , env (Context) new Initialcontext(.); } catch (NamingException ne) { throw new ServletException(ne); } }//init public void doFilter(ServletRequest request, ServletResponse response, Filterchain chain) throws lOException^ ServletException { javax.mail.Session mailSession ° null; try { <, mailSession = (javax.mail.Session) env.lookup("MyEmail"); } catch (NamingException ne) ( } HttpServletRequest hRequest = null; if (request instanceof HttpServletRequest){ hRequest = (HttpServletRequest) request; HttpSession hSession = hRequest.getSession() ; if (hSession ! null) hSession.setAttribute("MyEmail",mailSession); }//if chain.doFilter(request,response); }// doFilter public void destroy(){ /♦вызывается перед тем, как данный экземпляр фильтра удаляется из обслуживания web-контейнером*/ ) } 676 | Глава 25. Использование JNDI и компонентов Enterprise JavaBean
В примере 25.11 показана настройка этого фильтра в дескрипторе развертывания. Этот дескриптор развертывания обязательно должен присутствовать в web-приложении, которое вы или кто-то другой установили на сервере WebLogic. Пример 25.11. Фильтр, получающий доступ к JNDI-объекту на сервере WebLogic <!— начало web.xml —> <filter> < f i 11er-name>JndiFi1ter</fi1ter-name> <filter-class»com.jspservletcookbook.JndiFilter</filter-class> </filter> < fi1ter-mapping> < f i11er-name>JndiFi11er</fi1ter-name> <url-pattern»/ jndi Jsp. j sp</url-pattem> </.filter-mapping» <!— продолжение web.xml —> В примере 25.12 показана страница JSP, использующая данный JNDI-объект. Код страницы отображает в браузере имя класса этого объекта - тип javax.mail. Session - который (объект) в рецепте 25.4 был привязан как JNDI-объект на сервере WebLogic. Фильтр из примера 25.11 устанавливает этот объект в качестве атрибута сеанса (не перепутайте этот сеанс - сеанс связи с пользователем - с почтовым сеансом (j avax.mail. Session)). Соз- данный атрибут сеанса доступен всем web-компонентам, участвующим в этом же сеансе. Для доступа к этому атрибуту сеанса в теге с: set используется следующее EL-выражение. ${MyEmail}. Затем, с помощью тега с tout, страница отображает имя класса данного атрибута сеанса, чтобы можно было убедиться, что объект действительно имеет тип javax.mail. Session. Весь JavaMail-код, необходимый для отправки почтового сообщения, приведен в рецепте 25.6. Пример 25.12. Страница JSP, осуществляющая доступ к JavaMail-объекту как к атрибуту сеанса <%@ taglib uri="http://java.sun.com/jstl/core" prefix»"с" %> <html> <head»<title»Jnd4 Email</titlex/head> ,<body> <h2»Getting a javax.mail.Session object via JNDI...</h2> <c:set var="mSession" value»"${MyEmail}" /» <c:out value»"${mSession.class.name}" /> </body> </html> На рис. 25.7 показано окно браузера после того, как пользователь запросил страницу данную JSP. Доступ к JNDI-ресурсу WebLogic из JSP | 677
, Getting a javax.mail.Session object via JNDI... ii Object name: javax.mail. Session Puc. 25.7. Страница JSP, осуществляющая доступ к JNDI-объекту с помощью фильтра сервлета См. также Главу 19 по фильтрам; главу 23 по JSTL; рецепт 25.4 по конфигурированию JNDI-объ- екта на сервере WebLogic; рецепт 25.6 по доступу к JNDI-объекту из сервлета на сервере WebLogic; главу 2 по развертыванию web-компонентов на WebLogic.» 25.8 Доступ к компоненту EJB с использованием JNDI-дерева WebLogic Задача Вы хотите из сервлета получить доступ к компоненту EJB (Enterprise JavaBean) на сервере WebLogic. Решение Узнайте JNDI-имя требуемого компонента EJB и воспользуйтесь пакетом javax. naming для получения ссылки на этот EJBObj ect, то есть на интерфейс удаленного объекта, чтобы получить возможность вызывать методы этого компонента EJB. Обсуждение Сервлет получает доступ к компоненту EJB, используя его JNDI-имя. Этот процесс совершенно прозрачен для разработчика сервлета. Любые компоненты EJB, используемые в приложении составляют бизнес-уровень приложения. Сервлетй и JSP представляют web- уровёнь многоуровневой распределенной архитектуры типичного 12ЕЕ-приложения (Java 2 Enterprise Edition). Чтобы использовать компонент EJB в ваших программах, вам дос- таточно знать JNDI-имя, связанное с этим компонентом EJB. 678 | Глава 25. Использование JNDI и компонентов Enterprise JavaBean
1 ' •' Компоненты EJB - тема обширная, и данный рецепт ограничивается лишь 4 Демонстрацией того, как сервлет может подключиться к компоненту EJB. В секции f «См. также» приведено несколько книг и ссылок на информацию по EJB и J2EE. Чтобы использовать некоторый компонент EJB, вы должны знать, что делают методы компонента EJB, реализующие бизнес-логику, но вам совершенно не требуется быть экспертом по пакету javax. ejb. В примере 25.13 приведен исходный код компонента EJB, не сохраняющего информацию о состоянии сеанса (stateless session EJB), выполняющегося под управлением сервера приложений WebLogic. г-*^--- . * * Один из видов компонентов EJB - сеансовый компонент EJB, не сохраняющий состояние сеанса (stateless session) - инкапсулирует бизнес-логику, которая f„4.* не требует сохранения состояния этого объекта в промежутках х*ежду обращениями к его методам. Существует и другой вид - сеансовый компонент EJB, сохраняющий информацию о состоянии сеанса (stateful session) (например, объект, представляющий корзину покупателя) - он должен запоминать свое состояние (например, значения различных переменных экземпляра) между вызовами его методов, как часть процесса общения с клиентом компонента EJB. Компонент,из примера 25.13 предоставляет java.util .Мар (отображение) в кагором содержатся Названия штатов США й соответствующие им почтовые аббревиатуры. Дан- ный сеансовый компонент EJB (SessionBean) содержит лишь один бизнес-метод, getAbbreviation (), который получает в качестве параметра имя штата и возвращает почтовую аббревиатуру этого штата. Пример 25.13. Сеансовый компонент EJB, не сохраняющий состояние сеанса package com.j spservletcookbook; import javax.ejb.*; import java.util.Map; import j ava.util.HashMap; public class AbbrevBean implements SessionBean{ private»Sessioncontext context; • private Map abbrevMap; public AbbrevBean(){ //конструктор компонента //не принимающий аргументов //Отображение (Мар) содержащее имена штатов и аббревиатуры abbrevMap ж new HashMapO; abbrevMap.put ("ALABAMA" ,."AL") ; abbrevMap.put("ALASKA","AK"); abbrevMap.put ( "AMERICAN SAMOA" . "AS") ; abbrevMap.put("ARIZONA","AZ"); abbrevMap.put("ARKANSAS","AR"); abbrevMap.put("CALIFORNIA","CA"); Доступ к компоненту EJB с использованием JNDI-дерева WebLogic | 679
abbrevMap.put("COLORADO","CO"); abbrevMap.put ( "CONNECTICUT!" ,"CT"); abbrevMap.put("DELAWARE","DE"); abbrevMap.put("DISTRICT OF COLUMBIA","DC"); •abbrevMap.put("FEDERATED STATES OF MICRONESIA","FM"); abbrevMap.put("FLORIDA","FL"); abbrevMap.put ("GEORGIA" , "GA" ) ; abbrevMap.put ( "GUAM", "GU") ; abbrevMap.put("HAWAII","HI"); abbrevMap.put("IDAHO","ID"); abbrevMap.put("ILLINOIS","IL"); • abbrevMap. pu t (" INDIANA","IN"); abbrevMap.put (" IOWA", " IA"); abbrevMap.put("KANSAS","KS"); abbrevMap.put("KENTUCKY","KY"); abbrevMap.put("LOUISIANA-,"LA"); abbrevMap.put("MAINE","ME"); abbrevMap.put("MARSHALL ISLANDS","MH"); abbrevMap.put("MARYLAND","MD"); abbrevMap.put("MASSACHUSETTS","MA"); abbrevMap.put("MICHIGAN","MI"); abbrevMap.put("MINNESOTA-,"MN"); abbrevMap.put("MISSISSIPPI","MS"); abbrevMap.put("MISSOURI","MO"); abbrevMap.put("MONTANA","MT"); abbrevMap.put("NEBRASKA","NE"); abbrevMap.put("NEVADA","NV"); abbrevMap.put("NEW HAMPSHIRE","NH"); abbrevMap.put("NEW JERSEY","NJ"); abbrevMap.put ("NEW MEXICO". "NM") ; abbrevMap.put("NEW YORK","NY"); abbrevMap.put( "NORTH CAROLINA","NC"); abbrevMap.put("NORTH DAKOTA","ND"); abbrevMap.put("NORTHERN MARIANA ISLANDS","MP"); abbrevMap.put("OKLAHOMA",“OK"); abbrevMap.put("OREGON","OR"); abbrevMap.put("PALAU","PW"); abbrevMap.put ("PENNSYLVANIA", "PA"); !i abbrevMap.put("PUERTO RICO","PR"); abbrevMap.put("RHODE ISLAND","RI"); abbrevMap.put("SOUTH CAROLINA","SC"); abbrevMap.put("SOUTH DAKOTA’,"SD"); 680 | Глава 25. Использование JNDI и компонентов Enterprise JavaBean
abbrevMap.put("TENNESSEE","TN"); abbrevMap.put("TEXAS","TX"); abbrevMap.put("UTAH","UT"); abbrevMap.put("VERMONT","VT"); abbrevMap.put("VIRGIN ISLANDS"," VI"); abbrevMap.put ( "VIRGINIA" , "VA") ; abbrevMap.put ("WASHINGTON" ,"WA"); abbrevMap.put("WEST VIRGINIA","WV"); abbrevMap. put ( "WISCONSIN", "Wl") ; abbrevMap.put("WYOMING","WY"); }//конструктор public void setSessionContext(Sessioncontext ctx) throws EJBException { context = ctx; }//setSessionContext public Map getAbbrevMap(){ return abbrevMap; ' } //Бизнес-метод компонента EJB public String getAbbreviation(String state){ return (String) abbrevMap.get(state)3 } //метод класса javax.ejb.SessionBean; он должен //реализовываться в сеансовых компонентах EJB //но в компонентах, не сохраняющих состояния сеанса он пуст. public void ejbActivatef){} /./метод класса javax.ejb.SessionBean; он должен //реализовываться в сеансовых компонентах EJB //но в компонентах, не сохраняющих состояния сеанса он пуст. public void ejbPassivate(){} //метод класса javax.ejb.SessionBean; public void ejbRemoveO {} J Конечно, пример 25.13 можно легко реализовать в виде обычного вспомогательного или служебного Java-класса. Этот простой компонент EJB нужен лишь для того, чтобы показать, как сервлет может обращаться к таким объектам. Компоненты EJB имеют свой дескриптор развертывания, наподобие файла web.xml, используемого web-приложениями. Дескриптор развертывания компонента EJB должен иметь имя ejb-jar.xml. Когда вы упаковываете компонент(ы) EJB, перед тем как развернуть его на сервере приложений, включите данный дескриптор развертывания в создаваемый архив. Файл ejb-jar.xml описывает соответствующий компонентны) EJB; это описание используется сервером приложений для того, чтобы правильно развернуть компонент EJB. Доступ к компоненту EJB с использованием JNDI-дерева WebLogic | 681
. i TaR файл ejb-jar.xml из примера 25.14 указывает тип компонента EJB (например, сеан- совый компонент, не сохраняющий информацию о состоянии (stateless session EJB)) и пол- ностью квалифицированные имена связанных с ним Java-классов, например имя класса его удаленного интерфейса. Пример 25.14. Файл ejb-jar.xml <?xml version="1.0"?> <!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems, Inc. //DTD Enterprise JavaBeans 2.0//EN" “http: / / j ava.sun.com/dtd/ej b-j ar_2_0.dtd" <ejb-jar> <erterprise-beans> <session» <ejb-name>AbbreviationEjb</ejb-name> <home»com. j spservletcookbook .AbbrevHome</home> <remote>com. j spservletcookbook.Abbrev< /remote» <local-home>com.j spservletcookbook.AbbrevLocalHome<I local-home» <local»com.jspservletcookbook.AbbrevLocal</local» <ejb-class»com.jspservletcookbook.AbbrevBean</ejb-class> <session-type»Stateless</session-type> <transaction~type»Container</transaction-type» «/session» </enterprise-beans> </ejb-jar> Пакет, который содержит данный компонент EJB и посредством которого этот ком- понент развертывается на сервере приложений, представляет собой JAR-файл с именем myejb.jar (произвольное имя, вы не обязаны использовать именно его). Поскольку этот сеансовый компонент развертывается на сервере BEA WebLogic, дан- ный JAR-файл должен включать специфичный дескриптор развертывания weblogic-ejb-jar. xml. Этот дескриптор развертывания дает возможность настроить ряд аспектов развертыва- ния компонента EJB на сервере WebLogic, например JNDI-имена интерфейсов home и local. Объект «home» - это реализация интерфейса «home», а объект «local home» - реа- лизация интерфейса local home. Эти объекты являются «фабриками» для объектов и*; EJB, которые делегируют вызовы бизнес-методов компоненту EJB, развернутому на сервере. Фабрика - это Java-класс, который генерирует разного вида объекты Java-класса. В данном рецепте клиент использует JNDI для получения ссылки на объект home, который создает EJB-объект. Затем сервлет (клиент) вызывает метод getAbbreviation () объекта EJB; EJB-объект - это удаленный объект или «stub», который делегирует вызов этого метЬда подлинному компоненту EJB, хранящемуся на сервере. 682 | Глава 25. Использование JNDI и компонентов Enterprise JavaBean
В сервлете, описанном далее с этом рецепте, вы столкнетесь с JNDI-именем объекта home. Когда вы развертываете компонент EJB на сервере WebLogic с помощью консоли администрирования, WebLogic автоматически привязывает объекты home и local home к JNDI-дереву WebLogic используя имена, указанные в дескрипторе развертывания weblogic-ejb-jar.xml. В примере 25.15 показан дескриптор развертывания weblogic-ejb-jar.xml для нашего сеансового компонента, не сохраняющего информацию о состоянии. Пример 25.15. Файл weblogic-ejb-jar.xml <!DOCTYPE weblogic-ejb-jar PUBLIC ‘-//BEA Systems, Inc.//DTD WebLogic 7.0.0 EJB//EN* 'http://www.bea.ccm/servers/wls700/dtfi/weblogic-ejb-jar.dtd'> <weblogic-ejb-j ar> <weblogic-enterprise-bean> <ejb-name>AbbreviatiOnEjb</ejb-name> <stateless-session-descriptor> <pool> <initial-beans-in-free-pool>l</initial-beans-in-free-pool> </pool> </stateless-session-descriptor> < j ndi -naune>AbbrevHome< I j nd!-namo> < local - j ndi -name >AbbrevLocalHome< / local - j ndi -name> </weblogic-enterprise-bean> </weblogic-ejb-jar> Модуль EJB - это сложный пакет, включающий классы компонента EJB, удаленные интерфейсы и два разных дескриптора развертывания. Содержимое файла myejb.jar пока- зано в примере 25.16. Для отображения содержимого этого JAR-файла я использовал' команду tvf туеjb. jаг... набрав ее в окне с командной строкой (это работает и в Unix и в Windows). / Пример 25.16. Содержимое файла ejb-jar.xml Н:\book\cookbook\code\chap27\src\ejbs\ejbjar>jar tvf myejb.jar МЕТА-INF/ МЕТА-INF/MANIFEST.MF com/ com/j spservletcookbook/ com/j spservletcookbook/Abbrev.class com/j spservletcookbook/AbbrevBean.class com/jspservletcookbook/AbbrevHome.class com/jspservletcookbook/AbbrevLocal.class Доступ к компоненту EJB с использованием JNDI-дерева WebLogic 683
com/jspservletcookbobk/AbbrevLocalHome.class META-INF/ejb-j ar.xml META-INF Zweblogic-ejb-j ar;xml В примере 25.16 сеансовый компонент EJB - это класс AbbrevBean.class, удаленный / интерфейс - класс Abbrev.class, а объект home (фабрика объектов EJB, реализующих интерфейс Abbrev) - класс AbbrevHome. class. \ И наконец, в примере 25.17 показан сервлет, использующий компонент EJB из примера 25.13. Код сервлета - самодокументируемый. Важная вещь, которую необхо- димо помнить - сервлет получает ссылку на объект AbbrevHome из JNDI-дерева WebLogic. Затем в методе doGet () сервлета вызывается метод create () объекта AbbrevHome, чтобы получить экземпляр удаленного интерфейса данного сеансового компонента EJB (в данном примере - он имеет тип Abbrev). Пример 25.17. Сервлет, получающий доступ к компоненту EJB на сервере WebLogic, используя JNDI package com.jspservletcookbook; ‘ import java.io.lOException; import java.io.Printwriter,- import javax.naming.Context; import j avax.naming.Initialcontext; import javax.naming.NamingException; import j avax. rmi.PortableRemot eObject; import javax.servlet. *; import j avax.servlet.http.*; public class WebJndiServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.lOException { //Параметр запроса выглядит так: 'вtate=Massachusetts' String state request.getParameter("state"); Context env n null; Abbrev abbrev null; AbbrevHome home = null; try{ env = (Context) new Initialcontext(); //Ищем объект home (фабрику) на JNDI-дереве WebLogic Object localH “ env.lookup("AbbrevHome") ; //Вызов этого метода обязателен для EJB-кода, использующего //технологию RMI-IIOP 684 | Глава 25. Использование JNDI и компонентов Enterprise JavaBean /
V home c (AbbrevHome) РогtableRemoteObject.narrow(localH, AbbrevHome. class'); //закрываем начальный контекст env.closet); f- if (home null) throw new ServletException( "AbbrevHome is an unknown JNDI object")/ //Получаем удаленный интерфейс, вызывая //метод create() объекта home abbrev = (Abbrev) PortableRemoteObject.narrow(home.create(), Abbrev.class); } catch (NamingException ne) { try{ env.close();} catch (NamingException nex) { } throw new ServletException(ne); ) catch (javax.ejb.CreateException ce) { throw new ServletException(ce); ) //устанавливаем MIME-тип ответа "text/html“ < response , setContentType (? te^t/html") ; •»java.io.Printwriter out = response.getWriter(); out.printin("<html><head>"); out .printIn ("<title>State abbreviations</titlex/h^adxbody>") ; out.printIn("<h2>Here is the state's abbreviation</h2>"); //Вызываем метод-getAbbreviation()объекта EJB (EJBObject) ; //а этот объект делегирует вызов ртого метода настоящему //компоненту EJB. Переводим параметр запроса в верхний регистр //поскольку именно в таком виде имена штатов, являющиеся ключами // хранятся в отображении (java.util.Мар)в компоненте EJB. if (state Iя null) out.println( abbrev.getAbbreviationstate.toUpperCase()) ); try{ //Сервлет—завершает с объектом EJB (EJBObject); вызывая //его метод remove() abbrev. remove (); } catch (javax.ejb.RemoveException re){} out.println("</body></html>"); }//doGet public void doPost(HttpServletRequest request, \ * Доступ к компоненту EJB с использованием JNDI-дерева WebLogic | 685
HttpServletResponse response) throws ServletException, java.io.lOException { doGet(request, response); }//doPost }//WebJndiServlet Значение аббревиатуры для штата Орегон («Oregon») (как пример) в конце концов извлекается на стороне сервера с помощью вызова метода getAbbreviation () компо- нента EJB. На рис. 25.8 показано окно web-браузера после того, как пользователь обратился с запросом к данному сервлету. URL запроса при этом выглядел примерно так: , http:/flocalhost:7001/webjndi?state=0regon. В web.xml шаблон URL /webjndi отображен на сервлет из примера 25.17. Рис. 25.8. Отображение в браузере, формируемое сервлетом, обращающимся к компоненту EJB См. также Рецепт 25.4 по конфигурированию JNDI-объекта на сервере WebLogic; рецепт 25.6 по доступу к JNDI-объекту из сервлета на сервере WebLogic; главу 2 по развертыванию web-компонентов на WebLogic; web-ссылка на API javax. ejb: http://java.sun.com/j2ee/ 1.4/docs/api/javax/ejb/package-summary.html; страница с документацией по серверу WebLogic 7.0: http://edocs.bea.com/wls/docs70/index.html- ссылка на руководства по J2EE, в том числе руководство по Enterprise JavaBean: http://java.sun.com/j2ee/tutorial/index. html; книгу «Enterprise JavaBeans», третье издание (O'Reilly); книгу «J2EE Design Pat- terns» (O'Reilly). 686 | Глава 25. Использование JNDI и компонентов Enterprise JavaBean
ГЛАВА 26 Пожинание информации из Web 26.0 Введение В Web (Всемирной Паутине) хранится огромное количество информации. Большую ее часть можно легко получить, например, просто зайдя на web-сайт своей организации и читая расположенные там страницы или знакомясь с результа- тами поиска. Однако часто бывает нелегко отделить полезную информацию от шелухи. Значительное количество визуальных компонентов web-страниц обычно отдано под меню, логотипы, рекламные баннеры и анимацию (апплеты или Flash). А что если все, что вас интересует, - это крупинки данных в океане HTML? Ответ заключается в использовании Java для синтаксического разбора w6b- страницы и пожинания из. нее лишь определенных фрагментов информации. В web- терминах эта задача называется harvesting (пржинанием).или scraping (соскаблива- нием) информации из web-страницы. Возможно, web-сервисы (глава 27) со временем устранят необходимость пожинать данные из web. Но пока большинство основных web-сайтов не создадут и не наладят у себя API web-сервисов, для извлечения из web- страниц необходимого текста вы можете использовать Java и определенные подпа- кеты пакета j avax. swing. text. Как это делается? Ваша программа, используя протокол HTTP, соединяется с некоторой web-страницей и извлекает из нее HTML-текст. Синтаксический разбор HTML с web-сайта требует переноса по сети целой web-страницы, даже если вас интересует лишь ее небольшой фрагмент. f £ По этой причине использование web-сервисов — более эффективный способ получения необходимой информации с web-сайта. Затем с помощью Java-коди производится синтаксический разбор этой HTML- страницы с тем, чтобы извлечь из нее лищь интересующие данные, например, сводку погоды или котировку акции. В следующих ниже рецептах показаны Java-классы, которые вы будете использовать для пожинания web-информации. Далее в рецептах приводятся, в качестве примера; сервлет и JSP, использующие компонент JavaBean для извлечения и отображения текущей цены акции. 687
26.1 Синтаксический разбор HTML-страницы с использованием п од пакетов javax.s wing, text Задача Вы хотите воспользоваться классами J2SE (Java 2 Standard Edition), позволяющими осуществить синтаксический разбор HTML. Решение Для создания синтаксического анализатора HTML используйте различные подпа- кеты пакета j avax. swing. text. Обсуждение " . Версии 1.3 и 1.4 J2SE включают классы, необходимые для анализа web-страниц в поис- ках информации. Java-программы, приводимые в данных рецептах, импортируют следующие классы. javax.swing.text.html.HTMLEditorKit.ParserCallback; javax.swing.text.MutableAttributeSet; j avax. swing. text. htinl. parser. ParserDelegator ; Шаблон проектирования, используемый этими классами для чтения web-страниц, включает в себя три основных элемента. 1. Объект j ava. net. URL, который открывает сокет или входной поток (Input- Stream) web-страницы, используя протокол HTTP. Затем код использует данный объект для чтения web-страницы. , , t 2. Объект ParserDelegator, с помощью которого код анализирует данные web- страницы, вызывая для этого метод parse () этого объекта. 3. Объект Parsercallback, используемый объектом ParserDelegator для выполнения определенных действий в процессе разбора HTML-текста web- страницы. Вообще, термин callback (обратный вызов) означает объект, который Java- код обычно передает в качестве параметра в конструктор другого объекта. И этот другой (или охватывающий) объект затем управляет callback-объектом, вызывая те или иные его методы, которые программист реализует в соответствии с тем, какие действия должны выполняться при разборе HTML. По мере чтения рецептов роль callback-объекта станет понятнее. Сервлет и компонент JavaBean, определенные в данной главе, для реализации обратного вызова (callback) используют внутренний класс. Этот callback-класс, расширяющий класс j avax. swing. text. html. HTMLEdi torKi t. Par serCal 1 - back, показан в примере 26.1. ------------------------s,—.--,—,-------------------------------------- 688 | Глава 26. Пожимание информации из Web
Пример 26.1. Callback-класс для анализа данных web-страницы , class МуРыгьегСаНЬаск extends Parsercallback { //"bredcrump", которые ведут нас к месту, //где находится цена акции private boolean lastTradeFlag = false; private boolean boldFlag = false; public MyParserCallbackO { //Очищаем переменную экземпляра охватывающего класса, хранящую цену акции if (stockVal ! 0) stockVal «Of; } //Метод, который синтаксический анализатор (парсер) вызывает каждый раз, //когда сталкивается с открывающим тегом public void handleStartTag(javax.swing.text.html.HTML.Tag t, MutableAttrlbuteSet a,int pos) { if (lastTradeFlag && (t == javax.swing.text.html.HTML.Tag.В ))( boldFlag = true; } }I/handlestartTag // Метод, который парсер вызывает каждый раз, когда он получает //вложенное текстовое содержимое public void handleText(char(] data,int pos){ htmlText - new String(data); if (htmlText.indexOf("No such ticker symbol.") != -1){ throw new IllegalStateException( Invalid ticker symbol in handleTextO method."); } else if (htmlText.equals("Last Trade:")){ lastTradeFlag = true; } else if (boldFlag){ try( stockVal « new Float(htmlText).floatvalue(); 4 } catch (NumberFormatException ne) { try{ //вычесываем из числа любые запятые используя NumberFormat java.text.NumberFormat nf .« java.text.NumberFormat. getlnstance(); Double f « (Double) nf.parse(htmlText); stockVal (float) f.doubleValueO; } catch (java.text.ParseException pe){ throw new IllegalStateException ( "The extracted text " ♦ htmlText + Синтаксический разбор HTML-страницы | 689
" cannot be parsed as a number!"); }//try }//try //Очищаем переменные экземпляра внутреннего класса lastTradeFlag = false; boldFlag = false; }//if } //handleText }//MyParserCallback Этот callback-класс включает методы, вызов которых свидетельствуют о достижении в процессе синтаксического разбора определенного элемента. К примеру, синтаксический анализатор (объект, охватывающий данный callback-объект) вызывает Метод handie- st ar tTag () каждый раз, когда в процессе прохода по web-странице он наталкивается на открывающий тег. Открывающий тег - это, например, тег <html>, <title> или <body>. Таким образом, реализуя метод handl eSt ar tTag () в своем коде, вы управляете тем, что сделает ваша программа, когда будет найден открывающий тег, например, вы можете «приготовиться к извлечению текста, который появится между открывающим и закрывающим тегом title». В примере 26.1 такой алгоритм используется для поиска на web-странице изменяющийся цены акции и именно этим занимаются два метода (handleStartTag () and handleText ()) классаMyParserCallback. 1. Этот класс с помощью callback-метода handleTextO ищет текст «I,ast Trade»; когда находит, логической переменной lastTradeFlag присваивается значение true. Такой способ путешествия программы по огромному лесу HTML web- страницы напоминает движение по хлебным крошкам (bredcrump) героев сказки братьев Гримм, блуждавших по лесу. 2. Когда метод handleStartTag {) обнаруживает тег Ь, следующий сразу за текстом «Last Trade» (флаг lastTradeFlag установлен в true), он извлекает содержимое, вложенное в этот тег Ь, поскольку именно это содержимое является ценой акции. Большим недостатком такого пожинания web-информации, который частично А призваны устранить web-сервисы, является то, что когда изменится web-страница, которую вы разбираете, ваша программа начнет выбрасывать исключения и не сможет больше извлекать информацию, поскольку ее алгоритм базируется на старой структуре страницы. Перед тем как мы перейдем к сервлетам и JSP, ознакомьтесь с примером 26.2, где показан фрагмент кода, в котором используются объекты ParserDelegator и MyParserCallback, демонстрирующий идею того, как эти объекты работают вместе. 690 | Глава 26. Пожинание информации из Web
• ‘ • ‘4 / ' / Пример 26.2. Фрагмент кода, демонстрирующий совместную работу класса синтаксического анализатора и callback-класса //Переменные экземпляров private ParserDelegator htmlParser = null; private MyParserCallback callback = null; //Инициализируем BufferedReader и URL ьлутри метода для соединения //и чтения web-страницы BufferedReader webPageStream = null; URL stockSite = new URL(BASE_URL + symbol); //Соединение внутри метода webPageStream =. new BufferedReader( new InputStreamReader(stockSite.OpenStream())); //Создаем синтаксический анализатор и callback-объект htmlParser = new ParserDelegator(); callback = new MyParserCallback();//Parsercallback //Вызываем parseO, передавая ему BufferedReader и callback-объект htmlParser.parse(webPageStream,callback,true); Метод parse () класса ParserDelegator, которому callback-объект передается как аргумент, как раз и «запускает» процесс вызова методов этого callback-объекта. А теперь посмотрим, как эти классы работают в сервлете, компоненте JavaBean и JSP. См. также Ссылку на документацию по ParserDelegator: http://java.sun.corn/j2se/l.4.1/docs/ api/javax/swing/text/html/parser/ParserDelegator.html\ главу 27 по использованию API web-сервисов для извлечения информации с web-серверов. 26.2 Использование сервлетов для пожинания Web-данных I Задача Вы хотите использовать сервлет для пожинания web-информации. Решение Используйте классы программного интерфейса ( API) синтаксического разбора HTML, входящие в Java 2 SDK (Software Development Kit). Использование сервлетов для пожинания Web-данных | 691
Обсуадение В предыдущем рецепте мы познакомились с соответствующими подпакетами пакета j avax. swing. text; здесь мы увидим, как они используются в сервлете. Пример 26.3 импортирует классы, необходимые для синтаксического разбора HTML-страницы. Метод doGet () сервлета отображает форму, в которую пользователь вводит символ интересующей его акции (например, «INTC», регистр символов не важен). Затем метод doPost () пытается получить текущую цену, соответствующую этому символу, разбирая для этого web-страницу сайта finance yahoo.com. Пример 26.3. Пожинание web-данных с помощью сервлета package com.j spservletcookbook; import java.io.lOException; import j ava.io.Printwriter; import j ava.io.Buf feredReader; import j ava.io.InputStreamReader; import java.net.URL; import java.net.MalformedURLExceptidn; import javax.servlet.*; import javax.servlet.http.*; import j avax. swing. text. html. HTMLEditorKit. ParserCal Iback; import j avax. swing. text. MutableAttributeSet; import j avux. swing. text. html. parser. ParserDelegator ; public class HtmlParseServlet extends HttpServlet { private static final String BASE_URL = "http://finance.yahoo.com"+ "/q?d=t&s="; private ParserDelegator htmlParser null; private MyParserCallback callback w null; private String htmlText = private boolean lastTradeFlag = false; private boolean boldFlag = false; private float stockVal Of; public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.lOException { //устанавливаем MIME-тип ответа "text/html" response. setContentType (" text /html"); //используем Printwriter для отправки Текста java.io.Printwriter out = response.getWriter(); //Начинаем формирование HTML-содержимого out. print In (" <htmlxhead>") ; 692 | Глава 26. Пожинание информации из Web
out.printin("<title>Stock Price Fetcher</titlex/headxbody>")ч out.printlnf"<h2>Please submit a valid stock syiribol</h2>"); //убедитесь, что metJiod="POST*’ чтобы метод service сервлета // для обработки данных, введенных в форму вызывал doPost out.printIn( • "<form method=\"post\" action =\"" + reijuest.getContextPathO + "/stockservlet\" >"); out.printlnf"<table border=\"0\"xtrxtd valign=\"top\">"); out.printIn("Stock symbol: </td> <td valign=\"top\">"); out.printlnf"<input type=Vtext\" name=\" symbol\" size=\"10\">"); out.printlnf "</tdx/trxtrxtd valign=\"top\">’) ; out.printlnf "<input type=\"submi t\" value=\"Submit Info\"x/tdx/tr>"); out .printIn ("</tablex/form>“); out. print In ( " < /bodyx /html> " J ; } //doGet public void doPost(HttpServletRequest request, HttpServletResponse response) throws java.io.lOException{ String symbol;//здесь оудет храниться символ акции । float price///Последняя цена акции symbol * request.getParameter("symbol"); boolean isValid (symbol.» null || symbol.length() < 1) ? false : true; //устанавливаем MlME-тип ответа "text/html" response.setContentType("text/html"); java.io.Printwriter out = response.getWriter(); //Начинаем формирование HTML-содержимого out .printIn ("<htmlxhead> ") ; out .printin ("<title>Latest stock value</titlex/headxbody>"); - if (! isValid){ out.printlnf "<h2>Sorry, the stock symbol parameter was either empty " + "or null</h2>"); ) else { out.printIn("<h2>Here is the latest value of + symbol +"</h2>"); price = getLatestPrice(symbol); , । out.printlnf (price»O? "The symbol is probably invalid." : ""+price) ); } Использование сервлетов для пожинания Web-данных I 693
out', println ("</bodyx/html>") ; }// doPost private float getLatestPrice(String symbol) throws lOException, MalformedURLException { BufferedReader webPageStream null; URL stocksite new URL(BASE_URL + symbol); webPageStream new BufferedReader(new InputStreamReader(stocksite. openStreamQ )); htmlParser “ new ParserDelegator(); callback new MyParserCallback(); //код сконструирован так, чтоб» сделать вызов //метода parse() безопасным для параллельных потоков f synchronized (htmlParser) { htmlParser .parse (webPageStream, callback, true); }//synchronized return stockVal; )//getLatestPrice class MyParserCallback extends Parsercallback { II" bredcrujup" ведущие к месту нахождения цены акции private boolean lastTradeFlag false; private boolean boldFlag false; public MyParserCallback(){ //Очищаем переменные экземпляра объемлющего класса if (stockVal != 0) stockVal = Of; } public void handleStartTag(javax.swing.text.html.HTML.Tag t, MutableAttributeSet a,int pos) { if (lastTradeFlag && (t == javax.swing.text.html.HTML.Tag.В )){ boldFlag = true; _ } } /' /handleStartTag public void handleText (char [] data, int pos)( htmlText = new String(data); if (htmlText.indexOf("No such ticker symbol.") != -1){ throw new IlldgalStateExcdption( "invalid ticker symbol in handleText() method."); } else if (htmlText.equals("Last Trade:")){ lastTradeFlag = true; ) else if (boldFlag){ 694 | Глава 26. Пожинание информации из Web
' 1 ' - 4 try{ utockVal new Float (htmlText).floatvalue(); } catch (NumberFormatException ne) { try{ // вычищаем все запятые из числа, используя //NumberFormat java.text.NumberFormat nf java.text-NumberFormat. getXnstic» (); Double f " (Double) nf. parse (htmlText); stockVal (float) f.doubleValue(); } catch (java.text.ParвeException pe){ throw new XllegalStateExdeption( "The extracted text * + htmlText + 7 cannot be parsed as a number!"); )//try }//try lastTradeFlag = false; boldFlag = false; }//if } //handleText }//MyParserCallback }//HttpServlet Внутренний класс MyParserCallback определяет алгоритм разбора, используемый в сервлете, подробности в рецепте 26.1. Метод getLatestPrice () использует этот call- Ьаск-класс и синтаксический анализатор HTML, чтобы вернуть цену акции в виде числа стипом float. <2, ч Объект ParserDelegator синхронизируется, когда происходит вызов метода parse (), это сделано для того, чтобы в одно и то же время только один поток мог f «у разбирать web-страницу и? устанавливать значение переменной stockVal * (переменная экземпляра, представляющая значение цены). Получился сервлет, который слишком сложен, чтобы оставаться одним классом, поскольку в нем используется и API сервлета и классы API синтаксического разбора HTML. С точки зрения проектирования, лучше разделить эти функции по нескольким разным Java-классам. В последующих рецептах создается компонент JavaBean, который возьмет на себя синтаксический разбор HTML и извлечение текущей котировки акции. Выходная информация, формируемая методом doGet () сервлета, показана на рис. 26.1. На рис. 26.2 показан вид в браузере Netscape выхбдной информации, формируемой методом doPost (). 1 Использование сервлетов для пожинания Web-данных | 695 ’
Please submit a valid stock symbol ; Stock symbol: jaapl ii Puc. 26.1. Пользователь вводит символ акции и отсылает данные формы (g Latest stock value Netscape L-. ГЯП L Eie £<* yew fio Bookmarks loots Window Help i Here is the latest value of aapl 19.8 Till i iim—imm——Ha 'inn .H.IHI. ,йа„, Puc. 26.2. Сервлет возвратил последнюю цену акции, символ которой был задан пользователем См. также Рецепт 26.3 по созданцю компонента JavaBean, реализующего синтаксический разбор web-страницы; рецепты 26.4 и 26.5 по использованию этого .компонента JavaBean в сервлете и JSP соответственно. 696 | Глава 26. Пожимание информации из Web
26.3 Создание компонента JavaBean - синтаксического анализатора Web-страницы Задача Требуется создать компонент JavaBean, который web-компоненты могли бы исполь- зовать для разбора HTML-страницы. Решение Используйте классы Java API синтаксического разбора HTML из подпакетов пакета j avax. swing. text. Сохраните созданный компонент JavaBean в каталоге WEB-INF/ classes или в JAR-файле, в WEB-INFAib. Обсуадение В примере 26.4 приведен компонент JavaBean, предназначенный для разбора web- страницы с целью получения текущих котировок акций. Сервлеты и JSP могут использо- вать этот компонент для своих целей, тем самым, вынося -функции по реализации разбора страницы за пределы своего кода (разграничение областей ответственности). Весь-код ком- понента, включая внутренний класс, представляющий Parsercallback, уже присутст- вовал в предыдущих рецептах данной главы. К числу нововведений относится метод- установщик (setter) компонента JavaBean, для установки символа акции: setSym- bol(String symbol). Пример 26.4. Компонент JavaBean, предназначенный для использования в сервлетах и JSP package com.j spservletcookbook; inport j ava.io.Buf feredReader; import java. io. lOException ; import j ava.io.InputstreamReader; import java.net.URL; import java.net.MalformedURLException; import javax.awing.text.html.HTMLEditorKit.Parsercallback; import javax.swing.text.MutableAttributeSet; import javax.swing.text.html.parser.ParserDelegator; ' public class StockPriceBean { private static final String urlBase = "http:litinance.yahoo.com/’ + "q?d=t&s="; Создание компонента JavaBean - синтаксического анализатора Web-страницы | 697
private BufferedReader webPageStream = null; private URL stockSite = null; private ParserDelegator htmlParser = null; private MyParserCallback callback = null; private String htmlText = H"; private String symbol = ’ private float stockVal » Of; public StockPriceBean() {} x //Метод-установщик символа акции public void setSymbol(String symbol){ this.symbol symbol; } class MyParserCallback extends Parsercallback { //" bredcrump" ведущие к месту'нахождения цены акции private boolean lastTradeFlag =' false; private boolean boldFlag = false; public MyParserCallback () { // Очищаем переменные экземпляра объемлющего класса if (stockVal ! 0) stockVal Of; > public void handleStartTag(javax.swing.text.html.HTML.Tag t, MutableAttriouteSet a,int pos) { э if (lastTradeFlag && (t == javax.swing.text.html.HTML.Tag.В )){ boldFlag* = true; ) }//handleStartTag public void handleText(char[] data,int pos){ htmlText = new String(data); if (htmlText.indexOf("No such ticker symbol.") ! = -1){ throw new’IllegalStateException( "Invalid ticker symbol in handleText() method."); } else if (htmlText.equals('Last Trade:")){ lastTradeFlag = true; } else if (boldFlag)( try{ stockVal new Float (htmlText) . f loatValue (); } catch (NumberFormatException ne) { try{ 698 | Глава 26., Пожинание информации из Web
//вычищаем все запятые из числа, используя / /NilmberFormat java.text.NumberFormat nf java.text.NumberFormat. getlnstance(); Double f (Double) n£.parse(htmlText); stockVal •• (float) f.doubleValueO; } catch (java.text.ParseException pe){ throw new IllegalStateException( "The extracted text ” * htmlText + " cannot be parsed as a number!"); }//try }//try lastTradeFlag = false; boldFlag = false; }//if } //handleText )//MyParserCallback public float getLatestPrice() throws lOException,MalformedURLException { stockSitd new URL(urlBase + symbol); webPageStream = new BufferedReader (new InputStreamReader(stocksite. openStream ())); htmlParser " new ParserDelegator(); callback = new MyParserCallbackO;//Parsercallback synchronized(htmlParser){ htmlParser .parse (webPageStream, callback, true); }//synchronized //очищаем символ symbol return stockVal; }//getLatestPrice }//StockPriceBean После того как компонент завершает извлечение цены акции, он очищает переменную экземпляра, в которой хранится символ акции, присваивая ей пустую строку. Класс MyParserCallback очищает переменную stockVal, присваивая ей 0, чгобы раннее извлеченная цена акции не сохранялась между обращениями разных потоков к методу getLatestPrice(). ' См. также Рецепт 26.4 по использованию данного компонента JavaBean в сервлете; рецепт 26.5 по использованию данного компонента JavaBean в JSP, Создание компонента JavaBean - синтаксического анализатора Web-страницы | 699
26.4 Использование в сервлете компонента JavaBean, осуществляющего разбор Web-страницы Задача Вы хотите использовать компонент JavaBean, осуществляющий разбор Web- страницы, в сервлете. Решение В требуемом методе сервлета (doGet () или doPost ()) создайте экземпляр дан- ного компонента JavaBean и вызывайте его методы. Обсуждение Чтобы компонент JavaBean был доступен сервлету, он должен храниться в каталоге WEB-INF/classes, включая все подкаталоги, в соответствии с именем пакета этого компо- нента. Компонент также можно сохранить в JAR-файлё, в WEB-INFAib. Поскольку компонент JavaBean из примера 26.5 находится в том же пакете, что и класс данного сервлета (com. jspservletcookbook), классу сервлета не требуется импортировать класс этого компонента. Если бы класс компонента JavaBean находился в web-приложении в другом пакете, то в сервлет необходимо было бы включить оператор import, как в следующем ч* примере. import com.parkerriver.beans.BeanParserServlet; В методе doGet () формируется HTML-форма, в которую будет вводиться символ акции (например, «intc»). Затем в методе doPost () создается экземпляр компонента StockPriceBean, вызывается метод setSymbol() этого компонента и, наконец, по итогам вызова метода getLatestPrice () отображается цена акции. Пример 26.5. Сервлет, использующий компонент JavaBean, предназначенный для получения теку- щей цены акции ' package com.j spservletcookbook; import java.io.lOException; import java.io.Printwriter; import javax.servlet.*; Inport j avax.servlet.http.*; public class BeanParserServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) 700 | Глава 26. Пожинание информации из Web
У throws ServletException, java.xo.lOException { //задаем MIME-тип ответа, "text/html" response.setContent Type("text/html"); //используем Printwriter для отправки данных клиенту java.io.PrxhtWriter out = response.getWriter(); 3 fa //Начинаем формирование HTML-содержимого out.printIn("<html><head>"); . | out.println("<title>Stock Price Fetcher</titlex/headxbody>") ; out.printin("<h2>Please submit a valid stock symbol</h2>"); //убедитесь, что method="POST" чтобы метод service сервлета // для обработки данных, введенных в форму вызывал doPost out.printIn( H<form method=\" POST \" action =\*" + request.getContextPath() + "/stockbeanX" >"); out.println("<table border=\"0\"xtrxtd valign=\"top\">"); out.priritln("Stock symbol: </td> <td valign=\"top\">"); out.printing"cinput type=\"text\" name=\"symbolX" size=\"10\">"); out.println( "</tdx/trxtrxtd valign=\"top\">") ; out.println( "<input type=\"submit\" value=\"Submit InfoX"x/tdx/tr>") ; out. print In (" < / tablex / f orm>") ; out. pr,intIn (" < /bodyx /html > ") ; } //doGet public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.lOException { String symbol;//здесь будет храниться символ акции float price = Of; symbol = request.getParameter("symbol"); boolean isValid = (symbol == null || symbol.length() < 1) ? false : true; > , -//задаем MIME-тип ответа, "text/html" response.setContentType("text/html"); //используем Printwriter для отправки данных клиенту java.io.Printwriter out = response.getWriter(); //Начинаем формирование HTML-содержимого out. print In (" <htmlxhead>"); out.printin("<title>Latest stock value</titlex/headxbody>") ; if (! isValid){ out.println( "<h2>Sorry, the stock symbol parameter was either empty "+ Использование в сервлете компонента JavaBean | 701
"or null</h2>"); } else { out.printlnf"<h2>Here is the latest value of "+ symbol +"</h2>"); StockPriceBean spbean new StockPriceBean()} spbean. setSymbol ( symbol) ; price spbean.getLatestPrice(); out.printlnf (price*"O? "The symbol is probably invalid." : ""+price) ); }//if out.printlnf“</body></html>"); }// doPost a }//HttpServlet i HTML-форма сервлета (генерируемая в методе doGet ()) и отображение цены акции (генерируемое в методе doPost ()) выглядят в браузере так же, как это было показано на рис. 26.1 и 26.2. См. также Рецепт 26.3 по созданию компонента JavaBean, реализующего разбор web-страницы; рецепт 26.5 по использованию в JSP компонента JavaBean, осуществляющего разбор web-страницы 26.5 Использование в JSP компонента JavaBean, осуществляющего разбор Web-страницы Задача Для пожинания информации с web-страницы вы хотите использовать JSP совместно с компонентом JavaBean. Решение Для создания экземпляра компонента используйте стандартное действие j sp: useBean. Обсуждение Тот же самый компонент JavaBean, который в предыдущих рецептах был создан и сохранен в web-приложении в каталоге WEB-INF/classes, можно использовать и на странице JSP. Страница JSP из примера 26.6 для создания экземпляра этого компо- нента с именем priceFetcher исрользует стандартное действие jsp: useBean. Если 702 | Глава 26. Пожинание информации из Web
запрос не включает параметр, содержащий символ акции, JSP отображает HTML-форму, приведенную на рис. 26.1. Для генерирования такого условного поведения JSP использует, теги ядра JSTL. К этим тегам относятся с: choose, с: when, и с: otherwise. Если запрос к странице JSP включает параметр с символом акции, то JSP присваи- вает значение этого параметра свойству symbol компонента priceFetcher. Этот код эквивалентен вызову метода setsymbol () данного компонента JavaBean; он передает символ акции компоненту, после чего тот может извлечь из соответствующей web- страницы текущую цену этой акции. Пример 26.6. Страница JSP использует j sp: useBean, чтобы задействовать компонент Java* Bean, пожинающий информацию из Web taglib uri-^http://java.sun.com/jstl/core" prefix*"c" %> <jsp:useBean id="priceFetcher” class" "com.jspservletcookbook.StockPriceBean" /» <html> <head><title>Price Fetch<7titlex/head» <body> <c:choose» <c:when test""${empty param.symbol}"> <h2»Please submit a valid stock symbol</h2> <form method-"POST" action = '<c:out value»"${pageContext.request.contextPath} />/priceFetch.jsp ’ > <table borders"0"><tr»<td valign="top"»Stbck symbol: </td> <td valign="top"» <input type="text" name="symbol" size=“10"></tdx/tr> ctrxtd valign="top"> <input type=" submit" value="Submitr Info"»</tdx/tr> < / tablex / f orm> </с:when» <c:otherwise» <h2»Here is the latest value of <c:out valUe="${param, symbol}" /></h2> <jsp:setProperty name ""priceFetcher" property""symbol" value" "<%= request.getParameter(\"symbol\") %»* /> <jsp:getProperty names"priceFetcher* property""latestPrice"/> < Zc:otherwise» </cschoose» . </body> </html> Использование в JSP компонента JavaBean | 703
После того, как JSP передала компоненту символ требуемой акции, выполняется следующий код, вызывающий метод getLatestPrice () компонента: <jsp:getProperty name="priceFetcher" property="latestPrice"/> Если символ акции, переданный компоненту с помощью jsp:setProperty, ока- жется корректным, на выходе страницы JSP, на месте стандартного действия j sp: get- Property, будет выведена цена акции. Выходная информация страницы JSP из примера 26.6 выглядит так же, как показано на рис? 26.1 и 26.2. См. также Главу 23 по JSTL* рецепт 26.4 по использованию в сервлете компонента JavaBean, осуществляющего разбор web-страницы. 704 |' Глава 26. Пожинание информации из Web
ГЛАВА 27 Использование Web API сайтов Google и Amazon 27.0 Введение Google и Amazon.com - одни из первопроходцев в области web-сервисов. Google - это гигантская поисковая машина и каталог. Amazon.com - web-сайт электронной торговли, который начинался как онлайновый книжный магазин и со временем занялся многими другими товарами, например, программным обеспечением и электроникой. Оба сайта, независимо друг от друга, предложили разработчикам программного обеспечения свои интерфейсы прикладных программ (API) web-сервисов, дающие вам возможность, используя Java-объекты, управлять поиском Google, а также получать из Java-кода доступ к обширным каталогам товаров сайта Amazon.com. Для нас, Java-разработчиков, web-сервисы означают возможность создавать запросы и получать ответы, используя специальный XML-формат. Иными сло- вами, вы делаете запрос в текстовой форме, используя XML-элементы и атрибуты, и получаете ответ в таком же формате. Для переноса информации Web-сервисы обычно используют протокол SOAP (Simple Object Access Protocol - простой протокол доступа к объектам), основанный на языке XML. Проще говоря, SOAP представляет абстракцию конверта, который в свою очередь включает необязательные заголовки и тело сообщения. Сообщение складывается из его внешней оболочки, а также заголовков и тела, все это сделано из XML-элемен- тов, связанных с определенными пространствами имен. Технологии, описываемые в данной главе, для переноса данных SOAP-сообщений, основанных на XML, используют протокол HTTP. Я не мог до конца понять, что такое SOAP-сообщения, пока не ознакомился с некоторыми примерами таких сообщений. В примере 27.1 приводится часть SOAP- ответа Web-сервисов Amazon, на запрос осуществить поиск по ключевому слову «Lance Armstrong». Ответ представляет собой XML-файл, состоящий из корневого элемента Pro- ductInfo, включающего в себя один или несколько элементов Details. Каждый из элементов Details представляет одну книгу из каталога Amazon (я исключил все элементы, кроме одного, чтобы сделать пример нагляднее). В примере показана только одна из возвращенных в результате поиска книг. 23-1S45 705
Пример 27.1. SOAP-omeem от Web-сервисов Amazon, сформированный по итогам поиска по ключу «Lance Armstrong» <?xml versions"1.0" encodings"UTF-8"?> <!DOCTYPE ProductInfo PUBLIC "-//Amazon.com //DTD Amazon Product Info//EN" "http: / /xml. amazon. com/schemas/dev-lite. dtd" > <ProductInfо xmlns:xsi=^http: I/www. w3.org/2001/XMLSchema-instance11 xsi:noNamespaceSchemaLocation« "http://xml.amazon.com/schemas/dev-lite.xsd"* <Details url» "http://www.amazon.com/exec/obidos/ASIN/0399146113/webseprices-20? dev-t=DCJEAVXSDVPUD%26camp=2025%261ink_code»xm2"> <Asin*0399146113</Asin> <ProductName>It's Not About the Bike: My Journey Back to Life </Prpduc tName* <Catalog>Book</Catalog> <Authors> <Author>Lance Armstrong</Author> <Author>Sally Jenkins</Author> </AuthorS> <ReleaseDate>June, 2000</ReleaseDate> <Manufacturer>Putnam Pub Group</Manufacturer> <ImageUr1Sma11> http://images.amazon.com/images/P/0399146113.01.THUMBZZZ.jpg </ImageUrISmall* < ImageUr iMedi urn* http://images.amazon.eom/images/P/0399146113.01.MZZZZZZZ.jpg < / ImageUr lMedium> <ImageUrlLarge> http: //images.amazon. com/images/P/0399146113.0-1.LZZZZZZZ. jpg </ImageUriLarge* <ListPrice>$24.95</ListPrice> <0urPrice>$17.47</OurPrice> <UsedPrice>$9.99</UsedPrice> </Details* </ProductInfo* 706 | Глава 27. Использование Web API сайтов Google и Amazon
Вот три главные причины примейения web-сервисов, основанных на SOAP 1. Протокол SOAP базируется на стандартах, так что для работы с ним вы можете использовать любую технологию, для которой реализован SOAP API или набор инструментов, в том числе Java, .NET, Perl и Python. Объектно-ориентированные тех- нологии (такие, как Java) позволяют вам создавать и читать SOAP-сообщения, используя объекты, вместо того чтобы копаться в сыром XML, что делает работу с web-сервисами более приятной. 2. Web-сервисы не привязаны к конкретной технологии (интероперабельны). Сервер, использующий технологии J2EE, например сервлеты или JSP, может свободно обме- ниваться сообщениями с сервером, на котором используется .NET, поскольку они говорят на одном языке: SOAP и XML. 3. SOAP-сообщения могут свободно циркулировать между web-серверами, не вступая в конфликт с ограничениями межсетевых экранов, поскольку эти сообщения представляют собой XML-текст, переносимый при помощи протокола HTTP (самым обычным способом, как HTML-страницы). Разработчики выбирают SOAP как простую форму распределенных вычислений: этот протокол позволяет объекту, находящемуся в памяти одного сервера вызывать методы одного и более объектов, находящихся на других, удаленных компьютерах, путем обмена с ними SOAP-сообщениями. Во введении к рецепту нельзя раскрыть такую сложную тему, как SOAP, но сущест- вует много книг и открытых руководств по данной теме (некоторые из них приведены в секции «См. также»). Находясь в основном на бета-стадии разработки, программные интерфейсы (API) web- сервисов Amazon и Google позволяют Java-программе создавать очень полезные и слож- ные системы, взаимодействующие с Amazon и Google. Программы web-сервисов Amazon и Google созданы, чтобы познакомить разработчиков с новыми путями обработки запросов и ответов к двум популярным web-сайтам. Программы, работающие с web-сервисами, обычно включают создание учетной* записи разработчика и получение ключа или опознавательного знака, который будет передаваться в каждом из ваших запросов к этим сайтам. В данной главе описывается, что нужно сделать, чтобы начать использовать web-сервисы Amazon и Google, а затем показано, как интегрировать эти API с сервлетами и JSP. Введение | 707 и*
27.1 Предварительная подготовка к работе с API Web-сервисов Google Задача Вы хотите использовать Web API Google, чтобы из Java-программы осуществлять поиски по обширному web-индексу Google. Решение Скачайте SDK для работы с Web API Google. Создайте на Google учетную Запись и получите лицензионный ключ, который дает возможность использовать Web API Google. Обсуждение SDK для работы с Web API Google включает архив с именем google.jar. Этот файл содержит классы, которые ваша программа будет использовать для соединения с Google в процессе поисков. Ниже перечислены шаги, которые необходимо предпринять, чтобы подготовить web-приЛожение к соединению с Google. 1. Скачайте заархивированный SDK с http://www.google.com/apis/download.html. Распа- куйте этот файл в отдельный каталог (в бета-версии 3.0 Web API Google он имеет имя googleapi). Этот каталог содержит файл google.jar, а также большое количество примеров кода и документацию. 2. Создайте на сайте Google учетную запись и получите лицензионный ключ, представляющий собой закодированный текст, который выглядит примерно так: «5WlABCyzPSyI3rIa5Pt3DtXMatsdzaSGB». Этот код будет использоваться вашим Java-кодом при запросах к индексу Google. Запрос, не сопровождающийся правиль- ным ключом, потерпит неудачу. 3. Поместите файл google.jar в каталог WEB-INFAib своего web-приложения. 4. Используя пакет com. google. soap. search разработайте Java-классы для соеди- нения с Google. I См. также Домашнюю страницу Web API Google: http://www.google.com/apis/', SDK для Web API Google: http://www.google.com/apis/download.html. 708 | Глава 27. Использование Web API сайтов Google и Amazon
27.2 Создание компонента JavaBean для соединения с Google Задача Вы хотите использовать Web API Google для поиска на сайте Google из Java-кода. Решение k Создайте компонент JavaBean, поскольку его можно использовать и в сервлетах и в JSP Обсуждение Для начала следует выполнить подготовительные мероприятия и получить учетную запись, как описано в рецепте 27.1. После этого можно приступить к созданию компо- нента JavaBean, который будет выполнять поиск по ключевому слову на сайте Google и возвращать полученные результаты. В примере 27.2 сначала импортируется пакет com.google.soap.search, содержащийся в googleapi.jar. Если помните, этот JAR-файл необходимо было сохранить в каталоге WEB-INFAib. В результате, web-приложение способно найти необ- ходимые Java-классы из этого пакета, и компонент GoogleBean из примера 27.2 может их использовать. Пример 27.2. Компонент JavaBean, осуществляющий поиск по базе данных с web-информацией сайта Google package com.jspservletcookbook; Import com.google.soap.search.*; public class GoogleBean { private GoogleSearch search; private GoogleSearchResult googleRes; private final static String GOOGLE_KEY > " 5WlBWPyzPSyl3rIa5Pt3DtXMatsniSGB" ; private String lineSep » "\n"; //Устанавливаемые свойства компонента private String query; private boolean filter; private int rnaHRe suits; private int startRes; private boolean safeSearch; private String restrict; private String langRestrict; Создание компонента JavaBean для соединения c Google | 709
public GoogleBean(){ //Конструктор без аргументов query ; restrict = ""; langRestrict ""; } public String structureResult(GoogleSearchResuit res){ //Каждый из элементов GoogleSearchResultElement GoogleSearchResultElement!] elements = res.getResultElementsO ; String url """; String results = "Estimated total results count: " + res•getEstimatedTotalResultsCount() + lineSep + lineSep; for (int.i “0; i < elements.length; i++){ url в elements [i] .getURLO; results +» ("Title: " + elements[i].getTitle() + lineSep + "URL: <a href=\"" + url +• + url + "</a>"+ lineSep "Summary: " + elements[i].getSummary() + lineSep + "Snippet: " + elements[i].getSnippetf) + lineSep + lineSep) } return results; > public String getSearchResults() throws GoogleSearchFault { search = new GoogleSearch(); search.setKey(GOOGLE_KEY); search.setFilter(filter); if(restrict.length() > 0) search.setRestrict(restrict); search.setQuerystring(query); googleRes search.doSearch(); return structureResult(googleRes); } public void setLineSep(String lineSep){ this.lineSep=lineSep; } public String getLineSep(){ return lineSep; ) public void setQuery(String query){ this, query = query; } public String getQuery(){ return query; } 710 | Глава 27. Использование Web API сайтов Google и Amazon
public void setRestrict(String query) { this.restrict s restrict; ) public String getRestrict(){ return restrict; » ) public void setLangRestrict(String langRestrict){ this.langRestrict = langRestrict; } ' public String getLangRestrict(){ return langRestrict; } public void setFilter(boolean filter){ this.filter = filter; } public boolean' getFilter(){ return filter; } public void setSafeSearch(boolean safeSearch){ this.safeSearch = safeSearch; } public boolean getSafeSearch(){ return safeSearch; } public void setMaxResults(int maxResults){ this.maxResults = maxResults; } public int getMaxResults(){ return -maxResults; }. public void setStartRes(int startRes){ this.startRes- - startRes; j }- k ' , 1 public int getStartRes(){ return startRes; } }//GoogleBean Создание компонента JavaBean для соединения c Google | 711
В примере 27.2 самые интересные вещи происходят в методах get- SearchResults() иstructureResults(). В методе getSearchResults () создается объект GoogleSearch, который затем настраивается путем задания опций поиска Google, после чего вызывается метод doSe- arch () этого объекта. Формирование определённого поиска на сайте google.com произ- водится с помощью методов/установщиков свойств (setter) объекта GoogleSearch. Так, метод setQueryString () устанавливает условия поиска, заданные пользовате- лем. Java-объекты, которые используют наш компонент JavaBean, передают ему термы поиска, вызывая его метод setQuery (). Вы можете задавать различные опции поиска Google, вызывая методы- установщики свойств объекта класса GoogleSearch. Например, вызов setFiIter (true) производит фильтрацию так, чтобы результаты были получены только с одного web-хоста. И вы можете ограничить поиск определенными подсайтами Google, вызвав setRestrict («глас»). См. http.-// www.google.com/apis/reference.htnjl. Любой, связанный с SOAP, поиск с использованием Google, должен вызывать метод setKey () объекта GoogleSearch с правильным лицензионным ключом, в противном случае поиск будет отвергнут. Метод structureResults () форматирует результаты поиска. Результаты поиска Google инкапсулированы в объекте GoogleSearchResult. Этот объект содержит мас- сив объектов GoogleSearchResultElement, каждый из которых представляет один URL, из числа возвращенных поиском Google. Метод getResultElements () объекта GoogleSearchResult возвращает массив из GoogleSearchResultElement. Затем код перебирает в цикле все элементы этого массива. Каждый* возвращенный элемент (объект GoogleSearchResultElement) имеет методы, возвращающие значения его свойств (getter-методы), которые и предоставляют информацию о конкретной найденной web-странице. • getURL () возвращает URL найденного элемента. • getTitleO возвращает заголовок (title) найденной HTML-страницы. • getSummary () возвращает фрагмент (небольшой, возможно непонятный кусок тек- ста из этой web-страницы). • getSummary () возвращает краткое изложение найденной web-страницы. Компонент JavaBean использует эти методы для отображения URL, заголовка, фрагмента и краткого изложения каждой из web-страниц, возвращенных в результате поиска. На рис. 27.2 показано, в каком виде эти итоги отображаются. См. также Домашнюю страницу Web-сервиса Google: http://www.google.cpm/apis/; SDK для Web API Google: http://www.google.com/apis/download.html; рецепт 27.1 по подготовке программ- ного окружения для использования Web API Google. 712 | Глава 27. Использование Web API сайтов Google и Amazon
27.3 Использование сервлета для соединения а/ с Google Задача Из сервлета соединиться с Google и инициировать поиск. Решение В качестве вспомогательного класса, выполняющего поиск в Google, используйте компонент JavaBean из рецепта 27.2. Обсуждение Сервлет из примера 27.3 обращается к компоненту GoogleBean из рецепта 27.2 для выполнения поиска на google.com и отображает полученные результаты. Метод doGet () сервлета отображает HTML-форму. Клиент, с помощью этой формы вводит параметры поиска и затем отсылает (POST) эти данные в виде параметров формы обратно сервлету. И наконец, метод doPost () создает экземпляр GoogleBean для ини- циирования поиска. В данном случае используется дескриптор развертывания, отоб- ражающий любые запросы вида «/googleservlet» на сервлет из примера 27.3. Пример 27.3. Сервлет, использующий специальный компонент JavaBean для поиска в Google, и отображающий результаты поиска package com.jspservletcookbook; import java.io.lOException; impart java.io.Printwriter; import javax.servlet.*; import javax.servlet,http.*; public class GoogleServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.lOException { response.setContentType("text/html"); j^va.io.Printwriter opt = response.getWriter(); ' out.printIn("<html><head>"); out.printin("<title>Initiate a Google Search</titlex/head><body>H); out.printin("<h2>Please enter your search terms</h2>"); ________________J___________________ X , ___________;____________________ Использование сервлета для соединения с Google | 713
//Убедитесь, что method»"POST" чтобы метод service сервлета //для обработки данных, введенных в форму вызвал doPost ''Х out.printIn( "<form method=\"POST\" action »\"" + reques t.getCont ext Path () + X. "/googleservlet\" >"); out .pbiQtln ("«table border=\"0\ "xtrxtd valign=\ " top\" > "); out.printiii("Search terms: </td> «td valign=\"top\">"); out.printlnf"«input type=\"text\" name»\* query \" size=\"15\">"); out. print In (" < / tdx / trxtrx td valign=\ " top\ out.printlnf "Restrict to Google sub-site___ </td> <td valign=\"top\">"); out.printlnf "«select name=\"restrictX"xoption>iinclesam</options-" + " <option>linux</option>option>mac</optionxoption>bsd</option> + "</select>"); out.printin("</tdx/trxtrxtd valign=\"top\">") ; out.printlnf "«input type=\"submit\" value»\"Submit Info\"x/tdx/tr>"); out.printlnf "«/tablex/forms-") ; out.printlnf "</bodyx/html>") ; } //doGet public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException,java.io.lOException{ String query = request.getParameter("query"); String restrict = request.getParameter("restrict"); boolean isValid = (query »» null || query.length() < 1) ? false : true; //задаем MIME-тип ответа "text/html" response.setContentType("text/html"); java .io. Printwriter out = response.getWriter(); out.printIn("<html>«head>"); out .printin ("«titlesGoogle results«/titlex/head>«body>"); I if (I isValid){ out.printlnf "<h2>Sorry, the query parameter was either empty or null«/h2>"); } else { out.printlnf"<h2>Here are your search results«/h2>"); GoogleBean gb » new GoogleBeanO; gb.setFilter(true); //Настройка для web-отображения 714 | Глава 27. Использование Web API сайтов Google и Amazon
gb.setLineSep("<br />"); if (restrict 1= null && restrict.length() > 0) gb.setRestrict(restrict); gb. setQuery (quefy); try { out.println( gb.getSearchResults() ); } catch (Exception e){ throw new ServletException( e.getMessageO ); } }//if out .println ("</bodyx/html>"); }// doPost }I/GoogleServlet Использование класса GoogleBean в методе doPost () очевидно. Код устанавливает значения ряда опций поиска (например, setFilter(true)), а затем вызывает метод getSearchResults () компонента JavaBean. Этот метод возвращает строку (String) с отформатированными результатами поиска, затем сервлет, с помощью Printwriter, отображает эту строку в браузере. На рис. 27.1 показана простая HTML-форма, формируемая в методе doGet () сервлета. Рис. 27.1. Ввод ключевых слов для поиска в Google при помощи сервлета Использование сервлета для соединения с Google | 715
Часть «Restrict to Google sub-site... « позволяет пользователю выбрать одно из значений - unclesam, linux, mac, bsd, или ничего. Пользователь вводит в текстовое поле HTML-формы терм поиска «Lance Armstrong» и нажимает кнопку «Submit Info» (представить информацию). На рис. 27.2 показаны результаты поиска, отображаемые методом doPost () сервлета. Web API Google за один поиск отображает не более 10 найденных значений. J Google results - Microsoft Internet Explorer HHG Е*е tfgfc Fav«to.,rTociis.,-;Hejp^ Estimated total results count 458000 URL: httpj'Mv'W.lancearmstronq .conV Back Stop Refresh http:ZAxahostBOBO/home/googleservlet Here are your search results Title::: Lance Armstrong's Official Web Site:: Summary The official Lance Armstrong site. Updated daily with the latest news about Lance and the USPS team.... Snippet 8/12 ~ The folks over at VN.com have run down the latest on Lance, note nothing too surprisng: "Armstrong is spending the rest of August with his family in the... Title::: Lance Armstrong Official Website:: URL: http-/Mwtf.lancearmstronq conVlance/online2.nsffrnain?readform Summary. Snippet Lance Armstrong On-Linel... Si's Rick Reilly wrote "Lance Armstrong is more than a bicyclist now, more than an athlete. He's become a kind of hope machine.......... Puc. 27.2. Сервлет, использующий Web API Google, отображает некоторые результаты уюиска 716 | Глава 27. Использование Web API сайтов Google и Amazon
См. также Домашнюю страницу Web-сервиса Google: http://www.google.com/apis/', SDK для Web API Google: http://www.google.com/apis/download.html', рецепт 3.1 по отображению сервлета на имя в web.xml', рецепт 27.1 по подготовке программного окружения для использования Web API Google; рецепт 27.4 по использованию JSP для соединения с web-сервисами Google. 27.4 Использование JSP для соединения с Google Задача Вы хотите произвести поиск Google, используя Web API Google и JSP. <. Решение Используйте стандартное действие j sp: useBean для получения доступа к компо- ненту GoogleBean из примера 27.2, а затем используйте этот экземпляр компонента JavaBean для соединения с web-инструментами Google. Обсуждение JSP из примера 27.4 использует теги ядра JSTL, чтобы определить, послал ли пользо- ватель в составе запроса ’ строку поиска. Если параметр запроса, предназначенный для строки поиска пуст, то JSP отображает форму (см. рис. 27.1). См. главу 23, где приводится описание тегов ядра JSTL. Если же параметр запроса содержит строку поиска, то JSP использует GoogleBean для поиска в google.com и отображает результаты. Для создания экземпляра компонента JavaBean, который хранится в каталоге V/EB-INF/lib, страница JSP использует стандартное действие j sp: useBean. При мер 27.4. Страница JSP использует компонент JavaBean для поиска в google.com taglib uri“"http://java.sun.com/jstl/core" prefix="c" Яь> <html> <head><title>Search Google from a JSP</titlex/head> ' <body> X <c:choose» <c:when test= "${empty param.query}"> <h2>Please enter your search terms...</h2> Использование JSP для соединения c Google | 717
<%-- Отображение HTML-формы... —%» «form method="POST" action =*<c:out values " $ {pageContext. request. contextPath}" />/google. jsp' > <table border»"0"» <tr»«td valign="top"> Search terms: </td> <td valign="top"» <input type="text" name= "query" size="15"> </tdx/tr> <tr»«td valign="top"> Restrict to Google sub-site... </td> <td valign="top"> «select names"restrict"> «option selected>none</optionxoption>unclesam</option> <option>linux</option> <option>mac</optionxoption>bsd</optionx/select> </tdx/tr> <trxtd valign="top"> «input type="submit" value="Submit Info"></tdx/tr> < / tablex / f orm> <%-- Конец HTML-формы... —%> «/c:when» «с:otherwise» «4— Создаем экземпляр GoogleBean —%> <jsp:useBean id»"gBean" class» "com. j spservletcookbook. GoogleBean" /> <h2>Here are your search results«/h2> <%-- Задаем строку поиска, ограничение, и разделитель строк, устанавливая свойства объекта GoogleBean —%> < jsp: setProperty name»"gBean" property» "query" params "query"/» «jsp:setProperty name«"gBean" property»"restrict" params"restrict/> <jsp:setProperty name-rgBean" property»"lineSep" value»"<br /»<br />"/» < %— Теперь отображаем результаты поиска —%» < jsp: get Property name»"gBean" property» "searchResults" /> </c:otherwise» </c:choose» f «/body» </html> 718 | Глава 27. Использование Web API сайтов Google и Amazon
JSP использует стандартное действие jsp: set Property для установки значений свойств query, restrict и lineSep экземпляра компонента JavaBean. Свойство query представляет термы поиска; restrict может принимать значения mac, linux, bsd или unclesam, представляющие различные подсайты Google, и свойство lineSep определяет символ разделения строк, используемый при форматировании результатов (в данном случае <Ьг/>). И наконец, код использует j sp: get Property для вызова метода get- SearchResults () объекта GoogleBean, который отсылает SOAP-сообщение на сайт Google и форматирует получаемый ответ. См. также Домашнюю страницу Web-сервиса Google: http://www.google.com/apis/', SJDK для Web API Google: http://www.google.com/apis/download.html', рецепт 27.1 по подготовке программного окружения для использования Web API Google; рецепт 27.3 по использо- ванию сервлета для соединения с web-сервисами Google. 27.5 Подготовка к использованию API Web-сервисов Amazon Задача Вы хотите соединяться с Web-сервисами Amazon (AWS - Amazon Web Services), используя для этого сервлет или JSP. Решение Скачайте SDK для работы с Web-сервисами Amazon, получите от Amazon пароль разработчика (token) и создайте пакет Java-SOAP для взаимодействия с AWS. Обсуждение Процесс подготовки к работе с AWS происходит примерно следующим образом. 1. Скачайте AWS SDK с http://www.amazon.eom/gp/aws/download_sdk.html/002-2688331- 0628046. Этот файл kit.zip включает несколько примеров кода и документацию по API Web-сервисов Amazon в HTML формате. 2. Получите пароль разработчика с http://associates.amazon.com/exec/panama/associates/ join/developer/application.html/002-2688331-0628046. Подобно лицензионному ключу, который мы использовали црй работе с Web API Google, этот бесплатный пароль включает серию закодированных символов, которая должна содержаться в каждом взаимодействии между вашим Java-кодом и AWS. Подготовка к использованию API Web-сервисов Amazon | 719
3. Разработайте Java API для создания SOAP-запросов к AWS. В конечном итоге дол- жен получиться JAR-файл, содержащий классы, которые ваш сервлет или JSP будут использовать для создания SOAP-запросов. В заключительной части данного рецепта описывается, как ^генерировать такой JAR-файл, поскольку это многошаговый процесс. 1 Взаимодействие с AWS с использованием SOAP-сообщений - это только одна из возможностей, предоставляемых сайтом Amazon разработчикам. Другая включает кодирование запросов к web-сервисам в составе URL, что позволяет создавать AWS-запросы, используя HTTP (это называется «XML по HTTP»). В рецепте 27.7 приведен пример такого URL-поиска (полезного при отладке ваших SOAP-приложений). Если вы сохраните XSLT-файл в Web, AWS использует этот файл для форматирования ответа на'запросы «XML по HTTP». Детали ищите в документации из SDK. SOAP и Apache Axis Создание API Java-SOAP, позволяющего использовать AWS, начинается со скачивания набора инструментов для работы с SOAP под названием Apache Axis (open source - ПО с открытым исходным кодом) (http://ws.apache.org/axis/). Ниже перечислены шаги по сдз- данию требуемого API. 1. Скачайте Axis и распакуйте полученный ZIP-файл в любой выбранный вами каталог (это приведет к созданию в нем подкаталога axis-l_l). 2. В каталоге axis-1 _l/lib находится несколько JAR-файлов. Поместите путь к этим JAR- файлам в свой путь к классам и запустите программу org.apache.axis.wsdl. WSDL2Java, которая сгенерирует исходные файлы на языке Java. Эти исходные файлы и составляют Java API, который вы будете использовать для работы с AWS, когда вы откомпилируете эти файлы. 3. Скачайте WSDL-файл (Web Services Description Language - язык описания web- сервисов), связанный с Web-сервисами Amazon. В момент написания книги этот файл можно было найти по адресу: http://sdap.antaZon.com/schemas3/AmazonWebServices.wsdl. 4. Приводимая чуть ниже командная строка генерирует пакет com. amazon. soap. axis для вашего Java API. Командные строки, приводимые в данном рецепте работают как для Windows, так и для UNIX-ориентированных систем. Данная командная строка сде- лана в предположении, что файл AmazonWebServices.wsdl находится в текущем ката- логе. Программа WSDL2Java генерирует Java-классы, основываясь на XML- элементах, описанных в XML-файле с WSDL (преобразование XML-Java). Это позво- ляет вам работать с AWS используя только Java-объекты, что замечательно и полностью окупает труды по созданию данных Java-классов! Здесь командная строка разбита на несколько строк исключительно для удобства чтения, и когда вы будете реально запускать ее, вся командная строка должна быть набрана в одну строчку java -ср .;lib/axis.jar;lib/commons-discovery.jar;lib/commons- logging.jar;lib/jaxrpc.jar;lib/saaj.jar;lib/wsdl4j.j ar org.apache.axis.wsdl.WSDL2Java AmazonWebServices.wsdl —verbose t —package com.amazon.soap.axis 720 | Глава 27. Использование Web API сайтов Google и Amazon
5. Эта командная строка генерирует исходные Java-файлы в дереве каталогов, отвечающем заданному имени пакета (com. amazon, soap. axis). Теперь вам нужно откомпилировать эти классы с помо£цью инструмента javac, см. следующую командную строку (текущий каталог содержит каталог сот). Еще раз, я разбил единую, командную строку на несколько отдельных строк исключительно для удобства чтения (при запуске ее нужно набирать как единую строку). javac -classpath . ;lib/axis. jar;lib/commons-discovery. jar;lib/commons- logging.jar;lib/jaxrpc.jar;lib/saaj.jar;lib/wsdl4j.jar com/amazon/soap/axis/*.java 6. Теперь заархивируйте вей эти файлы в JAR-файл. Находясь в том же каталоге, содержащем каталог верхнего уровня сот, выполните следующую команду, создающую JAR-файл amazonapi.jar (имя придумано произвольно). jar evf amazonapi.jar ./edm 7. Поместите файл amazonapLjar (или как вы его там назвали) в каталог WEB-INFAib. Остался заключительный шаг. 8. Убедитесь, что JAR-файлы, или библиотеки, от которых зависит пакет com. amazon. soap. axis теперь доступны web-приложению. Файл amazonopi.jar зависит от тех же библиотек Axis, которые вы добавили в путь к классам в предыдущих командных строках с j ava и j avac. Вы также можете добавить эти JAR-файлы в каталог WEB- INFAib (в противном случае ваш сервер приложений сделает все эти библиотеки дос- тупными вообще всем web-приложениям). А теперь просто для удовольствия, о том, где ваш Java-код получает возможность озна- комиться с книгами и всем остальным, находящимся на сайте Amazon, используя сервлеты. Ваши сервлеты должны использовать для этой цели пакет com. amazon. soap. axis. См. также AWS SDK http://www. amazon. com/gp/aws/download_sdk. html/002-2688331-0628046; Apache Axis: http://ws.apache.org/axis/; WSDL-файл для Web-сервисов Amazon: http://soap. amazon.com/schemas3/AmazonWebServices.wsdl. ' 27 .6 Создание компонента JavaBean для соединения c Amazon Задача Вы хотите создать компонент JavaBean как вспомогательный класс Для поиска по сайту Amazon., Решение Установите Amazon API, как описано в рецепте 27.5, а затем создайте компонент Java- Bean, использующий пакет com. amazon. soap. axis из этого API. Создание компонента JavaBean для соединения с Amazon | 721
I Обсуждение Компонент JavaBean с именем AmazonBean, приведенный в примере 27.5 импортирует пакет com. amazon. soap. axis. Этот пакет содержится в файле amazonapi. jar, который был сгенерирован в рецепте 27.5. Сохраните этот JAR-файл в каталоге WEB- INFAib web-приложения, a AmazonBean - в каталоге WEB-INF/classes (или также в JAR- файле, в WEB-INFAib). Компонент из примера 27.5 соединяется с Amazon в методе getSearchResults (). Результаты поиска AmazonBean форматирует и отображает в методе structureRe- sult s (). Комментарии в коде описывают детали происходящего. Пример 27.5. Класс компонента JavaBean, осуществляющего поиск по Amazon package com.jspservletcookbook; import java.net.URL; import com.amazon.soap.axis.*; public class AmazonBean { > //Пароль (token) разработчика private final static String AMAZON—KEY « "DCJEAVDSXVPUD"; //ЗАМЕЧАНИЕ: AWS версии 3 использует "http://xml.amazon.com/xml3" private final static String END_POINT = "http://soap.amazon.com/onca/soap"; private final static String AMAZON_TAG = "webservices-20"; private URL endpointUrl; private String lineSep = "\n"; private String totalResults; private String keyword; private String page; private String type; private String mode; public AmazonBean(){}//конструктор без аргументов //простой способ тестировать компонент вне сервлета public static void main(String[] args) throws Exception{ AmazonBean bean = new AmazonBean(); bean.setKeyword("Lance%20Armstrong"); beAn.setType("heavy"); bean.setMode("books"); bean.setPage("1"); > System.out.printlnf bean.getSearchResults() ); } //Структурируем результаты Поиска в виде строки 722 | Глава 27. Использование Web ДР1 сайтов Google и Amazon
public String вtruetureResult(ProductInfo info){ //Поиски no Amazon возвращают объекты ProductInfo, которые //содержат массив объектов Details. Объект Details //представляет отдельный результат поиска Details[] details info.getDetailsO ; String results « ""; J //В объекте Details злаьдой найденной книги | //содержится массив авторов книги String[] authors null; String usedP ® null;//объект UsedPrice String rank = null;//объект SalesRank //для каждого элемента, возвращенного поиском... for (int i = 0; i < details.length; i++){ • -1 if(mode ! null && mode.equals("books")){ authors " details[i].getAuthors(); } //Включаем имя товара «results ♦« "<strong>"+(i+l)+". Product name:</strong> " + details[i].getProductName() + lineSep; //Если это книги, включаем именя всех авторов if(mode 1= null && mode.equals("books")){ for (int j = 0; j < authors.length; j++){ results += "Author name "+(j+l)+": " + authors[j] + lineSep; }//for / }//if usedP details[i] .getUsedPriceO///получаем цену // товара, бывшего в употреблении rank в details [1] .getSalesRankO;//получаем рейтинг продаж results + "Sales rank: " * (rank == null ? "N/A" rank) + lineSep +"List price:" + details[1].getListPriceO + lineSep * "Our price: " + details [i] .getOurPriceO + lineSep + "Used price: " + (usedP == null ? "N/A" : usedP) + lineSep + lineSep; } return results; }//structureResult //Соединяемся c Web-сервисами Amazon, затем вызываем structureResult() public String getSearchResults() throws Exception{ Создание компонента JavaBean для соединения c Amazon | 723
endpointUrl new URL(END POINT); AmazonSearchService webService = new AxnazonSearchServiceLocator(); //Соединяемся c AWS * AnazonSearchPort port webService.getAmazonSearchPort(endpointUrl); KeywordRequest request getKeywordRequest( ) ; //Возвращаем результаты поиска ProductInfo prodlnfo = port.keywordSearchRequest(request); //Устанавливаем значение totalResults no результатам поиска setTotalResults( prodlnfo.getlotalResults() ); //Структурируем и отображаем результаты поиска книг return struetureResult(prodlnfo); }//getSearchResuits //Методы получения и установки значении свойств (Setter и getter)».. public void setLineSep(String lineSep){ this.lineSep=linesep; } public String getLineSep(){ return lineSep; } x //Объект KeywordRequest инициализируется термами поиска, режимом, числом //страниц, которое нужно вернуть, типом ('lite* или 'heavy'), и //паролем разработчика. public KeywordRequest getKeywordRequest(){ KeywordRequest request new KeywordRequest(); request.setKeyword(keyword);//термы поиска request.setMOde(mode);//режим, например книги ('books') request.setPage(page);//число страниц, которые нужно вернуть request.setType(type);//тип, 'lite* или 'heavy* request.setDevtag(AM' ZON_KEY);//пароль разработчика request.setTag(AMAZON_TAG);//тег, 'webservices-201 return request; } public void setKeywprd(String keyword)f this.keyword = keyword; } public String getKeyword(){ return keyword; I public void setMode(String mode){ this, mode = mode; } 724 | Глава 27. Использование Web API сайтов Google и Amazon
public String getModel){ return mode; } public void setPage(String page){ this.page - page; • ) public String getPage(){ return page; } public void setType(String type){ this.type = type; b public String getType(){ return type; } public void setTotalResults(String results){ totalResults = results; }' public String getTotalResults(){ return totalResults; } } //ArtiazonBean Этот компонент имеет метод main (), который позволяет тестировать его из кшавд- ной строки. Ниже приведен код из этого метода, в котором создается экземпляр компо- ч нента JavaBean, осуществляется поиск книги, используя термы поиска Lance Armstrong» и отображаются некоторые результаты. AmazonBean bean = new AmazonBean(); bean.setKeyword("Lance%2 OArmstrong”); bean. setType("Heavy"); bean.setMode("books"); bean.setPage("1"); System.out.printlnf bean.getSearchResults() ); Перед запуском этого компонента из командной строки, убедитесь, что включили все необходимые библиотеки, связанные с Axis, в ваш путь к классам '(classpath) ^см. рецепт 27.5). Ниже приводится командная строка, которая запускает компонент для проверки его работы. Обратите внимание, что командная строка включас г файл ата- zonapi. j аг, сгенерированный в рецепте 27.5. > java -ср .;jaxrpc.jar;axis.jarjamazonapi.jar;commons-logging.jar;commons- discovery .jar;saaj.j ar com.j spservletcookbook.AmazonBean Создание компонента JavaBean для соединения c Amazon | 725
Если вы присвоите опции type (тип) значение heavy (расширенный) (вместо lite - . облегченный), то поиск вернет также рейтинг продаж книги на сайте Amazon. 7 $ Облегченные (lite) SOAP ответы не включают значение рейтинга продаж. См. также AWS SDK http://www. atnazon.com/gp/aws/download_sdk. html/002-2688331-0628046', рецепт 27.7 по совместному использованию сервлета и компонента JavaBean для соеди- нения с AWS. 27.7 Использование сервлета для соединения с Amazon Задача Соединиться с AWS, используя сервлет. Решение Для выполнения задач, связанных с AWS, используйте специально созданный для этого компонент JavaBean. Обсуждение Сервлет из примера 27.6 построен так же, как сервлет, использованный дтя соединения с Google, так что его код покажется вам очень знакомым. В ответ на HTTP-запрос GET, сервлет генерирует HTML-форму, в которую пользователем вводятся термы поиска на Amazon, и отсылаются этому же сервлету. Интересные действия имеют место в методе doPost (), где сервлет использует класс AmazonBean (из рецепта 27.6) для соединения с AWS, а также отображает результаты поиска. Пример 27.6. Сервлет, использующий компонент JavaBean для соединения cAWS package com.j spservletcookbook; import java.io.lOException; import java.io.Printwriter; inport jaVax.servlet.*; import javax.servlet.http.*; public class AmazonServlet extends HttpServlet { public void doGet(HttpServletRequest request, 726 | Глава 27. Использование Web API сайтов Google и Amazon
HttpServletResponse response) throws ServletException, java.io.lOException { //Задаем MIME’тип ответа "text/html* response.setContentType("text/html"); java.io.Printwriter out = response.getWriter(); //Начинаем формирование HTML-содержимого out.printIn("<html><head>"); out.printIn( "<title>Initiate sin Amazon Book Search</titlex/headxbody>"); out.println("<h2>Please enter your Amazon search terms</h2>"); //Отображаем HTML-форму, которая посыпает запрос обратно сервлету //’/amazonservlet', что приведет к вызову метода doPostO //Убедитесь, что method»"POST", чтобы метод service сервлета //для обработки данных, введенных в форму вызвал doPost . out.println( "<form method=\"POST\" action =\"" + request.getContextPath() + */amazonservlet\" >"); out.println(h<table border»\"0\"xtrxtd valign=\"top\">"); out.println("Search terms: </td> <td valign=\"top\">"); out.println("<input type=\"text\* name»\"query\" size=\"15\">"); out. println (" < / tdx / tr >") ; out .printin ("<trxtd valign=\"top\">"); out.println( "<input type=\"submit\" value»\"Submit Info\"x/tdx/tr>"); out. print In (" < / tablex / form> "); out .printIn ("</bodyx/html>") ; } //doGet public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException,java.io.IOException{ String query = request.getParameter("query"); boolean isValid = (query == null || query.length() < false : true; response.setContentType("text/html"); java.io.Printwriter out = response.getWriter(); out. print In (" <htmlxhead> "); i out .printin ("<title>Amazon book results</title></headxbody>") ; if (1 isValid){ Использование сервлета для соединения с Amazon | 727
cut.println ( "<h2>Sorry, the query parameter was either empty or null</h2>"); } else { AmazonBean amBean n new AmazonBeanO ; amBean. setKeyword (query); amBean. setType ("lite"); amBean. setMode ("books"); amBean.setPage("1"); amBean.setLineSep("<br />"); out.println("<h2>Here are your search results</h2>"); try { , out.println( amBean.getSearchResults() ); } catch (Exception e){ out.println( "The search terms either returned zero results "+ "or were invalid.")-; } } out.println("</body></html>"); }//doPc5st ' }//AmazonServlet Чтобы сделать код проще, поиск по ключевым словам ограничен только поиском книг. AWS предлагает мощный метод поиска по нескольким каталогам сайта, однако он рабо- тает, если API не ограничивать только поиском по ключевым словам среди книг. К примеру, среди предлагаемых товаров также присутствует DVD, электроника, музыка, компьютерные комплектующие, программы и игрушки. Вы также можете инициировать несколько других типов поиска (в дополнение к поиску по ключевым словам), например ASIN поиски (Amazon Standard Item Number - стандартный номер товара Amazon). На рис. 27.3 показан результат работы,метода doGet (). На рис. 27.4 показаны фрагменты результатов, отображаемые сервлетом, сформирован- ные в методе doPost (). После того, как вы скачали и распаковали AWS SDK, вы можете найти программную документацию в Amazon WebServices/APl Guide/index.html. Шри отладке своих сервлетов и AWS-поисков, вы можете инициировать AWS-поиск, задавая в браузере URL специального вида. Ниже приведен пример URL, инициирующего поиск по ключу книги, где терм поиска задан так: «British Empire». http://xml.amazon.com/onca/xml?v=l.0&t=webservices-20& dev-t=DCJEAVXSDVPUD&KeywordSearch=British%2OEmpire&mode=books& type=lite&page=l&f=xml 728 | Глава 27. Использование Web API сайтов Google и Amazon
Рис. 27.3. HTML-форма сервлета, для ввода термов поиска по сайту Amazoe Запрос по этому URL возвратит XML-файл, который выглядит примерно так, как пока- зано в примере 27.1. Рейтинг продаж обозйачен как «N/А», поскольку была задана опция поиска type=lite и в результате поиска для рейтинга было возвращено значение null. Чтобы получить значение рейтинга продаж сайта Amazon, используйте type=heavy. См. также . AWS SDK http://www.amazon.com/gp/aws/download_sdk.html/002-2688331-0628046', книгу «Web Services Essentials» (O'Reilly); рецепт 3.1 по отображению сервлета на имя в web.xml\ рецепт 27.8 по совместному использованию JSP и компонента JavaBean для соединения с AWS. 27.8 Использование JSP для соединения с Amazon Задача Соединиться с AWS с помощью JSP. Решение С помощью стандартного действия j sp: useBean, создайте экземпляр класса Ama- zonBean из рецепта 27.6. Используя этого экземпляр, управляйте AWS-поиском и его результатами. Использование JSP для соединения с Amazon | 729 1
Amazon book results Microsoft Internet Explorer Favjiter |AFIDui I Beta Chai Search Back Р'йгрй Stoo Rehest Home Address.]^] http:/AxMhcMt8D80/homeZariazon*ervlet History Here are your search results 1. Product name: Empire: The Rise and Demise of the British World Order and the Lessons for Global Power Author name 1: Niall Ferguson Author name 2: Neil Ferguson Sales rank N/A List price: $35.00 Our price: $24.50 Used price: $16.75 2. Product name: Captives: Britain. Empire, and the World. 1600-1850 Author name 1: Linda Colley Sales rank N/A List price: $27.50 Our price: $19.25 Used price: $9.29 3. Product name: The Last Empire: Photography in British India. 1855-1911 Author name 1: Earl Mountbatten Author name 2: Ainslie Embree Author name 3: Clark Worswick Author name 4: Ainsley Embree Sales rank N/A List price: $39.95 Our price: $39.95 Used price: $31.03 Puc. 27.4. Результаты поиска книг на сайте Amazon Обсуждение В этом рецепте используется та же стратегия, что и в JSP из рецепта 27.4:. создайте экземпляр компонента JavaBean, который осуществляет AWS-поиск, и отобразите результаты этого поиска. Стандартное действие jsp:useBean, создает экземпляр класса com. jspservletcookbook.AmazonBean, который находится в каталоге WEB-INF/classes. 730 | Глава 27. Использование Web API сайтов Google и Amazon
Затем в коде, с помощью j sp: set Property, задаются несколько опций поиска, после чего используется j sp: getProperty для выполнения поиска. Чтобы -lepex ватить любые прерывания, выброшенные в методе getSearchResults () класса Amazon- Bean, в примере 27.7 используется JSTL-тег с: catch. Переменная except имеет тип Throwable, и се сообщение об ошибке отображается тегом с: out в случае, если поиско- вый запрос^ был некорректным. Страница JSP из примера 27.7 отображает HTML-форму и результаты поиска, показанные на рис.-27.3 и 27.4. Код j sp: getProperty эквивалентен вызову метода getSearchResults () класса AmazonBean, который возвращает строку (String) с отформатированными результатам# поиска. Пример 27.7. Страница JSP, запускающая AWS-поиск книг <%в taglib uri""http://java.sun.com/jstl/core" prefix»"c" %> <html> <head><title>Search Amazon.com for a Book</title></head> <body> <c:choose» ' <c:when test""§{empty param.keyword}"» <h2»Please enter your Amazon search terms.. .</h2> " <%— Отображаем HTML-форму. .*. —%> Kfor^i method""POST" action * *<c:out value*11 $ {pageContext. request. contextPath}" /»/amazon.jsp'» <%— теги form и table... —%> ctable border="OM><tr><td valign="top"> Search terms: </td> <td valign="top"> <input type="text" name="keyword" size="15"> </td></tr><trxtd valign="top"> <trxtd valign="top"> <input type="submit" value="Submit Info"x/tdx/tr> < / tabl ex / f orm> </bodyx/html> </с:when» <c:otherwise» <jsp:useBean id»"aBean" class""com. jspservletcookbook.AmazonBean" /> <jsp:setProperty name*"aBean" property»"keyword" param""keyword"/» <jsp:setProperty name="aBean" property»"mode” value»”books"/> <jsp:setProperty name»"aBean" property*"page" value»"1"/» <jsp:setProperty name*"aBean" property*"type" value*"lite"/> <jsp:setProperty name*"aBean" property*"lineSep" value*"<br />"/> Использование JSP для соединения c Amazon | 731
<h2>Here are your search results*/h2> <c:catch var*"excep"> <%-- Теперь отображаем все результаты поиска --%> <jsp:getProperty пате*"аЕъап" property*"searchResults" /> </cscatch > <%-- Печатаем любые сообщения об ошибках, например, 'Bad Request1, если термы поиска - бессмысленны —%> <c:out value*"${ехсер.message}"/> </c:choose> <7body> См. также AWS SDK http://v,\^,.amawn.com/gp/aws/download_sdk.html/002-2688331-0628046', кишу «Web Services Essentials» (O'Fcilly); рецепт 3.1 по отображению сервлета на имя В web.xml', рецепт 27.7 по совместному использованию сервлета и компонента JavaBean для соединения с AWS; главу 23 по использованию JSTL. 732 | Глава 27. Использование Web API сайтов Google и Amazon
Предметный указатель # "выскабливание" информации из web-страницы (см. пожинание web-информации) блоки сценария (JavaScript), 290 ${ } ограничители EL-выражений, 610 инкапсуляция вызова функции, 533 разыменование переменных и значение свойства, 266 % в шаблонах преобразования, 334 (см. HTTP-коды возврата) (см. MIME-типы) (см. PDF-файлы) * (звездочка) ♦* шаблон в элементах Ant, для нуля и более каталогов, 99,108 групповой символ в шаблонах URL, 67 . (точка) в именах атрибутов. 610 задающая текущий каталог, 25 . (точка) оператор, 266 .jsp, файлы, 20 •jspx файлы, 8,116,124 .swf, расширение файла, 415,418 / (прямой слеш) /• в шаблоне URL, 67 для запускающего сервлета (invoker), 72 для сервлета-контроллера, 74, 77 перезаписывается сервлетом-контроллером, 76 в шаблонах URL, 65,69 исключение из расширенного отображения, 69 < !— —>, комментарии в файле server.xml на Tomcat, 353 < %-- --%> комментарии на странице JSP, 21 < % %>, в скрнптлетах JSP, 522 <%@.синтаксис, директивы JSP, 20,126 401 код состояния HTTP - Недостаточно полномочий,357 403 код состояния HTTP - запрещено, 469 404 код состояния HTTP - не найдено, 215 500 код состояния HTTP - внутренняя ошибка сервера, 215 \ А abort( ) (LoginModule), 369 activationjar архив, 472 ActiveX, элемент управления, Flash-файл, встроенный в JSP,браузером Internet Explorer, 418 addCookie( ) (HttpServletResponse), 234 Ant, инструмент, 90-114 JAR-файлы Tbmcat, включение в путь к классам Ant, 96-99 online-руковрдство для, 33 WAR-файлы, создание. 103-106 запуск приложения Tomcat с помощью, 109-112 компиляция и создание WAR-файлов с помощью, 25 компиляция классов сервлетов, 22 компиляция сервлета с использованием сборочного файла, 100-1]03 остановка приложения Tomcat с помощью, 112-114 получение и установка, 91 JAXP-совместимый синтаксический анализатор XML, 91 скачивание двоичного или исходного дистрибутива, 91 развертывание web-приложения на Tomcat, 44-50 файл build.properties (пример), 46 файл build.xml (пример), 46 50 - этапы процесса, 45 развертывание web-приложения на WebLogic Server 7.0,50 развертывание одиночного сервлета на Tomcat, 29-34 развертывание Сервлетов на сервере WebLogic Server 7. 0,37-41 создание JAR-файлов, 106-109 файл build.xml, 33 файл globaLproperties, 33 цели, использование, 93-96 выполнение нескольких целей в определенной последовательности, 94 файл build.xmlfile, выполнение, 93 ANT_HOME переменная окружения, 92 ' antcail задача, 49 antcall элемент, 34 Apache Ant (см. Ant, инструмент) Xerces2 синтаксический анализатор XML, 91 Предметный указатель | 733
Apache Jakarta Project библиотека тегов Standard 1.0 taglib, 586 дистрибутив log4j, скачивание c, 326 ' справочная реализация JSTL, 587 Apache Software Foundation, библиотека Log4j, 445 Apache Software, лицензия, 326 API сервлета версии 2.3 дескриптор развертывания, конфигурирующий страницы ошибок (error page), 215-218 сеансы, настройка в web.xml, 28 файл web.xml, 27 (см. также web.xml) API сервлета версии 2.4 web.xml, использование в Tomcat 5 и JSTL 1.1,590 файл web.xml, 27.624 . (см. также web.xml) фильтры, использование с RequestDispatchers, 459 API сервлета, документация, 15 appBase, каталог (Tomcat, 'файл server.xml), 50 appender вывода в чередующийся файл (rolling file appender), 333 appender, 324 ConsoleAppender, 328 rolling Ole appender (чередующийся файл), 333 добавление к корневому logger'y, 330-332 использование шаблона с, 332-336 наследование, 346 спецификация разметки (layout), 349 Apple Computer, видео-ролйки QuickTime, 421 ArithmeticException, класс, 220 attribute, директива, 579,580 attributeAdded() (HttpSessionAttributeListener), 280 attributeRemoved() (HttpSessionAttributeListener), 280 attrjbuteRepIaced() (HttpSessionAttributeListener), 280 auth-constraint, элемент (web.xml), 83 роли в, 356 auth-method, элемент (web.xml), 355 аутентификация на основе формы, 359 значения для, 85 available, задача (Ant, build.xml file), 48 В Base64 механизм кодирования содержимого, 355 basedir, атрибут задача jar, Ant, 108 элемент project, Ant, 93 BasicConfigurator, класс. 327 BASIC-аутентификация, 84 аутентификация на основе формы, использование с, 358 использование с web-приложениями па. Tomcat, 354-356 элементы web.xml для инициации, 356 BEA WebLogic (см. WebLogic) body тег (HTML), 416 BodyPart, класс, 489,493,495 BodyTagSupport,класс, 339,554 BufferedlnputStream, класс, 310 build каталог (сборочный), 23 включение вложенных каталогов в WAR-файл с помощью задачи Ant, 48 build.properties, файл (Ant), 46 build.xml, файл(см. сборочные файлы) развертыване web-приложения на WebLogic Server 7.0,51 Builder (WebLogic), xv c С язык, функция printf, 334 c:chooserer, 612 c:forEach тег, 173,586 итерации, выполненные с помощью, 590 перебор значений отображения (тар), 247 перебор значений отображения (Мар), хранящего заголовки запросов, 613 c:if тег использование в JSP (пример), 592 проверка выражений для условного выполнения кода, 590 c:import тег, 164 включение модуля JavaScript в JSP, 293 импорт сборочного файла Ant в JSP, 595 испол ьзование в JSP для доступа к закрытым ресурсам, 166 модуль JavaScript, импорт в JSP, 293 c:otherwise, тег, 612 c:out, тег, 21,158,586 вывод данных, введенных в форму, с помощью компонента JavaBean, 476 вывод значений'отдельных параметров, 175 вывод свойств компонента JavaBean, 622 информация об исключении, отображение для JSP, 229 использование в JSP (пример), 592 краткое перечисление функций, 590 передача значения атрибута request_uri в, 226 символы, выводимые с помощью escape-последовательностей, 593 c:param, теги, включение значений параметров в JSP, 166 c:set, тег, 586 привязка объекта к области видимости приложение (application), 386 сводка функций, 590 установка переменной в области видимости сеанс (session), 609 ' c:when, тег, 612 .. CallableStatement, класс, 525-529 executeUpdate(), 529 сервлет, используемый для вызова хранимой процедуры, 526-528 callback (обратный вызов) ParserDelegator, использование с, 690 класс для for анализа web-страниц, 688-691 определение, 688 CallbackHandler, класс, 366,370,375 734 | Предметный указатель
Caucho Resin (исполнительная подсистема для сервлетов), xv cbcktlog (пользовательский тег JSP), 337.341' CDATA, секции в XML-файлах, использование для прохождения теста на корректность формата, 164 CGI (Common Gateway Interface - общий шлюзовой интерфейс), 14 CheckEmail, функция, JavaScript, 289 classes, каталог, 24,42 TLD и, 564 задание классов в задаче war инструмента Ant, 48 сервер WebLogic 7.0. размещение класса сервлета в, 36 файл iog4j.properties, 330-331 classes, элемент(Ап1), 105 classpath (путь к классам) в файле build.xml, для JAR-файлов в каталогах Tomcat, 33 классы, связанные с почтовыми сообщениями, включение в. 472 путь к классам Ant, включающий JAR-файлы Tomcat, 96-99 CLASSPATH, переменная окружения, прекомпиля- ция JSP и, 118 classpath, элемент (Ant), 97 вложенный в задачу javac, 98 Clock, класс (пример апплета), 409 ссылка на, встроенный в JSP, 412 close() класс InitialContext, 661 класс PrintWriter, 148 Collections, класс, synchronizedMap(), 385 - com.oreilly.servlet, библиотека класс LocaleNegotiator, 631 класс MultipartFilter, 209 классы для выгрузки файлов, 198 com.oreilly.servletmultipart, пакет, 207 comment, атрибут (cookies), 233 Common Gateway Interface (CGI), 14 configure() (BasicConfigurator), 328 configure() (Propertyconfigurator) задание имени файла log4j.properties, 342 Connection, класс commit!), 539,544 getMetaData( ),.5Д2 getTransactionlsoIatiOn!). 544 . листинг уровней изоляции, 544 rollback!), 539,544 setAutoCommit!), 539 Connector, элемент (server.xml), 353 ConsoleAppender, класс, 330 Content-Disposition, заголовок, 196,309 Content-Length, заголовок, 310 contenttype, атрибут (элемент ‘ jsp:directive.page), 128 Content-Type, заголовок, 196 application/msword, 311- MIME-типы. 306 Context, элемент (файл server.xml сервера Tomcat), 34,49 contextDestroyed() (ServletContextListener), 345 contextlnitialized (ServletContextListener), 345 Contextobject, класс (пример), 384 context-param, элемент (web.xml) возраст cookie; значения для, 236 контекст локализации, установка, 642,652 обеспечение пути для файлов, включаемых в JSP, 160 параметр dataSource для соединения с базой данных. 603 ресурсы интернационализации, 645 страница JSP, открывающая всплывающее окно, 299 cookie, 232-250,347 доступ с помощью EL, 616 запрет пользователем, 234 необязательные пары имя/значение, 233 определение, 232 отслеживание сеанса, использование, 251 запрет cookie,.269 пара имя/значение. 233 перезапись или удаление существующих cookie, 249 удаление пользователем, 237 чтение значений в JSP. 246-248 чтение значений в сервлете, 243-245 cookie, 233 Cookie, заголовок запроса, 245 Cookie, класс(продолжение) setMaxAge! >,236.241 значение аргумента, равное нулю, 249 setVa!ue(), 241 методы для установки и считывания значений атрибутов, 245 Cookie, класс, 234 getName! / и getValue!), 238,244 g<JtPath(). 245 cookie, неявно создаваемый объект (JSTL), 246,616 cookie, установка с помощью jspzsetProperty, 239 сору, задача (Ant), 93 Copyright, сервлет (пример), 139 cos.jar, файл, 200 create-jar, цель (Ant), 107 create-war, цель (Ant, файл build.xml), 48 Create Window, функция, JavaScript, 290 открытие нового окна браузера с помощью сервлета, 294-296 D DatabaseMetaData, интерфейс, 552 DataHandler, класс, 499 DataSource (источник данных) DataSource на WebLogic, использование в JSP. 522-524 getConnection!), 515,519 JNDI, доступ с, 655 использование в сервлете на Tomcat, 512-515 настройка в web.xml для JSTL-тегов работы с SQL, 603-604 настройка в web.xml. 546 Предметный указатель | 735
настройка для сервлета в Tomcat, 509 создание объекта DataSource на WebLogic, 515-518 настройка. шаги по. 516 явная установка для JSTL-тегов работы с SQL в JSP, 607-608 DataSourceLoginModule, 370,376 Date, класс, getTime(), 592 DateFormat, класс, 601,643 format( X 264 getDateTimeInstance(), 644 dateStyle и timeStyle, атрибуты (fint:formatDate), 601 DEBUG, уровень протоколирования, 323,329 в корневом logger, 330 default, атрибут, (элемент project, Ant), 93 DefaultFileRenamePolicy, класс, 207 компонент JavaBean для выгрузки файлов, использование в, 211 depends, атрибут (элемент target инструмента Ant), 94 deploy-application, цель (Ant, файл build.xml), 48 отредактированный для развертывания WebLogic 7.0,51 Deployer, ynuiHTa(WebLogic), 60-62 deploy-servlet, цель (Ant, файл buikLxml), 33,40 description, атрибут(Ат элементы target), 94 destfile, атрибут Ant, задача jar, 108 Ant, задача war, 105 dir, атрибуты Ant, элемент classes, 105 Ant, элементы fileset, 98 dispatcher, элементы (web.xml), 459,460 displayMessage( ) AttachBean, класс (пример), 489 EmailBean, класс (пример), 487 Header Accessor, класс (пример). 501 MailAccessor класс (пример), 482 doCatch() (TryCatchFinally), 568 docBase, атрибут (элемент Context, Tomcat), 50 doEndTag(), 556,568 doFilter!), 283 заблокированные запросы. 453 изменение ответа сервлета. 456 использование в фильтре класса HttpServletResponseWrapper, 458 класс FilterChain, 432 приведение параметра запроса, 286 doFina!ly() (TryCatchFinally), 568 doGet( ) encodeRedirectURL, использование в, 274 FirstServlet класс (пример), 15 - выходная информация, отображаемая в браузере, 18 HttpServlet. класс. 15 включение содержимого в, 138 компонент JavaBean, используемый для отправки почты, 478 PrivacyServlet, класс (пример), включение ресурса, заданного параметром инициализации, 141 метод сервлета, на который отображен фильтр. 286 сервлет, доступ к базе данных с помощью JDBC, 506 сервлет, использующий компонент JavaBean для отправки почты. 480 сервлет, использующий компонент JavaBean для работы с почтовыми вложениями, 493 domain, атрибут (cookies), 233 doPost( ) FirstServlet, класс (пример), 15 выходная информация, отображаемая в браузере, 19 HttpServlet. класс, 15 включение содержимого в, 138 HTTP запроси POST, в ответ на, 169 отслеживание количества перерисовок страницы у пользователя, 437 doStartTag(), 556,568 doTag(),571 упрошенные обработчики тегов, 576 DriverManager, класс, 506 DTD (Document type Definition - определение типа документа), 27 JSP1.2,561 XML-Схема и, 562 в TLD-файле JSP 1.2,585 порядок элементов в web.xml (API сервлета 2.3), 511 Е EAR файл (Enterprise Application Archive - архив приложения предприятия), 53 EAR-файл (Enterprise Application Archive - архив приложения предприятия), 53 echo, задача (Ant), 93 EIS (Enterprise Information System - информационная система предприятия), xiv EIS (Enterprise Information Systems - информационные системы предприятия), xiv EJB (Enterprise JavaBean), 656 доступ к компоненту EJB на WebLogic из сервлета, с использованием JNDI, 678-686 дескриптор развертывания (ejb-Janxml), 682 дескриптор развертывания, специфичный для производителя. 683 сеансовый компонент не сохраняющий состояния (пример). 679-681 сервлет (пример), 684-686 ejb-jar.xml, файл, 682 его содержимое, отображение, 683 EL (Expression Language - язык выражений), xvi вызов компонента LoginBean EL (см. язык выражений) email (почтовое сообщение), 472-504 JavaBeans Activation Framework (JAF) (инфраструктура активации компонентов JavaBean), 471 JavaMail API, 471 добавление вложений к почтовому приложению в сервлете, 495-500 доступ из сервлета с использованием компонента JavaBean. 487 . доступ из сервлета. 481-486 отправка из сервлета с помощью компонента JavaBean, 477-481 736 | Предметный указатель
EmailBean (пример), 478-480 задание частей почтового сообщения с помощью методов компонента JavaBean, 480 сервлет, использующий JavaBean для отправки почтового сообщения, 480 отправка из сервлета, 473-476 взаимодействие сервлета с почтовым сервером, 476 получение в сервлете, работа с вложениями в, 488-494 размещение классов, относящихся к, в путь к классам (classpath), 472 чтение заголовков полученного почтового сообщения в сервлете. 500-504 email-адреса, верификация при вводе в форму, с использованием JavaScript, 290 embed, теги (HTML), 409,410 автоматическое генерирование HTML-файла с помощью Flash, 415 которые создает HTML-конвертор, 413 encodeRedirectURL (HttpServletResponse), 274 encodeURL() (HttpServletResponse), 272 Enterprise JavaBean (см. EJB) entrySet() (Map), 172,194 Enumeration, тип, 634 ERROR, уровень протоколирования, 323 ErrorData, класс, 230 errorPage, атрибут (директива page), 228 error-page, элемент (web.xml), 214,225 конфигурирование страниц ошибок, 215-218 escapeXml, атрибут (тег c:out), 592 EventObject, класс, 282 exception, неявно создаваемый объект, 229 exception-type, элемент (web.xml), 217 exclude element (Ant), 98 executeUpdate() (CallableStatement), 529 expires, атрибут (cookies), 233 Extensible Stylesheet Language (XSL), 597 Extensible Stylesheet Language Transformations (XSLT), 313 external-include, свойство, 158 F FATAL, уровень протоколирования, 323 FileDataSource, класс, 499 FilerConfig, класс getlnitParameter(), 451 FileRenamePolicy, интерфейс, 207 fileset, элементы (Ant), 98,105 вложенные в задачу jar. 108 множественное вложение в элементе path, 99 Filter, интерфейс, 283 действия, которые могут совершать фильтры, 442 filter, элементы(угеЬ.хт1), 443,667 дочерний элемент init-param, 451 FilterChain, класс doFilter(). 432.453,456 FilterConfig, класс getlmtParameierNamest), 453 filter-mapping, элементы (web.xml), 443,667 вложенный элемент url-pattem. 446 изменение порядка, 450 отображение нескольких фильтров на сервлет, 448 применение фильтра к сервлету с использованием RequestDispatcher, 459 Flash-файлы встраивание в сервлет, 419 шаблон HTML для встраивания в JSP автоматическая генерация, 415 написание, 418-419 fmt:formatDate тег, 645 fmt:f ormatDate, тег, 645 текущая локаль, использование для форматирования дат и чисел. 601 fmt:formatNumber тег, 601 форматирование денежной суммы для некоторой локали, 648 fmtmessage тег, 645 fmt:setBundle тег, 642 fmt:setLocale тег, 601 header, неявно создаваемый объект, 431 sqkquery тег, 547 605 sqksetDataSource тег, 607-608 sql:transaction тег, 546 sqkupdate тег, 547 SQL-теги, 522 без конфигурирования источника данных DataSource, 607-608 с конфигурированием источника данных DataSource, 603-606 url пользовательское действие, 269 272 x:out тег, 586 x:parse тег, 595 x:transform тег. 586.598 XML и XSLT теги, использование. 597-599 XML-теги ядра, использование в JSP, 594-597 в странице ошибок JSP, 226.229 заголовки запроса, доступ с помощью EL, 613 значение определенного заголовка, 615 контекст локализации для тегов. установка в web.xml. 652 параметры запроса доступ с помощью EL, 61Г переменные с разными областями видимости, доступ с помощью EL, 608 свойства компонента JavaBean, доступ с помощью EL, 618-622 страница JSP, использующая объект Result сохранен- ный как атрибут сеанса, 537 форматирование времени создания сеанса и времени последнего доступа. 266 х форматирование даты/имени сеанса с помощью, 268 форматирование чисел в виде процентов, 651 форматирующие теги, 600-602 отображение в JSP текста в соответствии с локалью клиента. 642 функции, использование в JSP, 623-627 функциональность, 586 fmt:formatN umber, тег отображение значения денежной суммы для некоторой локали, 648 Предметный указатель | 737 24-1S4S
отображение числа в виде процентов, 651 текущая локаль, использование для форматирования чисел, 601 fmt:message, теги, 645 fmt:setBundle, тег, 642 fmt:setLocale, ter, 601 fn:contains(), 623 fn:length( ), 623 fn:split(), 623 fn:toUpperCase(), 623 Folder, класс, 471,489 form, теги(НТМЬ), 195 DateFonnat класс, .264 format)) NumberFormat класс, 647 649 атрибут action, 463 атрибуты method, action, и enctype для выгрузки файлов, 196 form-login-config, элемент, 359 FORWARD значение (элементы dispatcher), 460 forward() (RequestDispatcher), 189 Front Controller, шаблон проектирования (Sun Microsystems), 74 function, тег, 533 G getAllHeaders() Message, класс. 501 Part, интерфейс, 500 getAttribute)) (ServletContext), 383 getBundle() (ResourceBundle), 157,638 getConnection') (DataSource), 515,519 getContent() (Message), 485 getContextPath() (HttpServletRequest), 234 getCookies() (HttpServletRequest), 238,243 getCurrency!nstance() (NumberFormat), 646 getDateTimelnstancet) (DateFormat), 644 getDisplayName)) (Locale), 631 getFilesystemName (MultipartRequest), 202 getFromi) (Message), 485 getHeader') HttpServletRequest, класс, 429 Part, интерфейс, 501 getHeaderNames)) (HttpServletRequest), 429 getID() (HttpSession), 265 getlnitParameter') (FilterConfig), 451 getlnitParameterNames)) (FilterConfig), 453 get!nputStream() PageData, класс, 134 Part, интерфейс, 493 get!nputStream() (PageData), 132 getJspBody)) (SimpleTagSupport), 573 getJspContext)) (SimpleTagSupport), 573 getLastAccessedTime)) (HttpSession), 261-264 getLocale() (ServletRequest), 629 getLocales)), 630 getLoginSuccess)), 379 LogiriBean класс (пример), 380 getMax!nactiveInterval() (HttpSession), 253 getMetaData') Connection, класс, 552 ResultSet, класс, 548 getName)) Cookie, класс, 238,244 Header класс, 501 getNamedDispatcher!) (ServletContext), 78 getParameter() (ServletRequest), 169,370 обработка присланных данных, 170 getParameterMap)) HttpServletRequest, класс, 185 ServletRequest, класс, 169,193 обработка присланных данных 170 getParameterMap(), 170-173 getParameterMap)), 193 getRequestDispatcher)), 139 меоды, используемые с методе doPost сервлета, 169 getParameterNames)) (ServletRequest), 169 getParameterValues() (ServletRequest), 169 getPath)) (Cookie), 245 getPercentlnstance)) (NumberFormat), 649 getQueryString HttpServletRequest, класс, 190 getQueryString)) обертки запроса, которые замешают, 434 getRealPath)) (ServletContext), 201 getRemoteAddr)) (HttpServletRequest), 467 getRemoteUser)) (HttpServletRequest), 363 getRequestDispatcher)) (ServletRequest), 139 getRequestURI) ) (HttpServletRequest), 440 getRequestURL() (HttpServletRequest), 190 getResultElements)) (GoogleSearchResult), 712 getRows)) (Result), 605 getSearchResults)), 712,722 getServletContext(), 385 getServletRequest)) (ServIetRequestEvent), 440 getSession) ) HttpServletRequest, класс, 257,258,259-261,262 HttpSessionBindingEvent, класс. 281 HttpSessionEvent, класс, 278 getSnippet)) (GoogleSearchResultElement), 712 getSource() (Eventobject), 282 getSummary)) (GoogleSearchResultElement), 712 getTitle)) (GoogleSearchResultElement), 712 getTYansactionlsolation)) (Connection), 544,544 getURL)) (GoogleSearchResultElement), 712 getValue)) Cookie, класс. 238,244 Header, класс, 501 HttpSessionBindingEvent, класс, 282 getValue)), 282 GET-запросы, 15 > ограничения безопасности, 356 ответ сервлета на, 169 globaLproperties, файл (Ant), 30 пример, 33,98 738 | Предметный указатель
Google, Web-сервисы, 707 опции поиска, установка, 712 подготовка к использованию, 708 соединение из JSP, 717-719 соединение из сервлета, 713-717 создание компонента JavaBean для соединения с, 709-712 GoogleSearch, класс (пример), 712 GoogleSearchResult, пример, 712 GoogleSearchResultElement, класс, 712 н handleMessages!) EmailBean, класс (пример), 487 MailAccessor класс (призер). 482 handleStartTag)),690 Hashtable, класс, 519 Header, класс, 501 header, неявно создаваемый объект, 431,613 headerValues, неявно создаваемый объект, 615 heading, атрибут (пользовательский тег), .567,577 home и local home, интерфейсы (компонент EJB на WebLogic), 682 Host, элемент (файл server.xml сервера Tomcat), 34,50 href, атрибут, 270 HTML form, тег, 195.292 обработчик события onSubmit, 294 head, тег, 292 input, тег, 195 атрибут src тега script, используемый для импорта модуля JavaScript, 292 классы API для синтаксического разбора, использование в сервлете, 691-696 « код для JSP который обрабатывает HTTP-запросы 20 компонент JavaBean для синтаксического разбора использование в JSP, 702-704 использование в серв лете. 700-702 создание, 697-699 преобразование XML-файла в HTML с помощью таблицы стилей XSL, 597 страница выгрузки файла, file upload page, - подготовка, 196-202 компонент для получения файла и сохранения его в локальном каталоге 200-202 страница ошибки (error page) отсылаемая клиенту. 223 шаблон для включения файлов Flash написание, 418-419 шаблон для встраивания файлов Flash автоматическое генерирование, 415 HTMLEditorKit, класс, 688 HTML-конвертор (Java Plug-in), 411-4.15 HTML-теги генерирование с помощью подключаемого модуля Java: HTML-конвертор тегов для загрузки апплета. 411 тег applet, 412 тег embed. 409,410 тег object, 409,410 HTML-формы верификация введенных данных с помощью фильтра. 462-467 страница JSP, содержащая форму {пример), 462 фильтр, проверяющий значения (пример), 464-465 добавление параметров строку запроса использование JSP. 191 использование сервлета. 189 обработка сервлетом, 15-19 FirstServlet. класс (пример), 15 передача данных, введенных в форму, программе, работающей на стороне сервера с помощью метода POST, 168 пересылка данных методом POST из JSP. 185-1 §8 перехват фильтром и чтение, данных, введенных в форму, 192-194 присвоение в JSP атрибуту области видимости значения параметра формы. 179-181 установка свойств компонента JavaBean значениями, введенными в, 175-178 HTTP 403 или 404, обработка web-контейнером, 217 500, возвращаемый web-контейнером, 217 SOAP-сообщения, построенные на базе XML й. 705 безопасные соединения (см. SSL (Secure Sockets Layer)) заголовки запроса (см заголовки запроса) заголовки ответов (см. заголовки ответов) запросы на выгрузку файлов, 195 коды возврата HTTP, 214 метод GET (см. GET-запросы) метод POST (см. POST-запросы) ограничения безопасности в методах, 83 параметр int метода sendError!), 221 разработка JSP для обработки Запросов, 20 HttpClient (Jakarta Commons), 122 автоматическая отправка данных из сервле га в другие программы, 182 выгрузка, 182 использование в компоненте JavaBean, который посылает данные из JSP, 186 сервлет, используемый для отправки (post) данных KJSP, 182 http-method, элементы, 83 ограничения безопасности для, 356 https://, URL, которые начинаются с, 353 HttpServlet, класс init(), включение содержимого в. 138 service(), 15 HttpServletRequest, класс getContextPath!). 234 getCookies!), 238,243 getHjadtr!), 429 getHeaderNames(), 429 getParameterMap!), 185 getQueryString!), 190 getRemoteAddr!), 467 getRemoteUser!), 363 getRequestURl!), 440 getRequestURL!). 190 Предметный указатель | 739 24*
getSessionf), 257,258.262 isUserlnRole(), 363 , проверка существования сеанса, 259-261 HttpServletRequest, объекты, 15 HttpServletRequestWrapper, класс, 433 HttpServletResponse, класс addCookiet ), 234 encodeRedirectURL!), 274 encodeURL(), 272 sendError(), 221-223,469 sendRedirect(), 261. 274 . заголовок ответа Refresh, добавление, 436 HttpServletResponse, объекты, 15 HttpServletResponseWrapper, класс, для использования в фильтре (пример), 457 HttpSession, класс getki ), 265 getLastAccessedTime!), 261-264 getMaxInactivelntervai!), 253 invalidate!), 362 removeAttribute(), 280 setAttribute!), 280 setMaxfnactivelnterval!), 257,259 HttpSessionActivationListener, интерфейс, 275 HttpSessionAttributeListener, интерфейс, 279 HttpSessionBindingEvent, класс getSession!), 281 HttpSessionBindingllistener, интерфейс, 276 HttpSessionEvent, класс, getSession(), 278 HttpSessionListener, интерфейс, 276 sessionCrcated() и уведомление о создании и разрушении сеанса, 278 I il8n (см. интернационализация) if/then/else операторы, 612 IllegalStateException, обработка в web-приложениях, 217 img, тег (HTML), 579 import, пользовательское действие, 153 indudet ) (RequestDispatcher), 138,289 верифицирующий JavaScript, включение в сервлет, 299 включение JavaScript-функции CreateWindow в сервлет, 294 включение файла сервлета Верхнего уровня. 144 встраивание Flash в сервлет, 419 сервлет, импортирующий JavaScript-файл, 290 include, директива в JSP-документе, разворачиваемая в XML-представлении, 128 включение XML-фрагмента в JSP-документ. 164 включение в JSP редко изменяемого ресурса, 149 действие jsp:include и ..., 150, 157 практика применения. 164 изменения, сделанные на включаемой странице, отражение во включающей странице, 157 INCLUDE, значение (элементы dispatcher), 459 include, элемент (Ant), 98 includes, атрибут(Л nt-задача jar), 108 IncludeServlet, класс (пример), 138 INFO, уровень протоколирования, 323 init, цель (Ant, файл build.xml), 33,95 InitialContext, класс, 512,659 close!). 661 lookup!), 515, 519 initialize() (LoginModule), 370 initParam, неявно создаваемый JSTL-объект, 298,299 init-param, элемент (web.xml), 451 input, теги (HTML), 195 атрибуты type, name, и accept, для выгру ikh файлов, 196 выгрузка Нескольких файлов, 198 Inputstream, XML-представление страницы JSP, возвращаемое в виде, 129,132 INSERT, оператор (SQL), выполнение в JSP, в составе транзакции, 546 Internet Explorer HTML-тег object, загрузка апплета с помощью, 410 встраивание Flash-файлов в web-компоненты, 418 языковые предпочтения, установка (версия 5.5), 630 InternetAddress, класс, 471,485 invalidate() (HttpSession), 362 invoke() (JspFragment), 573 invoker (вызывающий) сервлет, 71 перезапись для направления всех запросов сервлету-контроллеру, 74 регистрация сервлетов в web.xml, 72 lOException, класс, 18,200 web-приложения', обработка, 217 страница JSP выбрасывающая (пример), 228 IP-адреса, блокирование запросов с определенных адресов с помощью фильтра, 467-470 isErrorPage, атрибут(директива page), 228 ISO (International Standards Organization - организация по международным стандартам) коды стран, 640,646 коды языков, 629 isUserlnRole() (HttpServletRequest), 363 is-xml, элемент, 116 f iterated) (Set), 193 Iterator, класс, 172 next!), 193 J J2EE (Java 2 Enterprise Edition), xiv Core Blueprints web page, 74 JavaMail и JAF (JavaBeans Activation Framework - инфраструктура актмВации компонентов JavaBean), 471 компоненты EJB на бизнес уровне, 656 J2SE (Java 2 Standard Edition), 688 JAAS (Java Authentication and Authorization Ser- vice - Java-служба аутентификации и авторизации) LoginModule, класс, 365-371 Callbai kHandler для. 37Q пример кода. 366-369 online-документация. 370 using in a JSP, 377-381 740 | Предметный указатель
аутентификация клиентов сервлетЪ, 375,377 файл конфигурации, создайие, 372-374 значения флага, перечень, 373 местонахождение файла, 374 JAF (Ja vaBeans Activation Framework - инфраструктура активации компонентов JavaBean), 471,495 Jakarta Commons HttpClient (см. HttpClient) Jakarta Tomcat (cm. Tomcat) jar, задача (Ant), 93,106-109 атрибут basedir. 108 атрибут destfile, 108 атрибут includes, 108 файл манифеста, создание, 109 элемент fileset (вложенный). 108 jar, инструмент как поместить библиотеку тегов в JAR-файл 565 jar, инструмент, создание WAR-файлов с помощью, 25 JAR-файлы (Java ARchive - Java-архив) log4jjar, 322 mail jar и activation jar. 472 включение JAR-файлов Tomcat в путь к классам Ant, 96-99 для Jakarta Commons HttpClient, 182 каталог WEB-INF/lib, что содержит, 24 создание с помощью Ant, 106-109 спецификация Sun Microsystems для. 106 упаковка библиотеки тегов в, 565 упаковка файла тега JSP в. 581 Jasper (JSP-контейнер для Tomcat), 128 JASPER_HOME, переменная окружения, 117 jasper-runtime.jar, 118 .Java 2 Enterprise Edition (см. J2EE) Java API для обработки XML (JAXP), 91,134,589 Java Database Connectivity (cm. JDBC) Java Development Kit (JDK), 23 Jqva Runtime Environment (JRE) апплеты, запуск в, 412 выполнение апплета с помощью подключаемого модуля Java, 409 java, инструмент командной строки, установка пользовательского пути к классам, 91 JAVA .HOME, переменная окружения, 352 установка при инсталляции Ant, 92 JavaBeans cookies, создание, 239 CookieBean (пример), 241 JAAS API, использование для аутентификации, 377-380 вложения в постовые сообщения, обработка (пример), 489-493 вспомогательный класс для поиска в Google, 713-717 выгрузка файлов (пример), 210-211 данные страницы JSP, динамическая отправка другому процессу, также выполняющемуся на стороне сервера, 185-188 для разбора web-страницы использование в JSP. 702-704 использование в сервлете. 700-702 доступ к электронной почте из сервлета. 487 как JNDI-pecypc на Tomcat доступ из сервлета, 659-665 конфигурирование как JNDI-объекта на Tomcat; 656-658 настройка ответов, отправляемых клиентам, 456 отправка почтового сообщения из сервлета. 477-481 EmailBean (пример), 478-480 задание частей почтового сообщения с помощью четодов компонента JavaBean. 480 сервлет. использующий JavaBean дзя отправки почтового сообщения, 480 свойства доступ с помощью EL, 618-622 установка в JSP, 175-178 сервлет, использующий компонент JavaBean для соединения с Web-сервисами Amazon. 726-728 создание в качестве синтаксического анализатора web-страницы, 697-699 создание для поиска по Amazon, 721-726 создание для поиска по web-базе данных Google, 7Q9-712 страница JSP использующая для поиска в google.copi, 717-719 JavaBeans Activation Framework (JAF), 471,495 javac, задача (Ant); 93 компиляция сервлетов приложения в сборочный каталог, 49 элемент classpath, вложенный в, 98 javac, компилятор, 22 встроенный, в составе Sun Microsystems JDK. 23 JavaMail, 471 классы Session, Store, Folder, и Message, 489 перечень заголовков почтового сообщения, 501 получение почтовых сообщений, 482 установка свойств для JNDI-объекта Session, 670 JavaScript, 288-304 JavaServer Pages (см. JSP) javax, его подпакеты для BEA WebLogic, 22 javaxjnail, пакет, 472 javax.mail.intemet, пакет, 472 javax.naming, пакет, 512,655 javax.servleLjsp.tagext, пакет, 554 javax.sql. пакет, 603 Java-интерфейс именования и каталогов (см. JNDI) Java-служба аутентификации и авторизации (см. JAAS) JDBC (Java Database Connectivity), xvi API транзакций, 539-544 ResultSet, преобразование в объект Result, 534-538 вызов из сервлета хранимых процедур с помощью CallableStatement, 525-529 доступ из сервлета к базе данных с помощью JDBC, 506-509 драйвер classes I2.zip для Oracle, 508 Драйвер базы данных и расположение хранилища данных, 508,603 интерфейс ResultSetMetaData, 548-552 класс DriverManager. 506 Предметный указатель | 741
объект javax.sql.DataSource JSTL-тег SQL, использование, 603 на WebLogic, использование в JSP, 522-524 создание на WebLogic, 515-518 объекты Connection 506 спецификация, адрес для скачиваний. 509 JDK (Java Development Kit - набор инструментов Java-разработчика), 23 version 1.1, поддержка Ant, 91 JNDI (Java Naming and Directory Interface - Java- интерфейс именования и каталогов), 505,655 JNDI-дерево на WebLogic для источника данных (DataSource), 518 доступ к EJB с помощью, 678 686 просмотр. 671 JNDI-ресурс Tomcat доступ из JSP, 665-668 доступ из сервлета, 659-665 JNDI-ресурс WebLogic доступ из JSP, 675-678 доступ из сервлета. 672-675 использование классов API для получения источника данных (DataSource). 515 конфигурирование JNDI-объекта на Tomcat. 656-658 конфигурирование JNDI-pecypca на WebLogic. 669 поиск, использование для доступа к источнику данных (DataSourqe) на WebLogic, 518 JRE (Java Runtime Environment - среда исполнения Java) апплеты, выполняемые в. 412 выполнение апплетов с помощью подключаемого модуля Java, 409 JSP (JavaServer Page), xiii атрибуты запроса, доступ со стороны включаемых ресурсов, 147 генерирование XML-представления из, 128-136 как XML-документы. 116 класс реализации. 115 написание. 19-21 базовые шаги по, 20 в виде XML-файлов, 20 просмотр выходной информации в браузере. 21 пользовательские XML-теги (см. пользовательские теги; теги) прекомпиляция, 115 на Tbmcat. 117 на WebLogic, 119-121 протокол прекомпиляции, использование. 121 прекомпиляция, отображение на класс реализации страницы, 122 развертывание (см развертывание сервлетов и JSP) серверы приложений как программный хост для, xiv создание "с нуля" в виде JSP-документа, 124-127 / создание для сервлета URL в стиле JSP, 68 у упаковка в WAR-файлы, 24 фрагменты, 26 jsp:directive.include, элемент, 164 jsp:directive.page, элемент, 126 добавляемый к JSP-документу, при создании XML-представления, 128 jsp:doBody, действие, 580,580 j$p:forward, действие, 191 , jsp:ge^Property, действие, 211 jsp:id, атрибут для XML-элементов в JSP-документ, 128 jsptinclude, действие, 148 включение XML-фрагмента в JSP-документ. 161 включение ресурса в страницу JSP каждый раз, когда она получает запрос, 152-156 внешний файл конфигурации, используемый для включения ресурсов в JSP, 157-160 директива include и, 150,157 правила практического использования, 164 немедленное отражение изменений во включаемых файлах, 157 jspzparam, действие, 191 jsp.'param, элементы, обеспечивающие встроенные апплеты парами параметр/значение, 409 jsptplugin, действие HTML-теги. генерируемые для загрузки Java апплета. 410 встраивание апплета в JSP, 408-410 jsprroot, элемент, 116,124 добавленный XML-представлением в JSP-документ, 128 jspisetProperty, действие, 175-178 значение cookie, установка, 241 свойства cookie, установка, 239 установка имени каталога для сохранения вьп ружаемых файлов. 211 установка свойства компонента JavaBean значением, введенным в форму, 179-181 jsp:text, элемент, 127 jsp:useBean, действие, 20,21,126,176,267 атрибут id, .621 использование для установки свойств компонента JavaBean значениями, введенными в форму, 179 компонент JavaBean создающий cookie, использование bJSP, 239 создание в JSP экземпляра компонента JavaBean, выгружающего файлы, 211 jsp_precompile, параметр, 121 jspc (инструмент прекомпиляцин weblogicjspc), 119-121 JspC инструмент командной строки (Tomcat), 117 JspContext, класс, 573 JspFragment, интерфейс, 573 файлы .jspf и, 151 jsp-property-group, элемент, 116,124 JspWriter, класс, 573 JSTL (JavaServer Pages Standard Tag Library - библиотека стандартных тегов JSP), xv, 586-627 cxhoose, c:when. и c:otherwise теги, 612 crforEach тег, 173,247, 586, 590,613 перебор присланных данных, 173 c:ifTer.590,592 ' 742 | Предметный указатель
c:import тег, 164,166,293,292,595 c:out тег, 21,158,175.226,586,622 escaped characters, 593 информация об исключении, отображение для JSP, 229 использование в JSP (пример), 592 сводка функций, 590 с:рагаштег, 166 c:set тег, 386,586,609 сводка функций, 590 cookie неявно создаваемый объект, 246 cookies, доступ с помощью EL, 616 идентификатор текущего сеанса, отображение, 265 массив отображений (Мар), использование для перебора значений, 534 . пользовательские теги в JSP, создавацных в виде XML-файлов, 124 скачивание и использование, 587 JSTL 1.1,589 директивы taglib для различных библиотек JSTL 1.0,588 справочная реализация 1.0, содержимое каталога lib, 588 теги ядра, использование в JSP, 590-594 JVM (Java Virtual Machine - виртуальная машина Java), сервлеты и, 14 К keystore файл, 352 keytool, утилита, 352 L ПОп (см. локализация) language, элемент, 629 layout (разметка) для протоколируемых сообщений, 324 PattemLayout, 334 SimpleLayout, класс, в сообщениях корневого logger'a, 330 в наследуемых appender'ax, 349 шаблон, используемый для BasicConfigurator. 329 lib, каталог, 24,42 JAR-файлы. находящиеся в. 106 к^4}.]аг,файл, 327 TLDh, 566 t , включение вложенных каталогов в WAR-файл с помощью задачи war инструмента Ant. 48 lib, элемент (Ant), 105 listener, элемент (web.xml), 584 'ListResourceBundle, класс, 637 load-on-startup, элемент (web.xml), 345 local home, интерфейс (EJB на WebLogic), 682 Location, заголовок, 275 location, элемент (web.xml), 217 log() (ServletContext), 193,283,285,324-325,435 log4j,322 Apache Log4j library, 445 слушатель событий сеанса, использующий, 347-349 установка, 326 файл конфигурации, используемый слушателем контекста сервлета, 346 log4j.properties, файл, 330-331 задание различных имен для, 342 конфигурирование appender, 332 LoggerTag, класс, .337,339 login() DataSourceLoginModule, класс, 376,376 LoginContext, класс, 366,376 LoginModule, 370 login.html, страница (пример), 359 LoginBean, класс (пример), 378-379 getLoginSuccess(), 380 login-config, элементы (web.xml), 82 auth-method, вложенный элемент, 355 значения для, 85 использование с элементом security-constraint, 84 LoginContext, класс, 376 loginErronlitml, страница, 359 ioginErrorJsp, страница, 361 LoginModule, класс, 365-371 методы, 370 пример кода. 366-369 проверка имени и пароля пользователя по информации из базы данных, 370 Iogout( ) (LoginModule), 370 lookup( ) (InitialContext), 515,519 м Mac OS X 10.2, операционная система на основе Unix, переменная PATH для javac, 23 Macromedia Flash (см. Flash-файлы) mail.jar, архив, 472 mailDefaults.properties, файл, 480 Manager, приложение (Tomcat), 109 online-документация по, НО StopTask, класс. 113 Мар (отображение), объекты, 172 ContextObject, класс (пример), 384,386 методы, добавляющие в отображение (Мар) ключи и значения, 384 cookie, неявно создаваемый EL-объект. 616 Entry, подкласс, getKey() и entrySet(), 194 , getValue(), 173 header, неявно создаваемый объект, 431.613 ' headerValues, неявно создаваемый объект, 615 параметры JSP, передаваемые для передачи компоненту JavaBeap. 185 Message, класс, 489 getAUHeaders(), 501 getContent!). 485 getFrom(). 485 MessagingException, класс, 481 МЕТА-INF, каталог, TLD в, 566 method,, атрибут (HTML-тег form), 196 Method, объекты, 341 Microsoft Word, файл Предметный указатель | 743
. как вложение в почтовое сообщение. 499 отправка в виде двоичных данных, 311-313 MimeMessage, класс, 476,495 MlmetypesFileTypeMap, класс, 499 MIME-типы application/msword, 499 application/pdf, 306 MP3 файлы, 316 multipart/mixed, 494 ассоциация расширения файла с. 499 документ Microsoft Word, 311 общие, перечень, 307 MPEG аудио уровень 3 (MP3) встраивание файла в. JSP, 426 отправка аудио файла как, 316-318 Multipart, класс, 495 Multipart, тип содержимого, 489 multipart/form-data, тип содержимого, 195 MuitipartFiiter, класс, 209 MultipartParser, класс, 202-206 MultipartRequest, класс компонент JavaBean для выгрузки файлов, использование, 210 сервлет, использующий, создание, 200-202 Multipurpose Internet Mail Extensions (многоцеле- вые расширения Интернет-почты) MutableAttributeSet, класс, 688 N name, атрибут (элемент project инструмента Ant), 93 Netscape JavaScript, web-сайт разработчика, 288 запрет cookie, 234 установка языковых предпочтений (в версии 7.1), 630 ntxu , (итератор), 193 nullrole, роль безопасности, 88 NumberFormat, класс formatf), 647,649 getCurrency!nstance(), 646 getPercentInsiance(), 649 О object, теги (HTML), 409,410 Е/В, фабрики для. 682 автоматическое генерирование HTML для Flash. 415 области видимости для. 609 объекты созданные с помощью HTML-конвертор (пример). 413 onSubmit, обработчик события (HTML-тег form), 292,294 open source, приложения, xiv openConnection() (URL), 319 Oracle 8i, СУБД сервлет, запрашивающий ResultSet, 548-551 хранимая процедура, добавляющая строку в таблицу, 525 Огас1е-последовательности, 526 org.apache.jasper.runtime, пакет, 118 Р Г page, атрибут (jsp:include), 158,160 page, директива errorPage, атрибут, 228 import, атрибут, 522 isErrorPage, атрибут, 228 перезапись конфигурации страницы ошибок (error-page), 229 pageContext, неявно создаваемый объект, 266,634 PageData, класс, getInputStream(), 132,134 pageEncoding, атрибут (jsp элемент directive.page), 128 РАМ (Pluggable Authentication Module - подключаемый модуль аутентификации), 365-371 param, неявно создаваемый объект, 173,271,425, 611 param-value, элемент, изменение для импорта включаемых ресурсов, 141 parse() (ParserDelegator), 664,691 синхронизация, 695 ‘ ParserCallback, 688 ParserDelegator, класс, 688 callback (обратный вызов), использование, 690 parse(), 664,691 синхронизация, 695 Part интерфейс getAUHeaders(), 500 getHeaderf), 501 getlnpurStream ), 493 path, атрибут (cookies), 233 доступ к значениям. 245 установка значения, равного пути к контексту. 234 path, атрибут (элемент Context, Tomcat), 50 path, параметр пути, 271 PATH, переменная окружения, 48 включение пути к каталогу bin вашего Java SDK, 565 для компилятора javac из Sun JDK. 23 каталог /bin инструмента Ant, добавление к, 92 path, элемент(Ат), 33,41,97 вложение трех файловых наборов в, 99 PatternLayout, класс, 324,334 PDF-файлы (Portable Document Foftnat - формат переносимых документов), 306-310 Portable Document Format POST-запросы, 15 выгрузка файлов и. 196 доставка данных, введенных в HTML-форму, программе, работающей на стороне сервера, посредством, 168 обработка в JSP. 173-175 ' обработка в сервлете, 169-173 с помощью Servlet Request.getParameterf ) ugetParameterMapl), 170-173 ограничения безопасности в. 356 отправка данных из JSP, 185-188 отправка данных из сервлета другим программам, работающим на стороне сервера, 182-184 744 | Предметный указатель
HttpClient, использование для отправки данных к JSP, 182 PRECLASSPATH, переменная окружения, 118 prefix, атрибут (директива taglib), 566 prepare, цель (Ant, файл build.xml), 33,41 printf, функция языка С, 334 PrintWriter, класс, 148 HTML-страница, возвращаемая блокирующим фильтром, 455 PrivacyServIet, класс (пример), 141 doGet(). включение ресурсов, заданных начальным параметром, 141 JSP-фрагмент, включаемый посредством, 142 project, элемент (Ant), 93 \ Ant-задача property, импорт файла global .properties, 114 Ant-файл global properties, 33 cookie, установка, 239 jar-name, свойство, 108 JavaBean лоступ с помощью EL 618-622 установка в JSP, 175-178 части почтового сообщения, хранящиеся в виде, 477 атрибуты Тега как556 внешний файл (indude.properties). задающий ресурс для включения в JSP, 158 загрузка в build.xml, 33 как сделать их доступными для сборочного файла, 98 передача в файл Ant задача property, использование для. 111 командная строка, используемая для, 111 свойства упорядочение элементов target внутри, 93 файл build.properties (Ant), для развертывания web-приложения. 46 файл log4j properties, 330-331 файл mailDefaults.properties, 480 файл wl.properties для сборки Ant на WebLogic. 40 файл свойств для автоматического генерирования XML-представления, 130 property, задача (Ant), 95, 111 Propertyconfigurator, класс, 342 Q QuickTime-ролик, встраивание в JSP, 421 R refid, атрибут (Ant, элемент classpath), Refresh, заголовок, 436 страница JSP, добавляющая его к ответу, 438 reloadable, атрибут (элемент Context, Tomcat), 35,50 removeAttribute() (HttpSession), 280 request, неявно создаваемый объект, 152 RequestDispatcher, интерфейс, 77-79,87 forward!), 189,537 include!), 138, 289 включение JavaScript-функции Create Window в сервлет, 294 включение верифицирующего модуля JavaScript в сервлет, 3(Х) включение файла сервлета самого высокого уровня. 144 встраивание Flash-файла в сервлет 419 сервлет, импортирующий JavaScript-файл^ 290 JSP-фрагмент, включаемый в сервлет с помощью, 142 перенаправление HTTP-запросов. не затрагивая ограничений безопасности, 83 перенаправление запросов к сервлетам с учетом ограничений безопасности, 89 RequestDispatcher, объекты, использование фильтров с, 459-461 requestlnitiahzed!), 439 requestscope, неявно создаваемый JSTL-объект, 403,406 Resource, элементы (server.xml), 509,656,657 ResourceBundle, объекты, 132,480 gctBundle!), 157, 638 использование в JSP. 642 использование в сервлете, 638-641 создание.в виде Java-класса, 637 создание в виде файла свойств, 635 установка в web.xml контекста локализации, 653 resource-env-ref, элемент (web.xml), 656,657 ResourceParams, элементы (server.xml), 510,656,657 resource-ref, элемент (web.xml), 510 response, неявно создаваемый объект (JSP), 224 Result, интерфейс, 534,537 Result,o6beKTbi, 605 преобразование ResultSet (результирующего множества) к, 534-538 ResultSet, объекты, 605 поиск информации о. 548-552 ResultSetMetaData, интерфейс, 548 сервлет, использующий, 548-551 ResultSupport, класс, 537 toResult(), 534 rollback() (Connection), 539,544 RoIlingFileAppender, класс, 333 root, элемент (см. jsp:root element) s save-dir, элемент (web.xml), 212 SAX (Simple API for XML - простой API для XML), 134 SAX, (Simple API for XML - простой API для работы c XML), 134 Scalable Vector Graphics (SVG), файл, встраивание в JSP, 424 SDK (Software Development Kit - набор для разработки ПО) Google Web API SDK, 708 Google Web API. 708 PATH, переменная окружения, 565 переменная окружения PATH, 565 Secure Sockets Layer (SSL) встроенный механизм отслеживания сеанса. 252 установка на сервере Tomcat, 352 secure, атрибут (cookies), 233 security-constraint, элементы (web.xml), 76 ,\ Предметный указатель | 745
login-config, элемент, использование с, 84 web-resource-collection, вложенный элемент, 356 блокирование всех запросов кроме тех, что исходят от RequestDupatcher.forward, 87 ' вложенный элемент auth-constraint, 356 доступ к сервлету, ограничение доступом только со стороны сервлета-контроллера. 87 задание web-ресурсов, требующих аутентификации. 355 инициирование аутентификации в JSP, 355 ограничение запросов к определенным сервлетам, 81 пример. 83 security-role, элементы (web.xml), 82,85,88 select, атрибут (x:forEach), 595 SELECT, оператор (SQL), 548 выполнение на странице JSP в транзакции. 546 отправка к базе данных с помощью тега sql.quety, 605 send() (Transport), 476,495 sendError() (HttpServletResponse), 221-223,469 sendRedirect() (HttpServletResponse), 261,274 server.xml, файл (Tomcat), 34 элемент Connector, 353 элемент Context, 49 элементы Resource и ResourceParams, 510 service() (HttpServlet), 15 Servlet, интерфейс, 14 servlet, элементы (web.xml), 64 вложенный элемент load-on-startup, 345 генерируемые с помощью JspC, отображение сервлетов на. 122 связь с несколькими элементами servlet-mapping, 66 servleLjer, включение в переменную окружения PRECLASSPATH, 118 servlet-class, элемент (web.xml), 64 Servlet Config, интерфейс, 14 ServletContext, атрибуты установка в JSPs, 386-388 установка в срвлетах, 383-385 объект, кторый сервлет привязывает к ServletContext, 383 сервлет, который привязывает объект к ServletContext. 385 ServletContext, класс, 78,343 getAttribute(), 383 getReaIPath(), 201 log(), 193,283,285,324-325,435 setAttribute(), 383,385 возврат dispatcher равного null, 79 ServletContextlog(), 324-325 ServletContextListener, интерфейс, 342-346 contextlnitialized() и contextDestroyed!). 345 ServletException, клаес, 18,217 servlet-mapping, элементы (web.xmi) WebLogic Server 7.0,36 отображение всех запросов к web-приложению на сервлет, 73 удаление или изменение всех элементов, позволяющих обращаться с запросом в обход сервлета-контроллера, 75 746 | Предметный указатель отображение статического содержимого на сервлет, 70 связь нескольких ... с одним элементом servlet, 66 сгенерированные посредством JspC отображающие сервлеты на 122 сервлеты без, активизация, 71 создание псевдонима сервлета, 64 шаблон URL в стиле JSP в, 68 элемент url-pattem 65 servlet-name, элемент (web.xml), 64 символ шаблона *, не используемый в. 68 ServletOutputStream, класс, 310 ServletRequest, интерфейс getLOcaJef). 629 getParameter!) и getParameter!), 370 ServIetRequestEvent, класс, getServletRequestf), 440 ServletRequestListener, класс, 439-441 ServletResponse, класс setBufferSize< ), 223 изменение ответа при помощи фильтра, 456 Session, класс, 471,476,482,489 привязка объектов к JNDI на WebLogic, 669 сервлет, получающий объект Session (сеанс) от JNDI на WebLogic 672-675 session-config, элемент (web.xml), 27,253 время простоя сеанса для всех приложений сервера Tomcat, 256 sessionCreated() (HttpSessionListener), 276,278,347 sessionDestroyed() (HttpSessionListener), 276,278,347 sessionDestroyed(), 347 sessionDidActivate() (HttpSessionActivationListener), 276 SessionFilter, класс (пример), 284 sessionScope, неявно создаваемый JSTL-объект, 396,399,610 session-timeout, элемент(жеЬ.хт1), 256 session WillActivate() (HttpSessionActicationListener), 276 Set, класс, iterator!), 193 setAttribute!) класс HttpSession, 280 класс ServletContext. 386 setAutoCommit() (Connection), 539 setBufterSize!) (ServletResponse), 223 setContent(), 480 Set-Cookie, заголовок ответа, 250,251 setFrom(), 480 setKey() (GoogleSearch setKey), 712 setMaxAge!) (Cookie), 241 вызов co значением аргумента равным нулю. 249 setMax!nactiveInterval() (HttpSession), 257,259 setQuery!), 712 setQueryString() (GoogleSearch), 712 setValue!) (Cookie), 241 show-props, цель (Ant), 95 Simple Object Access Protocol (cm. SOAP) SimpleLayout, класс, 324
сообщения, протоколируемые посредством корневого logger'a, 330 SimpleTag, интерфейс, 553,571 SimpleTagSupport, класс, 571 getJspBodyt), 573 ge JspContextf), 573 'SOAP (Simple Object Access Protocol - простой протокол доступа к объекту), 705 web-сервисы, базирующиеся на.... причины использования, 707 ответ Amazon на запрос поиска по ключу (пример), 705 SQL (Structured Query Language - структурирован- ный язык запросов) JSTL-теги для SQL без настройки источника данных (DataSource), 607-608 JSTL-теги для SQL, 522,586 с настройкой источника данных (DataSource), 603-606 выполнение нескольких операторов в одной транзакции, 539-544 сервлет, использующий транзакцию, 540-543 транзакции, использование со страницами JSP, 545-548 хранимые процедуры, 525-529 SQL PLUS, база данных, хранимая процедура addEvent (пример), 526 sqkquery, тег, 547 отправка SQL-оператора SELECT к базе данных, 605 sqksetDataSource, тег, 607-608 sqktransaction, тег, 546 sqkupdate, тег, 547 src, атрибут (HTML-тег script), импорт модуля JavaScript в сервлет, 292 src, каталог, 23 . SSL (см. Secure Sockets Layer) start и stop, задачи(Ап1, файл build.xml), 33 StartTask, задача, 109 stop и start, задачи(Ап1, файл build.xml), 33 StopTask, класс, 113 Store, класс, 471,482,489 structureResults(), 712,722 Sun Microsystems Java Development Kit (JDK), 23 online-руководства, 13 документация no JAAS, 370 примерьышплетов, 409 руководство по web-сервисам, включающее обсуждение XPath, 595 сайт для скачивания Java WSDP, 589 спецификация J AR-файла, 106 шаблон проектирования Front Controller, 74 SVG-файл, встраивание в JSP, 424-425 synchronizedMap() (Collections), 385 т t:toxml, элемент, 130 tag, директива, 580 taglib, директивы, 20,560 JSP-сегмент, содержищий ... (пример), Г51 во включаемом JSP-сегменте, для использования в тегах JSTL 1.0c:out, 158 для пользовательского тега, задающие uri для библиотеки тегов, 566 для разных библиотек JSTL 1.0,588 задающая библиотеку тегов ядра JSTL, 126 значения uri и prefix, используемые с библиотекой функций, 623 значения атрибута uri в JSTL 1.1.589 которые необходимо использовать для библиотек йдра и SQL JSTL 1.0,604 taglib, элемент атрибут version, 563 атрибут xmlns, 563 атрибут xmlns:xsi, 563 атрибут xsi:schemaLocation, 563 TagLibraryValidator, класс, 116 включение библиотек тегов как атрибутов пространства имен в JSP документах, 127 использование XML-представления для проверки пользовательских тегов на странице JSP, 128 определение, 553 пользовательские (см. также пользовательские теги) пользовательские теги в JSPs, 20 созданные заранее для страниц JSP (JSTL), xv теги TagSupport, класс, 129 target, элементы (Ant), группировка задач в, 93 taskdef, элемент(Ап1, build file), 33 задача start, определение, ПО задача stop, определение, 113 Throwable объекты доступ на странице ошибок JSP, 230 связанные с исключениями, доступ, 218 Throwable, класс, 217 timeStyle, атрибут (fmt:formatDate), 601 TLD (см. Tag Library Descriptor) tld, расширение файла, 560 TLD-файлы (Tag Library Descriptor - дескриптор библиотеки тегов), 26,42 uri для библиотеки тегов, 566 XML DTD, использование в JSP 1.2,585 для XML-представления пользовательского тега, 131 добавление элемента listener к, 584 задание верификатора для, 128 информация о Пользовательском теге, обеспечение, 337 конфигурирование функции EL, 532 определение. 554 пользовательский тег для протоколирования, 339 размещение в каталоге META-INF JAR-файла библиотеки тегов, 565 создание TLD по версии JSP 1.2 для классического обработчика тега, 558-560 создание TLD по версии JSP 2.0 для классического обработчика тега, 561-563 создание для упрощенного обработчика тега, 574 Tomcat, xiv DataSource, используемый в сервлете, 512-515 компиляция сервлетов на..., 22 Предметный указатель | 747
настройка источника данных (DataSource) используемого в сервлете, 509 развертывание отдельной страницы JSP на, 41 создание имен и паролей пользователей. 350 элемент coniext(confZserver.xml), для содержимого JSP импортируемого из-за пределов контекста, 166 tomcat-users.xml, файл, 82 имена и пароли пользователей, чувствительность в регистру, 355 имена, пароли, и роли для аутентификации. 351 отображение пользователя на роль безопасности, 85 пример типичного файла. 88 роль manager, отображение пользователя на. 110 роль безопасное ги nullitoll, предоствращение отображе- ния пользователя на, 88 toResuJt() (Resultsupport), 534,537 ToXmlValidator, класс, 132 Transport, класс, send(), 476,495 try/catch блок, для исключений, выбрасываемых во время операций включения, 146 TryCatchFinally, интерфейс, 568 и undeploy, цель(Ап(, build.xml file), 48 Unix keytool, утилита, создание цифрового сертификата с помощью, 352 командный сценарий для прекомпиляции JSP-файлов, 118 командный сценарий для прекомпиляции всех страниц JSP приложения. .120 UPDATE, оператор (SQL), 544 URI запрос, использование страницей ошибок JSP, 226 отображение элементов uri в TLD, как указано в директиве taglib в JSP-файлах, 560 uri, атрибут (директива taglib), 566 JSTL 1.1, различные значения в. 589 URL в стиле invoker (вызывающий), для сервлетов, 71 внешний импорт в JSP с помощью тега c:import, 165 для статичного содержимого, отображающий на сервлет. 70 задание только каталога, 80 инициализация поиска по Amazon посредством, 7J28 соединение с, открытие, 319 URL для загрузки нового окна, созданного в сервлете с помощью функции JavaScript, 296 url, пользовательское действие, 269-272 encodeURL() против, 274 добавление параметров с помощью, 271 перезапись URL посредством, 270 URLConnection, класс, 310 url-pattern, элемент (web.xml), 65 * символ подстановки в, 68 отображение любых запросов к web-приложению на сервлет. 73 отображение фильтра на JSP, 446 чувствительность к регистру 66 748 | Предметный указатель URL-шаблоны, 64 *.jsp - отображение на расширение? 447 ZsqlJsp.jsp, инициация BASIC-аутентификации для. 35<^ JSP-типа, создание для сервлета 68 заданные элементами security-constraint, 77 направление всех запросов сервлету-контроллеру, 73 ограничение любых запросов, 87 требование точного совпадения для, 67 User-Agent, заголовок, 429 V ValidateHandler, класс, 134 var, атрибут(с:1огЕасЬ tag), 592 variable, директива, 580 version, атрибут epokies, 233 элемент taglib. 563 w war, задача (Ant), 93,104 атрибуты destfile и webxml, 105 элементы classes, lib, и fileset (вложенные), 105 WARN, уровень протоколирования, 323 WAR-файлы (Web ARchive), xiv генерирование с помощью задачи war инструмента Ant, 48 для сервлетов и JSP, 24 открытие в WebLogic Builder для редактирования файла web.xml, 57 поиск уже развернутых на Tomcat 48 просмотр содержимого, 25 развертывание web-приложения в виде.... используя консоль администрирования WebLogic. 53 создание с помощью Ant, 103-106 web.xrtil, файл, 42 (см. также servlet-mapping элементы) API сервлета версии 2.3,27 переход к версии 2.4 ради преимуществ JSTL 1.1, 624 * *7*1 И*’*'’’*’ 9 Д 99 использование с iumcui j uj>)1 ь /./, jyu BASIC-аутентификация, инициирование с помощью JSP-файла. 355 lOExceptions, элемент для управления, 200 URL JSP-типа для сервлета, 68 атрибут enor-page. отображение типов исключений на JSP, 225 аутентификация на основе формы, установка. 358 время простоя для всех приложений на Tomcat, 256 время простоя, установка, 253-255 источник данных (DataSource). Настройка в. 546 настройка страницы ошибок, 147 > объявление обработчиков исключений в, 214-218 ограничение запросов к определенным сервлетам. 81 отображение всех ссылок к страницам JSP на один сервлет, 69 отображение сервлета на имя, 64-66 предотвращение запросов к любым сервлетам кроме управляющего (контроллера). 76 просмотр с помощью сервлета, 319-321
редактирование для WebLogic, чтобы , зарегистрировать сервлет, 36 сервлет, создающий всплывающее окно, 297 сервлеты без отображения в, 71 слушатели (см. также слушатели) запросов к < ервлету, зарегистрированные 440 настройка в API cepeiemoe версии 2.3'. 28 содержимое, 26 создание нескольких отображений на сервлет, 66 создание с помощью утилиты JspC, 117 фильтр, блокирующий IP, отображение, 469 . фильтр, проверяющий данные, введенные в форму, зарегистрированный и отображенный в. 466 фильтры настройка в, 283 отображение на JSP, 446 отображение, 434 'отображенные на путь к сервлету (пример), 443 элемент context-param, добавление для вкладываемого файла в JSP, 160 элемент jsp-property-group. 116,124 элемент listener, 584 элемент resource-ref, 5 Ю элемент save-dir из параметров контекста, 212 элемент servlet со вложенным элементом load on-startup, 345 Web-app, элемент (web.xml), 28 атрибуты, необхо лимые для дескриптора развертывания сервлета версии 2.4,625 webapps, папка (Tomcat), 44 WEB-INF, каталог, 15,24 содержимое, 42 WEB-INF/jspf (необязательный каталог), 26 WEB-JNF/tids (необязательный каталог), 26 WebLogic Server 7.0 настройка безопасности в weblogic.xml, 85 развертывание web-приложёния на. использование WebLogic Builder. 55-59 использование инсрумента Ant, 50 использование консоли администрирования WebLogic, 52-54 использование утилиты командной строки weblogic. Deployer, 60-62- развертывание отдельного сервлета на, 36-41 ра свертывание web-прилоЖения, 37 реда тирование web.xml для регистрации сервлета, 36 файл Ат, использование, 37-41 развертывание отдельной страницы JSP на, 43 WebLogic, xiv JNDI-дерево доступ к компоненту EJB посредством, 678-686 просмотр, 671 JNDI-поиск, использование для доступа к источнику данных (DataSource), 518 JNDI-pecypc доступ из JSP 67'5-678 доступ из сервлета, 672-675 настройка, 669 классы сервлета и подпакеты javax. 22 прекомпиляция страниц JSP на, 119-121 рецепты часто встречающихся задач, xv создание источника данных (DataSource) на, 515-518 настройка, шаги по, 516 weblogicDeployer, утилита командной строки, 60-62 weblogic.jspc, утилита, 119-121 weblogicxml, файл, конфигурация безопасности в, 85 weblogic-ejb-jar.xml, файл, 683 web-resource-collection, элементы (web.xml), 84 задание ограничений, связанных с безопасностью, 356 защищенные ресурсы в элементе security-constraint, 85 webxml, атрибут (Ant, задача war), 105 web-браузеры (см. браузеры) web-каталог, включение вложенных каталогов в WAR-файл с помощью задачи war инструмента Ant, 48 web-компоненты, 14 | web-контейнеры, 14 (см. также Tomcat) обработка исключений в, 217 web-приложение, развертывание на Tomcat, 44-50 web-приложение, развертывание на WebLogic использование WebLogic Builder, 55-59 использование инструмента Ant. 50 использование консоли администрирования WebLogic. 52 54 использование утилиты командной строки weblogic. Deployer, 60-62 web-приложения №АК-файлы(См. WAR-файлы) дескриптор развертывания, создание для, 26-28 запуасна Tomcat с помощью файла Ant, 109-112 на базе Java, xvi нащ ройка механизма |og4j, 349 определение 14 остановка на Tomcat с помощью файла Ant, 112-114 отображение всех запросов на управляющий сервлет (контроллер), 73-76 подсчет количества запросов к, 439 развертывание на Tomcat, 44-50 Конфигурирование Tomcat чтобы указать на приложение во внешнем каталоге, 49 сборочный файл Ant. использование, 44-50 развертывание на WebLogic используя WebLogic Builder 55 59 используя инструмент Ant 50 используя консоль администрирования WebLogic, 52-54 используя утилиту командной строки weblogic. Deployer, 60-62 сервлет, создание для, 13-19 создание начальных (welcome) файлов для, 80 структура каталогов (см. структура каталогов web-приложения) упаковка библиотеки тегов в 564 упаковка файла тега JSP в, 580 экземпляр контекста сервлета, 343 Предметный указатель | 749
web-приложения на базе Java, xvi Web-сервисы Amazon, 707 SOAP-ответ на запрос поиска по ключевому слову. 705 соединение с помощью JSP, 729 соединение с помощью сервлета, 726-729 I создание компонента JavaBean для соединения с, 721-726 установка 719-721 web-сервисы, 705 (см. также Web-сервисы Amazon; Web-сервисы Google) Amazon и Google. 707 Amazon, установка, 7(9-721 Google Web API, установка, 708 SOAP использование для переноса информации 705 на базе SOAP, обоснования применения. 707 web-страница этой книги, xix welcome-file-list, элемент (web.xml), ВО windoid, 294 Windows, семейство ОС вариант локали для, 628 запуск WebLogic Builder, 57 командный сценарий ля запуска JspC, 117 переменная окружения PATH для компилятора javac (на NT), 23 прекомяиляция всех страниц JSP приложения с помощью weblogic.jspc, 120 wl.ap plications, свойство(Ап1, файл build.properties), 52 wl.properties, файл(д тя сборочного файла Ant на WebLogic), 40 WLCLASSPATH, переменная окружения, 120 WSDP (Web Services Developer Pack - набор для разработки web-сервисов), 589 X x:forEach, тег, 586,595 x:if, тег, 595 x:out, тег, 586 x:parse, тег, 595 x:transform, тег, 586 атрибут xml, 598 атрибут xslt, 598 связывание таблицы стилей с XML-файлом, 598 Xerces2, синтаксический анализатор XML, 91 XML JSP-документ и XML-представление. 128 JSTL-теги ядра для XML, использование в JSP, 594-597 TLD-файлы (Tag Library Descriptor - дескриптор библио- теки тегов), 554 web-ссрвисы, 705 включение фрагмента XML в JSP-документ, 161-164 отправка XML-файла в виде двоичных данных. 313-315 преобразование (xztransform), 586 сборочный файл Ant. 93 создание JSP-документа в виде XML-файла, 124-127 сообщения SOAP по HTTP, 705 страницы JSP в виде XML-файлов, 20,116 XML-эквиваленты директив JSP 124 теги JSTL XML и XSLT. использование в JSP. 597-599 XML синтаксические анализаторы, JAXP-совместимые, 91 xmlns, атрибут (элемент taglib), 563 xmlns:xsi, атрибут (элемент taglib), 563 XML-представление, 116 XML-документ JSP и..., 128 автоматически генерируемое для страницы JSP, 128 генерирование из страницы JSP, 128-136 XML-Схема, экземпляры, 563 XML-Схемы в файле web xml по версии 2.4 API сервлета, 27 для TLD JSP 2 0,563 XML-элементы задачи Ant, 92 пользовательские теги, используемые в JSP, 337 пространство имен для элементов, связанных с экземплярами XML-Схемы, 563 пространство имен, определение. 563 чувствительность к контексту в именах элементов и атрибутов, 553 элемент path, Определение пути к классам для Ant, 97 XPath, 595 xskschemaLocation, атрибут (элемент taglib), 563 XSL (Extensible Stylesheet Language), 597 XSLT, преобразование XML к удобочитаемому формату, 313 ' 750 | Предметный указатель
A автоматическое обновление JSP, 4Л8 автоматическое обновление сервлета, 436 авторизация (см. также JAAS) аутентификация и, 366 адреса, почтовое сообщение, 471,485 апплеты встраивание в JSP с помощью jsp: plugin, 408-410 встраивание в JSP с пЬмощыо инструмента HTML-конвертор, 411-415 пример (Sun Microsystems), 409 атрибут с областью видимости JSP, установка значения из параметра формы, 179-181 атрибуты запроса доступ со стороны ресурсов, включаемых в JSP, 147 доступ со стороны сервлета, ббрабатывающего ошибки, 219 ' ' \ информация об исключении из, 226 атрибуты объектов для web-приложений, 586 установка области видимости с помощью тега c:set. 590 атрибуты с различными областями видимости использование EL и тега c:out для получения значения, 609 атрибуты, 382-407 аудио файлы встраивание в JSPs, 425-427 отправка в виде МРЗ-файлов, 316-318 аутентификация клиента (см. аутентификация) аутентификация на основе формы, 358 завершение работы пользователя в системе, использующей, 362-364 форма для, 360 аутентификация, 84,350-381 BASIC-аутентификация, использование - в web-приложениях на Tomcat, 354-356 JAAS, использование в JSP, 377-381 JAAS, использование в сервлете, 375-377 LoginModule, создание с помощью^ A AS, 365-371 SSL, установка на сервере Tomcat, 352 авторизация и, 366 на основе формы. 358 завершение работы пользователя, 362-364 создание имени пользователя и пароля на Tomcat, 350 файл конфигурации JAAS, создание, 372-374 Б базы данных, 505-552 DataSource, использование в сервлете на Tomcat, 512-515 JNDI-поиск, использование Для доступа к DataSource на WebLogic, 518 JSTL-теги, которые взаимодействуют с, 586 взаимодействие с, путем настройки DataSource^) web.xml, 603-606 вызов хранимой процедуры из JSP, 530-534 выполнение нескольких операторов SQL в одной транзакции, 539-544 доступ из сервлета без конфигурирования источника данных (DataSource), 506-509 информация ResultSet, поиск, 548-552 источник данных (DataSource) на. WebLogic, использование в JSP, 522-524 настройка источника данных (DataSource) в Tomcat, 509 преобразование ResultSet (результирующего множества) к объекту Result, 534-538 создание DataSource на WebLogic, 515-518 транзакции, использование с JSP, 545-548 хранимая процедура, вызов из сервлета, 525-529 безопасность включение кода, связанного с безопасностью в сервлеты, 82 доступ к сервлету, ограничение только-доступом со стороны контроллера, 87-89 конфигурирование web.xml для безопасности web-приложения. 82 ограничение запросов ' к определенным сервлетам, 81-86 элементы, относящиеся к, в дескрипторах развертыва- ния, не использующих версию 2.4 сервлетов, 82 безопасность, управляемая контейнером, 82 библиотека тегов, 553 класс слушателя, добавление к, 584 упаковка в JAR-файл, 565 упаковка в web-приложение, 564 блокирующие фильтры IP-адреса, блокирование запросов от, 467-470 запросы, возможное блокирование, 453-455 браузеры JavaScript в, 288 встраивание Ftash-файла в JSP, 418 определение MIME-типа встроенного МРЗ-файла и активизация вспомогательного приложения для прослушивания музыки, 426 / отображение XML-файлов в удобочитаемом формате, 319 поддержка cookies со стороны, 234 просмотрщики для SVG-файлов, 424 В валюты, форматирование для локалей в JSP. 648-649 в сервлете, 646-647 вариант (в локалях), 628 верификация XML-документы, задание верификатора для TLD-файла, 128 данных введенных в форму, 172 JavaScript, использование в JSP, 294, 303 JavaScript, использование в сервлете, 290, 299-302 компонент JavaBean, использование, 179-181 фильтр, использование для, 462-467 пользовательские теги на страницах JSP, 128-135 верхний колонтитул, включаемый в JSP с помощью действия jsp:include, 155 Предметный указатель | 751-
виртуальная машина Java (JVM), сервлеты и, 14 вложения, почтовое сообщение добавление к почтовому сообщению в сервлете, 495-500 обработка почтового сообщения, полученного с помощью сервлета, 488-494 внутренние ресурсы из web-приложения, просмотр, 319-321 , время последнего доступа для сеансов отслеживание в JSP, 264-268 отслеживание в сервлетах, 261-264 время простоя для сеансов установка в web.xml. 253-255 установка в коде сервлета, 257-259 установка для всех приложений на Tomcat, 256 время создания для сеансов отслеживание с помощью JSP, 264-268 отслеживание с помощью сервлетов, 261-264 всплывающее окно, создание с помощью функции JavaScript, 294-296 выбрасывание исключений, информация об... в странице ошибок JSP, 226,226 выгружаемые файлы com.oreilly.servlet, класс для выгрузки файлов, 198 JSP, обработка.... 209-213 JSP, которая выгружает и показывает информацию о.... 211-213 компонент JavaBean для выгрузки файлов, создание, 210-211 выгрузка нескольких файлов, 198,202-206 переименование выгружаемых файлов, 207-209 с помощью вашего собственного Java-класса. 207 подготовка HTML-страницы для, 196-202 компонент для получения выгружаемого файла и сохраненные его в локальном каталоге, 200-202 выгрузка файлов (см. выгружаемые файлы) вызовы функций, встроенные в код JSP с помощью JSTL, 586,624 Г голубые страницы по ядру J2EE, 74 д даты и время "срок годности" cookies, 233 время создания и время последнего доступа для сеансов отслеживание в JSP, 264-268 отслеживание в сервлетах, 261-264 действий по протоколированию, 325 страница JSP, отображающая текущие, 20 форматирование дат в log4j. 335 форматирование дат в соответствии с локалью запроса eJSP. 645 в сервлете, 643 в 'сервлете, 643 форматирование при помощи JSTL-тегов fmtcfomatDate. mei, егоатрибуты, 601 текущая да.пав стиле Швейцарии и США, 600 двоичные данные, 305 PDF-файл, отправка в виде, 306-310 XML-файл, отправка в виде, 313-315 файл текстового редактора, отправка в виде, 311-313 действия JSP созданные в виде XML-файлов, 124 декларативная безопасность, 82 дескрипторы развертывания (см. также файл web.xml) EJB. 682 специфика поставщика (weblogic-ejb-jar.xml), 683 J2EE, пространство имен, 563 открытие и редактирование в WebLogic Builder, 56 создание, 26-28 API сервлета версии 2.4, 28 для A PI сервлета версии 2.3, 27 содержимое файла web.xml, 26 сохранение в каталоге WEB-INF. 24 файл web.xml в каталогеЛ¥ЕВ-1КЕ 42 элемент taglib, 129 элементы, связанные с безопасностью в версиях, отличных от API сервлета 2.4,82 динамически включаемое содержимое, 137-167 включение содержимого в JSP при каждом запросе, 152-156 включение содержимого, находящегося вне контекста, в JSP, 164-167 включение фрагмента XML в JSP-документ. 161-164 внешний файл конфигурации, использование для включения ресурса в JSP, 157-160 импорт ресурса при каждой обработке сервлетом запроса, 138-140 настройка включаемого ресурса во внешнем файле 141 производительность сервера и, 140 ресурсы, вкладываемые в сервлет на нескольких уровнях, 144-149 атрибуты запроса, доступ со стороны включаемых ресурсов, 147 внешний включающий сервлет, 144 второй включаемый сервлет, 148 информация об исключении, отображение. 148 первый внутренний включаемый сервлет, 146 ресурсы которые редко изменяются. включение в JSP, 149-152 динамическое содержимое, объединение multimedia в JSP, 408 директивы, JSP, 20 ХМЕэквиваленты для, 124 в правильном XML-файле, 126 752 | Предметный указатель
3 завершение работы пользователей, приложение с аутентификацией на основе формы, 362-364 заголовки HTTP (см. заголовки запроса; заголовки ответа) почтового сообщения, чтение из сервлета. 500-504 заголовки запроса, 429 доступ с помощью неявно создаваемого EL-объекта. 613 значение определенного заголовка. 615 проверка в JSP, 431 проверка в сервлете, 429-430 фильтр, используемый для изменения, 432-436 заголовки ответа Refresh. 436 страницы JSP добавляющие. 438 определение, 251 задание включаемого ресурса с помощью, 141 задачи (Ant), 92 группы.... представленные элементом target 93 задача property, 95 наборы файлов, 98 задачи web-разработчика, рецепты из этой книги, xiv реализация с помощью BEA WebLogic, xv закрытие соединений с базой данных, 515,521 запрос, ServletRequestListeners, 439-441 запросы блокирование запросов с определенных адресов с помощью фильтра, 467-470 возможное блокирование с помощью фильтра, 453-455 запросы клиента, 428-441 автоматическое обновление сервлета, 436 автоматическое обновление страницы JSP, 438 подсчет для web-приложения, 439 проверка заголовков HTTP-запросов в JSP, 431 проверка заголовков НТТР-запросов в сервлете, 429-430 фильтр, использование для изменения заголовков запроса, 432-436 запуск Tomcat из Ant, 110-112 зарегистрированные имена для сервлетов, 64 запуск зарегистрированных сервлетов с помощью вызывающего (invoker) сервлета, 72 фильтры, отображение на. 287 звуковая дорожка, встраивание в JSP, 425-427 значение времени простоя для сервлета, настройка, 253 И идентификатор сеанса, 252 URL, которые автоматически включают.... создание, 269 1 отображение с помощью JSTL-тега, 265 идентификаторы часовых поясов, вспомогательный класс для отображения, 591 именование сервлетов, 63-89 JSP-тип URL для сервлетов. 68 активизация сервлета без отображения в web.xmh 71 ограничение запросов к определенным сервлетам, 81-86 отображение всех запросов к web-приложению на сервлет, 73-76 отображение всех запросов на контроллер с сохранением всех отображений на сервлеты. 76 отображение сервлета на имя в web.xml, 64-66 отображение статического содержимого на сервлет, 70 создание множественных отображений на сервлет, 66 создание начальных (welcome) файлов web-приложения, 80 имя потока в протоколируемых сообщениях, 329 имя приложения (web-приложення), 25 интернационализация, 600,628-653 ResourceBundle, использование в JSP, 641 ResourceBundle, использование в сервлете. 638-641 ResourceBundle, как файл свойств, создание, 635 контекст локализации, установка в web.xml, 652 локаль клиента, определение в JSP, 633-634 локаль клиента, Определение в сервлете. 629-632 определение, 628 создание ResourceBundle в виде Java-класра, 637 форматирование валют . в JSP, 648-649 в сервлете, 646-647 форматирование дат eJSP.645 в сервлете, 643 форматирование процентов в JSP, 651-652 в сервлете, 649-650 исключения в классе пользовательского тега. 568-570 выброшенное во время операций включения, 147 обработка в web-приложениях. 214-231 объявление обработчиков исключений в web.xml, 214-218 объявление страницы JSP, обрабатывающей ошибки для других JSP, 228-230 отправка ошибки из JSP, 224 отправка ошибки из сервлета, 221-223 сервлет, обрабатывающий ошибки, 218-220 создание страницы JSP. обрабатывающей ошибки, 225 228 исполнительные подсистемы сервлетов, коммерческие, xv К к каталогу хранения, длц сервлета, выгружающего файлы, 200,201 к сервлету, 63 различия между исполнительными подсистемами сервлетов, 65 создание для пользователей web-приложения. 65 каталоги URL, задающий только каталог, 80 локальный, сохранение выгруженных файлов в, 201 Предметный указатель | 753
шаблон ** в элементах Ant (ноль или более каталогов). 99,108 . классические обработчики тегов, 553 создание TLD JSP 1.2 для, 558-560 создание TLD JSP 2.0 для, 561-563 создание для пользовательского действия, 554-558 классы класс реализации страницы JSP. 115 сервлета. 13,22 элемент servlet-class в web.xml, 64 код состояния сервера 403 Forbidden, 469 коды возврата HTTP коды возврата сервера коды состояния HTTP (см. коды возврата HTTP) status_code, атрибут, 226 коды стран (ISO), 640,646 коды сущностей для специальных символов, 270,272 c:out, тег, символы, отображаемые с помощью escape-последовательностей. 593 листинг с, 272 коды сущностей символов, 270,272 листинг, 272 символы, которые выводятся с помощью escape-последовательностей в Tere.c:out, 593 коды языков (ISO), 629 командный файл, для прекомпиляции JSP на Tomcat, 118 комментарии commit/) JSP. 21 класс Connection, 540.544 класс LoginModule, 370 коммерческие исполнительные подсистемы для сервлетов (servlet engines), xv раскомментированный элемент Connector в файле server.xml сервера Tomcat. 353 компиляция сервлетов, 22 (см. также прекомпилядия JSP) Использование инструмента Ant, 23 переменная окружения PATH для javac, 23 со сборочным файлом Ant, 100-103 консоль администрирования (WebLogic), xv конфигурирование JNDl-pecypca. 669 просмотр JNDI-дерева. 671 контейнеры сервлетов, 14 контекст включение файлов JSP, импорт содержимого извне, 164-167 по умолчанию, для сервлетов, 65 > контекст локализации установка в web.xml, 652 контекста сервлета (ServletContext), установка в JSP, 386-388 < - контекста сервлета (ServletContext), установка в сервлетах, 383-385 объекты, которые сервлеты привязывают к ServletContext, 383 сервлеты,которые привязывают объект к ServletContext, 385 конфигурирование страницы ошибок (error-page), 27 в web.xrrtl, пример, 147 перезаписанное объявлением директивы page. 229 страница: аутентификация на пройдена (/loginError.html), 359 элемент в web.xml для lOExceptions, 200 корневой logger, 332 добавление appender'a к, 330-332 корректные неполные запросы, 80 Л локализаторы, 628 локализация, 600 ‘Определение; 628 локаль (locale) использование JSTL-тегов форматирования д ля отображения сообщения в соответствии с, 642 определение локали клиента в JSP, 633-634 определение локали клиента в сервлете, 629-632 определение, 601,628 предпочитаемая, 629 форматирование даты для отображения eJSP.645 в сервлете!, 643 форматирование значений денежных сумм для в JSP, 648 649 в сервлете, 646-647 м массив cookies, 238,243 массивы ) из SortedMaps, перебор, 605 массив GoogleSearchResultElement. 712 перебор элементов с помощью тега c:forEach. 592 методы (JavaBean), соглашение об именовании для, 186 механизмы включения динамического содержимого, 137-167 включение XML-фрагмента в JSP-документ, 161-164 включение в JSP содержимого из-за пределов контекста, 164-167 включение содержимого в JSP при каждом запросе, 152-156 внешний файл конфигурации, использование для включения ресурсов в JSP. 157-160 импорт ресурса каждый раз, когда сервлет обрабатывает запрос запрос, 138-140 конфигурирование включаемого ресурса во внешнем файле, 141 ресурсы, вкладываемые в сервлет на нескольких уровнях. 144-149 атрибуты запроса, доступ со стороны включаемых ресурсов, 147 внешний включающий сервлет, 144 второй включаемый сервлет, 148 информация об исключениях, отображение. 148 первый внутренний включаемый сервлет, 146 754 | Предметный указатель
ресурсы, которые редко изменяются, включение в JSP, 149-152 модули, JavaScript организация и хранение, 289 пример. 289 проверка данных введенных в форму (validate.js), 300 мультимедиа, встраивание в JSP, 408-427 встраивание Flash-файла в сервлет, 419 встраивание SVG-файла в JSP. 424-425 встраивание апплета с помощью jsp:plugin, 408-410 встраивание апплета с помощью инструмента HtML-конвертор, 411-415 встраивание в JSP фонового звука. 425-427 встраивание ролика QuickTime в JSP, 421 шаблон HTML для встраивания Flash-файлов автоматическая генерация. 415 написание вручную, 418-419 н набор инструментов разработчика Web-сервисов (Java Web Services Developer Pack - WSDP), 589 набор инструментов разработчика Web-сервисов Java, скачивание JAXP, 134 начальные (welcome) web-приложения, 80 начальный контекст, 655 не воспроизводимое чтение, 543 неявно создаваемые объекты bJSP. 152 application, 386 exception, 229 pageContext, 266, 634 response, 224 session, 154 * в JSTL applicationScope, 391 cookie. 246, 616 header, 431,613 headerValues, 615 initParam, 298. 299 param. 173, 271, 425, 611 ' requestScope, 403 406 sessionScope. 396, 399,610 неявно создаваемые объекты, 247,299,391 неявно создаваемый JSTL-объект applicationScope, 391 неявно создаваемый объект application (приложение), 152,386 неявно создаваемый объект session, 152,154 нижний колонтитул, включаемый в JSP с помощью действия jsp: include, 155 номера портов, используемые безопасными HTTP-соединениями, в отличие от безопасных, 354 О области видимости для хранящихся объектов, 609 привязывание объектов, 382 область видимости page (страница), 396,609 область видимости запрос (request), 396,609 область видимости приложение (application), 396,609 привязка объекта к, 386 область видимости сеанс (session), 396,609 JNDI-объект, помешенный в...,с использованием фильтра, 666-667 обработчики тегов, 553 имя пакета для классов, 565 классический; 553 создание TLD по версии JSP 1.2 для, 558-560 создание, 554-558 упрощенный, 553 использование в JSP, 576 создание TLD для, 574 создание, 571 574 объект, реализующий страницу, 20 объекта, соглашение об именовании, 610 объекты request (запрос) и response (ответ), 15 страница ошибок, доступ к, 218 ограничение запросов к определенным сервлетам, 81-86 окна браузера, создание с помощью JavaScript, 290 в JSP, 298-299 в сервлете, 294-296 окна, браузер (см. окна браузера, создание с помощью JavaScript) определение, 382 определение, 679 остановка приложения на Tomcat с.помощью Ant, 112-114 ответы настройка с помощью компонента JavaBean, 456 фильтрация НТТР-ответов. 456-458 отвязывание объектов сеанса, слушатель для, 280 относительно контекста, 149 относительно страницы, 150 относительный путь к странице, 149 отправка не-HTML данных, 305-321 - PDF файлы, 306-310 XML-файлы, 313-315 аудио файлы, 316-318 просмотр внутренних ресурсов в сервлете, 319-321 файлы текстовыхредакторов, 311-313 отражение, использование в протоколировании, 341 п пакеты для сервлета и вспомогательных классов. 15 полностью квалифицированное имя класса и, 24 параметры запроса доступ с помощью неявно создаваемого EL-объекта. 271,611 получение для CallbackHandler, 370 параметры инициализации настройка для фильтра, 451 сервлет, зарегистрированное имя и, 73 пароли (см. также аутентификация) для файла keystore и цифрового сертификата, 352 создание в файле tomcat-users.xml, 351 Предметный указатель | 755
пары ключ/значение (cookies), 247 перебор присланных (posted) значений с помощью JSTL, 173 перебор элементов набора (collection) данных, 586 тег c:forEach, перебор элементов массива, 592 перезапись cookie, 249 перезапись URL, 234,252 использование в JSP 269-272 использование в сервлете, 272-275 переименование выгружаемы* файлов, 207-209 с помощью вашего Java-класса, 207 переменная окружения WLCLASSPATH, 120 переменные (с разными областями видимости), доступ с помощью EL, 396,609 перенаправление запросов инициация фильтра для, 460 объектами, реализующими интерфейс RequestDis- patcher, без включения ограничений безопасности, 83 с помощью forward!) (RequestDispatcher), 77 с помощью getNamedDispatcher() (ServletContext), 78 сервлет-контроллер перенаправляющий запросы с помощью RequestDispatcher,forward( ), 87 перенаправление запросов (после проверки корректности сеанса), 260 подключаемый модуль (Piug-in)Java, 409 HTML-конВертор, встраивание апплета в JSP. 411-415 наличие нескольких разных установленных версий, вызывает проблемы с загрузкой апплетов, 415 подключаемый модуль аутентификации (Pluggable Authentication Module - РАМ), 365-371 пожинание web-информации, 687-704 использование сервлета. 691-696 компонент JavaBean для синтаксического разбора HTML использование в JSP. 702-704 использование в сервлете, 700-702 разбор ЦТМЬ-странииы с помощью подпакетов пакета javax.swing.text, 688-691 callback-класс (пример), 688 691 создание компонента JavaBean, действующего как HTML-napcep, 697-699 полностью квалифицированные имена классов, 24 активизация зарегистрированного сервлета посредством, 73 имена атрибутов объекта и, 610 класса слушателя в элементе listener-class, 585 цользователи роль manager (Tomcat), 110 файл tomcat-users.xml, 82 имена пользователей, пароли, и роли в, 351 отображение нА роли безопасности, 85 пользовательские действия, 553 (см. также пользовательские теги) JSP использующая для доступа к log4j, 341 пользовательские теги, 20,337,553-585 cbck:log, тег уровень протоколирования, 341 TLD для пользовательского тега для logger, 339 вызовы функций, встроенные в шаблонный текст, 623 756 | Предметный указатель использование в JSP, 566 класс слушателя, добавление г библиотеке тегов, 584 обработка исключений в классе пользовательского тега, 568-570 связанные с файлом тега, использование. 582 создание TLD для упрощенного обработчика тега, 574 создание TLD по версии JSP 1.2 для классического обработчика тега, 558-560 создание TLD по версии JSP 2.0 для классического обработчика тега, 561 -563 создание классического обработчика тега для, 554-558 тег. использующий log4j, 337-339 упаковка библиотеки тегов в JAR-файл, 565 упаковка библиотеки тегов в web-приложение. 564 упаковка файла тега JSP в JAR, 581 упрощенный обработчик тега, использование в JSP, 576 упрощенный обработчик тега, создание, 571-574 файл конфигурации, включение вашего собственного, 342 файл тега JSP, создание, 578-580' файл тега, упаковка в web-приложСние, 580 пользовательский путь к классам, 91 пользовательский тег протоколирования, 339 пользовательский, представленный переменной окружения CLASSPATH, 91 последовательности, Oracle, 526 потовое сообщение (см. email) почтовые сеансы, 471,482 сервлет, получающий их при помощи JNDI на WebLogic, 672-675 почтовый сервер, взаимодействие с сервлетом, отправляющим почтовые сообщения, 476 правила использования примеров кода, xviii предпочитаемая локаль, 629 прекомпиляция JSP, 21,115 в Tomcat, 117 в WebLogic. 119-121 отображения сервлетов, 117 с протоколом прекомпиляции, 121 преобразование JSP в сервлет, 20 преобразование XML, использование JSTL-тегов, связанных с XML и XSLT, 597-599 приведение типов параметры запроса, 286 типа ServletRequest к HttpServletRequest, 445 привязанные объекты к ServletContext (контексту сервлета) с помощью JSP, 386-388 с помощью cepeiema, 383-385 объект Session (сеанс), для JNDI-реализации WebLogic, 669 слушатель привязки и отвязывания объектов сеанса, 280 приложение SVG Viewer от Adobe System, 424 приложения-просмотрщики для SVG, 424 программируемая безопасность, 82 просмотр внутренних ресурсов в сервлете, 319-321
просмо । рщик SVG-файлов фирмы Corel, 424 прост ране । но имен, 553 атрибут xmliis элемента taglib, 563 для XML-элементов, связанных с экземплярами XML-СхемЫ, 563 определение, 563 определяемое по атрибуту prefix директивы taglib, 566 протоколирование, 322-349 appender. 324 добавление к корневому logger'у, 330-332 layout (разметка), 324 log4j и слушатель событий контекста сервлеУа. использование, 343-346 log4j, использование в JSP, 337-342 LogFilter (пример), отображенный на сервлет, 443 logger. 322 использование без файла конфигурации, 327 создание собственного и назначение ему appender'a, . 332-336 запросы, 439 с сервлетом, используюшим слушатель событий сеанса, использование. 347-349 установка log4j, 326.327 фильтр, который протоколиркет некоторую информацию (пример), 444 < проценты в JSP, 651-652 в сервлете, 649-650 псевдонимы сервлетов, созданные в файле web.xml, 64-66 . путь к контексту для web-приложения, 25 атрибут path элемента Context (Tomcat), 50 путь относительно контекста, 149 Р развертывание отдельного сервлета на, 29-34 шаги по30 Jasper (контейнер JSP), 128 JNDI-объект, настройка, 656-658 JNDI-pecypc доступ из JSP, 665-668 доступ из сервлета, 659-665 JSTL 1.1, особенности, использование в версией 590 security-constraint, элемент в web.xml, использование, 83 SSL, установка, 352 включение JAR-файлов ₽ путь к классам сборочного файла Ant, 96-99 время простоя сеанса, установка для всех web-приложеннй 256 вызывающий сервлет. 71 раскомментированце 72 запуск web-приложения с помощью Ant, 109-112 класс реализации страницы JSP. просмотр, 115 начальный контекст встроенной реализации JNDI, 655 неявное отображение на компилятор JSP и выполнение сервлета для запросов к .jsp. 69 остановка web-приложения с помощью Ant. 112-114 прекомпиляция JSPsb, 116 развертывание web-приложения на. 44-50 сборочный файл Ant, использование, 44-50 указание внешнего каталога, содержащего web-приложение, 49 развертывание сервлета как часть элемента Context в server.xml, 34 страница ошибки, отображаемая сервлетом, обрабатывающим ошибки. 221 файлы протокола, 325 развертывание сервлетов и JSP, 29-62 отдельная страница JSP. развертывание на Tomcat, 41 отдельная страница JSP. развертывание на WebLogic, 43 отдельный сервлет- развертывание на Tomcat, 29-34 инструмент Ant, использование, 29-34 шаги процесса, 30 отдельный сервлет, развертывание на WebLogic Server 7.0,36-41 переразвертывание web-приложения, 37 редактирование файла web.xml для регистрации сервлета, 36 файл Ап/, использование, 37-41 развертывание как часть элемента Context в файле server.xml сервера Tomcat, 34 распределенные вычисления, SOAP, как простая форма, 707 расширение файла связь MIME-типов с, 499 реестр RMI (Remote Method Invocation registry - реестр вызова методов удаленных объектов), 655 роли безопасности, 83 nullrole, предотвращение отображения пользователей на, в файле tomcat-users.xml. 88 роли пользователей (см. роли) роли, 351 задание в элементе auth-constraint. 356 контроль пользователей, обращающихся к сервлету, 363 руководства, online (Sun Microsystems), 13 С самоподписанный цифровой сертификат, создание для Tomcat, 352 сборочные файлы (Ant), 30 JSP, осуществляющая разбор файла с помощью XML-тегов JSTL, 594 wl.properties для файда Ant на WebLogic, 40 выполнение нескольких целей в заданной последовательности, 94 имена, отличные от build.xml, 94 импорт файла build.properties в. 46 командная строка для выполнения, 92 компиляция сервлета с помощью. 100-103 преобразование в HTML. используя таблицу стилей XSL 597 приложение Tomcat Manager, использование из, 109 развертывание web-приложения (пример). 46-50 редактирование для развертывания на WebLpgic 7.0,51 функции, 33 Предметный указатель | 757
свойство already .deployed (Апифайл build.xml), 48 сеансовый компонент EJB, не сохраняющий состояния (stateless Session EJB), 679-681 сеансы, 347 время простоя, установка в web.xml, 253-255 в коде сервлета. 257-J59 для всех приложений на Tomcat 256 завершение сеанса пользователя, 362 настройка в файле web.xml для API сервлетов версии 2.3,28 определение, 251 отслеживание активности сеанса eJSP, 264-268 в сервлетах, 261-264 отслеживание атрибутов сеанса с помощью слушателя, 279-282 отслеживание атрибутов сеанса с помощью фильтра, 283-287 отслеживание жизненного цикла с помощью слушателя, 275-279 отслеживание с помощью перезаписи URL в JSP, 269 272 в сервлетах, 272-275 проверка существования ... в HttpServletRequest, 259-261 сегменты, JSP, 151 серверы приложений, xiv BEA WebLogic. рецепты книги, посвященные, xv , серверы, приложение, xiv сервлет, принимающий термы для поиска по Amazon, 729 сервлет-контроллер (controller), 73-76 ограниченный доступ к определенным сервлетам, установка, 87-89 t отображение всех запросов при консервации всех отображений на сервлеты, 76 сервлеты, xiii, 22 JavaServer Pages (см. JSP) дескриптор развертывания для API сервлета версии 2.3,27 дескриптор развертывания, создание для A Pl cepejiema версии 2.4, 28 именование (см. именование сервлетов) компиляция (см. компиляция сервлетов! написание, 13-19 жизненный цикл, управление. 15 класс First Servlet (пример), 15 пакеты, создание для, 15 преобразование JSP в, 20 развертывание (см. развертывание сервлетов и JSPs) сервер приложений как программный хост для, xiv упаковка в WAR-файлы. 24 Символы символы подстановки в шаблонах URL, 67 перезапись специфическими отображениями, 74 символы, выводимые с помощью escape-последовательностей, тег c:out, 593 синтаксический разбор HTML с помощью подпакетов javax.swing.text, 688-691 JSP перед преобразованием в реализацию страницы. 116 XML • JAXP-совместимые синтаксические анализаторы, 91 xtparse, тег, 595 скачивание набора для разработки Web-сервисов Java (Java Web Services Developer Pack), 589 скриптлеты, 522 слушатели верификация данных, введенных в форму с помощью, 301 добавление класса слушателя к библиотеке тегов, 584 запросы, отслеживание для web-приложения, 439-441 мониторинг атрибутов сеанса, 279-282 растройка в файле web.xml (API сервлета версии 2.3), 28 отслеживание жизненного цикла сеанса, 275-279 проверка НТТР-запрсов с помощью, 79 слушатель событий контекста сервлета, использование в протоколировании. 343-346 событий приложения, 343 событий сеанса, 343 использование в протоколировании, 347-349 слушатели событий приложения, 343 слушатели событий сеанса, 342 соглашения по именованию, Iog4j, 333 содержимое (контент) (см. также динамически включаемое содержимое; статическое содержимое) не-HTML, отправка, 305-321 PDF файлы. 306-310 XML-файлы, 313-315 аудио файлы, 316-318 t просмотр внутренних ресурсов в сервлете, 319-321 файлы текстовых редакторов. 311-313 соединения, база данных, 505 DataSource (источник данных) как фабрика для, 603 открытие и закрытие, 515 сервлет использующий пул соединений WebLogic, 518-522 создание пула соединений с помощью консоли WebLogic, 516 создание нового окна браузера в сервлете, 294-296 включение модулей JavaScript в JSP, 293-294 в сервлет, 289-292 проверка значений, введенных в форму eJSP.303 в сервлет, 299-302 сокетное соединение с сервером базы данных, 603 состояние клиента, 232 спецификаторы преобразования в языке шаблонов, 334 справочная реализация, xiv для JSTL, 587 срок хранения coqkies установка с помощью jsp:setProperty, 239 установка. 236 758 | Предметный указатель
стандартная библиотека тегов JSP (см. JSTL) статичная страница для мультимедиа- содержимого, 408 статичное содержимое запросы кперехватываемые сервлетом- контроллером, 76 Отображение на сервлет, 70 производительность сервера и, 140 статичные методы, Java-метод, реализующий EL-функцию для страницы JSP, 532 строки запроса добавление к ним параметра с помощью JSP, 191 добавление к ним параметра с помощью сервлета, 189 запрос, добавление параметра к, 434 структура каталогов web-приложений, 24 для приложения, развертываемого с помощью инструмента Ant, 45' пример, 25 файлыЗБРв, 42 формат - - "распакованный по каталогам", 56 структура наследования в log4j, 332,346,349 ’ сценарии командной оболочки Tomcat, завершение работы,. 30 для прекомпиляции 38Р-файлов(на Unix), 118 для прекомпиляции всех страниц JSP в приложении (на Unix), 120 т таблицы стилей (XSL), интеграция в JSP, 597 тег applet (HTML), 412 теги форматирования (JSTL), 600-602 отображение даты на странице JSP, в соответствии с локалью пользователя, 645 отображение текста на странице JSP, в соответствии с локалью запроса. 642 текстовые файлы, страницы JSP, написанные как, 19 типы данных long возвращаемые методами класса HttpSession, связанными с датами, 264,266 типы содержимого multipart (несколько 4aereii)/form-data ' (данные формы). 195 . почтовое сообщение, 485. 489 из нескольких частей (Multipart), 489 Транзакции выполнение нескольких SQL-операторов в одной транзакции, 539-544 сервлет, использующий транзакцию, 540-543 использование в JSP, 545-548 . создание с помощью методов Connection, 539 уровни изоляции, 544 У удаление cookie, 249 упрощенный обработчик тега, 553 использование в JSP, 576 создание (JSP 2.0). 571 -574 создание TLD для, 574 уровни изоляции для транзакций, 544 в теге sqlrtransaction, 547 установка из сервлета, 234-239 сохранение запрошенныхсоо1ае в массиве, 238 установка объекта в качестве атрибута сеанса, 666-667 MultipartFilter, класс, 209 верификация данных, введенных в форму с помощью фильтра, 462-467 возможное блокирование запроса с помощью фильтра. 453-455 заголовки запроса, изменение, 432-436 ' использование фильтра с объектами RequestDispatcher, 459-461 мониторинг атрибутов сеанса. 283-287 отображение нескольких фильтров на сервлет. 448-449 отображение фильтра на сервлет, 443-446 фильтр, который протоколирует некоторую информацию, 444 > отображение фильтра на страницу JSP, 446 передача JNDI-объекта в JSP на WebLogic, 675-678 Ф фаза запроса (JSP), 20 фаза трансляции (для JSP), 20,115 включение JSP-сегмента в страницу JSP, 150 файл манифеста для JAR-файлов, 109 файл свойств, ResourceBundle как, 635 файлы тегов, 553 пользовательский тег, связанный с.... использование, 582 создание. 578-580 упаковка в JAR, 581 упаковка в web-приложение, 580 файлы текстовых редакторов, отправка, 311-313 файлы формата Portable Document Format (PDF), 306-310 s фантомное чтение, 543 фильтрация, 442-470 • HTTP-ответы, 456-45$ - JNDI-объект, доступ из JSP, 665-668 блокирование IP-адресов с помоШью, 467-470 изменение порядка в котором фильтры применяются к сервлетам. 450 настройка параметров инициализации для фильтра. 451 . настройка фильтра в файле web.xml'(API сервлетов версии 2.3), 28 перехват и чтение из входного потока, 192-194 фоновый звук, встраивание в JSP, 425-427 формат "распакованный по каталогам", 56 фрагменты, JSP, 573 включение в серВлет используя RequestDispatcher 142 теперь известные как сегменты JSP. 151 . функции JavaScript. 290 JSTL использование в JSP. 623-627 листинг, с указанием назначения каждой, 625 Предметный указатель | 759
язык выражений, вызывающий хранимые процедуры. 530-534 Java-класс, реализующий функции, 530-532 JSP, использование в, 533 TLD-файл для конфигурирования функции, 532 X ход выгрузки файлов, отображение для пользователя, 310 хранилища сообщений, 471,482 хранимые процедуры вызов из JSP, 530-534 вызов из сервлета, 525-529 ц цели (Ant), 93-96 в build xml, демонстрация имен, 595 цифровой сертификат, создание для сервера Tbmcat, 352 Ч черновое чтение, 543 числа, форматирование с помощью JSTL-тегов, 600 fmt.setLocale, тег, 601 чувствительность к контексту имя и пароль пользователя в файле tomcat-users.xml, 355 шаблон url в элементах servlet-mapping, 66 ш шаблон границы, для разделения файлов в HTTP-запросах на выгрузку, 195 шаблоны URL для расширенного отображения, 68 ассоцирование фильтра со всеми файлами .jsp, 447 - отображение всех ссылок к страницам JSP на один сервлет, 69 отображение статического содержимого на сервлет, 70 шаблоны преобразования, разметка прото- колируемых сообщений, 324 Э элементы, XML в файле web.xml для API сервлетов версии 2.3,27 в файле web.xml для API сервлетоЬ версии 2.4,28 * задачи Ant, 92 порядок в корректном XML-файле, 80 файл build.xml (Ant), 33 элемент Context (файл server.xmlcepBepa Tomcat), 34 я язык выражений (EL) (см. язык выражений) язык шаблонов для преобразования разметок протоколируемых сообщений, 334 языковые предпочтения, установка в Netscape 7.1,630 760 | Предметный указатель
Содержание Предисловие..............................................................................................................._ 5 Что вы найдете в этой книге .......................................................................................... 7 Аудитория............................................................................................................. 8 Организация........................................................................................................... 9 Соглашения, используемые в этой книге..................................................................................10 Использование примеров кода............................................................................................11 Вопросы и замечания...........................................,.............................11 Благодарности..........................................................................................................12 Глава. 1. Создание сервлетов и JSP..................................................................................... 13 Введение ............................................................................. ...............................13 Создание сервлетов.....................................................................................................13 Создание JSP......................................................................................................... 19 Компиляция сервлетов.................................................................................................. 22 Упаковка сервлетов и JSP............:................................................................................ 24 Создание дескриптора развертывания.....................................................................................26 Глава. 2. Развертывание сервлетов и JSP.......................................................................... 29 Введение........................................................................................................... 29 Развертывание отдельного сервлета.................................................................................... 29 Использование элемента Context в файле server.xml для Tomcat...........................................................34 Развертывание отдельного сервлета в WebLogic.......................;...................................................36 Развертывание одиночной страницы JSP под Tomcat........................................................................41 Развертывание одиночной страницы JSP на WebLogic.......................................................................43 Развертывание web-приложения на сервере Tomcat ........................................................................44 Развертывание web-приложения на WebLogic при помощи Ant................................................................50 Использование консоли а, (министрирования WebLogic.....................................................................52 Использование компоновщика WebLogic Builder ' для развертывания web-приложения'.................................................................................. 55 Использование инструмента командной строки weblogic.Deployer...........................................................60 Глава. 3. Присвоение имен сервлетам................................................................................ 63 Введение........................................................................................................... 63 Отображение сервлета на определенное имя с помощью файла web.xml.............................. 64 Создание более одного отображения на сервлет......................................................................... 66 Создание для сервлетов URL как у JSP ......................-...........................................................68 Отображение на сервлет обращений к статичному содержимому.......................»......................................70 Вызов сервлетов, не имеющих отображения в web.xml...........................................71 Содержание | 761
Отображение на сервлет всех запросов к web-приложенпю.............................73 Отображение запросов на контроллер с сохранением отображений сервлетов...........76 Создание начальных (welcome) файлов web-приложения................................80 Ограничение запросов к определенным сервлетам................................... 81 Предоставление доступа к определенным сервлетам только для контроллера...........87 Глава. 4. .Использование Apache Ant.................................................................................. 90 Введение..........................................................................90 Как получить и установить Ant ......................... ..................... 91 Использование целей Ant.................................................... ,....93 Включение JAR-файлов Tomcat в путь к классам сборочного файла ....................96 Компиляция сервлета ,с помощью сборочного файла Ant............................ 100 Создание WAR-файла с помощью Ant............»................................ 103 Создание JAR-файла с помощью Ant............................................... 106 Запуск с помощью Ant приложения на Tomcat................... .....,..............109 Остановка Tomcat с помощью Ant............-..................................... 112 Глава. 5. Различные форматы страниц JSP ................................................................... 115 Введение.........................................................................115 Прекомпиляция JSP в Tomcat ...........................................1..........117 Прекомпиляция JSP в WebLogic..................;..................................119 Прекрмпиляция JSP с помощью протокола .прекомпиляции.............................121 Отображение страницы JSP на соответствующий ей класс реализации страницы . 122 Создание JSP в виде JSP-документа «с нуля»...................................... 124 Генерирование XML-представления страницы JSP ............................ ....128 'Глава. 6. Динамически подключаемое содержимое страниц JSP и сервлетов........ 137 Введение.............................................,...........................137 Подключение ресурса при каждой обработке запроса ...................,............138 Использование внешней настройки включения ресурса в сервлет......................141 Включение в сервлет ресурсов с несколькими уровнями вложенности..................144 Включение в JSP редко изменяемых ресурсов .................................... 149 Включение содержимого в JSP при каждой обработке запроса.........................152 Использование внешнего файла конфигурации для включения ресурсов в JSP...........156 Включение фрагмента XML в JSP-документ..................................... 161 Включение содержимого, находящегося за пределами контекста JSP...................164 Глава. 7« Обработка данных от web-формы в сервлетах и JSP.................................. 168 Введение.........................................................................168 Обработка НТТР-запроса POST в сервлете.............................<.............169 Обработка НТТР-запроса POST в JSP............................................. 173 Установка свойств компонента JavaBean в JSP......................................„ 175 Присвоение в JSP значения параметра формы атрибуту, принадлежащему определенной области видимости ................................. 179 Передача данных (POST) от сервлета.................,л..4....................... 182 Пересылка данных методом POST из JSP ............................................185 762 | Содержание
Использование сервлета для добавления параметра в строку запроса.......................189. Использование JSP для добавления йараметра к строке запроса............................191 Использование фильтра для чтения значений параметров ..................................192 Глава. 8. Выгрузка файлов_____________....................................................................................... 195 Введение............„................................................................ 195 Подготовка HTML-страницы для выгрузки файлов................................ .'.......196 Использование библиотеки com.oreilly.servlet j.........................................198 Выгрузка одного файла.............._____.’........................................... 200 Выгрузка нескольких файлов.............................................................202 Изменение имен файлов..................................................................207 Использование JSP для обработки выгрузки файлов...............J........................209 Глава. 9. Обработка исключений в web-приложениях.................................................. 214 Введение........................................................... 4..................214 Объявление обработчиков ошибок в web.xml...............................................214 Создание сервлета, обрабатывающего исключения..........................................218 Возврат из Сервлета сообщения об ошибке................................................221 Возврат сообщения об ошибке из JSP................................................. 224 Создание страницы JSP, обрабатывающей ошибки ..........................................225 Объявление специальной JSP, обрабатывающей ошибки, возникающие в других JSP................,..............................................228 Глава. 10. Чтение и запись Cookie..................................................................................... 232 Введение...............................................................................232 Установка cookie из сервлета...........................................................234 СозДание массива всех cookie, переданных в запросе................................... 238 Установка cookie из JSP.................;..............................................239 Чтение значения cookie в сервлете........................................... ;.......243 Чтение значения cookie в JSP..................................................... 246 Изменение значения или удаление существующего cookie...................................249 Глава. 11. Отслеживание сеанса .................................................................................—..251 Введение...............................................................................251 Установка в web.xml времени простоя сеанса (time out)..................................253 Установка времени простоя для всех web-приложений, работающих на Tomcat... 256 Программная установка времени простоя сеанса...........................................257 Проверка запроса HttpServletRequest - существует ли для него сеанс ...................259 Отслеживание активности сеанса в сервлете..............................................261 Отслеживание активности сеанса в JSP...................................................264 Использование в JSP метода перезаписи URL................................ ............269 Использование перезаписи URI в сервлетах.............................................. 272 Использование слушателя для отслеживания жизненного цикла сеанса.......................275 Использование слушателя для наблюдения за атрибутами сеанса............................279 Использование фильтра для наблюдения за атрибутами сеанса .............................283 Содержание | 763
Глава. 12. Интеграция JavaScript с сервлетами и JSP................._________.................288 Введение........................................................................ 288 Включение модулей JavaScript в сервлет ................. .............-........289 Включенйе модулей JavaScript в JSP........................................... „.293 Создание из сервлета, с помощью JavaScript, нового окна......................... 294 Создание нового окна из JSP при помощи JavaScript.................................298 Использование JavaScript в сервлете для проверки,данных, введенных в форму ...299 Использование JavaScript в JSP для проверки значений, введенных в форму..........303 Глава. 13. Отправка не-HTML содержимого......................................;.............»............. 305 Введение........................................................................ 305 Отправка PDF-файла........................./................................... „306 Отправка файла, созданного в Word .............................................. 311 Отправка XML-файла................................................................313 Отправка аудиофайлов .............................................................316 Просмотр внутренних ресурсов в сервлете...........................................319 Глава. 14. Протоколирование сообщений сервлетов и JSP.......................................... 322 Введение.........:.......................................... ....................322 Протоколирование без Log4j...................................................... 324 Установка Log4j‘................................................................ 326 Использование logger без файла конфигурации ......................................327 Добавление Appender к корневому Logger................,......... ................330 Использование шаблона для appender.............................................. 332 Использование log4j в JSP.............................,...........................337 Протоколирование сообщений слушателя событий контекста сервлета..................л 343 Протоколирование сообщений слушателя событий сеанса...............................347 Глава. 15. Аутентификация клиентов............___...._____..... ..................................... 350 Введение..........................................J...............................350 Создание пользователей и паролей в Tomcat....................................... 350 Установка SSL-соединения в Tomcat.................................................352 Использование BASIC-аутентификацИи............................................ 354 Аутентификация с использованием формы.............................................358 Завершение работы с аутентифицированным пользователем.............................362 Использование JAAS для создания аутентифицирующего модуля.........................365 Создание файла конфигурации JAAS ............................................... 372 Использование JAAS в сервлетах.....................,..'......................... 375 Использование JAAS в JSP......................................................... 377 Глава. 16. Привязка, доступ и удаление атрибутов в web-приложениях ..................382 Введение...................... '.................................................382 Задание в сервлетах атрибутов контекста сервлета................................. 383 Установка атрибутов контекста сервлета (объекта ServletContext) с помощью JSP............................................386 Доступ и удаление в сервлете атрибутов контекста сервлета.........................389 764 | Содержание
Доступ из JSP к атрибуту контекста сервлета или его удаление ...........391 Установка из сервлета атрибутов сеанса..................................393 Установка атрибутов сеанса из JSP.............................. .>....395 Доступ, из сервлета к атрибутам сеанса и их удаление ...................397 Доступ и удаление атрибута сеанса с помощью JSP.........................399 Установка атрибутов запроса с помощью сервлета............'.............400 Установка атрибутов запроса с помощью JSP...............................401 Использование сервлета для доступа к атрибутам запроса и их удаления.....404 Использование JSP для доступа к атрибутам запроса и их удаления.........406 ' Глава. 17. Внедрение мультимедиа в страницы JSP...*..................M.........M.....M............4O8 Введение.............................................................. 408 Внедрение апплета в JSP с использованием jsp:plugin......................408 Внедрение апплета в страницу JSP с помощью HTML-конвертера .............411 Автоматическое создание шаблона HTML для включения Flash-файлов..........415 Собственноручное создание шаблона HTML для внедрения файла Flash.........418 Внедрение файлов Flash в сервлет....*............................... 419 Внедрение в JSP фильмов в формате QuickTime............................ 421 Внедрение SVG-файла в JSP..........................................:....424 . Внедрение фонового звука в JSP.........................................425 Глава. 18. Работа с запросом клиента............................................................................... 428 Введение........................................................... ...428 Анализ заголовков HTTP-запроса в сервлете...............................429 Анализ заголовков HTTP-запроса в JSP ................................. 431 Использование фильтра для модификации заголовков запроса................432 Автоматическое обновление сервлета......................................436 Автоматическое обновление JSP...........................................438 Подсчет числа запросов к web-прйложению............................... 439 Глава. 19. Фильтрация запросов и ответов...—442 Введение............................................................. 442 Отображение фильтра на сервлет.........................................443 Отображение фильтра на JSP............................................ 446 Отображение на сервлет более одного фильтра.............................448 Изменение порядка, в котором фильтры применяются к сервлетам.......... 450 Настройка параметров инициализации для фильтра......,...................451 Возможное блокирование запроса с помощью фильтра........................453 Фильтрация НТТР-ответа............................................ 456 Использование фильтров с объектами RequestDispatcher....................459 Проверка параметров формы с помощью фильтра..................,........ 462 Блокирование IP-адреса с помощью фильтра ................................467 Содержание | 765
Глава. 20. Работа с электронной почтой в сервлетах й JSP.................._...................471 Введение........................................................;..............471 Размещение пути к классам, имеющим отношение к электронной почте, в вашем пути к классам ................................. 472 Отправка почтового сообщения из сервлета..................................... 473 Отправка из сервлета почтового сообщения с использованием компонента JavaBean ...».................................. 477 Доступ к почтовому сообщению из сервлета.......................................481 Доступ из Сервлета к электронной почте 4 с использованием компонента JavaBean....................*..»................ .487 Работа с вложениями в почтовые сообщения, полученные сервлетом ................488 ' Добавление вложения к почтовому сообщению в сервлете..........................495 Чтение в сервлете заголовков полученного почтового сообщения ..................500 Глава. 21. Доступ к базам данных.......__............._................................................505 Введение......................................,................................505 Доступ из сервлета к базе данных без использования DataSource..................506 Настройка DataSource в Tomcat.................................................509 Использование DataSource в сервлетах на сервере Tomcat.........................512 Создание DataSource на сервере WebLogic .......................................515 Использование JNDI-поиска для доступа к DataSource из WebLogic.................518 Использование DataSource страницами JSP на сервере WebLogic ...................522 Вызов хранимых процедур из сервлета .......................................... 525 Вызов хранимой процедуры из JSP.....................................’..........530 Преобразование объекта java.sql.ResultSet к javax.servlet.jsp.jstl.sql.Result .534 Выполнение нескольких SQL-операторов в рамках одной транзакции.................539 Использование транзакций в JSP............................................... 545 Получение информации о ResultSet............................................. 548 Глава. 22. Использование пользовательской библиотеки тегов...........__..................553 Введение......................................................................,553 Создание классического обработчика тега........................................554 Создание TLD (по версии JSP 1.2) для классического обработчика тега ,..........558 Создание TLD-файла (по версии JSP 2.0) для классического обработчика тега.....561 Как включить библиотеку тегов в web-приложение .............'................ 564 Упаковка библиотеки тегов в JAR-файл...........................................565 Использование пользовательского тега в JSP .................................. 566 Обработка исключений в классе пользовательского тега...........................568 Создание упрощенного обработчика тега .........................................571 Создание TLD для упрощенного обработчика тега............'.....................574 Использование упрощенного обработчика тега в JSP...............................576 Создание файла тега JSP........................................................578 Упаковка файла тега JSP в web-приложение.......................................580 Упаковка файла тега JSP в JAR ...............................................581 Использование пользовательского тега, связанного с файлом тега.................582 Добавление к библиотеке тегов класса слушателя.................................584 766 | Содержание
Глава. 23. Использование JSTL.......................W.................................................................. 586 Введение..........'........................................................... 586 Скачивание JSTL 1.0 и использование тегов JSTL на страницах JSP...................587 Скачивание пакета разработчика web-сервисов Java......................:.............. 589 Использование тегов ядра JSTL ................................................ 590 Использование тегов JSTL, связанных с обработкой XML .............................594 Использование тегов преобразования XML...........?....................J............т. 597 Использование JSTL-тегов форматирования ..........................................600 Использование SQL-тегов JSTL с настройкой DataSource .............................603 Использование SQL-тегов библиотеки JSTL без предварительной настройки DataSource..........................................607 Доступ к переменным, имеющим различные области видимости, с помощью EL . 609 Доступ к параметрам запроса с помощью EL..........................................611 Использование EL для доступа к заголовкам запроса.................................613 Использование EL для доступа к конкретному заголовку запроса......................615 Доступ к cookie с помощью EL.................................................. 616 Использование EL для доступа к свойствам компонентов JavaBean.....................618 Использование функций JSTL...................................................... 623 Глава. 24. Интернационализация...................................................................................... 628 Введение..........................................................................628 Определение локали клиента в сервлете ............................................629 Определение всех локалей клиента в JSP....................................i.......633 Создание ResourceBilndle в виде файла свойств..................................,..635 Создание ResourceBundle в виде Java-класса.................................... 637 Использование Res< (urceBundlee сервлете........................................ 638 Использование ResourceBundle в JSP................................................641 Форматирование дат в сервлете.................................................. 643. - Форматирование дат в JSP ...........................л...................:......... 645 . Форматирование денежных сумм в серьлете..........................................646/ Форматирование денежных сумм в JSP.............................................. 648 Форматирование процентов в сервлете ..............................................650 Форматирование процентов в JSP ...................................................652 Установка контекста локализации в дескрипторе развертывания.......................653 Содержание | 767
Глава. 25. Использование JNDI и компонентов Enterprise JavaBean........._________________..........655 Введение................................................................................. 655 Конфигурирование JNDI-объекта на Tomcat .................................................. 656 Доступ из сервлета к JNDI-pecypcy Tomcat....................................................659 ' Доступ к JNDI-pecypcy сервера Tomcat из JSP............................................... 665 Конфигурирование JNDI-pecypca в WebLogic.....................................................669 ' Просмотр JNDI-дерева на сервере WebLogic ............................................... 671 Доступ из сервлета к JNDI-pecypcy на сервере WebLogic..................................... 672 Доступ к JNDI-pecypcy WebLogic из JSP............................„.........................675 Доступ к компоненту EJB с использованием JNDI-дерева WebLogic..............................678 Глава. 26. Пожинание информации из Web..................................................................... 687 Введение............................................................................... 687 Синтаксический разбор HTML-страницы....................................................... 688 Использование серв тегов для пожинания Web-данных..........................................691 Создание компонента JavaBean - синтаксического анализатора Web-страницы .... 697 Использование в сервлете компонента JavaBean................................................700 Использование в JSP компонента JavaBean................................................... 702 Глава. 27. Использование Web API сайтов Google и Amazon....................................... 705 Введение...................................................................................705 Предварительная подготовка к работе с API Web-сервисов Google................................708 ' Создание компонента JavaBean для соединения с Google.......................................709 Использование сервлета для соединения с Google..............................................713 Использование JSP для соединения с Google...................................................717 Подготовка к использованию API Web-сервисов Amazon..........................................719 Создание компонента JavaBean для соединения сJ\mazon...................................... 721 Использование сервлета для соединения с Amazon .............................................726 Использование JSP для соединения с Amazon...................................................729 Глава. 28. Предметный указатель...................................................................................... 586 Глава. 29. Содержание.......................................................................................................... 762 768 | Содержание . «1
категория > программирование Java, сервлеты и JSP,.: сборник рецептов "ТСервлеты и JSP относятся к "web-уровню" архитектуры Java Enterprise. Эти компоненты работают на сервере и инициируют выполнение типовых задач, связанных с web, например обеспечение взаимодействия с почтовыми серверами, базами данных, компонентами EJB и web-сервисами. 4*^ ' Сборник рецептов по Java сервлетам и JSP содержит свыше 230 рецептов по ряду _____________ важнейших повседневных web-технологий с использованием в качестве контейнеров сервлетов Tomcat и BEA WebLogic. Книга начинается с вводных рецептов, посвященных упаковке сервлетов и JSP, созданию дескриптора развертывания, развертыванию сервлетов и JSP с помощью Apache Ant, прекомпиляции JSP и созданию JSP в виде XML-документов. Книга заполнена большим количеством рецептов по решению сложных рутинных задач разработки: • работа с сеансами, фильтрами, пользовательскими тегами, библиотекой JSTL • аутентификация web-клиентов • взаимодействие с базами данных и почтовыми серверами • чтение и установка cookie • выгрузка файлов от клиентов • интеграция JavaScript с сервлетами и JSP • встраивание мультимедийных файлов (например, цифровых видеороликов и музыки) в JSP и сервлеты • работа с web-клиентами, "говорящими" на разных языках, - интернационализация • протоколирование сообщений от сервлетов и JSP • динамическое включение фрагментов содержимого, как это делается в традиционном коде включения на стороне сервера (SSI) • использование в JSP или сервлетах API Web-сервисов сайтов Amazon и Google В рецептах также раскрывается множество новых возможностей, связанных со спецификацией сервлета версии 2.4 и спецификацией JSP версии 2.0, включая ServletRequestListener, а также с новыми функциями JSTL 1.1, использованием элементов JSTL внутри шаблонного текста, файлами тегов, дескриптором развертывания на базе XML-схемы. Брюс У. Перри (Bruce W. Perry) - независимый разработчик программного обеспечения и писатель. С 1996 года он разрабатывает web-приложения и базы данных для различных некоммерческих организаций, дизайнерских и маркетинговых фирм, а также рекламных агентств. В промежутках, когда он не пишет и не работает над ПО, он любит кататься на велосипеде, а также путешествовать по Соединенным Штатам и Швейцарии. Он живет в городе Ньюберипорт (Newburyport), штат Массачусетс (Massachusetts), с женой Стейси ЛеБарон (Stacy LeBaron) и двумя детьми, Рейчел (Rachel) и Скоттом (Scott). Посетите Web-сайт O'Reilly по адресу www.oreilly.com По вопросу приобретения книг обращайтесь в издательство по тел.: 333-82-11, с II00 до 1700. Наш адрес: ул. Профсоюзная, д. 84/32, под. 6, эт. 11. м. Калужскаг ISBN 5-9579-0073-7 КУДИЦ-ОБРАЗ Тел./факс: (095) 333-82-11, 333-65-67 E-mail: zakaz@okc.ru; http://books.kudits.ru 121354, Москва, а/я 18, "КУДИЦ-ОБРАЗ" 9 785957 900733