/
Автор: Дейтел Х.М. Дейтел П.Дж. Сантри С.И.
Теги: языки программирования компьютерные технологии программирование язык программирования java
ISBN: 5-9518-0034-X
Год: 2003
Текст
Этот файл был взят с сайта
http://all-ebooks.com
Данный файл представлен исключительно в
ознакомительных целях. После ознакомления с
содержанием данного файла Вам следует его
незамедлительно удалить. Сохраняя данный файл
вы несете ответственность в соответствии с
законодательством.
Любое коммерческое и иное использование кроме
предварительного ознакомления запрещено.
Публикация данного документа не преследует за
собой никакой коммерческой выгоды.
Эта книга способствует профессиональному росту
читателей и является рекламой бумажных изданий.
Все авторские права принадлежат их уважаемым владельцам.
Если Вы являетесь автором данной книги и её распространение
ущемляет Ваши авторские права или если Вы хотите
внести изменения в данный документ или опубликовать
новую книгу свяжитесь с нами по email.
Х.М. Деител, П.Дж. Деител, СИ. Сантри
ТЕХНОЛОГИИ
ПРОГРАММИРОВАНИЯ НА
Java™ 2
КНИГА 3
КОРПОРАТИВНЫЕ СИСТЕМЫ, СЕРВЛЕГЫ, JSP, WEB-СЕРВИСЫ
Перевод
с английского
под редакцией
Ю.А. Левчука
Москва
Издательство БИНОМ
2003
УДК 004.43
ББК 32.973.26-018.1
Д27
Х.М. Дейтсл, П.Дж. Дсйтсл, СИ. Сантри
Технологии программирования на Java 2: Книга 3. Корпоративные системы,
сервлеты, JSP, Web-сервисы. Пер. с англ. — М.: ООО «Бином-Пресс», 2003 г. —
672 с: ил.
Предлагаемая книга является переводом третьей части оригинального издания
«Advanced Java 2 Platform Mow to Program». Оригинал содержит более 1800 страниц,
поэтому было принято решение русское издание разбить на три части. Первая часть
посвящена созданию графического пользовательского интерфейса, двухмерной и трехмерной
графике, компонентам JavaBeans, взаимодействию с базами данных. Вторая часть
посвящена созданию распределенных приложении, а третья часть, которую вы держите в
руках, посвящена созданию серверных приложений и корпоративных систем.
В первых главах книги рассматривается применение технологий сервлетов и
JavaServer Pages для создания серверных приложений. После этого читатели
познакомятся с технологиями Enterprise JavaBeans, J2ME, Java Message Service и SOAP, которые
находят применение при создании корпоративных систем. Будут также рассмотрены
некоторые популярные серверы приложений. Книга насыщена многочисленными
примерами и упражнениями. Особое место занимает большой практический пример приложения
для электронного бизнеса, в котором нашли отражение все рассматриваемые а книге
технологии.
Все права защищены. Никакая часть этой книги не мажет быть воспроизведена в любой форме U.1U любыми
средствами, электронными или механическими, включая фотографирование, магнитную щпись или иные
средства копирования или сохранения информации без письменного разрешения издательства.
Translation copyright © 2002 by Binom Publishers.
Advanced Java 2 How to program, First Edition by Harvey Deitsl, Copyright © 2002, All Right Reserved
Published by arrangement with the original publisher, PRENTICE HALL, INC, a Pearson Education Company.
© PRENTICE 11 ALL, INC,
a Pearson Education Company. 2002
ISBN 5-9518-0034-Х (рус.) © Издание на русском языке.
ISBN 0-13-0S9560-1 (англ.) Издательство Бином, 2003
Научно-техническое издание
Харви Дейтел, Пол Дейтел, СИ. Сантри
Технологии профаммировапия на Java 2
Книга 3. Корпоративные системы, сервлеты, JSP, Web-сервисы
Компьютерная верстка С. Д Лычагина
Подписано в печать 24.03.03. Формат 70х100У|6. Усл. печ. л. 54,6. Гарнитура «Школьная».
Бумага газетная. Печать офсетная. Тираж 3000 экз. Заказ N9 1080.
Издательство «Бином-Пресс», 2003 г.
170026, Тверь, Комсомольский просп., !2
Отпечатано в издательско-полиграфическом комплексе «Звезда»
6И990, I. Перми ['СП-131, ул. Дружбы, 34
Содержание
Предисловие редактора русского перевода . 11
Предисловие 13
Особенности книги 14
Некоторые замечания для преподавателей 16
Подход к обучению 18
Благодарности 21
Об авторах 23
О компании Deitel & Associates, Inc 24
Консорциум World Wide Web (W3C) 25
Глава 1. Введение .27
1.1. Введение 28
1.2. Архитектура книги 29
1.2.1, Серверные приложения и Web-сервисы 29
1.2.2, Корпоративные приложения Enterprise Java 30
1.2.3, Практический пример корпоративного приложения 31
1.3. Краткий путеводитель по книге 31
1.4. Выполнение примеров 36
1.5. Паттерны проектирования 37
1.5.1. История паттернов проектирования 38
1.5.2. Обзор паттернов проектирования 39
1.5.3. Паттерны параллельного выполнения 42
1.5.4. Архитектурные паттерны проектирования ' 43
1.5.5. Дополнительные ресурсы по паттернам проектирования 44
Глава 2, СервлетЫ 47
2.1. Введение 48
2.2. Обзор технологии сервлетов и их архитектура 51
2.2.1. Интерфейс Servlet и жизненный цикл сервлета 52
2.2.2. Класс HttpServlet 53
2.2.3. Интерфейс HttpServletReqtiest 54
2.2.4. Интерфейс HttpServletResponse 55
2.3. Обработка HTTP-запросов get 56
2.3.1. Установка сервера Apache Tomcat 60
2.3.2. Развертывание Web-приложения 62
6 Технологии программирования на Java 2
2.4. Обработка HTTP-запросов get, содержащих данные 66
2.5. Обработка HTTP-запросов post 69
2.6. Переадресация запросов 72
2.7. Отслеживание состояния сеанса 76
2.7.1. Cookies 77
2.7.2. Отслеживание сеанса с помощью интерфейса HtlpSession 85
2.8. Многоуровневые приложения: использование средств JDBC
из сервлета 93
2.9. Класс HttpUtils 100
2.10. Ресурсы в Internet и во Всемирной паутине 101
Глава 3. JavaServer Pages {JSP) 109
3.1. Введение : 110
3.2. Обзор технологии JavaServer Pages Ill
3.3. Первый пример JSP-страницы 112
3.4. Неявные объекты 114
3.5. Сценарии 116
3.5.1. Компоненты сценария 116
3.5.2. Пример сценария 117
3.6. Стандартные действия 120
3.6.1. Действие <jsp:inelude> 121
3.6.2. Действие <jsp;forward> 126
3.6.3. Действие <jsp:plugin> 129
3.6.4. Действие <jsp:useBean> 133
3.7. Директивы 149
3.7.1. Директива page 150
3.7.2. Директива include 152
3.8. Библиотеки нестандартных тегов 154
3.8.1. Простой нестандартный тег 155
3.8.2. Нестандартный тег с атрибутами 159
3.8.3. Обработка тела нестандартного тега 162
3.9. Ресурсы в Internet и во Всемирной паутине 169
Глава 4. Книжный Internet-магазин, реализованный
с использованием сервлетов и JSP 177
4.1. Введение 178
4.2. Архитектура приложения книжного Internet-магазика 179
4.3. Доступ в Internet-магазин 181
4.4. Получение списка книг из базы данных 184
4.5. Просмотр информации о книге 192
4.6. Добавление элемента в магазинную тележку 199
4.7. Просмотр содержимого магазинной тележки 202
4.8. Подсчет стоимости и оформление заказа 205
4.9. Обработка заказа 208
4.10. Развертывание приложения в J2EE 1.2.1 210
4.10.1. Настройка источника данных books 211
4.10.2. Запуск сервера Cloudscape и сервера J2EE 212
4.10.3. Запуск средства разпертывания приложений J2EE 213
Содержание 7
4.10.4. Создание приложения книжного Internet-магазина 213
4.10.5. Создание Web-компонентов BookServlet и AddToCartServlet . . , 214
4.10.6. Добавление в приложение компонентов,
не являющихся сервлетами 220
4.10.7. Задание контекста Web, ссылок на ресурсы, имен JNDI
и файлов приветствия 223
4.10.8. Развертывание и выполнение приложения 225
Глава 5. Разработка приложений для беспроводной связи
на базе Java и J2ME 233
5.1. Введение 234
5.2. Обзор сервлета WelcomeServlet 237
5.3. Обзор сервлета TipTestServlet 243
5.3.1. Запрос от браузера Internet Explorer 256
5.3.2. Запрос от браузера WAP 263
5.3.3. Запрос от браузера i-motie Pixo 266
5.3.4. Запрос от клиента J2ME 271
5.4. Java 2 Micro Edition 274
5.4.1. Connected Limited Device Configuration (CLDC) 274
5.4.2. Mobile Information Device Profile (MIDP) 275
5.4.3. Обзор мидлета TipTestMIDlet 276
5.5. Инструкции по установке 297
5.6. Ресурсы в Internet и во Всемирной паутине 300
Глава 6. Сеансовые компоненты EJB и распределенные
транзакции 307
6.1. Введение 308
6.2. Обзор технологии EJB 308
6.2.1. Удаленный интерфейс 309
6.2.2. Собственный интерфейс 309
6.2.3. Реализация EJB 310
6.2.4. Контейнер EJB 310
6.3. Сеансовые компоненты 310
6.3.1. Сеансовые компоненты EJB с состоянием 311
6.3.2. Развертывание сеансовых компонентов EJB 323
6.3.3. Сеансовые компоненты EJB без состояния 330
6.4. EJB-транзакции 340
6.4.1. Собственный и удаленный интерфейс EJB MoneyTransfer 340
6.4.2. Разграничение транзакций с управлением
на стороне компонента 341
6.4.3. Разграничение транзакций с управлением
на стороне контейнера 347
6.4.4. Клиентский EJB-компонент MoneyTransfer 352
6.4.5. Развертывание EJB-компонента MoneyTransfer 357
6.5. Ресурсы в Internet и во Всемирной паутине 357
Глава 7. Компоненты EJB с данными 363
7.1. Введение 364
7.2. Обзор EJB-компонентов с данными 365
8
Технологии программирования на Java 2
7.3. Компонент-сущность EJB Employee,
хранящий информацию о сотруднике 365
7.4. Собственный и удаленный интерфейсы
EJB-компонента Employee 366
7.5. EJB-компонент Employee с персистентностью,
управляемой компонентом 368
7.5.1. Реализация EJB-компонента Employee 368
7.5.2. Развертывание EJB-компонента Employee 378
7.6. EJB-компонент Employee с персистентяосткю,
управляемой контейнером 380
7.7. Клиент EJB-компонента Employee 384
7.8. Ресурсы в Internet и во Всемирной паутине 394
Глава 8. Обмен сообщениями с помощью
Java Message Service (JMS) 397
8.1. Введение 398
8.2. Установка и настройка J2EE 1.3 399
8.3. Обмен сообщениями «от точки к точке» 400
8.3.1. Приложение для голосования Voter: обзор 400
8.3.2. Приложение Voter: серверная сторона 400
8.3.3. Приложение Voter: принимающая сторона 405
8.3.4. Приложение Voter: настройка и выполнение 410
8.4. Обмен сообщениями в модели * издатель/подписчик » 411
8.4.1. Приложение Weather: обзор 412
8.4.2. Приложение Weather: часть, относящаяся к издателю 412
8.4.3. Приложение Weather: часть, относящаяся к подписчику 417
8.4.4. Приложение Weather: настройка и выполнение 426
8.5. Компоненты Enterprise JavaBeans, управляемые сообщениями . . . 426
8.5.1. Приложение Voter: обзор , 427
8.5.2. Приложение Voter: принимающая сторона 427
8.5.3. Приложение Voter: настройка и выполнение 436
Глава 9. Практический пример корпоративного приложения.
Обзор архитектуры 449
9.1. Введение 450
9.2. Приложение книжного Internet-магазина Deitel Bookstore 451
9.3. Общая архитектура системы 451
9.4. Компоненты Enterprise JavaBeans 452
9.4.1. EJB-сущности 453
9.4.2, Сеансовые EJB-компоненты с состоянием 454
9.5. Логика управления, реализуемая сервлетами 454
9.6. Логика внешнего представления данных посредством XSLT .... 454
Глава 10. Практический пример корпоративного приложения.
Логика представления данных и логика управления .... 469
10.1. Введение 470
10.2. Базовый класс XMLServlet 471
10.3. Сервлеты, реализующие магазинную тележку 482
Содержание 9
10.3.1. Сервлет AddToCartServlet 483
10.3.2. Сервлет ViewCartServlet 486
10.3.3. Сервлет RemoveFromCartServlet 495
10.3.4. Сервлет UpdateCartServlet 497
10.3.5. Сервлет CheekoutServlet 499
10.4. Сервлеты, обслуживающие каталог товаров 501
10.4.1. Сервлет GetAllProductsServlet 502
10.4.2. Сервлет GetProductServlet 505
10.4.3. Сервлет ProductSearchServlet 508
10.5. Сервлеты для обслуживания покупателей 511
10.5.1. Сервлет RegisterServlet 512
10.5.2. Сервлет LoginServlet 515
10.5.3. Сервлет ViewOrderffistoryServlet 519
10.5.4. Сервлет ViewOrderServlet 522
10.5.5. Сервлет GetPasswordffintServlet 525
Глава 11. Практический пример корпоративного приложения.
Бизнес-логика: часть 1 531
11.1. Введение 532
11.2. Архитектура компонентов EJB 532
11.3. Реализация магазинной тележки 534
11.3.1. Удаленный интерфейс ShoppingCart 534
11.3.2. Реализация ShoppingCartEJB удаленного интерфейса
ShoppingCart 535
11.3.3. Собственный интерфейс ShoppingCartHome 542
11.4. Реализация EJB-компонента Product 543
11.4.1. Удаленный интерфейс Product 543
11.4.2. Реализация ProductEJB удаленного интерфейса Product ..... 543
11.4.3. Собственный интерфейс ProductHome 546
11.4.4. Класс ProductModel 547
11.5. Реализация EJB-компонента Order 551
11.5.1. Удаленный интерфейс Order 551
11.5.2. Реализация OrderEJB удаленного интерфейса Order 551
11.5.3. Собственный интерфейс OrderHome 557
11.5.4. Класс OrderModel 558
11.6. Реализация EJB-компонента Order Product 563
11.6.1. Удаленный интерфейс OrderProduct 563
11.6.2. Реализация Order ProductEJB удаленного интерфейса
OrderProduct 564
11.6.3. Собственный интерфейс OrderProductHome 566
11.6.4. Класс первичного ключа OrderProductPK 567
11.6.5. Класс OrderProduct Model 569
Глава 12, Практический пример корпоративного приложения.
Бизнес-логика: часть 2 573
12.1. Введение 574
12.2. Реализация EJB-компонента Customer 575
12.2.1. Удаленный интерфейс Customer 575
12.2.2. Реализация CustomerEJB удаленного интерфейса Customer 575
10 Технологии программирования на Java 2
12.2.3. Собственный интерфейс CustomerHome 582
12.2.4. Класс CustomerModel 583
12.3. Реализация EJB-компонента Address 589
12.3.1. Удаленный интерфейс Address 589
12.3.2. Реализация AddressEJB удаленного интерфейса Address 589
12.3.3. Собственный интерфейс AddressHome 593
12.3.4. Класс AddressModel 593
12.4. Реализация EJB-компонента SequenceFactory 599
12.4.1. "Удаленный интерфейс SequenceFactory 599
12.4.2. Реализация SequenceFactoryBJB удаленного интерфейса
SequenceFactory 600
12.4.3. Собственный интерфейс SequenceFactoryHome 602
12.5. Развертывание приложения Deitel Bookstore средствами J2EE . . 603
12.5.1. Развертывание компонентов-сущностей EJB
с персистентностью, управляемой контейнером 603
12.5.2, Развертывание сервлетов 611
Глава 13. Серверы приложений 615
13.1. Введение 616
13.2. Спецификация J2EE и ее преимущества 616
13.3. Коммерческие серверы приложений 617
13.3.1. BEA WebLogic 6.0 61Т
13.3.2. iPlanet Application Server 6.0 618
13.3.3. IBM WebSphere Advanced Application Server 4.0 619
13.3.4. Сервер приложений JBoss 2.2.2 619
13.4. Развертывание приложения Deitel Bookstore
на сервере BEA WebLogic 620
13.5. Развертывание приложения Deitel Bookstore
на сервере IBM WebSphere 644
13.6. Ресурсы в Internet и во Всемирной паутине 646
Глава 14. Введение в Web-сервисы и SOAP 649
14.1. Введение 650
14.2. Простой протокол доступа к объектам (SOAP) 651
14.3. Служба погоды, реализованная посредством SOAP 657
14.4. Ресурсы в Internet и во Всемирной паутине 670
Предисловие редактора
русского перевода
Книга, которую Вы держите в руках, является переводом третьей части
оригинального издания «Advanced Java™ 2 Platform. How to Program». Поскольку
объем оригинала превышает 1800 страниц, при переводе было принято решение
русское издание разбить на три части. Данная книга посвящена компонентам
Enterprise JavaBeans, программированию серверных приложений и корпоративных
приложений. Первая часть посвящена программированию графического
пользовательского интерфейса приложений, двухмерной и трехмерной графике,
взаимодействию с базами данных, компонентам JavaBeans. Вторая часть посвящена
созданию распределенных приложении.
Книга насыщена многочисленными примерами и упражнениями. Читатели
могут использовать программный код, загрузив примеры с сайта издательства, и
запускать программы при изучении книги. Весь код доступен для загрузки через
Internet на следующих Web-сайтах:
www.deitel.com
www.prenthall.com/deitel
www.binom-press.ru/rfr_prog_adv_j ava2.htm
Хотелось бы обратить внимание читателей на то, что файлы примеров
размещены в папках, имена которых соответствуют номерам глав оригинала. В то же
время, нумерация глав, принятая в данной книге, не совпадает с нумерацией глав
в оригинале. В этой связи предлагаем читателям при работе с примерами
пользоваться следующей таблицей соответствий.
Номер главы
2
3
4
5
б
7
S
Имя папки с примерами
ch.09
chIO
chll
chl2
ch14
rhIS
chl6
Номер главы
9
10
11
12
13
14
Имя папки с примерами
ch17
сМ8
ch19
ch20
ch21
ch29
Предисловие
Жизнь состоит из фрагментов. Надо только их соединить.
Эдвард Морган Форстер
Добро пожаловать в волнующий мир передовых концепций программирования,
реализованных в трех основных платформах Java: Java™ 2 Enterprise Edition
(J2EE), Java 2 Standard Edition (J2SE) и Java 2 Micro Edition (J2ME).
Принимая участие в работе конференции Internet/World Wide Web, которая состоялась
в ноябре 1995 г. в Бостоне, мы едва ли могли представить, к каким результатам
это приведет: четыре издания книги Как программировать на Java (книга,
ставшая мировым бестселлером среди книг по Java), а теперь и данная книга,
посвященная технологиям для разработки программ на Java, которая будет полезной
при углубленном изучении компьютерных технологий на старших курсах высших
учебных заведений, а также для профессиональных разработчиков.
До появления Java мы были убеждены, что в течение следующего десятилетия
С~+ заменит С в качестве доминирующего языка разработки приложений и языка
системного программирования. Однако World Wide Web и Java в совокупности
способствовали выдвижению Internet на первые роли при планировании и
реализации информационных систем. Организации хотят напрямую, без излишних
издержек, интегрировать Internet в свои информационные системы. Java для этой
цели подходит больше, чем C++. Согласно заявлению Sun Microsystems, по
состоянию на 2001 г. более 95% корпоративных систем поддерживают технологию J2EE.
В книге мы рассмотрим технологии Java, которые могут быть незнакомыми
для большинства программистов на Java и вызвать их интерес. Каждая глава
построена таким образом, чтобы читатель мог получить представление о передовых
и достаточно сложных технологиях Java, не углубляясь при этом во все нюансы.
Фактически, каждой из рассматриваемых тем может быть посвящена отдельная
книга объемом 600-800 страниц.
Для примеров в этой книге мы используем подход, отличный от того, который
применялся в других наших книгах. Количество программ уменьшилось, но эти
программы увеличились в объеме и иллюстрируют довольно изощренные приемы
проектирования программного обеспечения. Создавая книгу для разработчиков, мы
интегрировали в нее множество технологий, чтобы вы могли продвинуться дальше
и экспериментировать с современными технологиями и наиболее широко
применяемыми, принципами и.рииктщюва.нин программного обеспечения. Это ли не лучший
способ научиться работать с реальными технологиями и программным кодом'.-'
Прежде, чем принять решение, какие темы включить в эту книгу, мы прочли
десятки журналов, изучили информацию на Web-сайте Sun Microsystems и
приняли участие в многочисленных конференциях и презентациях. Мы пересмотрели
наш материал в свете новейших технологий, представленных на конференции
JavaOne. В работе этой конференции, спонсором которой выступила корпорацией
Sun Microsystems, приняли участие ведущие разработчики на Java, Мы также про-
14 Технологии программирования на Java 2
смотрели ряд книг по Java, посвященных специальным темам. После проведения
такого обширного исследования мы создали основной набросок книги и отдали его
для рецензирования специалистам по Java. Наше желание включить достаточно
большое количество тем привело к тому, что объем книги превысил 1800 страниц.
Мы готовы принести извинения за неудобства, связанные со столь большим
объемом книги, но материал и количество изучаемых тем слишком обширны. При
переиздании книги мы, возможно, разделим ее на два тома1.
Эта книга существенно выиграла от тщательного ее рецензирования
многочисленной группой экспертов. Нам также очень помогла подробная документация,
которая имеется на Web-сайте корпорации Sun (www.sun.com). Рецензенты,
работающие в корпорации Sun и многих других известных компаниях, оказали
большую помощь при формировании структуры книги. Нам хотелось, чтобы опытные
разработчики просмотрели наш текст и примеры кода, и мы могли предоставить
советы специалистов, которые реально работают с рассматриваемыми
технологиями, применяя их на практике.
Мы решили включить в книгу обзор серверов приложений (глава 13). Тремя
наиболее популярными программными продуктами для серверов приложений
являются BEA WebLogic, IBM WebSphere и Sun/Netscape iPlanet. Компания iPlanet
на момент издания этой книги готовила к выпуску новую версию сервера
приложений. В соответствии с соглашением между iPlanet и Deitel & Associates, Inc.
компания iPlanet предоставила ссылку на сайт www.iplanet.com/ias__deitel,
специально посвященный этой книге, с которого читатели смогут загрузить последнюю
версию программного продукта iPlanet. Мы также приводим указания по
развертыванию нашего учебного примера на сервере iPlanet. Эти указания можно найти
на Web-сайте www.deitel.com.
Особенности книги
Эта книга имеет следующие особенности:
• Технология Enterprise Java и комплексный пример корпоративного
приложения Java. Разработчики используют Java для построения сложных
и ответственных корпоративных приложений. В главах 2-4, 6—8 и 13
исследуются компоненты, необходимые для реализации корпоративных решений,
В частности, сервлеты, страницы JavaServer Pages, распределенные
транзакции, промежуточное программное обеспечение, ориентированное на обмен
сообщениями, и серверы приложений. В комплексном практическом примере
корпоративного приложения Enterprise Java в главах 9-12 нашли применение
многие технологии, такие как Enterprise JavaBeans, сервлеты, RMI-II0P,
XML, XSLT, XHTML, а также WML и cHTML (для разработки приложений
для беспроводных устройств). В примере книжного Internet-магазина Deitel
Bookstore демонстрируется, как использовать архитектуру модель—вид—
контроллер (model—view—controller — MVC) для построения
корпоративного приложения. В данном приложении используются технологии, которые
дают возможность поддерживать клиентов практически любого типа, в том
числе сотовые телефоны, мобильные устройства и Web-браузеры. В
современном мире проводных и беспроводных сетей деловая информация должна
надежно и безопасно доставляться прЕДПолагаемым получателям.
• Java 2 Micro Edition (J2ME) и приложения для беспроводных устройств.
Согласно прогнозам, к 2003 г. большая часть людей во всем мире будет
осуществлять доступ в Internet через беспроводные устройства и через настольные
1 В связи с этим при переводе было принято решение русское издание разбить на три
части. — Ирам. ред.
Предисловие
15
компьютеры. Java 2 Micro Edition (J2ME) представляет собой платформу Java
для беспроводных устройств, имеющих ограниченные функциональные
возможностями, таких как сотовые телефоны и карманные компьютеры, В главе
5 представлен комплексный практический пример, в котором содержимое из
централизованного хранилища данных доставляется нескольким клиентам,
в том числе клиенту J2ME, по беспроводной связи,
Web-сервисы.. Web-еервисы — это приложения, предоставляющие
общедоступные интерфейсы, которые могут использоваться другими приложениями
в Web. Web-сервисы построены на базе известных протоколов, таких как
HTTP, и осуществляют обмен информацией с помощью XML-сообщений.
Службы (сервисы) каталогов дают возможность клиентам выполнять поиск
для обнаружения имеющихся Web-сервисов. Протокол простого доступа
к объектам Simple Object Access Protocol (SOAP) использует XML для
предоставления коммуникационных возможностей Web-сервисам, Технологии SOAP
посвящена глава 14, Многие из рассматриваемых в этой книге технологий
могут быть использованы для построения Web-сервисов.
Применение паттернов проектирования. Наиболее крупные
практические примеры в книге, такие как трехуровневое приложение с
использованием сервлетов и страниц JavaServer Pages в главе 4, трехуровневое
приложение для беспроводного доступа в главе 5 и корпоративное приложение
книжного Internet-магазина Deitel Bookstore в главах 9-12, содержат тысячи
строк кода. Реальные системы, такие как системы для банкоматов или для
управления воздушным движением, могут содержать сотни тысяч или даже
миллионы строк кода. При построении таких сложных систем важную роль
приобретает эффективное проектирование. За последнее десятилетие в
индустрии разработки программного обеспечения был достигнут значительный
прогресс в области паттернов проектирования — проверенных
архитектурных решений для построения гибкого и хорошо управляемого
объектно-ориентированного программного обеспечения.1 Использование паттернов
проектирования может существенно уменьшить сложность процесса
проектирования программного обеспечения. При создании программ в этой книге мы
применяли множество паттернов проектирования. В главе 1 приведены
начальные сведения о паттернах проектирования, рассказывается, какую
пользу они приносят, и перечислены паттерны проектирования, применяемые
в этой книге.
XML. Использование расширяемого языка разметки Extensible Markup
Language (XML) становится правилом при разработке программных продуктов,
и мы постоянно используем его в материале книги. Обладая синтаксисом для
создания языков разметки, не зависящим от платформы, переносимые данные
XML хорошо интегрируются с переносимыми приложениями и сервисами
Java. Если вы не знакомы с XML, вам следует предварительно получить
представление об основах XML, прочитав какую-либо книгу по этой тематике.2
Библиография и ресурсы. Главы этой книги содержат списки
соответствующей литературы и унифицированные указатели ресурсов (URL),
предлагающих дополнительную информацию о рассматриваемых технологиях. Это
сделано, чтобы те читатели, которые хотели бы более подробно изучить данную
тему, могли обратиться к ресурсам, которые мы сочли полезными.
Э. Гамма, Р. Хелм, Р. Джонсон, Дж. Влнссидес «Приемы объектно-ориентированного
проектирования. Паттерны проектирования». СПб.: Питер, 2001 г., 368 с.
Можно порском он доиать читателю книгу Х.М. ДейтеХ П.Дж. Дейтел, Т.Р. Нието и др.
«Как программировать на XML*. М.: Издательство Вином, 2001 г,, 944 с. — Прим.ред.
16 Технологии программирования на Java 2
Некоторые замечания для преподавателей
Мир объектно-ориентированного программирования
Когда мы писали первое издание книги Пак программировать на Java,
университеты еще были ориентированы на процедурные языки программирования, такие
как Pascal и С. В некоторых учебных заведениях уже изучали объектно-ориенти
рованный язык программирования C++, но и здесь по большей части
использовалось процедурное программирование в сочетании с объектно-ориентированным
программированием — такое сочетание характерно для C++, но не для Java. На
момент выхода третьего издания книги Как программировать на Java многие
университеты начали переходить с изучения C++ на изучение основ Java, а
преподаватели стали склоняться к чисто объектно-ориентированному подходу к
программированию. Одновременно с этим сообщество разработчиков программных
продуктов стандартизировало подход к моделированию
объектно-ориентированных систем с помощью UML. Сложилась также практика применения паттернов
проектирования. Данная книга использует 100% объектно-ориентированный
подход и уделяет особое внимание паттернам проектирования.
Необходимым предварительным условием для изучения этой книги является
знакомство с книгой Как программировать на Java. Издание 4-е (или
эквивалентной книгой по Java), которая предоставляет прочный фундамент для
программирования на Java. Ниже перечислены главы и приложения, вошедшие в книгу Как
программировать на Java. Издание 4-е, более подробное описание содержания
можно найти на сайте www.deitel.com: Введение в компьютеры, Internet и Web;
Введение в приложения Java; Введение в апплеты Java; Структуры управления,
часть 1; Структуры управления, часть 2; Методы; Массивы; Программирование на
основе объектов; Объектно-ориентированное программирование; Строки и
символы; Графика и Java 2D; Компоненты графического интерфейса пользователя,
часть 1; Компоненты графического интерфейса пользователя, часть 2; Обработка
исключений; Программные потоки; Файлы и потоки; Сетевые средства;
Мультимедиа: Изображения, анимация, аудио и видео; Структуры данных; Пакет утилит
Java и манипулирование битами; Коллекции; Инфраструктура Java Media и Java
Sound; Ресурсы по Java; Диаграмма приоритета операторов; Набор символов
ASCII; Системы счисления; Создание HTML-документации с помощью javadoc;
Интерфейсы событий и слушателей Elevator; Модель Elevator; Представление
Elevator; Карьерные возможности; Unicode; Литература.
Почему студентам нравится Java
Студенты имеют высокую мотивацию, поскольку осознают, что изучают
перспективный язык (Java) и передовую парадигму программирования
(объектно-ориентированное программирование) для построения больших систем. Java
сразу же демонстрирует свои преимущества в мире, где Internet и Всемирной
паутине отводится основополагающая роль, и работодателям требуются
программисты для разработки сложных корпоративных систем. Студенты быстро
обнаруживают, что при помощи Java они достаточно быстро могут сделать многие вещи,
поэтому они с удовольствием затрачивают дополнительные усилия на изучение Java.
Java помогает программистам реализовать свой творческий потенциал. Мы
хорошо видим это в ходе преподавания базового и расширенного курсов по Java,
которое осуществляет компания Deitel & Associates, Inc.
Главное назначение книги
Наша цель была вполне очевидна — создать книгу по Java-технологиям для
изучения студентами старших курсов высших учебных заведений по
специальностям, связанным с информационными технологиями, для программистов на Java
Предисловие
17
средней квалификации, а также для более глубокого изучения ряда теоретических
и практических вопросов профессионалами. Чтобы удовлетворить этим
требованиям, мы создали книгу, которая вызовет интерес у программистов на Java. Мы
представляем полноценные примеры по изучаемым темам и часто обращаемся
к уже известному материалу. Мы следуем стилю и сложившейся практике при
написании исходного кода при его форматировании, правильном использовании
интерфейсов прикладного программирования Java, конструкций и технологий. В этой
книге представлены приложения Java, которые читатели могут использовать,
чтобы немедленно приступить к работе с рассматриваемыми технологиями.
Эволюция книги
Данная книга была закончена сразу же после книги Как программировать на
Java. Издание 4-е. Сотни тысяч студентов университетов и профессионалов по
всему миру изучали Java с помощью нашей книги. После своего издания книга Как
программировать на Java 2 использовалась в университетах, корпорациях и
правительственных организациях по всему миру. Компания Deitel & Associates, Inc.
осуществляла преподавание курсов по Java среди тысяч студентов, для чего
использовались написанные нами книги. Мы тщательно следим за эффективностью
подачи материала и соответствующим образом меняем содержимое наших книг.
Реализация концепций Java
Мы верим в Java. Реализация концепций Java корпорацией Sun Microsystems,
является просто блестящей. В основу нового языка Sun были положены языки С
и C++: два наиболее популярных и широко используемых в мире языка. Это
немедленно привлекло на сторону Java большую группу высококвалифицированных
программистов, которые участвовали в реализации коммуникационных систем,
систем управления базами данных, приложений для персональных компьютеров
и системного программного обеспечения. Разработчики Sun отбросила более
сложные и порождающие ошибки функции C/C++ (такие, как указатели, перегрузка
операторов, множественное наследование и некоторые другие). Языку был придан
компактный вид за счет удаления специализированных средств, используемых
лишь небольшой частью сообщества программистов. Язык стал реально
переносимым, что дало возможность реализовывать приложения для Internet и Web. В него
были включены такие необходимые для разработчиков средства, как работа со
строками, графикой, компонентами графического пользовательского интерфейса,
обработка исключений, организация многопоточной обработки, мультимедийные
возможности (изображения, анимация, аудио и видео), структуры данных,
взаимодействие с системами управления базами данных, сетевые средства
взаимодействия клиент/сервер на базе Internet и Web, технологии для создания
распределенных и корпоративных систем. Язык был сделан свободно доступным для
миллионов потенциальных программистов по всему миру,
2,5 миллиона разработчиков на Java
Язык программирования Java был создан в 1995 г. как средство для добавления
динамического содержимого в Web-страницы. В дополнение к традиционному
статическому тексту и графики Web-страницы могут теперь быть оживлены с
помощью аудио и видео, анимации, интерактивных средств и, в скором времени,
трехмерных изображений.^Однако помимо этого, в Java есть и множество других
достоинств. Возможности Java полностью отвечают современным требованиям
компаний и организаций к обработке информации. Мы сразу же разглядели
имеющийся в Java потенциал, который позволит Java стать одним из основных языков
программирования общего назначения. Фактически технологии Java вызвали
революцию в разработке программного обеспечения, предоставив возможности для
создания активно использующего средства мультимедиа, не зависящего от плат-
18
Технологии программирования на Java 2
формы, объектно-ориентированного кода для широко используемых,
функционирующих в Internet и корпоративных сетях приложений и апплетов. Java сейчас
объединяет 2,5 млн. разработчиков по всему миру — впечатляющее достижение,
если учесть, что Java доступен только в течение всего шести лет. Ни один другой
язык программирования до сих пор не приобретал столь большую аудиторию
разработчиков за такое короткое время.
Подход к обучению
Данная книга содержит множество примеров и упражнений из многих областей
практической деятельности, дающих читателю возможность решать интересные
практические задачи. Книга концентрирует внимание на принципах хорошего
стиля и техники программирования, что особенно важных при создании больших
программ, подобных рассматриваемым в этой книге. Мы старались избегать
хитроумных терминов и описаний синтаксиса, отдавая предпочтение обучению на
примерах. Наши примеры были протестированы на популярных платформах Java.
Книга написана преподавателями, которые посвящают большую часть времени
обучению передовым технологиям практического программирования и написанию
книг по этой тематике.
Подход к обучению Java посредством живого кода (live-code™)
Книга насыщена примерами живого кода (live-code™). Это основа нашего метода
преподавания программирования и написания учебных пособий, а также основа
нашего мультимедийного курса обучения Cyber Classrooms и курсов дистанционного
обучения на базе Web (Web-Based Training Courses). Каждое новое понятие
представлено в контексте законченной, работающей программы, за которой следует одна
или несколько врезок с входным и/ выходными данными программ. Мы называем
такой стиль обучения и написания учебных пособий методом живого кода (live-
code™). Мы используем язык программ для обучения языкам программирования.
Чтение программного кода во многом заменяет ввод его в компьютер и выполнение.
Доступ в World Wide Web
Весь код доступен для загрузки через Internet на следующих Web-сайтах:
www.deitel.com
www.prenhall.com/deitel
www.binom-press.ru/books/adv_java2.htm
Цели
Каждая глава начинается с раздела Цели. Здесь говорится, чего ожидать и что
можно узнать, прочитав главу, чтобы читатели определились, нужен ли им
материал главы. Это повышает доверие и способствует позитивному восприятию.
Цитаты
За задачами обучения следуют цитаты. Некоторые из них юмористические,
некоторые — философские, некоторые представляют собой интересные точки
зрения. Нашим читателям нравится, что материал главы сопровождается цитатами.
Многие из цитат стоит прочитать снова после прочтения главы.
План
План главы поможет читателям подойти к изучению материала по принципу
от простого к сложному. Это также поможет им заранее узнать, какие темы будут
рассматриваться дальше, и установить для себя удобный и эффективный темп
обучения.
Предисловие
19
Тысячи строк хода, большое число примеров программ
(с результатами их выполнения)
Мы представляем возможности Java в контексте законченных,
работоспособных программ. Программы в этой книге являются довольно объемными и
содержат сотни или даже тысячи строк кода (например, пример приложения для
книжного Internet-магазина содержит около 10000 строк кода). Читатели могут
использовать программный код, загрузив примеры с сайта издательства, и запускать
программы при изучении книги.
Иллю страц uu/рисунки
Многие из рисунков являются примерами кода, но в книге также имеется
множество диаграмм, рисунков и копий экранов с результатами выполнения
программ. Например, весьма выразительны структурные схемы, поясняющие
архитектуру комплексного учебного приложения Enterprise Java в главе 9.
Советы по программированию
Мы включили советы по программированию, чтобы помочь читателям
сосредоточиться на наиболее важных аспектах создания программ. Мы выделили эти советы
в виде рубрик Хороший стиль программирования, Типичные ошибки
программирования, Советы по тестированию и отладке, Советы по повышению
эффективности. Советы по переносимости программ, Общие методические рекомендации
и Внешний вид программы. Эти советы и рекомендации представляют собой
лучшее, что мы собрали за несколько десятилетий активного программирования и
преподавания. Одна из наших студенток — математик — говорила иам, что этот подход
подобен выделению аксиом, теорем и следствий в книге по математике; это создает
основу, на которой строится хорошее программное обеспечение.
Хороший стиль программирования
Мы выделяем рекомендации по хорошему стилю программирования при
написании программ, которые позволяют создавать более ясные, более
понятные для восприятия, легко отлаживаемые и сопровождаемые программы.
Типичные ошибки программирования
Особое выделение типичных ошибок программирования помогает
читателям не делать таких ошибок в своей риботе.
Советы по тестированию и отладке
Когда мы впервые решили включить советы по тестированию и отладке,
мы думали, что они будут подсказывать, как тестировать и
отлаживать программы на Javu. Фактически большая часть из этих советов
описывает различные аспекты Java, которые уменьшают вероятность
возникновения ошибок и, тем самым, упрощают процесс тестирования
и отладки.
Советы по повышению эффективности
Мы включили советы по повышению эффективности, которые
раскрывают возможности увеличения эффективности работы программ, — как
сделать, чтобы программы работала быстрее, или как мшшмизироаать
объем памяти, который требуется для их выполнения.
Советы по переносимости программ
Одним из основных преимуществ Java является переносимость, поэтому
некоторые программисты полагают, что если они реализуют приложение
20 Технологии программирования на Java 2
на Java, это приложение будет автоматически обладать
переносимостью на все платформы, поддерживающие Java. К сожалению, это не
всегда так. Мы включили советы по переносимости, чтобы помочь
читателям научиться писать обеспечивающий переносимость код и
предоставить информацию о том, как Java достигает столь высокую степень
переносимости.
Общие методические рекомендации
Парадигма объектно-ориентированного программирования требует
полного переосмысления способа построения программных систем. Java
является эффективным языком для разработки качественного программного
обеспечения. В этой рубрике выделяются архитектурные проблемы,
которые влияют на построение систем программного обеспечения, особенно
больших систем.
Внешний вид программы
Мы предусмотрели эту рубрику, чтобы особо подчеркнуть соглашения,
принятые при построении графического интерфейса пользователя. Эти
наблюдения помогут читателям разрабатывать свои собственные
графические интерфейсы пользователя в соответствии с принятыми нормами.
Резюме
Каждая глава заканчивается дополнительным методическим материалом. Мы
представляем подробное, представленное в виде основных положений, резюме
главы. На каждую главу в среднем приходится по 26 пунктов резюме. Это поможет
читателям быстро просмотреть и закрепить ключевые понятия.
Терминология
Мы включили раздел Терминология, содержащий перечень всех основных
терминов, используемых в главе, в алфавитном порядке, — опять же, для
закрепления изученного. На каждую главу в среднем приходится по 51 термину.
Упражнения для самоконтроля с ответами на них
Для самостоятельного обучения предусмотрены многочисленные упражнения
для самоконтроля и ответы на них. Это дает читателю возможность научиться
уверенно ориентироваться в изученном материале и подготовиться к выполнению
основных упражнений. Читателям следовало бы попробовать выполнить все
упражнения для самоконтроля и проверить свои ответы.
Упражнения
Каждая глава завершается рядом упражнений. Преподаватели могут
использовать эти упражнения для домашних заданий, зачетов и экзаменов. Решения для
большинства упражнений включены в Руководство Инструктора и в Компакт-
диск Инструктора, доступные только для инструкторов через
представителей издательства Prentice Hall. {ПРИМЕЧАНИЕ. Пожалуйста, не обращайтесь
к нам с просьбами предоставить Руководство Инструктора. Распространение их
строго ограничено преподавателями высших учебных заведений, которые
осуществляют обучение на основе этой книги. Инструкторы могут получить
Руководство только от официальных представителей издательства Prentice Hall. Мы
сожалеем, что не можем предоставить решения профессионалам.) Решения
примерно к половине упражнений имеются на мультимедийном CD-ROM Advanced
Java 2 Platform Multimedia Cyber Classroom, который также является составной
частью полного обучающего курса The Complete Advanced Java 2 Platform Training
Course. Информацию но заказу вы можете найти, посетив наш Web-сайт www.dei-
tel.com.
Предисловие
21
Благодарности
Нам бы очень хотелось поблагодарить за проделанную работу многих людей,
чьих фамилий нет на обложке, но чья упорная работа, сотрудничество, дружеская
помощь и понимание сыграли очень важную роль в создании этой книги.
Многие другие сотрудники компании Deitel & Associates, Inc. посвятили не
один час работе над этой книгой,
• Джонатан Гэдаик, выпускник школы технических и прикладных наук
Колумбийского Университета (бакалавр компьютерных наук), принимал
участие в написании введения и главы 5.
• Кайл Ломели, выпускник колледжа Оберлин, специализирующийся по
компьютерным наукам, рецензировал главу 5.
• Мэтью Ковалевски, выпускник колледжа Бентли, главный специалист по
системам учета информации и директор по разработке приложений для
беспроводного доступа компании Deitel & Associates, Inc., внес вклад в
написание главы 5.
• Кейт ШтеЙнбюлер, выпускница Бостонского колледжа по специальности
английский язык и средства общения занималась получением разрешений на
публикацию.
• Бэтси ДуВальд, выпускница колледжа Метрополитен в Денвере,
дипломированный специалист в области технических средств общения (набор и
редактирование текстов) и имеющая вторую специальность в области
компьютерных информационных систем, является директором редакционного отдела
компании Deitel &. Associates, Inc. Она принимала участие в написании
Предисловия и помогала готовить рукопись к публикации.
Мы также хотели бы поблагодарить участников нашей программы College
Internship компании Deitel & Associates, Inc.1
• Крис Хэнсон, студент-старшекурсник университета Брендейс (компьютерные
науки), участвовал в написании главы 14. Он также рецензировал главу 13
и участвовал в техническом редактировании глав 6, 7 и 14.
• Одри Ли, студентка-старшекурсница колледжа Уэллесли,
специализирующийся по компьютерным наукам и математике, участвовал в написании
главы 8 и внес вклад в создание главы 10.
• Джеффри Хэмм, студент второкурсник Северо-Восточного университета,
специализирующийся по компьютерным наукам, принимал участие в
написании главы 13.
• Варун Ганапати, студент-второкурсник университета Корнелл,
специализирующийся в области компьютерных наук и электротехники, участвовал в
написании главы 5 и написал исходный код для реализации клиентов i-mode
и WML в практическом примере в главе 10.
• Сьюзен Уоррен, студентка младшего курса, специализирующаяся по
компьютерным наукам в университете Браун, работала над Руководством
Инструктора и дополнительными материалами для глав 2 и 3.
1 Программа College Internship Program компании Deitel & Associates, Inc. предлагает
ограниченное количество платных вакансий для студентов колледжей в районе Бостона,
специализирующихся главным образом в области компьютерных наук,
информационных технологий, маркетинга или английского языка. Студенты работают в нашем офисе
у С&дбзри, штат Массачусетс, полный рабочий день летом и/или по вечерам в течение
учебного года. Для выпускников колледжей имеются вакансии на полный рабочий день.
Для получения более полной информации об этой конкурсной программе, пожалуйста,
свяжитесь с Эбби Дейтел по адресу deitel@deitel.com и справьтесь на нашем Web-сайте
www.deitel.com.
22
Технологии программирования на Java 2
• Юджин Изумо, студент-второкурсник университета Браун,
специализирующийся по компьютерным наукам, работал над Руководством Инструктора
и дополнительными материалами для глав 2 и 3.
• Кристина Корней, студентка-старшекурсница, изучающая психологию и
бизнес в Высшей школе Фрэмингхэм, помогала готовить Предисловие и
библиографию для нескольких глав.
• Эми Гипс, студентка-второкурсница Бостонского колледжа,
специализирующаяся по маркетингу и финансам, занималась подбором цитат для всей
книги и помогала готовить предисловий.
• Фабиан Морган (выпускник Массачусетского технологического института
лета 2000 г.) написал первоначальные версии примеров для глав 6, 7 и
комплексного практического примера корпоративного приложения в главах 9~12.
• Джош Гоулд (выпускник университета Кларк) участвовал в работе над
главами 2 и 3.
• Нам выпало счастье работать над этим проектом вместе с группой
талантливых и квалифицированных специалистов по издательскому делу из
компании Prentice Hall. Мы особенно высоко ценим выдающиеся усилия нашего
редактора по компьютерным наукам, Петры Ректер, и ее шефа — нашего
наставника в издательском деле — Марши Хортен, главного редактора
отделения Технических к компьютерных наук издательства Prentice Hall. Вине
О'Брайен и Камилла Трентакост проделали громадную работу по
организации издательского процесса.
Мультимедийный курс Advanced Java 2 Platform Multimedia Cyber Classroom
был разработан параллельно с данной книгой. Мы вполне оценили преимущества
нового подхода к обучению, прошедшего техническую экспертизу нашего главного
редактора по мультимедийному и компьютерному обучению, наставника и друга
Марка Тауба. Он и наш редактор по мультимедиа, Карен МакЛин, проделали
значительную работу по подготовке курса к публикации в сжатые сроки. Майкл Руэл
проделал огромную работу в качестве менеджера проекта Cyber Classroom.
Мы должны высказать особую благодарность в адрес Тамары Ньюнэм Ковалло
{smart_art@earthliiik.net), которая проделала оформительскую работу с нашими
значками-рисунками к советам по программированию и с обложкой. Она
придумала этого восхитительного жука, который делится с вами советами по
программированию. Барбара Дейтел также помогала создавать образы жуков, которых вы
видите на обложке.
Мы хотели бы отметить усилия наших рецензентов:
Джеф Аллен (Sun Microsystems)
Дибиенду Баски (Sun Microsystems)
Тим Бодро (Sun Microsystems)
Пол Бирн (Sun Microsystems)
Онно Клуйт (Sub Microsystems)
Питер Корн (San Microsystems)
Петр Козел (Sun Microsystems)
Ион Пай к вист (Sun Microsystems)
Томас Павек (Sun Microsystems)
Мартин Ризл (Sun Microsystems)
Даванум Сринивас (менеджер по JNI-FAQ,
Кип Microsystems)
Брэндон Тэйлор (Sun Microsystems)
Вики Аллан (университет штата Юта)
Джавад Аслам (аналитик/разработчик при
ложений, Tektronix)
Генри Болен (автор CORBA)
Кати Баршацки (Javakathy.com)
Дон Бенши (Ben-Cam Intermedia)
Кейт Биделоу (Lutris)
Даррин Бишон (Levi, Ray and Shoup, Inc.)
Карл Бурнхэм (Southpoint)
Джоя Конли {институт ДеВряй)
Чарльз Костарелла (колледж Antelope
Valley)
Джонатан Эрл (Technical Training
Consultants)
Джесс Глик (NctBeans)
К ев Гил л юр (Amdocs, Inc.)
Джейсон Гордое (Verizon)
Кристофер Грин (Colorado Springs Technical
Consultants)
Майкл Гай (XOR)
Дебора Хукен (Mnemosyne Consulting)
Предисловие
23
Элизабет Кальман (Национальная библноте- Срикапт Раджу (штатный инженер, Sun
ка Лос-Аламос) Microsystems)
Салви Каруппасвами (EDS) Робин Роу (MovicEditor.com)
Джоди Крогалис (Compuware) Майкл Шмальц (Accenture)
Энтони Левенсейлор (Compuware) Джошуа Шарф (Joshua Sharif Associates)
Дерек Лэйн (президент компании Дэн Шеллман (инженер-программист)
Gunalinger Software and Consulting, Inc.) Иен Сигел (OMG)
Рик Лаек. (Callidus Software} Ума Саббьх (Urographies)
Ашши Махиджани (главный ааалитик, про- Арун Таксали (jataayusoft)
граммист) Вадим Ткаченко (Sera Nova)
Пол МакЛохлан (Compuware) Ким Топли (автор книг Core Java
Рэнди Мейерс (Net Com) Foundation Classes и Core Swing: Advanced
Пол Манди (Imation) Programming, изданных Prentice Hall)
Стивен Ньютон (ведущий программист/ака- Джон Воргезе (университет Рочестер)
литик, Standard Insurance Company) Ксинчжу Ванг (Emerald Solutions)
Виктор Питере (NextStepEducation) Карен Вислевски (Titan Insurance)
Брайаи Понтарелли (консультант) Джесс Уилкннс (Metalinear Media)
В сжатые сроки эти рецензенты тщательно прочитали текст и внесли
многочисленные предложения, позволяющие повысить точность и полноту представления
материала.
Мы искренне заинтересованы выслушать ваши комментарии, замечания,
поправки и предложения по совершенствованию книги. Пожалуйста, посылайте всю
корреспонденцию на наш электронный адрес:
deitel@deitel. com.
Мы ответим незамедлительно. Кажется, все. Добро пожаловать в
увлекательный мир программирования на Java. Мы надеемся, что вам понравится наш
взгляд на разработку современных компьютерных приложений. С наилучшими
пожеланиями
Д-р Харви М. Дейтел
Пол Дж. Дейтел
Шоп Э. Сзнтри
Об авторах
Д-р Харви М. Дейтел, руководитель фирмы Deitel & Associates, Inc., обладает
40-летним опытом в области информатики компьютерной техники, включая
обширный опыт работы в науке и промышленности. Он — один из ведущих в мире
инструкторов и руководителей семинаров в области компьютерных наук. Д-р
Дейтел получил степень бакалавра и магистра В Массачусетсом Технологическом
институте и степень доктора философии в Бостонском университете. Он работал над
первыми проектами операционных систем с виртуальной памятью в компании
IBM и Массачусетском Технологическом институте, где были разработаны
технологии, ныне широко используемые в таких системах, как UNIX, Linux и Windows
NT. Он обладает 20-летним стажем преподавания в колледже, в том числе в
качестве председателя Департамента компьютерных наук в Бостонском колледже до
создания компании Deitel & Associates, Inc. совместно с Полом Дж. Дейтелем. Он
является автором или соавтором нескольких десятков книг и мультимедийных
курсов, он продолжает писать и сейчас. Работы д-ра Харви М. Дейтела были
опубликованы в переводах на японский, русский, испанский, китайский, корейский,
французский, польский, португальский и итальянский языки, они получили
международное признание. Д-р Дейтел проводил профессиональные семинары по
всему миру для сотрудников крупных корпораций, правительственных и военных
учреждений.
24
Технологии программирования на Java 2
Пол Дж. Дейтел, Главный Технолог компании Deitel & Associates, Inc.,
закончил Школу менеджмента Слоун Массачусетского Технологического института, где
он изучал информационные технологии. Работая в компании Deitel & Associates,
Inc., он преподавал программирование на Java, С, С—К а также обучал созданию
приложений для Internet и Всемирной паутины сотрудников различных
организаций, в том числе Compaq, Sun Microsystems, White Sands Missile Range, Rogue
Wave Software, Computerviaion, Stratus, Fidelity, Cambridge Technology Partners,
Boeing, Lucent Technologies, Adra Systems, Entergy, CableData Systems, NASA
(Космический центр имени Кеннеди), National Severe Storm Laboratory, IBM,
и многих других организаций. Он читал лекции по C++ и Java на Бостонском
отделении Ассоциации по вычислительной техники, а также вел курсы по Java в
совместном предприятии Deitel & AssociatBS, Inc., Prentice Hall и Technology
Education Network. Он и его отец, д-р Харви М. Дейтел, авторы учебников по
компьютерным наукам, ставших мировыми бестселлерами.
Шон Э. Сэатри, директор по разработке программных продуктов компании
Deitel & Associates, Inc., выпускник Бостонского колледжа, где он изучал
компьютерные науки и философию. Обучаясь в колледже, он выполнил оригинальное
исследование по применению метафизических систем при проектированию
объектно-ориентированного программного обеспечения. Работая в компании Deitel &
Associates, Inc., он преподавал ознакомительные и углубленные курсы для
сотрудников таких промышленных компаний, как Sun Microsystems, EMC2, Dell,
Compaq, Boeing и других. Он принимал участие в создании ряда публикаций Deitel,
таких как Java Bow to Program. Forth Edition (Как программировать на Java. Изда
ние 4-е); XML How to Program (Как программировать на XML); C++ How to
Program, Third Edition (Как программировать на C++. Издание 3-е); С How to
Program, Third Edition (Как программировать на С. Издание 3-е); e-Business and
e-Commerce How to Program (Как программировать приложения для электронного
бизнеса и электронной коммерции) и e-Buainens and e-Commerce for Managers
(Электронный бизнес и электронная коммерция для менеджеров). До
поступления на работу в Deitel & Associates он разрабатывал приложения для электронного
бизнеса в одной из ведущих в регионе Бостона консалтинговых фирм.
О компании Deitel & Associates, Inc.
Компания Deitel & Associates, Inc. является широко известной организацией,
осуществляющей обучение и специализирующейся на языках программирования,
Web-технологиях, объектных технологиях, электронном бизнесе и коммерции.
Компания Deitel & Associates, Inc. является членом консорциума Всемирной
паутины (W3C). Компания осуществляет обучение современным курсам
программирования на Java™, C++, Visual Basic*, С, С#, Perl, Python, XML™, для Internet
и Всемирной паутины, объектным технологиям, созданию приложений для
Internet и Всемирной паутины, электронного бизнеса и электронной коммерции.
Руководят компанией Deitel & Associates, Тпс. д-р Харви М. Дейтел и Пол Дж.
Дейтел. В числе клиентов компании многие крупнейшие компьютерные
компании, правительственные учреждения, отделения военных и коммерческих
организаций. Благодаря сотрудничеству с издательством Prentice Hall, компания Deitel &
Associates, Inc. издает современные учебники по программированию, книги для
профессионалов, интерактивные мультимедийные курсы из серии Cyber
Classrooms на CD-ROM и дистанционные обучающие курсы. С компанией Deitel &
Associates, Inc. и авторами можно связаться по электронной почте по адресу
deitelSdeitel.com
Предисловие
25
Чтобы больше узнать о компании Deitel & Associates, Inc., ее публикациях и
программах обучения, посетите сайт:
www.deitel.com
Компания Deitel & Associates, Inc. предлагает конкурсные вакансии в своей
Программе интернатуры для студентов в Бостонском регионе. Для нс-лучения информации,
пожалуйста, свяжитесь с Эбби Дейтел по электронному адресу deitel@deitel.com.
Желающие приобрести книги Х.М. Дейтела и П.Дж. Дейтела и учебные курсы
(Complete Training Courses) могут сделать это на сайте
www.deitel.com
Консорциум World Wide Web (W3C)
Г» Компания Deitel & Associates, Inc. является членом консорциума
World Wide Web Consortium (W3C). W3C был основан в 1994 г.
\л «дли разработки протоколов для развития Всемирной паутины».
Как член W3C, мы имеем представительство в Совещательном
комитете W3C (нашим представителем в Совещательном комитете является Пол
Дейтел). Члены Совещательного комитета помогают обеспечивать «стратегическое
направление» деятельности W3C через проведение конференций и встреч по
всему. Организации-члены также помогают разрабатывать рекомендации по
стандартам для Web-технологий (таких как HTML, XML) путем участия в деятельности
W3C и рабочих группах. Членами W3U могут быть крупные компании и
организации. Для получения информации о том, как стать членом W3C, посетите сайт
www .w3.org/Consortium /Prospe с t us/J oinin g.
/
Введение
Цели
• Познакомиться со структурой
книги.
• Узнать об особенностях
работы с примерами.
• Получить представление
о паттернах проиктирования
и способах их применения
в контексте данной книги.
• Познакомиться с кратким
путеводителем по книге.
Прежде чем начать, следует все
тщательно спланировать.
Марк Туллий Цицерон
Дела лучше всего идут в самом
начале.
Близ Паскаль
Высокие мыли должны
излагаться высоким языком.
Аристофан
Наша жизнь растрачивается на
мелочи... Упрощайте, упрощайте.
Генри Торо
Благоволите смелому началу.
Вергилий
Думаю, я начинаю кое-что
в этом понимать.
Огюст Ренуар
28
Глава 1
• гкч. й.2:1:у'сЩ|ернЁ1е пр^оженияи^ргсерЩОй
•-. ^и1.2;2Й1СорГ10рэтив1№1еприлож€Нйя(Еп4^гюте Java,
"13:^ .,Кра1»сии|Г1>Ш^Дит^|»:Й0 книге/ -:%y#.-. ■r r^v
^ ^^^бШЙСГОР^ . Г
^1*5;2^0вд!йр^|йттернов; проектирования fei .---?■ . :
J;
■-■
-"•кегельного выполнения л.: .<
те1<турнь»ш паттерны проектирования ,
1.1. Введение
*а^
е ресурсы по. паттернам проектирования
Добро пожаловать в мир разработки приложений на платформе Java 2! Мы
много потрудились, чтобы создать книгу, которая, как мы надеемся, будет
информативной, увлекательной и познавательной1.
Технологии Java, с которыми вы познакомитесь, предназначены для
разработчиков и программных средств. Книга рассчитана на читателя, который имеет
представление об основах Java и объектно-ориентированном программировании,
прочитав, например, книгу Как программировать на Java. Издание 4-е, В нашей
книге многие вопросы программирования на Java рассматриваются более глубоко.
Также обсуждается ряд новых тем, сопровождаемых несколькими тысячами строк
исходного кода и многочисленными иллюстрациями, позволяющими лучше
понять принципы программирования. Рассматриваемые технологии нашли свое
отражение в корпоративных системах, демонстрирующих эти технологии во
взаимодействии. Подобный подход мы называем методом «живого кода» (live-code™).
Мы познакомимся с технологиями, входящими в состав трех редакций Java:
Java 2 Standard Edition (J2SE), Java 2 Enterprise Edition (J2EE) и Java 2 Micro
Edition (J2ME). В книге основное внимание уделяется дополнительным
функциональным возможностям J2EE, и предоставляются примеры корпоративных
приложений. Помимо этого, мы рассмотрим передовые технологии J2ME и средства
программирования приложений с беспроводным доступом.
Данная книга является переводом третьей части книги «Advanced Java™ 2 Platform.
How to Program». Оригинал содержит более 1800 страниц, поэтому было принято
решение русское издание разбить на три части. Третья часть, Которую Вы дернейте в
руках, посвящена созданию серверных приложений и корпоративных систем.
Первая часть посвящена программированию графического пользовательского
интерфейса, двухмерной и трехмерной графике, взаимодействию с базами данных,
компонентам JavaBeans. Вторая часть посвящена созданию распределенных приложений. —
Прим.. ред.
Введение
29
Объектно-ориентированное программирование и паттерны проектирования
играют важную роль в разработке приложений и программных систем с
использованием многих рассматриваемых в книге технологий. Эти инструментальные
средства обеспечивают модульность, давая возможность программистам эффективно
разрабатывать классы и программы. Паттерны проектирования особенно важны для
построения серьезных программ, которые мы представляем в этой книге.
Многие из рассматриваемых в этой книге приложений используют
возможности расширяемого языка разметки Extensible Markup Language (XML), который
фактически является стандартом для создания языков разметки, позволяющим
описывать структурированные данные не зависимым от используемой платформы
образом. XML могут использовать все приложения, начиная от обычных Web-
страниц и заканчивая сложными системами предприятие предприятие (business-
to-business — В2В). Принцип переносимости данных XML дополняет принцип
переносимости программ, разработанных для платформы Java 2. Возможности XML
для описания данных позволяют системам, построенным на основе различных
технологий, совместно использовать данные, не заботясь о совместимости на уровне
машинного кода, что очень важно для разработки сложных, в том числе
распределенных систем на Java. Мы предполагаем, что читатель имеет представление
о XML и об интерфейсе прикладного программирования XML Java, Для тех, кто не
знаком с этими вопросами, рекомендуем предварительно прочесть какую-либо
книгу по XML1. По мере чтения книги у вас может возникнуть желание
обратиться на наш Web-сайт www.deitel.com для получения обновлений и дополнительной
информации по передовым технологиям, которые вы будете изучать.
1.2. Архитектура книги
1.2.1. Серверные приложения и Web-сервисы
Главы 2-5, 14
Популярность Всемирной паутины и значимость ее для ведения бизнеса за
последние несколько лег резко возросли. Web-сервисы дают возможность
осуществлять совместное использование информации, ведение бизнеса и иные
взаимодействия компаний между собой и компаний с потребителями с использованием
стандартных Web-протоколов. Web-сервисы эволюционировали из
существующих Web-технологий, таких как формы HTML, и корпоративных технологий,
таких как системы обмена сообщениями и электронными документами (EDI).
Web-сервисы основаны на существующих протоколах и стандартах.
Глава 2 знакомит с сервлетами. Сервлеты способны динамически генерировать
документы (например, XHTML-документы), отправляемые клиентам в ответ на их
запросы на получение информации. В главе 3 вы познакомитесь с серверными
страницами Java Server Pages (JSP)y которые также осуществляют доставку
клиентам динамического содержимого. JSP-страницы динамически обрабатывают
Web-еодержимоё с помощью скриптлетов (scriptlets) и компонентов JavaBeans
в контексте документа. Эти документы транслируются в сервлеты контейнером
JSP, который представляет собой серверное приложение, ответственное за
обработку запросов к JSP-страницам. В главе 4 представлен практический пример,
в котором нашли воплощение технологии, рассмотренные в главах 2 И 3. В нем
объединены возможности компонентов JavaBeans, сервлетов, JSP-страниц, XML
и XSLT для создания книжного Internet-магазина.
1 Можно порекомендовать читателю книгу Х.М. Дейтел, П,Дж. Дейтел, Т.Р. Нието и др.
«Как программировать на XML*. М.г Издательство Бином, 2001 г., 944 с. — Прим.ред.
30
Глава 1
Для поддержки работы с беспроводными устройствами было создано несколько
новых технологий, таких как Wireless Application Protocol (WAP), Wireless
Markup Language (WML), i-mode и Java 2 Micro Edition (J2ME). В главе 5 вы
познакомитесь с этими технологиями и примените их для построения трехуровневого
приложения, в котором сервлеты и XML используются для доставки содержимого
нескольким типам беспроводных устройств.
Глава 14 знакомит с Web-сервисами — приложениями, предоставляющими
общедоступные интерфейсы, которые могут использоваться другими приложениями
в Web. Web-сервисы доступны через протокол HTTP и другие Web-протоколы
и осуществляют обмен информацией с. помощью XML-сообщений, Службы
каталогов дают возможность клиентам выполнять поиск для обнаружения доступных
Web-сервисов. Протокол простого доступа к объектам Simple Object Access Protocol
(SOAP) использует XML для предоставления многим Web-сервисам средств
коммуникационного взаимодействия. SOAP дает возможность приложениям
осуществлять удаленные вызовы процедур для обращения к общедоступным методам
Web-сервиса. В этой главе будет реализован сервис, который предоставляет
информацию о погоде в различных регионах США, получая эти сведения с сервера
Национальной метеослужбы National Weather Service с помощью SOAP.
1.2.2. Корпоративные приложения Enterprise Java
Главы 6—8, 13
Язык Java стал чрезвычайно популярен для построения корпоративных
приложений. Изначально Java задумывался как язык программирования для создания
небольших программ, встраиваемых в Web-страницы, но постепенно Java
разросся до мощного языка разработки корпоративных приложений. Согласно
заявлению Sun Microsystems, на сегодняшний день более 95% корпоративных систем
поддерживают технологию J2EE.
Бизнес-логика образует ядро функциональных возможностей корпоративного
приложения. Бизнес-логика отвечает за реализацию сложных бизнес-правил,
которые необходимы для проведения транзакций и обработки информации. В главе 6
рассказывается о компонентной модели Enterprise JavaBeans (EJB) и
применении ее для построения бизнес-логики корпоративных приложений. В частности,
будут рассмотрены сеансовые компоненты EJB для бизнес-логики и
распределенные транзакции, которые позволяют EJB-компонентам работать с несколькими
базами данных, обеспечивая при этом целостность данных. В главе 7 будут
рассмотрены EJB-компоненты управления данными (EJB-сущности), которые дают
возможность разработчикам формировать объектный уровень для доступа к
информации, содержащейся в долговременном хранилище, таком как база данных.
Корпоративные приложения требуют для своей работы многочисленных
сервисов и средств поддержки выполнения для доступа к базам данных, проведения
распределенных транзакций, управления производительностью и т.д. Серверы
приложений предоставляют окружение выполнения для компонентов корпоративного
приложения. В главе 13 вы познакомитесь с тремя наиболее популярными
коммерческими серверами приложений: BEA WebLogic, IBM WebSphere и iPlanet
Application Server. Мы также предоставим полные инструкции для развертывания
на серверах BEA WebLogic и IBM WebSphere практического примера
корпоративного приложения.
Введение
31
1.2.3. Практический пример корпоративного приложения
Главы 9-12
В главах 9-12 представлено приложение, в котором нашли отражение все
основные темы по Enterprise Java, рассматриваемые в книге. Это практический
пример применения многих технологий Java в большом приложении (почти 10000
строк кода) для книжного Internet-магазина. Приложение Deitel Bookstore
построено с использованием компонентов Enterprise JavaBeans с перс и стентн остью
(сохранемостью), управляемой контейнером сервлетов, а также с использованием
технологий RMI-ПОР, XML, XSLT, XHTML, WML и cHTML. Главной
особенностью этого примера является применение XML и XSLT для обеспечения
поддержки клиентов практически любого типа, включая стандартные Web-браузеры и
мобильные устройства, такие как сотовые телефоны. Расширяемая модульная
архитектура дает возможность разработчикам добавлять поддержку для новых типов
клиентов, просто предоставляя соответствующих XSLT-документы, которые
транслируют XML-документы в содержимое, подходящее для клиентов этого типа.
Практический пример Deitel Bookstore также демонстрирует архитектуру модель—
вид—контроллер (model—view—controller — MVC) в контексте приложения
Enterprise Java.
1.3. Краткий путеводитель по книге
В этом разделе мы вкратце пройдемся по главам книги и выделим
многочисленные технологии Java, которые обсуждаются в книге. Возможно, вы встретитесь
с терминами, которые окажутся для вас незнакомыми, — они будут определены
в последующих главах книги. В конце многих глав имеется раздел «Ресурсы
в Internet и во Всемирной паутине», где перечислены Web-сайты, которые вам
следует посетить, чтобы получить дополнительную информацию по обеуждаемым
в этой главе технологиям. Вы также можете посетить Web-сайты www.deitel.com
и www.prenhall.com/deitel, где получите последнюю информацию, сведения об
ошибках и опечатках и узнаете о дополнительных информационных и обучающих
ресурсах.
Глава 1. Введение
Эта глава содержит обзор технологий, представляемых в книге, и знакомит
с архитектурой книги. Мы включили в эту главу краткий путеводитель по книге
с обзором каждой из глав. Для рассматриваемых в книге примеров мы приводим
инструкции по установке и выполнению. Здесь же вкратце обсуждаются паттерны
проектирования и принципы их применения в наших примерах.
Глава 2. Сервлеты
Сервлеты расширяют функциональные возможности серверов (обычно Web-
серверов). Сервлеты эффективны при разработке Web-приложений, в которых
осуществляется взаимодействие с базами данных в интересах клиента, динамически
генерируется содержимое, отображаемое браузерами, и сохраняется уникальная
для каждого клиента информация о сеансе. Многие разработчики осознают, что
сервлеты являются верным выбором для интенсивно работающих с базами данных
приложений, которые взаимодействуют с так называемыми топкими клиента
ми — приложениями, которые требуют минимальной обработки на стороне
клиента. Клиенты осуществляют соединение с сервером с помощью стандартных
протоколов, таких как HyperText Transfer Protocol (HTTP), доступных для
Web-браузеров большинства клиентских платформ. Таким образом, логика приложения
может быть написана один раз и размещена на сервере для доступа к ней клиентов.
32
Глава 1
Интерфейс прикладного программирования API Java Servlet позволяет
разработчикам добавлять в Web-серверы функциональные возможности для обработки
клиентских запросов. В отличие от интерфейса Common Gateway Interface (CGI),
в котором для каждого клиентского запроса может быть запущен отдельный
процесс, сервлеты обычно являются потоками одного процесса в виртуальной машине
Java (JVM). Сервлеты также допускают многократное применение на различных
Web-серверах и на различных платформах. В этой главе демонстрируется
механизм запрос/ответ (главным образом, посредством HTTP-запросов get и post),
возможности отслеживания состояния сеанса, переадресации запросов другим
ресурсам и взаимодействия с базами данных через JDBU.
Глава 3. Java Server Pages (JSP)
В этой главе рассматривается расширение технологии сервлетов под названием
Java Server Pages (JSP), Технология JSP позволяет осуществлять доставку
динамически генерируемого Web-содержимого и используется главным образом для
разработки логики визуального представления данных в корпоративных
приложениях Enterprise Java. JSP-страницы могут содержать код Java в виде скриптле-
тов, а также могут использовать компоненты JavaBeans. Библиотеки
нестандартных тегов дают возможность дизайнерам Web-страниц, не знакомым с Java,
усовершенствовать Web-страницы за счет добавления динамического содержимого
и новых возможностей обработки, создаваемых разработчиками на Java. Для
повышения производительности каждая JSP-страница компилируется в сервлет
Java; это обычно происходит при первом запросе JSP-страницы клиентом.
Последующие запросы клиента обрабатываются уже откомпилированным сервлетом.
Глава 4. Книжный Internet-магазин, реализованный
с использованием, сервлетов и JSP
Эта глава является основополагающей для изучения технологий JSP и
сервлетов. В ней мы реализуем Web-приложение для книжного Internet-магазина, в
котором интегрированы технологии JDBC, XML, JSP и сервлетов- В этой главе
осуществляется развертывание приложения для книжного Internet-магазина на сервере
приложений эталонной реализации J2EE 1.2.1. В состав эталонной реализации
J2EE 1.2.1 входят средство Apache Tomcat JSP и контейнер сервлетов. Изучив эту
главу, вы сможете реализовать большое распределенное Web-приложение с
множеством компонентов, а также осуществить развертывание этого приложения на
сервере приложений J2EE 1,2.1.
Глава 5. Разработка приложений для беспроводной связи
на базе Java и J2 ME
Одной из тем, вызывающих особый интерес при изучении вопросов создания
приложений для электронного бизнеса и электронной коммерции, является
технология беспроводного коммуникационного взаимодействия через Internet.
Беспроводные технологии превращают электронный бизнес (e-business) в мобильный
бизнес ( m-business ). Они позволяют вам подключаться к Internet в любое время
и практически в любом месте. Вы можете использовать эти технологии для
проведения сетевых транзакций, покупок товаров в Internet-магазинах, игры на бирже
и отправки сообщений электронной почты. Новые технологии дают возможность
организовать мобильный офис, в котором компьютеры, телефоны и другое офисное
оборудование подключены к сети без помощи кабелей. Эта глава знакомит с
некоторыми наиболее популярными беспроводными технологиями, такими как WAP,
i-mode и Java 2 Platform Micro Edition™ (J2ME). J2ME расширяет технологию
Java на встроенные устройства и бытовые устройства, имеющие ограниченные
мощности по обработки данных и небольшие ресурсы памяти. J2ME имеет в своем
составе специализированные API для многих бытовых устройств, таких как сото-
Введение
33
вые телефоны, смарт-карты, приборы, имеющие возможность подключения в
Internet, и карманные компьютеры, например, Palm™ и PocketPC. Система К
Virtual Machine представляет собой усеченную версию виртуальной машины Java
для бытовых устройств, которая обеспечивает возможности для выполнения
Java-программ на этих устройствах. Мы рассмотрим практический пример
трехуровневого приложения с использованием сервлетов и XML, которое способно
работать с беспроводными устройствами нескольких различных типов.
Глава 6, Сеансовые компоненты EJB и распределенные транзакции
Компоненты Enterprise JavaBeans (EJB) дают возможность разработчикам на
Java строить надежные и устойчивые многоуровневые приложения, В
многоуровневом приложении ответственность по предоставлению услуг клиенту может быть
поделена между несколькими серверами. Типовое двухуровневое приложение
состоит из уровня клиента и уровня сервера. В трехуровневом приложении в
качестве среднего уровня между клиентским Web-браузером и сервером базы данных
часто используется сервер приложений. Компоненты Enterprise JavaBeans
предоставляют инфраструктуру для построения реализаций бизнес-логики среднего
уровня. Используя RMT и контейнеры EJR, компоненты Enterprise JavaBeans
также способствуют распределению средств бизнес-логики по различным узлам
в сети. Мы познакомимся с компонентами Enterprise JavaBeans, которые
обеспечивают компонентную модель для построения бизнес-логики в корпоративных
приложениях Java. Будут рассмотрены две формы сеансовых компонентов EJB:
с состоянием и без состояния, и продемонстрировано, как разрабатывать
сеансовые компоненты EJB. Мы также познакомимся с поддержкой EJB-компонентами
распределенных транзакций, которая помогает обеспечить целостность данных
в базах данных и на серверах приложений. Мы покажем, как создавать EJB-kom-
поненты, которые используют средства поддержки распределенных транзакций
J2EE для атомарного обновления данных в нескольких базах данных.
Глава 7. EJB-компоненты с данными
В этой главе будет продолжено наше обсуждение технологии Enterprise
JavaBeans и рассмотрены компоненты Enterprise JavaBeans с данными, или
компоненты-сущности EJB. В отличие от сеансовых компонентов EJB,
компоненты-сущности EJB содержат информацию в долговременных хранилищах, таких как базы
данных. Компоненты-сущности EJB обеспечивают объектно-ориентированное
представление постоянных данных, таких как данные, хранящиеся в
реляционных базах даяных или в наследованном приложении. Компоненты-сущности EJB
могут быть использованы для построения мощных и гибких приложений для
работы с данными. Имеется два типа компонентов EJB с данными: с персистентно-
стью (сохраняемостью), управляемой самим компонентом и с персистентно-
стъю, управляемой контейнером. Компоненты-сущности EJB, которые
используют управление персистентностью на стороне компонента, сами реализуют код для
сохранения и извлечения данных из источников данных. Например,
компонент-сущность EJB, использующий персистентность, управляемую компонентом,
может использовать JDBC для сохранения и извлечения данных из реляционной
базы данных. Для компонентов-сущностей EJB, в которых используется
персистентность, управляемая контейнером, обращения к данным, хранящимся в
постоянном источнике, реализуются контейнером EJB. Разработчик должен
предоставить информацию о постоянном источнике данных при развертывании EJB-kom-
поиента. В этой главе демонстрируются оба типа компонентов-сущностей EJB.
Глава 8. Обмен сообщениями с помощью Java Message Service (JMS)
Технология Java Message Service (JMS) предоставляет интерфейс
прикладного программирования для интегрирования корпоративных приложений Java с сие-
34
Глава 1
темами промежуточного программного обеспечения, ориентированными на работу
с сообщениями (message-oriented middleware —- MOM), Подобные системы дают
возможность приложениям взаимодействовать посредством отправки друг другу
сообщений. Промежуточное программное обеспечение, ориентированное на работу
с сообщениями, является популярной технологией для построения
слабосвязанных приложений. Эта глава знакомит с двумя основными моделями систем обмена
сообщениями: *от точки к точкеь и «издатель/подписчика. Мы
продемонстрируем интерфейсы Java для обеих моделей. Мы также познакомимся с EJB-компо-
нентами, управляемыми сообщениями, которые являются новой особенностью,
появившейся в версии 1.3 J2EE.
Глава 9. Практический пример корпоративного приложения.
Обзор архитектуры
Технологии в составе Java 2 Enterprise Edition (J2EE) позволяют
разработчикам создавать устойчивые, расширяемые корпоративные приложения. В этом
практическом примере мы построим приложение для электронного бизнеса,
используя ряд функциональных возможностей J2EE, таких как сервлеты,
компоненты Enterprise JavaBeans, XML и XSLT. Мы также интегрируем в наше
приложение технологию беспроводного доступа к информации, в частности, WAP/WML
и i-mode/cHTML. В этой главе содержится обзор архитектуры приложения Deitel
Bookstore, в котором используются паттерны проектирования MVC в контексте
корпоративного приложения. R последующих главах мы рассмотрим логику
управления, реализованную с помощью сервлетов (глава 10), бизнес-логику и
абстракцию данных, реализованные с помощью компонентов EJB (главы 11 и 12).
Глава 10. Практический пример корпоративного приложения.
Логика представления данных и логика управления
В этой главе описана реализация логики управления и визуального
представления данных для книжного Internet-магазина Deitel Bookstore. Логика управления
(контроллер) представляет собой приложение, ответственное за обработку
клиентских запросов. Логика управления в этом приложении реализована на сервле-
тах Java. Каждый запрос пользователя обрабатывается сервлетом, который
предпринимает соответствующее действие в зависимости от типа поступившего запроса
(например, запрос на просмотр каталога товаров) и представляет содержимое
клиенту. Мы используем XSLT-трансформации для реализации логики визуального
представления данных для приложения. После вызова методов бизнес-логики для
обработки клиентских запросов сервлет генерирует XML-документы, которые
включают в себя содержимое, отображаемое клиенту. Эти XML-документы не
являются специфичными для конкретных типов клиентов (например; Web-браузер,
сотовый телефон и т.д.); они просто описывают данные, предоставленные
бизнес-логикой. XSLT-трансформация применяется к XML-документам для
отображения пользователю информации в соответствующем формате. Например, XSLT-
трансформация может генерировать XHTML-документ для отображения его
в Web-браузере, либо WML-документ для отображения его в WAP-браузере.
XSLT-трансформащш необходимо выполнять для каждого поддерживаемого
приложением типа клиента. Мы можем добавлять в приложение поддержку других
типов клиентов, просто реализовав дополнительные таблицы стилей и
отредактировав файл конфигурации.
Глава 11. Практический пример корпоративного приложения.
Бизнес-логика, часть 1
В этой главе мы рассмотрим бизнес-логику, реализующую магазинную тележку
покупателя в модели Internet-магазина Doitel Bookstore, а также и
компоненты-сущности EJB для хранения информации о товарах. Главное назначение при-
Введение
35
ложения для Internet-магазина — дать возможность пользователям покупать
товары. EJB-компонент бизнес-логики реализует бизнес-правила, которые управляют
этим процессом. Мы реализуем бизнес-логику для управления группой товаров,
которые пользователь хотел бы купить, в виде EJB-компонента ShoppingCart.
EJB-компонент ShoppingCart устанавливает бизнес-правила, которые
определяют, каким образом товары добавляются в магазинную тележку, каким образом
создаются магазинные тележки, и каким образом потребители совершают свои
покупки. Мы также рассматриваем компоненты-сущности EJB, которые
представляют товары в Internet-магазине и информацию о доставке заказов. Изучив эту
главу, вы поймете, как использовать EJB-компоненты в контексте приложения для
электронного бизнеса, а также получите представление о дополнительных
возможностях EJB-компонентов, таких как собственные классы первичных ключей и
отношения типа -«многие ко многим*.
Глава 12. Практический пример корпоративного приложения.
Бизнес-логика, часть 2
В этой главе мы рассмотрим бизнес-логику для управления взаимодействием
с покупателем в нашем Internet-магазине Deitel Bookstore. Наличие информации
о покупателях Internet-магазина может сделать покупки более удобными для
пользователя. Информации об оплате и доставке при этом сохраняется на сервере.
Отдел маркетинга Internet-магазина также может использовать собранные данные
для распространения рекламных материалов и анализа демографического состава
покупателей. Мы также представляем компонент-сущность EJB, который
генерирует уникальные идентификаторы для EJB-компонентов Customer, Order и
Address. Экземпляры этих EJB-компонентов создаются, когда потребитель проходит
регистрацию, и когда потребитель размещает новые заказы. В реляционных базах
данных необходимо иметь уникальные первичные ключи для обеспечения
ссылочной целостности и выполнения запросов. Мы предоставляем EJB-компонент Seq-
uenceFactory для генерирования этих уникальных идентификаторов, поскольку
не все базы данных могут автоматически генерировать значения составных
первичных ключей.
Глава 13. Серверы приложений
Эта глава знакомит с несколькими серверами приложений: программным
обеспечением, которое интегрирует серверные компоненты логики, создавая условия
для взаимодействия между различными компонентами и уровнями в архитектуре
программы. Серверы приложений также управляют персистентностью,
жизненным циклом, безопасностью и другими сервисами для логических компонентов.
Мы обсудим концепции, связанные с серверами приложений, и рассмотрим три
популярных коммерческих сервера приложений: BEA WebLogic, IBM WebSphere
и iPlanet Application Server. Мы пройдем через все этапы развертывания
приложения Deitel Bookstore на серверах BEA WebLogic и IBM WebSphere. На момент
подготовки книги к публикации компания iPlanet выпустила новую версию сервера
приложений. Пожалуйста, посетите сайт www.iplanet.com/ias-deitel, чтобы
загрузить последнюю версию. Полные инструкции по развертыванию для учебного
приложения Deitel Bookstore на сервере iPlanet можно будет найти на нашем
Web-сайте www.deitel.coni.
Глава 14. Введение в Web-сервисы и SOAP
Функциональная совместимость, или беспроблемное взаимодействие между
различными программными системами, является главной заботой компаний и
организаций, которые используют в своей деятельности компьютерные и сетевые
технологии. Эта глава знакомит с Web-сервисами, использующими протокол
простого доступа к объектам Simple Object Access Protocol (SOAP), который призван
36
Глава 1
разрешить упомянутую выше проблему. Web-сервисы могут представлять собой
приложения, доступные через Web, например, Web-страницы с динамическим
содержимым. В более узком смысле Web-сервисы обеспечивают общедоступные
интерфейсы для использования их Web-приложениями. SOAP — это протокол,
который использует XML для осуществления удаленных вызовов процедур через
HTTP, чтобы обеспечить совместимость между различными Web-приложениями.
1.4. Выполнение примеров1
Многие из примеров программ а книге являются достаточно сложными и
требуют специального программного обеспечения. Так, в главах 9—12 представлен
комплексный практический пример, для которого требуется сервер приложений,
обеспечивающий окружение выполнения и сервисы для корпоративного приложения.
Для этого комплексного примера также требуется база данных. Мы обновляем
инструкции по установке программного обеспечения на нашем сайте www.deitel.com
по мере выпуска новых версий Java SDK корпорацией Sun.
При написании этой книги мы ориентировались на версию 1.2.1 эталонной
реализации Java 2 Enterprise Edition. На сегодняшний день доступна версия 1.3,
имеющая ряд усовершенствований и нововведений. Например, в версии 1.3
реализован сервис обмена сообщениями Java Messaging Service (JMS 1.0.2), технология
J2EE Connector Technology и Java API for XML Processing (JAXP 1.1). В
технологии сервлетов Java Servlets (версия 2.3) реализованы фильтры, упрощенная
инфраструктуру передачи данных для запросов и ответов, мониторинг жизненных
циклов приложений и усовершенствованная многоязыковая поддержка. В
реализации Java Server Pages (версия 1.2) усовершенствована поддержка выполнения
для библиотек тегов и проверки JSP-страницы на этапе трансляции. Реализация
технологии Enterprise JavaBeans в версии 1.3 (EJB 2.0) поддерживает управление
корпоративными компонентами с помощью сообщений, а также обеспечивает
совместимость между контейнерами EJB и технологией управления персистентно-
стью на стороне контейнера Container-Managed Persistence 2.0.
В примерах в книге используется стандартное соглашение по именованию
пакетов. Мы помещаем каждый из примеров в соответствующим образом именованный
подпакет пакета com.deitel. Например, в объявлении
package com.deitel.advjhtpl,gui.webbrowser;
акроним advjhtpl в имени пакета соответствует начальным буквам слов в
оригинальном названии книги (Advanced Java 2 Platform How to Program), 1 указывает
на 1-е издание книги. Такая структура пакета требует, чтобы вы компилировали
примеры с использованием соответствующей структуры каталогов.
Управление пакетами с помощью компилятора командной строки Java и с
помощью инструментальных средств может оказаться излишне обременительным,
поэтому мы рекомендуем читателям использовать интегрированные среды
разработки, чтобы упростить создание и выполнение примеров и упражнений в этой
книге. Мы использовали для работы с примерами в этой книге интегрированную
среду разработки Sun Forte for Java Community Edition, которая является
производной от NetBeans (www.netbeans.org). Рекомендации по установке Forte и его
использованию для разработки приложений вы можете найти, обратившись
к справочной системе Forte или к документации по адресу
www.sun.com/forte/ffj/documentation/index.html
Примеры к книге можно загрузить по адресу http://www.deitel.com/books/downlo-
ads.html (раздел, посвященный книге Advanced Java™ 2 Platform How to program), a
также по адресу http://www.binom-press.ru/books/adv-java2.htni. — Прим. ред.
Введение
37
Большинство интегрированных сред разработки Java дают возможность
разработчикам загружать структуры каталогов, содержащие пакеты Java, Чтобы
облегчить работу с кодом, мы в примерах сохраняем полную структуру каталогов с
соответствующим размещением исходных файлов. Мы рекомендуем вам скопировать
эту структуру каталогов на жесткий диск своего компьютера. Скопировав
структуру каталогов, вы можете загружать примеры в соответствии с инструкциями для
вашей интегрированной среды разработки.1
1.5. Паттерны проектирования
Большинство примеров, представляемых в книгах по Java начального
уровня — таких как наша книга Как программировать на Java. Издание 4-е —
содержат не более 150 строк кода. Эти примеры практически не требуют какого-либо
проектирования, поскольку они используют всего несколько классов и
иллюстрируют элементарные приемы программирования. Однако большинство программ
в настоящей книге например, трехуровневое приложение для беспроводного
доступа (глава 5) и приложение для книжного Internet-магазина Deitel Bookstore
(главы 9-12), являются гораздо более сложными. Такие большие приложения могут
потребовать для реализации тысячи строк кода, содержать множество взаимных
связей между объектами и взаимодействий с пользователями. Для таких
программ важно применять проверенные, эффективные стратегии проектирования.
Системы для управления банкоматами или системы управления воздушным
движением могут содержать миллионы или даже сотни миллионов строк кода.
Эффективное проектирование крайне важно при разработке таких сложных систем.
За последнее десятилетие индустрия разработки программного обеспечения
добилась большого прогресса в области паттернов проектирования — проверенных
архитектур для построения гибкого и легко управляемого
объектно-ориентированного программного обеспечения [I].2 Применение паттернов проектирования
может значительно уменьшить сложность процесса проектирования. Хорошо
спроектированное объектно-ориентированное программное обеспечение дает
возможность разработчикам повторно использовать и интегрировать ранее созданные
компоненты в другие системы. Паттерны проектирования дают следующие
преимущества для разработчиков:
• Помогают создавать надежное программное обеспечение с использованием
проверенных архитектур и накопленного опыта их практического
применения.
• Способствуют повторному использованию кода и проектных решений во
вновь создаваемых системах.
• Помогают выявлять типичные ошибки и заблуждения, которые имеют место
при построении систем.
• Помогают разрабатывать системы вне зависимости от языков, на которых
они в конечном итоге будут реализованы.
• В процессе проектирования устанавливают единый «словарь» для
разработчиков.
• Сокращают длительность фазы проектирования в процессе разработки
программного обеспечения.
Имена папок с примерами не соответствуют нумерации глав, аринятой в этой книге.
Обратитесь к таблице соответствий в «Предисловии редактора русского перевода». —
Прим. ред.
См. раздел «Использованные источники» в конце главы. — Прим. ред.
38
Глава 1
Идея применения паттернов проектирования при проектировании
программных систем позаимствована из архитектуры. Архитекторы используют готовые
архитектурные элементы, такие как арки и колонны, при проектировании зданий.
Проектирование с применением арок и колонн — проверенная стратегия при
строительстве надежных сооружений. Эти элементы можно рассматривать как
архитектурные паттерны проектирования.
1.5.1. История паттернов проектирования
В течение 1991-1994 гг. Эрик Гамма, Ричард Хелм, Ральф Джонсон и Джон
Влиссидес — вместе они известны как «группа четырех» — объединили свой опыт
для написания книги Design Patterns, Elements of Reusable Object-Oriented
Software (Addison-Wesley: 1995). В этой книге было показано, что паттерны
проектирования естественным образом эволюционировали на протяжении развития отрасли.
Джон Влиссидес утверждает, что «наиболее важным моментом при создании
паттерна проектирования является трезвое размышление [2]». Это означает, что для
создания паттернов разработчики должны типизировать и документировать свои
успехи (и неудачи) при проектировании и реализации программных систем.
Разработчики используют паттерны проектирования, чтобы заимствовать и
применять этот коллективный опыт, который в конечном итоге помогает им совместно
с другими разработчиками использовать успешные решения.
В книге, написанной «группой четырех», описывается 23 паттерна
проектирования, каждый из которых предоставляет решение одной из типовых задач при
разработке программного обеспечения. В книге паттерны проектирования разбиты
на три категории: порождающие, структурные и поведенческие. Эти паттерны
проектирования перечислены в таблице на рис. 1.1.
Порождающие
Abstract Factory
Builder
Factory Method
Prototype
Singleton
Структурные
Adapter
Bridge
Composite
Decorator
Facade
Flyweght
Proxy
Паттерны поведения
Chain-of-Responsibility
Command
Iterator
Interpreter
Observer
Mediate
Memento
State
Strategy
Template Method
Visitor
Рис. 1.1. Паттерны проектирования, рассмотренные «группой четырех» е своей книге
Порождающие паттерны описывают способы реализации экземпляров объектов
(или групп объектов). Эти паттерны проектирования решают задачи, связанные
с созданием объекта, например, препятствование созданию системой более одного
объекта класса (паттерн проектирования Singleton) или отсрочка принятия
решения, какие типы объектов создавать, до момента выполнения (паттерн
проектирования Factory Method). Например, предположим, что мы разрабатываем
программу для трехмерного рисования, в которой пользователь может создавать несколько
Введение
39
трехмерных геометрических объектов, таких как цилиндры, сферы, кубы,
тетраэдры и т.д. На этапе компиляции программа не может получить информацию,
какие типы фигур пользователь выберет для добавления в рисунок. На основе данных,
введенных пользователем в процессе выполнения, эта программа должна
определить класс, для которого нужно создать экземпляр объекта. Если пользователь
решил создать цилиндр, программа должна «знать», что следует создать экземпляр
объекта класса Cylinder и добавить его в рисунок. Когда пользователь решает,
какой геометрический объект рисовать, программа должна определить конкретный
подкласс, для которого нужно создать экземпляр объекта.
Структурные паттерны проектирования описывают типовые способы
организации классов и объектов в системе. Разработчики часто сталкиваются с двумя
проблемами, связанными с плохой организацией. Первая состоит в том, что классам
поручается слишком много задач. Такие классы могут наносить ущерб сокрытию
информации и нарушать инкапсуляцию, поскольку каждый класс может иметь
доступ к информации, которая принадлежит другому классу. Вторая проблема
связана с возможным перекрытием возложенных па классы задач. Загромождение
системы ненужными классами отнимает лишнее время у разработчиков,
поскольку им придется затратить много часов на расширение или модификацию классов,
бея которых вполне можно обойтись. Как мы увидим, структурные паттерны
проектирования помогают разработчикам избежать этих проблем.
Поведенческие паттерны проектирования назначают задачи объектам. Эти
паттерны также предоставляют проверенные стратегии для моделирования
взаимодействия объектов друг с другом и предлагают особые способы поведения,
предназначенные для широкого спектра применений. Паттерн проектирования
Observer является классическим примером взаимодействия между объектами
и назначения задач для объектов. Например, компоненты графического
пользовательского интерфейса используют яти паттерны проектирования для
взаимодействия со своими слушателями, которые реагируют на действия пользователей.
Слушатели наблюдают за изменением состояний определенного компонента, для чего
они должны быть зарегистрированы в качестве обработчиков событий этого
компонента. Когда пользователь взаимодействует с компонентом, этот компонент
уведомляет своих слушателей (их также называют наблюдателями), что состояние
компонента изменилось (например, была нажата кнопка).
1.5.2. Обзор паттернов проектирования
Паттерны проектирования реализуются в программном коде в виде набора
классов и объектов. Чтобы эффективно использовать паттерны проектирования,
разработчики должны иметь представление о наиболее популярных и
эффективных паттернах проектирования, применяемых при построении программ. Ниже
мы вкратце обсудим основные паттерны проектирования и архитектуры, а также
их важную роль при построении качественных и эффективных приложений.
В данной книге паттерны проектирования нашли применение в главе 5 при
создании приложения с беспроводным доступом. В частности, мы будем использоваться
порождающие паттерны Factory Method и Singleton, а также паттерн поведения
Command. В главе 14 при реализации Web-сервиса прогноза погоды используется
структурный паттерн проектирования Adapter.
Мы используем не все паттерны проектирования из перечисленных в таблице
на рис. 1.1, а лишь те, которые подходят для решения конкретных задач
проектирования, с которыми мы сталкиваемся при написании примеров и учебных
приложений в этой книге. Ниже мы рассмотрим другие популярные паттерны
проектирования, рассмотренные «группой четырех», которые могут оказаться полезными
при разработке программного обеспечения, хотя мы и не используем их в
примерах в этой книге.
40
Глава 1
Prototype
Иногда системе требуется сделать копию объекта, но до момента выполнения
программы класс объекта неизвестен. Например, возьмем программу рисования
с несколькими классами фигур (например, классы Line, Oval, Rectangle и т.д.),
которые расширяют абстрактный суперкласс Shape. Пользователь этой
программы должен в любой момент иметь возможность создавать, копировать и вставлять
новые экземпляры классов Shape для добавления фигур в рисунки. Паттерн
проектирования Prototype дает возможность пользователям делать это. Этот паттерн
проектирования позволяет объекту — он называется прототипом — клонировать
самого себя. Прототип подобен штемпелю, который может использоваться для
создания нескольких идентичных оттисков. В программном компоненте каждый
прототип должен принадлежать классу, который реализует типовой интерфейс,
позволяющий прототипу клонировать самого себя. Например, Java API
предоставляет метод clone интерфейса java.lang.Cloneable — любой объект класса, который
реализует интерфейс Cloneable, использует метод clone для создания копии самого
себя. Конкретно, метод clone создает копию объекта, а затем возвращает ссылку
на этот объект. В программе рисования, если мы выбираем в качестве прототипа
класс Line, то он должен реализовывать интерфейс Cloneable. Чтобы создать в
нашем рисунке новый отрезок линии, мы клонируем прототип Line — этот прототип
будет возвращать ссылку на другой экземпляр объекта Line. Чтобы скопировать
ранее существующий отрезок линии, мы клонируем этот объект Line.
Разработчики часто испсль&уют метод clone, чтобы не допустить изменений объекта по его
ссылке, поскольку метод clone возвращает ссылку на копию объекта, а не ссылку
на сам объект.
Bridge
Предположим, мы разработали класс Button для операционных систем
Windows и Macintosh. Класс Button содержит специфическую информацию о
кнопке, такую как слушатель действия ActionListener и строку текста надписи.
Мы разработали классы Win32Button и MacBatton, чтобы расширить класс
Button. Класс Win32Button содержит информацию о внешнем виде кнопки и
способе ее отображения в Windows, а класс MacBatton содержит ту же информацию
для отображения кнопки в операционной системе MacOS.
При таком подходе возникают две проблемы. Во-первых, если мы создаем
новые подклассы класса Button, мы должны создавать соответствующие подклассы
классов Win32Button и Mac But ton. Например, если мы создаем класс ImageBut-
ton (кнопку Button с изображением Image), который расширяет класс Button, мы
должны создать дополнительные подклассы Win32ImageButton и Maclmage-
Button. Фактически, мы должны создавать подклассы класса Button для каждой
операционной системы, которую мы хотим поддерживать, что увеличивает время
разработки. Вторая проблема заключается в том, что при появлении на рынке
новой операционной системы мы должны создать дополнительные подклассы класса
Button, специфичные для этой операционной системы.
Паттерн проектирования Bridge решает эти задачи путем разделения
абстракции (например, класса Button) и его реализаций (например, подклассы Win32-
Button, MacButton и т.д.) на отдельные иерархии- Например, классы AWT Java
используют паттерн проектирования Bridge, чтобы дать возможность
разработчикам создавать подклассы AWT Button без необходимости создавать
соответствующие подклассы, специфичные для каждой из операционных систем. Каждый
класс AWT Button содержит ссылку на класс But ton Peer, который является
суперклассом для специфичных для платформ реализаций, таких как Win32But-
tonPeer, MacButtonPeer и т.д. Когда программист создает объект Button, класс
Button определяет, какой специфичный для платформы объект ButtonPeer созда-
Введение
41
вать, и сохраняет ссылку на этот объект ButtonPeer — эта ссылка является мостом
(bridge) в паттерне проектирования Bridge. Когда программист вызывает методы
объекта Button, объект Button вызывает соответствующий метод объекта Button-
Peer для удовлетворения запроса. Если разработчик создает подкласс Image-
Button класса Button, ему не нужно создавать соответствующий класс Win32Ima-
geButton или MadmageButton. Класс ImageButton является классом Button,
поэтому, когда программист вызывает метод класса ImageButton — например,
set Image — объекта ImageButton, суперкласс Button транслирует вызов метода
в вызов соответствующего метода класса ButtonPeer — например, drawlmage.
Совет по переносимости программ 1.1
Разработчики часто используют паттерн проектирования Bridge,
чтобы повысить независимость своих систем от платформы. Мы можем
разрабатывать подклассы класса Button, не беспокоясь о том, как
операционная система будет реализовыватъ каждый из подклассов.
Iterator
Разработчики используют структуры данных, такие как массивы, связанные
списки и хэш-таблицы, для организации данных в программе. Паттерн
проектирования Iterator дает возможность объектам осуществлять доступ к отдельным
объектам структур данных, не имея представления о реализации этой структуры
данных или о том, как эта структура хранит ссылки на объекты. Инструкции по
обработке структуры данных и доступа к ее элементам хранятся в отдельном
объекте под названием итератор (iterator). Каждая структура данных имеет
соответствующую реализацию итератора, способную обрабатывать эту структуру данных.
Другие объекты могут использовать этот итератор, который реализует
стандартный интерфейс, вне зависимости от базовой структуры данных или реализации.
Интерфейс Iterator из пакета java.uti] использует паттерн проектирования
Iterator. Рассмотрим систему, которая содержит структуры данных Set, Vector и List.
Алгоритмы для извлечения данных из каждой структуры различаются для
разных классов. При использовании паттерна проектирования Iterator каждый класс
содержит ссылку на объект Iterator, который хранит информацию относительно
обработки, специфичную для каждой структуры данных. Для объектов этих
классов мы вызываем метод iterator для получения ссылки на итератор Iterator,
относящийся к этому объекту. Мы вызываем метод next интерфейса Iterator для
получения следующего элемента в структуре. При этом нам не нужно знать о деталях
реализадии структуры.
Memento
Рассмотрим программу, которая дает возможность пользователю рисовать
графические изображения. Бывает, что пользователь неправильно разместил
изображение в области рисования. Программа может предоставлять функцию отмены,
которая позволяет пользователю устранять подобные ошибки. В частности,
программа может восстанавливать оригинальное состояние области рисования (до
помещения в нее изображения пользователем). Более сложные программы
рисования имеют журнал, который хранит список нескольких состояний, чтобы
пользователь мог восстановить любое состояние программы из имеющихся в списке.
Паттерн проектирования Memento позволяет объекту сохранять свое состояние,
чтобы — если необходимо — пользователь мог восстановить предыдущее
состояние объекта.
Для паттерна проектирования Memento требуется три типа объектов. Объект
хозяин (Originator) сохраняет некоторое состояние — набор значений атрибутов
в определенный момент выполнения программы. В нашем примере с программой
42
Глава 1
рисования хозяином является область рисования, поскольку она может
принимать несколько состояний. В начальном своем состоянии область не содержит
каких-либо элементов. Объект хранитель (memento object) хранит копии всех
атрибутов, ассоциированных с состоянием инициатора. Хозяин представляется в
качестве первого элемента в журнале, который действует как объект механизма
отката (Caretaker) —т.е. объект, который содержит ссылки на все объекты
хранителя, ассоциированные с хозяином.
Теперь предположим, что пользователь рисует окружность в области
рисования. Область теперь принимает другое состояние — она содержит объект, центр
которого задается координатами х-у. После этого область рисунка использует
другой хранитель для хранения этой информации. Этот хранитель представляется
как второй элемент в Журнале. Список элементов журнала отображается на
экране, поэтому пользователь может выбрать, какое состояние восстановить.
Предположим, пользователь хочет удалить окружность, — если пользователь выберет
первое состояние из списка, область рисунка будет использовать первый
хранитель для восстановления своего исходного состояния.
Strategy
Пакет java.awt предлагает несколько менеджеров компоновки LayoutManager,
таких как классы FlowLayout, BorderLayout и GridLayout, с помощью которых
разработчики строят графические интерфейсы пользователя. Каждый менеджер
компоновки организует компоненты графического пользовательского интерфейса
в контейнерах (объект Container) — однако каждая реализация интерфейса
Layout Manager использует свой алгоритм для организации этих компонентов.
Компоновка FlowLaycnit размещает компоненты в последовательности слева
направо, BorderLayout помещает компоненты в пяти различных областях, a Grid-
Layout организует компоненты по строкам и столбцам. Интерфейс LayoutManager
играет роль стратегии в паттерне проектирования Strategy.
Паттерн проектирования Strategy дает возможность разработчикам
инкапсулировать набор алгоритмов — он называется стратегией, — каждый из которых
имеет одну и ту же функцию (например, организации компонентов графического
пользовательского интерфейса), но различные реализации. Например, интерфейс
LayoutManager (стратегия) представляет собой набор алгоритмов, которые
организуют компоненты графического пользовательского интерфейса. Каждый
конкретный подкласс LayoutManager (например, FIowLayout, BorderLayout и Grid-
Layout) реализует метод add Lay out Component для предоставления определенного
алгоритма организации компонентов.
1.5.3. Паттерны параллельного выполнения
С того момента, как «группа четырех» издала свою книгу, знакомящую с
применением паттернов проектирования в объектно-ориентированных системах, было
создано множество дополнительных паттернов проектирования. Некоторые из
этих новых паттернов проектирования относились к особым типам
объектно-ориентированных систем, таких как параллельные и распределенные системы. Языки
программирования с ьозможностью организации множества программных
потоков, такие как Java, позволяют разработчикам задавать параллельные действия —
т.е. действия, которые выполняются одновременно. Неправильно спроектирован--
ныв параллельные системы могут привести к проблемам параллельного
выполнения. Например, два объекта, пытающиеся одновременно изменить совместно
используемые данные, могут повредить эти данные. Кроме того, если два объекта
ожидают, пока другой объект завершит выполнение своей задачи, и если ни один
из них не может завершить свою задачу, эти объекты потенциально могут нахо-
Введение
43
дится в состоянии ожидания неопределенно долгое время — такая ситуация
называется тупиковой ситуацией (deadlock). Используя Java, Дуг Ли и Марк Гранд
создали набор паттернов параллельного выполнения для многопотоковых
архитектур, чтобы избежать различных проблем, связанных с многопоточной
организацией выполнения программ. Вот несколько из этих паттернов проектирования:
• Паттерн проектирования Single-Threaded Execution не дает нескольким
программным потокам одновременно вызывать один и тот же метод другого
объекта [3]. В Java разработчики могут использовать для применения этого
паттерна проектирования ключевое слово synchronized.
• Паттерн проектирования Balking обеспечивает, что метод будет
отвергнут — т.е. отменен без выполнения каких-либо действий — если объект
находится в состояний, в котором этот метод не может быть выполнен [4]. В другом
варианте этого паттерна проектирования метод выдает исключение,
описывающее, почему этот метод ие может быть выполнен, — например, метод
выдает исключение при обращении к структуре данных, которая не существует.
• Паттерн проектирования Read/Write Lock позволяет нескольким
программным потокам одновременно получать доступ для чтения к объекту, но
препятствует получению несколькими программными потоками
одновременного доступа к этому объекту для записи. Только один программный поток
в данный момент времени может получить доступ к объекту для записи, —
когда этот поток получает доступ для записи, объект блокируется для всех
других потоков [5].
» Паттерн проектирования Two-Phase Termination обеспечивает
освобождение потоком ресурсов— например, других порожденных потоков — в памяти
(первая фаза) перед завершением (вторая фаза) [6]. Б Java объект Thread
может использовать этот паттерн проектирования в методе run. Например,
метод run может содержать бесконечный цикл, который завершается в случае
определенного изменения состояния, при завершении метод run может
вызвать private метод, ответственный за останов порожденных потоков (первая
фаза). Затем поток завершается после завершения метода run (вторая фаза).
1.5.4. Архитектурные паттерны проектирования
Паттерны проектирования дают возможность разработчикам проектировать
определенные фрагменты систем, например, абстрагироваться от создания
экземпляров объектов, агрегировать классы в более крупные структуры, или назначать
задачи для объектов. Архитектурные паттерны проектирования, со своей
стороны, предоставляют для разработчиков проверенные стратегии для проектирования
подсистем и задают порядок взаимодействия этих подсистем друг с другом [7].
Например, архитектурный паттерн проектирования модель—вид—контроллер
отделяет данные приложения (содержащиеся в модели) от компонентов
графического представления (вида) и логики обработки входных данных (контроллера). В
проекте простого текстового редактора пользователь вводит текст с клавиатуры и
форматирует этот текст с помощью мыши. Программа хранит этот текст и информацию
о форматировании в наборе структур данных, а зятем отображает эту информацию
яа экране, чтобы пользователь мог видеть введенные им данные. Модель, которая
содержит данные приложения, может содержать только символы для разметки
документа. Если пользователь предоставил некоторые входные данные, контроллер
модифицирует данные модели в соответствии с введенными данными. При
изменении модели она уведомляет вид об изменении, чтобы представление могло быть
обновлено с учетом измененных данных, — например, могут отображаться символы
с использованием определенного шрифта и определенного размера.
44
Глава 1
Архитектурный паттерн проектирования Layers разделяет функциональные
возможности на отдельные группы, отвечающие за определенные задачи в
системе. Такие группы называют уровнями. Например, трехуровневое приложение,
в котором каждый уровень состоит из уникального системного компонента,
является примером архитектурного паттерна проектирования Layers. Приложение
подобного типа содержит три компонента, каждый из которых выполняет
уникальную задачу. Информационный уровень (его также называют нижним уровнем)
содержит данные для приложения, обычно храня данные в базе данных. Клиентский
уровень (его также называют верхним уровнем) представляет собой
пользовательский интерфейс приложения, например, стандартный Web-браузер. Средний
уровень действует в качестве посредника между информационным уровнем и
клиентским уровнем, обрабатывая запросы клиентского уровня, читая данные из базы
данных и записывая данные в базу данных.
Использование архитектурных паттернов проектирования способствует
расширяемости при разработке систем, поскольку разработчики могут модифицировать
компонент без необходимости модифицировать другой компонент. Например,
текстовый редактор, который использует архитектурный паттерн проектирования
модель—вид—контроллер, является расширяемым; разработчики могут
модифицировать представление, которое отображает структуру документа, не модифицируя при
этом модель, другие представления или логику. Система, разработанная с
использованием архитектурного паттерна проектирования Layers, также является
расширяемой; разработчики могут модифицировать информационный уровень, чтобы
адаптировать программу к определенной системе управления базами данных, но им
нет необходимости серьезно модифицировать клиентский или средний уровень.
1.5.5. Дополнительные ресурсы по паттернам проектирования
Надеемся, что вы захотите больше узнать о паттернах проектирования. Мы
рекомендуем просмотреть следующие ресурсы и прочесть упомянутые ниже
источники по мере изучения паттернов проектирования в настоящей книге. Мы в
особенности рекомендуем вам прочесть книгу, написанную «группой четырех».
Паттерны проектирования
www.hillside.net/patterns
Эта страница содержит ссылки иа информацию по паттернам проектирования и
языкам программирования.
www.hillside.net/patterns/books/
Ha этом сайте перечислены книгя по паттернам проектирования.
www.netobjectives.com/design.htm
На этом сайте представлен обзор по паттернам проектирования и разъясняется их
назначение.
umbel .Tjmbc.edu/~tacr/dp/dp .html
Этот сайт содержит ссылки па Web-сайты, относящиеся к паттернам проектирования,
а также на учебные материалы и статьи,
vfww.links2go.com/topic/Desigti_Pattecns
Этот сайт содержит ссылки па сайты п информационные ресурсы по паттернам
проектирования.
www.с2.сош/ррс/
Этот сайт посаящен последним разработкам по паттернам проектирования и идеям для
будущих проектов.
Введение
45
Паттерны проектирования и Java
www. research,umbc.edu/~tarr/csJ91/fall007cs491 .html
Этот сайт посвящен курсу по паттернам проектирования для Java, который изучается
в университете штата Мэриленд. Здесь вы найдете массу примеров, а также правила
применения паттернов проектирования в Java.
www.anteгасt.com/~bradapp/javapats.html
На этом сайте обсуждаются паттерны проектирования в Java, а также рассматривается
применение их в распределенных системах,
www.meurrens.org/ip-Links/java/designPatterns/
На этом сайте представлены многочисленные ссылки на ресурсы и информацию по
паттернам проектирования в Java.
Архитектурные паттерны проектирования
compsci . about. coro/science/compsci/lit>rary/weeJcly/aa03060Qa.htin
На этом сайте представлен обзор архитектуры модель—вид—контроллер.
www.javaworld,com/javaworld/jw-04-1998/jw-04-howto.html
Этот сайт содержит статьи, в которых рассматривается, как компоненты Swing
используют архитектуру модель—вид—контроллер.
www,ootips.org/mve-pattern.html
На этом сайте представлена информация и сонеты по применению архитектуры
модель—вид—контроллер (MVC).
www.ftech.co.uk/~honeyg/article3/pda.htm
Этот сайт содержит статьи, посвященные важной роли архитектурных паттернов
проектирования при разработке программного обеспечения.
www. tml .but. f i/Opinnot/Tik-109 . 450/l99fl/niska/sld001. htjn
Этот сайт предоставляет информацию по архитектурным паттернам, паттернам
проектирования и идиомам (паттернам, предназначенным для конкретных языков).
Используемые источники
1. Е. Gamma, et al, Design Patterns; Elements of Reusable Object-Oriented Software
(Boston, MA: Addison-Wesley, 1995). Русский перевод: Э. Гамма, Р. Хелм, Р. Джонсон,
Дж. Влиссидес «Приемы объектно-ориентированного проектирования. Паттерны
проектирования». С.Пб.: Питер, 2001 г., 368 с.
2. J. Vllssides, Pattern Hatching; Design Patterns Applied (Boston, MA: Addison-Wesley,
1998) 146.
3. M. Grand, Patterns in Java; A Catalog of Reusable Design Patterns Illustrated with UML
(New York, NY: John Wiley and Sons, 1998) 399-407.
4. M. Grand, 417-420.
5. M. Grand, 431-439.
6. M. Grand, 449-453.
7. R. Hartman. «Building of Patterns». Application Development Trends May 2001: 19-26.
Литература
Carey, J., B. Carlson and T. Graser. San Francisco'"' Design Patterns: Blueprint for
Building Software. Boston, MA: Addison-Wesley, 2000.
Coad, P., M. Mayficld and Jon Kern. Java Design; Building Better Apps and Applets,
Second Edition. Englewood Cliffs, NY: Yourdon Press, 1999.
Cooper, J. Java Design Patterns; A Tutorial. Boston, MA: Addison-Wesley, 2000.
Lea, D., Concurrent Programming "i Java. Second Edition; Design Principles and
Patterns. Boston, MA: Addison-Wesley, 1999,
6
Глава 1
Gamma, R., R. Helm, R. Johnson and J. Vlissides. Design Patterns; Elements of Reusable
Object Oriented Software. Boston, MA: Addison-Wesley, 1995. Русский перевод: Э.
Гамма, Р. Хелм, Р. Джонсон, Дж. Влиссидес «Приемы объектно-ориентированного
проектирования. Паттерны проектирования». СПб.: Питер, 2001 г., 368 с.
Vlissides, J. «Composite a la Java, Part 1». Java Report. 6:. no. 6 (2001): 69-70, 72.
Vlissides, J. «Pattern Hatching: GoF a la Java*. Java Report Online (March. 2001)
<www.javareport.com/html/from_pages/article. asp ?id=355>
9
Сервлеты
Цели
• Научиться выполнять
сервлеты с использованием
сервера Apache Tomcat.
• Научиться обрабатывать
HTTP-запросы с помощью
класса HttpServlet.
• Научиться хранить
информацию о сеансе
с помощью cookies и объектов
Http Session.
• Научиться осуществлять
доступ к базам данных из
сер влетов.
Справедливое требование надо
исполнять безропотно.
Данте Алигьери
Большая часть путешествия
состоит в прохождении через
ворота.
Марк Теренций Варрон
Если меня выдвинут, я не
смирюсь; если мепя изберут, я не
буду прислуживать.
Генерал Уильям Т. Шерман
Я хочу коржик,'
Коржик (Cookie Monster),
персонаж из Улицы Сезам
Как только время тихих размышлений
настает
Я воскрешаю в памяти своей былое...
Уильям Шекспир
Друзья делят с тобой все.
Пифагор
Если вы не. добились успеха с первого раза,
уничтожьте все доказательства вашей
попытки.
Ньют Хейлшер
48
Глава 2
2.1. Введение
В Internet и Всемирной паутине имеется масса интересного и волнующего.
Internet связывает воедино мир информации. Всемирная паутина облегчает
использование Internet и обогащает возможностями мультимедиа. Организации
отводят Internet и Web важную роль в своих стратегиях построения
информационных систем. Java предоставляет мощные сетевые средства, которые облегчают
разработку приложений на основе Internet и Web. Они не только обеспечивают
параллельное выполнение за счет многопоточности, но также позволяют
программам осуществлять поиск информации и взаимодействовать с программами,
выполняющимися на других компьютерах в других организациях и других странах.
Java также дает возможность апплетам и приложениям, выполняющимся на
одном компьютере, взаимодействовать друг с другом с соблюдением ограничений,
устанавливаемых системой безопасности.
Работа в сети и сетевые взаимодействия — большая и сложная тема. В
программах учебных заведений, готовящих специалистов по информационным
технологиям, на полное изучение этой тематики отводится целый семестр, а на старших
курсах эти вопросы рассматривается еще более глубоко. Java предоставляет
множество сетевых функциональных возможностей и как правило выбирается в качестве
средства реализации при обучении основам создания корпоративного программно-
Сервлеты
49
го обеспечения. В этой книге мы познакомимся с некоторыми принципами и
возможностями сетевого программирования.
Сетевые возможности Java сгруппированы в нескольких пакетах. Основные
сетевые возможности определяются классами и интерфейсами пакета java.net,
посредством которых Java предоставляет возможность осуществлять такие взаимодействия
на основе сокетов, что позволяет приложениям воспринимать такие взаимодействия
кап обмен потоками данных — программа может прочесть из сокета или записать
в сокет данные точно так же, как прочесть из файла или записать в файл. Классы
и интерфейсы пакета java.net также позволяют осуществлять взаимодействия на
основе пакетов, передавая отдельные пакеты информации, — обычно используемые
для передачи через Internet данных аудио и видео. В нашей книге Как
программировать на Java. Издание 4-е показано, как создавать и работать с сокетами, а также как
осуществлять взаимодействие с помощью пакетов данных.
На высоком уровне сетевые функциональные возможности предоставляется
классами и интерфейсами, содержащимися в пакетах Java.rmi (пять пакетов) для
технологии удаленного вызова методов (RMI — Remote Method Invocation), и в
пакетах org.omg (семь пакетов) для технологии Common Object Request Broker
Architecture (CORBA), которые являются составной частью API Java 2. Пакеты
RMI дают возможность объектам Java, выполняющимся на разных виртуальных
машинах Java (обычно и на различных компьютерах) взаимодействовать
посредством удаленных вызовов методов. Такие вызовы методов внешне представляются
адресованными объекту в этой же программе, но фактически имеют встроенные
сетевые средства (на основе пакета java.net), которые передают вызовы методов
другому объекту на другом компьютере. Пакеты CORBA могут быть использованы
для организации взаимодействия между двумя приложениями,
воспринимающими CORBA, в том числе между приложениями, написанными на других языках
программирования. В главе 2 книги «Технологии программирования на Java 2.
Книга 2» рассказывается о возможностях технологии RMI Java. В главах 7-8 этой
же книги обсуждаются основные концепции CORBA и представлен практический
пример, реализующий распределенную систему на CORBA.
При обсуждении возможностей работы в сети в следующих двух главах мы
сосредоточим внимание на обеих сторонах, участвующих во взаимоотношении
клиент-сервер. Клиент запрашивает некоторое действие, а сервер выполняет действие
и посылает ответ клиенту. Модель взаимодействия запрос-ответ является основой
высокоуровневого представления сетевых взаимодействий в Java — сервлетов
и серверных страниц JavaServer Pages (JSP). Сервлет расширяет
функциональные возможности сервера. Пакеты javax.servlet и javax.servlet.http
предоставляют классы и интерфейсы для функционирования сервлетов. Пакеты javax.eerv-
let.jsp и javax.servlet.jsp.tagext предоставляют классы и интерфейсы, которые
расширяют возможности сервлетов для взаимодействия со страницами JavaServer
Pages. С помощью специального синтаксиса технология JSP дает возможность
программистам создавать Web-страницы, которые используют
инкапсулированные функциональные возможности Java, и даже включать скриптлеты,
содержащие реальный код Java, непосредственно в страницы.
Типичной реализацией модели запрос-ответ является взаимодействие между
браузерами и серверами Web. Когда пользователь выбирает Web-сайт для
просмотра с помощью своего браузера (клиентское приложение), на соответствующий
Web-сервер (серверное приложение) посылается запрос. Сервер обычно отвечает
клиенту отправкой соответствующей Web-страницы в формате XHTML. Сервлеты
эффективны при разработке Web-решений, которые помогают предоставлять
безопасный доступ к Web-сайту, взаимодействовать с базами данных в интересах
клиента, динамически генерировать XHTML-документы для отображения в браузерах
и сохранять уникальную информацию о сеансах клиентов.
50
Глава 2
® Общая методическая рекомендация 2.1
Хотя сервлеты обычно используются в распределенных
Web-приложениях, не все сервлеты обязательно предназначены для расширения
функциональных возможностей Web-сервера.
В этой главе мы начинаем обсуждение средств сетевого взаимодействия с
рассмотрения сервлетов, которые расширяют функциональные возможности серверов
World Wide Web и являются наиболее типичной на сегодняшний день формой
сервлетов. В главе 3 рассматриваются JSP-страницы, которые транслируются
в сервлеты. JSP-страницы являются удобным и мощным способом реализации
Web-приложеяий, позволяющим не углубляться в низкоуровневую детализацию
сервлетов. Совместно сервлеты и JSP-страницы образуют в пакете Java 2
Enterprise Edition (J2EE) уровень взаимодействия с Web.
Многие разработчики считают, что сервлеты являются верным выбором для
интенсивно работающих с базами данных приложений, которые взаимодействуют
с так называемыми тонкими клиентами — приложениями, которые требуют
минимальной поддержки на стороне клиента. Сервер отвечает за доступ к базам
данных. Клиенты соединяются с сервером с использованием стандартных протоколов,
доступных на большинстве клиентских платформ. Таким образом, код,
реализующий логику внешнего представления динамического содержимого, может быть
написан один раз и размещен на сервере для доступа к нему клиентов, чтобы дать
возможность программистам создавать эффективно работающие тонкие клиенты.
В этой главе примеры сервлетов демонстрируют применяемый в Web механизм
запрос/ответ (главным образом, с помощью методов get и post), возможности
отслеживания состояния сеанса, переадресацию запросов и возможности
взаимодействия с базами данных через JDBC. (Предполагается, что читатель знаком с
интерфейсом JDBC и базами данных. Технология JDBC рассматривалась в главе 8 книги
«Технологии программирования на Java 2. Книга 1»). В главе 4 мы создадим
Web-приложение для виртуального книжного магазина с использованием XML,
JDBC, технологии сервлетов, рассматриваемой в этой главе, и технологии JSP,
рассматриваемой в следующей главе. В комплексном учебном примере мы
представим дополнительные возможности применения сервлетов.
Корпорация Sun Microsystems через процесс Java Community Process отвечает
за разработку спецификаций для сервлетов и JavaServer Pages. Эталонная
реализация этих стандартов в настоящее время разрабатывается альянсом Apache
Software Foundation (www.apache.org) как часть проекта Jakarta Project
(Jakarta, ара che.org). Как заявлено на основной странице проекта Jakarta Project, «Цель
Jakarta Project — предоставить серверные решения коммерческого качества,
основанные на платформе Java, которые разрабатываются открыто и при взаимном
сотрудничестве». В проекте Jakarta имеется множество подчиненных проектов,
призванных оказать помощь коммерческим разработчикам серверных приложений.
Сервлеты и JSP являются составной частью проекта Jakarta, получившей
название Tomcat. Это официальная ятя лонная реализация стандартов для сервлетов
и JSP. Мы используем Tomcat в этой главе для демонстрации возможностей
сервлетов. Последней на момент написания этой книги реализацией Tomcat была
версия 3.2.31. Последнюю версию Tomcat можно загрузить с Web-сайта Apache Group.
Чтобы иметь возможность выполнять сервлеты в этой главе, нужно установить
Tomcat или другой сервер приложений, поддерживающий сервлеты и JavaServer
Pages. Об установке и настройке Tomcat мы поговорим в разделах 2.3.1 и 2.3.2
после того, как познакомимся с нашим первым примером.
На момент подгатоикн к изданию перевода книги актуальной была версия 4.1.12
Tomcat. — Прим. ред.
Сервлеты
51
В указаниях по тестированию каждого из примеров в этой главе постоянно
подчеркивается, что вам следует скопировать файлы в определенные каталоги
Tomcat. Все файлы примеров для этой главы имеются на Web-сайте компании Deitel
www.dcite\.com, а также на Web-сайте издательства «Бином» www.binom-press.ru/
books/ad v_] ava2.htm.
[Замечание. В конце раздела 2.10 имеется список спецификаций Internet
(упомянутых в спецификации Servlet 2,2 Specification) для технологий, имеющих
отношение к разработке сервлетов. Для каждой спецификации указывается номер
документа RFC (Request for Comments). Мы приводим URL Web-сайта, на котором
можно найти и просмотреть каждую из спецификаций.]
2.2. Обзор технологии сервлетов и их архитектура
В этом разделе представлен обзор технологии сервлетов Java, Мы рассмотрим
классы, методы и исключения, относящиеся к сервлетам. В следующих
нескольких разделах будут представлены примеры «живого кода», в которых мы создадим
многоуровневые системы клиент-сервер с помощью сервлетов и технологии JDBC.
internet использует множество протоколов. Протокол HTTP (Hypertext
Transfer Protocol), который составляет основу Всемирной паутины, использует
унифицированные идентификаторы ресурсов (Uniform Resource Identifiers — URI, их
также иногда называют унифицированными указателями ресурсов — Universal
Resource Locators, или URL) для определения местоположения ресурсов в Internet.
Обычно URI указывают на файлы или каталоги, но также могут быть
использованы для выполнения таких сложных задач, как поиск в базах данных и в Internet,
Дополнительную информацию о форматах URL можно получить на сайте
www.w3.org/Addressing
За дополнительной информацией о протоколе HTTP обратитесь на сайт
www.w3,org/Protocols/HTTP
За общей информацией, связанной с Всемирной паутиной, посетите сайт
www.w3.org
Технология JavaServer Pages является расширением технологии сервлетов.
Технология ЛЙР используется главным образом в том случае, если значительная часть
содержимого, отправляемого клиенту, является статическим текстом и разметкой,
и лишь небольшая его часть генерируется динамически с помощью кода Java,
Сервлеты, как правило, используются в том случае, когда лишь малая часть
содержимого, отправляемого клиенту, представляет собой статический текст или разметку. На
самом деле некоторые сервлеты не выдают никакого содержимого. Вместо этого они
выполняют определенную задачу в интересах клиента, а затем вызывают другие
сервлеты или JSP-страницы для формирования ответа. Заметим, что в большинстве
случаев технологии сервлетов и JSP являются взаимозаменяемыми. Сервер, на
котором выполняется сервлет, часто называют контейнером сервлетов.
Сервлеты и страницы JavaServer Pages стали настолько популярными, что на
сегодняшний день они поддерживаются непосредственно или через подключаемые
модули сторонних поставщиков большинством Web-серверов и серверов
приложений, включая Netscape iPlanet Application Server, Microsoft Internet Information
Server (IIS), Apache HTTP Server, сервер приложений ВЕА WebLogic, сервер
приложений IBM WebSphere, Web-сервер Jigsaw консорциума World Wide Web и
многие другие,
В этой главе сервлеты демонстрируют взаимодействие между клиентом и
сервером по протоколу HTTP. Клиент отправляет HTTP-запрос серверу или контейнеру
сервлетов. Сервер или контейнер сервлетов получает запрос и направляет его для
52
Глава 2
обработки соответствующему сервлету. Сервлет выполняет обработку, которая
может включать в себя взаимодействие с базой данных или с другими серверными
компонентами, такими как другие сервлеты, JSP-страницы или компоненты
Enterprise JavaBeans (глава 7). Сервлет возвращает результаты клиенту, обычно
в виде документа в формате HTML, XHTML или XML, отображаемого браузером,
однако могут быть использованы и другие форматы данных, такие как
изображения и двоичные файлы.
2.2.1. Интерфейс Servlet. и жизненный цикл сервлета
Архитектурно все сервлеты должны реализовыватъ интерфейс Servlet. Как
и многие ключевые методы апплетов, методы интерфейса Servlet вызываются
автоматически (сервером, на котором установлен сервлет). Этот интерфейс
определяет пять методов, описанных в таблице на рис. 2.1.
| Метод I Описание
void init[ ServletConfig config )
Этот метод автоматически вызывается один рат при выполнении сррвгета для
его инициализации. Параметр ServletConfig предоставляется контейнером
сервлетов, который исполняет сервлет.
ServletConfig getServletConfig()
Этот метод возвращает ссылку на объект, который реализует интерфейс
I ServletConfig. Этот объект предоставляет доступ к информации
о конфигурации сервлета, такой как параметры иницизгмгации сервпета
и объект контекста сервлета ServletContext, который дает доступ к сервлету
и его окружению (т.е. контейнеру сеэвлетов, в котором исполняется сервлет).
String getServletlnfoO
Этот метод определяется программистом, создаощим сервлет, для возврата
строки, содержащей информацию о сервлете, такую как данные об авторе
сервлета и номере версии сервлета.
void service( ServletRequest request, ServletResponse response )
Контейнер сервлета вызывает этот метод для отзетз на клиентский запрос
сервлету.
void destroy()
Этот «чистящей» метод вызывается лосле завершения выполнения сервлета
контейнером сервлетов. Данный метод следует применять для освобождения
ресурсоо, используемых сервлетом, таких как скрытые файлы или открытые
соединения с базами данных.
Рис. 2.1. Методы интерфейса Servlet (пакет javax.servlet)
S Общая методическая рекомендация 2.2
Все сервлеты должны реализовыватъ интерфейс Servlet из пакета ja-
vax.servlet.
Жизненный цикл сервлета начинается, когда контейнер сервлетоа загружает
сервлет в память, обычно в ответ на первый запрос, который получает сервлет.
Перед тем, как сервлет получит возможность обработать этот запрос, контейнер
сервлетов вызывает метод init сервлета. После завершения выполнения метода init
сервлет может ответить на свой первый запрос. Все запросы обслуживаются мето-
Сервлеты
53
дом service сервлета, который получает запрос, обрабатывает его и отправляет
ответ клиенту. В течение жизненного цикла сервлета метод service вызывается один
раз для каждого запроса. Каждый новый запрос обычно обрабатывается в новом
программном потоке (создаваемом контейнером сервлетов), в котором
выполняется метод service. Когда контейнер сервлета завершает выполнение сервлета,
вызывается метод destroy сервлета для освобождения ресурсов сервлета.
Совет по повышению эффективности 2.1
Создавать новый программный поток для каждого запроса более
эффективно, чем создавать для этого новый процесс, как это делается в
некоторых других серверных технологиях, таких как CGI. [Замечание.
Подобно сервлетам, технология Fast CGI позволяет избежать затрат,
связанных с созданием нового процесса для каждого запроса.]
Пакеты сервлетов определяют два абстрактных класса, которые реализуют
интерфейс Servlet: класс Generics e r vlet (из пакета javax,servlet) и класс HttpSer-
vlet (из пакета javax.servlet.http). Эти классы предоставляют реализации по
умолчанию для всех методов интерфейса Servlet. Большинство сервлетов расширяют
либо класс GenericServlet, либо класс HttpServlet, и замещают некоторые или все
их методы.
Все примеры, рассматриваемые в этой главе, расширяют класс HttpServlet,
который определяет возможности обработки запросов для сервлетов, обогащающие
функциональные возможности Web-copBopa. Ключевым -методом в каждом сервле-
те является метод service, который принимает в качестве параметров объект
ServletRequest и объект ServletRespon.se. Эти объекты предоставляют доступ
к потокам ввода и вывода, которые дают возможность сервлетам получать данные
г. клиента и отправлять данные клиенту. Эти потоки могут быть либо байтовыми,
либо символьными. Если в процессе выполнения сервлета возникает проблема,
возбуждается либо исключение ServlctExeeption, либо исключение IOException.
Общая методическая рекомендация 2.3
Сервлеты могут реализавывать интерфейс javax.servlet.SingleThread-
Model для указания, что только один программный поток выполнения
одновременно может обращаться к методу service в определенном
экземпляре сервлета. Если сервлет реализует интерфейс SingleThreadModel,
контейнер сервлетов может создавать множество экземпляров сервлета для
обработки множества запросов к сервлету параллельно. В этом случае
может потребоваться предоставлять синхронизированный доступ к
коллективным ресурсам, используемым в методе service.
2.2.2. Класс HttpServlet
Сервлеты, работающие с клиентами через Web, обычно расширяют класс
HttpServlet. Метод service, как правило, переопределяется, чтобы иметь возможность
различать стандартные методы запросов, получаемые от Web-браузера клиента.
Двумя наиболее распространенными типами запросов HTTP (их также называют
методами запросов) являются get и post. Запрос get получает (или извлекает)
информацию. Типичное применение запросов get — получение HTML-документа или
изображения. Запрос post помещает (или отправляет) данные на сервер.
Типичное применение запросов post — отправка на сервер информации, например, для
аутентификации, или данных из формы, в которую пользователь ввел информацию.
54
Глава 2
В классе HttpServlet определены методы doGet и doPost для реакции на
запросы типа get и post клиента. Эти методы вызываются методом service класса
HttpServlet, который, в свою очередь, вызывается при поступлении запроса на
сервер. Метод service сначала определяет тип запроса, а затем вызывает
соответствующий метод. Имеются и другие, менее употребительные типы запросов, но
в этой книге мы не будем их касаться. Методы класса HttpServlet, которые
реагируют на другие типы запросов, представлены в таблице на рис. 2.2. Все они
принимают параметры типа HttpServletRequest и HttpServIetRespoase и ничего не
возвращают. Методы, представленные на рис. 2.2, используются довольно редко. Для
получения дополнительной информации относительно протокола HTTP, посетите сайт
www,w3.org/Protocols
Ш Общая методическая рекомендация 2.4
Не переопределяйте метод service в подклассе класса HttpServlet. Если
это сделать, сервлет не будет способен различать типы запросов.
Методы do Get и doPost принимают в качестве параметров объекты
HttpServletRequest и HttpServletResponse, которые дают возможность осуществлять
взаимодействие между клиентом и сервером. Методы интерфейса HttpServletRequest
облегчают доступ к данным запроса. Методы интерфейса HttpServletResponse
облегчают сервлету возврат результатов Web-клиенту в виде HTML. Интерфейсы
HttpServletRequest и HttpServletRespon.se будут рассмотрены в следующих двух
разделах.
Метод
doDelete
doOptions
doPut
doTrace
Описание
Вызывается в ответ на HTTP-запрос delete. Такой запрос обычно используется
для удзления файла с сервера. Это действие для некоторых серверов может
быть запрещено из-за угроз внутренней безопасности (поскольку клиент
может удалить файл, который является важным для сервера или приложения).
Вызывается в ответ на HTTP-запрос options Этот метод возвращает клиенту
информацию, указывающую на опции HTTP, поддерживаемые сервером,
нагример, версию HTTP (1.0 или 1.1) и методы запросов, поддерживаемые
сервером.
Вызывается в ответ на HTTP-запрос put. Такой запрос обычно используется для
сохранения файла на сервере. Это действие для некоторых серверов ыожег
быть запрещено из-за угроз для внутренней безопасности (тзк, клиент может
поместить исполняемое поиложение на сервер, которое, в случае выполнения,
может нанести ущерб серверу - например, путем удаления взжных файлов
или захвата ресурсов).
Вызывается в ответ на HTTP-запрос trace. Такой запрос обычно используется
при отлздке. Реализация этого метода автоматически возвращает клиенту |
HTML-документ, содержащий информацию о заголовке запроса (данные, {
посылаемые браузером как часть запросз). |
Рис, 2.2. Другие методы клзсса HttpServlet
2.2.3. Интерфейс HttpServletRequest
При каждом вызове методы doGet и doPost класса HttpServlet принимают
в качестве параметра объект, который реализует интерфейс HttpServletRequest.
Web-серъер, который исполняет сервлет, создает объект HttpServletRequest и
передает его методу service сервлета (который, в свою очередь, передает его методу
Сервлеты
55
doGet или doPost). Этот объект содержит запрос, поступивший от клиента.
Имеется множество методов, дающих возможность сери лету обрабатывать клиентский
запрос. Некоторые из этих методов принадлежат интерфейсу ServletReqaest —
интерфейсу, который расширяется интерфейсом HttpServletRequest. Некоторые
ключевые методы, используемые в этой главе, представлены в таблице на рис. 2.3.
Полный список методов интерфейса HttpServletRequest вы можете найти по адресу
Java.sun.com/j2ee/32sdkee/techdocs/api/javax/servlet/http/
HttpServletRequest.html
Вы также можете загрузить и установить сервер Tomcat (см. раздел 2.3.1) и
просмотреть соответствующую документацию на него на вашем локальном компьютере.
Метод
Описание
String getParameter( string паше )
Возвращает значение, ассоциированное с параметром, отправленным
сервлету как част-, запроса get или post Параметр name представляет гобой
имя параметра.
Emuneration getParameterNames()
Возвращает имена всех параметров, отгравленных сервлету как часть запроса
post.
String[] getParameterValues( String name )
Для параметра с несколькими качениями зю меюд возвращает ci роковый
массив, содержащий значения указанного параметра.
Cookie[] getCookies()
Возвращает массив объектов Cookie, сохраненных на клиенте сервером.
Cookie могут быть использованы для уникальной идентификации клиентов
сервером.
HttpSession getSess±on( boolean create )
Возвращает объект HttpSession, ассоциированный с текущим сеансом
клиента. С помощью этого метода может быть создан объект HttpSession
(в этом случае значением параметра является true), если объект HttpSession
для клиента еще не существует. Объекты HttpSession могут использоваться
энало'ично Cookie для уникальной идентификации клиентов.
Рис. 2.3. Некоторые методы интерфейса HttpServletRequest
2.2.4. Интерфейс HttpServletResponse
При каждом обращении методы doGet или do Post объекта HttpServlet
принимают объект, который реализует интерфейс HttpServletResponse. Web-сервер,
который исполняет сервлет, создает объект HttpServletResponse и передает его
методу service сервлета (который, в свою очередь, передает его методу doGet или
doPost). Этот объект содержит ответ клиенту. Имеется множество методов,
дающих возможность сервлету сформировать ответ клиенту. Некоторые из этих
методов принадлежат интерфейсу ServletResponse — интерфейсу, который
расширяется интерфейсом HttpScrvletRespon.se, ЕГесколько ключевых методов,
используемых, в этой главе, представлены в таблице на рис. 2.4. Полный список методов
интерфейса HttpServletResponse вы можете найти по адресу
Java.sun,com/j2ee/j2sdkee/techdocs/api/javax/servlet/http/
HttpServletResponse.html
56
Глава 2
Вы также можете загрузить и установить Tomcat (см. раздел 2.3.1) и
просмотреть соответствующую документацию на вашем локальном компьютере.
Метод
Описание
void addCookie{ Cookie cookie )
Используется для добавления Cookie в заголовок ответа клиенту Установленный
максимальный возраст Cookie, а также то, разрешено ли клиентом хранение
Cookie, определяют, будут ли Cookies сохранены на клиенте.
ServletOutputSfcream getOutputStream ()
Получает бинарный поток вывода, позволяющий отправлять бинарные
данные клиенту.
Fr in tWr i tor getWriterH
Получает символьный поток вывода, позволяющий отправлять текстовые
данные клиенту.
void setContentType{ String type )
Задает MIME-тип ответа браузеру, MIME-тип помогает браузеру определить,
как отображать данные (или, возможно, какое другое приложение глрдует
запустить для обработки данных). Например, MIME-тип "text/html"
указывает, что ответ является HTML-документом, поэтому браузер отображает
HTML-страницу.
Рис. 2,4. Некоторые методь интерфейса HttpServletResponse
2.3. Обработка HTTP-запросов get
Основное назначение HTTP-запроса get — извлекать содержимое, хранящееся по
указанному URL — обычно содержимым является HTML-документ или XHTML-до-
кумент (т.е. Web-страница). Сервлет, представленный на рис. 2.5, и XHTML-доку-
мент на рис. 2.6 демонстрируют обработку HTTP-запроса get. Когда пользователь
щелкает на кнопке Get HTML Document (рис. 2.6), запрос get посылается сервлету
WelcomeServlet (рис. 2.5). Сервлет отвечает на запрос, динамически генерируя
для клиента XHTML-доку мент, который отображает текст "Welcome to Servlets!"
Па рис. 2.5 представлен исходный код сервлета WelcomeServlet. На рис. 2.6
показан XHTML-документ, который клиент загружает для доступа к сервлету. Здесь
же показана копия экрана клиентского браузера до и после взаимодействия с серв-
летом, [Замечание. В разделе 2.3.1 рассказывается, как установить и настроить
Tomcat для выполнения этого примера.]
В строках 5 и 6 импортируются пакеты javax.servlet и javax.http. В примере
мы используем несколько типов данных из этих пакетов.
Пакет javax.servlet.http предоставляет суперкласс HttpServlet для сервлетов,
которые обрабатывают HTTP-запросы get и HTTP-запросы post. Этот класс
реализует интерфейс javax.servlet.Scrvlet it добавляет методы, которые поддерживают
запросы по протоколу HTTP. Класс WelcomeServlet в связи с этим расширяет
класс HttpServlet (строка 9).
Суперкласс HttpServlet предоставляет метод doGet для реакции на запросы get.
В соответствии с установками по умолчанию, метод указывает на ошибку «Method
not allowed» («Недопустимый метод»). Обычно эта ошибка отображается в Internet
Explorer сообщением «This page cannot be displayed» («Страница не может быть
отображена»), а в Netscape Navigator — сообщением «Error: 405» («Ошибка: 405»).
R строках 12-44 замещается метод doGet, чтобы обеспечить собственную обработку
запроса get. Метод doGet принимает два параметра: объект HttpServletRequest
Сервлеты
57
и объект HttpServletResponse (оба из пакета javax.servlet.http). Объект HttpSer-
vletKequest представляет клиентский запрос, а объект HttpServletKesponse —
ответ сервера клиенту. Если метод doGet не способен обработать клиентский запрос,
он возбуждает исключение javax.servlet.ServletException. Если метод doGet
сталкивается с ошибкой в процессе обработки потока (чтение с клиента или запись на
клиент), он возбуждает исключение java.io.IOException.
1 // Рис. 2.5. WelcomeServlet.Java
2 // Простой сервлет для обработки запросов get.
3 package com.dieitel .advjhtpl. servlets ;
4
5 import javax.servlet.*;
6 import javax.servlet.http.*;
7 import 3ava.io.*;
a
9 public class WelcomeServlet extends HttpServlet {
10
11 // обработка клиентских запросов get
12 protected void doGet( HttpServletHequest request,
13 HttpServletResponse response )
14 throws ServletException, IOException
15 {
16 response,setContentType( "text/html" );
17 PrilitWriter out = response.getWriter();
18
19 // отправка XHTML-страницы клиенту
20
21 // начало XHTML-документа
22 out.println( "<?xml version = \"1.0\"?>" );
23
24 out.printlnf "<!DOCTYPE html PUBLIC V-//W3C//DTD " +
25 "XHTML 1.0 Strict//EN\" V'http://www.w3.org" +
26 "/TR/xhtmll/DTD/xhtjnll-strict.dtd\,,>" );
27
28 out.println(
29 "<html xmlns = \"http://www.w3,org/1999/xhtml\">" );
30
31 // заголовок документа
32 out.println< "<head>" );
33 out.println( "<title>A Simple Servlet Example</title>" );
34 out.println( "</head>" );
35
36 // тело документа
37 out.printlnC "<body>" );
38 out.println( "<hl>Welcome to Servlets!</hl>" );
39 owt.pri»tln{ "</body>" );
40
41 // колец XHTML-документа
42 out.println( "</ht*nl>" );
43 out.closet); // закрытие потока и конец страницы
44 }
45 }
Рис. 2.5. Сервлет WelcomeServlet, который обрабатывает простой HTTP-запрос get
58
Глава 2
Реагируя на запрос get, наш сервлет создает XHTML-док у мент, содержащий текст
приветствия "Welcome to Servlcts!" Ответом клиенту является текст XHTML-доку-
мента. Ответ посылается клиенту через объект PrintWriter, полученный из объекта
HttpServletResponse.
В строке 16 используется метод SetContentType объекта response для задания
типа содержимого данных, которые будут посылаться в качестве ответа клиенту.
Это дает возможность клиентскому браузеру правильно обрабатывать полученное
содержимое. Тип содержимого также известен как М/М£-тип [Multipurpose
Internet Mail Extension) данных. В этом примере тип содержимого text /html указывает
браузеру, что ответом является XHTML-документ. Браузер знает, что он должен
прочесть теги XHTML, содержащиеся в документе, отформатировать документ
в соответствие с тегами и отобразить документ в окне браузера. Для получения
более подробной информации о MIME-типах, посетите сайт www.irvine.com/-mime.
В строке 17 используется метод get Write объекта response для получения
ссылки на объект PrintWriter, который дает возможность сервлету отправлять
содержимое клиенту. [Замечание. Если ответом являются двоичные данные, например,
изображение, для получения ссылки на объект ServletOutputStream
используется метод getOutputStream.]
В строках 22-42 создается XHTML-документ путем записи строк с помощью
метода prititln объекта out. Этот метод выводит символ новой строки после вывода
строки, представленной параметром типа String. При отображении Web-страницы
браузером символ новой строки не используется, но он присутствует в исходном
коде XHTML, который вы можете увидеть, выбрав Source из меню View в Internet
Explorer или Page Source из меню View в Netscape Navigator. В строке 43
осуществляется закрытие потока вывода, очищается буфер вывода, и отправляется
информация клиенту. Тем самым завершается ответ клиенту.
XHTML-документ, представленный на рис. 2.6, содержит форму form, которая
вызывает сервлет, код которого приведен в листинге на рис, 2.5. Атрибут action
(/advjhtpl/welcome) элемента form содержит URL, указывающий на сервлет, а
атрибут method элемента form указывает, что браузер отправляет серверу запрос
get, что приводит к вызову метода doGct сервлета. Об URL, задаваемом в качестве
значения атрибута action в этом примере, мы подробнее поговорим в разделе 2.3.2
после того, как установим и настроим сервер Apache Tomcat для выполнения
сервлета, представленного в листинге на рис. 2.5,
Обратите внимание, что на копиях экрана в адресной строке браузера
присутствует имя localhost — стандартное имя Web-сервера на локальном компьютере. Мы
часто используем имя localhost при демонстрации работы сетевых программ на
локальном компьютере, чтобы читатели, не имеющие подключения к сети, могли, тем
не менее, ознакомиться с принципами сетевого программирования. В этом примере
localhost указывает, что сервер, на котором установлен сервлет, выполняется на
локальной машине. За именем хоста следует :8080, что соответствует номеру порта
TCP, на котором сервер Tomcat ожидает запросы клиента. В Web-браузерах по
умолчанию используется порт 80 TCP, но сервер Tomcat ожидает клиентские
запросы на порту 8080 TCP. Это позволяет выполнять Tomcat на том лее компьютере, что
и стандартный Web-сервер. Если мы явно не зададим номер порта в URL, сервлет не
получит наши запросы, а браузер отобразит сообщение об ошибке.
1 <?xml version = "1.0"?>
2 <!D0CTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
3 "http://www.w3.org/TR/xhtBill/DTD/xhtrftll-strict.dtd">
4
5<!-- Рис. 2.Б. WelcomeServlet.html -->
6
7 <html xmlns = "http://www.w3.org/1999/xhtml">
Сервлеты
59
8 <head>
9 <titl£>Handling an HTTP Get Request</title>
10 </head>
11
12 <body>
13 <forro action = "/advjhtpl/welcomel" method = "get">
14
15 <pXlabel>Cliclc the button to invoke the servlet
16 <input type = "submit" value = "Get HTML Document" />
17 </labelX/p>
IS
19 </form>
20 </body>
21 </html>
mmm
штшшашыт
Revert» Tools" МНр'^^>
шё.
<ЙЕ*Г..-1в»Ы
ИШ i'T*W
Seardi
|^^Jgjhttp:;)lIicahii!t'B080f«Ktt»lfM'Vfcfe<W'fc™'Se'vfe'-№i|1 z\:&&
Click Iha button to invoke the soviet
SA-'Gei. HTML Doom anl
5
ШШШРЗШ"'
~3
ш/тщдт*штшттлпттшт^^г-^и
„1
fiUf,4J-<]
Slop Hsfre* Нам -• -Sestft ..'J
■ ftdAnii jg] Ь1ф:№сд»»Я!аз801«>»Нр1/«№к<гёГ»
1Г*М
Welcome to SeiMets!
HP™
Рис. 2.6. HTML-документ, в котором атрибут action элемента form используется для
вызова сервлетэ WelcomeServlet через псевдоним welcomel, заданный в документе
web.xml
Общая методическая рекомендация 2.5
В документации на Tomcat указывается, как интегрировать Tomcat с
популярными Web-серверами, такими как Apache HTTP Server и Microsoft IIS.
Порты в данном случае не являются физическими аппаратными портами, к
которым подключаются кабели; они являются логическими адресами с
целочисленными значениями, которые дают возможность клиентам запрашивать различные
сервисы на одном сервере. Номер порта задает логическое местоположение, где
сервер ожидает и принимает соединения от клиентов. Когда клиент соединяется с
сервером для запроса сервиса, клиент должен указать номер порта для этого сервиса;
в противном случае клиентский запрос не будет обработан. Номера портов
представляют собой положительные целые числа со значениями до 65535. Нумерация
портов осуществляется отдельно для протоколов TCP и TJDP. Многие операционные
системы резервируют номера портов до 1024 для системных сервисов (таких как
60 Глава 2
электронная почта и Всемирная паутина). Эти порты не следует использовать в
своих серверных программах. Некоторые операционные системы требуют
специальных привилегий для использования номеров портов с номерами, меньшими 1024.
Имея такое множество портов для выбора, как клиент может узнать, какой
именно порт использовать при запросе сервиса? Термин общеизвестный номер
порта (well-known port number) часто используется при описании популярных
сервисов в Internet, таких как Web-серверы и серверы электронной почты.
Например, Web-сервер по умолчанию ожидает запросы клиентов на порту 80. Все
Web-браузеры знают этот номер как общеизвестный порт Web-серверов, через
который поступают запросы на HTML-документы. Поэтому, когда вы вводите URL
в Web-браузере, браузер обычно соединяется с портом 80 сервера. Аналогично,
сервер Tomcat использует порт 8080 в качестве своего номера порта. Таким образом,
в запросах к Tomcat на Web-страницы или вызовах сервлетов и страниц JavaServer
Pages должно быть указано, что сервер Tomcat ожидает запрос на порту 8080.
Клиент может осуществить доступ к сервлету только в том случае, если сервлет
установлен на сервере, который реагирует на запросы к сервлетам. В некоторых
случаях поддержка сервлетов непосредственно встроена в Web-сервер, и для
обработки запросов к сервлетам специальной настройки не требуется. В других
случаях необходимо интегрировать контейнер сервлетов с Web-сервером (как это
делается для сервера Tomcat или Web-серверов Apache и IIS). Для Web-серверов, которые
поддерживают сервлеты, обычно предусмотрена процедура установки средств
работы с сервлетами. Если вы намерены выполнять ваш сервлет под управлением
Web-сервера, пожалуйста, обратитесь к документации на ваш Web-сервер за
информацией, как установить сервлет. В наших примерах мы демонстрируем работу
с сервлетами с помощью сервера Apache Tomcat. В разделе 2.3.1 описывается
процесс установки и настройки Tomcat для использования его в этой главе. В разделе
2.3.2 описывается процесс развертывания сервлета, представленного на рис. 2.5.
2.3.1. Установка сервера Apache Tomcat
Tomcat является полнофункциональной'реализацией стандартов JSP и
сервлетов. В его состав входит Web-сервер, что позволяет использовать Tomcat как
автономный контейнер для тестирования JSP-страниц и сервлетов. Tomcat также
может применяться в качестве обработчика запросов JSP-страниц и сервлетов,
принимаемых популярными Web-серверами, такими как Apache, разработанный
Apache Software Foundation, или Microsoft Internet Information Server (IIS).
Tomcat включен в эталонную реализацию Java 2 Enterprise Edition корпорации
Sun Microsystems.
Последняя версия Tomcat (версия 3.2.3) может быть загружена с сайта
jaJtarta.apache.org/builds/iakarta-tomeat/release/v3.2 .З/Ып/
где содержится ряд архивных файлов. Полная реализация Tomcat содержит
файлы, начинающиеся с имени jakarta-tomcat-3.2.3. Предоставляются файлы zip, tar
и сжатые файлы tar для Windows, Linux и Solaris.
Извлеките содержимое архивных файлов в каталог на вашем жестком диске.
По умолчанию именем каталога, содержащего файлы Tomcat, является jakarta-
tomcat-3.2.3. Чтобы Tomcat работал корректно, нужно определить переменные
окружения JAVA_HOME и ТОМСАТ^НОМЕ. JAVA_HOME должна указывать на
каталог, содержащий Java (у нас это d:\jdkl.3.1), а ТОМСАТ_НОМЕ должна
указывать на каталог, который содержит Tomcat (у нас это d:\jakarta-tomcat-3.2.3).
Сервлеты
61
Совет по тестированию и отладке 2.1
На некоторых платформах вам может потребоваться перезагрузить ваш
компьютер, чтобы новые переменные окружения вступили в действие.
Задав переменные окружения, можно запустить сервер Tomcat. Откройте окно
команд и перейдите к подкаталогу bin в jakarta-tomcat-3.2.3. В этом каталоге
находятся файлы totncat.bat и tomcat, »h для запуска сервера Tomcat,
соответственно, под Windows и под UNIX (Linux или Solaris). Чтобы запустить сервер, введите
tomcat start
Эта команда запускает сервер Tomcat. Сервер Tomcat выполняется на ТСР-порту
8080, чтобы не допустить конфликтов со стандартными Web-серверами, которые
обычно выполняются на ТСР-порту 80. Для проверки функционирования Tomcat
откройте ваш Web-браузер и введите в адресную строку
http://localhost:8080/
Отобразится основная страница документации на Tomcat (рис. 2.7), localhost
указывает Web-браузеру, что он должен запросить основную страницу с сервера
Tomcat на локальном компьютере.
Если основная страница документации на Tomcat не отображается, попробуйте
задать URL
http://127.0.0.1:8080/
localhost соответствует IP-адресу 127.0.0.1.
Совет по тестированию и отладке 2.2
Если имя localhost на вашем компьютере не работает, попробуйте
заменить его на IP-адрес 127.0.0.1.
Чтобы завершить работу сервера Tomcat, выполните команду
tomcat stop
из командной строки (или командной оболочки).
:-i»~~i""Y".0
|j**tt»-[a
httjKftkxfltafl'HWrhfe* tin*
*#(glj
Ш*,
Tomcat
Tfrni a tfie sJrUuit Tvciildl bijnir page Ttur fAjfl эtrues W £ qu:cJt
rrftmct fniLile to rtkwd «<^iKCts lud Ы l/icaled at
м- </path/to/tomcat ^/wefcpages/index, htnl
Tn&hidcd wi&aB tin* ntlt ue tr* fo-jch^nsl «rampJej with uiociited s oute 11 odi, API
docuniEncaaop far stMtU mi J?Pr a README, a (eebnical FAQ on this rde« ? яа.4
an аа^оПшый of jar S1j5£ whyih art pf e-г; quuitea £>T tmHmucd development tffwtb
I«hnol*£es induing JSP and Smriets,
EsaatpL*!'
тщ-
Рис. 2.7. Основная страница документации на Tomcat (публикуется с разрешения
Apache Software Foundation)
62 Глава 2
2.3.2. Развертывание Web-приложения
JSP-страницы, сервлеты я их вспомогательные файлы развертываются как
часть Web-приложения. Обычно Web-приложения развертываются в подкаталоге
webapps каталога jakarta-tomcat-3.2.3. Web-приложение имеет известную
структуру каталогов, в которых размещаются все файлы, являющиеся составной частью
приложения. Эта структура каталогов может быть создана администратором
сервера в каталоге webapps, либо вся структура каталогов может быть
заархивирована б архивном файле Web-приложения. Такой архив известен как WAR-файл
и имеет расширение .war. Если WAR-файл помещен в каталог 'webapps, то в
начале выполнения сервер Tomcat извлекает содержимое WAR-файла в
соответствующую структуру подкаталогов webapps. Для простоты при изучении сервлетов
и JavaServer Pages мы создаем структуру каталогов для всех примеров в этой главе
и в главе 3.
Структура каталогов Web-приложения состоит из корня контекста —
каталога верхнего уровня для всего Web-приложения — и нескольких подкаталогов. Эти
подкаталоги описаны в таблице на рис. 2.8.
Типичная ошибка программирования 2.1
При использовании в качестве кор}ш контекста каталога «servlet» или
«servlets», на некоторых серверах сервлет не будет работать корректно.
Для настройки корня контекста для Web-приложения в Tomcat достаточно
просто создать подкаталог в каталоге webapps. Когда Tomcat начинает выполняться,
он создает корень контекста для каждого подкаталога в webapps, используя имя
подкаталога в качестве имени корня контекста. Чтобы протестировать примеры из
этой главы и из главы 3, создайте подкаталог advjhtpl в каталоге webapps Tomcat.
Каталог
Корень контекста
WEB 3NF
web_INF/classes
WEB_INF/lib
Описание
Это корневой каталог для Web-приложения. Имя этого каталога
выбиоается разработчиком Web-приложения. Все JSP-страницы.
HTML-доку менты, сервлеты и вспомогательные файлы, такие как
изображения и файлы классов, размещаются в этом каталоге или в его
подкаталогах. Имя этого каталога задается создателем
Web-приложения, Чтобы задать структуру в Web-гриложении,
подкаталоги могут быть помещены в корень контекста. Например, если
приложение использует много изображений, можно поместить в этот
каталог подкзтзгог изображений. Примеры из этой главы и из главы 3
используют в качестве корня контекста advjhtpl
Этот каталог содержит дескриптор развертывания (web.xml)
Web-приложения.
Этот <аталог содержит файлы классов сервлета и другие файлы классов
поддержки, используемые а Web-приложении. Если классы язляются
частью пакета, здесь должна присутствовать полная структура каталогов
пакета.
Этот каталог содержит файлы архивов J3va (JAR). JAR-файлы могут
содержать файлы классов сервлета и другие файлы классов
поддержки, используемые в Web-приложении.
Рис. 2.8. Стандартные катзлоги Web-приложения
После настройки корня контекста, мы должны сконфигурировать наше Web-
приложение для обработки запросов. Это конфигурирование осуществляется в
дескрипторе развертывания, который хранится в файле с именем web.xml. Деск-
Сервлеты
63
риптор развертывания задает различные параметры конфигурации, такие как
имя, используемое для вызова сервлета (т.е. псевдоним), описание сервлета,
полное имя класса сервлета и карта сервлета (т.е. путь или пути, которые
используются при вызове сервлета контейнером сервлетов). Для этого примера вы должны
создать файл web.xml самостоятельно. Многие средства программирования на
Java при развертывании Web-приложения сами создают файл web.xml. Файл
web.xml для первого примера в этой главе представлен на рис. 2.9. На протяжении
этой главы мы усовершенствуем этот файл по мере добавления других сервлетов
в Web-приложение.
1 <!DOCTYPE web-app PUBLIC
2 "-//Sun Hicrosysterns, Inc.//DTD Web Application 2.2//EH"
3 "http://javs.sun,com/;)2ee/dtds/web-app_2_2-dtd">
4
5 <web-app>
6
7 <!-- общее описание вашего Web-приложения -->
8 <display-name>
9 Advanced Java How to Program JSP
10 And Servlet Chapter Examples
11 </display-name>
12
13 <description>
14 This is the Web application in which we
15 demonstrate our JSP and Servlet examples.
16 </description>
17
18 <!— определение сервлета -->
19 <servlet>
20 <servlet-name>weleomel</servlet-name>
21
22 <deseription>
23 A simple servlet that handles an HTTP get request.
24 </description>
25
26 <servlet-class>
27 com.deitel.advjhtpl.servlets
28 </servlet-class>
29 <servlet>
30
31 <!-- задание карты сервлета -->
32 <servlet-mapping>
33 <servlet-name>welcomel</servlet-name>
34 <url-pattern>welcomel</url-pattern>
35 </servlet-mapping>
36
37 </web-app>
Рис. 2.9. Дескриптор развертывания (web-xml) для Web-приложения advptpl
В строках 1-3 задается тип документа для дескриптора развертывания и
местонахождение DTD для этого XML-файла. Элемент web-app (строки 5-37)
определяет конфигурацию каждого сервлета в Web-приложении и путь к сервлету. Элемент
display-name (строки 8-11) задает имя, которое может быть отображено
администратору сервера, на котором установлено Web-приложение, Элемент description
64
Глава 2
(строки 13-16) задает описание Web-приложения, которое может быть отображено
администратору сервера.
Элемент servlet (строки 19—29) описывает сервлет. Элемент servletname
(строка 20) представляет собой имя, которое мы выбрали для этого конкретного сервле-
та. Опять же, это имя может быть отображено администратору Web-сервера.
Элемент servlet-class (строки 26-28) задает полное имя класса сервлета. Так, сервлет
welcomel определяется классом com.deitel.advjhtpl.servlets.WelcomeServIet.
Элемент servlet-mapping (строки 32—35) задает элементы servlet-name и url-
pattern. Шаблон URL помогает серверу определять, какие запросы посылаются
сервлету (welcomel). Наше Web-приложение будет установлено как часть корня
контекста advjhtpl, рассмотренного в разделе 2,3.2. Так URL, который мы
предоставили браузеру для вызова сервлета в нашем примере, имеет вид
/advjtitpl /welcomel
где /advjhtpl задает корень контекста, который помогает серверу определить,
какое Web-приложение обрабатывает запрос, a /welcomel задает шаблон URL,
который соответствует сервлету welcomel, обрабатывающего запрос. Обратите
внимание, что сервер, на котором размещен сервлет, здесь не указывается, хотя можно
это сделать, например, следующим образом:
http://localhost:8080/advjhtpl/weleomel
Если сервер и номер порта в URL явно не указаны, браузер полагает, что
обработчик формы (т.е. сервлет, указанный в свойстве action элемента form) размещен на
этом же сервере и том же номере порта, откуда браузер загрузил Web-страницу,
содержащую форму form.
Может быть использовано несколько форматов шаблонов URL. Формат
/welcomel URL требует точного соответствия шаблону. Можно также задать
соответствия по пути, соответствия по расширению и сервлет по умолчанию для
Web-приложения. Формат с соответствием по пути начинается с /, а
заканчивается /*. Например, шаблон URL
/advjhtpl/exainple/*
указывает, что данные будут отправлены сервлету, имеющему URL, который
начинается с /advjhtpl/example/. В формате с соответствием по расширению в
начале указывается *,, а в конце — расширение файла. Например, шаблон URL
*.jsp
указывает, что любой запрос на файл с расширением .jsp будет отправлен
сервлету, который обрабатывает JSP-запросы. Фактически, серверы с контейнерами JSP
имеют неявно установленное соответствие по расширениям .jsp с сервлетом,
который обрабатывает JSP-запросы. Шаблон URL / представляет сервер по умолчанию
для Web-приложения. Здесь есть аналогия с документом по умолчанию для
Web-сервера. Например, если вы введете URL wwwr.deitel.com в вашем
Web-браузере, то документом, который вы получите с Web-сервера, по умолчанию будет
документ irtdex.html. Если шаблон URL соответствует сервлету по умолчанию для
Web-при л ожени я, этот сервлет вызывается, чтобы возвратить клиенту ответ по
умолчанию. Это может быть полезным при персонализации Web-содержимого для
определенных пользователей. О персонализации мы поговорим в разделе 2.7,
«Отслеживание состояния сеанса».
Наконец, мы готовы поместить наши файлы в соответствующие каталоги,
чтобы закончить развертывание нашего первого сервлета и приступить к его
тестированию. Имеются три файла, которые мы должны поместить в соответствующие
каталоги: WeleomeServlet.html, WelcomeServIet.class и web.xml. В подкаталоге
webapps каталога jakarta-tomcat-3.2.3 создайте подкаталог advjhtpl, который
представляет корень контекста для нашего Web-приложения. В этом каталоге соз-
Сервлеты
65
дайте подкаталоги с именами servlet и WEB_INF. Мы помещаем наши HTML-
файлы для сервлетов из этой главы в каталог servlets. Скопируйте файл Welcome-
ServJet.html в каталог servlets. В каталоге WEB_INF создайте подкаталог classes,
затем скопируйте файл web.xml в каталог WEB_INF и скопируйте файл Welcome-
Servlet.class, включая все каталоги его пакета, в каталог classes. Структура
файлов и каталогов в каталоге webapps должна иметь вид, представленный на
рис. 2.10 (имена файлов выделены курсивом).
Структура каталогов и файлов Web-приложен и я WekomeServlet
advjhtpl
servlets
HelcomeServlet. htntl
WEB-INF
veb.jcni
classes
com
deitel
advjhtpl
servlets
WelcameServlet.class
Рис. 2.10. Структура каталогов и файлов Web-приложения WekomeServlet
Совет по тестированию и отладке 2.3
Перезапустите сервер Tomcat после внесения изменений в файл
дескриптора развертывания web.xml. В противном случае Tomcat не сможет
распознать новое Web-приложение.
Поместив файлы в соответствующие каталоги, запустите сервер Tomcat,
откройте браузер и введите следующий URL
http://localhost:8080/advjhtpl/servlets/WelcomeServlet.htjnl
чтобы загрузить документ WelcomeServlet.btml в Web-браузер. Затем щелкните
на кнопке Get HTML Document, чтобы вызвать сервлет. Вы должны увидеть
результат, показанный на рис. 2,6. Можно попробовать испытать работу этого серв-
лета в нескольких различных Web-браузерах, чтобы убедиться в идентичности
выдаваемых им результатов.
Типичная ошибка программирования 2.2
Если не поместись сервлет или другие файлы классов в
соответствующую структуру каталогов пакета, сервер не сможет обнаружить эти
классы. Это, в свою очередь, приведет к выдаче Web-браузером клиента
сообщения об ошибке. Такое сообщение в Netscape Navigator обычно имеет
вид «Not Found (404)» («Не найдено»), а в Microsoft Internet Explorer —
«Thepage cannot be found» («Страница не найдена»), и содержит
необходимые пояснения.
На самом деле HTML-файл, представленный на рис. 2.6, не является
обязательным для вызова этого сервлета. Запрос get может быть отправлен на сервер путем
простого ввода URL в адресной строке Web-браузере. Фактически, именно это вы
и делаете, когда запрашиваете Web-страницу в браузере. В этом примере вы
можете ввести
http://localhost:8080/advjhtpl/welcomel
в поле Address или Location вашего браузера, чтобы вызвать сервлет напрямую
66
Глава 2
Совет по тестированию и отладке 2.4
Вы можете протестировать сервлет, который обрабатывает
HTTP-запросы get, введя в поле Address или Location вашего браузера URL, кото
рый непосредственно вызывает сервлет.
2А. Обработка HTTP-запросов get, содержащих данные
При запросе документа или ресурса с Web-сервера можно передать данные как
часть запроса. Сервлет WelcomeServlet2, показанный на рис. 2.11, реагирует на
HTTP-запрос get, который содержит имя, предоставленное пользователем.
Сервлет использует имя как часть ответа клиенту.
1 // Рис. 2.11. WeleomeServlet2.Java
2 // Обработка HTTP-запросов get, содержащих данные.
3 package com.deitel.advjhtpl.servlets;
4
5 import javax.servlet.*;
6 import j avax.servlet.http.*;
7 import java.io.*;
8
9 public class WelcomeServlet2 extends HttpServlet {
10
11 // обработка клиентского запроса get
12 protected void doGet( HttpServJLetRequest request,
13 HttpServletRespqnse response )
14 throws ServletException, IOExceptiort
15 t
16 String firstName = request.getParameter( "firstname" );
17
18 response.setContentType( "text/html" );
19 PrintWriter out = response.getWriter[);
20
21 // отправка XHTML-документа клиенту
22
23 // начало XHTML-документа '
24 out.println( "<?xml version = V'1.0\"?>" );
25
26 out.println( "<!DOCTYPE html PUBLIC V-//W3C//DTD " +
27 "XHTML 1.0 Strict//EN\" V'http://www.w3.org" +
28 "/TR/xhtmll/DTD/xhtInll-strict.dtd\,,>,, );
29
30 out.printlnf
31 H<html xmlns = V'http: //www. w3 . org/1 gag/xhtmiy:»" ) ,-
32
33 // заголовок документа
34 out.println( "<head>" );
35 o«t.println(
36 "<title>Proceasing get requests with data</title>" );
37 out.println( "</head>" >;
38
39 // тело документа
40 out.println( "<body>" );
41 out.println( "<hl>Hello " + firstWame + ",<br />" );
42 out.println( "Welcome to Servlets!</hl>" );
43 out.println( "</body>" );
Сервлеты
67
44
45 // конец XHTML-документа
46 out.println( "</html>" );
4 7 out.close(); // закрытие потока и конец страницы
48 }
49 }
Рис. 2.11. Сервлет WekomeServlet2 реагирует на запрос get, содержащий данные
Параметры в запросе get передаются в виде пар имя/значение. В строке 16
демонстрируется, каким образом получить информацию, которая передается сервле-
ту как часть клиентского запроса. Метод getParameter объекта request принимает
в качестве аргумента имя параметра и возвращает соответствующее строковое
(String) значение или null, если параметр не является частью запроса. В строке 41
результат выполненного в строке 16 действия помещается в ответ клиенту.
Документ WeIcomeServlet2.html (рис. 2.12) предоставляет форму (элемент
form), в которой пользователь может ввести имя firstname в качестве текста,
запрашиваемого элементом input (строка 17) и щелкнуть на кнопке Submit, чтобы
вызвать сервлет WelcomeServlet2. Когда пользователь нажимает кнопку Submit,
значения, содерисащиеся в элементах input, помещаются в пары имя/значение
в составе запроса, отправляемого серверу- Взглянув на вторую копию экрана на
рис. 2.12, вы можете увидеть, что браузер добавил
?firsfcname=Paul
в конец URL, указанного в параметре action. Символ ? отделяет строку запроса
(т.е. данные, передаваемые как часть запроса get) от остальной части URL в
запросе get. В паре имя /значение имя отделяется от значения символом =. Если имеется
более одной пары имя/значение, пары отделяется друг от друга символом &.
1 <?xml version = "1.0"?>
2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
3 "http://www.w3.org/TR/xhtmll/DTD/xhtmll-strict.dtd">
4
5 <1— Рис. 2.12. WelcomeServlet2.html -->
6
7 <html xmlns = "http://www.w3.org/1999/xhtnil">
8 <head>
9 <title>Processing get requests with data</title>
10 </head>
11
12 <body>
13 <form action = "/advjhtpl/welcome2" method = "get">
14
15 <pxiabel>
16 Type your first name and press the Submit button
П <br /Xinput type = "text" name = "firstname" />
18 <input type = "submit" value = "Submit" />
19 </pX/label>
20
21 </form>
22 </body>
23 </html>
Рис. 2.12. HTML-документ, в котором действие action формы form приводит к вызову
сервлета WelcomeServlet2 через псевдоним wekome2, заданный в документе web.xml
(часть 1)
68
Глава 2
* . ■■+■ Г~'Э Щ Га"-7"/"#""":'"""'
. В»*.
S»**
[^w»)@htlii:fAotallitKM81!Ki,'«Witpi;sgvleaWelciira5efvfet;.htoil jj. t^S° j
Type your first name afid press the Submit button
[i^i [ Submit J
SSS£T
:Г^Ш&«<«КУ
Trf
Ji ft Qccsuni^ qct * cojut-tf* W*b del Л - MiCrtrttift IHliajflilSf^
J:jfc.-JE*.
a»t^a^
«я». Fjyfftli Тонь Нф
]М*|Я,Л
Sttfl ■ Itefttil) bant j .S«Mh
W*^*jC] t*tP' tflccahKt 'вОЩ* sdvMp 1 ^4fccme2*irstr4nie-P5ul '
*ZJ ,.£><*!
Hello Paul,
Welcome to Servlets!
ieiBssfe .^штШШ:
■mmmgms*?
Данные,
полученные \лз
фор^ы,
присутствуют
a URL как часть
запроса get
Рис. 2.12. HTML-документ, в котором действие action формы forrn приводит к вызову
сервлета WekomeServlet2 через псевдоним welcome2, заданный в документе web.xml
(часть 2)
Мы, как и ранее, используем наш корень контекста advjhtpl для демонстрации
работы сервлета, представленного на рис. 2.11. Поместите документ Welcome-
Servlet2.html в каталог servlets, созданный в разделе 2.3.2. Поместите класс
WelcomeServlet2.class в подкаталог classes каталога WEB_INF в корне контекста
advjhtpl. Помните, что классы в пакете должны быть помещены в
соответствующую структуру каталогов пакета. Затем отредактируйте дескриптор
развертывания web.xml в директории WEB_INF, чтобы включить в него информацию,
приведенную в таблице на рис. 2.13. Эта таблица содержит данные для элементов servlet
и servletmapping, которые добавляются в дескриптор развертывания web.xml.
Текст, выделенный курсивом, вводить в дескриптор развертывания не следует.
Перезапустите Tomcat и введите следующий URL в вашем Web-браузере:
http://localh.ost:8080/advjhtpl/servlets/WelcomeServlet2.html
Введите ваше имя в текстовом доле Web-страницы, затем щелкните на кнопке
Submit для вызова сервлета.
Еще раз заметим, что запрос get можно ввести и непосредственно в поле
Address или Location браузера следующим образом:
bttp://localhost:8080/advjhtpl/welcome2?firstname=Paul
Попробуйте проделать это с вашим собственным именем.
Элемент Дескриптора
servlet элемент
servlet-name
Description
servlet-class
Значение
welcome2
Handling HTTP get requests with data
com.deitel.advjhtpl,servlets.WelcomeServlet2
Сервлеты
69
Элемент дескриптора
servlet-mapping элемент
servlet-name
url-pattern
Значение
welcome2
/■w«lcoms2
Рис. 2.13. Информация в дескрипторе развертывания, относящаяся к сервлету
Wekom e S e rv Iet2
2.5. Обработка HTTP-запросов post
HTTP-запрос post часто используется для передачи данных из HTML-формы
серверному обработчику формы, который выполняет обработку данных.
Например, когда вы отвечаете на вопросы Web-анкеты, информация, которую вы ввели
в HTML-форме, обычно передается на Web-сервер э виде запроса post.
Браузеры часто кэшируют Web-страницы, чтобы иметь возможность быстро их
перезагрузить. Если последняя версия, хранящаяся в кэш, не отличается от
текущей версии в Web, время, затрачиваемое на отображение страницы, существенно
сокращается. Браузер сначала запрашивает у сервера, изменился ли документ,
или истекло ли определенное установленное время после кэширования файла.
Если нет, браузер загружает документ из кэша. Таким образом, браузер
минимизирует количество данных, которые должны быть загружены, чтобы можно было
просмотреть Web-страницу. Браузеры обычно не кэшируют ответ сервера на
запрос post, поскольку следующий запрос post может не вернуть тот нее самый
результат. Например, при проведении опроса многие пользователи могут посетить
одну и ту же Web-страницу и ответить на вопрос анкеты. Результаты опроса могут
затем быть представлены пользователю. Каждый новый ответ изменяет общий
итог опроса.
Когда вы пользуетесь поисковым сервером в Web, браузер обычно
предоставляет поисковому серверу информацию, которую вы задаете в HTML-форме, с
помощью запроса get. Поисковый сервер выполняет поиск, а затем возвращает вам
результаты в виде Web-страницы. Такие страницы часто кэшируются браузером на
случай, если вы снова будете выполнять тот же поиск. Как и запросы post,
запросы get могут предоставлять Web-серверу параметры как часть запроса.
Сервлет WelcomeServtet3, представленный на рис. 2.14, идентичен сервлету на
рис. 2.11, за исключением того, что он определяет метод doPost (строка 12) для
реакции на запросы post, вместо метода doGet. В соответствии с настройкой по
умолчанию, метод do Post вызывает ошибку *Method not allowed» («Недопустимый
метод»). Мы замещаем этот метод, чтобы обеспечить собственную обработку запроса
post. Метод doPost принимает те же два параметра, что и метод doGet: объект,
который реализует интерфейс HttpSeivletRequest для представления клиентского
запроса, и объект, который реализует интерфейс HttpServletResponse для
представления ответа сервера. Как и метод doGet, метод doPost возбуждает
исключение ServletException, если он не способен обработать клиентский запрос, и
исключение IOException, если при обработке потока возникает проблема.
1 // Рис. 2.14. WelcomeServlet3.Java
2 // Обработка запросов post, содержащих данные.
3 package com.deitel.advjhtpl,servlets;
4
5 import javax.servlet.*;
6 import javax.servlet.http.*;
7 import java.io.*;
70
Глава 2
в
9 public class WelcomeServlet3 extends HttpServlet {
10
11 // обработка клиентского запроса post
12 protected void doPost( HttpServletRequest request,
13 HttpServletResponse response )
14 throws ServletException, IOException
15 1
16 String firstName = request.getParameter{ "firstname" );
17
18 response.setContentType( "text/html" );
19 PrintWriter out = response.getWriter();
20
21 // отправка XHTML-страницы клиенту
22
23 // начало XHTML-документа
24 out.println( "<?xml version = \"1.0\"?>" );
25
2 6 out.println( "<!DOCTYPE html PUBLIC V-//W3C//DTD " +
27 "XHTML 1.0 Strict//EN\" V'http://www.w3.org" +
28 "/TR/xhtmll/DTD/xhtmll-strict.dtd\">" ) :
29
30 out. priii tin {
31 "<html xmlns = \"http://www.w3.org/1999/xhtml\">" );
32
33 // заголовок документа
34 out.println( "<head>" );
35 out.println(
36 "<title>Processing post requests with data</title>" );
37 out.println( "</head>" );
38
39 // тело документа
40 out.println( "<body>" );
41 out.println( "<hl>Hello " + firstName + ",<br />" ) ;
42 out.println( "Welcome to Servlets!</hl>" );
43 out.println [ "</body>" ) ,-
44
45 // конец XHTML-документа
46 out.println{ "</html>" );
47 out. closed; // закрытие потока и конец страницы
48 }
49 }
Рис. 2.14. Сервлет WekomeServlet3 отвечает на запрос post, содержащий данные
Документ WeIcomeServlet3.html (рис. 2.15) предоставляет форму (элемент
form, строки 13-21), в которую пользователь может ввести имя firstname в
качестве запрашиваемого элементом input (строка 17) текста, а затем щелкнуть на
кнопке Submit, чтобы вызвать сервлет WelcomeServlstS. Когда пользователь
нажимает кнопку Submit, значения элементов input посылаются на сервер как часть
запроса. Однако имейте в виду, что значения при этом не добавляются в строку
URL запроса. Обратите внимание, что в качестве метода в этом примере указано
post. Имейте в виду также, что запрос post не может быть введен в поле Address
или Location браузера, и пользователи не могут помещать запросы post в список
избранных запросов в своих браузерах.
Сервлеты
71
1
2
3
4
5
6
7
8
9
10
11
12
13
Id
15
16
17
18
19
20
21
22
23
<?xml version = "1.0"?>
<!DOCTTTPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtmll/DTD/xhtmll-strict.dtd">
<!
Рис. 2.15: WelcomeServlet3.html —>
<html xmlns = "bttp://www.w3.org/1999/xhtoil">
<head>
<title>Handling an HTTP Post Request with Data</title>
</head>
<body>
<fonn action = "/advjhtpl/welcome3" method = "post">
<pXlabel>
Type your first name and press the Submit button
<br /Xinput type = "text" name = "firstname" />
<input type = "submit" value = "Submit" />
</labelX/p>
</form>
</body>
</html>
1 Handling an i-г I и1 ftrtt Redur«t itflh Oate - МнтфиА
'. '"ffiw* . * ivwwja.^ •• '■agi :V •'' 1цй^'. 4<м» • jfe; Swnfr - -.'
t't^^ft^'-^^ «••',;/
Type your first name and press the Submit button
[Paul
.:*:. L
7f7?gjR!yaiCS«4»te^ft^
J Processmy THJ*t request* withddL^ - MHfOtoft Int«14$
Jjbj-; ЕЛ 5; irtw*. '.Fevarito* _T«fcii
is^Jtei
■■■■1J»~-
1Яр ;(/1ка1мя lOBO/jdvMlil/""1:"™ 3
zJ ^1
Hello Paul,
Welcome to Servlets!
d
-г^г^р1^щ1й^^^^.~^
Рис. 2.15. HTML-документ, в котором в качестве действия action для формы form
предусмотрен вызов сере, пет a Wekome5eivlet3 через псевдоним welcome3, заданный
в документе web.xml
Мы используем корень контекста advjhtpl для демонстрации работы сервлета,
представленного на рис. 2.14. Поместите документ WelcomeServlet3.html в
каталог servlets, созданный в разделе 2.3.2. Поместите файл WelcomeServlet3 в
подкаталог classes каталога WEB_INF в корне контекста advjhtpl. Затем
отредактируйте дескриптор развертывания web.xml в каталоге WEB_INF, чтобы включить
72
Глава 2
в него информацию, приведенную в таблице на рис. 2.L6. Перезапустите Tomcat
и введите следующий URL в вашем браузере:
http://localhost:8080/advihtpl/servlets/WelcomeServlet3.html
Введите ваше имя в текстовом поле на Web-странице, а затем щелкните на кнопке
Submit, чтобы выполнить сервлет.
Элемент дескриптора
servlet элемент
servlet-name
description
servlet-class
servlet-mapping элемент
servlet-name
url-pattern
Значение
welcome3
Handling HTTP post requests with data
com.deitel.advjhtpl.servlets.WelcomeServlet3
we1come3
/welcome3
Рис. 2.16. Информация в дескрипторе развертывания, относящаяся к сервлету
WeicomeServtet3
2.6. Переадресация запросов
Иногда полезно переадресовать запрос другому ресурсу. Например, сервлет
может определить тип клиентского браузера и переадресовать запрос на
Web-страницу, которая была специально разработана для этого браузера. Сервлет Redirect-
Servlet, представленный на рнс. 2.17, принимает параметр страницы как часть
запроса get, а затем использует этот параметр для переадресации запроса на другой
ресурс.
1// Рис. 2.17. RedirectServlet.java
2 // Переадресация пользователя к другой Web-странице.
3 package com.deitel.advjhtpl.servlets;
4
5 import javax.servlet.*;
6 import javax.servlet.http.*;м
7 import java.io.*;
8
9 public class RedirectServlet extends HttpServlet {
10
// обработка клиентского Запроса get
protected void doGet( HttpServletRequest request,
HttpServletResponse response )
throws ServletException, lOException
11
12
13
14
15
16
17
IB
19
20
21
22
23
24
t
String location = request.getParameter( "page" );
if ( location != null )
if ( location.equals( "deitel" } )
response.sendRedirect( "http://www.deitel.com" )
else
if ( location.equals( "welcome!" ) )
response.sendRedirect( "welcomel" );
Сервлеты
73
25
2 6 // код, который выполняется только в тон случае, если этот
27 // сервлет не переадресовывает пользователя к другой странице
28
29 response.setContentType( "text/html" );
30 PrintWriter out = response,getWriterО;
31
32 // начало XHTML-документа
33 out.println( "<?xrol version = \"1.0\"?>" );
34
35 out.println( "<!DOCTYPE html PUBLIC V-//W3C//DTD " +
36 "XHTML 1.0 Strict//EN\" \"http://www.w3.org" +
37 "/TR/xhtmll/DTD/xhtmll-strict.dtd\">" );
38
39 out.printlnf
40 "<html xmlns = V'http://www.w3.org/1999/xhtml\">" );
41
42 // заголовок документа
43 out.println( "<head>" );
44 out.println( "<title>Invalid page</title>" );
45 out.println( "</head>" );
46
47 // тело документа
48 out.println( "<body>" );
49 out.println( "<hl>Invalid page requested</hl>" );
50 out.println( "<pXa href — " +
51 "\"servlets/RedirectServlet.html\">" );
52 out,println( "Click here to choose again</aX/p>" );
53 out.println( "</body>" );
54
55 // конец XHTML-документа
56 out.println( "</html>" );
57 out,close О; // закрытие потока и конец страницы
58 )
59}
Рис. 2.17. Переадресация запросов другим ресурсам
В строке 16 параметр page извлекается из запроса. Если возвращаемое
значение не равно null, структура if /else в строках 20-24 определяет, соответствует ли
значение "deitel" или "welcorael". Если значением является "deitel", метод setid-
Redirect объекта response (строка 21) переадресовывает запрос на www.deitel.coni,
Если значением является "welcomel", в строка 24 запрос переадресовывается
сервлету, представленному на рис, 2.5. Обратите внимание, что в строке 24 корень
контекста advjhtpl для нашего Web-приложения не задан явно. Когда сервлет
использует относительный путь для ссылки на другой статический или
динамический ресурс, сервлет предполагает использование того же базового URL и корня
контекста, как и те, которые использовались при вызове сервлета, если только для
ресурса не был задан абсолютный URL. С учетом этого в строке 24 в
действительности запрашивается ресурс, расположенный по адресу
http://localhost:80B0/adv3htpl/welcomel
Аналогично, в строке 51 в действительности запрашивается ресурс,
расположенный по адресу
http://localhost:8080/advjhtpl/servlets/RedirectServlet.html
74
Глава 2
Общая методическая рекомендация 2.6
Использование относительных путей для ссылки на ресурсы,
размещенные в этом же корне контекста, делает Web-приложение более гибким.
Например, еы можете изменить корень контекста, не внося изменений
в статические и динамические ресурсы в приложении.
После выполнения метода sendRedirect обработка запроса сервлетом
RedirectServlet завершается. Если метод sendRedirect не был вызван, обработчик
внештатных ситуаций метода doPost выводит Web-страницу, указывающую, что был
сделан некорректный запрос. Страница дает возможность пользователю повторить
попытку, возвращая XHTML-документ, представленный на рис. 2.18. Обратите
внимание, что одна переадресация осуществляется на статическую Web-страницу,
а другая — на сервлвт.
Документ RedirectServIet.html (рис. 2.18) предоставляет две гиперссылки
(строки 15-16 и 17-18), которые дают возможность пользователю вызывать серв-
лет RedirectServlet. Обратите внимание, что для каждой гиперссылки параметр
page задан как часть URL. Чтобы продемонстрировать передачу в качестве
параметра некорректной страницы, вы можете ввести в браузере URL без указания
значения для параметра page.
Мы используем наш корень контекста advjhtpl для демонстрации работы серв-
лета, представленного на рис. 2.17. Поместите файл RedirectServlet.html в
каталог servlets, созданный в разделе 2.3.2. Поместите файл RedirectServlet.class
в подкаталог classes каталога WEB_INF в корне контекста advjhtpl. Затем
отредактируйте дескриптор развертывания web.xml в каталоге WEB_INF, чтобы
включить в него информацию, приведенную в таблице на рис. 2.19. Перезапустите
Tomcat и введите следующий URL в вашем браузере:
http://localhost:8080/advjhtpl/servlets/RedirectServlet.html
Щелкните на гиперссылке на Web-странице, чтобы вызвать сервлет.
1 <?xml version = "l.Q"?>
2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
3 "http://www.w3.org/TR/xhtmll/DTD/xhtmll-strict.dtd">
4
5<'-- Рис. 2.18. RedirectServlet.html —>
6
7 <html xmlns = "http://www.w3.org/1999/xhtml">
8 <head>
9 <title>Redirecting a Request to Another Site</title>
10 </head>
11
12 <body>
13 <p>Click a link to be redirected to the appropriate page</p>
14 <p>
15 <a href = "/advjhtpl/redirect?page=deitel">
16 www.deitel.com</aXbr />
17 <a href = "/advjhtpl/redirect?page=welcomel">
18 Welcome servlet</a>
19 </p>
20 </body>
21 </html>
Рис. 2.18. Документ RedirectServlet.html, демонстрирующий переадресацию запросов
другим ресурсам (часть 1)
Сервлеты
75
'Ь,!^'^^^»ИД)Ш№1ИРИ!
, .^^^l^t^^f^atio^BCaO/advjNipl.'sgvlats/Rj^MtStrvlet.htiri'' " ' jfjljgie^fe
СНск a link to be redirected to the appropriate page
www dedtl com
Weicomafftrvlet
в
..а
; Utal intranet
"^
■a A Simple *er»lrt Eiijtnif's - Microiofl Internet Ьц&иг
Fto" Edt'wewf'Fworlter те* Нф
'-'"'■--I V i
»i
-т"-;Г.*Г:; '.9 -.a"-:--Ja-.'i ©. ." ш . . .
'"* "?** IM'J^A.-1*^*»*^ ;.±-jt^. * :.jr T_ .. *■■*-■* *t* '■■■"4- J-*-* -J ■■■. т х-— ' ~* *■ "V*~* * "^
jfrt^ffibttpifflKa»og:30Wfad*№i;wefcofrMl _ jJT'y&t j
Welcome to Servlets!
mpvwmmm^-
tsmmm
Рис. 2.18. Документ RedirectServlet.html, демонстрирующий переадресацию запросов
другим ресурсам (часть 2)
Элемент дескриптора
servlet элемент
serv1еt-name
description
servlet-class
servlet-mapping элемент
servle t-name
url-pattern
Значение 1
Redirect i
Redirecting to static Web-pages and other
servlets
com.deitel.advjhtpl.servlets.RedirectServlet
Redirect
/redirect
Рис. 2.19. Информация в дескрипторе развертывания, относящаяся к сервлету
RedirectServlet
При переадресации запросов параметры исходного запроса передаются новому
запросу. Могут также передаваться дополнительные параметры запроса.
Например, URL, передаваемый методу sendRedirect, может содержать пары
имя/значение. К имеющимся параметрам добавляются любые новые параметры. Если новый
параметр имеет то лее имя, что и существующий параметр, новое значение
параметра имеет приоритет над исходным значением. Однико передаются по-прежнему
все значения. В подобном случае полный набор значений для параметра с
заданным именем может быть получен путем вызова метода getParameterValue
интерфейса HttpSorvletRequest. Этот метод принимает в качестве параметра имя
параметра и возвращает массив строк, содержащих значения параметра, в порядке от
самого последнего к самому первому.
76
Глава 2
2.7. Отслеживание состояния сеанса
Многие компании, занимающиеся электронным бизнесом, используют
индивидуальную настройку (персонализацию) Web-страниц в соответствии с личными
предпочтениями пользователей и предоставляют пользователям возможность
получать только нужное им содержимое. Это достигается путем слежения за
перемещениями потребителя со Internet и использования этих данных в совокупности
с информацией, предоставленной потребителем, такой, как сведения о сделанных
покупках, интересы, увлечения и т.д. Персонализация облегчает для многим
людям процесс «серфинга» по Internet, делает его более интересным и позволяет
быстрее находить то, что им нужно. Потребители и компании, ведущие свой бизнес
через Internet, могут воспользоваться преимуществами, которые дает персонали-
зация, уникальным образом интерпретируя свой данные для определенного
пользователя. Предоставляя содержимое в соответствии с интересами посетителя,
можно установить с ним определенные отношения, которые можно воссоздавать
каждый раз, когда этот посетитель возвращается на сайт. Индивидуальная работа
с потребителями путем персональных предложений, направленной рекламы,
скидок и оказания персональных услуг может повысить степень расположения
потребителя — многим людям нравится, когда им оказывают особое, персональное
внимание. Первоначально в Internet не было такой индивидуальной работы с потребителем,
которая характерна для частных магазинов. Усовершенствованные технологии дают
возможность многим Web-сайтам индивидуально работать со своими посетителями.
Например, такие Web-сайты, как MSN.com и CNN.com позволяют вам настраивать
свои основные страницы в соответствии с вашими нуждами. Сайты виртуальных
магазинов часто настраивают свои Web-страницы под отдельных покупателей.
Такие сайты должны уметь различать клиентов, чтобы предлагать соответствующие
товары и цены для каждого клиента. Персонализация важна для маркетинговой
деятельности в Internet и для установления доверительных отношений с
потребителями, чтобы добиться их расположения.
Однако при всех тех плюсах, которые дает переонализация, она порождает
проблему, связанную с вторжением в частную жизнь. Что, если компания,
занимающаяся электронным бизнесом, которой вы сообщили ваши личные данные,
продаст или передаст эти данные другой организации без вашего ведома? Что, если вы
не хотите, чтобы ваши перемещения по Internet отслеживались неизвестно кем?
Что, если не имеющая на то полномочий сторона получит доступ к вашим
конфиденциальным данным, таким как номера кредитных карт или история болезни?
Это лишь некоторые из многих вопросов, с которыми должны считаться
потребители, компании, ведущие бизнес в Internet, и законодатели.
Как уже говорилось, механизм запрос/ответ основан на протоколе HTTP. К
сожалению, HTTP представляет собой протокол без сохранения состояния — он не
поддерживает сохранение информации, которая помогает Web-серверу
определить, какой запрос поступил от какого клиента. С точки зрения Web-сервера,
каждый из запросов мог поступить как от одного и того же клиента, так и от разных
клиентов. В этой связи сайты, подобные MSN.com и CNN.com, нуждаются в
механизме для идентификации отдельных клиентов. Чтобы помочь серверу различать
клиентов, каждый клиент должен идентифицировать себя перед сервером. Для
того чтобы различать клиентов, используется ряд приемов. Мы познакомимся
с двумя методами отслеживания клиентов: cookies (раздел 2.7.1) и отслеживание
сеансов (раздел 2.7.2). Двумя другими методами, которые не будут
рассматриваться в этой главе, являются использование скрытых полей форм и перезапись VRL.
При использовании скрытых полей формы сервлет может записывать данные о
сеансе в форму на Web-странице, которую сервлет вернул клиенту при обработке его
предшествующего запроса. Когда пользователь отправляет форму в новой Web-
Сервлеты
77
странице, все данные формы, включая скрытые поля, посылаются обработчику
формы на сервере. При перезаписи URL сервлет встраивает информацию о сеансе
в качестве параметров запроса get в URL гкперссылок, на которых пользователь
может щелкнуть мышью, чтобы отправить следующий запрос Web-серверу.
2.7.1. Cookies
Одним из популярных способов индивидуальной настройки Web-страниц
является применение cookies. Браузеры могут хранить cookies на компьютере
пользователя для последующего извлечения в этом же сеансе или в последующих сеансах.
Например, cookies могут использоваться в приложениях Internet-магазинов,
чтобы хранить уникальные идентификаторы пользователей. Когда пользователи
добавляют элементы в свои магазинные тележки или выполняют другие задачи,
результатом которых является выдача запроса Web-серверу, сервер принимает
файлы cookies, содержащие уникальные идентификаторы пользователей. Сервер
затем использует уникальный идентификатор для нахождения магазинной
тележки и выполнения необходимой обработки. Cookies могут также использоваться для
идентификации клиентских предпочтений. При взаимодействии с клиентом
сервлет может просмотреть cookie (или несколько cookies), которые он отправил
клиенту при предыдущем взаимодействии, идентифицировать предпочтения клиента
и немедленно отобразить интересующие клиента товары.
Cookies представляют собой текстовые данные, которые посылаются сервлета-
ми (или другими серверными технологиями) как часть ответов клиентам. Каждое
взаимодействие на базе HTTP между клиентом и сервером включает в себя
заголовок, содержащий информацию о запросе (при направлении взаимодействия от
клиента к серверу) или информацию об ответе (при направлении взаимодействия
от сервера к клиенту). Когда объект HttpServlet принимает запрос, заголовок
содержит информацию, такую как тип запроса (например, get или post) и cookies,
которые были отправлены сервером для сохранения их на компьютере клиента.
Когда сервер подготавливает свой ответ, заголовок может включать любые cookies,
которые сервер хочет сохранить на компьютере клиента, а также другую
информацию, например, MIME-тип ответа.
j^*. Совет по тестированию и отладке 2.5
\Wy Некоторые клиенты не принимают cookies. Если клиент отвергает
cookies, Web-сайт или браузер может проинформировать пользователя,
что сайт, возможно, не будет функционировать надлежащим образом
при запрещенных cookies.
В зависимости от максимального возраста cookie, Web-браузер либо хранит
cookie в течение сеанса просмотра (т.е. до тех пор, пока пользователь не закроет
Web-браузер), либо сохраняет cookie на компьютере клиента для последующего
использования. Когда браузер запрашивает ресурс с сервера, cookies, ранее
отправленные клиенту этим сервером, возвращаются сервлету как часть запроса,
выданного браузером. Cookie автоматически удаляются по истечении их срока хранения
(т.е. по достижении ими максимального возраста).
Пример на рис. 2.20 демонстрирует применение cookies. В примере
пользователь может выбрать любимый язык программирования и отправить (методом post)
свой выбор на сервер. Ответом является Web-страница, в которой пользователь
может выбрать другой любимый язык или щелкнуть на ссылке, чтобы просмотреть
список рекомендуемых книг по данному языку программирования. Когда
пользователь выбирает список рекомендуемых книг, на сервер посылается запрос get.
Cookies, ранее сохраненные на клиенте, прочитываются сервлетом и используются
для формирования Web-страницы, содержащей рекомендуемые книги.
Глава 2
1 // Рис. 2.20. CookieServlet.java
2 // Использование cookies для хранения данных на компьютере клиента.
3 package com.deitel.advjhtpl.servlets;
4
5 import javax.servlet.*;
6 import javax.servlet.http.*;
7 import java.io.*;
8 import java.util.*;
9
10 public class CookieServlet extends HttpServlet {
11 private final Кар books = new HashMapO;
12
13 // инициализация таблицы books
14 public void init()
15 {
16 books.put( "С", "0130895725" );
17 books.put{ "C++", "0130895717" );
18 books.put( "Java", "0130125075" );
19 books.put( "VB6", "0134569555" );
20 J
21
22 // получение выбранного языка и отправка клиенту cookie,
23 // содержащего рекомендуемую книгу
24 protected void doPost( HttpServletRequest request,
25 HttpServletHesponse response )
26 throws ServletException, lOException
27 {
28 String language = request.getParameter( "language" );
29 String isbn = books.get( language ) .toStringO ;
30 Cookie cookie = new Cookie( language, isbn );
31
32 response .addCookie ( cookie ) ; // должно предшествовать getHriter
33 response.setContentType( "text/html" );
34 PrintWriter out = response.getWriter();
35
36 // отправка XHTML-страницы клиенту
37
38 // начало XHTML-документа
39 out.println( "<?xml version = \"1,0V'?>" );
40
41 out.println{ "<!DOCTYPE html PUBLIC \"-//W3C//DTD " +
42 "XHTML 1.0 Strict//EN\" V'http://www.w3.org" +
43 M/,ni/xhtmll/DTD/xhtmll-strict.dtdV,>" );
44
45 out.println{
46 "<html xmlns = \"httpr//www.w3.org/1999/xhtml\">" );
47
48 // заголовок документа
49 out .print-in f "<head>" );
50 out.println( "<title>Welcome to Cookies</title>" );
51 out.println( "</head>" ) ;
52
53 // тело документа
54 out.println( "<body>" );
55 out.println( "<p>Welcome to Cookies' You selected " +
56 language + "</p>" ),-
Сервлеты
79
57
58 out.printlnt "<pXa href = " +
59 "\"/advjhtpl/servlete/CookieSelectLanguage.html\">" +
60 "Click here to choose another language</aX/p>" ) ;
61
62 out.printlnt "<pxa href = \"/advjhtpl/cookies\">" +
63 "Click here to get book recommendations</aX/p>" ) ;
64 out.printlnt "</body>" );
65
66 // конец XHTML=документа
67 out.printlnt "</html>" );
68 out.closed; // Закрытие потока
69 }
70
71 // чтение cookie с клиента и создание XHTML-документа,
72 // содержащего список рекомендуемых книг
73 protected void doGet( HttpServletRequest request,
74 HttpServletResponse response )
75 throws ServletException, XOException
76 {
77 Cookie cookies[] = request.getCookies(); // получение cookies
78
79 response.setContentType( "text/html" );
80 PrintWriter out = response.getWriter();
ei
82 // начало XHTML-документа
83 out.println( "<?xml version = \"1.0\"?>" );
84
85 out.printlnt "<!DOCTYPE html PUBLIC \"-//W3C//DTD " +
86 "XHTML 1.0 Strict//EN\" V'http://www.w3.org" +
87 "/TR/xhtmll/DTD/xhtmll-strict.dtd\">" ) ,-
88
89 out.printlnt
90 "<html xmlne = V'http://www.w3.org/1999/xhtml\">" );
91
92 // заголовок документа
93 out.printlnt "<head>" );
94 out.printlnt "<title>Recommendations</title>" );
95 out.printlnt "</head>" );
96
97 // тело документа
98 out.printlnt "<body>" );
99
100 // если имеются cookies, рекомендовать книги для изучения
101 if t cookies != null && cookies.length != 0 ) {
102 out.printlnt "<hl>Recommendations</hl>" );
103 out.printlnt "<p>" );
104
105 // получение имени каждого из cookies
106 for ( int i = 0; i < cookies.length; i++ )
107 out.printlnt cookies[ i ] .getHante (> +
108 " How to Program. ISBN#: " +
109 cookies! i ].getValue() + "<br />" );
110
111 out.printlnt "</p>" );
112 }
80
Глава 2
113 else { // cookies отсутствуют
114 out.printIn( "<hl>No Recommendations</hl>" );
115 out.println( "<p>You did not select a language.</p>" );
116 }
117
118 out.println( "</body>" );
119
120 // конец XHTML-документа
121 out.printlnt "</htral>" } ;
122 out.close(); // закрытие потока
123 }
124 }
Рис. 2.20. Хранение данных пользователя на компьютере клиента с помощью cookies
Сервлет CookieServlet (рис. 2.20) обрабатывает как запросы get, так и запросы
post. Документ CookieSelectLangnage.html, представленный на рис. 2.21,
содержит четыре переключателя (С, C++, Java и VB 6), а также кнопку Submit. Когда
пользователь нажимает кнопку Submit, вызывается сервлет CookieServlet с
использованием метода post. Сервлет добавляет cookie, содержащий выбранный
язык, в заголовок ответа и отправляет клиенту ХНТМЪ-документ. Каждый раз,
когда пользователь щелкает на кнопке Submit, cookie отправляется клиенту.
В строке 11 определяется карта соответствий books как объект типа HashMap
(пакет java.util), в котором мы храним пары ключ/значение. В качестве ключа
используется название языка программирования, а в качестве значения — ISBN-код
рекомендуемой книги. Метод init класса CookieServlet (строки 14-20) заполняет
четыре пары ключ/значение названиями книг. Метод doPost (строки 24-69)
вызывается в ответ на запрос post от XHTML-документа, представленного'на рис. 2.21.
В строке 28 используется метод getParameter для получения выбранного
пользователем языка language. В строка 29 из хэш books извлекается ISBN-код для
выбранного языка.
В строке 30 создается новый объект Cookie (пакет javax.servlet.http) с
использованием значений language и isbn в качестве имени cookie и значения cookie,
соответственно. Имя cookie идентифицирует cookie; значением cookie является
информация, ассоциированная с cookie. Браузеры, поддерживающие cookies, должны
иметь возможность хранить до 20 cookies на один Web-сайт и до 300 cookies на
пользователя. Браузеры могут ограничивать размер cookie 4 Кб (4096 байт).
Каждый cookie, хранящийся на машине клиента, включает в себя имя домена. Браузер
посылает cookie только тому домену, имя которого сохранено в cookie.
1 <:?xml version = "1.0"?>
2 <!D0CTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
3 "http://www.w3.org/TR/xhtmll/DTD/xhtmll-strict.dtd">
4
5<M-- Рис. 2.21. CookieSelectLanguage.html —>
6
7 <±Ltml xmlns = "http://www.w3.org/1999/xhtml">
8 <head>
9 <title>Using Cookies</title>
10 </head>
11
12 <body>
13 <form action = "/advjhtpl/cookies" method = "post">
14
15 <p>Select a programming language:</p>
Сервлеты
81
16 <р>
П <input type = "radio" name = "language"
18 value = "C" />C <br />
19
20 <input type = "radio" name = "language"
21 value = "C++" />C++ <br />
22
23 <\— эта кнопка, выбранная по умолчанию —>
24 <input type = "radio" name = "language"
25 value = "Java" checked = "checked" />Java<t»r />
26
27 <input type = "radio" name = "language"
28 value = "VB6" />VB 6
29 </p>
30
31 <pXinput type = "submit" value = "Submit" /x/p>
32
33 </form>
34 </body>
25 </tvtml>
Рис. 2.21. Документ CookieSetectLanguage.html для выбора языка программирования
и передачи данных сервлету CookieServlet {часть 1)
82
Глава 2
■J using Cookies - *fctro*olt internet ^прилег
._, __._. , __ _. _ Л^ЕЩ
,i а&-а»*а аь?аь
-^|^[ЙЬод:;^^^;Д)зд;^у)Ьф1^у|^|^ооИд5Ы^апди^.И(яа ^
Select a programming language:
<~C
(ГС++
■*" Java
<~VB6
рруг-'-'':
'Г^Р^Щи^мцр»
■+3 Welcome to Cookie - Mirtosnlt Interne* ЁкрЗогеГ
.I-.JU^a.-.nm Ж--,*„■■■■„■■ Л\ _^ ■ J *■■ ..rb:tffi№.
№-:
j£j
"1 http:/fl«*oaL8060/»(W*pl(™*j« Д'".^?"
Welcome to Cockiest Y«u selected C++
Click: here to choose another language
Cbck hen togetbookjrecommendarioiis
И
(ШЩьЩшёШщщш^
~nm
■a
ЦЩ$)|}Й httpi I flxdha*: S08Cfadyjh>P ЦсаоКн
5 Recommendatory Miotiwft: Internet Explorer
&• ~ #™к)У^г .-Jsttp '•' Я*"* i**"
15'«$»
Recommendations
lava How to Program ISBN#: 0130125СГ75
C++ How to Program ISBN£ 0130895717
тш">
тш1*шт>
a1
..;'--.'**.
Рис. 2.21. Документ CookieSelectLsnguage.html для выбора языка программирования
и передачи данных сервлету CookieServlet (часть 2)
Общая методическая рекомендация 2.7
Пользователи браузеров могут запрещать cookies, поэтому Web-приложе-
ния> использующие cookies, могут работать неправильно для клиентов
с запрещенными cookies.
Сервлеты
83
Общая методическая рекомендация 2.8
По умолчанию cookies существуют только в течение текущего сеанса
просмотра (до мех пор, пока пользователь не закроет браузер). Чтобы
cookies существовали и после завершения текущего сеанса, вызовите
метод set Max Age класса Cookie для указания количество секунд до
истечения срока хранения cookie.
В строке 32 осуществляется добавление cookie в объект response с помощью
метода addCookie интерфейса HttpServlet Response, Cookie посылаются клиенту как
часть HTTP-заголовка. Информация заголовка всегда первой предоставляется
клиенту, поэтому cookies должны быть добавлены в ответ (объект response)
методом addCookie до того, как данные будут записаны в состав запроса. После
добавления cookies сервлет отправляет клиенту XHTML-документ (см. вторую копию
экрана на рис. 2.21).
Типичная ошибка программирования 2.3
Запись данных в ответ клиенту до вызова метода addCookie с целью
добавления cookies в ответ является логической ошибкой. Первыми делом
следует добавить cookies в заголовок.
XHTML-документ, посылаемый клиенту в ответ на запрос post, содержит
гиперссылку, которая приводит к вызову метода doGet (строки 73—123). Метод
прочитывает любые Cookies, которые были записаны на клиенте методом doPost. Для
каждого записанного Cookie сервлет рекомендует книгу Deitel по данной теме. На
Web-странице, создаваемой сервлетом, может быть отображено до четырех книг.
В строке 77 cookie извлекается с клиента с помощью метода getCookies
интерфейса HttpScrvletRequcst, который возвращает массив объектов Cookies. При
выполнении для вызова сервлета операций get или post, cookies, ассоциированные
с доменом этого сервера, автоматически отправляются сервлету.
Если метод getCookie ке возвращает null (что имеет место, если cookies не были
обнаружены), в строках 106-109 извлекается имя каждого Cookie с
использованием метода getName класса Cookie, значение каждого Cookie извлекается с
использованием метода getValue класса Cookie, и эти извлеченные данные записываются
клиенту в виде строки, содержащей название рекомендуемой книги и ее ISBN-код.
Общая методическая рекомендация 2.9
Обычно каждый класс сервлета обрабатывает только один тип запроса
(например, get или post, но не оба).
На рис. 2.21 представлен XHTML-документ, который пользователь загружает
для выбора языка. Когда пользователь нажимает кнопку Submit, значение
текущего выбранного переключателя посылается на сервер как часть запроса post
сервлету CookieServlet, для которого в нашем примере мы используем ссылку cookies.
Мы используем корень контекста advjhtpl для демонстрации работы сервлета,
представленного на рис. 2.20. Поместите файл CookieSelectLanguage.html в
каталог serviets, созданный ранее. Поместите файл CookieServiet.ciass в подкаталог
classes каталога WEB_INF в корне контекста advjhtpl. Затем отредактируйте
дескриптор развертывания web.xml в каталоге "\VEB_INF, чтобы включить в него
информацию, указанную в таблице на рнс. 2.22. Перезапустите Tomcat и введите
следующий URL в вашем браузере:
http://localhost:8080/advjhtpl/servlets/
CooJcieSelectLanguage.html
Выберите язык программирования и нажмите кнопку Submit на Web-странице,
чтобы вызвать сервлет.
84
Глава 2
Имеются различные методы класса Cookie для работы с его членами.
Некоторые из них представлены в таблице на рис. 2.23.
[ Элемент дескриптора
servlet элемент
|servlet-name
description
servlet-class
servlet-mapping: элемент
servlet-name
url-pattern
Значение |
Cookies |
Using cookie to maintain state information j
com.deitel .advjhtpl. servlets .CookieServlet ||
Cookies 1
/cookies |
Рис. 2.22. Информация в дескриггторе развертывания, относящаяся к серелету CookieServlet
Метод
getConuaentO
getOomain()
getMaxAge()
getName()
getPath()
getSourceO
Описание
Возвращает строку, описывающую назначение cookie (null, если
не было установлено каких-либо комментариев с помощью
метода setComment).
Возвращает строку, содержащую имя домена cookie. Тем самым
определяется, какие серверы могут получить cookie. По умолчанию
cookie посылаются серверу, который отправил cookie клиенту.
Возвращает целое число, представляющее максимальный возраст
cookie в секундак.
Возвращает строку, содержащую имя cookie, как оно бьло
установлено конструктором.
Возвращает строку, содержащую префикс URL для cookie. Cookie
могут быть «нацелены» на определенные URL, которые вкпючают ,
каталоги Web-сервера. По умолчанию cookie возвращается
сервисам, работающим в том ж каталоге, что и сервис, который
отправил cookie, или в подкаталоге этого каталога.
Возвращает булево значение, указывающее, допжен ли cookie
передаваться с использованием протокола, обеспечивающего
безопасность (true).
getvaiue{) Возвращает строку, содержащую значение cookie, как оно было
установлено методом set Value или конструктором.
getVersion()
Возвращает целое число, содержащее номер версии протокола
cookies, используемого для создания cookie. Значение, равное 0
(по умолчанию), указывает на изначальный протокол cookies,
определенный Netscape. Значение, равное 1, указывает на
текущую версию, основанную на документе RFC 2109.
setCoiranent( String ) Комментарии, описывающий назначение cookie, которое
.предоставляется пользователю браузером. (Некоторые браузеры
.дают возможг-iocib пользователю принимать каждый cookie
i индивидуально.)
setDomain ( string ) Определяет, какие серверы могут получать cookie. По умолчанию
cookie отпоавляются серверу, который послап cookie клиенту.
Домен задается в виде ".deitel.com", что указывает на
, возможность получения этого cookie всеми серверами, имена
|доменоз которые оканчивалиil» ив .deite3.com.
Сервлеты
85
Метод
setMaxAge( int )
setPath( String )
setSecure( boolean )
setValue( String )
sefcVersion( int )
Описание
Устанавливает максимальный возраст cookie в секундах.
Устанавливает префикс «целевого» URL, указывающий каталоги
на сервере, которые соответствуют сервисам, способным
принимать этот cookie.
Значение true указывает, что cookie должен посылаться только
с использованием протокола, обеспечивающего безопасность.
Задает значение cookie.
Задает протокол cookies для этого cookie.
Рис 2.23. Основные методы класса Cookie
2.7.2. Отслеживание сеанса с помощью интерфейса HttpSession
Java предоставляет расширенную поддержку отслеживания сеансов с помощью
интерфейса HttpSession API сервлетов. Чтобы продемонстрировать основные
приемы отслеживания сеансов, мы модифицировали сервлет, представленный на
рис. 2.20, чтобы использовать объекты HttpSession (рис. 2.24). Этот сервлет,
опять таки, обслуживает и запросы get, и запросы post. Документ Session-
SelectLanguage.html, представленный на рис. 2.25, содержит четыре
переключателя (С, C++, Java и VB 6) и кнопку Submit, Когда пользователь нажимает
кнопку Submit, вызывается сервлет SessionServlet с использованием метода post,
Сервлет отвечает созданием объекта типа HttpSession для клиента (или
использует существующий для этого клиента сеанс) и добавляет выбранный язык и ISBN-
код для рекомендуемой книги в объект сеанса HttpSession. Затем сервлет
отправляет клиенту XHTML-страницу. Каждый раз, когда пользователь щелкает на
кнопке Submit, в объект HttpSession добавляется пара язык /ISBN-код.
Общая методическая рекомендация 2.10
Сервлет не должен использовать переменные экземпляра для хранения
информации о состоянии клиента, поскольку клиенты, осуществляющие
доступ к этому сервлету параллельно, могут переписать совместно
используемые переменные экземпляров, Сервлеты должны хранить
информацию о состоянии клиента в объектах HttpSession.
1 // Рис. 2.24. SessionServlet.java
2 // Использование объекта HttpSession для хранения информации
о состоянии клиента.
3 package com.deitel.advjhtpl.servlets;
4
5 import javax.servlet.*;
6 import javax.servlet.http.*;
7 import j ava.io,*;
8 import java.util.*;
9
10 public class SessionServlet extends KttpServlet {
11 private final Map books = new HaghKap();
12
13 // инициализация таблицы books
14 public void init(}
lb {
16 books.put( "C", "0130895725" );
86
Глава 2
17 books.put( "C++", "0130895717" );
18 books.put( "Java", "0130125075" );
19 books.putt "VB6", "0134569555" );
20 }
21
22 // получение выбранного языка и создание объекта
23 // HttpSession, содержащего рекомендуемые клиенту книги
24 protected void doPost( HttpServletRequest request,
25 BttpServletResponse response )
26 throws ServletException, IOException
27 {
28 String languege = request.getParameter( "language" };
29
30 // Получение объекта сеанса session для пользователя.
31 // Создать сеанса (true), если он не существует.
32 HttpSession session = request.getSession< true );
33
34 // добавление значения для выбора пользователя в объект session
35 session.setAttrxbute( language, books.get( language ) );
36
37 response.setContentType( "text/html" );
38 PrintWriter out = response.getWriter();
39
40 // отправка XHTML-страницы клиенту
41
42 // начало XHTML-докукента
43 out.printlnf "<?xml version = \"1.0\"?>" );
44
45 out.println( "<!DOCTYPE html РОБЫС \"-//W3C//DTD " +
46 "XHTML 1.0 St*ict//EN\" \"fcttp://www.w3.org" +
47 "/TR/xhtmll/DTD/xhtmll-strict.dtd\">" );
46
49 out.printlnf
50 "<html xmlns = \"http://www.w3.org/1999/xhtml\,,>^, );
51
52 // заголовок документа
53 out.printlnf "<head>" );
54 out.println( "<title>Welcome to Sessions</title>" );
55 out.println( "</head>" );
56
57 // тело документа
58 out.printlnf "<body>" ) ,-
59 out.printlnf "<p>Welcome to Sessions! You selected " +
60 language + ",</p>" );
61
62 // отображение информации о сеансе
63 out.println( "<p>Your unique session ID is: " +
64 session.getld() + "<br />" };
65
66 out.println(
67 "This " + ( session.isNew() ? "is" : "is not" ) +
68 " a new session<br />" );
69
70 out.printlnf "The session was created at: " +
71 new Date( session.getCreafcionTime() ) + "<br />" } ,■
72
73 out.printlnf "You last accessed the session at: " +
74 new Date( session.getLastAccessedTime() ) + "<br />" )
75
76 out.printing "The maximum inactive interval i.s: " +
77 session.getMaxInactivelnterval{) + " seconds</p>" ) ,-
78
79 out.println( u<pXa href = " +
80 "\"servlets/SessionSelectLanguage.html\">" +
SI "Click here to choose another language</aX/p>" J ;
82
83 out.println( "<pXa href = \"sessions\">" +
84 "Click here to get book recommendations</aX/p>" ) ;
85 out.printlfi( "</body>" };
86
87 // конец XHTML-документа
88 out.println( "</htrel>M );
89 out.close(); // закрытие потока
90 1
91
92 // чтение атрибутов сеанса и создание XHTML-документа,
93 // содержащего рекомендуемые книги
94 protected void doGetf HttpServletRequest request,
95 HttpServletResponse response )
96 throws ServletException, IOException
97 {
98 // Получение объекта сеанса session для пользователя.
99 // Не создавать сеанс (false), если он не существует.
100 HttpSession session = request.getSession( false );
101
102 // получение имен значений для объекта session
103 Enumeration valueNames;
104
105 if ( session != null )
106 valueNames = session.getAtfcributeNames();
107 else
108 valueNames = null;
109
110 FrintWriter out = response.getWriter();
111 response.setContentType( "text/html" );
112
113 // начало XHTML-документа
114 out.println( "<?xml version = \"1.0\"?>" );
115
116 out.println( "<!D0CTYPE html PUBLIC \"-//W3C//DTD " +
117 "XHTML 1.0 Strict//EN\" V'http://www.w3.org" +
118 "/TR/xhtmll/DTD/xhtmll-strict.dtd\">" );
119
120 out.printlrt (
121 "<html xralns = \"http://www.w3.org/1999/xhtml\M>" );
122
123 // раздел заголовка документа
124 out.println( "<head>" );
125 out.println( "<title>Recomraendatiotis</title>" );
126 out.println( "</head>" );
127
128 // раздел тела документа
129 out.println< "<body?" );
88
Глава 2
131 if { valueNames != null fiS
132 valueNames.hasMoreElementsO ) {
133 out.println( "<nl>Reeommendations</hl>" );
134 out.println( "<p>" );
135
136 String name, value;
137
13B // получение значения для каждого имени в списке valueNames
139 while { valueNames.hasMoreElements() ) {
140 паше = valueNames.nextElement().toStringO;
141 value = session.getAttribute( name ).toString{);
142
143 out.println( name + " How to Program. " +
144 "ISBN#: " + value + "<br />" );
145 >
14 6
147 out.println( 4/p>" ) ;
148 }
149 else {
150 out.println( "<hl>No Reoommendations</hl>" );
151 out.println( "<p>You did not select a language.</p>" );
152 }
153
154 out.println( "</body>" );
155
156 // конец XHTML-документа
157 out.println( "</html>" );
158 out.close(); // закрытие потока
159 )
160 >
Рис. 2.24. Сохранение информации о состоянии с помощью объекта HttpSession
Большая часть класса SessionServlet идентична классу CookieServIet (рис. 2.20),
поэтому мы сосредоточим внимание только на его новых особенностях. Когда
пользователь выбирает язык на странице SessionServletLanguage.html (рис. 2.25) и
нажимает кнопку Submit, вызывается метод doPost (строки 24-90). В строке 28
извлекается выбранный пользователем язык language. Затем в строке 32
используется метод getSession интерфейса HttpServletRequest для получения объекта
HttpSession для клиента. Если сервер уже имеет объект HttpSession для клиента
из предыдущего запроса, метод getSession возвращает этот объект HttpSession.
В противном случае параметр true, передаваемый методу getSession, указывает,
что сервлет должен создать новый уникальный объект HttpSession для клиента,
В случае укялания параметра false метод getSession возвращает null, если объект
HttpScssion для клиента пока не существует. Применение параметра false может
помочь определить, обращался ли уже клиент к Web-приложению.
Подобно cookies, объект HttpSession может хранить пары имя/значение.
Применительно к сеансу эти пары называются атрибутами и помещаются в объект
HttpSession с помощью метода set Attribute. Б строке 35 используется метод
setAttribute для помещения названия языка программирования и
соответствующего -ISBN.кода рекомендуемой книги в объект HttpSession. Одним иа главных
преимуществ применения объектов HttpSession по сравнению с cookies является
то, что объекты HttpSession могут хранить в качестве значения атрибута любой
объект (не только строки String). Это предоставляет программистам на Java
гибкость при выборе типа информации о состоянии, которую они хотят сохранять для
Сервлеты
89
клиентов своих Web-приложений. Если атрибут с определенным именем уже
существует при вызове метода set Attribute, объект, ассоциированный с этим
атрибутом, замещается.
Общая методическая рекомендация 2.11
Пары имя/значение, добавленные в объект HttpSession с помощью метода
set Attribute, остаются доступными до тех пор, пока не закончится
текущий сеанс просмотра клиента, или пока сеанс не будет явным образом
завершен вызовом метода invalidate объекта HttpSession. Эти атрибуты
также могут быть потеряны в случае перезапуска контейнера сервлетов.
После добавления значений в объект HttpSession сервлет отправляет клиенту
XHTML-доку мент (см. вторую копию экрана на рис. 2.25). В этом примере
документ содержит различную информацию об объекте HttpSession для текущего
клиента. В строке 64 используется метод getID объекта HttpSession для получения
уникального идентификатора сеанса. В строке 67 определяется, существует ли
уже сеанс, для чего вызывается метод isNew, который возвращает true или false.
В строке 7 с помощью метода getCreatiouTime извлекается время создания сеанса.
В строке 74 с помощью метода getLastAccessedTime извлекается время
последнего обращения к сеансу. В строке 77 используется метод getMaxInactivelnterval
для получения максимального значения времени, в течение которого объект
HttpSession может оставаться неактивным, прежде чем он будет аннулирован
контейнером сервлетов.
XHTML-документ, посылаемый клиенту в ответ на запрос post, включает
гиперссылку, которая приводит к вызову метода doGet (строки 94-159). Метод
получает объект HttpSession для клиента с помощью метода getSession (строка 100).
Мы не хотим выдавать калие-либо рекомендации, если клиент не имеет объекта
HttpSession. В связи с этим при этом вызове метода getSession используется
параметр false. Таким образом, метод getSession возвращает объект HttpSession
только в том случае, если он уже существует для данного клиента.
Если метод getSession не возвращает null, в строке 106 используется метод
getAttributedames объекта HttpSession для извлечения списка (объект типа
Enumeration) имен атрибутов (т.е. имен, используемых в качестве первого
параметра метода setAttribute объекта HttpSession). Каждое имя передается в
качестве параметра методу getAttribute (строка 141) для извлечения ISBN-кода книги из
объекта HttpSession. Метод getAttribute принимает имя и возвращает ссылку
типа Object на соответствующее значение. Далее, в ответ клиенту записывается
строка, содержащая название и код ISBN рекомендуемой книги.
На рис. 2.25 представлен XHTML-документ, который пользователь загружает
для выбора языка программирования. Когда пользователь нажимает кнопку
Submit, значение выбранного в данный момент переключателя посылается на
сервер как часть запроса post сервлету SessionServlet, которому в нашем примере мы
присвоили ссылку sessions.
Мы используем корень контекста advjhtpl для демонстрации работы сервлета,
представленного на рис. 2.24. Поместите файл Sessian.SelectLanguage.h.tml в
каталог servlets, созданный ранее. Поместите файл SessionServlet.class в
подкаталог classes каталога WEB-INF в корне контекста advjhtpl. Затем отредактируйте
дескриптор развертывания web.xml в каталоге WEB-INF, чтобы включить в него
информацию, указанную в таблице на рис. 2.26. Перезапустите Tomcat и введите
следующий URL в вашем браузере:
http://loealhost:6080/advjhtpl/servlets/
SessionSelectLanguage .tttml
Выберите язык и нажмите кнопку Submit на Web-странице, чтобы вызвать сервлет.
90
Глава 2
1 <?зап1 version = "1.0"?>
2<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
3 "http://www.w3.org/TR/xhtmll/DTD/xhtmll-strict.dtd">
4
5 <!— Рис. 2.25. SessionSelectLanguage.html —>
6
7 <html xmlns = "http://www.w3.org/1999/xhtml">
8 <head>
9 <title>Using Sessions</title>
10 </head>
11
12 <body>
13 <form action = "/advjhtpl/sessions" method = "post">
14
15 <p>Select a programming language:</p>
16 <p>
17 <input type = "radio" name = "language"
18 value = "C" />C <br />
19
20 <input type = "radio" name = "language"
21 value = "C+-1-" />C++ <br />
22
23 <!-- атот переключатель выбирается по умолчания -->
24 <input type = "radio" name = "language"
25 value = "Java" checked = "checked" />Java<br />
26
27 <input type = "radio" name = "language"
28 value = "VB6" />VB 6
2? </P>
30
31 <pXinput type = "submit" value = "Submit" /X/p>
32
33 </£orm>
34 </body>
35 </html>
Рис. 2.25. Документ SessionSelectLangjage.html для выбора языка программирования
и передачи данных сервлету SessionServlet (часть 1)
[fciiMiij.jimmj.iM.iHHUJwiwiJHw^^Mg-jt?
| j*. eet'jJBw* FavbAJ»' то* Hit Г
Badt. .--''jaMSfJ ■ ' 'Stop ЦвмК^у-йм» . Sw«h fWta*
h|tp:/Jlin:atiKt:Sa80;*itvlhtolfiKsiaH ~ "дПР'
Welcome to Sessions I You selected Java
Ycur unique session ID is: tvjn6158hl
Ths is a new sessioii
The session was created at: Tue Feb 20 10:17:17 EST 2001
Ycu last accessed Ifae session at Tbe Feb 20 10-17:17 EST 2001
The maiamum inactive mtetval is. 1230 seconds
Click here to chaose another language
Cbck here to fiet book recommendations
Ш!^^^.^^^,!^!"^^!,™.
^•; Sf-.V <э ■■■.ал Ф-Y <*■-■ &\
5eenA FAVOritef
ДМ|Йи [Й Wtp:JJloalhiist:aO30;advihlpl(Mr><ttiBei5m5^eftLangLH[>g.)itml «J- j>'
VBaA" '. F$№.W v* Step Sttfresh'-Л'йв»
Select a programming language:
<"C
Г C++
f* Java
ЩаЙГ • ИИ.;; ■ ""^^ШШШШЩЬ^т*.-~!Щ
2И*
ш:аа,уагшшж^;-
^М*1^*.ЦЁЗ hfc^VfjtPcj^eiBJD^^jl^pi/swaoro jjjjJlJjjife
Welcome to Sessions! You selected Java.
Your unique session ф is: tvjn6l53hl
This is a ucw Ktsiflft
Tte siission was created ar Tue Feb 20 10:17:17 EST 2001
You last accessed the session at Tue Feb 20 10:17 17 EST 2001
The maximum inactive interval is: 1300 seconds
Click here ю choose another language
Сйдк here to get bwk if Lonniendations
'ffl;gtWta«t^eWa*v»ifrl<»vWsfiri£^^
Рис. 2.25. Д<жуьлент SessionSelectLanguage.html для выбора языка пр
и передачи данных сервлету SessionServlet (часть 2)
92
Глава 2
-4 thing SrMnons - Mrtrosoft Internet EKpkwer
iVn.n.rrthJ^I....
.j,Jfcf,^ft--.WMl"rp«™*= Togli Яф ' * *!л?,,:. '■ - .1 V* ;
|й11|^1аьир:^^Ь«^ЗП№/а^^|Ьф1^^1^^5Н1Х^ЫкйдЧ1»ае.ЬЫ^ ^><& ^
Stop,
RqFrftrf) Hart» '
3
Select a programming language.
<~C
Г-С++
f~ Java
| Stibmrt
iteip»»"
"X^rr-jg](3S2tS^r
jWcltome: to Sessions -MJciosoft internet Ев*йч*с1
HKatffc: '■ ■ ' forward. ■ -- 5top ; Jfe™b .Jg*..„ ' »■*-■-^wtitw
i'.fW'.-E* -Viet Г*Ю*>» 10* Htlp.
12»Ы5И
;«d«B |© htto:ffloc*ostie08C/4aY»*P lessons
31-«*e
"Welcome to Sessions! You selected VB6
Your unique session ID is: Evjn^lSShl
This is not anew session
The session was created at:TueEeb 20 "0 17:17 EST 2001
You last ас с e s sed the ses sion at: Tue Feb 20 10:17:17 EST 2001
The maximum inactive interval is: 1800 seconds
Click here to choose another language
Click here to eet boolLfecommendabon?
fe hta/flKah^^e^J/sessb^',". T"
I BJSriti****.
^i
ВЛИЯНИЯМИ
■<:---tDtxl
■JMJMWItjfe] http:/Jlixjtiost:SiB0f3M)tpi;sesgmi jjj ^4». i
№* ■ Bdt.' ¥e« FawiirtES Toot
U .a
Recommendations
Java How to Program. ISBN#: 0130125075
VB6 How to Program. ISBNS: 0134565555
Рис. 2.25. Документ SessionSelectLanguage.html для выбора языка программирования
и передачи данных сервлету SessionServlet (часть 3)
Сервлеты
93
Элемент дескриптор а
servlet элемент
servlet-name
Description
servlet-clas s
servlet-mapping элемент
servlet-name
url-pattern
Значение |
Sessions
Using sessions to maintain state information
com.deitel.advjhtpl.servlets.SessionServlet
Sessions
/sessions
Рис. 2.26. Информация в дескрипторе развертывания, относящаяся к сервлету
Wei com s Se rvi et 2
2.8. Многоуровневые приложения: использование
средств JDBC из сервлета
Сервлеты могут взаимодействовать с базами данных через интерфейс JDBC
(Java Database Connectivity). JPBC предоставляет для Java-программ
единообразный способ соединения с различными базами данных вне зависимости от
особенностей систем управления базами данных.
Многие современные приложения представляют собой трехуровневые
распределенные приложения, состоящие из интерфейса пользователя, бизнес-лотки
и средств доступа к базам данных. Интерфейс пользователя в таком приложении
часто создается с использованием HTML, XHTML (как в этой главе) или Dynamic
HTML. В некоторых случаях на этом уровне также используются алдлеты Java.
HTML и XHTML являются наиболее предпочтительными средствами для
интерфейса пользователя в системах, где важное значение имеет переносимость.
Поскольку HTML поддерживается всеми браузерами, разработка интерфейса
пользователя, доступ к которому осуществляется через Web-браузер, гарантирует
переносимость между всеми платформами, на которых имеются Web-браузеры.
Используя сетевые возможности, предоставляемые браузером, интерфейс
пользователя может взаимодействовать с бизнес-логикой среднего уровня. Средний
уровень может затем осуществлять доступ к базе данных для манипулирования
данными. Три уровня могут физически размещаться на отдельных компьютерах,
которые связаны через сеть.
В многоуровневой архитектуре средний уровень часто представлен
Web-сервером. На них функционирует бизнес-логика, которая манипулирует данными,
хранящимися в базах данных, и взаимодействует с клиентскими Web-браузерами.
Сервлеты через JDBC могут взаимодействовать с системами управления базами
данных. Разработчикам не нужно знать о специфике каждой из систем
управления базами данных. Вместо этого разработчики используют SQL-запросы, а
драйвер JDBC берет иа себя специфику взаимодействия с каждой из систем управления
базами данных..
Сервлет SurveyServlet, представленный на рис. 2.27, и документ,
представленный на рис. 2.28, демонстрируют трехуровневое распределенное приложение,
которое отображает интерфейс пользователя в браузере с помощью XHTML. Средним
уровнем является сервлет Java, который обрабатывает запросы браузера клиента
и предоставляет доступ к третьему уровню — базе данных Cloudscape, доступной
через JDBC. Сервлет в этом примере используется для проведения Web-опроса,
который дает возможность пользователям проголосовать за свое любимое животное.
94
Глава 2
Принимая запрос post от документа Survey.himl, сервлет обновляет общее число
голосов в базе данных, отданных за это животное, и возвращает клиенту
динамически генерируемый XHTML-документ, содержащий результаты опроса.
1 // Рис. 2.27. SurveyServlet.Java
2 // Web-опрос, в котором средства JDBC используются из сервлета.
3 package com.deitel.advjhtpl.servlets■
4
5 import java.io.*;
6 import j ava.text.*;
7 import j ava.sql.*;
8 impost javax.servlet.*;
9 import javax.servlet.http.*;
10
11 public class SurveyServlet extends HttpServlet {
12 private Connection connection;
13 private PreparedStatement updateVotes, totalVotes, results;
14
15 // настройка соединения с базой данных и подготовка операторов SQL
16 public void init( ServletConfig config )
17 throws ServletException
18 {
19 // попытка соединения с базой данных и создания операторов
PreparedStatement
20 try {'
21 Class.forName( "COM.cloudscape.core.RmiJdbcDriver" );
22 connection = DriverManager.getConnection{
23 "jdbc:rmi:jdbc:cloudscape:animalsurvey" );
24
25 // оператор PreparedStatement для добавления
26 // голоса, поданного sa определенное животное
27 updateVotes =
2 8 connection.prepareStatement(
29 "UPDATE surveyresulta SET votes = votes + 1 " +
30 "WHERE id = ?"
31 ),-
32
33 // оператор PreparedStatement для суммирования голосов
34 totalVotes =
35 connection.prepareStatement(
36 "SELECT sum( votes ) FROM survfcyresults"
37 );
3B
39 // оператор PreparedStatement для получения данных
из таблицы параметров опроса
40 results =
41 connection.prepareStatement(
42 "SELECT surveyoption, votes, id " +
43 "FROM surveyresults ORDER BY id"
44 ) ;
45 )
46
47 // для любого исключения возбуждать исключение CTnavailableException
48 // для указания, что сервлет s данный момент не доступен
4 9 catch ( Exception exception ) {
50 exception .printstaclcTrace () ;
Сервлеты
95
51 throw new UnavailableException( exception,gatMessage() );
52 }
53
54 } // конец метода init
55
56 // обработка ответа
57 protected void doPost{ HttpServletRequest request,
53 HttpServletResponse response )
59 throws ServletException, IOSxception
60 {
61 // настройка ответа., отправляемого клиенту
62 response.setContentType( "text/html" );
63 PrintWriter out = response.getWriter()•
64 DecimalFormat twoDigita = new DecimalFormat( "0.00" );
65
66 // начало XHTML-документа
67 out.println( "<?xml version = 4"1,0\"?>" );
68
69 out.println( "<!DOCTYPE html PUBIIC \"-//W3C//DTD " +
70 "XHTML 1.0 Strict//EK\" V'http://www.w3.org" +
71 "/TR/xhtaill/DTD/xlitmll-strict.dtdV'>" );
72
73 out.println(
74 "<html xmlns = V'http://www.w3.org/1999/xhtml\">" );
75
76 // заголовок документа
77 out.printlnt "<head>" ) ,-
7B
79 // чтение текущего ответа на опрос
SO int value =
81 Integer.parselnt( request.getfarameter( "animal" ) );
82
83 // попытка обработать голос и выдать текущий результат опроса
84 try {
85
86 // обновление суммы голосов для текущего ответа на опрос
87 updateVotes.setInt{ 1, value );
88 updateVotes.executeUpdate();
89
90 // получение сумны всех ответов на опрос
91 ResultSet totalRS = totalVotes.executeQuery();
92 totalRS.nextO :
93 int total = totalRS.getlnt( 1 );
94
95 // получение результатов
96 ResultSet jresultsRS = results.executeQuery();
97 out.println( "<title>Thank you»</title>" );
98 out.println( "</nead>" );
99
100 out.println( "<body>" );
101 out.printlnt "<p>Thank you for participating." );■
102 out.printlnt "<br />Results:</pXpre>" );
103
104 // обработка результатов
105 iiit votes;
106
96
Глава 2
107 while ( resultsRS.next() ) {
108 out.print( resultsRS.. getString( 1 ) );
109 out.print( ": " );
120 votes = resultsRS.getint( 2 ) ;
111 out.print( twoDigits.format(
112 ( double ) votes / total * 100 ) );
113 out.print( "% responses: " );
114 out.println( votes );
115 }
116
117 resultsRS.close {) ,"
118
119 out.print( "Total responses: " );
120 out.print( total );
121
122 // конец XHTML-документа
123 Out.println( "</preX/body></html>" );
124 out.close();
125 )
126
127 // если возникает исключение при обращении к баэе данных.
Возвратить страницу, указывающую на ошибку
128 catch { SQLException sqlException ) {
129 sqlException.printStackTracef);
130 Out.println( "<title>Error</title>" );
131 Out.println( "</head>" );
132 out.println{ "<bodyXp>Database error occurred, " );
133 out.println! "Try again later. </px/bodyX/html>" );
134 out.close() ;
135 ]
136
137 } // конец метода doPost
138
139 // закрытие операторов SQL и базы данных по завершении работы
серилета
140 public void destroy()
141 {
142 // попытка закрыть операторы и соединение с базой данных
143 try {
144 updateVotes.close();
145 totalVotes.close{);
146 results.close();
147 connection.close(};
148 }
149
150 // обработка исключения при работе с базой данных,
выдача сообщения об ошибке клиенту
151 catch( SQLException sqlException ) {
152 sqlException.printStackTrace',) ;
153 }
154 } // конец метода destroy
155 }
Рис. 2.27. Многоуровневое Web-приложение, использующее XHTML, сервлеты и JDBC
Сервлеты
97
В этом примере используется программный продукт Cloudscape. Полную
информацию по системе управления базами данных Cloudscape можно найти на
сайте www.cloudscape.com. Следуйте предоставленным инструкциям, чтобы
установить сервер Cloudscape.
Чтобы запустить сервер Cloudscape, откройте командное окно. Перейдите к
каталогу, в котором установлен Cloudscape (Cloudscape_3.6 по умолчанию). В этом
каталоге имеется подкаталог frameworks. В Cloudscape имеется два режима
выполнения: embedded и RmiJdbc. Режим embedded дает возможность выполнять
Cloudscape как часть приложения Java. Режим RmiJdbc дает возможность
выполнять Cloudscape как автономный сервер баз данных. Именно этот режим будет
использоваться в данном случае, В каталоге каждого режима выполнения имеется
подкаталог bin, содержащий командные файлы (Windows) и командные сценарии
(Linux/Unix) для установки переменных окружения и выполнения Cloudscape.
Перейдите в подкаталог bin в режиме RmiJdbc. Выполните командный файл или
сценарий, начинающийся с setServerCloudscapeCP, чтобы установить переменные
окружения, необходимые серверу. Затем выполните командный файл или
сценарий, начинающийся с startCS, чтобы запустить сервер баз данных Cloudscape.
Сервер можно остановить, выполнив сценарий stopCS из другого командного
окна.
В каталоге примеров для этой главы содержится SQL-сценарий animalsur-
vey.sql, который вы можете использовать для создания базы данных animal-
survey, применяемой в этом примере. Чтобы создать базу данных animalsurvey,
сначала убедитесь, что сервер Cloudscape запущен. Откройте новое окно
командной строки, а затем перейдите к каталогу frameworks/RmiJdbc/Ьш Cloudscape.
В этом каталоге выполните командный файл или сценарий, начинающийся
с setClientCloudscapeCP, для установки переменных окружения, необходимых
сценарию создания базы данных createDatabase. Далее, перейдите к каталогу на
вашем компьютере, в который содержатся файлы примеров для этой главы, и
введите
createDatabase animalsurvey.sql
чтобы выполнить SQL-сценарий. База данных будет создана. [Замечание. Мы
написали этот сценарий, чтобы вы могли в любое время снова его выполнить и
восстановить исходное содержимое базы данных.]
В строках 12 и 13 объявляется ссылка Connection для управления соединением
с базой данных и три ссылки PreparedStatement для обновления числа голосов,
поданных за животное, суммирования всех голосов и получения окончательных
результатов опроса.
Сервлеты инициализируются замещением метода init (строки 16-54). Метод
init вызывается только один раз в течение срока жизни сервлета перед принятием
каких-либо клиентских запросов. Метод init принимает параметр servletConfig
и возбуждает исключение ServletException. Параметр предоставляет сервлету
информацию о параметрах инициализации (т.е. параметрах, не ассоциированных
с запросом, но передаваемых сервлету для инициализации переменных сервлета).
Эти параметры задаются в дескрипторе развертывания web.xml как часть
элемента servlet. Каждый из параметров присутствует в элементе init-param, имеющем
следующий вид:
<init-param>
<param-name> здесь указывается имя параметра </param-name>
<param-value> здесь указывается значение параметра </param-valu6>
</ini t-param>
98
Глава 2
Сервлеты могут получать значения параметров инициализации, вызывая метод
getlnitParameter класса ServletConfig, который принимает строку,
представляющую собой имя параметра.
В этом примере метод init сервлета (строки 16-54) осуществляет соединение
с базой данных Cloudscape. В строке 21 загружается драйвер (COM.cloudscape.co-
re.RmiJdbcDriver). В строках 22-23 осуществляется попытка открыть соединение
с базой данных animalsuxvey. База данных содержит одну таблицу (surveyresults),
которая состоит из трех полей: уникального целого числа с именем id,
идентифицирующего каждую запись; строки с именем surveyoption, представляющей собой
выбранный вариант ответа; и целого числа с именем votes, представляющего собой
количество голосов, отданных за данный вариант ответа.
В строках 27-44 создаются подготовленные операторы (объекты типа Prepared-
Statements) с именами updateVotcs, total Votes и results. Оператор updateVotes
добавляет единицу к значению votes записи базы данных с заданным
идентификатором. Оператор totalVotes использует встроенную функцию sum SQL для
суммирования всех голосов votes в таблице surveyresults. Оператор results возвращает
все данные из таблицы surveyresnlts.
Когда пользователь отправляет ответ на опрос, метод doPost (строки 57-137)
обрабатывает запрос. В сроках 80-81 осуществляется получение ответа на опрос,
после чего блок try (строки 84-125) пытается обработать ответ. В строках 87-88
ответ на опрос задается в качестве первого параметра подготовленного оператора
(объект типа PreparedStatement) updateVotes, а затем выполняется обновление
базы данных. В строках 91-93 выполняется подготовленный оператор totalVotes
для извлечения общего количества полученных голосов. Далее, в строках 96-123
выполняется подготовленный оператор results и обрабатывается результирующее
множество Resultset для создания страницы с итогами опроса, отправляемой
клиенту. Когда контейнер сервлетов завершает выполнение сервлета, метод destroy
(строки 140—154) закрывает каждый из подготовленных операторов
PreparedStatement, а затем закрывает соединение с базой данных. На рис. 2.28 представлен
документ survey.html, который вызывает сервлет SurveyServlet через псевдоним
animalSurvey, когда пользователь отправляет заполненную им форму опроса.
1 <?xml version = "1.0"?>
2<!D0CTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
3 "http://www.w3.org/TR/xhtnai/DTD/xhtmll-strict.(itd">
4
5<!-- Рис. 2.28. Survey.html -->
6
1 <html xmlns = "http://www.w3.org/1999/xhtrol">
8 <head>
9 <title>Survey</title>
10 </head>
11
12 <body>
13 <form method = "post" action = "/advjhtpl/aninialsurvey">
14
15 <p>What is your favorite pet?</p>
16
17 <p>
IB <input type = "radio" name = "animal"
19 value = "1" />Dog<br />
20 <input type = "radio" name = "animal"
21 value = "2" />cat<br />
22 <inp\it type = "radio" name = "animal"
Сервлеты
99
23
24
25
26
27
28
29
30
31
32 </fonn>
33 </body>
34 </html>
value = "3" />Bird<br />
<input type = "radio" name = "animal"
value = "4" />Snake<br />
<input type = "radio" name = "animal"
value = "5" checked = "checked" />None
</p>
<pXinput type = "submit" value = "Submit" /X/p>
шжеевзя
. r^fajffijftj
«Э,
imp.-
.'St:
.t3> i $
НИЩА kWJVX<lfrtt5t:B08W»№pWi«<^sfojy<;y.btri 3.^^
■\Ubat is your favorite pet'
ffDog
I" Cat
<"Bjrd
С Snake
<* None
g^-'lf»'.
wis
si
SF**1
Г5Щ5Г
„|ИИ|Я|,.,,Я,
jtddim |f j -ittp:>ftoca*№t:aoaojjdvjnpijarirrMfejfffy
^ШШ
'ttiank you for participating
Results:
Dog: 57,394 responses: 22
Слс: 23-Ё84 responses: 9
Bxcd: Ш.53% responses: 4
SnaKe: £.2 5* responsesi 2
None: 2.63% responses: 1
Total responses: 3S
©Dor?7
"Г^Г"Ш.Км( «ten*"
11
Рис. 2.28. Документ Survey.html, который дает возможность пользователям отправлять
ответы на Web-опрос сервлету SurveyServlet
Мы используем наш корень контекста advjhtpl для демонстрации сервлета,
представленного на рис, 2,27. Поместите файл Survey.html в каталог servlets,
созданный ранее. Поместите файл SurveyServlet.class в подкаталог classes каталога
WEB-INF в корне контекста advjhtpl. Затем отредактируйте дескриптор
развертывания web.xml в каталоге WEB-INF, чтобы включить в него информацию,
указанную в таблице ка рис. 2.29. Кроме того, эта программа не сможет быть выпол-
100
Глава 2
нена в Tomcat, если Web-приложение не будет осведомлено о местоположении
JAR-файлов cloudscape.jar и RmiJdbc.jar, которые содержат драйвер базы данных
Cloudscape и его файлы поддержки. Файл cloudscape.jar расположен в каталоге lib
Cloudscape. Файл RmiJdbc.jar расположен в каталоге frameworks\RmiJdbc\clas-
ses Cloudscape. Поместите копии этих файлов в подкаталог lib каталога WEB-INF,
чтобы сделать их доступными для Web-приложения.
Элемент дескриптора
ssrvlet элемент
servlet-name
description
servlet-class
servlet-mapping элемент
secvlet-name
url-pattern
Значение
Animalsurvey
Connecting to a database from a servlet
com.deitel.advjhtpl.servlets.SurveyServlet
animalsurvey
/animalsurvey
Рис. 2.29. Информация в дескрипторе развертывания, относящаяся к сервлету
SurveyServlet
Копии этих файлов должна быть помещена в подкаталог lib каталога WEB-INF
корня контекста advjhtpl. Скопировав эти файлы, перезапустите Tomcat и
введите следующий URL в Web-браузере:
http://localhost:8080/advjhtpl/servlets/Survey.html
Выберите вариант ответа и нажмите кнопку Submit на Web-странице, чтобы
вызвать сервлет.
2.9. Класс HttpUtils
Класс HttpUtils предоставляет три статических метода для упрощения
программирования сервлетов. Эти методы описаны в таблице на рис. 2.30.
Метод
Ge tReque s tURL
ParsePostData
ParseQueryString
Описание
Этот метод принимает в качестве параметра объект
HttpServletRequest и возвращает буфер StringBuffer, содержащий
URL ресурса, инициировавшего запрос.
Этот метод принимает в качестве параметра целое число и объект
ServletlnputStream. Целое число представляет количество байт
в потоке ввода ServletlnputStream. Объект Servlet In put Stream
содержит пары ключ/значение, передаваемые сервлету (с помощью
метода post) из формы (элемент form). Метод возвращает хэш
Hashtable. содержащий пары ключ/значение.
Этот метод принимает в качестве параметра строку (String),
содержащую строку запроса в запросе get, и возвращает хэш
Hashtable, содержащий пары ключ/значение в строке ззпроса.
Значением каждого ключа является строковый массив. Сiрока запроса
может быть получена с помощью метода getQueryString интерфейса
HttpServletRequest
Рис. 2.30. Методы класса HttpUtils
Сервлеты
101
2.10. Ресурсы в Internet и во Всемирной паутине
В этом разделе приведено множество ресурсов в Internet, имеющих отношение
к сервлетам, и дано краткое описание каждого из них.
Java.sun,com/products/servlet/index.html
Страница сервлетов Web-сайта Java корпорации Sun Microsystems, Inc. предоставляет
последнюю информацию, относящуюся к сервлетам, и список ресурсов по сервлетам.
Jakarta.apache.org
Это осноапая страница проекта Apache Project, посвященная Jakarta Project Tomcat —
эталонной реализации сервлетов и JavaServer Pages — одному из многих подчиненных
проектов проекта Jakarta Project,
j akarta.apache.org/tomcat/index.html
Основная страница эталонной реализации сервлетов Tomcat и серверных страниц
JavaServer Pages,
j 3v3.apache.org
Это основная страница проекта Apache Project для всех технологий, связанных с Java.
Этот сайт предоставляет доступ ко многим пакетам Java, полезным для разработчиков
сервлетов и JSP.
www.servlets.com
Это Web-сайт книги Java Servlet Programming (Программирование сервлетов Java)
издательства Q'Reilly. Книга предоставляет множество разнообразных ресурсов. Она
является прекрасным источником информации для программистов, изучающих
технологию сервлетов.
theserverside.com
Сайт TheServerSide.com содержит информацию и ресурсы по Enterprise Java.
www-servletSource.com
Сайт ServletSource.com — ресурс по сервлетам, содержащий примеры кода, советы,
учебный материал и ссылки на многие другие Web-сайты с информацией по сервлетам.
www■cookiecentral.com
Хороший всеобъемлющий ресурс по cookies.
developer.netscape.com/docs/manuals/communicator/jsguide4/cookies.htm
Описание технологии cookies, предоставленное Netscape.
www.j avacorporate.com
Основная страница для инфраструктуры с открытым кодом ExpressoFramework,
которая содержит библиотеку расширяемых компонентов сервлетов, способствующих
ускорению разработки Web-приложений,
www.servlet.com/srvdev.jhtml
Форум Servlet Developers Forum предоставляет ресурсы разработчикам серверных
приложений на Java и информацию о Web-серверах, поддерживающих технологии
сервлетов.
www.servletforura.com
Сайт ServletForum.com — это группа новостей, в которой можно задавать вопросы и
получать ответы на них от других участников группы.
www.coolservlets.com
Этот сайт бесплатно предоставляет сервлеты Java с открытым исходным кодом.
www.cetus-links.org/oo_java_servleta.html
Предоставляет список ссылок на ресурсы по сервлетам и другим технологиям.
www.javaskyline.com
Java Skyline — это сетевой журнал для разработчиков сервлетов.
102
Глава 2
www.гfc-editor.org
Сайт RFC Editor предоставляет поисковый сервер для нахождения документов RFC).
Многие из RFC предоставляют подробное описание технологий Web, Интерес для
разработчиков сервлетов могут представлять следующие документы RFC: URIs (RFC
1630), URLs (RFC 1738), Relative URLs (RFC 1808), HTTP/1.0 (RFC 1945), MIME (RFC
2045-2049), HTTP State Management Mechanism (RFC 2109), Use and Interpretation of
HTTP Version Numbers (RFC 2145), Hypertext Coffee Pot Control Protocol (RFC 2324),
HTTP/1.1 (RFC 2615) и HTTP Authentication: Basic and Digest Authentication (RFC
2617).
www.irvine.com/-mime
Сайт часто задаваемых вопросов (FAQ) no Multipurpose Internet Mail Extensions
предоставляет информацию о стандарте MIME, список зарегистрированных MIME-типов,
а также ссылки на другие ресурсы по МШЕ.
Резюме
• Классы и интерфейсы, используемые в серзлетах, содержатся в пакетах javax.servlet
и javax.servlet.bttp.
• В Internet испольяуетг.я множество протоколов. Протокол HTTP (Hypertext Transfer
Protocol), который составляет основу Всемирной паутины, использует унифицированные
идентификаторы ресурсов URI (Uniform Resource Identifiers) для определения
местоположения ресурсов в Internet.
• URL как правило указывает на файлы или каталоги, но могут являться указателями на
приложения, решающие сложные задачи, например, поиск в базах данных и в Internet.
• Технология JavaServer Pages является расширением технологии сервлетов.
• Сервлеты обычно выполняются под управлением Web-сервера (также называемого
контейнером сервлетов).
• Сервлеты и серверные страницы JavaServer Pages стали настолько популярными, что
в настоящее время поддерживаются большинством Web-серверов и серверов приложений.
■ Все сервлеты должны реалиэовывать интерфейс Servlet. Методы интерфейса Servlet
автоматически вызываются контейнером сервлетов.
• Жизненный цикл сервлета начинается, когда контейнер сервлетов загружает сервлет
в память — обычно в ответ на первый запрос к этому сервлету. Перед тем, как сервлет
сможет обработать первый запрос, контейнер сервлетов вызывает метод init сервлета. По
завершении выполнения метода init сервлет может ответить на первый поступивший
запрос. Все запросы обрабатываются методом service сервлета, который может многократно
вызываться в течение жизненного цикла сервлета. Когда контейнер сервлета завершает
выполнение сервлета, вызывается метод destroy для освобождения ресурсов,
используемых сервлетом.
• Пакеты сервлетов определяют два абстрактных класса, которые реализуют интерфейс
Servlet: класс GenericServlet и класс HttpServlet. Большинство сервлетов расширяют
один их этих классов и замещают некоторые, или все их методы, чтобы получить
соответствующее поведение.
• Ключевым методом каждого сервлета является метод service, который принимает объект
Servlet Request и объект ServletResponse. Эти объекты предоставляют доступ к потокам
ввода и вывода, которые дают возможность сервлету прочитывать данные с клиента и
отправлять данные клиенту.
• Сервлеты для использования в Web обычно расширяют класс HttpServlet. Класс
HttpServlet замещает метод service, чтобы иметь возможность различать типы запросов,
получаемых от клиентского Web-браузера. К двум наиболее типичным HTTP-запросам (их
также называют методами запроса) относятся get и post.
• Класс HttpServlet определяет методы doGet и doPost для реакции, соответственно, на
запросы get и post, поступающие от клиента. Вызов этих методов осуществляется методом
service класса HttpServlet, который, в свою очередь, вызываются при поступлении
запроса на сервер.
• Методы doGet и doPost принимают в качестве параметров объект ilttpServletRequest
и объект HttpServletResponse, которые позволяют осуществлять взаимодействие между
клиентом и сервером.
Сервлеты
103
• Ответ посылается клиенту с помощью объекта PrintWriter, возвращаемого методом
getWriter объекта HttpServletResponse.
• Метод setContentType объекта HttpServletResponse задает MIME-тип ответа,
посылаемого клиенту. Это дает возможность браузеру пользователя распознавать и должным
образом обрабатывать содержимое.
• Имя localhost сервера (IP-адрес 127.0.0.1) представляет собой общеизвестное имя
локального компьютера. Это имя может быть использовано для тестирования прилоя{ешш
TCP/IP на локальном компьютере.
• Сервер Tomcat ожидает запросы от клиентов на порту 8080. Этот номер порта должен
быть задан как часть URL при запросе сервлета, выполняющегося на Tomcat.
• Клиент может осуществлять доступ к сервлету только в том случае, если сервлет установлен
на сервере, который реагирует на запросы, обращенные к сервлетам. Для Web-серверов,
поддерживающих сервлеты, обычно предусматривается процедура установки сералетоа.
• Tomcat представляет собой полнофункциональиую реализацию стандартов JSP и сервле-
тов. В состав Tomcat входит Web-сервер, поэтому его можно использовать как
автономный контейнер для тестирования JSP-страниц и сервлетов.
• Tomcat может быть принят в качестве обработчика запросов на JSP-страницы и еералеты,
принимаемых популярными Web-серверами, такими как Apache и IIS. Tomcat также
интегрирован в эталонную реализацию Java 2 Enterprise Edition от Sun Microsystems.
• JSP-страницы, сервлеты и их файлы поддержки развертываются как часть
Web-приложений. В Tomcat Web-приложения развертываются в подкаталоги webapps Tomcat.
• Web-приложение представляет собой структуру каталогов, в которой размещаются все
файлы, относящиеся к приложению. Эта структура каталогов может быть задана
администратором сервера Tomcat в каталоге webapps, или же вся структура каталогов может
быть заархивирована в файл архива Web-приложения. Такой архив известен как WAR-
файл и имеет расширение .war.
• Если WAR-файл помещен в каталог vfebapps, сервер Tomcat при запуске извлекает
содержимое WAR-файла в соответствующую структуру подкаталогов webapps.
• Структура каталогов Web-приложения состоит из корня контекста — каталога верхнего
уровня для всего Web-приложения — и нескольких подкаталогов. Корень контекста
является корневым каталогом для Web-приложения. Все JSP-страницы, HTML-документы,
сервлеты и вспомогательные файлы, такие как изображения и файлы классов,
размещаются а этом каталоге или в его подкаталогах. Каталог WEB-INF содержит дескриптор
развертывания Web-приложения (web.xml). Каталог WЕВ-INF/classes содержит файлы
классов сервлета и другие файлы классов поддержки, используемые а Web-приложении.
Каталог WEB-INF/ИЬ содержит файлы архивов Java (JAR), которые могут включать
файлы классов сервлета и другие файлы классов поддержки, используемые в
Web-приложении.
• Прежде, чем осуществить развертывание Web-приложения, контейнер сервлетов должен
определить корень контекста Web-приложения. В Tomcat для этого достаточно просто
поместить каталог в подкаталог webapps. Tomcat использует имя каталога в качестве имени
контекста.
• Развертывание Web-приложения требует создания дескриптора развертывания (web.xml).
• HTTP-запросы get могут непосредственно вводиться в поле Address или Location браузера.
• Параметры передаются в запросе get в виде пар имя/значение. Знак ? отделяет URL от
данных, передаваемых как часть запроса get. В передаваемых парах имя/значение имя
отделено от значения символом =. Если имеется несколько пар имя/значение, каждая
пара отделяется от другой символом &.
• Метод getParameter интерфейса HttpServletRequest принимает в качестве параметра
имя параметра и возвращает соответствующее строковое значение или null, если
параметр не является частью запроса.
• НТТР-эатгрос post часто используется для передачи данных из формы на Web-странице
серверному обработчику формы, который обрабатывает данные.
• Браузеры часто кэшируют (сохраняют на диске) Web-страницы, чтобы иметь
возможность быстро перезагружать страницы. Браузеры не кэшируют ответ сервера на запрос
post.
• Метод doPost принимает те же даа параметра, что и метод doGet, — объект, который
реализует интерфейс HttpServletRequest для представления клиентского запроса и объект,
который реализует интерфейс HttpServletResponse для представления ответа сервера.
• Метод setRedirect интерфейса HttpServletResponse переадресовывает запрос ресурсу
с указанным URL.
104
Глава 2
• Если сервлет использует относительный путь для ссылки на другой статический или
динамический ресурс, сервлет предполагает использование этого же корневого каталога
контекста, если только для ресурса не задан абсолютный URL.
• После выполнения метода sendRedirect обработка запроса сервлетом, который вызвал
метод sendRedirect, завершается.
• При переадресации запросов параметры из первоначального запроса передаются в
качестве параметров новому запросу. Могут также передаваться дополнительные параметры
запроса,
• Новые параметры добавляются к имеющимся параметрам запроса. Если новый параметр
имеет то же имя, что и существующий параметр, значение нового параметра имеет
приоритет над первоначальным значением. Тем не менее, передаются все значения.
• Полный набор значений для параметра запроса с заданным именем может быть получен
путем вызова метода getParameterValnes интерфейса HttpServletRequest, который
принимает а качестве аргумента имя параметра и возвращает массив строк, содержащий
значения параметра в порядке, начиная от последнего добавленного значения для этого
параметра к первому добавленному значению.
• Многие современные Web-сайты предоставляют настраиваемые Web-страницы и/или
различные функциональные возможности для различных клиентов.
• HTTP является протоколом без сохранения состонния — он не поддерживает сохранение
информации, которая может помочь Web-серверу определить, какой запрос поступил от
какого клиента.
• Cookies могут хранить информацию на компьютере пользователя для последующего ее
извлечения в этом или в будущих сеансах просмотра.
• Cookies представляют собой текстовые дачные, посылаемые еервлетами (или другими
серверными технологиями) как часть ответов клиентам.
• Каждое HTTP-взаимодействие между клиентом и сервером содержит заголозок,
включающий информацию о запросе (при направлении взаимодействия от клиента к серверу)
или информацию об ответе (при направлении взаимодействия от сервера к клиенту).
• Когда сервер получает запрос, заголовок содержит информацию, такую, как метод
запроса (например, get или post) или cookies, сохраненную на клиентском компьютере
сервером.
• Когда сервер формирует свой ответ, заголовок содержит любые cookies, которые сервер
хотел бы сохранить на компьютере клиента, а также другую информацию, например,
МШЕ-тип ответа.
• В занисимости от максимального возраста cookie, Web-браузер либо хранит cookie в
течение сеанса просмотра, либо сохраняет cookie на компьютере клиента для последующего
использования. Когда браузер запрашивает ресурс с сервера, cookies, ранее отправленные
клиенту этим сервером, возвращаются серверу как часть запроса, сформированного
браузером. Cookies автоматически удаляются по истечении их срока хранения.
• По умолчанию cookies существуют только в течение текущего сеанса просмотра (до тех
пор, пока пользователь не закроет браузер). Чтобы cookie сохранялся и после текущего
сеанса, следует вызвать метод setMaxAge класса Cookie и указать количество секунд до
завершения срока существования cookie.
• Метод getCookies интерфейса HttpServletRequest возвращает массив объектов Cookie.
Метод getCookies возвращает nnll, если cookie в запросе отсутствует,
• Метод getName класса Cookie изалекает имя cookie. Метод getValne класса Cookie
извлекает значение cookie.
• Java предоставляет альтернативу применению cookie для отслеживания информации о
сеансе. Эта возможность реализуется с помощью интерфейса HttpSession и позволяет
избежать проблем, связанных с запрещением cookies пользователями, делая механизм
отслеживания сеанса прозрачным для программиста.
• Метод getSession интерфейса HttpServletRequest получает объект HttpSession для
клиента.
• Подобно cookies, объект HttpSession может хранить пары имя/значение. Применительно
к сеансам они называются атрибутами, сохраняются методом setAttribnte и извлекаются
методом getAttribute.
• Пары имя/значение, добавленные в объект HttpSession с помощью метода setAttribute,
остаются доступными до завершения текущего сеанса просмотра или до тех пор, пока сеанс
не будет явным образом уничтожен путем: вызова метода invalidate объекта HttpSession.
• Метод getID класса HttpSession получает уникальный идентификатор сеанса.
Сервлеты
105
• Метод isNew класса HttpSession определяет, является ли сеанс новым, или же он уже
существует. Метод getCreationTime получает время создания сеанса.
• Метод getLastAccessedTime получает время последнего обращения к сеансу.
• Метод getMaxInactivelnterval класса HttpSession получает максимальную длительность
неактивности объекта HttpSession до того, как контейнер сервлета его уничтожит.
• Многие современные приложения являются трехуровневыми и состоят из интерфейса
пользователя, бизнес-логики и средств доступа к базам данных.
• В многоуровневых архитектурах средний уровень часто функционирует на Web-серверах.
Они обеспечивают бизнес-логик у, которая манипулирует данными, хранящимися в базах
данных, и взаимодействует с клиентскими Web-браузерами.
• Метод init интерфейса Servlet принимает яргумент ServletConfig и возбуждает
исключение ServletException. Аргумент предоставляет сервлету информацию о параметрах
инициализации, которые задаются н элементе servlet дескриптора развертывания. Каждый
параметр инициализации присутствует в элементе init-param с дочерними элементами
рагаш-name и рагаш-value.
Терминология
addCookie, метод интерфейса
HttpServletResponse
Apache Tomcat, сервер
cache a Web-page — кэширование
Web-страницы
commit a response — завершение ответа
context root — корневой каталог контекста
Cookie, класс
delete, метод запроса
deploy a Web application — развертывание
Web-приложения
deployment descriptor — дескриптор
развертывания
destroy, метод интерфейса Servlet
doGet, метод интерфейса HttpServlet
doPost, метод интерфейса HttpServlet
GenericServIet, класс из пакета
javax.servlet
get," метод запроса
getAttribute, метод интерфейса HttpSession
getAttribnteNames, метод интерфейса
HttpSession
getCookies, метод интерфейса
HttpServletRequest
getCreationTime, метод интерфейса
HttpSession
getlD, метод интерфейса HttpSession
getLastAccessedTime, метод интерфейса
HttpSession
getMaxInactivelnterval, метод интерфейса
HttpSession
getName, метод класса Cookie
getOutputStream, метод интерфейса
HttpServletKesponse
getParameter, метод интерфейса
HttpServletReqnest
getParameterNames, метод интерфейса
HttpServletReqnest
getParameterValues, метод интерфейса
HttpServletRequest
getServletConfig, метод интерфейса Servlet
getServIetlnfo, метод интерфейса Servlet
get Session, метод интерфейса
HttpServletRequest
get Value, метод класса Gookie
get Writer, метод интерфейса
HttpServletResponse
host name — имя хоста
HTTP (Hypertext Transfer Protocol), протокол
HTTP header — заголовок HTTP
HTTP request — НТТР-запрос
HttpServlet, интерфейс
HttpServletRequest, интерфейс
HttpServletResponse, интерфейс
HttpSession, интерфейс
init, метод интерфейса Servlet
initialization parameter — параметр
инициализации
invalidate, метод интерфейса HttpSession
isNew, метод интерфейса HttpSession
Jakarta, проект
JAVA_HQME, аеременкая окружения
javax.servlet, пакет
javax.servlet.http, пакет
Jigsaw, Web-сервер
localhost (127.0.0.1)
maximum age of cookie — максимальный
возраст cookie
MIME type — MIME-тип
options, тип запроса,
path, атрибут
port — порт
post, запроса
put, метод запроса
redirect a request — переадресация запроса
request method — метод запроса
request parameter — параметр запроса
sendRedireet, метод интерфейса
Ht t p S ervletRespo use
service, метод интерфейса Servlet
servlet — сервлет
servlet container — контейнер сервлетов
106
Глава 2
Servlet, интерфейс thin client — тонкий клиент
servlet life cycle — жизневвый цикл сервлета ТОМСАТ_НОМЕ, геремеиная окружения
servlet mapping —карта соответствий для trace, запрос
сервлета URL pattern — шаблон URL
ServletConfig, интерфейс WAR (Web application archive) file — файл
ServletContext, интерфейс архива Web-приложения
ServletException, класс Web application — Web-приложение
ServletOutpntStream, класс Web application deployment descriptor
ServletRequest, интерфейс (web.xml) — дескриптор развертывания
ServletResponse, интерфейс Web-приложения
session tracking — отслеживание сеанса webapps, каталог
setAttribate, метод интерфейса HttpSession W~EB_INF, каталог
setContentType, метод интерфейса WEB_INF/classes, каталог
HttpServletResponse WEB_INF/lib, каталог
shopping cart — магазинная тележка well-known port number — общеизвестный
text/html, MIME-тип номер порта
Упражнения для самоконтроля
2.1. Заполните пропуски в следующих высказываниях:
а) Классы HttpServlet и GenericServlet реализуют интерфейс ■
b) Класс HttpServlet определяет методы и для реакции на
запросы get и post, поступающие от клиента.
c) Метод интерфейса HttpServletResponse получает символьный поток
вывода, который дает возможность отправлять текстовые данные клиенту.
d) Атрибут __ элемента form определяет серверный обработчик формы, т.е.
программу, которая обрабатывает запрос.
e) представляет собой имя, которое относится к локальному компьютеру.
f) Метод класса Cookie возвращает строку, содержащую имя cookie, как
оно было установлено конструктором .
g) Метод getSession интерфейса HttpServletRequest возвращает клиенту объект
2.2. Ответьте, является ли каждое из следующих высказываний истиняым или ложным.
Если высказывание ложно, объясните, почему.
a) Сервлеты обычно используются в сетевом приложении на стороне клиента.
b) Методы сервлета выполняются автоматически.
c) Двумя основными HTTP-запросами являются запросы get и put.
<i) Общеизвестным номером порта для Web-запросов является 55.
e) Cookies имеют неограниченное время действия.
f) Срок действия объектов HttpSession закапчивается только при завершении сеанса
просмотра или при вызове метода invalidate.
g) Метод getAttribute интерфейса HttpSession возвращает объект, ассоциированный
с определенным именем.
Ответь/ на упражнения для самоконтроля
2.1. a) Servlet. b) doGet, doPost. с) getWriter. d) action, e) localhost f) getName, setName.
g) HttpSession.
2.2. а) Ложно. Сервлеты обычно используются на стороне сервера.
b) Истинно.
c) Ложно. Двумя основными типами HTTP-запросов являются запросы get и post.
d) Ложно. Общеизвестным номером порта для Web-запросов является 80.
e) Ложно- Срок действия Cookies истекает только по достижении ими максимального
возраста.
f) Истинно.
g) Истинно.
Сервлеты
107
Упражнения
2.3. Модифицируйте пример Cookie, представленный на рис. 2.20, чтобы для каждой
рекомендованной книги указывалась ее цена. Помимо этого, дайте пользователю
возможность выбирать некоторые или все рекомендованные книги и заказывать их.
Разверните ваше Web-приложение на сервере Tomcat.
2.4. Модифицируйте нример HttpSessktn, представленный на рис, 2.24, чтобы для каждой
рекомендованной книги указывалась ее цена. Помимо этого, дайте пользователю
возможность выбирать некоторые или все рекомендованные книги и заказывать их.
Разверните ваше Web-приложен не на сервере Tomcat.
2.5. Создайте Web-приложение для реализации динамических FAQ (часто задаваемых
вопросов). Приложение должно получать информацию для создания динамических
Web-стравиц FAQ из базы данных, которая состоит из таблиц Topics и FAQ. Таблица
Topics должна иметь два поля: уникальный целочисленный идентификатор для
каждой темы (topicED) и название темы (topicName). Таблица FAQ должна иметь три
поля: topi с ID (внешний ключ), строку, представляющую вопрос (question), и Ответ на
вопрос (answer). При вызове сервлета он должен прочитать данные из базы данных
и возвращать динамически создаваемую Web-страницу, содержащую каждый вопрос
и ответ, отсортированные по темам.
2.6. Модифицируйте Web-приложение из Упражнения 2.5, чтобы начальный запрос к серв-
лету возвращал Web-страницу с темами FAQ из базы данных. После этого пользователь
сможет переходить по гиперссылке к другому сервлегу, который возвращает только
часто задаваемые вопросы по определенной теме.
2.7. Модифицируйте Web-приложение, представленное на рис. 2.27, чтобы дать
возможность пользователю видеть результаты опроса, не отвечал ыа вопрос.
2.8. Web-приложение, представленное на рис. 2.27, дает возможность пользователям
голосовать любое количество раз путем многократного возврата к Web-страниде опроса
и последующего голосования. Модифицируйте решение Упражнения 2.7, использовав
cookie со сроком хранения один день, чтобы не позволять пользователю голосовать
более одного раза в день. Когда пользователь возвращается на Web-сайт, cookie, ранее
сохраненный в его системе, отправляется на сервер. Сервлет должен проверять наличие
cookie и, если он существует, указывать клиенту, что оп уже участвовал в голосовании.
Сервлет также должен возвращать текущие результаты опроса.
2.9. Модифицируйте Web-приложение, представленное на рис. 2,27, чтобы обобщить его
для любого опроса. Используйте параметры сервлета (см, раздел 2.8), чтобы задавать
опции опроса. Когда пользователь запрашивает участие в опросе, динамически
генерируется форма, содержащая опции опроса. Осуществите развертывание этого
Web-приложения с использованием различных корней контекста. Замечание. Возможно, вам
придется модифицировать базу данных, используемую в этом примере, чтобы иметь
возможность хранить в ней данные сразу для нескольких опросов,
2.10. Напишите Web-приложение, которое состоит из сервлета (DirectoryServlet) и
нескольких Web-документов. Первым документом, который увидит пользователь, должен быть
документ index.html. В этом документе должен содержаться ряд гиперссылок на другие
Web-страницы вашего сайта. При щелчке мышью на гиперссылке должен вызываться
серзлет с помощью запроса get, который содержит параметр page. Сервлет должен
получать параметр page и переадресовывать запрос к соответствующему документу.
2.11. Модифицируйте Web-приложение, представленное на рис. 2.10, чтобы первый
документ, который пользователь видит в браузере, динамически генерировался сервлетом
HomePageServlet на основе параметров иницийлизяпии (см. раздел 2.8). Для каждой
страницы Web-сайта должны быть предусмотрены свои параметры инициализации.
Сервлет HomePageServlet прочитывает имя и значение каждого параметра и создает
хэш Hash Map пар имя/значение параметров. Эта информация должна использоваться
для динамического создания начальной страницы. Хэш (объект Hash Map) также
должен быть помещен в контейнер ServletContext с помощью метода set At tribute, чтобы
ее мог использовать сервлет DirectoryServlet для определения, куда направлять
каждый из запросов. Динамическая основная страница должна содержать гиперссылки на
каждый документ Web-сайта. Как и в Упражкепии 2.10, когда пользователь щелкает
на ссылке, должен вызываться сервлет DirectoryServlet с передачей ему параметра
страницы. Затем сервлет DirectoryServlet должен получить хэш HashMap из
контейнера ServletContext, найти соответствующий документ и переадресовать пользователя
к этому документу.
8
JavaServer Pages (JSP)
Цели
• Научиться создавать
и развертывать JSP-страницы.
• Научиться применять
неявные объекты JSP и Java
для создания динамических
Web-страниц.
• Научиться задавать
глобальную для JSP-страницы
информацию с помощью
директив.
• Научиться использовать
действия для
манипулирования
компонентами JavaBeans
в JSP-странице, чтобы
осуществлять динамическое
включение ресурсов
и переадресацию запросов
к другим JSP-страницам.
• Научиться создавать
собственные библиотеки
нестандартных тегов,
инкапсулирующие в новые
теги сложные
функциональные
возможности, которые могут
быть многократно
использованы
программистами JSP-страниц
и дизайнерами Web-страниц.
Мы думаем, что помидор кс способен
общаться с другим помидором. Возможно,
мы ошибаемся.
Густав Экштейн
Осел — это лошадь в переводе на
голландский.
Георг Кристоф Лихтенберг
Вопрос таланта — это вопрос количества.
Талант состоит не в том, чтобы написать
одну страницу, а в том, чтобы написать их
триста.
Жюль Ренар
110
Глава 3
3.1. Введение
Наше обсуждение сетевого взаимодействия между клиентом и сервером в этой
главе мы продолжим рассмотрением серверных страниц Java (JavaServer
Pages — JSP). JavaServer Pages представляет собой расширение технологии сервле-
тов для упрощения доставки динамического Web-содержимого. Страницы Java-
Server Pages дают возможность программистам Web-приложения создавать
динамическое содержимое за счет многократного использования ранее определенных
компонентов и за счет взаимодействия с компонентами путем написания
сценариев, выполняющихся на стороне сервера. Программисты JavaServer Pages могут
многократно использовать компоненты JavaBeans и создавать собственные
библиотеки нестандартных тегов, которые инкапсулируют сложные динамические
функциональные средства. Библиотеки нестандартных тегов также дают
возможность дизайнерам Web-страниц, не знакомым с языком Java, усовершенствовать
Web-страницы, добавляя средства динамического отображения содержимое и
новые возможности по обработке.
В дополнение к классам и интерфейсам для программирования сервлетов (из
пакетов javax.servlet и javax.servlet.http), в пакетах javax.servlet.jsp и javax.ser-
vlet.jsp.target содержатся классы и интерфейсы, относящиеся к
программированию JavaServer Pages. В ходе главы мы обсудим многие из этих классов и интер-
JavaServer Pages (JSP)
111
фейсов по мере знакомства с основами технологии JavaServer Pages. Полное
описание технологии JavaServer Pages можно найти в спецификации JavaServer
Pages 1.1 по адресу java.sun.com/products/jsp/download.html1. В разделе 3.9
приводятся и другие ресурсы, имеющие отношение к технологии JSP. [Замечание.
Исходный код и изображения для всех рассматриваемых в этой главе примеров
можно найти па Web-сайте компании Deitel www.deitel.com, а также на Web-сайте
издательства «Бином» www.biiiom-prcss.ru/books/adv_java2.litiii.]
3.2. Обзор технологии JavaServer Pages
В JSP имеются четыре ключевых компонента: директивы, действия, скриптле-
ты и библиотеки тегов. Директивы представляют собой сообщения для
контейнера JSP, которые дают возможность программисту задавать параметры страницы,
включать содержимое из других ресурсов и задавать собственные библиотеки
нестандартных тегов для использования их в JSP-странице. Действия инкапсулируют
функциональные возможности в предопределенных тегах, которые программисты
могут встраивать в JSP-страницу. Действия часто выполняются на основе
информации, посылаемой на сервер в составе запроса от определенного клиента. Действия
также могут создавать объекты Java для использования их в скриптлетах JSP.
Скриптлеты (scriptlets), или элементы сценария, дают возможность программистам
вставлять код Java, который взаимодействует с компонентами JSP-страницы (и,
возможно, с другими компонентами Web приложения) для обработки запроса. Биб
л истеки тегов являются составной частью механизма расширения тегов, который
дает возможность программистам создавать собственные теги. Такие теги позволяют
программистам манипулировать содержимым JSP. Эти типы компонентов JSP
подробно рассматриваются в последующих разделах.
Во многом страницы JavaServer Pages выглядят как стандартные XHTML- или
XML-документы. В действительности JSP-страницы обычно содержат разметку
XHTML или XML. Такая разметка носит название данных с неизменной
структурой (fixed template data) или текста с неизменной структурой. Наличие данных
с неизменной структурой часто помогают программисту принять решение, какую
технологию следует использовать: сервлеты или JSP. Программисты предпочитают
использовать JSP, если большая часть посылаемого клиенту содержимого
представляет собой данные с неизменной структурой, и лишь небольшая часть содержимого
генерируется динамически с помощью кода Java. Программисты используют
сервлеты, если только небольшая часть содержимого, посылаемого клиенту,
представляет собой данные с неизменной структурой. На самом деле некоторые сервлеты не
генерируют какого-либо содержимого. Вместо этого они выполняют определенную
задачу в интересах клиента, а затем вызывают другие сервлеты или JSP-страницы,
чтобы выдать ответ. Заметим, что в большинстве случаев сервлеты и JSP являются
взаимозаменяемыми. Подобно сервлетам, JSP-страницы обычно выполняются на
Web-сервере. Сервер при этом часто называют контейнером JSP.
S Общая методическая рекомендация 3.1
Символьный текст, содержащийся в JSP-странице. в сервлете
превращается в строковые литералы, которые представляют преобразованную
J SP-страницу.
Когда сервер, способный поддерживать технологию JSP, принимает первый
запрос на JSP-страницу, контейнер JSP транслирует эту JSP-страницу в сервлет
Java, который обслуживает текущий запрос и все последующие запросы к этой
1 На момент подготовки к изданию перевода книги актуальной была версия 1.2.
спецификации JavaServer Pages. — Прим. ред.
112
Глава 3
JSP-странице. Если при компиляции нового сервлета возникают ошибки, эти
ошибки приводят к ошибкам на этапе трансляции. Контейнер JSP на этапе
трансляции помещает операторы Java, которые реализуют ответ JSP-страницы,
в метод _jspSeroice. Если сервлет компилируется без ошибок, контейнер JSP
вызывает метод _jspService для обработки запроса. JSP-страница может обработать
запрос непосредственно или же вызвать другие компоненты Web-приложения,
чтобы содействовать обработке запроса. Любые ошибки, которые имеют место
в процессе обработки запроса, называются ошибки на этапе запроса.
—з Совет по повышению эффективности 3.1
"^^ Некоторые контейнеры JSP транслируют JSP-страницы в серелеты на
этапе установки. Это позволяет избежать потерь в
производительности для первого клиента, запрашивающего JSP-страницу.
В целом механизм запрос/ответ и жизненный цикл для JSP-страниц и для серв-
летов одинаков. JSP-страницы могут определять методы jspln.it и jspDestroy
(схожие с методами init и destroy для сервлетов), которые вызываются, когда JSP-
страница, соответственно, инициализируется и завершает свое действие.
Программисты JSP-страниц могут определять эти методы с помощью объявлений JSP —
составной части механизма создания сценариев JSP.
3.3. Первый пример JSP-страницы
Мы начнем наше знакомство с технологией JavaServer Pages с простого
примера (рис. 3.1), в котором в Web-страницу с помощью выражения JSP вставляются
текущие дата и время.
Как можно видеть, страница clock.jsp содержит XHTML-разметку. В случаях,
подобных этому, проще реализовать JSP-страницу, чем сервлет. В сервлете,
который выполняет ту же задачу, что и данная JSP-страница, каждая строка XHTML-
разметки обычно представляет собой отдельный оператор Java, который помещает
строку, содержащую разметку, в ответ клиенту. Написание кода для вывода
разметки часто сопровонедается ошибками. Большинство средств редактирования
JSP-страниц предоставляют возможности выделения цветом различных элементов
синтаксиса, что способствует соблюдению программистами надлежащего
синтаксиса разметки.
1 <?xml version = "1.0"?>
2<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
3 "http://www.w3.org/TR/xhtjnll/DTD/xhtmll-sti:ict.dtd">
4
5<!— Рис. З.1. clock.jsp —>
6
7<html xmlna = "http://www.w3.org/1999/xlitiia">
8
9 <head>
10 <mefca http-equiv = "refresh" content = "60" />
11
12 <title>A Simple JSP Example</title>
13
14 <style type = "text/css">
15 .big { font-family: helvetica, arial, sans-serif;
16 font-weight: bold;
17 font-size: 2em,- }
18 </style>
JavaServer Pages (JSP)
113
19
20
21
22
23
2d
25
26
27
28
29
30
31
32
33
34
35
36
37
38
</head>
<body>
<p class = "big">Simple JSP Example</p>
<table style = "border: 6px outset;">
<tr>
<td style = "background-color: black;">
<p class = "big" style = "color: cyan;">
<!— выражение JSP для вставки даты/времени
<%= new java.util.Date() %>
</p>
</td>
</tr>
</table>
</body>
</html>
Simple JSP Example
Tue Sep 19 14:07:32 EDT 2000
В5Г
"! Пйй«^"
feW [И l«»:/)bt«I«ist<llC0№iJBg4 J4
Simple JSP Example
Tue Sep 19 14:07:37
2000
Ms
ШШ.
1ШПШШШ&+
~шш№*
Л
Рис. 3.1. Использование выражения JSP для вставки даты и времени на Web-страницу
Общая методическая рекомендация 3.2
Страницы JavaServer Pages проще реализовывлть, чем сервлеты. если
ответ на клиентский запрос состоит главным образом из разметки,
которая в промежутке между различными запросами остается неизменной.
114
Глава 3
JSP-етраница, представленная на рис. 3.1, генерирует XHTML-докумеыт,
который отображает текущие дату и время. Основной строкой в этой JSP-странице
(строка 30) является выражение
<%= new Java.util.Date( ) %>
Выражения JSP заключаются в ограничители <%= и %>. В данном случае
выражение создает новый экземпляр класса Date из пакета java.util. Когда клиент
запрашивает эту JSP- страницу, данное выражение вставляет а ответ клиенту строку,
представляющую текущие дату и время. [Замечание. Чтобы обеспечить
надлежащую локализацию, эта JSP-страница должна возвращать клиенту дату в
региональном (принятом в данной местности) формате. В этом примере представление даты
Date определяется регионом местонахождения сервера. JSP-страница clock2.jsp,
представленная на рис. 3.9, демонстрирует, как определить регион
местонахождения (местность) клиента. Страница использует объект DateFormat (пакет
Java.text) для форматирования даты в соответсгвии с правилами, сложившимися
для данной местности.]
Ш Общая методическая рекомендация 3.3
Контейнер JSP преобразовывает результат выполнения каждого
выражения JSP в строку, которая выводится как часть ответа клиенту.
Обратите внимание на использование элемента XHTML meta в строке 10
для установки интервала обновления документа, равного 60 секундам. Это застав
ляет браузер запрашивать страницу clock.jsp каждые 60 секунд. При каждом
запросе страницы clock.jsp контейнер JSP заново вычисляет выражение в строке 30,
создавая новый объект Data с новыми текущими значениями даты и времени сервера.
Как и в главе 2, для тестирования наших JSP-страниц в ранее созданном
Web-приложении advjhtpl мы используем сервер Apache Tomcat. Подробности,
связанные с созданием и настройкой Web-приложения, вы можете найти в
разделах 2.3.1 и 2.3.2. Чтобы протестировать страницу clock.jsp, создайте новый
каталог с именем jsp в подкаталоге advjhtpl каталога web&pps Tomcat. Далее,
скопируйте файл clock.jsp в каталог jsp. Откройте ваш Web-браузер и введите в строке
адреса следующий URL для тестирования страницы clock.jsp:
http://localhost:8080/advjJitpl/jsp/cloek.jap
При первом вызове JSP-страницы обратите внимание на задержку, вызванную
тем, что Tomcat транслирует JSP-страницу в сервлет и вызывает сервлет в ответ на
ваш запрос. [Замечание. Каталог с именем jsp в Web-приложении создавать не
обязательно. В данном случае этот каталог используется для того, чтобы отделить
примеры этой главы от примеров сервлетов в главе 2.]
3.4. Неявные объекты
Неявные объекты предоставляют программистам доступ ко многим
функциональным возможностям сервлетов в контексте JavaServer Page. Неявные объекты
имеют четыре области видимости: приложения, страницы, запроса и сеанса.
Приложение контейнера JSP и сервлетов владеет объектами с областью видимости
в пределах приложения. Такими объектами может манипулировать любой сервлет
или JSP-страница. Объекты с областью видимости в пределах страницы
существуют только на той странице, на которой они определены. Каждая страница имеет
свои собственные экземпляры неявных объектов со страничной областью
видимости. Объекты с областью видимости в пределах запроса существуют в течение
запроса. Например, JSP-страница кожет частично обработать страницу, а затем
переслать запрос другому сервлету или JSP-странице для дальнейшей обработки.
JavaServer Pages (JSP)
115
Объекты с областью видимости в пределах запроса теряют свое действие по
завершении обработки запроса выдачей ответа клиенту. Объекты с областью
видимости в пределах сеанса существуют в течение всего клиентского сеанса просмотра,
В таблице на рис. 3.2 описаны неявные объекты JSP и их области видимости.
В этой главе будет продемонстрировано применение некоторых из этих объектов.
Неявный объект
Описание
Область видимости в пределах приложения
application
Этот объект javax.servlet.ServletContext представляет собой
контейнер, в котором исполняется JSP-страница,
Область видимости в пределах страницы
config
exception
out
page
pageContext
response
Этот объект javax.servlet.ServletConfig содержит параметры
конфигурации. Как и для сералет™, параметры конфигурации могут
быть заданы в дескригторе Web-приложения.
Этот объект java.lang.Throwable представляет исключение, которое
передается странице сообщения об ошибках JSP. Объект доступен
только на странице сообщения об ошибках JSP.
Этот объект javax.servlet.jsp.JspWriter записывает текст как часть
ответа на запрос. Этот объект неявно используется выражениями JSP
и действиями, которые вставляют строку содержимого е ответ,
Этот объект java.lang.Object представляет ссылку this для текущего
экземпляра JSP-страницы.
Этот объект javax.servlet.jsp.PageContext скрывает детали реализации
базового контейнера JSP и сервлетов и предоставляет программистам
JSP-страниц доступ к неявным объектам, описанным в этой таблице.
Этот объект представляет ответ клиенту Объект обычно представляет I
собой экземпляр класса, который реализует интерфейс
HttpServletResponse (пакет javax.servlet.http). Если используется
протокол, отличный от HTTP, этот объект является экземпляром класса,
который реализует интерфейс javax.servlet.5ervletResponse.
Область видимости в пределах запроса
request
Этот объект представляет клиентский запрос. Объект обычно является
экземпляром класса, который реализует интерфейс HttpServletRequest
(пакет javax.servlet.http). Если используется протокол, отличный от
HTTP, этот объект является экземпляром подкласса
javax.servlet.ServletRcquest. \
Область видимости в пределах сеанса |
session
Этот объек-javax.servlet.http.Http5ession представляет информацию
о клиентском сеансе, если такой сеанс был создан. Этот объект
доступен только на страницах, которые участвуют е сеансе,
Рис. 3.2. Неявные объекты J5P
Обратите внимание, что многие из неявных объектов расширяют классы или
реализуют интерфейсы, которые обсуждались в главе 2. Таким образом,
JSP-страницы могут использовать для взаимодействия с такими объектами те же методы,
которые используют сервлеты (см. главу 2). В большинстве примеров в этой главе
используется один или несколько из представленных в таблице на рис. 3.2
неявных объектов.
116
Глава 3
3.5. Сценарии
Страницы JavaServer Pages часто формируют динамически генерируемое
содержимое как часть XHTML-доку мента, отправляемого клиенту в ответ на запрос.
В некоторых случаях содержимое является статическим, но выводится только при
соблюдении определенных условий, указанных в запросе (например, при
предоставлении значений в форме, которая осуществляет отправку запроса).
Программисты JSP-страниц могут вставлять код Java и логику управления в JSP-страницу
путем написания соответствующего сценария.
Общая методическая рекомендация 3.4
В настоящее время страницы JavaServer Pages поддерживают сценарии,
написанные только на Java. Последующие версии JSP, возможно, будут
поддерживать и другие языки сценариев.
3.5.1. Компоненты сценария
К компонентам сценария JSP относятся скриптлеты, комментарии,
выражения, объявления и управляющие последовательности (escape-последовательности).
В этом разделе описывается каждый из этих компонентов сценария. Многие из
них демонстрируются в примере на рис. 3.4 в конце раздела 3.5.2.
Скриптлеты представляют собой фрагменты кода, ограниченные символами
<% и %>. Они содержат операторы Java, которые контейнер помещает в метод
_jspServlet на этапе трансляции.
JSP-страницы поддерживают три типа комментариев: комментарии JSP,
комментарии XHTML и комментарии языка сценариев. Комментарии JSP
заключаются в ограничители <%— и —%>. Такие комментарии могут быть размещены
в любом месте JSP-страницы, но не внутри скриптлетов. Комментарии XHTML
заключаются в ограничители <!— и —>. Эти комментарии могут быть
размещены в любом месте JSP-страницы, но не внутри скриптлетов. Комментарии языка
сценариев в настоящий момент представляют собой комментарии языка Java,
поскольку Java сейчас является единственным возможным языком написания
сценариев JSP. В скриптлетах могут использоваться как однострочные комментарии
Java (ограничиваемые символами / и /), так и многострочные комментарии
(ограничиваемые символами /* и */).
Типичная ошибка программирования 3.1
Помещение комментария JSP или комментария XHTML внутрь скрипт-
лета является синтаксической ошибкой, выявляемой на этапе
трансляции, которая препятствует корректной трансляции JSP-страницы.
Комментарии JSP и комментарии языка сценариев игнорируются и не
присутствуют в ответе клиенту. Когда клиент просматривает исходный код ответа,
отправленного JSP-страницей, он видит только комментарии XHTML. Иметь
различные типы комментариев полезно, чтобы отделять комментарии, которые
являются видимыми для пользователя, от комментариев, которые обрабатываются на
сервере.
Выражение JSP, заключенное в ограничители <%= и %>, содержит выражение
Java, которое вычисляется, когда клиент запрашивает JSP-страницу, содержащую
это выражение. Контейнер преобразовывает результат JSP-выражения в строковый
объект (String), а затем помещает эту строку в ответ клиенту.
Объ явления (заключенные в ограничители <%/ и %>) дают возможность
программисту JSP определять переменные и методы. Переменные становятся пере-
Java Serve r Pages (JSP)
117
менными экземпляра класса сервлета, который представляет транслированную
JSP-страницу. Аналогично, методы становятся членами класса, который
представляет транслированную JSP-страницу. В объявлениях переменных и методов
на JSP-странице используется синтаксис Java. Таким образом, объявление
переменной должно заканчиваться точкой с запятой, например
<%! int counter =0; %>
Типичная ошибка программирования 3.2
Объявление переменной без использования завершающего символа точки
с запятой является синтаксической ошибкой.
Общая методическая рекомендация 3.5
Переменные и методы, декларированные в объявлениях JSP,
инициализируются при инициализации JSP-страницы и доступны, для использования во
всех скриптлетах и выражениях на этой JSP-странице. Переменные,
объявленные подобным образом, становятся переменными экземпляра класса
сервлета, который представляет транслированную JSP-страницу.
Общая методическая рекомендация 3.6
Как и сервлеты, JSP-страницы не должны сохранять информацию о
состоянии клиента в переменных экземпляров. Для этого JSP-страницы
должны использовать неявный объект JSP session.
Специальные символы или последовательности символов, которые контейнер
JSP обычно использует в качестве ограничителей JSP-кода, могут включаться на
JSP-страницу как буквенные (литеральные) символы в элементах сценария, в
данных с неизменной структурой и в значениях атрибутов с помощью управляющих
escape-последовательностей. В таблице на рис. 3.3 представлены буквенные
символы и соответствующие escape-последовательности, а также поясняется, где
следует применять escape-последовательности.
3.5.2. Пример сценария
JSP-страница, представленная на рис. 3.4, демонстрирует основные
возможности сценария при обработке запросов get. JSP-страница позволяет пользователю
ввести имя, а затем возвращает это имя в ответе. Используя скриптлег,
JSP-страница определяет, был ли параметр first Name передан JSP-странице в составе
запроса; если нет, JSP-страница возвращает XHTML-доку мент, содержащий форму,
в которой пользователь может ввести имя. В ином случае JSP-страница получает
значение firstName и использует его как часть XHTML-документа, который
содержит приветственное сообщение, выдаваемое пользователю.
Литерал
<*
%>
Escape-последовательность
<\*
%\>
Описание
Символьная последовательность <% обычно указывает на
начало скриптлета, Escape-последовательность <\% помещает
буквенные символы <% в ответ клиенту.
Символьная последовэтегьность %> обычно указывает на конец
скриптлета. Escape-последовательность %\> помещает
буквенные символы %> в ответ клиенту,
118
Глава 3
Литерал
■
■т
\
Escape-после-
довател ьн ость
V
V
Описание
Как и строковые литералы а программе Java,
escape-последовательности для символов ', " и \ дают
возможность использовать эти символы в значениях атрибутов.
Напомним, что символьный текст на JSP-странице в серелете
превращается в строковые литералы, которые представляют
транслированную JSP-страницу, |
Рис. 3.3. Escape-последовательности JSP
1
2
3
4
5
б
1
В
9
10
11
12
13
14
15
16
17
18
19
2D
21
22
23
24
25
26
27
28
2»
30
31
32
33
34
35
36
37
38
39
40
41
<?xml version = "1.0"?>
-ODOCTYPE html POBLIC "-//W3C//DTD XHTML 1.0 Strict//EH"
"http://www.w3.org/TR/xhtmll/DTD/xhtmll-strict.dtd">
<!— Рис. 3.4. welcome.jsp —>
<\— JSP-страница, обрабатывающая запрос get,
содержащий данные. —>
<html xmlns = "http://www.w3.ocg/1999/xhtml">
<!-' раздел заголовка документа —>
<head>
<title>Processing "get" requests with data</title>
</head>
<!-- раздел тела документа —>
<body>
<% // начало скриявдета
String паше = request.getParameter( "firstHame" );
if ( name != null ) {
%> <%— закрытие скришлета для вставки данных с неизменной
структурой --%>
<Ы>
Hello <%= name %>, <br />
Welcome to JavaServeг Pages!
</hl>
<% // продолжение скриптлета
} // конец блока if
else {
%> <%-- закрмтие скриптлета для вставки данных с неизменной
структурой --% >
<£orm action = "welcome.jap" method = "gef">
<p> Type your first name and press Submit</p>
<pXinput type = "text" name ~ "firgtSame" />
<input type = "submit" value = "Submit" />
JavaServer Pages (JSP)
«9
42 </p>
43 </form>
44
45 <% // продолжение скриптлета
46
47 ) // конец блока else
48
49 *> <i— конец слгриптлета —i>
50 </body>
51
52 </html> <!-- конец XHTML-документа —>
■ A****-
ЬРЧЕ
Type your
(Paul
«ЙЩ^
Wew Favatei bob rtdjF
,.jfMvil g-; -Sup *:',jK
IT
N(p:fl1«al»5t.saaa/advjhtpl/M>>relcome.|!p
first name and press Submit
r"s"bnaft.A
•«lar -, -r^
■■ ш
в&
Home
;. 1
ДИ№
.1 ■ *a
'Ш ~ 1
'^i* '51
"ik**H
* j
£1
t'*K^«!«!*' ■'.:£$
http:Jteat.Ht;B0S0)advihl:pl);alf».ak:are,l5p;Sratome=Paul -SJJjfe0
Hello Paul,
Welcome to JaVa Server Pages!
I & .. zi
worn: :Ш»:Г~Щ;;, Ч. ччр*: -;1Р%ГШШ^5ЩГ~5
Рис. 3.4. JSP-стрзнииз wekome.jsp, содержащая сценарий
Обратите внимание, что большая часть кода, представленного в листинге на
рис. 3.4, является XHTML-разметкой (т.е. данными с неизменной структурой).
В составе элемента body имеется несколько екрилтлетов (строки 17-23, 30-35
и 45-49) а также выражение JSP (строка 26). Как можно видеть, в этой JSP-стра-
нице используются три типа комментариев.
Скриптлеты содержат структуру if/else, которая определяет, получила ли
JSP-страпица в составе запроса значение для имени. В строке 19 используется
метод getParameter неявного объекта JSP request (объект класса
HttpServIetRequest) для получения значения параметра firstName, лоиле чего
результат присваивается переменной name. В строке 21 проверяется, что значение
паше не есть null (т.е. что имя было передано JSP-странице в составе запроса).
Если условие истинно (true), скриптлет временно закрывается для вывода данных
с неизменной структурой в строках 25-28. Выражение JSP в строке 26
осуществляет вывод значения переменной name (т.е. имени, переданного JSP-странице как
параметр запроса). Скриптлет возобновляется в строках 30-35 с закрывающей фи-
120
Глава 3
гурной скобки структуры if, после которой начиняется блок else структуры if/else.
Если условие в строке 21 ложно (имеет значение false), вывод в строках 25-28 не
производится. Вместо этого в строках 37-43 осуществляется вывод формы.
Пользователь может ввести имя в форме и нажать кнопку Submit для повторного
запроса JSP-страницы и выполнения тела структуры if (строки 25-28).
Общая методическая рекомендация 3.7
Скриптлеты, выражения и данные с неизменной структурой совместно
используются на JSP-странице, чтобы формировать различные
варианты ответа в зависимости от информации* содержащейся в запросе
к JSP-странице.
Совет по тестированию и отладке 3.1
Иногда, при отладке JSP-страницы бывает довольно сложно обнаружить
ошибки, поскольку номера строк, сообщаемые контейнером JSP,
относятся К номерам строк с ере лет а, представляющего транслированную
JSP-страницу, а не к изначальным номерам строк кода JSP-страницы.
Интегрированные среды разработки программ, такие как Forte for Java
Community Edition компании Sun Microsystems, Inc., дают возможность
компилировать JSP-страницы в собственном окружении, чтобы иметь
возможность наблюдать сообщения о синтаксических ошибках. Эти
сообщения включают в себя описание оператора сервлета. который
представляет транслированную JSP-страницу, что может оказаться полезным
при выявлении ошибок.
Совет по тестированию и отладке 3.2
Многие контейнеры JSP сохраняют серелеты, представляющие
транслированные JSP-страницы. Например, в каталоге Tomcat имеется
подкаталог work, в котором можно найти исходный код для сервлетов,
транслированных Tomcat.
Чтобы протестировать в Tomcat JSP-страницу, представленную на рис. 3.4,
скопируйте файл welcome.jsp в каталог jsp, созданный в разделе 3.3. Откройте Web-браузер
и введите следующий URL, чтобы протестировать страницу welcome.jsp:
http;//localhost:8080/advjlitpl/isp/welcome.jsp
При первом выполнении ЛЯР-страницы отображается форма, в которую вы можете
ввести ваше имя, поскольку параметр firstName JSP-странице через URL не
передавался. После того как вы отправите ваше имя, окно браузера должно иметь вид,
представленный ка второй копии экрана на рис. 3.4. Замечание. Как и для
сервлетов, параметры запроса get можно передавать через URL. Следующий URL
передает параметр firstName JSP-странице welcome.jsp г
http://l0carhost:8O8O/advjhtpl/jsp/welcome.jsp?firstName=Paul
3.6. Стандартные действия
В продолжение обсуждения технологии JSP рассмотрим стандартные
действия JSP 1рис. 3.5). Эти действия предоставляют реализации JSP возможность
доступа к нескольким наиболее типичным задачам, выполняемым на JSP-странице,
таким как включение содержимого из других ресурсов, переадресация и
взаимодействие с компонентами JavaBeans. Контейнеры JSP обрабатывают действия на
этапе запроса. Описания действий ограничиваются тегами <jsp:3eucmeue>
и </)$р:действие>, где действие — это имя стандартного действия. В случаях, ко-
JavaServer Pages (JSP)
121
гда между начальным и конечным тегами ничего нет, может быть использован
синтаксис XML для пустых элементов <}$р:действие/>. Стандартные действия
JSP приведены в таблице на рис. 3.5.
Действие
<jsp:include>
<jsp:forward>
<jsp:plugin>
< j sp: parajn>
Описание
Динамически включает другой ресурс в JbP-страницу. При
выполнении JSP-стрзницы этот ресурс включается и обрабатывается.
Переадресовывает обработку запроса другой JSP-странице, сервлету
или статической аранице. Это действие завершает выполнение
текущей JSP-сфаницы.
Дает возможность добавления е страницу подключаемого
компонента в виде специфичного для браузера HTML-элемента
object или embed. /Для апппета Java это действие дает возможность
загружать и устанавливать подключаемые модули Java Plug in, если
они еще не установлены на компьютере клиента.
Используется совместно с действиями include, forward и plugin для
задания дополнительных пар имя/значение для данных,
используемых этими действиями.
Манипулирование компонентами JavaBeans
<jsp:useBean>
<j sp:setProperty>
<j sp:getProperty>
Указывает, что JSP-страница использует экземпляр компонента
JavaBeans. Это действие задает область видимости компонента
и присваивает ему идентификатор, который компоненты сценария
могут использовать для манипулирования компонентом.
Задает свойство указанного экземпляра компонента JavaBeans,
Особенностью этого действия является автоматическое
отождествление параметров запроса со свойствами компонента,
носящими эти же имена.
Получает свойство для заданного экземпляра компонента JavaBeans
и преобразовывает результат в строку для вывода ее а составе ответа.
Рис. 3.5. Стандартные действия JSP
3.6-1. Действие <jsp:include>
В JavaServer Pages поддерживается два механизма включений: действие <jsp:
inclu.de> и директива include. Действие <jsp:include> дает возможность включать
динамическое содержимое в страницу JavaServer Page. Если включаемый ресурс
в промежутке между запросами изменился, следующий запрос к JSP-страпице,
содержащей действие <jsp:inelude>, будет осуществлять включение нового
содержимого ресурса. В свою очередь, директива include копирует содержимое на JSP-
страниду один pas, на этапе трансляции. Если включенный ресурс изменился,
новое содержимое не будет отражено в JSP-странице, которая использует директиву
include, если только JSP-страница не будет перекомпилирована. В таблице на
рис. 3.6 описаны атрибуты действия <jsp:include>.
S Общая методическая рекомендация 3.8
Согласно спецификации JavaServer Pages 1.1, контейнеру JSP
предоставляется возможность определять, был ли изменен ресурс, включенный с
помощью директивы include. Если да, контейнер может
перекомпилировать JSP-страницу, которая включает ресурс. Однако спецификация не
предоставляет какой-либо механизм, который может быть использован
для указания контейнеру на изменение включенного ресурса.
122
Глава 3
Совет по повышению эффективности 3.2
Действие <jsp:include> обладает большей гибкостью, чем директива
include, но требует больших непроизводительных затрат в случае
частого изменения содержимого страницы. Используйте действие <jsp:
included только в том случае, если необходимо иметь динамически
меняющееся содержимое.
Типичная ошибка программирования 3.3
Установка для атрибута flush действия <JKp:include> значения false
является ошибкой, выявляемой на этапе трансляции. На сегодняшний день
для атрибута flush поддерживается только значение true.
Атрибут
page
flush
Описание
Задает относительный UR1 к включаемому ресурсу. Ресурс должен быть
частью этого же Web-приложения.
Задает, следует ли очищать буфер после выполнения директивы include.
Согласно спецификации JSP 1.1, этот атрибут должен иметь значение true.
Рис. 3.6. Атрибуты действия <jsp:include>
Типичная ошибка программирования 3.4
Если для действия <jsp:include> не указать атрибут flush, на этапе
трансляции будет зафиксирована ошибка. Задание этого атрибута
является обязательным.
Типичная ошибка программирования 3.5
Указание в действии <jsp:include> страницы, которая не является
частью этого же Web-приложения, порождает ошибку на этапе запроса.
В этом случае действие <jsp:include> не будет осуществлять включение
какого-либо содержимого.
Следующий пример демонстрирует действие <jsp:include>, использующее
четыре ресурса XHTML и JSP, которые представляют как статическое, так и
динамическое содержимое. JSP-страница include.jsp (рис. 3.10) осуществляет
включение трех внешних ресурсов: banncr.html (рис. 3.7), toc.html (рис. 3.8) и clock2.jsp
(рис. 3.9). JSP-страница include .jsp создает XHTML-документ, содержащий
таблицу table, в которой ресурс banner.html занимает два столбца в первой строке,
ресурс toc.html — левый столбец во второй строке, а ресурс cIock2 jsp (упрощенная
версия страницы, представленной на рис. 3.1) — правый столбец во второй строке.
Страница, представленная на рис. 3.10, использует три действия <jsp:include>
(строки 38 39, 48 и 55 56) в качестве содержимого элементов td в таблице table.
С помощью XHTML-документов и JSP-страницы, представленной на рис. 3.10,
демонстрируется, каким образом в JSP-страницы может включаться статическое
и динамическое содержимое. На копиях экрана иа рис. 3.10. показаны результаты
выполнения двух отдельных запросов к странице include.jsp.
Страница, представленная на рис. 3.9 (clock2.jsp), демонстрирует, как
определить местность клиента (класс Locale из пакета java.util), и использует этот
объект Locale для форматирования даты Date в соответствии с форматом DateFormat
(пакет java.text). В строке 14 вызывается метод getLocale объекта request,
который возвращает местность клиента (объект Locale). В строках 17-20 вызывается
статический метод getDateTimelnstance класса DateFonnat для получения объек-
JavaServer Pages (JSP)
123
та DateFormat. Первые два параметра указывают, что дата и время должны быть
представлены в формате LONG (другими возможными форматами являются
FULL, MEDIUM, SHORT и DEFAULT). Третий параметр задает местность, для
которой объект DateFormat должен осуществить форматирование даты. В сроке 25
вызывается метод format объекта DateFormat для формирования строки,
представляющей дату. Объект DateFormat форматирует эту строку для местности,
указанной в строках 17-20. [Замечание. Этот пример работает для западных языков,
в которых используется набор символов ISO-8859-1. Для языков, которые не
поддерживают этот набор символов, JSP-страница должна предоставить
соответствующий набор символов с помощью директивы JSP page (раздел 3.7.1). На сайте
java.s un.com/j 2se/1.3/docs/guide/intl /encoding/doc.html представлен перечень
кодировок символов. Тип содержимого ответа определяет набор символов,
который будет использоваться в ответе. Тип содержимого имеет вид: "mime-mun;
charset^rcodupoerca" (например, "text/html;charset=ISO-8859-1"),]
Чтобы протестировать в Tomcat страницу, представленную на рис, 3.10,
скопируйте файлы banner.html, toc.html, clock2.jsp, include.jsp и каталог images в
каталог jsp, созданный в разделе 3.3, Откройте Web-браузер и введите следующий
URL, чтобы протестировать страницу welcome.jsp:
http://localhost:8080/advjhtpl/jsp/include.jsp
1<!-- Рис. 3.7. banner.html —>
2<»-- "Шапка", включаемая в другой документ. —>
3 <div style = "width: 5B0px">
4 <p>
5 Java(TM), С, C++, Visual Basic(R),
6 Object Technology, and <br /> Internet and
7 World Wide Web Programming Training6nbsp;<br />
8 On-Site Seminars Delivered Worldwide
9 </p>
10
11 <p>
12 <a href = "mailto:deitel@deitel.com">
13 deitel@deitel.conK/aXbr />
14
15 978.579.9911<br />
16 4 90B Boston Post Road, Suite 200,
17 Sudbury, MA 01776
18 </p>
19 </div>
Рис. З.7. «Шапка» (banner.html), которая включается в начало XHTML-доку мента,
создаваемого JSP-стрэницей, представлен ной на рис. 3.10
1 <Г— Рис. 3.8. toc.html —>
2 <!-- Содержимое, включаемое в другой документ —>
3
4 <рХа href = "http://www.deitel.com/books/index.htnil">
5 publications/BoolcStore
6 </аХ/р>
7
8 <рХа href = "http://www.deitel.com/whatsnew.html">
9 What's New
10 </aX/p>
124
Глава 3
11
12 <рХа href = "http: //www.deitel .com/books/downloads.html">
13 Downloads/Resources
14 </ax/p>
15
16 <pXa href = "http://www.deitel.com/faq/index.htBil">
17 FAQ (Frequently Asked Questions)
18 </ax/p>
19
20 <pXa href = "http://www.deitel.com/intro.html">
21 Who we are
22 </ax/p>
23
24 <pxa href = "http://www.deitel.eoin/index.htiiil">
25 Home Page
26 </aX/p>
27
28 <p>Send questions or comments about this site to
29 <a href = "mailto:deitel@deitel.coni">
30 deitel@deitel.com
31 </aXbr />
32 Copyright 1995-2002 by Deitel tamp; Associates, Inc.
33 All Rights Reserved.
34 </p>
Рис. З.8. Содержимое (toc.html), которое включается в левую часть XHTML-документа,
создаваемого JSP-страницей, представленной на рис. 3.10
1 <Г— Рис. 3.9. clock2.jsp —>
2 <!— Дата и время, включаемые в другой документ. —>
3
4 <table>
5 <tr>
6 <td style = "background-color: black;">
7 <p class = "big" style = "color: cyan; font-size: 3em;
8 font-weight: bold;">
9
10 <%-- сценарий для определения местности клиента --%>
11 <%— и формата даты --%>
12 <%
13 // получение местности клиента
14 java.util.Locale locale = request.getLocale();
15
16 // получение формата данкьгх DateFormat для местности
клиента
17 java-text.DateFormat dateFormat =
18 Java. text. DateFormat. getDateTimelnstance. (
19 java.text.DateFormat.LONG,
20 java.text.DateFormat.LONG, locale ) ;
21
22 %> <%— конец сценария --%>
23
24 <%— вывод даты —%>
25 <%^ dateFormat.format( new java.util.Date О ) %>
26 </p>
JavaServer Pages (JSP)
125
27 </td>
28 </tr>
29 </tabJ-e>
Рис. З.9. JSP-страница clock2.jsp, которая включается в виде основного содержимое
в XHTML-доку мент, создаваемый JSP-страницей, представленной на рис. 3.10
1 <?хи»1 version = "1.0"?>
2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
3 "http://www.w3.org/TR/xhcnul/DTD/xhtmll-strict.dtd">
4
5<!-- Рие, 3.10. include.jsp —>
6
1 <html xmlns = "http://www.w3.org/1999/xhtna">
8
9 <head>
10 <title>Os±ng jsp:include</title>
11
12 <style type = "text/css">
13 body {
14 font-family: tahoma, helvetica, arial, sans-serif;
15 }
16
17 table, tr, td {
18 font-size: . 9em;
19 border: 3px groove;
20 padding: 5px;
21 background-color: tdddddd;
22 }
23 </style>
24 </head>
25
26 <body>
27 <table>
28 <tr>
29 <td style = "width: 160px; text-align: center">
30 <img src = "images/logotiny,png"
31 width = "140" height = "93"
32 alt = "Deitel S Associates, Inc. Logo" />
33 </td>
34
35 <td>
36
37 <%— включение документа banner,html в эту JSP-страницу --%>
38 <jsp:include page = "banner.html"
39 flush = "true" />
40
41 </td>
42 </tr>
43
44 <tr>
45 <td style = "width: 160px">
46
47 <%-- включение документа toe.html в эту JSP-страницу --%>
48 <jsp:include page я "toe.html" flush = "true" />
126
Глава 3
49
50
51
52
53
54
55
56
57
58
59
60
61
<%-
</td>
<td style = "vertical-align: top">
- включение документа clock2.jsp в эту jSP-страницу --%>
<3sp: include page = "cloelt2 . jsp"
flush = "true" />
</td>
</tr>
</tat>le>
</body>
62 </html>
ЩН
■--: + й
s 3nten« and v^drtJVsteWtfiPraoaimig Т:**ч
' ovata Earnf Meed waUMde
aaAsiagEijMfaf'''
ЙЙ
M?<
тргг
July 31, 2001 9:46:42 ДМ EDTi
Ж
I
шшшвшт
ЩШЙШ!
"ЯЯШТГ
шё"№^;ащащ
Рис. 3.10. JSP-страница indude.jsp, огущесгнляющая включение ресурсен г помощью
действия <jsp:include>
3.6,2. Действие <jsp:forward>
Действие <jsp:forward> дает возможность JSP-странице переадресовывать
обработку запроса. Обработка запроса исходной JSP-страницей завершается, как
только JSP-страница переадресовывает запрос. Действие <jsp:forward> имеет
единственный атрибут page, задающий относительный URL ресурса (в этом же
Web-приложеннн), к которому должен быть переадресован запрос.
Общая методическая рекомендация 3.9
При использовании действия <}sp:forward> ресурс, к которому будет
переадресован запрос, должен располагаться в том же контексте
(Web-приложении), что и JSP-страница, изначально принявшая запрос.
iavaServer Pages (iSP)
127
JSP-страница forwardl.jsp (рис. 3.11) представляет собой модифицированную
версию страницы welcome.jsp (рис. 3.4). Главное различие состоит в строках 22—25,
в которых JSP-страница forwardl.jsp переадресовывает запрос JSP-странице for-
ward2.jsp (рис. 3.12). Обратите внимание на действие <jsp:param> в строках 23-24.
Это действие добавляет параметр запроса, представляющий дату и время получения
изначального запроса, который переадресовывается странице forward2.jsp.
Действие <jsp:param> задает пары имя/значение для данных, которые
передаются действиям <jsp:mclude>, <jsp:forward> и <jsp:plugin>. Каждое действие
<jsp:param> имеет два обязательных атрибута: name и value. Если действие
<jsp:param> задает параметр, который уже присутствует в запросе, новое
значение для параметра имеет приоритет над первоначальным значением. Все
имеющиеся значения для этого параметра могут быть получены е помощью метода get-
ParamcterValue неявного объекта JSP request, который возвращает массив строк.
JSP-страница forward2.jsp использует имя пате, заданное в действии <jsp:
param> ("date"), для получения даты и времени. Она также использует параметр
firstName, изначально переданный странице forwardl.jsp, чтобы получить имя
пользователя. Выражение JSP из листинга на рис. 3.12 (строки 23 и 24)
осуществляет вставку значения параметров запроса в ответ клиенту. На копии экрана на
рис. 3.11 показано начало взаимодействия с клиентом. На копии экрана на
рис. 3.12 показаны результаты, возвращаемые клиенту после переадресации
запроса к странице forward2.jsp.
Чтобы протестировать в Tomcat JSP-страницы, представленные на рис. 3.11
и 3.12, скопируйте файлы forwardl.jsp и forward2.jsp в каталог jsp, созданный
в разделе 3.3. Откройте ваш Web-браузер и введите следующий URL, чтобы
протестировать страницу welcome.jsp:
. http://localhost:8080/advjhtpl/jsp/forwardl.jsp
1 <?xml version = "1.0"?>
2 «C'DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EH"
3 "http://www.w3.org/TR/xhtmll/DTD/xhtmll-strict.dtd">
4
5<!-- Рис. 3.11. forwardl.jsp —>
6
7 <html xmlns = "http://www.w3.org/1999/xhtml">
a
9 <head>
10 <title>Forward request to another JSP</title>
11 </head>
12
13 <body>
14 <* // начало скрипилета
IS
16 String name = request.getPararoeter( "firstName" );
17
18 if ( name ! = null ) {
19
20 %> <%— закрытие скриптлета для вставки данных с неизменной
структурой --%>
21
22 <jsp:forward page - "£orward2.jsp">
23 <jsp:param паше = "date"
24 value = "<%= new javs.util.DateO %>" />
25 </jsp:forward>
26
128
Глава 3
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
4В
49
<% // продолжение скриптлета
} // конец блока if
else {
%> <%-- закрытие скриптлета для вставки данных с неизменной
структурой —%>
<forjn action = "forwardl.jsp" method = "get">
<p>Type your first name and press Submit</p>
<pXinput type = "text" name = "firstName" />
<input type = "submit" value = "Submit" />
</p>
</form>
<% // продолжение скрилтлета
} // конец блоха else
%> <%— конец скриптлета --%>
</body>
</html> <?-- конец XHTML-документа —>
ШЩШШЗШ!!
Л Forward requ
Ш&.*ягЩФ№М
ттштштм
^Pffi***108* :atlSuJadvtitpW]Spff<»'''»ril 'Й>. .ijjjjj
-в
ТУре your Erst name and press Submit
Pod
Submit!
Рис. 3.11. JSP-страница forwardtjsp принимает параметр firstName, добавляет дату
к параметрам запроса и переадресовывает запрос странице forward2.jsp для
дальнейшей обработки
1 <?xml version = "1.0"?>
2 <ID0CTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
3 "http://www.w3.org/TR/xhtmll/DTD/xhtmll-strict.dtd">
4
5<!-- forward2.3sp —>
6
7 <atml xmlns = "http://www.w3.org/1999/xhtml">
8
9 <head>
10 <title>Processing a forwarded request</title>
11
12 <style type = "text/ess">
13 .big {
14 font-family: tahoma, helvetica, arial, sans-serif:
JavaServer Pages (JSP)
129
15 font-weight: bold;
16 font-size: 2em;
17 }
18 </style>
19 </head>
20
21 <body>
22 <p class = "big">
23 Hello <%= request,getParameter( "firstName" ) %>, <br />
24 Your request was received <br /> and forwarded at
25 </p>
26
27 <table style = "border: 6px outset;">
28 <tr>
29 <td 3tyle = "background-color: black;">
30 <p class = "big" style = "color: cyan;">
31 <%= request.getParameter( "date" ) %>
32 </p>
33 </td>
34 </tr>
35 </table>
36 </body>
37
38 </html>
J:** "*.^..^*** 1°* * : . :, -Р...ШШЛ-
у*— ■ _-- -
fe^ ~.*t-?9. Ф ^Jjft^^.JS.^gg^CHjf^'Jlft
lilies
Hello Paul,
Your request was received
and forwarded at
Sun Jul 22 17:18:43 EDT 2001
1ёоЯИШШВЭТЩ;
Г ' .jjfuiajwwwiiS
Рис. 3.12. JSP-страница forward2.jsp принимает запрос (в данном примере - от
страницы forwardl.jsp) и использует параметры запроса при выдаче ответа клиенту
3.6.3. Действие <jsp:plugin>
Действие <jsp:plugin> добавляет апплет или компонент JavaBeans в
Web-страницу в виде специфичного для браузера XHTML-элемента object или embed. Это
действие также дает возможность клиенту загружать и устанавливать
подключаемый модуль Java Plug-in, если он еще не был установлен. В таблице на рис. 3.13
описаны атрибуты действия <jsp:plugin>.
Атрибут
type
code
Описание
Тип компонента: компонент JavaBeans или апплет.
Класс, который гредставляет компонент.
130
Глава 3
Атрибут
codebase
align
archive
height
hspace
jreversion
name
vspace
title
width
nsplugimirl
iepluginurl
Описание
Местоположение класса, задаваемого атрибутом code, и архивов,
задаваемых атрибутам archive.
Способ выравнивания компонента.
Список архивных файлов, разделенных пробелами, которые содержат
ресурсы, используемые компонентом. Такой архив может включат^ класс,
задаваемый атрибутом code.
Высота компонента на странице, заданная в пикселах или в процентах.
Пространство слева и справа от компонента, выраженное в ликсегах.
Версия окружения выполнения Java Runtime Environment и подключаемого
модуля, необходимого для выполнения компонента. Значением по
умолчанию является 1.1.
Имя компонента.
Пространство над и под компонентом, выраженное в пикселах.
Текст, который описывает компонент
Ширина компонента на странице, выраженная в пикселах или в процентах.
Адрес для загрузки подключаемого модуля Java Plug-in для Netscape Navigator.
Адрес для загрузки подключаемого модуля Java Plug-in для Internet Explorer.
Рис. 3.13. Атрибуты действия <jsp:plugin>
На рис. 3.14 представлен апплет, который формирует изображение с помощью
средств API Java2D, Апплет имеет три параметра, которые дают возможность
программисту JSP-страницы задавать фоновый цвет для рисунка. Параметры
представляют составляющие красного (red), зеленого (green) и синего (blue) цвета
значениями палитры RGB в диапазоне 0-255. Апплет получает значения параметров в
строках 21-23. Если в процессе обработки параметров возникают какие-либо
исключения, они перехватываются в строке 32 и игнорируются, оставляя для ап-
плета белый цвет фона, используемый по умолчанию.
1// Рис. 3.14. ShapesApplet.Java
2 // Апплет, демонстрирунщий рисование произвольной траектории в Java2D.
3 package com.deite1.advjhtpl.jsp.applet;
4
5 // Набор базовых пакетов Java
6 import Java.applet.*;
7 import Java.awt.event.*;
8 import Java.awt.*;
9 import Java.awt.geom.*;
10
11 // Пакет» расширений Java
12 import javax,swing.*;
13
14 public class ShapesApplet extends JApplet {
15
16 // инициализация апплета
17 public void init()
16 i
19 // получение параметров цвета из XHTML-файла
20 try {
21 int red = Integer.paraelnt( getParameter( "red" ) );
JavaServer Pages (JSP)
131
22 int green = Integer.parselnt{ getParameter( "green" ) )
23 int blue = Integer.parselnt( getParameter{ "blue" ) } ;
24
25 Color backgroundColor = new Color{ red, green, blue );
26
27 setBackground( backgroundColor );
28 }
29
30 // если при обработке параметров цвета возникло
31 // исключение, перехватить его и игнорировать
32 catch ( Exception exception ) {
33 // ничего не предпринимать
34 }
35 }
36
37 public void paint( Graphics g )
38 {
39 // создание массивов координат хну
40 int xPoints[] =
41 { 55, 67, 109, 73, 83, 55, 27, 37, 1, 43 };
42 int yPoints[] =
43 { 0, 36, 36, 54, 96, 72, 96, 54, 36, 36 },-
44
45 // получение ссылки на объект Graphics2D
46 Graphics2D g2d = ( Graphics2D ) g;
47
4 8 // создание звезды из набора точек
49 GeneralPath star = new GeneralPathf) ,-
50
51 // задание начальной координаты траектории GeneralPath
52 star.moveTo( xPoints[ 0 ], yPoints[ 0 ] );
53
54 // создание звезды -- звезда здесь не рисуется
55 for ( int к = 1; к < xPoints.length; k+* )
56 star.lineTo( xPoints[ k ], yPoints[ k ] );
57
58 // замыкание контура фигуры
59 star.closePath(};
60
61 // перемещение начала в точку {200, 200)
62 g2d.translate{ 200, 200 );
63
64 // вращение вокруг начала и рисование звезд случайно
выбранными цветами
65 for ( int j = 1; j <= 20; j++ ) {
66 g2d.rotate( Math.PI / 10.0 );
67
68 g2d.setColor(
69 new Color( ( int ) { Math.random() * 256 ),
70 { int ) { Math.random() * 256 ),
71 ( int ) ( Math.random() * 256 ) ) );
72
73 g2d.fill( star ); // рисование закрашенной звезды
74 }
75 }
7€ }
Рис. 3.14. Апплет. демонстрирующий действие <jsp:plugin>
132
Глава 3
Большинство используемых на сегодняшний день Web-бра узе ров не
поддерживают апплетов, написанных для платформы Java 2. Для выполнения таких аппле-
тов в большинстве современных браузеров требуется подключаемый модуль Java
Plug-in. JSP-страница, представленная на рис. 3.15, использует действие <jsp:
plagin> (строки 10-22) для подключения модуля Java Plug-in. В строке 11
указывается имя пакета и имя класса для класса апплета. В строке 12 указывается
местоположение класса апплета. В строке 13 задается, что апплет должен иметь окно
шириной в 400 пикселов, а в строке 14 задается, что высота окна апплета должна
составлять 400 пикселов. В строках 16-20 задаются параметры апплета. Вы можете
изменить цвет фона в апплете, изменив значения для красного, зеленого и синего
цветов. Учтите, что действие <jsp:plugm> требует, чтобы любые имеющиеся действия
<jsp:param> указывались в действии <jsp:params>.
Чтобы протестировать действие <jsp:plugin> в Tomcat, скопируйте файлы
plugin.jsp и ShapesApplet.class в каталог jsp, созданный в разделе 3.3.
{Замечание. Класс ShapesApplet определен в пакете com.deit el.advjhtpl. jsp. applet. Этот
пример будет работать только в том случае, если в каталоге classes определена
надлежащая структура каталогов пакета.] Откройте ваш Web-браузер и введите
следующий URL, чтобы протестировать страницу plugin.jsp:
http://localhost:8080/advjhtpl/jsp/plugin.jsp
На копиях экрана на рис. 3.15 показано выполнение апплета в Microsoft
internet Explorer 5.5 и в Netscape Navigator 6.0.
1<!-- Рис. 3.15. plugin.jsp —>
2
3 <html>
4
5 <head>
6 <title>Using jsp:plugin to load an applet</title>
7 </head>
S
9 <body>
10 <jsp:plugin type = "applet"
11 code = "com.deitel.advjhtpl.jsp.applet.ShapesApplet"
12 codebase = "/advjhtpl/jsp"
13 width = "J00"
14 height = "flOO1^
15
16 <jsp :paranis>
П <jsp:paiam name = "red" value = "255" />
18 <jsp:param name = "green" value = "255" />
19 <jsp:param name = "blue" value = "0" />
20 </jsp:params>
21
22 </3sp:plugin>
23 </body>
24 </htitll>
Рис. 3.15. Использование действия <jsp:plugin> для встраивания апплета Java2
в JSP-страницу (часть 1)
JavaServer Pages (JSP)
133
|j lib .jg^'ngT; FW»ty Too* нф '."'J Vp
J**t»gidKt°:fAitJica,3ll8niatitml/Hifelm>n.№ '±|'(^Ч:
"в.
aw
«i^lfet
Л
"3
^BJp^wWWiff*1'™!^*
т-гвдавдшН!
Рис. 3.15. Использование действия <jsp:plugin> для встраивания апплета Java2
в JSP-страницу (часть 2)
3.6.4. Действие <jsp:useBean>
Действие <jsp:useBean> дает возможность JSP-странице манипулировать
объектом Java. Это действие создает объект Java или находит существующий объект
для его использования JSP-страницей, В таблице на рис. 3.16 представлены
атрибуты действия <jsp:useBean>. Если атрибуты class и beanName не заданы,
контейнер JSP пытается найти имеющийся объект с типом, задаваемым атрибутом
type. Подобно неявным объектам JSP, объекты, задаваемые действием <jsp:
useBean>, имеют области видимости page (страница), request (запрос), session
(сеанс) или application (приложение), которые определяют, где эти объекты могут
использоваться в Web-приложении. Объекты с областью видимости page (страница)
доступны только для страницы, на которой они определены. В принципе,
несколько JSP-страниц могут иметь доступ к объектам с другими областями видимости.
Например, все JSP-страницы, которые обрабатывают один запрос, могут иметь
доступ к объекту с областью видимости request (запрос).
3 Типичная ошибка программирования 3.6
Должен быть задан один или оба атрибута действия <jsp:useBean>:
class и type; в противном случае возникнет ошибка на этапе трансляции.
134
Глава 3
На страницах многих современных Web-сайтов размещается меняющаяся
реклама. При каждом посещении одоой из таких стрениц в Web-браузере
пользователя, как правило, отображается новая реклама. Обычно щелчок мышью на
изображении с рекламой переносит вас на Web-сайт компании, поместившей рекламу.
Наш первый пример действия <jsp:useBean> демонстрирует простой компонент
JavaBeans, который циклически отображает пять рекламных изображений.
В этом примере в качестве рекламы используются рисунки обложек ряда книг по
программированию. При щелчке на обложке вы переходите на Web-сайт
Amazon.com, где сможете прочесть информацию о книге и, возможно, заказать ее.
Компонент Rotator (рис. 3.17) определяет три метода: get Image, getLink
и nextAd. Метод getlmage возвращает имя файла изображения для рисунка
обложки. Метод getLink возвращает гиперссылку на информацию о книге на сайте
Amazon.com. Метод nextAd обновляет объект Rotator, чтобы следующие
обращения к методам getlmage и getLink возвращали информацию о другой рекламе.
Методы getlmage и getLink представляют, соответственно, свойства только для
чтения image и link компонента JavaBeana. Объект Rotator отслеживает текущую
рекламу с помощью переменной selectedlndex, которая обновляется путем вызова
метода nextAd.
Атрибут
id
scope
class
beanName
type
Описание
Имя, используемое для манипулирования объектом Java с помощью
действий <jsp:setProperty> и <jsp:getProperty> Переменная с этим именем
также объявляется для использования ее в элементах сценария JSP.
Задаваемое здесь имя чувствительно к регистру.
Область видимости, в которой доступен объект Java; page, request, session
или application. Область видимости по умолчанию * раде (страница).
Полное имя класса объекта Java.
Имя компонента, которое используется методом instantiate клзсса
java.beans.Beans для загрузки компонента JavaBeans в память.
Тип компонента JavaBeans. Этот тип может совладать с типом атрибута class,
быть суперклассом этого типа или интерфейсом, реализуемым этим типом.
Значением по умолчанию является тип class Исключение ClassCastException
возбуждается, если тип объекта Jsva не совпадает с типом, задзвземым
атрибутом type.
Рис. 3.16. Атрибуты действия <jsp:useBean>
1 // Рис. 3.17. Rotator.java
Iff Компонент JavaBeans для создания меняющейся рекламы.
3 package com.deitel.advjhtpl.jsp.beans;
4
5 public class Rotator {
6 private String images[] = { "images/jhtp3,jp^",
7 "images/xmlhtpl.jpg", "images/ebechtpl.jpg",
8 "images/iw3htpl.jpg", "images/cpphtp3.jpg"};
9
10 private String links[] = {
11 "http://www.amazon.com/exec/obidos/ASIN/0130125075/" +
12 "deitelassociatin",
13 "http://www.ama2on.com/exec/obidos/ASIN/01302SJ173/" +
14 "deitelassociatin",
15 "http://www.amazon.com/exec/obidos/ASIN/01302e419X/" +
JavaServer Pages (JSP)
135
16 "deitelassociatin",
17 "http://www.amazon.conL/exec/obidos/ASIN/0130161438/" +
18 "deitelassociatin",
19 "http://vfww.amazon.com/exec/obidos/ASIiJ/0130895717/" +
20 "deitelassociatin" };
21
22 private int selectedlndex = 0;
23
24 // возврат файла изображения для текущей реклам»
25 public String getlmageO
26 {
27 return images[ selectedlndex ];
28 )
29
30 // возврат URL Web-сайта, разместившего рекламу
31 public String getLinM)
32 {
33 return links[ selectedlndex ];
34 }
35
36 // обновление индекса selectedlndex, чтобы последующие
37 // обращения к getImage и getLink возвращали другую рекламу
38 public void nextAdO
39 {
40 selectedlndex = ( selectedlndex + 1 ) % images.length;
41 >
«J „__
Рис. 3.17. Компонент Rotator, который работает с несколькими рекламными
изображениями
В строках 7- 8 JSP-страницы adrotator.jsp (рис. 3.18) извлекается ссылка на
экземпляр класса Rotator. Атрибут id для компонента имеет значение rotator.
JSP-страница использует это имя для манипулирования компонентом. Объект
имеет сеансовую область видимости (session), чтобы каждый отдельный клиент
видел одну и ту же последовательность рекламных изображений в течение сеанса
просмотра. Когда JSP-страница adrotator.jsp получает запрос от нового клиента,
контейнер JSP создает компонент и сохраняет его в JSP-странице в данных сеанса
session для этого клиента (объект типа HttpSession). Для каждого запроса к этой
JSP-странице в строке 22 используется ссылка rotator, созданная в строке 7,
чтобы вызвать метод nextAd компонента Rotator. Таким образом, каждый запрос
будет получать следующую рекламу, обслуживаемую компонентом Rotator. В
строках 29-34 определяется гилерссылка на сайт Amazon.com для определенной книги.
В строках 29-30 вводится действие <jsp:getProperty> для получения значения
свойства link компонента Rotator. Действие <jsp:getProperty> имеет два
атрибута — name и property — которые задают объект, подлежащий манипулированию,
и свойство, значение котпрого следует получить. Если объект JavaBeans использует
стандартные соглашения по именованию компонентов JavaBeans, для получения
значения свойства link из компонента следует использовать метод get!.ink.
Действие <jsp:getProperty> вызывает метод getLink для компонента, на который
указывает ссылка rotator, преобразует возвращенное значение в строку и выводит эту
строку в составе ответа клиенту. Свойство link становится значением атрибута
href гиперссылки. Гиперссылка представляется на итоговой Web-странице в виде
изображения обложки книги. В строках 32-33 создается элемент img и использу-
136
Глава 3
ется другое действие <jsp:getProperty> для получения значения свойства image
компонента Rotator.
Имейте в виду, что свойства link и image могут быть получены с помощью
выражений JSP. Например, действие <jsp:getProperty> в строках 29-30 может быть
заменено выражением
<%=rotator.getLink( )%>
Аналогично, действие <jsp:getProperty> в строках 32-33 может быть заменено
выражением
<%=rotator,getlmage( )%>
1 <?xml version = "1.0"?>
2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
3 "http://www.w3.org/TR/xhtmll/DTD/xhtmll-strict.dtd">
4
5 <!-- Рис. 3.18. adrotator.jsp -->
e
7 <jsp:useBean id = "rotator" scope = "session"
8 class - "com.deitel.advjhtpl.jsp.beans.Rotator" />
9
10 <html xmlns = "http://www.w3.org/1999/xhtml">
11
12 <head>
13 <title>AdRotator Example</title>
14
15 <style type = "text/ess">
16 .big { font-family: helvetica, arial, sans-serif;
17 font-weight: bold;
18 font-size: 2em; >
19 </style>
20
21 <%-- обновление рекламы —%>
22 <% rota tor. nextAdO ; %>
23 </head>
24
25 <body>
26 <p class = "big">AdHotator Example</p>
27
28 <p>
29 <a href = "<3sp:getProperty name = "rotator"
30 property = "link" />">
31
32 <img sre = "<jsp:getProperty name = "rotator"
33 property = "image" />" alt = "advertisement" />
34 </a>
35 </p>
36 </body>
37 </html>
Рис. 3.18. JSP-страница adrotator.jsp использует компонент Rotator для отображения
новой рекламы при каждом следующем запросе страница (часть 1)
JavaServer Pages (IS?)
137
№№$«**£; .7-^Vm<i ^ГШШмЩШШЩ
Рис. 3.18. JSP-страница adrotator.jsp испогьзует компонент Rotator для отображений
новой рекламы при каждом следующем запросе страницы {часть 2)
Чтобы протестировать JSP-страницу adrotator.jsp в Tomcat, скопируйте файл
adrotator.jsp в каталог jsp, созданный в разделе 3.3. Вы должны были
скопировать каталог Images в каталог jsp, когда тестировали JSP-страницу,
представленную на рис. 3.10. Если вы этого не сделали, скопируйте каталог images сейчас.
Скопируйте файл Rotator.class в каталог WEB-INF\classes корня advjhtpl Web-
приложения в Tomcat. [Замечание. Этот пример будет работать только в том
случае, если в каталоге classes определена надлежащая структура каталогов для
Rotator. Класс Rotator определен в пакете com.deitel.advjhtpl.jsp.beans.]
Откройте ваш Web-браузер и введите следующий URL, чтобы протестировать
JSP-страницу adrotator.jsp:
http://localhost:8080/advjhtpl/jsp/adrotator.jap
Попробуйте несколько раз перезагрузить эту JSP-страницу в вашем браузере,
чтобы увидеть, как меняется реклама при каждом последующем запросе.
Действие <jsp:setProperty> может задавать значения свойств компонента
JavaBeans. Это действие особенно полезно для связывания значений параметров
запроса со свойствами компонента JavaBeans. Параметры запроса могут быть
использованы для установки свойств примитивных типов boolean, byte, char, int,
long, float и double, а также типов String, Boolean, Byte, Character, Integer,
Long, Float и Double из пакета java.lang. Атрибуты действия <jsp:setProperty>
приведены в таблице на рис. 3.19.
13S
Глава 3
Атрибут
name
property
pa ram
value
Описание
Идентификатор компонента JavaBeans, для которого будет задаваться
свойство (или свойства).
Имя задаваемого свойства. Задан/е "«" в качестве значения для этого
атрибута приводит к тому, что JSP-страница отождествляет параметры запроса
со свойствами компонента. Для каждого параметра запроса, который
отождествляется (т.е. имя параметра запроса идентично имени свойства
компонента) с соответствующим свойством компонента, этот параметр
задается в качестве значения свойства. Если в качестве значения параметра
задано "", значение свойства компонента остается неизменным.
Если имена параметров запроса не совпадают с именами свойств
компонента, этот атрибут может использоваться для указания, какой
параметр запроса следует использовать для получения значения
определенного свойства компонента. Этот атрибут является необязательным
Если атрибут не задан, имена параметров запроса должны совпадать
с именами свойств компонента.
Значение, которое присваивается свойству компонента. Значение обычно
является результатом выполнения выражения JSP. Этот атрибут особенно
полезен для задания свойств компонента, которые не могут быть заданы
с помощью параметров запроса. Этот атрибут является необязательным. Если
атрибут не задан, свойство компонента JavaBeans должно иметь такой тип
данных, который допускает установку через параметры запроса. |
Рис. 3.19, Атрибуты действия <jsp:setProperty>
Типичная ошибка программирования 3.7
Используйте атрибут value действия <J8p:setProperty> для задания
типов свойств компонента JavaBeans, которые не могут быть заданы с
помощью параметров запроса; в противном случае возникнет ошибка
преобразования.
Общая методическая рекомендация 3.10
Действие <jsp:setProperty> может использовать значения параметров
запроса для задания свойств компонента JavaBeans только для следующих
типов свойств: String, примитивных типов (boolean, byte, char, short, int,
long, float и double) и классов-оберток для типов (Boolean, Byte,
Character, Short, Integer, Long, Float и Double).
В качестве следующего примера рассмотрим приложение гостевой книги,
которое дает возможность пользователям помещать свое имя, фамилию и адрес e-mail
в базу данных. После отправки этой информации пользователю отображается
Web-страница, содержащая перечень всех пользователей, зарегистрированных
в гостевой книге. Адрес e-mail каждого лица отображается в виде гиперссылки,
которая позволяет пользователю отправлять этому лицу сообщения электронной
почты. Пример демонстрирует применение действия <jsp:setProperty>. Кроме
того, пример знакомит с директивой page JSP и страницами ошибок JSP.
Пример приложения гостевой книги состоит из компонентов JavaBeans Guest-
Bean {рис. 3.20) и GuestDataBean (рис, 3.21), а также JSP-страницы guestBook-
ErrorPage.jsp (рис. 3.24). Образцы выходных результатов для этого примера
представлены на рис. 3.25.
JavaServer Pages (JSP)
13Э
Компонент JavaBeans Guest Bean (рис. 3.20) определяет три свойства,
относящихся к гостю: имя firstNamc, фамилия lastName и адрес электронной почты
email. Каждое из свойств является свойством для чтения/записи и предполагает
применение методов set и get для манипулирования свойством.
1 // Рис. 3.20. GuestBean.Java
2 // Компонент JavaBeans для хранения данных о госте в гостевой книге.
3 package com.deitel.advjhtpl.j sp.beans;
4
5 public class GuestBean {
6 private String firstNamc, lastName, email;
7
8 // задание имени гости
9 public void setFirstName( String name )
10 {
11 firstName = name;
12 }
13
14 // получение имени гостя
15 public String getFirstName()
16 {
17 return firstName;
ia )
19
20 // задание фамилии гостя
21 public void setLastName( String name )
22 i
23 lastName = name;
24 )
25
26 // получение фамилии гостя
27 publio String getLastName{)
28 {
29 return lastName;
30 )
31
32 /У задание адреса e-mail гостя
33 public void setEmail( String address )
■ 34 (
35 email = address;
36 }
37
38 // получение адреса e-mail гостя
39 public String getEmailO
40 {
41 return email;
42 )
ДЗ ) ___
Рис. 3.20. Компонент Guest Book хранит информацию для одного гостя
Компонент JavaBeans Guest Da taBean (рис. 3.21) осуществляет соединение с
базой данных guestbook и предоставляет методы getGuestList и addGueat для
манипулирования базой данных. База данных guestbook имеет одну таблицу (guests),
содержащую три столбца (firstName, lastName и email). Мы предоставляем
сценарий SQL (guestbook.sql) вместе с этим примером, который может быть использо-
140
Глава 3
ван для создания базы данных Cloudscape guestbook. Другие подробности,
связанные с созданием базы данных с помощью Cloudscape, вы можете найти на сайте
www.cloudscape.com, а также в главе 8 книги «Технологии программирования на
Java 2. Книга 1».
1 // GuestDataBean.Java
2 // Класс GuestDataBean создает соединение с базой данных и
3 // поддерживает вставку и извлечение данных из базы данных.
4 package com.deitel.advjhtpl.j sp.beans;
5
6 // Набор базовых пакетов Java
7 import java.io.*;
8 import java.sql.*;
9 import Java.util.*;
10
11 public class GuestDataBean {
12 private Connection connection;
13 private PreparedStatement addRecord, getRecords;
14
15 // создание объекта TitlesBean
16 public GuestDataBean() throws Exception
17 ^
18 // загрузка драйвера Cloudscape
19 Class.forName( "COM.cloudscape.core.RmiJdbcDriver" );
20
21 // соединение с базой данных
22 connection = DriverManager.getCoiwiection(
23 "jdbc:rmi:jdbc:cloudscape:guestbook" );
24
25 getRecords =
2 6 connection.prepareStatement(
27 "SELECT firstMame, lastHаде, email FROM guosts"
28 );
29
30 addRecord =
31 connection.prepareStatement(
32 "INSERT INTO guests ( " +
33 "firstName, lastName, email ) " +
34 "VALUES ( ?, ?, ? )"
35 );
36 }
37
38 // возврат списка объектов GuestBean
39 public List getGuestList() throws SQLException
40 {
41 List guestList = new ArrayList();
42
43 // получение списка названий
44 KesultSet results = getRecords.executeQuery0;
45
46 // получение строки данных
47 while ( results.next{) ) {
48 GuestBean guest = new GuestBean();
49
50 guest.setFirstName( results.getString( 1 ) );
51 guest.setLastName{ results.getString< 2 ) );
JavaServer Pages (JSP)
141
52 guest.setEmail( results.getString( 3 ) } ;
53
54 guestList.add( guest ) ;
55 }
56
57 return guestList;
58 }
59
60 // вставка сведений о госте в базу данных гостевой книги
61 public void addGuest( GuestBean guest ) throws SQLExcepLion
62 {
63 addRecord.setString( 1, guest.getFirstName() );
64 addRecord. setStringt 2, guest. getLastNameO ) •'
65 addRecord.setString( 3, guest.getEmail() );
66
67 addRecord.executeUpdate{);
68 }
69
70 // закрытие операторов и завершение соединения с базой данных
71 protected void finalize(}
72 {
73 // попытка закрыть соединение с базой данных
74 try (
75 getRecords.close(};
76 addRecord.close();
77 connection.close();
78 }
79
80 // обработка исключения SQLException при операции закрытия
81 catch ( SQLException sqlException ) {
82 sqlException.prints taclcTrace () ;
83 >
84 }
85 }
Рис. 3.21. Компонент GuestDataBean осуществляет доступ к базе данных от имени
JSP-страницы guestBookLogin.jsp
Метод getGuestList (строки 39-58) класса GuestDataBean возвращает список
ArrayList объектов GuestBean, представляющих гостей в базе данных. Метод
getGuestList создает объекты GuestBean из результирующего множества ResultSet,
возвращенного подготовленным оператором (объект типа PreparedStatement) get-
Records, определенного в строках 25—28 и выполняемого в строке 44)
Метод add Guest класса GuestDataBean (строки 61-68) принимает в качестве
параметра объект GuestBean и использует свойства объекта GuestBean в качестве
параметров для подготовленного оператора (объект типа PreparedStatement)
addRecord, определенного в строках 30-35. Этот подготовленный оператор
(выполняемый в строке 67) помещает в базу данных сведения о новом госте.
Обратите внимание, что методы конструк'гора GuestDataBean getGuestList
и addGucst не обрабатывают потенциальные исключения. В конструкторе в строке
19 может возбуждаться исключение ClassNotFoundException, а другие операторы
могут возбуждать исключения SQLException. Аналогично, исключения SQL-
Exception могут возбуждаться из тела методов getGuestList и addList. В этом
примере мы сознательно разрешили передачу любых возникающих исключений
обратно JSP-странице, которая вызывает конструктор или методы класса Guest-
142
Глава 3
DataBean. Это дает возможность продемонстрировать применение страниц ошибок
JSP. Если JSP-страница выполняет операцию, которая возбуждает исключение,
JSP-страница может осуществить включение скриптлетов, перехватывающих
исключение и обрабатывающих его. Исключения, которые не были перехвачены,
могут быть отправлены для обработки в страницу ошибок JSP.
JSP-страница guestBookLogin.jsp (рис. 3.22) представляет собой
модифицированную версию JSP-страницы forwardl.jsp (рис. 3.11) и выводит форму, в которой
пользователи могут ввести свое имя, фамилию и адрес e-mail. Когда пользователь
отправляет форму, страница guestBookLogin.jsp запрашивается снова, чтобы
убедиться, что все значения данных были введен и. Если нет, страница
guestBookLogin.jsp еще раз выводит форму, чтобы пользователь мог заполнить пустое поле
(поля). Если пользователь заполнил все три поля, JSP-страница guestBook-
Login.jsp переадресовывает запрос к странице guestBook View.jsp, которая
отображает содержимое гостевой книги.
1 <?xml version = "1.0"?>
2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
3 "http://www.w3.org/TR/xhtmll/DTD/xhtmll-strict.dtd">
4
5<!-- Рис. 3.22. guestBookLogin.jsp —>
6
7 <%-- параметры страницы --%>
8 <%@ page errorPage = "guestBookErrorPage.jsp" %>
9
10 <%-- компоненты JavaBeans используемые этой JSP-страницей --%>
11 <jsp:useBean id = "guest" scope = "page"
12 class = "com.deitel.advjhtpl.jsp.beans.GuestBean" />
13 <jsp:useBean id = "guestData" scope = "request"
14 class = "com.deitel.advjhtpl.Jsp.beans.GuestDataBean" />
15
16 <html xmlns = "http://www.w3.org/1999/xhtral">
17
18 <head>
19 <title>Guest Book Login</title>
20
21 <style type = "text/css">
22 body (
23 font-family: tahoma, helvetica, arial, sans-serif;
24 )
25
26 table, tr, td {
27 font-size: . 9em;
2 8 border: 3px groove;
29 padding: 5px;
30 background-color: fdddddd,-
31 )
32 </style>
33 </head>
34
35 <body>
36 <jsp:setProperty name = "guest" property = "*" />
37
38 <% // начало скриптлета
39
40 if ( guest.getFirstHame() = null ||
41 guest.getLastMame() = null ||
JavaServer Pages (JSP)
143
42 guest.getEmail() == null ) {
43
44 %><%-- закрытие скриптлета для вставки данных с неизменной
структурой —%>
45
46 <form method = "post" action = "guestBooJcLogin. jsp":>
47 <p>Enter your first name, last name and email
46 address to register in our guest book.</p>
49
50 <table>
51 <tr>
52 <td>First name</td>
53
54 <td>
55 <input type ■ "text" name = "firstName" />
56 </td>
57 </tr>
58
59 <tr>
60 <td>Last name</td>
61
62 <td>
63 <input type = "text" name = "lastName" />
64 </td>
65 </tr>
66
67 <tr>
68 <td>Email</td>
69
70 <td>
71 <input type - "text" name = "email" />
72 </td>
73 </tr>
74
75 <tr>
7 6 <td colspan = "2">
77 <input type ■ "submit"
78 value ■ "Submit" />
79 </td>
80 </tr>
81 </table>
82 </form>
83
84 <% // продолжение скриптлета
85
86 } // конец блока if
87 else {
88 guestData.addGuest( guest );
89
90 %> <%— закрытие скриптлета для вставки действия jsp:forward —%>
91
92 <%— переход к отображению содержимого гостевой книги —%>
93 <jsp:forward page = "guestBookView.jsp" />
94
95 <% // продолжение скриптлета
96
144
Глава 3
97 } // конец блока else
98
99 %> <%— конея скриптлета —%>
100 </body>
101
102 </html>
Рис. 3.22. JSP-стрзница guestBookLogin.jsp дает возможность пользователю отправлять
имя, фамилию и адрес e-mail для помещения их в гостевую книгу
Строка 8 в листинге кода страницы guestBookLogin.jsp содержит директиву
page, которая определяет информацию, глобально доступную на JSP-странице.
Директивы заключаются в ограничители <%@ и %>. В данном случае атрибут ег-
rorPage директивы page указывает на страницу guestBookErrorPage.jsp (рис. 3.24),
т.е. все не перехваченные исключения пересылаются для обработки странице
guestBookErrorPage.jsp. Полное описание директивы page дается в разделе 3.7.
В строках 11-14 определяются два действия <jsp:useBean>. В строках 11-12
создается экземпляр класса GuestBean с именем guest. Этот компонент имеет
етраничную область видимости (page): он пригоден для использования только на
этой странице. В строках 11-14 создается экземпляр класса GuestDataBean с
именем guestData. Этот компонент имеет область видимости в пределах запроса
(request) и пригоден для использования на этой странице и на любых других
страницах, которые содействуют обработке одного клиентского запроса. Таким
образом, если страница guestBookLogin.jsp переадресует запрос странице guestBook-
Vie-w.jsp, компонент GuestDataBean по-прежнему остается доступным для
использовании на странице guestBookYiew.jsp.
В строке 36 демонстрируется задание для свойств компонента GuestBean с
именем guest значений, получаемых из параметров запроса. Элементы input в строках
55, 63 и 71 посят те же имена, что и свойства объекта GuestBean. В этой связи
используется способность действия <jsp:SetPropcrty> отождествлять параметры
запроса со свойствами путем задания значения "*" для атрибута property. Можно
также индивидуально задавать свойства с помощью следующих строк:
<jsp:setProperty name = "guest" property = "firstName"
paiam = "firstName" />
<jsp:setProperty name = "guest" property = "lastName"
param = "lastName" />
<jsp:setProperty name = "guest" property = "email"
param ~ "email" />
Если параметры запроса носят имена, которые отличаются от имен свойств
компонента GuestBean, атрибут param в каждом из приведенных выше действий <jsp:
SetProperty> может быть заменен на имя соответствующего параметра запроса.
JSP-страница guestBoofeView.jsp (рис. 3.23) формирует XHTML-документ,
содержащий записи гостевой книги в табличном формате. Директивы page
определены в строках 8-10. В строке 8 страница guestBookErrorPage.jsp задается в
качестве страницы обработки ошибок для данной JSP страницы. В строках 9-10 мы
встречаем новый для нас атрибут import директивы page. Атрибут import
позволяет программистам задавать классы и пакеты Java, которые используются в
контексте JSP-страницы. В строке 9 задается, что в этой JSP-странице используются
классы из пакета java.util, а в строке 10 задается, что используются также классы
из пакета com.deitel.advjhtpl.jsp.bean.
JavaServer Pages (JSP)
145
В строках 13-14 задается действие <jsp:useBe»n>, которое получает ссылку на
объект GuestDataBean. Если объект GuestDataBean уже существует, действие
возвращает ссылку на имеющийся объект. В противном случае действие создает
объект GuestDataBean для использования его этой JSP-страницей. В строках 50-59
определен скриптлет, который получает список гостей из объекта GuestDataBean,
и начинается цикл вывода записей. Б строках 61-70 совместно используются
текст с неизменной структурой и выражения JSP для создания строк в таблице
данных гостевой книги, которая будет отображаться клиенту. Скриптлет в
строках 72-76 завершает цикл.
1 <?xml version = "1.0"?>
2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
3 "http://www.w3.org/TR/xhtmll/DTD/xhtmll-strict.dtd">
4
5<(— Рис. 3.23. guestBookView.jsp —>
6
7 <%-- параметры страницы —%>
8 <%@ page errorPage = "guestBookErrorPage.jsp" %>
9 <%@ page import = "ja.va.util.*" %>
10 <%@ page import = "com.deite1.advjhtpl.jsp.beans.*" %>
11
12 <%— компонент GuestDataBean для получения списка гостей --%>
13 <jsp:useBean id = "guestData" scope = "request"
14 class = "com.deitel.advjhtpl.jsp.beans.GuestDataBean" />
15
16 <n.tiul Ktilns = "http://www.w3.org/1959/xht»l">
17
18 <head>
19 <title>Guest List</title>
20
21 <style type = "text/css">
22 body {
23 font-family: tahoma, helvetica, arial, sans-serif;
24 }
25
26 table, tr, td, th {
27 text-align: center;
28 font-size: . 9em;
29 border: 3px groove;
30 padding: 5px;
31 background-color: #dddddd;
32 }
33 </style>
34 </head>
35
3 6 <body>
37 <p style = "font-size: 2em;"XSuest List</p>
3B
39 <table>
40 <thead>
41 <tr>
42 <th style = "width: lOOpx;">Last name</th>
43 <th style = "width: lQQpx; ">First name</th>
44 <th style = "width: 200px; ">EroaiK/th>
45 </tr>
46 </thead>
47
146
Глава 3
48
49
50
51
52
53
54
55
56
57
56
59
60
61
62
63
64
65
66
67
6S
69
70
71
72
73
74
75
76
77
78
79
80
81
82
<
<tbody>
<% // начало скриптлета
List guestList = guestData.getGuestList[);
Iterator guestListlterator = guestList.iterator();
GuestBean guest;
while ( guestListlterator.hasNext{) ) {
guest = ( GuestBean ) guestListlterator.next[);
%> <%— закрытие скриптлета для вставки данных
с неизменной структурой —%>
<tr>
<tdx%= guest. getLastMameO %x/td>
<tdX%= guest.getFirstName() %X/td>
<td>
<a href = "ntailto:<%= guest.getEmail () %>">
<%= guest.getEmailO %X/a>
</td>
</tr>
<% // продолжение скриптлета
} // конец блоха while
%> <%-- конец скриптлета --%>
</tbody>
/table>
</body>
c/html>
Рис. 3.23. JSP-страиица guestBookView.jsp отображает содержимое гостевой книга
JSP-страница (рис. 3.24) выводит XHTML-доку мент, содержащий сообщение об
ошибке, в зависимости от типа исключения, которое привело к вызову страницы
ошибок. В строках 8-10 определено несколько директив pages. В строке 8 исполь-
% зуется атрибут isErrorPage директивы page. Задание для этого атрибута значения
true приводит к созданию страницы сообщений об ошибках JSP и разрешает
доступа к неявному объекту exception JSP, который ссылается на объект
исключения, указывающий на наличие проблемы.
Типичная ошибка программирования 3.8
Неявный объект JSP exception может использоваться только
страницами обработки ошибок. Использование этого объекта другими JSP-страни-
цами приводит к возникновению ошибки на этапе трансляции.
В строках 29-46 определены скриптлеты, которые выявляют тип имевшего
место исключения. Здесь нее начинается вывод соответствующего сообщения об
ошибке с помощью данных с неизменной структурой. Фактическое сообщение об
ошибке для исключения выводится в строке 56.
JavaServer Pages (JSP)
147
1 <?xml version = "1.0"?>
2 <"DOCTYPE html PUBLIC "-//W3C//DTD XHTHL 1.0 Strict//EN"
3 "http://www.w3.org/TR/xhtnai/DTD/athtnill-strict.dtd">
4
5 <!— Рис. Э.24. guestBookErrorPage.jsp —>
6
7 <%-- параметры страницы —%>
8 <%@ page isErrorPage = "true" %>
9 <%@ page import = "java.util.*" %>
10 <%@ page import = "java.sql.*" %>
11
12 <html xmlns = "http://www.w3.org/1999/xhtml">
13
14 <head>
15 <title>Error!</title>
16
17 <style type = "text/css">
18 .bigRed {
19 font-size: 2em;
20 color: red;
21 font-weight: bold;
22 }
23 </style>
24 </hea.d>
25
2 6 <body>
27 <p class = "bigRed">
28
29 <% // схриптлет для определения типа исключения
30 //и вывода начала сообщения об ошибке
31 if ( exception instanceof SQLException )
32 %>
33
34 An SQLException
35
36 <%
37 else if ( exception instanceof ClassHotFoundException )
38 %>
39
40 A ClassNotFoundException
41
42 <%
43 else
44 %>
45
46 An exception
47
48 <%— закрытие скриптлета для вставки данных
с неизменной структурой —%>
49
50 <%-- продолжение вывода сообщения об ошибке --%>
51 occurred while interacting with the guestbook database.
52 </p>
53
54 <p class = "bigRed">
55 The error message was:<br />
56 <%= exception. getMessageO %>
148
Глава 3
57
58
59
60
€1
62 </html>
</р>
<р class = "bigRed">Please try again later</p>
</body>
Рис. 3.24. JSP-страница guest BookErrorPage.jsp реагирует на исключения, возникающие
на страницах guestBookLogin.jsp и guestBookView.jsp
На рис. 3.25 показаны образцы взаимодействий между пользователем и JSP-
страницами в приложении для гостевой книги. В первых двух случаях (два
верхних ряда копий экранов) разные пользователи вводят свое имя, фамилию и адрес
e-mail. Каждый раз текущее содержимое гостевой книги возвращается и
отображается пользователю. В третьем случае пользователь указал адрес e-mailj который
уже имеется в базе данных. Адрес e-mail является первичным ключом таблицы
guest базы данных guestbook, поэтому его значения должны быть уникальными.
Таким образом, база данных препятствует вставке новой записи, и возникает
исключение. Исключение пересылается для обработки странице guest
BookErrorPage.jsp, результат которого показан на последней копии экрана.
■AdfrBg^H^JftynlTegaMllrtvttBlJfep.'gigattMUc^Ha! ^j ^^
Enter you first name, last name end email adoress
bo register In our guest bock.
Ftotnmh
1 Letrome
bn*
Isuhmjj
jPaul
Deuel ;
|dei[el@dertet.cbm 1
ёд&и"
Ф^ВШйкй?-
Ш: ■■, Т±.1ШШ^1*№1к,^..,.^ Г~ ,
Guest List
■Ц
| Let na™ I! First rune I
1_1;Ш: Щ::"r&Wj
EmsJ |-
"-" .tSje«j6iBI,cora:.'- Jl
ш~]щщ^ш^Е^Ш!}£~^^^^~
\&--** *» Htttoft>»:
Fonerjitf " 9np
\fWw\<Qm&-.flkKilbrt:9aW*drt&ilwt^*aQdij^& ^1 ^&>
Enter your first name, last name and email iridess
to register in our guest bock.
73
Festive*?'
asrj«n»|'|SaiHfy_
j ?ean® bug ?t»ug .oom
Jj
*]ч
Tr^SiiST
d
frtdiwgS |^3 http^JcxralimrBDBDfMrv^lfltpfguwtBQQliloqp )tp ^[ ^d'
лзет:
'ЙЭ^^-ГйЖ*
Guest List
|.-w.ttii%; f Щ»уп*|?}
| мм 1|--№-1
F «^"it-Jfwv-.]
...bnal j
— j.
sa*i«*uaaiio_nw i|
IE"
^ШЗШШКЩШЙ
Рис. 3.25. Образцы выходных результатов для JSP-страницы гостевой книги (часть 1)
JavaServer Pages (JSP)
149
j^^B*Jta*»toUn*i«ffl*™iBi 2i &^,
i] fit Gdt 4e» fn&ijn leif J*t*^|-7-. .::Щ?&Г'*-;:'! 2!
Enter vor first ГкЭте, last name 2nd emal address
to register in :>jr g№« bxk
13
ffttrunw
l£$tn*re
Erred
(jftSWii:
Ji^-J,
_—__—___
j |Ha>vey j
i
\bv> J
|dei:sl@dEi'elcam j
i
J
iQiSj^ir ~^—:. таэйщгщяа^мг-- -^
«в : £*'j; №• . .fsvtrte ■ lnds неф
»ЭД а
■a' la
jaJ&mj |^http:/^ahcU:308a/advrepl^p;gu№cEoobLixi jsp
!3-.«!й
An SQLException occurred while
interacting with the «liestbook database.
The error message was:
The statement was aborted because it would
have caused a duplicate key value in a
unique or primary key constraint.
Please try again later
ГЗ
©в
"WSS^SSF
Рис. 3.25. Образцы выходных результатов для JSP-страницы гостевой книги (часть 2)
Чтобы протестировать гостевую книгу в Tomcat, скопируйте файлы guestBoofe-
L о gin. jsp, guestBookView.jsp и guestBookErrorPage.jsp в каталог jsp, созданный
в разделе 3.3. Скопируйте файлы GucstBean.class и GuestDataBcan.class в
каталог WEB-INF\classes корень advjhtpl Web-приложения в Tomcat, [Замечание.
Этот пример будет работать только в том случае, если в каталоге classes
определена надлежащая структура каталогов для классов GuestBean и GuestDataBcan. Эти
классы определены в пакете com.dcitcl.advjhtpl.jsp.beans.] Откройте ваш Web-
браузер и введите следующий URL, чтобы протестировать JSP-страницу guest-
BookLogin.jsp:
http://localhost:80B0/advjhtpl/jsp/guestBookLogin.jsp
3.7. Директивы
Директивы представляют собой сообщения контейнеру JSP, посредством
которых программист может задавать параметры и настройки для страницы (например,
задавать страницу обработки ошибок), включать содержимое из других ресурсов
и задавать собственные библиотеки нестандартных тегов для их использования
150 Глава 3
в JSP-странице. Директивы (ограничиваемые символами <%@ и %>)
обрабатываются на этапе трансляции. Таким образом, директивы не формируют выходных
данных немедленно, поскольку они обрабатываются до того, как JSP-страница
принимает какие-либо запросы. В таблице на рис. 3,26 описаны три типа директив. Эти
директивы будут рассмотрены в следующих нескольких подразделах.
3.7.1. Директива page
Директива page задает глобальные настройки для JSP-страницы в контейнере
JSP. Может использоваться несколько директив page при условии, что при этом
имеется только одно вхождение каждого из атрибутов. Единственным
исключением из этого правила является атрибут import, который может многократно
применяться для импортирования пакетов Java, используемых JSP-страницей. В
таблице на рис. 3.27 представлены атрибуты директивы page.
Директива
раде
include
taglib
_
Описание I
Определяет параметры страницы для обработки их контейнером JSP. ;
Заставляет контейнер JSP выполнять вставку содержимого другого ресурса
на этапе трансляции. При трансляции JSP-страницы в сервлет
и компиляции указываемый файл замещает директиву include
и транслируется, как если бы он изначально являлся частью JSP-страницы.
Дает возможность программистам включать свои собственные теги
в виде библиотек тегов. Эти библиотеки могут использоваться для
инкапсулирования функциональных возможностей и упрощения процесса
создания кода для JSP-страницы.
Рис. 3.26. Директивы JSP
-*- Типичная ошибка программирования 3.9
Использование нескольких директив page, у которых один или более
атрибутов совпадают, приводит к ошибке на этапе трансляции
JSP-страницы.
Типичная ошибка программирования 3.10
Использование директивы page с атрибутом или значением, которое не
распознается, приводит к ошибке на этапе трансляции JSP-страницы.
Атрибут
language
extends
import
_
Описание
Язык сценария, используемый JSP-crpaht^e^ На данный момент
единственным допустимым значением для этого атрибута является Java.
Задает класс, котооому будет наследовать транслированная JSP-страница.
В качестве значения этого атрибута должно задаваться полное имя пакета
или класса.
Задает список отделяемых запятыми имен классов и/или пакетов,
которые будут использоваться в текущей JSP-cipdHHue. Если нзыком
сценария является Java, список для импортирования по умолчанию имеет
вид: java.lang.*, javax.servlet.*, javax.servlet.jsp.*. javax.servlet.http.*
Если задано несколько свойств import, контейнер помещает имена
пакетов в список.
avaServer Pages (JSP)
151
Атрибут
session
juffer
autoFlush
isThreadSafe
info
чггогРаде
isErrorPage
conatAntType
Описание
Задает, участвует ли страница а сеансе. Значением этого атрибута может
быть true (участвует в сеансе - по умолчанию) или false (не участвует
в сеансе). Если страница участвует в сеансе, неявный объект JSP session
доступен для использования на странице. В противном случае объект
session недоступен, и использование его в коде сцензрия приводит
к ошибке на этапе трансляции.
Задает размер буфера аызода, используемого неявным объектом out.
Этот зтрибут может иметь значение попе при отсутствии буферизации,
либо конкретное значение, например, 8 kb (размер буфера по
умолчанию). Спецификация JSP указывает, что размер используемого
буфера должен быть не меньше заданного размера.
При установке значения true (значение по умолчанию) этот атрибут
указывает, что буфер вывода, используемый с неявным объектом out.
должен автоматически очищаться при заполнении буферз. При установке
значения false в случае переполнения буфера возбуждается исключение.
Для этого атрибута следует установить значение true, если для атрибута
buffer задано значение попе.
Определяет, обеспечивает ли страница безопасное выполнение потоков
(thread safe). При задании значения true (по умолчанию) считается, что
страница обеспечивает безопасность программных потоков и может
обрабатывать несколько запросов одновременно. При задании значения
false сервлет, который представляет страницу, реализует интерфейс
java.iang.SingleThreadModel, ^ этой JSP-страницей может одновременно 1
обрабатываться только один запрос. Стандарт JSP допускает
существование нескольких экземпляров J5P для JSP-страниц, которые не
являются безопасными для выполнения потоков Это позволяет
контейнеру более эффективно обрабатывать запросы Однако при этом
не гарантируемся, что доступ к ресурсам, совместно используемым
экземплярами J5P, будет осуществляться посредством безогасных
программных потоков.
Задает строку информации, описываощей страницу. Эта строка
возвращается методом getServletlnfo сервлета, который представляет
собой транслированную JSP-страницу. Этот метод может быть вызван
посредством неявного объекта JSP page.
Любые, не перехваченные на текущей странице исключения,
отправляются дня обработки странице ошибок. Неявный объект
exception страницы ошибок ссылается на изначально возникшее
исключение.
Определяет, яэляется ли текущая страница страницей обработки ошибок,
которая будет вызываться в ответ на ошибку, имевшую место в другой
странице. Если атрибут имеет значение true, создается неявный объект
exception, который ссылается на изначально возникшее исключение.
Если атрибут имеет значение false (no умолчанию), любое использование
объекта exception на странице приводит к ошибке на этапе трансляции.
Задает MtME-тип данных в ответе клиенту. По умолчанию используется
Тип text/html.
'ис. 3.27. Атрибуты директивы page
152 Глава 3
Общая методическая рекомендация 3.11
Как указывается в разделе 2.7.1 спецификации JSP, атрибут extends «не
следует использовать без тщательного учета всех факторов, поскольку
он ограничивает возможность контейнера JSP предоставлять
специализированные суперклассы, которые могут улучшить качество
предоставляемого сервиса». Напомним, что класс Java может расширять только
один другой класс. Если в JSP-странице задан явный суперкласс,
контейнер JSP не сможет транслировать JSP-страницу в подкласс одного из
собственных усовершенствованных классов контейнера сервлетов в
приложении.
^ Типичная ошибка программирования 3.11
Использование неявного объекта JSP session в JSP-странице, для которой
не задан атрибут session директивы page со значением true, приводит
к ошибке на этапе трансляции,
3.7.2. Директива include
Директива include включает содержимое другого ресурса один раз, на этапе
трансляции JSP-страницы. Директива include имеет только один атрибут — file —
который задает URL включаемой страницы. Разница между директивой include
и действием <jsp:include> проявляется только в том случае, если включаемое
содержимое изменяется. Например, если определение XHTML-документа
изменяется после того, как оно было включено директивой include, при дальнейших
вызовах JSP-страницы будет отображаться изначальное содержимое XHTML-доку
мента, а не новое содержимое. В противоположность этому, действие <jsp:include>
обрабатывается при каждом запросе этой JSP-странице. Следовательно,
изменения включенного содержимого будут учтены при следующем запросе
JSP-страницы, которая использует действие <jsp:include>.
Общая методическая рекомендация 3,12
Спецификация JavaServer Pages 1,1 не предоставляет механизм для
обновления текста, включенного на JSP-страницу с помощью директивы
include. Версия 1.2 спецификации JSP предусматривает предоставление
контейнером такого механизма, но сама спецификация напрямую его не
предоставляет.
JSP-страница includeDirective.jsp (рис. 3.28) повторно реализует JSP-страницу
include.jsp (рис. 3.10) с помощью директив include. Чтобы протестировать JSP-
страницу includeDirective.jsp в Tomcat, скопируйте файл includeDirective.jsp
в каталог jsp, созданный в разделе 3.3. Откройте Web-браузер и введите
следующий IJRL, чтобы протестировать JSP-страницу includeDirective.jsp:
http://localhost:8080/advjhtpl/jsp/includeDirective.jsp
1 <?xiftl version = "1,0°?>
2 «C'DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
3 "http://www.w3_org/TR/xhtmll/DTD/xhtroll-strict.dtd">
4
5 <•— Рис. 3.28. includeDirective.jsp -->
6
7 <html xmlns = "http://www.w3.org/1999/xhtinl">
8
JavaServer Pages (JSP)
153
9 <head>
10 <title>Oaing the include directive*:/title>
11
12 <style type = "text/css">
13 body {
14 font-family: tahoma, helvetica, arial, sans-serif;
15 }
16
17 table, tr, td {
18 font-size: . 9em;
19 border: 3px groove;
20 padding: 5px;
21 background-color: #dddddd;
22 }
23 </style>
24 </head>
25
26 <body>
27 <table>
28 <tr>
29 <td style = "width: 160px; text-align: center">
30 <img src = "imagea/logotiny.png"
31 width = "140" height = "93"
32 alt - "Deitel & Associates, Inc. Logo" />
33 </td>
34
35 <td>
36
37 <%— включение документа banner.html в JSP-страницу --%>
38 <%@ include file = "banner.html" %>
39
40 </td>
41 </tr>
42
43 <tr>
44 <td style = "width: 160px">
45
46 <%-- включение документа toe.html в эту JSP-страницу --%>
47 <%@ include file = "toe.html" %>
48
49 </td>
50
51 <td style = "vertical-align: top">
52
53 <%-- включение документа clock2.jsp в эту JSP-страницу --%>
54 <%@ include file = "clock2.jsp" %>
55
56 </td>
57 </tr>
58 </table>
59 </body>
60 </html>
Рис. 3.28, JSP-страница indudeDirective.jsp демонстрирует включение содержимого на
этапе трансляции с помощью директивы include (часть 1)
154
Глава 3
Рис. 3.28. JSP-страница indudeDirective.jsp демонстрирует зключение содержимого на
этапе трансляции с помощью директивы include (часть 2)
3.8. Библиотеки нестандартных тегов
Ранее в этой главе мы выяснили, каким образом страницы JavaServer Pages
облегчают доставку динамического Web-содержимого. Теперь мы перейдем к
рассмотрению библиотек пользовательских (нестандартных) тегов JSP, которые
предоставляют другой механизм для инкапсулирования сложных функциональных
возможностей, используемых JSP-страницами. Библиотеки пользовательских тегов
определяют один или несколько нестандартных тегов, которые программисты
JSP-страниц могут использовать для создания динамического содержимого.
Функциональные возможности этих нестандартных тегов определены в классах Java,
которые реализуют интерфейс Tag (пакет javax.servlet.jsp.tagext), обычно путем
расширения класса TagSupport или класса BodyTagSupport. Этот механизм
позволяет программистам на Java создавать более сложные функциональные средства для
дизайнеров Web-страниц, не знакомых с программированием на Java.
Ранее мы познакомились с применением действия <jsp:useBean> и
компонентов JavaBeans для встраивания в JSP-страницу сложных, инкапсулированных
функциональных возможностей. Во многих случаях действие <jsp:useBean>
и компоненты JavaBeans могут выполнять те же задачи, что и нестандартные теги.
Однако применение действия <jsp:useBean> и компонентов JavaBeans имеет
недостатки: компоненты JavaBeans не могут манипулировать содержимым JSP-стра-
ницы, а дизайнеры Web-страниц должны обладать определенным знанием языка
Java, чтобы использовать в странице компоненты JavaBeans. В случае применения
нестандартных тегов дизайнерам Web-страниц не обязательно знать Java.
В этом разделе будут рассмотрены три примера нестандартных тегов. Каждый
из тегов является составной частью одной библиотеки тегов, которую мы назвали
advjhtpl. JSP-страница осуществляет включение библиотеки нестандартных тегов
с помощью директивы taglib. Атрибуты директивы taglib представлены в таблице
на рис. 3.29.
JavaServer Pages (JSP)
155
Атрибут
uri
tagPrefix
Описание
Задает относительный или абсолютный URI дескриптора библиотеки тегов.
Задает обязательный префикс, который отличает нестандартные
(пользовательские) теги от стандартных (встроенных) тегов.
Зарезервированными являются имена префиксов jsp, jspx, Java, javax.
servlet, sun и sunw. j
Рис. 3.29. Атрибуты директивы taglib
В каждом из примеров в этом разделе используется директива taglib.
Существует несколько типов нестандартных тегов, которые имеют различный уровень
сложности. Мы продемонстрируем применение простых тегов, простых тегов с
атрибутами и тегов, которые способны обрабатывать элементы в собственном теле.
Чтобы получить полную информацию о библиотеках нестандартных тегов,
обратитесь к ресурсам, приведенным в разделе 3.9.
3.8.1. Простой нестандартный тег
Рассмотрим первый пример, который реализует простой нестандартный тег,
вставляющий в JSP-страницу строку "Welcome to JSP Tag Libraries". При
реализации нестандартных тегов необходимо определить класс обработчика тега для
каждого из тегов, который реализует функциональные возможности тега; дескриптор
библиотеки тега, который предоставляет контейнеру JSP информацию о
библиотеке тегов и содержащихся в ней нестандартных тегах; а также JSP-страницу,
которая использует нестандартный тег. Первый пример использования нестандартного
тега представлен на рис. 3.30 (customTag Welcome .jsp). В конце этого раздела будет
рассказано, как настроить этот пример для тестирования его в Tomcat.
1 <?xml version = "1.0"?>
2 «C'DOCTYPE html POBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
3 "http://www.w3.org/TR/xhtmll/DTD/xhtmll-strict.dtd">
4
5 <!— Рис. 3.30. customTagWelcome.jsp -->
6 <!— JSP-страниыа, которая использует нестандартный тег для вывода
содержимого. -->
7
8 <%— директива taglib —%>
9 <%@ taglib uri = "advjhtpl-taglib.tld" prefix = "advjhtpl" %>
10
11 <html xmlns = "http://www.w3.org/1999/xhtna">
12
13 <head>
14 <title>Simple Custom Tag Example</title>
15 </head>
16
17 <body>
18 <p>Tne following text demonstrates a custom tag:</p>
19 <hl>
20 <advjhtpl:welcome />
21 </hl>
22 </body>
23
24 </html>
Рис. 3.30. JSP-страница aistomTagWelcome.jsp использует простой нестандартный тег
156
Глава 3
ьмямдишмшимиииЛйШ
Fie ЕЛ Vim.
gafj^S^C1 T- ' '^
*Г:*
Step..
1|Й|ЙИф:;|1и:а1Ь1^:а(ет;^^№1/]»/<^от1"11*=1сыпе.рр 3 ^?_J
The following :ea demist: ales a custom tag:
Welcome to JSP Tag Libraries!
ъ __ d
[d В»'.
jswrpy
Рис. 3.30. JSP-страница customTagWekome.jsp использует простой нестандартный тег
(часть 2)
Директива taglib в строке 9 дает возможность JSP-странице использовать теги
из нашей библиотеки тегов. Директива задает uri файла дескриптора библиотеки
(advjhtpl-taglib.tld, рис. 3.32), который предоставляет информацию о нашей
библиотеке тегов контейнеру JSP, и префикс prefix для каждого тега (advjhtpl).
Программисты JSP используют префикс библиотеки тегов при обращении к тегам,
содержащимся в определенной библиотеке. В строке 20 используется нестандартный
тег с именем welcome для вставки текста в JSP-страницу. Обратите внимание, что
имя тега предваряется префиксом advjhtpl. Это дает возможность контейнеру JSP
интерпретировать назначение тега и вызывать соответствующий обработчик
тега. Также имейте в виду, что строка 20 может быть записана с указанием
открывающего и закрывающего тегов следующим образом:
<advjhtpl:welcome> </advjhtpl:welcome>
На рис. 3.31 представлено определение класса WeleomeTagHandler:
обработчика тега, который реализует функциональные возможности нашего нестандартного
тега welcome. Каждый обработчик тегов должен реализовывать интерфейс Tag,
определяющий методы, которые контейнер JSP вызывает для встраивания
функциональных возможностей, описываемых тегом, в JSP-страницу. Большинство
классов обработчиков тегов реализуют интерфейс Tag путем расширения либо
класса TagSupport, либо класса Body TagSupport.
Общая методическая рекомендация 3.13
Классы, которые содержат определения обработчиков нестандартных
тегов, должны реализовывать интерфейс Tag из пакета javax.serv-
let.jsp.tagext.
Общая методическая рекомендация 3.14
Класс обработчика нестандартных тегов должен расширять класс Tag-
Support, если тело тега игнорируется, или если во время обработки не
стандартного тега осуществляется лишь вывод данных.
Общая методическая рекомендация 3.15
Класс обработчика нестандартных тегов должен расширять класс
BodyTagSupport, если обработчик взаимодействует с содержимым тела тега.
Общая методическая рекомендация 3.16
Обработчики нестандартных тегов должны быть определены в пакетах
Java.
JavaServer Pages (JSP)
157
Класс WelcomeTagHandler реализует интерфейс Tag, расширяя класс TagSup-
port (оба из пакета Java.servlet.jsp.tagext). Наиболее важными методами
интерфейса Tag являются методы doStartTag и doEndTag. Контейнер JSP вызывает
эти методы, когда обнаруживает начальный (открывающий) нестандартный тег
и конечный (закрывающий) нестандартный тег, соответственно. Эти методы
возбуждают исключения JspException, если при обработке нестандартного тега
возникают проблемы.
1 // Рис. 3.31. WelcomeTagHandler.Java
2 // Нестандартный обработчик тегов, обрабатывающий простой тег,
3 package com.deitel, advjhtpl. jsp. taglibrary ,-
A
5 // Набор базовых пакетов Java
6 import, j ava.io.*;
7
8 // Пакеты расширений Java
9 import javax.servlet.jsp.*;
10 import javax.servlet.jsp.tagext.*;
11
12 public class WelcomeTagHandler extends TagSupport (
13
14 // Метод/ вызываемый, чтобы начать обработку тега
15 public int doStartTag() throws JspException
16 {
П II попытка обработки тега
18 try 1
19 // получение объекта JspWriter для вывода содержимого
20 JspWriter out = pageContexfc.getOut();
21
22 // вывод содержимого
23 out.print{ "Welcome to JSP Tag Libraries!" );
24 }
25
26 // повторное возбуждение исключения IOException контейнеру
JSP как исключения JspException
27 catch( IOException ioException ) {
28 throw new JspException( ioException.getMessage{) );
29 }
30
31 return SKIP_BODY; // игнорировать тело тега
32 }
33 } ..
Рис. 3.31. Обработчик нестандартного тега WelcomeTagHandler
Общая методическая рекомендация 3.17
Если в классе обработчика нестандартного тега возбуждаются
исключения, отличные от JspException, они должны перехватываться и
обрабатываться. Если такие исключения препятствуют надлежащей
обработке тега, они должны быть возбуждены повторно как исключения типа
JspException.
В этом примере класс WelcomeTagHandler переопределяет метод doStartTag,
чтобы вывести текст, который становится частью ответа, посылаемого
JSP-страницей. В строке 20 используется объект pageContext обработчика нестандартного
158
Глава 3
тега (унаследованный от класса TagSupport) для получения объекта JSP
JspWriter, который метод doStartTag использует для вывода текста. В строке 23
осуществляется вывода строки с использованием объекта Jsp Writer. В строке 31
возвращается статическая целочисленная константа SKIP_BODY (определенная в
интерфейсе Tag) для указания, что контейнер JSP должен игнорировать любой текст
или другие элементы, которые присутствуют в теле тега. Чтобы включить тело
содержимого в ответ, задайте статическую целочисленную константу EVAL_BO-
DY__INCLUDE в качестве возвращаемого значения. В этом примере при
обнаружении контейнером JSP конечного тега какой-либо обработки не требуется, поэтому
мы не переопределяем метод doEndTag.
На рис. 3.32 представлено определение файла дескриптора библиотеки
нестандартных тегов. Этот XML-документ содержит информацию, необходимую для
контейнера JSP, например, номер версии библиотеки тегов (элемент tlibversion),
номер версии JSP (элемепт jspversion), сведения о библиотеке (элемент info) и
сведения о тегах в библиотеке (по одному элементу tag для каждого тега). В этом
дескрипторе библиотеки тегов элемент tag в строках 18-30 определяет наш
нестандартный тег welcome. В строке 19 задается имя пате тега, используемое
программистами JSP для доступа к нестандартным функциональным возможностям
с JSP-страниды. В строках 21-23 задается элемент tag class для класса
обработчика нестандартного тега. Этот элемент связывает имя тега с определенным классом
обработчика тега. Элемент bodycontext (строка 25) указывает, что наш
нестандартный тег имеет пустое (empty) тело. Этот элемент также может иметь значение
tagdependent или JSP. В строках 27-29 задается информацию о теге с помощью
элемента info. [Замечание. По мере необходимости мы будем знакомиться с
другими элементами дескриптора библиотеки тегов. Полное описание дескриптора
библиотеки тегов имеется в спецификации JavaRerver Pages 1.1, которую можно
загрузить по адресу Java.sun.com/products/jsp/downtoad.htmt.]
Общая методическая рекомендация 3.18
Класс обработчика нестандартною тега должен задаваться в элементе
tagclass дескриптора библиотеки тегов с указанием полного имени пакета.
1 <?xml version = "1.0" encoding = "ISO-8859-1" ?>
2<!DOCTYEE taglib PUBLIC
3 "-//Sun Microsystems, Inc.//DTD JSE Tag Library 1.1//EN"
4 "http://java.sun.com/j2ee/dtds/web-jsptaglibrary_l l.dtd">
5
6 <!— дескриптор библиотеки тегов —>
7
8 <taglib>
9 <tlibversion>l.0</tlibversion>
10 <jspversion>l.l</jspversion>
11 <shortname>advjhtpl</shortncune>
12
13 <info>
14 A simple tab library for the examples
15 </info>
16
1? <!— Простой тег, который осуществляет вывод содержимого —
18 <tag>
19 <name>welcome</name>
20
21 <tagclass>
JavaServer Pages (JSP) 159
22 com.deitel.advjhtpl. jsp.taqLibrary.WelcomeTagBandlei:
23 </tagclass>
24
25 <bodycontent>empty</bodycontent>
26
27 <in£o>
28 Inserts content welcoming user to tag libraries
29 </info>
30 </tag>
31 </taglib> ___^_
Рис. 3,32. Файл advjhtpl-taglib.tld дескриптора библиотеки нестандартных тегов
Чтобы протестировать JSP-страницу customTagWelcome.jsp в Tomcat,
скопируйте файлы customTagWelcome.jsp и advjhtpl-taglib.tld в каталог jsp, созданный
в разделе 3.3. Скопируйте файл WelcomeTagHandler.class в каталог WEB-INF\
classes Web-приложения advjhtpl на сервере Tomcat. [Замечание. Класс Welcome-
TagHandler должен быть размещен в каталоге classes с соблюдением надлежащей
структуры каталогов пакета. Класс WelcomeTagHandler определен в пакете
com.deitel. advjhtpl .jsp.taglibrary-] Откройте Web-браузер и введите следующий
URL, чтобы протестировать JSP-страницу customTagWelcome.jsp:
http://localhost:8080/advjhtpl/jsp/customTagWelcome.jsp
3.8.2. Нестандартный тег с атрибутами
Многие элементы XHTML И JSP используют атрибуты для настройки своих
функциональных возможностей. Например, элемент XHTML может иметь атрибут
style, который задает, каким образом элемент должен быть отформатирован в
клиентском Web-браузере. Аналогичным образом, элементы действий JSP имеют
атрибуты, которые помогают настроить их поведения на JSP-странице. Следующий
пример демонстрирует, как задавать атрибуты для нестандартных тегов,
JSP-страница, представленная на рис. 3.33 (customTagAttribute.jsp), схожа со
страницей, представленной на рис. 3.30. В этом примере для вставки текста
в JSP-страницу используется новый тег с именем welcome2, который
настраивается в зависимости от значения атрибута firstName. На копии экрана показаны
результаты действия тега weIcome2 в строках 20 и 30. Тег в строке 20 задает
значение "Paul" для атрибута iLrstName, В строках 26-28 определен скриптлет,
который получает значение параметра запроса нате и присваивает его строковой
ссылке name. В строке 30 ссылка name в выражении JSP используется в качестве
значения для атрибута firstName. На образце копии экрана эта JSP-страница была
вызвана с помощью следующего TJRL:
http://localhost:8080/advjhtpl/jsp/
customTagAttribute.jsp?firstName=Sean
1 <?xml version = "1.0"?>
2 <!DOCTYPE html PUBLIC "-//W3C//DTO XHTML 1.0 Strict//EN"
3 "http; //www.w3.org/TR/xhtrall/DTD/xhtJiai-strict.dtd">
4
5 <!— Рис. З.ЭЗ. customTagAttribute.jsp —>
6<!-- JSP-страница, нспольэугацая нестандартный тег для вывода
содержимого. -->
7
8 <%— директива taglib --%>
9 <%Й taglib uri = "advjhtpl-taglib.tld" prefix = "advjhtpl" %>
160
Глава 3
10
11 <html xmlns = "http://www.w3.org/1999/xhtira">
12
13 <head>
14 <title>Specifying Custom Tag Attributes</title>
15 </head>
16
17 <body>
18 <p>Demonstrating an attribute with a string value</p>
19 <hl>
20 <adv]htpl:welcome2 firstName = "Paul" />
21 </hl>
22
23 <p>Demonstrating an attribute with an expression value</p>
24 <hl>
25 <%— скриптлет для получения параметра паше запроса —%>
26 <%
27 String name = request.getParameter{ "name" );
2S %>
29
30 <advjhtpl:welcome2 firetNajne = "<%= name i>" />
31 </hl>
32 </body>
33
34 </html>
Рис. 3.33. Задание атрибутов для нестандартного тега
При определении обработчика нестандартного тега для тега с атрибутами
необходимо предоставить методы, которые дают возможность контейнеру JSP задавать
значения атрибутов в обработчике тега. Методы, которые манипулируют
атрибутами, следуют тому же соглашению о присвоении имен методам set- и get-, что
и свойства компонентов JavaBeans. Так, значение нестандартного атрибута first-
Name задается с помощью метода setFirstName. Аналогично, для получения
значения атрибута firstName использовался бы метод getFirstName (в данном
примере этот метод не определяется). Класс \Velcome2TagHandler (рис. 3.34) определяет
переменную firstName (сгрока 13) и соответствующий метод set setFirstName
(строки 37-40). Когда контейнер JSP обнаруживает на JSP-странипе тег welco-
JavaServer Pages (JSP)
161
ше2, он создает новый объект Welcome2TagHandler для обработки тега и задания
атрибутом тегов. Далее, контейнер вызывает метод doStartTag (строки 16-34) для
выполнения обработки нестандартного тега. В строках 24-25 значение атрибута
firstName используется как часть выводимого нестандартным тегом текста.
1 // Рис. 3.34. Welcome2TagHandler.java
2 // Нестандартный обработчик тегов, обрабатывающий простой тег.
3 package com.deitel.advjhtpl.jsp.taglibrary;
4
5 // Набор базовых пакетов Java
6 import 3 ava.io.*;
7
8 // Пакеты расширений Java
9 import javax.servlet.jsp.*;
10 import javax.servlet.jap.tagext.*;
11
12 public class Helcome2TagHandler extends TagSupport (
13 private String firstName = "";
14
15 // Метод, вызываемый, чтобы качать обработку тега
16 public int doStartTagО tbrows JspException
17 {
16 // попытка обработки тега
19 try {
20 // получение объекта JspWriter для вывода содержимого
21 JspWriter out = pageContext.getOut();
22
23 // вывод содержимого
24 out.print( "Hello " + firstName +
25 ", <br />Welcome to JSP Tag Libraries!" );
26 }
27
28 // повторное возбуждение исключения IOException контейнеру
JSP как исключения JspException
29 eaten( IOException ioException ) (
30 throw new JspException( ioException.getMessage() );
31 >
32
33 return SKIP_BODY; // игнорировать тело тега
34 }
35
36 // задание в качестве значения атрибута firstName имени
пользователя
37 public void setFirstName( String username )
38 i
39 firstName = username;
40 }
41 1 ___
Рис. 3.34. Обработчик нестандартного тега Welcome2TagHandler для тега с атрибутами
До того, как тег welcome2 будет использован на JSP-странице, необходимо
уведомить контейнер JSP о добавлении его в библиотеку тегов. Чтобы сделать это,
добавьте элемент tag, представленный на рис. 3.35, в качестве дочернего элемента
в элемент taglib в дескрипторе библиотеки тегов advjhtpl-tagiib.tld. Как и в
предыдущем примере, элемент tag содержит элементы name, tagclass, bodycontent и info.
162
Глава 3
В строках 16-20 используется элемент attribute для задания характеристик
атрибутов тега. Каждый атрибут должен иметь отдельный элемент атрибута, который
содержит элементы name, required и rtexprvalue. Элемент паше (строка 17) задает
имя атрибута. Элемент required определяет, является ли атрибут обязательным
(true) или необязательным (false). Элемент rtexprvalue определяет, может ли
значение атрибута быть результатом вычисления выражения JSP в процессе выполнения
(true), или же оно должно представлять собой строковый литерал (false).
Чтобы протестировать JSP-страницу customTagAttribute.jsp в Tomcat,
скопируйте файл customTagAttribute.jsp и модифицированный файл advjhtpl-
taglib.tld в каталог jsp, созданный в разделе 3.3. Скопируйте файл Welcome2Tag-
Handler.class в каталог WEB-INF\classes приложения advjhtpl на сервере
Tomcat. [Замечание. Этот пример будет работать только в том случае, если в
каталоге classes определена надлежащая структура каталогов пакета для класса
WeIcome2TagHandler.] Откройте Web-бряузер и введите следующий URL, чтобы
протестировать JSP-страницу customTagAttribute.jsp:
http://localbost:8080/advjbtpl/jsp
customTagAttribute.jsp?firstNaroe=Sean
Текст ?firstName=Sean в приведенном выше URL задает значение для параметра
запроса name, который используется нестандартным тегом weleome2 в строке 30
листинга, представленного на рис. 3.33.
1 <!-- Тег с ач?рибутом -->
2 <tag>
3 <name>welcome2</name>
4
5 <tagclass>
6 com.deitel.advjhtpl.jsp.taglibrary.Welcome2TagHandler
7 </t&gclass>
6
9 <bodycontent>eiiipty</bodycontent >
10
11 <info>
12 Inserts content welcoming user to tag libraries. Uses
13 attribute "name" to insert the user's name.
14 </info>
15
16 <attri±>ute>
17 <nama>firstname</name>
18 <required>tr-ue</required>
19 <rtexprvalue>true</rtexprvalue>
20 </attribute>
21 <tag> „___^_ __„
Рис. 3.35. Элемент tag для нестандартного тега welcome
3.8.3. Обработка тела нестандартного тега
Нестандартные теги особенно хороши для обработки содержимого элемента
body (тела документа). Для взаимодействий нестандартного тега с элементом тела
документа необходимы дополнительные методы. Эти методы определены в классе
BodyTagSupport. В следующем примере будет повторно реализована JSP-страница
guestBookView.jsp (рис. 3.23), в которой вместо обработки компонента JavaBeans,
выполняемой JSP-страницей, используется нестандартный тег guest list.
JavaServer Pages (JSP)
163
JSP-страница, представленная на рис. 3.36 (custoraTagBody.jsp) использует
нестандартный тег guestiist в строках 41-52, Обратите внимание, что выражения
JSP в теле элемента guestiist используют имена переменных, которые не
определены на JSP-странице, Эти переменные определяются обработчиком нестандартного
тега при обнаружении нестандартного тега. Обработчик нестандартного тега
помещает переменные в контейнер PageContext JSP-страницы, чтобы цеременные
могли быть использованы страницей. Хотя в JSP-странице не определен цикл
повторного вычисления, обработчик нестандартного тега по определению обрабатывает
все записи для гостей в базе данных guestbook. Это действие приводит к созданию
строки таблицы в итоговой Web-странице для каждого гостя в базе данных.
1 <?xml version = "1.0"?>
2 -ODOCTiFE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
3 "http://www.w3.org/TR/xhtmll/DTD/xhtmll-strict.dtd">
4
5<!-- customTagBody.jsp -->
6
7 <%-- директива taglib --%>
8 <%@ taglib uri = "advjhtpl-taglib.fcld" prefix = "advjhtpl" %>
9
10 <html xmlns = "http://www.w3.org/1999/xhtml">
11
12 <head>
13 <title>Gueet List</title>
14
15 <style type = "text/ess">
16 body {
17 font-family: tahoma, helvetica, arial, sans-serif
18 }
19
20 table, tr, td, th {
21 text-align: center;
22 font-size: . 9em;
23 border: 3px groove;
24 padding: 5px;
25 background-color: idddddd
26 }
27 </style>
28 </head>
29
30 <body>
31 <p style = "font-size: 2em">Guest List</p>
32
33 <table>
34 <thead>
35 <th style = "width: 100px">Last name</th>
36 <th style = "width: 100px">First name</th>
37 <th style = "width: 200px">Email</th>
38 </thead>
39
40 <%-- нестандартный тег guestiist --%>
41 <advjhtpl:guestlist>
42 <tr>
43 <tdX% = lastMame %x/td>
44
45 <tdx%= firstName %X/td>
164
Глава 3
46
47 <td>
48 <а href = "mailto:<%= email %>">
49 <%= email %X/a>
50 </td>
51 </ti>
52 </advjhtpl:guestlist>
53 </table>
54 </body>
55
56 </html>
Рис. 3.36. Использование нестандартного тега, который осуществляет взаимодействие
с собственным телом (элемент body)
Как и для страницы guestBookView.jsp, обработчик нестандартного тега Goest-
BookTag (рис. 3.37) создает объект GuestDataBean для доступа к базе даниых
guestbook. Класс GuestBookTag расширяет класс BodyTagSupport, который
содержит несколько новых методов, включая doInitBody и do After Body (из
интерфейса BodyTag). Метод doInitBody вызывается один раз, после вызова метода
doStartTag и перед вызовом метода doAfterBody. Метод do After Body может
многократно вызываться для обработки тела нестандартного тега.
Ш Общая методическая рекомендация 3.19
Перед обработкой тела нестандартного тега методом doAfterBody
обычно выполняется его однократная обработка с помощью метода
doInitBody. Если метод doStartTag возвращает Tag.SKIP_BODY, метод
doInitBody не вызывается.
1 // Рис. 3.37. GuestBookTag.java
2 // Нестандартный обработчик тегов, который получает информацию из
3 // базы данных гостевой книги и делает эти данные доступными для
JSP-страницы.
4 package com.deitel.advjhtpl■jsp.taglibrary;
5
6 // Набор базовых пакетов Java
7 import java.io.*;
8 import iava.util.*;
9
10 // Пакеты расширений Java
JavaServer Pages (JSP)
165
11 import 3avax.servlet.3sp.*;
12 import javax.servlet.jsp.tagext.*;
13
14 // Пакеты Deitel
15 import com.deitel.advjhtpl.jsp.beans.*;
16
17 public class GuestBookTag extends BodyTagSupport {
18 private String firstName;
19 private String lastNarae;
20 private String email,-
21
22 private GuestDataBean guestData;
23 private GuestBean guest;
24 private Iterator iterator;
25
26 // Метод, который вызывается, чтобы начать обработку тега
27 public int doStartTagO throws JspException
28 {
29 // попытка обработки тега
30 try {
31 guestoata = new GuestDataBean();
32
33 List list = guestData.getGuestList();
34 iterator = list.iterator();
35
36 if t iterator.hasNext() ) {
37 processNextGuest();
38
39 return EVAL__BODY_TAG; // продолжение обработки тела тега
40 }
41 else
42 return SKIF_BODY; // завершение обработки тела тега
43 }
44
45 // если возникает исключение, не продолжать обработку
46 // тела тега
47 catch ( Exception exception ) {
48 exception.printstackTrace0;
49 return SKIP_BODY; // игнорировать тело тега
50 }
51 }
52
53 // обработка тела тега и определение, следует ли
54 // продолжать обработку
55 public int doAfterBodyO
56 {
57 // попытка вывода данных тела тега
58 try 1
59 DodyContent.writeOut< getPreviousOut<) );
60 )
61
62 // если возникает исключение, завершить обработку тела тега
63 catch ( IOException ioException ) {
64 ioException.printStackTraee0;
65 return SKIP_BODY; // завершение обработки тела тега
66 )
166
Глава 3
67
68 bodyС onten t.сlearBody() ;
«9
70 if ( iterator.hasNext() ) {
71 processNextGuestO ,-
72
73 return EVAL_BODY_TAG,- // продолжение обработки тела тега
74 }
75 else
76 return SKIP_B0DY; // завершение обработки тела тега
77 }
78
79 // получение спедуящего объекта GuestBean и извлечение данных иэ него
80 private void processNextGuest()
81 {
82 // получение данных для следующего госия
83 guest = ( GuestBean ) iterator.next();
84
85 pageContext.setAttribute (
86 "firstName", guest.getFirstName() );
87
88 pageContext.setAttribute(
89 "lastName", guest.getLastName() );
90
91 pageContext.setAttribute(
92 "email", guest.getEmail() );
93 )
94 )
Рис. 3.37. Обработчик нестандартного тега GuestBookTag
Контейнер JSF вызывает метод doStartTag (строки 27-51), когда он
обнаруживает на J8F-cTpaHHn,e нестандартный тег guestlist. В строках 31-34 создается
новый объект Guest Da taBean, извлекается список компонентов GuestBean из
компонента GuestDataBean, и создается итератор Iterator для манипулирования
содержимым списка ArrayList. Если в списке нет элементов (это проверяется в строке
36), в строке 42 возвращается значение SKIP_BODY, указывающее, что
контейнеру не следует выполнять дальнейшую обработку тела тега guestlist. В противном
случае в строке 37 осуществляется вызов частного метода processNextGuest
(строки 80-93) для извлечения информации о первом госте и создания переменных,
хранящих эту информацию в контексте страницы PageContext (представляемом
переменной pagecoatext, унаследованной из класса BodyTagSupport). Метод
processNextGuest использует метод setAttribute интерфейса PageContext для
задания имени и значения каждой из переменных. Контейнер ответственен за создание
реальных переменных, используемых JSP-страницей. Это осуществляется с
помощью класса GuestBookTagExtralnfo (рис. 3.38).
Метод doAfterBody (строки 55-77) выполняет циклическую обработку тела
тега guestlist. Контейнер JSP определяет, следует ли вызывать метод
doAfterBody, на основе возвращаемого методом значения. Если doAfterBody возвращает
EVAL_BODY_TAG, контейнер вызывает метод doAfterBody снова. Если
doAfterBody возвращает SKlP_BODY, контейнер прекращает обработку тела и вызывает
метод doEndTag обработчика нестандартного тега для завершения собственной
обработки. В строке 59 вызывается метод writeOut для переменной bodyContent
(унаследованной от класса BodyTagSupport), чтобы обработать данные для
первого клиента (сохраненные при вызове метода doStartTag). Переменная bodyContent
JavaServer Pages (JSP)
167
ссылается на объект класса BodyContent из пакета javax.servlet.jsp.tagext.
Параметр, передаваемый методу writeOut, является результатом выполнения метода
getPreviousOut (унаследованного от класса BodyTagSupport), который
возвращает объект JspWriter для JSP-страиицы, вызывающей нестандартный тег. Это
позволяет нестандартному тегу формировать ответ клиенту с помощью того же
потока вывода, который использует JSP-страница. Далее, в строке 68 вызывается
метод clearBody для переменной bodyContent, чтобы гарантировать, что выведенное
содержимое тега не будет обрабатываться как чаеть следующего вызова doAfter-
Body, В строках 70-76 определяется, имеются ли еще в гостевой книге записи,
подлежащие обработке. Если да, метод do Afterbody вызывает частный метод
processNextGuest для получения данных для следующего гостя и возвращает
значение EVAL_BODY_TAG, указывающее, что контейнер должен снова вызвать метод
doAfterBody. В противном случае doAfterBody возвращает значение SKIP_BODY,
чтобы завершить обработку тела.
Контейнвр JSP не может создавать переменные в контексте страницы Page-
Context, если ему не известны имена и типы этих переменных. Эта информация
определяется классом, который носит то же имя, что и обработчик нестандартного
тега, но заканчивается на Extralnfo (GuestBookTagExtralnfo на рис. 3.38).
Классы Extralnfo расширяют класс TagExtralnfo (пакет javax.servlet.jsp.tagext).
Контейнер использует информацию, задаваемую подклассом класса TagExtralnfo,
для определения, какие переменные следует создать (или использовать) в
контексте PageContext. Чтобы задать информацию о переменной, следует
переопределить мвтод getVaridblelnfo. Этот метод возвращает массив объектов Variablelnfo,
которые контейнер использует либо для создания новых переменных в контексте
страницы PageContext, либо для того, чтобы дать возможность нестандартному
тегу использовать существующие переменные в контексте страницы PageContext,
Конструктор Variablelnfo принимает четыре параметра: строку, представляющую
имя переменной, строку, представляющую имя класса переменной, булево
значение, указывающее, следует ли контейнеру создавать переменную (true, если да)
и статическую целочисленную константу, задающую область видимости
переменной на JSP-странице. В классе Variablelnfo определены константы NESTED,
AT_BfiGIN и AT_END. Константа NESTED указывает, что переменная может
быть использована только в теле нестандартного тега. Константа AT_BEGIN
указывает, что переменная может быть использована в любом месте JSP-страницы
после начального тега нестандартного тега. Константа AT_END указывает, что
переменная может использоваться в любом месте JSP-страницы после конечного тега
нестандартного тега.
Прежде, чем использовать тег guestlist на JSP-странице, необходимо сообщить
контейнеру JSP о теге, добавив его в библиотеку тегов. Для этого следует добавить
элемент tag (см. рис. 3.39) в качестве дочернего элемента для элемента taglib в
дескриптор adYJh.tpl-taglib.tld библиотеки тегов. Как и в предыдущем примере,
элемент tag содержит элементы name, tagclass, bodycontent и info. В строках 10-12
вводится элемент teiclass для задания класса Extralnfo нестандартного тега.
Чтобы протестировать JSP-странину customTagBody.jsp в Tomcat, скопируйте
файл customTagBody.jsp и модифицированный файл advjb.tpl-taglib.tld в каталог
jsp, созданный в разделе 3.3. Скопируйте файлы GuestBookTag.class и
GuestBookTagExtralnfo. class в каталог WEB-INF\classes Web-приложеиия advjhtpl на
сервере Tomcat. [Замечание. Этот пример будет работать только в том случае, если
в каталоге classes определена надлежащая структура каталогов пакета для
классов GuestBookTag GuestBookTagExtralnfo.] Откройте Web-браузер и введите
следующий URL, чтобы протестировать JSP-страницу customTagBody.jsp:
http://localhost:8080/advjhtpl/jsp/customTagBody.jsp
168
Глава 3
1 // Рис.3.38. GuestBookTagExtralnfo.Java
2 // Класс, который определяет имена и филы переменных,
3 // создаваемых нестандартным обработчиком тегов GuestBookTag.
4 package com,deitel.advjhtpl.j sp.taglibrary;
5
6 // Набор базовых пакетов Java
7 import javax.servlet.j sp.tagext.*;
8
9 public class GuestBookTagExtralnfo extends TagExtralnfo {
10
11 // метод, который возвращает информацию о переменных
12 // GuestBookTag, созданных для использования их на JSP-странице
13 public VariableInfo [] getVariablelnfо( TagData tagData )
14 {
15 Variablelnfo firstName = new Variablelnfо( "firstName",
16 "String", true, Variablelnfо.NESTED );
17
18 Variablelnfo lastName = new Variablelnfo( "lastName",
19 "String", true, Variablelnfо.NESTED );
20
21 Variablelnfo email = new Variablelnfo( "email",
22 "String", true, Variablelnfо.NESTED );
23
24 Variablelnfo varablelnfo [] =
25 { firstName, lastName, email };
26
27 return varablelnfo,-
28 }
29 } __^
Рис. 3.38. Класс GuestBookTagExtralnfo используется контейнером для определения
переменных сценария на JSP-странице, которая использует нестандартный тег guestlist
1 <!— Тех1, который осуществляет обход массива -->
2 <!— компонентов GuestBean для вывода их в JSP-странице. —>
3 <tag>
4 <name>guestlist</name>
5 <tagclass>
6 com.deitel.advjhtpl.jap.taglibrary.GuestBookTag
7 </tagclass>
8
9 <teiclass>
10 com.deitel.advj htpl.j sp.taglibrary.GuestBookTagExtraInfo
11 </teiclass>
12
13 <bodycontent>JSP</bodycontent>
14
15 <info>
16 Обход списка объектов GuestBean.
17 </info>
18
19 <tag>
Рис. 3.39. Элемент tag для нестандартного тега guest list
JavaServer Pages (JSP)
169
В этой главе были рассмотрены многие из возможностей технологии JSP.
Однако ряд дополнительных возможностей остался за пределами рассмотрения этой
книги. За полной информацией обратитесь к спецификации JavaServer Pages 1.1,
которую можно загрузить по адресу j a va.s an .com/product s/jsp/do wnload.html.
Другие ресурсы по JSP приведены в разделе 3.9. В следующей главе будет
продолжено обсуждение технологии JSP и сервлетов и рассмотрен большой практический
пример, демонстрирующий их применение: книжный Internet-магазин. В этом
примере нашли отражение многие технологии Java, в том числе JDBC, сервлеты,
JSP и XML. В ходе рассмотрении примера также будут обсуждены некоторые
дополнительные возможности, связанные с сервлетами. Представленные в
практическом примере приемы и методы закладывают основу для создания большого
примера приложения для электронного бизнеса, рассматриваемого в главах с 8 по 11.
3.9. Ресурсы в Internet и во Всемирной паутине
Java.sun.com/products/jap
Основная страница сайта Sun Microsystems, посвященного Java, которая содержит
информацию по технологии JavaServer Pages.
Java.sun.com/prodiicts/servlet
Основная страница сайта Sun Microsystems, посвященного Java, которая содержит
информацию по технологии сервлетов
j ava. sun, com/ j 2ee
Основная страница сайта Sun Microsystems, посвященного Java, которая содержит
информации по Java 2 Enterprise Edition.
ии.мЗ . oxg
Основная страница консорциума World Wide Web Consortium. Этот сайт предоставляет
информацию о действующих и разрабатываемых стандартах для Internet и Web, таких
как XHTML, XML и CSS.
jsptags.com
Этот сайт содержит учебный материал, библиотеки тегов, программные продукты
и другие ресурсы, полезные для программистов JSP.
j spinsidsr.com
Этот сайт удаляет основное внимание ресурсам для программистов JSP. Здесь
представлены программные продукты, учебный материал, статьи, образцы кода, ссылки на
другие ресурсы, имеющие отношение К JSP и к средствам программирования в Web.
Резюме
• Технология JavaServer Pages (JSP) является расширением технологии сервлетов.
• Технология JavaServer Pages позволяет программистам Web-приложений создавать
динамическое содержимое путем многократного использования ранее определенных
компонентов и взаимодействия с компонентами с помощью сценария, выполняющегося на
стороне сервера.
• Программисты JSP могут создавать библиотеки собственных (нестандартных) тегов,
которые позволяют дизайнерам Web-страниц, не знакомых с программированием на Java,
усовершенствовать свои Web-страницы, добавляя новые возможности обработки и
динамического отображения содержимого.
• Классы и интерфейсы, относящиеся к программированию JSP-страниц, содержатся в
пакетах javax.servlet.jsp и javax.servlet.jsp.tagext.
• Спецификацию JavaServer Pages 1.1 можно загрузить по адресу java.sun.com/prodiicts/
jsp/download/htm].
• Имеется четыре ключевых компонента JSP: директивы, действия, скриптлеты и
библиотеки тегов.
170
Глава 3
• Директивы задают глобальную информацию, которая не связана с конкретным запросом
JSP.
• Действия инкапсулируют функциональные возможности в предопределенных тегах,
которые программисты могут встраивать в JSP-страницу.
• Скриптлеты, или элементы сценария, дают возможность программистам включать код
Java, который взаимодействует с компонентами JSP-страницы (и, возможно, с
компонентами другого Web-приложения) для обработки запроса.
• Библиотеки тегов являются частью механизма расширения тегов, который позволяет
программистам создавать новые теги, инкапсулирующие сложные функциональные
возможности Java.
• JSP-страницы обычно содержат XHTML- или XML-разметку. Подобная разметка
известка как данные с неизменной структурой илн текст с неизменной структурой.
• Программисты используют JSP-страницы, если большая часть содержимого,
отправляемого клиенту, представляет собой данные с неизменной структурой, и лишь малая часть
содержимого генерируется динамически с помощью кода Java.
• Программисты используют сервлеты, если лишь малая часть содержимого представляет
собой данные с неизменной структурой.
• JSP-страницы обычно выполняются на Web-сервере. Сервер при этом называют
контейнером JSP.
• Когда сервер, поддерживающий технологию JSP, получает первый запрос на
JSP-страницу, контейнер JSP транслирует ее в сервлет, который обрабатывает текущий запрос и
последующие запросы к этой JSP-странице.
• Контейнер JSP помещает операторы Java, которые реализуют ответ JSP-страницы, в
оператор _jspService на этапе трансляции.
• Механизм запрос/ответ и жизненный никл для JSP-страниц и для сервлетов аналогичен.
• JSP-страницы могут определять методы jsplnit и jspDestroy, которые вызываются,
соответственно, когда контейнер инициализирует JSP-странкцу и когда контейнер завершает
обработку JSP-страницы.
• Выражения JSP заключаются б ограничители <% = и %>. Эти выражения
преобразовываются контейнером JSP в строки и выводятся как часть ответа.
• Элемент meta XHTML может устанавливать интернат обновления для документа,
который загружен в браузер. При этом браузер периодически запрашивает документ через
заданные в секундах интервалы времени.
■ Первый вызов JSP-страницы в Tomcat сопровождается задержкой времени, связанной
с необходимостью трансляции JSP-страницы в сервлет и вызова сервлета для ответа на
запрос,
• Неявные объекты предоставляют программистам функциональные возможности
сервлетов в контексте JavaServer Page.
• Неявные объекты имеют четыре области видимости: приложения, страницы, запроса
и сеанса.
• Объекты с областью видимости в пределах приложения являются частью приложения
контейнера JSP и сервлетов.
• Объекты с областью видимости в пределах страницы существуют только в составе
страницы, в которой они используются. Каждая страница имеет свои собственные экземпляры
неявных объектов со страничной областью видимости.
• Объекты с областью видииости в пределах запроса существуют в течение запроса.
Объекты с областью видимости в пределах запроса теряют свое действие при завершении
обработки запроса выдачей ответа клиента.
• Объекты с областью видимости в пределах сеанса существуют в течение всего
клиентского сеанса просмотра.
• К компонентам сценария JSP относятся скриптлеты, комментарии, выражения,
объявления и escape-последовательности.
■ Скриптлеты представляют собой фрагменты кода, ограничиваемые символами <% и % >.
Они содержат операторы Java, которые помещаются в метод _j'spService, когда
контейнер транслирует JSP-страницу в сервлет.
• Комментарии JSP заключаются в ограничители <%— и —%>. Комментарии XHTML
заключаются в ограничители <!— и —>. Внутри скриптлетов могут использоваться
однострочные комментарии Java (//) и многострочные комментарии Java (заключенные
в ограничители /* и */)■
JavaServer Pages (JSP)
171
• Комментарии JSP и комментарии языка сценария игнорируются и н ответе не
присутствуют,
■ Выражение JSP, заключенное в ограничители <%= и %>, содержит выражение Java,
которое вычисляется, когда клиент запрашивает JSP-страницу, содержащую это
выражение. Контейнер преобразовывает результат вычисления выражения JSP в строку, а затем
выводит строку в составе ответа клиенту.
• Объявления, заключенные в ограничители <%! и %>, дают возможность программисту
JSP определять переменные и методы. Переменные становятся переменными экземпляра
класса, который представляет транслированную JSP-страницу. Аналогично, методы
становятся членами класса, который представляет транслированную JSP-страницу.
■ Специальные символы или последовательности символов, которые контейнер JSP обычно
использует в качестве ограничителей кода JSP, могут включаться в JSP-страницу в виде
буквенных символов в элементы сценария, в данные с неизменной структурой и в
значения атрибутов с помощью escape-последовательностей.
• Стандартные действия JSP предоставляют программистам JSP-страниц доступ к
нескольким наиболее типичным задачам, выполняемым JSP-стрянипей. Контейнеры JSP
обрабатывают действия на этапе запроса.
■ Страницы JavaServer Pages поддерживают два механизма включений: действие <jsp:
inclTJde> и директиву include.
• Действие <jsp:inclnde> позволяет включать в JSP-страницу динамическое содержимое.
Если включаемый ресурс в промежутке между последовательными запросами
изменяется, следующий запрос к JSP-странице, содержащий действие <:jsp:inclttde>, будет
осуществлять включение новое содержимое ресурса.
• Директива include обрабатывается один раз, на этаге трансляции, и приводит к
копированию содержимого на JSP-страницу. Если включаемый ресурс изменяется, новое
содержимое не будет отражено в JSP-странице, использующей директиву включения, пока
JSP-страница не будет откомпилирована повторно.
• Действие <jsp:forward> дает возможность JSP-странице переадресовывать обработку
запроса другому ресурсу. Обработка запроса исходной JSP-страницей завершается, как
только запрос переадресовывается.
• Действие <jsp:param> задает пары имя/эначение для информации, которая передается
действиям Include, forward и plugin. Каждое действие <jsp:param> имеет два
обязательных атрибута: name и value. Если действие рагаш задает параметр, который уже
присутствует в запросе, новое значение для параметра имеет приоритет над исходным
значением. Все значения для этого параметра могут быть получены с помощью метода getPa-
rameterValues неявного объекта JSP request, который возвращает строковый массив.
■ Действие JSP <jsp:plugin> позволяет добавлять на Web-странйцу апплет или компонент
JavaBeans в виде специфичного для браузера XHTML-элемеята object или embed. Это
действие также дает возможность загружать и устанавливать подключаемые модули Java
Plug-in, если они еще не установлены на компьютере клиента.
• Действие <jsp;useBean^ позволяет JSP-странице манипулировать объектом Java. Это
действие может быть использовано для создания объекта Java, используемого
JSP-страницей, или для нахождения существующего объекта.
• Подобно неявным объектам Java, объекты, задаваемые действием <jsp:oseBean>, имеют
области видимости в пределах, страницы (page), запроса (request,), сеанса (session) иди
приложения (application), которые указывают, где они могут использоваться в
Web-приложении.
• Действие <jsp:getProperty> получает значение свойства компонента JavaBeans. Действие
<J5p:getProperty> имеет два атрибута — name и property — которые задают объект
JavaBeans, подлежащий манипулированию, и свойство, которое следует получить-
• Значения свойств компонента JavaBeans могут задаваться с помощью действия ^jsp:
setProperty>. Это действие особенно полезно для установки соответствия между
значениями параметров запроса и свойствами компонента JavaBeans. Параметры запроса
могут бычь использованы для установки свойств примитивных типов boolean, byte, спаг,
int, long, float и double и типов String, Boolean, Byte, Character, Integer, Long, Float
и Double из пакета java.lang.
• Директива page определяет информацию, которая глобально доступна дл» JSP-Страни-
цы. Директивы заключаются в ограничители <%@ и %>. Атрибут errorPage директивы
page задает, куда следует пересылать для обработки все перехваченные исключения.
• Действие <jsp:setProperty> способно отождествлять параметры запроса СО свойствами
компонента с теми же именами путем задания "*" в качестве значения атрибута property.
172
Глава 3
• Атрибут import директивы page дает возможность программистам задавать классы и
пакеты Java, которые будут использоваться в контексте JSP-страницы.
• Если для атрибута iaErrorPage директивы page задано значение true, JSP-страница
является страницей обработки ошибок. При этом открывается возможность доступа к
неявному объекту JSP exception, который представляет собой объект исключения,
указывающий на наличие проблемы.
• Директивы представляют собой сообщения для контейнера JSP, которые позволяют
программисту задавать параметры страницы (например, страницу ошибок), включать
содержимое других ресурсов и задавать библиотеки нестандартных тегов, которые могут
использоваться JSP-страницей. Директивы обрабатываются на этапе трансляции
JSP-страницы в сер влет и компиляции. Таким образом, директивы не выдают какой-либо
результат немедленно.
• Директива page задает глобальные настрэйкн для JSP-страницы в контейнере J3P.
Возможно использование множества директив page при условии, что при этом имеется
только одно вхождение каждого из атрибутов. Исключением из этого правила является атри-
бут import, который может использоваться многократно дли импорта пакетов Java.
• Библиотеки нестандартных тегов определяют один или несколько нестандартных тегов,
которые программисты JSP-страниц могут использовать для создания динамического
содержимого. Функциональные возможности этих нестандартных тегов определены в
классах Java, которые реализуют интерфейс Tag (пакет javax.servlet.jsp.tagext), обычно
путем расширения класса TagSupport или класса BodyTagSupport.
• JSP-страница может осуществлять включение библиотеки нестандартных тегов с
помощью директивы taglib.
• При реализации нестандартных тегов необходимо определить для каждого из тегов класс
обработчика тега, который содержит функциональные возможности тега, а также
дескриптор библиотеки тегов, который предоставляет информацию о библиотеке тегов и
нестандартных тегах контейнеру JSP и JSP-етранице, использующей нестандартный тег.
• Наиболее важными методами интерфейса Tag являются методы dc-S tart Tag и doEndTag.
Контейнер JSP вызывает эти методы, когда встречает, соответственно, начальный
(открывающий) нестандартный тег и конечный (закрывающий) нестандартный тег.
• Файл дескриптора библиотеки нестандартных тегов представляет собой XML-документ,
содержащий информацию о библиотеке тегов, которая необходима для контейнера JSP.
• Класс BodyTagSupport содержит несколько методов для взаимодействия с телом
нестандартного тега, такие как методы doInitBody и doAfterBody (из интерфейса BodyTag).
Метод doInitBody вызывается один раз после вызова метода doStartTag и перед вызовом
метода doAfterBody. Метод doAfterBody может многократко вызываться для обработки
тела нестандартного тега.
Терминология
% \>, escape-последовательность для
символов %>
<\— и —>, ограничители комментариев
XHTML
<%— и --—%>, ограничители комментарии
ев JSP
<% и %>, ограничители скршмлета
<%! и %>, ограничители объявления
<%= и %>, ограничители выражения JSP
<%@ и %>, ограничители директивы
<\%, escape-последовательыоеть для <%
action — действие
align, атрибут действия <jsp:plugin>
application, неявный объект
application scope — область видимости
в пределах приложения
archive, атрибут действия <jsp:plugin>
AT_BEGIN, константа
AT_END, константа
attribute, элемент дескриптора библиотеки
тегов
autoFlash, атрибут директивы page
bean Name, атрибут действия <jsp:usebean>
bodyContent, элемент дескриптора
библиотеки тегов
BodyContent, интерфейс
BodyTag, интерфейс
BodyTagSupport, класс
buffer, атрибут директивы page
class, атрибут действия <jsp:usebean>
client-server networking — сетевое
взаимодействие между клиентом и сервером
code, атрибут действия <jsp:plngin>
codebase, атрибут действия <jsp:plugin>
comment — комментарий
coafig, неявный объект
container — контейнер
contentType, атрибут директивы page
JavaServer Pages (JSP)
173
custom tag — нестандартный тег
custom tag attribute —■ атрибут
нестандартного тега
custom tag handler — обработчик
нестандартного тега
custom tag library — библиотека
нестандартных тегов
custom tag with attributes —нестандартный
тег с атрибутами
declaration — объявление
directive — директива
doAfterBody, метод интерфейса BodyTag
doEndTag, метод интерфейса Tag
doInitBody, метод интерфейса BodyTag
doStartTag, метод интерфейса Tag
dynamic content — динамическое
содержимое
error page — страница ошибок
errorPage, атрибут директивы page
escape sequence —
escape-последовательность
EVAL_BODY_INCLUDE, константа
exception, неявный объект
expression — выражение
extends, атрибут директивы page
file, атрибут директивы include
fixed template data — данные с неизменной
структурой
fixed template text — текст с неизменной
структурой
flush, атрибут действия <jsp:include>
forward a request — пересылка запроса
getParatneterValues, метод объекта request
getVariablelnfo, метод класса TagExtralnfo
height, атрибут действия <jsp:plugin>
hspace, атрибут действия <jsp:plugin>
HttpSession (javax.servlet.http), интерфейс
id, атрибут действия <jsp:oseBean>
ieplugmurl, атрибут действия <jsp:plugin>
implicit object — неявный объект
implicit abject scopes — области видимости
неявного объекта
import, атрибут директивы page
include a resource — включение ресурса
include, директива
info, атрибут директивы page
isErrorPage, атрибут директивы page
isThreadSafe, атрибут директивы page
Java Plug-in — подключаемый модуль Java
JavaServer Pages (JSP) — серверные
страницы Java
JavaServer Pages 1.1 specification, пгтеггифи-
кация
javax.servtet.jsp, пакет
javax.servlet.jsp.tagext, пакет
jreversion, атрибут действия <jsp:plugin>
<jsp:forward>, действие
<jsp:getProperty>, действие
<jsp:include>, действие
<jep:param>, действие
<jsp:plugin>, действие
<jsp:setProperty>, действие
<jsp:useBean>, действие
jspDestroy, метод
jsplnit, метод
_jspService, метод
jspversion, элемент дескриптора библиотеки
тегов
Jsp Writer (пакет javax.servlet.jsp)
language, атрибут директивы page
match request parameters — отождествление
параметров запроса
meta, элемент
name, атрибут действия <jsp:param>
name, атрибут действия <jsp:plugin>
name, атрибут действия <jsp:setProperty>
name, элемент дескриптора библиотеки
тегов
name/value pair — пара имя/значение
NESTED, константа
nspluginurl, атрибут действия <jsp:plugin>
out, неявный объект
page, атрибут действия <jsp:forward>
page, атрибут действия <jsp:include>
page, директива
page, неявный объект
page scope — область видимости в пределах
страницы
PageContext, интерфейс
pageContext, неявный объект
рагат, атрибут действия <jsp:setProperty>
prefix, атрибут директивы taglib
property, атрибут действия
<jsp:setProperty>
refresh interval — интервал обновления
request, неявный объект
request scope — область видимости в
пределах запроса
request-time error — ошибка на этапе запроса
required, элемент дескриптора библиотеки
тегов
response, неявный объект
rtexprvalne, элемент дескриптора
библиотеки тегов
scope, атрибут действия <jsp:useBean>
scope of a bean — область видимости
компонента JavaBeans
scripting element — элемент сценария
scriptlet — скриптлет
session, атрибут директивы page
session, неявный объект
session scope — область видимости в
пределах сеанса
setAttribute, метод интерфейса
PageContext
simple custom tag — простой нестандартный
тег
SKIP_BODY, константа
specify attributes of a custom tag — задание
атрибутов нестандартного тега
174
Глава з
standard actions — стандартные действия teiclass, элемент дескриптора библиотеки тегов
tag, элемент дескриптора библиотеки тегов title, атрибут действия <jsp:plugin>
tag extension mechanism — механизм рас- tlibversion, элемент дескриптора библиоте-
ширения тегов ки тегов
tag handler — обработчик тега translation-time error — ошибка на этапе
Tag, интерфейс трансляции
tag library — библиотека тегов translation-time include — включение на
tag library descriptor — дескриптор библио- этапе трансляции
теки тегов type, атрибут дейстаия <jsp:plugin>
tagclass, элемент дескриптора библиотеки type, атрибут дейстаия <jsp:ngeBean>
тегов uri, атрибут директивы taglib
TagExtralnfo, класс value, атрибут действия <jsp:setProperty>
taglib, директива value, атрибут действия <jsp:plngin>
tagPrefix, атрибут директивы taglib value, атрибут действия <jsp;param>
TagSupport, класс width, атрибут действия <jsp;plugin>
Упражнения для самоконтроля
3.1. Заполните пропуски в следующих высказываниях:
a) Действие JSP позволяет добавлять апплет или компонент JavaBeans на
Web-страницу в виде специфичного для браузера XHTML-элемеНта object или embed.
b) Действие способно отождествлять параметры запроса со свойствами
компонента с теми же именами при задании "*" в качестве значения атрибута property.
c) Имеется четыре ключевых компонента JSP-страяицы: _, ,
и .
d) JSP-страница может осуществлять включение библиотеки нестандартных тегов
с помощью директивы .
e) Неявные объекты имеют четыре области видимости: , ,
И ■
f) Директива ^___ обрабатывается один раз на этапе трансляции JSP-страни-
цьг и приводит к копированию содержимого на JSP-страницу.
g) Классы и интерфейсы, относящиеся к программированию страниц JavaServer
Pages, содержатся в пакетах __ и .
h) JSP-страницы обычно выполняются на Weh-сервере, который называется
i) Метод может многократно вызываться для обработки тела
нестандартного тега.
j) К компонентам сценария JSP относятся , , ,
__ и .
3.2. Ответьте, являются ли следующие высказывания истинными или ложными. Если
высказывание ложно, объясните, почему.
a) Объект со страничной областью видимости существует в пределах всех JSP-страниц
определенного Web-приложения.
b) Директивы задают глобальную информацию, которая не связана с определенным
запросом JSP.
c) Контейнер JSP вызывает методы doInitBody и doAfterBody, когда встречает,
соответственно, начальный нестандартный тег и конечный нестандартный тег.
d) Библиотеки тегов представляют собой составную часть механизма расширения
тегов, который позволяет программистам создавать новые теги, инкапсулирующие
сложные функциональные возможности Java.
e) Дейстнне <jsp:include> обрабатывается один раз на этапе трансляции страницы.
f) Подобно комментариям XHTML, комментарии JSP и комментарии языка
сценариев присутствуют в ответе клиенту.
g) Объекты с областью видимости в пределах приложения явлвются частью
определенного Web-приложения.
h) Каждая страница имеет свои собственные экземпляры неявных объектов со
страничной областью видимости.
iavaServer Pages (iSP)
175
i) Действие <jsp:setProperty> способно отождествлять параметры запросов со
свойствами компонента с теми же именами при задании "*" в качестве значения атрибута
property.
j) Объекты с областью видимости в пределах сеанса существуют в течение всего
клиентского сеанса просмотра.
Ответы на упражнения для самоконтроля
3.1. a) <jsp:plugin>- b) <jsp:setProperty>. с) директивы, действия, скриптлеты,
библиотеки тегов, d) taglib. e) приложения, страницы, запроса и сеанса, f) include, g) ja-
vax.servlet.jsp, javax.servlet.jsp.tagext. h) контейнером JSP. i) doAfterBody, j)
скриптлеты, комментарии, выражения, объявления, escape-последовательности,
3.2. а) Ложно. Объекты со страничной областью видимости существуют только на той
странице, на которой они используются.
b) Истинно.
c) Ложно. Контейнер JSP вызывает метод doStartTag и doEndTag, когда встречает,
соответственно, начальный нестандартный тег и конечный нестандартный тег.
d) Истинно.
e) Ложно. Действие <Jsp:ittclude> позволяет аключать динамическое содержимое
в страницу JavaServer Page.
f) Ложно. Комментарии JSP и комментарии языка сценариев игнорируются и в
ответе не присутствуют.
g) Ложно. Объекты с областью видимости в пределах приложения являются частью
приложения контейнера JSP и сервлетов,
h) Истинно.
i) Истинно,
j) Истинно.
Упражнения
3.3. Создайте класс ResultSetTag (обработчик нестандартного тега), который может
отображать информацию из любого результирующего множества ResuItSet. Возьмите за
основу класс GuestBw>kTag {ряс. 3.37). В качестве имен атрибутов объекта page-
Context должны использоваться имена столбцов в результирующем множестве Result-
Set. Имена столбцов могут быть получены с помощью объекта ResnltSetMetaData,
ассоциированного с результирующим множеством ResuItSet. Создайте дескриптор
библиотеки тегов для нестандартного тега н протестируйте нестандартный тег на
JSP-странице.
3.4. Создайте JSP-страницу и адресную книгу на основе технологии JDBC. Возьмите за
основу пример приложения гостевой книги (рис. 3,20-3,24). Rama адресная книга
должна давать возможность вставлять записи, удалять записи и осуществлять поиск
записей.
3.5. Инкорпорируйте класс ResultSetTag ая Упражнения 3.3 в приложение адресной
книги из Упражнения 3.4.
3.6. Создайте новую реализацию вашего решения Упражнения 2.5 (Dynamic Web FAQ) с
использованием JSP-страниц вместо сервлетов. Создайте обработчик нестандартного
тега, подобный тому, который вы создали в Упражнении 3.3, чтобы отображать
информацию для FAQ.
3.7. Модифицируйте ваше решение Упражнения 3.6, чтобы первая вызываемая
пользователем JSP-стрвлиця возвращала список рубрик часто задаваемых вопросов (FAQ), из
которого можно выбрать интересующую тему. Для каждой темы должна быть
предусмотрена гиперссылка, которая вызывает другую JSP-страницу с параметром,
указывающим, к какой теме хотел бы обратиться пользователь, JSP-страница должна
обращаться с запросом к базе данных FAQ и возвращать XHTML-документ, содержащий
только FAQ по этой теме.
3.8. Реализуйте Web-приложение, представленное на рис. 2.27 (Web-onpoc «ваше любимое
животное»), с использованием JSP.
1?6
Глава 3
3.9. Модифицируйте ваше решение Упражнения 3.8, чтобы пользователь мог видеть
результаты опроса, не отвечай на его вопросы,
3.10. Реализуйте пример, представленный на рис. 2.24 (рекомендуемые книги по
программированию), с использованием JSP-страннц. Воспользуйтесь неявным объектом JSP
session, чтобы отслеживать выбор пользователя и определять, какие книги
рекомендовать. Используйте директиву page для указания, что каждая из JSP-страниц участвует
в сеансе.
4
Книжный Internet-магазин,
реализованный с
использованием сервлетов и JSP
Цели
• Построить трехуровневое
распределенное
Web-приложение по схеме
клиент/сервер, использующее
сервлеты Java и технологию
JavaServer Pages.
• Научиться осуществлять
вз аимо действия
с сервлетами/JSP.
• Научиться использовать
интерфейс Re quest Dispatcher
для переадресации запроса
и дальнейшей его обработки.
• Научиться создавать XML-код
с помощью сервлетов
и применять
XSLT-трансформации для
преобразования его в формат,
который может быть
отображен клиентом.
• Познакомиться с сервером
эталонной реализации Java 2
Enterprise Edition.
• Научиться развертывать
Web-приложение
с использованием средств
Java 2 Enterprise Edition.
Мир — это книга, и те, кто не
путешествуют, читают лишь одну ее
страницу.
Святой Августин
Если мы не поставим себя на службу
человечеству, кому мы будем служить?
Джон Адаме
Пользуйтесь подарками судьбы, ибо
милость ее не беспредельна.
Уильям Шекспир
178
Глава 4
4.1. Введение
Эта глава является кульминацией нашего рассмотрения технологии JSP и серв-
летов. В ней мы реализуем Web-приложение книжного Internet-магазина, которое
использует технологии JDBC, XML, JSP и сервлеты. Практический пример
знакомит и с дополнительными возможностями сервлетов, которые мы будем обсуждать
по ходу рассмотрения примера.
В этой главе вы также познакомитесь с эталонной реализацией Java 2
Enterprise Edition 1Л.1, которая будет использоваться в главах 6-10. В отличие от
глав, посвященных JSP и сервлетам, в которых примеры демонстрировались
с применением JSP Apache Tomcat и контейнера сервлетов, в этой главе
развертывание приложения осуществляется на эталонной реализации сервера приложений
J2EE 1.2.1, которую можно загрузить по адресу java.sim.com/j2ee/dowiiload.html.
В состав эталонной реализации J2EE 1.2,1 входят JSP Apache Tomcat и контейнер
сервлетов. Прочитав эту главу, вы сможете реализовать большое распределенное
Web-приложение с множеством компонентов, а также выполнить развертывание
этого приложения на сервере приложений J2EE 1.2.1.
Книжный Internet-магазин, реализованный с использованием сервлетов и J5P 179
4.2. Архитектура приложения книжного Internet-магазина
В этом разделе представлен обзор архитектуры приложения книжного
Internet-магазина Bug2Bug.com. Будет рассмотрена схема основных взаимодействий
между XHTML-документами, JSP-страницами и сервлетами. Мы также
представим таблицу всех документов и классов, используемых в примере. Образцы
выходных результатов демонстрируют, как отображаются XHTML-документы,
отправляемые клиенту.
Наше приложение Bug2Bug.com состоит из ряда XHTML-документов, JSP-
страниц и сервлетов, которые взаимодействуют между собой, имитируя книжный
магазин, торгующий продукцией компании Deitel- Пример реализован как
распределенное трехуровневое Web-приложение. Клиентский уровень представлен Web-
браузером пользователя. Браузер отображает статические XHTML-документы
и XHTML-документы, создаваемые динамически, которые дают возможность
пользователю взаимодействовать с серверным уровнем. Серверный уровень
состоит из нескольких JSP-страниц и сервлетов, которые действуют в интересах
клиента. Эти JSP-страницы и сервлеты выполняют такие задачи, как создание списка
публикаций, создание документов, содержащих сведения о публикации,
добавление элементов в магазинную тележку, просмотр содержимого магазинной тележки
и обработка окончательного заказа. Некоторые JSP-страницы и сервлеты
осуществляют взаимодействие с базой данных в интересах клиента.
На информационном уровне приложение использует базу данных books. О том,
как создать эту базу данных, будет рассказано в разделе 4.10.1.
На рис. 4.1 представлена схема взаимодействий между компонентами
приложения. На схеме имена без расширений (displayBook и addToCart) представляют собой
псевдонимы сервлетов (т.е. имена, используемые для вызова сервлетов). Как вы
узнаете далее, при развертывании приложения в разделе 4.10, в состав реализация
Java 2 Enterprise Edition 1.2.1 входит инструментальное средство развертывания
Application Deployment Tool. Помимо множества других возможностей, оно
позволяет задавать псевдоним, используемый для вызова сервлета. Например, addToCart
является псевдонимом для сервлета addToCartServlet. Средство Application
Deployment Tool создает дескриптор развертывания для сервлета как часть процесса
развертывания приложения.
После того как приложение развернуто, пользователи могут посетить книжный
магазин, введя следующий URL в адресной строке браузера:
http://localhost:8000/advjhtpl/store/
index.html
М XHTML-доку мент
<^> JSP-страница или сервлет
order,html
Рис. 4.1. Взаимодействия между компонентами приложения Bug2Bug.com
180
Глава 4
Этот URL представляет собой запрос основной страницы Internet-магазина
(index.html). Пользователь может просматривать список товаров (книг), щелкая
на кнопке, имеющейся на основной странице. При этом вызывается сценарий
faook.jsp, который взаимодействует с базой данных для динамического создания
списка книг. Результатом является XHTML-документ, содержащий ссылки на
сервлет с псевдонимом display Book, Этот сервлет принимает в качестве параметра
ISBN-код для выбранной книги и возвращает XHTML-документ, содержащий
информацию об этой книги. В этом документе пользователь может щелкнуть на
кнопке, чтобы поместить книгу в магазинную тележку или просмотреть
содержимое тележки. Добавление книги в магазинную тележку приводит к вызову сервле-
та с псевдонимом addToCart. При просмотре содержимого тележки вызывается
сценарий viewCart.jsp, который возвращает XHTML-документ с описанием
содержимого тележки, подсчитывает цену в долларах для каждой позиции и общую
сумму в долларах по всем позициям в тележке. Когда пользователь добавляет
книгу в магазинную тележку, сервлет addToCart обрабатывает запрос пользователя,
а затем осуществляет переход к сценарию viewCart.jsp для создания документа,
который представляет текущее содержимое тележки. В этот момент пользователь
может либо продолжить покупки (books.jsp), либо перейти к подсчету стоимости
и оформлению заказа (order.html). В последнем случае пользователю
предоставляется форма для ввода имени, адреса и информации о кредитной карте. Затем
пользователь отправляет форму, чтобы активизировать сценарий process.jsp, который
завершает транзакцию отправкой пользователю подтверждающего документа.
В таблице на рис. 4.2 дано краткое описание XHTML-док у ментов, JSP-страниц,
сервлетов, компонентов JavaBeans и других файлов, используемых в этом
практическом примере.
Файл
index.html
styles.ess
books.j sp
BookBean.Java
TitlesBean.Java
Описание !
Это основная страница книжного магазина, испольэуемзя по
умолчанию, которая отображается при вводе следующего URL I
в Web-бэаузере клиента:
http://localhost:8000/advjhtpl/store
Этот файл каскадной таблицы стилей (CSS) связан со всеми
XHTML-документами, отображаемыми клиентом, файл CSS
позволяет применять единообразное форматирование ко всем
воспроизводимым статическим и динамическим документам.
Эта JSP-страница использует объекты BookBean и объект
TitlesBean для создания XHTML-документа, содержащего
список товаров. Объект TitlesBean запрашивает базу данных
books для получения списка названий книг, имеющихся в базе
данных. Результаты обрабатываются и помещаются в список
ArrayList объектов BookBean. Список хранится как атрибут
сеанса для данного клиента.
Экчрмппяр чтого компонента JavaBean содержит данные для
одной книги Метод getXML компонента возвращает элемент
XML, представляющий книгу.
JSP-страницэ books.jsp использует экземпляр этого компонента
JavaBear для получения списка ArrayList, содержащего объекты
BookBean для каждой книги в базе данных.
Книжный Internet-магазин, реализованный с использованием сервлетов и JSP 181
Файл
BookServlet.Java
book.xsl
CartltemBean.Java
AddToCartServlet.Java
viewCart.j sp
order.html
process,j sp
Описание |
Этот сервлет (на рис. 4.1 он обозначен под псевдонимом
displayBook) получает XML- представление книги, выбранной
пользователем, а затем применяет таблицу стилей XSL
(XS'.T-трэнсформацию) к XML-коду для получения
XHTML-документа, который может бьггь отображен клиентом.
В этом примере предполагается, что клиентом является браузер,
поддерживающий каскадные таблицы стилей (CSS). 8 последующих
рассматриваемых в этой книге примерах для различных типов
клиентов применяются различные XSLT-трэнсформэции.
Эта таблица стилей XSL определяет, как преобразовать
XML-представление книги в XHTML-документ, который может
отображаться браузером клиента.
Экземпляр этого компонента JavaBean содержит объект
Book Bean и количество экземпляров данной книги в магазинной
тележке. Эти компоненты хранятся в хэш {объект HashMap),
который представляет содержимое магазинной тележки.
Этот сервлет (на рис. 4.1 он обозначен под псевдонимом
addToCart) обновляет содержимое магазинной тележки. Ёсти
тележка не существует, сервлет создает тележку (в данном
примере - объект HashMap). Если компонент CartltemBean
для этого товара (книги) уже имеется в тележке, сервлет
обновляет в компоненте сумму для этой позиции. В противном
случае сервлет создает новый компонент CartltemBean
с количеством, равным 1. После обновления содержимого
тележки пользователь переадресовывается на стэаницу
viewCart.jsp для просмотра текущего содержимого тележки.
Эта JSP-страница извлекает компоненты CartttemBean из
магазинной тележки, подсчитывает стоимость по каждой позиции
в тележке, общую сумму для всех позиций в тележке и создает
XHTML-документ, который дает возможность клиенту
просматривать содержимое тележки в табличном представлении.
При просмотре содержимого тележки пользователь щелкает на
кнопке Check Out для просмотра бланка заказа (формы).
В этом примере форма не несет какого-либо функционального
назначения Однако она присутствует, чтобы создать целостную
картину приложения. |
Эта JSP-страница предназначена для обработки информации
о кредитной карте пользователя и создания XHTML-документ,
сообщающего, что заказ был обработан, и содержащего общую
сумму заказа, J
Рис. 4.2. Сервлеты и JSP-страницы, используемые в приложении книжного магазина
4.3. Доступ в Internet-магазин
На рис. 4.3 представлена основная страница по умолчанию (index.html) для
приложения Bug2Bug.com. Этот файл также называют файлом приветствия.
Файл приветствия задается на этапе развертывания приложения (см. раздел 4.10).
Если приложение выполняется на вашем компьютере в эталонной реализации
Java 2 Enterprise Edition 1.2.1, вы можете ввести следующий TJRL в вашем
Web-браузере, чтобы отобразить основную страницу:
http://localhost:8000/advjHtpl/atore
182
Глава 4
1 <?xml version = "1.0"?>
2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
3 "http://www.w3.org/TR/xhtmll/DTD/xhtmll-strict.dtd">
4 <!-- index.html —>
5
6 <html xmlns = "http://www.w3.org/1999/xbtml">
7
8 <head>
9 <title>Shopping Cart Case Study</title>
10
11 <link rel = "stylesheet" href = "styles.ess"
12 type = "text/ess" />
13 </head>
14
15 <body>
16 <p class = "bigFont">Bug2Bug.com</p>
17
18 <p class = "bigFont italic">
19 Deitel £amp; Associates, Inc.<br />
20 Shopping Cart Case Study
21 </p>
22
23 <!-- форма для запроса страницы books.jsp -->
2Л <form method = "get" action = "books.jsp">
25 <pXinput type = "submit" name = "enterButton"
26 value = "Click here to enter store" /x/p>
27 </form>
28 </body>
29
30 </html>
Рис. 4.3. Основная страница книжного Internet-магазина (index.html)
В строках 11-12 задается связанная таблица стилей styles.ess (рие. 4.4). Все
XIITML-документы, отправляемые клиенту, используют эту таблицу стилей, что
позволяет применить единообразное форматирование к документам. Форма в
строках 24-27 содержит кнопку submit, которая позволяет войти в магазин. Щелчок
на этой кнопке активизирует сценарий books.jsp (рис. 4.7), который создает и воз-
Книжный Internet-магазин, реализованный с использованием сер влетов и JSP 183
вращает XHTML-доку мент, содержащий список имеющихся в магазине товаров
(книг).
В таблице стилей styles.ess, представленной на рис. 4.4, определены общие
стили для отображения XHTML-документов в этом практическом примере. В строках
1-2 задается, что весь текст в элементе body должен быть центрирован, и что в
качестве фонового цвета должен использоваться стольной голубой цвет. Фоновый
цвет представляется шестнадцатеричным числом #b0c4de. В строке 3 определен
класс .bold для применения к тексту жирного начертания. Цвет шрифта —
темно-синий (представляется шестнадцатеричным числом #00008Ь). Если шрифт
Helvetica недоступен, браузер будет пытаться использовать пхрифт Arial, а затем
общеродовой шрифт sans-serif. В строке 8 определен класс -italic для применения
курсивного стиля начертания к тексту. В строке 9 определен класс .right для
выравнивания текста по правому краю. В строках 10-11 задается, что все элементы
table (таблицы), th (данные заголовка таблицы) и td (данные таблицы) должны
иметь объемную рамку толщиной три пиксела с внутренним просветом между
текстом в ячейке таблицы и рамкой этой ячейки, равным пяти пикселам. В строках
12—14 задается, что все элементы tables должны иметь ярко-голубой цвет фона
(представляемый шестнадцатеричным числом #649Sed), и что все элементы tables
должны использовать автоматически устанавливаемые поля слева и справа. Тем
самым таблица размещается по центру страницы. Не все эти стили используются
в каждом из XHTML-документов. Однако применение единой связанной таблицы
стилей позволяет легко и быстро изменить внешний вид Internet-магазина путем
модификации CSS-файла. Для получения более подробной информации по
каскадным таблицам стилей посетите сайт
www.v3.org/Style/CSS
Здесь вы найдете ряд спецификации по CSS. Каждая спецификация содержит
предметный указатель всех используемых на сегодняшний день атрибутов CSS
и перечень их допустимых значений.
гт с<>ветпо
переносимости программ
ТчвУЯ Различные браузеры
таблиц стилей.
имеют различный
4.1
уровень
поддержки
каскадных
1 body { text-align: center;
2 background-color: #B0C4DE }
3 .bold { font-weight: bold; }
4 .bigFont { font-family: helvetica, arial, sans-serif;
5 font-weight: bold;
6 font-size: 2em;
7 color: #0000SB; )
8 .italic { font-style: italic; )
9 .right { text-align: right; }
10 table, th, td ( border: 3px groove;
11 padding: 5px; }
12 table ( background-color: #6495ed;
13 margin-left: atlto;
Id margin-right: auto; } ^^
Рис. 4.4. Общая каскадная таблица стилей (styles.css), используемая для применения
одинакового форматирования к XHTML-документам, отображаемым клиентом
184
Глава 4
4.4. Получение списка книг из базы данных
Страницы JavaServer Pages часто генерируют XHTML-код, который посылается
клиенту для отображения. JSP-страница books.jsp (рис. 4.7) генерирует
XHTML-документ, содержащий список гиперссылок для получения информации по каждой
из книг, имеющейся в таблице titles базы данных books. Из этого списка
пользователь может перейти к просмотру информации об определенной книге, щелкая на
гиперссылке для этой книги. Эта JSP-страница использует объект TitlesBean
(рис. 4.5) и объекты ВоокВеап (рис. 4.6) для создания списка товаров. Каждый из
объектов JavaBeans и сценарий books.jsp будут рассмотрены далее в этом разделе.
На рис. 4.7 показано, как отображается XHTML-докумеита, отправляемый в
браузер сценарием books.jsp.
Компонент JavaBean TitlesBean (рис. 4.5) выполняет запрос к базе данных для
получиния списка названий книг в базе данных. Затем полученные результаты
обрабатываются и помещаются в список ArrayList объектов ВоокВеап. Как мы
увидим при рассмотрении кода, представленного на рис. 4.7, список ArrayList
хранится сценарием books.jsp как атрибут сеанса для данного клиента.
1 // TitlesBean.Java
2 // Класс TitlesBean устанавливает соединение с базой данных
3 // и извлекает информацию о книгах иэ базы данных.
4 package com.deitel.advjhtpl.store;
5
6 // набор базовых пакетов Java
7 import java.io.*;
8 import java.sql.*;
9 import java.util.*;
10
11 // Пакеты расширений Java
12 import javax.naming.*;
13 import javax.sql.*;
Id
15 public class TitlesBean implements Serializable (
16 private Connection connection;
17 private PreparedStatement titlesQuery;
18
19 // формирование объекта TitlesBean
20 public TitlesBean()
21 {
22 // попытка соединения с базой данных и создание операторов SQL
23 try (
24 InitialContext ic = new InitialContext();
25
2 6 DataSource source =
27 ( DataSource ) ic.lookup(
28 "java:comp/env/jdbc/books" );
29
30 connection = source.getConnection();
31
32 titlesQuery =
33 connection.prepareStatement(
34 "SELECT isbn, title, editionNumber, " +
35 "copyright, publisherlD, imageFile, price " +
36 "FROM titles ORDER BY title"
37 ) ;
Книжный Internet-магазин, реализованный с использованием сервлетов и J5P 185
38 }
39
40 // обработка исключений при установке базы данных
41 catch ( SQLException sqlException ) {
42 sqlException.printStackTrace() ,-.
43 }
44
45 // обработка исключений при нахождении источника данных
4Б catch ( NamingException namingException } {
47 namingException.printStackTrасе();
48 }
49 }
50
51 // возврат списка компонентов BookBean
52 public List getTitlesO
53 '{
54 List titlesList = new ArrayList{) ,-
55
56 // получение списка книг
57 try {
58 ResultSet results = titlesQuery.executeQuery();
59
60 // получение строки данных
61 while ( results.next() ) {
62 BookBean book = new BookBean(};
63
64 book.setISBN( results.getString( "isbn" ) J;
65 book.setTitie( results.getString( "title" j );
66 book.setEditionNumber(
67 results.getlnt( "editionNumber" ) );
68 book.setCopyright( results.getString( "copyright" ) );
69 book.setPublisherID(
70 results.getint( "publisheriD" ) );
71 book.setImageFile( results.getString( "imageFile" ) );
72 book.setPrice( results.getDouble( "price" ) );
73
74 titlesList.add( book );
75 }
76 i
77
78 // обработка исключений при запросе базы данных
79 catch ( SQLException exception ) {
80 exception.printstackTrace();
61 1
82
83 // возврат списка названий
84 finally {
85 return titlesList;
86 }
87 >
88
89 // закрытие операторов и завершение соединения с базой данных
90 protected void finalize()
92 // попытка закрыть соединение с базой данных
93 try {
186
Глава 4
94 connection. close () ,-
95 )
96
97 // обработка исключения SQLException при операции закрытия
9в catch ( SQLException sqlException ) {
99 sqlException.printStackTrace();
100 )
101 }
102 }
Рис. 4.5. Компонент THIesBean для получения информации о книге из базы данных
books и создания списка объектов BookBean
Чтобы применять компонента JavaBeans TitlesBean, необходимо иметь
представление об интерфейсе идентификации и маршрутизации Java Naming and
Directory Interface (JNDI). Приложения Enterprise Java часто осуществляют
доступ к информации и ресурсам (например, к базам данных), которые являются
внешними для этих приложений. В некоторых случаях эти ресурсы являются
ресурсами, распределенными в сети. Точно так же, как RMI-клиент использует
реестр RMI для нахождения объекта сервера, чтобы клиент мог обратиться с
запросом к серверу, компоненты корпоративного приложения должны уметь находить
ресурсы, которые они используют. Контейнер приложения Enterprise Java должен
предоставлять сервис идентификации, который реализует интерфейс JNDI и дает
возможность компонентам, выполняющимся в контейнере, осуществлять поиск
соответствующих имен для нахождения ресурсов. Сервер эталонной реализации
J2EE 1.2.1 содержит такой сервис идентификации, который будет использоваться
для нахождения базы данных books в ходе выполнения приложения.
Объект TitlesBean использует JNDI для взаимодействия с сервисом
идентификации и нахождения источника данных (т.е. базы данных books). Конструктор
TitlesBean (строки 20-49) пытается установить соединение с базой данных, используя
класс InitialContext из пакета javax.naming и интерфейс DataSource из пакета
javax.sql. [Эти пакеты должны быть доступны компилятору, чтобы иметь
возможность откомпилировать данный пример.] При развертывании приложения Enterprise
Java (см. раздел 4.10) указываются ресурсы (например, базы данных), необходимые
приложению, и имена JNDI для этих ресурсов. Используя класс контекста
InitialContext, компонент приложения может найти нужный ресурс. Класс контекста
InitialContext предоставляет доступ к окружению идентификации приложения.
В строке 24 создается новый объект InitialContext. Конструктор InitialContext
возбуждает исключение NamingException, если не может найти источник данных
books. В сроках 26—28 вызывается метод lookup класса InitialContext для
нахождения источника данных books. Текст java:comp:/env в составе параметра задает,
что метод lookup должен осуществлять поиск ресурса в записях окружения
компонента приложения (т.е. в списке имен ресурсов, задаваемых при развертывании).
Текст jdbc/books задает, что ресурс представляет собой источник данных JDBC
под именем books. Метод lookup возвращает объект DataSource и возбуждает
исключение NammgException, если он не может вычислить имя, которое получает
в качестве параметра. В строке 25 используется объект DataSource для установки
соединения с базой данных. В строках 32-37 создается оператор SQL Prepared-
Statement, который при выполнении возвращает информацию о каждом названии
книги, содержащейся в таблице titles базы данных books.
Метод getTitles (строки 52-87) возвращает список titleList, содержащий
компоненты JavaBeans BookBean для каждой книги, имеющейся в базе данных.
В строке 58 выполняется запрос titlesQuery. В строках 57-76 обрабатывается
результирующее множество results (объект класса ResnltSet). Для каждой записи
Книжный lnternet-магззин, реализованный с испальзованием сервлетов и JSP 187
в результирующем множестве results в строке 62 создается новый о&ъегст ВооЬ-
Веап, а в строках 64-72 задаются атрибуты объекта ВоокВеап для столбцов в
строке результирующего множества. Методы getString, getlnt и getDouble
интерфейса ResultSet возвращают данные из столбца в соответствующем формате. В строке
74 в список titleList добавляется новый объект ВоокВеап. Список titleList
возвращается в блоке finally. Если в процессе взаимодействий с базой данных
возбуждается исключение, или если в базе данных нет запиеей, список будет пуст.
Экземпляр объекта JavaBean ВоокВеап (рис. 4.6) представляет свойства для
одной книги, включая ISBN-код книги, название, авторские права, имя файла с
рисунком обложки, номер издания, идентификатор издателя и цену. Каждое из этих
свойств является свойством для чтения/залиеи. Часть этой информации в данном
примере не используется. Метод getXML класса ВоокВеап возвращает XML-зле-
мент (объект Element), представляющий книгу.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
3d
35
36
37
38
39
40
41
42
// ВоокВеап,Java
// Объект ВоокВеап содержит данные для одной книги.
package com.deitel.advjhtpl.store;
// Набор базовых пакетов Java
import j ava.io.*;
import j ava,text.*;
import java.util.*;
// Пакеты сторонних поставщиков
import org.w3c.dom.*;
public class ВоокВеап implements Serializable {
private String ISBN, title, copyright, imageFile;
private int editionNumber, publisherlD;
private double price;
// задание кода ISBB
public void setISBN( String isbn }
ISBN
i sbn ;
// возврат кода ISBN
public String getISBN()
return ISBN;
// задание названия книги
public void setTitle( String booJcTitle )
title = bookTitle;
// возврат названия книги
public String getTitle()
return title;
// задание года издания
public void setCopynght ( String year )
copyright = year;
// возврат года издания
public String getCopyright()
return copyright;
// задание имени файла с рисунков обложки
public void setlmageFile( String filename )
imageFile = fileName;
// возврат имени файла с рисунком обложки
public String getImageFile()
return imageFile;
// задание номера издания
public void setEditionNumber( int edition )
editionNumber = edition;
// возврат номера издания
public int getEditionNumber()
return editionNumber;
// задание идентификатора издателя
public void setPublisherlD( int id )
publisherID = id;
// возврате идентификатора издателя
public int getPublisherlDO
return publisherlD;
// задание цены
public void setPrice( double amount )
price = amount;
// возврат цены
public double getPrice()
(
Книжный Internet-магазин, реализованный с использованием сервлетов и JSP 189
99 return price;
100 }
101
102 // получение XML-представления (сниги
103 public Element getXML( Document document )
104 {
105 // создание корневого элемента product для товара
106 Element product = document.createElement{ "product" );
107
108 // создание элемента isbn, добавление его как дочернего
элемента для product
109 Element temp = document.createElement( "isbn" );
110 temp.appendChild( document.createTextNode( getlSBNf) ) );
111 product.appendChild( temp );
112
113 // создание элемента title, добавление его как дочернего
элемента для product
114 teny = document.createElementt "title" ) ;
115 temp.appendChild( document.createTextNode( getTitle{) ) );
116 product.appendChild( temp );
117
118 // создание объекта форматирования денежных единиц для долл.США
119 NumberFormat priceFormatter =
120 NumberFormat.getCurrencyInstance{ Locale.US );
121
122 // создание элемента price, добавление его ках дочернего
элемента для product
123 temp = document.createElement( "price" );
124 temp.appendChild( document.createTextNode(
125 priceFormatter.format( getPrice() ) ) );
126 product.appendChild( temp );
127
128 // опадание элемента imageFile, добавление его ках дочернего
элемента для product
129 temp = document.createElement( "imageFile" );
130 temp.appendChild(
131 document.createTextNode( getlmageFile() ) );
132 product.appendChild( temp );
133
134 // создание элемента copyright, добавление его как дочернего
элемента для product
135 temp = document.createElement( "copyright" );
136 temp.appendChild(
137 document.createTextNode( getCopyrightf) ) ) ;
138 product.appendChild( temp );
139
140 // создание элемента publisherlD, добавление его как дочернего
элемента для product
141 temp = document.createElement', "publisherlD" ) ;
142 temp.appendChild( document.createTextNode(
143 String.valueOf( getPublisherlDO ) ) );
144 product.appendChild( temp );
145
146 // создание элемента editionMumber, добавление его как
дочернего элемента для product
147 temp = document.createElement( "editionNumber" );
190
Глава 4
14в temp.appendChild( document.createTextNode[
149 String.valueOf( getEditionNumber() ) ) );
150 product.appendChild( temp );
151
152 // возврат элемента product
153 return product;
154 }
155 } __ ____
Рис. 4.6. Класс ВоокВеап, представляющий информацию об одной книге и определяющий
формат XML для этой информации
Метод getXML (строки 103-154) использует интерфейсы Document и Element
из пакета org.w3c.dom для создания XML-представления данных для книги как
часть документа (объекта Document), которая передается методу в качестве
параметра. Полная информация для одной книги помещается в элемент product
(созданный в строке 106). Элементы для индивидуальных свойств книги добавляются
в элемент product как дочерние. Например, в строке 109 используется метод
create Element интерфейса Document для создания элемента Isbn. В строке 110
используется метод createTextNode интерфейса Document для задания текста в
элементе isbn и метод appendChild интерфейса Element для добавления текста в
элемент isbn. Затем, в строке 111 элемент isbn добавляется как дочерний в элемент
products с помощью метода appendChild интерфейса Element. Схожие операции
выполняются и для других свойств книги. В строках 119-120 осуществляется
получение объекта Number-Format, который форматирует денежные единицы для
отображения цены книги в долларах США (строка 125). В строке 150 элемент
product возвращается вызвавшему процессу. Мы вновь обратимся к методу
getXML в ходе рассмотрения сервлета BookServlet (рис. 4.8).
JSP-страница books.jsp динамически генерирует список названий книг как
XHTML-документ, отображаемый клиентом. В строках 7—11 задаются параметры
JSP-страницы. Эта страница использует классы из пакета com.deitel.advjhtpl.store
и пакета java.util. Данная JSP-страница также использует возможности
отслеживания состояния сеанса. Динамические составляющие этой JSP-страницы определены
в строках 31-64 с помощью сценариев JSP и выражений.
1 <?xml version = "1.0"?>
2 <»DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
3 "http://www.w3.org/TR/xhtmll/QTD/jehtmll-strict.dtd">
4<!— books.jsp -->
5
6 <%-- параметры JSP-страницы —%>
7 <%@
8 page language = "java"
9 import = "com.deitel.advjhtpl.store.*, java.util.*"
10 session = "true"
11 %>
12
13 <!-- начало документа —>
14 <html xmlns = "http://www.w3.org/1999/xhtml">
15
16 <head>
17 <title>Book List</title>
18
19 <link rel = "stylesheet" href = "styles.ess"
20 type = "text/ess" />
Книжный Internet-магазин, реализованный с использованием сервлетов и JSP 191
21 </head>
22
23 <body>
24 <р class = "bigFont">Available Books</p>
25
26 <p class = "bold">Click a link to view book in£ormation</p>
27
28 <p>
29
30 <%-- начало екриптлета JSP для создания списка книг --%>
31 <%
32 TitlesBean titlesBean = пен TitlesBean () ;
33 List titles = titlesBean.getTitlesО;
34 BookBean currentBook;
35
36 // сохранение названий в данных сеанса для последующего
исп оль зов ания
37 session.setAttribute( "titles", titles );
38
39 // получение итератора для набора ключей в списке List
40 Iterator iterator = titles.iterator();
41
42 // использование итератора для получения каждого
43 // компонента BookBean и создания ссылки для каждой из книг
44 while ( iterator.hasNext() ) {
45 currentBook. = ( BookBean ) iterator.next();
46
47 %> <%-- завершение скриптлета для вставки литерала XHTML --%>
48 <%— и выражений JSP, вычисленных в этом чикле --%>
49
50 <%— ссылка на информации о книге —%>
51 <span class = "bold">
52 <а href =
53 "displayBook?isbn=<%= currentBook.getISBN() %>">
54
55 <%= currentBook.getTitie() + ", " +
56 currentBook.getEditionHuiriber 0 + "e" %>
57 </a>
58 </spanXbr />
59
60 <% // продолжение екриптлета
61
62 } // конец цикла while
63
64 %> <%-- конец екриптлета --%>
65
66 </р>
67 </body>
68
69 </html> __
Рис. 4.7. JSP-страница books.jsp, возвращающая клиенту XHTML-документ, содержащий
список книг (часть 1)
192
Глава 4
Рис. 4.7. JSP-страница books.jsp, возвращающая клиенту XHTML-доку мент, содержащий
список книг (часть 1)
Фрагмент кода сценария (скриптлет) начинается в строке 31. В строке 32
создается объект TitlesBean, а в строке 33 вызывается его метод get Titles для
получения списка объектов BookBean. В строке 37 задается атрибут сеанса titles для
хранения списка, который в дальнейшем будет использоваться в клиентском сеансе.
В строке 40 осуществляется получение итератора Iterator для списка List. В
строках 44-45 начинается цикл, который использует итератор для вывода каждой из
гиперссылок. Скриптлет здесь временно завершается, чтобы дать возможность
вставить XHTML-разметку (строки 51-58). В этой разметке в строке 53
используется выражение JSP для вставки ISBN-кода в качестве значения в паре
имя/значение, которая передается сервлету displayBook (объект класса BookServlet) в
качестве параметра. В строках 55-56 используется другое выражение JSP для вставки
названия книги и номера издания в качестве текста, отображаемого в
гиперссылке. В строках 60-64 скриптлет продолжается закрывающей фигурной скобкой
цикла while, начатого в строке 44.
4.5. Просмотр информации о книге
Подобно многим другим компаниям, компания Bng2Bug.com начала активно
использовать на своем Web-сайте XML. Когда пользователь выбирает книгу на
странице books.jsp, приложение Bug2Bug.com преобразует информацию о книге
в XML-код. Сервлет BookServlet (рис. 4.S) трансформирует XML-представление
книги в XHTML-документ с помощью XSL-таблицы стилей book.xsl (рис. 4.9).
Книжный Internet-магазин, реализованный с использованием сервлетов и JSP 193
Метод doGet объекта BookServlet (строки 24-103) состоит из двух основных
частей. Е строках 28—62 осуществляется поиск объекта ВоокВеап для книги, вы
бранной пользователем на странице books.jsp. В строках 65-102 обрабатывается
XML-представление книги и применяется XSLT-трансформация.
1 // BookServlet.java
2 // Сервлет для возврата клиенту информации об одной книге.
3 // Сервлет выдает XML-код, который преобразуется с помощью
4 // XSL для формирования XHTML-страниц», отображаемой клиентом.
5 package com.deitel.advjhtpl.store;
6
1 I/ Набор базовых пакетов Java
8 import java.io- *;
9 import java.util.*;
10
11 // Пакеты расширений Java
12 import javax.servlet.*;
13 import javax.servlet.http.*;
14 import javax.xml.parsers.*;
15 import j avax.xml.transform.*;
1 б import j avax.xml.trans form.dom.*;
17 import javax.xml.transform.stream.*;
18
19 // Пакеты сторонних поставщиков
20 import org.w3c.dom.*;
21 import org.xml.sax.*;
22
23 public class BookServlet extends HttpServlet {
24 protected void doGet{ HttpServletRequest request,
25 HttpServletResponse response )
26 throws ServletException, IOException
27 {
28 HttpSession session = request.getSession( false );
29
30 // диспетчер RequestDispatcher для переадресации клиента на
31 // основную страницу, если сеанс не существует, или ни одна
из книг не выбрана
32 RequestDispatcher dispatcher =
33 request.getRaquestDispatcher( "/index.html" );
34
35 // если сеанс не существует, переадресация к index.html
36 if ( session = null )
3"? dispatcher .forward! request, response ) ;
38
39 // получение книг из объекта сеанса
40 List titles =
41 < List ) session.getAttribute( "titles" );
42
43 // нахождение компонента ВоокВеап для выбранной книги
44 Iterator iterator = titles.iterator();
45 ВоокВеап book = null;
46
47 String isbn = request, get Parameter ( "isbn" ) ,-
48
49 while ( iterator.hasNextO ) {
50 book = ( ВоокВеап > iterator.next();
194
Глава 4
51
52 if ( isbn.equals< book.getlSBN() ) ) {
53
54 // сохранение книги в атрибуте сеанса
55 session.setAttribute( "bookToAdd", book );
56 break; // isbn-код для текущей книот*
57 >
58 }
59
60 // если книги кет в списке, переадрееация к index.html
61 if ( book = null >
62 dispatcher.forward{ request, response >;
63
64 // получение XML-дакумента и преобразование его для просмотра
клиентом
65 try {
66 // получение объекта DocumentBuilderFactory для создания
67 // синтаксического анализатора XHL DocumentBuilder
68 DocumentBuilderFactory factory =
69 DocumentBuilderFactory.newlnstanceО;
70
71 // получение объекта DoeumentrRiii lder для построения дерева DCH
72 DocumentBuilder builder =
73 factory.newDocumentBuilder();
74
75 // создание нового объекта Document (пустое дерево DOM)
76 Document messageDocument = builder.newDocumentО;
77
78 // получение XHL из объекта BookBean и добавление в объект
Document
79 Element bookElement = book.getXML{ messageDocument ) ;
80 messageDocument.appendChild( bookElement );
81
82 // получение объекта PrintWriter для записи данных клиенту
83 response.setContentType( "text/html" );
84 PrintWriter out = response.getWriter();
85
86 // открытие потока ввода InputStream для XSL-документа
87 InputStream xslStream =
88 getServletContext().getResourceAsStream(
89 "/Ьоок.кз!" );
90
91 // преобразование XML-документа с использованием XSLT
92 transform( messageDocument, xslStream, out );
93
94 // очистка и закрытие объекта PrintWriter
95 out.flush();
96 out.close();
97 )
98
99 // перехват исключений от синтаксического анализатора XML
100 catch ( ParserConfigurationException pcException ) {
101 pcException.printStackTrace(>;
102 )
103 }
104
Книжный Internet-магазин, реализованный с использованием сервлетов и J5P 195
105 // преобразование XML-документа с использованием потока ввода
106 // XSLT InputStream и запись полученного документа в объект
PrintWriter
107 private void transform( Document document,
108 InputStream xslStream, PrintWriter output )
109 {
110 try {
111 // создание объекта DOMSource для исходного XML-документа
112 Source xmlSource = new DOMSource( document );
113
114 // создание объекта StreamSource для XSLT-документа
115 Source xslSource =
116 new StreamSource( xslStream );
117
118 // создание объекта StreamResult пня результата трансформации
119 Result result = new StreamResult( output );
120
121 // создание объекта TransformerFactory для получения
объекта Transformer
122 TransformerFactory transformerFactory =
123 TransformerFactory.newlnstance(};
124
125 // создание объекта Transformer для выполнения
XSLT-трансформачип*
126 Transformer transformer =
127 transformerFactory.newTransformer( xslSource );
128
129 // выполнение трансформации и доставка содержимого клиенту
130 transformer.transform( xmlSource, result );
131 J
132
133 // обработка исключения при трансформации XML-документа
134 catch ( TransformerSxception transformerException ) {
135 transformerException.printStackTrace( System.err };
136 )
137 }
138 1 _____
Рис. 4.8. Сервлет BookServiet получает ХМ!_-предста8ление книги и применяет
XSLT-трзнсформацию для вывода XHTML-документа в ответ на запрос клиента
В строке 28 осуществляется получение объекта HttpSession для текущего
клиента. Этот объект содержит атрибут сеанса, указывающий на книгу, выбранную
пользователем на странице books,jsp. В строках 32-33 осуществляется получение
диспетчера запросов Requ.estDispatch.er для документа "/index.html" путем
вызова метода getRcquestDispatcher класса ServletRcquest. Класс RequestDispatcher
(пакет javax.scrvlet) предоставляет два метода — forward и include — которые
дают возможность сервлету переадресовывать клиентский запрос другому ресурсу
или включать в ответ сервлета содержимое из другого ресурса- В этом примере
в случае, если для текущего клиента объект сеанса не создан (строки 36-37), или
если никакая книга не выбрана (строки 61-62), запрос пересылается обратно на
основную страницу index.html книжного магазина. Каждый из методов forward
и include принимает два параметра: объект HttpServletRequest и объект Http-
ServletRespon.se для текущего запроса.
196
Глава 4
Заметим, что объект RequestDispatcher может быть получен с помощью метода
getRequestDispatcher из объекта, реализующего интерфейс SorvletKequest, или
из объекта ServletContext с помощью методов getRequestDispatcher или getNa-
medDispatcher. Метод getNamedDispatcher интерфейса ServletContext
принимает в качестве параметра имя сервлета, а затем ищет объект контекста
ServletContext для сервлета с указанным именем. Если такой сервлет не найден, метод
возвращает null. Методы getRequestDispatcher как класса ServletKequest, так
и интерфейса ServletContext просто возвращают клиентскому браузеру
содержимое заданного пути, если путь не указывает на сервлет,
В строках 40—41 из объекта сеанса извлекается список (List) объектов Book-
Bean. В строках 44-58 осуществляется линейный поиск для нахождения объекта
BookBean для выбранной книги. (Замечание. Для больших баз данных удобнее
использовать в качестве списка объект типа Map, а не List.) ISBN-код для этой книги
хранится в параметре isbn, передаваемом сервлету (извлекается в строке 47). Если
объект BookBean найден, в строке 55 этот объект BookBean устанавливается в
качестве значения атрибута сеанса bookToAdd. Сервлет AddToCartServIet (рис. 4.10)
использует этот атрибут для обновления содержимого магазинной тележки.
Блок try (строки 65-97) выполняет обработку кода XML и XSL, в результате
чего формируется XHTML-документ, содержащий информацию об одной книге.
Прежде, чем воспользоваться возможностями XML и XSL, необходимо загрузить
и установить программное средство Sun Java API for XML Parsing (JAXP) версии 1.1
со страницы java.sun.com/xml/download.htm. Корневой каталог JAXP (jaxp-1.1)
содержит три JAR-файла — crimson.jar, jaxp.jar и xalan.jar — которые необходимы
для компиляции и выполнения программ, использующих JAXP. Эти файлы
должны быть добавлены к средствам расширения Java 2 Standard Edition. Поместите
копии этих файлов в каталог расширений Java, (jre/lib/ext в Linux/UNIX и jre\
lib\ext в Windows.)
Общая методическая рекомендация 4.1
JAXP 1.1 является составной частью эталонной реализации J2EE 1.3.
Для создания из XML-документа дерева объектной модели документа (DOM)
требуется объект синтаксического анализатора DocumentBuilder. Объекты Docu-
mentBuilder создаются мастером DocumentBuilder Factory. В строках 68 69
осуществляется получение объекта мастера DocumentBuilderFactory. В строках 72-73
получается объект синтаксического анализатора DocumentBuilder, который дает
возможность программе создать дерево объекта Document, в котором элементы
XML-документа представляются объектами Element. В строке 76 используется
объект DocumentBuilder для создания нового документа (объекта Document).
В строке 79 вызывается метод getXML компонента BookBean для получения
представления элемента (объект Element) книги. В строке 80 этот элемент добавляется
в документ messageDocument (объект Document). Классы
DocumentBuilderFactory и DocumentBuilder содержатся в пакете javax.xml.parsers. Классы Document
и Element содержатся в пакете org.w3c.dom.
Далее, в строке 83 задается тип содержимого ответа, а в строке 84 получается
объект PriniWriter для вывода ответа клиенту. В строках 87-89 создается поток
ввода InputStream, который будет использоваться процессором XSLT для чтения
XSL-файла. Ответ создается с помощью XSLT-трансформации, выполняемой
методом transform (строки 107-137). Этому методу передается три параметра:
XML-документ, к которому будет применена XSLT-транс формация (messageDocument),
поток ввода, который считывает XSL-файл (xslStream), и целевой поток, в
который будут записываться результаты (out). Целевой поток вывода может быть раз-
Книжный Internet-магазин, реализованный с использованием сер влетов и JSP 197
личным, в том числе символьным потоком (как поток response объекта Print-
Writer в данном примере).
В строке 112 создается объект DOMSource, который представляет XML-Доку-
мент. Этот объект служит в качестве источника XML-кода для
XSLT-трансформации. В строках 115-116 создается объект StreamSource для XSL-файла. Он
содержит исходный XSL-код, который используется для трансформации документа
DOMSource. В строке 119 создается объект StreamResult для потока PrintWriter,
в который записываются результаты XSLT-трансформации. В строках 122-123
с помощью статического метода newlnstance создается объект TransformerFac-
tory. Этот объект дает возможность программе получать объект Transformer,
который применяет XSLT-трансформацию. В строках 126-127 с помощью метода
newTransformer класса Transform erF ас tor у создается объект Transformer,
который принимает параметр StreamSource, представляющий собой XSL-таблицу
стилей (в данном примере — xslSource). В строке 130 вызывается метод transform
объекта Transformer для выполнения XSLT-трансформации над заданным
объектом DOMSource (xmlSource) и записи результата в заданный объект StreamResult
(result). В строках 134-136 перехватывается исключение TransformeTExceptioii,
если возникает проблема при создании объекта TransformerFactory, создании
объекта Transformer или выполнении трансформации.
На рис. 4,9 представлен файл таблицы стилей book.xsl, используемой при
XSLT-трансформации. В результирующий XHTML-документ помещаются
значения шести элементов XML-документа. В строке 23 название книги помещается
в элемент title документа, а в строке 30 название книги помещается в первый
абзац элемента body документа. В строке 36 определяется элемент img, в котором
значение элемента imageFile XML-документа задает имя файла, содержащего
изображение обложки книги. В строке 37 определяется атрибут alt элемента img с ис-
нолъзованием названия книги title. В строках 43, 51, 59 и 67 элементы price, isbn,
editionNumbcr и copyright помещаются в ячейки таблицы. Полученный XHTML-
документ представлен на копии экрана на рис. 4.9.
1 <?xml version = "1.0"?>
2
3 <xsl:stylesheet xmlns:xsl = "http://www.w3.org/1999/XSL/Transform"
4 version = "1.0">
5
6<xsl:output method = "xml" omit-xml-declaration = "no"
7 indent » "yes" doctype-system =
8 "http://www.w3.org/TR/xhtmll/DTD/xhtmll-strict.dtd"
9 doctype-public = ."-//W3C//DTD XHTML 1.0 Strict//EN"/>
10
11 <!— book.xsl —>
12 <!-- XSL-докумект, который преобразует XML в XHTML —>
13
14 <!-- задание корня XML-документа, —>
15 <<-- на который ссылается эта таблица стилей —>
16 <xsl:template match = "product">
17
18 <html xrolns = "http://www.w3.org/1999/xhtml">
19
20 <head>
21
22 <!— получение названия книги от JSP-страницы для помещения
его в заголовок —>
23 <titleXxsl:value-of select = "title"/x/title>
24
25 <link rel = "stylesheet" href = "styles.ess"
26 type = "text/ess" />
27 </head>
26
29 <body>
30 <p class = "bigFont"Xxsl:value-of select = "title"/x/p>
31
32 <table>
33 <tr>
34 <•-- создание ячейки таблицы для рисунка обложки —>
35 <td rowspan = "5"> <!— ячейка занимает 5 строк -->
36 <img style = "border: thin solid black" sre =
37 "images/{ imageFile }" alt = "\ title }" />
38 </td>
39
40 <!-- создание ячеек таблицы для отображения цены
в строке 1 -->
41 <td class = "bold">Price:</td>
42
43 <tdXxslrvalue-of select = "price "/X/td>
44 </tr>
45
46 <tr>
47
48 <!— создание ячеек таблицы для ISBN-кода в строке 2 -
49 <td class = "bold">ISBN #:</td>
50
51 <tdXxsl:value-of select = "isbn"/X/td>
52 </tr>
53
54 <tr>
55
56 <<— создание ячеек таблицы для отображения номера
издания в строке 3 —>
57 <td class = "bold">Edition:</td>
58
59 <tdXxsl:value-of select = "editionHumber"/x/td>
60 </tr>
61
62 <tr>
63
64 <!— создание ячеек таблицы для отображения года
издания в строке 4 -->
65 <td class = "bold">Copyright:</td>
66
67 <tdXxsl:value-o£ select = "copyright"/X/td>
68 </tr>
69
70 <tr>
71
72 <!-- создание кнопки добавления книги в тележку
в строке 5 -->
73 <td>
7 4 <fonn method = "post" action = "addToCart"Xp>
75 <input type = "submit" value - "Add to Cart" />
76 </pX/form>
Книжный Internet-магазин, реализованный с использованием сервлетов и JSP 199
77 </td>
7В
79 <!— создание в строке 5 кнопки для просмотра
содержимого тележки —>
80 <td>
81 <foxm method = "get" action = "viewCart. jsp"Xp>
82 <input type = "submit" value = "View Cart" />
83 </pX/form>
84 </td>
85 </tr>
86 </table>
87
88 </body>
89
90 </htxnl>
91
92 </xsl:template>
93
94 </xsl:sty!esheet> _^^^___^_^
Рис. 4.9. Таблица сталей XSL (books.xsl), которая трансформирует XML-представление
книги в XHTML-документ
4.6. Добавление элемента в магазинную тележку
Когда пользователь щелкает на кнопке Add To Cart в XHTML-документе,
сформированном в предыдущем разделе, сервлет AddToCartServIet (он имеет
псевдоним addToCarf) обновляет содержимое магазинной тележки. Если тележка не
существует, сервлет создает ее (в данном примере — объект HashMap). Товары в ма-
200
Глава 4
газинной тележке представлены компонентами CartltemBean. Экземпляр этого
компонента JavaBean содержит объект BookBean и текущее количество
экземпляров для данной книги в магазинной тележке. При добавлении' пользователем
книги в тележку в случае, если такая книга уже имеется в тележке, компонент
CartltemBean, обновляет количество экземпляров этой книги в составе данного
компонента. В ином случае сервлет создает новый компонент CartltemBean с
атрибутом количества, равным 1. После обновления содержимого тележки
пользователь переадресовывается к странице viewCart.jsp для просмотра текущего
содержимого тележки.
Класс CartltemBean (рис. 4.10) содержит объект BookBean и количество
экземпляров для этой книги. Он хранит объект BookBean как свойство компонента,
допускающее только чтение, и переменную quantity как свойство компонента,
допускающее запись.
1 // CartltemBean.java
2 // Класс, который хранит информацию о книге и ее количестве.
3 package com.deitel.advjhtpl.store;
4
5 import java.io.*;
6
7 public class CartltemBean implements Serializable -I
8 private BookBean book;
9 private int quantity;
10
11 // инициализация объекта CartltemBean
12 public CartltemBean( BookBean bookToAdd, int number )
13 {
14 book = bookToAdd;
15 quantity = number;
16 )
17
IB // получение книги (это свойство только для чтения)
19 public BookBean getBookO
20 {
21 return book;
22 }
23
24 // задание количества
25 public void setQuantity( int number )
26 (
2 7 quantity = number;
28 }
29
30 // получение количества
31 public int getQuantityO
32 {
33 return quantity;
34 }
35)
Рис. 4.10. Компоненты CartltemBean содержат объект BookBean и количество экземпляров
книги (quantity) в магазинной тележке
Класс AddToCartServlet представлен на рис. 4.11. Метод doPost класса AddTo-
CartServlet получает объект httpSession для текущего клиента {строка 18). Если
сеанс для этого клиента не существует, диспетчер запросов RequestDispatchcr пе-
Книжный Internet-магазин, реализованный с использованием сервлетов и J5P 201
реадресовывает аялрос основной странице книжного магазина index.html (строки
22-26), В противном случае в строке 29 извлекается значение атрибута сеанса
cart — объект типа Map, который представляет магазинную тележку. В строках
30-31 извлекается значение атрибута сеанса bookToAdd. Этим атрибутом
является объект BookBean, представляющий книгу, добавляемую в магазинную
тележку. Если магазинная тележка не существует, в троках 34—39 создается новый
объект типа HashMap для хранения содержимого тележки, а затем этот объект
HashMap помещается в атрибут cart объекта session. В строках 42—43
осуществляется попытка найти компонент CartltemBean для книги, добавляемой в тележку.
Если таковой существует, в строке 48 инкрементирует количество экземпляров
для этого компонента. В противном случае в строке 50 создается новый компонент
CartltemBean с количеством, равным 1, и помещается в магазинную тележку
(объект cart типа Map). Затем в строках 53-55 создается диспетчер запросов
RequestDispatcher для JSP-страницы viewCart.jsp, и обработка запроса
переадресовывают этой JSP-странице, которая отображает содержимое тележки.
1 // AddToCartServlet.Java
2 // Сервлет для добавления книги в магазинную тележку.
3 package com.deitel.advjhtpl.store;
4
5 // Набор базовых пакетов Java
6 import java.io.*;
7 import Java.Util.*;
8
9 // Пакеты расширений Java
10 import javax.servlet.*;
11 import javax.servlet.http.*;
12
13 public class AddToCartServLet extends HttpServlet {
Id protected void doPost( HttpServletRequest request,
15 HttpServletRespOnse response )
16 throws ServletException, lOException
П {
18 HttpSession session = request.getSession( false );
19 RequestDispatcher dispatcher;
20
21 // если сеанс не существует, переадресация к странице index.html
22 if ( session == null ) {
23 dispatcher =
24 request.getRequestOispatcher{ "/index.html" );
25 dispatcher.forward( request, response );
26 }
27
29 // если сеанс существует, подучить объект HashMap для тележки
и книгу для добавления
29 Map cart = ( Map ) session.getAttribute( "cart" );
30 BookBean book =
31 ( BookBean ) session.getAttributs( "bookToAdd" );
32
33 // если тележка не существует, создать ее
34 if { cart = null ) {
35 cart = new HashMap О ;
36
37 // задание атрибута сеанса cart
38 session,setAttribute( "cart", cart ) ;
202
Глава 4
39 }
40
41 // определение, имеется ли книга в тележке
42 CartltemBean cartltem =
43 ( CartltemBean ) cart.get( book.getlSBN{) >;
44
45 // Если книга уже имеется в тележке, обновить ее количество.
46 //Б противном случае, создать элемент для помещения в тележку.
47 if { cartltem != null )
48 cartltem.setQuantity( cartltem.getQuantityO + 1 );
49 else
50 cart.put( book.getISBN(), new CartltemBeaM book, 1 ) );
51
52 // отправка страницы viewCart.jsp пользователю
53 dispatcher =
54 request.getRaquestDispatcher( "/viewCajrt.jsp" );
55 dispatcher.forward! request, response );
56 }
57)
Рис. 4.11. Сервлет AddToCarrServlet помещает товар е магазинную тележку и вызывает
сценарий viewCart.jsp для отображения содержимого тележки
4.7. Просмотр содержимого магазинной тележки
JSP-страница viewCart.jsp (рис. 4.12) извлекает компоненты CartltemBean из
магазинной тележки, подсчитывает стоимость для каждого товаров в тележке,
вычисляет суммарную стоимость всех товаров в тележке и создает XHTML-доку мент,
который дает возможность клиенту просматривать содержимое тележки в табличном
формате. Эта JSP-страница использует классы из пакета com.deitel.advjhtpl.store и из
пакетов java.util и Java,text.
Скриптлет в строках 25-43 извлекает атрибут сеанса для объекта магазинной
тележки (строка 26). Если магазинной тележки не существует, JSP-страница
выводит сообщение, указывающее, что тележка пуста. В противном случае в строках
34-41 создаются переменные, используемые для получения информации, которая
отображается в результирующем XHTML-документе. В частности, в стропе 34
осуществляется получение набора ключей, содержащихся в объекте-списке cart. Эти
ключи используются для извлечения объектов CartltemBean, которые
представляют каждую из книг в тележке.
В строках 45-51 осуществляется вывод XHTML-разметки, которой начинается
таблица, фигурирующая в документе. В строках 55-63 скриптлет продолжается
циклом, который использует каждый из ключей, содержащихся в объекте-списке
cart, для получения соответствующего компонента CartltemBean, извлекает
данные из него, вычисляет стоимость в долларах для этого товара и вычисляет общую
стоимость в долларах всех имеющихся в данный момент в тележке товаров (книг).
Окончание цикла находится вне тела скриггтлета в строках 70-86. Здесь
выполняется форматирование ранее полученных данных и помещение их в строку
XHTML-таблицы. Для помещения каждого значения данных в соответствующую
ячейку таблицы используются выражения JSP. После окончания цикла (строка
90) в строках 95-100 выводится суммарная стоимость в долларах всех книг в
тележке, а в строке 105 устанавливается атрибут сеанса, содержащий сумму. Это
значение используется сценарием process.jsp (рис. 4.14) для отображения
суммарной стоимости в долларах в сообщении, подтверждающем обработку заказа.
Книжный Internet-магазин, реализованный с использованием сервлетов и JSP 203
В строке 101 осуществляется зывод в последнюю строку XHTML-таблицы
суммарной стоимости в долларах всех книг, имеющихся в тележке.
1 <?xml version = "1.0"?>
2 <!D0CTYPE html PUBLIC "-//H3C//DTD XHTML 1.0 Strict//EN"
3 "http:/ /www .w3 . org/TR/xhtmll/DTD/xntmll-strict. dtd">
4 <!— viewCart.psp -->
5
6 <%-- параметры JSP-етраницы --%>
7 <%@ page language = "java" session = "true" %>
8 <%@ page import = "com.deite1.advjhtpl.store.*" %>
9 <%9 page import = "java.util. *'' %>
10 <%@ page import = "Java.text.*" %>
11
12 <html xmlns = "http://www.w3.org/1999/xhtml">
13
14 <head>
15 <title>Shopping Cart</title>
16
17 <linJc rel = "stylesheet" href = "styles.ess"
18 type = "text/ess" />
19 </head>
20
21 <body>
22 <p class = "bigFont">Shopping Cart</p>
23
24 <%-- начало скриптлета для отображения содержимого магазинной
тележки —%>
25 <%
26 Map cart = { Map ) session.getAttribute{ "cart" );
27 double total = 0;
28
29 if ( cart = null |[ cart.size() == 0 )
30 out.println( "<p>Shopping cart is currently empty.</p>" );
31 else {
32
33 // создание переменных, используемых при отображении
содержимого тележки
34 Set eartltems = cart.keySetО;
35 Iterator iterator = eartlterns.iterator();
36
37 BOOkBean book;
38 cartltemBean cartltem;
ЗЭ
40 int quantity;
41 double price, subtotal;
42
43 %> <%-- завершение скриптлета для вывода литерала XHTML -- %>
44
45 <table>
46 <theadxtr>
47 <th>Produet</th>
48 <th>Quantity</th>
4 9 <th>Priee</th>
50 <th>Total</th>
51 </tr></thead>
204
Глава 4
52
53 <% // продолжение скриптлета
54
55 while ( iterator.hasKextО ) {
56
5*7 // получение данных о книге; вычисление промежуточных
и итоговых сумы
58 cartrtem = ( CartltemBean ) cart.get( iterstor.next О )/
59 book = cartltern.getBook();
60 quantity = cartltem.getQuantity();
61 price = book.getPrice();
62 subtotal = quantity * price;
63 total += subtotal;
64
65 %> <%— Завершение скриптлета для вывода XHTML-кода и —%>
66 <%—'выражений JSP в этом цикле —%>
67
68 <%-- отображение строки таблицы с названием книги, —%>
69 <%-- количеством, ценой и промежуточной суммой --%>
70 <tr>
71 <tdx%= book.getTitlet) %X/t<J>
72
73 <tdX%= quantity %x/td>
74
75 <td class = "rights
7 6 <%=
77 new DeeimalFomiat( "0.00" ).format( price )
78 %>
79 </td>
80
81 <td class = "boid right">
82 <%=
83 new DacimalForaatl "0.00" ).format( subtotal )
84 %>
85 </td>
86 </tr>
87
88 <% // продолжение скриптлета
89
90 f // конец цикла while
' 91
92 %> <%— завершение скриптлета для литерала XHTML и —%>
93
94 <%— отображение строки таблицы, содержащей общую стоимость --%>
95 <tr>
96 <td eolspan = "4" class = "bold right">Total:
97 <%= new DeciatalFonnat ( "0,00" ).fonnat( total ) %>
98 </td>
99 </tr>
100 </table>
101
102 <% // продолжение скриптлета
103
104 // запись текущей суммы в атрибут сеанса
105 session. setAt tribute ( "total", new Doublet total ) ) ,-
106 } // конец блока else
Книжный Internet-магазин, реализованный с использованием сервлетов и JSP 205
107
108 %> <%-- конец екришглсгса --%>
109
110 <!-- ссыпка для возврата к books.jsp и продолжения покупок —>
111 <p class = "bold green">
112 <а href = "books.jsp">Continue Shopping</a>
113 </p>
114
115 <!-- фориа для полечена общей стоимости —>
116 <£orm method = "get" action = "order.html">
117 <pXinput type = "submit" value = "Check Out" /X/p>
118 </form>
119 </body>
120
121 </html>
Рис. 4.12, JSP-страница viewCart.jsp получает объект магазинной тележки и выводит
XHTML-документ, отображающий с содержимое тележки в табличном формате
Просматривая XHTML-док у мент, сформированный этой JSP-страницей,
пользователь может либо продолжить покупку, либо щелкнуть на кнопке Check Out,
чтобы перейти к странице оформления заказа order.html (рис. 4.13). *
4.8. Подсчет стоимости и оформление заказа
При просмотре содержимого тележки пользователь может щелкнуть на кнопке
Check Out, чтобы перейти к странице подсчета общей стоимости и оформления
заказа order.html (рис. 4.13). В этом примере форма не несет каких-либо
функциональных возможностей, однако она помогает создать целостную картину
приложения. Обычно в реальных приложениях предусмотрена определенная проверка
элементов формы на стороне клиента, определенная проверка элементов формы на
стороне сервера или сочетание этих проверок. Когда пользователь щелкает на
кнопке Submit, браузер запрашивает страницу process.jsp для окончательной об
работки заказа.
1 <?xml version = "1.0"?>
2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EH"
3 "http://www.w3.org/TR/xhbnll/DTD/xhtmll-strict.dtd">
4<!-- order.html —>
5
6 <html xmlns = "http://www.w3.org/1999/xhtml">
7
8 <head>
9 <title>Order</title>
10
11 <link rel = "stylesheet" href = "styles.ess"
12 type = "text/ess" />
13 </head>
14
15 <body>
16 <p class = "bigFont">Shopping Cart Check Out</p>
17
18 <!— Форма для ввода сведений о пользователе и о его кредитной
карте. -->
19 <'-- Примечание: Б этом примере реальные данные вводить
не нужно. —>
20 <form method = "post" action = "process.jsp">
21
22 <p style = "font-weight: bold">
23 Please input the following information.</p>
24
25 <F— таблица элементов формы -->
26 <table>
27 <tr>
28 <td class = "right bold">First name:</td>
29
30 <td>
31 <input type = "text" name = "firstname"
32 size = "25" />
33 </td>
34 </tx>
35
36 <tr>
37 <td class = "right bold">Last name:</td>
38
39 <td>
40 <input type = "text" name = "lastname"
41 size = "25" />
42 </td>
43 </tr>
44
45 <tr>
46 <td class = "right bold">Street:</td>
47
48 <td>
49 <input type = "text" name = "street" size = "25" />
50 </td>
51 </tr>
52
53 ' <tr>
54 <td class = "right bold">City:</td>
Книжный Internet-магазин, реализованный с использованием сервлетоа и JSP Z07
55
56 <td>
57 <input type = "text" name = "city" size = "25" />
58 </td>
59 </tr>
60
61 <tr>
62 <td class = "right bold">State:</td>
63
64 <td>
65 <input type = "text" name - "state" size = "2" />
66 </td>
67 </tr>
68
69 <tr>
70 <td class = "right bold">Zip code:</td>
71
72 <td>
73 <input type = "text" name = "zipcode"
74 size = "10" />
75 </td>
76 </tr>
77
78 <tr>
79 <td class = "right bold">Phone #:</td>
80
81 <td>
82 (
83 <input type = "text" name = "phone" size = "3" />
84 )
85
86 <input type = "text" name = "phone2"
87 size = "3" /> -
88
89 <input type = "text" name = "phone3" size = "4" />
90 </td>
91 </tr>
92
93 <tr>
94 <td class = "right bold">Credit Card #:</td>
95
96 <td>
97 <input type = "text" name = "creditcard"
98 size = "25" />
99 </td>
100 </tr>
101
102 <tr>
103 <td class = "right bold">Expiration (шш/уу):</td>
104
105 <td>
106 <input type = "text" name = "expires"
107 size = "2" /> /
108
109 <input type = "text" name = "expires2"
110 size = "2" />
208
Глава 4
ill </td>
112 </tr>
113 </table>
114
115 <!-- разрешить пользователю отправку формы -->
116 <pxinput type = "submit" value = "Submit" /x/p>
117 </fonti>
118 </body>
119
120 </html>
Рис. 4.13. Форма заказа (order.html), в которой пользователь вводит имя, адрес
и информацию о кредитной карте для завершения обработки заказа
4.9. Обработка заказа
JSP-страница process.jsp (рис. 4.14) предназначена для обработки информации
о кредитной карте пользователя и создания XHTML-документа, содержащего
сообщение, что заказ был обработан, и отображающего окончательную сумму заказа
в долларах. Скриптлет в строках 19-28 получает атрибут сеанса total. Объект
Double возвращается приведенным к типу double и сохраняется в переменной
total Java. В нашей имитации книжного Internet-магазина реальная обработка
кредитной карты не выполняется, потому завершения транзакции не происходит.
Книжный Internet-магазин, реализованный с использованием сервлетов и JSP 209
В этой связи в строке 26 вызывается метод invalidate интерфейса HttpSession для
уничтожения объекта сеанса для текущего клиента. В реальном Internet-магазине
сеанс не будет завершен, пока покупка не будет подтверждена компанией,
обслуживающей кредитную карту. В строках 30-40 определено тело XHTML-докумен-
та, отправляемого клиенту. Б строке 37 используется выражение JSP для вставки
общей стоимости всех приобретенных книг.
1 <?xml version = "1.0"?>
2<TDOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EM"
3 "httpi//www.w3.org/TR/xhtioll/DTD/xhtmll-strict.dfcd">
4 <!— process.jsp —>
5
6 <%— параметры JSP-страницы --%>
7 <%@ page language = "Java" session = "true" %>
8 <%@ page import = "}ava.. text .«" %>
9
10 <html xrolns = "http://www.w3.org/1999/xhtml">
11
12 <head>
13 <title>Thank You!</title>
14
15 <link rel = "stylesheet" href ■ "styles.ess"
16 type = "text/ess" />
17 </head>
18
19 <% // качало скриптлета
20
21 // получение общей суммы заказа
22 Double d = ( Double ) session,getAttribute{ "total" );
23 double total = d.doubleValue(};
24
25 // уничтожение сеанса по причине завершения обработки
2 6 session.invalidate();
27
28 %> <%— конец скриптлета —%>
29
30 <body>
31 <р class = "bigFont">TJiank You</p>
32
33 <p>Your order has been processed.</p>
34
35 <p>Your credit card has been billed:
36 <span class = "bold">
37 $<%= new DecimalFosraat( "0.00" ).£ormat( total ) %>
38 </span>
39 </p>
40 </body>
41
42 </html>
Рис. 4.14. JSP-стрэница process.jsp, выполняющая окончательную обработку заказа
(часть 1)
210
Глава 4
isoft Interne!: f«ptorer
В*
щт : л^дгчд
)ei«w»-
•m
ГЧЭ"
^iilF
Рис. 4.14. JSP-страница process.jsp, выполняющая окончательную обработку заказа
(часть 2)
АЛО. Развертывание приложения в J2EE 1.2.1
Теперь мы выполним развертывание приложения в эталонной реализации Java
2 Enterprise Edition 1.2.1. Предполагается, что вы уже загрузили и установили
пакет J2EE 1.2.1. Если нет, обратитесь к инструкциям по настройке. Файлы для
этого законченного приложения книжного Internet-магазин а можно найти на сайте
www. deitel.com.
В разделах с 4,10.1 по 4.10.8 будут описаны действия, необходимые для
развертывания этого приложения:
1. Настройте источник данных books для использования его с сервером
эталонной реализации J2EE 1.2.1.
2. Запустите сервер баз данных Cloudscape и сервер эталонной реализации
J2EE 1,2,1 для развертывания и выполнения приложения.
3. Запустите инструментальное средство развертывания Application
Deployment Tool. Это средство предоставляет графический интерфейс
пользователя для развертывания приложений на сервере J2EE 1.2.1.
4. Создайте новое приложение в Application Deployment Too].
5. Добавьте библиотеку JAR-файлов в приложение. Эти файлы доступны для
всех компонентов приложения.
6. Создайте в приложении новый Web-компонент для сервлета BookServlet.
7. Создайте в приложении новый Web-компокент для сервлета AddToCart-
Servlet.
8. Добавьте в приложение компоненты, не являющиеся сервлетами. К ним
относятся XHTML-документы, JSP-страницы, изображения, CSS-файлы, XSL-
файлы и компоненты JavaBeans, используемые в приложении.
9. Задайте контекст Web, который способствует выполнению данного
приложения J2EE. Это предполагает задание URL, который будет
использоваться для активизации приложения.
10. Задайте ресурс базы данных (т.е. books), используемый приложением,
11. Установите имя JNDI для базы данных в приложении. Это предполагает
регистрацию имени с помощью сервиса идентификации и маршрутизации
Java Naming and Directory Service, что позволит находить базу данных во
время выполнения.
Книжный Internet-магазин, реализованный с использованием сервлетов и JSP 211
12. Задайте файл приветствия длл приложения. Это начальный файл,
который возвращается, когда пользователь вызывает приложение.
13. Осуществите развертывание приложения.
L4. Выполните приложение.
В конце раздела 4.10.8 будет описано, как осуществить развертывание и
тестирование приложения книжного Internet-магазина.
4.10.1. Настройка источника данных books
Прежде, чем осуществить развертывание приложения, вы должны настроить
источник данных books, чтобы сервер J2EE зарегистрировал источник данных
с помощью сервера идентификации. Это даст возможность приложению
использовать JNDI для нахождеиия источника данных в процессе выполнения. В состав
J2EE входит Cloudscape — программное средство для управления базами данных,
написанное на «чистом» Java, разработанное компанией Informix Software. Мы
используем Cloudscape для выполнения манипуляций над базой данных в этом
практическом примере.
Для создания базы данных books, используемой в этом примере, мы
предоставляем сценарий SQL, который будет устанавливать базу данных и ее таблицы. Этот
сценарий иожет быть выполнен с помощью интерактивного инструментального
средства под названием ij, которое входит в состав Cloudscape. Мы предоставляем
командный файл (createDatabase.bat) и командный сценарий (createData-
base.ksh), которые вы можете использовать для запуска ij и выполнения
SQL-сценариев. В каталоге примеров для этой главы имеются сценария createDatabase
и SQL-сценарий books.sql1. Чтобы создать базу данных books, сначала убедитесь,
что сервер Cloudscape запущен. Откройте новое окно команд, а затем перейдите
к каталогу frameworks/RmiJdbc/bin Cloudscape. В этом каталоге выполните
командный файл или командный сценарий, начинающейся с имени setCHentClo-
udscapeCP, чтобы установить переменные окружения, необходимых сценарию
createDatabase. Далее, перейдите к каталогу на вашем компьютере, в который вы
поместили файлы примеров для этой главы, и взедите
createDatabase books.sql
чтобы выполнить SQL-сценарий, База данных books будет создана. [Замечание. Мы
предусмотрели этот сценарий, чтобы вы в любое время снова могли его выполнять
для восстановления исходного содержимого базы данных. При первом выполнении
сценария он выдаст четыре сообщения об ошибках, поскольку пытается удалить
четыре таблицы в базе данных. Раз база данных еще не существует, то нет и таблиц,
которые можно было бы удалить. Эти сообщения следует просто игнорировать.]
Чтобы настроить источник данных Cloudscape, необходимо модифицировать
файл конфигурации J2EE по умолчанию default, properties в каталоге config
J2EE. Ниже комментария JDBC URL Example имеется строка, которая
начинается с jdbc.datasources. Добавьте к этой строке следующий текст:
|jdbc/books|jdbc:cloudscape:rmi:books;create=true
Вертикальная линия | в начале текста отделяет новый источник данных, который
мы регистрируем, от источника данных, который регистрируется по умолчанию,
когда вы инсталлируете J2EE. Текст jdbc/books представляет собой имя JNDI для
базы данных. После второго символа | в тексте следует URL JDBC jdbc:cloud-
scape:rmi:books. URL указывает, что J2EE будет использовать протокол JPBC для
1 Эти сценарии имеются в комплекте примеров для главы 8 книги «Технологии
программирования на Java 2. Книга 1е (папка ch08). Их следует скопировать в папку с
примерами для данной главы (примеры для этой главы находятся в папке chili.— /Трим. ред.
212
Глава 4
взаимодействия с подчиненным протоколом Cloudscape, который, в свою очередь,
использует RMI для взаимодействия с базой данных (в нашем случае books).
Наконец, create=tme задает, что J2EE следует создать базу данных, если она пока не
существует. [О том, как создать базу данных, будет рассказано ниже.] После
настройки базы данных сохраните файл default.properi.ies. Этим завершается 1-й
шаг процесса развертывания (см. список в Разделе 4.10).
Совет по переносимости программ 4.2
Каждый драйвер базы данных обычно имеет свой собственный формат
URL, который дает возможность приложению взаимодействовать с
базой данных, хранящейся на этом сервере баз данных. За более подробной
информацией обратитесь к документации на сервер баз данных.
4.10.2. Запуск сервера Cloudscape и сервера J2EE
На шаге 2 (см. список в разделе 4.10) необходимо запустить сервер баз данных
Cloudscape и сервер J2EE, чтобы иметь возможность осуществить развертывание
и выполнить приложение. Сначала откройте окно команд и запустите сервер
Cloudscape.
Чтобы запустить сервер, сначала откройте окно команд. Перейдите к каталогу
установки Cloudscape (Cloudscape_3.6 по умолчанию). В этом каталоге имеется
каталог frameworks. В Cloudscape имеется два режима выполнения: embedded
я RmiJdbc. Режим embedded дает возможность выполнять Cloudscape как часть
приложения Java. Режим RmiJdbc дает возможность выполнять Cloudscape как
автономный сервер баз данных. Именно этот режим выполнения Cloudscape мы и
будем использовать. В каталоге каждого режима выполнения имеется подкаталог bin,
содержащий командные файлы (Windows) и командные сценарии (Linux/Unix) для
установки переменных окружения и выполнения Cloudscape. Перейдите
к подкаталогу bin режима RmiJdbc. Выполните командный файл или командный
сценарий, начинающийся с имени setServerCIoudscapeCP, чтобы установить
переменные окружения, необходимые серверу. Затем выполните командный файл или
командный сценарий, начинающийся с имени startCS, чтобы запустить сервер баз
данных Cloudscape. Работу сервера можно завершить, выполнив сценарий stopCS
из другого окна команд.
После того как сервер баз данных Cloudscape запущен, откройте окно команд
и перейдите к подкаталогу bin J2EE. Затем выполните следующую команду:
j2ee -verbose
для запуска сервера J2EE. В состав сервера J2EE входит сервер Tomcat JSP и
контейнер сервлетов, которые рассматривались в главах 2 и 3.
Совет по переносимости программ 4.3
В некоторых системах UNIX/Linux вам может потребоваться
предварять команды, которые запускают сервер Cloudscape и сервер J2EE,
символами ./ для указания, что команда расположена в текущем каталоге.
Совет по тестированию и отладке 4.1
Используйте отдельные окна команд (или процессоры команд) для
выполнения команд, которые запускают сервер базы данных Cloudscape и сервер
J2EE 1.2.1, чтобы иметь возможность наблюдать все сообщения об
ошибках, генерируемые этими программами.
Книжный Internet-магазин, реализованный с использованием сервлетов и JSP 213
jg&L Совет по тестированию и отладке 4.2
\^?у Чтобы гарантировать корректное взаимодействие сервера J2EE с
сервером Cloudscape (или любым другим сервером базы данных), всегда
запускайте сервер базы данных до запуска сервера J2EE. В противном случае
при попытках сервера J2EE настроить свои источники данных будут
выдаваться исключения.
Чтобы завершить работу сервера J2EE, выполните в окне команд следующую
команду из подкаталога bin J2EE:
j2ee -stop
jC&l Совет по тестированию и отладке 4.3
\^№у Всегда останавливайте сервер J2EE перед остановом сервера баз данных
^"""^ Cloudscape, чтобы гарантировать, что сервер J2EE не попытается
связаться с сервером баз данных: Cloudscape после того, как работа сервера
баз данных была завершена. Если сначала был отключен сервер
Cloudscape. существует возможность, что сервер J2EE получит другой запрос
и попытается снова осуществить доступ к базе данных. Это приведет
к возбуждению исключений.
4.10.3. Запуск средства развертывания приложений J2EE
Шаг 3 (см. список в разделе 4.LQ) является начальным этаном в процессе
развертывания приложения книжного Internet-магазина. Эталонная реализация J2EE
имеет в своем составе служебное приложение под названием Application
Deployment Tool, которое содействует развертыванию приложений Enterprise Java.
В главе 2 мы вручную создавали XML-дескриптор развертывания, чтобы внедрить
наши сервлеты. Инструментальное средство Application Deployment Tool хорошо
тем, что оно само записывает файлы дескрипторов развертывания и
автоматически архивирует компоненты Web-приложения. Инструментальное средство
помещает все компоненты Web-приложения и вспомогательные файлы для
конкретного приложения в один файл архива Enterprise Application Archive (EAR). Этот
файл содержит информацию о дескрипторе развертывания, WAR-файлы с
компонентами Web-приложеняя и некоторую дополнительную информацию, о чем
пойдет речь далее в этой книге.
Чтобы запустить инструментальное средство развертывания, откройте окно
команд К перейдите к подкаталогу bin J2EE. Затем введите следующую команду:
deploytool
Откроется окно Application Deployment Tool {рис. 4.15). [Замечание. При
обсуждении процесса развертывания мы касаемся только тех аспектов инструментального
средства развертывания, которые необходимы для развертывания нашего
приложения. Далее в книге будут более подробно рассматриваться и другие аспекты.]
4.10.4. Создание приложения книжного Internet-магазина
Инструментальное средство Application Deployment Tool упрощает задачу
развертывания приложений Enterprise Java. Далее (Шаг 4 в списке в разделе 4.10) мы
создадим новое приложение. Щелкните на кнопке New Application, чтобы
отобразить окно New Application (рис. 4.16).
214
Глава 4
В поле Application File Name можно ввести имя EAR-файла, в котором
средство Application Deployment Tool хранит компоненты приложения, либо можно
щелкнуть на кнопке Browse, чтобы задать и имя, и место размещения файла.
В поле Application Display Name можно задать имя для приложения. Это имя
будет присутствовать в списке Local Application основного окна инструментального
средства развертьшшшя (рис, 4.15). Щелкните наОК, чтобы создать приложение.
Появится основное окно Application Deployment Tool, как показано на рис. 4.17.
Новое приложение Новый Web-компонент Сохранить
Рис. 4.15. Основное окно Application Deployment Tool
Рис. 4.16. Окно создания приложения New Application
4.10.5. Создание Web-компонентов BookServlet
и AddToCartServlet
Шаг 6 (см. список в разделе 4.10) состоит в создании Web-компонентов для
сервлетов BookServlet и AddToCartServlet. Это дает возможность задавать
псевдоним, который будет использоваться для вызова каждого из ссрвлетов. Ниже мы
подробно рассмотрим процесс создания Web-компонента BookServlet. Затем вы
можете повторить эти действия для создания Web-компонента AddToCartServlet.
Щелкните на кнопке New Web Component (см. рис. 4.15), чтобы отобразить
начальное окно Introduction мастера создания нового Web-компонента New Web
Component Wizard (рис. 4.18).
Щелкните на кнопке Next>, чтобы отобразить окно WAR File General
Properties (рис. 4.19) мастера New Web Component Wizard.
Книжный Internet-магазин, реализованный с использованием сервлетов и JSP 215
^Application Repayment Too]: J5i» and Setytet Boqfestort fchanged).
г ь* "'eSfeasw». h^>
<JSlSJ
wt«tiwj$p}Ms*<™fceiiiie*»*""
Sstvlet Botietim
JJSP and E?ivIf1 Books lore
:«■■
*«*cation № Same;
Contents:
META-INF/MANIFEST.MF
META-3N F;ap pitc afcon *m I
МЕТА-IN F/s и n-J 2ee- n лггч
Add
"-:.*. ■"«
J>esiiyt^;|
Application description
"1
ft««K'
a*
""'КЗ
Ш-
■ттш«&:: • - ^
Рис. 4.17, Основное окно средства развертывания Application Deployment Tool после
создания нового приложения
и.ц.ц-и..щ1.лтим—
&&*SSSS£3-sii
лгг fill» tW тзшгз'-есШ. tben puke^ft +b*-etlect«f
jiJ#o into * Web iftcftiSt L H ,wut) - £^14**($£&иь r
pt*mt* the iiepbsi*tft?;4»*i*lpbwr гыдт^Гв& + * -.
£е%*£1вЗ t5jjj tadp ti^HBtlit- №ptii« tbt*Ctii&«lfrJ
••&
• Kg
ifc,
&.k ; m<>
3 Ц§*ц
Рис. 4.18. Начальное окно мастера New Web Component Wizard - Introduction
216
Глава 4
Рис. 4,19. Окно общих свойств WAR-файлой мастера New Web Component Wizard -
WAR Files General Properties
. Убедитесь, что в раскрывающемся списке Web Component Will Go In: выбрано
JSP and Servlet Bo ok Store. В поле WAR Display Name введите имя (StoreCompo-
nents) для WAR-файла, которое будет отображаться в списке Local Applications
основного окна средства развертывания (см. рис. 4.15). Затем щелкните на кнопке
Add..,, чтобы отобразить окно добавления в архив файлов содержимого Add Files
to .WAR — Add Content Files (рис. d.20). Файлы содержимого (content files) —
Рис. 4.20. Окно добавления в архив файлов содержимого Add Files to .WAR - Add
Content Files
Книжный Internet-магазин, реализованный с использованием сер влетов и JSP 217
это файлы, не являющиеся сервлетами, такие как изображения, XHTML-докумен-
ты, таблицы стилей и JSP-страницы. Эти файлы мы добавим на другом этапе
процесса развертывания, поэтому щелкните на Next>, чтобы перейти к окну
добавления в архив файлов классов Add Files to .WAR — Add Class Files (рис. 4.21).
Рис. 4.21. Окно добавления е архив файлов классов Add Files to .WAR - Add Class Files
Для добавления файла класса BookServlet.class щелкните на кнопке Browse...,
чтобы отобразить окно выбора корневого каталога Choose Root Directory (рис. 4.22).
При добавлении файла класса для класса, входящего в пакет (каковыми являются
все классы в нашем примере) требуется, чтобы файлы добавлялись и сохранялись
в соответствии с полной структурой каталогов их пакета или входили в состав
JAR-файла, который содержит полную структуру каталогов пакета. В этом примере
мы не создавали JAR-файл, содержащий весь пакет com.deitel.advjhtpl.store.
Следовательно, нужно найти каталог, в котором содержится первый каталог пакета.
Рис. 4.22. Окно выбора корневого каталога Root Directory
218 Глава 4
В нашей системе каталог com, который является первым каталогом в имени
пакета, расположен в каталоге Development. Когда бы щелкаете на кнопке выбора
корневого каталога Choose Root Directory, вы возвращаетесь в окно Add Files to
.WAR — Add Class Files. В этом окне вам следует найти каталог com (рис. 4.23).
Рис. 4.23. Окно добавления файлов классов Add Files to .WAR - Add Class Files после
выбора корневого кзтапсга, в котором находятся файлы
Дважды щелкните на имени каталога com, чтобы развернуть в окне его
содержимое. Проделайте то же самое для подкаталога deitel, затем для подкаталога
advjhtpl и, наконец, для каталога store. В каталога store выберите файл класса
.class для BookServlet, а затем щелкните на кнопке Add. В нижней части окна
Add Files to .WAR — Add Class Files будет отображен файл .class с полной
структурой каталогов пакета. Проделав это, щелкните на кнопке Finish, чтобы
вернуться в окно общих свойств WAR-файла New Web Component Wizard — WAR File
General Properties. Обратите внимание, что файлы, выбранные в окне Add Files to
.WAR, теперь отображаются в списке Contents (рис. 4.24).
Типичная ошибка программирования 4.1
Если вы не указали полную структуру каталогов пакета для класса,
входящего в пакет, приложение не сможет загрузить класс и надлежащим
образом его выполнить.
Щелкните на кнопке Next>, чтобы перейти к окну выбора типа компонента
New Web Component Wizard — Choose Component Type, и выберите Servlet
(рис. 4.25).
Щелкните на кнопке Next>, чтобы перейти к окну общих свойств компонента
New Web Component Wizard — Component General Properties (рис. 4,2б>.
Выберите класс BookServlet в раскрывающемся списке Servlet Class и введите
BookServlet в поле Web Component Display Name.
Книжный Internet-магазин, реализованный с использованием сервлетов и J5P 219
М-^ТТ
^БЭЗ
^4^itKii<tu»«**:!!^n1ta5»^#$™^
14* (»ЧЧ(ВДКЭЙОЛ. ОСЙЙЙ«I1C|9Ub1inB1ОДДО J^?rf№-M (ДОШфв. fOt^K
fta«^fl|^tfWin*.sdu!rie.«Md11l«Ffc&t$fi£t». *
— in ..?*. ,
Рис. 4.24. Окно общих свойств WAR-файла New Web Component Wizard - WAR Files
General Properties после выбора файла BookServlet.class
Рис. 4.25. Окно выбора типа компонента New Web Component Wizard - Choose
Component Type
Два раза щелкните на кнопке Ncxt>, чтобы отобразить окно псевдонимов
компонентов New Web Component Wizard — Component Aliases (рис. 4.27).
Щелкните на кнопке Add, "чтобы задать псевдоним для сервлета BookServlet. Щелкните
на пустом поле, которое имеется в окне, введите displayBook в качестве
псевдонима для сервлета и нажмите клавишу Enter. Далее, щелкните на кнопке Finish,
чтобы закончить настрийку параметров для сервлета BookServlet.
Теперь создайте Web-компонент для сервлета AddToCartServlet (Шаг 7 в
списке в разделе 4.10), повторив действия, описанные в этом разделе. Для этого
Web-компонента задайте Add To Cart Servlet в качестве отображаемого имени
Web Component Display Name и AddToCart в качестве псевдонима для сервлета.
После добавления двух Web-компонентов сервлетов окно Application Deployment
Tool должно выглядеть, как показано на рис. 4.28.
220
Глава 4
Рис. 4.26. Окно общих свойств компонентов New Web Component Wizard -
Component General Properties
Рис 4.27. Окно псевдонимов компонентов New Web Component Wizard —
Component Aliases
4.10.6. Добавление в приложение компонентов,
не являющихся сервлетами
Далее мы добавим в приложение компоненты, не являющиеся сервлетами (Шаг 8
в списке в разделе 4.10), К этим компонентам относятся JSP-страницы, .ХЬГШЬ-до-
кументы, таблицы стилей, изображения и компоненты JavaBeans, используемые
в приложении.
Для начала раскройте дерево компонентов приложения и щелкните на Store
Component в списке Local Applications в окне инструментального средства
Application Deployment Tool (см. рис. 4,28). В области содержимого окна Application
Deployment Tool щелкните на кнопке Add..., чтобы отобразить окно добавления
в WAR-архив файлов содержимого Add Files to .WAR — Add Content Files
(рис. 4.29).
Книжный Internet-магазин, реализованный с использованием сералетов и JSP 221
Рис 4.28. Окно инструментального средства Application Deployment Tool после
развертываний серзпетоа BookServlet AddToCartServlet
О AtfaToOrtS&rYlel.tJaes
О AddToCartSetviei jawa
D G Dolus I
Q ВооШеЗП class
Q Bookfiearijjva
Q boots jsP
Q Books enrfetctass
i^I^finiikSfjMfllJJiiKa
s
Рис. 4.29. Окно добавления в WAR-архив файлов содержимого Add Files to .WAR
Add Content Files
222
Глава 4
Перейдите в вашей системе к каталогу, в котором содержатся файлы
приложения книжного Internet-магазина. В поле списка, которое имеется в окне, найдите
каждый из следующих файлов и каталогов: book.xsl, books.jsp, Images, index.html,
order.html, process.jsp, styles.css и viewCart.jsp. Для каждого файла или каталога
щелкните на кнопке Add. Вы можете выбрать несколько элементов за раз,
удерживая клавишу <Ctrl> и щелкая на каждом из элементов. Все добавленные вами
элементы должны появляться в текстовой области в нижней части окна. Сделав это,
щелкните на Next>, чтобы отобразить окно добавления в WAR-архив файлов
классов Add Files to .WAR — Add Class Files (рис. 4.30).
Мы используем это окно для добавления в наше приложение файлов .class
классов, не являющихся сервлетами (т.е. компонентов JavaBeans), Помните, что
компоненты JavaBeans, используемые в приложении, содержатся в составе
пакета, поэтому файлы .class должны добавляться и сохраняться в соответствии с
полной структурой каталогов их пакета. Снова щелкните на кнопке Browse..., чтобы
отобразить окно выбора корневого каталога Choose Root Directory и найти
каталог, в котором содержится имя первого каталога пакета. Выберите этот каталог
в качестве корневого. Дважды щелкните на имени каталога com, чтобы раскрыть
его содержимое в окне. Проделайте то же самое для подкаталога deitel, затем для
подкаталога advjhtpl и, наконец, для каталога store. В каталоге store выберите
файлы .class для каждого из компонентов JavaBeans в этом примере (Book-
Bean.class, CartltemBean.class и TitlesBean.class), затем щелкните на кнопке
Add. В нижней части окна Add Files to .WAJR — Add Class Files каждый из
файлов .class будет отображен в соответствии с полной структурой каталогов пакета.
Проделав это, щелкните на кнопке Finish, чтобы вернуться в окно Application
Deployment Tool. Обратите внимание, что файлы, выбранные в окне Add Files to
.WAR, теперь фигурируют в текстовой области Contents окна. Щелкните на
кнопке Save, чтобы сохранить результаты вашей работы.
Рис. 4.30. Окно добавления в WAR-архив файлов классов Add Files to .WAR — Add
Class Files
Книжный Internet-магазин, реализованный с использованием серелетов и JSP 223
4.10.7. Задание контекста Web, ссылок на ресурсы,
имен JNDI и файлов приветствия
На шагах с 9 по 13 (см. список в разделе 4.10) осуществляется окончательная
настройка и развертывание приложения книжного Tnternet-магазина. Проделав
действия, описываемые в этом разделе, вы сможете выполнить приложение.
Мы начнем с задания Web-контекста для нашего приложения (шаг 9 в списке
в разделе 4.10). В начале этой главы мы указывали, что пользователь должен
ввести URL
http://localhost:8000/advjhtpl/store
в адресной строке браузера, чтобы осуществить доступ к приложению книжного
Internet-магазина. Контекст Web является частью указанного URL, которая дает
возможность серверу определять, какое приложение следует выполнить при
получении запроса от клиента. В данном случае контекстом Web является advjhtpl/
store. Снова обращаем ваше внимание, что сервер J2EE использует порт 8000, а не
порт 8080, используемый, сервером Tomcat.
•57jfq Типичная ошибка программирования 4.2
При задании в VRL неправильного номера парта, который
предполагается использовать для доступа к серверу J2BE, ваш Web-браузер
просигнализирует, что сервер не был найден.
Совет по тестированию и отладке 4,4
При развертывании приложения Enterprise Java на рабочем сервере (сервер
J2EE предназначен только для тестирования) обычно нет необходимости
указывать в URL номер порта для доступа к приложению. За более
подробной информацией обратитесь к документации на ваш сервер приложений.
Чтобы задать контекст Web, щелкните на узле JSP and Servlet Bookstore
в списке Local Applications в окне Application Deployment Tool. Затем щелкните
на вкладке Web Context (рис. 4.31). Щелкните на пустом поле в столбце Context
Root и введите advjhtpl/store; затем нажмите Enter.
^ Ф Store ConpDnenrj
ф AdeToCalSaiYlel
ЗШЕТЩЕ
&№
-У'
Ssnra АдАвопх
Jtn*4*frt^
Рис. 4.31. Задание контекста Web Context в окне Application Deployment Tool
Далее, мы должны задать ресурс базы данных, на который ссылается
приложение (шаг 10 в списке в разделе 4.10). Щелкните на узле Store Components в списке
Local Applications в окне Application Deployment Tool. Затем щелкните на
вкладке Resource RePs (рис, 4.32). Щелкните на кнопке Add. Ниже столбца Coded
Name щелкните на пустом поле и введите jdbc/books (имя JNDI нашего источника
данных). На рис. 4.32 показано окно Application Deployment Tool после создания
ссылки на ресурс.
224
Глава 4
Рис. 4.32. Задание ссь лки на ресурс в окне Application Deployment Tool
Далее, мы задаем имя JNDI для базы данных в приложении {шаг 11 в списке
в разделе 4.10), Оно используется для регистрации имени с помощью сервиса Java
Naming and Directory Service, чтобы база данных могла быть найдена
приложением в процессе выполнения.
Чтобы задать имя JNDI для базы данных, щелкните на узле JSP and Servlet
Bookstore в списке Local Applications в окне Application Deployment Tool. Затем
щелкните яа вкладке JNDI names (рис, 4,33). В столбце JNDI Name щелкните на
пустом поле и введите jdbc/books; затем нажмите Enter.
Рис. 4.33. Задание имени JNDI в окне Application Deployment Tool
Последнее действие, которое нам осталось выполнить перед тем, как
приложение будет развернуто, — это задать файл приветствия, которое будет отображаться
при первом посещвнии пользователем книжного магазина. Щелкните на узле
Store Components в списке Local Applications в окне Application Deployment
Tool. Затем щелкните на вкладке File Reps (рис. 4,34). Щелкните на кнопке Add.
Книжный Internet-магазин, реализованный с использованием сер влетов и JSP 225
В разделе Welcome Files щелкните на пустом поле и введите index.html. На
рис. 4.34 представлено окно Application Deployment Tool после задания файла
приветствия. Щелкните на кнопке Save, чтобы сохранить настройки приложения.
Рис. 4.34. Задание файла приветствия на вкладке File Ref s окна Application
Deployment Tool
4.10.8. Развертывание и выполнение приложения
Теперь можно выполнить развертывание приложения книжного Inter
net-магазина, чтобы протестировать его. На рис. 4.35 показаны кнопки панели управления
инструментального средства Application Deployment Tool для обновления файлов
приложения и развертывания приложения. Кнопка Update Application Files
обновляет ЕАВ-файл приложения после внесения изменений в любой из файлов,
например, после повторной компиляции классов или модификации файлов. Кнопка
Deploy Application заставляет средство Application Deployment Tool во
взаимодействии с сервером J2EE выполнить развертывание приложения.
Функциональные возможности обеих кнопок объединены в кнопке Update and Redeploy
Application.
Развертывание приложения
м
Обновление файлов приложения
Обновление и повторное
развертывание приложения
Рис. 4.35. Кнопки панели инструментов Application Deployment Tool для обновления
файлов приложения и развертывания приложения
226
Глава А
Щелкните на кнопке Deploy Application, чтобы отобразить окно Deploy JSP
and Servlet Bookstore — Introduction (рис. 4.36). Щелкните яа кнопке Next> три
раза, затем щелкните на кнопке Finish. Появится окно индикации процесса
развертывания Deployment Progress. Это окно дает возможность наблюдать, когда
развертывание завершится. После того как это произойдет, откройте Web-брауаер
и введите следующий URL для тестирования приложения:
http://localhoat:8000/advjhtpl/store
Рис. 4.36. Окно Deploy JSP and Servlet Bookstore — Introduction
В этой главе было представлено первое значительное приложение Enterprise
Java. Рассмотренные в этом разделе действия по развертывания приложения
книжного Internet-магазина, по сути, являются лишь некоторыми из действий,
необходимых для развертывания типового приложения Enterprise Java.
Например, в приложении книжного магазина не предъявлялось каких-либо требований
к безопасности. В реальных приложениях Enterprise Java некоторые, или даже все
компоненты приложения имеют ограничения, связанные с безопасностью,
например, «пользователь должен ввести правильное имя пользователя и пароль, прежде
чем он получит доступ к компоненту». Такие ограничения задаются на этапе
развертывания с помощью инструментального средства Application Deployment Tool
или какого-либо другого подобного средства в среде разработки Enterprise Java.
Эти ограничения, связанные с обеспечением безопасностью, накладываются
сервером приложений. В нашем примере книжного Inteniet-магазина, в случае, если
JSP-страницы имеют ограничения, связанные с безопасностью, следует
осуществлять развертывание каждой из них индивидуально, как мы это делали для сервле-
тов BookServlet и AddTo Cart Servlet. В последующих главах будут более подробно
рассмотрены параметры развертывания для компонентов приложения.
Спецификация Java 2 Enterprise Edition Specification (доступная по адресу java.sun.com/
j2ee/download.html) описывает полный набор параметров развертывания,
которые обязательны для серверов приложений, совместимых с J2EE.
В следующей главе будет продолжено обсуждение архитектуры клиент/сервер.
В ней мы используем сервлеты и XML для создания содержимого для беспроводных
устройств, таких как пейджеры, сотовые телефоны и карманные компьютеры.
Книжный Internet-магазин, реализованный с использованием сервлетов и JSP 227
Резюме
• В состав эталонной реализации Java 2 Enterprise Edition 1.2.1 входит сервер Apache
Tomcat JSP и контейнер сервлетов.
• Трехуровневое распределенное Web-приложение состоит из клиентского уровня,
серверного уровня и уровня базы данных.
• Клиентский уровень в Web-приложен и и часто представлен Web-браузером пользователя,
• Серверный уровень в Web-приложении часто реализуется с помощью JSP-страяиц и
сервлетов, которые действуют в интересах клиента.
• Уровень баз данных содержит, базу данных, доступ к которой осуществляется с
серверного уровня,
• В состав эталонной реализации Java 2 Enterprise Edition 1.2.1 входит инструментальное
средство развертывания Application Deployment Tool. Помимо многих других
возможностей, это средство позволяет задавать псевдонимы, используемые для вызова сервлета.
• Средство Application Deployment Tool создает дескриптор развертывания для сервлета
в процессе развертывания приложения,
• Файл приветствия — это документ по умолчанию, отправляемый в качестве ответа
клиенту, когда клиент первый раз взаимодействует с приложением J2EE,
• Различные браузеры имеют различный уровень поддержки каскадных таблиц стилей
Cascading1 Style Sheet.
• Страницы JavaServer Pages часто генерируют XHTML-код, который отправляется
клиенту для отображения.
• Интерфейс идентификации и «аршрутизации Java Naming and Directory Interlace (JNDI)
дает возможность компонентам приложения Enterprise Java осуществлять доступ к
информации и ресурсам (например, к базам данных), которые являются внешними дли
приложения. В некоторых случаях эти ресурсы являются распределенными в сети.
• Контейнер приложения Enterprise Java должен предоставлять сервис идентификации,
который реализует интерфейс JNDI и дает возможность компонентам выполняться в этом
контейнере, чтобы осуществлять поиск имен для нахождения нужных ресурсов.
Эталонная реализация сервера J2EE 1.2.1 содержит подобный сервис идентификации.
• При развертывании приложения Enterprise Java задаются ресурсы, используемые
приложением (например, базы данных), и имена JNDI для этих ресурсов.
• Используя класс InitialContext, компонент приложения Enterprise Java может
осуществлять поиск нужного ресурса. Класс InitialContext предоставляет доступ к окружению
идентификации приложения.
• Метод lookup класса InitialContext находит ресурс по заданному имени JNDI. Метод
lookup возвращает объект (Object) и возбуждает исключение NamingException, если не
может найти ресурс по имени, полученном в качестве параметра,
• Интерфейс Data Source используется для установки соединения с базой данных.
• Интерфейсы Document и Element пакета org.w3c.dom используются для создания дерева
XML-документа.
• Метод createElemeat интерфейса Document создает элемент в XML-документе.
• Метод createTextNode интерфейса Document задает текст для элемента Element.
■ Метод appendChild интерфейса Element добавляет узел в элемент Element как его
дочерний элемент.
• XML-документ может быть трансформирован в XHTML-документ с аошэщью таблицы
стилей XSL..
• Метод getRequestDispatcher интерфейса ServIctRequest возвращает объект Request-
Dispatcher, который может переадресовывать (forward) запросы к другим ресурсам или
включать (include) другие ресурсы в ответ сервлета.
• В случае вызова метода forward интерфейса RequestDispatcher обработка аанроса
данным сервлетом завершается,
• Объекты типа RequestDispatcher могут быть получены с помощью метода
getRequestDispatcher из объекта, который реализует интерфейс ServletRequest, или из объекта
ServletContext с помощью методов getRequestDispatcher и getNamedDispatcher.
• Метод getNamedDispatcher интерфейса. ServletRequest получает имя сервлета в качестве
параметра, а затем ищет сервлет в контексте ServletContext по этому имени. Если сера-
лет не найден, метод возвращает null.
228
Глава 4
• Как метод getRequestDispateher интерфейса ServletRequest, так и метод getReqnest-
Dispatcher интерфейса ServletContext просто возвращают содержимое заданного пути,
если путь не указывает на сервлег.
• Прежде, чем вы сможете воспользоваться возможностями XML и XSL, вы должны
загрузить и установить Java API (or XML Parsing (JAXP) версии 1.1 с сайта java.sun.com/xml/
download.htm.
• Корневой каталог JAXP (обычно он носит имя jaxp-1-l) содержит три JAR-файла,
которые необходимы для компиляции и выполнения программ, использующих JAXP: crim-
sun.jar, jaxp.jar и xalan.jar. Эти файлы должны быть добавлены к средствам расширения
Java 2 Standard Edition.
• JAXP 1.1 является составной частью эталонной реализации J2EE 1.3.
• Для создания дерева объектной модели документа (DOM) из XML-документа требуется
объект синтаксического анализатора DocumentBuilder. Объекты DocumentBuilder
создаются объектом-мастером Document Builder Factory.
• Классы Document и Element содержатся в пакете org.w3e.dom.
• Объект DOMSoarce представляет XML-документ в XSLT-трансформации. Для чтения
потока байтов, содержащихся в XSL-файле, может быть использовав объект StreamSource.
• Объект StreamResnlt определяет поток PrintWriter, в который будут записываться
результаты XSLT-трансформации.
• Статический метод newlnstance интерфейса TransformerFactory создает объект Trans-
forme rFaetory. Этот объект дает возможность программе получать объект Transformer,
который применяет XSLT-трансформацию.
■ Метод newTransformer интерфейса TransformerFactory принимает параметр Stream-
Source, представляющий XSL-таблицу стилей, которая будет применяться к ХМЪ-доку-
менту.
• Метод transform интерфейса Transformer выполняет XSLT-трансформацию над
заданным объектом DOM Source и записывает результат в заданный объект StreamResult.
• Исключение TransformerException возбуждается, если возникает проблема при создании
объекта TransformerFactory, создании объекта Transformer или выполнении
трансформации.
• Метод invalidate интерфейса HttpSessica уничтожает объект сеанса для текущего
клиента.
• Перед развертыванием приложения Enterprise Java необходимо настроить источники
данных и другие ресурсы, чтобы сервер J2EE мог зарегистрировать эти ресурсы с
помощью сервера идентификации. Это дает возможность приложению использовать JNDI для
нахождения ресурсов в процессе выполнения.
• В состав J2EE входит Cloudscape — приложение, разработанное компанией Informix
Software для управления базами данных, наиисанное на Java.
• Чтобы настроить источник данных Cloudscape, необходимо модифицировать файл
конфигурации J2EE по умолчанию default.properties в каталоге config J2EE. Ниже строки
комментария JDBC URL Examples имеется строка, которая начинается с jdbc.datasources.
Добавьте в эту строку следующий текст (в котором Источник Данных представляет собой
имя вашего источника данных):
I jdbc/ИстоминкДанных| jdbo: cloudscape: тл.\ИстачникДанных-,ъпа.Ъв=Ъ.Т11е
• Каждый сервер баз данных обычно имеет свой формат URL, который дает возможность
приложению взаимодействовать с базами данных, размещенными на сервере баз данных.
• Прежде, чем осуществить развертывание и выполнить приложение, необходимо
запустить сервер баз данных Cloudscape и сервер J2EE.
• Чтобы обеспечить надлежащее взаимодействие сервера. J2EE с сервером Cloudscape (или
любым другим сервером баз данных), всегда запускайте сервер баз данных перед
запуском сервера J2EE.
• Всегда отключайте сервер J2EE перед отключением сервера баз данных Cloudscape, чтобы
гарантировать, что сервер J2EE не попытается взаимодействовать с сервером баз данных
Cloudscape после того, как сервер баз данных был отключеп.
• В состав эталонной реализации J2EE входит инструментальное средство Application
Deployment Tool, которое помогает осуществлять развертывание приложений Enterprise
Java.
• Инструментальное средство развертывания Application Deployment Tool хорошо тем, что
оно само записывает файлы дескрипторов развертывания и автоматически архивирует
компоненты Web-приложения. Инструментальное средство помещает все компоненты
Книжный Internet-магазин, реализованный с использованием сервлегов и JSP 229
Web-приложения и служебные файлы для определенного приложения в один файл
архива Enterprise Application Archive (EAR). Этот файл содержит информацию о дескрипторе
развертывания, WAR-файлы с компонентами Web-приложения и дополнительную
информацию, о которой будет говориться далее в этой книге.
• При добавлении в приложение с помощью средства Application Deployment Tool файла
класса для класса в составе пакета, обязательно следует добавлять и сохранять файлы
с их полной структурой каталогов пакета или же в составе JAR-файла, который содержит
полную структуру каталогов пакета.
• Если вы не включите полную структуру каталогов пакета для класса в составе пакете,
приложение не сможет надлежащим образом загрузить и выполнить класс.
• Контекст Web для приложения представляет собой часть URL, которая дает возможность
серверу определить, какое именно приложение выполнять, когда сервер получает запрос
от клиента.
• Сервер J2EE использует порт 8000, тогда как Tomcat использует порт 8080.
• При развертывании приложения Enterprise Java на рабочем сервере приложений обычно
нет необходимости указывать в URL номер порта для доступа к приложению.
• Частью процесса развертывания приложения является задание ссылок на ресурсы для
компонентов приложения.
• Каждая ссылка на ресурс имеет соответствующее имя JNDI, которое используется
инструментальным средством развертывания для регистрации ресурса с помощью сервиса
Java Naming and Directory Service. Это дает возможность приложению находить ресурс
в процессе выполнения.
Терминология
Application Deployment Tool, средство
развертывания приложения
Cascading Style Sheets (CSS) — каскадные
таблицы стилей
component environment entries — записи
окружения компонента
configure a data source — настройка
источника данных
create a Web component — создание
Web-компонента
database resource — ресурс базы данных
Data Sou гее, интерфейс
deploy an application — развертывание
приложения
dynamic XHTML document — динамический
XH ТМ L-док умен т
Enterprise Application Archive (EAR) file —
файл архива приложения
external resource — внешний ресурс
forward, метод интерфейса
Requ es tDispat cher
getRequestDispatcher, метод интерфейса
Servlet Request
include content from a resource —
включение содержимого из ресурса
include, метод интерфейса
RequestDispatcher
InitislContext, класс
invalidate, метод интерфейса HttpSession
J2EE с on fig directory — каталог
конфигурации J2EE
Java 2 Enterprise Edition 1.2,1 reference
implementation — эталонная реализации
Java 2 Enterprise Edition 1.2.1
Java API for XML Parsing (JAXP) — API
Java для синтаксического анализа XML
Java Naming and Directory Interface
(JNDI) — интерфейс идентификации
и маршрутизации
j a vax. naming, пакет
jdbc.datasource, свойство конфигурации
J2EE
jdbc:cloudscape:rmi:books, URL JDBC
JNDI name — имя JNDI
locate a naming service — нахождение
сервиса идентификации
lookup, метод класса Initial Context
name lookup — поиск имен
name resolution — вычисление имени
register a data source with a naming
server — регистрация источника данных
сервером идентификации
RequestDispatcher, интерфейс
server tier — серверный уровень
ServletContext, интерфейс
shopping cart — магазинная тележка
style sheet — таблица стилей
Web component — Web-компонент
Web context — контекст Web
welcome file — файл приветствия
XML
XSL transformation — XSLT-трансформация
230
Глава А
Упражнения для самоконтроля
4.1. Заполните пропуски в следующих высказываниях:
a) Трехуровневое распределенное Web-приложение состоит из уровней ,
и .
b) __^ представляет собой документ по умолчанию, отправляемый в качестве
ответа клиенту, когда клиент в первый раз взаимодействует с приложением J2EE,
c) Интерфейс дает возможность компонентам приложения Enterprise
Java осуществлять доступ к информации и ресурсам (таким, как базы данных),
которые являются внешними для приложения.
d) Объект предоставляет доступ к окружению идентификации
приложения.
e) Объект RequestDispatcher может запросы к другим ресурсам или
другие ресурсы как часть ответа текущего сервлета.
1) API Sun предоставляет Java-программе возможности работы с XML
и XSL.
g) Метод интерфейса уничтожает объект сеанса для текущего
клиента.
h) для приложения является частью URL, которая дает возможность
серверу определять, какое именно приложение выполнять, когда сервер получает запрос
от клиента.
i) Контейнер приложения Enterprise Java должен предоставить , который
реализует интерфейс JNDI и дает возможность компонентам, выполняющимся в этом
контейнере, выполнять поиск имен для нахождения ресурсов.
j) В состав эталонной реализации J2EE входит приложение, называемое ,
которое содействует развертыванию приложений Enterprise Java.
4.2. Ответьте, является ли каждое из следующих высказываний истинным или ложным.
Если высказывание ложно, объясните, почему.
a) Сервер J2EE ожидает клиентские запросы на порту 8080.
b) При развертывании приложений с помощью сервера J2EE вы можете запускать
серверы Cloudscape и J2EE в любом порядке.
c) Метод lookup класса InitialContext находит ресурс по указанному имени JNDI.
d) Метод lookup возвращает объект Connection.1 представляющий соединение с базой
данных.
e) В состав эталонной реализации Java 2 Enterprise Edition 1.2.1 входит сервер Apache
Tomcat JSP и контейнер сервлетов.
f) Когда вызывается метод forward интерфейса RequestDispatcher, обработка
текущего сервлета яремеано иряостакавливаегея в ожидании ответа от ресурса, к которому
был переадресован запрос.
g) И метод getRequestDispateher интерфейса ServletRequest, и метод getRequest-
Dispatcher интерфейса ServletContext возбуждают исключение, если параметром
метода getRequestDispateher не является сервлет.
h) Каждая ссылка на ресурс имеет соответствующее имя JNDI, которое используется
инструментальным средством развертывания для регистрации ресурса с помощью
сервиса Java Naming and Directory Service.
i) Если вы ке настроили ваши источники ценных и другие ресурсы перед
развертыванием приложения Enterprise Java, сервер J2EE будет осуществлять поиск в
приложении, чтобы определить используемые ресурсы и зарегистрировать эти ресурсы с
помощью сервера идентификации.
j) Если вы не включите полную структуру каталогов пакета для класса, приложение
не сможет надлежащим образом загрузить и выполнить класс.
Ответы на упражнения для самоконтроля
4.1. а) клиента, сервера, базы данных, о) Файл приветствия, с) Java Naming and Directory
Interface (JNDI). d) InitialContext. e) переадресовывать (forward), включать (include).
Книжный Internet-магазин, реализованный с использованием сервлетов и JSP 231
f) Java for XML Parsing (JAXP). g) invalidate, HttpSession. h) Контекст Web. i) сервис
идентификации, j) Application Deployment Tool.
4.2. а) Ложно. Порт 80SO является портом по умолчанию для сервера Tomcat, Сервер J2EE
использует порт 8000.
b) Ложно. Чтобы обеспечить надлежащее взаимодействие сервера J2EE с сервером
Cloudscape («ли любым другим сервером баз данных), сервер баз данных должен быть
запущен перед запуском сервером J2EB.
c) Истинно,
d) Ложно. Метод lookup возвращает объект DataSource, который может быть
использован для получения соединения.
e) Истинно.
f) Ложно. Когда вызывается метод forward интерфейса ReqnestDispatcher, обработка
запроса текущим еервлетом завершается.
g) Ложно. И метод getltequestDispatcher интерфейса ServletRequest, и метод getRe-
questDispatcher интерфейса ScrvletContext просто возвращают содержимое заданного
пути, если путь не указывает на сервлет.
h) Истинно.
i) Ложно. Перед развертыванием приложения Enterprise Java вы должны настроить
ваши источники данных и другие ресурсы, чтобы сервер J2EE мое зарегистрировать
эти ресурсы с помощью сервера идентификации. В противном случае при попытках
приложения осуществить доступ к ресурсам будут возбуждаться исключения.
j) Истинно.
Упражнения
4.3. Модифицируйте практический пример книжного Inlernet-магазина чтобы дать
возможность клиенту изменять количество для каждой из книг, содержащихся в данный
момент в магазинной тележке. В странице viewCart.jsp отобразите это количество
в элементе input типа text в форме form. Предусмотрите кнопку UpdateCart, которая
дает возможность пользователю отправлять форму сервлету, осуществляющему
обновление количества для каждой из книг в тележке. Сервлет должен переадресовывать
запрос сценарию viewCart.jsp, чтобы пользователь мог увидеть обновленное содержимое
тележки. Выполните развертывание приложеаия и протестируйте его возможности по
обновлению содержимого тележки.
4.4. Усовершенствуйте компонент TitlcsBean в примере для книжного Internet-магазина,
чтобы получать информацию об авторе из базы данных books. Включите данные об
авторе в класс ВоокВеап и отображайте информацию об авторе как часть 1ЛГеЪ-Страни.цы,
которую пользователи будут видеть, когда выбирают книгу и просматривают
информацию о книге.
4.5. Добавьте средства проверки серверной формы в бланк заказа в примере для книжного
Internet-магазина. Проверяйте дату истечения срока действия кредитной карты.
Сделайте все поля в форме обязательными. Если пользователь указал данные не во всех
имеющихся полях, возвратите XHTML-документ, содержащий бланк заказа. Все поля,
в которые пользователь ранее ввел данные, должны содержать эти данные. Дли
выполнения этого упражнения замените документ order.html JSP-сценарием, который
динамически генерирует форму.
4.6. Создайте таблицу order и таблицу orderltems в базе данных books, чтобы хранить
заказы, размещенные покупателями. Таблица order должна хранить идентификатор
заказа orderlD, дату заказа orderDate и адрес email покупателя, разместившего заказ.
[Замечание. Вам понадобится модифицировать форму из Упражнения 4.6, чтобы
включить в нее адрес email покупателя.] Таблица orderltem должна хранить
идентификатор заказа orderlD, код ISBN, цену price и количество quantity для каждой книги в
заказе. Модифицируйте страницу process.jsp, чтобы она хранила информацию о заказе
в таблицах order и ordeTltems.
4.7. Создайте JSP-странипу, чтобы дать возможность клиенту просматривать архив
заказов. Интегрируйте эту JSP-страницу в приложение книжного магазина.
232
Глава 4
4.8. Создайте и выполните разъертываяие единого приложения, которое дает возможность
пользователю тестировать все примеры JSP-страниц из главы 3. Приложение должно
содержать файл приветствия, который представляет собой XHTML-докумект, содержа-
щий ссылки на каждый из примеров главы 3.
4.9. Создайте и выполните развертывание единого приложения, которое дает возможность
тестировать все сервлеты из главы 2. Приложение должно содержать файл
приветствия, который представляет собой XHTML-документ, содержащий ссылки на каждый
из примеров главы 2,
5
Разработка приложений
для беспроводной связи
на базе Java и J2ME
Цели
• Построить трехуровневое
приложение клиент/сервер.
• Научиться использовать XML
и XSLT для представления
содержимого нескольким
типам клиентов,
• Уяснить основные
возможности платформы
Java 2 Micro Edition (J2ME)
Platform.
• Получить представление
о жизненном цикле мидлета.
• Научиться использовать
средства CLDC и МЮР J2ME,
• Понять, каким образом
в практическом примере
используются возможности
технологии J2ME.
Я знаю одно: по-настоящему
счастливы будут лишь те из
вас, которые будут искать
и найдут свой путь служения
обществу.
Альберт Швейцер
Это был удивительный, редкий
прибор...
Сэмюел Тейлор Колеридж
Знание бывает двух видов.
Мы либо знаем сам предмет, либо знаем,
где можно найти информацию о нем.
Сэмюел Джонсон
... должно быть, весь имеющийся свет
сосредоточился на этой переплетенной
сети...
Джордж Эллиот
Если вы способны делать обычные вещи
необычным способом, вы заслуживаете
всеобщего внимания.
Джордж Вашингтон Карвер
234
Глава 5
5.1. Введение
В этой главе представлен практический пример приложения для проведения
тестирования (Tip-Test), предлагающего несколько вариантов ответов, чтобы
читатели могли проверить, насколько хорошо они запомнили условные обозначения
рубрик советов и рекомендаций по программированию, В каждом вопросе теста
предлагается рисунок, обозначающий рубрику советов, и приводится список из
четырех возможных вариантов ответов. Клиент загружает тест с сервера. Затем
пользователь выбирает ответ, который ему кажется правильным, и отправляет его
серверу. Сервер в ответ сообщает, является ли ответ верным или неверным. После
этого клиент может загрузить другой вопрос теста, и так далее.
Приложение Tip-Test имеет трехуровневую архитектуру, представленную на
рис. 5.1. Информационный уровень состоит из базы данных и содержит таблицу
(она определена в SQL^cueHapHH tips.sql), состоящую из семи строк и пяти
столбцов. Каждая строка содержит информацию о рубрике советов по
программированию Deitel: «Хороший стиль программирования» (Good Programming Practice),
«Общая методическая рекомендация» (Software-Engineering Observation), «Совет
по повышению эффективности» (Performance Tip), «Совет по переносимости
программ» (Portability Tip), «Внешний вид программы» (Look-And-Feel Observation),
«Совет по тестированию и отладке* (Testing and Debugging Tip) и «Типичная
ошибка программирования» (Common Programming Error). В первом столбце базы
данных хранятся целые числа, которые представляют собой уникальные
идентификаторы для каждой рубрики советов. Во втором столбце хранятся названия
рубрик советов. В третьем столбце хранятся описания рубрик советов и пояснения
значений каждой из них. В четвертом столбце хранятся имена рисунков условных
обозначений для каждой из рубрик советов. В пятом столбце хранятся
сокращенные названая рубрик советов; например, рубрика *Хороший стиль
программирования» (Good Programming Practice) имеет аббревиатуру GPP, На рис. 5,2
представлено содержимое базы данных tips.sql.
Разработка приложений для беспроводной связи на базе Java и J2ME 235
WAP
i-mode ЙМЕ
Клиентский
уровень [—
(верхний) I—
□ □ □ □
Средний уровень
WelcomeServlet
TipTestServlet
Информационный
уровень
(нижний)
База
данных
Рис, 5.1. Трехуровневая архитектура приложения Tip Test
Средний уровень представлен двумя сервлетами: WelcomeServlet и
TipTestServlet. Сервлет WelcomeServlet доставляет «окно приветствия», которое первым
отображается пользователю. Затем сервлет WelcomeServlet переадресовывает
клиента к сервлету TipTestServlet. Используя базу данных, сервлет TipTestServlet
произвольно выбирает рисунок для рубрики советов и четыре возможных варианта
ответа (в виде аббревиатур названий рубрик советов) и размечает эту информацию
как XML-документ. После этого сервлет TipTestServlet применяет XSLT-трансфор-
мацию к XML-документу и отправляет полученное содержимое клиенту.
tlpID
1
2
3
tipName
Хороший стиль
программирования
(Good Programming
Practice)
Типичная ошибка
программирования
(Common Programming
Error)
Внешний вид
программы
Oonk-And-Feel
Observation)
tipDescription
Рубрика «Хороший стиль
программирования»
привлекает внимание
к приемам, которые...
Студенты, изучающие язык
программирования, как
правлпо, совершают
определенного рода ошибки...
Мы предоставляем рубрику
«Внешний вид программы»,
чтобы сконцентрировать
внимание на графическом
интерфейсе...
tiplmage
good Prog ramming
prog ramming Error
LookAndFeel
shortName
GPP
CPE
UF
236
Глава 5
tiplD
4
5
б
7
tipName
Совет по повышению
эффективности
(Performance Tip)
Совет no
переносимости
программ
(Portability Tip)
Общая методическая
рекомендация
(Software Engineering
Observation)
Совет по тестированию
и отладке {Testing and
Debugging Tip)
tipDescription
В рубрике «Совет по
повышению эффективности»
подчеркиваются
яозможкости для повышения
эффективности работы
программ...
Организации,
разрабатываю! i 1ие
программные продукты,
часто предоставляют версии
программы, настраиваемые...
В рубрике «Общая
методическая
рекомендация» уделяется
внимание приемам,
архитектурным...
Большая часть этих советов
представляет собой
замечания относительно
возможностей
и особенностей...
tiplmage
pe'f
portability
software
Engineering
testing Debugging
shortName
PERF
PORT
SFO
TAD
Рис. 5.2. Содержимое базы данных tips.sql
Клиентский уровень представлен клиентами четырех типов: Internet Explorer,
WAP (Wireless Application Protocol), i-mode и J2ME. Каждый из клиентов может
отображать содержимое определенного типа. Сервлет TipTestServlet управляет
всей логикой работы приложения, т.е. произвольно выбирает вопрос, определяет,
является ли ответ пользователя правильным, и отправляет содержимое каждому
из клиентов. Браузер Microsoft Internet Explorer принимает содержимое в формате
XHTML (Extensible HyperText Markup Language). Имитатор браузера Openwave
UP — это WAP-клиент, который принимает содержимое в формате WML (Wireless
Markup Language). WAP — это протокол, который дает возможность
беспроводным устройствам передавать информацию через Internet. Содержимое,
отображаемое беспроводным устройством, размечается в формате WML. Pixo Internet Micro-
browser — это клиент i-mode, который принимает cHTML-содержимое, i-jnode
предоставляет популярный Internet-сервис на японском языке, a cHTML
(компактный HTML) представляет собой подмножество языка HTML,
предназначенное для устройств с ограниченными ресурсами. Имитатор MTDP-устройства
Sun выступает в качестве клиента J2ME, который принимает содержимое в виде
обычного текста. J2ME™ (Java™ 2 Micro Edition) — это новая платформа Java,
созданная Sun, предназначенная для разработки приложений для различных
бытовых устройств, таких как телевизионные игровые приставки, Web-терминалы,
встроенные системы, мобильные телефоны и пейджеры. MIDP (Mobile Information
Device Profile) представляет собой набор интерфейсов прикладного
программирования, который дает возможность разработчикам решать специфичные для
управления мобильными устройствами задачи, такие как создание интерфейсов
пользователя, локальное хранение информации и подключение к сети. Устройства, на
которых выполняются приложения для MIDP, называются MTDP-устройстеами
(например, сотовые телефоны или пейджеры). Более подробно о J2ME и MIDP
будет рассказываться в разделе 5.4.
Разработка приложений для беспроводной связи на базе Java и J2ME 237
При обсуждении данного практического примера будет рассмотрено, как
каждый из сервлетов отправляет информацию клиентам каждого типа, и как эти
клиенты отображают предоставленное им содержимое. В разделах 5.2 и 5.3
рассказывается, как сервлеты WelcomeServlet и TipTestServIet обрабатывают клиентские
запросы. В разделах 5.3.1-5.3.4 рассматривается, как сервлет TipTestServIet
реагирует на клиентские запросы, и как каждый из клиентов отображает
сформированное сервлетом содержимое. Наконец, в разделе 5.4 обсуждаются возможности
пакета Java 2 Micro Edition. Нас будут интересовать лишь клиентские
возможности J2ME (поскольку наши сервлеты не используют технологию J2ME), поэтому
эти возможности обсуждаются после рассмотрения сервлетов. До изучения раздела'
5.4 мы рекомендуем воспринимать клиента J2ME просто как «другой тип
клиента», который принимает сформированное сервлетом содержимое (т.е. вам не
нужно разбираться в технологии J2ME, чтобы понять поведение сервлета). В разделе
5.4 будет обсуждаться, как клиент J2ME принимает и интерпретирует
содержимое, например, как клиент J2ME отображает эти данные в интерфейсе
пользователя. Мы также рекомендуем следовать приведенным в разделе 5.5 инструкциям
для установки и настройки программного обеспечения, используемого в данном
практическом примере.
5.2. Обзор сервлета WelcomeServlet
Для начала рассмотрим класс WelcomeServlet (рис. 5Л), который
переадресовывает клиентский запрос статической странице, отображающей справочную
информацию по работе с приложением Tip-Test. Эта статическая страница содержит
ссылку на сервлет TipTestServIet, который дает возможность пользователю
непосредственно выполнить тест.
Клиенты взаимодействуют с сервлетами, посылая им запросы get или post.
Клиенты посылают запросы get сервлету WelcomeServlet, чтобы получить окно
приветствия. Если клиент посылает сервлету WelcomeServlet запрос get, этот
запрос обрабатывается методом doGet (строки 15-39).
Клиент каждого типа принимает от сервлета свое собственное окно
приветствия, поскольку каждый из них поддерживает свой тип содержимого. Например,
Internet Explorer принимает в качестве окна приветствия страницу Lndex.html,
так как Internet Explorer способен отображать XHTML-документы. С другой
стороны, имитатор Openwave UP принимает страницу index.wml, поскольку WAP-брау-
зер может отображать только WML-документы. Имитатор МШР-устройств Sun
может отображать только обычный текст, поэтому сервлет WelcomeServlet
отправляет этому устройству файл index.txt.1 Браузер Pixo для i-mode может
воспроизводить cHTML (компактный вариант HTML), поэтому сервлет посылает
страницу index.html, отличную от той, которая предназначалась для Internet
Explorer.
1 // WelcomeServlet.Java
2 // Доставка экрана приветствия клиенту
3 package com.deitel.advjhtpl.wireless;
4
5 // Базовый пакет Java
6 import java.io.*;
7
8 // Пакеты расширений Java
На момент написания книги клиенты J2MB могли интерпретировать XML-документы
только с помощью специального программного обеспечения, поставляемого
дополнительно, т.е. какого-либо стандарта для интегрирования XML в J2ME не существовало.
9 import javax.servlet.*;
10 import javax. servlet.http. * ;
11
12 public class WelcomeServlet extends HttpServlet {
13
14 // oinev на sanpoc get
15 protected void doGet( HttpServletRequest request,
16 HttpServletResponse response )
17 throws ServletException, IOException
IB {
19 // определение заголовка User-Agent
20 String userAgent = request.getHeader( "User-Agent" );
21
22 // отправка экрана приветствия соответствующему клиенту
23 if ( userAgent.indexOf {
24 ClientOserAgentHeaders.IE ) != -1 )
25 sendXEClientResponse( request, response );
26
27 else if ( userAgent.indexOf( // WAP
28 ClientUserAgentHeaders.WAP ) != -1 )
29 sendWAFClientsesponse( request, response );
30
31 else if ( userAgent.indexOf( // i-mode
32 ClientUserAgentHeaders.IMODB ) ! = -1 )
33 sendIModeClientResponse( request, response ) ,-
34
35 else if ( userAgent.indexOf( // J2ME
36 ClientUserAgentHeaders.J2ME ) != -1 )
37 sendJ2MEClientResponse( request, response ) ,-
38
39 > // конец метода doGet
40
41 // отправка экрана приветствия клиенту IE
42 private void sendlEClientResponse(
43 HttpServletRequest request, KttpServletResponee response )
44 throws IOException, ServletException
45 {
46 redirect( "text/html", "/XHTML/index.html", request,
47 response );
« }
49
50 // отправка экрана приветствия WAP-клиенту Nokia
51 private void sendHAPClientResponse(
52 HttpServletRequest request, HttpServletResponse response )
53 throws IOException, ServletException
54 {
55 redirect! "text/vnd.wap.wml", "/WAP/index.wml", request,
56 response );
57 }
58
59 // отправка экрана приветствия клиенту i-mode
60 private void sendlModeClaentResponse(
61 HttpServletRequest request, HttpServletResponse response )
62 throws IOException, ServletException
63 {
64 redirect( "text/html", "/iMode/index.html", request,
65 response );
Разработка приложений для беспроводной связи на базе Java и J2ME 239
66 }
67
в8 // отправка, экрана приветствия клиенту J2KE
69 private void sendJ2MEClientResponse(
70 HttpServletRequest request, HttpServletResponse response )
71 throws TOException
72 {
73 // отправка текстовых данных клиенту J2ME
74 response.setContentType\ "text/plain" );
75 PrintWriter out = response.getWriter();
76
77 // открытие файла для отправки клиенту J2ME
78 BufferedReader bufferedRe&der =
79 new BufferedReader( new FileReader(
60 getServletContext0.getRealPath(
81 "j2me/index.txt" ) ) );
82
83 String inputString = bufferedReader.readLine();
84
85 // отправка каждой из содержащихся в файле строк клиенту J2ME
86 while { inputString != null ) {
87 out.println( inputString );
88 inputString = buf f eredReader. readLine (),-
89 }
90
91 out.close(); // отправка данных
92
93 } // конец метода sendJ2MEClientResponse
94
95 // переадресация клиентского запроса другой странице
96 private void redirect( String contentType, String redirectPage,
97 HttpServletRequest request, HttpServletResponse response )
98 throws IOException, ServletException
99 {
100 // установка нового типа содержимого
101 response. setContentType ( contentType ) ,-
102 RequestDispatcher dispatcher =
103 getServietCon.textO .getRequeatDiepatcher (
104 redirectPage );
105
106 // переадресация запроса странице redirectPage
107 dispatcher.forward( request, response );
108 }
109 1
Рис. 5.3. Класс WelcomeServIet отправляет начальное окно, которое предоставляет
клиенту справочную информацию, как пользоваться тестом
Прежде чем ответить клиенту, метод doGet должен определить тип клиента,
обратившегося с запросом. Каждый клиент включает в каждый запрос заголовок
User-Agent. Этот заголовок содержит информацию о типе клиента, запросившего
данные с сервера. В интерфейсе ClientUserAgentHeader (рис. 5.4) определены
уникальные строки заголовка User-Agent для каждого из клиентов в нашем
приложении. Например, заголовок User-Agent для браузера Microsoft Internet Explorer
под Windows 2000, имеет следующий вид:
Moailla/4.0 (compatible; MSIE 5.0; Windows NT 5.0}
240
Глава 5
Мы ищем подстроку "MSIE 5" в заголовке User-Agent, чтобы отличить запросы
Internet Explorer от запросов, посылаемых браузерами других платформ. Кроме
того, сервлет WelcomeServlet будет распознавать различные версии Internet
Explorer 5 (например, v.5.0, v.5.5 и т.д.). Например, заголовок User-Agent для
клиента Windows 98 может не совпадать с приведенным выше, но, тем ие менее,
содержать подстроку "MSIE 5".
1 // ClientUserAgentHeaders.Java
2 // Содержит заголовки User-Agent для различных клиентов
3 package coin.deitel. advjhtpl. wireless ;
4
S public interface ClientUserAgentHeaders {
6
7 // заголовок User-Agent для браузера Internet Explorer
8 public static final String IE = "MSIE 5";
9
10 // заголовок User-Agent для браузера WAP
11 public static final String WAP = "UP";
12
13 // заголовок User-Agent для браузера iMode
14 public static final String IMODE = "Pixo";
15
16 // заголовок User-Agent для браузера J2ME
17 public static final String J2ME = "MIDP-1.0";
18 }
Рис. 5.4. Интерфейс ClientUserAgentHeader содержит уникальны? строки заголовка
User-Agent для всех типов клиентов
В строке 20 класса WelcomeServlet из запроса HttpServJetRequest извлекается
заголовок User-Agent. В строках 23-37 определяется, какой клиент послал
запрос, путем сопоставления фактического заголовка User-Agent с заголовками,
определенными в интерфейсе ClientUserAgentHeader. Если запрос отправлен
браузером Internet Explorer, в строке 25 вызывается метод sendlEClientResponse
(строки 42-48). В строках 46-47 вызывается метод redirect (строки 96-108), который
переадресовывает запрос статической странице. В случае поступления запроса от
Internet Explorer в строке 10 вызывается метод setContentType объекта HttpServ-
letResponse для установки MIME-muna. Значение text/html представляет собой
МШЕ-тип для клиентов XHTML. MIME-mun {Multipurpose Internet Mail
Extension) помогает браузерам определить, как интерпретировать данные. В строках
102-107 осуществляется переадресация запроса странице index.html путем
создания объекта RequestDispatcher с именем, соответствующим имени статической
страницы, и вызова метода forward объекта RequestDispatcher. После этого
браузер IE отображает страницу index.html (рис. 5.5).
Если запрос был сделай имнгагором Openwave UP, в строке 29 вызывается
метод sendWAPClientReaponse (строки 51-57), который вызывает метод redirect
с указанием в качестве MIME-типа text/vnd.wap.wml (МШЕ-тип для WML
клиентов) и переадресовывает запрос странице index.wml. После этого имитатор
отображает страницу index.wml, как показано на рис. 5.6.
Если запрос был сделан браузером Pixo i-mode, в строке 33 вызывается метод
sendModeClientResponse (строки 60-66). Этот метод также вызывает метод
redirect, но устанавливает в качестве MIME-типа text/html (МШЕ-тип для cHTML-
клиентов), после чего запрос переадресовывается версии страницы index.html
в формате cHTML. После этого браузер Pixo отображает страницу index.html, как
показано на рис. 5.7.
Разработка приложений для беспроводной связи на базе Java и J2ME 241
eLeandug Deitel Programming Tips
Рис. 5.5. Результат, формируемый сервлетом WekomeServlet (index.html) для клиента
XHTML
Рис. 5.6. Результат, формируемый сервлетом WekomeServlet (index.wml) для
WAP-клиента. (Изображение UP SDK предоставлено с разрешения корпорации
Openwave Systems Inc. Openwave, логотип Openwave и UP SDK. являются торговыми
марками корпорации Openwave Systems Inc. Все права соблюдены,)
Если запрос был сделал имитатором MIDP-устройства Sun, в строке 37
вызывается метод sendJ2MECIientBesponse (строки 69-93). Чтобы доставить страницу
index.txt клиенту J2ME, сервлет должен отправить страницу index.txt через
поток, существующий между сервлетом и клиентом J2ME. В строке 74 б качестве
MIME-типа устанавливается text/plain. В строке 75 вызывается метод getWriter
242
Глава 5
класса HttpServletResponse для получения объекта PrintWriter и отправки
данных клиенту, В строках 78-81 создается объект BufferedReader, который
прочитывает файл index.txt из каталога j2me, расположенного в контексте сервлета.
В строках 86-89 осуществляется отправка каждой строки, содержащейся в буфере
BufferedReader, клиенту. После этого имитатор Sun отображает страниду
index.txt, как показано на рис. 5.8,
м ьшш. [\тш JJUai* гШш
■*fM*:"-:V^M?* бЦ"*; ФШ-
PIXO
}/rtemeWtcrabmvser2.1 Я
1
i
\
•иг
шш>>шт
И :
ншШШж ■
1^Чреяй*^И
с? ^ S
•w <ca cs7
■С^^£^Ж£ЁЯН№
Рис. 5.7. Результат, формируемый сервлетом WelcomeServlet (index.html) для клиента
i-mode. (Публикуется с разрешения компании Pixo, Inc )
О Lx-M с^оглЖЩШ
Рис. 5,8. Результат, формируемый сервлетом WelcomeServlet (index.txt) для клиента
J2ME. (Публикуется с разрешения корпорации Sun Microsystems, Inc.)
Разработка приложений для беспроводной связи на базе Java и J2ME 243
5.3. Обзор сервлета TipTestServlet
В этом разделе мы обсудим, каким образом сервлет TipTestServlet (рис. 5.9)
формирует и посылает вопросы и ответы теста Tip-Test каждому из клиентов.
Каждое окно приветствия содержит ссылку на окно «инструкций» — info.html,
info.wml или info.txt, в зависимости от типа клиента, — которое содержит
справочную информацию, как пользоваться тестом. Окно инструкций содержит
ссылку на сервлет TipTestServlet.1
Если пользователь обращается к сервлету TipTestServlet, а сервлет ранее не
получал запросов от клиента, контейнер сервлета вызывает метод init (строки 30—67)
для инициализации сервлета TipTestServlet. Этот метод выполняет операции,
которые сервлету TipTestServlet нужно выполнить только один раз, например,
загрузка драйвера JDBC, установка соединения с базой данных и реализация
экземпляров объектов для создания XML-документов. В строках 36 -38 из параметра
инициализации сервлета в документе web.xml (который рассматривался при
установке Tomcat) извлекается имя класса для драйвера базы данных JDBC, а в строке
40 осуществляется загрузка этого драйвера.
1 // TipTestServlet.java
2 // Сервлет TipTestServlet отправляет допросы теста Tip Test клиентом.
3 package com.deitel.advjhtpl.wireles s;
4
5 // Набор базовых пакетов Java
6 import Java. i-o. *;
7 import java.sql.*;
8 import java.util.*;
9
10 // Пакеты расширений Java
11 import javax.servlet.*;
12 import j avax.servlet.http.*;
13 import javax.xml.parsers.*;
14 import javax.xml.transform.*;
15 import j avax.xml.transform.dom.*;
16 import javax.xml-transform.stream.*;
17
18 // импорт пакетов сторонних поставщиков
19 import org.w3c.dom.*;
20 import org.xml.sax.SAXException;
21
22 public class TipTestServlet extends HttpServlet {
23
24 private Connection connection; // соединение с базой данных
25
26 private OocumentBuilderFactory factory;
27 private TransformerFactory transformerFactory;
28
29 // инициализация сервлета
30 public void initO throws ServletExeeption
31 {
32 // загрузиа драйвера базы данных и реализация мастеров XML
33 try {
34
1 Клиенту не обязательно осуществлять доступ к сервлету TipTestServlet через сервлет
WelcomeServlet. Пользователь может ввести UEL TipTestServlet в браузере и обойти
«окно приветствия», которое генерирует сервлет WelcomeServlet.
244
Глава 5
35 // получение драйвера JDBC ив контейнера сервлетов
36 String jdtocDriver =
37 getServletConfigO .getlnitParameter<
38 "JDBCJDRIVER" };
39
40 Class.forHame ( jdbcDriver ); // загрузка драйвера JDBC
41
42 // получение ORL базы данных ив контейнера сервлетов
43 String databaseUrl =
44 getServletConfigO.getlnitParameter(
45 "DATABASE_URL" ) ;
46
47 connection = DriverManager .getConnection ( databaseUrl ) ,-
48
49 // создание мастера Factory для формирования XML-документов
50 factory = DocujnentBuilderFactory .newlnstance () ;
51
52 // создание нового мастера TransformerFactory
53 transformerFactory = TransformerFactory.newlnstanceО;
54
55 } // конец блока try
56
57 // обработка исключения, если драйвер базы данных не существует
59 catch ( ClassNotFoundException classNotFoundException ) {
59 classNotFoundException.printStackTrace();
€0 }
61
62 // обработка исключения при установке соединения
€3 catch ( SQLExceptioa sglException > {
64 sqlException.printStackTrace();
65 }
66
67 ) // конец метода in.it
68
69 // ответ на запросы get
70 protected void doGet( HttpSeirvletRequest request,
71 HttpServletResponse response )
72 throws ServletException, lOException
73 (
74 // получение оператора Statement из базы данных и отправка
вопроса теста lip-Test
75 try I
76
77 // SQL-запрос к базе данных
78 Statement statement = connection.createstatement();
79
80 // получение информации ив базы данных через SQL-запрос
81 ResultSet resultSet =
82 statement.executeQuery< "SELECT * FROM tiplnfo" );
83
84 // синтаксический разбор и отправка результирующего
множества ResultSet клиенту
85 if ( resultSet != null } (
86
87 // запрет кэширования вопросов теста клиентон
88 response.setHeader( "Cache-Control",
Рагработка приложений для беспроводной связи на базе Java и J2ME 245
89 "no-cache, must-revalidate" );
90 response.setHeader( "Pragma", "no-cache" );
91
92 sendTipTestQuestionf request, response, resultSet );
93 }
94
95 statement.close(); // закрытие оператора Statement
96 }
97
98 // обработка исключения при выполнение оператора SQL
99 catch ( SQXiException sqlException ) {
100 sqlException.printSt&ckTraceО ;
101 )
102
103 } // конец метода doGet
104
105 // ответ на запросы post
106 protected void doPost( HttpServletRequest request,
107 HttpServletSespon&e response )
108 throws ServletException, IOException
109 {
110 // отправка результирующего множества ResultSet
соответствующему клиенту
111 try {
112
113 // определение заголовка User-Agent
114 String usееAgent = request.getHeader( "User-Agent" );
115
116 // если клиентом является Internet Explorer
117 if { userAgent.indexOf(
118 ClientUserAgentHeaders.IE ) 1= -1 ) {
119
120 Document document =
121 createXMLTipTestAnswer( request );
122
123 // задание соответствующего типа содержимого
Content-Type для клиента
124 response.setContentType( "text/html" );
125
126 // отправка. XML-сонержииого после XSLT-трансформации
127 applyXSLT( "XHTML/XHTMLTipAnswer.xsl", document,
128 response );
129 }
130
131 // если клиентом является WAP-браузер
132 else if ( userAgent.indexOf{
133 ClientUserAgentHeadeSs.WM> ) '= -1 ) {
134
135 Document document =
136 createXMLTipTestAnswer[ request ) ;
137
138 // задание соответствующего типа содержимого
Content-Type для клиента
139 response.setContentType( "text/vnd.wap.wnl" 1 ;
140
141 // отправка XML-содержимого после XSLT-трансформации
246
142 applyXSLT( "HAP/WAPTipAnswer.xsl", document,
143 response );
144 }
145
146 // если клиентом является браузер i-mode
147 else if ( userAgent.indexOf(
148 ClientOserAgentHeaders.IMODE ) != -1 ) (
149
150 Document document =
151 createXMLTipTestAnswer( request );
152
153 // Задание соответствующего типа содержимого
Content-Type для клиента
154 response.setContentType{ "text/html" );
155
156 // отправка XML-содержимого после XSLT-трансформации
157 applyXSLT( "iMode/IMODETipAnswer.xsl", document,
158 response };
159 >
160
161 // если клиентом является J2ME
162 else if ( userAgent.indexOf(
163 ClientUserAgentHeaders.J2ME ) != -1 )
164 sendJ2M£Answer( request, response );
165
166 } // конец блока try
167
168 // обработка исключения, если документ отсутствует (null)
169 catch ( NullPointerException nullPointerException ) {
170 nullPointerException.printStackTrace();
171 )
172
173 } П конец метода doPost
174
175 // отправка данных теста Tip-Test клиенту
176 private void sendTipTestQuestion(
177 HttpServletRequest request, HttpServletResponse response,
178 ResultSet resultSet > throws IOException
179 {
190 // отправка результирующего множества ResultSet
соответствующему клиенту
181 try {
182
183 // определение Заголовка User-Agent
184 String userAgent = request.getHeader( "User-Agsnt" );
185
186 // если клиентом является Internet Explorer
187 if ( userAgent.indexOf(
188 ClientOserAgentHeaders.IE )!=-!)(
189
190 Document document =
191 createXMLTipTestQuestion( resultSet, request,
192 request.getContextPath() + "/XHTML/images/",
193 ",gif" );
194
195 // задание соответствующего типа содержимого
Разработка приложений для беспроводной связи на базе Java и J2ME 247
Content-Type для клиента
196 response.setContentType{ "text/html" );
197 applyXSLT( "XHTML/XHTMLTipQuestion.xsl", document,
198 response );
199 )
200
201 // если клиентом является НАР-браузер
202 else if ( userAgent.indexOf(
203 ClientUserAgentHeaders.WAP ) != -1 ) {
204
205 Document document =
206 createXMLTipTestQoestiont resultSet, request,
207 request.getContextPathO + "/WAP/images/",
208 ".wbmp" );
209
210 // задание соответствующего типа содержимого
Content-Type для клиента
211 response.setContentType( "text/vnd.wap.wml" );
212 applyxsLT( "WAP/WAPTipQuestion.xsl", document,
213 response );
214 }
215
216 // если клиентом является браузер i-mode
217 else if ( userAgent.indexOf(
218 ClientUserAgentHeaders.IMODE ) != -1 ) {
219
220 Document document =
221 createXMI>TipTestQuestion( resultSet, request,
222 request.getContextPath[) + "/iMode/images/",
223 ".gif" );
224
225 // Задание соответствующего типа содержимого
Content-Type для клиента
226 response.setContentType ( "text/html" );
227 applyXSLT( "iMode/lMODETipQuestion.xsl", document,
226 response );-
229 }
230
231 // если клиентом является J2KE
232 else if ( userAgent.indexOf(
233 ClientUserAgentHeaders.J2ME ) != -1 )
234 sendJ2MEClientResponse( resultSet, request,
235 response );
236
237 1 // конец блока try
238
239 // обработка исключения, если документ отсутствует (null)
240 catch ( NullPointerException nullPointerExceptlon ) {
241 nullPointerException.printStackTrace{);
242 }
243
244 } // конец метода sendTipTestQuestion
245
246 // отправка вопросов теста Tip Test клиенту Internet Explorer
247 private Document createXMLTipTestQuestion(
248 ResultSet resultSet, HttpServletRequest request,
248
Глава 5
249 String imagePrefix, String iroageSuftix )
250 throws IOException
251 {
252 // преобразование множества ResultSet
в двухмерный строковый массив
253 String reaultTable[][] = getResultTablet resultSet );
254
255 // создание генератора случайных чисел
256 Random random = new Random( System.currentTimeMillisО )i
257
258 // создание 4 произвольно выбранных рубрик советов
259 int randomRow[] = getRandomlndices{ random );
260
261 // произвольное определение верного индекса из 4 случайно
выбранных индексов
262 int correctAnswer = Math.abs( random.nextlnt() ) %
2 63 randotnRow. length;
264
2 65 int correctRow = randomRowf correctAnswer ];
266
267 // открытие нового сеанса
268 HttpSession session = request. getSessionO;
269
270 // сохранение правильного ответа в данных сеанса
271 session.setAttribute( "correctAnswer",
272 new Integer( correctAnswer ) );
273
274 // сохранение правильного названия рубрики
275 session.setAttributef "correctTipMame", new String(
276 resultTable[ correctRow ][ 1 ] ) );
277
278 // сохранение правильного описания рубрики
279 session.setAttribute( "correctTipDescription", new String(
2B0 resultTable[ correctRow ][ 2 I ) );
281
282 // определение изображения, отправляемого клиенту
263 String imageName - imagePrefix +
284 resultTable[ correctRow ][ 3 ] + imageSuffix;
285
286 // создание XML-документа на основе случайным образом
определенной информации
287 try {
288
289 // создание документа
290 DocumentBuilder builder = factory.newDocumentBuilder{);
291 Document document = builder.newDocument{);
292
293 // создание лоряевого элемента question
294 Element root = document.createElement( "question" );
295 document.appendChild{ root );
296
297 // добавление элемента image, указывающего на имя файла
изображения
298 Element image = document.createElement( "image" };
299 image.appendChild(
300 document.createTextNode( imageName } );
Разработка приложений для беспроводной связи на базе Java и J2ME 249
301 root.appendChild( image );
302
303 // создание элемента choices дли хранения 4 элементов choice
304 Element choices = document.createElement( "choices" );
305
306 // добавление 4 элементов, choice, представликютх выбор
пользователя
307 for ( int i = 0; i < randomRow.length; i++ )
308 {
309 // определение элементов choice из таблицы resultTable
310 Element choice = document.createElement[ "choice" );
311 choice,appendChildI document,createTaxtHode(
312 resultTable[ randomRowf i ] ][ 4 ] ) );
313
314 // установка элемента choice как правильного или
неправильного ответа
315 Attr attribute =
316 document.createAttributet "correct'' ) ;
317
318 if ( i = correctAnswer )
319 attribute.setValue< "true" );
320 else
321 attribute.setValue ( "false" );
322
323 // добавление элемента choice в элемент choices
324 choice.setAttributeNode( attribute );
325 choices.appendChiId( choice );
326 }
327
32B root.appendChild( choices );
329
330 return document;
331
332 ) // конец блока try
333
334 // обработка исключения при построении документа
335 catch ( ParserConfigurationExeeption parserException ) {
336 parse rExcept ion.printStacMrace();
337 }
338
339 return null;
340
341 } // конец метода createXMLTipTestQuestion
342
343 // отправка вопросов теста клиенту J2ME
344 private void sendJ2MEClientResponse{ ResultSet resultSet,
345 HttpServletRequest request,
346 HttpServletResponse response ) throws lOException
347 {
348 // преобразование множества ResultSet в двухмерный строковый
массив
349 String resultTable[][] = getResultTable( resultSet );
350
351 // создание генератора случайных чисел
352 Random random = new Kandom( System.currentTimeMillis0 );
353
250
354 // создание 4 случайно выбранных рубрик советов
355 int randomRow[] = getRandomlndices( random );
356
357 // произвольное определение правильного индекса из 4 случайно
выбранных индексов
358 int correctAnswer = Math.abs{ random,nextlnt() ) %
359 randomRow.1ength ;
360
361 int correctRow = randomRowt correctAnswer ];
362
363 // открытие прежнего сеанса
364 KttpSeasion session = request.getSession();
365
366 // сохранение правильного ответа в данных сеанса
367 session.setAttribute{ "correctAnswer",
368 new Integer{ correctAnswer ) );
369
370 // сохранение правильного названия рубрики в данных сеанса
371 session.setAttribute( "correctTipHame", new String(
372 resultTable[ correctRow ][ 1 ] ) );
373
374 // сохранение правильного описания рубрики в данных сеанса
375 session.setAttribute( "correctTipDescription", new String{
376 resultTable[ correctRow ][ 2 ] ) J;
377
378 // отправка клиенту J2ME имени файла изображения
37 9 String imageName = "/j2ir.e/images/" +
380 resultTable[ correctRow ][ 3 ] + ".png",-
381
382 response.setContentType( "text/plain" );
383 PrintWriter out = response.getWriter{)/
384 out.println( xmageName );
385
386 // отправка вопроса теста клиенту J2ME
387 for ( int i = 0; i < randomRow. length,- i++ )
388 out.println( resultTable! randomRow[ i ] ][ 4 ] );
389
390 } // конец метода sendJ2MEClientResponse
391
392 // преобразование множества ResultSet в двухмерный строковый
массив
393 private String[][] getResultTable( ResultSet resultSet )
394 {
395 // создание таблицы строк для хранения результирующего
множества ResultSet
396 String resultTable[][] = new String[ 7 ][ 5 ],-
397
398 for ( int i = 0; i < 7; i++ ) {
399
400 for ( int 3 = 0; j < 5; j++ )
401 resultTable[ i ][ j ] = "";
402 )
403
404 // сохранение всех столбцов в таблице
405 try {
Разработка приложений для беспроводной связи на базе Java и J2ME 251
407 // для каждой строки в resultSet
408 for ( int tow = Q; resultSet.nextl)г row++ ) {
409
410 // для каждого столбца в resultSet
411 for ( int column = 0; col-шип < 5; column.*-*- ) \
412
413 // сохранение элемента resultSet в таблице resultTable
414 resultTable[ row }[ column ] +=
415 resultSet,getObject( column + 1 );
416 )
417 }
418 }
419
420 // обработка исключения, если сервлет не может получить
объект ResultSet
421 catch ( SQLException sqlException ) {
422 sqlException.printstacKTraceO;
423 return null;
424 }
425
426 return resultTable;
427
428 ) // конец метода getResultTable
429
430 // получение 4 случайно сформированных индексов иэ таблицы
resultTable
431 private intИ getRandomlndices( Random random )
432 {
433 // создание списка, содержащего индексы строк для resultTabLe
434 int listl] = new int[ 7 ];
435
436 for ( int i = 0; i < list.length; i++ ]
437 list[ i ] = I;
438
439 int randomRow[] = new int( 4 ];
440
441 // выбор 4 случайно сформированных индексов иэ списка
442 for ( int i = 0; i < randomRow.length; i++ )
443 randomRow[ i ] = getRandomRow( list, random ) ;
444
445 return randomRow; // возврат этих индексов
446
447 } // конец метода getRandomlndices
448
449 // получение прсивволыюпо элемента иэ списка и его аннулирование
450 private int getRandomRow( int list[]. Random random )
451 {
452 // получение произвольного элемента из списка
453 int randomRow = Math.abs( random.nextlnt() ) % list.length;
454
455 while ( list[ randomRow ] < 0 )
456 randomRow = Math.aba( random.nextlnt() ) % list.length;
457
45S liatt randomRow ] = -1; // аннулирование элемента
459
4 60 return randomRow;
252
Глава 5
461
462 > // конец метода getRandomRow
463
464 // применение таблицы стилей XSL к XML-документу
465 private void applyXSLT( String xslFile,
466 Document xmlDocument, RttpServletResponse response )
467 throws IOException
468 {
469 // применение XSLT-трансформацки
470 try {
471
472 // открытие потока ввода InputStream для XSL-документа
473 InputStream xslStream =
474 getServletContextO.getResourceAsStream( xslFile );
475
476 // создание потока StreamSource для XSLT-документа
477 Source xslSource « new StreamSource( xslStream );
■178
4 79 // создание объекта DOMSource для исходного XML-документа
480 Source xmlSource = new DOMSouree( xmlDocument );
481
482 // получение объекта PrintWriter для записи данных клиенту
483 PrintWriter output = response.getwriter();
484
485 // создание объекта StreamRestilt для трансформации
результата
486 Result result = new StreamResult( output );
487
4BB // создание объекта Transformer для XSL-трансформации
489 Transformer transformer =
490 transformerFactory. newT ran a former ( xslSource ) ,-
491
492 // трансформация и доставка содержимого клиенту
493 transformer.transform( xmlSource, result ) ;
494
495 } // конец блока try
496
497 // обработка исключения при трансформации содержимого
498 catch ( TransformerException exception ) {
499 exception.printstackTrace();
500 }
501
502 } // конец метода applyXSLT
503
504 // создание XML-документа, содержащего ответ
на вопрос теста TipTest
505 private Document createXMLTipTestAnswer(
506 HttpServletRequest request ) throws IOException
507 {
508 // получение сеанса
509 HttpSession session = request.getSession();
510
511 // сопоставление правильного ответа с ответом, полученным
в ходе сеанса
512 Integer integer =
513 ( Integer ) session.getAttribute( "correctAnswer" );
Разработка приложений для беспроводной связи ма базе Java и J2M6 253
514 int correctftnswer = integer.Antvalue();
515
516 // предоставление клиенту правильного названия рубрики
советов и ее описания
517 String correctTipMame =
518 ( String ) session.getAttributef "correctTipName" );
519
520 String correctTipDescfiption =
521 ( String ) session.getAttribute(
522 "correctTipDescription" );
523
524 // получение выбора от пользователя
525 int selection = Integer.parselntt
526 request,getParameter( "userAnswer" ) ) ,-
527
529 String answer;
529
530 // определение, является ли выбор пользователя правильным
531 if < correctftnswer = selection )
532 answer = "Correct";
533 else
534 answer = "Incorrect";
&35
536 // получение ссылки на сервлет TipTestServlet
537 String servletName = request.getContextFathО + "/" +
538 getServletConfigQ .getServletName() ;
540 // создание XML-докумеита на основе случайным обрааои
определенной информации
541 try {
542
543 // создание документа
544 DocumentBuilder builder = factory. newDocumentBuilder () ,-
545 Document document = builder-newDoOUment() ;
546
547 // создание корневого элемента question
548 Element root = document.createElement( "answer" );
549 document.appendChild! root );
550
551 // добавление элемента, информирующего клиента
о правильном ответе
552 Element correct = document.createElement( "correct" );
553 correct.appendChild{
554 document.createTextNode( answer ) );
555 root.appendChild( correct ) ;
556
557 // добалекие Элемента, содержащего название рубрики советов
558 Element name =
559 document.createElement( "CorrectTipName" );
560 name.appendChild(
561 document.createTextNode( correctTipName > );
562 root.appendChild( name );
563
564 // добавление элемента, содержащего описание рубрики советов
565 Element description =
566 document.createElement( "correctTipDescription" );
2S4
Глава 5
567 description.appendChild(
568 document.createTextNode( correctTipDescription ) );
569 root.appendChild( description );
570
571 // добавление элемента, содержащего ссылку на сервлет
TipTeatServlet
572 Element aervletLink =
573 document.cre&teElement{ "eervletHame" );
574 8ervletLink.app*ndChild(
575 document.createTextNode{ servletName ) );
576 root.appendChildt aervletLink );
577
57В return document;
579
580 } // конец блока try
581
582 // обработка исключения при построении документа
583 catch ( ParserConfigurationException parserException ) {
584 parserException.printStackTrace();
585 }
586
587 return null;
588
589 ] // Конец метода createXMLTipTestAftswer
590
591 // отправка ответа клиенту J2ME
592 private void sendJ2MEAnsver( HttpServletRequest request,
593 HttpServletResponse response ) throws IOException
594 {
595 // получение ответа клиента на вопрос теста
596 BufferedReader in = request.getReader();
597 int selection = Integer.parselntf in.readLine{),trim() );
598
599 // отправка клиенту J2ME текстовых данных
600 response. sefcContentType ( "text/plain" ) ,*
601 PrintWriter out = response.getWriter();
602
603 // информирование клиента, является ли его ответ правильным
или неправильным
604 HttpSession session = request.getSessionf);
605
606 // сопоставление правильного ответа с ответом, полученным
в ходе сеанса
607 Integer integer =
608 ( Integer ) session.getAttribute( "correctAnswer" );
609 int coxrectAnswer = integer.intValue();
610
611 // отправка правильного названия рубрики советов и ее описания
612 String correctTipName =
613 ( String ) session.getAttribute( "correctTipName" );
614
615 String correctTipDescription =
616 ( String ) session.getAttribute(
617 "correctTipDescription" );
618
619 /J определение/ является ли ответ правильным
Разработка приложений для беспроводной связи на базе Java и J2ME 255
620 if ( selection = correctAnswer )
621 out.println( "Correct" );
622 else
623 out.println( "Incorrect" );
624
625 // предоставление клиенту правильного названия рубрики
советов и ее описания
626 out.println( correctTipName );
627 out.println ( correctTipDeseription );
628
629 ) // конец метода sendJ2MEAnswer
630
631 // метод, вызываемый при закрылки сервлета
632 public void destroy()
633 {
634 // закрытие соединения с базой данных
635 try {
636 connection.close О;
637 )
638
639 // обработка исключения, если соединение не может быть Закрыто
640 catch ( SQLExeeption sqlException ) {
641 sqlException-printStackTiaceО ;
642 }
643
644 } // конец метода destroy
645 }
Рис. 5.9. Сервлет TipTestServiet реализует основную логику работы теста и посылает
вопросы теста Ttp-Tes1 клиентам
Использование метода getlnitParameter в строке 37 дает возможность задавать
внешнюю для сервлета базу данных, При этом можно менять используемую базу
данных (например, вместо базы данных Cloudscape использовать базу данных
mySQL), модифицируя элемент <param-value> в элементе <init-parara>
документа web.xinl без повторной компиляции сервлета. В данном практическом примере
этот элемент содержит
<init-param>
<param-name> JDBC_DRTVER </paxam-name>
<param-value>
COM.cloudscape.core.BmiJdbcDriver
</param-value>
</init-psram>
В строках 43—45 из документа web.xml извлекается URL базы данных, а в строке
47 осуществляется соединение с ресурсом., задаваемым этим URL. Заметим, что
закрытие этого соединения осуществляется методом destroy (строки 632-644). Если
возникнет необходимость изменить местоположение базы данных, для этого
нужно будет всего лишь модифицировать элемент <param-valu.e> в отдельном
элементе <init-param> документа web.xml. В данном практическом примере этот
элемент содержит
<init-param>
<param-name> DATABASE_ORL </param-name>
<parasi-value>jdbc: cloudscape; rroi: tips </param-val,ue>
</init-param>
2S6
Глава S
В строках 50-53 создаются объекты, которые позднее будут использоваться для
построения XML-документов (объекты Document), и объекты Transformer для
применения XSLT-трансформаций. Далее в этом разделе будут рассмотрены
пакеты Java, относящиеся к XML.
Сервлет TipTestServlet вызывает метод doGet (строки 70-103) для обработки
запроса get. В строке 78 из объекта соединения Connection, реализованного в
методе init, создается объект оператора SQL Statement. В строках 81^82 в результате
действий этого оператора извлекается результирующее множество ResultSet всех
элементов базы данных. Как говорилось ранее, база данных (объект ResultSet)
содержит семь строк и пять столбцов — всего 35 строковых объектов. В строках
88-90 задается заголовок ответа HttpServletResponse, чтобы клиент не кэширо-
вал информацию, отправляемую сервлетом TipTestServlet. В строке 92
осуществляется вызов метода sendTipTestQuestion (строки 176-244) для реализации
логики работы теста и отправки клиенту вопроса теста Tip-Test. В строке 184 из
запроса HttpServletRequest извлекается заголовок User-Agent. В строках 187-235
определяется тип клиента, пославшего запрос, путем сопоставления фактического
заголовка User-Agent с заголовками, определенными в интерфейсе ClientUser-
AgentHeaders. Процесс определения типа клиента идентичен процессу, который
имел место в сервлете Welcomes ervlet.
До этого момента сервлет выполнялся одинаковым образом для всех
клиентских запросов. Теперь же сервлет TipTestServlet начинает выполнять различные
операции в зависимости от типа клиента, пославшего запрос. Эти операции почти
идентичны для Internet Explorer, имитатора Openwave UP и браузера i-mode Pixo.
Операции же, выполняемые сервлетом для клиента J2ME, отличаются от
операций, выполняемых для трех других типов клиентов, поскольку клиент J2ME не
может интерпретировать XML-документы без использования дополнительного
специализированного программного обеспечения (хотя в будущем ситуация,
вероятно, изменится). Далее мы рассмотрим поведение сервлета в случае поступления
запроса от клиента каждого из поддерживаемых типов. Начнем мы с рассмотрения
поведения сервлета, если запрос поступил от имитатора Openwave UP. Затем мы
обсудим, как себя ведет сервлет, если запрос поступил от браузера i-mode Pixo.
После этого ш рассмотрим поведение сервлета в случае поступления запроса от
MIDP-устройства Sun.
5.3.1. Запрос от браузера Internet Explorer
Если запрос был отправлен Internet Explorer, в строках 190-193 вызывается
метод createXMLTipTestQuestion (строки 247-341), который создает и
возвращает XML-документ (объект Document), содержащий вопрос теста Tip-Test.
Интерфейс Document из пакета org.w3c.dom представляет собой узел верхнего уровня
XML-документа, который обеспечивает доступ ко всем узлам документа. В строке
253 осуществляется вызов метода getResultTable (строки 393-428), который
преобразует результирующее множество ResultSet в двухмерный массив строк. Этот
метод используется для облегчения доступа к отдельным элементам
результирующего множества ResultSet, организованным в виде массива. В строках 396-402
реализуется экземпляр этого массива, а в строках 408-417 массиву передается
содержимое результирующего множества ResultSet.
После того как метод getResuItTable возвращает строковый массив, в отроке
256 создается объект Random, который дает возможность сервлету TipTestServlet
произвольным образом выбирать рубрику советов и возможные варианты ответа.
В строке 259 ссылка на объект Random передается методу getRandomlndices
(строки 431-447), который возвращает массив из четырех целых чисел. Каждое
целое число имеет свой, случайно выбранный индекс, который соответствует руб-
Разработка приложений для беспроводной связи на базе Java и J2ME 257
рике советов из двухмерного строкового массива, формируемого методом getRe-
sultTable. В строках 434—437 создается массив с именем list из семи целых чисел.
Каждое целое число представляет собой индекс этого целого числа в массиве (т.е.
list[0]=0, Hst[l]=l и т.д.), В строке 439 создается пустой массив из семи целых
чисел, который соответствует четырем возможным ответам (названиям рубрик
советов). В строках 442-443 осуществляется вызов метода getRandomRow (строки
450-462) для каждого элемента в массиве из четырех целых чисел. В строке 453
случайным образом выбирается число из массива, содержащего семь целых чисел.
Если число уже было выбрано ранее (это индицируется значением —1), в строках
455-456 выбирается новое число. Если данное целое число ранее не было выбрано,
в строке 458 осуществляется аннулирование элемента этого массива путем
присвоения ему значения —1. Аннулирование этого элемента гарантирует, что этот
метод getRandomRow не будет возвращать повторяющихся целых чисел. В строке
460 возвращается целое число.
Массив randomRow содержит четыре различных целых числа, которые
соответствуют рубрикам советов из таблицы resultTable. В строках 262—263
произвольно выбирается целое число из массива randomRow. Это целое число указывает
на правильный ответ на вопрос теста. Фактически, это целое чиело соответствует
строке в таблице resultTable, которая содержит информацию для правильного
названия рубрики советов (строка 265). Сервлет TipTestServlet хранит эту
информацию в объекте HttpSession (строка 268), чтобы при отправке клиентом сделанного
пользователем выбора обратно сервлету TipTestServlet последний мог сравнить
этот выбор с правильным ответом, хранящимся в объекте HttpSession. Объект
HttpSession действует подобно cookies: он также может хранить пары
имя/значение. Применительно к сеансу такие пары называются атрибутами. Атрибуты
можно сохранить в объекте HttpSession с помощью метода setAttribute. В строках
271-280 осуществляется сохранение правильного ответа, названия рубрики и
описания рубрики советов в объекте HttpSession. С учетом правильного ответа и
параметров imagePrefix и imageSuffix метода createXMLTipTestQuestion, в строках
283-284 определяется надлежащее условное изображение (рисунок) рубрики,
отправляемое клиенту.
Итак, сервлет TipTestServlet сформировал вопрос теста Tip-Test (изображение
и четыре возможных варианта ответа), который будет отправлен клиенту. Теперь
поясним, как сервлет TipTestServlet размечает эти данные в виде
XML-документа, применяет XSLT-трансформацию к этому документу, а затем отправляет
полученный документ клиенту.
В строках 290-328 выполняется разметка вопроса теста Tip-Test в виде XML-
документа (объект Document), и предоставляются средства для создания и
извлечения его узлов. В строке 290 создается объект DocumentBuilder с помощью
мастера DocumentBuilderFactory, экземпляр которого реализуется в методе init. В
строке 291 с помощью DocumentBuilder создается XML-документ.
Класс Element представляет узел элемента в XML-документе- Объект Document
создает элемент (объект Element) с помощью метода createElement. В строках
294-295 создается объект Element с именем question, который становится
корневым элементом в документе. В строках 298-301 на основе верного условного
изображения рубрики советов создается элемент (объект Element) с именем image.
Этот элемент становится дочерним узлом элемента question. В строке 304
создается элемент с именем choices, который содержит четыре элемента choice,
созданных в строках 307-326. Каждый элемент choice представляет собой одни из
вариантов ответа ка вопрос теста. В строках 315-324 осуществляется включение
атрибута, указывающего, является ли сделанный выбор правильным (correct) или
неправильным (incorrect). Естественно, только один из атрибутов может
указывать на верное значение (correct). В строке 325 осуществляется добавление элемен-
258
Глава 5
тов choice в элемент choices, а в строке 328 элемент choices добавляется в корневой
элемент question.
После того как метод createXMLTipTestQuestion возвратил XML-документ
(объект Document), сервлет TipTestServlet должен применить XSLT-трансформацию
к этому документу и отправить результат трансформации клиенту. В строке 124
в качестве ЩМЕ-ткпа ответа HttpServletResponse устанавливается text/html,
поскольку XSLT-трансформация генерирует XHTML-документ. В строках 127-128
осуществляется вызов метода appIyXSLT (строки 465-502) для применения
к XML-документу таблицы стилей XHTMLTipQuestion.xsl (ряс. 5.10). В строках
473-474 открывается поток ввода InputStream для файла XHTMLTipQuesti-
on.xsl. В строке 480 создается объект DOMSource для XML-документа. В строке
486 создается объект Result для результата трансформации — в данном случае,
XHTML-документа. Сервлет TipTestServlet использует объект Print Writer для
создания объекта результата Result, чтобы отправлять результаты трансформации
непосредственно клиенту. В етроках 489—490 создается объект Transformer с
использованием XSL-таблицы StreamSource и мастера TransforraerFactory
(экземпляр которого реализуется в методе init). Transformer — это объект, который
применяет XSLT-трансформациго к XML-документу. В строке 493 осуществляется вызов
метода transform класса Transformer для применения трансформации и получения
XHTML-документ,
В листинге на рис. 5.10 в строках 9—11 с помощью элемента xsl:output задается
DTD и другие параметры вывода. В строке 15 задается, что элемент question
является корневым элементом XML-доку мента, который будет подвергнут
трансформации с помощью таблицы стилей XrlTMLTipQuestion.xsl. XHTML-документ
начинается в строке 16 элементом html. Элемент body в строках 22-86 содержит
описание рисунка для рубрики советов и четыре возможных ответа. В строках 27-29
отображается рисунок, ассоциированный с элементом image в XML-документе.
В строках 40-83 создается форма с четырьмя возможными вариантами ответов.
В строках 47-77 создаются четыре переключателя, ассоциированные с этими
ответами. В строке 82 создается кнопка Submit, чтобы пользователь мог отправить
сделанный выбор серверу. На рис. 5.11 представлено приложение Tip-Test в
действии после применения сервлетом XSLT-трансформации к XML-документу. На
рисунке слева показана фаза выбора ответа пользователем, а на рисунке справа —
фаза отправки ответа пользователем.
1 <?xml version="1.0"?>
2
3 <!— XHTMLTipQuestion.xel -->
4 <!— таблица свилей XHTML -->
5
6 <xsl:stylesheet version = "1.0"
7 xmlneixsl = "http://www.w3.org/1999/XSL/Transform">
8
9 <xsl:output method = "xml" omit-xml-declaration = "no"
10 doctype-system = "DTD/xhtmll-strict.dtd"
11 doctype-public = "-//W3C//DTD XHTML 1.0 Strict//EN"/>
12
13 <•-- задание корня XML-документа, —>
14 <>-- который указывает иа эту таблицу стилей -->
15 <xsl: template match = "<Juestion">
16 <html xmlns="http://www.w3.org/1999/xhtml">
17
18
19 <head>
20 <title>Tip Test</title>
Разработка приложений для беспроводной связи на базе Java и J2ME 259
21 </head>
22
23 <body>
24
25 <р>
26
27 <!— вывод изображения —>
28 <img name = "image" alt = "Tip Image"
29 src = "{image}">
30 </img>
31
32 </p>
33
34 <p>
35 What is the name of the icon shown?
36 </p>
37
38 <p>
39
40 <!— создание формы с четырьмя переключателями —>
41 <form method = "poet"
42 action = "/advjhtpl/tiptest">
43
44 <!— построение таблицы с вариантами выбора —>
45 <table>
46 <tr>
47 <td>
48 <inptit type = "radio"
49 name = "userAnswer" value = "0">
50 </input>
51 <xsl:value-of select =
52 "choices/choice[1]"/>
53 </td>
54 <td>
55 <input type = "radio"
56 name = "userAnswer" value = "1">
57 </input>
58 <xsl:value-of select =
59 "choices/choice[2]"/>
60 </td>
61 </tr>
62 <tr>
63 <td>
64 <input type = "radio"
65 name = "userAnswer" value = "2">
66 </input>
67 <xsl:value-of select =
68 "choices/choice[3]"/>
£9 </td>
70 <td>
71 <input type = "radio"
72 name = "userAnswer" value = "3">
73 </input>
74 <xsl:value-of select =
75 "choices/choice[4]"/>
76 </td>
77 </tr>
260
Глава 5
78
79
80
81
82
83
84
85
86
87 </
88
89
</table>
<input type =
</form>
</p>
</body>
</html>
</xsl:templates
ksI:atylesheet>
= "submit" value = "Submit"/>
Рис. 5.10. Таблица стилей XHTMLTipQuestion.xsl для трансформации XML-документа
с вопросом теста г XHTML-документ
What is the name of the icon shown?
r CPE rPERF
rGPP &20KT
BSf*
zl
1ДВД««£^^
■.-HkjHa»i
What is the name of the icon shown?
r CPE rpERF
r OPP ffPORT
) :.-■ ШЩи^т#1£^Ш: к-щ
Рис. 5.11, Отображение окна с вопросом теста Tip-Test в Internet Explorer
Каждый переключатель в XHTML-документе содержит уникальное значение,
чтобы при нажатии пользователем кнопку Submit Internet Explorer отправлял
выбранное значение еервлету TipTestServlet (с помощью метода post). Сервлет
TipTestServlet (рис. 5.9) вызывает метод doPost (строки 106-173) при получении
данных (отправленных методом poet). В строках 117-129 распознается, что запрос
поступил от Internet Explorer, и вызывается метод createXMLTipTestAnswer
(строки 505—589) для определения, является ли ответ пользователя правильным.
В строке 509 извлекается объект HttpSession, а в строках 512-522 из объекта
HttpSession извлекается правильный ответ, название рубрики советов и ее
описание, В строках 525-526 из объекта HttpServletRequest извлекается сделанный
пользователем выбор. В строках 531 -534 выбор пользователя сопоставляется с
ответом, чтобы определить, правильный ли ответ дал пользователь. В строках
537-538 определяется URL сервлета TipTestServlet, чтобы клиенты могли
повторно соединиться с сервлетом TipTestServlet для получения следующего вопроса
теста Tip-Test.
В строках 544-576 осуществляется разметка содержимого объекта сеанса
HttpSession и URL сервлета TipTestServlet как XML-документа на основе
шаблона Document Builder. В строке 544 создается объект DocumentBuilder, а в строке
545 — XML-документ (объект Document). В строках 548-549 создается элемент
(объект Element) answer и назначается корневым элементом документа. В строках
Разработка приложений для беспроводной связи на базе Java и J2ME 261
552-554 создается элемент correct, который хранит информацию, является ли
ответ пользователя правильным или неправильным. В строке 555 элемент correct
назначается дочерним элементом элемента answer. В строках 558—561 и в строках
565-568 создаются элементы correctTipName и correctTipDescription, которые
хранят, соответственно, название правильной рубрики советов и ее описание.
В строках 562 и 569 эти элементы назначаются дочерними элементами элемента
answer. В строках 572-575 создается элемент servletName, который хранит URL
сервлета TipTestServlet. В строке 576 этот элемент назначается дочерним
элементом элемента answer.
После того как метод createXMLTipTestAnswer возвращает XML-документ
(строка 578), сервлет TipTestServlet должен применить XSLT-трансформацию
к этому документу и отправить результат клиенту. В строке 124 устанавливается
МШЕ-тип text/html, а в строках 127-128 вызывается метод apply XSLT для
применения к XML-документу таблицы стилей XHTMLTipAnswer.xsl (рис. 5.12).
1 <?xml versAon="I.0"?>
2
3 <•— XHTMLTipAnswer.xsl —>
4 <!— таблица стилей XHTML —>
5
6 <xsl:stylesheet version = "1.0"
7 xmlns:xsl = "http://www.w3.org/1999/XSL/Transform">
8
9 <xsl:output method = "xml" omit-xml-declaration = "no"
10 doctyp^-system = "DTD/xhtJBll-strict.dtd"
11 doctype-public = "-//W3C//DTO XHTML 1,0 Strict//EN"/>
12
13 <!— задание корня XML-документа, —>
14 <!-- который указывает на эту таблицу стилей —>
15 <xsl:template match = "answer">
16 <html xmlfts="nttp://www.w3.org/1999/xntml">
17
18 <bead>
19 <title>Tip Test Answer</title>
20 </head>
21
22 <body>
23
24 <p>
25 <hl>
26 <xsl:value-of select = "correct"/>
27 </hl>
28 </p>
29
30 <p>
31 <h2>Tip Name</h2>
32 </p>
33
34 <p>
35 <h3>
36 <xsl:value-of select = "correctTipName"/>
37 </h3>
38 </p>
39
40 <p>
41 <h2>Tip Description</h2>
262
Глава 5
42 </р>
43
44 <р>
45 <ЬЗ>
46 <xsl:value-of
47 select = "coerectTipDeacription"/>
48 </h3>
49 </p>
50
51 <p>
52 <h2>
53 <a href="{servletName)">Next Tip</a>
54 </h2>
55 </p>
56
57 </body>
58 </htai>
59 </xsl: t«nplate>
60 </xsl: stylesheet> ^^
Рис. 5.12. Таблица стилей XHTMLTipAnswecxsl трансформирует ответ на вопрос теста
Tip-Test из XML s XHTML-документ
В листинге на рис. 5.12 в строках 9-11 с помощью элемента хsi:output задается
DTD. В строке 15 [задается, что элемент answer является корневым элементом
XML-документа, который будет подвергнут трансформации с помощью таблицы
стилей XHTMLTipAnswer.xsl. XML-доку мент начинается в строке 16. В строке 26
используется элемент correct XML-документа для отображения, является ли
выбранный пользователем ответ правильным. В строках 36 и 47 используются
элементы correctTipName и correctTipDescription для отображения, соответственно,
названия рубрики и ее описания для правильного ответа. В строке 53 используется
элемент servIetName для предоставления ссылки на сервлет TipTestServlet, чтобы
пользователь мог принять следующий вопрос теста Tip-Test. На рис. 5.13
показаны результаты ответа на вопрос теста Tip-Test в Internet Explorer.
Tip Description
Organizations that develop software must often S
produce versions customized to a variety of -
computers and operating systems. These tips offer j,
suggestions to make your applications more portable. |s
Рис. 5.13. Окно с результатом ответа на вопрос теста Tip-Test в Internet Explorer
Разработка приложений для беспроводной связи на базе Java и J2ME 263
5.3.2. Запрос от браузера WAP
Если изначальный get-запрос сервлетуТ1рТе818епг1ей (рис. 5.9) был сделан
имитатором Open wave UP, в строках 202-214 определяется, что обратившийся с
запросом клиент — это WAP-клиент. В строках 205-208 осуществляется вызов метода
createXMLTipTestQuestion для создания XML-документа, который содержит
вопрос теста Tip-Test. Используя параметры для этого метода, нгы задаем, что рисунки
для рубрик советов имеют формат wbmp и расположены в каталоге WAP/unages
контекста сервлета. В строке 211 задается МГМЕ-тип text/vnd.wap.wml для
создания WML-содержимого. В строках 212-:213 вызывается метод applyXSLT для
применения таблицы стилей WAPTipQuestion.xsl {рис. 5.14) к XML-документу.
1 <?xml version="i.0"?>
2
3 <!— WAPTipQuestion.xsl -->
4 <!— таблица стилей для клиента WAP —>
5
6 <xsl:stylesheet
7 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
8 version="1.0">
9
10 <xsl:output method = "xml" omit-xral-declaration = "no"
11 doctype-system = "http://www.wapforum.org/DTD/wml_l.1. xml"
12 doctype-publie = "-//WAPFORUM//DTD WML l.l//EN"/>
13
14 <!— задание корня XML-докумема, -->
15 <!-- указывайте го на эху таблицу стилей —>
16 <xsl:template match = "guestion">
17
18 <wml>
19 <card id = "card!" title = "Tip Test">
20
21 <do type = "accept" label = "OK">
22 <go href = "#card2"/>
23 </do>
24
25 <p>
26 <img sre = "(image}" height = "55" width = "55"
27 alt = "Tip Image"/>
28 </p>
29
30 </card>
31
32 <card id = "card2" title = "Tip Test">
33 <do type = "accept" label = "Submit">
34 <go method = "post" href = "/advjhtpl/tiptest">
35 <postfield name = "userAnswer"
36 value = "$(question)"/>
37 </go>
38 </do>
39
40 <P>
41 The tip shown on the previous screen is called:
42 </p>
43
44 <p>
264
Глава 5
45 <select name = "question"
46 iname =• "iquestion" lvalue = "1">
47
46 <Option. value = "0"Xxsl:value-of
49 select = "choices/choice[1] "/X/option>
50
51 <option value = "l"Xxsl:value-of
52 select = "choices/choice[2] "/x/option>
53
54 «jption value = "2 "Xxel: value-of
55 select = "choices/choice [3] "/X/option>
56
57 «Sption value = "3"Xxsl: value-of
58 select = "choices/choice[4] "/X/option>
59 </seleet>
60 </p>
61 </card>
62 </wal>
63 </xsl: tenrplate>
64 </xsl: stylesheet>
Рис. 5.14. Таблица стилей WAPTipQuestion.xsl трансформирует вопрос теста Tip-Test
в формате XML в WML-документ
В листинге на рис. 5.14 в строках 10-12 с помощью элемента xshoutput
задается DTD. В строке 16 указывается, что элемент question является корневым
элементом XML-докумеета, который будет подвергнут трансформации с помощью
таблицы стилей WAPTipQuestion.xsl. XML-документ начинается в строке 18 элементом
wnxl. В строках 19-30 осуществляется объявление первой карты, или страницы,
которая хранит WML-cодержимое, отображаемое браузером. В строках 26-27
отображается рисунок, ассоциированный с элементом image XML-документа,
Элемент do (строки 21—23) информирует имитатор, что вторую карту следует показать,
когда пользователь нажимает кнопку ОК. В строках 52-61 объявляется вторая
карта, на которой содержатся четыре возможных ответа. При помощи элементов choice
в элементе choices, в строках 45—59 создается список, который содержит варианты
ответов. Элемент do (строки 33—38) указывает имитатору, что сделанный
пользователем выбор следует отправить в сервлет TipTestServlet, когда пользователь
нажимает кнопку Submit.
На рис. 5.] 5 показало якран тее.тн Tip-Test после применения сервлетом
TipTestServlet XSLT-трансформации к XML-документу. На рисунке слева
показано условное изображение для рубрики советов, на рисунке справа показан выбор
ответа пользователем.
Каждый пункт в списке для выбора содержит уникальное значение. Когда
пользователь нажимает кнопку Submit, имитатор UP отправляет (с помощью
метода post) выбранное значение сервлету TipTestServlet. Сервлет TipTestServlet
(рис, 5.9) вызывает метод doPost, когда получает отправленные методом post
данные. В строках 132—144 определяется, что запрос поступил от имитатора Openwave
UP. В строках 135—136 осуществляется вызов метода createXMLTipTestAnswer
для создания XML-документа, который информирует, является ли ответ
пользователя правильным, содержит название рубрики советов и ее описание для
правильного ответа, а также URL сервлета TipTestServlet. В строке 139 устанавливается
MIME-тип text/vnd.wap.wrnl для создания WML-содержимого, а в строках
157-158 вызывается метод applyXSLT для применения к XML-документу
таблицы стилей WAPTipAnswer.xsl (рис. 5.16).
Разработка приложений для беспроводной связи на базе Java и J2ME 265
Рис. 5.15. Экран имитатора Openwave UP с вопросом теста Tip-Test (Изображение
UP.SDK предоставлено компанией Openwave Systems Inc. Openwave, логотип Openwave
и UP.SDK являются торговыми марками корпорации Openwave Systems Inc. Все права
соблюдены.)
В строках 10—12 листинга на рис. 5.16 с помощью элемента xsl:output задается
DTD. В строке 16 задается, что элемент answer является корневым элементом
в XML-документе, который будет подвергнут трансформации с помощью таблицы
стилей WAPTipAnswer.xsl. WML-документ начинается в строке 18 элементом
wml. В строках 20-48 осуществляется объявление карты (экрана с ответом) в
составе этого WML-документа. В строке 29 используется элемент correct WML-доку-
мента для отображения, является ли выбранный пользователем ответ
правильным. В строках 37 и 45 элементы correctTipName и correctTipDescription
используются, соответственно, для отображения названия рубрики советов и ее описания
для правильного ответа. Используя элемент servletName в XML-документе,
элемент do (строки 22-26) предоставляет ссылку на сервлет TipTestServlet, чтобы
пользователь мог получить следующий вопрос теста Tip-Test. На рис. 5.17 показан
экран с ответом на вопрос теста Tip-Test в имитаторе Openwave UP.
1 <?xml version="l.0"?>
2
3 <!— WAPTipAnswer.xsl -->
4 <!— таблица стилей для клиента WAP -->
5
6 <xsl:stylesheet
7 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="l.0">
8
9
10
11
12
13
<xsl:output method = "xml" omit-xml-declaration = "no"
doctype-system = "http://www.wapforum.org/DTD/wml_l.1.xml"
doctype-public = "-//WApFORUMZ/dtd wml 1.1//en"/>
266
Глава 5
14 <<— задание корня XML-документа, —>
15 <!-- укавыааюцщ'о на эту таблицу стилей -->
16 <xsl:template natch = "answer">
17
18 <wml>
19
20 <card id = "cardl" title = "Tip Test Answer">
21
22 <do type = "accept" label = MOK">
23 <go method = "get"
24 href = "/advjhtpl/tiptest">
25 </go>
26 </do>
27
28 <p>
29 <xsl:value-of select = "correct"/>
30 </p>
31
32 <p>
33 Tip Name
34 </p>
35
36 <p>
37 <xsl:value-of select = "eorreetTipName"/>
38 </p>
39
40 <p>
41 Tip Description
42 </p>
43
44 <p>
45 <xel:value-of select ■ "correctTipOescription"/>
46 </p>
47
48 </card>
49
50 </wml>
51 </xsl:template>
52 </xsl:3tylesheet>
Рис. 5.16. Таблица стилей WAPTtpAnswer.xsl трансформирует ответ в WML-документ
5.3.3. Запрос от браузера i-mode Pixo
Если изначальный get-залрос поступил сервлету TipTestServlet (рис. 5.9) от
браузера Pixo, в строках 217-229 определяется, что обратившийся с запросом
клиент — это клиент i-mode. В строках 220-223 осуществляется вызов метода
createXMLTipTestQuestion для создания XML-документа, который содержит
вопрос теста Tip-Test. Используя параметры для этого метода, мы задаем, что
рисунки для обозначения рубрик советов имеют формат gif и расположены в каталоге
iMode/images контекста сервлета. В строке 226 устанавливается МШЕ-тип
text/html для создания cHTML-содержимого. В строках 227-228 осуществляется
вызов метода applyXSLT для применения к XML-документу таблицы стилей
IMODETipQuestion.xsl (рис. 5.18).
Разработка приложений для беспроводной связи на базе Java и J2ME 267
Рис. S.17. Экран имитатора Openwave UP с ответом на вопрос теста Tip-Test.
(Изображение UP.SDK предоставлено компанией Openwave Systems Inc. Openwave,
логотипы Openwave и UP.SDK являются торговыми марками корпорации Openwave
Systems Inc. Все права соблюдены.)
1
2
3
4
5
б
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?xml version="1.0"?>
<!— IMODETipQuestion.xsl —>
<!-- таблица стилей для клиента i-mode -->
<xsl:stylesheet version = "1.0"
xmlns:xsl = "http://www.w3.org/1999/XSL/Transform">
<xsl:output method = "html" omit-xml-declaration = "yes"
doctype-public = "-//W3C//DTD Compact HTML 1.0 Draft//EN"/>
<!— задание корня XML-документа, —>
<!— указывающего на эту таблицу стилей -->
<xsl:template match = "question">
<html>
<head>
<title>Tip Test</title>
</tiead>
<body>
<P>
<!— вывод изображения —>
<img name = "image" height = "40"
width = "40" alt - "Tip Image"
arc = "{1даде}">
268
Глава 5
29 </img>
30 </р>
31
32 <р>
33 What is the name of the icon shoim?
34 </p>
35
36 <p>
37
38 <!— создание формы с четырьмя переключателями -->
39 <form method = "post"
40 action = "/advjhtpl/tiptest">
41
42 <!— построение таблицы с вариантами выбора -->
43 <table>
44 <tr>
45 <td>
46 <input type = "radio"
47 name = "uaerAnswer" value = "0">
48 </input>
49 <xsl:value-of select =
50 "choices/choice[1]"/>
51 </td>
52
53 <td>
54 <input type = "radio"
55 name = "userAnswer" value = "1">
56 </input>
57 <xsl:value-of select =
58 "choioea/ehoic*[2]"/>
59 </td>
60 </tr>
61
62 <tr>
63 <td>
64 <input type = "radio"
65 name = "userAnswer" value = "2">
66 </input>
67 <xsl:value-of select =
68 -choices/choice[3]"/>
69 </td>
70
71 <td>
72 <input type = "radio"
73 name = "userAnswer" value = "3">
74 </input>
75 <xsl:value-of select =
76 "choices/choice[4]"/>
77 </td>
78 </tr>
79 </table>
80
81 <input type = "submit" value = "Submit"/>
82 </form>
83 </p>
84
Разработка приложений для беспроводной связи на базе Java и J2ME 269
85 </body>
86 </html>
87 </xs1:template>
88 </xsl.-gtyleshaat>
Рис. 5.
в формат
.18. Таблица стилей IMODETipQuestion.Xsl трансформирует вопрос теста Tip-Test
иате XML в с HTML-доку мент
В строках 9-10 листинга на рис. 5.18 с помощью элемента xshoutput задается
DTD. В строке 14 задается, что элемент question является корневым элементом
XML-доку мента, который будет подвергнут трансформации с помощью таблицы
стилей IMODETipQiiestion.xsl. с HTML-документ начинается в строке 15
элементом html. Элемент body, описываемый в строках 21-85, содержит графическое
изображение для рубрики советов и четыре возможных варианта ответа. В строках
26-29 осуществляется вывод изображения, ассоциированного с элементом image
XML-документа. В строках 39-82 создается форма с четырьмя вариантами ответа.
В строках 46-76 создаются четыре переключателя, ассоциированные с этими
ответами. В строке 81 создается кнопка Snbmit, чтобы пользователь мог отправить
сделанный им выбор.
На рис. 5.19 показан тест Tip-Test в действии после применения сервлетом
TipTestServlet XSLT-трансформации к XML-документу. На рисунке слева
показан экран при выборе ответа пользователем, а на рисунке справа показан экран
перед отправкой ответа пользователем.
и>1.>1*теш№г«1Ж»аВ1Щ?
Рис. 5.19. Экран браузера i-rnode Pixo с вопросом теста Tip-Test. (Публикуется с разрешения
компании Pixo, Inc.)
Каждый переключатель в cHTML-документе содержит уникальное значение,
чтобы при нажатии пользователем кнопки Submit браузер Pixo отправлял (с
помощью метода post) выбранное значение сервлету TipTestServlet. Сервлет
TipTestServlet вызывает метод doPost, когда получает отправленные методом post
данные. В строках 147-159 определяется, что запрос был послан браузером Pixo.
В строках 150-151 осуществляется вызов метода createXMLTipTestAnswer для
создания XML-документа, хранящего информацию, является ли ответ пользовате-
270
Глава 5
ля правильным, название рубрики советов и ее описание, а также URL сервлета
TipTestServlet. В строке 154 задается МГМЕ-тип text/html для создания cHTML
содержимого, а в строках 157-158 осуществляется вызов метода appIyXSLT дяя
применения к XML-документу таблицы стилей IMODETipAnswer.xsl {рис. 5.20).
В строках 9-10 листинга на рис. 5.20 с помощью элемента xsl:output создается
DTD. В строке 14 задается, что элемент answer является корневым элементом
XML-документа, который будет подвергнут трансформации с использованием
таблицы стилей IMODETipAnswer.xsl. cHTML-документ начинается в строке 15
элементом html. В строке 25 используется элемент correct XML-документа для
отображения, является ли сделанный пользователем выбор правильным. В строках
35 и 45-46 элементы correctTipName и correctTipDescription используются,
соответственно, для отображения названия рубрики советов и ее описания для
правильного ответа. В строке 52 с помощью элемента servletName предоставляется
ссылка на еервлет TipTestServlet, чтобы пользователь мог принять следующий
вопрос теста Tip-Test. На рис. 5.21 показан экран с ответом на вопрос теста Tip-Test
в браузере i-mode Pixo.
1 <?хм1 version="1.0"?>
2
3<!-~ IMODETipAnswer.xsl —>
4 <!— таблица стилей для клиента i-mode —>
5
6 <xsl:stylesheet version = "1.0"
7 xntlns:xsl = "http://www.w3.org/1999/XSL/Transform">
8
9 <xsl:output method =* "html" omit-xml-declaration = "yes"
10 doetype-public = "-//W3C//DTD Compact HTML 1.0 Draft//EN"/>
11
12 <!— задание корня XML-документа, —>
13 <!— который указывает на эту таблицу стилей —>
14 <xsl:template match = "answer">
15 <html>
16
17 <head>
18 <title>Tip Test Answer</title>
19 </head>
20
21 <body>
22
23 <p>
24 <hl>
25 <xsl;value-of select = "correct"/>
26 </hl>
27 </p>
28
29 <p>
30 <h2>Tip Name</b2>
31 </p>
32
33 <p>
34 <h3>
35 <xsl:value-of select = "correctTipName"/>
36 </h3>
37 </p>
38
39 <p>
Разработка приложений для беспроводной связи на базе Java и J2ME 271
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56 </body>
57 </html>
58 </xsl:template>
59 </xsl:stylesheet>
<h2>Tip Description</h2>
</p>
<P>
<h3>
<xsl:value-of
select = "correetTipDescciption"/>
</h3>
</p>
<P>
<h2>
<a href="{servletName}">Next Tip</a>
</h2>
</p>
Рис. 5.20. Таблица стилей IMODETipAnswer.xsl трансформирует ответ на вопрос теста
Tip-Test в формате XML г cHTML-документ
"pixo
AltMMtMfcmtmmM-2.1
Рис. 5.21. Экран браузера i-mode Pixo с ответом на вопрос теста Tip-Test. {Публикуется
с разрешения компании Pixo, Inc.)
5.3.4. Запрос от клиента J2ME
Сервлет TipTestServlet ведет себя по-другому, если get-запрос был отправлен
клиентом J2ME. Сервлет TipTestServlet не использует XML для клиента J2ME,
поскольку на сегодняшний день клиент J2ME не способен интерпретировать
данные XML без использования специализированного программного обеспечения.
Как заявлено корпорацией Sun Microsystems, «пока еще не ясно, как в будущем
будут развиваться взаимоотношения Java — XML, но уже можно сказать, что
в WAP 2.0 в качестве языка разметки будет использоваться XHTML... В не столь
272
Глава 5
далеком будущем появятся тысячи, а затем и миллионы бытовых устройств,
использующих XHTML.»1
Поскольку для клиентов J2ME XML не используется, сервлет TipTestServlet не
отправляет размеченные данные нашему клиенту J2ME. Можно было бы
использовать XSLT для генерирования обычного текста, но мы не стали этого делать по
двум причинам. Во-первых, обычный текст не является правильно построенным
(well-formed) и ее отвечает основному предназначению XSLT-трансформации:
создавать правильно построенные документы. Во-вторых, результирующее
множество ResultSet в сервлете TipTestServlet уже содержит обычный текст, поэтому
преобразование текста в XML-документ с последующим обратным его
преобразованием представляется излишним.
В строках 232-235 сервлета TipTestServlet (рис. 5.9) определяется, что запрос
был отправлен клиентом J2ME, и вызывается метод seadJ2MEClientResponse
(строки 344.-390). В строке 349 осуществляется вызов метода get ResultTable для
преобразования результирующего множества ResultSet в двухмерный массив
строк resultTable, чтобы иметь быстрый доступ к содержимому базы данных.
В строке 352 создается объект Random, который дает возможность сервлету
TipTestServlet случайным образом выбирать рисунок для рубрики советов и
вопросы теста для отображения. В строке 355 объект Random передается методу
getRandomlndices, который возвращает массив из четырех целых чисел. Каждое
из этих чисел представляет собой уникальный, случайным образом выбранный
индекс, который соответствует рубрике советов в массиве resultTable.
В строках 358-359 иа массива randomRow произвольным образом выбирается
целое число. Это целое число представляет собой «правильный» ответ на вопрос
теста. Фактически, это число соответствует строке в таблице resultTable)
хранящей информацию о правильном ответе. С учетом правильного ответа в строке 361
определяется строка в таблице resultTable, которая содержит название рубрики
советов для правильного ответа и ее описание. Сервлет TipTestServlet хранит эту
информацию б объекте HttpSession (строка 364), чтобы в случае, если клиент
J2ME отправит сделанный пользователем выбор обратно на сервер, сервлет
TipTestServlet мог проверить ответ пользователя, сравнивая его с правильным
ответом, хранящемся в объекте HttpSession. В строках 367-376 правильный ответ,
название рубрики советов и ее описание сохраняются в объекте HttpSession. С
учетом правильного ответа в строках 379-380 определяется изображение, которое
следует отправить клиенту J2ME. В строке 382 задается MIME-тип text/plain,
поскольку клиенты J2ME интерпретируют обычный текст. В строке 383 создается
объект Print Writer, с помощью которого сервлет TipTestServlet отправляет
данные клиенту J2ME. В строке 384 отправляется имя рисунка для рубрики советов, а
в строках 387-388 клиенту отправляется четыре сокращенных названия для
вариантов ответов. В разделе 5.4.3 подробно разъясняется, как клиент J2ME
отображает информацию теста Tip-Test и обменивается данными с сервлетом TipTestServlet
и со своими собственными модулями. На рис. 5.22 показан образец выходных
данных после того, как сервлет TipTestServlet отправил вопрос теста Tip-Test
клиенту J2ME. На рисунке слева показано изображение для рубрики советов, а на
рисунке справа — выбор огвега пользователем.
Когда клиент отправляет сделанный пользователем выбор, сервлет
TipTestServlet вызывает метод doPost. В строках 162-164 определяется, что запрос
поступил от клиента J2ME, и осуществляется вызов метода sendJ2MEAnswer (строки
592-629). В строках 596-597 используется объект BufferedReader для чтения
сделанного пользователем выбора. В строке 600 задается MIME-тип text/plain, a
Day, В., «Developing Wireless Applications using Java™ 2 Platform, Micro Edition
(J2 ME™) *, developer. Java. sun. со m/de ve 1 ope r/p rod uc ts / wi rel es s / gets tart /artic les/wireles-
sdev/wirelessdev.pdf, June 2001.
Разработка приложений для беспроводной связи на базе Java и J2ME 273
Рис. 5.22. Экран клиента J2ME с вопросом теста Tip-Test (Публикуется с разрешения
корпорации Sun Microsystems, Inc.)
в строке 601 получается объект Print Writer, с помощью которого сервлет TipTest-
Servlet осуществляет отправку содержимого. В строке 604 извлекается объект
HttpSession, который хранит правильный ответ, название рубрики советов к
описание. В строках 607-617 эта информация извлекается из объекта HttpSession.
В строках 620-623 выбор пользователя соиоставляется с правильным ответом
и определяется, является ли предоставленный пользователем ответ верным.
В строках 626-627 посредством объекта PrintWriter клиенту отправляется
правильное название рубрики советов и ее описание. На рис. 5.23 показан экран с
ответом на вопрос теста в эмуляторе MIDP-устройства Sun. На рисунке слева показан
экран, который содержит правильное название рубрики советов и ее описание.
Рис. 5.23. Экран клиента J2ME с ответом на вопрос теста Tip-Test. (Публикуется
с разрешения корпорации Sun Microsystems, Inc.)
274
Глава 5
Если пользователь осуществит прокрутку содержимого этого экрана вниз, он
увидит картинку, показанную справа, на которой представлена оставшаяся часть
описания рубрики советов.
5.4. Java 2 Micro Edition
Этот раздел содержит ознакомительную информацию по Java 2 Micro Edition
(J2ME™): платформе, которая дает возможность разработчикам создавать
приложения для различных бытовых устройств, таких как телевизионные игровые
приставки, Web-терминалы, встроенные системы, мобильные телефоны и пейджеры.
Мы рассмотрим технологии Connected Limited Device Configuration (CLDC) и
Mobile Information Device Profile (MIDP), которые предоставляют разработчикам
интерфейсы прикладного программирования для написания ^МЕ-приложений —
мидлетпов (MIDlet) — и развертывания их на мобильных устройствах различных
типов. Затем мы поговорим о жизненном цикле мидлета и поясним, каким
образом J2ME используется в нашем практическом примере.
5.4.1. Connected Limited Device Configuration (CLDC)
Connected Limited Device Configuration (CLDC) представляет собой набор
интерфейсов прикладного программирования, который дает возможность
разработчикам создавать приложения для устройств, имеющих ограниченные ресурсы (т.е.
ограниченный размер экрана, памяти, мощность и скорости передачи). В состав
CLDC J2ME входит виртуальная машина — интерпретатор, который выполняет
приложения — и набор классов, которые разработчики могут использовать для
создания и выполнения программ на устройствах с ограниченными ресурсами.
Виртуальная машина KVM, используемая в CLDC, выполняет приложения J2ME
(подобно тому, как виртуальная машина JVM выполняет приложения J2SE).
Буква "К" в аббревиатуре KVM обозначает "kilo", поскольку приложения J2ME
занимают достаточно небольшой объем памяти, измеряемый в килобайтах.
CLDC J2ME содержит пакеты java.io, java.lang, java.utii, которые разработчики
используют для выполнения таких стандартных операций, как создание
примитивных типов данных, использование простых структур данных, а также отправка
и получение данных из сети. Эти пакеты являются подмножеством пакетов J2SE
java.io, java.Ung и java.utii, поэтому пакеты CLDC J2ME не содержат всех классов,
имеющихся в пакетах J2SE. В таблице на рис. 5.24 описаны пакеты java.io,
javaJang и java.utii J2ME. Для экономии места в эту таблицу не были включены
исключения и ошибки, присущие каждому из классов. Полный список классов
CLDC J2ME можно найти по адресу java.sun.com/j2me/docs/pdf/cJdcapi.pdf.
Типичная ошибка программирования 5.1
Попытка использовать пакеты J2SE в KVM приведет к ошибке
компиляции, поскольку виртуальная машина KVM не может обслуживать
большое количество классов по причине ограниченности ресурсов KVM.
Одна из проблем программирования в J2ME состоит в том, что интерфейс
прикладного программирования не содержит ряда типов данных и классов, которые
разработчики, как правило, «имеют под рукой» в других платформах Java.
Например, в J2ME нет операций с плавающей запятой, операций с сериализуемыми
объектами, с группами программных потоков, а также отсутствует интерфейс JNI
(Java Native Interface). По мере развития технологий для беспроводных устройств,
вполне вероятно, что в последующих версиях J2ME эти возможности будут
поддерживаться.
Разработка приложений для беспроводной связи на базе Java и J2ME 275
Классы
Интерфейсы
Классы
java.io
Datalnput
DataOutput
ByteArrayInputS tre am
ByteArrayOutputStream
DatalnputStream
DataOutputStream
InputStream
InputstreamReader
Outputs tream
OutputStreamReader
PrintStream
Reader
Writer
java.lang
Runnable
Boolean
Byte
Character
Class
Integer
Long
Math
Object
Runtime
Shott
String
String Buffer
System
Thread
Throwable
java.util 1
Enumeration
Calendar
Data
Hashtable
Random
Stack
Timer
Timer Task
Timer Zone
Vector
Рис. 5.24. Пакеты java.io, java.lang и java.util J2ME
5.4.2. Mobile Information Device Profile (MIDP)
Mobile Information Device Profile (MIDP) — это набор интерфейсов
прикладного программирования, который дает возможность разработчикам реализовывать
специфичные для мобильных устройств действия, такие как создание
пользовательских интерфейсов, локальное хранение информации и определение
жизненных циклов клиентских МГОР-приложений (мидлетов). Устройства, которые
выполняют приложения с помощью MIDP, называются MI DP-устройствами. К
таким устройствам откосятся сотовые телефоны и пейджеры.
В состав MIDP входят пакеты javax.microedition.lcdui, javax.microedition.io,
javax.microedition.rms и javax.microedition.midlet. Пакет javax.microedition.lcdui
содержит классы, которые дают возможность разработчикам создавать
пользовательские интерфейсы для мидлетов, пакет javax.microedition.io дает возможность
осуществлять сетевые взаимодействия между мидлетами и другими системами,
пакет javax.microedition.rms содержит классы, которые дает возможность
локально хранить информацию, а пакет javax.microedition.midlet определяет
жизненный цикл мидлетов. В таблице на рис. 5.25 представлены пакеты MIDP
javax.microedition.lcdui и javax.microeditioii.io. В таблице на рис. 5.26
представлены пакеты MIDP javax.microedition.rms и javax.microedition.midlet. Для
экономии места в эти таблицы не были включены исключения и ошибки, присущие
этим классам. Полный список классов MIDP J2ME можно найти по адресу
Java.eun.oom/prodijcts/midp/midp-virelessapps-wp.pdf
Чтобы выполнить приложение MIDP, MIDP-устройство должно иметь
монохромный дисплей с разрешением не менее 96x54 пикселов, иметь
двунаправленное подключение к беспроводной сети, некое устройство ввода (например, ми-
нй-клавиатура или чувствительный экран), память объемом не менее 128
килобайт для классов CLDC/MIDP и не менее 32 килобайт для KVM. Мидлет будет
работать на любом устройстве, которое отвечает этим требованиям.
276
Глава 5
Классы
Интерфейсы
Классы
javax.microedition.kdui
Choice
CommandLis tener
ItemLi stener
Alert
MettType
Canvas
ChoiceGroup
Command
DataField
Display
Diaplayable
Font
Form
Gauge
Graphics
Image
Imageltem
Item
List
Screen
Screen
Item
TextBox
TextField
Ticker
javax.microedition.io j
Connection
ContentConnection |
Datagram j
DatagramConnection
HttpConnection
InputConnection
OutputConnecfcion
StreamConnection
S treSmConneсtionNoti£ier
Connector
Рис. 5.25. Пакеты MID3 Javax.mfcroedition.fcdui и javax.microedition.io
Классы
Интерфейсы
Классы
javax.microedrtion.rms
RecordComparator
RecordEnumeration
RecordFilter
RecordListener
Recordstore
javax.rnicroedition.midlet |
MIDlet |
Рис. 5.26. Пакеты MIDP javax.microedition.rms и javax.microedition.midlet
5.4.3. Обзор мидлета TipTestMIDIet
В разделе 5.3.4 обсуждалось, как сервлет отправляет данные клиенту J2ME,
который представлен мидлетом. Как мы уже говорили, мидлеты не могут
интерпретировать XML-документы без использования специализированных программных
продуктов и должны получать всю информацию через потоки или объекты
PrintReader. E этом разделе мы более подробно остановимся на мидлетах,
рассмотрим жизненный цикл мидлета и познакомимся с мидлетом TipTestMIDlet,
который используется в нашем практическом примере.
Разработка приложений для беспроводкой связи на базе Java и J2ME 277
Мидлет — это приложение для мобильного информационного устройства
Mobile Information Device, которое выполняется на МГОР-устройстве. Название
«мидлет» было принято по аналогии с названиями ♦апплет» и «сервлет», поскольку эти
приложения обладают схожими характеристиками: например, всем им присущ
определенный жизненный цикл, и они занимают различные состояния в процессе
выполнения программы. Кроме того, разработчик не вызывает явным образом
конструктор для объектов этих классов (Applet, HttpServlet и MIDlet), чтобы
реализовать экземпляры этих объектов. В разделе 2.2.1 говорилось, что сервлет
загружает в память контейнер сервлетов (обычно в ответ на первый запрос,
принимаемый сервлетом). Мидлеты загружаются схожим образом. Разработчики MIDP
хранят группу мидлетов в jar-файле, который называется комплектом мидлетов, на
сервере. МШР-устройство содержит программу управления приложением
(application management software — AMS), которая загружает комплект мидлетов с
сервера, открывает его, а затем запускает указываемый пользователем мидлет на
MIDP- устройстве.
AMS использует файл дескриптора приложения для загрузки приложения
с мидлетами. Этот файл имеет расширение .jad и содержит такую информацию, как
список мидлетов в комплекте мидлетов, размер комплекта мидлетов и его URL, имя
каждого из мидлетов, имя поставщика и номер версии, а также профиль и
конфигурацию МГОР-устройства. AMS использует эту информацию, чтобы обеспечить
выполнение приложения на данном MIDP-устройстве. Как интегрированная среда
разработки Wireless Toolkit J2ME, так и Forte, создают этот файл при создании нового
комплекта мидлетов (см. раздел 5.5). Приведенный ниже код демонстрирует
структуру файла дескриптора приложения для мидлета TipTestMIDlet.
MIDlet-1: TipTestMIDlet, TipTestMIDlet.png,
com,deltel.advjhtpl.wireless.TipTestMIDlet
MIDlet-Jar-Size: 9577
MIDlet-Jar-URL: TipTestMIDlet.jar
MIDlet-Nama: TipTestMIDlet
MIDlet-Vendor: Sun Microsystems
MIDlet-Version: 1.0
MicroEdition-Configuration: CLDC-1.0
MieroEditiou-Profile: MIDP-1.0
Мидлет TipTestMIDlet представлен на рис. 5.27. Прежде чем начать
рассматривать, как мидлет TipTestMIDlet извлекает информацию с сервера, нужно уяснить
жизненный цикл мидлета. Каждый мидлет должен расширять класс MIDlet
(строка 13) из пакета javax.microedition.midJet (строка 9). Жизненный цикл
начинается, когда AMS вызывает конструктор мидлета (строки 43-75) для запуска
мидлета. После этого мидлет переходит в состояние паузы и не может принимать ввод
пользователя или отображать содержимое экрана, созданное разработчиком. По
завершении работы конструктора AMS вызывает метод startApp (строки 78-82),
который переводит мидлет в активное состояние, разрешая ему отображать
содержимое и принимать ввод пользователя. После этого мидлет ожидает ввода от
пользователя или иного уведомления от AMS. Если AMS вызывает метод pauseApp
(строка 85), мидлет возвращается в состояние паузы. Если мидлет находится в
состоянии паузы, AMS должен вызвать метод startApp, чтобы дать возможность
мидлету вновь перейти в активное состояние. Если AMS вызывает метод destroy-
Арр (строка 88) для очистки памяти устройства перед загрузкой другого
приложения, выполнение мидлета заканчивается. Методы startApp, pauseApp и dest-
royApp являются абстрактными методами класса MIDlet, поэтому каждый
подкласс класса MIDlet должен переопределять эти методы.
В строке 6 импортируется пакет ввода/вывода CLDC J2ME, который дает
возможность мидлету TipTestMIDlet отправлять и принимать данные от сервлетов.
278
Глава 5
В строках 9-11 импортируются пакеты MIDP, которые определяют жизненный
цикл мидлета, создают пользовательские интерфейсы и осуществляют сетевые
взаимодействия. В J2ME пользовательские интерфейсы делятся на API нижнего
уровня и API верхнего уровня. Низкоуровневые API дают разработчикам
возможность встраивать в программы графические рисунке и фигуры, точно задавая ах
координаты в пикселах, а также предоставляют средства анимации для
приложений, таких как игры. Высокоуровневые API пользовательского интерфейса
позволяют разработчикам встраивать в программы (например, в приложения для
электронной коммерции) и базовые пользовательские интерфейсы текстовые поля,
списки, формы и изображения.
1 // TipTestMlDlet.java
2 // Этот нидлет получает вопросы теста Tip-Test от сервлета
3 package com,deitel.advjhtpl.wireless,■
4
5 // Подмножество J2ME пакетов Java
6 import java.io.*;
7
8 // Пакеты J2ME
9 import javax. microedition.midlet.*;
10 import javax.microedition.ledui.*;
11 import javax.microedition,io.*;
12
13 public class TipTestMlDlet extends MIDlet {
14
15 private Display display; // менеджер дисплея
16
17 // экраны, отображаемые пользователям
IB private List mainScreen;
19 private List welcomeScreen;
20 private Form infoScreen;
21 private Form tipScreen;
22 private Form answerScreen;
23
24 // действия для программных кнопок
25 private Command selectCommand;
2 б private Command nextCommand;
27 private Command backCommand;
2B
29 private static final String servletBaseURL =
30 "http://localhost:80fi0/advjbtpl/";
31
32 private static final String welcomeServletName = "welcome";
33
34 // определение сервлета приветствия и сервлета, выполнякжцего тест
35 private String tipTestServletName;
Зв
37 private static final String velcomeServletURL =
38 servletBaseURL + welcomeServletName;
39
40 private String sessionID;
41
42 // инициализация дисплея и основного экрана
43 public TipTestMlDlet()
44 {
45 // создание команд для программных кнопок
Разработка приложений для беспроводной связи на базе Java и J2ME 279
Л6 eelectCommand = new Command( "Select", Command.OK, D );
47 nextCommand = new Command( "Next Tip", Command.OK, 0 );
48 backCommand = new Command( "Back", Command.BACK, 1 );
49
50 // создание основного экрана, дающего возможность соединиться
с сервлетом приветствия
51 mainScreen = new List( "TipTestHIDlet", List.IMPLICIT };
52 mainScreen.addCommand( seleetCoramand );
53
54 // разрешение доступа к экрану mainScreen через программную
кнопку
55 mainScreen.setCoantandListener(
56 new CommandListener() {
57
58 // вызывается, когда пользователь нажимает программную
кнопку
59 public void commandftetion(
60 Command command, Displayable displayable )
61 {
62 // получение данных от сервлета приветствия
63 String data - getServerData{ welcomeServlatURL );
64
65 // создание экрана приветствия на основе данных иэ
сервлета
66 display.satCurrent( createWelcomeScreen( data ) );
& )
68
69 } // конец анонимного внутреннего класса
70 );
71
12 // получение соответствующего объекта Display для устройства
73 display = Display.getDisplay( this );
74
75 } // конец конструктора TipTeatMIDlet
76
77 // начало нидлета MIDlet
78 public void startAppO
79 {
80 // настройка изображения основного экрана
81 display.setCurrent( mainScreen ) ;
82 }
83
84 // пауза в выполнении кидлета MIDlet
85 public void pauseApp() {}
86
87 // закрытие мидлета MIDlet
88 public void destroyApp( boolean unconditional ) {}
89
90 // создание экрана приветствия для теста
91 private Screen createWelcomeScreen{ String data )
92 {
93 String listf] = pareeData( data, ';' >;
94
95 // создание экрана, приветствующего пользователя
96 walcomeScreen = new List( list[ 0 }, List.IMPLICIT };
97
280
Глава
98 welcomeScreen.append( "Take TipTest", null );
99 welcomeScreen.addCommand ( selectConnnand );
100 welcomeScreen.addCommand{ backCommand );
101
102 // получение URL информационной страницы
103 final String url = new String< list[ 1 ].toCharArray{) );
104
105 // разрешение доступа к экрану welcomeScreen черев
программную кнопку
106 welcomeScreen.setCommandListener{
107 new ComraandListenerO {
108
109 // вызывается, когда пользователь нажимает программную
кнопку
110 public void commandAction(
111 Command command, Displayable displayable )
112 t
113 // если нажатой является программная кнопка SELECT
114 if ( command.getCommandType() == Command.OK ) {
115
116 // получение дата» со статической страницы
117 String data =
118 getServerData ( servletBaseURL + url ) ;
119
120 // отображение этих данных
121 display.setCurrent(
122 createInformationscreen( data ) );
123 }
124
1Z5 // если нажатой является программная кнопка BACK
126 else if ( command.getCommandType() —
127 Command.BACK ) {
128 display.setCurrent( mainScreen );
129 }
130
131 ) // конец метода commandAction
132
133 } // конец анонимного внутреннего класса
134 ) ;
135
136 return welcomeScreen;
137
138 } // конец метода createWelcomeScreen
139
140 // создание экрана, показывающего, с какими сервлетани можно
установить соединение
141 private Screen createInformationScreen( String data )
142 {
143 String list[] =parseData{ data, ';' );
144
145 // создание формы, отображающей доступные сервлеты
146 infoScreen = new Form( "Information" );
147
148 // создание элемента Stringltem, предоставляющего указания
149 Stringltem infoTitle = new Stringltem( list[ 0 ], null );
150 infoScreen.append( infoTitle );
Разработка приложений для беспроводной связи на базе Java и J2ME 281
151
152 iI создание группы ChoiceGloup, позволяющей пользователе
выбрать сервлет
153 final ChoiceGroup choices = new ChoiceGroup( "",
154 ChoiceGroup.EXCLUSIVE );
155 choices.append( list[ 1 ], null );
156
157 // добавление группы ChoiceGroup в форку Form
158 infoScreen.append( choices );
159
160 infoScreen.addCommand{ selectCommand );
161 infoScreen. addCommand ( bacJcCofflmand ) ;
162
163 // предоставление возможности доступа к атоку экрану через
программную кнопку
164 infoScreen.setCommandListener(
165 new CommatidListener () {
166
167 // впаивается, хорда пользователь нажимает программную
кнопку
168 public void commandAction(
169 Command command, Displayable displayable )
170 (
171 // если нажатой является программная кнопка SELECT
172 if ( command.getCommandType() == Command.OK ) {
173
174 // выбор пользователя, с каким сервлетом
установить соединение
175 int selectedlndex = choices .getselectedlndex () ;
176
177 tipTestServletName =
178 choices.getString( selectedlndex );
179
180 // соединение с сервлетом и получение данных
181 String data = getServerData( servletBaaetTRL +
182 tipTestServletName );
183
184 // отображение следующего экрана в соответствии
с данными
185 display.setCurrent( createTipTestScreen(
156 servletBaseURL + data } );
187 }
188
189 // если нажатой является программная кнопка BACK
190 ■ else if ( command.getCommandType() == Command.BACK )
191 display.setCurrent{ welcomeSereen };
192
193 } // конец метода commandAction
194
195 } // конец, анонимного внутреннего класса
196 ) ;
197
198 return infoScreen;
199
200 } // конец метода createlnformationScreen
201
282
Глава 5
202 // создание экрана для отображения вопроса теста
203 private Screen createTipTestScreen{ String data )
204 {
205 // синтаксический анализ данных, полученных с сервера
206 String list[) = parseData{ data, '\n' )■
207
208 // создание новой формы для отображения вопроса теста
209 tipScreen = new Form( "Tip Test" );
210
211 // создание изображения на основе данных, полученных с сервера
212 Image serverlmage = getServerImage( list[ 0 ] );
213
214 // добавление изображения в форму
215 if ( serverlmage != null )
216 tipScreen.append( serverlmage );
217
218 String choiceList[] = new String[ 4 J;
219
220 // формирование из полученных данных списка для выбора
ChoiceGroup
221 for ( int i = 0; i < choicelist.length; i++ )
222 choiceListf i } = listf 1 + 1 );
223
224 // создание группы ChoiceGroup, позволяжщей пользователю
сообщить о своем выборе
225 final ChoiceGroup choices ■= new ChoiceGroup( "Tip Test",
226 ChoiceGroup.EXCLUSIVE, choicebist, null );
227
228 // добавление группы ChoiceGroup в форму
22 9 tipScreen.append( choices );
230
231 tipScreen.addCommand( selectCommand );
232
233 // разрешение доступа к этому экрану через программную кнопку
234 tipScreen.setCommandXistener(
235 new CoramandListener() {
236
237 // вызывается, когда пользователь нажимает программную
кнопку
238 public void coromandAction(
239 Command command, Displayable displayable )
240 {
241 // отправка выбора пользователя сервлету
242 int selection = choices.getSelectedlndex();
243
244 String result = postDataf selection );
245
246 // отображение результатов
247 display.setCurrent(
248 createAnswerScreen( result ) );
249 }
250
251 } // конец анонимного внутреннего класса
252 ) ;
253
254 return tipScreen;
Разработка приложений для беспроводной связи на базе Java и J2ME 283
255
256 } // конец метода createTipTestScreen
257
25S // создание экрана для отображения ответа на вопрос теста
и результатов теста
259 private Screen createAnswerScreen{ String data )
260 {
261 // синтаксический анализ данных, полученных с сервера
262 String list[] = parseData( data, '\n' )■
263
264 // создание новой формы для отображения ответов
265 answerScreen = new Form( list[ 0 ] );
266
267 // создание элемента Stringltem, отображажщего название рубрики
268 Stringltem tipNameltem =
269 new Stringltem( "Tip Name:\n", list[ 1 J );
270
271 // создание элемента Stringltem, отображающего описание рубрики
272 Stringltem tipDescriptionltem =
273 new Stringltem( "\nTip Description:\n", list[ 2 ] );
274
275 // добавление элементов Stringltem в форму
276 answerScreen.append( tipNameltem );
277 answerScreen.append( tipDescriptionltem );
278
279 answerScreen.addCommand( nextCpmmand );
280
281 // разрешение доступа к этому экрану через программную кнопку
282 answerScreen.setCommandListener(
283 new CommandListener(> {
284
285 // впаивается, когда пользователь нажимает программную
кнопку
286 public void commanditetion (
287 Command command, Displayable displayable )
288 {
289 // получение следующего вопроса теста
290 String data = getServerData( servletBaseURL +
291 tipTestServletKame );
292
293 // отображение следующего вопроса теста
294 display.setCurrentt createTipTestScreenI
295 servletBaseURL + data ) );
296 \
297
298 } // конец анонимного внутреннего класса
299 ) ;
300
301 return answerScreen;
302
303 } // конец метода createAnswerScreen
304
305 // отправка сервлету сделанного пользователем выбора
306 private String postData( int selection )
307 (
308 // соединение с сервлетон и отправка данных
284
Глава 5
309 try {
310
311 // соединение с сервером, пославшим заголовок User-Agent
312 HttpConnection httpConnection =
313 ( HttpConnection ) Connector.open(
314 servletBaseORL + tipTestServletName,
315 Connector.READ_HRITE );
316
31 "J setUserAgentHeader( httpConnection ) ;
316
319 // отправка идентификатора сеанса seesionID,
если таковой имеется
320 if ( seesionID != null )
321 httpConnection.setRequestProperty(
322 "cookie", sessionID );
323
324 // информирование сервлета о тиле запроса (post)
325 httpConnection.setRequestMethod( HttpConnection.POST );
326
327 // открытие потока вывода для сервлета
32S DataOutputStream out =
329 httpConnection.openDataOutputStream();
330
331 // отправка сделанного пользователем выбора
332 out.writeUTP( Integer.toString( selection ) );
333 out. flush () ,-
334
335 // получение результатов от сервлета
336 String data = getOata( httpConnection );
337
338 httpConnection.close<); // закрытие ооединеиия
339
340 return data;
341
342 j // конец блока try
343
344 // выполняется, если мидлет MIDlet не может открыть
HTTP-соединение
345 catch ( IOException ioException ) {
346 ioException,printStackTraoe(> ;
347 J
348
349 return null;
350
351 } // кохец метода postData
352
353 // разбор строки на составлякюрте подстроки и помещение их в массив
354 private String[] parseData( String data, char delimiter )
355 i
356 int newLines =0;
357
358 // определение количества разделителей s строке
359 for ( int i = 0; i < data.length 0 ; i++ )
360
361 // увеличение количества разделителей на 1
362 if ( data.charAt< i ) = delimiter )
Разработка приложений для беспроводной связи на базе Java и J2ME 285
363 newLines++;
364
365 // создание нового строкового массива
366 String listj] = new String[ newLines ];
367
368 int oldHewLinelndex = 0;
369 int currentNewLinelndex,-
370
371 // сохранение составляющих строк в массиве
с использованием разделителей
372 for ( int i = 0; i < newLines; i++ > {
373
374 // определение индекса местоположения разделителя
375 currentMewLineindex =
376 data.indexOf( delimiter, oldNewLinelndex );
377
378 // извлечение строки, ограниченной разделителями
379 list[ i ] = data.substring( oldNewLinelndex,
380 currentNewLinelndex - 1 );
381
382 oldNewLinelndex = currentNewLinelndex + 1;
383 )
384
385 return list;
386
387 } // конец метода parseData
388
389 // соединение с сервером и получение данных
390 private String getServerData( String aervertJrl )
391 {
392 // соединение с сервером, получение данных
393 try К
394
395 // соединение с сервером, отправившим Заголовок User-Agent
396 HttpConnection httpConnection =
397 ( HttpConnection ) Connector.open( serverUrl );
398 setUserAgentHeader( httpConnection );
399
400 // отправка серверу идентификатора сеанса sessionID
401 if ( sessionID != null )
402 httpConnection.setReguestProperty{
403 "cookie", sessionID );
404
405 // получение идентификатора сеанса sessionID от сервера
406 String sessionlDHeaderField =
407 httpConnection.getHeaderField( "Set-cookie" );
408
409 // сохранение идентификатора sessionID,
извлеченного из cookie
410 if ( sessionlDHeaderField ?= null ) (
411 int index = sessionlDHeaderField.indexO£( ';' );
412 sessionID =
413 sessionlDHeaderField.substring! °< index );
414 }
415
416 // получение данных с сервера
286
Глава 5
417 String data = getData( httpConnection );
416
419 httpConnection.close(); // закрытие соединения
420
421 return data;
422
423 } // конец блока try
424
425 // обработка исключения при взаимодействии с HTTP-сервером
426 catch ( lOExCaption ioException ) {
427 ioException.printStackTrace();
428 }
429
430 return nvill;
431
432 } // конец метода getServerData
433
434 // загрузка изображения с сервера
435 private Image getServerlmage{ String imageUrl )
436 {
437 // загрузка изображения
438 try {
439
440 // открытие соединения с сервером
441 HttpConnection httpConnection =
442 ( HttpConnection ) Connector.open( imageUrl );
443
444 int connectionSize = ( int } httpConnection.getLength();
445 byte imageBytes(] = new bytef connectionsize ];
446
447 // чтение изображения с сервера
448 Inputstream input = httpConnection.openInputStream(};
449 input.read( imageBytes };
450
451 // создание изображения кз объекта. iraageBytes
4 52 return
453 Image.ereatelmage{ imageBytea, 0, connectionSize );
454 )
455
456 // обработка исключения, если объект InputStream не может
осуществить ввод
457 catch ( IOException ioException ) {
458 ioException.printStaeklrace();
459 }
460
461 return null;
462
463 ) // конец метода getServerlmage
464
465 // получение заголовка User-Agent для идентификации клиента
сервлетом
466 private void setUserAgentHeader<
467 HttpConnection httpConnection )
468 {
469 // задание заголовка User-Agent
470 try {
Разработка приложений для беспроводной связи на базе Java и J2ME 287
471
472 // использование свойств profile/configuration заголовка
User-Agent
473 String userAgentHeader = "Profile=" +
474 System.getProperty( "microedition.profiles" ) +
475 " Configurations" +
476 System.getProperty ( "microedition,configuration" );
477
478 // задание заголовка
479 httpConnection.setRequestProperty(
480 "User-Agent1', userAgentHeader ) ;
431 }
482
483 // обработка исключения при получении свойства запроса
4в4 catch ( IoException ioException ) {
4 8 5 ioException.printstackTrace<);
486 1
487
4 88 } // конец метода setUserAgentHeader
489
490 // открытие потока ввода DatalnputStream для приема данных
491 private String getData( HttpConnection httpConnection )
492 throws ioException
493 {
494 String data = ""; // сохранение данных
495
496 // открытие потока ввода для соединения
497 DatalnputStream datalnputstream =
498 new DatalnputStream (
499 httpConnection.openInputstrearn() );
500
501 int inputCharacter = datalnputstream. read {) ;
502
503 // чтение всех данных
504 while ( inputCharacter != -1 ) {
505 data = data + ( char > inputCharacter;
506 inputCharacter = datalnputstream.read();
507 }
508
509 datalnputstream.close(}; // закрытие потока
510
511 return data;
512
513 } // конец метода getData
514 ) „^
Рис. 5.27. Мидлет TipTestMIDIet загружает тест Tip-Test из сервлета TipTestServlet
На рис. 5.28 показан фрагмент API пользовательского интерфейса
J2ME.Каждый класс в составе API изображен в виде прямоугольника. Классы, имена
которых выделены курсивом, является абстрактным, а стрелки обозначают отношения
наследования (стрелка указывает на суперкласс). В API пользовательского
интерфейса J2ME абстрактный суперкласс Display able представляет содержимое,
которое MIDP-устройство может отобразить на экране. Абстрактные суперклассы
Screen и Canvas наследуют классу Dis playable и представляют, соответственно,
высокоуровневое и низкоуровневое отображаемое содержимое. Классы Alert,
288
Глава 5
Form, TextBox и List являются конкретными подклассами класса Screen.
Извещение (класс Alert) представляет собой экран (класс Screen), который мидлет
отображает на короткое время перед отображением другого экрана. Форма (класс
Form) объединяет текстовые поля, изображения и группы элементов, выбираемых
пользователем. Текстовое поле (класс TextBox) дает возможность пользователю
вводить и редактировать текст. Список (класс List) представляет собой перечень
строк (String), из которого пользователь может сделать выбор с помощью
клавиатуры МШР-устройства. В нашем практическом примере для отображения
информации на экране используются классы Form и List. Класс Canvas не содержит
каких-либо подклассов. Чтобы использовать класс Canvas, нужно сначала создать
конкретный класс, который расширяет класс Canvas, а затем переопределить его
метод paint для рисования графики на «холсте». В нашем практическом примере
используются только классы высокоуровневого пользовательского интерфейса,
поэтому мы не будем касаться использования класса Canvas.
javax.mlcroedition.lcdui.DIsplayable
Рис. 5.28. Иерархия классов API пользовательского интерфейса J2ME
Совет по переносимости программ 5.1
Низкоуровневый API пользовательского интерфейса J2ME позволяет
разработчикам создавать визуально более привлекательные экраны, чем те,
которые могут быть созданы с помощью высокоуровневого API. Однако
применение низкоуровневых API не гарантирует единообразную
компоновку элементов на экранах устройств, имеющих различные размеры
экрана. Высокоуровневые API обеспечивают более согласованную
компоновку для различных устройств.
В строке 15 класса TipTestMIDlet объявляется экземпляр класса Display,
который действует в качестве менеджера дисплея для мидлета. Мидлет должен
содержать один и только один экземпляр класса Display, чтобы осуществлять
отображение любого объекта типа DIsplayable. Класс Display является примером паттерна
проектирования Singleton, который обеспечинает, что система будет содержать
один и только один объект класса, т.е. после реализации системой экземпляра
этого объекта программе не будет разрешено создавать дополнительные объекты этого
Разработка приложений для беспроводной связи на базе Java и J2ME 289
класса. Поскольку MIDP -у с тройство имеет всего один экран, мидлет должен
содержать только один менеджер дисплея для отображения содержимого на этом
экране, и, следовательно, в каждом мидлете может существовать только один объект
типа Display. Статический метод getDisplay класса Display возвращает ссылку на
единственный в системе объект Display (этот объект Display также называется еди
ничным объектом). Если объект Display был создан, последующие вызовы метода
getDisplay просто возвращают одну и ту же ссылку на единственный объект
Display.
Объект Display гарантирует, что мидлетом TipTestMIDlet за один раз будет
отображаться только один экран (подкласс класса Displayable). В строках 18-22
объявляется пять экранов (объекты Screen) для мидлета TipTestMIDlet. Мидлет
TipTestMroiet содержит список List (строка 18) для представления основного
экрана, который содержит ссылку на сервлет "WelcomeServlet, список List (строка
19) для отображения текста из файла index.txt, форму Form (строка 20) для
отображения текста из файла info.txt (который содержит ссылку на сервлет TipTest-
Servlet), форму Form (строка 21) для отображения вопросов теста Tip-Test и форму
Form (строка 22) для отображения ответов на вопросы теста Tip-Test.
Экраны Screen также поддерживают программные кнопки: кнопки, которые
обычно располагаются на беспроводных устройствах непосредственно иод
дисплеем (но над клавиатурой). На рис. 5.8 курсор мыши наведен на правую
программную кнопку, которая подсвечена. Слово Select на экране над правой программной
кнопкой указывает, что при нажатии пользователем этой программной кнопки
будет выбран выделенный элемент из списка List, а после этого на дисплей будет вы
веден другой экран. J2ME предоставляет мидлетам эту функциональную
возможность посредством объектов Command, инкапсулирующих действие, которое будет
выполняться объектом, принимающим объект Command. В строках 25-27
мидлета TipTestMIDlet объявляются три объекта типа Command: select Command,
nextCommand и backCommand. Объект selectCommand, как мы вскоре увидим,
дает возможность пользователю выбирать на экране элементы из списка List.
Объект nextCommand дает возможность пользователю при воспроизведении теста
Tip-Test принимать следующий вопрос с вариантами названий рубрик еоветов.
Объект backCommand позволяет пользователю просмотреть предыдущий экран.
В строках 46-48 реализуются экземпляры этих объектов. Первым параметром
конструктора Command является имя, или надпись, которая будет отображаться
на экране над программной кнопкой. Второй параметр представляет собой
константу, которая задает, каким образом мидлет будет реагировать на нажатие
пользователем программной кнопки. Например, константа Command.0K указывает,
что пользователь предоставил некоторые входные данные (через текстовое поле
или через выбор элемента списка). Константа Command.BACK указывает, что
пользователю нужно просмотреть предыдущий экран. Логика, которая управляет
поведением мидлета для команд каждого типа, реализована программно. Третий
параметр задает, над какой программной кнопкой будет помещена надпись. При
реализации ряда объектов Command объект Command с наименьшим номером
будет иметь надпись, расположенную справа от программной кнопки в окне
эмулятора МГОР-устройства Sun. Для объекта, ассоциированного со следующим
наименьшим номером, устройство Sun поместит надпись над левой программной
кнопкой. В соответствии с командами в строках 46-48, устройство помещает
надпись «Select» над правой программной кнопкой, надпись «Next Tip» над правой
программной кнопкой и надпись «Back» над левой программной кнопкой.
290
Глава 5
Ш Совет по переносимости программ 5.2
Схема нумерации команд Command может быть различной для разных
устройств. Например, в эмуляторе Sun текст команды Command no
умолчанию помещается над правой программной кнопкой. В других уст-
ройствах текст может располагаться над левой программной кнопкой.
В строке 51 реализуется список с именем mainScreen, который предоставляет
ссылку на сервлет Welcomeservlet. Первый параметр, передаваемый объекту List,
представляет собой имя списка — этот текст появляется в верхней части экрана
MIDP-устройства. Второй параметр представляет собой константу, указывающую
на тип реализуемого списка. Тип списка определяет, каким образом пользователь
может перемещаться по списку с помощью клавиатуры. Этот параметр может
иметь одно из трех значений: List.IMPLICIT, List.EXCLUSIVE и List.MULTIPLE.
Константа LaetJMPLlCIT задает, что текущий элемент, на котором находится
фокус в списке, представляет собой выбор пользователя, т.е. пользователь изменяет
свой выбор при прокрутке списка. Константа List.EXCLUSIVE требует, чтобы
пользователь нажимал центральную программную кнопку для указания на
выбираемый элемент списка, после чего следует ему нажать правую программную
кнопку для завершения выбора. Однако при этом перед окончательным выбором
(но после того, как выбираемый элемент помечен), пользователь может
осуществлять прокрутку списка. Константа List.MULTIPLE дает пользователю
возможность выбирать сразу несколько элементов в списке.
В строке 52 команда selectCommand добавляется в экран main Screen, чтобы
пользователь видел надпись "Select" над правой программной кнопкой. В строках
55-69 объекту mainScreen разрешается прослушивать события от команды select-
Command путем создания нового объекта CommandListener, Когда пользователь
нажимает программную кнопку, объект selectCommand вызывает метод сот-
mandAction (строки 59-67). Этот метод принимает в качестве параметров объект
Command, ассоциированный с последней нажатой программной кнопкой, и объект
Display able, над которым это действие выполняется. Логика работы метода сот-
mandAction будет разъяснена ниже.
Оператор в етроке 73 получает объект менеджера дисплея для устройства.
Когда конструктор возвращает управление, AMS вызывает метод startApp, который
предписывает мидлету TipTestMIDlet принять ввод пользователя и отобразить
содержимое экранов. В строке 81 с использованием менеджера дисплея в качестве
текущего экрана устанавливается экран mainScreen. На рис. 5.29 показан
результат выполнения этой операции.
Теперь мидлет TipTestMIDlet ожидает ввода от пользователя. Единственные
события, зарегистрированные для TipTestMIDlet, связаны с правой программной
кнопкой. Когда пользователь нажимает эту кнопку, объект selectCommand
вызывает метод commandAction любого зарегистрированного слушателя Command-
Listener. В строке 63 URL сервлета WelcomeScrvlet передается методу get-
ServerData (строки 390-432), который связывается с сервером и принимает
данные. В строках 396—397 с использованием параметра URL открывается соединение
HttpConnection. В строке 398 осуществляется вызов метода setUserAgentHeader
(строки 466-488), чтобы сервер мог идентифицировать, какой клиент отправил
запрос. В отличие от Internet Explorer, браузеров Openwave UP и Pbto, клиент J2ME
не имеет стандартного заголовка User-Agent, поэтому необходимо определить
собственный заголовок и сохранить его в интерфейсе ClientUserAgentHeaders, чтобы
сервлеты могли распознать мидлет TipTestMIDlet как клиента J2ME. В строках
473-476 с помощью информации о профиле и конфигурации TipTestMIDlet
создается заголовок User-Agent. В строках 479-480 этот заголовок назначается запросу
HttpConnection,
Разработка приложений для беспроводной связи на базе Java и J2ME 291
Рис. 5.29. Основной экран мидлета TipTestMIDIet (Публикуется с разрешения корпорации
Sun Microsystems, Inc.)
Нам также необходимы средства для отслеживания данных этого сеанса, чтобы
сервлет мог хранить информацию о состоянии, например, правильный ответ,
в промежутке между сеансами. В строках 406-407 осуществляется сохранение
поля заголовка Set-cookie соединения HttpConnection, которое предоставляет
информацию об этом сеансе, в строке sessionlD (строка 40 листинга). Заголовок
Set-cookie содержит информацию, разделяемую точками с запятой. Сервер хранит
строку идентификатора сеанса перед первой точкой с запятой. Идентификатор
сеанса извлекается в строках 401-403. При следующем соединении мидлета
TipTestMIDIet с сервером операторы в строках 401-403 отправляют эту информацию,
чтобы идентифицировать сеанс.
В строке 417 осуществляется вызов метода get Data (строки 491-513), чтобы
принять данные с сервера. В строках 497—499 открывается поток ввода Datalnput-
Stream для чтения формируемых сервером данных. В строках 501—507 эти данные
записываются в строку, после чего эта строка возвращается.
На данный момент мидлет TipTestMIDIet установил соединение с сервлетом
WelcomeServlet и получил данные — текст из файла index.txt — которые
представляют собой экран приветствия. Вто следующие данные:
eLearning Deitel Programming Tips ;j2me/in£o.txt ;
В строке 66 листинга эта строка передается в качестве параметра методу
crate WelcomeScreen (строки 91-138). В строке 93 осуществляется вызов метода
parseData (строки 354-387), который выполняет синтаксический разбор данных
и помещает их в массив строк, чтобы можно было осуществить доступ к отдельным
строкам. Метод parseData действует аналогично классу java.util.StringTokenizer
J2SE, который отсутствует в пакете java.ntil J2ME (по причине ограниченности
ресурсов MIDP-устройства). Мы используем точку с запятой в качестве
разделителя при синтаксическом анализе данных, чтобы метод parseData возвращал
строковый массив из двух элементов, который содержит текст «eLearning Deitel
Programming Tips» (название экрана) и *j2me/info.txt» (ссылка на каталог
приложения Tip-Test). В строке 96 создается экран WelcomeScreen с использованием
первого элемента в строковом массиве в качестве имени списка. В строке 98 в
список добавляется строка «Take TipTest», информирующая пользователя, что ему
292
Глава 5
следует загрузить тест Tip Test. Экран приветствия мидлета TipTestMIDlet
показан на рис. 5.30.
В строках 99-134 объект welcomeScreen регистрируется в качестве слушателя
CommandListener для событий операций selectCommand и backCommand. Когда
пользователь нажимает программную кнопку, один из объектов, либо select-
Command, либо backCommand, в зависимости от того, какая кнопка нажата,
вызывает метод commandAction (строки 110-131). Объект Command является
примером командного паттерна проектирования Command Design Pattern. В J2ME
объект Command может содержать различные команды или инструкции, такие
как «показать следующий экран», «показать предыдущий экран» или «выйти из
приложения». Эти команды могут прослушиваться несколькими объектами типа
Displayable в системе: разработчики MIDP-приложений программируют
операции, которые каждый из объектов Displayable выполняет при получении команд.
Например, мы регистрируем объект welcomeScreen в качестве слушателя для
объектов selectCommand и backCommand. Когда каждый из объектов Screen
получает событие Command, метод commandAction использует метод getCommandType
события Command для определения, имеет ли команда тип Command.OK
(«показать следующий экран»), или тип Command.BACK («показать предыдущий
экран»), Мидлет TipTestMIDlet разработан таким образом, чтобы он предпринимал
действия в соответствии с тийом полученной команды. Если пользователь нажал
левую программную кнопку (Back), в строке 128 устанавливается дисплей (объект
Display) для отображения экрана mainScreen. Если пользователь нажал правую
программную кнопку (Select), в строках 117—122 отображается информационный
экран. В строках 117-118 вызывается метод getServerData, который
осуществляет соединение с сервером и получает текст из файла j2me/info.txt как строку
In this exercise, we will test your knowledge of the Oeitel
programming tips ;tiptest ;
В строках 121-122 эта строка передается в качестве параметра методу create-
InformationScreen (строки 141-200), который формирует экран,
предоставляющий справочную информацию об использовании теста Tip-Test. В строке 143
осуществляется вызов метода parseData для синтаксического разбора
сформированных сервером данных и помещения их в двухэлементный строковый массив.
Первый элемент строкового массива представляет собой название создаваемого
экрана, а второй элемент — ссылку на сервлет TipTestServlet. В строке 146
создается форма infoScreen. Форма использует компоненты Stringltem, содержащие
строки, для отображения нескольких строк текста. В списке List может быть
отображена только одна строка текста и, следовательно, только часть первого
элемента строкового массива. Форма Form используется, чтобы отобразить первый
элемент строкового массива целиком. В строках 149-150 элемент Stringltem
добавляется в форму infoScreen с использованием первого элемента строкового массива.
В строках 153-158 создается объект ChoiceGroup, представляющий собой группу
элементов, из которых пользователь может выбирать. Этот объект создается с
использованием второго элемента строкового массива, чтобы мидлет TipTestMIDlet
мог связываться с сервлетом TipTestServlet. Обратите внимание, что в строке 154
группа ChoiceGroup объявляется как EXCLUSIVE, т.е. пользователь должен явно
указать, какой элемент выбрать. В нашем примере имеется только один элемент
для выбора (tiptest), поэтому этот элемент является выбранным по умолчанию.
Информационный экран мидлета TipTestMIDlet показан на рис. 5.31.
Типичная ошибка программирования 5.2
Группы ChoiceGroup могут объявляться либо как EXCLUSIVE, либо как
MULTIPLE. Объявление группы ChoiceGroup как INCLUSIVE приведет
к возбуждению исключения IllegalArgumentException.
Разработка приложений для беспроводной связи на базе Java и J2ME 293
Рис. 5.30. Экран приветствия, формируемый мидлетом TipTestMIDlet. (Публикуется
с разрешения Sun Microsystems, inc.)
Рис. 5.31. Информационный экран, формируемый мидлетом TipTestMIDlet.
(Публикуется с разрешении Sun Microsystems, Inc.)
В строках 160-196 объект infoScreen регистрируется в качестве слушателя
CommandListener для событий, связанных с операциями selectCommand и back-
Command. Когда пользователь нажимает программную кнопку, один из объектов,
selectCommand или backCommand, в зависимости от того, какую кнопку нажал
пользователь, вызывает метод comznandAction (строки 168-193). Если
пользователь нажал левую программную кнопку (Back), в строке 191 устанавливается
объект типа Display для отображения экрана w«lcnmeSereen. Если пользователь
нажал правую программную кнопку (Select), в строках 175-186 отображается экран
294
Глава 5
с вопросом теста. В строке 175 определяется, какой элемент в группе ChoiceGroup
выбран. В строках 177-178 этому элементу назначается ссылка на сервлет TipTest-
Servlet. В строках 181-182 осуществляется вызов метода getServIetData, который
осуществляет соединение с сервлетом TipTestServlet и принимает вопрос теста Tip
Test. Сервлет TipTestServlet случайным образом генерирует информацию каждый
раз, когда мидлет TipTestMIDlet устанавливает соединение; данные, которые
генерирует сервлет TipTestServlet, имеют следующий формат:
http://loealhost:8080/advjhtpl/j2rae/png/portability.png
PERF
CPE
TAD
PORT
В строках 185—186 осуществляется вызов метода create'Mpl'estScreen (строки
203-256) для отображения вопроса теста Tip-Test. В строке 206 вызывается метод
parseData для синтаксического разбора вопроса теста Tip-Test и помещения его
в пятиэлементный строковый массив. Первый элемент содержит имя файла
изображения, размещенного на сервере. Остальные четыре элемента содержат четыре
аббревиатуры рубрик советов, из которых пользователь должен выбрать
правильный ответ. Форма Form создается, чтобы нметь возможность отобразить
изображение Image и группу с вариантами выбора ChoiceGroup, поскольку никакие другие
подклассы класса Display able не обеспечивают такую возможность, В строке 209
создается новая форма tipScreen для отображения вопроса теста Tip-Test.
В строке 212 первый элемент строкового массива передается методу getServer-
Image (строки 435-463), который создает объект Image из файла изображения,
хранящегося на сервере. В строках 441-442 создается соединение HttpConnection
с сервером, в строках 448-449 эти данные записываются в поток ввода Input-
Stream, а в строках 452-453 осуществляется возврат изображения Image из
потока ввода Inputs tre am.
В строках 215-216 изображение Image добавляется в экран tipScreen. Теперь
мы должны представить четыре возможных ответа, ассоциированных С этим
изображением, в виде группы ChoiceGroup. В строках 218-222 создается строковый
массив для хранения всех четырех ответов. В строках 225-226 реализуется
экземпляр объекта ChoiceGroup, а в строке 229 группа ChoiceGroup добавляется в экран
tipScreen. Обратите внимание, что конструктор ChoiceGroup принимает четыре
параметра (в отлячие от конструктора ChoiceGroup в методе createlnformation-
Screen, который принимает два параметра). Первый параметр представляет собой
имя группы для выбора ChoiceGronp, которую мы назвали «Tip Test». Второй
параметр объявляет тип этой группы как EXCLUSIVE, поэтому пользователь
должен подтвердить свой выбор, прежде чем продолжить выполнение. Третий
параметр представляет собой строковый массив, который содержит все возможные
ответы. Четвертый параметр представляет собой массив изображений Image,
ассоциированных с элементами в строковом массиве. На экране устройства каждое
из изображений, содержащихся в массиве Image, помещается рядом с
соответствующей строкой из массива String. В данном случае в качестве этого параметра
передается null, поскольку нам не нужно выводить изображения рядом с каждым
из четырех вариантов ответа.
Экран tipScreen представлен на рис, 5.32. На рисунке слева показано
изображение Image, для которого пользователь должен выбрать правильное название
рубрики советов. Если пользователь осуществит прокрутку этого экрана вниз
(рисунок справа), он увидит четыре возможных варианта ответа. Пользователь
должен нажать центральную программную кнопку, чтобы подтвердить свой выбор.
Разработка приложений для беспроводной связи на базе Java и J2ME 295
Рис. 5.32. Экран с вопросом теста Tip-Test, формируемый мидлетом TipTestMIDlet
В строках 231-251 объект tipScreen регистрируется в качестве слушателя
CommandListener для событий операции selectCommand. Когда пользователь
нажимает кнопку Select, объект selectCommand вызывает метод commandAction
(строки 238-249). В строке 242 определяется, какой элемент в группе ChoiceGroup
был выбран пользователем. В строке 244 сделанный пользователем выбор
передается методу postData (строки 306-351), который отправляет этот выбор сервлету
TipTestServlet. В строках 312-315 осуществляется соединение с сервлетом
TipTestServlet и указывается, что соединение является двунаправленным, т.е. мид-
лет TipTestMIDlet может как отправлять данные сервлету TipTestServlet, так
и принимать от него данные. В строке 317 задается заголовок User-Agent, чтобы
сервлет TipTestServlet мог распознать мидлет TipTestMIDlet как клиента J2ME.
В строках 320-322 осуществляется отправка идентификатора сеанса, который был
сохранен с помощью метода getServerData, чтобы идентифицировать сеанс.
В строке 325 задается, что сервлег TipTestServlet будет принимать от мидлета
TipTestMIDlet данные, отправленные с помощью метода post. В строках 328-329
открывается поток вывода DataOutputStream, через который в строке 322 выбор
пользователя отправляется сервлету TipTestServlet. Как нам известно из раздела
5.3.4, после получения этих данных сервлет TipTestServlet посылает клиенту
J2ME правильный ответ. В строке 336 осуществляется вызов метода getData для
получения строковых данных с сервера. Эти данные имеют следующий вид:
Correct
Portability Tip
Organizations that develop software must often produce versions
customized to a variety of computers and operating systems. These
tips offer suggestions to make your applications more portable
В строке 338 закрывается соединение между мидлетом TipTestMIDlet И
сервлетом TipTestServlet, а в строке 340 возвращаются данные, которые содержат
правильный ответ. В строках 247-248 эти строковые данные передаются методу
createAnswerScreen (строки 259-303), который создает экран (объект Screen),
отображающий ответ. В строке 262 осуществляется вызов метода parseData для
синтаксического разбора данных и помещения их в трехэлементный строковый
296
Глава 5
массив. Первый элемент указывает, является ли ответ пользователя правильным
или неправильным. Второй и третий элементы содержат, соответственно,
правильное название рубрики советов и ее описание. В строке 265 реализуется форма
answerScreen путем передачи конструктору первого элемента строкового массива.
Форма используется для отображения полного описания рубрики. В строках
268-273 реализуются два объекта Stringltem для хранения названия рубрики
советов и ее описания. В строках 276-277 эти элементы Stringltem добавляются
в экран answerScreen. Экран answerScreen представлен на рис. 5.33. На рисунке
слева показан экран, содержащий правильное название рубрики советов и ее
описание. Если пользователь осуществит прокрутку этого экрана вниа, он увидит
остальную часть описания (см. рисунок справа). В строках 279-299 объект tipScreen
регистрируется в качестве слушателя commandListeuer для событий операции
nextCommand. Поведение объекта nextCommand идентично поведению объекта
selectCommand; однако, поскольку мы не можем изменить надпись для объекта
selectCommand на «Next Tip», нам приходится реализовывать другой объект
Command, чтобы над правой программной кнопкой отображался текст «Next Tip».
Когда пользователь нажимает правую программную кнопку, объект nextCommand
вызывает метод commandAction (строки 286 296), который вызывает метод сге-
ateTipTestScreea для формирования следующего вопроса теста Tip-Test:
пользователи могут выполнять тест Tip-Test сколь угодно долго.
Рис. 5.33. Экран с ответом на вопрос теста, формируемый мидлетом TipTestMIDIet
(Публикуется с разрешения Sun Microsystems, Inc.)
На этом завершается рассмотрение практического примера приложения для
беспроводной связи, реализованного средствами Java и Java 2 Micro Edition.
В этом разделе мы создали трехуровневую архитектура, в которой сервлет TipTest-
Servlet (средний уровень) размечает случайным образом сгенерированный вопрос
теста Tip Test как XML-документ, применяет XSLT-трансформацию к
XML-документу, а затем отправляет полученный документ клиентам. Затем мы
познакомились с J2ME, обсудив технологии CLDC и MIDP, которые составляют основу
интерфейса прикладного программирования J2ME для создания приложений,
выполняющихся на мобильных устройствах. Мы изучили жизненный цикл мидлета
Разработка приложений для беспроводной связи на базе Java и J2ME 297
и выяснили, как создавать новый мидлет из класса Mlplet. Мы создали МГОР-при-
ложение TipTestMlDlet, а затем обсудили, каким образом оно извлекает данные
из сервлетов Welcomeservlet и TipTestServlet. Мы также рассмотрели, как
мидлет TipTestMlDlet использует эти данные для построения пользовательских
интерфейсов, и каким образом, используя объекты Command, мидлет TipTestMlDlet
дает возможность пользователю переходить от одного экрана Screen к другому.
В главе 6 будет рассмотрена технология Enterprise JavaBeans (EJB), которая
предоставляет модель для построения бизнес-логики в корпоративных
приложениях Java.
5.5. Инструкции по установке
В этом разделе приводятся инструкции по установке программного
обеспечения, используемого в примере.
Настройка Web-сервера,
Для выполнения этого практического примера требуется Web-сервер,
поддерживающий технологию сервлетов. Мы рекомендуем использовать сервер Apache
Tomcat Server. Состав каталогов для установки Tomcat описан в разделе 2.3.1.
Установив Tomcat, скопируйте содержимое каталога advjhtpl из комплекта
примеров к данной главе в каталоге Tomcat в вашей системе. В каталоге advjhtpl
имеются четыре подкаталога — iMode, j2me, XHTML, WAP — в которых хранится
содержимое, посылаемое сервлетами клиентам соответствующих типов.
Далее необходимо скопировать содержимое, приведенное на рис. 5.34, которое
представляет собой содержимое элемента webapp документа wml.xml (этот
документ имеется в комплекте примеров к данной главе). Файл wml.xml должен
располагаться в каталоге WEB-INF Tomcat. Например, в нашей системе файл web.xml
находится в каталоге
С •. \ jakacta- tomcat- 3.2.2\webapps\adv}htpl\W£B-lHF\
1 <!— Определения сервлетов —>
2 <servlet>
3 <s#rvlet-naae>wel.come</servlet-name>
4
5 <description>
6 A servlet that returns a "Welcome" screen through
7 ад HTTP get request
8 </description>
9
10 <servlet-class>
11 com.deitel.advjhtpl.wireless.WelcomeServlet
12 </servlet-class>
13 </servlet>
14
15 <servlet>
16 <servlet-name>tiptest</servlet-name>
17
18 <description>
19 A servlet access a database to generate tests for
20 Deitel programming tips
21 </description>
22
23 <i.nit-param>
24 <param-name>DATABASE_URL</param~name>
298
Глава 5
25 <param-value> jdbc; cloudscape: nil: tips</param-value>
26 </init-param>
27
28 <init-para«n>
29 <param-name>JDBC_DRIVER</param-name>
30 <param-value>
31 COM.eloudseape.core.FmiJdbcDriver
32 </param-value>
33 </init-paea«>
34
35 <servlet-class>
36 com.deitel.advjhtpl.wireless.TipTestServlet
37 </servlet-class>
38 </servlet>
3»
40 <!— Карты сервлетов -->
41 <servlet-mapping>
42 <servlet-name>welcome</3ervlet-name>
43 <utl-pattern>/welcune</url-pattern>
44 </servlet-mapping>
45
46 <servlet-mapping>
47 <servlet-name>tiptest</3ervlet-name>
4 8 <ur1-pattern>/tiptest</ur1-pattern>
49 </servlet-mapping>
50
51 <mime-mapping> <!-- Формат WML —>
52 <extension>wml</extension>
53 <miuie-type>text/vnd.wap.wml</iiLLme-type>
54 </mime-mapping>
55
56 <miiae-mapping> <!-- Формат Wireless Bitmap —>
57 <extension>wbmp</extension>
58 <mjjae-type>image/vnd.wap.wbnrp</m±me-type>
59 </mime-mapping>
Рис. 5.34. Дескриптор развертывания для выполнения сере летав WelcomeServlet
и TipTestServlet
В листинге на рис. 5.34 в строках 2-13 описывается сервлет WelcomeServlet, a
в строках 15-38 — сервлет TipTestServlet. В строках 23-33 объявляются два
элемента init-param, которые дают нам возможность менять используемую сервлетом
TipTestServlet базу данных без модификации файла TipTestServlet.java. В
строках 41-49 двум сервлетам ставятся в соответствие определенные URL. В строках
51 59 осуществляется настройка Tomcat для обработки WML-содержимого с
надлежащими МШЕ-типами. Если не указать эти MIME-типы, WAP-клиенты не
смогут получать содержимое.
Последний этап настройки Tomcat дли выполнения практического примера
состоит в задании классов, которые Tomcat использует для выполнения сервлетов.
Создайте структуру каталогов com/deitel/advjhtpl/wireless в каталоге WEB-INF/
classes. Например, в нашей системе структура каталогов будет следующей:
С:\Jakarta-tomcat-3.2.2\webapps\advjhtpl\WEB-INF\classes\com\
deitel\advjhtplWireless
Скопируйте файлы WelcomeServlet.class и TipTestServlet.class в этот каталог.
Разработка приложений для беспроводной связи на базе Java и J2ME 299
Настройка базы данных
Для этого практического примера также требуется база данных, из которой
сервлет может извлекать информацию о рубриках советов. Мы рекомендуем
использовать базу данных Cloudscape. Скопируйте файл tips.sql и каталог tips из
каталога примеров к этой главе в каталог frameworks/RmiJdbc/Ъш, в котором в
вашей системе установлен Cloudscape. Например, в нашей системе Cloudscape
установлен в каталоге c:\cloudscape_3.6, поэтому файл tips.sql и каталог tips должны
содержаться в каталоге C:\cloiidscape_3.6\frameworKs\RmiJdbc\bm\. Далее,
скопируйте файлы cloudscape.jar и RmiJdbe.jar (они имеются в комплекте примеров
к этой главе) в каталог W ЕВ -INF/lib Tomcat. Например, в нашей системе это будет
каталог
С:\jakarta-tomcat-З.2.2\webapps\advjhtpl\WEB-INF\lib\
Поместив файлы cloudscape.jar и RmiJdbe.jar в этот каталог, вы дадите
возможность сервлету TipTcstServlet соединяться с базой данных Cloudscape я извлекать
из нее информацию.
Установка и настройка J2ME Wireless Toolkit
Чтобы воспользоваться MIDP-устройством Sun для разработки МЮР-приложе-
ний, нужно применить набор инструментальных средств J2ME Wireless Toolkit.
Этот инструментарий можно загрузить с сайта
java.sun.com/product3/j2mewtoolkit/
На момент написания этой книги для загрузки была доступна версия 1.0.2.
В процессе установки можно указывать, хотите ли вы интегрировать J2ME
Wireless Toolkit с Forte (это облегчает разработку MIDP-приложений в Forte), либо
будете выполнять инструментарий как автономное приложение. Мы рекомендуем
интегрировать инструментарий в Forte.
Бели вы интегрировали инструментарий, создайте структуру каталогов com/
deitel/advjhtpl/wireless в каталоге разработки Forte в вашей системе. Например,
в нашей системе каталогом разработки Forte является c:\forte4j\development,
поэтому мы создаем структуру каталогов
С:\forte43\Development\com\deitel\advjhtpl\wirelessS
Далее, скопируйте файлы TipTestMIDlet.java и TipTestMIDlet.jar из
комплекта примеров к этой главе в данный каталог. Раскрыв эту структуру каталогов
в Forte, вы должны увидеть
TipTestMIDlet.Java
TipTestMIDlet.jar
Файл TipTestMIDlet.jar представляет собой комплект мидлетов, который
содержит класс TipTestMIDlet- Чтобы выполнить класс TipTestMIDlet, щелкните
правой кнопкой мыши на значке TipTestMIDlet.jar, а затем выберите операцию
выполнения.
Если вы предполагаете использовать инструментарий Wireless Toolkit (или
если вы установили инструментарий как автономное приложение), сначала
откройте Wireless Toolkit, выбрав KToolbar в каталоге, в который вы хотите
установить инструментарий. Далее» нажмите кнопку New Project. R текстовом поле
Project Name введите
TipTestMIDlet
В текстовом поле MIDlet Class Name введите
com.deitel.advjhtpl.wireless.TiptestMIDlet
затем нажмите кнопку create Project. В появившемся окне Settings нажмите
кнопку ОК. Далее, скопируйте файл TipTestMIDlet.java из комплекта примеров
300
Глава 5
к этой главе в подкаталог apps/TipTestMIDIet/src каталога, в котором установлен
инструментарий. Например, в нашей системе мы установили инструментарий
в каталог C:\J2mewtk, поэтому следует скопировать файл TipTestMIDlet.java
в каталог
С: \J2mewtJc\apps\TipTestMIDlet\srcS
Вернитесь в окно Wireless Toolkit и нажмите кнопку Build. После завершения
компиляции и предварительной проверки мидлета TipTestMIDlet нажмите кнопку
Ran, чтобы выполнить мидлет TipTestMIDlet. Memo Device, расположенное в
правой части окна ипструмектария, дает возможность выбирать, на каком устройстве
выполнять мидлет TipTestMIDlet. Хотя устройством, предлагаемым по умолчанию,
является эмулятор МШР-устройства Sun с экраном, позволяющим воспроизводить
оттенки серого, вы можете выбрать для выполнения мидлета TipTestMIDlet и
другие устройства, такие как RIM Blackberry-957™ и Motorola i85s™.
Другие типы клиентов
В таблице на рис. 5.35 приведены адреса сайтов, с которых можно загрузить
браузеры, используемые в данном практическом примере.
Браузер
Microsoft Internet Explorer
Имитатор Open wave UP
Pixo Internet Microbrowser
URL
www.microsoft.com/downloads/search.asp?
developer.openwave.com/download/license_j41.html
www.pixo.com/products/products 001.htm
Рис. 5,35. URL браузеров, используемых в практическом примере
5.6. Ресурсы в Internet и во Всемирной паутине
www.Java.sun.com/j2me
С этого сайта можно загрузить пакет Sun Java 2 Micro Edition.
www,onjava.com/pub/a/onjava/2001/03/OB/J2me.html
Этот сайт содержит статью, посвященную J2ME.
www.jguru.com/faq/home.jsp?topic=J2ME
На этом сайте имеется FAQ (часто задаваемые вопросы) по J2ME.
www.wireless da vne t.com/channe1s/j ava/features/j 2ma_http.phtrol
Этот сайт затрагивает проблемы сетевого программирования с использованием
устройств, поддерживающих J2ME.
www. eri-cgiguere. com/micro java/Eallacies. html
Этот сайт содержит перечень некоторых характерных заблуждений относительно
J2ME.
www.motorola.com/Java
Этот сайт посвящен вопросам интеграции J2ME в новые устройства беспроводной
связи Motorola.
www.internetnews.com/wd-news/article/О, ,10_5ЭЭ091, 00.html
На этом сайте можно найти информацию, посвященную интеграции J2ME в
карманные компьютеры Palm.
www.mot.com/Java/devices.html
На этом сайте приведен список имеющихся на рынке беспроводных устройств,
которые поддерживают технологию J2ME,
Разработка приложений для беспроводной связи на базе Java и J2ME 301
www.nttdocomo.com/i/index.html
Это Web-сайт кампании NTT DoCoMo — создателя технологии i-roode.
www.anywhereyougo.com/ imode/index .po
На этом сайте представлены новости, относящиеся к технологии i-mode.
www.i-modesales.com
Этот сайт предоставляет информацию о моделях телефонов и сервисах, использующих
технологию i-mode.
www.wep.com
Этот сайч содержит новости и информацию о новых разработках в сфере W АР
-технологий.
www.wapforum.org/
На этом сайте обсуждаются преимущества, которые обеспечивает технология WAP.
Резюме
• Данный практический пример представляет собой построенное по трехуровневой
архитектуре приложение для проведения теста (Tip Test) с несколькими вариантами ответов,
которое дает возможность пользователям проверить знания условных обозначений
рубрик советов по программированию, используемых в книге.
• Разработчики могут использовать технологию Java для создания приложений для
беспроводной связи и серверных приложений, которые также могут создаваться с помощью
технологии ASP.
• Информационный уровень представлен базой данных. Средний уровень представлен
двумя сервлетами — WelcomeServlet и TipTestServlet — которые генерируют содержимое
для клиентов различного типа. Клиентский уровень представлен клиентами четырех
типов: Internet Explorer, WAP, i-mode и J2ME. Клиент каждого типа воспроизводит
содержимое по-своему.
• Microsoft Internet Explorer воспринимает XHTML-содержимое.
• Имитатор Openwave UP — это WAP-клиент, который воспринимает WML-содержимое.
• Pixo Internet Microbrowser — это клиент i-mode, который воспринимает сНТМЬ-содержи-
мое.
• Эмулятор MIDP-устройства Sun отображает клиента J2ME, который воспринимает
содержимое в виде обычного текста.
• J2ME™ (Java™ 2 Micro Edition) — это новая платформа Java, созданная Sun для
разработки приложений для различных бытовых устройств, таких как телевизионные игровые
приставки, Web-терминалы, встроенные системы, мобильные телефоны и пейджеры.
• MIDP (Mobile Information Device Profile) представляет собой набор интерфейсов
прикладного программирования, который позволяет разработчикам решать задачи, характерные
для мобильных устройств, такие как создание пользовательских интерфейсов, локальное
хранение информации и сетевые взаимодействия.
• Устройства, которые выполняют приложения для MIDP, называются MIDP-устройствами
(например, сотовые телефоны или пейджеры).
■ Клиенты взаимодействуют с сервлетами, направляя сервлетам запросы get и post. Если
клиент отправляет сервлету (объект HttpServlet) get-запрос, запрос обрабатывается
методом doGet. Бели клиент отправляет сервлету post-запрос, запрос обрабатывается методом
doPost.
• Все клиенты имеют уникальный заголовок User-Agent, который содержит информацию
о типе клиента, запросившего данные с сервера.
• Метод getlnitParameter интерфейса HttpServlet дает возможность указывать на
информацию (например, местонахождение базы данных или драйверов), объявленную в
документе web.xml. Для изменения этой информации достаточно модифицировать элемент
<param-value> в элементе <init-param> документа web.xml.
• Объекты org.w3c.dom.Element представляют элементы, являющиеся узлами
XML-документов.
• Для получения различного содержимого для клиента различного типа следует применить
к XML-документу XSLT-трансформапию, Клиент каждого типа воспроизводит это
содержимое соответствующим образом.
302
Глава 5
• Для клиента J2ME XML не использовался, поскольку на момент написания этой книги
Л2МЕ не поддерживал ХМТ. бея применения специализированного программного
обеспечения на базе XML.
• J2ME использует технологии Connected Limited Device Configuration. (CLDC) и Mobile
Information Device Profile (MIDP), которые предоставляют разработчикам интерфейсы
прикладного программирования для написания приложений J2ME и развертывания их
на мобильных устройствах различных типов.
• CLDC — это набор интерфейсов прикладного программирования, который дает
возможность разработчикам создавать приложения для устройств с ограниченными ресурсами,
т.е. с ограниченным размером экрана, объемом памяти, мощностью и скоростью
передачи.
» В настоящий момент CLDC не содержит ряда возможностей, которые обычно «имеют под
рукой» разработчики на других платформах Java, например, операции с плавающей
запятой, сериалиэуемые объекты и группы программных потоков.
• MIDP представляет собой набор интерфейсов прикладного программирования, который
дает возможность разработчикам реализовывать специфичные для мобильных устройств
функции, такие как создание пользовательских интерфейсов, локальное хранение
данных и определение жизненных циклов приложений для МЩР-клиеятОБ.
• Мидлет — это разновидность приложения для МГОР-клиеитов. Все мидлеты расширяют
класс javax.microedition.midlet.MIDlet.
• Разработчики мыдле-гоэ хранят несколько мидлетов в jar-файле, называемом комплектом
мидлетов, на сервере.
• Программа управления приложением (Application Management Software — AMS),
размещенная на МШР-устройстве, загружает комплект мидлетов с сервера, открывает
комплект мидлетов, а затем запускает указанный пользователем мидлет на МШР-устройстве.
• Жизненный цикл мидлета определяется методами startApp, pauseApp и destroyApp.
• В J2ME API пользовательского интерфейса делятся на высокоуровневые и
низкоуровневые. Низкоуровневые АРЕ позволяют разработчикам добавлять в приложения графику
и анимацию, в то время как высокоуровневые API пользовательского интерфейса дают
возможность разработчикам добавлять в приложения текстовые поля, списки и формы.
• Класс Displayable представляет содержимое, которое MIDP-устройство может отобразить
на экране, Классы Screen (высокоуровневые) и Canvas (низкоуровневые) расширяют
класс Displayable.
• Класс Display действует в качестве менеджера мидлета. В мидлете может иметься только
один объект Display.
• J2ME предоставляет поддержку программных кнопок в мвдлете посредством объектов
Command, которые инкапсулируют действие, выполняемое объектом, принимающим
объект Command.
• Два высокоуровневых класса пользовательского интерфейса — List и Form — расширяют
класс Screen,
• Форма (объект Form) может содержать группы элементов (объект ChoiceGroup), из
которых пользователь может делать выбор.
Терминология
application management software (AMS) —
программа управления приложением
cHTML (компактный вариант HTML)
class Canvas, класс
class ChoiceGronp, класс
class Command, класс
class CommandListener, класс
class Display, класс
class Displayable, класс
class DocamentBuilder, класс
class DocamentBiiilderFactory, класс
class DOMSource, класс
class Element, класс
class Form, класс
class HttpServletResponse, класс
class HttpServletRequest, класс
class HttpSession, класс
class luputStream, класс
class List, класс
class Screen, класс
class Stream Source, класс
class Stringrtem, класс
class TextBox, класс
class Transfomer, класс
class TransfomerFactory, класс
client — клиент
CLDC packages java.io, java.lang, java.util,
пакеты CLDC
Connected Limited Device Configuration
(CLDC)
Разработка приложений для беспроводной связи на базе Java и J2ME 303
database — база данных
Extensible Hyper Text Markup Language
(XHTML)
get and post data — методы get и past
отправки данных
i-mode
information tier — информационный
уровень
Java 2 Micro Edition (J2ME)
J2ME high-level user-interface API —
высокоуровневый API пользовательского
интерфейса J2ME
J2ME low-level user-interface API —
низкоуровневый API пользовательского
интерфейса J2ME
Java application descriptor file — файл
дескриптора приложения Java
KVM, виртуальная машина
Openwave UP simulator — имитатор
Openwave UP
method commandAction of class
CommandListener — метод
commandAction класса CommandListener
method doGet of class HttpServlet — метод
doGet класса HttpServlet
method doPost of Ыаяя HttpServlet — метод
doPost класса HttpServlet
method destroyApp of class MIDlet — метод
destroyApp класса MIDlet
method pa use App of class MIDlet —
метод panseApp класса MIDlet
method startApp of class MIDlet —
метод startApp класса MIDlet
Упражнения для самоконтроля
5.1. Заполните пропуски в следующих предложениях:
a) Трехуровневая архитектура обычно состоит из уровня, уровня
и __ уровня.
b) Сервлеты в нашем практическом примере генерируют -содержимое для
Internet Explorer, -содержимое для WAP-клиента, -содержимое для
клиента i-mode и содержимое в виде ___ для клиента J2ME.
c) Сервлет TipTestServlet использует объект для хранения правильного
названия рубрики советов и ее описания.
d) CLDC — сокращение от _.
e) MIDP — сокращение от .
f) Разработчики могут упаковывать несколько мидлетов в jar-файл под названием
g) Файл дескриптора приложения имеет расширение .
h) Жизненный цикл мидлета определяется методами , и .
i) Класс из пакета javax.microedition.ledui представляет содержимое,
которое MIDP-устройство может отображать на экране,
j) Класс javax.microedition.Display является примером паттерна проектирования
5.2. Ответьте, является ли каждое из приведенных ниже высказываний истпинным или
ложным. Если высказывание ложно, объясните, почему.
а) Объект HttpServlet вызывает метод doGet при получении запроса get и метод
doPost при получении запроса post (истинно/ложно).
middle tier — средний уровень
MIDlet — мидлет
MEDlet lifecycle — жизненный цикл мидле-
та
MIDlet suite — комплект мидлетов
MIDP application — MIDP-приложение
MIDP devices — MIDP-устройства
MIDP package javax.microeditionЛо, аакет
MIDP
MIDP package javax.microeditlon.ledni,
пакет MIDP
MIDP package javax.microedition.nns, пакет
MIDP
mobile device — мобильное устройство
Mobile Information Device Profile (MIDP)
namespace — пространство имен
Pixo Internet Microorowser — микробраузер
Pixo
server — сервер
Sun MlDP-device emulator — эмулятор
MIDP-устройства Sun
three-tier architecture — трехуровневая
архитектура
User-Agent header — заголовок User-Agent
servlet — сервлет
Wireless Application Protocol (WAP),
протокол
Wireless Markup Language (WML), язык
разметки
XML
XML Document — XML-документ (объект
Document)
XSLT
304
Глава 5
b) Заголовок User-Agent содержит информацию о сервере (истинно/ложно).
c) Статический метод newDocumentBuilder класса DocnmentBuilder создает новый
объект DocnmentBuilder (истинно/ложио).
d) В данном практическом примере сервлет TipTestServlet использует XSLT для
трансформации XML-документов теста Tip-Test а правильное содержимое для всех
типов клиентов (истинно/ложно).
e) В состав CLDC J2ME входят пакеты java.io, Java.Jang и java.net (истинно/ложно).
f) Программа управления приложением загружает мидлет н MIDP-устройство
(истинно/ложно).
g) Файл дескриптора приложения задает такую информацию, как конфигурация
MIDP-устройстаа и ими мидлега (истшшо/ложио).
h) Классы Alert. Form, Screen и List являются конкретными классами пакета
высокоуровневого пользовательского интерфейса MIDP (истинно/ложно).
1) Объект javax.microedition.ledui.Command инкапсулирует действие, которое будет
выполняться объектом, принимающим объект Command (истинно/ложно),
j) Поле заголовка Set-Cookie сеанса HttpSession содержит информацию о сеансе
(истинно/ложно).
Ответы на упражнения для самоконтроля
5.1. а) клиентского (верхнего), среднего, информационного (нижнего), о) XHTML, WAP,
cHTML, обычного текста, с) HttpSession. d) Connected Limited Device Configuration, e)
Mobile Information Device Profile, f) коплект мидлетов. g) .jad. h) startApp, panseApp,
destroyApp. i) Displayable. j) Singleton.
5.2. а) Истинно, b) Ложно. Заголовок User-Agent содержит информацию о типе клиента,
запросившего данные с сервера, с) Ложно. Новый объект DocnmentBnilder создает
статический метод newDocumentBuilder класса DocumentBuilderFactory. Класс Docu-
mentBnilder создает новые объекты Document, d) Ложно. Сервлет TipTestServlet
использует XSLT для трансформации XML-документов теста Tip-Test н правильное
содержимое только для клиентов Internet Explorer, WAP и i-mode. e) Ложно. В состан
CLDC J2ME входят пакеты java.io, java.lang и java.util. f) Истинно, g) Истинно, h)
Ложно. Классы Alert, Form, TextBos и List являются конкретными классами пакета
высокоуровневого пользовательского интерфейса. Класс Screen является абстрактным
классом, который расширяют указанные выше классы, i) Истинно, j) Истинно.
Упражнения
5.3. Чтобы повысить степень управляемости и расширяемости сервлета TipTestServlet, мы
храним имя драйвера и URL базы данных в элементе <init-param> в документе
web.xral. Назовите 3 другие константы, которые можно хранить в элементе
<init-param> для сервлетов WelcomeServlet и TipTestServlet.
5.4. Расширьте список возможных нариантов ответов на вопрос теста Tip-Test с четырех до
пяти.
5.5. Метод getResultTable класса TipTestServlet создает двухмерный строковый массив
для представления результирующего множества. Однако в связи с тем, что размер
этого массива является жестко заданным (семь строк на пять столбцов), этот массив не
сможет хранить дополнительные строки, если мы добавим а базу данных еще одну
рубрику советов. Модифицируйте сералет TipTestServlet, чтобы ои мог работать с любым
числом рубрик советов из базы данных. Создайте прокручиваемое (scrollable)
результирующее множество ResultSet, чтобы иметь аозможность согласованно задавать
количество строк в таблице ResultSet и размер строкового массива.
5.6. В строках 29-38 класса TipTestMlDlet задается URL базы сервлетов и URL сервлета
WelcomeServlet. Подобный подход может создать проблемы, если администратор сети
изменит какой-либо из этих URL. Исходя из подобных соображений, желательно, чтобы
файл дескриптора приложения содержал базовый URL сервлетов, и чтобы пользователь
MIDP-устройетва задавал URL сервлета WelcomeServlet с клавиатуры MlDP-устройства.
Разработка приложений для беспроводной связи на базе Java и J2ME 305
a) Запишите код для базового URL сервлетов (http://localhost:8080/advjhtpl/) в файл
дескриптора приложения, введя
SerVlet-ORL:http://localhost:80eO/advjhtpl/
в конец jad-файла мидлета TipTestMIDlet. В теле мидлета TipTestMIDlet используйте
метод getAppProperty класса MIDlet, возвращающий URL сералета. (Метод
getAppProperty принимает в качестве параметра строку С именем тега свойства, например,
" Servlet-URL.", и возвращает строку, ассоциированную с этим свойствам.)
b) Предоставьте пользователю возможность задавать URL сервлета TipTestServIet-
Модифицируйте конструктор класса TipTestMIDlet, чтобы создавать основной экран
mainScreen (строка 51) как объект TextBox: подкласс класса Screen, который
позволяет пользователю вводить текст. Конструктор TextBox принимает четыре параметра:
1) строку, представляющую заголовок текстового ноля TextBox; 2) строку,
представляющую начальное содержимое текстового поля TextBox; 3) целое число,
представляющее максимальное количество символов, которые могут содержаться в текстовом
поле TextBox; 4) целое число в виде ограничителя, задающего требуемый формат вво^
да; например, если вы хотите, чтобы пользователь ввел номер телефона, в качестве
ограничителя следует указать TextField.FHQNENUMBER. Нам яужно, чтобы
пользователь вводил URL, поэтому в качестве ограничителя следует указать TextField.URL.
Используйте метод getStrinjf класса TextBox, возвращающий содержимое текстового
поля (введенное пользователем), чтобы сохранить URL сервлета WelcomeServlet,
когда пользователь нажимает программную кнопку Select.
5.7. Модифицируйте мидлет TipTestMIDlet, чтобы дать возможность пользователю также
осуществлять выход из приложения из экрана ответа на вопрос теста Tip-Test, а не
только принимать следующий вопрос теста. Создайте объект Command, который
использует константу Command.EXIT, и зарегистрируйте answerSereen в качестве
слушателя для этого объекта Command. (Подсказка. Переопределите метод destroyApp,
чтобы вызывать метод notifj'Destroy класса javax.microedition.inidlet.MrDlet.)
Литература
Leng, Y. and Zhu, J., Wireless Java™ Programming with J2ME. SAMS. Indiana; 2001.
Giguere, E., Java 2 Micro Edition; Professional Developer's Guide. John Wiley & Sons.
2000.
Knudsen, J. Wireless Java: Developing with Java 2, Micro Edition. Apress, California;
2001.
Kroll, M. And Haustein, S., Java 2 Micro Edition (J2ME) Application Development.
SAMS. Indiana; 2001.
Morrison, M. Sams Teach Yourself Wireless Java with J2ME in 21 Days. SAMS. Indiana;
2001.
Riggs, R., Taivalsaari, A., and VatldenBrink, M,, Programming Wireless Devices with
Java"* 2 Platform, Micro Edition. Addison-Wesley. Boston; 2001.
6
Сеансовые компоненты EJB
и распределенные
транзакции
Цели
• Получить представление о EJB '
как о компонентах
бизнес-логики.
• Узнать о преимуществах
и недостатках сеансовых
компонентов EJB с состоянием
и без состояния.
• Понять роль JNDI
в корпоративных
приложениях Java.
• Получить представление
о распределенных
транзакциях.
• Узнать о преимуществах
и недостатках транзакций,
управляемых контейнером,
и транзакций, управляемых
компонентом.
Только в дороге начинаешь
ценить домашний уют и острее
чувствовать радость оттого,
что ты снова дома.
Генри Дэвяд Торо
Молодость была бы идеальной
порой жизни, если, бы наступала
чуть попозже.
Герберт Генри Эскит
Мы не можем порождать события. Наше
дело — разумно усовершенствовать их.
Сэмюэл Адаме
зов
Глава 6
_ ■■ .--уая* ■--.■■кг- •■" --:э-ь-
ЬбЙ., i Введение^!
Е.1.£Удаленнь№
-..! *> 6;2.1.^Удал5нныи интерфейс - -■■*"■ -*■- : ^Ь *
J--»
■»
6:3. Сеансовые|компонёнты ■ Т£ ГДШ^. ...i
""."'., .ь43.1\>Хеансовыё компоненты EJB ссостояни
'"" Sr' 4-3.2;''Развертывант сеаi1сзды)£кампоне1itc
■^ 6.33. Сеансовые компоненты"EJB'без.сосгоя
":.6.4. ,Е]В-тоаНзакцииЧ;* ..> >'%„-«.-■■.-$,;■' fr' -':'гЙ-" ~- I
-i .-Я4"*-'J4E •й"ч5г»У'1"' ' f'»***1!. -a ■?■•-■- 'Sib" ' -»*.1"- -■* ' . ' .'3
. .. D.d-2- Р»Шши»иий mauMKi luSV'unnaiinauuBu из ппмив ?
6.1. Введение
В предыдущих главах мы познакомились с применением серьлетов Java и
серверных страниц JavaServer Pages для реализации бизнес-логики и логики
представления данных в многоуровневых приложениях. В этой главе мы познакомимся
с компонентами Enterprise JavaBeans (EJB), которые предоставляют
компонентную модель для построения бизнес-логики п корпоративных приложениях Java.
В данной главе будут рассмотрены сеансовые EJB в двух формах: с состоянием
и без состояния. Мы также познакомимся с поддержкой EJB-компонентами
распределенных транзакций, которые помогают обеспечить целостность данных на
серверах баз данных и серверах приложений. Изучив эту главу, вы сможете
разрабатывать сеансовые EJB-компоненты с состоянием и без состояния. Вы также
сможете создавать EJB-компоненты, которые используют средства поддержки
распределенных транзакций J2EE для атомарного обновления данных при наличии
нескольких баз данных.
6.2. Обзор технологии EJB
Каждый компонент EJB состоит из удаленного интерфейса, собственного
интерфейса и реализации EJB-компонента. Удаленный интерфейс определяет
бизнес-методы, которые клиент EJB может вызывать. Собственный («домашний»)
интерфейс предоставляет методы create для создания новых экземпляров EJB, ме-
Сеансовые компоненты EJB и распределенные транзакции
309
годы поиска (finder) для нахождения экземпляров EJB и методы remove для
удаления экземпляров EJB. Реализация EJB-компонента определяет бизнес-методы,
объявленные в удаленном интерфейсе, и методы создания, удаления и поиска
собственного интерфейса. Контейнер EJB предоставляет окружение выполнения
EJB-компонента и средства управления его жизненным циклом.
Спецификация J2EE определяет шесть ролей для программистов, реализующих
корпоративные системы. Каждая из ролей ответственна за создание определенной
части распределенного приложения. Поставщик компонентов EJB реализует
классы Java для EJB. Сборщик приложения формирует компоненты приложения
из EJB, реализованных поставщиком EJB-компонентов. Администратор
развертывания принимает компоненты приложения, предоставленные сборщиком
приложения, и развертывает приложение в контейнере EJB, обеспечивая при этом
соблюдение всех зависимостей. Поставщик сервера EJB и поставщик контейнера
EJB реализуют сервер приложений, предназначенный для развертывания
приложений J2EE. Сервер приложений обычно содержит контейнер EJB и контейнер
сервлетов и предоставляет сервисы, такие как служба каталогов JNDI, пулы
соединений с базами данных, средства интеграции с распределенными системами
и средства управления ресурсами. Один и тот же разработчик или группа
разработчиков при конструировании и развертывании распределенного приложения могут
выступать одновременно в нескольких ролях. Для получения более подробной
информации о различных ролях при построении приложений J2EE обратитесь к
списку ресурсов в разделе 6.5.
6.2.1. Удаленный интерфейс
Удаленный интерфейс EJB объявляет бизнес-методы, которые клиент
EJB-компонента может вызывать. Удаленный интерфейс должен расширять интерфейс
javax.ejb.EJBObject. Контейнер EJB генерирует класс, который реализует
удаленный интерфейс. Этот сгенерированный класс реализует методы интерфейса
EJBObject и делегирует вызовы бизнес-методов реализации EJB-компонента
(см, раздел 6.2.3). В таблице на рис. 6.1 описаны методы интерфейса EJBObject.
В каждом методе удаленного интерфейса должно быть объявлено, что он
возбуждает исключение Remote Exception. Каждый метод также может генерировать
исключения, специфичные для приложения, например, IHegalArgnmentExcepti-
оп, если предоставленный параметр не отвечает определенному критерию.
6.2.2. Собственный интерфейс
Собственный («домашний») интерфейс EJB объявляет методы для создания,
удаления и поиска экземпляров EJB. Собственный интерфейс должен расширять
интерфейс javax.ejb.EJBHome. Контейнер EJB обеспечивает реализацию
собственного интерфейса. В зависимости от типа EJB-компонента (т.е. сеансовый
компонент или компонент-сущность) контейнер будет вызывать методы реализации
EJB, которые соответствуют методам создания (create), удаления (remove) и
поиска (finder) собственного интерфейса. Методы поиска дают возможность клиентам
обнаруживать определенный экземпляр EJB-компонента. В таблице на рис. 6.2
описаны методы интерфейса EJBHome.
Метод
getEJBHomu
getHandle
Описание
Воззрзщает интерфейс EJBHome длр объекта EJBObject.
Воззрзщэет ссылку Handle для объекта EJBObject. Handle представляет
собой неишрнну<г), се реализуемую (Serial liable) ссылку на объект
EJBObject.
310
Глава 6
Метод
getPrimaryKey
isIdentical
remove
Описание
Возвращает первичный ключ объекта EJBObject, если EJBObject является
компонентом -сущностью EJB.
Возвращает булево значение, указывающее, идентичен ли параметр
EJBObject текущему объекту EJBObject.
Удаляет объект EJBObject.
Рис. 6.1. Методы интерфейса javax.ejb.EJBObject
Метод
getEJBMetaData
getHoraeHandle
reiBove
'■ ■■"■ " ■ =*^—
Описание
Возвращает объект EJBMetaData, который предоставляет информацию
о EJ8-компоненте, например, класс его собственного интерфейса.
а также информирует, является пи он сеансовым EJ В -компонентом.
Возвращает ссылку Handle для интерфейса EJBHome.
Удаляет объект EJBObject, идентифицируемый по заданной ссылке
Handle или объекту первичного ключа.
Рис. 6,2. Методы интерфейса javax.ejb.EJBHome
6.2.3. Реализация EJB
Реализация EJB-компонента определяет бизнес-методы, объявленные в
удаленном интерфейсе EJB, и методы создания, удаления и поиска, объявленные в
собственном интерфейсе EJB. Сеансовые EJB также должны реалиаовывать интерфейс
javax.ejb.SessionBean. Подробнее интерфейс SessionBean мы обсудим в разделе 6.3.
6.2.4. Контейнер EJB
Контейнер EJB управляет взаимодействиями с клиентом EJB-компонента,
вызовами методов, транзакциями, безопасностью, исключениями и т.д. Клиент
EJB-компонента не взаимодействует с EJB напрямую. Клиенты осуществляют
доступ к контейнеру EJB для получения удаленных ссылок на экземпляры
EJB-компонента. Когда клиент вызывает бизнес-метод EJB-кошгонента, вызов сначала
поступает контейнеру EJB, который затем делегирует вызов бизнес-метода
реализации EJB-компонента.
Контейнер EJB также управляет жизненным циклом своих EJB-компонентов.
Контейнеры EJB обычно организуют пулы экземпляров EJB-компонентов для
повышения производительности. Имея пул неактивных экземпляров
EJB-компонентов, контейнер EJB может увеличить производительность, избегая затрат,
связанных с созданием новых экземпляров EJB-компонентов для каждого клиентского
запроса. Контейнер EJB просто активирует экземпляр из пула и выполняет
необходимую инициализацию. Контейнер EJB также может создавать новые
экземпляры EJB-компонентов и удалять имеющиеся экземпляры. Кроме того, контейнер
EJB предоставляет расширенные сервисы для EJR-сущностей (см. главу 7).
6.3. Сеансовые компоненты
Экземпляр сеансового компонента EJB выполняет обработку бизнес-логики для
конкретного клиента. Сеансовые EJB-компоненты могут манипулировать данными
в базе данных, но, в отличие от EJB-сущностей (глава 7), сеансовые EJB-комяоненты
Сеансовые компоненты EJB и распределенные транзакции
311
не являются сохраняемыми (персистентными) и не представляют информацию из
базы данных непосредственно. Экземпляры сеансовых EJB-компоненгов теряются,
если имеет место сбой в контейнере EJB. Существует два типа сеансовых
компонентов EJB: с состоянием (stateful) и без состояния (stateless). В разделе 6.3.1
рассказывается о сеансовых компонентах EJB с состоянием, а в разделе 6.3.3 — о сеансовых
компонентах EJB без состояния. В разделе 6.3.2 обсуждаются проблемы
развертывания сеансовых компонентов EJB средствами эталонной реализации J2EE 1.2.1.
6.3.1. Сеансовые компоненты EJB с состоянием
Сеансовые компоненты EJB с состоянием сохраняют информацию о состоянии
между вызовами бизнес-методов. Например, сеансовый EJB-компонент с
состоянием хранит информацию о содержимом магазинной тележки покупателя, пока
покупатель делает покупки в режиме онлайн. Сеансовые компоненты EJB с
состоянием предоставляют бизнес-методы для добавлении и удаления товаров из
магазинной тележки. Каждый раз, когда покупатель добавляет товар в магазинную
тележку, информация о товаре, такая как его цена и количество, будут сохранены
в сеансовом компоненте EJB с состоянием. Бели покупатель покидает сайт или
прекращает сеанс каким-либо другим способом, информация о содержимом
магазинной тележки теряется.
Интерфейс InterestCalcalator (рис. 6.3) представляет собой удаленный
интерфейс для сеансового компонента EJB с состоянием, который вычисляет простой
процент. Методы getPrincipal (строки 15-16), set Interest Rate (строки 19-20)
и setTerm (строки 23-24) устанавливают значения основной суммы (капитала),
процентной ставки и срока хранения, необходимые для расчета простых
процентов. Метод getBalance (строка 27) вычисляет общий баланс (остаток на счету)
после начисления процентов для заданного срока хранения. Метод
getlnterestEarned (строка 30) вычисляет сумму начисленных процентов.
Клиенты EJB-компонента InterestCalculator могут вызывать только те методы,
которые объявлены в удаленном интерфейсе InterestCalcalator. Контейнер EJB
для EJB-компонента InterestCalculator будет создавать класс, который реализует
удаленный интерфейс InterestCalcalator, включая методы, объявленные в
интерфейсе javax.ejo.EJBObject. При вызове клиентом метода удаленного интерфейса
InterestCalcalator контейнер EJB вызывает соответствующий метод в реализации
EJB-компонента InterestCalculatorEJB (рис. 6.5). При вызове клиентом метода,
объявленного в интерфейсе javax.ejb.EJBObject, контейнер вызывает
соответствующий метод класса, сгенерированного контейнером EJB.
1 // InterestCalculator.Java
2 // InterestCalculator - удаленный интерфейс для
3 // EJB-компонента InterestCalculator.
4 package com.deitel.advjhtpl.ejb.session.stateful.ejb;
5
6 // Базовые библиотеки Java
7 import java.rmi.RemoteException;
8
9 // Стандартные расширение Java
10 import javax.ejb.EJBObject;
11
12 public interface InterestCalculator extends EJBObject {
13
14 // задание основной сукны
15 public void setPrincipal( double amount )
16 throws RemoteException;
312
Глава 6
17
18 // задание процентной ставки
19 public void setlnterestRate( double rate )
20 throws RemoteExcaption;
21
22 // задание срока хранения в годах
23 public void setTerm{ int years )
24 throws RemoteException;
25
26 // получение остатка на счету (баланса)
27 public double getBalance0 throws RemoteException;
28
29 // получение суммы начисленных процентов
30 public double getlnterestEarnedt) throws RemoteException;
31)
Рис. 6.З. Удаленный интерфейс InterestCalculator для вычисления простого процента
Интерфейс InterestCalculatorHome (рис. 6.4) представляет собой собственный
интерфейс для EJB-компонента InterestCalculator. Интерфейс
InterestCalculatorHome предоставляет метод create (строки 15-16) для создания экземпляров
EJB-компонента InterestCalculator. Когда клиент вызывает метод create
интерфейса InterestCalculatorHome, контейнер EJB вызывает метод ejbcreate класса
InterestCalculatorEJB (рис. 6.5). Собственный интерфейс может объявлять нуль
или более методов create. Например, мы можем объявить дополнительный метод
create, принимающий параметр типа double, который инициализирует основную
сумму (principal), используемую для вычисления простого процента.
Класс InterestCalculatorEJB (рис. 6.5) реализует бизнес-методы, объявленные
в удаленном интерфейсе InterestCalculator. В строке 12 InterestCalculator
реализует интерфейс SessionBean. Это свидетельствует о том, что InterestCalculatorEJB
представляет собой сеансовый компонент EJB. В строках 17-19 объявляются
переменные, которые хранят состояние компонента EJB между вызовами
бизнес-методов EJB-компонента. В информацию о состоянии входит основная сумма principal,
величина процентов interestRate и срок хранения term. Метод setPrincipal
(строки 22-25) устанавливает основную сумму principal и сохраняет значение в
переменной состояния principal. Метод setlnterestRate (строки 28-31) устанавливает
переменную состояния interestRate для вычисления процентов. Метод setTerm
(строки 34-37) устанавливает срок хранения term, для которого будет
начисляться процент. Метод getBalance (строки 40-44) использует формулу
а = р (1 + к)"
где
р — основная сумма (капитал)
г — процент годовых (например, 05 для 5%)
п — количество лет
а — сумма депозита на конец n-го года.
для вычисления баланса (т.е. суммы депозита). Метод getlnterestEarned (строки
47-50) вычисляет сумму процентов, вычитая основную сумму principal из
балансовой суммы, вычисляемой методом getBalance.
1// InterestCalculatorHome.Java
2 // InterestCalculatorHome - собственный интерфейс для
3 // EJB-компонента InterestCalculator.
4 package com.deitel.advjhtpl.ejb.session.stateful.ejb;
5
Сеансовые компоненты EJB и распределенные транзакции
313
6 // Базовые библиотеки Java
7 import 5ava.rmi.RemoteException;
8
9 // Стандартные расширения Java
10 import javax.ejb.*;
11
12 public interface InterestCalculatorHome extends EJBHome {
13
14 // создание EJB-компокента InterestCalculator
15 public InterestCalculator created throws RemoteEKC^ption,
16 CreateException;
17 }
Рис. 6.4. Собственный интерфейс InterestCalculatorHome для создания EJB-компонентов
InterestCalculator
1 // InterestCalculatorEJB.Java
2 // InterestCalculator - сеансовый компонент EJB с
3 // состоянием для вычисления простого процента.
4 package com.deitel.advjhtpl.ejb.session.stateful.ejb;
5
6 // Базовые библиотеки Java
7 import java.util.*;
8
9 // Стандартные расширения Java
10 import javax.ejb.*;
11
12 public class InterestCalculatorEJB Implements SessionBean {
13
14 private SessionContext sessionContext;
15
16 // переменные состояния
17 private double principal;
18 private double interestRate;
19 private int term;
20
21 // задание основной суммы
22 public void setPrincipal( double amount )
23
24
25
26
27 // задание процентной ставки
28 public void setlnterestRate( double rate )
29
30
31
32
33 // задание срока хранения в годах
34 public void setTerm( int years )
35
36
37
38
39 // получение баланса спета
principal = amount;
interestRate = rate;
term = years;
314 Глава 6
40 public double getBalance{)
41 {
42 // вычисление простых процентов
43 return principal * Math.powf 1.0 + interestRate, term );
44 }
45
46 // получение суммы начисленных процентов
47 public double getIntereetEarned()
4B {
49 return getBalance() - principal;
50 }
51
52 // задание контекста SessionContext
53 public void setSeasionContext( SessionContext context )
54 {
55 SessionContext = context;
56 }
57
58 // создание экземпляра InterestCalculator
59 public void e]bCreate() {)
60
61 // удаление экземпляра InterestCalculator
62 public void ejbRemove() {)
63
64 // пассивация экземпляра InterestCalculator
65 public void ejbPassivate() {}
66
67 // активация экземпляра InterestCalculator
68 public void ejbActivate(> {}
69 j
Рис. 6.5. Реализация InterestCalculatorEJB удаленного интерфейса InterestCalculator
Метод set SessionContext (строки 53-56) представляет собой метод обратного
вызова, определенный в интерфейсе SessionBean. Контейнер EJB вызывает метод
setSessionContext после создания экземпляра компонента EJB. Интерфейс
SessionContext расширяет интерфейс EJBContext, который определяет методы для
получения информации о контейнере EJB.
Типичная ошибка программирования 6.1
Возврат ссылки как указателя this из метода или передача ссылки
методу через указатель this для EJB-компонента не допускается. Для
получения ссылки на текущий объект следует использовать метод getEJB-
Object интерфейса SessionContext или EntityContext.
Когда клиент вызывает метод create в собственном интерфейсе, контейнер EJB
вызывает метод ejbCreate (строка 59). Реализация EJB-компонента должна
предоставить метод ejbCreate для каждого метода create, объявленного в собственном
интерфейсе. Методы ejbCreate должны иметь такое же количество и такие же
типы параметров, что и соответствующие методы create. Методы ejbCreate также
не должны ничего возвращать (void). Например, если интерфейс InterestCalcu-
latorHome определяет метод create, который принимает параметр типа double для
суммы principal, реализация EJB должна определять метод ejbCreate, который
принимает параметр типа double. EJB-компонент InterestCalculator имеет пустую
реализацию метода ejbCreate, поскольку для этого EJB-компонента никакой
инициализации не требуется.
Сеансовые компоненты EJB и распределенные транзакции 315
Контейнер EJB вызывает метод ejbRemove (строка 62) в ответ на вызов метода
remove в собственном интерфейсе. Контейнер EJB также может вызывать метод
ejbRemove, если сеанс прекращает свое существование по причине длительного
бездействия. Метод ejbRemove должен освобождать ресурсы, используемые
EJB-компонентом.
Контейнер EJB вызывает метод ejbPassivate (строка 65), если контейнер
определяет, что EJB-компонент больше не нужно хранить в памяти. Алгоритм,
который контейнер EJB использует, чтобы определить, когда следует пассивировать
компонент EJB, зависит от особенностей конкретного сервера приложений.
Многие серверы приложений применяют политику замещения компонента с
наиболее давним использованием (least recently used policy )t в соответствии с которой
пассивируются EJB-компоненты, к которым за последнее время не было
обращений. Когда контейнер EJB пассивирует EJB-компонент, контейнер сериализует
состояние EJB-компонента и удаляет EJB-компонент из памяти.
Контейнер EJB вызывает метод ejbActivate (строка 68) для восстановления
экземпляра EJB-компонента, который контейнер ранее пассивировал. Контейнер
EJB активирует экземпляр EJB-компонента, если клиент, ассоциированный
с этим экземпляром EJB-компонента, вызывает его бизнес-метод. Контейнер EJB
прочитывает информацию о состоянии, которую он сохранил во время
пассивации, и восстанавливает экземпляр EJB-компонента в памяти.
Общая методическая рекомендация 6.1
Используйте ключевое слово transient, чтобы отмстить переменные
экземпляра, которые контейнер EJB не должен сохранять и
восстанавливать при активации и пассивации.
InterestCaleulatorClient (рис. 6,6) — приложение, которое использует
EJB-компонент IntcrestCalculator для вычисления простого процента. В строке 25
объявляется ссылка на компонент IntcrestCalculator, который приложение
InterestCaleulatorClient использует для вычисления простого процента. Метод
createlnterestCalculator (строки 71-108) создает экземпляр EJB-компонента
InterestCalculator, который будет использоваться в приложении. Клиентское
приложение должно использовать каталог JNDI для поиска собственного интерфейса
для EJB-компонента. В строке 77 создается интерфейс InitialContext, который
представляет собой интерфейс в каталоге JNDI. Интерфейс InitialContext
предоставляет контекст идентификации, который устанавливает отношения между
именами (например, «InterestCalculator») и объектами, такими как EJB-компо-
ненты. В строках 80-81 используется метод lookup класса InitialContext для
извлечения удаленной ссылки типа Object на интерфейс IntcrestCalculator.
Параметр типа String, передаваемый методу lookup, представляет собой имя, которое
выделено для EJB-компонента в каталоге JNDI.
1 // InterestCaleulatorClient.java
2 // InterestCaleulatorClient - GUI для взаимодействия с
3 // EJB-компокентои IntcrestCalculator.
4 package com.deitel.advjhtpl.ejb.session.stateful.client;
5
6 // Базовые библиотеки Java
7 import java.awt.*;
8 import java.awt.event.*;
9 iinport j ava. rmi. * ;
10 import j ava.text.*;
11 import java.util.*;
12
13 // Стандартные расширения Java
Глава
14 import javax.sving.*;
15 import javax. nni.*;
16 import iavax.naming.*;
17 import javax.ejb.*;
18
19 // Библиотеки Deitel
20 import com.dei tel.advjhtpl.ejb.session.etateful.ejb.*;
21
22 public class ItiterestCalculatorClient extends JFrame {
23
24 // удаленная ссылка InterestCalculator
25 private InterestCalculator calculator;
26
27 private JTextField principalTextField;
28 private JTextField rateTextField;
29 private JTextField termTextField;
30 private JTextField balanceTextField,-
31 private JTextField interestEarnedTextField;
32
33 // конструктор InterestCalculatorClient
34 public InterestCalculatorClient{)
35 {
36 super{ "Stateful Session EJB Example" };
37
38 // создание объекта InterestCalculator для вычисления процентов
39 createlnterestCalculator();
40
41 // создание поля JTextField для ввода основной суммы
42 createPcincipalTextFieldO;
43
44 // создание поля JTextField для ввода процентной ставки
45 createRateTextField[);
46
47 // создание поля JTextField для ввода срока хранения
48 createTennTextFieldO ;
49
50 // создание нерадахтируемых полей JTextField•для
51 // отображения баланса и начисленных процентов
52 balanceTextField = new JTextField{ 10 );
53 balanceTextField.setEditable( false );
54
55 interestEarnedTextField = new JTextField( 10 );
56 interestEarnedTextField.setEditablet false );
57
58 I) размещение компонентов GUI
59 layoutGOIO;
60
61 // добавление слушателя WindowListener для удаления
62 // экземпляре^ EJB, когда пользователь -Закрывает окно
63 addHindowListener( getWindowListener() );
64
65 setSize( 425, 200 J;
66 setvisible( true );
67
68 } // конец конструктора interestCalculatorClient
Сеансовые компоненты EJB и распределенные транзакции
317
70 // создание] экземпляра InterestCalculator
71 public void creat.elnterestCalcula.tor ()
72 {
73 Ii поиск интерфейса. interestCalculatorHome и создание
74 // BJB-компонента InterestCalculator
75 try \
76
77 InitialContext initialContext = new InitialСontext();
78
79 // поиск EJB-компонента InterestCalculator
80 Object homeObject =
81 initialContext.lookup( "InterestCalculator" );
82
83 // получение интерфейса InterestCalculatorHome
84 InterestCalculatorHome calculatorHome =
85 ( InterestCalculatorHome )
86 PortableRemoteObject.narrow( homeObject,
87 InterestCalculatorHome.class );
88
89 // создание экземпляра EJB-компокента InterestCalculator
90 calculator = calculatorHome.create();
91
92 } // конец оператора try
93
94 // обработка исключения, если ЕОВ~компонент
IntarestCalculator не найден
95 catch ( NamingException namingException ) {
96 namingException.printStacxTrace();
97 }
98
99 // обработка исключения при создании EJB-жомпонента
InterestCalculator
100 catch { RemoteException remoteException ) {
101 remoteException.printStackTrace();
102 }
103
104 // обработка исключения при создании EJB-компонента
Intere stCalculator
105 catch ( CreateException createException ) {
106 createException.printstackTraca();
107 }
108 } // конец метода createinterestCalculator
109
110 // создание поля JTextField для ввода основной суммы
111 public void createFrincipalTextFieldO
112 {
113 principalTextField = new JTextField( 10 };
114
115 principalTextField.addActionListener(
116 new ActionListener{) {
117
118 public void actionPerformed( ActionBvent event )
119 {
120 // задание основной суммы в InterestCalculator
121 try {
122 double principal = Double.parseDouble(
318
Глава 6
123 principalTextField.getText() );
124
125 calculator.setPrincipal( principal );
126 }
127
128 // обработка исключения яри задании основной суммы
129 catch ( RemoteException remoteException ) {
130 remoteException.printStackTrace();
131 }
132
133 // обработка при неверном формате суммы
134 catch ( HumberForm&tException
135 nuraberFormatExeeption ) {
136 miiuberFormatException.printStackTrace () ;
137 }
138 }
139 )
140 ); // конец метода addActionListener
141 } // коней метода cxeatePrincipalTextField
142
143 // создание поля JTextField для ввода процентной ставки
144 public void createRateTextField()
145 {
146 rateTextField = пен JTextField{ 10 };
147
148 rateTextField.addActionListener(
149 new Actionalstener() {
150
151 public void actionPerformed( ActionEvent event }
152 {
153 // задание процентной ставки в InterestCalculator
154 try {
155 double rate = Double.parseDouble(
156 rateTextField.getText() );
157
158 // преобразование формата в процентный
159 calculator.setlnterestRate( rate / 100.0 );
160 >
161
162 // обработка исключения при задании процентной ставки
163 catch ( RemoteException remoteException ) {
164 remoteException.printStackTrace();
165 }
166 )
167 }
168' ) ; // конец метода addActionListener
169 } // конец метода createRateTextField
170
171 // создание поля JTextField для ввода срока хранения
172 public void createTermTextField0
173 {
174 termTextField = new JTextField( 10 );
175
176 termTextField.addActionListener[
177 new ActionListener() {
178
Сеансовые компоненты EJB и распределенные транзакции
319
179 public void actionFerformed( ActionEvent event )
1B0 {
181 // задание срока хранения в InterestCalculator
182 try {
1S3 int term = Integer.parselnt(
184 termTextField.getTextO );
185
186 calculator,setTenn( term 1;
187 }
188
189 // обработка исключения при задании срока хранения
190 catch ( RemoteException remoteException ) {
191 remoteException.printStackTrace();
192 }
193 }
194 }
195 ); // конец addActionListener
196 } // конец метода getTermTextField
197
198 // получение кнопки JButton для запуска вычислений
199 public JButton getCalculateButtonO
200 (
201 JButton calculateButton = new JButton( "Calculate" );
202
203 calculateButton.addActionListener(
204 new ActionListener() {
205
206 public void actionFerformed( ActionEvent event )
207 {
208 // использование InterestCalculator для вычисления
процентов
209 try {
210
211 // получение баланса и начисленных процентов
212 double balance = calculator. getBalanceO;
213 double interest =
214 calculator,getinterestEarned(};
215
216 NumberFormat dollarFormatter =
217 NrnnberFormat.getCurrencylnstance(
218 Locale.US );
219
220 balanceTextField.setText(
221 dollarFormatter.format( balance ) );
222
223 interestEarnedTextField.setText(
224 dollarFormatter.format< interest ) );
225 }
226
227 // обработка исключения при вычислении процентов
228 catch ( RemoteException remoteException ) {
229 remoteException.prii\t£tacfcTrace(>;
230 }
231 } // конец метода actionPerformed
232 }
233 ) ; // конец addActionListener
320
Глава 6
234
235 return calculateButton;
236
237 ) // конец метода getCalculateButton
238
239 // размещение компонетов GUI в окне JFrame
240 public void. layoutGUI ()
241 {
242 Container contentPane = getContentPane();
243
244 // размещение компонентов пользовательского интерфейса
245 JPanel inputPanel a new JPanel[ new GridLayout( 5, 2 ) );
246
247 inputPanel.add( new JLabel( "Principal" ) );
248 inputPanel.add( principalTextField );
249
250 inputPanel.add( new JLabel( "Interest Bate (%)" ) );
251 inputPanel.add( rateTextField );
252
253 inputPanel.add( new JLabel( "Term (years)" > );
254 inputPanel.add( ten&TextField );
255
256 inputPanel.add( new JLabel( "Balance" ) );
257 inputPanel,add( balancaTextField );
258
259 inputPanel.add( new JLabel( "Interest Earned" ) );
260 inputPane.add{ interestEarnedTextField ); 1
261
262 // добавление панели inputPanel в панель contentPane
263 contentPane.add( inputPanel, BorderLayout.CENTER ),-
264
265 // создание панели JPanel для кнопки calculateButton
266 JPanel controlPanel = new JPanel( new FlowLayoutO );
267 controlPanel.add( g/etCalculateButton () }:
268 contentPane.add( controlPanel, BorderLayout.SOOTH );
269 }
270
271 // получение слушателя WindowListener для выхода Из приложения
272 public WindowListener gotWindowListener{)
273 {
274 return new WindowAdapter() (
275
276 public void windowClosing( WindowEvent event )
277 {
278 // проверка, не есть ли значение null
279 if ( calculator.equals( null ) ) {
280 System.exit( -1 );
281 )
282
283 else {
284 // удаление экземпляра InterestCalculator
285 try {
286 calculator.remove();
287 }
28S
Сеансовые компоненты EJB и распределенные транзакции
321
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312 >
}
// обработка исключения при удалении
InterestCalculatox
catch { RemoveException removeException ) {
removeExceptiort.printStackTrace (> ;
System.exit( -1 );
}
// обработка исключения при удалении
InterestCalculator
catch ( RemoteExceptlon remoteException ) {
remoteExeeption.printStaclcTrace () ;
System.exit( -1 );
}
System,exit( 0 );
};
> // конец метода getWindowListener
// выполнение приложения
public static void main( String[] arga )
{
new InterestCalculatorClient();
Рис. 6.6. Приложение InterestCalculatorClient для взаимодействия с LIB-компонентом
InterestCakulator
В строках 84-87 используется метод narrow класса PortabteRemateObjeet для
преобразования удаленной ссылки в удаленную ссылку типа
InterestCalculatorHome. Это стандартный метод для приведения удаленной
ссылки к надлежащему типу интерфейса при использовании технологии RMI — ПОР.
RMI — ПОР дает возможность объектам RMI взаимодействовать с компонентами
CORBA, которые обмениваются информацией через протокол Internet Inter-Orb
Protocol (ПОР). CORBA представляет собой независимую от языка
программирования инфраструктуру для построения распределенных систем. Чтобы обеспечить
взаимную совместимость между EJB-компонентами и компонентами CORBA,
компоненты EJB используют технологию RMI — ПОР.1
Технологии ССЖВА и KMI — ПОР рассматриваются в книге «Технологии
программирования на Java 2. Книга 2*. — Прим. ред.
322
Глава 6
В строке 90 вызывается метод create интерфейса InterestCalculatorHome для
создания нового экземпляра EJB-компонента InterestCalculator. Этот экземпляр
EJB-компонента InterestCalculator является эксклюзивным для клиента,
который его создал, поскольку он представляет собой сеансовый компонент EJB.
Экземпляр EJB-компонента InterestCalculator будет хранить информацию о
состоянии, поскольку он является сеансовым компонентом EJB с состоянием. Метод
create возвращает удаленную ссылку на вновь созданный экземпляр
EJB-компонента InterestCalculator. В строке 90 эта удаленная ссылка присваивается
переменной экземпляра calculator. Экземпляры класса InterestCalculatorCHent могут
использовать эту удаленную ссылку для вызова бизнес-метода, определенного
в удаленном интерфейсе InterestCalculator.
В строках 95-97 перехватывается исключение NamingException, которое
указывает на наличие проблемы при доступе к каталогу JNDI. Если контекст
InitialContext не может быть создан, конструктор InitialContext возбуждает
исключение NamingException. Метод lookup также возбуждает исключение Na-
mingException, если имя, передаваемое в качестве параметра, не может быть
найдена в каталоге JNDI.
В строках 100—102 перехватывается исключение RemoteException. Если а
процессе коммуникационного взаимодействия с контейнером EJB возникает ошибка,
метод create возбуждает исключение RemoteException. В строках 105-107
перехватывается исключение CreateException. Метод create возбуждает исключение
CreateException, если имеет место ошибка при создании EJB-компонента.
Метод createPrincipalTextField (строки 111-141) создает текстовое поле
JTextField для ввода пользователем основной суммы principal. Анонимный внутренний
класс ActionListener (строки 116-139) использует статический метод parseDouble
класса Double для получения величины principal, введенной пользователем
(строки 122-123). В строке 125 устанавливается величина principal в EJB-компоненте
InterestCalculator путем вызова метод setPrincipal. Заметим, что объекты
JTextField генерируют события ActionEvent, когда пользователь нажимает
клавишу Enter в поле JTextField. Следовательно, пользователь должен нажимать
кнопку Enter после ввода каждого фрагмента данных, чтобы принудить метод
actionPerformed интерфейса ActionListener вызывать соответствующий
бизнес-метод интерфейса InterestCalculator.
Метод createRateTextField (строки 144-169) создает текстовое поле JTextField
для ввода процентной ставки. В строках 149-167 создается анонимный
внутренний класс ActionListener для синтаксического анализа данных, введенных
пользователем (строки 155-156), и вызывается метод setlnterestRate интерфейса
InterestCalculator для установки процентной ставки (строка 159).
Метод createTennText Field (строки 172-196) создает текстовое поле
JTextField для ввода срока хранения (б годах) вклада, для которого будет
вычисляться процент. Анонимный внутренний класс ActionListener (строки 177-194)
осуществляет синтаксический анализ введенных пользователем данных (строки
183-184) и устанавливает срок хранения в объекте InterestCalculator, вызывая
метод setTerm (строка 186) удаленного интерфейса InterestCalculator.
Метод getCalcnlatorButton (строки 99-237) создают кнопку JButton, которая
при щелчке на ней мышью вызывает методы getBalance и getlnterestEarned
интерфейса InterestCalculator. В строках 220-224 устанавливается текст в полях
balanceTextField и interestEarnedTextField для отображения вычисленных
результатов пользователю.
Конструктор InterestCalculatorCHent (строки 34-68) вызывает метод createln-
terestCalculator для создания нового экземпляра EJB-компонента
InterestCalculator (строка 39). В строках 42-56 создаются компоненты пользовательского
интерфейса приложения, и вызывается метод layoutGUI (строка 59), который созда-
Сеансовые компоненты EJB и распределенные транзакции
323
ет надпись JLabel для каждого текстового поля JTextField и осуществляет
размещение компонентов (строки 240-269).
Метод getWindowListener (строки 272-305) создает анонимный внутренний
класс window Adapter, который предоставляет метод windowClosing для
выполнения очистки, когда пользователь закрывает окно приложения. В строке 279
проверяется, не равно ли null значение calculator. Если значение есть null, программа
прекращает работу и сообщает; код ошибки (строка 280). В противно» случае
в строке 286 вызывается метод remove удаленного интерфейса IsterestCalcnlator
для удаления экземпляра EJB-компонента, который используется приложением.
Если при удалении экземпляра компонента EJB возникает ошибка, в строках
290-293 перехватывается исключение RemoteException. Если имеются ошибка
при коммуникационном взаимодействии с контейнером EJB, в строках 296-299
перехватывается исключение RemoteException.
6.3.2. Развертывание сеансовых компонентов EJB
Компоненты Enterprise JavaBeans исполняются в контексте контейнера EJB,
который является главной составляющей сервера приложений, совместимого
с J2EE. В этом разделе подробно описываются действия, необходимые для
развертывания сеансового EJB-компонента InterestCalcuiator на эталонной реализации
Java 2 Enterprise Edition версии 1.2.1 от Sun Microsystems. Если вы еще не
установили и не настроили пакет J2EE SDK, обратитесь к инструкциям по установке.
Создайте новое распределенное приложение, выбрав New Application из меню
File (рис. 6.7). Задайте имя для EAR-файла и имя приложения в диалоговом окне
и щелкните на ОК (рис. 6.8).
Рис. 6.7. Создание нового приложения с помощью средства развертывания Application
Deployment Tool
324
Глава 6
Рис. 6.8. Задание EAR-файла для нового приложения
Выберите New Enterprise Bean из меню File, чтобы начать развертывание
EJB-компонеета (рис. 6.9). Щелкните на Next, укажите имя JAR Display Name
и щелкните на Add, чтобы добавить файлы классов для EJB-компонента
InterestCalculator (рис. 6.10).
Рис, 6.9. Создание нового компонента Enterprise Bean
Задайте корень RootDirectory для классов, которые относятся к EJB-компоненту
InterestCalculator (рис. 6.11). Классы для InterestCalculator содержатся в пакете
com.deitel.advjhtpl.ejb.session.stateful.ejb, поэтому выберите в качестве корня
каталог, который содержит каталог com. Выберите файлы классов для Interest-
Calculator — InterestCalcnlator.class, InterestCalculatorHoine.class и Interest-
CalculatorEJB.class — и щелкните на Add, а затем на ОК. После добавления файлов
классов они появятся в списке Contents в окне EJB JAR (рис. 6.12). Щелкните на
Next, чтобы начать добавление файлов классов в JAR-архив EJB-компонента.
Задайте класс Enterprise Bean Class, собственный интерфейс Home Interface
и удаленный интерфейс Remote Interface, выбрав соответствующие классы из
раскрывающихся списков (рис. 6.13). Задайте имя Enterprise Bean Display Name,
которое будет отображаться в инструментальном средстве развертывания
Application Deployment Tool, и выберите тип Bean Туре. EJB-компонент
InterestCalculator представляет собой сеансовый компонент с состоянием, поэтому выберите
опции Session и Stateful и щелкните на кнопке Next (рис. 6.14). При
развертывании компонента MathTooIEJB укажите Stateless (с состоянием) вместо Stateful
(без состояния) для типа компонента Bean Type.
Сеансовые компоненты EJB и распределенные транзакции
|'<8аа. f -щах'"]. ''p-'frfo':':]-;
Рис. 6.10. Добавление классов для EJ В- компонента InterestCaiculator
f Easistelkit
•-□client
f Oeib
ош«$
Q inlersstCalcul aim. lava
Q InterestCaleulalorEJB.isva
0 rffiasWattliattrtiqrTie.ciass'
0 nlarsstcaltuiamrHome iava
ffilinitabisAd^Kl;
Г—^—1Ы ■ 44,
&■■■ '-;'"!'.*it:""*«yff^*
ИИНИИЧ
Рис. 6.11. Выбор классов для EJB-компонента InterestCaiculator, подлежащих
добавлению
326
Глава 6
Рис. 6.12. Результат добзвлрния классов дпя компонрнта FJB InterestCalculator
„ци^.м.'м
iftei атэд & >ч* s* js&tv vviim siifajgm ■■ наг гл jae yi y'lmnifc м
р?гУ:-^ЭДЮщ^Щ|
Рис. 6.13. Задание класса Enterprise Bean Class для LIB-компонента InterestCalculator
Установите режим контейнерного управления транзакциями
Container-Managed Transactions в диалоговом окне Transaction Management
(рис. 6.15). Для каждого метода Method задайте Supports в качестве типа
транзакции и щелкните на кнопке Next. Подробнее о транзакциях будет рассказано в
разделе 6.4.
Сеансовые компоненты EJB и распределенные транзакции
327
Рис. 6.14. Задание классов для EJB-компонента InterestCalculator и типа Stateful
(с состоянием) для сеансового (Session) компонента
Рис. 6.15. Задание опции контейнерного управления транзакциями Container Managed
Transactions для EJB-компонента InterestCalculator
На рис. 6.16 представлен XML-код дескриптора развертывания,
сформированного для EJB-компонента InterestCalculator. Сервер приложений использует
XML-дескриптор для настройки EJB-компонента InterestCalculator.
328
Глава б
ч?вг|| »ersioF^"1.0" eraodieips'Gpi 251"'-
i 'OOCTtPE врЬ-iarPuauc '-WSun Mitjosrsiems. mc aotd
'[JeBcriptori'nodescrlBllafKftf^siTipbyii-
if-name»BuSjness. Lfrgic «Jdisp3ay-n вте>
««rtefprfca-beans*
^iBtsEDrt-
**ispi3t-namt*irte*P8tCik«lalor*ffltspiey-namc>
«ejb-лап-Е»inta rasJC aicuMor «("sib- n*me>
«noms^Dm deM.auvjhlpI. ejB.fi*es;on elateRJ.ejtJ.lnteretlC BituiaWiHome^tiDmE »
<fBnwte*ccirn de№t e*|htpi .Effi^es5iofi.staiafui.f|o.i«irestCaBuii»r-4™nriD«>
*BjD-riiss*com tieiTfl.atfqrireT ijo session statefu! е^Ь lnlsr»fC»ltul9toTbJB^e5ti-[iass>
* б** story йч»*Б1а1еай tree ss icft-type >
«bsnsiciioA'toe' coniain&TMlrsni*;!] on-typ e >
*t0 нЩг№-ЩП вдЯпп»
■* ejOname " IntetesSC ak uralBr*Jepb-neme »
*melfiQ* inflf* Serrate «rmalnod-inlf»
«methofl- n*me>se^rincipal *(rna!riBfl-n.3 me»
«mettico-perim**
m>dCLihle </твВпм1-р:
Рис. 6.16. XML-дескриптор развертывания для EJB-компонента InterestCakulator
Вы должны задать имя JNDI Name для компонента InterestCalculator, чтобы
клиенты могли получать ссылки на EJB-компоненты. Для данного примера
укажите InterestCalculator в качестве имени JNDI Name (рис. 6.17). Имя JNDI Name
не обязательно должно совпадать с именем EJB-компонента.
Рис. 6.17. Задание имени JNDI Name для LIB-компонента InterestCalculator
Хороший стиль программирования 6.1
Используйте имя удаленного интерфейса EJB в качестве имени JNDI для
EJB-компонента. Это сделает ваш. код более удобным для чтения и
позволит легче запомнить имя JNDI.
Выполните развертывание приложения на сервере J2EE, выбрав пункт Deploy
Application из меню Tools, либо щелкнув на кнопке Deploy Application в панели
инструментов (рис. 6.18).
Сеансовые компоненты EJB и распределенные транзакции
329
Рис. 6.18. Развертывание корпорэтир! юга приложения на локальном компьютере localhost
Укажите localhost в качестве целевого сервера Target Server и установите
флажок Return Client Jar (рис. 6.19). JAR-файл клиента содержит классы-заглушки,
которые клиент будет использовать для взаимодействия с Е JB-компонентом.
Рис. 6.19. Задание возвращаемого клиенту JAR-фэйла Return Client Jar в средстве
развертывания Application Deployment Tool
Осуществив развертывание EJB-компонента InterestCalculator (рис. 6.20)
выполните приложение InterestCalculatorClient для тестирования компонента
InterestCalculator. При выполнении приложения IntcrestCalcuIatorClient в ката-
330
Глава 6
логе, задаваемом переменной CLASSPATH, должны содержаться файлы
setCalculater,Client.jar и j2ee.jar. Например, в командной строке введите
Java -classpath Dr\j2sdkee,2.l\lib\j2ee.jar,D:\Inteirest-
CalculatorClient.jar;. Com.deitel.advjhtpl.ejb.session.stateful.
client.InterestCalculatorClient
Рис. 6.20. Успешное завершение процесса развертывания
6.3.3. Сеансовые компоненты EJB без состояния
Сеансовые компоненты EJB без состояния не сохраняют информацию о
состоянии между вызовами бизнес-методов. Как результат, контейнер EJB может
использовать любой экземпляр EJB-компонента без состояния для ответа на
запрос любого клиента.
—^ Совет по повышению эффективности 6.1
" т^^ Сеансовые компоненты EJB без состояния могут выполняться более
эффективно, чем сеансовые компоненты EJB с состоянием, поскольку овин
экземпляр сеансового EJB без состояния может совместно
использоваться многими клиентами, что сокращает потребности в памяти,
операциях процессора и других ресурсах сервера.
В листинге на рис. 6.21 описывается удаленный интерфейс MathTool.
MathTool представляет собой сеансовый компонент EJB без состояния,
определяющий бизнес-методы для генерирования ряда Фибоначчи и вычисления
факториалов. Метод getFibonacciSeries (строки 14-15) генерирует ряд Фибоначчи,
длина которого задается целочисленным параметром howMany. Метод getFactorial
(строки 18-19) вычисляет факториал заданного целого числа.
1 // MathTool.java
2 // MathTool - удаленный интерфейс для EJB-компонента MathTool.
3 package com.deitel.advjhtpl.ejb.session.stateless.ejb;
4
5 // Базовые библиотеки Java
Сеансовые компоненты EJB и распределенные транзакции
331
6 import java.rmi.ReraoteException;
7
8 // Стандартные расширения Java
9 import javax.ejb.EJBObject;
10
11 public interface MathTool extends EJBObject
12 {
13 // получение ряда Фибоначчи
14 public int[] getFibonacciSeries( int howMany )
15 throws RemoteException, IllegalArguraentException;
16
17 // получение факториала заданного целого числа
18 public int getFactorial( int number )
19 throws RemoteException, IllegalArgumentExcaption;
20 }
Рис. 6.21. Удаленный интерфейс MathTool для вычисления факториалов и генерирования
ряда Фибоначчи
Компонент MathToolEJB (рис. 6.22) предоставляет реализации бизнес-методов,
объявленных в удаленном интерфейсе MathTool. MathToolEJB реализует
интерфейс SessionBean (строка 9), указывая, что MathTool является сеансовым
EJB-компонентом. Метод getFibonacciSeries (строки 14-48) генерирует ряд
Фибоначчи. Ряд Фибоначчи
0, 1, 1, 2, 3, 5, 8, 13, 21 ...
начинается с О и 1 и имеет то свойство, что каждое последующее число Фибоначчи
является суммой предыдущих двух чисел Фибоначчи. EJB-компонент MathTool
вычисляет ряд Фибоначчи в строках 25—44. Каждое число в ряду помещается в
целочисленный массив series (строка 39). В строках 32-33 число О устанавливается
в качестве нулевого числа в ряду, а число 1 устанавливается в качестве первого
числа в ряду. В строке 39 в качестве значения следующего числа в ряду
устанавливается сумму предыдущих двух чисел.
1 // MathToolEJB.Java
2 // MathToolEJB - сеансовый компонент EJB беа состояния с
3 // методами для вычисления ряда Фибоначчи и факториала.
A package com.deitel.advjhtpl.ejb,session.stateless.ejb;
5
6 // Стандартные расширения Java
7 import javax.ejb.*;
8
9 public class MathToolEJB implements SessionBean {
10
11 private SessionContext sessionContext;
12
13 // получение ряда Фибоначчи
14 public int[] getFibonacciSeries( int howMany )
15 throws IllegalArgumentException
16 {
17 // возбуждение исключения IllegalArgumentException,
18 // если длина ряда меньше двух
19 if ( howMany < 2 )
20 throw new IllegalArgumentException{
21 "Cannot generate Fibonacci series of " +
22 "length less than two." );
332
Глава 6
23
24 // начальные значения
25 int startPointl = 0;
26 int startPoint2 = 1;
27
28 // массив для хранения ряда Фибоначчи
29 int[] series = new int[ howMany ];
30
31 // задание базовых случаев
32 series[ 0 ] = 0;
33 series[ 1 ] = 1;
34
35 // формирование ряда Фибоначчи
36 for ( int i = 2; i < howMany; i++ ) {
37
38 // вычисление следующего числа в ряду
39 series[ i ] = startPointl + startPoin±2;
40
41 // задание начальных условий для следужщей итерации
42 startPointl = startPoint2;
43 startPoint2 = series[ i ];
44 }
45
46 return series;
47
48 } // конец метода getFibonacciSeries
49
50 // получение факториала заданного целого числа
51 public int getFactorial( int number )
52 throws IllegalArgumentException
53 {
54 // возбуждение исключения IllegalArgumentException,
если число меньше 0
55 if ( number < О )
56 throw new IllegalArgumentException(
57 "Cannot calculate factorial of negative numbers." );
58
59 // базовый случай рекурсии, возврат 1
60 if ( number = 0 )
61 return 1,-
62
63 // рекурсивный вызов getFactorial для вычисления факториала
64 else
65 return number * getFactorial( number - 1 );
66
67 } // конец метода getFactorial
68
69 // задание контекста SesaionContext
70 public void setSessionContext( SessionContext content *
71 {
72 sessionContext = context;
73 }
74
75 // создание нового экземпляра MathTool
76 public void e]bCreate() {}
77
Сеансовые компоненты EJB и распределенные транзакции 333
78 // удаление экземпляра. MathTool
79 public void ejbRemove() {}
80
81 // активация экземпляра MathTool
82 public void ejbActivateО {}
83
84 // пассивации экземпляра MathTool
85 public void ejbPassivate() {}
86 }
Рис. 6.22. Реализация MathToolEJB удале-шого интерфейса MathTool
Метод get Factorial (строки 51-67) вычисляет факториал неотрицательного
целого числа, который по определению представляет собой произведение
■ п - (л-1) ■ (п-2) • ... * 1
В строках 56-57 возбуждается исключение IllegalArgumentException, если
целочисленный параметр number меньше 0. В строках 60—61 реализуется базовый
класс для рекурсивного вычисления. В строке 65 вычисляется факториал путем
рекурсивных вызовов метода getFactorial.
Контейнер EJB вызывает метод setSessionContext (строки 70-73), когда
активирован экземпляр EJB-компонента, и клиенту предоставлен удаленный
интерфейс. Параметр SessionContext реализует метод getEJBObject, который EJB-kom-
понент может использовать для извлечения ссылки на текущий экземпляр
EJB-компонента.
При вызове клиентом метода create интерфейса MathToolHome (рис. 6.23)
контейнер EJB вызывает метод ejbCreate (строка 76) Метод ejbCreate выполняет
инициализацию EJB-компонента. Метод ejbCreate интерфейса MathToolEJB имеет
пустую реализацию, поскольку этот EJB-компонент не требует инициализации.
Контейнер EJB вызывает метод ejbRemove (строка 79) для удаления экземпляра
EJB-компонента MathTool.
Контейнер EJB вызывает метод ejbActivate (строка 82), когда экземпляр
EJB-компонента извлекается из пула EJB-контейнера и ассоциируется с конкретным
клиентом. Контейнер EJB вызывает метод ejbPassivate (строка 85), когда экземпляр
EJB-компонента больше не нужен и может быть возвращен в пул готовности.
Интерфейс MathToolHome (рис. 6.23) представляет собой собственный
интерфейс для EJB-компонента MathTool. Метод create (строки 15-16) создает новый
EJB-компонент MathTool. Контейнер EJB вызывает метод ejbCreate интерфейса
MathToolEJB, когда клиент вызывает метод create интерфейса MathToolHome.
Инструкции по развертыванию вы можете найти в разделе 6.3.2. Напомним,
что MathTool является сеансовым EJB-кОмпонентом без состояния, поэтому вы
должны указать на зто в процессе развертывания. Не забудьте также указать
соответствующее имя JNDI (например, MathTool) для EJB-компонента MathTool.
1 // MathToolHome.j ava
2 // MathToolHome - собственный интерфейс для EJB-компонента MathTool.
3 package com.deitel.advjhtpl.ejb.session.stateless.ejb;
4
5 // Базовые библиотеки Java
6 import Java.rmi.RemoteExсерtion;
7
8 // Стандартные расширения Java
9 import javax.ejb.EJBHome;
10 import javax.eib.CreateException;
11
334
Глава 6
12 public interface MathToolHome extends EJBHome {
13
14 // создание нового БJB-компонента MathTool
15 public MathTool create<) throws RemoteException,
16 CreateException ,-
17)
Рис 6.23. Интерфейс MathToolHome для создания EJB-компонентов MathTool
Mat hToolC lien t (рис. 6.24) представляет собой клиентское приложение для
EJB-компонента MathTool. Пользовательский интерфейс состоит из текстового
поля JTextField, в которое пользователь может вводить целое число, кнопки
J Butt on для вызова метода getFactorial интерфейса MathTool и кнопки JBntton
для вызова метода getFibonacciSeries интерфейса MathTool. Результаты
вычислений отображаются в текстовой области JTextArea.
Конструктор MathTool Client (строки 29-45) вызывает метод createMathTool
(строка 35), чтобы создать новый экземпляр EJB-компонента MathTool. В строке
38 вызывается метод createGUI для создания и размещения компонентов GUI
пользовательского интерфейса приложения. Метод createMathTool (строки 48-83)
использует класс InitialContext (строка 53) для поиска интерфейса MathToolHome
в каталоге JNDI (строки 56-57). В строке 65 вызывается метод create интерфейса
MathToolHome для создания экземпляра EJB-компонента MathTool.
Метод getFactorialBntton (строки 86-122) создает кнопку JButton, которая
при нажатии на ней вызывает метод getFactorial EJB-компонента MathTool.
В строках 100-104 осуществляется синтаксический анализ числа, введенного
в поле numberTextField, и вызывается метод getFactorial удаленного интерфейса
MathTool. В строках 107-108 в текстовой области resultsTextArea отображается
значение факториала. В строках 113-115 перехватывается исключение Remote-
Exception, если имеет место ошибка при вызове метода getFactorial.
Метод getFibonacciButton (строки 125-182) создает кнопку JBntton, при
нажатии которой аызывается метод getFibonacciSeries EJB-компонента MathTool.
В строках 140—145 осуществляется синтаксический анализ числа, введенного
в поле numberTextField, и вызывается метод getFibonacciSeries удаленного
интерфейса MathTool. Метод getFibonacciSeries возвращает массив целых чисел,
содержащий ряд Фибоначчи, длина которого задается целочисленным параметром
howMany. В строках 148-168 осуществляется формирование буфера StringBufCer,
содержащего ряд Фибоначчи, и отображение ряда в текстовой области
resultsTextArea.
1 // MathToolClient.Java
2 If MathToolClient - GUI для вычисления факториалов и
3 // рядов Фибоначии с помощь» EJB-компоаента MathTool,
4 package com.deitel.advjntpl.ejb.session.stateless.client;
5
6 // Базовые библиотеки Java
7 import j ava.awt.*;
8 import Java.awt.event.*;
9 import java.rmi.*;
10
11 // Стандартные расширения Java
12 import javax.swing,*;
13 import javax.rmi.*;
14 import javax.naming.*;
15 import javax.e jb.*;
16
Сеансовые компоненты EJB и распределенные транзакции
335
17 // Библиотеки Deitel
18 inport com.deitel.advjhtpl.ejb.session.stateless.ejb.*;
19
20 public class HathToolClient extends JFrame {
21
22 private HathToolHome mathToolHome;
23 private HathTool mathTool;
24
25 private JTextftrea resultsTextArea;
26 private JTextEield numberTextEield;
27
28 // конструктор MathToolClient
29 public MathToolClient()
30 {
31 super( "Stateless Session EJB Example" );
32
33 // создание объекта MathTool для вычисления
34 // факториалов и рядов Фибоначчи
35 createHathToolO ;
36
37 // создание и размещение компонентов GUI
36 createGUIO;
39
40 addffindowListener( getWindowListener() );
41
42 setSizef 425, 200 );
43 setVisible( true );
44
45 > // конец конструктора MathToolClient
46
47 // создание экземпляра EJB-компонентз MathTool
4в private void createMathTool()
49 {
50 // поиск интерфейса MathToolHome к создание EJB-комповента
MathTool
51 try {
52
53 InitialContext initialContext = new lnitialContext<);
54
55 // поиск EJB-компонента MathTool
56 Object homeObject =
57 initialContext.lookup( "MathTool" ),-
58
59 // получение интерфейса MathToolHome
€0 mathToolHome = < MathToolHome )
61 PortableRemoteObject.narrow! homeObject,
62 HathToolHome.class );
63
64 // создание экземпляра EJB-компонента HathTool
65 mathTool = mathToolHome.create();
66
67 ) // конец оператора try
68
69 // обработка исключения, если EJB-хомпонект MathTool не найден
70 catch ( NamingException namingException ) {
71 namingException.printStackTrace();
72 }
336
Глава 6
73
74 // обработка исключения при создании ЕОВ-хомпонента MathTool
75 catch ( RemoteException remoteException ) {
7-6 remoteException. printStaclcTrace ( System. ere ) ;
77 }
78
79 //обработка исключения при создании EJB-хонпокента MathTool
80 catch ( CreateException createException ) {
81 createException.printstackTrace( System.ere );
82 }
S3 } // конец метода createMathTool
84
85 // создание кнопки JButton для вычисления факториала
86 private JButton getFactorialButton()
87 {
B8 JButton factorialButton =
89 new JButton( "Calculate Factorial" ) ,-
90
91 // добавление слушаетеля ActionListener для кнопки вычисления
факториала
92 factorialButton.addActionListener(
93 new ActionListener() {
94
95 public void actionPerformad( ActionEvent event )
96 {
97 // использование EJB-компонента MathTool для
вычисления факториала
98 try I
99
100 int number = Integer.parseInt(
101 munberTextField.getText() ):
102
103 // получение факториала целого числа,
введенного пользователей
204 int result = mathTool.getFactorial( number );
105
106 // отображение результатов в области
resultsTextArea
107 resultsTextArea.setTfext( number +"!=-+
108 result } ;
109
110 \ I f конец оператора try
111
112 // обработка исключения при вычислении факториала
113 catch ( RemoteException remoteException ) (
114 remoteException.printStackTraco();
115 J
116 } // конец метода actionPerformed
117 }
118 }; // конец слушателя addActionListener
119
120 return factorialButton;
121
122 } // конец метода getFactorialButton
123
124 // создание кнопки JButton для генерирования ряда Фибоначчи
125 private JButton getEibonacciButton()
Сеансовые компоненты EJB и распределенные транзакции
337
126 {
127 JButton fibonacciBufcton =
128 new JButton( "Fibonacci Series" );
129
130 // добавление слушателя ActionListener для генерирования
ряда Фибоначчи
131 fibonacciButton.addActionListener(
132 new ActionListener0 {
133
134 public void actionPerformed( ActionEvent event )
135 {
136 // генерирование ряда Фибоначчи с помощью
EJB-компонента MathTool
137 try {
138
139 // получение числа, введеннного пользователем
140 int number = Integer.parselnt(
141 numberTextField.getText() );
142
143 // получение ряда Фибоначчи
144 int[] series = piathTool.getFibonacciSeries(
145 number );
146
147 // создание буфера StringBufirer для хранения ряда
148 StringBuffer buffer =
149 new StringBuffer( "The first " );
150
151 buffer.append{ number );
152
153 buf fer. append ( " Fibonacci number (a): \n". };
154
155 // добавление каждого из чисел ряда в буфер
156 for ( int i = 0; i < series.length; i++ ) (
157
158 // не добавлять запятую перед первым числом
159 if { i != О )
160 buffer.append( ", " );
161
162 // добавление следующего чисела ряда в буфер
163 buffer.append( String.valueOff
164 series[i] ) ) ;
165 }
166
167 // отображение ряда в области resultsTextArea
168 resultsTextArea.setText( buffer.toStringO );
169
170 } // конец оператора try
171
172 // обработка исключения при вычислении ряда
173 catch ( RemoteException гелю teException ) {
174 remoteException.printStackTrace();
175 }
176 } // конец метода actionPerformed
177 }
178 ); // конец слушателя addActionListener
179
180 return fibonacciButton;
338
Глава 6
181
182 } // конец метода getFibonacciButton
183
184 // создание макета размещения компонентов GUI
185 public void createGOIO
186 {
187 // создание области JTextArea дли отображения результатов
1S6 resultsTextArea = new JTextArea О;
189 reaultsTextArea.setLineWrap{ true );
190 reaultsTextArea.setWrapStyleWoxd< true );
191 resultsTextArea,setEditable( false );
192
193 // создание поля JTextField для пользовательского ввода
194 nuiriberTextField = new JTextField( 10 );
195
196 // создание кнопки JButton для вычисления факториала
197 JButton factorialButton = getFactorialButton();
198
199 // создание кнопки JButton для генерирования ряда Фибоначчи
200 JButton fibonacciButton = getFibonaceiButton();
201
202 Container contentPane = getContentPane();
203
204 // помещение объекта resultsTextArea в контейнер JSerollPane
205 JSerollPane resultsSerollPane =
206 new JSerollPane( resultsTextArea );
207
208 contentFane.add( resultsScrollPane,
209 BorderLayout.CENTER );
210
211 // добавление элементов ввода в новую панель JPanel
212 JPanel inputPanel = new JPanel( new FlowLayoutO );
213 inputPanel.add( new JLabel( "Enter an integer: " ) );
214 inputPanel.add( nuniberTextField );
215
216 // добавление коыпонетов JButton в новую панель JPanel
217 JPanel buttonPanel = new JPanel( new FlowLayoutO ) ."
218 buttonPanel.add( factorialButfcon );
219 buttonPanel.add( fibonacciButton );
220
221 // добавление панели inputPanel и панели buttonPanel в новую
панель JPanel
222 JPanel controlPanel =
223 new JPanel( new GridLayout( 2, 2 ) ) ;
224
225 controlPanel.addt inputPanel );
226 controlPanel.add( buttonPanel );
227
228 contentPane.add{ controlPanel, BorderLayout.NORTH );
229
230 } // конец метода createGUI
231
232 // получение слушателя WindowListener для выхода иэ приложения
233 private WindowListener getWindowListener{)
234 {
235 return new WindowAdapter<) {
236
Сеансовые компоненты EJB и распределенные транзакции
339
237
23S
233
240
241
242
243
244
245
246
247
24В
249
250
251
252
253
254
255
256
257
258
259
260
261
262
2 63
264
265
266 }
public void windowClosirtg{ WindowEvent event )
{
II удаление экземпляра MathTool
try {
mathTool.remove();
}
// обработка исключения при удалений EJB-компонента
MathTool
catch ( RemoveException. removeException ) {
removeException.printStackTrace();
System.exit( ~1 );
)
// обработка исключения при удалении EJB-компонента
MathTool
catch ( KemoteException remoteException ) {
remoteException.printstackTrace();
System.exit( -1 );
>
System.exit( 0 );
} // конец метода windovCLosing
},
} // конец метода getWindowListener
// выполнение приложения
public static void main( String[] args )
{
MathToolClient client, ■ new MathToolClientO
|7!=S04<I
ЩШ
pifi first 7 Fibonacci numbEr(s)'
B, l.i.2, 3.5. t
Рис. 6.24. Приложение MathToolClient для взаимодействия с EJB-компонентом MathTool
340
Глава 6
6.4. EJB-транзакции
8 Java 2 Enterprise Edition имеется поддержка распределенных транзакций.
Распределенная транзакция — это транзакция, которая имеет дело с несколькими
базами данных или с несколькими серверами приложений. Например,
распределенная транзакция может использоваться для атомарной передачи капитала со
счета в одном банке на счет в другом банке.
J2EE поддерживает два метода для определения границ действия транзакции:
разграничение с управлением, на стороне компонента и разграничение с
управлением на стороне контейнера. Разграничение с управлением на стороне
компонента требует, чтобы разработчик компонента EJB вручную писал код для границ
транзакций в EJB-компонентах с помощью средств API Java Transaction (JTA).
Разграничение с управлением и а стороне контейнера дает возможность
разработчику EJB-компонента декларативно задавать границы транзакции при
развертывании EJB-компонента.
Общая методическая рекомендация 6.2
Компоненты-сущности EJB могут использовать только разграничение
транзакций, управляемое контейнером.
6.4.1. Собственный и удаленный интерфейс EJB MoneyTransfer
EJB-компонент MoneyTransfer демонстрирует актуальность применения
распределенных транзакций и представляет их реализацию с использованием
разграничения, управляемого компонентом, и разграничения, управляемого
контейнером. В этом примере мы осуществляем перевод денег со счета в банке ВапкАВС на
счет в банке BankXYZ. Сначала мы снимаем деньги со счета в банке ВапкАВС,
а затем зачисляем их на счет в банке BankXYZ. Транзакции необходимы, чтобы
гарантировать, что деньги будут возвращены на счет ВапкАВС, если перевод денег
на счет BankXYZ окончится неудачей. Нам также нужно гарантировать, что если
снятие денег со счета ВапкАВС закончится неудачей, деньги не будут зачислены
на счет BankXYZ.
Удаленный интерфейс MoneyTransfer (рис. 6.25) предоставляет методы для
перевода денег с одного счета на другой и для получения остатков по счетам в двух
различных банках. Метод transfer (строка 15) переводит заданную сумму со счета
в банке ВапкАВС на счет в банке BankXYZ. Метод getBaokABCBalance (строка 18)
возвращает баланс (остаток) счета в йанке ВапкАВС. Метод getBankXYZRalance
(строка 21) возвращает баланс счета в банке BankXYZ. Интерфейс MoneyTrans-
ferHome (рис. 6.26) предоставляет метод create (строки 15-16) для создания
экземпляров EJB-компонента MoneyTransfer.
1 // MoneyTransfer.java
2 // MoneyTransfer - удаленный интерфейс для EJB-компонента
3 // MoneyTransfer,
4 package com.daitel.advjhtpl.ejb.transactions;
5
6 // Набор базовых библиотек Java
7 import Java.rmi .RemoteException;
3
9 // Стандартные расширения Java
10 import iavax.ejb.EJBObject;
11
12 public interface MoneyTransfer extends EJBObject {
13
Сеансовые компоненты EJB и распределенные транзакции
341
14 // перевод суммы из банка BankABC в банк BankXYZ
15 public void transfer( double amount ) throws RemoteException;
16
17 // получение баланса счета в банке BankABC
IB public double getBankABCBalance() throws RemoteException;
19
20 // получение баланса счеса в банке BankXYZ
21 public double getBankXYZBalance() throws RemoteException;
22 )
Рис. 6.25. Удаленный интерфейс MoneyTransfer для перевода денег и получения
балансов по счетам
1 // MoneyTransferHome.java
2 // MoneyTransferHome - собственный интерфейс для
3 // EJB-компонента MoneyTransferHome.
4 package com.deitel.advjhtpl.ejb.transactions;
5
6 // Базовые библиотеки Java
7 import Java.rmi.RemoteException;
8
9 // Стандартные расширения Java
10 import javax.ejb.*;
11
12 public interface MoneyTransferHome extends EJBHome {
13
14 // создание EJB-хомпонента MoneyTransfer
15 public MoneyTransfer create О throws RemoteException,
16 CreateException;
17} ____
Рис. 6.26. Интерфейс MoneyTransferHome для создания EJB-компонентов MoneyTransfer
6.4.2. Разграничение транзакций с управлением на стороне
компонента
Разграничение транзакций с управлением на стороны компонента требует,
чтобы разработчик EJB-компонента вручную описывал границы действия
транзакции в коде EJB-компонента. Разграничение транзакций с управлением на стороне
компонента может быть использовано только для сеансовых EJB-компонентов.
Компонент MoneyTransferEJB (рис. 6.27) реализует удаленный интерфейс
MoneyTransfer с использованием разграничения транзакций, управляемого
компонентом, чтобы обеспечить атомарность обновлений баз данных в методе transfer
(строки 26-81). В строках 29-30 создается транзакция Us erTrans action.
В строке 34 транзакция transaction начинается с вызова метода begin интерфейса
UserTransaction. Все операторы, следующие после начала транзакции
transaction, являются составной частью транзакции до тех пор, пока транзакция не будет
завершена или отменена.
1 // MoneyTransferEJB.java
2 // MoneyTransferEJB - сеансовый компонент EJB без состояния для
3 // перевода денег со счета в банке BankABC на счет в банке BankXYZ
4 // с использованием разграничения транзакции, управляемого компонентом.
5 package com. deitel. advjhtpl. ejb. transactions .beanmanaged,-
6
7 // Базовые библиотеки Java
8 import Java. util. * -,
9 import Java. sql. * r-
10
11 // Стандартные расширения Java
12 import javax.ejb-*;
13 import j avax.naming.*;
14 import javax.transaction.*;
15 import 3avax.sql.*;
16
17 public class MoneyTransferEJB implements SessionBean {
18
19 private SessionContext sessionContext;
20 private Connection bankOneConnection;
21 private Connection bankTwoConnection;
22 private Preparedstatement withdrawalStatement;
23 private PreparedStatement depositStafcement,-
24
25 // перевод денег ив банка BankABC в банк BankXYZ
26 public void transfer( double amount ) throws EJBException
27 {
28 // создание транзакции для перевода денег
29 UserTransaetion transaction =
30 sessionContext.getUserTransaction();
31
32 // качало разграничения транзакции, управляемого компонентом
33 try {
34 transaction.begin();
35 }
36
37 // перехват исключения, если метод не достигает успеха
38 catch ( Exception exception ) {
39
40 // возбуждение исключения EJBException, указывающего
на неудачу транзакции
41 throw new EJBException{ exception );
42 }
43
44 // перевод денег со счета в банке ВалкАВС на счет в банке
45 // BankXYZ с использованием разграничения транзакции,
управляемого компонентом
46 try {
47
48 withdrawalStatement.ееtDoublef l, amount );
49
50 // снятие денег со счета в банке BankABC
51 withdrawalStatement.executeUpdate();
52
53 depositstatement.setDouble{ 1, amount );
54
55 // начисление денет- на счет в банке BankXYZ
56 depositstatement.executeUpdate();
57
58 // завершение транзакции
59 transaction.commit();
Сеансовые компоненты EJB и распределенные транзакции
343
60
61 } // конец блока try
62
63 // обработка исключений при снятии денет", начислении
64 // девег и завершении транзакции
65 catch ( Exception exception ) {
66
67 // попытка отмени (отката) транзакции
68 try {
69 transaction.rollback();
70 }
71
72 // обработка исключений при откате транзакции
73 catch ( SystemEXception systemException ) {
74 throw new EJBException{ systeaiException );
75 }
76
77 // возбуждение исключения EJBException, указывающего
на неудачу транзакции
78 throw new EJBException( exception );
79 }
80
81 } // конец метода transfer
82
83 // получение баланса счета в банке BankABC
84 public double getBankABCBalanceО throws EJBException
85 {
86 // получение баланса счета в банке BankABC
87 try {
88
89 // выбор номера счета # 12345 для получения баланса
90 String select = "SELECT balance FROM Account " +
91 "WHERE acoountlD = 12345";
92
93 PreparedStatement selectStatement =
94 bankOneConnection.prepareStatenient( select );
95
96 ResultSet resultSet = selectStatement.executeQuery();
97
98 // получение первой записи в ResultSet и вовврат баланса
99 if ( resultSet.next() )
100 return resultSet.getOouble( "balance" );
101 else
102 throw new EJBException( "Account not found" );
103
104 } // конец блока try
105
106 // обработка исключения пря получении баланса счета
107 catch ( SQLException sqlException ) {
108 throw new EJBException( sqlException );
109 >
110
111 } // конец метода getBankABCBalanee
112
113 // получение баланса счета в банке BankXYZ
114 public double getBankXYZBalance{) throws EJBException
344
Глава 6
115 {
116 // получение баланса счета в банке BankXYZ
117 try {
118
119 // выбор номера счета # 54321 для получения баланса
120 String select = "SELECT balance FROM Account " +
121 "WHERE accountID = 54321";
122
123 PreparedStatement selectStatement =
124 bankTwoConnection.preparestatement{ select );
125
126 ResultSet resultSet = selectStatement.executeQuery();
127
128 // получение первой записи в ResultSet и возврат баланса
129 if ( resultSet.next() )
130 return resultSet.getOouble( "balance" );
131 else
132 throw new EJBException( "Account not found" );
133
134 } // конец блока try
135
136 // обработка исключения при получении баланса счета
137 oatch ( SQLException sqlException ) {
138 throw new EJBException( sqlException );
139 J
140
141 } // конец метода getBankXYZBalance
142
143 // задание контекста SessionContext
144 public void SetSessionContext( SessionContext context )
145 throws EJBException
146 {
147 SessionContext = context;
148
149 openDatabaseResources () ;
150 )
151
152 // создание экземпляра компонента MoneyTransfer
153 public void ejbCreste() {}
154
155 // удаление экземпляра компонента MoneyTransfer
156 public void ejbRemove() throws EJBException
157 [
158 closeDatabaseResources() ;
159 >
160
161 // пассивация экземпляра компонента MoneyTransfer
162 publio void ejbPaesivate() throws EJBException
163 (
164 closeDatabaseResources() ;
165 )
166
167 // активация экземпляра компонента MoneyTransfer
168 public void ejbActivate() throws EJBException
169 {
170 openDatabaseResources О .■'
Сеансовые компоненты EJB и распределенные транзакции
345
L71 }
172
173 // закрытие соединений с бааой данных и подготовленных операторов
174 private void closeDatabaseResources() throws EJBException
175 i
176 // закрытие ресурсов базы данных
177 try {
17В
179 // закрытие подготовленных операторов
180 depositStatement.close();
181 depositStateroent = null;
182
1B3 withdrawalStatement.close();
184 withdrawalStatement = null;
1B5
186 // закрытие соединений с базой данных
187 bankOneConnection.close();
188 bankOneConnection = null;
189
190 bankTwoConnection.close();
191 bankTwoConnection = null;
192 1 '
193
194 // обработка исключения при закрытии соединений с базой данных
195 catch { SQLException sqlException ) {
196 throw new EJBExceptioft( sqlException );
197 )
19B
199 } // конец метода closeDatabaseConnections
200
201 // открытие соединений с базой данных и создание подготовленных
операторой
202 private void openDatabaseResources() throws EJBException
203 {
204 // поиск источников данных для BankABC и BankXYZ и
205 // соединений с ними
206 try {
207 Context initialContext = new initialContext (J ;
208
209 // получение ссылки на источник данных из каталога JTIDI
210 OataSource dataSource = ( DataSource )
211 initialContext.lookup(
212 "java:co>np/env/jdbc/BankABC" ) ;
213
214 // получение соединения Connection из источника данных
DataSource
215 bankOneConnection = dataSource.getConnection();
216
217 dataSource = ( DataSource) initialContext.lookup(
218 "java:comp/env/jdbc/BankXYZ" );
219
220 bankTwoConnection = dataSource.getConnection();
221
222 // подготовка оператора снятия денег со счета #12345 а
223 // банке BankABC
346
Глава 6
224 String withdrawal = "UPDATE Account SET balance = " +
225 "balance - ? WHERE accountID = 12345";
226
227 withdrawalStatement =
228 bankOneConnection.prepareStatement( withdrawal );
229
230 // подготовка оператора зачисления денег на счет
231 // #54321 в банке BankXYZ
232 String deposit = "UPDATE Account SET balance = " +
233 "balance + ? WHERE accountID = 54321";
234
235 depositStatement =
236 bankTwoConnection.prepareStatement( deposit );
237
238 } // коней блока try
239
240 // обработка исключения, если источник данных не найден
в каталоге
241 catch ( NamingException namingException ) (
242 throw new EJBException( namingException );
243 }
244
245 // обработка исключения при получении соедиения с источником
даяиих
246 catch { SQLException sqlException ) {
247 throw new EJBException( sqlException );
248 }
249
250 } // конец метода openOatabaseConnections
251 }
Рис. 6.27. Реализация MoneyTransferEIB удаленного интерфейса MoneyTransfer
с использованием разграничения транзакции с управлением на стороне компонента
В строках 4.8—51 осуществляется снятие заданной суммы amount со счета в базе
данных ВапкЛВС. В строках 53-56 осуществляется зачисление заданной сумму
amount на счет в базе данных BankXYZ. Оба эти обновления являются частью
транзакции transaction, начатой в строке 34, несмотря на то, что они
применяются к различным базам данных. В строке 59 осуществляется завершение
транзакции transaction для сохранения обновлений каждой из баз данных.
В строках 65-79 перехватываются исключения Exception, возбуждаемые
в строках 46-61. В строке 69 вызывается метод rollback интерфейса UserTraits-
action. для отмены любых обновлений, которые имели место внутри границ
транзакции. Метод rollback обеспечивает, что если какая-либо важная часть метода
transfer оканчивается неудачей, все изменения, сделанные в обеих базах данных,
отменяются, и гарантируется целостность данных. В строках 73-75
перехватывается исключение SystemException, которое возбуждается методом rollback
интерфейса JJsezTraBsactios, если выполнение метода rollback оканчивается неудачей.
В строках 74 и 78 возбуждаются исключения EJBException, которые облегчают
отладку приложения.
Методы getBankABCBalance (строки 84-111) и getBankXYZBalance (строки
114-141) выполняют простые операторы SQL SELECT для извлечения балансов по
счетам в каждом из банков. Методы set Session Con text (строки 144-150)
и ejbActivate (строки 168-171) вызывают метод openDatabascResource (строки
202-250) для создания соединений (Connection) и подготовленных операторов
Сеансовые компоненты EJB и распределенные транзакции
347
(PreparedStatement) для каждой из баз данных, которые будут использоваться
в течение срока существования экземпляра EJB-компонента MoneyTransfer.
Методы ejbRemove (строки 156-159) и ejbPassivate (строки 162-165) вызывают
метод closeDatabaseResource (строки 174-199) для закрытия соединений и
подготовленных операторов.
6.4.3. Разграничение транзакций с управлением на стороне
контейнера
Разграничение транзакций с управлением на стороне контейнера дает
возможность разработчику EJB-компонента реализовывать EJB без указания границ
действия транзакции. Разработчик EJB-компонента декларативно предоставляет
семантику разграничения транзакций при развертывании приложения.
Компонент MoneyTransferEJB (рис. 6.28) реализует удаленный интерфейс
MoneyTransfer с использованием разграничения транзакции, управляемого
контейнером. Метод transfer (строки 25-51) схож с методом transfer компонента,
представленного на рис. 6.27. Однако обратите внимание, что эта версия метода
transfer не объявляет каких-иибо границ транзакции, предоставляя сделать зто
администратору развертывания EJB-компонента. Администратор развертывания
компонента EJB задает границы транзакции, устанавливая один из шести типов
транзакций, приведенных в таблице на рис. 6.29.
1 // MoneyTrajisferEJB,java
2 // MoneyTransferEJB - сеансовый кохпонент EJB без состояния для
3 // перевода, денег со счета в банке ВапУ&ВС на счет в банке BankXXZ с
4 // использованием разграничения транзакции, управляемого контейнером.
5 package com.deitel.advjhtpl.ejb.transactions.containermanaged;
6
1 i/ Базовые библиотеки Java
8 import java.util.*;
9 import java.sql.*;
10
11 // Стандартные расширения Java
12 import javax.ejb.*;
13 import javax.naming.*;
14 import j avax.sql.*;
15
16 public class MoneyTransferEJB implements SessionBean {
17
1Й private SassionContext sessionContext;
19 private Connection bankOneConnection;
20 private Connection bankTwoConnection;
21 private PreparedStatement *itlidxawalStatement;
22 private PreparedStatement depositStatement;
23
24 // перевод денег- из банка BankABC в банк BankXYZ
25 public void transfer( double amount ) throws EJBException
26 I
27 // перевод денег со счета в банке BankABC на счет в банке
28 // B^nJtXYZ с использованием рааграиичевдя тгранаахции под
управлением контейнера
29 try {
30
31 withdrawalstatement.setDouble( 1, amount );
32
33 // снятие денег со счета в банке Вап*АВС
348
Глава Б
34 withdrawalstatement.executeUpdate<);
35
36 depositStatement.setDouble( 1, amount );
37
3B // помещение денег на счет в банке BankXYZ
39 depoaitStatement.oxecutoOpdatc();
40
41 i // кошен блока try
42
43 // обработка исключения при снятии и зачислении денег на счет
44 catch { SQLException sqlException ) {
45
46 // Возбуждение исключения EJBException, указыважщего на
47 // неудачу перевода. Откат транзакции с контейнерным
управлением
48 throw new EJBException( sqlException );
49 }
50
51 } // конец метода transfer
52
53 // получение баланса счета в банке ВаякАВС
54 public double getBankABCBalance() throws EJBException
55 (
56 // получение баланса счета в банке ВапкАВС
57 try {
58
59 // выбор счета # 12345 для получения баланса
60 String select = "SELECT balance FROM Account " +
61 "WHERE accountID = 12345";
62
63 PreparedStatement selectstatement =
64 bankOneConnection.prep&reStatement( select );
65
66 ResuitSet resultSet = selectstatement.executeQuery{);
67
6B // получение первой записи в ResultSet и возврат баланса
69 if ( resultSet.next() )
70 return resultSet.getDouble( "balance" );
71 else
72 throw new EJBException( "Account not found" );
73
74 J // конец блока try
75
76 // обработка исключения при получении баланса счета
77 catch ( SQLException sqlException ) {
78 throw new EJBException( sqlException );
79 }
80
81 } // конец метода getBankABCBalance
82
83 // получение баланса счета в банке BankXYZ
84 public double getBankX¥2Balance() throws EJBException
85 {
86 // получение баланса счета в банке BankXYZ
87 try {
88
89 // выбор счета # 54321 для получения баланса
90 String select = "SELECT balance ИЮМ Account " +■
Сеансовые компоненты EJB и распределенные транзакции
349
91 "WHEBE accountlD = S4321";
92
93 PreparedStatement selectStatement =
94 banJcTwoConnection.prepareStatement( select );
95
96 ResultSet resultSet = selectStatement.executeQuery();
97
98 // получение первой записи в ResultSet и возврат баланса
99 if ( resultSet.nextО )
100 return resultSet.getDouble( "balance" );
101 else
102 throw new EJBException( "Account not found" );
103
104 } // конец блока try
105
106 // обработка исключения при получении баланса счета
107 catch ( SQLException sqlExcepfcion ) {
108 throw new EJBException( sqlException );
109 }
110
111 } // конец метода getBankXYZBalance
112
113 // задание контекста SessionContext
114 public void setSessionContext( SessionContext context )
115 throws EJBException
116 {
117 SessionContext = context;
118
119 openDatabaseResourcesО;
120 }
121
122 // создание экземпляра компонента MoneyTransfer
123 public void ejbCraateО {}
124
125 // удаление экземпляра компонента MoneyTransfer
126 public void ejbRemove(} throws EJBException
127 {
128 closeDatabaseResourcesO;
129 }
130
131 // пассивация экземпляра компонента MoneyTransfer
132 public void ejbPassivate0 throws EJBException
133 (
134 closeDatabaseResourcesO;
135 }
136
137 // активация экземпляра компонента MoneyTransfer
138 public void ejbActivate() throws EJBException
139 {
140 openDatabaseResources 0;
141 }
142
143 // Закрытие соединений с базой данных и подготовленных операторов
144 private void closeDatabaseResourcesO throws EJBException
145 {
146 // закрытие ресурсов базы данных
147 try {
148
Глава б
149 // закрытие подготовленных операторов
150 depositstatement.close() ;
151 depositstatement = null;
152
153 withdrawalStatement.close();
154 withdrawalStatement = null ,-
155
156 // закрытие соединений с базой данных
157 bankOneConnection.close();
158 bankOneConnection = null;
159
160 bankTwoConnection.closet);
161 bankTwoConnection = null;
162 }
163
164 // обработка исключения при закрытии соединений с базой данных
165 catch < SQLExoeption sqlSxdsption } (
166 throw new EJBException( sqlException );
167 }
168
169 } // конец метода closeDatabaeeConnectiona
170
171 // открытие соединений с базой данных и подготовленных операторов
172 private void openDatabaseResources0 throws EJBException
173 {
174 // поиск источников данных для BankABC и BankXYZ и
175 // создание соединений с ними
17 6 try {
177 Context initialContext = new InitialContext();
178
179 // получение ссылки на источник данных из каталога JNDI
180 DataSourсе dataSource = { DataSource )
181 initialContext.lookup{
182 "java:comp/env/jdbc/BankABC" );
183
184 // получение соединения от источника данных
185 bankOneConnection = dataSource.getConnectiont);
186
187 dataSource = ( DataSource) initialContext.lookup(
188 "java:conip/env/;}dbc/BankXYZ" );
189
190 bankTwoConnection = dataSource.getConnectionO:
191
192 // подготовка оператора снятия денег со счета
193 // #12345 в банке BankABC
194 String withdrawal = "UPDATE Account SET balance = " +
195 "balance - ? WHERE accountID = 12345";
196
197 withdrawalStatement =
196 bankOneConnection.prepareStatement{ withdrawal );
199
200 // подготовка оператора зачисления денег на очах
201 // #54321 в банке BankXYZ
202 String deposit = "UPDATE Account SET balance = " +
203 "balance + ? WHERE accountID = 54321";
гол
205 depositstatement =
206 bankTwoConnection.preparestatement( deposit );
Сеансовые компоненты EJB и распределенные транзакции
351
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221 }
} // конец блока try
// обработка исключения, если источник данных не найден
в каталоге
catch { NamingExcepfcion namingException ) {
throw new EJBExceptian ( namingException 'j ;
}
// обработка исключения при получении соединения с источником
данных
catch { SQLExceptiort sqlException ) {
throw new EJBException( sqlExeeption );
)
} // конец метода openDatabaseConnections
Рис, 6.28. Реализация MoneyTransferEJB удаленного интерфейса Money!ransfer,
использующая разграничение транзакций с управлением на стороне контейнера
В строке 66 возбуждается исключение EJBException в ответ на любое
исключение SQLException, возбужденное в строках 29-41. Контейнер EJBContainer
осуществляет откат текущей транзакции transaction., если метод transfer возбуждает
исключение EJBException (строка 48).
В таблице на рис. 6.29 приведен перечень существующих типов транзакций,
применяемых при контейнерном управлении разграничением. Администратор
развертывания задает тип транзакции для каждого бизнес-метода при
развертывании приложения.
Тип транзакции Описание
NotSupported
Метод не поддерживает транзакции. Контейнер EJB
приостанавливает контекст существующей транзакции, если метод
вызывается внутри контекста транзакции.
Required | Метод требует применения транзакции. Контейнер EJB создает новую
! транзакцию, если метод вызывается без контекста существующей
| транзакции, и завершает транзакцию в конце выполнения метода.
Supports Метод поддерживает транзакции. Контейнер EJB не будэт создаватэ
новую транзакцию, если метод вызывается без контекста
1 существующей транзакции, но будет выполнять метод как часть
1 существующей транзакций, если таковая имеется.
RequiresNew Метод требует новой транзакции. Контейнер EJB приостанавливает
контекст существующей транзакции и начинает новую транзакцию,
1 если метод вызывается как часть Другой транзакции.
Mandatory
Never
Метод должен выполняться в контексте существующей транзакции.
Контейнер EJB возбуждает исключение
TransactionRequiredException, если метод вызывается без
действительного контекста транзакции.
Метод не должен выполняться в контексте транзакции. Контейнер
EJB возбуждает исключение RemoteExceptron. если метод
вызывается внутри контекста транзакции. J
Рис. 6,29. Типы транзакций для разграничения транзакций с упразлением на стороне
контейнера
352
Глава б
6.4.4. Клиентский EJB-компонент MoneyTransfer
Приложение MoneyTransferEJBClient (рис. 6.30) предоставляет
пользовательский интерфейс для взаимодействия с EJB-компонентом MoneyTransfer. В
строках 24-26 объявляются объекты JTextField для отображения балансов счетов
и приема от пользователя перечисляемой суммы transfer amount. В строке 34
вызывается метод createMoneyTransfer для создания нового EJB-компонента
MoneyTransfer. В строке 37 вызывается метод createGUI для создания и
размещения компонентов графического интерфейса пользователя (GUI) для приложения.
GUI состоит из текстовых полей JTextField для отображения балансов счетов
и ввода переводимой суммы, а также кнопки JButton для осуществления
перевода. В строке 40 вызывается метод display Balances для отображения текущих
балансов счетов в банках ВапкАВС и BankXYZ.
Метод createMoneyTransfer (строки 47-80) использует интерфейс MoneyTrans-
ferHome для создания экземпляра EJB-компонента MoneyTransfer. В строке 51
создается объект InitialContext для нахождения EJB-компонента MoneyTransfer
в каталоге JNDI. В строках 54—59 создается метод lookup класса InitialContext
для получения удаленной ссылки на интерфейс MoneyTransfer. В строке 62
создается новый экземпляр EJB-компонента MoneyTransfer путем вызова метода create
интерфейса MoneyTransferHome.
Метод getTransferButton (строки 108-142) создает кнопку JButton для
перевода средств со счета в банке ВапкАВС на счет в банке BankXYZ. В строках 120-124
осуществляется чтение переводимой сумму amount, введенной пользователем,
и вызывается метод transfer интерфейса MoneyTransfer. В строке 127 вызывается
метод displayRalances для обновления содержимого экрана новыми значениями
балансов счетов.
На рис. 6.30 показаны три копии содержимого экрана для приложения
MoneyTransferEJBClient. Введите любое допустимое значение в текстовое поле и
нажмите кнопку Transfer. EJB-компонент должен обновить записи в базах данных,
1 // MoneyTransferEJBClient.java
2 // MoneyTransferEJBClient ~ клиент для взаимодействия с
3 // EJB-компонентом MoneyTransfer.
4 package com.deitel.advjhtpl.ejb.transactions.client;
5
6 // Бааовые библиотеки Java
7 import 3 ava.awt.*;
8 import j ava.awt.event.*;
9 import java.rmi.*,-
10
11 // Стандартные расширения Java
12 import j avax.swing.*;
13 import javax.ejb.*;
14 import javax.rmi.*;
15 import javax.naming. *;
16
17 // Библиотеки Deitel
18 import com. deitel. advjhtpl. ejb. transactions. *,-
19
20 public class MoneyTransferEJBClient extends JFrame {
21
22 private MoneyTransfer moneyTransfer;
23
24 private JTextField banXABCBalanceTextField;
25 private JTextField bankXYZBalanceTextField;
Сеансовые компоненты EJB и распределенные транзакции
353
26 private JTextField transierAmountTextField;
27
28 // конструктор MoneyTransferEJBClient
29 public MoneyTransferEJBClient( String JttDIName )
30 {
31 super( "MoneyTransferEJBClient" );
32
33 // создание EJB-компонента MoneyTransfer для перевода денег
34 createMoneyTransfer ( JNDINanse ) ;
35
36 // создание и размещение компонентов GUI
37 createGUIO ;
38
39 // отображение текущих балансов счетов в банках BankABC
и BankXYZ
40 displayBalances() ;
41
42 setsize( 400, 300 );
43 setVisible( true );
44 }
45
46 // создание EJB-компонента MoneyTransferEJB для перевода денег
47 private void createMoneyTransfer( String JNDIName )
48 {
49 // поиск EJB-компонента MoneyTransfее по заданному имени JNDIName
50 try {
51 InitialContext context = new InitialContext {) ;
52
53 // поиск EJB-хомпокеита MoneyTranafar
54 Object homeObject = context.lookup( JNDIName );
55
56 // получение интерфейса MoneyTransfer
57 MoneyTransferHome moneyTransferHome =
58 ( MoneyTransferHome ) PortableRemoteObject.narrow{
59 homeObject, MoneyTransferHome.class );
60
€1 // создание экземпляра EJB-компонента. MathTool
62 moneyTransfer = moneyTransferHome.create();
63
64 ) // конец блока try
65
66 tf обработка исключения при поиске EJB-компонента MoneyTransfer
67 catch ( NamingException naroingException ) {
68 namingException.printstacXTrace<);
69 ]
70
71 // обработка исключения при поиске EJB-компонента MoneyTransfer
72 catch ( CreateException createException ) t
73 createException.printStacxTraee(};
74 1
75
76 // обработка исключения при поиске EJB-компонента MoneyTransfer
77 catch ( RemoteException remoteException ) {
78 remoteException.printStackTraceО;
79 }
80 } // конец метода createMoneyTransfer
81
82 // отображение баланса счета в банке BankABC
83 private void displayBalances()
84 {
85 try {
86
87 // получение и отображение баланса счета в банке БапкАВС
85 double balance = moneyTransfer.getBankABCBalance();
89
90 bankABCBalanceTextField.setText(
91 String.valueOf( balance ) );
92
93 // получение и отображение баланса счета в банке BankXYZ
94 balance = moneyTransfer.getBankXYZBalance();
95
96 banJeXYZBalanceTextField.aetText(
97 String.valueOf{ balance ) );
98 }
99
100 // обработка исключения при вызове методов EJB-компонента
MoneyTransfer
101 catch ( RemoteEKception remoteException ) {
102 JOptionPane.showMessageDialog( this,
103 remoteException. getMessageO ) ;
104 }
105 } // конец нетода displayBalances
106
107 // совдание кнопки для перевода денег со счета на счет
105 private JButton getTransferButtonQ
109 {
110 JButton transferButton = new JButton( "Transfer" );
111
112 transfaxButton.addActiohListener(
113 new ActionListener() (
114
115 public void actionPerformedf ActionEvent event )
116 {
117 try (
118
119 // получение переводимой суммы из содержимого
поля JTextField
120 double amount = Double.parseDouble(
121 transferAmountTextField.getTextO );
122
123 // перевод денег
124 moneyTransfer.transfer( amount );
125
126 f f отображение новых балансов
127 displayBalances();
125 )
129
130 // обработка исключений при переводе денег
131 catch ( RemoteException remoteException ) {
132 JOptionPane.showMessageDialog(
133 MoneyTransferBJBClient.this,
134 remoteException.getMessage() );
135 }
136 } // конец метода aetionPerformed
137 }
138 ); // конец слушателя addActionListener
Сеансовые компоненты EJB и распределенные транзакции
355
139
140 return transferButton,-
141
142 } // коней, метода getTtransferButton
143
144 // создание и размещение компонентов GUI
145 private void createGUX(}
146 {
147 // создание текстовых полей JTextFields для ввода И отображения
148 banfcABCBalanceTextTield = new JTeKtField( ID );
149 bankABCBalanceTextField.setEditable( false ) ,-
150
151 bankXYZBalanceTextField = new JTextField{ 10 );
152 bankXYZBalanceTextField.setEditable{ false );
153
154 transferAmountTextField = new JTextField( 10 );
155
156 // создание кнопки для перевода денег со счета на счет
157 JButton transferButton = getTransferButton();
158
159 // компоновка пользовательского интерфейса
160 Container contentPane = getContentPane{);
161 contentPane.setLayout( new GridLayout( 3, 2 ) );
162
163 contentPane.add( transferButton );
164 contentPane.add( transferAmountTextField );
165
166 contentPane.add( new JLabel( "Bank ABC Balance: " ) );
167 contentPane.add{ bankABCBalanceTextField );
168
169 contentPane.add( new JLabel( "Bank XYZ Balance: " ) );
170 contentPane,add( oankXYZBalatxceTextEield. ) ;
171
172 ) // конец метода createGUT
173
174 // получение слушателя WindowListener для выхода из приложения
175 private WindowListener getWindowListener()
176 I
177 // удаление EJB-коыпонента MoneyTransfer при выходе
пользователя из приложении
178 return new WindowAdapter() {
179
180 public void windowClosing{ WindowEvent event ) (
181
182 II удаление EJB-компонента MoneyTransfer
183 try {
184 moneyTransfer. remove () ;
185 }
186
187 // обработка исключения при удалении EJB-компонента
MoneyTransfer
188 catch ( RemoveException removeException ) {
189 removeException.printStacxTraceО;
190 System.exit{ 1 );
191 }
192
193 // обработка исключения при удалении EJB-коыпонента
MoneyTransfer
356
Глава 6
194
195
196
19"?
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217 J
ь-
catch ( RemoteException remoteException ) {
гелю teExcep ti on.prin tS tackTrace(>;
System.exit( 1 );
)
System.exit( 0 );
} // конец метода windowClosing
} // конец метода getWindowListener
// выполнение приложения
public static void main( String[] args )
{
// проверка, что пользователь предоставил имя JNDI для
EJB-компонента MoneyTransfer
if ( args.length '= 1 )
System.err.println(
"Usage: Java MoneyTransferEJBCIient <JNDI Hame>" );
// запуск приложения с использованием предоставленного имени JNDI
else
new MoneyTransferEJBCIient( args[ 0 ] };
)
tbtfunrn
?&W \i: Ш^-W ■-■■:':■
'a/ ■ ■■ 4t~i,.■ ■!..'• •:-■'•'. rts». ■ ..■;
вяцднД^.п^!-: ■ .. j«uJHfc -ft*,-■■■.■■■■ ■.■-■••■4
'"■',- " '■%..!■- ■". ■•'• •!,. ' ■'■ .*;. i'-.: ■■• i]
'->■• ■■■' %%. • •■■■ -■'"■;...n
£'#&: C-
■Л.-Л
-!■,-,. ;
^ ;;4t>'»*B*«t* .. ■/*/: , J1000.00
tmm.u
Ъ^г
Si'- *":... ■ ' ■ •*,: ;■■■> ■ <■* S.ji&* i-'. i- . *
Рис. 6.30. Приложение MoneyTransferEJBCIient для взаимодействия с EJB-компонентом
MoneyTransfer (часть 1)
Сеансовые компоненты EJB и распределенные транзакции
357
Рис. 6.30. Приложение MoneyTransferEJBCIient для взаимодействия с EJ В-компонентом
MoneyTransfer (часть 2)
6.4.5. Развертывание EJB-компонента MoneyTransfer
EJB-комяонент MoneyTransfer осуществляет доступ к двум базам данных,
которые хранят информацию о счете. В примерах используется система управления
базами данных Cloudscape, с которой мы познакомились в главе 4. Следуйте
инструкциям, приведенным в разделе 4.10.1, чтобы установить и настроить базы
данных для EJB-компонента MoneyTransfer. SQL-сценарий transacti.on.sql для баз
данных имеется в комплекте примеров к этой главе на сайте компании Deitel
www.deitel.com, а также на сайте издательства «Бином» www.binoni-press.ru/
books/adv_java2.htm. Добавьте следующий текст
|jdbc/BankABC[jdbc:cloudscape:rrai:BankXYZ;create=true;
| jdbc/BankXYZ | jdbc: cloudscape: rmi: BankXYZ,- create=true ;
в свойство jdbc.dataSource в файле C:\j2sdkeel-2.1\config\default.properties-
Развертывание каждой из версий EJB-компонента MoneyTransfer очень
похоже на развертывание других сеансовых компонентов EJB. При развертывании
EJB-компонента MoneyTransfer необходимо создать в мастере New Enterprise
Bean Wizard (рис. 6.31) ссылки на ресурсы для двух баз данных. Щелкните на
кнопке Add, чтобы добавить новую ссылку на ресурс. Заполните поля Coded Name
и JNDI Name строками jdbc/BankABC для базы данных BankABC (рис. 6.32)
и jdbc\BankXYZ для базы данных BankXYZ (рис. 6.33). В диалоговом окне
Transaction Management мастера New Enterprise Bean Wizard выберите Bean-
Managed Transaction для версии MoneyTransferEJB с управлением транзакцией
на стороне компонента (рис. 6.34) или Container-Managed Transaction для версии
MoneyTransferEJB с контейнерным управлением транзакцией (рис. 6.35). Для
EJB-компонента с контейнерным управлением выберите тип транзакции для
каждого метода (см. раздел 6.4.3, в котором дано описание типов транзакций). В
остальном процесс развертывания аналогичен развертыванию других сеансовых
EJB-компонвнтов. Перед выполнением MoneyTransferEJBCIient убедитесь, что
сервер Cloudscape запущен.
358
Глав
Рис. 6.31. Диалоговое окно ссылок на ресурсы Resource References мастера New
Enterprise Bean Wizard
Рис. 6.32. Добавление ссылки на ресурсы для банка ВапкАВС
Сеансовые компоненты EJB и распределенные транзакции
359
Рис. 6.33. Добавление ссыпки на ресурсы для банка BankXYZ
Рис. 6.34. Выбор управления Рис. 6.35. Выбор контейнерного
транзакциями на стороне компонента управления транзакциями
Bean-Managed Transaction Container-Managed Transaction
6.5. Ресурсы в Internet и во Всемирной паутине
Java.sun.com/products/ejb
Основная страница сайта корпорации Sun, сосвященного Enterprise Java Bean.
Содержит статьи, докумеатвцию и примеры.
www,javaworld.com/javaworld/topicalindex/jw-ti-ejb.htanl
Список статей, имеющих отношение к технологии EJB.
www . jguru. com/f a<j/h(jnie. j зр? topic=S JB
Список часто задаваемых вопросов (FAQ) no EJB на сайте jguru.com.
www.theserverside.com
TheServerside.com — сетевое сообщество разработчиков на J2EE. Здесь вы найдете
форумы, статьи и другие ресурсы для построения приложений с помощью J2EE.
360
Глава 6
Резюме
• Каждый компонент EJB состоит из удаленного интерфейса, собственного (* домашнего»)
интерфейса и реализации EJB-компонента.
• Удаленный интерфейс объявляет бизнес-методы, которые могут вызывать клиенты
EJB-компонента, Собственный интерфейс предоставляет методы create для создании
новых экземпляров EJB-компонента, методы finder для нахождения экземпляров
EJB-компонента и методы remove для удаления экземпляров EJB-компонента. Реализация
EJB-компонента определяет бизнес-методы, объявленные в удаленном интерфейсе, и
методы создания (create), удаления (remove) и поиска (finder) собственного интерфейса.
• Компоненты EJB имеют сложный жизненный цикл, который управляется контейнером
EJB. Контейнер EJB создает классы, которые реализуют собственный и удаленный
интерфейсы.
• Спецификация J2EE определяет шесть ролей для реализации корпоративных систем.
Каждая роль отвечает за формирование определенной части распределенного приложения.
• Удаленный интерфейс для компонента EJB объявляет бизнес-методы, которые может вы-
зывать клиент EJB-компонента. Удаленный интерфейс должен расширять интерфейс
javax.ejb.EJBObject.
■ Каждый метод удаленного интерфейса должен объявлять, что он возбуждает исключение
java.rmi.RejnoteException. Каждый метод также может возбуждать специфичные для
приложения исключения, например, Illegal Argument Except ion, если предоставленный
параметр не отвечает определенному критерию.
• Собственный интерфейс для компонента EJB объявляет методы для создания, удаления
и нахождения экземпляров EJB-компонента. Собственный интерфейс должен расширять
интерфейс javax.ejb.EJBHome.
• В зависимости от типа EJB-компонента (т.е. сеансовый компонент или
компонент-сущность) контейнер вызывает методы реализации EJB-компонента, которые соответствуют
методам создания (create), удаления (remove) и поиска (finder) собственного интерфейса.
• Реализация EJB-компонента определяет бизнес-методы, объявленные в удаленном
интерфейсе EJB-компонента, и методы создания (create), удаления (remove) и поиска (finder)
собственного интерфейса EJB-компокеита. Реализация EJ8-компонента должна также
реалнзовыватъ методы интерфейса javax.ejb.SessionBean для сеансовых компонентов
EJB или интерфейса javax.ejb.EntityBean для компонентов-сущностей EJB.
• Контейнер EJB управляет жизненным циклом, взаимодействиями с клиентами и
вызовами методов, транзакциями, безопасностью и исключениями для EJB-компонент а.
Клиенты EJB-компонента не взаимодействуют с EJB-компонентом напрямую. При вызове
клиентом бизнес-метода удаленного интерфейса EJB-компонента вызов сначала поступает
контейнеру EJB, который затем делегирует вызов бизнес-метода реализации
EJB-компонента.
• Сеансовые компоненты EJB существуют в течение сеанса данного клиента. Каждый
экземпляр сеансового компонента EJB ассоциируется с одним клиентом. Сеансовые
компоненты EJB могут манипулировать данными в базе данных, но, в отличие от
компонентов-сущностей EJB, сеансовые EJB-компоненты не являются сохраняемыми (персистент-
ными) и не представляют данные из баз данных напрямую.
• Сеансовые компоненты EJB с состоянием хранят информацию о состояпии между
вызовами бизнес-методов. Например, сеансовые компоненты EJB с состоянием могут хранить
информацию о содержимом магазинной тележки покупателя в процессе посещения
потребителем книжного Internet-магазина.
• Интерфейс SessionContext расширяет интерфейс EJBContainer, который предоставляет
методы для получения информации о контейнере EJB.
• Контейнер EJB вызывает метод ejbCreate, когда клиент вызывает метод create в
собственном интерфейсе. Реализация EJB-компонента должна предоставлять метод ejbCreate
для нахождения метода create, объявленного в собственном интерфейсе. Методы
ejbCreate должны иметь такое же количество и те же типы параметров, что и
соответствующие методы create.
• Контейнер EJB вызывает методы ejbRemove в ответ sa вызов метода remove собственного
интерфейса.
• Контейнер EJB вызывает метод ejbPassivate, если он определяет, что EJB-компоаент
больше не следует хранить в памяти.
Сеансовые компоненты EJB и распределенные транзакции
361
• Контейнер EJB вызывает метод ejbActivate для восстановления экземпляра
EJB-компонента, который контейнер ранее пассивировал. Коптейнер EJB активирует экземпляр
EJB-компонента, если клиент вызывает бизнес-метод этого экземпляра EJB-компонента.
• Технология RMI — ПОР дает возможность объектам RMI взаимодействовать с
компонентами CORBA, которые связываются друг с другом с помощью протокола Internet
Inter-Orb Protocol (ПОР). CORBA представляет собой независимую от языка
программирования инфраструктуру для построения распределенных систем. Чтобы обеспечить
совместимость между компонентами EJB и CORBA, EJB-компоненты взаимодействуют между
собой с помощью интерфейса RMI — ПОР.
» Сеансовые компоненты EJB без состояния не сохраняют информацию между вызовами
бизнес-методов. Как результат, при ответе на клиентский запрос может быть использован
любой экземпляр сеансового компонента EJB. Это способствует более высокой
производительности сеансовых компонентов EJB без состояния в сравнении с сеансовыми
компонентами EJB с состоянием.
• Java 2 Enterprise Edition поддерживает распределенные транзакции. Распределенная
транзакция — это транзакция, которая имеет дело с несколькими базами данных и
несколькими серверами EJB.
• В J2EE поддерживается два метода для определения границ действий транзакций:
разграничение транзакций с управлением ка стороне компонента и разграничение
транзакций с управлением па стороне контейнера.
• Разграничение транзакций с управлением на стороне компонентам требует, чтобы
разработчик EJB-компонента вручную писал код Для границ действия транзакций в EJB-kom-
понентах, используя средства API Java Transaction Services. Разграничение транзакций
с управлением на стороне контейнера дает возможность разработчику EJB-компонента
декларативно задавать границы транзакций при развертывании EJB-компонентов.
• Разграничение транзакций с управлением на стороне компонента может использоваться
только для сеансовых компонентов EJB,
• Разграничение транзакций с контейнерным управлением дает возможность разработчику
EJB реализовывать EJB-компонент без указания границ транзакции. Разработчик
EJB-компонента декларативно предоставляет семантику разграничения транзакций при
развертывании приложения.
Терминология
application server — сервер приложений
bean-managed transaction demarcation —
разграничение транзакций с управлением
на стороне компонента
business methods — бизнес-методы
container-managed transaction
demarcation — разграничение транзакций
с управлением на стороне контейнера
CORBA (Common Object Request Broker
Architecture), технология построения
распределенных приложений
create methods — методы создания
distributed transaction — распределенная
транзакция
EJB container — контейнер EJB
EJB implementation — реализация EJB
EJB server — сервер EJB
ejbActivate, метод
EJBContext, интерфейс
ejbCreate, методы
ejbPassivate, метод
ejbRemote, метод
Enterprise Java Beans (EJB)
entity EJB — компонент-сущность EJB
home interface - - собственный интерфейс
IllegalArgumentException, исключение
Internet Inter-Orb Protocol (HOP), протокол
J*2KK (Java 2 Enterprise Edition)
Java Transaction Services (JTS), сервисы
управления транзакциями Java
j a va. r mi. RemoteExceptio n
java:eomp/env — контекст идентификации
j a va x. e jb .EJBHome
javax.ejb.EJBObjeet
javax.ejb.SessionBean
JNDI (Java Naming and Directory Interface)
directory — служба каталогов JNDI
least recently used — замещение компонента
с наиболее давним использованием
naming context — контекст идентификации
remote interface of on EJB — удаленный
интерфейс EJB-компонента
remove methods — методы удаления
RMI — ПОР, технология построения
распределенных приложений
session EJB — сеансовый компонент EJB
SessionContext, интерфейс
statefui session EJB — сеансовый компонент
EJB с состоянием
stateless session EJB — сеансовый
компонент EJB без состояния
362
Глава 6
Упражнения для самоконтроля
6.1. Назовите два основных типа сеансовых компонентов EJB. В чем состоит главное
различие между ними?
6.2. Назовите три объекта Java, которые разработчик компонентов EJB должен
предоставить для каждого EJB-компонента.
6.3. Какие функции возложены на контейнер EJB?
6.4. Как клиент получает удаленную ссылку на экземпляр EJB-компонента?
6.5. Какие тины разграничения транзакций могут использовать EJB-компоненты? Какие
преимущества имеет каждый из типов?
Ответы на упражнения для самоконтроля
6.1. Существуют сеансовые компоненты EJB с состоянием и сеансовые компоненты EJB без
состояния. Сеансовые компоненты EJB с состоянием хранят информацию о состоянии
между вызовами бизнес-методов в ходе сеансе с клиентом. Сеансовые компоненты EJB
без состояния не сохраняют информацию о состоянии между вызовами бизнес-методов.
6.2. Разработчик компонента EJB должен предоставить удаленный интерфейс,
собственный интерфейс и реализацию EJВ-компонента.
6.3. Контейнер EJB отвечает за управление жизненным циклом EJB-компонента-
Контейнер EJB создает классы для реализации собственного и удаленного интерфейсов и
делегирует вызов бизнес-методов реализациям EJB-компонентам, предоставленным
разработчиком. Контейнер EJB также предоставляет ресурсы, например, соединения с
базами данных, во время выполнения приложения, а также управляет жизненным циклом
компонентов,
6.4. Клиент ищет собственный интерфейс EJB-компонента в каталоге JNDI. Для сеансовых
компонентов EJB клиент после этого вызывает один из методов создания (create)
собственного интерфейса. Для компонентов-сущностей EJB клиент может вызвать один нз
методов создания (create) или поиска (finder) собственного интерфейса.
6.5. EJB-компоневты могут использовать либо разграничение транзакций с управлением
на стороне компонента, либо разграничение транзакций с управлением на стороне
контейнера. Разграничение транзакций с управлением на стороне компонента дает
возможность разработчику избирательно управлять границами действия транзакций.
Разграничение транзакций с управлением на стороне контейнера упрощает
реализацию EJB-компонента, позволяя разработчику EJB-компонента декларативно задавать
границы транзакции на этапе развертывания.
Упражнения
6.6. Сеансовые компоненты EJB бее состояния имеют преимущество в производительности
перед сеансовыми компонентами без состояния. Преобразуйте пример,
представленный на рис. 6.3, 6.4, 6.5 и 6.6, использовав вместо сеансового EJB-компонента с
состоянием сеансовый EJB-компонент без состояния.
6.7. Добавьте новый рекурсивный бизнес-метод power(base, exponent) в EJB-компонент
MathTool (рис. 6.21, 6.22, 6.23), который при вызове возвращает
base "4"Mdt
где base — основание, a exponent — показатель степени. Например, power(3, 4) =
3*3*3*3. Если exponent не является целым числом, большим или равным 1,
должно возбуждаться исключение IllegalArgumentException. [Подсказка. На шаге
рекурсии должно использоваться отношение
base -ч™»* = base * base «P°«™-i
а условие окончания наступает при равенстве 1 показателя степени exponent, поскольку
base1 = basa
Модифицируйте клиента, представленного на рис. 6.24, чтобы дать возможность
пользователю вводить значения для основания степени base и показателя степени
exponent.]
7
Компоненты EJB с данными
Цели
• Понять, как EJB* компоненты
с данными обеспечивают
сохранение данных.
• Получить представление
о проблемах, связанных
с синхронизацией
EJB-компонентов с данными,
хранящимися в базе данных.
• Познакомиться с жизненным
циклом EJB-Компонента
с данными.
• Узнать о преимуществах
и недостатках управления
персистентностью на стороне
контейнера и управления
персистентностью на стороне
компонента.
В бизнесе нет ничего более
важного, чем своевременная
доставка.
Джозеф Эдисон
Как известно, смысл
определяется толкованием.
Джордж Элиот
События, которые предопределены,
требуют минимального вмешательства.
Они совершаются без нашего участия, и м
вдруг осознаем, что дело, к которому мы
боялись приступить, уже сделано.
Амедия Барр
364
Глава 7
7.1. Введение
Основной составляющей корпоративного приложения является
информационный уровень, хранящий данные, используемые приложением. В этой главе будут
рассмотрены компоненты EJR для управления данными
(компоненты-сущности), которые дают возможность разработчикам создавать представления
данных, полученных от информационного уровня (например, данных, хранящихся
в реляционной базе данных), на основе объектов. Контейнеры EJB предоставляют
расширенные функции, которые упрощают разработку компонентов-сущностей
EJB. Так, на основе информации, предоставленной на этапе развертывания
(например, посредством SQL-запросов), контейнер EJB-сущности может
автоматически генерировать код для сохранения и извлечения данных, представляемых
EJB-компонентом. Для компонентов-сущностей EJB, которые представляют более
сложные данные (например, данные, хранящиеся в дескольких таблицах базы
данных), программист может самостоятельно написать код для сохранения и
извлечения данных.
В этой главе будут рассмотрены две версии компонента-сущности Е JB, который
отображает информацию об одном сотруднике компании. В первой версии
компонент-сущность EJB использует JDBC для сохранения данных в реляционной базе
данных. Во второй версии используется способность контейнера управлять
сохранением и извлечением данных, что позволяет упростить реализацию EJB-компо-
нента. Изучив эту главу, вы сможете создавать и развертывать
компоненты-сущности EJB, посредством которых компоненты бизнес-логики, такие как сеансовые
компоненты EJB (см. главу 6), могут осуществлять доступ к данным
информационного уровня.
Компоненты EJB с данными
365
7.2. Обзор EJB-компонентов с данными
Каждый экземпляр компонента-сущности EJB отображает определенный блок
данных, например, запись в таблице базы данных. Имеется два типа
компонентов-сущностей EJB: с персистентностью (сохраняемостью)1, управляемой
самим компонентом, и с персистентностью, управляемой контейнером.
Компоненты-сущности EJB, в которых используется управление персистентностью на
стороне компонента, должны сами реализовывать код для хранения и извлечения
данных из источников данных, которые они представляют. Например,
компонент-сущность EJB, в котором управление персистентностью осуществляется
самим компонентом, может использовать JDBC для сохранения и извлечения
данных из реляционной базы данных. Компоненты-сущности EJB, в которых
используется управление персистентностью на стороне контейнера, реализуют вызовы
для доступа к информации из постоянных источников данных, основываясь на
возможностях контейнера EJB. Администратор развертывания должен
предоставить информацию об источнике данных при развертывании EJB-компонента.
Компоненты-сущности EJB предоставляют методы create для создания новых
экземпляров EJB-компонентов, методы remove для удаления экземпляров
EJB-компонентов и методы finder для поиска экземпляров EJB-компонентов. Для
компонентов-сущностей EJB, которые представляют информацию, хранящуюся
в базе данных, каждый метод create выполняет операции вставки INSEBT для
создания новых записей в базе данных, а каждый метод remove выполняет
операции DELETE для удаления записей из базы данных. Каждый метод finder находит
экземпляры компонентов-сущностей EJB, которые удовлетворяют определенному
критерию поиска (например, с помощью операций SELECT). Далее в этой главе
мы обсудим каждый из этих методов.
7.3. Компонент-сущность EJB Employee,
хранящий информацию о сотруднике
В последующих разделах мы создадим компонент-сущность EJB (Employee),
который хранит информацию о сотруднике. Будут рассмотрены две реализации
EJB-компонента Employee. В первой реализации (раздел 7.5) для хранения и
извлечения информации о сотруднике из основной базы данных используется
управление персистентностью на стороне самого компонента. Во второй реализации
(раздел 7.6) используется управление персистентностью на стороне контейнера. Обе
эти реализации используют один и тот же удаленный интерфейс Employee и
собственный интерфейс EmployeeHome, которые будут рассмотрены в разделе 7.4.
Для хранения данных о сотрудниках будет использоваться база данных
Cloudscape Employee. Чтобы создать базу данных Employee, выполните
SQL-сценарий employee.sql, который имеется в комплекте примеров для книги на сайте
компании Deitel www.deitel.com, а также на сайте издательства «Бином» www.binom-
press.ru/books/advjava2.htm. Инструкции по выполнению SQL-сценариев
в Cloudscape можно найти к главе 4. Чтобы настроить эталонную реализацию J2EE
для работы с базой данных Employee, добавьте текст
| jdbc/Employee|jdbc:cloudscape:rmi:Employee;create=true
в конец значения свойства jdbc.datasou.rces в файле конфигурации
default.properties J2EE.
Используется также термин «постоянство*. — Прим. ред.
Збб
Глава 7
7.4. Собственный и удаленный интерфейсы
EJB-компонента Employee
Удаленный интерфейс Employee (рис. 7.1) предоставляет методы для задания
и получения информации о сотруднике. Обратите внимание, что интерфейс
Employee расширяет интерфейс EJBObject (строка 11). Это обязательно для всех
удаленных интерфейсов EJB. Удаленный интерфейс Employee предоставляет
методы set и get для каждого свойства объекта Employee, включая socialSeeurity
Nnmber, firstName, lastName, title и salary. Для свойства employee ID метод set
не предусмотрен, поскольку это поле является первичным ключом. Каждый метод
set и get возбуждает исключение RemoveException. Это обязательно для всех
методов, определенных в удаленном интерфейсе.
1 // Employee.java
2 // Employee - удаленный интерфейс для БJB-компонента Address.
3 package com.deitel.advjhtpl.ejb.entity;
4
5 // Базовые библиотеки Java
6 import java.rmi.RemoteException;
7
8 // Стандартные расширения Java
9 import javax.ejb.EJBObject;
10
11 public interface Employee extends EJBObject {
12
13 // получение идентификатора сотрудника EmpioyeelD
14 public Integer getEmployeelD() throws RemoteException;
15
16 // задание номера свидетельства социального страхования
17 public void setSocialSecurityNumber( String number )
18 throws RemoteException;
19
20 // получение номера свидетельства социального страхования
21 public String getSocialSecurityNumber()
22 throws RemoteException;
23
24 // задание имени
25 public void setFirstName( String name )
26 throws RemoteException,-
27
28 // получение имени
29 public String getFirstName() throws RemoteException;
30
31 // задание фамилии
32 public void setLastName( String name )
33 throws RemoteException;
34
35 // получение фамилии
36 public String getiastName() throws RemoteException;
37
38 // задание должности
39 public void setTitle( String title )
40 throws RemoteException;
41
42 // получение должности
Компоненты EJB с данными
367
43 public String getTitle{) throws RemoteException;
44
45 // задание величины оклада
46 public void setSalary{ Double salary ) throws RemoteException;
47
48 // получение величины оклада
49 public Double getSalaryO throws RemoteException;
50 }
Рис. 7.1. Удаленный интерфейс Employee для задания и получения информации
о сотруднике
Экземпляр EJB-компонента представляет определенную строку в
соответствующей таблице базы данных. Собственный интерфейс для компонента-сущности EJB
определяет методы finder для поиска определенных строк в таблице и методы
create для вставки новых записей.
Интерфейс EmpIoyeeHome (рис. 7.2) предоставляет метод поиска
findByPrimary Key (строки 15-16) для нахождения экземпляров EJB-компонента Employee на
основе первичного ключа. Первичным ключом для EJB-компонента Employee
является employeelD. Метод findByPrimaryKey возбуждает исключение Finder-
Exception, если сотрудник с указанным первичным ключом primaryRey не может
быть найден. Метод create (строки 19-20) создает новые экземпляры
EJB-компонента Employee. Метод create возбуждает исключение CreateException, если
возникает проблема при создании экземпляра EJB-компонента.
1 // EmpIoyeeHome.Java
2 // EmpIoyeeHome - собственный интерфейс для EJB-компонента Employee.
3 package coni.deiteX . advjtitpl *e jb .entity;
4
5 // Базовые библиотеки Java
6 import java.rmi.*;
1 import java.util.*;
8
9 // Стандартные расширения Java
10 import javax.ejb.*;
11
12 public interface EmpIoyeeHome extends EJBHome {
13
14 // поиск сотрудника по заданному первичному ключу
15 public Employee findByPrimaryKey( Integer primaryKey )
16 throws RemoteException, FinderException;
П
18 // создание нового EJB-компонента Employee
19 public Employee create{ Integer primaryKey )
20 throws RemoteException, CreateException;
21 }
Рис. 7.2. Интерфейс EmpIoyeeHome для нахождения и создания EJB-компонентов
Employee
Метод findByPrimaryKey является одним из методов поиска для
компонентов-сущностей EJB. Каждый компонент-сущность EJB должен иметь метод
findByPrimaryKey, который принимает в качестве аргумента класс первичного
ключа компонента EJB. Компоненты-сущности EJB также могут определять
дополнительные методы поиска. Имя метода поиска должно начинаться с fiadBy
368
Глава 7
и заканчиваться именем свойства, используемым в качестве критерия поиска.
Например, метод поиска для нахождения сотрудников на основе значения свойства
title должен носить имя findByTitle. Метод поиска для нахождения сотрудников,
имеющих определенный уровень заработной платы, должен носить имя findBy-
Salary Range.
7.5. EJB-компонент Employee с персистентностью,
управляемой компонентом
В этом разделе описывается EJB-компонент Employee с персистентностью,
управляемой компонентом, и осуществляется его развертывание. В реализации
с управлением персистентностью на стороне компонента для хранения сведений
о сотруднике в базе данных используются средства JDBC.
7.5.1. Реализация EJ В-компонента Employee
На рис. 7.3 представлена реализация EJB-компонента Employee, в которой
используется управление персистентностью на стороне компонента. Класс
EmployeeEJB реализует интерфейс EntityBean (строка 15). Все реализации
компонента-сущности EJB должны реализовывать интерфейс EntityBean. В строке 17
объявляется ссылка типа EntityContext для EJB-компонента entityContext.
Entity Con text предоставляет собой EJB-компонент, содержащий информацию о
контейнере, в котором развернут EJB-компонент. Объект Connection (строка 18)
представляет соединение EJB-компонента с базой данных Employee. В строках 20-25
объявляются частные переменные экземпляра, которые кэшируют данные,
извлеченные из базы данных, и обновления, внесенные клиентом.
1 // EmployeeEJB.Java
2 // EmployeeEJB - компонент-сущность EJB, который сам осуществляет
3 // управление персистентностью для хранения сведений о сотруднике
в базе данных.
4 package com.dei tel.advj htpl.e jb.en ti ty.bmp;
5
6 // Базовые библиотеки Java
7 import java.sql.*;
8 import java.rmi.RemoteException;
9
10 // Стандартные расширения Java
11 import javax.ejb.*;
12 import javax.sql.*;
13 import javax.naming.*;
14
15 public class EmployeeEJB implements EntityBean {
16
17 private EntityContext entityContext;
IS private Connection connection;
19
20 private Integer employeelD;
21 private String socialSecurityNuniber;
22 private String firstName;
23 private String lastName;
24 private String title;
25 private Double salary;
26
Компоненты EJB с данными
369
// получение идентификатора сотрудника EmployeelD
public Integer getEmployeelD()
return employeelD;
// задание номера свидетельства социального страхования
public void ssetSocialSecurityNumber ( String number }
socialSecurityltumber = number;
// получение номера свидетельства социального страхования
public String getSocialSecurityNumber()
return socialSecurityNumber;
// задание имени
public void setFirstNarae( String name )
firstName = name;
// получение имени
public String getFirstName()
return firatName;
// задание фамилии
public void setLastKame{ String name )
lastName = name;
// получение фамилии
public String getiastMame()
return lastName;
// задание должности
public void setTitle( String jobTitle )
title = jobTitle;
// получение должности
public String getTitleO
return title;
// задание величины оклада
public void aetSalary( Double amount )
370
Глава 7
83 {
84 salary = amount;
85 J
86
87 // получение величины оклада
88 public Double getSalaryO
89 {
90 return salary;
91 }
92
93 // создание нового объекта Employee
94 public Integer ejbCreatef Integer primaryKey )
95 throws CreateException
96 {
97 employeelD = primaryKey,-
98
99 // вставка (IHSERT) сведений о новом сотруднике в базу данных
100 try {
101
102 // создание оператора INSERT
103 String insert = "INSERT INTO Employee " +
104 "( employeelD ) VALUES ( ? )" ;
105
106 // создание подготовленного оператора PreparedStatement
для операции вставки
107 PreparedStatement insertstatement =
108 connection.prepares tatament( insert );
109
110 // задание значений для оператора PreparedStatement
111 insertstatement.setlnt( 1, employeelD.intValue() );
112
113 // выполнение вставки (INSERT) и закрытие оператора
PxeparedStatement
114 insertstatement.executeUpdate();
115 insertstatement.close();
116
117 return employeelD;
118 }
119
120 // возбуждение исключения EJBException, если вставка
оканчивается неудачей
121 catch ( SQLException sqlException ) {
122 throw new CreateException( sqlException.getMessage() );
123 }
124 } // конец метода ejbCreate
125
126 // выполнение остальных действий после создания нового объекта
Employee
127 public void еjbPoetCreate( Integer primaryKey ) {}
128
129 // удаление информации о сотруднике из базы данных
130 public void ejbRemove() throws RemoveException
131 I
132 // удаление (DELETE) записи о сотруднике
133 try I
134
Компоненты EJB с данными
371
135 // получение первичного ключа для удаляемого сотрудника
136 Integer primaryKey =
137 ( Integer ) entityContext.getPrimaryKey();
138
139 // создание оператора удаления DELETE
140 String delete = "DELETE FROM Employee WHERE " +
HI "employeelD = ?" ;
142
143 // создание подготовленного оператора FreparedStatement
для выполнения удаления
144 FreparedStatement deleteStatement =
145 connection.prepareStatement( delete );
146
147 // задание значений для оператора PreparedStatement
148 deleteStatement.setlntf 1, primaryKey.intValue() J;
149
150 // выполнение удаления и Закрытий подготовленного оператора
PreparedStatement
151 deleteStatement.executeUpdate();
152 deleteStatement.close();
153 }
154
155 // возбуждение нового исключения EJBException, если удаление
заканчивается неудачей
156 catch { SQLException sqlException } {
157 throw new RemoveException( sqlException.getttessage(> );
158 I
159 } // конец метода ejbRemove
160
161 // сохранение информации о сотруднике в базе данных
162 public void ejbStoreO throws EJBException
163 {
164 // обновление (UPDATE) записи для сотрудника
165 try {
166
167 // получение первичного ключа для сотрудника
168 Integer primaryKey =
169 { integer ) entityContejct.getPriroaryKeyO !
ПО
171 // создание оператора обновления UPDATE
172 String update = "UPDATE Employee SET " +
173 "socialSeeurityNumber - ?, firstName = ?, " +
174 "lastHame = ?, title = ?, salary = ? " +
175 "WHERE employeelD ■ ?";
176
177 // создание подготовленного оператора FreparedStatement
для выполнения обновления
178 FreparedStatement updateStatement =
179 connection.prepareStatement( update );
ieo
181 // задание значений для оператора PreparedStatement
182 updateStatement.setStringt 1, socialSecurityHumber );
183 updateStatement.setString( 2, firstName );
184 updateStatement.setString( 3, lastName );
185 updateStatement.setString( 4, title );
186 updateStatement.setDouble( 5, salary.doubleValue() );
372
Глава 7
187 updatestatement.setint( 6, primaryKey.intValue() );
188
189 // выполнение обновления и закрытие объекта PreparedStatement
190 updateStatement.executeUpdate(};
191 updat&statement.close();
192 >
193
194 // возбуждение исключения ЕJBException, если обновление
заканчивается неудачей
195 catch ( SQLException sqlException ) {
196 throw new EJBException( sqlException );
197 }
198 } // конец метода ejbStore
199
200 // загрузка информации о сотруднике из базы данных
201 public void ejbLoad{) throws BJBException
202 {
203 // получение записи для сотрудника из таблицы Employee
базы данных
204 try {
205
206 // получение первичного ключа для сотрудника
207 Integer primaryKey =
208 ( Integer ) entityContext.getPrimaryKey();
209
210 // создание оператора SELECT
211 String select = "SELECT * FROM Employee WHERE " +
212 "employeelD = ?";
213
214 // создание подготовленного оператора PreparedStatement
для оператора SELECT
215 PreparedStatement selectstatement =
216 connection.prepareStatementt select );
217
218 // задание значения employeelD в операторе PreparedStatement
219 selectstatement.ееtint( 1, primaryKey.intvalue() );
220
221 // выполнение оператора selectstatement
222 ResultSet resultset = selectstatement. executeQuery () ,-
223
224 // получение информации о сотруднике из результирующего
225 // множества и обновление локальных переменных экземпляра
для хэширования данных
226 if ( resultset.next{) ) {
227
228 // получение идентификатора employeelD
229 employeelD = new Integer( resultSet.getlnt(
230 "employeelD" ) );
231
232 // получение номера свидетельства социального страхования
233 socialSeCurityNmober = resultSet.getString(
234 "socialSecurityNumber" );
235
236 // получение имени
237 firstName = resultSet.getString( "firstMame" );
238
Компоненты EJB с данными
373
239 // получение фамилии
240 lastName = resultSet.getString( "lastName" );
241
242 // получение названия должности
243 title = resultSet.getString( "title" );
244
245 // получение величины оклада
246 salary = new Doublet resultSet.getDouble<
247 "salary" ) );
248
249 } // конец блока if
250
251 else
252 throw new EJBException( "No such employee." );
253
254 // закрытие объекта PreparedStatement
255 selectStatement.close{);
256
257 } // конец блока try
25B
259 // возбуждение исключения EJBException, если выборка
ваканчивается неудачей
260 catch ( SQLException sqlException ) {
2 61 throw new EJBException( sqlExoeption );
262 }
263 } II конец метода ejbLoad
264
265 // поиск сотрудника по его первичному ключу
266 public Integer ejbFindByPrimaryKey( Integer primaryKey )
267 throws FinderException, EJBException
268 {
269 // нахождение сотрудника в базе данных
270 try {
271
272 // создание оператора SELECT
273 String select = "SELECT employeeID FROM Employee " +
274 "WHERE employeelD = ?";
275
276 // создание подготовленного оператора PreparedStatement
для SELECT
277 PreparedStatement selectStatement =
278 connection.prepareStatement( select );
279
280 // получение значения employeelD в операторе PreparedStatement
281 selectStatement.setlnt( 1, primaryKey.intValue() );
282
2B3 // выполнение оператора selectStatement
2B4 ResultSet resultSet = selectStatement.executeQuery();
2B5
286 // возврат первичного ключа, если оператор SELECT
возвращает запись
287 if ( resultSet.next О ) {
288
289 // закрытие объектов resultSet и selectStatement
290 resultSet.close();
291 selectStatement.close О ;
374
Глава 7
292
293 return primaryKey;
294 }
295
296 // возбуждение исключения ObjeetNotFoundException,
297 // если выборка SELECT не возвращает записей
298 else
299 throw new ObjeetNotFoundException();
300 }
301
302 // возбуждение исключения EJBException, если выборка
заканчивается неудачей
303 catch ( SQLException sqlExeeption ) (
304 throw new EJBException( sqlExeeption );
305 }
306 } // конец метода ejbFindByPrimaryKey
307
308 // задание контекста и создание соединения с источником данных
309 public void setEntityContext( EntityContext context )
310 throws EJBException
311 {
312 // задание контекста entityContext
313 entityContext = context;
314
315 // поиск источника данных Employee и создание соединения
316 try I
317 InitialContext initialContext = new InitialContext();
31B
319 // получение ссылки на источник данных из каталога JNDI
320 DataSource dataSource = ( DataSource )
321 initialContext.lookup(
322 "java:comp/env/jdbc/Employee" );
323
324 // получение соединения с источником данных
325 connection = dataSource.getConnectionO;
326 }
327
328 // обработка исключения, если источник данных не найден
в каталога
329 catch ( NamingException namingException ) {
330 throw new EJBException{ namingException );
331 J
332
333 // обработка исключений при получении соединения с источником
данных
334 oatch ( SQLException sqlExeeption ) {
335 throw new EJBException< sqlExeeption );
336 }
337 } // конец метода aetEntityContext
338
339 // сброс контекста EntityContext
340 public void unsetEntityContext() throws EJBException
341 {
342 entityContext = null;
343
344 // вакрьггие соединения с источником данных
Компоненты EJB с данными
375
345 try {
346 connection.close(};
347 >
34S
349 // возбуждение исключения ЕJBException, если закрытое
соединения заканчивается неудачей
350 catch ( SQLException eqlExCeption ) {
351 throw new EJBException( sqlException );
352 }
353
354 // подготовка повторного соединения
355 finally \
356 connection = null;
357 }
358 }
359
360 // установка для employeelD значения null при пассивации
EJB-компонента контейнером
361 public void ejbEaesivate0
362 {
363 employeelD = null;
364 }
365
366 // получение первичного клича ври активации EJB-компонента
контейнером
367 public void ejbActivate()
368 {
369 employeelD = ( Integer ) entityContext.getPrimaryKey();
370 }
371 } __
Рис. 7.З. Реализация EmployeeEJB удаленного интерфейса Employee, использующая
управление персистентностью на стороне компонента
В строках 28-91 реализуются бизнес-методы set и get, объявленные в
удаленном интерфейсе Employee. При вызове клиентом метода create интерфейса
EmployeeHorae контейнер EJB вызывает метод ejbCreate (строки 94-126), В этом
EJB-компоненте персистентность данных обеспечивается самим компонентом,
поэтому метод ejbCreate должен реализовывать соответствующую логику для
создания новой записи о сотруднике в основной базе данных. В строке 97 для
переменной экземпляра EmployeelD устанавливается значение аргумента primaryKey.
В строках 103 111 создается оператор SQL (объект класса PreparedStatement) для
вставки (INSERT) информации о новом сотруднике в базу данных. В строке 111
задается значение идентификатора EmployeelD в операторе SQL, а в строке 114
осуществляется вставка новой записи путем вызова метода executeUpdate класса
PreparedStatement. В строке 115 осуществляется закрытие оператора Prepared-
Statement, а в строке 117 возвращается первичный ключ EmployeelD. В строках
121-122 перехватывается исключение SQLException, которое может быть
возбуждено при создании, выполнении или закрытии оператора PreparedStatement.
Исключение SQLException указывает на наличие проблемы при вставке записи,
поэтому в строке 122 возбуждается исключение CreateException для указания, что
метод ejbCreate не смог создать экземпляр EJB-компонента Employee.
Метод ejbCreate объявляет целочисленный (Integer) возвращаемый тип. Все
методы ejbCreate в компоненте-сущности EJB должны возвращать класс
первичного ключа EJB-компонента. Первичный ключ для таблицы Employee представлл-
376
Глава 7
ет собой целое число, поэтому классом первичного ключа для EJB-компонента
Employee является класс Java.lang.Integer. Большинство EJB-компонентов
используют в качестве класса первичного ключа стандартный класс Java (например.
Integer или String). Если таблица базы данных имеет составной первичный ключ
(т.е. первичный ключ, состоящий из нескольких полей), разработчик должен
определить собственный класс первичного ключа. Образец собственного класса
первичного ключа можно найти в примере EJB-компонента OrderProduct в главе 4.
Контейнер EJB вызывает метод ejbPostCreate (строка 127) после вызова метода
ejbCreate, чтобы выполнить все необходимые после создания экземпляра
EJB-компонента действия. Например, метод ejbPostCreate может изменять
формат номера свидетельства социального страхования socialSecurityNumber для
включения в него дефисов. В данном случае никаких дополнительных действий не
требуется, поэтому в строке 127 указывается пустая реализация метода
ejbPostCreate.
При вызове клиентом метода remove в удаленном интерфейсе Employee либо
в интерфейсе EmptoyeeHome, контейнер EJB вызывает метод ejbRemove (строки
130-159). Удалить (remove) экземпляр компонента-сущности EJB — значит
удалить (DELETE) соответствующую запись в базе данных. В строках 136-137 из
объекта entityContext, ассоциированного с EJB-компонентом, извлекается
первичный ключ для текущего экземпляра EJB-компонента. В строках 140-145
создается оператор SQL для удаления (с помощью операции DELETE) из базы данных
записи, относящейся к сотруднику. В строке 148 задается первичный ключ
employeelD, используемый в формируемом операторе SQL. В строках 151-152
осуществляется выполнение и закрытие сформированного оператор SQL. В сроках
156-158 перехватывается исключение SQLException, которое может быть
возбуждено при создании, выполнении или закрытии сформированного оператора SQL.
Если было возбуждено исключение SQLException, в строке 157 возбуждается
исключение Remove Exception, указывающее, что удаление EJB-компонента
Employee окончилось неудачей.
Контейнер EJB вызывает метод ejbStore, чтобы сохранить данные о сотруднике
в базе данных. Контейнер EJB определяет наилучший момент для обновления
базы данных, поэтому метод ejbStore вызывается только контейнером. В строках
168-169 из объекта entityContext извлекается первичный ключ для текущего
экземпляра EJB-компонента. В строках 172-187 создается оператор SQL для
обновления (с помощью операции UPDATE) в базе данных записи для сотрудника.
В строках 190-191 осуществляется выполнение и закрытие сформированного
оператора SQL. В строках 195-96 перехватывается исключение SQLException,
которое может быть возбуждено при создании, выполнении и закрытии
сформированного оператора SQL. В строке 196 возбуждается исключение EJBException,
указывающее, что метод ejbStore не смог должным образом осуществить обновление
базы данных. В строке 196 исключение SQLException передается конструктору
EJBException, чтобы облегчить отладку приложения.
Контейнер EJB вызывает метод ejbLoad для загрузки информации о
сотруднике из базы данных и сохранения данных в переменных экземпляра
EJB-компонента. Контейнер EJB определяет наилучший момент для загрузки информации
из базы данных, поэтому метод ejbLoad вызывается только контейнером, В
строках 207-208 Employee из контекста EntityContext EJB-компонента извлекается
первичный ключ для текущего экземпляра EJB-компонента. В строках 211-219
создается оператор SQL для выборки (с помощью операции SELECT) информации
о сотруднике из базы данных. В строке 226 проверяется, что результирующее
множество ResultSet содержит данные, а в строках 229-247 частные переменные
экземпляра обновляются данными из результирующего множества. В строках
260-262 перехватывается исключение SQLException, и возбуждается исключение
Компоненты EJB с данными
377
EJBException, указывающее, что метод ejbLoad не смог загрузить информацию
о сотруднике из базы данных.
При вызове клиентом метода findByPrimaryKey интерфейса EmployeeHome
контейнер EJB вызывает метод ejbFindByPrimaryKey (строки 266-306). Метод
findByPrimaryKey должен принимать один параметр, тип которого соответствует
классу первичного ключа EJB-компонента. В строке 273-281 создается оператор
SQL для выборки из базы данных (с помощью операции SELECT) информации
о сотруднике с заданным первичным ключом employeelD. В строках 287—294
проверяется, был ли найден сотрудник, и возвращается его первичный ключ. Если
сотрудник с заданным первичным ключом в базе данных найден не был, в строках
298-299 возбуждается исключение Object Not F о undException. Если при поиске
информации о сотруднике возникла ошибка, в строках 303-305 перехватывается
исключение SQLException и возбуждается новое исключение EJBException.
Контейнер EJB вызывает метод setEntityContext после первого создания
экземпляра EJB-компонента, но до связывания экземпляра EJB-компонента с
определенной записью в базе данных. Параметр EntityContext предоставляет методы для
получения информации о контейнере EJB, в котором выполняется
EJB-компонент. В строке 313 задается значение переменной экземпляра entityContext, чтобы
другие методы могли использовать данный объект EntityContext для получения
информации о контейнере EJB. На использование метода setEntityContext
накладывается несколько ограничений, так как при вызове этого метода экземпляр
EJB-компонента еще не связан с определенной записью в базе данных. Например,
EJB-компонент не должен вызывать метод getPri meryKey класса EntityContext,
поскольку отсутствует первичный ключ, ассоциированный с текущим
экземпляром EJB-компонента. Вызов метода getPrimeryKey класса EntityContext приведет
к возбуждению исключения IlIegalStateExccption.
Метод getEntityContext должен выделять ресурсы, которые потребуются
экземпляру EJB-компонента в течение его жизненного цикла. Данная реализация
EJB-компонента Employee использует управление персистентностью на стороне
самого компонента, поэтому необходимо иметь соединение с базой данных, чтобы
обмениваться информацией с ней в течение времени жизни EJB-компонента.
В строке 317 создается новый объект InitialContext, который EJB-компонент
будет использовать для поиска базы данных Employee в каталоге JNDI. В строках
320-322 вызывается метод lookup класса InitialContext для получения ссылки
типа DataSource на базу данных Employee. Приложение J2EE использует
специальный контекст идентификации JNDI с именем java:comp/env, который
определяет окружение компонента. В контексте идентификации java:comp/env имеются
подчиненные контексты для объектов различных типов. Например, EJB-компо-
ненты содержатся в подчиненном контексте ejb, а базы данных содержатся в
подчиненном контексте jdbc. В строке 322 использует имя JNDI java:comp/env/jdbc/
Employee для нахождения базы данных Employee в каталоге JNDI. В строке 325
устанавливается переменная connection, указывающая на создаваемое соединение
с базой данных. Если база данных с указанным именем не может быть найдена,
в строках 329—331 перехватывается исключение NamingException, которое
возбуждается конструктором InitialContext или методом lookup.
-, Типичная ошибка программирования 7.1
Контекст идентификации java:comp/env доступен только из
приложения J2EE (например, из сервлета, с JSP-страницы или из
EJB-компонента). В случае использования этого контекста идентификации в
приложении Java или в апплете, будет возбуждено исключение javax.naming.Na-
mingException.
378
Глава 7
Контейнер EJB вызывает метод unset En tityCon text (строки 340-358), если
экземпляр EJB-компонента больше не нужен. Метод unsetEntityContext должен
освобождать любые ресурсы, которые экземпляр EJB-компонента использовал в
течение своего жизненного цикла. В строке 346 закрывается соединение с базой
данных. Если при закрытии соединения с базой данных возникла ошибка, в строках
350-352 перехватывается исключение SQLException, и возбуждается исключение
EJBExcepiion. В строках 355-357 объект соединения Connection устанавливается
в null для последующего его использования.
Контейнер EJB содержит пул неактивных экземпляров EJB-компонента,
которые контейнер при необходимости может ассоциировать с определенными записями
в базе данных. Такая организация пула предотвращает возникновение
дополнительных затрат для контейнера EJB, связанных с необходимостью каждый раз
создавать новый экземпляр EJB-компонента, когда в нем возникает потребность.
Контейнер EJB вызывает метод ejbPassivate (строки 361-364), чтобы поместить активный
EJB-компонент обратно в пул неактивных экземпляров. В строке 363 в качестве
значения employeelD устанавливает null, поскольку пассивированный
EJB-компонент больше не ассоциируется с какой-либо записью базы данных.
Контейнер EJB вызывает метод ejbActivate {строки 367-371) для активации
экземпляра EJB-компонента, извлеченного из пула неактивных экземпляров
EJB-компонента. В строке 369 в качестве значения идентификатора employeelD
устанавливается значение первичного ключа PrimaryKey, извлеченного из
объекта entityContext EJB-компонента, чтобы связать EJB-компонент с
соответствующей записью в базе данных.
7.5.2. Развертывание EJB-компонента Employee
Развертывание компонентов-сущностей EJB сходно с развертыванием
сеансовых компонентов EJB (см. главу 6), но имеется и несколько отличий. В диалоговом
окне General мастера New Enterprise Bean Wizard выберите опцию Entity в поле
Bean Type (рис. 7.4). Далее, выберите тип управления персистентностью в
диалоговом окне Entity Settings. Щелкните на переключателе Bean-Managed, чтобы ус-
Рис. 7.4. Диалоговое окно General мзстера New Enterprise Bean Wizard
Компоненты EJB с данными
379
тановить режим управления персистектносгью на стороне компонента, и введите
имя класса для первичного ключа в текстовом поле Primary Key Class (рис. 7.5).
Наконец, в диалоговом окне Resource References добавьте ссылку на базу данных
Employee. Информация, содержащаяся в ссылке, должна соответствовать той,
которая приведена в копии экрана на рис. 7.6. В остальном развертывание
осуществляется точно гак же, как и для сеансовых компонентов EJB. В качестве имени
JNDI EJB-компонента Employee рекомендуется для версии с управлением перси-
стентностью на стороне компонента указать имя BMPEmployee.
Рис. 7.5. Выбор типа управления перслстентностью Bean-Managed Persistence
в диалоговом окне Entity Settings
Рис. 7.6. Диалоговое окно Resource References мастера New Enterprise Bean Wizard
380
Глава 7
7.6. EJB-компонент Employee с персисгентностью,
управляемой контейнером
На рис. 7.7 представлена реализация EJB-компонента Employee, в которой
используется управление персисгентностью на стороне контейнера, что позволяет
упростить реализацию EJB-компонента. Класс EmployeeEJB реализует интерфейс
EntityBean (строка 12), что определяет этот EJB-компонент как
компонент-сущность EJB, В строке 14 объявляется ссылка на объект типа EntityContext,
хранящий данные, относящиеся к экземпляру EJB-компонента.
В строках 17—22 объявляются управляемые контейнером поля для EJB-компо-
нента Employee. Управляемые контейнером поля представляют собой переменные
экземпляра, которые EJB-компонент с персистентностью, управляемой
контейнером, использует для кэширования данных, извлеченных из базы данных. Поля,
управляемые контейнером, должны носить те же имена, что и поля в
соответствующей таблице базы данных. Управляемые контейнером поля также должны
быть помечены как открытые (public), чтобы контейнер EJB мог напрямую
осуществлять доступ к ним. Контейнер ЕЛВ ответственен за синхронизацию
управляемых контейнером полей с базой данных. При развертывании EJB-комтюнента
администратор развертывания должен предоставить операторы SQL для изменения,
удаления и извлечения данных из базы данных. В строках- 25-88 определены
реализации методов, объявленных в удаленном, интерфейсе Employee,
Метод ejbCreate (строка 91-96) принимает параметр Primary Key типа Integer
и сохраняет его в переменной экземпляра employeell) (строка 93). Контейнер EJB
выполняет оператор SQL INSEKT (этот оператор должен быть определен
администратором развертывания) после завершения метода ejbCreate. Этот оператор SQL
INSERT принимает текущие значения управляемых контейнером полей
в EJB-компоненте и помещает эти значения в базу данных. Обратите внимание,
что хотя в методе ejbCreate для возвращаемого значения задан тип Integer (строка
91), в строке 93 возвращается null. В соответствии со спецификацией EJB, метод
ejbCreate должен возвращать тип класса первичного ключа. Для EJB-компонента
Employee таким типом является Integer. Однако контейнер EJB игнорирует
возвращаемое методом ejbCreate значение при управлении персистентностью со
стороны контейнера, поэтому мы возвращаем null. Согласно спецификация EJB, для
каждого метода ejbCreate должен иметься соответствующий метод ejbPostCreate.
В строке 99 предоставляется пустая реализация метода ejbPostCreate, поскольку
этот EJB-компонент не требует дальнейшей инициализации.
Методы eetEntityContext (строки 102-105) и unsetEntityContext (строки
108-111) управляют переменной экземпляра EntityContext. Для этой реализации
EJB-компонента не имеется каких-либо ресурсов, которые были бы необходимы
в течение времени жизни EJB-компонента, поэтому никаких дополнительных
действий для этих методов не требуется.
1 // EmployeeEJB.Java
2 // EmployeeEJB - компонент-сущность EJB, в котором используется
3 // контейнерное управление персистентностью для хранения сведений
о сотруднике в базе данных.
4 package com.deltel,advjhtpl.ejb.entity.emp;
5
6 // Базовые библиотеки Java
7 import Java. rmi.. ReidotaExcepticm;
8
9 // Стандартные расширения Java
10 import javax.ejb.*;
Компоненты EJB с данными
381
public class EmployeeEJB implements EntityBean {
private EntityContext entity-Context;
// поля, управляемые контейнером
public Integer employeelD;
public String socialSecurityNumber;
public String firstName,-
public String lastName;
public String title;
public Double salary;
// получение идентификатора EmployeelD
public Integer getEmployeelD()
return employeelD;
// задание номера свидетельства социального страхования
public void setSocialSecurityNumberf String number )
socialSecurityNumber = number;
// получение номера свндегельсгм социального страхования
public String getSocialSecurityNumberQ
return socialSecurityNumber;
// задание имени
public void setFirstName( String name )
firstName = name;
// получение имени
public String getFirstName()
return firstName;
// задание фамилии
public void setLastName( String name )
lastName = name;
// получение фамилии
public String getLastNaroeО
return lastName;
// задание должности
382
public void setTitle{ String jobTitle )
title = jobTitle;
/ получение должности
public String getTitleO
return title;
/ задание неличины оклада
public void setSalary ( Double amount )
salary = amount;
/ получение аеличшш оклада
public Double getSalaryO
return salary;
/ создание нового экземпляра об-ьекта Employee
public Integer ejbCreate{ Integer primaryKey )
employeeID = primaryKey;
return null;
/ выполнение необходимых действий после создания нового об-ьекта
Employee
public void ejbPostCreate{ Integer primaryKey ) {}
/ установка контекста EntityContext
public void setEntityContext( EntityContext context )
entityContext = context;
/ сброс контекст» EntityContext
public void unsetEntityContextO
entityContext = null;
/ активация экземпляра Employee
public void sjbActivatef)
employeelD = ( Integer ) entityContext.getPrimaryKey()
/ пассивация экземпляра Employee
public void ejbPassivate{)
Компоненты ЕЗВ с данными 383
122 employeelD = null;
123 }
124
125 // загрузка экземпляра Employee в базу данник
126 public void ejbLoad() {)
127
128 // сохранение экземпляра Employee в базе данных
129 public void ejbStore() I)
130
131 // удаление экземпляра Employee из Сазы данных
132 public void ejbRemoveO {)
133 J
Рис. 7.7. Реализация EmployeeEJB удаленного интерфейса Employee, использующая
управление персистентностью на стороне контейнера
Методы ejbActivate (строки 114-117) и ejbPassivate (строки 120-123)
выполняют те же функции, которые они выполняют в версии EJB-компонента Employee
с персистентностью, управляемой компонентом. Метод ejb Activate связывает
экземпляр EJB-компонента, взятого из пула неактивных экземпляров, с
определенной записью в базе данных, А метод ejbPassivate отменяет связывание экземпляра
EJB-компонента с записью Сазы данных.
Методы ejbLoad (строка 126), ejbStore (строка 129) и ejbRemtrve (строка 132)
также выполняют те же самые функции, что в версий EJB-компонента Employee
с персистентностью, управляемой компонентом. Однако при управлении
персистентностью на стороне контейнера администратор развертывании должен
предоставить необходимые операторы SQL SELECT, UPDATE и DELETE при
развертывании приложения. Контейнер EJB выполняет эти операторы в процессе
исполнения. Эти методы могут при необходимости выполнять дальнейшую обработку
данных. Для нашего EJB-компонента Employee никакой дальнейшей обработки не
нужно, поэтому эти методы имеют пустые реализации.
Типичная ошибка программирования 7.2
Для компонентов-сущностей EJB, использующих управление
персистентностью на стороне контейнера, контейнер EJB загружает данные
базы данных и сам вызывает метод ejbLoad при первом вызове
бизнес-метода клиентом. Однако при этом бизнес-метод должен вызываться в
контексте транзакции.
Совет по повышению эффективности 7.1
Компоненты-сущности EJB с персистентностью, управляемой
контейнером, могут выполняться медленнее, чем компоненты с
персистентностью, управляемой компонентом, поскольку все данные из базы данных
загружаются до того, как контейнер EJB вызывает метод ejbLoad.
Компоненты-сущности EJB, управление персистентностью для которых
осуществляется компонентом, могут откладывать загрузку данные из
базы данных, пока эти данные не понадобятся, что способствует
повышению производительности для EJB-компонентов, оперирующих
большими объемами данных.
Чтобы осуществить развертывание версии EJB-компонента Employee с
персистентностью, управляемой контейнером, следуйте .инструкциям, приведенным
в разделе 7.5.2. Рекомендуется использовать для версии EJB-компонента
Employee с персистентностью, управляемой контейнером, имя JNDI CMPEmp-
384
Глава 7
loyee. В диалоговом окне Persistence Management (рис. 7.8) щелкните на
переключателе Container-Managed и установите флажок рядом с каждым из полей,
управляемых контейнером. Введите имя класса первичного ключа в текстовое
поле Primary Key Class и выберите имя поля первичного ключа из
раскрывающегося меню Primary Key Field Nams.
Рис. 7.8. Выбор управления персистентностью на стороне контейнера
Container-Managed Persistence в диалоговом окне Entity Settings
7.7. Клиент EJ В-компонента Employee
На рис. 7.9 представлен класс EmployeeEJBCHent, предназначенный для
взаимодействия с компонентом-сущностью EJB Employee. В графическом интерфейсе
пользователя предусмотрены кнопки для поиска, добавления, изменения и
удаления EJB-компоиентов Employee и текстовые поля JTeittField для отображения
информации о сотруднике. В строках 26-27 объявляется ссылка типа EmployееНоше
и ссылка типа Employee для доступа к EJB-коМПоненту Employee. Конструктор
EmpLoyeeEJBClient принимает строковый аргумент, содержащий имя JNDI
EJB-компонента Employee, который следует загрузить. Этот клиент работает как
с версией EJB-компонента Employee с персистентностью., управляемой самим
компонентом, так и с версией EJB-компонента Employee с персистентностью,
управляемой контейнером, поскольку обе версии используют одни и те же собственный
и удаленный интерфейсы. Конструктор использует аргумент JNDIName, чтобы
определить, какую версию EJB-компонента Employee использовать,
В строке 47 создается новый объект InitialContext для поиска EJB-компонента
Employee на основе аргумента JNDIName. В строках 50—51 осуществляется поиск
интерфейса Employ ееНоше с помощью метода lookup класса InitialContext.
В строках 54-56 исаользуется метод narrow класса PortableRemoveObject для
приведения типа Object ссылки, возвращаемой методом [ookup, к типу
EmployeeHome. Эта ссылка будет использоваться в ходе программы для создания,
поиска и удаления EJB-компонентов Employee. Если при создании объекта Initial-
Компоненты EJB с данными
385
Context или при поиске интерфейса EmployeeHome возникает ошибка, в строках
60—62 перехватывается исключение NamingException.
Метод getEmployeelD (строки 74-85) отображает панель JOptionPane, в которой
у пользователя запрашивается идентификатор employeelD, Этот идентификатор
может быть иеигользовая для нахождения существующего экземпляра
EJB-компонента Employee или для создания нового экземпляра EJB-компонента Employee.
Метод getEmployee (строки 88-119) используют метод jfetEmployeelD в строке
91, чтобы запросить у пользователя идентификатор employeelD сотрудника,
информацию о котором пользователь хотел бы найти. В строках 99-100 используется
метод findByPrimaryKey интерфейса EmployeeHome для нахождения экземпляра
EJB-компонента Employee для сотрудника с заданным идентификатором
employeelD. Метод findByPrimaryKey возвращает удаленную ссылку на
экземпляр EJB-компонента Employee. В строке 103 вызывается метод set Current
Employee для отображения пользователю информации о сотруднике. Если при
взаимодействии с EJB-компонентом Employee возникает ошибка, в строках 107-111
перехватывается исключение BemoteException. Если сотрудника с заданным
идептификатором не существует, в строках 114-118 перехватывается исключение
FinderException.
Метод addEmployee (строки 122-150) осуществляет добавление сотрудника
в базу данных, создавая новый EJB-компонент Employee. В строке 126 вызывается
метод getEmployeelD для запроса у пользователя идентификатора employeelD.
В строке 133 вызывается метод create интерфейса EmployeeHome для создания
нового экземпляра EJB-компонента Employee с заданным идентификатором
employeelD, В строке 136 вызывается метод setCurrentEmployee для отображения
пользователю информации о сотруднике. В строках 140-143 перехватывается
исключение CpeateException, которое возбуждается, если при создании EJB-компонента
Employee возникает ошибка. Например, если идентификатор employeelD,
введенный пользователем, уже используется другим EJB-компонентом, контейнер EJB
возбуждает исключение DupHcateKeyException, которое является подклассом
класса CreateException. Если при взаимодействии с EJB-компонентом Employee
возникает ошибка, в строках 146-149 перехватывается исключение Remote-
Exception.
Метод updateEmployee (строки 153-184) используют значения,
предоставленные в текстовых полях JTextField, для обновления текущей информации о
сотруднике. В строках 159-175 вызываются методы set EJB-компонента Employee с
передачей им в качестве параметров значений каждого из текстовых полей
JTextField. Если при взаимодействии с Et/B-компонентом Employee возникает
ошибка, в строках 180-182 перехватывается исключение RemoteExceptioit.
Метод deleteEmployee (строки 187-214) удаляет информацию о текущем
сотруднике из базы данных, вызывая метод удаления EJB-компонента Employee
(строка 191). В строках 194-197 устанавливаются пустые строки в качестве
значений для текстовых полей JTextField в пользовательском интерфейсе. Если при
взаимодействии е EJB-компонентом Employee возникает ошибка, в строках
204—207 перехватывается исключение RemnteException. Если ошибка возникает
при удалении EJB-компонента Employee, в строках 210-213 перехватывается
исключение RemoveException.
Метод setCurreiitEmployee (строки 217-266) принимают в качестве аргумента
удаленную ссылку типа Employee и обновляет пользовательский интерфейс
информацией, содержащейся в этом EJB-компонента Employee, В строках 224-258
извлекаются значения для каждого из свойств EJB-компонента Employee, и
обновляются текстовые поля JTextField в пользовательском интерфейсе. Если при
взаимодействии с EJB-компонентом Employee возникает ошибка, в строках 262-265
перехватывается исключение RemoteException.
386
Глава 7
1 // EmployeeEJBClient.Java
2 // EmployeeEJBClient - пользовательский интерфейс для взаимодействия
3 // с EJB-компонентами Employee с г.ерсистентиость», управляемой
контейнером.
4 package com.deitel.advjntpl. ejb.entity.client;
5
6 // Базовые библиотеки Java
7 import java.ewt.*;
8 import java.awt.event,*;
9 inport Java.text,*;
10 import ja.va.util.* ;
11 import java.rmi.RemoteException,-
12
13 // Стандартные расширения Java
14 import javax.swing.*;
15 iinport javax.ejb.*;
1 6 import javax.naming.*;
17 iinport javax.rmi.*;
18
19 // Библиотеки Deitel
20 import com.deitel-advjntpl.ejb.entity.*;
21
22 public class EmployeeEJBGlieTit extends JFrame {
23
24 // переменные для доступа к EJB-компонентам
25 private InitialContext initialContext;
26 private EmployeeHome employeeHome;
27 private Employee currentEmployee;
28
29 // поля JTextField для ввода данных пользователем
30 private JTextField employeelDTextField;
31 private JTextField socialSecurityTextField;
32 private JTextField firstNameTextField;
33 private JTextField lastNameTextField;
3d private JTextField titleTextField;
35 private JTextField salaryTextField;
36
37 // конструктор BMPEmployeeEJBClient
38 public EmployeesJBClient( String JNDIName )
39 {
40 super( "Employee EJB Client" Jj
41
42 // создание пользовательского интерфейса
43 createGUIО ;
44
45 // получение ссылки на EmployeeHome для EJB-компонента Employee
46 try {
47 initialContext = new InitialContext();
48
49 // поиск EJB-компонентa Employee no заданному имени JNDI
50 Object homeObject =
51 iaitia-lContext. lookup ( JHDIName };
52
53 // получение интерфейса EmployeeHome
54 employeeHome = ( EmployeeHome )
55 PortableRemoteObject.narrow(
Компоненты EJB с данными
387
56 homeObject, EmployeeHome.class );
57 }
56
59 // обработка исключения при войске EJB-компонента Employee
60 catch ( NamingException namingException ) {
61 namingException.printstackTrace( System.err );
62 }
63
64 // закрытие приложения при закрытии окна пользователем
65 setDefaultCloseOperation( EXIT_ON_CLOSE );
66
67 // установка размеров окна приложения, задание видимости окна
68 setSize< 600, 300 );
€9 setVisiole( true );
70
71 } // конец конструктора EmployeeEJBClient
72
73 // запрос у пользователя идентификатора employeelD
74 private Integer getEmpioyeelD()
75 {
76 String primaryKeyStiring - JOptionPane.sbowlnputDialog(
77 this, "Please enter an employeelD" );
78
7 9 // проверка, не есть ли primaryKeyString null,
80 // если нет, вернуть целое число
81 if ( primaryKeyString = null )
82 return null;
83 else
84 return new Integer( primaryKeyString );
85 }
86
87 // получение ссылки на объект Employee для заданного пользователем
значения employeelD
88 private void getEmployee()
89 {
90 // запрос у пользователя идентификатора employeelD и получение
ссылки на объект Employee
91 Integer employeelD = getEmployeelDO .'
92
93 // возврат, если employeelD есть null
94 if ( employeelD = null )
95 return;
96
97 try {
98 // поиск сотрудника по заданному идентификатору
99 Employee employee =
100 employeeHome.findByPrimaryKey< employeelD );
101
102 // обновление экрана информацией о текущем сотруднике
Employee
103 setCurrentEmployee( employee );
104 J
105
106 // обработка исключения при поиске сотрудника
107 catch ( RemoteException remoteException ) {
108 JOptionPane.showMeasageDialog(
388
109 EmployfeeEJBClient. this,
110 remoteException.getMessageO );
111 J
112
113 // обработка исключения при поиске сотрудника
114 catch ( FinderException finderException ) {
115 JOptionPane.showMessageDialog(
116 EmployeeEJBClient.this, "Employee not " +■
117 "found; " + finderException.getMessage<) );
118 }
119 } // конец метода getEmployee
120
121 // добавление нового сотрудника созданием нового экземпляра
EJB-компонента Employee
122 private void addEn$>loyee0
123 {
124 // запрос у пользователя идентификатора employeelD и создание
объекта Employee
125 try {
126 Integer employeelD = getEmployeelD();
127
128 (I возврат, если employeelD есть null
129 if ( eflsployeelD == null )
130 return;
131
132 // создание нового объекта Employee
133 Employee employee = employeeHome,create( employeelD );
134
135 // обновление экрана данными о новой сотруднике
136 setCurrentEmployee( employee );
137 }
138
139 // обработка исключения при создании объекта Employee
140 catch ( CreateException createException ) {
141 JOptionPane,showMessageDialog( this,
142 createException.getMe s sage О );
143 }
144
145 // обработка исключения при создании объекта Employee
14 6 catch ( RemoteException remoteExeeption ) {
147 JOptionPane.showMessageDialog( this,
148 remoteException.getMessageО );
149 }
150 } // конец нетода addEmployee
151
152 // обновление данных для текущего сотрудника на основе введенной
пользователей информацией
153 private void updateEmployee()
154 {
155 // получение информации из ч:екстовых полей JTextField
и обновление объекта Employee
156 try {
157
158 // задание значения socialSeciirityNumber
159 currentflmployee.setSocialSecurityNumber(
160 socialSecuxityTextField.getText(J );
Компоненты EJB с данными
161
162 // задание значения firstName
163 curcentEraployee.setFirstNaroe(
164 firstMameTextField.getText() );
165
166 // задание значения lastName
167 currentEmployee.setLastHame(
168 lastHameTextField.getText() );
169
170 // Задание значения title
171 curtentEmployee.eetTitlei titleTextField.getText() );
П2
173 // задание значения salary
174 Double salary = new Doublet salaryTextField.getText{) );
175 eurrentEinployee. setSalary { salary );
176
177 } // конец блока try
17B
179 // обработка исключения при вызове бизнес-методов объекта
Employee
180 catch ( RemoteExcepticn remoteException ) {
181 JOptionPane.showMessageDialog( this,
182 remoteException.getMessage() );
1S3 )
184 } // конец метода updateEmployee
185
186 // удаление текущего объекта Employee
187 private void deleteEmployee()
188 {
189 // удаление текущего EJB-компонента Employee
190 try I
191 currentEmployee.remove();
192
193 // очистка полей JTextField
194 employeeIDTextField.setText( "" );
195 socialSeeurityTextField.setText( "" ) ;
196 firstMameTextField.setText( "" );
197 lastNameTextField.setText( ■'" ) ;
198 titleTextField.setText( "" );
199 salaryTextField.setText( "" );
200
201 ) Il конец блока try
202
203 // обработка исключения при удалении обявхта Employee
204 catch ( RemoteException remoteException ) {
205 JOptionPane.showMessageDialog{ this,
206 remoteException.getMessage О ) ;
207 J
208
209 // обработка исключения при удалении объекта Employee
210 catch ( RemoveException removeException ) {
211 JOptionPane.showMessageDialog( this,
212 removeException.getMessage() );
213 }
214 } // конец метода deleteEmployee
215
390
216 // обновление экрана информацией о текущем сотруднице
217 private void setCurrentEmployee{ Employee employee )
218 {
219 // получение информации для текущего сотрудника
currentEmployee и обновление изображения
220 try {
221 currentEmployee = employee;
222
223 // получение значения employeeID
224 Integer employeelD = ( Integer )
225 CurrentEmployee.getEn^iloyeelD();
226
227 // обновление экрана
228 employeelDTextField.setText( employeelD.toString() );
229
230 // задание значения socialSecurityNtimber для отображения
231 socialSecurityTextField.setText{
232 currentEnployee.getSocialSecurityNumber{) );
233
234 // задание значения firstName для отображения
235 firetNaieeTextField.setText(
236 currentEmployee.getFiretName{) );
237
238 // задание значения lastName для отображения
239 lastSameTextField,setText(
240 currentEraployee.getLastNameO );
241
242 // задание значения title для отображения
243 titleTextField.setText( currentEmployee.getTitle() );
244
245 // получение величины оклада salary для сотрудника
246 Double salary = currentEmployee.getSalary();
247
248 // проверка, что salary не есть null и обновление экрана
249 if { salary »= null ) {
250 NumberFormat dollarFormatter =
251 NumberFormat.getCurrencylnstance(
252 Locale.US );
253
254 salaryTextField.setText( dollarFormatter.format(
255 salary ) ) ;
256 }
257 else
258 salaryTextField.setText( "" );
259 } // конец блока try
260
261 // обработка исключения при вызове бизнес-методов объекта
Employee
262 catch ( RemoteExceptioit renaoteException ) {
263 JOptionPane.showMeggageDialog( this,
264 remoteException.getMessage() );
265 }
266 } /I конец метода setCurrentEmployee
2 67
268 // создание GUI приложения
269 private void createGUIU
Компоненты EJB с данными
391
270 {
271 // создание компонента JPanel, содержащего элементы формы
для сотрудника
272 JPanel formPanel = new JPanel( new GridLayout( 6, 2 ) );
273
274 ff создание полей JTextFleld для идентификатора employeeID
275 employeeIDTextFieId = new JTextFieldO;
276 employeelDTextField.setEditable( false );
211 formPanel.add( new JLabel( "Employee ID" ) >;
278 formPanel.add( employeelDTextField );
279
280 // создание компонентов JTextFleld и JLabel для номера
социального страхования
281 socialSecurityTextField = new JTextFieldQ;
262 formPanel.add( new JLabel( "Social Security #" ) );
283 formPanel.add( socialSecurityTextField );
284
285 // создание компонентов JTextField и JLabel для имени
2B6 firstNameTextField = new JTextField();
287 formPanel.add( new JLabel( "First Name" ) );
288 formPanel.add( firstNameTextField );
289
290 // создание компонентов JTextFleld и JLabel для фамилии
291 lastNameTextField = new JTextFieldO;
292 formPanel.add( new JLabel( "Last Name" ) );
293 formPanel.add( lastNameTextField );
294
295 // создание компонентов JTextField и JLabel для должности
296 titleTextField = new JTextField();
297 formPanel.add( new JLabel( "Title" ) );
298 formPanel.add( titleTextField );
299
300 // создание компонентов JTextField и JLabel для величины оклада
301 salaryTextField = new JTextField();
302 formPanel.add< new JLabel( "Salary" J );
303 formPanel.add< salaryTextField );
304
305 // добавление панели formPanel в панель содержимого
contentPane охна JFrame
306 Container contentPane = getContentPane();
307 contentPane.add( formPanel, BorderLayout.CENTER );
308
309 // создание панели JPanel для кнопох JButtons
310 JPanel employeeButtonPanel =
311 new JPanel{ new FlowLayout() );
312
313 // создание кнопки JButton для добавления сотрудников
314 JButton addButton = new JButton( "Add Employee" );
315 addButton.addActionListener(
316 new ActionListener() {
317
318 public void actionPerformed( ActionEvent event )
319 {
320 addEraployee();
321 >
322 }
392
Глава 7
323 ) ;
324 enrployeeButtonPanel.add( addButton );
325
326 // создание кнопки JButton для сохранения информации
о сотруднике
327 JButton saveButton = new JButton( "Save Changes" );
328 saveButton.addActionListener(
329 new ActionListener() {
330
331 public void actionPerformed( ActionEvent event )
332 {
333 updateEmployeeO ;
334 }
335 }
336 ) ;
337 employeeButtonPanel.add< saveButton );
338
339 /J создание кнопки JButton для удаления сотрудников
340 JButton deleteButton = new JButton( "Delete Employee" );
341 deleteButton,addActionListener{
342 new ActionListener() {
343
34 4 public void actionPerformed( ActionEvent event )
345 {
34 6 deleteEmployee () ,-
347 У
348 }
349 ) ;
350 employeeButtonPanel.add( deleteButton );
351
352 // создание кнопки JButton для поиска сущесявужщих сотрудников
353 JButton findButton = new JButton{ "find Employee" };
354 findButton.addActionListener(
355 new ActionListener0 {
356
357 public void actionPerformed( ActionEvent event )
358 {
359 getEmployee(} ;
360 }
361 }
362 ) ;
363 employeeButtonPanel.add( findButton );
364
365 // добавление панели employeeButtonPanel в панель содержиого
contentPane окна JFrame
366 contentPane.add( employeeButtonPanel,
367 BorderLayout.NORTH >;
368
369 } // конец метода createGUl
370
371 // выполнение приложения
372 public static void main( String[] args }
373 {
374 // проверка, что пользователь предоставил имя JNDI
для EJB-компонента Employee
375 if ( args.length != 1 ) {
Компоненты EJB с данными
393
376 System.err.println(
377 "Usage: Java EmployeeEJBClient <JNDI Name>" );
378 System.exit( 1 );
379 }
380
381 // запуск приложения с использованием предоставленного имени JNDI
382 else
383 пен EmployeeEJBClient( args[ 0 ] );
384 }
385 }
Рис. 7.9. Класс EmployeeEJBClient для взаимодействия с EJ В-компонентом Employee
Метод createGUI (строки 269-369) формирует пользовательский интерфейс для
класса BmployeeEJBCIieiit. В строках 275-303 создаются текстовые поля
JTextField и надписи JLabel для каждого из свойств объекта Employee. В строках
314-363 создаются кнопки JButton и соответствующие слушатели ActionLis-
394
Глава 7
tener, чтобы дать возможность пользователю добавлять, обновлять и находить
информацию о сотрудниках.
Метод main (строки 372-384) требует, чтобы пользователь предоставил
аргумент командной строки, содержащий имя JNDI EJB-компонента Employee,
используемого в приложении. Этот аргумент командной строки дает возможность
пользователю задавать имя JNDI ддя версий EJB-компонента Employee либо с
персистентностью, управляемой компонентом, либо с персистентностью, управляемой
контейнером (например, BMPEmployee или CMPEmpioyee), В строках 376-377
осуществляется вывод указаний для пользователя, если имя JNDI не
предоставлено. В строке 383 создается новый экземпляр класса EmployeeEJBCHent с
использованием в качестве параметра заданного имени JNDI. При выполнении класса
EmployeeEJBCHent проверьте, чтобы клиентский JAR-файл для EJB-компонента
Employee располагался в каталоге, задаваемом переменной CLASSPATH для
класса EmployeeEJBCHent.
7.8. Ресурсы в Internet и во Всемирной паутине
j ava. sun. com/products/a jb/news. hbnl
Этот сайт предоставляет статьи и новости, имеющие отношение к технологии
Enterprise JavaBeans.
www. theserverside. com/rescrurces/pdf /e jbmatrixl 1, pdf
Этот документ содержит краткий справочник (в формате PDF) по интерфейсам,
классам и методам EJB.
java. sun.com/products/ejb/dcics .html
Основная страница спецификации Enterprise JavaBeans, содержащая документацию
и спецификации по технологии EJB,
developer,java.sun.com/developer/tachnicelArticlee/J2EE/build
В этой статье на сайте Java Developer Connection (для получения доступа к сайту
необходимо зарегистрироваться) обсуждается, как обеспечить переносимость приложений
J2EE для серверов приложений различных, производителей.
java. sun. com/ j гее/blueprint:
На сайте J2EE Blueprints рассматриваются Проверенные стратегии для построения
корпоративных приложений с помощью средств J2EE. Здесь можно найти технические
статьи, в которых обсуждаются раэличвые проекты и содержатся примеры кода,
реализующие эти проекты в реальных системах.
Резюме
• Компоненты-сущности EJB представляют собой объектно-ориентированное
представление постоянных данных, например информации, хранящейся в базе данных.
■ Компоненты-сущности EJB, которые используют управление персистентностью ни
стороне самого компонента, требуют, чтобы разработчик реализовал код для хранения и
извлечения данных из постоянных источников данных, которые представляет EJB-компонент.
• Компоиенты-сущиости EJB используют управление персистентностью на стороне
контейнера RJB, чтобы реализовать доступ к данным из постоянных источников. Разработчик
должен предоставить информацию об источнике данных при раэаертъзваыйи EJB-компонента.
• Компоненты-сущности EJB определяют методы create для создания новых экземпляров
EJB-компонентя, методы remove для удаления экземпляров EJB-компонекта и методы
finder для поиска экземпляров EJB-компонентов, Каждый метод create выполняет
операции вставки INSERT для создания новых записей а базе данных. Каждый метод remove
выполняет операции DELETE для удаления записей из базы данных. Каждый метод
finder ищет экземпляры EJB-сущностей, которые отвечают определенному критерию,
■ Метод findByPrimaryKey — один из методов поиска (finder) для компонентов-сущностей
EJB. Каждая EJB-сущность имеет метод findByPrimaryKey, который принимает в каче-
Компоненты EJB с данными
395
стве аргумента класс первичного ключа BJB-еущности, Компоненты-сущности EJB могут
также определять и другие методы поиска. Имя метода поиска должно начинаться
с finally и закапчиваться именем свойства, которое будет использоваться в качестве
критерия поиска.
• Все реализации EJB-сущности должны реализовывать иптерфейс EntityBean.
• Класс EntityContext предоставляет для EJB-компонента информацию о контейнере, в
котором развернут EJB-компонент.
• Все методы ejbCreate EJB-сущности должны возвращать класс первичного ключа
EJB-компонента. В большинстве EJB-компонентов в качестве класса первичного ключа
используются стандартный класс Java (например, Integer или String).
• Если таблица базы данных имеет составной первичный ключ (т.е. первичный ключ, кото-
рый состоит из нескольких полей), разработчик должен определить собственный (нестан-
дартный) класс первичного ключа.
■ Контейнер EJB вызывает метод ejbPostCreate после вызова метода ejbCreate для
выполнения любых необходимых соеле создания EJB-компонента действий.
• На использование метода setEntityContext накладываются некоторые ограничения,
поскольку при вызове этого метода экземпляр EJB-компонента еще не ассоциируется с
определенной записью базы данных. Например, EJB-компонент не должен вызывать метод
getPrimaryKey класса EntityContext, поскольку нет первичного ключа,
ассоциированного с текущим экземпляром EJB компонента.
• Метод setEntityContext должен выделять ресурсы, которые будут нужны экземпляру
EJB-компонента в течение его жизненного цикла (например, соединение с базой данных),
• Приложения J2EE используют специальный контекст идентификации JNDI
java:com.p/env, определяющий окружения компонента. EJB-компоненты содержатся
в подчиненном контексте ejb, а базы данных — в подчиненном контексте jdbc.
• Поля с контейнерным управлением представляют собой переменные экземпляра, которые
экземпляр EJB-компонента с персистентностыо, управляемой контейнером, использует
для кэширования информации, извлеченной из базы данных, Поля, управляемые
контейнером, также должны быть домечены как открытые (public), чтобы контейнер EJB мог
напрямую осуществлять доступ к ним.
Терминология
application server —сервер приложений
bean-managed persistence — персистентность
(сохраняемость), управляемая
компонентом
business methods — бизнес-методы
complex primary key —составной первичный
ключ
container-managed field — поле,
управляемое контейнером
container-managed persistence —
персистентность, управляемая контейнером
EntityBean, интерфейс
EntityContext, интерфейс
Упражнения для самоконтроля
7,1. Ответьте, является ли каждое из следующих высказываний истинным илн ложным.
Если высказывание ложно, объясните, почему.
a) Данные, ассоциированные с EJB-сущкостью, обычно хранятся в реляционной базе
данных.
b) Удаленный интерфейс для компонента-сущности EJB представляет собой таблицу
базы данных, с которой ассоциируется EJB-компонент.
c) Компоненты-сущности EJB, в которых используется управление нерсистектностья)
на стороне компонента, требуют, чтобы администратор развертывания определил
запросы SQL для вставки, изменения, удаления и выборки информации из базы данных.
findByPrimaryKey, метод
ejbPassivate, метод
ejbRemove, метод
entity EJB — компонент-сущность EJB
finder methods — методы поиска
home interface — собственный интерфейс
javax.ejb.EntityBean, интерфейс
primary-key class — класс первичнши)
ключа
remove methods — методы удаления
setEntityContext, метод
UnsetEutityContext, интерфейс
396
Глава 7
d) Методы создания (create) в собственном интерфейсе для компонента-сущности EJB
осуществляют вставку новых записей в основную базу данных,
e) Ковдгоаенты-сущности ЕJB дол-лны получать необходимые ресурсы С помощью
метода ejbCreate.
f) Компоненты-сущности EJB, в которых используется управление персистевтностью
на стороне контейнера, должны реализовывать интерфейс CMPEntityBean, тогда как
EJB-сущкости, в которых используеягся управление персистентностью на стороне
компонента, должны реализовывать интерфейс ЕпШуВеап.
7.2. Заполните пропуски в следующих высказываниях:
a) Для компонента-сущности EJB, в котором используется управление персистентно-
стью на стороне . для того, чтобы хранить данные в реляционной базе
данных, должен определить запросы SQL при развертывании EJB-компонен-
та.
b) Для компонента-сущности EJB, в котором используется управление персистентно-
стью на стороне __, для того, чтобы сохранить данные в реляционной базе
данных, должен реализовать код, который синхронизирует данные с базой
данных,
c) Каждый метод создания в собственном интерфейсе должен иметь соответствующий
метод в реализации EJB-компонснта,
d) Если компонент-сущность EJB имеет составной первичный ключ, разработчик
должен предоставить собственный , который представляет этот составной
первичный КЛЮЧ,
Ответы на упражнения для самоконтроля
7.1. а) Истинно. Ь) Ложно. Удаленный интерфейс определяет бизнес-методы для EJB-kom-
понента. Таблицу базы данных представлнет собственный интерфейс, с) Ложно.
Разработчик должен предоставить код для управления синхронизацией с основной базой
данных, d) Истинно, е) Ложно. Компоненты-сущности EJB должны получать
необходимые ресурсы с помощью метода setEntityContext. f) Ложно. Компоненты-сущности
EJB должны реализовывать интерфейс ЕпШуВеап.
7.2. а) ковтейнера, администратор развертывания приложения. Ъ) компонента,
разработчик, с) ejbCreate. d) класс первичного ключа.
Упражнения
7.3. Какие типы персистентности может использовать компонент-сущность? Какие
преимущества имеет каждый из типов?
7.4. Создайте компонент-сущность EJB для таблияы Titles базы данных books из главы 4.
Компонент-сущность EJB должен использовать управление персистентностью на
стороне компонента для синхронизации своих данных со значениями, хранящимися
в базе данных. Представьте методы $et и get для каждого поля в таблице Titles.
Представьте метод create, который принимает в качестве параметров ISBN-коД (ISBN) и
название книги (Title).
7.5. Модифицируйте компонент-сущность EJB, созданный вами в Упражнении 7,4, чтобы
использовать управление иерсистецтноссыо иа стороне контейнера вместо управления
персистентностью на стороне компонента.
8
Обмен сообщениями
с помощью Java Message
Service (JMS)
Цели
• Понять, что собой
представляет промежуточное
программное обеспечение,
ориентированное на о&ыеа
сообщениями.
• Понять, что собой
представляет модель обмена
сообщениями «от точки
к точке».
• Понять, что собой
представляет модель обмена
сообщениями
«издатель/подписчик».
• "Уяснить разницу между
двумя моделями обмена
сообщениями и узнать, когда
уместно применять
соответствующую модель.
• Понять, как использовать
интерфейс прикладного
программирования Java
Message Service (JMS) для
посгроеиия приложений Java
реализующих обмен
сообщениями.
• Познакомиться
с компонентами EJB,
управляемыми сообщениями.
Чтобы ощутить, что такое прожить
жизнь еще раз, нужно вспомнить все
прожитое и сделать эти воспоминания ка
можно более долговечными, изложив их на
бумаге.
Бенджамин Франклин
Сейте добрые дела: из них вырастут
приятные воспоминания.
Авна Луиза Жермен де Сталь
...и часто получать послания с небес...
Роберт Берне
398
Глава 8
8.1. Введение
При создании корпоративных приложений часто бывает полезно, чтобы
стандартные, не связанные друг с другом компоненты приложений «общались» между
собой. Например, клиенты книжного Internet-магазина должны иметь
возможность отправлять свои заказы. Одно из решений состоит в том, чтобы установить
слабую (косвенную) связь между клиентским приложением для заказа и покупки
книг к приложением поставщика, которое исполняет полученные заказы,
посредством системы обмена сообщениями, которую иногда называют пролежу точным
программным обеспечением, ориентированным на обмен сообщениями (message-
oriented middleware — MOM), Системы обмена сообщениями дают возможность
компонентам передавать сообщения, которые могут быть прочитаны другими
компонентами. Существует две основные модели систем обмена сообщениями: «от
точки к точке» и * издатель/подписчик*.
Модель обмена сообщениями лот точки к точке* позволяет компонентам
отправлять сообщения в очередь сообщений. В этой модели подразумевается, что
отправитель адресует сообщения потребителю сообщений: целевому компоненту,
который обрабатывает полученные сообщения. Когда этот целевой компонент
связывается с очередью для приема сообщений, он получает все сообщения, не
востребованные до сих лор. Сообщение считается востребованным после того, как сервер
отправляет его целевому компоненту, Заметим, что в модели «от точки к точке»
потребителем сообщения может быть только один клиент.
Обмен сообщениями с помощью Java Message Service {JMS)
399
Модель обмена сообщениями «издатель/подписчик» дает возможность
компонентам публиковать сообщения в тематическом разделе на сервере. Сервер
содержит различные тематические разделы, с которыми могут связываться
компоненты. Компоненты, заинтересованные в сообщениях, опубликованных в
определенном тематическом разделе, могут подписаться на данный раздел. Когда
издатель публикует сообщение в заданном тематическом разделе, текущие
подписчики получают это сообщение. Заметим, что в модели «издатель/подписчик», в
отличие от модели «от точки к точке», каждое опубликованное сообщение может
получить нуль или более подписчиков,
В обеих моделях обмена сообщениями сообщение имеет заголовок, свойства (не
обязательно) и тело (также не обязательно). Заголовок сообщения содержит такую
информацию, как адресат сообщения и время отправки. Свойства сообщения дают
возможность получателям выбирать, какие типы сообщений они хотели бы
принимать; отправитель сообщения может устанавливать эти свойства. Получатели
сообщений используют селекторы сообщений для фильтрации сообщений.
Фильтрация выполняется на стороне сервера; сервер не отправляет отсеянные фильтром
сообщения получателю.
Компоненты, управляемые сообщениями, представляют собой компоненты
Enterprise JavaBeans, которые поддерживают обмен сообщениями. Подобно тому,
как контейнер KJB может использовать любой экземпляр сеансового EJB-компо-
нента без состояния для обработки клиентских запросов, контейнер EJB для
компонентов, управляемых сообщениями, может использовать любой экземпляр
такого компонента для обработки входящих сообщений для заданной очереди или
тематического раздела. Поскольку Контейнер может использовать любой экземпляр,
компоненты, управляемые сообщениями, не могут сохранять состояние для
конкретного клиента. Используя компоненты, управляемые сообщениями, компонент
может принимать сообщения асинхронно, не расходуя ресурсы при ожидании
поступления сообщения.
В этой главе будет рассмотрен интерфейс прикладного программирования (API)
Java Message Service (JMS). JMS стандартизирует API для корпоративного обмена
сообщениями к поддерживает обе модели обмена сообщениями: <г от точки к точке»
и «издатель/подписчик». JMS поддерживает пять типов сообщений: BytesMessage,
MapMessage, ObjectMcssage, StreamMessage и TextMessage. Существует
несколько реализаций JMS от различных поставщиков. Более подробную информацию об
этих поставщиках и о JMS можно найти по адресу java.sun.coin/jms.
8.2. Установка и настройка J2EE 1.3
Эталонная реализация Java 2 Enterprise Edition 1,3 предоставляет поддержку
API Java Message Service. Заметим, что версия J2EE 1.3 поддерживает только
операционные системы Windows 2000, Windows NT 4.0, Solaris SPARC 7, Solaris
SPARC 8 и RedHat Linux v.6.1. Заметим также, что для работы J2EE 1.3
необходим пакет J2SE 1.3.1. Чтобы установить эталонную реализацию J2EE 1.3,
выполните следующие действия:
1. Загрузите и распакуйте соответствующий набор программного обеспечения
с сайта java.sun.com/j2ee/j2sdkee-beta/index.html.
2. Задайте для переменных окружения значения, приведенные в таблице на
рис. 8.1.
400
Глава 8
Переменная
окружения
J2EE НОМЕ
JAVA HOME
PATH
Значение
Каталог, в котором установлен пакет J2EE 1.3 (например, C:\j2sdkee1.3)
Каталог, в котором установлен пакет J2SE 1.3.1 (например. C:\jdk1.3.1)
Существующее значение переменной окружения PATH с добавлением
каталога bin J2EE 1.3 (например, C:\j2sdkee1.3\bin)
Рис. 8,1. Задание переменных окружения для установки J2EE 1.3
8.3. Обмен сообщениями «от точки к точке»
Модель обмена сообщениями «от точки к точке» (рис. 8.2) позволяет клиентам
отправлять сообщения в очередь сообщений. Получатель связывается с очередью
для приема сообщений, которые еще не были востребованы. В общем случае
подразумевается, что очередь рассчитана только на одного клиента, поэтому только
один клиент устанавливает соединение с очередью в качестве получателя. Если
получателя нет, сервер хранит отправленные в очередь сообщения до тех пор, пока
получатель ке установит соединение и не востребует эти сообщения.
Отправитель
Отправитель
Очередь
Сообщение
Получатель
Сообщение
Рис. 8.2. Модель обмека сообщениями «от точки к точке»
8.3.1 Приложение для голосования Voter: обзор
Для демонстрации модели обмена сообщениями «от точки к точке* рассмотрим
приложение для проведения голосования «ваш любимый язык
программирования». Класс Voter (раздел 8.3.2) отправляет голоса в виде сообщений в очередь
Votes, Эти сообщения представляют собой простые объекты TcxtMessage (из
пакета javax.jms). Тело сообщения содержит название языка программирования, за
который пользователь отдает свой голос. Класс VoteCollector (раздел 8.3.3) получает
(востребует) сообщения и подсчитывает голоса. Класс VoteCollector может
связываться с очередью Votes перед или после отправки сообщения. По мере
поступления в очередь дополнительных голосов класс VoteCollector обновляет счетчик
голосов и отображает итоговые результаты. Общая структура приложения
представлена на рис. 8.3.
8.3.2. Приложение Voter: серверная сторона
Часть приложения, относящаяся к отправителю, состоит из одного класса
Voter (рис. 8.4). Класс Voter дает возможность пользователю выбирать свой
любимый язык программирования и отправлять отданный за него голос в очередь Votes
в виде сообщения TextMessage.
Обмен сообщениями с помощью Java Message Service (JMS)
401
VoteCollector
I
Рис. 8.З. Общая структура приложения Voter
l
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
П
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// Voter.java
// Voter - это GUI, который дает возможность клиенту проголосовать
// эа свой любимый яэшс программирования. Пользователь отправляет
// свой голос в очередь Votes ж виде сообщения TextMessage,
package com.daitel.advjhtpl.jms.voter;
// Набор базовых пакетов Java
impor t 3 ava,awt.*;
import java.awt.event.*;
// Пакеты расширений Java
import j avax. swing, *,-
import j avax.jms.*;
import javax.naming.*;
public Class Voter extends JFrame {
private String selectedXanguage;
// Переменные JMS
private QueueConnection queueConnection;
private QueueSession queueSession;
private QueueSender queueSender;
// Конструктор Voter
public Voter()
i
// компоновка пользовательского интерфейса
super( "Voter" ) ,-
Container container = getContentPane();
container.setLayout( new BorderLayout() );
JTextArea voteArea =
new JTextArea( "Please vote for yourNn" +
"favorite programming language" );
voteArea.setEditablet false );
container.add( voteArea, BorderLayout.NORTH );
Глава
39
40 JPanel languagesPanel = new JPanelO;
41 languagesPanel.setLayout( new GridLayout( 0, 1 ) );
42
43 // добавление каждого иэ языков как об*ьекта JChockBox в группу
44 // ButtonGroup гарантирует, что будут выбран только один язык
45 ButtonGroup langvagesGroup = new ButtonGroup();
46 CheckBoxHandler CheckBoxHandler = new Che.ckBoxHaTvdl.er О ;
47 String languages[] =
48 { "C", "C++", "Java", "Lisp", "Python" );
49 selectedLanguage = "";
50
51 // создание переключателей JCheckBox для каждого из языков
52 // и добавление их в труппу ButtonGroup и в панель JSanel
53 for ( int i = 0; i < languages.length; i++ ) {
54 JCheckBox checkBox = new JCheckBox{ languages[ i ] );
55 checkBox.addItemListener( CheckBoxHandler ) ;
56 languagesPanel.add( checkBox );
57 languagesGroup.add{ checkBox );
58 }
59
60 container .add ( languagesPanel, BorderLayout. CENTER );
61
62 // создание кнопки для голосования
63 JButton submitButton - new JButton( "Submit vote!" );
64 container,add( submitButton, BorderLayout.SOUTH );
65
66 // вызов метода submitVote при нажатии кнопки submitButton
67 submitButton.addActionListener (
68
69 new ActionListene.ro {
70
71 public void actionPerformed ( ActionEvent event ) {
72 submitVote();
73 }
74 }
75 };
76
77 // вызов метода quit при Закрытии окна
78 addWindowLiatener(
79
вО new Window Adapter 0 i
SI
82 public void windowClosing{ WindowEvent event ) {
83 quit{) ;
84 J
85 }
86 ) ;
87
88 // соединение с очередью сообщений
89 try {
90
91 // создание контекста JNDI
92 Context jndi.Cont.ext. = new InitialContext () ,-
93
94 // извлечение мастера соединения с очередью
Обмен сообщениями с помощью Java Message Service (JMS)
40;
95 //из контекста JNDI
96 QueueCoruiecfcionFactory queueConnectionFactory =
97 ( QueueConnectionFactory )
98 jndicontext.lookup( "VOTE_FACTORY" );
99 Queue queue = ( Queue ) jndiContext.lookup( "Votes" );
100
101 // создание соединения, сеанса и отправителя
102 queueConnection =
103 queueConnectionFactory.createQueueConnection();
104 queueSession =
105 queueConnection.createQueueSession( false,
106 Session.AUTO_ACKN0WLEDGE );
107 queueSender = queueSession.createSender( queue );
108 )
109
110 // обработка исключения JNDI при идентификации
111 catch ( NamingException namingException ) {
112 namingException.printstackTrace();
113 System.exit( 1 );
114 }
115
116 // обработка исключения JMS при соединении с очередью
или cosдании сеанса
117 catch ( JMSException jmsException ) {
118 jmsException.printStackTrace();
119 System.exit( 1 );
120 }
121
122 ) // конец конструктора Voter
123
124 // отправка голоса в очередь Votes как сообщения TextMessage
125 public void submitVoteO
126 {
127 if ( seleetedbanguage != "" } 1
128
12 9 // создание текстового сообщения, содержащего сведения
о выбранном языке
130 try 1
131 TextMessage voteMessage =
132 queueSession.createTextMessage();
133 voteMessage.setText( seleetedbanguage );
134
135 // отправка сообщения в очередь
136 queueSender.send( voteMessage ) ;
137 }
138
139 // обработка исключения JMS
140 catch ( JMSException jmsException ) {
141 jmsException.printStackTrace () ;
142 }
143 }
144
145 ) // конец метода submitVote
146
147 // закрытие клиентского прилокения
148 public void quit О
404
Глава 8
Ю I
150 if ( queueConnection ! = null ) {
151
152 // закрытие соединения с очередью, если оно существует
153 try {
154 qu&ueConneetion.close();
155 }
156
157 // обработка исключения JMS
158 catch ( JMSException jmsException ) {
159 jjftaExoeption.printStackTraceO ;
160 }
161 }
162
163 System.exit( 0 );
164
165 } // конец метода quit
166
167 // запуск приложения Voter
168 public static void main( String args[) )
169 {
170 Voter voter = new Voter{);
171 voter.pack();
172 voter.setVisible( true );
173 }
174
175 // CheckBoxHandler обрабатывает событие при выборе переключателя
176 private class CheckBoxHandler Implements ItemListener (
177
178 // событие, связанное с выбором переключателя
179 public void itemStateChanged( ItemEvent event )
180 {
181 // обновление переменной selectedLanguage
182 JCheckBox source = ( JCheckBox } event.getSource();
183 selectedLanguage = source.getText(};
184 )
185 >
186 } _____
Рис. 8.4. Класс Voter отправляет голоса в очеаедь в виде сообщений
В строке 13 импортируется пакет javax.jms, который содержит классы и
интерфейсы API JMS. В строках 29-86 настраивается графический интерфейс
пользователя (GUI) для клиента Voter. В строках 53—58 создаются объекты JCheckBox,
с помощью которых клиент осуществляет голосование. Обратите внимание, что
они добавляются в группу переключателей Button Group (строки 45-58), чтобы
пользователь мог выбрать только одного кандидата.
В строках 89-120 настраиваются соединения JMS. В строке 92 создается
контекст JNDI, из которого программа извлекает объекты типа QueucConnection-
Factory и Queue, идентифицируемые, соответственно, по именам VOTE_FACTO-
RY и Votes. Заметим, что администратор сервера должен создать очередь и
мастера для соединения С очередью (раздел 8.3.4). Мастер для соединения с очередью
создает объект QueueConnection (строки 102-103). Объект QueueConnection
создает сеанс QueueSession (строки 104-106). Наконец, объект QueueSession создает
либо объект отправителя QueucScnder, либо объект получателя QueueReceiver.
Обмен сообщениями с помощью Java Message Service (JMS)
405
В данном случае QueueSession создает объект QueucSender для очереди Votes
(строка 107); теперь объект QueueSender может посылать сообщения в очередь.
После того как пользователь выбрал язык и щелкнул на кнопке Submit vote
(рис. 8.5), метод submit Vote создает объект TextMessage и устанавливает
значение selectedLanguage в качестве текста сообщения (строки 131-133). В строке 136
сообщение отправляется в очередь Votes.
Программа вызывает метод quit (строки 148-165), когда пользователь
закрывает окно приложения. В строках 150-161 осуществляется закрытие соединения
с очередью.
8,3.3. Приложение Voter: принимающая сторона
В рассматриваемом нами примере — приложении Voter — класс VoteCollector
(рис. 8.6) является предполагаемым получателем сообщений, отправленных в
очередь Votes. Класс VoteCollector получает голоса из очереди и отображает
количество полученных голосов. Заметим, что класс Voter может отправлять сообщения
в очередь до установки соединения с классом VoteCollector; класс VoteCollector
получит эти сообщения после того, как соединение будет установлено.
lOBMKlEftit
Please vote for your
favorite prog ram m»A§ language
□c - ■■ ;■'
new
lgJa»»l. . ,....:.;■ '
□ 1Л» 4
□ Python ■ .
Рис. 8.5. Приложение Voter осуществляет Web-опрсс «ваш любимый язык
программирования» и подсчитывает количество голосов
1 // VoteCollector.Java
2 // Класс VoteCollector подсчиталает голоса,- отправленные как сообще-
3 // ния TeJctMes sages в очередь Votes, и отображает количество голосов.
4 package com.deitel.advjhtpl.jms.voter;
5
6 // Набор базовых пакетов Java
7 import j ava.awt.*;
6 import Java.awt.event.*;
9 import java.util.*;
10
11 // Пакета расширений Java
12 import 3avax.swing.*;
13 import javax.jms.*;
14 import javax.naming.*•
15
16 public class VoteCollector extends JFrame {
П
lfl private JPanel displayPanel;
19 private Map tallies = new HashMapO ;
20
21 // Переменные JMS
22 private QueueConnection queueConnection;
24 // Конструктор VoteCollector
25 public VoteCollector[J
26 {
27 super( "Vote Tallies" );
28
29 Container container = getContentPane();
30
31 // подсчитанные результаты будут отображаться в панели
di splауРапе1
32 dispiayPanel = new jPanelO;
33 displayPanel.setLayout( new GridLayoutf 0, 1 ) );
34 container.add( new JScrollPanef displayPanel ) );
35
36 // вызов метода quit при закрытии окна
37 addWindowListener(
38
39 new WindowAdapter(} (
40
41 public void windowClosingf WindowEvent event } {
42 quit<);
43 }
44 }
45 >;
46
47 // соединение с очередью Votes
48 try (
49
50 // создание контекста JHDI
51 Context jndiContext = new InitialContextO;
52
53 // извлечение мастера соединения с очередью и
54 // очереди из контекста JNDI
55 QueueConnectionTactory queueConnectionFactory =
56 ( QueuaConnectionJactory }
57 jndiContext.lookup( "VOTE^FACTORI" >;
58 Queue queue = ( Queue ) jndiContext,lookup( "Votes"
59
60 // создание соединения, сеанса и получателя
61 queueConnection =
62 queueConnectionFactory.createQueueConnection(>;
63 QueueSession queueSession =
64 queueConnection.createQueueSession( false,
65 Session.AUT0_ACKNOWLEDGB );
66 QueueReceiver queueReceiver =
67 queueSession,createReceiver( queue );
68
69 // икициалиаация и установка слушателя сообщения
70 queueReceiver.setMessageListener(
71 new VoteListener( this } >;
72
73 // установка соединения
74 queueConnection.start();
75 }
76
7 7 // обработка исключения JNDI при идентификации
Обмен сообщениями с помощью Java Message Service (JMS)
407
7в catch ( HamingException namingException ) {
79 namingException.printStaekTraceO;
80 System.exit( 1 );
81 )
82
83 // обработка исключения JMS при соединении с очередью или
создании сеанса
84 catch ( JMSException jmsException ) {
85 jmsException.printStacMraceO ;
86 System.exit( 1 );
87 J
88
89 } // конец конструктора VoteCollector
90
91 // добавление голоса к соответствующей сумме голосов
92 public void addVote( String vote )
93 i
94 if ( tallies containsKey( vote ) ) {
9S
96 // если голос уже учтен
97 TallyPanel tallyPanel =
98 I TallyPanel ) tallies.get{ vote );
99 tallyPanel.updateTallyO;
100 }
101
102 // добавление в GUI данных по количеству набранных голосов
103 else {
104 TallyPanel tallyPanel = new TallyPanel ( vote, 1 ) ,'
105 displayPanel.add( tallyPanel ) ;
106 tallies.put( vote, tallyPanel );
107 validated ;
108 }
109 }
110
111 // выход из приложения
112 public void quit{)
113 {
114 if { queueConnection != null ) {
115
116 // закрытие соединения с очередью, если оно существует
117 try {
118 queueConnection.close();
119 }
120
121 // обработка исключения JMS
122 catch ( JMSException jmsExeeption ) {
123 j maException. prill tS tacJeTrace ( > ;
124 System.exit( 1 );
125 }
126
127 }
128
129 System.exit( 0 );
130
131 ) // конец метода quit
132
408
Глава 8
133 // запуск приложения VoteCollector
134 public static void main( String args[] )
135 {
136 VoteCollector voteCollector = new VoteCollector<);
131 VoteCollector.setSize( 200, 200 );
138 VoteCollector.setvisible( true );
139 )
140 }
Рис. 8.6. Класс VoteCollector получает и подсчитывает голоса, отданные за любимый
язь,к программистов в кия
Класс VoteCollector осуществляет сбор и подсчет голосов, извлеченные из
очереди "Votes. Переменная tallies (строка 19) представляет собой карту соответствий
(объект Map) между именами кандидатов и объектами TaJlyPaoel (рис. 8.9); в
соответствии со значением этой переменной обновляется счетчик голосов при
поступлении нового голоса. В строках 27-45 осуществляется компоновка GUI. Обратите
внимание, что если класс VoteCollector не получил каких-либо голосов, никакая
информация в GUI не отображается.
В строках 48—87 устанавливается соединение с очередью Votes. В строке 51
создается контекст JNDI для извлечения объектов QueneCotuiectioiLFactory и Queue
(строки 55-58). Объект QueueConnectionFactory позволяет программе создавать
соединение с очередью QueueConnection (строки 61-62). Объект QueueCormection
создает сеанс QueueSession (строки 63-65). Объект QueueSession, в свою очередь,
создает объект QueueReceiver (строки 66-67), который получает голоса из очереди
Queue. Б строках 70-71 создается новый объект VoteListener (рис. 8.8), а объект
VoteListener устанавливается в качестве слушателя сообщений для объекта
QueueReceiver. В строке 74 начинается установка соединения queueConnectian;
с этого момента VoteListener будет обрабатывать сообщения, полученные из
очереди queue.
Метод addVote (строки 92-109) обновляет количество голосов и отображает его
(рис. 8.7). Если имеется объект TallyPanel, соответствующий языку, за который
отдан голос vote, в строках 97-99 количество голосов инкрементируется путем
вызова метода updateTally. Если соответствующего объекта TallyPanel кет, в
строках 104-106 в таблицу talliesMap добавляется новая запись. Программа вызывает
метод quit (строки 112-131), когда клиент закрывает окно приложения. В строках
114-127 осуществляется закрытие соединения queueConnev.tion.
Рис. 8.7. Класс VoteCollector подсчитывает количество голосов и отображает число
голссоа отданных за различные языки программирования
Класс VoteListener (рис. 8.8) реализует интерфейс MessageListener. Когда
объект QueueReceiver получает сообщение, слушатель сообщения QueueReceiver
обрабатывает его. Интерфейс MessageListener определяет единственный метод
Обмен сообщениями с помощью Java Message Service (JMS)
409
onMessage (строки 22-49), который программа вызывает, когда поступает новое
сообщение. В строке 29 проверяется, имеет ли сообщение тип Text Message. Если
да, в строках 30-31 извлекается текст сообщения; затем в строке 32
осуществляется вызов метода addVote класса VoteCollection для обновления счетчика голосов.
1 // VoteListener.java
2 // VoteListener - это слушатель сообщений для
3 // очереди Votes. Он реализует заданный метод
4 // onMessage для обновления GUI при получении
5 // очередного голоса.
6 package com.deitel.advjhtpl.jms.voter;
7
8 // Пакеты расширений Java
9 import javax.jms.*;
10
11 public class VoteListener implements Me&sageListenec \
12
13 private VoteCollector voteCollector;
14
15 // Конструктор VoteListener
16 public VoteListener( VoteCollector collector }
17 {
18 voteCollector = collector;
19 J
20
21 // прием нового сообщения
22 public void onMessage{ Message message )
23 (
24 TextMessage voteHessage;
25
26 // извлечение и обработка сообщения
27 try {
28
29 if ( message instanceof TextMessage ) {
30 voteMessage = ( TextMessage ) message;
31 String vote = voteMessage.getText();
32 voteCollector. addVote ( vote ) ;
33
34 System.out.println( "Received vote: " + vote );
35 }
36
37 else {
38 System.out.println( "Expecting " +
39 "TextMessage object, received " +
40 message.getClass().getUame0 );
41 }
42 }
43
44 // обработка исключения JMS при передаче сообщения
45 catch ( JMSException jmsException ) {
4 6 jmsException.printStacXTracet);
47 }
48
49 } // конец метода onMessage
50 }
Рис. 8.8. Класс VoteListener принимает сообщения из очереди
410
Глава 8
Класс TallyPanel (рис. 8.9) хранит и отображает количество голосов tally для
языка программирования, за который отдается голос. Метод updateTally (строки
37-41) инкрементирует значение tally на единицу.
1 // TallyPanel.java
2 // Та11Panel - это компонент GUI, который отображает
3 // имя кандидата и набранное им число голосов.
4 package com.deitel.advjhtpl. jms.voter;
5
6 // Набор базовых пакетов Java
7 import java.awt.*;
8
9 // Пакеты расширений Java
10 import javax.swing.*;
11
12 public class TallyPanel extends JPanel {
13
14 private JLabel nameLabel;
15 private JTextField tallyField;
IS private String name;
17 private int tally;
18
19 // Конструктор TallyPanel
20 public TallyPanel{ String voteHame, int voteTally )
21 {
22 name = VOteName;
23 tally = voteTally;
24
25 nameLabel = new JLabel( name );
26 tallyField =
27 new JTextField( Integer.toString{ tally ), 10 );
28 tallyField.setEditablet false );
29 tallyField.setBackground( Color,white );
30
31 add( namaLabel );
32 add I tallyField );
33
34 \ // конец конструктора TallyPanel
35
36 // прибавление единицы к числу набранных голосов
37 public void updateTally()
38 (
39 tally++;
40 tallyField.setText( integer.toString( tally ) );
41 }
42 }
Рис. 8.9, Класс TallyPanel отображает имя кандидата и набранное им количество голосов
8.3.4. Приложение Voter: настройка и выполнение
Чтобы выполнить приложение, введите следующие команды в приглашение
командной строки:
1. Запустите сервер J2EE в окне команд:
j2ee -verbose
Обмен сообщениями с помощью Java Message Service (JMS)
411
2. В новом окне команд создайте очередь Votes:
}2eeadmin —adcUJmsDestination Votes queue
3. Проверьте, что очередь была создана:
j2eeadmin -listJmsDestination
4. Создайте мастер соединений:
j2eeadmin -addJmsFactory VOTE_FACTORY queue
5. Запустите класс VoteCollector:
Java -classpath %J2EE_HOME%\lib\j2ee.jar;.
-Djms.properties = %J2EE_HOME%\config\jms_client.properties
com.deitel.advjhtpl.jms.voter.VoteCollector
6. Запустите класг. voter в новом окне команд:
java -classpath %J2EE_H0ME%\lib\j2ee.jar;.
-Djms.properties = %J2EE_HQME%\config\jms_client.properties
com.deitel.adv^htpl.jms.voter.Voter
После выполнения приложения мастер соединений может быть удален с
помощью команды:
j2eeadmin -removeJmsFactory VOTE_FACTORY
Чтобы удалить тематический раздел, воспользуйтесь командой:
j2eeadmin -removeJmsDestination Votes
Чтобы завершить работу сервера J2EE, воспользуйтесь командой:
j2ee -stop
8.4. Обмен сообщениями в модели «издатель/подписчик»
Модель обмена сообщениями «издатель/подписчик» (рис. 8.10) дает
возможность нескольким клиентам соединяться с тематическим разделом на сервере и
отправлять и принимать сообщения. Установив соединение, клиент может
публиковать сообщения или подписаться на тематический раздел. Когда клиент публикует
сообщение, сервер отправляет это сообщение тем клиентам, которые подписались на
данный тематический раздел. Клиент может иметь подписку двух типов:
кратковременную и долгосрочную. При кратковременной подписке сообщения получаются
только во время активного состояния подписчика. При долгосрочной подписке
сообщения могут быть получепы и при неактивном состоянии: сервер хранит
сообщения, отправленные в тематический раздел, когда подписчики неактивны, и
отправляет эти сообщения клиенту, когда тот вновь активизирует подписку. Заметим, что
если клиент указывает селектор для фильтрадии сообщений, сервер хранит только
те сообщения, которые соответствуют задаваемому этим селектором условию.
Издатель
Сообщение
Издатель
Сообщение
Тема
Сообщение
Подписчик
Сообщение
Подписчик
Рис. 8.10. Модель обмена сообщениями «издатель/подписчик»
412
Глава 8
8.4.1. Приложение Weather: обзор
Рассмотрим пример обмена сообщениями в модели «издатель/подписчик»,
в котором используется кратковременная подписка. Следующее приложение
публикует сообщения в тематическом разделе Weather на сервере. Эти сообщения
содержат информацию о погоде для различных клиентов на территории США.
Информация извлекается со страницы Национальной метеорологической службы
США (NWS) (iwin.Jiws.uoaa.gov/iwin/us/tTaveler.html). Класс WeatherPublisher
(раздел 8.4.2) извлекает последние сведения о погоде и публикует их в виде
сообщений в тематическом разделе. Класс WeatherSnbscriber (раздел 8.4.3)
определяет графический пользовательский интерфейс, который дает возможность
пользователю выбирать города, сведения о погоде в которых его интересуют. Класс
WeatherS ubscriber осуществляет подписку на тематический раздел Weather
и принимает сообщения, относящиеся к выбранным городам, используя селектор
сообщений. Структура приложения представлена на рис. 8.11.
Национальная
метеорологическая
служба
HTML
flea the г Publishe r
ОЬ э еctMaa»адя
Тематический
раздел Weather
ObjectMessage
ОЬj ectMessage
Weather-Subscriber
WeatherSubscrib»r
Рис. 8.11. Схема приложения Weather
8.4.2. Приложение Weather: часть, относящаяся к издателю
Класс Weather Publisher (рис. 8.L2) извлекает последние сведения о погоде
с сайта Национальной метеорологической службы США и публикует их в виде
сообщений в тематическом разделе Weather. Сообщение типа ObjectMessage
содержит информацию о погоде в каждом из городов. Свойство City объекта
ObjectMessage определяет соответствующий город. Класс WeatherSubscriber
использует свойство City в своем селекторе сообщений.
1 // Weather-Publisher, Java
2 // Weather/Publisher извлекает информацию о погоде с сайта Национальной
3 // метеослужбы (NWS) и публикует их в тематическом разделе Weather
4 // в виде сообщений ObjectMessage, содержащих компоненты WeatherBeans.
5 // Название города используется в свойстве City в заголовке сообщения,
6 package com.deitel.advjhtpl.jms.weather;
7
8 // Набор базовых пакетов Java
Обмен сообщениями с помощью Java Message Service (JMS) 413
9 import java.io-*,"
10 import Java.net. *,-
11 import java.util.*;
12
13 // Пакеты расширений Java
14 import javax.jms.*;
15 import javax.naming.*;
16
17 // Пакеты Deitel
18 import com.deitel.advjhtpl.rmi.weather.WeatherBean;
19
20 public class HeatherPublisher extends TimerTask {
21
22 private BufferedReader in;
23 private TopicConnection topicConnection;
24
25 // Конструктор WeatherPublisher
26 public WeatherPublisher()
27 {
28 // обновление информации о погоде каждую минуту
29 Timer timer = new Timer();
30 timer.scheduleAtFixedRate( this, Qr 60000 );
31
32 // разрешить пользователю выйти иа приложения
33 InputStreamReader inputStreamReader =
34 new InputStreamReader( System.in );
35 char answer = '\0',-
36
37 // ожидание ввода пользователем символа q или Q
38 while ( ! ( ( answer == 'q' ) I I ( answer = 'Q' ) ) } {
39
40 // чтение символа
41 try {
42 answer = ( char ) inputStreamReader.read();
43 }
44
45 // обработка исключения при вводе/выводе
4 6 catch ( lOException ioException ) (
47 ioException,printstaclcTrace () ;
48 System, exit ( 1 ) ,-
49 }
50
51 ) // конец цикла while
52
53 // закрытие соединений
54 try (
55
56 // закрытие соединении topicConnection, если оно существует
57 if ( topicConnection != null ) (
58 topicConnection.close ();
59 )
60
61 in.closet); // закрытие соединения с ИеЬ-сервером NWS
62 timer, cancel {) ,- // останов таймера
63 }
64
414
Глава 8
65 // обработка исключения JMS при закрытии соединения
с тематическим разделом
66 eetch ( JMSException jmsException ) (
67 jmsExeeption.printstackTrace();
€8 System.exit( 1 );
6» )
70
71 // обработка исключения ввода/вывода при закрытии
72 // соединения с Web-сервером NWS
73 catch ( lOExoeption ioException ) {
74 ioException.printStackTrace();
75 System.exit( 1 );
76 }
77
78 System.exit t 0 ') ;
79
80 } // конец конструктора WeatherPublisher
81
82 // получение информации о породе с сайта HWS
83 public void run()
84 {
85 // соединение с тематическим разделом Weather
66 try {
87 System.out.printing "Update weather information..." );
88
89 // создание контекста JHDI
90 Context jndiContext = new initialContext();
91 String topicNaroe = "Heather";
92
93 // получение мастера соединения с тематическим
94 // разделом и раздела иэ контекста JNDI
95 TopicConnectionFactory topicConnectionFactory -
96 ( TopicConnectionFactory )
97 jndiContext.lookup( "WEATHER_FACTORY" );
9$
99 Topic topic =
100 ( Topic } jndiContext.lookup( topicName ),-
101
102 // создание соединения, сеанса, издателя и сообщения
103 topicConnection =
104 topicConnectionFactory.createTopicConnection();
105
106 TopicSession topicSession =
107 topicConnection.createTopicSession( false,
108 Sessiun.AUTO_ACKHOWLEDGE );
109
110 TopiePublisher topicPublisher =
111 topicSession.oreatePublisber( topic );
112
113 ObjectMeasage message =
114 topicSession.createObjectMessage();
115
116 // соединение с сайтом Национальной метеослужбы и публикация
117 // информации о погоде в тематическом разделе
118
119 // страница сайта метеослужбы National Heather Service
Travelers Forecast
Обмен сообщениями с помощью Java Message Service (JMS)
415
120 URL url = new ORL(
121 "http://iwin.nws.rtoaa.gov/iwin/us/traveler.html" );
122
123 // настройка текстового потока ввода для чтения
содержимого Web-страницы
124 in = new BufferedReader(
125 new InputstreamReadar( url.openStream() ) >,-
126
127 // разделитель, помогавший определить начало данных на
Web-странице
128 String separator = "TAV12";
129
130 // нахождение символа-разделителя на Web-странице
131 while ( !in.readLine().startsWith( separator ) )
132 ; // отсутствие действий
133
134 // строки, представляющие на Web-странице Travelers
135 // Forecast заголовки для погоды в дневное и ночное время
136 String dayHeader =
137 "CITY WEA HI/LO WEA HI/LO";
138
139 String nightHeader =
140 "CITY WEA LO/HI WEA LO/HI";
141
142 String inputLine = "",-
143
144 // нахождение заголовка, представляющего начало данных
О породе
145 do {
146 inputLine = in.readLine 0;
147 }
148
149 while ( !inputLine.equals( dayHeader ) ЬЬ
150 !inputLine.equals( nightHeader ) );
151
152 // создание объектов WeatherBean с данными по каждому из
153 // городов и публикация их в разделе Heather с использованием
названия города в качестве типа сообщения
154 inputLine = in.readLine();
// получение информации для первого города
155
156 // Часть строки inputLine, содержащая нужные нам данные,
157 // должна иметь длину, равную 28 символам. Бели длина
158 // строки превышает 28 символов, выполнить обработку данных.
159 while ( inputLine.length() > 28 ) (
160
161 // создание объекта WeatherBean для города
162 // первые 16 символов относятся к названию города
163 // следующие шесть символов относятся к описанию погоды
164 // следующие шесгь символов относятся к ночной и дневной
температуре HI/L0
165 WeatherBean weather — new WeatherBean(
166 inputLine.substring( 0, 16 ).trim(),
167 inputLine.substring( 16, 22 ).trim(),
168 inputLine.substring( 23, 29 ).trim() );
169
170 // Публикация объекта WeatherBean с указанием названии
416
Глава 8
171 // города в качестве свойства сообщения. Это свойство
172 // будет использоваться клиентами при выборе города.
173 message.setObject{ weather );
174 message.setStringProperty( "City",
175 weather.ge tCityName() );
176 topicPublisher.publish( message );
177
П8 System.out.pxintlnt "published message for city: "
179 + weather. getCityNameO );
1B0
181 inputLine = in.readLine();
// получение данных для еледужщего города
182 J
183
184 System.out.println{ "Weather information updated." );
185
186 } // конец блока try
187
188 // обработка исключения JNDI при идентификации
189 catch ( NamingException namingException ) {
190 namingException.printStackTrace();
191 System.exit( 1 );
192 }
193
194 // обработка исключения JMS при соединении,
195 ff создании сеанса, публикации или сообщении
196 catch ( JMSException jmsException ) {
197 j msException.prints tackTrace();
198 System.exit( 1 ) ;
199 J
200
201 // обработка ошибки при соединении с сайтом Национальной
мете ослужбы
202 catch ( java.net.ConnectException connectException ) {
203 connectException.printStackTrace();
204 System.exit( 1 );
205 }
206
207 // обработка других исключений
208 catch ( Exception exception } {
209 exception.printStackTrace(J;
210 System, exit ( 1 > ;
211 }
212
213 } // конец метода run
214
215 // запуск приложения WeatherPubiisher
216 public static void main( String args[] )
217 {
218 System.err,println( "Initializing server..An" +
219 "Enter 'q' or 'Q' to quit" );
220
221 WeatherPubiisher publisher = new WeatherPubiisher0;
222 }
223 }
Рис. 8.12. Класс WeatherPubiisher публикует сообщения в тематическом разделе Weather
Обмен сообщениями с помощью Java Message Service (JMS)
417
Класс WeatherPublisher публикует информацию о погоде в городах США в
тематическом разделе Weather на сервере. Каждую минуту класс WeatherPublisher
получает информацию о погоде с сайта Национальной метеорологической службы
и публикует отдельные сообщения о погоде в каждом городе. На рис. 8.13
показана работа сервера в действии. Метод run (строки 83—213), определяемый классом
TimerTask, осуществляет соединение с тематическим разделом и публикует
сообщения. В строке 90 создается контекст JNDI, в котором класс WeatherPublisher
ищет объекты TopicConnectionFactory и Topic (строки 95-100). Объект TopicCon-
nectionFactory — его должен создать администратор сервера —■ создает объект
TopicConnection (строки 103-104). Затем объект TopicConnection создает объект
Topic Session (строки 106-108). В строках 110-111 из объекта Topic Session
извлекается объект TopicPublisher. Объект TopicSfission также создает объект Object-
Message (строки 113-114); объект ObjectMessage будет содержать объекты Wea-
therBean, которые хранят информацию о погоде для заданного города. В строках
165-168 создается объект WeatherBean, содержащий данные, полученные с сайта
Национальной метеорологической службы. Метод setObject класса ObjectMessage
помещает объект WeatherBean в сообщение. В строках 174-175 используется
метод setStringProperty класса Message для задания в качестве значения строкового
свойства City названия города. Подписчик может использовать это свойство для
фильтрации сообщений. Наконец, метод TopicPuhlisher публикует сообщение
в тематическом разделе topic (строка 176).
Рис. 8.13. Класс WeatherPublisher публикует сообщения с обновленной информацией
о погоде
8.4.3. Приложение Weather: часть, относящаяся к подписчику
Югасс WeatherSubscriber (рис. 8.14) осуществляет подписку на тематический
раздел Weather для получения свежей информации о погоде для выбранных
городов. Класс WeatherSubscriber создает графический интерфейс пользователя,
который дает возможность пользователю выбирать города, и отображает погодные
условия в указанных городах.
Как и класс WeatherPublisher, класс WeatherSubscriber использует контекст
JNDI для получения объектов TopicConnectionFactory и Topic (строки 55-65).
Объект TopicConnectionFactory создает объект TopicConnection (строки 68-69),
который создает объект TopicSession (строки 72-73). В строке 76 новый
экземпляр класса WeatherListener (рис. 8.17) инициализируется в качестве слушателя
сообщения для получателя информации иа тематического раздела (который был
418
Глава 8
создан в методе getWeather). В строках 90-135 настраивается пользовательский
интерфейс; погодные условия отображаются с помощью класса WeatherDisplay
(рис. 8.18). Графический интерфейс клиента представлен на рис. 8.15.
1 // WeatherSubscriber.java
2 // Класс WeatherSubscriber определяет GDI для клиента,
3 // запросившего информацию о погоде в различных городах.
4 // WeatherSubscriber извлекает информацию о погоде из раздела Weather.
5 // Тело каждого сообщения содержит объект WeatherBean.
6 // Заголовок сообщения содержит строковое свойство City,
7 // которое позволяет клиентам выбирать нужные города,
в package com.deitel.advjhtpl,jms.weather;
9
10 // Набор базовых пакетов Java
11 import Java.awt.*;
12 inport java.awt,event,*;
13
14 // Пахеты расширений Java
15 import javax.swing.*;
I б import j avax.naming.*;
II import jarvax. jms. * ;
18
19 public class WeatherSubscriber extends JFrama {
20
21 // переменные GDI
22 private WeatherDisplay weatherDisplay;
23 private JList citieeList;
24
25 // города, для которых в тематической разделе Weather
26 // имеется свежая информация о погоде
27 private String cities £J = { "ALBANY NY", "ANCHORAGE",
28 "ATLANTA", "ATLANTIC CITY", "BOSTON", "BUFFALO",
29 'BURLINGTON VT", "CHARLESTON WV", "CHARLOTTE", "CHICAGO",
30 "CLEVELAND", "DALLAS FT WORTH", "DENVER", "DETROIT",
31 "GREAT FALLS", "HARTFORD SPGFLD", "HONOLULU",
32 "HOUSTON INTCNTL", "KANSAS CITY", "LAS VEGAS",
33 "LOS ANGELES", "MIAMI BEACH", "MPLS ST PAUL", "NEW ORLEANS",
34 "NEW YORK CITY", "NORFOLK VA", "OKLAHOMA CITY", "ORLANDO",
35 "PHILADELPHIA", "PHOENIX", "PITTSBURGH", "PORTLAND ME",
36 "PORTLAND OR", "RENO" );
37
38 // переменные JMS
39 private TopieConnection topicConnection;
40 private TopicSession topicSession;
41 private Topic topic;
42 private TopicSubscriber topicSubscriber;
43 private WeatherListener topicListener;
44
45 // конструктор WeatherSubscriber
46 public WeatherSubscriber{)
47 {
48 super( "JMS WeetherSubscribet..." );
49 weatherDisplay = new WeatherDisplayО;
50
51 // настройка контекста JNDI и соединений JMS
52 try {
Обмен сообщениями с помощью Java Message Service (JMS)
419
53
54 // создание контекста JHDI
55 Context. jndiContext = new InitialContext () ;
56
57 // получение мастера соединения с разделом
58 //из контекста JNDI
59 TopicConnectionFactory topicConnectionFactory =
60 { TopicConnectionFactory ) jndiContext.lookup(
61 "WEATHER__FACTORY" ) ;
62
63 // извлечение хеиы из контекста JNDI
64 String topicName = "Weather";
65 topic = ( Topic ) jndiContext.lookup( topicName );
66
67 // создание соединения с тематическим разделом
68 topicConnection =
69 topicConnectionFactory.createTopicConnection();
70
71 // создание сеанса
72 topicSession = topicConnection.createTopicSession( false,
73 Session.AUTO_ACKNOWLEDGE );
74
7 5 // инициализация слушателя
76 topicListener = new WeatherListener( treatherDisplay );
"Л }
78
79 // обработка исключения JHDI при идентификации
SO catch ( NamingException namingBxception ) {
81 namingException.printstackTrace();
82 }
83
84 // обработка исключения JMS при соединении с разделом
или создании сеанса
85 catch ( JMSException jmsException ) {
86 jmsException.printStackTra.ceO;
87 }
88
89 // компоновка интерфейса пользователя
90 Container container = getContentPane();
91 container.setLayout( new BorderLayoutО );
92
93 JPanel selectionPanel = new JPanel();
94 selectionPanel.setLayout( new BorderLayout() );
95
96 JLabel selectionLabel = new JLabel( "Select Cities" );
97 selectionPanel.add( selectionLabel, BorderLayout.NORTH );
90
99 // создание списка городов, для которых пользователи
100 // могут запрашивать свежу» информацию о породе
101 citiesList = new JList{ cities );
102 selectionPanel.add{ new JSего11Pane{ citiesList ),
103 BorderLayout.CENTER );
104
105 JButton gstWeatherButton = new JButton( "Get Weather..." };
106 selectionPanel.add( getWeatherButton, BorderLayout.SOUTH );
107
420
Глава 8
108 // вызов метода getWeather при нажатии кнопки getWeatherButton
109 getWeatherButton.addActionListener {
110
111 пен ActionListener () {
112
113 public void aetionperformed ( ActionEvent event )
114 {
115 getWeather () ,-
116 }
117 }
118
119 } ; // конец обращения к addActionListener
120
121 container.add( selectionPanel, BorderLayout.WEST );
122 container.»dd( weatherDisplay, BorderLayout.CENTER );
123
124 // вызов метода quit при закрытии окна
125 addWindowListener(
126
127 new WindowAdapterO (
128
129 public void windowClosing( WindowEvent event )
130 {
131 quit();
132 }
133 }
134
135 ); // конец обращения к addWindowListener
136
137 } // конец конструктора WeatherSubscriber
138
139 // получение информации о породе в выбранных городах
140 public void getWeather()
141 {
142 // извлечение индексов для выбранных городов
143 int seleetedlndices[] = citiesList.getSelactedlndices{);
144
145 if ( seleetedlndices.length > 0 ) {
146
147 // если подписчик раздела существует,
148 // значит метод ранее уже вызывался
149 if ( topicSubscriber ! = null ) (
150
151 // закрытие предыдущего подписчика на раздел
152 try {
153 topicSubscriber.close();
154 J
155
156 // обработка исключения JMS
157 catch ( JMSException imsException ) {
158 jmaException.printStacJcTrace () ;
159 }
160
161 // очистка экрана, содержащего данные для предыдущих
городов
162 weatherDisplay.clearCities();
Обмен сообщениями с помощью Java Message Service (JMS)
421
163 )
164
165 // создание селектора сообщений для извлечения
заданных городов
166 StringBuffer messageSelector = new StringBufferО;
167 messageSelector.append(
168 "City = ' " +■ cities [ selectedlndices [ 0 ]]+'"" )
169
170 for ( int i = 1; i < selectedlndices.length; i++ ) {
171 messageSelector.append{ " OR City = '" +
172 cities[ selectedlndices[ i ] ] + "'" );
173 }
174
175 // создание подписчика на раздел и подписки
176 try {
177 topicSubscriber = topicSession.createSubscriber(
178 topic, messageSelector.toString(), false );
179 topicSubscriber.setMessageListener( topicListener )
180 topicConnection.start();
181
182 JOptionPane.showMessageDialog( this,
183 "A weather update should be arriving soon..." );
184 }
185
186 // обработка исключения JMS
187 catch ( JMSException jmsException ) {
188 jmsException.printStackTr.ace () ;
189 }
190
191 } // конец блока if
192
193 } // конец метода getWeather
194
195 // выход из приложения WeatherSubscriber
196 public void quitO
197 {
198 // закрытие соединения и подписки на тематический раздел
199 try {
200
201 // закрытие подписчика на раздел
202 if t topicSubscriber (= null J {
203 topicSubscriber.close 0 ;
204 }
205
206 // закрытие соединения с разделом
207 topicConnection.close(};
208 }
209
210 // обработка исключения JMS
211 catch ( JMSException jmsException ) {
212 jmsException.pirirttStacicTrace() ;
213 System.exitt 1 ) ;
214 }
215
216 System.exit( 0 );
217
422
Глава 8
218 } // конец метода quit
219
220 // запуск приложения WeatherSubscriber
221 public static void main{ String args [] )
222 {
223 WeatherSubecriber subscriber — new WeatherSubecriber();
224 subscriber.pack();
225 subscriber.setVisible{ true );
226 }
227 }
Рис. 8.14. Класс WeatherSubscriber дает возможность пользователю принимать свежую
информацию о погоде
Рис. 8.15. Выбор городов для получения свежей информации о погоде
Программа вызывает метод getWeather (строки 140-193), когда пользователь
нажимает кнопку Get Weather. Если метод ранее уже вызывался (т.е.
пользователь до этого уже нажимал эту кнопку), в строках 149—163 осуществляется
закрытие предыдущего объекта TopicSubscriber, чтобы новый подписчик мог
выполнять фильтрацию для выбранных городов с помощью селектора сообщений. Если
клиент задал селектор сообщений для подписки, сервер будет отправлять только
те сообщения, которые удовлетворяют условиям фильтрации, установленным
клиентом. В строках 16G-173 создается селектор message Selector, чтобы отображать
погодные условия для выбранных городов. В основу синтаксиса селектора
message Set ее tor положен синтаксис SQL92 (подробности содержатся в разделе
Message документации javadoc). В строках 177 178 из объекта TopicSession
создается объект TopicSubscriber, которому в качестве параметров передаются объект
Topic и селектор messageSelector. Третий параметр, имеющий значение false,
указывает, что подписчик может принимать сообщения, опубликованные в
течение его собственного соединения. В строке 179 в качестве слушателя сообщений
для TopicSubscriber устанавливается topicListener. Слушатель сообщений для
TopicSubscriber обрабатывает новые сообщения по мере их поступления. Наконец,
в строке 180 открывается соединение TopicConnection; после открытия
соединения класс TopicSubscriber будет принимать сообщения, опубликованные в этом
тематическом разделе.
Когда пользователь закрывает окно приложения, в строках 202-204
осуществляется закрытие объекта TopicSubscriber, если таковой существует. В строке 207
закрывается соединение TopicConnection.
Класс Weather-Listener (рис. 8.17) реализует интерфейс MessageListener.
В этой связи он определяет метод onMcssage (строки 26-57) для получения
входящих сообщений. Когда поступает новое сообщение, в строке 32 проверяется, имеет
ли сообщение надлежащий тип: ObjcctMessage. Если да, метод getObject извлека-
Обмен сообщениями с помощью Java Message Service (JMS)
423
ет объект WeatherBean из объекта Object Message. Объект WeatherBean затем
передается объекту WeatherDisplay, который отображает соответствующую
информацию пользователю. На рис. 8.16 показано окно приложения после получения
обновленной информации о погоде.
Рис. 8.16. Получение классом WeatherSubscrtber обновленной информации о погоде
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
П
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// WeatherListener.Java
// WeatherListenei: - это слушатель типа MessageListener для
// подписки на тематический раздел Weather. Он реализует
// указанный метод onMessage для обновление GC7I
// информацией о погоде в заданной городе.
package com.deitel.advjhtpl.jms.weather;
// Пакеты расширений Java
import javax.jms.*;
import javax.swing.*;
// Пакеты Deitel
import com.deitel.advjhtpl. rail.weather.WeatherBean;
public class HeatherListener implements MessageListener {
private WeatherDisplay WeatherDisplay;
// Конструктор WeatherListener
public WeatherListener( WeatherDisplay display )
{
)
WeatherDisplay = display;
// прием нового сообщения
public void onMessage( Message message )
i
II извлечение и обработка сообщения
try t
// проверка, что сообщение имеет тип ObjectMessage
if ( message instanceof ObjectMessage ) {
// получение компонента WeatherBean из сообщения
ObjectMessage
ObjectMessage ObjectMessage =
( ObjectMessage ) message;
424
Глава 8
3*7 WeatherBean weatherBean =
38 ( WeatherBean ) objectMessage.getobject();
39
40 // добавление компонента WeatherBean для его отображения
41 weatherDisplay.addltem( weatherBean );
42
43 } // конец блока if
44
45 else {
46 System.out.println( "Expected ObjectMessage," +
47 " but received " + message.getclass{).getName {) };
Л8 }
49
50 } // конец блока try
51
52 // обработка исключения JMS от сообщения
53 catch ( JMSException jrosException ) {
54 jmsException.printstackTraee();
55 }
56
$7 } // хонец метода onMessage
58 } __
Рис, 8.17. Класс WeatherUstener осу теста ляет подписку на тематический раздел
Weather для получения прогноза погоды
Класс WeatherDisplay (рис. 8.18) отображает объекты WeatherBean в списке
weatherList. Список weatherList использует классы WeatherListModel и Wea-
tberCellRenderer для отображения объектов WeatherBean. Метод addltem (строки
47-65) добавляет указанный объект WeatherBean для его отображения, если
информация о соответствующем городе в данный момент не отображена. Если
информация о городе уже отображена, в строках 56-58 удаляется предыдущий объект
WeatherBean для города и добавляется обновленный объект WeatherBean.
1 // WeatherDisplay.Java
2 // WeatherDisplay расширяет класс Jpanel для отображения результатов
3 // клиентского запроса не получение информации о погоде.
4 package com.deitel.advjhtpl.jus,weather;
5
6 /V Набор базовых пакетов Java
1 import j ava.awt.*;
8 import java,awt.event,*;
9 import java.util,*;
10
11 // Пакеты расширений Java
12 import javax.swing.*;
13
14 // Пакета Deitel
15 import com.deitel.advjhtpl.rmi.weather.*;
16
11 public class WeatherDisplay extends JPanel {
18
19 // Объекты WeatherListModel и Нар дли хранения компонентов
WeatherBean
20 private WeetherListModel WeatherListModel;
21 pri.va.tA Hap ve&therltems;
Обмен сообщениями с помощью Java Message Service (JMS)
425
22
23 // Конструктор HeatherDisplay
24 public WeatherDisplay(J
25 {
26 setLay<rat( new BorderLayoutf) );
27
28 Image Icon header-image = new ImageIcon(
29 HeatherDisplay,class.getResource{
30 "images/header.jpg" ) );
31 add( new Jbabel( headerlmage ), BorderLayout.NORTH );
32
33 // использование списка JList для отображения
34 // свежей информации о погоде для заданных городов
35 WeatherListModel = new WeatherListModel();
36 Jbist weatherJList = new JList( WeatherListModel );
37 weatherJList.setCellRenderer( new WeatherCellRenderer() );
3B
39 add( new JScrollPane( weatherJList ), BorderLayout.CENTER );
40
41 // сохранение компонентов WeatherBean в хэш HasnMap
42 weatherltems = new HashHap()•
43
44 } // конец конструктора WeatherDisplay
45
4 6 // добавление компонента WeatherBean для его отображения
47 public void addltetn( WeatherBean weather )
48 {
49 String city = weather.getCityName();
50
51 // проверка, не отображена ли уже информация для города
52 if ( weatherltems.containsKey( city ) ) {
53 *
54 // если город присутствует в таблице Иар, и, следовательно,
55 // информация для него уже отображается, удалить объект
WeatherBean
56 WeatherBean previousWeather =
57 ( WeatherBean ) weatherltems,remove( city );
58 WeatherListModel.remove( prsviousWeather );
59 }
60
61 // добавление компонента WeatherBean в таблицу Иар и в модель
WeatherListModel
62 WeatherListModel.add( weather );
63 weatherltems.put{ city, weather );
64
65 } // конец метода addltero
66
67 // очистка отображаемой информации для всех городов
68 public void clearCities{)
69 {
70 weatherltems .ciear();
71 WeatherListModel.clear();
72 }
73 )
Рис. 8.18. Класс WeatberDisplay отображает объекты WeatherBean в списке JList
с использованием объекта WeatherCellRenderer
426
Глава 8
8.4.4. Приложение Weather: настройка и выполнение
Чтобы выполнить приложение, введите следующие команды в приглашение
командной строки:
1. Запустите сервер J2EE в окне команд:
j2ee —verbose
2. В новом окне команд создайте тематический раздел Weather:
j2eeadmin -addJmsDestination Heather topic
3. Проверьте, что этот тематический раздел был создан:
j2eeadmin -listJmsDestination
4. Создайте мастер соединений:
j2eeadinin -addJmsFactory WEATHERJFACTORY topic
5. Запустите класс WeatherPublisher:
Java -classpath %J2BE_HOME%\lib\j2ee.jar;.
-Djms.properties = %J2EE_HOME%\oonfig\jms_client.properties
com.deitel.advjhtpl,jms.weather.WeatherPublisher
6. Запустите класс WeatherSubscriber в новом окне команд:
Java -classpath, %J2EE_HOME%\lib\j2ee.jar;.
-Djms.properties = %J2EE_H0ME%\config\jms_client.properties
com.deitel.advjhtpl.jms.weather.WeatherSubscriber
После выполнения приложения мастер соединений может быть удален с
помощью команды:
j2eeadmin -removeJmsFactory WEATHER_FACTOBY
Чтобы удалить тематический раздел, воспользуйтесь командой:
j2eeadmin -removeJmsDestination Heather
Чтобы завершить работу сервера J2EE, воспользуйтесь командой:
j2ee -stop
8.5. Компоненты Enterprise JavaBeans,
управляемые сообщениями
EJВ-компоненты, управляемые сообщениями представляют собой новый тип
компонентов Enterprise JavaBeans, предусмотренных в пакете Enterprise Java-
Beans версии 2.0, который входит в состав Java 2 Enterprise Edition 1.3.
Компоненты, управляемые сообщениями, способны обрабатывать сообщения JMS,
помещенные в очередь или в тематический раздел. При получении сообщения контейнер
EJB использует любые доступные экземпляры конкретного компонента,
управляемого сообщениями, чтобы обработать сообщение. Здесь есть аналогия с тем, как
контейнер EJB использует любые экземпляры сеансового компонента EJB без
состояния для обработки клиентского запроса. Поскольку может быть использован
любой экземпляр EJB-компонента, управляемого сообщениями, компоненты,
управляемые сообщениями, не могут быть отнесены к конкретному клиенту и не
должны хранить информацию о состоянии клиента. Заметим, что любой заданный
экземпляр EJB-компонента может обрабатывать сообщения от нескольких
клиентов. В отличие от сеансовых компонентов и компонентов-сущностей (для которых
разработчики должны предоставить собственный и удаленный интерфейсы), для
компонентов, управляемых сообщениями, разработчики должны предоставить
только класс реализации компонента.
Обмен сообщениями с помощью Java Message Service (JMS)
427
8.5.1. Приложение Voter: обзор
В этом разделе представлена реализация приложения Voter кз раздела 8.3, в
которой для подсчета количества голосов, поступивших в очередь Votes,
используются компоненты, управляемые сообщениями. Класс Voter (рис. 8.4), который
помещает сообщения с отданными голосами в очередь, остается точно таким же — это
важное преимущество слабосвязанных приложений. Отправитель просто посылает
сообщения в очередь вне зависимости от реализации получателя. В данном случае
принимающая часть приложения (см. раздел 8.5.2) представляет собой компонент,
управляемый сообщениями. Компонент-сущность Candidate (рис. 8.20, рис. 8.21
и рис. 8.22) представляет определенного кандидата, за которого могут
проголосовать пользователи. Компонент-сущность хранит количества голосов в базе данных
Voting. Компонент VoteCoIiectorEJB, управляемый сообщениями (рис. 8.23),
использует EJB-компонент Candidate для обновления количества голосов при
поступлении в очередь Votes нового сообщения с отданным за определенного кандидата
голосом. Класс TallyDisplay (рис. 8.24) осуществляет доступ к EJB Candidate, чтобы
отобразить GUI с текущими значениями подсчитанных голосов, извлеченными из
базы данных. Структура приложения показана на рис. 8.19.
Voter
Voter
TextMessage
Тех tMessage
Votes
очередь
TextHessage
VoteColleсtorEJB
Рис. 8.19. Структура приложения Voter
8.5.2. Приложение Voter: принимающая сторона
EJB сущность Candidate представляет определенного кандидата, за которого
могут голосовать пользователи, и общее количество голосов, отданных за этого
кандидата. Собственный интерфейс CandidateHome (рис. 8.20) определяет методы findByPri-
maryKey (строки 15-16) и findA 11 Candidates (строки 19-20) для нахождения,
соответственно, указанного кандидата Candidate или коллекции Collection всех
кандидатов Candidate. Метод create (строки 23-24) создает новый EJB-компонент
Candidate с указанным именем CandidateName и с нулевым количеством голосов.
Удаленный интерфейс Candidate (рис. 8.21) определяет метод incrementVote-
Count для добавления нового голоса за кандидата. Метод getVoteCount (строка 18)
возвращает текущее количество голосов, набранное кандидатом. Метод getCandi-
dateName (строка 21) возвращает имя кандидата.
1 // CandidateHome.Java
2 // CandidateHome - это собственный интерфейс для компонента Candidate.
3 package cojn.deitel.advjhtpl.jms.mdb;
4
5 // Набор базовых библиотек Java
428
Глава 8
6 import java.rmi.*;
7 import java.util,*;
8
9 // Стандартные расширения Java
10 import javax.ejb.*;
11
12 public interface CandidateHome extends EJHHome {
13
14 // поиск кандидата с заданный именем
15 public Candidate findByPrimaryKey( String candidateName )
IE throws RemoteException, FinderException;
17
IS // поиск всех кандидатов
19 public Collection f indAHCandidates ()
20 throws RemoteException, FinderException;
21
22 // создание нового EJB-компонента Candidate
23 public Candidate create( String candidateName )
24 throws RemoteException, CreateException;
25)
Рис 8,20. Собственный интерфейс CandidateHome для EJB-компонента Candidate
1 // Candidate.Java
2 // Candidate - это удаленный интерфейс для EJB-компонента
3 // Candidate, который хранит количество набранных голосов.
4 package com.deitel.advjhtpl.jms.mdb;
5
6 // Набор базовых библиотек Java
7 import Java.rmi.RemoteException;
8
9 // Стандартные расширения Java
10 import javax.ejb.EJBObject;
11
12 public interface Candidate extends EJBQbject {
13
14 // добавление голоса за этого кандидата
15 public void incrementVoteCount() throws RemoteException;
16
17 // получение общего количества голосов для этого кандидата
18 public Integer getVoteCount() throws RemoteException;
19
20 // получение имени кандидата
21 public String getCandidateNameO throws RemoteException;
22 J
Рис, 8,21. Удаленный интерфейс Candidate для EJB-компонента Candidate
Класс CandidateEJB (рис. 8.22) реализует EJB-сущность Candidate. EJB-ком-
понент Candidate использует персистентность, управляемую коптейиером, для
хранения информации о кандидате в базе данных. В строках 17-18
осуществляется обновление управляемых контейнером полей voteCoimt и name для
сохранения, соответственно, общего количества голосов, набранных кандидатом, и его
имени. Метод IncrementVoteCount (строки 21-25) инкрементнрует число голосов,
набранных кандидатом. Метод getName (строки 34-37) возвращает имя кандида-
Обмен сообщениями с помощью Java Message Service (J MS)
429
та. Метод ejbCreate (строки 40-47) создает новый EJB- компонент Candidate,
устанавливает имя паше кандидата и инициализирует счетчик voteConnt, сбрасывая
его в нуль.
1 J/ CandidateEJB.Java
2 // CandidateEJB - эхо компонент-сущность EJB, который использует
3 // персистентность, управляемую контейнером, для хранения имени
, кандидата и навранного им числа голосов
4 package com.deitel.advjhtpl.jms.radb;
5
6 // Набор базовых пакетов Java
7 import java.xmi.RemoteException;
8
9 // Стандартные расширения Java
10 import javax.ejb.*;
11
12 public class CandidateEJB implements EntityQean (
13
14 private EntityContext entityContext;
15
16 // поля, управляемые контейнером
17 public Integer voteCount;
19 public String name;
19
20 // добавление голоса за этого кандидата
21 public void incrementVoteCount()
22 (
23 int newVoteCount = voteCount.intValue() + 1;
24 voteCount = new Integer( newVoteCount );
25 }
26
27 // получение общего количества голосов, отданных за этого кандидата
28 public Integer getVoteCountO
29 (
30 return voteCount;
31 }
32
33 // получение имени кандидата
34 public String getCandidateHame()
35 (
36 return name;
37 \
за
39 // создание нового кандидата (объект Candidate)
40 public String ejbCreate( String candidateName )
41 throws CreateException
42 {
43 name = CandidateName;
44 voteCount = new Integer( 0 );
45
46 return null;
47 J
48
49 // выполнение стандартных действий при создании нового кандидата
50 public void ejbPostCreate( String candidateName ) {)
51
430
Глава 8
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
■33
74
75
76
77
78
79
80
81
82
83
84 )
// задание контекст» EntityContext
public void setEntityContext( EntityContext context )
entityContext = context;
// сброс коятгексч-а EntityContext
public void unsetEntityContext()
entityContext = null;
/ активация экземпляра объекта Candidate
public void ejbActivate()
name = ( String ) entityContext.getPrimaryKey()
// пассивация экземпляра объекта Candidate
public void ejbPassivatef)
name = null;
// загрузка сведений о кандидате из базы данных
public void ejt>Load() i)
// сохранение сведений о кандидате в базе данных
public void ejbStore() {}
// удаление сведений о кандидате из базы данных
public void ejbRemove() {)
Рис. 8.22. Класс CandidateEJB хранит количество голосов, избранных кандидатом
Для обработки входящих сообщений, поступивших из очереди Votes,
контейнер использует класс VoteColIectorEJB (рис. 8.23) компонента, управляемого
сообщениями. Когда VoteColIectorEJB получает новый голос, контейнер вызывает
метод DnMessage (строки 21-48).
1 // VoteColIectorEJB.java
2 // VoteColIectorEJB - это EJB-компонент, управляемый сообщениями,
который ведет подсчет голосов.
3 package com.deitel.advjhtpl.jms.mdb;
4
5 // Набор базовых пакетов Java
6 import Java.util.*;
7 import Java. rmi .* ,-
S
9 II Пакеты расширений Java
10 iroport javax.ejo.*;
11 import javax.rmi.*;
12 import javax. jms.*;
13 iwport Javax.naming.*;
14
Обмен сообщениями с помощью Java Message Service (JMS)
431
15 public class VoteCollectorEJB
16 implements MessageDrivenBean, MessageListener {
17
18 private MessageDrivenContext messageDrivenContext;
19
20 // получение нового сообщения
21 public void onMessage( Message message )
12 (
23 TextMessage voteMessage;
24
25 // извлечение и обработка сообщения
26 try {
27
28 if { message instanceof TextMessage ) {
29 voteMessage = ( TextMessage ) message;
30 String vote = voteMessage.getText();
31 countVote( vote );
32
33 System,out.println( "Received vote: " + vote );
34 } // конец блока if
35
36 else {
37 System,out.println( "Expecting " +
38 "TextMessage object, received " +
39 message.getClass().getName[) );
40 }
41
42 } // конец блока try
43
44 // обработка исключения JMS от сообщения
45 catch { JMSException jmsException > {
46 jmsException.printStackTraceO;
47 }
48 }
49
50 // добавление голоса к соответствующей сумме голосов
51 private void countVote( String vote )
52 {
53 // CandidateHome осуществляет поиск/создание кандидатов
54 CandidateHome candidateHome = null;
55
56 // нахождение кандидата и увеличение для него числа голосов
57 try {
53
59 // поиск EJB-компонента Candidate
60 Context initialContext = new initialContextO;
61
62 Object object = initialContext.lookup(
63 "Java:comp/env/ejb/Candidate" );
64
£5 candidateHome =
66 ( CandidateHome ) PortableRemoteGbject.narrow(
67 object, CandidateHome.class );
68
69 // поиск кандидата, за которого проголосовал пользователь
70 Candidate candidate =
432
Глава 8
71 eandidateHome.findByPrimaryKey( vote );
72
73 // увеличение числа голосов для кандидата
74 candidate.incrementVoteCount();
75
76 J // конец блока try
77
78 // если кандидат не найден, создать нового кандидата
79 catch { FinderException finderException ) I
80
81 // создание нового кандидата и инкремент числа r-олосов для него
82 try {
83 Candidate newCandidate = caxididateHome.create{ vote );
84 newCandidate.incrementVoteCount <);
85 }
86
87 // обработка исключения при создании нового кандидата
88 catch ( Exception exception ) {
89 throw new EJBException( exception );
90 }
91
92 V // конец блока catch FinderException
93
94 // обработка исключения при поиске EJB-компонента OrderProducts
95 catch ( NamingException namingException ) {
96 throw new EJBExceptionС namingException );
97 }
98
99 // обработка исключения при вызове методов OrderProducts
100 catch ( RemoteException remoteException ) {
101 throw new EJBException( remoteException );
102 }
103
104 } // конец метода countVote
105
106 // задание контекста для управления через сообщения
107 public void setMessageDrivenContext(
108 MessageDrivenContext context )
109 {
110 meSsageDrivenContext = context;
111 }
112
113 // создание экземпляра компонента
114 public void ejbCreate 0 {}
115
116 // удаление экземпляра компонента
117 public void ejbRemoveО О
118 )
Рис. 8.23. Класс VoteCollectorEJB подсчитывает количество голосов, поступивших из
очереди Votes
Класс VoteCollectorEJB реализует интерфейсы MessageDrivenBean и Message-
Listener. Контейнер вызывает метод ejbCreate перед реализацией нового
экземпляра компонента и метод ejbRemove перед уничтожением экземпляра. Интерфейс
MessageDrivenBean определяет метод setMessageDrivenContext. Заметим, что ин-
Обмен сообщениями с помощью Java Message Service (JMS)
433
терфейс MessageDrivenBean также определяет, что контекст управления
посредством сообщений должен храниться в качестве переменной экземпляра (Message-
Dri venCon te xt).
После получения сообщения контейнер вызывает метод onMessage,
определяемый интерфейсом MessageListener. В строке 28 проверяется, имеет ли полученное
сообщение тип TextMessage. Если да, в строке 31 осуществляется вызов метода
countVote для учета получеиного голоса. Метод countVote (строки 51-104) ищет
EJB-компонент Candidate (строки 60-67) и вызывает метод findByPrimaryKey
интерфейса CandidateHome для нахождения кандидата (объект Candidate), за
которого пользователь отдал iwioc. Если такой объекг Candidate найден, н строке 74
вызывается метод incrementVoteCount интерфейса Candidate для добавления
голоса, отданного за кандидата. Если объект Candidate не найден, в строках 79-92
перехватывается исключение FinderException.
Класс TallyDisplay (рис. 8.24) отображает -«моментальный снимок» кандидатов
и набранных ими голосов. Класс TallyDisplay использует EJB-компоиент
Candidate для извлечения данных голосования. В строках 36~46 осуществляется поиск
EJB-компонента Candidate, а также извлекается коллекция Collection всех
кандидатов Candidate.
Для каждого объекта Candidate в строках 51-58 создается и добавляется новая
панель TallyPanel (рис. 8.26). При этом конструктору TallyPanel передается имя
кандидата и число набранных им голосов.
1 // TallyDisplay.Java
2 // TallyDisplay отображает количества голосов,
хранящиеся в базе данных.
3 package com.deitel.advjhtpl.jros.mdb,-
4
5 // Набор базовых пакетов Java
6 import Java.awt.*;
7 import j ava.awt.event.*;
8 import java.xmi.*;
9 import j ava.uti1. *;
10 import Java.util.List;
11
12 // Пакет*: расширений Java
13 import javax.swing.*;
14 import javax. ejb.*;
15 import j &vax. rmi . *;
16 import javax.naming.*;
17
18 public class TallyDisplay extends JFrame (
19
20 // Конструктор TallyDisplay
21 public TallyDisplay()
22 {
23 super{ "Vote Tallies" );
24
25 Container container = getContentFane();
26
27 // сумма подсчитанных голосов отображается в панели displayPanel
28 JPanel displaypanel = new JPanelO;
29 • displayPanel.setLayout( new GridLayout( 0, 1 ) );
30 container.add( new J5crollPane( displayPanel ) );
31
32 // нахождение кандидатов и отображение числа набранных
ими голосов
434
Глава 8
33 try {
34
35 // поиск EJB-компонента Candidate
36 Context initialContext = new InitialContext{) ;
37
38 Object object = initialContext.lookup{
39 "Candidate" >;
40 CandidateHome candidateHome =
41 ( CandidateHome ) PortableRemoteObject.narrow(
42 object, CandidateHome,class );
43
44 // нахождение всех кандидатов
45 Collection candidates =
46 CandidateHome.findAllCandidates();
47
48 // добавление панели TallyPanel с именами кандидатов
49 //и числом набранных каждым кандидатом голосов
50 Iterator iterator = candidates.iterator();
51 while ( iterator.hasNext() ) {
52 Candidate candidate = ( Candidate ) iterator.next();
53
54 // создание панели TallyPanel для кандидата
55 TallyPanel tallyPanel =
56 new TallyPanel( candidate,getCandidateNarae(),
57 candidate.getVOteCountO.intValue() );
58 displayPanel,add( tallyPanel );
59 }
60
61 } // конец блока try
62
63 // обработка исключений при поиске кандидатов
64 catch ( FinderException finderException ) {
65 finderException-printStackTrace();
66 >
67 // обработка исключений при поиске EJB-комвонента Candidate
68 catch ( HamingExeeption namingException ) {
69 namingException.printStackTrace();
70 }
71
72 // обработка исключений при взаимодействии с объемом Candidate
73 catch ( RemoteException remoteException ) {
74 remoteException.printstackTrасе();
75 }
76
77 } // конец койструиторь TallyDisplay
78
79 // ваяуск приложения TallyDisplay
80 public static void inain( String args[) )
81 {
82 TallyDisplay tallyDisplay = new TallyDisplay();
83 tallyDisplay.setDe£aultCloseCperation( EXIT_ON_CL0SE ) ;
84 tallyDisplay.pack();
85 tallyDisplay.setVisible( true );
86 )
87 }
Рис. 8.24. Класс TallyDisplay отображает число гслосов, набранных кандидатами,
извлекая их из базы данных
Обмен сообщениями с помощью Java Message Service (JMS)
435
На рис. 8.25 представлена панель TallyDisplay. Обратите внимание, что в
панели отображаются только уже учтенные голоса; содержимое панели TallyDisplay не
обновляется по мере поступления новых голосов.
Класс TaUyPanel (рис. 8.26) отображает имя и число набранных голосов для
отдельного кандидата в панели JPanel.
<jtllA!>fl'ff
■ ср"
ГО' г—-
■ 'Us(r;i2
,iy>Hin[l6
1С»>-Ш|
'•"-"~r— &i:- ■■
Рис. 8.25. Класс TallyDisplay отображает число голосов, набранных кандидатами
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// TallyPanel.java
// Tallpanel - это компонент GUI, который отображает имя
// кандидата и количество набранных им голосов,
package com.deitel .advjhtpl. jms.mdb;
// Набор базовых пакетов Java
Import j ava.awt.*;
// Пакета расширений Java
import ^avax.swing.*;
public class TallyPanel extends JPanel {
private JLabel nameLabel;
private JTextField tallyField;
private String name;
private int tally;
// Конструктор TallyPanel
public TallyPanel( String voteName, int voteTally )
<
name = voteName;
tally = voteTally;
nameLabel = new JLabel( name );
tallyField =
new JTextField( Integer,toString( tally ), 10 ];
tallyField.setEditablef false );
tallyField.setBaekground( Color.white );
add( nameLabel );
add( tallyField );
} // конец конструктора TallyPanel
Рис. 8.26. Класс TallyPanel отображает имя и число набранных голосов длр кандидата
436
Глава 8
8.5.3. Приложение Voter: настройка и выполнение
В этом разделе описываются действия, необходимые для развертывания и
выполнения версии приложения Voter, в которой используются компоненты,
управляемые сообщениями. Поскольку приложение использует базу данных Cloudscape,
в файл свойств resource.properties в каталоге config, расположенном в основном
каталоге J2BE (например, C:\j2sdkeel.3\config\resource.properties), должны
быть добавлены следующие строки:
jdbeDataSource.5.name == jdbc/Voting
jdbcDataSouirce.5.url = jdbc:cloudscape:rmi:VotingDB; cteate»tirue
Заметим, что в этом файле свойств может быть несколько записей jdbcData Source.
В приведенных вып1е примерах вам следует заменить 5 на номер последней записи
в таблице jdbcData Source плюс 1. Например, если последней записью в jdbcData-
Source является запись номер 3, вам следует указать jdbcDataSource.4.name
и jdbcData Source.4. url. Добавив эти строки, запустите Cloudscape. Затем
запустите сервер J2EE, воспользовавшись командой
j2ee —verbose
В новом окне команд создайте очередь и мастер соединений (если они были
созданы в разделе 8.3.4, то уже должны существовать):
j2eeadmin -adcUmsDeatination Votes queue
jZee&dmin -listJmsDestination
j2eeadmin -addJmsFactory VOTE_FACTORY queue
Для развертывания приложения VoteCoIlector запустите инструментальное
средство deploytool.
Создайте новое приложение, выбрав File->New Application из строки меню.
В диалоговом окне щелкните на Browse и перейдите к каталогу, расположенному
на один уровень выше каталога com. Введите VoteCollectorApp.ear в качестве
имени файла и щелкните на New Application. Затем щелкните на ОК.
Теперь добавьте EJB-компонент Candidate, выбрав New Enterprise Bean из
меню File. В диалоговом окне EJB JAR выберите Create new EJB File in
Application и выделите VoteCollectorApp в появившемся раскрывающемся меню.
Введите VoteCollectorJAR в качестве имени EJB Display Name (см. рис. 8.27).
Щелкните на кнопке Edit, чтобы добавить файлы классов. В диалоговом окне Edit
укажите каталог, содержащий структуру пакета com.deitel.advjhtpl, в качестве
начального каталога Starting Directory. Перейдите к каталогу mdb (com/deitel/
advjhtpl/jms/mdb) и добавьте (щелкнув на кнопке Add) классы Candidate.class,
CandidateEJB.class и CandidateHome.class (рис. 8.28). Щелкните на OK, чтобы
выйти из диалогового окна Edit. Щелкните на Next, чтобы перейти к диалоговому
окну General.
В диалоговом окне General выберите Entity в качестве типа компонента Bean
Туре. Выберите com.deitel.advjbtpl.jms.mdb.CaudidateEJB из раскрывающегося
меню для Enterprise Bean Class. Введите Candidate в качестве имени Enterprise
Bean Name. В разделе Remote Interfaces выберите com.deitel.advjhtpl.jms.mdb.
CandidateHome и com.deitel.advjhtpl.jms.mdb.Candidate в качестве,
соответственно, собственного интерфейса Home Interface и удаленного интерфейса Remote
Interfaces (см. рис. 8.29). Щелкните на кнопке Next, чтобы перейти к
диалоговому окну Entity Settings.
В диалоговом окне Entity Settings выберите опцию Container managed
persistence (1.0). Установите оба флажка — и voteCount, и паше. Введите
java.lang.String в качестве класса первичного ключа Primary Key Class и
выберите паше из раскрывающегося меню Primary Key Field (см рис. 8.30), Щелкните на
Finish.
Обмен сообщениями с помощью Java Message Service (JMS)
437
ШВШ1 .Ш
^ФЙОЙ^ Se-'АЫ 104 tea StttOst« JAR Яе, unnlltiiiilit imj этд^внфм^ё!) tgft»
-§ЛЙ^1виЛ#^*ч-^тЦ "— -*^' ** 4j'*.'i' " ■■"[■ »A>gj»*A^^'.......t..;, ~»i»»—
;»- ■•¥*<
T#
**•:
'jfej
Tz^m^-\
\ ". SjtNwaWW^ J-[T;i«mj^(^w«m~-|;Fv
**r.
5U^j;'isO^ s:* ■'■■ r" |»ЗШ|р!1рР' СЗах
T^^^^§^v\i
Рис 8.27. Настройки JAR-архива EJB для приложения VoteCollectorApp
ф t3 torn
ф СЭ derteJ
9 C3 advjhipt
Ф (Slims
ф ЕЗгппЬ
Q candidatexia^s
Q Candidale.iswa
Q CandidateEJ&.c43ss
□ C*tdidateEJu.iaf«
Q CaniJiriiateHgrne.ciags
I
•йшиман*'
rj**v
EE3 META-H4F
^ ЁЭеот
t Л flelai
f G3 ad^hcpi
G Qrndh
■a^V^д^||"'" i} j—.т4.1.'Ил.^^]"
Рис. 8.28. Добавление файлов классов для EJB-компонента Candidate
438
Глава 8
Рис. 8.29. Общие настройки для EJB Candidate
Рис. 8.30. Настройка данных для EJB Candidate
В основном окне инструментального средства deploytool выберите Candidate
и щелкните на вкладке Entity (рис. 8.31). Щелкните на кнопке Deployment
Settings, В появившемся диалоговом окне щелкните на Database Settings. Введи
те jdbc/voting в качестве имени Database JNDI Name базы данных (рис. 8.32),
Щелкните на ОК. В диалоговом окне Deployment Settings щелкните на кнопке
General Default SQL. В появившемся диалоговом окне с сообщением SQL
Generation complete («Генерирование кода SQL завершено») щелкните на ОК
(рис. 8.33). В диалоговом окне Deployment Settings щелкните на ОК. Появится
окно предупреждения, информирующее об отсутствии оператора WHERE для
метода findAHCandidates; игнорируйте предупреждение, щелкнув на ОК (рис. 8.34).
Обмен сообщениями с помощью Java Message Service (JMS)
439
Рис. 8.31, Вкладка Entity для EJB Candidate
Рис. 8.32. Параметры настройки базы данных для EJB-компонента Candidate
Рис. 8.33. Формирование кода SQL для EJB Candidate
440
Глава 8
Рис. 8.34. Предупреждение SQL дли EJB Candidate
Теперь создайте EJB-компонент VoteCollector, выбрав File—>New Enterprise
Bean из строки меню. Щелкните на кнопке Add to Existing EJB File и выберите
VoteCollectorJAR(VoteColIectorApp) из появившегося раскрывающегося меню
(рис. 8.35). Щелкните на Edit и добавьте (Add) Класс VoteCollectorEJB.class из
структуры com/deitel/advjhtpl/jms/mdb (рис. 8.36). Щелкните на ОК, чтобы
выйти из диалогового окна Edit. Щелкните на Next, чтобы перейти к диалоговому
окну General.
Рис, 8.35. Настройки JAR-архива EJB для EJB- компонента VoteColtector
В диалоговом окне General (рис. 8.37) выберите Message-Driven Bean в
качестве типа Bean Туре компонента. Выберите
com.deitel.advjhtpl.jms.mdb.VoteCollectorEJB в качестве класса Enterprise Bean class. Введите VoteCollector в
качестве имени Enterprise Bean Name компонента. Щелкните на кнопке Next,
чтобы перейти к диалоговому окну Transaction Management.
В диалоговом окне Transaction Management (рис. 8.38) выберите Container-
Managed. Проверьте, чтобы для метода on Mess age для атрибута транзакции
Transaction Attribute было задано значение Required. Щелкните на Next, чтобы
перейти к диалоговому окну Message-Driven Bean Settings.
В диалоговом окне Message-Driven Bean выберите Queue (очередь) в качестве
типа места назначения Destination Type (рис. 8.39). Выберите Votes
и VOTEJFACTORY из раскрывающихся меню Destination и Connection Factory,
соответственно. Щелкните на Next, чтобы перейти к диалоговому окну
Environment Entries. В этом диалоговом окне не вводите никакой информации; снова
щелкните на Next, чтобы перейти к диалоговому окну Enterprise Bean Reference.
Обмен сообщениями с помощью Java Message Service (JMS)
441
О CanadalaEJS class
Q Candid altEJB jaw
Q (.snneaieHome class
□ camMaleHomajava
Q Tal^Oisplaytfasa
□ TalryCisplaijava
□ TalifFanei class
D TalfjFanel.Java
[} vote CotltctDfE J B*cla з s
QvoleColieclnrEJBlsvs
^««ccrt.XVotaCirftetiorJAll S;
*-ПЗ HETft-INF
^ C3 carri
? C3 dHIt l
* Giros
QcandieaKtass
О Can«0are6ja.clas*
Q candnaaleH&ma class
ci>^i fi?r^l
pi
Рис. 8.36. Добавление файла класса для EJB-компонента VoteCollector
,j$r?-. !^Д^
Рис. 8.37. Общие настройки для EJB-компонента VoteCollector
442
Глава 8
Рис. 8.38. Настройка параметров управления транзакциями для UB-комлонента
VoteCollector
Рис. 8.39. Задание параметров управления через сообщения для EJB VoteCollector
В диалоговом окне Enterprise Bean Reference (рис. 8.40) щелкните на кнопке
Add. Введите ejb/Candidate в качестве имени Coded Name. Выберите Entity для
Туре и Remote для Interfaces. Введите com.deiteLadvjhtpl.jms.mdb.CandidateHome
и corn.deitel.advjhtpl.jms.mdb.Candidate в качестве, соответственно, собственного
интерфейса Home Interface и мести ого/удаленного интерфейса Local/Remote
Interface. Выберите опцию JNDI Name и введите Candidate в соответствующее
текстовое поле. Щелкните на Finish.
В основном окне средства deploytool выберите VoteCollector Арр из
иерархической структуры и щелкните на вкладке JNDI Names. Введите Candidate в
качестве имени JNDI Name для Candidate. Убедитесь, что Votes задано в качестве имени
JNDI Name для VoteCollector (рис. 8.41).
Обмен сообщениями с помощью Java Message Service (JMS)
443
Рис. 8.40. Ссылки на компоненты Enterprise Beans для VoteCollector
Наконец, осуществите развертывание приложения, выбрав Deploy из меню
Tools. Выберите VoteCoIlectorApp в качестве объекта для развертывания Object
То Deploy. В диалоговом окне щелкните на Return Client JAR (рис. 8.42).
Щелкните на Next и проверьте имена JNDI, Щелкните на Next, а затем на Finish.
После развертывания приложения VoteCollector выполните клиентское
приложение Voter:
Java -classpath %J2EE_HOME%\lib\j2ee.jar;.
-Djms.prt>pertiea=%J2EE_H0ME%\config\jms_elient.properties
com.deibsl.advjhtpl.jms.mdb.Voter
Чтобы увидеть текущие результаты голосования, выполните класс Tally Display
(учтите, что клиентский JAR-файл должен быть включен в описание пути к классам):
java -classpath
%J2EE_HOME%\lib\j2ee.jar;VoterCollectorAppClient.jar;.
com.deltel.advjhtpl.jms.mdb.TallyDisplay
Рис. 8.41. Задание имен JNDI для приложения VoteCoIlectorApp
444
Глава 8
Рис. 8.42. Развертывание приложения VoteCollectorApp
Резюме
• Система обмена сообщениями осуществляют слабое (косвенное) связывание компонентов.
■ Системы обмена сообщениями дают возможность компонентам передавать сообщения для
чтения их другими компонентами.
• Имеется две основные модели систем обмена сообщениями: «от точки к точке» и
«издатель/подписчик». Модель обмена сообщениями «от точки к точке» дает возможность
компонентам отправлять сообщения в очередь сообщений. Потребитель сообщения — это
компонент-адресат, который обрабатывает полученные сообщения,
• В модели «от точки к точке» потребителем сообщения является один и только один
клиент;; сервер сохраняет сообщения, которые не были востребованы.
• Модель обмена сообщениями «издатель/подписчик» дает возможность компонентам
публиковать сообщения в тематическом разделе. Компоненты, заинтересованные в
опубликованных в определенном тематическом разделе сообщениях, могут подписаться на этот
тематический раздел.
• Когда издатель публянует сообщение в заданном тематическом разделе, текущие
подписчики получают это сообщение.
• В модели «издатель/подписчик» потребителями опубликованного сообщения могут быть
нуль или более подписчиков.
■ Сообщение состоит из заголовка, свойств (необязательно) и тела (также необязательно).
Заголовок сообщения содержит такую информацию, как адрес назначения сообщения
и время отправки сообщения.
• Свойства сообщения позволяют получателям сообщений выбирать, какие типы
сообщений они хотели бы получать; эти свойства могут устанавливаться отправителем
сообщения. Получатели сообщении используют селекторы сообщений для их фильтрации:
фильтрация выполняется на сервере.
■ Компоненты, управляемые сообщениями, представляют собой разновидность
корпоративных компонентов (Enterprise Beans), которые хорошо согласуются с промежуточным
программным обеспечением, ориентированным на обмен сообщениями (MOM).
• Контейнер EJB может использовать любой экземпляр компонента, управляемого
сообщениями, чтобы обрабатывать входящие сообщения для заданной очереди или
тематического раздела. Используя компоненты, управляемые сообщениями, компонент может
принимать сообщения асинхронно,
• Технология Java Message Service (JMS) стандартизирует корпоративный обмен
сообщениями, предоставляй интерфейс прикладного программирования для моделей «от точки
к точке» и «издатель/подписчик».
Обмен сообщениями с помощью Java Message Service (J MS)
445
• JMS поддерживает пять типов сообщений: BytesMcssage, MapMessage, Object Message,
StreamMessage и Text Message.
• Администратор сервера создает мастера соединений, очереди и тематические разделы.
« Объект QueueConnectionFactory дает возможность клиентам создавать объект
соединения с очередью QueueConnection,
• Объект QneueCoimecUon создает объект сеанса QueueSession. Объект QneneSession
создает либо объект QneneSender, либо объект QueneReceiver.
• Когда очередь или подписчик на тематический раздел получает сообщение, слушатель
сообщения обрабатывает его.
• Интерфейс MessageListener определяет метод onMessage, который вызывается, когда
поступает новое сообщение.
• Клиент может получать подписку двух типов: кратковременную и долгосрочную. При
кратковременной подписке сообщения принимаются только при активном состоянии
подписки.
• При долгосрочной подписке сообщения могут быть получепы и в случае цеазстивпого
состояния: сервер хранит сообщения, отправленные в тематический раздел, пока подписка
находится в неактивном состоянии, и отправляет их клиенту, когда подписка снова
становится активной. Заметим, что если для подписки установлен селектор, чтобы
осуществлять фильтрацию сообщения, сервер будет хранить только те сообщения, которые
удовлетворяют условию, задаваемому селектором,
• Объект TopicConnectionFactory, который был создан сервером, создает объект Topic-
Connection. Объект TopicConnection создает объект TopicSession. Объект Topic Session
создает объект TopicPublisber или TopicSubscriber.
• Подписчик на тематический раздел (или получатель в очереди) может фильтровать
сообщения с помощью селектора сообщений. Если клиент задал селектор сообщений, сервер
будет отправлять клиенту только те сообщения, которые пропускаются этим фильтром.
В основу синтаксиса селектора сообщений положен синтаксис SQL92.
• EJB-компоненты, управляемые сообщениями, представляют собой новый тин
компонентов Enterprise JavaBeans, предусмотренный в пакете Enterprise JavaBeans, версии 2.0,
который входит в состав Java 2 Enterprise Edition 1,3.
• Компоненты, управляемые сообщениями, способны обрабатывать сообщения JMS,
помещенные в очередь или в тематический раздел.
• После получения сообщения контейнер EJB использует любой имеющийся экземпляр
конкретного компонента, управляемого сообщениями, для обработки сообщения.
• Поскольку может быть использован любой экземпляр EJB-компонента, управляемого
сообщениями, такие компоненты не связаны с конкретным клиентом и не должны хранить
информацию о состояаии клиента.
• Любой заданный экземпляр EJB-компонента может обрабатывать сообщения от
нескольких клиентов.
• В отличие от сеансовых компонентов и компонентов-сущностей (для которых требуются
интерфейсы), для компонентов, управляемых сообщениями, разработчики должны
предоставить лишь класс реализации компонента.
• Интерфейс MessageDrivenBean определяет метод setMessageDrivenContext и указывает,
что контекст управления через сообщения следует хранить как переменную экземпляра
MessageDrivenContext.
Терминология
BytesMessage, интерфейс
durable subscription — долгосрочная иод-
писка
Java Message Service (JMS)
MapMessage, интерфейс
message — сообщение
message body — тело сообщения
message consumer — потребитель сообщения
message-driven bean — компонент,
управляемый сообщениями
message header — заголовок сообщения
message oriented middleware (MOM) —
промежуточное программное обеспечение,
ориентированное на обмен сообщениями
message property — свойство сообщения
message selector — селектор сообщений
messaging system — система обмена
сообщениями
nondurable subscription — кратковременная
подписка
ObjectMessage, интерфейс
point-to-point messaging model — модель
обмена сообщениями «от точки к точке»
446
Глава 8
publish — публиковать sender — отправитель
publish/subscribe messaging mode\ — модель StrearaMessage, интерфейс
обмена сообщениями «издатель/иодпис- subscribe — подписываться
чик» subscriber — подписчик
publisher — издатель subscription — подписка
queue — очередь TextMcSsage, интерфейс
QueueConnection, интерфейс topic — тематический раздел
QueueConnection Factory, интерфейс TopicConnection, интерфейс
QueneReceiver, интерфейс TopicConnectionFactory, интерфейс
QueueSender, интерфейс TopicPuMisher, интерфейс
Queue Session, интерфейс TopicSession, интерфейс
receiver — получатель Topic Subscriber, интерфейс
Упражнения для самоконтроля
8.1. Ответьте, являются ли следующие иысказывания истинными или ложными. Если
высказывание ложно, объясните, почему.
a) Сообщения в модели обмена, сообщениями «от точки к точке» предназначены для
нуля или более получателей.
b) Сообщения в модели обмена сообщениями «издатель/подписчик» предназначены
для нуля или одного получателя.
c) Если указан селектор сообщений, фильтрация осуществляется на стороне сервера.
d) При отсутствии получателя сервер хранит сообщения, опубликованные в
тематическом разделе, пока получатель не устааовит соединение.
e) При отсутствии получателя сервер хранит сообщения, отправленные в очередь,
пока получатель не установит соединение.
f) Компоненты, управляемые сообщениями, хранят состояние для конкретного
клиента.
8.2. Заполните пропуски в следующих предложениях:
a) Существует две модели обмена сообщениями: и .
b) В модели обмена сообщениями клиент отправляет сообщение
в . Подразумевается, что получателем этого сообщения является один
и только один клиент.
c) В модели обмена сообщениями клиент отправляет сообщение
в . Подразумевается, что это сообщение предназначено для нуля или более
клиентов.
d) Сервер будет хранить сообщения для подписки, пока подписка
является неактивной.
e) Компонент представляет собой разновидность корпоративного
компонента (Enterprise Bean), хорошо согласующегося с промежуточным программным
обеспечением, ориентированным на обмен сообщениями (MOM).
Ответь» на упражнения для самоконтроля
8.1. а) Ложио. Сообщения в модели обмена сообщениями «от точки к точке»
предназначены только для одного получателя. Ь) Ложно. Сообщения в модели обмена сообщениями
•издатель/подписчик» предназначены для нуля или более получателей, с) Истинно.
А) Ложно, Если текущей подписки не существует, сервер не будет хранить входящие
сообщения; следует заметить, однако, что в случае, если долговременная подписка
является неактивной, сервер будет сохранять сообщения для этой подписки, е) Истинно.
f) Ложно. Экземпляр компонента, управляемого сообщениями, может обрабатывать
сообщения от нескольких клиентов; компоненты, управляемые сообщениями, не
могут хранить состояние для конкретного клиента.
8.2. а) *от точки к точке», «издатель/подписчик». Ь) «от точки к точке», очередь, с)
«издатель/подписчик», тематический раздел, d) долгосрочной, е) управляемый
сообщениями.
Обмен сообщениями с помощью Java Message Service (JMS)
447
Упражнения
8.3. Каково предназначение системы обмена сообщениями?
8.4. Сравните и противопоставьте модели обмена сообщениями ют точки к точке* и
«издатель/подписчик*. В каких случаях следует предпочесть ту или иную модель?
8.5. Воспользовавшись моделью обмена сообщениями «от точки к точке*, создайте
приложение, которое дает возможность продавцу получать предложения цены
выставляемого на продажу товара. В сообщении с предложением должен содержаться адрес e-maj]
покупателя, участвующего в торгах, и предлагаемая им цена. (Подсказка. Продавец
должен выступать в качестве принимающего сообщений с предложениями цены, а
участвующий в торгах покупатель должеп выступать в качестве отправителей сообщений
с предложениями.)
8.6. Модифицируйте ваше решение Упражнения 8.5, чтобы продавец мог осуществлять
фильтрацию предложений с ценой, которая является меньше определенной.
(Подсказка. Установите предлагаемую цену в качестве свойства типа double сообщений с
предложениями и используйте это свойстзо в селекторе сообщений.)
8.7. Создайте приложение, использующее модель «издатель/подписчик», которое
принимает заказы от клиентов. Заказ должен быть опубликован в виде сообщения в
тематическом разделе Domestic-orders, если клиент имеет местный адрес доставки, либо в
тематическом разделе International-orders, если клиент имеет международный адрес
доставки.
8.8. Модифицируйте ваше решение Упражнения 8.7, чтобы дать возможность
подписчикам тематических разделов Domestic-orders и International-orders осуществлять
фильтрацию заказываемых товаров по категориям. Например, если компонент
ответственен за все * книжные* заказы, ему будет позволено отфильтровывать все заказы,
не относящиеся к категории «book*. (Подсказка. Используйте строковое свойство для
задания категории заказа и селектор сообщений для выполнения фильтрации по этому
свойству.)
Q
Практический пример
корпоративного приложения.
Обзор архитектуры
Цели
• Уяснить архитектуру учебного
приложения книжного
Internet-магазина Deitel
Bookstore, реализованного с
помощью средств Enterprise Java.
• Познакомится с основными
решениями, принятыми при
разработке приложения Deitel
Bookstore.
• Познакомиться
с применением архитектуры
модель—вид—контроллер
(MVC) в контексте
приложения Enterprise Java.
• Понять, каким образом
технологии XML и XSLT дает
возможность генерировать
содержимое для клиентов
различных типов.
• Понять роль, которую играют
сервлеты и компоненты EJB
в корпоративных приложениях.
• Уяснить принципы
построения многоуровневых
приложений
в инфраструктуре J2ЕЕ.
Вселенная шире, чем наши
представления о ней.
Генри Дэвид Торо
С горы легче спускаться, чем подниматься
на нее, зато, сколько можно увидеть
с вершины!
Арнольд Беннет
Не утруждайтесь созерцанием вида.
Он уже представлен в моей композиции.
Густав Малер
Целое — это то. что имеет начало,
середину и конец.
Аристотель
Основой хорошего закона является
не логика, а его практическая польза.
Оливер Уэнделл Холмс, младший
450
Глава 9
9.1. Введение
Технологии, которые входят в состав Java 2 Enterprise Edition (J2EE), дают
разработчикам возможность создавать устойчивые, масштабируемые, корпоративные
приложения. В этом практическом примере мы создадим приложение для
электронного бизнеса, реализующее книжный Internet-магазин. В приложении будет
использован ряд технологий J2EE, таких как сервлетьг, компоненты Enterprise
JavaBeans, XML, XSLT, XHTML, WML и cHTML.
В данном практическом примере приложения Java 2 Enterprise Edition
применяется архитектура модель—вид—контроллер (Model—View—Controller — MVC),
чтобы отделить данные и бизнес-логику (модель) от логики представления данных
(вид) и логики управления (контроллер). Модель приложения представлена
реляционной базой данных и компонентами-сущностями EJB. Сервлеты Java
реализуют логику управления для обработки вводимой пользователем информации, а
таблицы стилей XSL (XSLT-трансформации) реализуют логику внешнего
представления данных приложения.
Логика внешнего представления данных посредством XSLT позволяет
приложению формировать содержимое для клиентов нескольких различных типов. При
XSLT-трансформации данные приложения (размеченные с помощью XML)
обрабатываются таким образом, чтобы динамически генерировать XHTML, WML и
другие виды представления содержимого. Приложение может быть расширено, чтобы
обеспечить поддержку дополнительных типов клиентов и осуществлять настройку
выводимых данных для клиентов нового типа путем реализации дополнительных
XSLT-трансформаций. Например, можно разработать мидлет J2ME для
карманных устройств и реализовать ряд таблиц стилей XSL, которые формируют
выходные данные в формате, подходящем для этого мидлета.
В этой главе будет рассмотрена архитектуры учебного приложения Deite]
Bookstore. В последующих главах мы обсудим логику управления, реализованную
на еервлетах (глава 10), а также бизнес-логику и абстракцию данных,
реализованные с помощью EJB (главы 11 и 12), В главе 12 будут также предоставлены
инструкции по развертыванию учебного приложения Deitel Bookstore на эталонной
реализации сервера приложений J2EE Sun Microsystems. В главе 13 мы
познакомимся с тремя наиболее популярными коммерческими серверами приложений,
совместимыми с J2EE: ВЕА Web Logic, IBM WebSphere и iPlanet Application Server.
Мы обсудим возможности каждого из серверов приложений, а затем выполним
развертывание учебного приложения Deitel Bookstore на серверах ВЕА WebLogic
и IBM WebSphere.
Практический пример корпоративного приложения. Обзор архитектуры 451
9.2. Приложение книжного Internet-магазина
Deitel Bookstore
Приложение, которое будет разработано в этом практическом примере,
реализует ряд функциональных возможностей, характерных для коммерческих
Internet-магазинов. Приложение предоставляет каталог товаров, с помощью
которого покупатели могут найти нужную им книгу или просмотреть список
имеющихся в магазине книг. Приложение также предоставляет «магазинную
тележку», в которую покупатели могут добавлять приобретаемые товары. Покупатели
могут просматривать содержимое магазинной тележки, удалять товары, изменять
количество любого товара в тележке или осуществлять покупку товаров.
В приложении предусмотрены средства регистрации потребителя,
позволяющие покупателям вводить информацию для оплаты и организации доставки
товара. Покупателям также предоставляется возможность просматривать подробную
информацию по предыдущим заказам и восстанавливать забытые пароли.
Покупатели могут осуществлять доступ в Internet-магазин через стандартные
Web-браузеры, Web-браузеры Wireless Markup Language (WML) и браузеры cHTML (i-mode).
9.3. Общая архитектура системы
Приложение Deitel Bookstore является многоуровневым приложением. В
многоуровневых приложениях (их еще называют п-уровневыми приложениями)
функциональные возможности разнесены на разные уровни. Каждый уровень
может физически размещаться на отдельном компьютере. В приложении Deitel
Bookstore используется трехуровневая архитектура. Базовая структура
трехуровневого приложения представлена на рис. 9.1.
Клиентский
уровень
Средний
уровень
Информационный
уровень
Рис. 9.1. Модель трехуровневого приложений Deitel Bookstore
Информационный уровень (его также называют уровнем данных, или нижним
уровнем) содержит данные, используемые приложением. В корпоративных
приложениях для хранения данных информационного уровня обычно используются
реляционные базы данных. В приложении Deitel Bookstore база данных содержит
информацию о товарах (книгах), например, описание, цена и имеющееся на
складе количество, а также информацию о потребителях, такую как имя пользователя,
адрес для доставки и номер кредитной карты.
Средний уровень реализует бизнес-логику и логику управления, которая
координирует взаимодействия между клиентами приложения и данными приложения.
Средний уровень действует в качестве посредника между данными с
информационного уровня и клиентами приложения. Логика управления среднего уровня
обрабатывает клиентские запросы (например, запрос на просмотр каталога товаров)
и извлекает данные из базы данных. После этого логика создания внешнего
представления обрабатывает данные, полученные с информационного уровня, и
предоставляет содержимое клиенту.
452
Глава 9
Общая методическая рекомендация 9.1
Web-сервер в многоуровневом приложении может рассматриваться как
отдельный уровень, т.е. приложение становится четырехуровневым.
В данном практическом примере Web сервер считается частью среднего
уровня, поскольку Web сервер лишь делегирует запросы серверу
приложений и пересылает ответы на клиентский уровень.
Бизнес-логика определяет бизнес-правила и обеспечивает сохранность
информации перед обновлением базы данных или представлением данных пользователю.
Бизнес-правила определяют, как клиенты приложения могут и как не могут
осуществлять доступ к данным, и каким образом данные обрабатываются в
приложении. Например, в приложении книжного Internet-магазина может действовать
бизнес-правило, которое требует, чтобы эмитент кредитной карты покупателя
проверял кредитную карту перед отправкой заказа покупателю. Бизнес-логика может
реализовывать это бизнес-правило путем получения от потребителя номера
кредитной карты, срока ее действия и адреса для доставки счета с последующей проверкой
этой информации. Если проверка завершается успешно, бизнес-логика обновляет
базу данных и указывает, что магазин может отправить заказ покупателю.
Средний уровень также реализует логику внешнего представления данных.
Web-приложения обычно предоставляют клиентам информацию в виде XHTML-
документов. Учитывая последние достижения в технологиях беспроводной связи,
многие Web-приложения также предоставляют информацию клиентам с
беспроводным доступом в виде документов WML и cHTML. На среднем уровне
приложения Deitel Bookstore используются возможности технологий XML и XSLT для
динамического генерирования содержимого, отправляемого клиентам различных
типов, предоставляя поддержку для Web-брауэеров (XHTML), WAP-браузеров
(WML) и браузеров i-mode (cHTML).
Клиентский уровень, или верхний уровень, представляет собой
пользовательский интерфейс приложения. В Web-приложениях клиентский уровень обычно
представлен Web-браузером. Пользователи просматривают в Web-браузере
итоговый результат, сформированный приложением, и используют гипересылки
и кнопки, предусмотренные в форме, для взаимодействия с приложением. После
этого Web-браузер взаимодействует со средним уровнем, чтобы выдавать запросы
и извлекать данные с информационного уровня. Приложение Deitel Bookstore
поддерживает на клиентском уровне браузеры Web, WML и cHTML. Разработчики
могут добавлять поддержку и для других типов клиентов, предоставляя
соответствующие таблицы стилей XSL для этих клиентов. На рис. 9.2 представлена подробная
схема архитектуры корпоративного приложения Deitel Bookstore. В последующих
разделах будет рассмотрена каждая из составных частей этой схемы.
9.4. Компоненты Enterprise JavaBeans
Компоненты Enterprise JavaBeans (EJB) реализуют в приложении Deitel
Bookstore бизнес-логику и уровень абстракции базы данных. 1'лавным элементом
бизнес-логики является сеансовый EJB-компонент с состоянием, который
представляет магазинную тележку покупателя. Модель приложения Deitel Bookstore
реализует компонент-сущность EJB, который предоставляет
объектно-ориентированный интерфейс для взаимодействия с информационным уровнем. Любая
программа, которая способна осуществлять взаимодействия через RMI-HOP, может
использовать EJB-компоненты бизнес-логики. Например, может быть разработано
административное инструментальное средство в виде автономного приложения,
использующего EJB-компоненты бизнес-логики, чтобы модифицировать данные
приложении. Сер влеты в приложении Deitel Bookstore используют В JB-
компоненты бизнес-логики для реализации Internet-магазина.
Практический пример корпоративного приложения. Обзор архитектуры 453
Клиенты
вза и мо действуют
с Web-сервером,
который
пересылает
запросы сервгету,
выполняющемуся
в контейнере
сералетое сервера
приложении.
Сервлеты
реализуют
логику работы
приложения и
взаимодействуют
с
LIB-компонентами через
KIWI-HOP
^Клиент)
<Шй^ fflgiSj#
j Клиент IV
1 i-rnode Г
■'#
Web-
браузер
'0?
.*т>
айг-
Web-cepeep
Информационный уровень и
EJ В-компоненты
образуют модель
приложения
"s3gg^
•®1Ш
Контейнер сервлетов
XSLT-
преобразователь
(WMU
XSLT-
преобразователь
(cHTML)
XML
XSLT-
преобразовзтель
(XHTML)
XML
XML
XSLT-трамсформации
реализуют внешнее
представление
приложения.
настраивая
содержимое
для каждого типа
клиен-ов.
Контейнер сервлетов
управляет жизненным
циклом сервлетов
и взаимодействием
с Web-сервером.
Контейнер EJB
предоставляет
EJ В-компонентам
сервисы выполнения,
такие как соединения
с базой данных
и средства управления
жизненнь1м циклом
Рис. 9.2. Развернутая схема приложения Deitel Bookstore
9.4.1. EJB-сущности
EJB-компоненты с данными (компоненты-сущности) обеспечивают объектно-
ориентрованную абстракцию информационного уровня приложения. Каждая
EJB-сущность представляет определенный объект, хранящийся в реляционной
базе данных приложения. Экземпляры каждой из EJB-сущностей представляются
отдельными строками в базе данных. Например, экземпляр EJB-компонента
Customer представляет одного потребителя. В базе данных хранится имя,
фамилию, адрес доставки счета, адрес доставки товара и информация о кредитной карте
для каждого потребителя. Каждый экземпляр EJB-компонента Customer
представляет определенного покупателя и предоставляет методы для извлечения и
сохранения информации о покупателе.
Для упрощения передачи данных каждая EJB-сущность в приложении имеет
соответствующий класс модели, содержащий свойства для каждой из
EJB-сущностей. Например, EJB-компонент Product, который представляет товар в базе
данных, имеет соответствующий класс Product Model со свойствами, описывающими
ISBN-код книги, цену, автора и т.д. Каждый класс модели реализует интерфейс
Serializable и, следовательно, подходит для передачи через RML-ПОР.
Инкапсулирование данных в классы модели помогает набежать заторов в сети за счет сокра-
454
Глава 9
щеиия количества удаленных вызовов методов, необходимых для получения
информации от EJB-сущности. Например, сервлет может вызвать метод getProdnct-
Model для получения информации о товаре (объект Product), вместо того, чтобы
осуществлять отдельные вызовы методов, таких как getlSBN, getPrice, getAuthor
и т.д. Каждый класс модели также реализует интерфейс XMLGenerator, который
определяет метод getXML для извлечения XML-представления определенного
экземпляра класса модели, Сервлеты в приложении Deitel Bookstore используют этн
элементы XML для построения XML-документов, например, каталога товаров
и архива заказов.
9.4.2. Сеансовые EJ В -компоненты с состоянием
Сеансовый EJB-компонент с состоянием ShoppingCart, который реализует
магазинную тележку покупателя, является главным элементом бизнес-логики в
приложении Deitel Bookstore. Иногда бывает, что покупатели просматривают
предлагаемый сетевым магазином ассортимент, добавляют товары в тележки, а затем
решают не приобретать эти товары. Такие магазинные тележки можно считать
отставленными. Вместо того, чтобы хранить отставленную тележку в базе
данных, приложение Deitel Bookstore использует сеансовый EJB-компонент с
состоянием, что позволяет достигнуть максимальной аналогии с реальным магазином.
Если покупатель отставляет магазинную тележку, контейнер EJB удаляет
экземпляр EJB-компонента ShoppingCart.
9.5. Логика управления, реализуемая сервлетами
Сервлеты обеспечивают интерфейс среднего уровня между клиентом
и EJB-компонентами бизнес-логики. Сервлеты в приложении Deitel Bookstore
реализуют логику управления (контроллер) в архитектуре MVC. Сервлеты
обрабатывают клиентские запросы (поступающие через протоколы HTTP и WAP) и
взаимодействуют с EJB-компонентами бизнес-логики для удовлетворения этих запросов.
Далее сервлеты обрабатывают данные, извлеченные из EJB-компонентов, и
генерируют XML-документы, которые представляют эти данные. Такие
XML-документы действуют в качестве промежуточных моделей данных приложения. После
этого сервлеты подвергают ати XML-документы XSLT-трансформациям, которые
формируют представление данных для каждого из типов клиентов.
9.6. Логика внешнего представления данных
посредством XSLT
Каждый сервлет в приложении Deitel Bookstore применяет XSL-преобразова-
тель (объект Transformer) и XSLT-трансформации для генерирования
соответствующих представлений для каждого тика клиентов. Для каждого
поддерживаемого типа клиента приложению требуется отдельный набор XSLT-трансформаций,
Например, мы предоставляем один набор XRT/Г-трянсформаций для формирования
XHTML-кода, второй набор для формирования WML-кода -и третий набор для
формирования cHTML-кода. Сервлеты используют файл конфигурации для
определения, какую именно XSLT-трансформацию применить для данного типа клиента.
Сервлет GetProductServlet получает XML-описание товара из модели Product-
Model для данного товара. XSL-яреобразователь (объект Transformer) использует
XSLT-трансформацию для извлечения данных из XML-документа и создания их
внешнего представлении для клиента. Если клиентом является Web-браузер,
Практический пример корпоративного приложения. Обзор архитектуры 455
XSL-преобразователь Transformer использует XSLT-трансформацию, которая
формирует XHTML-код. Если клиентом является WAP-браузер (например, для
сотового телефона), XSL-преобразователь Transformer использует
XSLT-трансформацию, которая формирует WML-код.
На рис. 9.3 представлен образец XML-документа, сформированного сервлетом
GetProductServlet. Информация о товаре (книге), такая как ISBN-код, название,
автор, издатель, цена и т.д., размечена в виде XML-документа.
1 <?хи1 version = "1.0" encoding="UTF-8"?>
2 <catalog>
3 <product>
4 <isbn>Q130284173</isbn>
5 <publisher>Prentice Ha11</publisher>
6 <auth.ot>Deitel, Deitel, Nieto, Lin Samp; Sadhu</anthor>
7 <title>XML How to Program</title>
8 <price>$69.95</price>
9 <pages>1200</pages>
10 <image>images/xra3htpl-jpg</image>
11 <media>CD</media>
12 <quantity>500</guantity>
13 </product>
14 </catalog>
Рис. 9.З. XML-файл, сформированный сервлетом GetProductServlet
XSL-документ, представленный на рис. 9.4, трансформирует XML-документ,
сформированный сервлетом GetProductServlet, в XHTML-код, который
воспроизводится Web-браузером (рис. 9.5). При трансформации из XML-документа просто
извлекаются соответствующие фрагменты информации, и создается нужное
XHTML-представление. Структуры соответствующих таблиц стилей XSLT будут
рассматриваться в главе 10.
1 <?xml version = "1,0"?>
2
3<!-- ProductDetails. xsl -->
4 <!-- Таблица стилей XSLT для трансформации содержимого, —>
5 <!— сформированного сервлетом GetProductServlet, в XHTML-код. —>
6
7 <ks1:stylesheet version = "1.0"
8 xmlnsixsl = "http://www.w3.org/1999/XSL/TransformM>
9
10 <xsl .-output method = "xml" omit-xml-declaration = "no"
11 indent = "yes" doctype-system = "DTD/xhtmll-strict.dtd"
12 doctype-public = "-//W3C//DTD XHTML 1.0 Strict//EN"/>
13
14 <!-- включение шаблона для обработки элементов error -->
15 <xsl:include href = "/XSLT/XHTML/error.xsl"/>
16
17 <>— шаблон для элемента product -->
18 <xsl:template match = "product">
19 <html xmlns = "http://www.w3.org/1999/xhtml"
20 xml:lang = "en" lang = "en">
21
22 <head>
23 <title>
24 <xsl;value-of select = "title"/> — Description
456
Глава 9
25 </title>
26
27 «Clink rel = "Stylesheet" href = "styles/default.css"/>
28 </head>
29
30 <body>
31
32 <!— копирование заголовка со средствами навигации по
XHTML-документу -->
33 <xsl:for-each select =
34 "document( '/XSLT/XBTML/navigation.xml1 )">
35 <xsl:copy-of select = "."/>
36 </xsl:for-each>
37
38 <div class = "header">
39 <xsl:value-of select = "title"/>
40 </div>
41
42 <div class = "author">
43 by <xsl:value-of select = "author"/>
44 </div>
45
46 <!— создание элемента div со сведениями о товаре Product —>
47 <div class = "productDetails">
48 <table style = "width: 100%;">
49 <tr>
50 <td style = "text-align: center;">
51 <img class = "bookCover"
52 arc = "images/{image}"
53 alt = "{title} cover image."/>
54 </td>
55
56 <td>
57 <p style = "text-align: right;">
58 Price: <xsl:value-of select = "price"/>
59 </p>
60
61 <p style = "text-align: right;">
62 ISBN: <xsl:value-of select = "ISBN"/>
63 </p>
64
65 <p style = "text-align: right;">
66 Pages: <xsl:value-of select = "pages"/>
67 </p>
68
69 <p style = "text-align: right;">
70 Publisher:
71 <xsl:value-of select = "publisher"/>
72 </p>
73
74 <!— кнопка добавления в тележку AddToCart —>
75 <fonn method = "post" action = "AddToCart"?-
7 6 <p style = "text-align: center;">
77 <input type = "submit"
78 value = "Add to cart"/>
79
80 <input type = "hidden" name = "ISBN2
Практический пример корпоративного приложения. Обзор архитектуры 457
81
82
83
84
85
86
87
88
89
90
91
value =
</р>
</£огш>
</td>
</tr>
</table>
</div>
</body>
</htrol>
</xsl:template>
"{ISBN}"/>
92 <xsl:stylegheet>
Рис. 9.4. Таблица стилей XSL для генерирований XHTWL-кода из данных, выдаваемых
сервлетом GetProductServlet
1 <?xml version = "1.0" encoding = "PTF-8"?>
2 <!D0CTYPE html POBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
3 "DTD/xhtmll-strict.dtd">
4<html xmlns="http://www.w3.org/1999/xhtml"
5 lang="eit" xml:lang="en">
6 <head>
7 <title>XML How to Program — Description</title>
8 <link href="styles/defaiilt.e5S" rel="Stylesheet" />
9 </head>
10 <body>
11 <div>
12 <div class="logo">
13 <table style="width: 100%;">
14 <tr>
15 <td style="text-align: left;">
16 <img src="images/logotiny.gif"
17 alt="Deitel & Associates, Inc. logo." />
18 </td>
19
20 <td style="text-align: right;">
21 <div style=
22 "position; relative; bottom: -50px;">
23 <form action="ProductSearch" raethod="get">
24 <pXinput type="text" size="15"
25 name="searchString" />
26 <input type="submit" value="Search" />
27 </p>
28 </form>
29 </div>
30 </td>
31 </tr>
32 </table>
33 </div>
34
35 <div class="navigation">
36 <table class="menu">
37 <tr>
38 <td class="menu">
39 <a href="GetAllProducts">Product Catalog</a>
40 </td>
458
Глава 9
41
42 <td cLass="taen.u">
43 <a href="registration.html">Create Account</a>
44 </td>
45
46 <td class="menu">
47 <a href="login.html">Log in</a>
48 </td>
49
50 <td class="menu">
51 <a href="ViewCart">Shopping Cart</a>
52 </td>
53
54 <td class="mfinu">
55 <a hre£="ViewOrderHistory">Order History</a>
56 </td>
57 </tr>
58 </table>
59 </div>
60
61 </div>
62 <div class="header">XML How to Program</div>
63 <div class="author">
64 by Deitel, Deitel, Hieto, Lin Samp; Sadhu</div>
65 div class="productDetails">
66 <table style="width: 100%;">
67 <tr>
68 <td style="text-align: center;">
69 <img alt="XML How to Program cover image."
70 src="images/xralhtpl.jpg"
71 class="bookCover" /X/td>
72 <td>
73 <p> style="text-align; right;">
74 Price: $69.95</p>
75 <p> style="text-align: right;">
76 ISBN: 0130284173</p>
77 <p> style="text-align: right;">
78 Pages; HQQ</p>
79 <p> style="text-align: right;">
80 Publisher: Prentice Hall</p>
81
82 <form action="AddToCart" method="post">
83 <p style="text-align: center;">
84 <input value="Add to cart"
85 type="submit" />
86 <input value="0130284173"
87 name="ISBN" type="hidden" /X/p>
88 </form>
89 </td>
90 </tr>
91 </table>
92 </div>
93 </body>
94 </html>
Рис, Э.5. XHTML-документ, сформированный при XSLT-трансформации данных,
выдачных сервлетом Get Product Serviet {часть 1)
Практический пример корпоративного приложения. Обзор архитектуры 459
Ll.ii.i.iiii.iunaiM.ijy.i.iii.i.BJumMLj.MWwi)
Ffc E* К» ГянеНШ Т«*г
Lygfe , ./
.... .. ЛИ
3
DeiteI
XML Haw to Program
by Dpi lei, DeiteI, Meto, Lin & Sjdhni
^i^aach |
t nnfcrHiut&rv
sgj»°ffgiip^"?"' "
ajFfBiiiji" д$-
Рис. 9.5. XHTML-документ, сформированный при XSL -трансформации данных,
выданных сервлетом GetProduct5ervlet. (часть 2)
Таблица стилей XSL (рис. 9.6) трансформирует XML-документ,
сформированный сервлетом GetProductServlet, в WML-код, который воспроизводится WML-
браузером (рис. 9.7), Обратите внимание, что WML-документ содержит очень мало
информации по форматированию, WML-код воспроизводится небольшими
устройствами, например, сотовыми телефонами, имеющими ограниченные возможности
по отображению. Эти устройства также имеют ограниченные возможности сетевых
соединений, поэтому объем отправляемых данных должен быть минимальным.
Например, WML-документ не содержит изображения для обложки книги,
поскольку его загрузка при беспроводном соединении сопровождается большими
затратами времени.
1 <?xml version = "1.0"?>
2
3 <xsl:stylesheet version = "1.0"
4 xmlns:xsl = "http://www.w3.org/1999/XSL/Transform">
5
6
7
a
9
10
11
12
13
14
15
<xsl:output method = "xml" omit-xml-declaration = "no"
doctype-system = "http://www.wapforum.org/DTD/wml_l.1.xml'
doctype-public ■ ■'-//WAPFORUM//DTD WML l.l//EN"/>
<xsl:include href
■ /XSLT/WML/error.xsl"/>
<xsl:template match = "product">
<wml>
<card id = "product" title = "(title)">
460
Глава 9
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<do type = "accept" label = "Add To Cart">
<go href = "AddToCart" method = "post">
<postfield name = "ISBN" value = "{ISBW}"/>
</go>
</do>
<do type = "prev" label = "Back">
<prev/>
</do>
<p>Description:</p>
<pxxsl:value-of select = "title"/></p>
<p>by <xsl:value-of select = "author"/X/p>
<P>
<table columns = "2" title = "info">
<tr>
<td>ISBH-.
<xsl:value-of select = "ISBN"/>
</td>
<td>Price:
$<xsl:value-of select = "price"/>
</td>
</tr>
<tr>
<td>Publisher:
<xsl:value-of select = "publisher"/>
</td>
<td>Pages:
<xsl:value-of select = "pages"/>
</td>
</tr>
</table>
</p>
</card>
</wml>
</xsl:template>
54 </xsl
:stylesheet>
Рис. 9.6. Таблица стилей XSL, трансформирующая выданные сер влетом
GetProductServlet данные в WML-код
1 <?xml version = "1.0"?>
2 <!D0CTYPE wml PUBLIC "-//WAPFOBUM//DTD WML 1.1//EN"
3 "http://www.wapforum.org/DTD/vnnl_l.l.xml">
4
5 <wml>
6 <card title="XML How to Program" id="product">
7 <do label="Add To Cart" type="accept">
8 <go method="pcst" hre£="AddToCart">
9 <postfield value="0130284173" naroe="ISBN"/x/go>
10 </do>
11 <do label="Back" type="prev"Xprev/x/do>
12
13 <p>Description:</p>
Практический пример корпоративного приложения. Обзор архитектуры 461
14
15
16
17
18
19
20
21
22
23
2d
25
26
27
28
<р>ХВД» How to Program</p>
<р>Ьу Deitel, Deitel, Nieto, Lin lamp; Sadhu:</p>
<p>
<table title="info" columns="2">
<tr>
<td>ISBH: 0130284173</td>
<td>Prioe: $$69.65</td>
</tr>
<tr>
<td>Publisher: Prentice Hall</td>
<td>Pages: 1100</td>
</tr>
</table>
</p>
</card>
29 <Ляп1>
?ш
mWM(l'>a
Рис. 9.7. WML-документ, сформированный в результате XSLT-трансформации дакных,
выданных сервпеюм GetProductServlet. (Использовано изображение Image © 2001
Nokia Mobile Phones.)
1 <?xml version = "1.0"?>
2
3 <xsl:stylesheet version = "1.0"
4 xmlns:xsl = "http://www.w3.org/1999/xSL/Transform">
<xsl:output method = "html"
omit-xml-declaration = "yes"
indent = "yes"
doctype-system =
462
Глава 9
10 "http: //www. w3. org/MarxUp/btml-spec/html-spec_toe. html"
11 doctype-public = "-//W3C//DTD HTML 2.0//EN"/>
12
13 <xsl:include href = "/XSLT/cHTML/error.xsl"/>
14
15 <xsl:template match = "product">
16 <html>
17
18 <head>
19 <title>
20 <xsl:value-of select = "title"/> — Description
21 </title>
22 </head>
23
24 <body>
25 <&iv class = "header">
26 <xsl:value-of select = "title"/>
27 </div>
28
29 <div class = "author">
30 by <xsl:value-of select = "author"/>
31 </div>
32
33 <P— создание элемента div со сведениями о товаре Product —>
34 <div class = "productDetails">
35 <table>
36 <tr>
37 <td style = "text-align: center;">
38 <img class = "bookCover"
39 src = "images/{image}"/>
40 </td>
41
42 <td>
43 <p style = "text-align: right;">
44 Price: <xsl:value-of select = "price"/>
45 </p>
46
47 <p style = "text-align: right;">
48 ISBN: <xsl:value-of select = "ISBN"/>
4 9 </p>
50
51 <p style = "text-align: right;">
52 Pages: <xsl:value-of select = "pages"/>
53 </p>
54
55 <p style = "text-align: right;">
56 Publisher:
57 <xsl:value-of select = "publisher"/>
58 </p>
59
60 <!— кнопка добавления товара в тележку AddToCart -->
61 <form method = "post" action = "AddToCart">
62 <p style = "text-align: center;">
63 <input type = "submit"
64 value = "Add to cart"/>
65 </p>
Практический пример корпоративного приложения. Обзор архитектуры 463
66
67 <input type = "hidden" name = "ISBN"
68 value = "{T-SBN}"/>
69 </form>
70 </td>
71 </tr>
72 </table>
73 </div>
74 </body>
75
76 </html>
77 </xsl:ternplate>
78 </xsl:stylesheat>
Рис. 9.8. Таблица стилей XSL, трансформирующая выданные сервлетом
GetProductServlet данные в cHTML-код
1 <!DOCTYPB HTML PUBLIC "-//W3C//DTD HTML 2.0//EH"
2 "http://mfww.w3c.otg/Markup/html-spec/htinl-spec_toc .html">
3 <html>
4 <head>
5 <META http-equiv="Content-Type"
6 contents"text/html; charset=UTF-8">
7 <titls>XML How to Program — Description</title>
8 </haad>
9 <body>
10 <div class="header">XML How to Program</div>
11 <div class="author">
12 by Deitel, Deitel, Nieto, Lin Samp; Sadhu:
13 <div claas="productDetails">
14 <table>
15 <tr>
16 <td style-"text-align: center;">
17 <img src="image«/xmlbtpl.jpg" claese"bookCover"/>
18 </td>
19
20
21
22
23
24
25
26
27
28
29 <forro action="AddToCaxt" method="post">
30 <p style-"text-align: center;">
31 <input value="Add to cart" type="s__nit"/>
32 </p
33 <inpyt value="OI30284l73" na-e="ISBN"
34 type="hidden">
35 </forra>
36 </td>
37 </tr>
38 </table>
<td>
<P>
<P>
<P>
<P>
style-"text-align: right;">Price: $69.95</p>
style-"text-align: right:">
ISBN: 01302B4l73</p>
style-"text-align: right;">Pages: 1100</p>
style-"text-align: right;">
Publisher: Prentice Hall</p>
464
Глава 9
39 </6iv>
40 </body>
41 </html>
Рис. 9.9. cHTML-документ, полученный в результате XSLT-трансформации данных,
выданных сервлетом GetProductServlet (Изображение публикуется с разрешения
компании Pixo, Inc.)
В этой главе был представлен обзор архитектуры учебного приложения Deitel
Bookstore, в котором используются мощные корпоративные возможности Java,
такие как сервлеты, EJB, RMI, XML и XSLT. Реализации каждого из уровней
приложения рассматриваются в главах 10, 11 и 12. В главах 12, 13 будут приведены
инструкции по развертыванию приложения Deitel BookstOTe на эталонной
реализации J2EE и на серверах приложений BEA Web Logic и IBM WebSphere.
Резюме
• Паттерн проектирования MVC (модель—вид—контроллер) применительно к этому
учебному корпоративному приложению отделяет данные и бизнес-логику (модель) от логики
внешнего представления данных (вид) и логики управления (контроллер),
• Многоуровневые приложения (их также называют ft-уровкевьши приложениями) имеют
несколько составных модульных частей, называемых уровнями. Каждый уровень может
физически размещаться на отдельном компьютере,
• Информационный уровень, или уровень данных, содержит данные для приложения.
Хранение э-1'их данные, как правило, организуется с помощью системы управлении
реляционными базами данных (RDBMS). База данных может содержать информацию о товаре,
например, описание, цену и имеющееся на складе количество, а также информацию о
потребителе, такую как имя пользователя, адрес доставки счета и номер кредитной карты.
• Средний уровень реализует бизнес-логику и логику внешнего представления данных для
организации взаимодействий между клнект&ми приложения и данными приложения.
Средний уровень выступаем в качестве посредника между данными е информационного
уровни и клиентами приложения.
Практический пример корпоративного приложения. Обзор архитектуры 465
• Логика управления среднего уровня обрабатывает клиентские запросы (например, запрос
на просмотр каталога товаров) и извлекает данные из базы данных. После этого логика
внешнего представления среднего уровня обрабатывает данные, полученные с
информационного уровня, и отправляет содержимое клиенту.
• Бизнес-логика применяет бизнес-правила и обеспечивает сохранность и достоверность
данных перед обновлением базы данных или представлением данных пользователю.
Бизнес-правила определяют, как клиенты приложения могут и как не могут осуществлять
доступ к данным, и каким образом данные обрабатываются в приложении.
• Средний уровень также реализует логику внешнего представления данных приложения.
Средний уровень принимает клиентские запросы, извлекает данные из информационного
уровня и представляет содержимое клиентам.
• Web-приложения обычно представляют информацию клиентам в виде
XHTML-документов. Многие Web-приложении также представляют информацию клиентам с
беспроводным доступом в виде документов Wireless Markup Language (WML) или документов
Compact HyperText Markup Language (cHTML).
• На среднем уровне приложения Deitel Bookstore используются XML и XSLT для
динамического формирования содержимого клиентам различного типа, предоставляя поддержку
для XHTML, WML, cHTML и, в принципе, для клиентов любого другого типа.
• Клиентский уровень представляет собой пользовательский интерфейс приложения.
Пользователи взаимодействуют с приложением через пользовательский интерфейс. Клиент
взаимодействует со средним уровнем, чтобы посылать запросы и извлекать данные из
приложения. После этого Клиент отображает пользователю данные, полученные со
среднего уровня.
• Билнес-пряниля приложения Deitel Bookstore реализуются с помощью компонентой
Enterprise JavaBeans (EJB). В приложении Deitel Bookstore компоненты-сущности EJB
совместно с базой данных, которую они представляют, образуют модель приложения.
• Любая программа, которая способна осуществлять коммуникационные взаимодействия
через RMI-IIOP, может использовать EJB-компоненты бизнес-логики и объекты
абстракции базы данных. Например, может быть разработано административное
инструментальное средство в виде автономного приложения Java, которое использует EJB-компоненты
бизнес-логики для обработки заказов.
• EJB-еущиости обеспечивают объектно-ориентированное представление информационного
уровня приложения. Каждая EJB-сущность представляет определенный объект,
хранящийся в базе данных. Экземпляры каждой EJB-сувдности представляют отдельные
строки в базе данных.
• Покупатели, посещающие Internet-магазин, порой просматривают каталог продукции
и добавляют выбранные ими товары в магазинную тележку, но впоследствии принимают
решение не покупать эти товары. Магазинная тележка, таким образом, оказывается
отставленной. Вместо того, чтобы хранить такие отставленные тележки в базе данных,
лучше воспользоваться сеансовыми EJB-компонентами, чтобы пользователь ощущал себя
почти так же, как и при посещении реального магазина.
• Сервлеты обеспечивают на среднем уровне интерфейс между клиентом и
EJB-компонентами биэиес-логики. Сервлеты в приложении Deitel Bookstore реализуют в архитектуре
MVC логику управления (контроллер). Сервлеты обрабатывают пользовательские
запросы (поступающие через протоколы HTTP или WAP) и взаимодействуют с
EJB-компонентами бизнес-логики.
• Сер1влеты обрабатывают данные, извлеченные из EJB-комяояентов, и генерируют
XML-представления, которые выступают в качестве промежуточных моделей данных
приложения.
• Таблицы стилей XSL осуществляют внешнее представление данных приложения,
преобразуя модель XML в соответствующие форматы для клиентов различного типа.
• Каждый сервлет в приложении Deitel Bookstore применяет XSL-преобразователь (объект
Transformer) и XSLT-трансформации для генерирования содержимого для клиентов
каждого из типов. Для формирования содержимого для соответствующего типа клиента
предусмотрен отдельный набор XSLT-транСформаЦиЙ.
• Сервлеты формируют XML-документы, к которым XSL-преобразоватеяь (объект
Transformer) применяет XSLT-трансформации для создания соответствующего
содержимого. Для поддержки других типов клиентов разработчик может создать дополнительный
набор XSLT-трансформаций.
466
Глава 9
Терминология
abandoned shopping cart — отставленная
магазинная тележка
application server — сервер приложений
bottom tier — нижний уровень
brick-and-mortar store — реальный магазин
(супермаркет)
business logic — бизнес-логика
business rules — бизнес-правила
business logic component — компонент
бизнес-логики
client tier — клиентский уровень
Compact HyperText Markup Language
(cHTML)
container-managed persistence — персистент-
ность, управляемая контейнером
controller logic — логика управления
controller logic component — компонент
логики управления
data tier — уровень данных
delegate — делегировать
design patterns — паттерны проектирования
e-business — электронный бизнес
e-business application — приложение для
электронного бизнеса
e-commerce — электронная коммерция
e-commerce application — приложение для
электронной коммерции
Enterprise JavaBeans (EJB)
entity EJB — компонент-сущность EJB
four-tier application — четырехуровневое
приложение
information tier — информационный
уровень
middle tier — средний уровень
model—view—controller (MVC) —
архитектура модель—вид—контроллер
multi-tier application — многоуровневое
приложение
presentation logic — логика представления
данных
shopping cart e-coramerce model — модель
магазинной тележки для электронной
коммерции
three-tier architecture — трехуровневая
архитектура
top tier — верхний уровень
Wireless Access Protocol (WAP)
Wireless Markup Language (WML)
XHTML
XML
XSL transformation — XSLT-трансформация
XSL Transformer — XSL-преобразователь
(объект Transformer)
XSLT
Упражнения для самоконтроля
9.1. Какой архитектурный паттерн проектирования играет главную роль в структуре
приложения Deitel Bookstore? Какие преимущества дает применение этого паттерна
проектирования?
9.2. Какая часть в архитектуре приложения Deitel Bookstore может быть вынесева в
отдельный уровень? Почему?
9.3. В разделе 9.3 описывается гипотетическое бизнес-правило, требующее проверки
кредитной карты. Опишите еще одно бизнес-правило, которое могло бы использоваться
для приложения Deitel Bookstore,
9.4. Какие компоненты логики управления имеются в приложении Deitel Bookstore? Какие
имеются компоненты бизнес-логики? Как компоненты логики управления
взаимодействуют на сервере приложений с компонентами бизнес-логики?
9.5. Какой тип компонентов EJB в приложении Deitel Bookstore делает его наиболее
близким к. реальному магазину?
9.6. Каким образом приложение Deite! Bookstore способно представлять содержимое
клиентам практически любого типа?
9.7. Как может партнер в структуре отношений В2В (предприятие — предприятие)
(например, корпоративный клиент) взаимодействовать с приложением Deitel Bookstore?
Например, партнер В2В может захотеть заказывать на периодической основе большое
количество экземпляров определенной книги, используемой для обучения новых
сотрудников. Каким образом этот партнер В2В может программным образом осуществить
заказ книг?
Практический пример корпоративного приложения. Обзор архитектуры 467
Ответы на упражнения для самоконтроля
9.1. В приложении Deitel Bookstore используется архитектура модель—вид—контроллер
(Model—View.—Controller — MVC). В архитектуре MVC бизнес-логика (модель)
отделяется о? логики управления (контроллер) и логики внешнего представления данных
(вид). Реализации бизнес-логики, логики управления и логики внешнего
представления данных могут меняться независимо друг от друга, без необходимости внесения
изменений в другие компоненты.
9.2. Web-сервер может быть выделен в виде отдельного, четвертого уровня. Выделение
Web-сервера как отдельного, четвертого уровня обеспечит модульность приложения
и даст возможность осуществлять поддержку клиентов, использующих другие
протоколы, отличные от HTTP.
9.3. Дополнительное бизнес-правило может требовать, чтобы потребители предоставляли
свои адреса e-mail при регистрации. Бизнес-логика может обрабатывать формы для
регистрации и отвергать регистрационные записи, в которых не указан адрес e-mail.
9.4. Компонентами логики управления в приложении Deitel Bookstore являются сервлеты.
Компонентами бизиес-логики являются компоненты Enterprise JavaBeans. Сервлеты
взаимодействуют с EJB-компонентами через контейнер EJB посредством RMI-IIOP.
9.5. EJB-компонент ShoppingCart реализован как сеансовый компонент EJB с состоянием,
имитирующий магазинную тележку в реальном супермаркете.
9.6. Приложение Deitel Bookstore использует XSLT-трансформапии для представления
содержимого клиентам каждого из типов. Сервлеты генерируют XML-документы, а
разработчик предоставляет таблицы стилей XSL для преобразования сформированных серв-
летами XML-документов в содержимое, подходящее для клиентов каждого из типов.
ю
Практический пример
корпоративного приложения.
Логика представления
данных и логика управления
Цели
• Понять роль логики
представления данных
в многоуровневом приложении.
• Понять роль логики
управления в многоуровневом
приложении.
• Научиться применять
сервлеты при реализации
среднего уровня приложений
Enterprise Java.
• Понять, каким образом
технологии XML и XSLT
позволяют осуществлять
поддержку клиентов
различных типов
в Web* приложении.
• Понять, каким образом
параметры инициал и зад и и
сервлета способствуют
повышению гибкости
сервлетов.
Первым достается лучшее.
Народная мудрость
Расстояние придает виду особую
прелесть.
Томас Кзмпбел
Изобретение — всего лишь выдающееся
отклонение от эталона или же расширение
области применения хорошего эталона...
Эдвард Дж. Балвер-Литтон
Не становитесь рабом, шаблона.
Винсент Ван Гог
Пока Гений растрачивал свою силу на
эксцентричные порывы, мне увиделось лицо
совершенно иного склада, имя которому —
Применение.
Анна «Петиция Барболд
470
Глава 10
В этой главе мы рассмотрим логику внешнего представления даииых и логику
управления для приложения Deitol Bookstore. Логика управления отвечает в
приложении за обработку пользовательских запросов. Например, когда покупатель
посылает запрос на добавление книги в магазинную тележку, логика управления
обрабатывает запрос и вызывает соответствующие методы бизнес-логики для
выполнения запрашиваемого действия. После этого логика представления данных
отображает полученный результат пользователю.
Логика управления в приложении Deitel Bookstore реализована на сервлетах
Java. После вызова методов бизнес-логики для обработки клиентских запросов
сервлеты генерируют XML-документы, содержащие данные, отправляемые
клиенту. Эти XML-документы не являются специфичными для конкретного типа
клиента (например, Web-браузер, сотовый телефон и т.д.), они всего лишь размечают
данные, предоставленные бизнес-логикой. Логику представления реализуют
таблицы стилей XSL (XSLT-трансформации), образующие в архитектуре MVC
уровень представления данных (вид). Таблицы стилей XSL преобразуют XML-доку-
ментов в формат, подходящий для клиента соответствующего типа. Например,
XSLT-трансформация может генерировать XHTML-докуменТ для представления
его в Web-браузере стационарного компьютера, либо WML-документ для
представления его в WAP-браузере сотового телефона.
Применение XSLT-трансформаций в качестве логики внешнего представления
данных дает возможность разработчикам расширять набор типов клиентов,
которые поддерживаются приложением, не внося при этом изменений в логику управ-
Практический пример корпоративного приложения
471
ления (контроллер) или в бизнес-логику (модель). Для добавления поддержки
нового типа клиента разработчик просто предоставляет дополнительное множество
XSLT-трансформаций, которые формируют соответствующий выходной результат
для нового типа клиента. Затем разработчик модифицирует XML-файл
конфигурации, чтобы подключить поддержку нового типа клиента. [Замечание. Для
приложения Deite] Bookstore требуется XSL-преобразователь Xalan от Apache Software
Foundation. Загрузите Xalan версии 2.1 с сайта xml.apache.org/disi/xalan-} и
скопируйте файлы xalan.jar и xerces.jar в ваш каталог jre/lib/ext SDK J2SE1.]
Эта глава является первой из трех глав, в которых представлена реализация
учебного приложения Deitel Bookstore. В главах 11 и 12 будет рассматриваться
бизнес-логика и компоненты-сущ ноет и EJB, необходимые для обработки заказов
и организации взаимодействия с базой данных.
10.2. Базовый класс XMLServlet
Каждый сервлет в учебном приложении Deitel Bookstore расширяет класс
XMLServlet (рис. 10.1), который обеспечивает стандартную инициализацию
и предоставляет утилитарные методы. Чтобы обеспечить поддержку нескольких
типов клиентов, каждый сервлет в приложении Deitel Bookstore создает
XML-документ, содержащий исходные данные, полученные с уровня бизнес-логики и
информационного уровня приложения. XSLT-трансформация обрабатывает этот
XML-документ и отправляет полученный результат клиенту. Класс XMLServlet
реализует эти стандартные функциональные возможности.
Метод init (строки 41-85) инициализирует сервлет. Контейнер сервлетов
предоставляет аргумент ServletConfig при инициализации сервлета. В строке 49
извлекается имя таблицы стилей XSL, которая будет использоваться для
формирования содержимого для клиентов каждого из поддерживаемых приложением типов.
Это имя файла передается в качестве параметра инициализации сервлета, поэтому
разработчик должен определить этот параметр при развертывании сервлета.
В строке 52 создается экземпляр объекта DocumentBuilderFactory, который будет
использоваться для создания объектов DocumentBuilder, формирующих
XML-документы. В строке 55 создается объект-мастер TransformerFactory, с помощью
экземпляров которого класс XMLServlet выполняет XSLT-трансформации. В
строках 58-81 задается интерфейс UKlResolver для TransformerFactory, который
позволяет преобразователям Transformer вычислять относительные URI
в XSL-доку мент ах. Например, если XSL-документ ссылается на другой XSL-доку-
мент с использованием элемента xsl:include с указанием относительного XJRI
(например, /XSLT/XHTML/error.xsl), объект Transformer вызывает метод
интерфейса URIResolver для определения, откуда объекту Transformer следует
загрузить включаемую XSL-таблицу. В строке 67 вычисляется относительный URI
путем вызова метода getResource интерфейса ServletContext, который
возвращает объект URL. В строке 71 возвращается объект Stream Source для потока ввода
Input St ream объекта URL. Объект Transformer использует этот объект Stream-
Source для чтения содержимого включенного XSL-документа.
1 // XMLServlet.Java
2 // XMLServlet - базовый класс для сервлетов, которые
3 // генерируют XML-документы и выполняют XSLT-трансформации.
4 package com.deitel.advjhtpl.bookstore.servlets;
5
б // Набор базовых пакетов Java
1 На момент подготовки к изданию перевода книги актуальной была версии 2.4.1 Xalan. —
Прим. ред.
472
Глава 10
7 import java.io.*;
8 Import java.util.*;
9 import j ava,net.ORL;
10
11 // Пакеты расширений Java
12 import javax.servlet.*;
13 import 3avax.servlet.http.*;
14 import j avax.xml.parsers.*;
15 import javax.xml.transform.*;
16 import j avax. xml. transform, dom. *,-
17 import javax.xml.transform.stream.*;
18
19 // Пакеты сторонних поставщиков
20 import org.w3c.dom.*;
21 import org.xml.sax.SAXException;
22
23 // Пакеты Deitel
24 import com,deitel.advjlntpl.bookstore.model,*;
25
26 public class XMLServlet extends HttpServlet {
27
28 /( мастер дли создания объектов. Docvubent&uilder
29 private DocumentBuildexFactory builderFactory;
30
31 // мастер для создания преобразователей Transformer
32 private TransformerFactory transformerFactory;
33
34 // XSL-файл, представляющий содержимое сервлета
35 private String XSLFileName;
36
37 // список объектов ClientModel для определения типа клиента
38 private List clientList;
39
40 // инициализация сервлета
41 public void init{ ServletConfig config )
42 throws ServletException
43 {
44 // вызов метода init суперкласса для инициализации
45 super,init( config );
46
47 // использование параметра InitParameter для задания XSL-файла,
48 // используемого при трансформации сформированного содержимого
49 setXSLFileName( config.getlnitParaweter( "XSL FILE" \ );
SO
51 // создание нового объекта DocumentBuilderFactory
52 builderFactory = DocumentBuilderFactory.newInstance();
53
54 // создание нового объекта TransformerFactory
55 transformerFactory = TransformerFactory.newlnsfcance();
56
57 // настройка объекта DRIResolver для вычисления относительных
путей в XSLT-таблице
58 transformerFactory.setORIResolver(
59
60 new URIResolver() {
61
Практический пример корпоративного приложения
473
62 // вычисление ссылки href как относительного пути
к ServletContext
63 public Source resolve( String href, String base )
64 {
65 try 1
66 ServletContext context = getServletContext{);
67 URL url = context.getResource( href J;
68
69 // создание объекта StreamSource для чтения документа
70 return new StreamSource[ -url.openStream() );
71 }
72
73 // обработка исключения при получении заданного
документа
74 catch ( Exception exception ) (
75 exception.printStackTrace0;
76
77 return null;
78 }
79 }
BO >
81 ); // конец обращения к setURIResolver
82
83 // создание списка объектов ClientModel
84 clientLiat = buildClientList();
85 }
86
B7 // получение экземпляра класса DocumentBuilder для построения
XML-документов
88 public DocumentBuilder getDocumentBuilder( boolean validating }
B9 {
90 // создание нового объекта DocumentBuilder
91 try {
92
93 // установка режима проверки
94 builderFactory.setValidating{ validating );
95
96 // возврат нового объекта DocumentBuilder вызвавшему процессу
97 return builderFactory.newDocumentBuilder{>;
98 )
99
100 // обработка исключения при создании объекта DocumentBuilder
101 catch ( ParserConfigurationException parserException ) (
102 parserException.printStackTrace();
103
104 return null;
105 )
106
107 } // конец иетода getDocumentBuilder
10B
109 // получение синтаксического анализатора, не проверяющего
на допустимость
110 public DocumentBuilder getDocumentBuilder()
111 i
112 return getDocumentBuilder( false );
113 }
474
Глава 10
114
115 // установка имени XSL-файла для преобразования содержимого сервлета
116 public void setXSLFileName ( String fileName )
117 {
118 XSLFileName = fileName;
119 }
120
121 // получение имени XSL-файла для преобразования содержимого сервлета
122 public String getXSLFileName()
123 1
124 return XSLFileName;
125 }
126
127 // запись XML-документа клиенту с использованием предоставленного
128 // объекта ответа после преобразования XML-документа с
129 // применением специфичной для клиента таблиц» стилей XSL
130 public void writeXML( HttpServletRequest request,
131 HttpServletResponse response. Document document )
132 throwB XOException
133 {
134 // получение текущего сеанса, создание его, если сеанс не существует
135 HttpSession session = request.getSession[ true );
136
137 // получение объекта CHentModel из данных сеанса
138 ClientModel client = ( ClientModel )
139 aession.getAttribute( "ClientModel" );
140
141 // если клиента нет (null), получить новый объект ClientModel
142 // для заданного заголовка User-Agent и сохранить его
в данных сеанса
143 if { client = null ) {
144 String userAgent = request.getHeaderl "User-Agent" );
145 client = getClientModel( userAgent ) ,-
146 session.setAttribute{ "ClientModel", client };
147 }
148
149 // задание соответствующего типа содержимого Content-Type для клиента
150 response.setContentTypeС client.getCoiitentType.() );
151
152 // получение объекта PrintWriter для записи данных клиенту
153 PrintWriter output = response.getWriter(>;
154
155 // формирование имени файла для XSLT-документа
156 String xslFile = client.getXSLPatM) + getXSLFileName(J;
157
158 // открытие потока ввода Inputstream для XSL-документа
159 Inputstream xsIStream =
160 getServletContext().getResOurceAsStream( xslFile );
161
162 // преобразование XML-документа с применением XSLT-трзнсформации
163 transform( document, xslStream, output );
164
165 // освобождение и закрытие объекта PrintWriter
166 output.close[);
167
168 J // конец метода writeXML
Практический пример корпоративного приложения
475
169
170 // преобразование XML-документа с применением указанного потока
171 // ввода XSLT к алпись полученного документа в указанный объект
PrintWriter
172 public void transform( Document document,
173 Inputstrearn xslStream, PrintWriter output )
174 {
175 // создание объекта Transformer и применение XSLT-трансформации
176 try {
177
178 // создание объекта DOMSource для исходного XML-документа
179 Source xmlSource = new DOMSource( document );
180
181 // создание исходного потока StreamSource для XSLT-документа
182 Source xslSource =
183 new StreamSource( xslStream );
184
185 // создание потока вывода StreamReeult для результата трансформации
186 Result result = new StreamResult{ output };
187
188 // создание объекта Transformer для XSLT-трансформации
189 Transformer transformer =
190 transformerFactory.newTransformer( xslSource );
191
192 // трансформация и доставка содержимого клиенту
193 transformer.transform( xmlSource, result );
194
195 ) // конец блока try
196
197 // обработка исключения при трансформации XML-документа
198 catch ( TransformerException transformerException ) {
199 transformerException.printStackTrace();
200 }
201
202 } // конец метода transform
203
204 // формирование элемента error содержащего сообщение об ошибке
205 public Node buildErrorMessage( Document document,
206 String message)
207 {
208 // создание элемента error
209 Element error = document.createElement( "error" };
210
211 // создание элемента message
212 Element errorMessage -
213 document.createElement( "message" );
214
215 // создание текста сообщения и добавление его в элемент message
216 errorMessage.appendChild (
217 document.createTextNode( message ) );
218
219 // добавление элемента message в элемент error
220 error.appendChild( errorMessage ) ;
221
222 return error;
223 }
Глава
224
225 // формирование списка объектов ClientModel для доставки
226 // соответствующего соквржиного клиентам каждого из типов
227 private List buildClientListО
228 {
229 // получение проверяющего на допустимость анализатора
DocumentBuilder для клиентского XML-документа
230 DocumentBuilder builder = getDocumentBuildsr( true );
231
232 // создание списка клиентов
233 List clientList = new ArrayListO;
234
235 // получение имени XML-документа, содержащего информацию
236 // о клиентах, извлеченную иа объекта ServletContext
237 String clientXML = getServletContext().getlnitParameter(
23B "CLIENT_LIST" );
239
240 // чтение типов клиентов иа XML-документа и создание объектов
ClientModel
241 try {
242
243 // открытие потока ввода InputStream для XML-документа
244 InputStream clientXMLStreain =
245 getServletContext().getResourceAsStream(
246 clientXML )•
2Л1
248 // синтаксический анализ XML-документа
249 Document clientsDocument =
250 builder .parse [ clientXMLStreain );
251
252 // получение списка узлов NodeList элементов client
253 NodeList cliontElements =
254 clientsDocument.getEleraentsByTagName( "client" );
255
256 // получение длины списка NodeList
257 int listLength = clientElements,getLength();
258
259 // обработка списка NodeList элементов client
260 for { int i = 0; i < listLength; i++ ) {
261
262 // получение следующего элемента client
2 63 Elemsnt client =
264 ( Element ) ClientElements.item( i );
265
266 // получение элемента agent из элемента client
267 Element agentElement = ( Element }
268 client.getElementsByTagName(
269 "userAgent" >.item< 0 );
270
271 // получение дочернего текстового узла элемента agent
272 Text agentText =
2 73 ( Text ) agentElement.getFirstChild();
274
2 75 // получение значения текстового узла agent
276 String agent = agent™ext.getModeValue{);
Практический пример корпоративного приложения
477
278 // получение элемента contentType
279 Element typeElement = { Element )
280 client.getElementsByTagName(
281 "contentType" }.item{ 0 );
282
283 // получение дочернего текстового узла элемента
contentType
284 Text typeText =
285 ( Text ) typeElement.getFirstChild() ;
286
287 // получение значения текстового узла contsntType
288 String type = typeText.getNodeValue();
289
290 // получение элемента XSLPath
291 Element pathElement = ( Element )
292 client.getElementsByTagName(
293 "XSLPath" ).item( 0 );
294
295 // получение дочернего текстового узла элемента XSLPath
296 Text pathText =
297 ( Text ) pathElement.getFirstChildt);
296
299 // получение значения текстового узла XSLPath
300 String path = pathText.getKodeValueO;
301
302 // добавление нового объекта ClientModel со свойствами
303 // userAgent, contentType и XSLPath для данного
элемента client
304 clientList.add{
305 пен ClientModel! agent, type, path ) );
306 1
307
308 } // конец блока try
309
310 // обработка исключения SAXException при разборе XML-документа
311 catch { SAXException saxException ) {
312 saxException.printStackTrace();
313 }
314
315 // перехват исключения ввода/вывода при чтении XML-документа
316 catch ( lOException ioException ) {
317 ioException.printStackTrace () ,-
31B }
319
320 // возврат созданного списка объектов ClientModel
321 return clientList,-
322
323 > // конец метода buildClientList
324
325 // получение объекта ClientModel для аадакиого НТГР-эаголовка
User-Agent
326 private ClientModel getClientModel( String header )
327 {
328 // получение итератора для списка clientList
329 Iterator iterator = clientList.iterator();
330
478
Глава 10
331 // нахождение объекта ClientModel, свойство userAgent которого
332 // является подстрокой указанного HTTP-заголовка User-Agent
333 while ( iterator,hasNext() ) {
334 ClientModel client = ( ClientModel ) iterator.next{),-
335
336 // если данное свойство userAgent обгеиа
337 // ClientMode является подстрокой НТТР-ааголовка
338 // User-Agent, вернуть ссылку на объект ClientModel
339 if (■ header.indexOf( client.getUserAgent() ) > -1 }
340 return client;
341 }
342
343 // возврат объекта ClientModel no умолчанию, если другие
объекты не подходят
344 return new ClientModel(
345 "DEFAULT CLIENT", "text/htnO.", "/XHTML/" );
346
347 } // конец метода getClientModel
348 ) __ __
Рис. 10.1. Базовый класс XMLServlet для сервлетов а приложении Deitel Bookstore
В строке 84 вызывается метод bnildClientList, который прочитывает файл
конфигурации Clients.xml и создает список объектов ClientModel (рис. 10.4).
Экземпляры класса XMLServlet используют эти объекты для того, чтобы определить,
какую таблицу стилей XSL применять для клиента каждого из типов.
Метод getDocumentBuilder (строки 88-107) создает объект Document Builder
для синтаксического анализа и создания XML-документов. Аргумент типа boolean
определяет, должен ли метод getDocumentBuilder создавать синтаксический
анализатор XML, проверяющий документы на допустимость. Класс XMLServlet
сохраняет объект DocumentBuilderFactory в переменной экземпляра (строка 29),
что позволяет избежать необходимости создавать новый объект
DocumentBuilderFactory каждый раз, когда нужно получить объект DocumcntBuilder.
Перегруженный метод getDocumentBuilder (строки 110-113) вызывает метод getDo-
cumentBuilder с аргументом false, чтобы создать синтаксический анализатор
XML, не проверяющий документы на допустимость.
Методы setXSLFileName и getXSLFileName (строки 116-125) представляют
собой методы set и get для свойства XSLFileName класса XMLServlet. Свойство
XMLFileNaine определяет имя XSLT-документа, который преобразует
содержимое сервлета для определенного типа клиента, В приложении Deitel Bookstore для
XSLT-трансформаций, предназначенных для различных типов клиентов,
предусмотрены отдельные каталоги. Например, файл products, xsl в каталоге
/XSLT/XHTML формирует содержимое в формате XHTML, тогда как версия
файла products.xsl в каталоге /XSLT/WML формирует содержимое в формате WML.
Свойство XSLFileName определяет только имя файла (например, pro ducts, xsl);
соответствующий каталог для определенного типа клиента задается в каждом из
объектов ClientModel.
Метод writeXML (строки 130-168) определяет тип клиента, осуществляющего
доступ к сервлету, и вызывает метод transform для выполнения XSLT-трансфор-
мацик. В строках 138-139 осуществляется попытка получить объект ClientModel
из объекта HttpSession. Вели объект HttpSession не содержит объект ClientModel,
в строке 144 извлекается клиентский заголовок User-Agent, который уникально
идентифицирует тип клиента. В строке 145 вызывается метод getClientModel для
получения соответствующего объекта ClientModel для данного типа клиента.
Практический пример корпоративного приложения
479
В строке 146 объект Client Model помещается в объект HttpSession для
последующего использования. В строке 150 из объекта ClientModel извлекается тип
содержимого клиента и осуществляется настройка объекта HitpServletResponse.
В строке 156 формируется полный относительный путь к таблице стилей XSL
путем конкатенации свойства XSLPath объекта ClientModel со свойством XSLFile-
Name сервлета. В строках 159-160 открывается поток ввода InputStream для
XSLT-трансформации. В строке 163 вызывается метод transform для выполнения
преобразования и отправки полученных результатов клиенту.
Метод transform (строки 172-202) выполняет указанную XSLT-транеформа-
цию для заданного XML-документа и записывает ее результат в указанный объект
PrintWriter. В строке 179 создается объект DOMSource для XML-документа.
В строках 182-183 создается объект входного потока StreamSource для XSL-доку-
мента. В строке 159 создается объект выходного потока StreamResult для объекта
PrintWriter, в который объект преобразования Transformer будет записывать
результаты XSLT-трансформации. В строках 189-190 создается объект Transformer
путем вызова метода newTransformer класса TransfonnerFactory. В строке 193
вызывается метод transform класса Transformer для выполнения
XSLT-трансформации над заданным исходным объектом (Source) и записи результатов в
заданный целевой объект (Result).
Метод buildErrorMessage (строки 205—223) представляет собой утилитарный
метод для формирования элемента XML (объект Element), содержащего сообщение
об ошибке. В строке 209 с использованием предоставленного документа document
создается объект error типа Element. В строках 212-213 создается объект
errorMessage типа Element, который будет содержать фактическое сообщение об
ошибке. В строках 189-190 создается узел Text, содержащий текст сообщения об
ошибке, после чего этот узел Text добавляется в объект (элемент XML) error-
Message типа Element. В строке 220 объект (элемент XML) errorMessage
добавляется в объект (элемент XML) crrorElement, а в строке 223 возвращается
законченный объект (элемент XML) error.
Метод buildClientList (строки 227-323) формирует список (List) объектов
ClientModel, прочитывая информацию о клиенте из XML-файла конфигурации
(рис. 10.2). В строках 237-238 из параметра инициализации контекста сервлета
ScrvIetContext извлекается имя файла конфигурации. Администратор
развертывания должен указать значение этого параметра при развертывании приложения.
В строках 244-246 открывается поток ввода InputStream для чтения данных из
файла конфигурации. В строках 249-250 осуществляется синтаксический разбор
XML-файла конфигурации, и в памяти формируется объект Document. В строках
253-254 осуществляется получение списка узлов NodcList XML-элементов client
(объекты типа Element) из документа. Каждый XML-элемент client имеет атрибут
(объект типа Attribute) name и три дочерних элемента — user Agent, contentType
и XSLPath — каждый из которых соответствует свойству объекта ClientModel.
В строках 260-306 формируются объекты ClientModel для каждого элемента
client в файле конфигурации, и эти объекты ClientModel добавляются в список List.
Метод getClientModel (строки 326-347) возвращает объект ClientModel,
содержащий подстроку заголовка User-Agent, уникально идентифицирующую тип
клиента. Например, подстрока Mozilla/4.0 (compatible; MSIE 5) заголовка
User-Agent уникально идентифицирует клиента как браузер Microsoft Internet
Explorer версии 5. В строках 333-341 свойство userAgent каждого из объектов
ClientModel сравнивается с заданным заголовком User-Agent. Если свойство
userAgent является подстрокой заголовка User-Agent, значит, объект ClientModel
соответствует предполагаемому, и в строке 340 он возвращается вызвавшему
процессу. В строках 344-345 формируется обобщенная модель клиента ClientModel
в формате XHTML, используемая по умолчанию, если данный заголовок
User-Agent не соответствует какому-либо другому типу клиента.
480
Глава 10
На рис. 10.2 представлен образец файла конфигурадии, который дает
возможность приложению поддерживать несколько наиболее распространенных типов
клиентов. На рис. 10.3 представлено определение DTD для этого файла конфигурации.
1 <?xml version = "1.0" encoding = "0TF-8"?>
2<!DOCTYPE clients SYSTEM
3 "http://www.deitel.com/advjhtpl/clients.dtd">
4
5<!— Файл конфигурации для клиентов приложения Deitel Bookstore —>
6
7 <clients>
в
Э <!-- Клиент Microsoft Internet Explorer версии 5 —>
10 <client name = "Microsoft Internet Explorer 5">
11 <userAgent>Mozilla/4.0 (compatible; MSIE 5</userAgent>
12 <contentType>text/html</contentType>
13 <XSLPath>/XSLT/XHTML/</XSLPath>
14 </client>
15
16 <!-- Клиент Microsoft Internet Explorer версии б -->
17 <client name = "Microsoft Internet Explorer 6">
15 <userAgent?Mozilla/4.0 (compatible; MSIE 6</userAgent>
19 <contentType>text/html</contentType>
20 <XSLPath>/XSLT/XHTML/</xSLPath>
21 </elient>
22
23 <!— Клиент netscape версии 4. 7x —>
24 <client name = "Netscape 4.7">
25 <userAgent>Mozilla/4.7</userAgent>
26 <contentType>text/html</contentType>
27 <XSLPath>/XSLT/XHTML/</XSLPath>
28 </client>
29
30 <*— Клиент Mozilla/Netscape версии б —>
31 <client name = "Mozilla/Netscape 6">
32 <userAgent>Gecko</userAgent>
33 <contentType>text/html</contentType>
34 <XSLPath>/XSLT/XHTML/</xSLPath>
35 </client>
36
37 <!-- Клиент HML-браузер Phone.com -->
38 <client name = "Openwave SDK Browser">
39 <userAgent>OP.Browaer/4</userAgent>
40 <contentType>text/vnd.wap.wml</contentType>
41 <XSLPath>/XSLT/WML/</XSLPath>
42 </client>
43
44 <!-- Клиент WML-браузер Nokia —>
45 <client name = "Nokia WAP Toolkit Browser">
46 <userAgent>Nokia-WAP</userAgent>
4 7 <contentType>text/vnd.wap.wml</contentType>
48 <XSLPath>/XSLT/WML/</XSLPath>
49 </client>
50
51 <!-- Клиент браузер Pixo iMode -->
52 <client name = "Pixo Browser">
Практический пример корпоративного приложения
481
53 <userAgent>Pixo-Browser</iiserAgent>
54 <contentType>Lext/html</contentType>
55 <XSLPath>/XSLT/cHTML/</XSLPath>
56 </client>
57
58 </clients>
Рис. 10.2. Файл конфигурации, позволяющий осуществлять поддержку клиентов
различного типа
1 <!— clients.dtd —>
2 <!-- DTD для задания типов клиентов приложения Bookstore -->
3
4 <!ELEMENT clients ( client+ )>
5
6<!ELEMENT client { userAgent, contentType, xSLPath )>
7 <'ATTLIST client name CDATA #REQUIRED>
8
9 <1 ELEMENT userAgent ( #PCDATA )>
10 <!ELEMENT contentType ( #PCDATA )>
11 <!ELEMEMT XSLPath { #PCDATA )>
Рис. 10.3. Определение DTD для документа clients.xml
Класс ClientModel (рис. 10.4) представляет конкретный тип клиента. В строках
18-23 определен конструктор ClientModel. В строках 26-59 предоставляются
методы set get для свойств user Agent, contentType и XSLPath.
1 // ClientModel.Java
2 // ClientModel - утилитарный класс для определения надлежащего
3 // типа содержимого Content-Type и пути к XSL-файлам для каждого
4 // из типов клиентов, поддерживаемых приложением Bookstore.
5 package com.deitel.advjhtpl.bookstore.model;
6
ill Набор базовых пакетов Java
8 import j ava.io.*;
9
10 public class ClientModel implements Serializable {
11
12 // Свойства объекта ClientModel
13 private String userAgent;
14 private String contentType;
15 private String XSLPath;
16
17 // Конструктор ClientModel для инициализации объектов данных
18 public ClientModel( String agent, String type, String path )
19 i
20 setUserAgent( agent );
21 setContentType( type );
22 setXSLFath( path );
23 }
24
25 // задание подстроки UserAgent
26 public void setUserAgent{ String agent )
27' (
28 userAgent = agent;
482
Глава 10
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
}
// получение подстроки OserAgent
public String getUserAgent()
return userAgent;
// задание типа содержимого ContentType
public void setContentType( String type )
contentType = type;
/ получение типа, содержимого ContentType
public String getContentType()
return contentType;
// задание пути к таблице стилей XSL
public void setXSLPath( String path )
XSLPath = path;
// получение пути к таблице стилей XSL
public String getXSLPath()
return XSLPath;
Рис. 10.4. Класс Client Mode l для представления поддерживаемых топов клиентов
10.3. Сервлеты, реализующие магазинную тележку
Для реализации Internet-магазинов традиционно используется несколько
моделей электронной коммерции. К таким моделям относятся аукционные сайты,
электронные торги, бартерные сделки и сделки с назначением вашей собственной
цены. В нашем приложении используется известная модель с «магазинной
тележкой», в которой-покупатель имеет возможность просматривать ассортимент
имеющихся товаров и выбирать интересующие его товяры для покупки. Каждый
выбранный товар помещается в виртуальную магазинную тележку. После того как
покупатель закончит отбор товаров, активизируется процесс проверки
информации о платежных средствах покупателя и реквизитов для доставки товара, после
чего транзакция завершается. Обслуживание виртуальной магазинной тележки
является составной частью бизнес-логики, реализованной в EJB-компонентах
приложения.
На рис. 10.5 показан поток клиентских запросов и ответов для нового
покупателя, который заказывает несколько экземпляров книги в своем Web-браузере.
Зайдя на сайт, покупатель попадает на основную страницу Lndcx.html. Затек он
переходит по ссылке, чтобы просмотреть каталог товаров, который генерируется серв-
летом GetAHProductsServlet. После этого покупатель выбирает нужный ему товар
Практический пример корпоративного приложения
483
из каталога, а сервлет GretAllProductsServlet отображает подробные сведения об
этом товаре (в данном случае, к таким сведениям относятся рисунок обложки
книги, авторы книги и ее цена). Покупатель добавляет товар в магазинную тележку
с помощью сервлета AddToCartServlet, а сервлет ViewCartServlct отображает
содержимое магазинной тележки. Покупатель изменяет количество приобретаемого
вида товара с помощью сервлета UpdateCartServlct, и сервлет ViewCartServlet
снова отображает покупателю содержимое магазинной тележки. Далее Покупатель
переходит по ссылке, чтобы зарегистрироваться в качестве нового покупателя,
и заполняет регистрационную форму на странице register.html. Далее покупатель
отправляет форму сервлету RegisterScrvlet, Этот сервлет, в свою очередь,
вызывает сервлет LoginScrvIet, через который пользователь осуществляет вход в
Internet-магазин. Далее покупатель выбирает ссылку, вызывающую сервлет Check-
outServlet. Сервлет CheckontServlet размещает заказ и переадресовывает
покупателя к сервлету VicwOrderServlet, который отображает подробную информацию
о заказе.
О
index.htm
GetAilProductsServlet
GetProductServlet
О
CpdateCartServlet
О)
ViewCartServlet
Regis terServlet
AddToCartServlet
Л
zregieter.html
■О
-о
LoginServlet
Che ckoutSe rvle t
ViewOrderServlet
Рис. 10.5, Поток клиентских запросов и данные, возвращаемые приложением Deitel
Bookstore для клиентов XHTML
10.3.1. Сервлет AddToCartServlet
По мере просмотра покупателями содержимого Internet-магазина, они
добавляют нужные им товары в свои магазинные тележки. Сервлет AddToCartServlet
(рис. 10.6) обрабатывает каждый из запросов покупателя на добавление товара в
магазинную тележку. Класс AddToCartServlet расширяет класс XMLServlet, в
котором реализованы функциональные возможности, общие для всех сервлетов в
приложении Deitel Bookstore, например, инициализация и XSLT-трансформации,
В строке 41 из объекта HttpServletRequest извлекается ISBN-код книги,
которую покупатель хотел бы приобрести. В строках 37-38 из объекта HttpSession
сервлета извлекается ссылка на объект магазинной тележки (ShoppingCart).
Возможно, что у покупателя еще нет магазинной тележки, и в этом случае ссылкой на
484
Глава 10
объект ShoppingCart будет null. При этом в строке 59 с помощью собственного
интерфейса ShoppingCart создается новый объект ShoppingCart, а ссылка на него
сохраняется в объекте сеанса HttpSession сервлета для дальнейшего использования
(строка 62). EJB-компонент ShoppingCart представляет собой сеансовый
компонент с состоянием и будет, следовательно, сохранять содержимое магазинной
тележки покупателя в ходе сеанса просмотра.
1 // AddToCartServlet.Java
2 // Сервлет AddToCartServlet добавляет товар в магазинную
3 // тележку покупателя.
4 package com.deitel.advjhtpl.bookstore.servlets;
5
6 // Набор базовых пакетов Java
7 import Java.io.*;
8
9 // Пакеты расширений Java
10 import javax.servlet.*;
11 import javax.servlet.http.*;
12 import javax.naming.*;
13 import javax.rmi.*;
14 import javax.ejb.*;
15
16 // Пакеты сторонних поставщиков
17 import org.w3c.dom.*;
IS
19 // Пакеты Deitel
20 iroport com.deitel.advjhtpl.bookstore.model.*;
21 import com.deitel.advjhtpl.bookstore.ejb.*;
22 import com.deitel.advjhtpl.bookstore.exceptions.*,-
23
24 public Class AddToCartServlet extends XMLServlet {
25
26 // обработка НТТР-запросоа post
27 public void doPost( HttpSeivletRequest request,
28 HttpServletResponse response )
29 throws ServletException, lOException
30 {
31 Document document = getDocumentBuilder О . newDocument () ;
32
33 // получение объекта HttpSession для данного пользователя
34 HttpSession session = request,getSession();
35
36 // получение объекта ShoppingCart для покупателя
37 ShoppingCart ShoppingCart =
38 ( ShoppingCart ) session.getAttribute( "cart" );
39
40 // получение параметра ISBN из объекта запроса
41 String isbn = request.getParameter{ "ISBN" );
42
43 // получение объекта ShoppingCart и добавление приобретаемого
товара в тележку
44 try {
45 InitialContext context = new InitialContext();
46
47 // создать облеки ShoppingCart, если у покупателя «""° нет тележкгс
48 if ( ShoppingCart = null ) {
Практический пример корпоративного приложения
485
49 Object object = context.lookup(
50 "java:comp/env/ejb/ShoppingCart" );
51
52 // приведение ссыпки типа Object к типу ShoppingCartHome
53 ShoppingCartHome shoppingCarfcHome =
54 ( Shopping-Car tHome )
55 PortableRemoteObj ect.narrow(
56 object, ShoppingCartHome.class );
57
58 // создание объекта ShoppingCart с помощью интерфейса
SftoppingCartHoine
59 ShoppingCart = ShoppingCartHome.create();
60
61 // сохранение объекта ShoppingCart в данных сеанса
62 session.setAttribute( "cart", ShoppingCart );
63 }
64
65 // добавление товара в магазинную тел«жку покупателя
66 ShoppingCart.addProduct( isbn );
67
68 // переадресация покупателя к сервлету
69 // ViewCartServlet для просмотра содержимого тележки
70 response.sendRedirect( "ViewCart" );
71
72 } II конец блока try
73
74 // обработка исключения при поиске EJB-компонента ShoppingCart
75 catch ( NamingException namingException ) {
76 namingException.printStacfcTrасе () ;
77
78 Stxing error = "The ShoppingCart EJB was not " +•
79 "found in the JNDI directory.";
80
81 // добавление в XML-документ сообщении об ошибке
82 document.appendChiId( buildErrorMessage(
83 document, error ) );
84
85 writeXML( request, response, document );
86 }
87
88 // обработка исключения при создании EJB-компонента
ShoppingCart
89 catch ( CreateException CreateException ) {
90 CreateException.printstackTrace();
91
92 String error = "ShoppingCart could not be created";
93
94 // добавление в XML-документ сообщения об ошибке
95 document.appendChild( buildErrorMessage(
96 document, error ) );
97
98 writeXML( request, response, document ) ;
99 }
100
101 // обработка исключения, если товар не найден
102 catch ( ProductHotFoundException productException ) {
486
Глава 10
103 productException.printStackTrace();
104
105 // добавление в XML-документ сообщения об ошибке
106 document.appendChild( buildErrorMessage(
107 document, productExeeption.getMessage() ) );
108
10Э writeXMM request, response, document );
110 ]
111
112 \ 11 конец метода doGet
113 } „__ _ .^___
Рис. 10.6. Се pa лет AddToCartServiet д/m добавдения товаров в магазинную тележку
Вели сервлет получает допустимую ссылку на объект ShoppingCart, в строке 60
вызывается бизнес-метод addProduct класса ShoppingCart с указанием ISBN-кода
книги в качестве аргумента. В строке 70 вызывается метод sendRedirect класса
HttpServletResponse для переадресации клиента к сервлегу ViewCartServlet,
который отображает список книг, имеющихся в магазинной тележке.
В приложении предусмотрено три контролируемых исключения, которые
могут быть возбуждены из блока try в строках 44-72. В строках 75-86
перехватывается исключение NamingException, если EJB-компонент ShoppingCart не найден
в каталоге JNDI, В строках 89-99 перехватывается исключение CreateException,
если при создании магазинной тележки покупателя имела место ошибка. В
строках 102-110 перехватывается исключение ProdoctNotFoxradException,
представляющее собой специфичное для приложения исключение, которое будет подробно
рассматриваться в главе 11. Исключение ProductNotFoundException указывает,
что книга с заданным ISBN-кодом не может быть найдена в базе данных.
Каждый из обработчиков исключений использует метод buildErrorMessage
класса XMLServlet для создания сообщения об ошибке в виде XML-кода и
отправки его клиенту. В каждом случае предоставляется сообщение, информирующее
пользователя об ошибке, и выводится трасса стека для исключения, чтобы оказать
содействие разработчику при отладке приложения. При вызове метода writeXML
класса XMLServlet (строки 85, 95 и 109) содержимое отправляется клиенту.
10.3.2. Сервлет ViewCartServlet
После того как покупатель добавил товар в магазинную тележку, сервлет
ViewCartServlet (рис. 10.7) отображает содержимое тележки. Сервлет
ViewCartServlet является подклассом класса XMLServlet.
1 // ViewCartServlet.Java
2 // Сервлет ViewCartServlet отображает содержимое
3 / / магазинной тележке покупателя.
4 package com,deitel.advjhtpl.bookstore,servlets;
5
6 // Набор базовых пакетов Java
7 import java.io.*;
8 import Java.util.*;
9 import Java.text.*;
10
11 // пакеты расширений Java
12 import javax.aervlet.*;
13 import javax.servlet.http.*;
14 impost javax.rmi.*;
Практический пример корпоративного приложения
487
15
16 // Пакеты сторонних поставщиков
17 import org.w3c.dom.*;
18
19 // Пакеты Deitel
20 import com.deitel.advjhtpl.bookstore.model.*;
21 import com.deitel.advjhtpl.bookstore.ejb.*;
22
23 public class ViewCartServlet extends XMLServlet {
24
25 // обработка HTTP-запросов get
26 public void doGet( HttpServletRequest request,
27 HttpServletResponse response )
28 throws ServletException, IOException
29 {
30 Document document = getDocumentBuilder(),newDocument();
31 HttpSession session = request.getSession(J;
32
33 // получение из' данных сеанса объекта StioppingCart для покупателя
34 ShoppingCart shoppingCart =
35 ( ShoppingCart ) session.gfttAttribute( "cart" );
36
37 // формирование XML-документа, описывакнцего содержимое
магазинной тележки
38 if ( shoppingCart T= null ) {
39
40 // создание элемента cart в XML-документе
41 Element root = { Element ) document.appendChild(
42 document.createElement( "cart" } };
43
44 // получение общей стоимости товаров в магазинной тележке
45 double total = shoppingCart.getTotal();
46
47 // создание формата NumberFormat для местных денежных единиц
4 В WuiriberFormat priceFormatter- =
49 NumberFormat.getCurrencylnstance0;
50
51 // Форматирование стоимости товаров в тележке и
52 // добавление этого числа В качестве атрибута элемента cart
53 root.setAttribute{ "total",
54 priceFormatter.format( total ) );
55
56 // получение содержимого магазинной тележки
57 Iterator orderProducts =
58 shoppingCart.getContents().iterator();
59
60 // добавление элемента для из каждого товаров в
61 // тележке в XML-документ
62 while ( orderProdtscts . hasNext О ) {
63 OrderProductModel orderProductModel =
£4 ( OrderProduetModel ) order-Products . next () ;
65
66 root.appendChild(
67 orderproductModel.getXMM document ) );
68 }
69
488
Глава 10
70 J // конец блока if
71
72 else {
73 String error = "Your ShoppingCart is empty.";
74
75 // добавление в XML-документ сообщения об ошибке
76 document.appendChild( buildErrorMesaage(
77 document, error ) ) ;
78 }
79
80 // запись содержимого клиенту
81 writeXMLf request, response, document );
82
83 } // конец метода doGet
84 ) __
Рис. 10.7. Сервлет ViewCartServlet для просмотра содержимого магазинной тележки
Удаленная ссылка на магазинную тележку покупателя (объект ShoppingCart)
хранится в объекте HttpSession. В строках 34-35 эта удаленная ссылка
извлекается. После проверки, что удаленная ссылка на объект ShoppingCart не есть null,
в строках 41-42 созданием элемента cart начинается построение XML-документа,
который описывает магазинную тележку покупателя. В строке 45 вычисляется
общая стоимость всех товаров в магазинной тележке путем вызова метода getTotal
класса ShoppingCart. В строках 48-49 осуществляется форматирование данных об
общей стоимости с использованием формата NumbcrFormat, после чего
отформатированное значение total добавляется в XML-документ.
В строках 62-68 осуществляется обработка коллекции объектов Order Pro duct-
Model в магазинной тележке. Каждый объект OrderProductModel соответствует
определенному виду товара, имеющемуся в магазинной тележке, и содержит
количество этого товара. В строке 67 вызывается метод getXML класса
OrderProductModel Для получения элемента XML, который описывает данный объект
OrderProductModel и содержит, например, такие сведения, как ISBN-код книги
и ее количество в тележке. В строках 66-67 это описание добавляется в
XML-документ, сформированный сервлетом
Если удаленная ссылка на объект ShoppingCart, хранящаяся в объекте
HttpSession, есть nnll, в строках 72-78 генерируется сообщение об ошибке. В строке 81
осуществляется вызов метода writeXML для преобразования XML-содержимого
с помощью таблицы стилей XSL, после чего содержимое отправляется клиенту.
XSLT-грансформация (таблица стилей XSL), представленная на рис. 10.8,
формирует XIITML-код для документа, сгенерированного сервлетом ViewCartServlet.
В строках 6-8 задаются выходные параметры для трансформации. В строке 10 осу-
щестзляется включение шаблонов из документа error.xsl для трансформации
сообщений об ошибках. В строках 23—27 из внешнего XML-документа загружается
заголовок, содержащий средства навигации. Обратите внимание, что для обращения
к документам error.xsl и navigation.xml используются относительные URI.
Напомним, что класс XMLServlet создает собственный вычислитель URIResolver,
который дает возможность XSL-преобразователю вычислять эти относительные
TJRI. Если не предоставить собственный вычислитель URIResolver, в каждой
XSLT-трансформации пришлось бы указывать полный TJRI для этих документов,
что ограничило бы переносимость приложения. Заголовок со средствами
навигации содержит ссылки для просмотра каталога товаров, создания учетной записи
(счета), просмотра содержимого магазинной тележки и т.д. В строках 42-43
применяется шаблон, который осуществляет заполнение таблицы сведениями о това-
Практический пример корпоративного приложения
489
ре, такими как название книги, автор, ISBN-код, цена и количество. Форма
(элемент form) в строках 51-54 дает возможность покупателю подсчитывать стоимость
сделанных в Internet-магазине покупок и размещать свой заказ. Элемент xsi:temp-
late в строках 61-91 извлекает информацию по каждому из товаров из
XML-документа, сформированного сервлетом ViewCartServlet, и создает строку таблицы.
Элемент form в строках 75-80 дает возможность пользователю удалять товар из
магазинной тележки.
Таблица стилей XSL, представленная на рис. 10.9, формирует cHTML-код для
документа, созданного сервлетом ViewCartServlet. В строках 28-29
осуществляется вывод данных об общей стоимости товаров в тележке. В строках 38-41 для
элементов orderProduct применяется шаблон xsl:template, чтобы сформировать
неупорядоченный список сведений о товаре. Форма form в строках 44-47 дает
возможность покупателю подсчитывать стоимость сделанных в Internet-магазине
покупок и размещать свой заказ. Форма form в строках 64—69 позволяет
покупателю изменять количество для определенного товара в магазинной тележке. Форма
form в строках 72-76 дает возможность покупателю удалять товар из магазинной
тележки.
1 <?х»1 version = "1.0"?>
2
3 <xsl:stylesheet version = "1.0"
4 xmlns:xsl = "http://www.w3.org/1999/xSL/Transform">
5
€ <xal:output method = "xml" omit-xml-declaration = "no"
7 indent = "yes" doctype-system = "DTD/xhtmll-strict.dtd"
8 doctype-public = "-//W3C//DTD XHTML 1.0 Strict//EN"/>
9
10 <xsl:include href = "/XSLT/XHTML/error,xsl"/>
11
12 <xsl:template match = "cart">
13 <htral xmlns = "http://www.w3.org/1999/xhtml"
14 xml:lang = "en" lang = "en">
15
16 <heao>
17 <title>Your Online Shopping Cart</title>
18 <linx rel = "Stylesheet" href = "styles/default.css"/>
19 </head>
20
21 <body>
22
23 <xsl:for-each select =
24 "document( '/XSLT/XHTML/navigation.xml' )">
25
26 <xsl:copy-of select = "."/>
2 7 </xs1:for-each>
28
29 <div class = "header">Your Shopping Cart:</div>
30
31 <table class = "cart">
32 <tr>
33 <th>Title</th>
34 <th>Author(s)</th>
35 <th>ISBN</th>
36 <th>Price</th>
37 <th>Quantity</th>
38 </tr>
490
Глав
39
40 <xsl:apply-templates
41 select = "orderProduct"/>
42
43 </table>
44
45 <p>
46 Your total:
47 <xsl rvalue-:of select = "8total"/>
48 </p>
49
50 <p>
51 <form action = "Checkout" method = "post"?-
52 <input name = "Submit" type = "submit"
53 value = "Check Out"/>
54 </form>
55 </p>
56
57 </body>
58 </ntml>
59 </xsl:template>
60
61 <xsl:template match = "orderProduct">
62 <tr>
63 <td>
64 <a href = "GetProduct?ISBN={product/ISBN}">
65 <xsl: value-of select = "product/title"/x/a>
66 </td>
67
6B <tdxxsl: value-of select = "product/author"/X/td>
69
70 <td><xsl:value-©f select = "product/ISEN"/x/td>
71
72 <tdXxsl: value-of select = "product/price"/x/td>
73
74 <td>
75 <form action = "UpdateCart" method = "post">
76 <input type = "text" size = "2"
77 name = " {product/ISBN}''
78 value = "{quantity}"/>
79 <input type = "submit" value = "Update"/>
80 </form>
81 </td>
82
83 <td align = "center">
84 <form action = "RemoveFromCart" method = "poSt">
85 <input name = "ISBN" type = "hidden"
86 value = "{product/ISBU}"/>
87 <input type = "submit" value = "Rei»ove"/>
88 </form>
89 </td>
90 </tr>
91 </xsl:template>
92 </xsl:stylesheet>
Рис. 10.8. Таблица стилей XSL (XHTML/viewCart.xsl), применяемая сервлетом
ViewCartServlet для XHTML-браузеров (часть I)
Практический пример корпоративного приложения
491
Рис. 10.8. Таблица стилей XSL (XHTML/viewCart.xsl), применяемая сервлетом
ViewCartServlet для XHTML-брауэеров (часть 2)
I <?xml version = "U.0"?>
2
3 <xsl:stylesheet version = "1.0"
4 xmlns:xsl = "http://wwvr.w3.org/1999/XSL/Transform">
5
6 <xsl:output method = "html"
7 omit-xml-declaration « "yes"
8 indent = "yes"
9 doctype-system =
10 "http://www. w3.org/KarJcUp/html-spec/html-spec_too.html"
11 doctype-public = "-//«3C//DTD HTML 2.0//Eft"/>
12
13 <xsl:include href = "/XSLT/eHTKL/error,xsl"/>
14
15 <xsl:template match = "cart">
1G <html>
17
18 <head>
19 <title>Your Online Shopping Cart</title>
20 </head>
21
22 <body>
23 <div class = "haader">
24 Your Shopping Cart:
25 </div>
26
21 <p>
28 Your total:
492
Глава 10
29 <xsl:value-of select = "9total"/>
30 </p>
31
32 <p>Title</p>
33 <p>buthor(s)</p>
34 <p>ISBM</p>
35 <p>Price</p>
36 <p>Quantity</p>
37
38 <ul>
39 <xsl:apply-templates
40 select = "orderProduct"/>
41 </ul>
42
43 <p>
44 <form method = "post" action = "Checkout">
45 <input type = "submit" name = "Checkout"
46 value = "Checkout"/>
47 </form>
48 </p>
49
50 </body>
51
52 </html>
53 </xsl:template>
54
55 <xsl:template match = "orderProduct">
56 <li>
57 <a href = "GetProduct?ISBN={pi:oduct/ISBN} ">
58 <xsl:value-of select = "product/title"/x/a>
59 <br/>
60 <pXxsl:value-of select = "product/author"/></p>
Gl <pXxsl:value-of select = "product/ISBN"/X/p>
62 <pXxsl:value-of select = "product/price"/x/p>
63 <p>
64 <form method = "post" action = "UpdateCart">
65 <input type = "text" size = "2"
66 name = "{product/ISBN}"
67 value = "{quantity}"/>
68 <input type = "submit" value = "Update"/>
69 </fonn>
70 </p>
71 <p>
72 <form method = "post" action = "RemoveFromCart">
73 <input type = "hidden" name = "ISBN"
74 value = "{product/ISBN}"/>
75 <input type = "submit" value = "Remove"/>
76 </form>
77 </p>
78 <br/>
79 </Li>
80 </xsl:template>
81 </xsl:atylesheet>
Рис. 10.9. Таблица стилей XSL (cHTML/viewCartxsl), применяемая сервлетом
ViewCartServlet для браузеров i-modp (Изображение публикуется с разрешения
компании Р1ко, Inc.) (часть 1)
Практический пример корпоративного приложения
493
Piko Internet MJtrobrwm**^
я^Ш^» "*
*j
PIXO
Internet Mfcrobrowser 2.1
Рис. 10.9. Таблица сталей XSL (cHTML/viewCart.xs!), применяемая сервлетом
ViewCartServlet для браузеров i-moce. (Изображение публикуется с разрешения
компании Pixo, Inc.) (часть 2)
XSLT-трансформация (таблица стилей XSL), представленная на рис. 10.10,
формирует WML-код для сервлета VicwCartServlct. Элемент do в строках 21-23 дает
пользователю возможность подсчитывать стоимость сделанных в Internet-магазине
покупок и размещать свой заказ. В строках 25-49 осуществляется разметка списка
товаров, содержащихся в магазинной тележке. Элемент card в строках 66-81
предоставляет интерфейс для изменения количества определенного товара в
магазинной тележке. [Замечание. Чтобы загрузить WML-браузер Nokia WAP Toolkit,
пожалуйста, посетите страницу www.nokia.com/corporate/wap/dowiiloads.html.]
1
2
3
4
5
б
7
8
9
10
11
12
13
14
15
16
17
<?xml version = "1.0"?>
<xsl:stylesheet version = "1.0"
xmlns:xsl = "http://wwh.w3.org/1999/XSL/Transform">
<xsl:output method = "xml" omit-xml-declaration = "no"
doctype-sys tem = "bttp://www.wapforum.org/DTD/vrml_l.1.xml"
doctype-public * "-//WAPFOROM//DTD WML l.l//EN"/>
<xsl:include href = "/XSLT/WML/error.xsl"/>
<xsi:template match = "cart">
<wml>
<card title = "Shopping Cart">
<do type — "prev">
<prev/>
494
Глава 10
IB </do>
19
20 <do type = "accept" label = "Check Out">
21 <go href = "ChecJcout" method = "post"/>
22 </do>
23
24 <pXem>Shopping Cart</emx/p>
25
26 <p>
27 Your total: $<xsl: value-of select = "@fcotal"/>
28
29 <table columns = "2">
30 <tr>
31 <td>Tifcle</td>
32 <td>Price</td>
33 </tr>
34
35 <xsl:£or-each select = "orderProduct">
36 <tr>
37 <td>
38 <a href = "«ISBlUproduct/ISBHJ'^
39 <xsl:value-of select = "product/title"/>
40 </a>
41 </td>
42 <td>
43 $<xsl:value~o£ select = "product/price"/>
44 </td>
45 </tr>
46 </xsl:for-each>
47
48 </table>
49 </p>
50
51 </card>
52
53 <xsl:for-each select = "orderProduct" >
54 <card id = "ISBN{product/ISBN}">
55 <do label = "OK" type = "prev"Xprev/x/do>
56 <do label = "Change Quant" type = "options">
57 <go href = "#quant{product/ISBN)"/>
58 </do>
59 <pxxsl: value-of select = "product/title"/X/p>
60 <p>Quantity: <xsl:value-of select = "quantity"/X/p>
61 <pXxsl:value-of select = "product/author"/X/p>
62 <pXxsl:value-of select = "product/ISBN"/x/p>
63 </card>
64
65 <card id = "quant{product/lSBN}">
66 <p>Enter new quantity
67 <input name = "quantity" emptyok = "false"
68 type = "text" format = "*n"/>
69 </p>
70
71 <do type = "accept" label = "Update Quentity">
72 <go href =» "UpdateCart" method = "poSt">
73 <postfield name = "{product/ISBN}"
Практический пример корпоративного приложения
495
1А
75
76
77
78
79
ВО
81
82
ВЗ
84
</до>
</do>
«Зо type
</card>
</xsl:for-each>
</wml>
</xsl:template>
value = "$quantity"/>
«3o type = "prev" label = ' "Cancel "Xprev/X/do>
85 </xsl:stylesheat>
^tf^t,- //.', "^
Рис. 10,10. Таблица стилей XSL (WML/viewCart.xsl), применяемая сервлетом
ViewCartServlet для WML-браузеров (Использовано изображение Image© 2001 Nokia
Mobile Phones)
10.3.3. Сервлет RemoveFromCartServlet
После того как покупатель добавил товар в магазинную тележку, он может
захотеть удалить этот товар из нее. Сервлет RemoveFromCartServlet (рис. 10.11)
обрабатывает запросы на удаление товаров из магазинных тележек. В строке 34 из объекта
HttpSession извлекается ISBN-код книги, которую покупатель хотел бы удалить из
тележки. В строках 39-40 из объекта HttpSession извлекается удаленная ссылка на
объект ShoppingCart. После проверки, что ссылка на ShoppingCart не есть null,
в строке 44 вызывается метод rcmoveProduct объекта ShoppingCart, которому в
качестве параметра передается ISBN-код книги. В строке 48 клиент
переадресовывается к сервлету ViewCartServlet, который отображает результаты удаления товара из
магазинной тележки. Метод removeProduct возбуждает исключение ProductNot-
FoundExeeption, если товар с указанным ISBN-кодом не найден а магазинной
тележке. В строках 53-61 это исключение перехватывается, и генерируется
сообщение об ошибке, информирующее пользователя о возникшей проблеме.
Д96
Глава 10
1 // RemoveFroraCartServlet.Java
2 // Сервлет RemoveFromCartServlet удаляет товар иэ
3 // магазинной тележки покупателя.
4 package com.deitel.advjhtpl.bookstore.servlets;
5
6 // Набор базовых пакетов Java
7 import java. io. * ,-
В
9 // Пакеты расширений Java
10 import javax.servlet.*;
11 import javax.servlet.http.*;
12
13 // Пакеты сторонних поставщиков
14 import org.w3c.dom.*;
15
16 // Пакеты Deitel
17 import com.deitel.advjhtpl.bookstore.model.*;
18 import com.deitel.advjhtpl.bookstore.ejb.*;
19 import com.deitel.advjhtpl .bookstore.exceptions . *,-
20
21 public class RemoveFromCartServlet extends XMLServlet {
22
23 // обработка HTTP-запросов post
24 public void doPost{ HttpServletRequest request,
25 HttpServletResponse response )
26 throws servletException, IOExeeption
27 (
2B Document document = getDocumentBuilder().newDocument();
29
30 // удаление товара иэ магазинной тележки
31 try {
32
33 // получение ISBN-кода удаляемой книги
34 String isbn = request.getParameter( "ISBN" >;
35
36 // получение объекта ShoppingCart покупателя иэ данных сеанса
37 HttpSessioD session = request.getSession() ;
38
39 ShoppingCart shoppingCart =
40 ( ShoppingCart ) session.getAttribute( "cart" );
41
42 // если ShoppingCart ме есть null, удалить товар
43 if ( shoppingCart != null )
44 shoppingCart.removeProduct( isbn );
45
46 // переадресация покупателя к сервлету ViewCartServle
47 // для просмотра содержимого магазинной тележки
48 response.sendRedirect< "ViewCart" };
49
50 } // конец блока try
51
52 // обработка исключения, если товар не найден в тележке
53 catch { ProductNotFoundExoepti<m productException ) {
54 productException.printStackTrace();
55
56 // добавление в ХИОдокумек-г сообщения об ошибке
Практический пример корпоративного приложения
497
57 document.appendChild{ buildErrorMessage(
58 document, productException.getMessage0 ) )1
59
60 writeXML( request, response, document );
61 }
62
63 ) // конец метода doGet
64 }
Рис. 10.11. Сррвлрт RemoveFromCartServlet для удаления товароз из магазинной трлржки
10.3.4. Сервлет UpdateCartServlet
Каждый товар в магазинной тележке имеется в определенном количестве.
Сервлет UpdateCartServlet (рис. 10.12) дает возможность покупателям изменять
количества товаров в своих тележках. В строках 37-38 из объекта HttpSession
извлекается объект магазинной тележки пользователя ShoppingCart. В строке 44 из
объекта HttpSession извлекаются имена параметров в виде списка-перечисления
(Enumeration). Каждое имя параметра представляет собой ISBN-код книги в
магазинной тележке покупателя. Значением каждого из параметров является
желаемое количество товара в магазинной тележке. Для каждого имеющегося в тележке
товара в строках 50—51 из параметра request извлекается новое количество new-
Quantity этого товара. В строках 55-56 задаются количества каждого из товаров
в магазинной тележке путем вызова метода setProductQuantity объекта
ShoppingCart. После обновления количества всех товаров, в строке 61 осуществляется
переадресация клиента к ссрслсту ViewCartServlet для отображения обновленного со
держимого магазинной тележки.
1 // UpdateCartServlet.Java
2 // Сервлет UpdateCartServlet обновляет количества
3 // заданного товара в магазинной тележке покупателя.
4 package com.deitel.advjhtpl.bookstore.servlets;
5
6 // Набор базовых пакетов Java
7 import java.io.*;
8 import java.util.* ,-
9
10 // Пакеты расширений Java
11 import javax.servlet.*;
12 import javax.servlet.http.*;
13
14 // Пакеты сторонних поставщиков
15 import org/-w3c. dom. *;
16
17 // Пакеты Deitel
18 import com.deitel.advjhtpl.bookstore.model.*;
19 import com.deitel.advjhtpl.bookstore.ejb.*;
20 import com.deitel.advjhtpl.bookstore.exceptions.*;
21
22 publio class UpdateCartServlet extends XMLServlet {
23
24 // обработка НТТР-запросов post
25 public void doPost( HttpServletRequest request,
26 HttpServletResponse response )
27 throws ServletException, IOException
498
Глава 10
28 {
29 Document document = getDocumentBuilder().newDocument{);
30
31 // изменение количества данного товара в магазинной тележке
32 try {
33
34 // получение объекта ShoppingCart для покупателя
из данных сеанса
35 HttpSession session = request,getSession( false };
36
37 ShoppingCart shoppingCart = { ShoppingCart )
38 session.getAttribute( "cart" );
39
40 // получение перечислимого списка имен параметров
41 Enumeration parameters = request.getParameterNames();
42
43 // обновление количества книг для каждого значения
параметра ISBN
44 while ( parameters. hasMoreElemctits() ) {
45
46 // получение ISBN-кеда книги, количество которой
будет обновлено
47 String ISBN = ( String ) parameters.nextElement();
48
49 // получение нового количества для книги
50 int. newuuaiitity = Integer .parselnt(
51 request.getParameter( ISBN ) );
52
53 // установка количества для книги с заданным
54 // ISBN-кодом
55 shoppingCart.setProduetQuantity( ISBH,
56 newQuantity );
57 }
58
59 // переадресация покупателя к сервлету ViewCartServlet
60 // для просмотра содержимого магазинной тележки
61 response.sendRedirect{ "ViewCart" );
62
63 ) // конец блока try
64
65 // обработка исключения, если товар не найден а тележке
66 catch { ProductNotFoundException productException ) {
67 productException.printStackTrace();
6B
69 document.appendChild( buildErrorMessage(
70 document, productException.getMessage{) ) );
71
72 writeXML( request, response, document );
73 }
74
75 } // коней метода doGet
76 }
Рис. 10.12. Сервлет UpdateCartServlet для изменения количества товаров в магазинной
тележке
Практический пример корпоративного приложения
499
Метод setProductQuautity возбуждает исключение ProdnctNotFoundExcep-
tion, если товар с указанным ISBN-кодом не найден в магазинной тележке. В
строках 66-73 это исключение обрабатывается, и с использованием метода buildError-
Message генерируется XML-сообщение об ошибке. Метод writeXML отправляет
сообщение об ошибке клиенту {строка 72).
10.3.5. Сервлет CheckoutServlet
После того как покупатель закончил просмотр содержимого Internet-магазина
и добавил товары в магазинную тележку, заказ покупателя окончательно
обрабатывается с помощью сервлета CheckoutServlet (рис. 10.13).
Чтобы иметь возможность пройти кассовый контроль, покупатель при
посещении Internet-магазина должен зарегистрироваться. В строках 32—33 из объекта
HttpSession извлекается идентификатор пользователя userlD. После этого в
строках 39—40 из объекта сеанса session извлекается удаленная ссылка на объект
магазинной тележки ShoppingCart пользователя. После проверки, что ни Shopping-
Cart, ни userlD не есть null, в строке 51 вызывается метод checkout объекта
ShoppingCart для окончательного размещения заказа. Метод checkout возвращает
удаленную ссылку на экземпляр EJB-компонента Order, который представляет
заказ покупателя. В строке 54 путем вызова метода getPrimaryKey извлекается
идеитлфикагор заказа oruerlD. В строках 57—58 осуществляется переадресация
клиента к сервлету ViewOrderServlet, который отображает подробные сведения
о размещенном заказе.
Если идентификатор userlD есть null, в строках 60-70 генерируется сообщение
об ошибке, указывающее, что покупатель не зарегистрировался. В строках 75-83
перехватывается исключение Product Not Found Exception, которое метод checkout
возбуждает, если магазинная тележка пуста (т.е. не содержит товаров).
1 // CheckoutServlet.Java
2 // Сервлет CheckoutServlet дает возможность покупателю пройти
3 // контроль и оформить покупку товаров, содержащихся в магаэ. тележке.
4 package com.deitel.advjhtpl.bookstore.servlets;
5
6 // Набор базовых пакетов Java
7 import java.io.*;
8
9 // Пакеты расширений Java
10 import javax.servlet.*;
11 import javax,servlet.http.*;
12
13 // Пакеты сторонних поставщиков
li import org.w3cdom.*;
15
16 // Пакеты Deitel
17 import com.deitel.advjhtpl.bookstore.model.*;
18 import com.deitel.advjhtpl.bookstore.ejb.*;
19 import com.deitel.advjhtpl.bookstore.exceptions.*;
20
21 public class CheckoutServlet extends XMLServlet {
22
23 // обработка HTTP-запросов post
24 public void doPost( HttpServletRequest request,
25 HttpServletResponse response )
26 throws ServletException, lOException
27 (
500
Глава 10
28 Document document = getDocumentBuilder().newDocument();
29 HttpSeesion session = request,getSessian(J ;
30
31 // получение идентификатора покупателя userlD из данных сеанса
32 String userlD = ( String )
33 session.getAttrihute( "userlD" );
34
35 // получение объекта ShoppingCart и подсчет стоимости покупок
36 try {
37
38 // получение объекта ShoppingCart из данных сеанса
39 ShoppingCart shoppingCart = { ShoppingCart )
40 session.getAttribute( "cart" );
41
42 // проверка, имеет ли покупатель магазинную тележку
43 if ( shoppingCart = null )
44 throw new ProductNotFoundException{ "Your " +
45 "ShoppingCart is empty." J;
46
47 // проверка, что идентификатор user/ID не ест*, null и не пуст
48 if ( !( userlD = null I I userlD.equals( "" ) ) ) {
49
50 // вызов метода checkout для размещения заказа
51 Order order = shoppingCart.checkout( userlD );
52
53 11 получение идентификатора заказа, orderID для покупателя
54 Integer orderlD = ( Integer ) order.getPrimaryKey0;
55
56 // переход к сервлеву ViewOrder для отображения
данных заказа
57 response.sendRedirect( "ViewOrder?orderID=" +
58 orderID J ;
59 }
60 else {
61 // если userlD есть null, указать, что
62 // покупатель не был допущен в магазин
63 String error = "you are not logged in.";
64
65 // добавление в XML-документ сообщения об ошибке
66 document.appendChiId( buildErrorMessage( document,
67 error ) ) ;
68
69 writeXML( request, response, document };
70 1
71
72 } // конец блока try
73
74 // обработка исключения, если товар не найден в тележке
75 catch { ProductNotFoundException productException ) {
76 productExceptiorv.printStackTraceO ;
77
73 // добавление в XML-документ сообщения об ошибке
79 document.appendChiId( buildErrorMessage(
SO document, prodUctException.getMessage 0 ) )»'
81
82 writeXMH request, response, document );
Практический пример корпоративного приложения
S01
83 }
84
85 ) // конец метода doGet
86 >
щзтшЕшшвшшт
1Й?!
] *Ыр:ЦЫН>Л №X/t£otACTrrA»viOdet"V[SeT][>Z7
3.
'■j^B'fflft?]
OrderlO: 27 Order Date: Aug 18, 200111:21:00 AM
Title
:«^iS?;5wftipiaifafm
Author
ISBN Quanlity Price
rS^:^l.:.^: -
^ЖгДЩ?;
'"Г:ЖШШШ?|12>ё
PIXO
/fltwnet Mlcrebrowsei 2.1
Рис. 10.13. Сервлет CheckoutServlet для размещения заказов (Изображения
публикуются с разрешения компаний Pixo, Inc. и © 2001 Nokia Mobile Phones)
10.4. Сервлеты, обслуживающие каталог товаров
Сервлеты GretAllProductsServlet и ProductSearchServIet обслуживают каталог
товаров, имеющихся в Internet-магазине. Эти сервлеты извлекают список товаров
и представляют его покупателю. Сервлет GetProductServlet отображает
подробные сведения о заданном товаре.
502
Глава 10
10.4.1. Сервлет GetAIIProductsServlet
Сервлет GetAIIProductsServlet (рис. 10.14) предоставляет список товаров,
имеющихся в нашем Iiiternet-магазиые. В строках 35-44 извлекается удаленная
ссылка на EJB-компонент Product, который представляет один из товаров в
Internet-магазине. Метод findAllProdiicts интерфейса ProductHome возвращает
коллекцию EJB-компонентов Product, каждый из которых представляет один товар
(строки 47-48). В строках 58-70 обрабатывается коллекция товаров для
формирования XML-документа, который содержит информацию о каждом из товаров.
В строках 64—65 осуществляется получение объекта ProductModel для каждого
товара, а в строках 68-69 извлекается XML-описание каждого из товаров.
1 // GetAIIProductsServlet.Java
2 // Сервлет GetAIIProductsServlet извлекает список всех товаров,
3 // ямеящихся в магазине, и отображает этот список клиенту.
4 package com.deitel.advjhtpl.bookstore.servlets;
5
6 // Набор базовых пакетов Java
7 import java.io.*;
8 import java.util.*;
9
10 // Пакеты расширений Java
11 import j avax,servlet.*;
12 import javax.servlet.http.*;
13 import j avax. rroi.*;
14 import j avax.naming.*;
15 import javax.ejb.*;
16
17 // Пакеты сторонних поставщиков
18 import org.w3c.dom.*;
19
20 // Пакеты Deitel
21 import com.deitel.advjhtpl.bookstore.model.*;
22 import com.deitel.advjhtpl.bookstore.ejb.*;
23
24 public class GetAIIProductsServlet extends XMLServlet {
25
26 // обработка HTTP-запросов get
27 public void doGet( HttpServletRequest request,
28 HttpServletResponse response )
29 throws ServletException, IOException
30 {
31 Document document = getDocumentBuilder{).newDocument();
32
33 // формирование каталога товаров
34 try {
35 InitialContext context = new InitialContext();
36
37 // поиск EJB-компонента Product
38 Object object =
39 context.lookup{ "java:comp/env/ejb/Product" );
40
41 // получение интерфейса ProductHome для нахождения
всех товаров
42 ProductHome productHome = ( ProductHome )
43 PortableRemoteObject.narrow( object.
Практический пример корпоративного приложения
503
44 ProductHome.class );
45
46 // получение итератора для списка товаров
47 Iterator products =
48 productHome.findAllProducts{).iterator(};
49
50 // создание корня XML-документа
51 Element rootElement =
52 document.createElement( "catalog" );
53
54 // добавление элемента catalog в XML-документ
55 document,appendChild( rootElement );
56
57 // добавление каждого из товаров в XML-документ
58 while ( products .hasNextO ) {
59 Product product = { Product )
60 PortableRemoteObjeet.narrow( products.next(),
61 Product.class );
62
63 // получение объекта ProductModel для текущего товара
64 ProductModel productModel =
65 produo t.ge tProdu etModel();
66
67 // добавление в документ XML-элемента для товара
68 rootElement.appendChiId(
69 productModel.getXML( document ) ) ,■
70 }
71
72 } // конец блока try
73
74 // обработка исключения при поиске EJB-компонента Product
75 catch ( NamingException namingException ) {
76 namingException.printStackTraceО;
77
78 String error = "The Product EJB was not found in " +
79 "the JNDI directory.";
80
81 // добавление в XML-документ сообщения об ошибке
82 document.appendChildf buildErrorMessage(
83 document, error ) );
84 }
85
86 // обработка исключения, если товар не может быть найден
87 catch ( FinderException finderException ) {
88 finderException.printStackTrace();
89
90 String error = "No Products found in the etore.";
91
92 // добавление в XML-документ сообщения об ошибке
93 document.appendChild{ buildErrorMessage(
94 document, error ) );
95 }
96
97 // гарантированная запись содержимого клиенту
98 finally {
99 writeXML( request, response, document );
504
Глава 10
100 }
101
102 } // конец метода doGefc
103 }
=._ шшж
DeiteI
^Assocraits Inc.
. Saar^l
Title
t**ijo,ttJii'?wra"-
Aurtior Price
Del*. tiotaK' 3&31
S»*j" .< pSfS
ДОЩвм'гы
Celfrl Dstel a Mela
i
1
Perl Шуд HM-am:
Tbe Carp^cit- Tr?ri ry; caris
'«]
MfSlBOatEjl
teita $ Оёяг| .".
MM, DMA wm> Zt&L
aw*
d
-=Ёйй
PIXO
IIM9W2.1 M
(SBmS
Рис. 10.14. Сервлет GetAllProductsServlet для просмотра каталога товаров
{Изображения публикуются с разрешения компаний Pjxo, Inc и © 20СГ Nokia Mobile
Phones)
Практический пример корпоративного приложения
505
Метод lookup (строка 39) возбуждает исключение NamingException, если
EJB-компонент Product не может быть найден в каталоге JNDI. Соответствующий
блок catch (строки 75—84) формирует XML-сообщение об ошибке, указывающее,
что поиск EJB-компонента Product сервисом JNDI окончился неудачей. Метод
findAHProducts (строка 48) возбуждает исключение FinderException, если
никаких компонентов Product не было найдено. В строках 87—95 это исключение
перехватывается, и формируется XML-сообщение об ошибке, указывающее, что в базе
данных не было найдено соответствующих товаров. В блоке finally (строки
98-100) содержимое отправляется клиенту с помощью метода writeXML.
10.4.2. С ер влет GetProductServlet
Сервлет GetAllProductsServlet представляет список всех товаров (объектов
Product), имеющихся в магазине. Чтобы получить более подробные сведения для
данного товара, покупатель вызывает сервлег GetProductServlet (рис. 10.15). Этот
сервлет использует бизнес-логику приложения для извлечения подробной
информации о данном товаре и отображения ее покупателю.
1 // GetProductServlet.Java
2 // Сервлет GetProductServlet извлекает сведения о товаре и
3 // отображает ик покупателю.
4 package com.deitel.advjhtpl.bookstore.servlets;
5
6 // Набор базовых пакетов Java
7 import java.io.*;
8
9 // Пакеты расширений Java
10 import javax.servlet.*;
11 import javax.servlet.http.*;
12 import javax.naming.*;
13 import javax.ejb.*;
14 import j avax.rmi.*;
15
16 // Пакеты сторонних поставщиков
17 import org.w3c.dom.*;
18
19 // Пакеты Deitel
20 import com.deitel.advjhtpl.bookstore.model.*;
21 import com.deitel.advjhtpl.bookstore.ejb.*;
22
23 public class GetProductServlet extends XMLServlet {
24
25 public void doGet( HttpServletRequest request,
26 HttpServletResponse response )
27 throws ServletException, IOException
28 {
29 Document document = getDocumentBuilder().newDocument();
30
31 // получение ISBN-кода иэ объекта запроса
32 String isbn = request.getParameter( "ISBN" };
33
34 // генерирование XML-документа, содержащего подробные
сведения о товаре
35 try (
36 InitialContext context = new initialContextO;
506
Глава 10
37
38 // поиск EJB-компонента Product
39 Object object =
4 0 context. lookup ( "Java: comp/env/e jb/Product" ) ;
41
42 // получение интерфейса ProductHome для поиска товара
43 ProductHome productHome - { ProductHome )
44 PortableRemoteObject.narrow(
45 object, ProductHome,class );
46
47 // нахождение книги с заданным ISBN-кодом
48 Product product =
49 productHome,findByPrimaryKey( isbn J;
50
51 // создание корневого элемента XML-документа
52 Kode rootNode =
53 document.createElement( "bookstore" );
54
55 // добавление корневого элемента в XML-документ
56 document.appendChiId( rootNode );
57
58 // получение сведений о товаре в виде объекта ProductModel
59 ProductModel productModel =
60 product.getProductKodel();
61
62 // построение XML-документа, содержащего сведения о говере
63 rootNode.appendChiId(
64 productModel.getXML( document ) );
65
66 } // конец блока try
67
68 // обработка исключения при поиске ЕЛВ-компонента Product
69 catch ( NamingException namingException ) {
70 namingException.printStackTrace();
71
72 String error = "The Product EJB was not found in " +
73 "the JNDI directory,";
74
75 document.appendChild( buildErrorMessage(
76 document, error ) );
77 }
78
79 // обработка исключения, если EJB-компонент Product не найден
80 catch ( FinderException finderException ) {
81 finderException.printstackTrace();
82
83 String error = "The Product with ISBN " + isbn +
84 " was not found in our store.";
85
86 document.appendChiId( buildErrorMessage(
87 document, error ) ) ;
88 }
89
90 // гарантированная запись содержимого клиенту
91 finally {
92 writeXML( request, response, document ) ,-
Практический пример корпоративного приложения
507
93 }
94
95 } // конец метода doGet
96 }
ЬШШШШаЯ&ЛШШВЯШЕ'
I^KkEdt: :*п f(Mrt«' Toots Hefc
"31
DeiteL
&AsvDti*its lute.
■
XML How to Program
bvDeitelHDeitolHNietM-fcnG St^dhu
ffi$H
Зжга^^МЯи?*™
igtf№£
cRI
Paajjlg
£*™t$GSSU
1S8W:
0X3294173
рдотДОР
RVittBKll
ffiLKJHtrgi*_
PIXO
Internet Mlcn>browser2,1
mwser2,l Я
I i«t 01 ЫМч 173
' JOSS
I TftdiruC- . flack
4«$
'•«*,
■l / f 5+7-fJ i ■ fsfifssi
Рис. 10.15. Сервлет GetProductServlet для просмотра информации о товаре
(Изображения публикуются с разрешения компаний Pixo, inc. и © 2001 Nokia Mobile
Phones)
508
Глава 10
EJB-компонент Product предоставляет подробную информацию по заданному
товару в виде объекта Product Mod el. В строках 39-45 извлекается ссылка на
интерфейс ProductHome. В строках 48-49 осуществляется вызов метода findByPri-
maryKey интерфейса ProductHome для получения EJB-компонента Product
а-заданным ISBN-кодом (ISBN). В строках 59-64 осуществляется получение сведений
о товаре в виде объекта ProductModel, и используется метод getXML для
генерирования XML-содержимого для клиента.
В строках 69-77 перехватывается исключение NamingException, если поиск
(посредством метода lookup) интерфейса ProductHome окончился неудачей.
В строках 80-88 перехватывается исключение FinderException, которое
генерируется при обращении к методу findByPrimaryKey в строке 49, если книга с
заданным ISBN-кодом не найдена в базе данных. В каждом блоке catch создается
XML-сообщение об ошибке, информирующее пользователя о возникновении
проблемы. Блок finally в строках 91-93 оОесиечивает отправку клиенту содержимого
независимо от любых возбуждаемых исключений.
10.4.3. Сервлет ProductSearchServlet
Сервлет ProductSearchServlet (рис. 10.16) осуществляет поиск в базе данных
книг, названия которых содержат определенное ключевое слово. Метод findBy-
Title собственного интерфейса EJB-компонента Product принимает строковый
аргумент и ищет книгу, в названии которой присутствует указанное ключевое слово.
Поскольку при таком поиске может быть найдено множество книг, отвечающих
условию, метод fmdByTitle возвращает коллекцию (Collection) удаленных ссылок
на EJB-компоненты Product.
В строках 34 -35 из объекта запроса извлекается параметр search String, а в
начале и в конце искомой строки searchString помещается универсальный символ
замещения %. Этот символ используется базой данных для нахождения всех
названий книг, которые содержат заданную строку searchString. В строках 50-51
создается объект итератора Iterator для обработки списка книг, возвращенного
в результате поиска. В строках 58-69 в XML-документ добавляются XML-код для
каждой найденной книги.
1// ProductSearchServlet.Java
2 // Сервлет ProductSearchServlet дает возможность покупатели
3 // осуществлять поиск определенного товара в Internet-магазине.
4 package com.deitel.advjhtpl.bookstore.servlets;
5
6 // Набор базовых пакетов Java
7 import java.io.*;
8 import java.util,*;
9
10 // Пакет» расширений Java
11 import javax.aervlet.*;
12 import javax.servlet.http.*;
13 import javax.naming.*;
14 import javax.ejb.*;
15 import javax.rmi.PartableRemoteObject;
16
17 // Пакеты сторонанх поставщиков
18 import org.w3c.dom.*;
19
20 // Пакеты Deitel
21 import com.deitel.advjhtpl.bookstore.model.*;
Практический пример корпоративного приложения
509
22 import com.deitel.advjhtpl.bookstore.ejb.*;
23
24 public class ProductSearchServlet extends XMLServlet {
25
26 // обработка HTTP-запросов get
27 public void doGetf HttpServletRequest request,
28 HttpServletRespor.se response }
29 throws ServletException, IOException
30 {
31 Document document = getDocumentBuilder().newDocument();
32
33 // получение строки поиска searchstring из объекта запроса
34 String searchstring = "%" +
35 request. getParameter( "searchstring" ) + "%",-
36
37 // нахождение товара с использованием EJB-компонента Product
38 try {
39 InitialContext context = new InitialContext{);
40
41 // поиск EJB-коипонента Product
42 Object object =
43 context.lookup( "java:comp/env/ejb/Produet" );
44
45 ProductHome productHome = ( ProductHome )
46 PortableRemoteObject.narrow(
47 object, ProductHome.class );
48
49 // нахождение товаров, соответствующих строке поиска
searchstring
50 Iterator products = productHome.findByTitle(
51 searchstring ).iterator();
52
53 // создание элемента catalog документа
54 Node rootNode = document.appendChiId(
55 document.createElement( "catalog" ) );
56
57 // формирование списка товаров, отвечающих условию поиска
58 while ( products.hasNext() ) {
59 Product product = ( Product )
60 PortableRemoteObject.narrow( products.next(),
61 Product.class );
62
63 ProductModel productHodel = product.getProductModel();
64
65 // добавление в документ XML-элемента для
66 // текущего товара
67 rootNode.appendChild(
68 productModel.getXML{ document ) );
69 }
70
71 } // конец блока try
72
73 // обработка исключения при поиске EJB-компонента Product
74 catch ( NamingException namingException ) {
75 namingException,printStackTraoe();
76
510
Глава 10
77
78
79
80
31
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100 }
String error = "The Product EJB was not found in " +
"the ЛГО1 directory.";
document.appendChild( buildErrorMessage(
document, error ) );
}
// обработка исключения, если EJB-компонент Product не найден
catch ( FinderException finderException ) {
finderException.printStackTrace();
String error = "Mo Products match your search,";
document.appendChild( buildErrorMessage(
document, error ) );
}
// гарантированная запись содержимого клиенту
finally t
writeXML( request, response, document );
]
} // конец метода doGet
уЬ store - Microsoft Internet Explore
| J^^fyk-: -Vfe» Fivontei „ТасЬд^Нф-
l^-^fit^:<^'^a:an0''K'^Mt^^^t^Mch7sMchsl'ir^3»vJ
^ML^inJJil
Г**#^&.ШММ1&р M*°*M&¥&\ 8~д я«a*.
rs-
DeiteI
4Associates Inc.
Product cjtjlog sLrejte Ac count Login
Products
Title
'" * *. ', '3a^Hofr:ta'lS-Qqrarri Доуд 21,
Tha СпгтиЫе 1эуд гтгаШпл Carse
ЩШ'&>(гр№г^з Trying Cwrss 'Лжз
■■■■■"■"■■ ■ uj
Author
DeiteI, DprtsJ &
Deital a oeltel
DeStelSOefel
^^и^иЬйАи^мЛ^иЦкЦ^ь
Search |
Ж
"*-m$mmsffl&zm
4
Рис. 10.16, Сервлет ProductSearchServiet для поиска в каталоге товаров {Изображения
публикуются с разрешения компаний Pixo, Inc. и © 2001 Nokia Mobile Phones) (часть 1)
Практический пример корпоративного приложения
511
Рис. 10.16. Сервлет ProductSearchServlet для поиска в каталоге товаров (Изображения
публикуются с разрешения компаний Pixo, Inc. и © 2001 Nokia Mobile Phones) (часть 2)
10.5. Сервлеты для обслуживания покупателей
Internet-магазины обычно предоставляют покупателям возможность
регистрироваться и указывать сведения о себе (например, имя, адрес e-mail и адрес для
доставки, товара), которые будут храниться и использоваться дли различных целей.
Прежде всего, предоставленная пользователем при регистрации информация
нужна магазину для составления счета и доставки товаров. Регистрация имеет то
преимущество, что пользователю нужно ввести необходимую информации только
один раз. Впоследствии пользователь может повторно посетить магазин и
зарегистрироваться с указанием имени пользователя и пароля, чтобы извлечь ранее
введенную информацию о реквизитах счета и доставки товара. Регистрация
покупателей также дает возможность Internet-магазинам предоставлять покупателям
содержимое в соответствии с их личными предпочтениями, а также отображать
информацию о предыдущих покупках. Некоторые Internet-магазины также могут
использовать введенную при регистрации информацию для направленной
рекламы в зависимости от личных пристрастий потребителей или же в зависимости от
возраста потребителей.
Регистрацией покупателей в приложении Deitel Bookstore управляет сервлет
RegistrationServlet (раздел 10.5.1). После того как покупатель
зарегистрировался, сервлет LoginServlet разрешает покупателю вход па сайт, для чего следует
указать имя пользователя и пароль. Сервлет ViewOrderHistoryServlet дает
возможность покупателю просмотреть информацию о своих предыдущих заказах. Сервлет
GetLostPasswordServlet предоставляет покупателям подсказку, чтобы они могли
вспомнить забытые пароли.
512
Глава 10
10.5.1. Сервлет RegisterServlet
Сервлет RegisterServlet (рис. 10.17) обрабатывает регистрационные формы,
отправленные новыми покупателями. Сервлет создает экземпляр класса Customer-
Model (строка 34) и использует значения параметров, указанные клиентом, чтобы
ввести в модель сведения о покупателях (строки 38-125). После того как объект
CustomerModel заполнен данными, в строке 133 осуществляется поиск
EJB-компонента Customer, который представляет информацию о покупателе в базе
данных. В строках 141-142 осуществляется создание в базе данных новой
регистрационной записи для покупателя путем вызова метода create EJB-компонента
Customer с передачей ему в качестве аргумента вновь созданного объекта
CustomerModel. После регистрации покупателя, в строках 147-157 покупатель
переадресовывается к сервлету LoginServlet для входа в магазин.
1 // RegisterServlet.java
2 // Сервлет RegisterServlet обрабатывает регистрационную
3 // форму, чтобы зарегистрировать нового покупателя.
4 package com.deitel.adv jhtpl.bookstore.servlets;
5
6 // Набор базовых пзхето* Java
7 import java.io.*;
8 import java.util.*;
9
10 // Пакеты расширений Java
11 import javax.eervlet.*;
12 import javax.servlet.http.*;
13 import j avax.ejb.*;
14 impor t j avax.naming.*;
15 import javax.rmi.*;
16
17 // Пакеты сторонних поставщиков
18 import org.w3c.dom.*;
19
20 // Пакеты Deitel
21 import com.deitel.advjhtpl.bookstore.model.*;
22 import com.deitel.advjhtpl.bookstore.ejb.*;
23
24 public class RegisterServlet extends XHLServlet {
25
2 6 // обработка НТТР-эапросов post
27 public void doPost{ HttpServletRequest request,
28 HttpServletResponse response )
29 throws ServletException, IOException
30 t
31 Document document = getDocumentBuilder{).newDocument();
32
33 // создание объекта CustomerModel для хранения регистрационных
данных
34 CustomerModel customerModel = new CustomerModel{);
35
36 // задание свойств объекта CustomerModel с
37 // использованием значений, переданных через объект запроса
38 customerModel.setUserlD( request.getParameter(
39 "userlD" ) );
40
41 customerModel.setPassword( request.getParameter(
Практический пример корпоративного приложения
S13
42 "password" ) );
43
44 customerModel.setPasswordHint( request.getParameter(
45 "passwoz-dHint" ) ) ;
46
47 customerModel.setFixstName( request.getParameter(
4 В "firstName" ) );
49
50 customerModel.setLaetName( request.getParameter(
51 "laBtName" > );
52
53 // задание информации о кредитной карте
54 customerModel.setCreditCardName( request.getParameter(
55 "creditCardName" ) );
56
57 customerModel.setCreditCardNumber( request.getParameter{
58 "creditCardNwnber" ) );
59
60 customerModel.setCreditCardExpirationDate(
61 request.getParameter( "creditCardExpirationDate" ) );
62
63 // создание об'ьехта AddressModel для адреса доставки счета
64 AddressModel billingAddress = new AddressModel();
65
66 billingAddress.setFirstName( request.getParameter(
67 "billingAddressFirstNaate" } };
68
69 billingAddress.setLastHamet request.getParameter(
70 "billingAddressLastName" ) );
71
72 billingAddress.SetStreetAddressLinel(
73 request.getParameter( "billingAddressStreetl" ) );
74
75 billingAddress.setStreetAddressLine2(
76 request.getParameter( "billingAddressStreet2" ) );
77
78 billingAddress.setCityf request.getParameter(
79 "billingAddressCity" ) );
80
81 billingAddress.setState( request.getParameter(
82 "billingAddressState" ) );
83
84 billingAddress.setZipCode( request.getParameter(
85 "billingAddressZipCode" > );
86
B7 billingAddress.setCountry( request.getParameter(
88 "billingAddressCountry" ) );
89
90 billingAddress.setPhoneNumber( request,getParameter(
91 "billingAddressPhoneNumber" ) );
92
93 customerModel.setBillingAddrass( billingAddress );
94
95 // создание объекта AddressModel для адреса доставки товара
96 AddressModel shippingAddress = new AddressModel();
97
98 shippingAddress.setFirstHame( request.getParameter(
514
Глава 10
99 "shippingAddressFirstHame" ) );
100
101 shipping/Address . setLastName ( request. getParameter (
102 "shippingAddressLastNaroe" ) );
103
104 shippingAddress.setStreetAddressLinel(
105 request.getParameter( "shippingAddressStreetl" ) );
106
107 shippingAddress.setStreetAddressLine2(
108 request.getParameter{ "shippingAddressStreet2M ) );
109
110 shippingAddress.setCity{ request.getParameter(
111 "shippingAddressCity" ) 1 ;
112
113 shippingAddress,setState( request.getParameter(
114 "shippingAddressState" ) ) ;
115
116 shippingAddress,setZipCode( request.getParameter(
117 "shippingAddiessZipCode" ) ) ;
118
119 ShippingAddress.setCountry( request.getParameter(
120 "shippingAddressCountry" ) ) ;
121
122 shippingAddress.setPhoneNumber( request.getParameter(
123 "shippingAddressPhoneNumber" ) ) ;
124
125 customerModel.setShippin.gAddre.ss( shippingAddress );
126
127 // поиск EJB-компонента Customer и создание нового покупателя
128 try {
129 InitialContext context = new InitialContext();
130
131 // поиск ЕОВ-хомпонента Customer
132 Object object =
133 context.lookup( "java:comp/env/ejb/Customer" ) ,-
134
135 CustoaterHome customerHome = ( CustomerHome )
136 POrtableRemoteObject.narrow( object,
137 CustomerHome.class );
138
139 // создание нового покупателя на основе о&ьёхта Custamerltodel,
140 // содержащего регистрационную информаяию о покупателе
141 Customer customer =
142 customarHome.create( customerHodel );
143
144 customerModel = customer.getCustomerModel{);
145 // получение объекта RequestDispatcher для сервлета входа
146 RequestDispatcher dispatcher =
147 getServletContextO.getRequestDispateher( "/Login" );
148
149 // задание идентификатора userlD и народа для сервлета входа
150 request.setAttribute( "userID",
151 customerModel.getUserID(} );
152 request.setAttribute( "password",
153 custoroerModel.getPassniordО );
154
155 // переадресания пользователя к сералету LoginServlet
Практический пример корпоративного приложения
515
156 dispatcher.forward( request, response );
157
158 } УУ конец блока try
159
160 // обработка исключения при поиске EJB-компонента Customer
161 catch С NamingException namingException ) {
162 namingException.printstackTrace () ,-
163
164
165 String error = "The Customer EJB was not " +
166 "found in the JNDI directory.";
167
168 document.appendchildf buildErrorMessage(
169 document, error ) );
170
171 writeXML( request, response, document ) ;
172 }
173
174 // обработка исключения при создании нового покупателя
175 catch ( CreateException CreateException ) {
176 CreateException.printstackTrace() ;
177
178 String error = "The Customer could not be created";
179
ISO document.appendChiId( buildErrorMessage(
181 document, error ) ) ;
182
183 writeXML( request, response, document );
184 >
185
186 } // конец метода doFost
187 } __^
Рис. 10.17. Сервлет ReglsterServlet для регистрации новых покупателей
В строках 162-172 перехватывается исключение NamingExeeption, которое
указывает, что интерфейс CustomerHome не может быть найден в каталоге JNDI.
Поскольку сервлет RegisterServlet создает новый объект Customer, в строках
175-184 перехватывается исключение CreateException в случае, если EJB-компо-
нент Customer не может быть создан. Каждый блок catch формирует
XML-сообщение об ошибке с помощью метода buildErrorMessage и вызывает метод writeXML
для отображения сообщения об ошибке пользователю.
10.5.2. Сервлет LoginServlet
Чтобы войти в Internet-магазин, зарегистрированный покупатель должен
указать правильные идентификатор и пароль. Яти предо ста пленные покупателем
данные клиент отправляет сервлету LoginServlet. Сервлет LoginServlet (рис. 10.18)
проверяет идентификатор пользователя userlD и пароль password, сравнивая их
со значениями userlD и password, хранящимися в базе данных.
Сервлет LoginServlet использует EJB-компонент Customer для проверки
идентификатора и пароля, введенных покупателем. В строках 39-44 извлекается
ссылка на интерфейс CustomerHome. В строках 47-48 вызывается метод fmdByLogin
интерфейса CustomerHome, который возвращает удаленную ссылку на
покупатели (EJB-компонект Customer) с идентификатором userlD и паролем password, пре-
516
Глава 10
доставленными пользователем. После того как EJB-компонент Customer найден,
в строках 59-69 формируется XML-документ, который сообщает, что покупатель
допущен в магазин.
1// LoginServlet.Java
2 // Сервлет LoginServlet предоставляет покупателю возможность входа
на сайт.
3 package com.deitel.advjhtpl.bookstore.servlets;
4
5 // Набор базовых пакетов Java
6 import 3ave.io.*;
7
8 // Пакеты расширений Jave
9 import javax.servlet.*;
10 import javax.servlet.http.*;
11 import j avax.naming.*;
12 import javax.ejb.*;
13 import j avax.rmi.*;
14
15 // Пакеты сторонних поставщиков
16 import org.w3c.dom.*;
17
18 // Пакеты Deitel
19 import com.deitel.advjhtpl.bookstore.model.*;
20 import com.deitel.advjhtpl.bookstore.ejb.*;
21
22 public Class LoginServlet extends XMLServlet {
23
24 // обработка HTTP-запросов post
25 public void doPost( HttpServletRequest request,
26 HttpServletResponse response )
27 throws ServletExeeption, IOException
28 {
29 Document, document = getDocumentBuilder().newDocunent{};
30
31 String userlD = request.gatParameter( "userlD" );
32 String password = request.getParameter( "password" 1;
33
34 // использование EJB-компонента Customer для аутентификации
пользователя
35 try {
36 InitialContext context = new InitialContext();
37
38 // поиск EJB-компонента Customer
39 Object object =
40 context.lookup( "java:comp/env/ejb/Customer" );
41
42 CustomerHome customerRome = ( CvistomerHome >
43 PortableRemoteObjact.narrow( object,
44 CustomerHome,class );
45
46 // нахождение покупателя с заданным идентификатором
и пароле»
47 Customer customer =
48 customerHome.findByLogin( userlD, password );
49
50 // получение объекта CustomerModel для локупателя
Практический пример корпоративного приложения
517
51 CustomerModel customerModel =
52 customer .getCustomerModel () ,-
53
54 // задание идентификатора userID для сеанса дамного
покупателя
55 requeat.getSession().setAttribute( "userlD",
56 customerModel.getUserlDO );
57
58 // совдакиз XML-элемента login
59 Element login = document.createElement( "login" );
60 document.appendchild( login );
61
62 // добавление в XML-документ имени покупателя
63 Element firstHame =
64 document.createElement{ "firstName" );
65
66 firstName.appendChild( document.createTextNode(
67 customerModel.getFirstNameO ) );
68
69 login.appendChiId( firstName ) ;
70
71 ) // конец блока try
72
73 // обработка исключения при поиске EJB-коыпОнента Customer
74 catch ( NamingException namingException ) {
75 namingException.printstackTrace();
76
77 String error = "The Customer EJB was not found in " +
78 "the JNDI directory.";
79
80 document.app*mdChiId( buildBx-rorMeseage (
81 document, error ) );
82 }
83
84 // обработка исключении, если ЕоВ-жОипонент Customer н* найден
85 catch { FinderEacception finderException ) (
86 finderException. prin tStacJcIrace () ;
87
88 String error = "The userlD and password entered " +
89 "were not found.";
90
91 document.appendChiId( buildErrorMessage(
92 document, error ) );
93 }
94
95 // гарантированная запись содержимого клиенту
96 finally {
97 writeXKM request, response, document };
98 }
99
100 } // конец метода doPost
101 ) ^
Рис. 10.18. Сервлет LoginServlet для аутентификации зарегистрированных покупателей
(Изображения публикуются с разрешения компаний Pixo, Inc. и © 2001 Nokia Mobile
Phones) (часть 1)
518
Глава 10
rojoft Internet Explorer
M> 'iSWfil
L«*iBJ.al
Isbmg.m&.V:
ЖШШШ& ______ .___ _ _..__ _______
:^№*tl}@ titte; //localho-i- iSOOOJbPoksta-e/Hegiaef
n
DeiteI
&Амос1ат« Inc.
ss
Product Cdtaluy Create Accuui.t Log in _.hn^piiTg Cart tlrder History
Welcome to the Deitel Bookstore, Julie
Click hsre to browse аж producs
crdLbpre Id view YctJL_Qr.tfer h^tof у
[ВРШЮ
.1
.^
"S3
ilil
:Wf^»i»SI>iiPI_i4, j
P** 1,.
«?$
*.)*■}& trll**:: 7< s _
Рис. 10.18. Сервлет LoginServlet для аутентификации зарегистрированных покупателей
(Изображения публикуются с разрешения компаний Pixo, Inc. и © 2001 Nokia Mobile
Phones) (часть 2)
В строках 74-82 перехватывается исключение NamingException, если
EJB-компонент Customer кв может быть найден в каталоге JNDI. Если EJB-комповент
Customer, значения userlD и password для которого совпадают со значениями,
введенными пользователем, не найден, в строках 85-93 перехватывается исключение
FinderException. Каждый блок catch формирует XML-сообщение об ошибке,
отображаемое клиенту. В строках 96-98 содержимое отправляется клиенту.
Практический пример корпоративного приложения
519
10.5.3. Сервлет ViewOrderHistoryServlet
Зарегистрированные покупатели могут просматривать информацию о своих
предыдущих заказах. Сервлет ViewOrderHistoryServlet (рис. 10.19) дает
возможность покупателям просмотреть сведения о ранее размещенных ими заказах с
указанием их дат, общей суммы заказов и сведений, были ли заказы доставлены.
1 // ViewOrderHistoryServlet.Java
2 // Сервлет ViewOrderHistoryServlet представляет покупателю
3 // список ранее размещенных ни заказов,
4 package com.deitel.advjhtpl.bookstore.servlets;
5
6 // Набор базовых пакетов Java
7 import java.io.*;
8 import java.util.*;
9
10 // Пакет» расширений Java
11 import javax-servlet.*;
12 import javax.servlet.http.*;
13 import j avax.naming.*;
14 impo-rt javax. rmi. * ;
15 import javax.ejb.*;
16
17 // naicexu сторонних поставщиков
IB import org.w3c.dom.*;
19
20 // Пакеты Deitel
21 import com.deitel.advjhtpl.bookstore.model.*;
22 import com,deitel,advjhtpl.bookstore.ejb.*;
23 import com.deitel.advjhtpl.bookstore.exceptions.*;
24
25 public class ViewOrderHistoryServlet extends XMLServlet {
26
27 // обработка HTTP-запросов get
28 public void doGet( HttpServletRequest request,
29 HttpServletResponse response )
30 throws ServletException, IOException
31 {
32 Document document = getDocumentBuilder().newDoCument();
33
34 HttpSession session = request.getSession();
35 String userlD = ( String )
36 session.getAttribute( "userlD" );
37
38 // формирование архива заказов с помощью EJB-компонента
Customer
39 try 1
40 InitialContext context = new InitialContext () ;
41
42 // поиск EJB-компонента Customer
43 Object object =
44 context.lookup( "Java:comp/env/ejb/Customer" );
45
46 CustomerHome customerHome = ( CustomerHome )
47 PortableRemoteObject.narrow(
46 object, CustomerHome.class };
520
49
50 // поиск покупателя с заданным идентификатором userID
51 Customer customer =
52 customerHome.findByOserlD( userID );
53
54 // создание элемента orderHistory
55 Element rootNode = ( Element ) document.appendChiId(
56 document.createElement( "orderHistory" ) );
57
58 // получение списка предыдущих заказов покупателя
59 Iterator orderHistory =
€0 customer.getOrderHistory().iterator();
61
62 // просмотр архива заказов и добавление в
63 // XML-документ элементов для каждого заказа
64 while ( orderHistory.hasNext() ) {
65 OrderModel orderModel =
66 ( OrderModel ) orderHistory next();
67
68 rootNode.appendChiId(
69 orderModel.getXML( document ) );
70 1
71 } // конец блока try
72
73 // обработка исключения, если у покупателя нет архива заказов
74 catch ( NoOrderHistoryException historyException ) {
75 historyException.printStackTrace();
76
77 document.appendChiId( buildErrorMessage( document,
78 historyException.getMessage() ) ) ;
79 }
80
81 // обработка исключения при поиске EJB-хомпонента Customer
82 catch ( NamingException namingException ) {
83 namingException. printStacVTrace (> ;
84
85 String error = "The Customer EJB was not found in " +
86 "the JND1 directory.";
87
88 document.appendChiId{ buildErrorMessage(
89 document, error } );
90 }
91
92 // обработка исключения, если покупатель не найден
93 catch ( FinderException finderException ) {
94 finderException.printStaeJcTrace() ;
95
96 String error = "The Customer with \iserID " + vtserlD +
97 " was not found.";
98
99 document.appendChiId( buildErrorMessage(
100 document, error ) );
101 }
102
103 // гарантированная запись содержимого клиенту
104 finally {
Практический пример корпоративного приложения
521
105 writeXML( request, response, document );
106 }
107
10B J // конец меиода doGet
109 }
Ж
£\&1УВ-1РК1
bttp^AKefasCSOGQ/bocfcttve/vvttOtterrtstov
&At&OciATf* Inc.
~ш
Order ID Order Dale Total Shipped
^ 'a
HP
^'J-
тагаг-.л-*
!^&W&№*gjg
уЬ. .ЕЛ *Щг£$ф^ + ^*;г
'PIXO
Attentat MicnbrousarZl
Рис. 10.19. Сервлет ViewOrderHtstoryServlet для просмотра ранее сделанных
покупателем заказов (Изображения публикуются с разрешения компаний Pixo, Inc.
и © 2001 Nokia Mobile Phones)
522
Глава 10
Метод getOrdevHistory EJB-компонента Customer возвращает коллекцию
ранее сделанных покупателем заказов (объектов Order). В строках 51-52
извлекается EJB-компонент Customer для покупателя, который посещает Internet-магазин.
В строках 59—60 извлекается объект итератора Iterator для обработки архива
предыдущих заказов данного покупателя. В строках 64—70 осуществляется поиск
в архиве заказов, и формируется XML-документ для отображения его клиенту.
Если покупатель до этого момента не размещал каких-либо заказов
в Internet-магазине, метод get Order History возбуждает исключение NoOrder-
HistoryException. В строках 74-79 это исключение перехватывается и
формируется сообщение об ошибке для отображения его покупателю. Если интерфейс
CustomerHome не может быть найден, либо покупатель не может быть обнаружен
в базе данных, выдается, соответственно, исключение NamLngException или
Finder Exception. В строках 82-101 каждое из этих исключений перехватывается,
и формируется сообщение об ошибке с использованием метода DuildErrorMessage.
В строке 105 содержимое отправляется клиенту с помощью метода writeXML.
10.5.4. Сервлет ViewOrderServlet
Сервлет ViewOrderServlet (рис. 10.20) отображает информацию о заказе.
Сервлет CheckoutServlet переадресовывает клиентов к сервлету ViewOrderServlet,
когда покупатель делает заказ. Сервлет ViewOrderHistoryServIet переадресовывает
клиентов к сервлету ViewOrderServlet, чтобы отобразить информацию об уже
сделанном заказе.
1 // ViewOrderServlet.Java
2 // Сервлет ViewOrderServlet отображает содержимое заказа
3 // покупателю.
4 package com.deitel.advjhtpl.bookstore.servlets;
5
6 // Набор базовых пакетов Java
7 import j ava.io.*;
в
9 // Пакеты расширений Java
10 import javax.servlet.*;
11 import javax.servlet.http,*;
12 import javax.naming.*;
13 import javax.ejb.*;
14 import javax.rmi.*;
15
16 // Пакеты сторонних поставщиков
17 import org.w3c.dom.*;
18
19 // Пакеты Deitel
20 import com.deitel.advjhtpl.bookstore.model.*;
21 import com.deitel.advjhtpl.bookstore.ejb.*;
22
23 public class ViewOrderServlet extends XMLServlet {
24
25 // обработка HTTP-запросов get
26 public void doGet( HttpServletRequest request,
27 HttpServletResponse response }
28 throws ServletException, IOExeeption
29 {
30 Document document = getDocumentBuilder().newDocument();
31 Integer orderlD = null;
Практический пример корпоративного приложения
523
32
33 // поиск ЕЛВ-компонентa Order и получение сведений о
34 // заказе с заданным идентификатором orderlD
35 try {
36 ZnitialContext context = new InitialContext();
3T
38 // поиск EJB-компонента Order
39 Object object =
40 context.lookup( "java:comp/env/ejb/Order" );
41
42 OrderHome orderHome = ( OrderHorcte )
43 PortableReitioteObject. narrow {
44 object, OrderHome.class ) ;
45
46 // получение идентификатора orderlD из объекта запроса
47 orderlD = new Integer(
48 request.getParamater( "orderlD" ) ) ;
49
50 // поиск заказа с заданным идентификатором orderlD
51 Order order = orderHome.findByPrimaryKey( orderlD );
52
53 // получение подробных сведений о заказе в гиде объекта
OrderModel
54 OrderModel orderModel = order.getOrderModel();
55
56 // добавление сведений о заказе в XML-документ
57 document.appendChild(
58 orderModel.getXML( document ) );
59
60 } // конец блока try
61
62 // обработка исключения при лоиске ЕЛВ-конпонента Order
63 catch ( NamingException namingException ) {
64 namingException.printStackTrace();
65
66 String error = "The Order EJB was not found in " +
67 "the JNDI directory.";
68
69 document.appendChild( buildErrorMessa^e(
70 document, error ) ) ,-
71 }
72
7 3 // обработка исключения, если EJB-компонент Order не найден
74 catch ( FinderException finderException ) {
75 finderException.printStackTrace(};
76
77 String error = "An Order with orderlD " + orderlD +
78 " was not found.";
79
80 document.appendChild( buildErrorMessage(
81 document, error ) );
82 }
83
84 // гарантированная запись содержимого клиенту
85 finally (
86 writeXKM request, response, document );
524
Глава 10
В7 }
ВВ
89 } // конец метода doGet
90 }
]ЛЙ^З,)Д ^B-;)facJiM;300№iMliifaT;fffa«Onl»?aji5rIL'-i7
DeiteI
&A»socIates Inc.
srID: 27 Order Date: Aug IS, 2001 11:21:00 AM
Title
ftuthar
•л*чк>
ISBN Quantity Price
•>*■■ -iraa ^«^ "'*■:.•**
l^te;.,^1**ui». "^
г ЭД9.95
Total: 1209.95
«fib."* ■
;SE3§h|
If'"
.ift.
ЩггЩ0ЩЖ^1Ш:
a
^щ^гтт
PIXO
Internet Mtovbmw$er2.t
:*Ш1
€mP
<*rrst.?rl~ >**.*■>*,
Рис. 10.20. Сервлет ViewOrderServlet для просмотра информации о заказе (Изображения
публикуются с разрешения компаний Pixo, Inc. и © 2001 No<ia Mobile Phones)
В строках 39-44 осуществляется получение ссылки на интерфейс OrderHome.
В строках 47-48 из объекта request извлекается параметр orderlD. В строке 51
вызывается метод findByPrimaryKey для получения удаленной ссылки на заказ (объ-
Практический пример корпоративного приложения
525
ект Order) с заданным идентификатором orderlD. В строках 54-58 извлекается
модель OrderModel для заказа Order, и в XML-документ добавляется ее XML-пред-
ставленне.
8 строках 63-71 перехватывается исключение NamingException, которое
возбуждается из метода lookup, если EJB-компонент Order не может быть найден
в каталоге JNDI. В строках 74-82 перехватывается исключение FindcrEx^eption,
которое возбуждается методом findByPrimaryKey, если заказ (объект Order) с
заданным идентификатором orderlD не найден. В строке 86 XML-документ
отправляется клиенту с помощью метода writeXML.
10.5.5. Сервлет GetPasswordHintServlet
Зарегистрированные покупатели порой забывают свои пароли. Сервлет
GetPasswordHintServlet (рис, 10.21) предоставляет подсказки, помогающие
покупателям вспомнить свои пароли. Покупатель указывают текст подсказки для
пароля в процессе регистрации.
1 // GetPasswordHintServlet.Java
2 // Сервлет GetPasswordHintServlet дает возможность
3 // покупателю восстановить забытый пароль.
4 package com.deitel.advjhtpl.bookstore.servlets;
5
6 // Набор базовых пакетов Java
7 import java.io.*;
В
9 // Пакеты расширений Java
10 import javax.servlet.*;
11 import javax.servlet.http.*;
12 import javax.naming.*;
13 import javax.ejb.*;
14 import javax.rmi.*;
15
16 // Пакеты сторонних поставщиков
17 import org,w3c.dom.*;
IB
19 // Пакеты Deitel
20 import com.deitel.advjhtpl bookstore.model.* ;
21 import com,deitel.advjhtpl.bookstore.ejb.*;
22
23 public class GetPasswordHintServlet extends XMLServlet (
24
25 // обработка HTTP-запросов get
26 public void doGet( HttpServletRequest request,
27 HttpServletResponse response )
28 throws ServletException, lOException
29 {
30 Document document = getDocumentBuilder()-newDocument();
31 String userlD = request.getParameter( "userlD" );
32
33 // получение подсказки пароля из EJB-компонента Customer
34 try (
35 InitialContext context = new InitialContext();
36
37 // поиск EJB-компонента Customer
38 Object object =
526
Глава 10
39 context.lookup( "java:comp/env/ejb/Customer" );
40
41 CustomerHome customerHome = { CustomerHome }
42 portableRemoteObject.narrow( object,
43 CustomerHome.class );
44
45 // нахождение покупателя с заданным идентификатором userID
46 Customer customer =
47 customerHome,findByUserlD{ userlD );
48
4 9 // создание элемента passwordHint в XML-документе
50 Element hintElement =
51 document.createElement( "passwordHint" );
52
53 // добавление текста подсказки в XML-элемент
54 hintElement.appendChild( document.createTextNode{
55 customer.getPasswordHint() ) );
56
57 // добавление элемента passwordHint в XML-документ
58 document.appendChild( hintElement );
59
60 ) // конец блока try
61
62 // обработка исключения при поиске EJB-компонента Customer
63 catch ( NamingException namingException ) {
64 namingException.printStaCkTraceО;
65
66 String error = "The Customer EJB was not found in " +
67 "the ЛЮ1 directory.";
68
69 document.appendChild( buildErrorMessage(
70 document, error ) );
71 }
72
73 // обработка исключения, если EJB-компонеят Customer не найден
74 catch { FinderException finderException ) {
75 finderException.printStaclfcTraceО;
76
77 String error = "No customer was found with userlD " +
78 userID + ".";
79
80 document.appendchild( buildErrorMessage(
Bl document, error } );
82 }
83
B4 // гарантированная запись содержимого клиенту
85 finally i
86 writeXMH request, response, document ),-
87 )
88
89 } // конец метода doGet
90 } ^^___ ___
Рис. 10.21. Сервлет GetPasswoidHintServlet для отображения текста подсказки пароля
(Изображения публикуются с разрешения компаний Pixo, Inc. и © 2001 Nokia Mobile
Phones) (часть 1)
Практический пример корпоративного приложения
527
3 Hera's У<шг Password Hint ■ Murcstft [ntemet Екр&зют '
i££
;Nt-,f»ew/£l*e*e* . Jm* tfcfe--
-»«
DeiteI
&А««ос1аге* Inc.
;atalpq create Ad
bhoppsirj Cdrt Urrlpt
Password hint: my hint
Etn*rtAddress: j
fe" Л" ЗДов'Н' I
■rEJ-SjwP' ■'
^J
^ЯВ;|. .МЙГШШ» .-din
Рис. 10.21. Сервлет GetPasswordHintServlet для отображения текста подсказки пароля
(Изображения публикуются с разрешения компаний Pixo, Inc и © 2001 Nokia Mobile
Phones) (часть 2)
Подсказка хранится вместе с другой регистрационной информацией в EJB-kom-
поненте Customer. В строках 38-47 осуществляется поиск интерфейса Customer-
Home, и извлекается удаленная ссылка на EJB-компонент Customer. Метод get-
PasswordHint (строка 55) EJB-компонента Customer возвращает подсказку,
введенную пользователем при регистрации. В строке 58 подсказка добавляется
в XML-документ.
528
Глава 10
В этой главе мы рассмотрели логику управления и логику внешнего
представления данных для приложения Deitel Bookstore. Логика управления обеспечивает
интерфейс HTTP с объектами бизнес-логики, рассматриваемыми в главах 10 и 11.
Сервлеты Java представляют собой устойчивую и гибкую реализацию логики
управления. Логика внешнего представления данных посредством XSLT
позволяет приложению Deite\ Bookstore поддерживать множество различных типов
клиентов, не внося при этом изменений в реализации логики управления. В главах 11
и 12 будет рассмотрена бизнес-логика приложения Deitel Bookstore,
реализованная с помощью технологии Enterprise JavaBeans.
Упражнения для самоконтроля
10.1. Какая составная часть архитектуры MVC (модель—вид—контроллер) в приложении
Deifel Bookstore реализована на сервлетах? Какую составную часть архитектуры MVC
реализуют XSLT-трансформации?
10.2. Напятите фрагменты кода для поиска EJB-коьшонеита ShoppingCart в каталоге JNDI
и создания нового экземпляра с помощью интерфейса ShoppingCartHome.
Позаботьтесь о перехвате любых исключений, возбуждаемых при поиске KJB-комттонента или
создании нового экземпляра.
10.3. Каким образом сервлет ViewOrderServlet (рис. 10.20) находит заказ (EJB-компонент
Order), информацию о котором покупателю разрешено видеть?
10.4. Какие общие функциональные возможности класс XMLServlet (рис. 10.1)
предоставляет для сервлетоа а приложении Deitel Bookstore? Опишите предназначения
основных методов класса XMLServlet.
10.5. Каким образом класс XMLServlet определяет имя таблицы сталей XSL, которую
следует использовать при трансформации содержимого, сгенерированного сервлетом?
Какие преимущества обеспечивает такая стратегия?
10.6. Каким образом класс XMLServlet определяет, какую таблицу стилей XSL следует
использовать при трансформации содержимого, сгенерированного сервлетом для
определенного типа клиента? Какие преимущества обеспечивает такая стратегия?
Ответы на упражнения для самоконтроля
10.1. Сервлеты реализуют в архитектуре MVC логику управления (контроллер), поскольку
они обрабатывают все пользовательские запросы и данные, введенные пользователем.
XSLT-трансформации реализуют в архитектуре MVC логику представления данных
(вид), нескольку они определяют внешний вид данных в приложении.
10.2. Следующий фрагмент кода ищет EJB-компонепт ShoppingCart в каталоге JNDI и
создает новый экземпляр с помощью интерфейса ShoppingCartHome
try (
InitialContext context = new InitialContext;
Object object = context.lookup {
"3*4a:caKp/env/ejb/ShoppingCaTt" ):
ShoppingCartHome ShoppingCartHome =
{ ShoppingCartHome } PortableRemoteObject.narrow (
object, SliOppingCartHoina. class );
ShoppingCart ShoppingCart = ShoppingCartHome.create();
i
catch ( NamingException namingExceptiori ){
namingException .printStackTrace f) ,■
}
catch ( CreateExceptian createException ) }
createException.printStackTraceО;
}
Практический пример корпоративного приложения
529
10.3. Сервлет ViewOrderServlet использует метод find By Primary Key интерфейса Order-
Home для нахождения EJB-компонепта Order с идентификатором orderlD,
передаваемым в качестве параметра объекту HttpServletRequest.
10.4. Класс XMLServlet предоставляет общий метод mit для инициализации объектов
DocumentBuilderFactory, TransformerFactory и свойств, используемых каждым из
сервлетов. Класс XMLServlet предоставляет метод buildErrorMessage, который
создает элемент XML, содержащий описание сообщения об ошибке. Класс XMLServlet
также предоставляет метод writeXML, который использует метод transform для
преобразования XML-содержимого, сгенерированного каждым из сервлетов, с использованием
таблицы стилей XSL, характерной для конкретного клиента.
10.5. Класс XMLServlet имеет свойство XSLFileName, которое определяет имя XSL-файла,
используемого при трансформации содержимого, генерируемого сервлетом. Метод init
класса XMLServlet устанавливает для свойства FileName значение, заданное для
параметра инициализации XSL_FILE сервлета. Определение имени файла из параметра
инициализации дает возможность разработчику задавать имя файла при
развертывании приложения. Имя файла может быть впоследствии изменено без необходимости
повторной компиляции сервлета.
10.6. Класс XMLServlet использует объект ClientModel для того, чтобы определить, в каком
каталоге содержится тайлица стилей XSL, используемая для трансформации
XML-содержимого, сгенерированного сервлетом. Класс XMLServlet создает список объектов
ClientModel из XML-файла конфигурации при первой инициализации сервлета. В
каждом объекте ClientModel определен заголовок User-Agent, который уникально
идентифицирует клиента, заголовок Content-Type для отправки данных клиенту и каталог,
в котором следует искать таблицу стилей XSL для формирования содержимого,
специфичного для конкретного клиента. Это позволяет разработчику добавлять поддержку
новых типов клиентов без внесения изменений в код сервлета. Разработчик просто
предоставляет набор таблиц стилей XSL (XSLT-трансформаций) для нового типа клиента
и включает информацию о новом типе клиента в файл clients.xml.
11
Практический пример
корпоративного приложения.
Бизнес-логика: часть 1
Цели
• Познакомиться с моделью
данных на базе EJB,
используемой в учебном
приложении Deitel Bookstore.
• Уяснить бизнес-логику,
применяемую в учебном
приложении Deite] Bookstore.
• Получить представление
о передаче объектов через
RMI-IIOP и возникающих при
этом проблемах
с производительностью.
• Понять, какие преимущества
дает применение
EJB-комионентов
с персистентнастью,
управляемой контейнером,
для хранения информации
в базе данных.
• Познакомиться
с применением классов
первичного ключа для
представления составных
первичных ключей.
Управляй своим делом, иначе дело станет,
управлять тобой,
Бенджамин Франклин
Общее, дело — это ничье дело, а ничье
дело — это мое дело.
Клара Бартон
532
Глава 11
В этой главе будет рассмотрена реализованная на базе EJB-компонентов
бизнес-логика модели магазинной тележки, используемой в приложениях электронной
коммерции, а также компоненты-сущности EJB, которые предоставляют
объектно-ориентированный интерфейс для хранения каталога товаров, имеющихся
в Internet-магазине. Изучив эту главу, вы поймете, как использовать EJB-компо-
ненты в приложениях электронной коммерции, а также получите представление
о некоторых других вопросах, связанных с EJB, таких как нестандартные
(создаваемые разработчиком) классы первичного ключа и отношения «многие ко многим».
11.2. Архитектура компонентов EJB
EJB-компоненты реализуют в учебном приложении Deitel Bookstore
бизнес-логику. Логика управления, реализованная на сервлетах, взаимодействует с
бизнес-логикой, реализованной на EJB-компонентах, для обработки запросов
пользователей и извлечения информации из базы данных. Например, сервлет GetPro-
Практический пример корпоративного приложения. Бизнес-логика: часть 1 533
duetServlet обрабатывает запросы, поступившие от покупателя (EJB-компонент
Customer) для просмотра сведений о товаре (EJB-компонент Product). Сервлет
GetProductServlet использует каталог JNDI для нахождения собственного
интерфейса EJB-компонента Product. Сервлет GetProductServlet вызывает метод
findByPrimaryKcy интерфейса ProductHome для извлечения удаленной ссылки на
книгу (EJB-компонент Product) с запрашиваемым ISBN-кодом. Сервлет
GetProductServlet должен затем воспользоваться методами удаленного
интерфейса Product для извлечения информации о товаре. При каждом вызове метода
удаленного интерфейса Product создается сетевой трафик, поскольку взаимодействие
с EJB-компонентом осуществляется через RMI-ПОР. Если для извлечения
информации о названии книги {свойство title), авторе (свойство author), цене (свойство
price) и т.д. осуществляются отдельные вызовы мегомов, затраты на сетевые
взаимодействия могут серьезно ограничить производительность и масштабируемость
приложения.
Применение компонентов-сущностей EJB в учебном приложении Deitel
Bookstore позволяет уменьшить издержки, связанные с возникновением заторов
в сети, за счет использования моделей для передачи данных EJB-компонентами.
Модель представляет собой сериализуемый (Serializable) класс, который содержит
все данные для определенного EJB-компонента. Эти классы называются
моделями, поскольку каждый класс модели реализует ту составную часть архитектуры
MVC (модель—вид—контроллер), которая относится к модели. Каждая EJB-сущ-
ностъ предоставляет get-метод, который возвращает представление своей модели.
Например, EJB-компонент Product имеет метод getProductModel, который
возвращает объект ProductModel, содержащий ISBN-код (ISBN), автора (author),
цену (price) и другие свойства товара (EJB-компонент Product). Многие EJB-сущ-
ности также предоставляют методы создания (create), которые принимают модели
в качестве параметров. Эти методы создают новые экземпляры EJB-компонента
и устанавливают данные в EJB-компонентах, используя значения свойств,
предоставляемые моделью.
I——, Совет по повышению эффективности 11,1
"^Р^ Объединение EJB-компонентов в класс модели и возврат экземпляров
этого класса модели бизнес-методами EJB-компонента может повысить
эффективность работы EJB-компонента за счет сокращения сетевого
трафика, сопровождающего множественные вызовы методов через
RMI-IIOP.
На рис. 11.1 показан образец взаимодействия между сервлетом
GetProductServlet и EJB-компонентом Product. Чтобы получить информацию об указанном
товаре, сервлет GetProductServlet вызывает метод getProductModel EJB-компо-
нента Product. Метод getProductModel возвращает объект ProductModel,
содержащий данные для указанного товара, и выполняет сериализацию объекта Рго-
Контейнер сервлетов Контейнер Ш
GetProductServlet
getProduc tMode1
Produc tHode1
Product EJB
Рис. 11.1. Взаимодействие между сервлетом GetProductServlet и EJB-компонентом Product
534
Глава 11
duct Mode] через RMI-IIOP. Сервлет GetProductServlet извлекает значения
свойств объекта ProductModel для формирования выходного результата,
отображаемого пользователю.
11.3. Реализация магазинной тележки
Сеансовый EJB-компонент с состоянием ShoppingCart реализует бизнес-логику
для обслуживания магазинной тележки каждого из покупателей. EJB-компонент
ShoppingCart состоит из удаленного интерфейса, реализации EJB-компонепта
и собственного интерфейса. Магазинная тележка ShoppiugCart реализуется в виде
сеансового EJB-компонента с состоянием, чтобы каждый его экземпляр сохранял
свое состояние в течение сеанса посещения Internet-магазина покупателем. Точно
так же, как посетители супермаркетов используют магазинные тележки для
отбора товаров, так и посетители нашего Internet-магазина используют EJB-компонен-
ты ShoppingCart для отбора товаров при просмотре содержимого магазина.
11.3.1. Удаленный интерфейс ShoppingCart
Удаленный интерфейс ShoppingCart (рис. 11.2) определяет методы
бизнес-логики, доступные для EJB-компонента ShoppingCart. Каждый метод удаленного
интерфейса должен объявлять, что он возбуждает исключение RemoteException.
Каждый метод также должен объявлять любые специфичные для приложения
исключения, которые могут быть возбуждены из реализации. Метод getContents
(строка 20) возвращает коллекцию (Collection) товаров (Product), содержащихся
в магазинной тележке (ShoppingCart). Метод addProduct (строки 23-24)
принимает в виде строкового аргумента ISBN-код книги, добавляемой в магазинную
тележку. Метод addProduct возбуждает исключение ProductNotFoundException,
которое является специфичным для приложения исключением, указывающим, что
книга (объект Product) с данным ISBN-кодом (ISBN) отсутствует в базе данных и.
следовательно, не может быть добавлена в магазинную тележку (ShoppingCart).
1 // ShoppingCart.Java
2 // ShoppingCart - удаленный интерфейс для сеансового
3 // EJB-комлонента с состоянием ShoppingCart.
4 package com.deitel.advjhtpl.bookstore.ejb;
5
6 // Набор базовых пакетов Java
7 import j ava.. rmi. RemoteException;
8 import Java,util.Collection;
9
10 // Пакеты расширений Java
11 import javax.ojb.EJBObject;
12
13 // Пакеты Deitel
14 import com.deitel.advjhtpl.bookstore .model. *;
15 import com.deitel.advjhtpl.bookstore.exceptions,*;
16
17 public interface ShoppingCart extends EJBObject {
18
19 // получение содержимого магазинной тележки
20 public Collection getContents() throws RemoteException;
21
22 // добавление в тележку книги с заданным ISBN-кодом
23 public void addProduct( String isbn )
Практический пример корпоративного приложения. Бизнес-логика: часть 1 535
24 throws RemoteException, ProductNotFoundException;
25
26 // удаление из одлежки книги с заданным ISBN-кодом
27 public void removeProduct( String isbn )
28 throws RemoteExceptiort, ProductNotFoundException;
29
30 // изменение количества книг с заданным ISBN-кодом.
31 // имеющихся в тележке, на указанное число
32 public void setProductQuantity( String isbn, int quantity )
33 throws RemoteException, ProductNotFoundException,
34 IllegalArgumentException;
35
36 // подсчет стоимости товаров в тележке (создание нового заказа)
37 public Order checkout( string userlD )
38 throws RemoteException, ProductNotFoundException;
39
40 // получение общей стоимости товаров в магазинной тележке
41 public double getTotal() throws RemoteException;
42 )
Рис. 11.2. Удаленный интерфейс ShoppingCart для добавления, удаления и обновления
товаров, контроля и подсчета общей стоимости заказа
Метод removeProduct (строки 27-28) удаляет книгу (Product) с заданным
ISBN-кодом (ISBN) из магазинной тележки (ShoppingCart). Если книга с данным
ISBN-кодом не найдена в магазинной тележке, метод removeProduct возбуждает
исключение ProductNotFoundException.
Метод setProductQuantity (строки 32-34) обновляет количество книг с заданным
ISBN-кодом в магазинной тележке. Например, если в магазинной тележке
покупателя имелся один экземпляр книги Технологии программирования на Java 2.
Книга 3, для того, чтобы приобрести пять экземпляров книги, следует вызвать метод
setProductQuantity с указанием в качестве аргументов ISBN-кода книги
Технологии программирования на Java 2. Книга 3 и целого числа 5. Если книга с данным
ISBN-кодом в магазинной тележке отсутствует, метод setProductQuantity
возбуждает специфичное для приложения исключение ProductNotFoundException,
Метод checkout (строки 37-38) помещает книги (компоненты Product) в
соответствии со сделанным покупателем заказом (компонент Order) в магазинную тележку
(компонент ShoppingCart). Метод checkout принимает в качестве строкового
аргумента идентификатор userlD покупателя, сделавшего заказ. Заказы могут
размещать любые зарегистрированные покупатели. Метод getTotal (строка 41)
возвращает общую стоимость товаров, имеющихся в магазинной тележке покупателя.
11.3.2. Реализация ShoppingCartEJB удаленного
интерфейса ShoppingCart
Реализация ShoppingCartEJB (рис. 11.3) удаленного интерфейса ShoppingCart
содержит коллекцию объектов OrderProductMode! (строка 24). Объект OrderPro-
ductModel (рис. 11.25) представляет элемент в магазинной тележке ShoppingCart.
Каждый объект OrderProdnctModel содержит товар (компонент Product) и его
количество в магазинной тележке. Метод ejbCreate (строки 27-30) инициализирует
коллекцию (строка 29). Метод getContents (строки 33-36) возвращает содержимое
магазинной тележки как коллекцию объектов OrderProductMode 1.
1 // shoppingc3.rtEJB.3ava
2 // Сеансовый EJB-компонент с состоянием ShoppingCart
3 // представляет магазинную тележку покупателя.
4 package com.deitel.advjhtpl.bookstore.ejb;
5
6 // Набор баэовкх пакетов Java
7 import java.util.*;
8 import java.rmi.RemoteException;
9 import Java.text.DateFormat;
10
11 // Пакеты расширений Java
12 import javax.ejb.*;
13 import j avax.naming.*;
14 import javax.rmi.PortableRemoteObject;
15
16 // Пакеты Deitel
17 import com.deitel.advjhtpl.bookstore.model.*;
18 import com.deitel.advjhtpl.bookstore.exceptions.*;
19
20 public class ShoppingCartEJB implements SessionBean {
21 private SessionContext sessionContext;
22
23 // товары в магазинной тележке и их количество
24 private Collection orderProductModels ,-
25
26 // создание нового компонента ShoppingCart
27 public void ejbCreate{)
28 {
29 orderProductModels = new ArrayListO;
30 }
31
32 // получение содержимого магазинной тележки
33 public Collection getContentsO
34 {
35 return orderProductModels;
36 J
37
38 // добавление в тележку книги с заданным ISBN-кодом
39 public void addProductf String isbn )
40 throws ProductNotFoundExeeption, EJBException
41 {
42 // проверка, имеется ли в магазинной тележке
43 // книга с заданным ISBN-кодом
44 Iterator iterator = orderProductModels.iterator();
45
46 while ( iterator .tiasUext() ) {
47 OrderProductModel orderProductModel =
48 ( OrderProductModel ) iterator.next();
49
50 ProductModel productModel =
51 orderProductModel.getProductModel();
52
53 // если книга уже имеется в тележке,
инкрементировать ее количество
54 if ( productModel.getISBN().equals( isbn ) ) {
55
56 orderProductModel.setQuantity(
Практический пример корпоративного приложения. Б из и ее-логика: часть 1 537
57 orderProductModel.getQuantity() + 1 );
58
59 return;
60 }
61
62 } // конец блока while
63
64 // если книга отсутствует в тележке, найти книгу с заданным
65 // ISBN-кодом и добавить объект OrderProductModel
в компонент ShoppingCart
66 try i
67 InitialContext context = new InitialContext();
68
69 Object object = context.lookup(
70 "java:comp/env/ejb/Product" );
71
72 FroductHome productHome = ( ProductKome )
73 PortableRemoteObjeet.narrow( object,
74 ProductHome.class );
75
76 // нахождение книги с заданным ISBN-кодом
77 Product product = productHome.findByPrimaryKey( isbn );
78
79 // получение объекта ProductModel
80 ProductModel productModel ■ product.getProductModel(};
81
82 // создание объекта OrderProductModel для объекта
83 // ProductModel и задание количества товара дли него
84 OrderProductModel orderProductModel =
85 new OrderProductModel();
86
87 orderProductModel.setProductModel( productModel );
88 orderProductModel.setQuaatity( 1 );
89
90 // добавление объекта OrderProductModel в компонент
ShoppingCart
91 orderProductModels.add( orderProductModel );
92
93 } // Конец блока try
94
95 // обработка исключения при поиске записи дли товара
96 catch ( FinderException finderException ) {
97 finderException.printStackTrace();
98
99 throw new ProductNotFoundException( "The Product " +
100 "with ISBN " + isbn + " was not found." >;
101 }
102
103 // обработка исключении при вызове методов
EJB-комяонента Product
104 catch ( Exception exception ) {
105 throw new EJBExcoption( exception );
106 }
107
108 } // конец метода addProduct
109
110 // удаление книги с заданным tSBN-кодом из тележки
538
Глава 11
111 public void removeProduct( String isbn )
112 throws ProductNotFoundException
113 {
114 Iterator iterator = orderProductModels.iterator();
115
116 while { iterator.hasNext() ) {
117
118 // получение следующего объекта OrderProduct в компоненте
Shopp i ngCa r t
119 OrderProductModel OrderProductModel =
120 ( OrderProductModel ) iterator.next();
121
122 ProductModel productModel =
123 OrderProductModel.getProductModel() ;
124
125 // удаление книги с заданным ISBN-кодом из тележки
126 if ( productModel.getlSBN ().equals( isbn ) ) {
127 orderProductModels.remove( OrderProductModel );
128
129 return;
130 }
131
132 } // конец блока while
133
134 // возбуждение исключения, если книга не найдена в тележке
155 throw ivew ProductHotFoundException ( "The Product " +
136 "with ISBN " + isbn + " was not found in your " +
137 "ShoppingCart." );
138
139 } // коней метода removeProduct
140
141 // задание количества данного товара в тележке
142 public void setProductQuantity( String isbn,
143 int productQuantity ) throws ProductNotFoundException
144 {
145 // возбуждение исключения IllegalArgumentException,
если указано недопустимое количество
14 6 if ( productQuantity < 0 )
147 throw new IllegalArgumentException(
148 "Quantity cannot be less than zero." );
14 9
150 // удаление товара, если значение productQuantity меньше 1
151 if ( productQuantity = 0 ) (
152 removeProduct( isbn );
153 return;
154 }
155
156 Iterator iterator = orderProductModels.iterator();
157
158 while ( iterator.hasNextO } f
159
160 // получение следуянцего объекта OrdarProduct в компоненте
ShoppingCart
161 OrderProductModel OrderProductModel =
162 ( OrderProductModel ) iterator.next() ,-
163
164 ProductModel productModel =
Практический пример корпоративного приложения. Бизнес-логика: часть 1 539
165 orderProductModel.getProductModel{);
166
167 // установка количества для книги с заданным ISBN-кодом
168 if ( productModel.getlSBN{).equals{ isbn ) ) {
169 orderProductModel.setQuantity( productQuantity );
170 return;
171 }
172
17 3 } // конец блока while
174
175 // возбуждение исключения, если книга не найдена в тележке
176 throw new ProductNotFoundException( "The Product " +
177 "with ISBN " + isbn + " was not found in your " +
ПВ "ShoppingCart. " ) ;
179
180 } // конец метода setProductQuantity
131
182 // подсчет стоимости (создание нового заказа)
183 public Order checkout( String userlO )
184 throws ProductNotFoundException, EJBException
185 {
186 // возбуждение исключения, если тележка пуста
187 if ( orderProductModels.isEmpty() )
188 throw new ProductNotFoundException( "There were " +
189 ''no Products found in your ShoppingCart." );
190
191 // создание объекта OrderModel, содержащего сведения о заказе
192 OrderModel orderModel = new OrderModel();
193
194 // задание в качестве даты заказа сегодняшней даты
195 orderModel.setOrderDate( new Date() );
196
197 // задание списка объектов OrderProduct в заказе OrderModel
198 orderModel.setOrderProductModels( orderProductModels );
199
200 // задание значения false для флага доставки shipped
201 orderModel.setShipped( false );
202
203 // использование интерфейса OrderHome для создания нового заказа
204 try {
205 InitialContext context = new InitialContext{);
206
207 // поиск EJB-хомпонента Order
208 Object object = context.lookup(
209 "Java:comp/env/ejb/Order" );
210
211 OrderHome orderHome = ( OrderHome )
212 PortableRemoteObject.narrow( object,
213 OrderHome.class );
214
215 // создание нового компонента Order о использованием
216 // объекта OrderModel и идентификатора покупателя userlD
217 Order order = orderHoae.create( orderModel, user-ID );
218
219 // освобождение тележки для дальнейших покупок
220 orderProductModels = new ArrayList();
221
540
Глава 11
222 /7 возврат созданного EJB-компонента Order
223 return order;
224
225 } // конец блока try
226
227 // обработка исключения при поиске EJB-компонента Order
228 catch ( Exception exception ) {
229 throw new EJBException( exception );
230 J
231
232 } // конец метода checkout
233
234 // получение общей стоимости товаров в магазинной тележке
235 public double getTotalO
236 {
237 double total = 0.0;
23S Iterator iterator = orderProductModels . iterator О <"
239
240 // подсчет обшей стоимости заказа
241 while { iterator.hasNextO ) {
242
243 // получение следукщег'о объекта OrderProduct в компоненте
ShoppingCart
244 OrderProductModel orderProductHodel =
245 ( OrderProductHodel ) iterator.next();
246
247 ProductModel productModel =
248 OrderProductModel.getProductModel();
249
250 // добавление стоимости данного товара к общей сумме
251 total += ( productModel.getPrice(} *
252 OrderProductModel.getQuantity0 );
253 }
254
255 return total,-
256
257 } // конец метода getTotal
258
259 // установка контекста SessionContext
260 public void setSessionContext( SessionContext context )
261 {
262 SessionContext = context;
263 }
264
265 // активация экземпляра EJB-компонента ShoppingCart
266 public void ejbActivateO {}
267
268 // пассивация экземпляра EJB-компонента ShoppingCart
269 public void ejbPassivate() {}
270
271 // удаление экземпляра EJB-компонента ShoppingCart
272 public void ejbRamove() {}
273 }
Рис. 11.3. Реализация ShoppingCartEJB удаленного интерфейса ShoppingCart
Практический пример корпоративного приложения. Бизнес-логика: часть 1 541
Метод addProduct (строки 39-108) добавляет товар (книгу) в магазинную
тележку. В строках 46-62 определяется, имеется ли уже данная книга в тележке.
Если да, в строках 56-57 инкрементируется значение параметра quantity
соответствующего объекта OrderProductModel. В противном случае метод fiudBy-
PrimaryKey интерфейса ProductHome ищет книгу (компонент Product) с
заданным значением ISBN (строка 77). В строках 83-85 создается объект
OrderProductModel для хранения книги в магазинной тележке. В строке 87 в объект
OrderProductModel добавляется объект ProductModel, а в строке 88 для
параметра quantity объекта OrderProductModel устанавливается значения 1. В строке 91
объект OrderProductModel добавляется в коллекцию, что завершает процесс
добавления товара в магазинную тележку.
Если метод fiudBy Primary Key интерфейса ProductHome не находит книгу
с указанным первичным ключом, в строках 96-101 перехватывается исключение
finder Except ion. В строках 99-100 возбуждается исключение Product NotFound-
Exception, указывающее, что книга с данным ISBN-кодом не может быть найдена.
Метод removeProduct (строки 111-139) сравнивает ISBN-код (свойство ISBN)
каждой книги (объект Product) в коллекции объектов OrderProductModel компонента
ShoppingCart с ISBN-кодом удаляемой книги. Если книга с заданным ISBN-кодом
найдена, в строке 127 соответствующий объект OrderProductModel удаляется из
коллекции. Если книга (объект Product) в магазинной тележке не найдена, в
строках 135-137 возбуждается исключение ProductNotFoundException.
Метод setProductQuantity (строки 142—180) устанавливает свойство quantity
для объекта OrderProductModel, т.е. количество экземпляров даипой книги в
магазинной тележке. Если аргумент productQuantity меньше О, в строках 147-148
возбуждается исключение IUegalArgumentException. Если значение
productQuantity равно О, в строке 152 данная книга (объект Product) удаляется из
магазинной тележки (ShoppingCart). В строках 158-173 сравнивается ISBN-код каждой
из книг в коллекции OrderProductModel с заданным ISBN-кодом. В строке 169
обновляется количество quantity для соответствующей книги путем вызова метода
setQuantity интерфейса OrderProductModel. Если книга с заданным ISBN-кодом
не найдена в магазинной тележке, в строках 176-178 возбуждается исключение
ProductNotFoundException.
Метод checkout (строки 183-232) осуществляет размещение заказа (компонент
Order) на товары, имеющиеся в магазинной тележке. Каждый заказ (компонент
Order) должен иметь соответствующего покупателя (компонент Customer),
поэтому метод checkout принимает в качестве аргумента идентификатор покупателя
userlD. В строках 192-201 создается объект OrderModel для представления
подробной информации о заказе. Каждый заказ (компонент Order) имеет дату
оформления заказа orderDate, флаг доставки shipped и коллекцию объектов
OrderProductModel. В строке 195 устанавливается дата для объекта OrderModel. В строке
198 вызывается метод setOrderProductModels класса OrderModel для добавления
в компонент Order списка объектов OrderProductModel. В строке 201 для флага
shipped устанавливается значение false, которое указывает, что заказ не был
доставлен со склада. Компонент-сущность EJB Order будет рассмотрен в разделе 11.5.
В строке 217 вызывается метод create интерфейса OrderHome для создания
нового заказа. Метод create принимает в качестве аргумента объект OrderModel,
содержащий информацию о создаваемом заказе, и строку, содержащую
идентификатор покупателя userlD. В строке 220 осуществляется освобождение магазинной
тележки ShoppingCart путем присвоения нового списка типа Array List коллекции
ссылок orderProductModels. В строке 223 возвращается удаленная ссылка на
вновь созданный компонент Order. В строках 228-230 перехватываются любые
возникшие исключения.
Метод getTotal (строки 235-257) просматривает коллекцию объектов
OrderProductModel и вычисляет общую стоимость содержимого магазинной тележки.
542
Глава 11
11.3.3. Собственный интерфейс ShoppingCartHome
Интерфейс ShoppingCartHome (рис. 11,4) определяет единственный метод
create (строки 15-16), который создает новый экземпляр EJB-компонента
ShoppingCart. Контейнер EJB предоставляет реализацию для метода create.
На рис. 11.5 и 11.6 представлены параметры дли развертывания сеансового
EJB-компоненга с состоянием ShoppingCart. В дополнение к приведенным здесь
установкам, следует установить для типа транзакции Transaction Type значение
Required для всех бизнес-методов.
1 // ShoppingCartHome.Java
2 // ShoppingCartHome - собственный интерфейс для сеансового
3 // EJB-компонента с состоянием ShoppingCart.
4 package сою.deitel.advjhtpl.bookstore.ejb;
5
6 // Набор базовых пакетов Java
7 import java.rmi.RemoteException;
8
9 // Пакеты расширений Java
10 import javax.ejb. *,-
11
12 public interface ShoppingCartHome extends EJBHome {
13
14 // создание нового EJB-компонента ShoppingCart
15 public ShoppingCart create{)
16 throws RemoteException, CreateException;
17 ) , ___ ^__^__
Рис. 11.4. Интерфейс ShoppingCartHome для создания экземпляров EJB-компонента
ShoppingCart
Основные параметры развертывания для EJB-компонента ShoppingCart
Тип Bean Type
Класс Enterprise Bean Class
Собственный интерфейс
Home Interface
Удаленный интерфейс
Remote Interface
Stateful Session
com.deitel.advjhtpl.bookstore.ejb.ShoppirlgCartEJE
com.deitel.advjhtpl.bookstore.ejb.ShoppingCartHome
com.deitel.advjhtpl.bookstore.ejb.ShoppingCart
Рис. 11.5. Основные параметры развертывания для EJB-компонента ShoppingCart
Ссылки на EJB-компоненты для компонента ShoppingCart
Кодовое имя Coded Name
Тип Туре
Собственный интерфейс Ноте
Удаленный интерфейс Remote
Имя JNDI Name
Кодовое имя Coded Name
Тип Туре
Собственный интерфейс Ноте
ejb/Product
Entity
com.deitel.advjhtpl.bockstore.ejb.ProductHome
com.deitel.adviatpl.bookstore.ejb.Product
Product '
ejb/Order
Entity
com.deitel.advjhtpl.bookstore.ejb.OrderHome
Практический пример корпоративного приложения. Бизнес-логика: часть 1 543
Ссылки на EJB-компокенты для компонента ShoppingCart
Удзленный интерфейс Remote
Имя JNDI Name
com.deitel.advjhtpl.bookstore.ejb.Order
Order
Рис. 11,6. Ссылки на EJB-компоненты для компонента ShoppingCart
11.4. Реализация EJB-компонента Product
EJB-сущность Product использует контейнерное управление перси стентностью
для представления товара в Internet-магазине Deitel Bookstore. Контейнер EJB
реализует методы, которые осуществляют выборку, вставку, обновление и
удаление данные из базы данных. Администратор развертывания должен предоставить
информацию о том, как создавать таблицы в базе данных, а также задать на этапе
развертывания SQL-запросы, используемые для методов создания (create),
удаления (remove) и поиска (finder).
11.4.1. Удаленный интерфейс Product
Удаленный интерфейс Product (рис. 11.7) объявляет метод getProductModel
(строки 17-18), который возвращает объект ProductModel, содержащий
информацию о товаре.
1 // Product.Java
2 // Product - удаленный интерфейс для EJB-сущности Product.
3 pacfcage com.deitel,advjhtpl.bookstore.ejb;
4
5 // Набор базовых пакетов Java
6 import j ava.rmi.RemoteException;
7
8 // Пакеты расширений Java
9 import javax.ejb.*;
10
11 // Пакеты Deitel
12 import com.deitel.advjhtpl.bookstore.model.*;
13
14 public interface Product extends EJBObject 1
15
16 // получение сведений о товаре в виде объекта ProductModel
17 public ProductModel getProductModel()
18 throws RemoteException;
19 )
Рис. 11.7. Удаленный интерфрйг Product для внесения изменений в экземпляры
EJB-компонента Product, содержащие сведения о товаре
11.4.2. Реализация ProductEJB удаленного интерфейса Product
Реализация ProductEJB удаленного интерфейса Product (рис. 11.8) использует
контейнерное управление персистентностью. Контейнер EJB управляет
синхронизацией с содержимым базы данных открытых (public) элементов данных,
объявленных в строках 18-24. Метод getProductModel (строки 27—43) создает объект
ProductModel, который содержит информацию о товаре, В строке 30 создается эк-
544
Глава 11
земпляр класса ProductModel, а в строках 33-39 вызываются set-методы для
инициализации элементов данных объекта ProductModel.
1 // ProductsJB.java
2 // EJB-сущность Product представляет книгу и
3 // содержит такую информации, как ISBN-код, издатель, автор,
4 // название, цена, количество страниц и рисунок обложки.
5 package com.deitel.advjhtpl.bookstore.ejb;
б
111 Пакеты расширений Java
8 import javax.ejb.*;
9
10 // Пакеты Deitel
11 import com.deitel.advjhtpl.bookstore.model.*;
12 import com,deitel.advjhtpl.bookstore.*;
13
14 public class ProduetEJB implements EntityBean {
15 private EntityContext entityContext;
16
17 // поля, управляемые контейнером
18 public String ISBN;
19 public String publisher;
20 public String author;
21 public String title;
22 public double price;
23 public int pages;
24 public String image;
25
26 // получение сведений о книге в виде объекта ProductModel
27 public ProductModel getProduetModel()
28 {
29 // создание нового объекта ProductModel
30 ProductModel productModel = new ProductModel();
31
32 // инициализация объекта ProductModel данными иэ компонента
Product
33 productModel.setISBN( ISBN );
34 productModel.setPublisher( publisher );
35 productModel.setAuthorf author );
36 productModel.setTitle( title );
37 productModel.setPrice( price ) ;
38 productModel.setPages( pages );
39 productModel.setImage( image );
40
41 return productModel;
42
43 } // конец метода getProduetModel
44
45 // задание сведений о книге с использованием объекта ProductModel
46 private void setProductModel( ProductModel productModel )
47 {
48 // заполнение элементов данных компонента Product
49 // данными из предоставленного объекта ProductModel
50 ISBH = productModel.getlSBN();
51 publisher = productModel.getPublisher();
52 author = productModel.getAuthor();
53 title = productModel.getTitie(J;
54 price - productModel .getPrice 0-"
Практический пример корпоративного приложения. Бизнес-логика: часть 1 545
55 pages = productModel.getFages{) ,-
56 image = productModel.getImage();
57
58 } // конец метола setProductModel
59
60 // создание экземпляра EJB-компонента Product
с использованием заданного объекта ProductModel
61 public String ejbCreate{ ProductModel productModel )
62 <
63 setProductModel( productModel );
64 return null;
65 }
66
67 // выполнение необходимых после создания объекта действий
68 public void ejbPostCreatet ProductModel productmodel } (}
69
70 // задание контексша EntityContext
71 public void setEntityContext( EntityContext context )
72 {
73 entityContext = context;
74 }
75
76 // сброс контекста EntityContext
77 public void unsetEntityContextf)
78 {
79 entityContext = null;
SO )
81
82 // активация экземпляра EJB-компонента Product
83 public void ejbActivate(>
84 {
85 ISBN = ( String ) entityContext.getPrimaryKey();
86 }
87
88 // пассивация экземпляра EJB-компонента Product
89 public void ejbPassivate()
90 {
91 ISBN = null;
92 }
93
94 // удаление экземпляра EJB-компонента Product
95 public void ejbRemoveO {>
96
97 // сохранение данных EJB-компонента Product в базе данных
98 public void eJbStoreO ()
99
100 // загрузка данных EJB-компонента Product из базы данных
101 public void eJbLoadO {)
102 1 ____^____
Рис. 11.8. Реализация ProductEJB удаленного интерфейса Product
Метод setProductModel (строки 46-58) устанавливает подробную информацию
о товаре с использованием значений из заданной модели ProductModel. В строке
50 для элемента данных ISBN устанавливается значение ISBN-кода,
содержащегося в аргументе ProductModel. В строках 51-56 устанавливаются значения других
элементов данных ProductEJB. Метод ejbCreate (строки 61-65) принимает аргу-
546
Глава 11
мент Product Model. Метод ejbCreate вызывает метод setProdnctModel с
предоставленным объектом ProductModel для инициализации экземпляра EJB-компонента
Product (строка 63).
11.4.3. Собственный интерфейс ProductHome
Интерфейс ProductHome (рис. 11.9) создает новые экземпляры
EJB-компонента ProductEJB и объявляет методы поиска для нахождения имеющихся товаров
(компонентов Product). Метод create (строки 18-19) соответствует методу
ejbCreate, используемому в листинге, представленном на рис. 11,8, и обеспечивает
интерфейс для создания экземпляра EJB-компонента ProductEJB. Метод findBy-
PrimaryKey (строки 22-23) принимает в качестве строкового аргумента ISBN-код
для определенной книги в базе данных. Метод findAUProducts (строки 26-27)
возвращает коллекцию всех книг, содержащихся в базе данных. Метод findByTitle
(строки 30-31) осуществляет поиск книг, названия которых содержат заданную
строку searchString, и возвращает коллекцию удаленных ссылок на товары
(компоненты Product). Контейнер EJB реализует каждый из методов поиска с
использованием SQL-запросов, которые администратор развертывания должен
предоставить на этапе развертывания.
1 // ProductHome.Java
2 // ProductHome - собственный интерфейс для ЕJB-сущности Product.
3 package com.deitel.advjhtpl.bookstore,еЗЬ;
4
5 // Набор базовых пакетов Java
6 import. Java.rmi.RemofceException;
7 import java.util,Collection;
8
9 // Пакеты расширений Java
10 import javax.ejb.*;
11
12 // Пакеты Deitel
13 import com.deitel.advjhtpl.bookstore.model.*;
14
IS public interface ProductHome extends EJBHome (
16
17 // создание EJB-компонента Product с использованием
заданного объекта ProductModel
18 public Product create( ProductModel productModel )
19 throws RemoteException, CreateException;
20
21 // нахождение книги с заданным ISBN-кодом
22 public Product findByPrimaryKey( String isbn )
23 throws RemoteException, FinderException;
24
25 // нахождение всех книг
26 public Collection findAUProducts ()
27 throws RemoteException, FinderException;
2»
29 // нахождение книг с заданным названием
30 public Collection findByTitle( String title )
31 throws BemoteException, FinderException;
32)
Рис. 11.9. Интерфейс ProductHome для нахождения и создания экземпляров
EJB-компонента Product
Практический пример корпоративного приложения. Бизнес-логика: часть 1 547
11.4.4. Класс Product Model
Класс ProductModel (рис. 11.10) реализует интерфейс типа Serializable, чтобы
можно было осуществлять сериализацию экземпляров через RMI-IIOP. Класс
ProductModcl имеет частный (private) элемент данных (строки 18-25) и методы
set и get (строки 28-109) для каждого свойства EJB-компонента Product.
1 // ProductModel.java
2 // Класс ProductModel представляет книгу в Internet-магааине
3 // Deitel Bookstore и содержит ISBN-код, автора, название и рисунок
обложки книги.
4 package com.deitel.advjhtpl.bookstore.model;
5
6 // Набор базовых пакетов Java
7 import java.io.*;
8 import Java.util.*;
9 import Java.text.*;
10
11 // Пакеты сторонних поставщиков
12 Import org.w3c.dom.*;
13
14 public class ProductModel implements Serializable,
15 XMLGanerator {
16
17 // свойства объекта ProductModel
18 private String ISBN;
19 private String publisher;
20 private String author;
21 private String title;
22 private double price;
2 3 private int pages;
24 private String image;
25 private int quantity;
26
27 // задание ISBN-кода
28 public void setISBN{ String productlSBN )
29
30
31
32
33 // получение ISBN-кода
34 public String getlSBN()
35
36
37
38
39 // задание издателя
40 public void setPublisher{ String productPublisher )
41
42
43
44
45 // яолучвние издателя
46 public String getPublisher()
47 {
48 return publisher;
ISBN = productlSBN;
return ISBN;
publisher = productPublisher:;
548
}
// задание автора
public void setAuthor( string productAuthor )
author = productAuthor;
// получение автора
public String getAuthor()
return author;
// Задание названия книги
public void setTitle( String produCtTitle )
title = productTitle;
// получение названия книги
public String getTitle()
return title;
// задание цены
public void setPrice( double productPrice )
price = productPrice;
// получение цены
public double getPrice()
return price;
// задание количества страниц
public void setPages{ int pageCount )
pages = pageCount;
// получение количества страниц
public int getPagesf)
return pages;
// задание DRL файла рисунка обложки
public void setImage( String productlmage )
image = productlmage;
Практический пример корпоративного приложения. Бизнес-логика: часть 1 549
105 // получение UKL файла рисунка обложки
106 public String getImage(}
107 {
108 return image;
109 }
110
111 // получение XKL-предетавлекия товара (книги)
112 public Element getXML{ Document document )
113 {
114 // создание элемента product
115 Element product = document.createElement{ "product" J;
116
117 // создание элемента ISBN
118 Element temp = document.createElement( "ISBN" );
119 temp.appendChild(
120 document.createTextNode( getISBN() ) );
121 product.appendChild( temp );
122
123 // создание элемента publisher
124 temp = document.createElement( "publisher" );
125 temp.appendChild(
126 document.createTextNode( getPublisher() ) );
127 product.appendChild( temp );
128
129 // создание элемента author
130 temp = document.createElement( "author" );
131 temp.appendChild(
132 document.createTextNode( getAuthorO ) };
133 product.appendChiId{ temp );
134
135 // создание элемента title
136 temp = document.createElement( "title" );
137 temp.appendCh iId(
138 document.createTextNode^ getTitleO > } :
139 product.appendChild( temp ) ;
140
141 HumberFonnat priceFormatter =
142 NumberFormat.getCurrencylnstance( Locale.US );
143
144 // создание элемента price
145 temp = document.createElement( "price" );
146 temp.appendChild( document.createTextNode(
147 priceFormatter.format( getPrice() ) ) };
148 product.appendChild( temp );
149
150 // создание элемента pages
151 temp = document.createElement( "pages" };
152 temp.appendChi1d( documen t.crea teTextMode{
153 String.valueOf( getPages() ) ) );
154 product.appendChild( temp );
155
156 // создание элемента image
157 temp = document.createElement( "image" j;
158 temp.appendChi1d(
159 document.createTextNode( getImage О ) );
160 product.appendChild( temp );
550
Глава 11
161
162
163
164
165 }
return product;
} // конец метода getXML
Рис. 11.10. Класс Product Model для поиска данных в EJB- компоненте Product
Класс Product Model также реализует интерфейс X ML Generator (рис. 11.11),
который определяет единственный метод getXML. Метод getXML класса Product-
Model (строки 112-164) генерирует элемент (объект Element) XML для данных,
содержащихся в объекте ProductModel. Этот метод использует аргумент Document
для создания элементов XML для каждого объекта (элемента) данных класса Рго-
ductModel, однако метод getXML при этом не модифицирует документ (объект
Document). В строке 162 возвращается вновь созданный элемент Product.
1 // XMLGenerator.Java
2 // XMLGenerator - интерфейс для классов, которые способны создавать
3 // элементы XML. ХМЬ-элемент, возвращаемый методом getXML,
4 // должен содержать элементы для каждого открытого свойства.
5 package com.deitel.advjhtpl.bookstore.model;
6
7 // Пакеты сторонних поставщиков
8 import org.w3c.dom.*;
9
10 public interface XMLGenerator i
11
12 // формирование XML-элемекта для заданного объекта
13 public Element getXML( Document document );
14 ) —^_ - .
Рис. 11.11. Интерфейс XMLGenerator для генерирования элементов XML для открытых
свойств
На рис. 11.12 и 11,13 представлены параметры развертывания для
компонента-сущности EJB Product. В дополнение к этим параметрам, для всех
бизнес-методов следует установить значение Required для типа транзакции Transaction Type.
Основные параметры развертывания для EJB-компонента Product |
1ИГ1 Bean Type
Класс Enterprise Bean Class
Собственный интерфейс Home
Interface
Удаленный интерфейс Remote
Interface
Entity 1
com.deitel.advjhtpl.bookstore.ejb.ProductEJB |
com.deifcel.advjhtpl.bookstore.ejb.ProductHome 1
com.deitel.advjhtpl.bookstore.ejb.Product
Рис. 11.12. Основные параметры развертывания для EJB-компонента Product
Параметры управления данными и развертыванием для EJB-компонента Product
Управление персистентностью
Persistent Management
Container-Managed Persistence
Класс первичного ключа
Primary Key Class
java.lang.String
Практический пример корпоративного приложения. Бизнес-логика: часть 1 551
Параметры управления данными и развертыванием для EJB-компонента Product
Имя поля первичного ключз
Primary Key Fietd Name
Имя базы данных JNDI
Database JNDI Name
SQL-оператор SQL Statement
метода findByTitle
SQL-оператор SQL Statement
метода findAIIProducts
SQL-оператор SQL Statement
метода ejbStore
SQL-оператор SQL Statement
метода ejbCreate
SQL-оператор SQL Statement
метода ejbRemove
SQL-оператор SQL Statement
метода findByPrimaryKey
SQL-оператор SQL Statement
метода ejb Load
SQL-оператор создания
таблицы SQL Statement Table
Create
SQL-оператор удаления таблицы
SQL Statement Table Delete
ISBN
jdbc/Books tor*
SELECT ISBN FROM Product WHERE title LIKE ?1
SELECT ISBN FROM Product WHERE 1=1
UPDATE Product SET author=?, image=?, pages=?,
price=?, publisher=?, title=?, WHERE ISBN=?
INSERT INTO Product (ISBN, author, imag*, pages,
price, publisher, title) VALUES (?, ?, ?, ?, ?,
?, ?)
DELETE FROM Product WHERE ISBN=?
SELECT ISBN FROM Product WHERE ISBN=?
SELECT author, image, pages, price, publisher,
title FROM Product WHERE ISBN=?
CREATE TABLE Product (ISBN VARCHAR (255), author
VARCHAR (255), image VARCHAR (255) , pages
INTEGER NOT HULL, price DOUBLE PRECISION NOT
NULL, publisher VARCHAR (255), title VARCHAR
(255), CONSTRAINT pk_Product PRIMARY KEY (ISBN))
DROP TABLE Product
Рис. 11.13. Параметры управления данными и развертыванием для EJ В -компонента Product
11.5. Реализация EJ В-компонента Order
EJB-сущность Order представляет заказ, сделанный посетителем
Internet-магазина Deitel Bookstore. Каждый заказ Order содержит выбранные покупателем
товары Product с указанием их количеств, а также идентификатор customerlD
покупателя, разместившего заказ.
11.5.1. Удаленный интерфейс Order
Удаленный интерфейс EJB-компонента Order (рис. 11.14) определяет
бизнес-методы, доступные для этого EJB-компонента. Метод getOrderModel (строка 17)
возвращает объект OrderModel, содержащий информацию о заказе. Метод setShipped
(строки 20—21} делает отметку, что заказ был доставлен со склада. Метод is Shipped
(строка 24) возвращает булево значение, указывающее, был ли заказ доставлен.
11.5.2. Реализация OrderEJB удаленного интерфейса Order
Реализация OrderEJB (рис. 11.15) удаленного интерфейса Order объявляет
открытые методы для управления персистентностью на стороне контейнера (строки
26-29). Метод getOrderModel (строки 32-91) создает экземпляр класса Order-
552
Глава 11
Model, который содержит информацию о заказе. В строках 42-44 объект OrderMo-
del заполняется значениями элементов данных EJB-компонента Order.
1 // Order.java
2 // Order - удаленный интерфейс для EJB-супзяоети Order.
3 package com.deitel.advjhtpl.bookstore.ejb;
4
5 // Набор базовых пакетов Java
6 import Java.rmi.RemoteException;
7
8/7 Пакета расширений Java
9 import javax.ejb.*;
10
11 // Пакеты Deitel
12 import com.deitel.advjhtpl.bookstore.model.*;
13
14 public interface Order extends EJBObject {
15
16 // получение сведений о заказе в виде объекта OrderModel
17 public OrderModel getOxderModel() throws RemoteException;
18
19 // установка флага доставки shipped
20 public void setshipped( boolean flag )
21 throws RemoteException;
22
23 // получение флага доставки shipped
24 public boolean isShippedf) throws RemoteException;
25}
Рис. 11.14. Удаленный интерфейс Order для изменения информации, содержащейся
в экземплярах EJ В-компонента Order
1 // OrderEJB.Java
2 // EJB-сущность Order представляет заказ и
3 // содержит идентификатор заказа, дату заказа, обшув
4 // стойкость заказа и информации, был ли заказ доставлен.
5 package com.deltel.advjhtpl.bookstore.ejb;
€
7 // Набор базовых пакетов Java
8 import java.util.*;
9 import Java.text.DateFormat;
10 import Java.rmi.RemoteException;
11
\2 /I Пакеты расширений Java
13 import javax.ejb.*;
14 import javax.naming.*;
15 import javax.rmi.PortableRemoteObject;
16
17 // Пакеты Deitel
18 import com.deitel.advjhtpl.bookstore.model.*,
19
20 public class OrderEJB implements EntityBean {
21 private EntityContext entityContext;
22 private InitialContext initialContext;
23 private DateFormat dateForroat;
24
Практический пример корпоративного приложения. Бизнес-логика: часть 1 553
25 // поля, управляемые контейнером
26 public Integer orderID;
27 public Integer customerlD;
28 public String orderDate,*
29 public boolean shipped;
30
31 // получение сведений о заказе в виде объекта OrderModel
32 public OrderModel getOrderModel() throws EJBException
33 {
34 // создание нового объекта OrderModel
35 OrderModel orderHodel = new OrderModelО;
36
37 // поиск EJB-хомпонента OrderProduct для извлечения
38 // списка товаров, содержащихся в заказе
39 try (
40
41 // заполнение элементов данных OrderModel данными
из компонента Order
42 orderModel.setOrderID( orderlD );
43 orderModel.setOrderDate( dateFormat.parse( orderDate ) );
44 orderModel.setShipped( shipped );
45
46 initialContext = new InitialContext(};
47
48 Object object = initialContext.lookup (
49 "Java:comp/env/ejb/OrderProduct" );
50
51 OrderProductHome OrderProductHome =
52 ( OrderProductHome ) PortableRemoteObject.narrow(
53 object, OrderProductHome.class );
54
55 // получение записей таблицы OrderProduct для заказа
56 Collection orderProducts =
57 OrderProductHome.findByOrderlD( orderlD );
5B
59 Iterator iterator = orderProducts.iterator();
60
61 // объекты OrderProductModel, помещаемые в объект OrderModel
62 Collection orderProductModels = new ArrayListO;
63
64 // получение объекта OrderProductModel для каждого товара
в заказе
65 while { iterator.hasNext() ) [
66 OrderProduct OrderProduct = ( OrderProduct )
67 PortableRemoteObject.narrow) iterator.next(),
68 OrderProduct.class );
69
70 // получение объекта OrderProductModel для записи
таблицы OrderProduct
71 OrderProductModel orderProductModel =
72 OrderProduct.getOrderProductModel(};
73
74 // добавление объекта OrderProductModel в
75 // список объектов в компоненте Order
76 orderProduotModels.add( orderProductModel );
77 }
554
Глава 11
78
79 // добавление коллекции объектов OrderFrodtictModel
в объект OrderModel
80 orderModel .setOrderProductModele( orderProductModels )
81
82 } // конец блока try
83
84 // обработка исключения при работе с EJB-компонентом
OrderProduct
85 catch ( Exception exception ) (
86 throw new EJBException( exception \;
87 )
88
89 return orderModel;
90
91 } // конец метода getOrderModel
92
93 // установка флага доставки shipped
94 public void setshipped( boolean flag )
95 (
96 shipped = flag;
97 )
98
99 // получение флага доставки shipped
100 public boolean isShippedf)
101 I
102 return shipped;
103 J
104
105 // создание нового EJB-компонента Order с использованием
заданного объекта OrderModel и userXD
10 6 public Integer ejbCreete( OrderModel order, String userlD )
107 throws CreateException
108 {
109 // извлечение уникального значения для первичного ключа этого
110 // заказа с использованием EJB-компонента SequenceFactory
111 try (
112 initialContext = new InitialContext();
113
114 Object object = initialContext.lookup(
115 "javaicomp/env/ejb/SequenceFactory" );
116
117 SequenceFactoryHome SequenceFactoryHome =
118 ( SequenceFactoryHome )
119 PortableRemoteObjeCt,narrow(
120 object, SeguenceFactoryHome.class >;
121
122 // нахождение последовательности для таблицы CustomerOrder
123 SequenceFactory SequenceFactory =
124 seguenceFactoryHome.findByPrimaryKey(
125 "CustomerOrders" >;
126
127 // получение следящего уникального идентификатора orderlD
128 orderlD = SequenceFactory.getNextID();
129
130 // получение даты, стоимости, флага доставки и списка объ-
Практический пример корпоративного приложения. Бизнес-логика: часть 1 555
131 // ектов OrdarProduct иэ предоставленного объекта OrderModel
132 orderDate = dateFormat.formatf order, getOrderDate () );
133 shipped = order.getShipped();
134
135 // получение объектов OrderProductModel,
содержащих объекты OrderModel
136 Collection orderProductModels =
137 order.getOrderProductModels{);
138
139 // создание EJB-компонентов OrderProduct для каждого
140 // товара в заказе для отслеживания их количеств
141 object = initialContext.lookup(
142 "javarcomp/env/ejb/OrderProduct" );
143
144 OrderProductHome orderProductHome =
145 ( OrderProductHome } PortableRemoteObject.narrow(
146 object, OrderProductHome.class );
147
148 Iterator iterator = orderProductModels.iterator();
149
150 // создание EJB-компонента OrderProduct, содержащего
151 // ISBN-код, количество и номер для этого заказа
152 while { iterator.hasNext() ) {
153
154 OrderProductModel OrderProductModel =
155 ( OrderProductModel ) iterator.next();
156
157 // задание идентификатора orderlD для записи таблицы
OrderProduct
158 OrderProductModel.setOrderlD{ orderlD );
159
160 // создание экземпляра EJB-компонента OrderProduct
161 orderProductHome.create( OrderProductModel );
162 }
163
164 // получение идентификатора customerlD покупателя,
сделавшего заказ
165 object = initialContext.lookup(
166 "Java:comp/env/ejb/Customer" );
167
168 CustomerHome customerHome =
169 ( CustomerHome ) PortableRemoteObject.narrow(
170 object, CustomerHome. class ) ,*
171
172 // использование предоставленного идентификатора userlD
для нахождения покупателя
173 Customer customer =
174 customerHome.findByUserlD( userID );
175
176 customerlD = ( Integer ) customer.getPrimaryKey();
177
178 } // конец блока try
179
180 // обработка исключения при поиске ЕЛВ-компонентов
181 catch [ Exception exception ) {
182 throw new CreateException{ exception.getMessagef) );
556
Глава 11
163 }
184
185 return null;
186
187 } // конец метода ejbCreate
188
189 // выполнение необходимых после создания объекта действий
190 public void ejbPostCreate( OrdexModel order, String id ) {}
191
192 // задание контекста EntityContext
193 public void setEntityContext( EntityContext context >
194 {
195 entityContext = context;
196 dateFoxmat = DateFormat.getDateTimelnstance(
197 DateFoxmat.FULL, DateFoxmat.SHORT, Locale.OS );
198 }
199
200 // сброс контекста EntityContext
201 public void. unsetEntityContext()
202 {
203 entityContext = null;
204 >
205
206 // активация экземпляра БJB-компонента Order
207 public void ejbActivate()
208 {
209 orderlD = ( Integer ) entityContext.getPrimaxyKey () ,-
210 }
211
212 // пассивация экземпляра EJB-компонента Order
213 public void ejbPassivate()
214 {
215 orderlD = null;
216 }
217
218 // удаление экземпляра EJB-жомпонента Order
219 public void ejbRemoveO {}
220
221 // сохранение данных EJB-компонента Order в базе данных
222 public void ejbStore() {}
223
224 // загрузка данных EJB-коипонента Order из базы данных
225 public void ejbLoad<) {}
226 }
Рис, 11.15. Реализация OrderEJB удаленного интерфейса Order
Помимо атрибутов ordcrDatc, orderlD и флага shipped, EJB-компонент Order
содержит список объектов OrderProductModel. Отношение между заказом
(компонент Order) и входящими в него товарами (компоненты Product) и их количеством
представлено в базе данных в виде отношения «многие ко многим» (т.е. заказ
может включать множество товаров, и один и тот же товар может фигурировать в
нескольких заказов). В EJB-компоненте OrderProduct это отношение реализуется
путем установки соответствия между идентификатором заказа orderlD и
ISBN-кодам и ISBN книг, входящих в заказ. Для каждого заказа Order в таблице
OrderProduct имеются записи, содержащие ISBN-код и количество для каждой из
Практический пример корпоративного приложения. Бизнес-логика: часть 1 557
книг в заказе. Например, если покупатель заказывает один экземпляр книги Как
программировать на Java и два экземпляра книги Технологии программирования
на Java 2, в таблице OrderProduct будут иметься две записи. В каждой записи
идентификатор orderlD будет одним и тем же, но в одной из них поле ISBN будет
содержать ISBN-код книги Как программировать на Java, а поле quantity —
значение 1, тогда как в другой записи поле ISBN будет содержать ISBN-код для книги
Технологии программирования на Java 2, а поле quantity — значение 2.
В строках 56-57 вызывается метод find By Order ID для получения записей
таблицы OrderProduct для заказа. Метод findByOrderlD возвращает коллекцию
удаленных ссылок на объекты OrderProduct. В строках 65-77 осуществляется обход
коллекции удаленных ссылок на объекты OrderProduct с использованием
итератора (интерфейс Iterator), а в строках 71-72 извлекается объект OrderProduct-
Model для каждой записи в таблице OrderProduct. В строке 76 каждый из
объектов OrderProductModel добавляется в коллекцию. В строке 80 коллекция
объектов OrderProductModel добавляется в объект OrderModel, а в строке 89
возвращается вновь созданный объект OrderModel.
Метод set Shipped (строки 94—97) принимает булевый (boolean) аргумент и
обновляет флаг shipped EJ В-компонента Order. Приложение, отслеживающее
поступившее заказы, может использовать метод setShipped для обновления статуса
заказа при доставке заказа со склада. Метод isShipped (строки 100—103) возвращает
текущее значение элемента данных shipped, указывающее, был ли заказ
доставлен со склада.
Метод ejbCreate (строки 106-187) создает EJB-компонент Order с
использованием данных из указанного объекта OrderModel и значения userlD. Каждый заказ
(компонент Order) имеет соответствующий идентификатор orderlD, который
служит в качестве первичного ключа в таблице базы данных EJB-компонента Order.
Метод getNextlD интерфейса SequenceFactory генерирует уникальный
идентификатор заказа orderlD {строка 128).
В строках 132-133 осуществляется заполнение EJB-компонента Order
данными из объекта OrderModel. Объект OrderModel также предоставляет товары
Product и их количества в заказе Order в виде коллекции объектов
OrderProductModel. В строках 152-162 осуществляется обработка коллекции объектов
OrderProductModel, и создаются записи OrderProduct для каждого из них с
использованием метода create интерфейса OrderProduct Home (строка 161).
Каждый заказ имеет соответствующего покупателя, сделавшего этот заказ. Это
соответствие представляет собой отношением «один ко многим», поскольку один
покупатель может сделать несколько заказов, но заказ может ассоциироваться
лишь с одним покупателем. В строках 173-174 извлекается EJB-компонент
Customer для данного идентификатора userlD. Метод getPrimaryRey
EJB-компонента Customer извлекает идентификатор покупателя customer ID, В строке 176
задается идентификатор покупателя customerlD для заказа.
11.5.3. Собственный интерфейс OrderHome
Интерфейс OrderHome (рис. 11.16) создает экземпляры EJB-компонента Order
и находит имеющиеся заказы. Метод create (строки 18-19) соответствует методу
ejbCreate из листинга, представленного на рис. 11.15, и создает новые компоненты
Order с использованием модели OrderModel и идентификатора userlD. Метод
findByPrimaryKey (строки 22-23) находит имеющиеся заказы по их
идентификаторам orderlD. Метод findByCustomcrlD (строки 26-27) извлекает коллекцию
EJB-компонентов Order для данного покупателя Customer.
558
Глава 11
1 // OrderHome.java
2 // OrderHome - собственный интерфейс компонента-сущности EJB Order.
Ъpackage com.de itel.advjhtpl.books tore.ej Ь;
4
5 // Набор базовых пакетов Java
6 import j ava.util.*;
7 import java.rmi.ВетоteException;
8
9 // Пакеты расширений Java
10 import javax.ejb.*;
11
12 // Пакеты Deitel
13 import coa.deitel.advjbtpl.bookstore.model,*;
14
15 public interface OrderHome extends EJBHome {
16
17 // создание компонента Order с использованием заданного объекта
OrderModel и идентификатора userlD
18 public Order create( OrderModel orderModel, String userlD )
19 throws RemoteSxception, CreateException;
20
21 // поиск заказа по его идентификатору orderlD
22 public Order findByPrimaryKey{ Integer orderID )
23 throws RemoteException, FinderException;
24
25 // поиск заказов для покупателя с заданным идентификатором
CuatomerlD
26 public Collection findByCustomerlD( Integer customerID )
27 throws RemoteException, FinderException;
28 I
Рис. 11.16. Интерфейс OrderHome для поиска и создания экземпляров EJB-компонента
Order
11.5.4. Класс OrderModel
Класс OrderModel (рис. 11.17) инкапсулирует информацию из EJB-компонента
Order в сериализуемый (Serializable) объект, пригодный для доставки через
RMI-H0P. Класс OrderModel имеет частные (private) элементы данных (строки
19-22) с соответствующими методами set и get (строки 31-102) для каждого
элемента данных EJB-компонента Order. Класс OrderModel также содержит
коллекцию объектов OrderProductModel для отслеживания товаров в заказе. Класс
OrderModel реализует интерфейс XMLGenerator и метод getXML,
способствующий формированию XML-представления заказа (строки 105-166).
1// OrderModel. java
2 // Класс OrderModel представляет заказ и содержит
3 // идентификатор заказа, дату, общую стоимость заказа,
4 // а также булево значение, указывайте, был пи заказ доставлен.
5 package com.deitel,advjhtpl.bookstore.model;
6
7 // Набор базовых пакетов Java
8 import java.io.*;
Э import java.util.*;
10 import java.text.*;
Практический пример корпоративного приложения. Бизнес-логика: часть 1
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
// Пакеты сторонних поставщиков
import org.w3c.dora.*;
public class OrderModel implemen'ts Serializable,
XKLGenerator {
// свойства объекта OrderModel
private Integer orderlD;
private Date orderDate;
private boolean shipped,-
private Collection orderProductModels;
// формирование пустого объекта OrderModel
public OrderModel()
orderProductModels = new ArrayListO.
// задание идентификатора заказа
public void setOrderlD( Integer id )
orderlD = id;
// получение идентификатора заказа
public Integer getOrderID()
return orderlD;
// задание даты заказа
public void setOrderDate( Date date }
orderDate = date;
// получение даты заказа
public Date get0rderDate<)
return orderDate;
// получение общей стоимости заказа
public double getTotalCostO
{
double total =0.0;
Iterator iterator = orderProductModels.iterator();
// подсчет общей стоимости заказа
while f iterator.hasNextO ) {
// получение следующего товара в магазинной тележке
OrderProductModel OrderProductModel =
( OrderProductModel ) iterator.next();
560
67
6 В ProductModel productModel =
69 orderProductModel.getProductModel();
70
71 // добавление стоимости данного товара к общей стоимости
72 total += С productModel.getSriceО *
73 orderProductModel.getQuantity() );
74 }
75
76 return total;
77 }
78
79 // установка флага доставки
80 public void setshippedt boolean orderShipped )
81 {
82 shipped = orderShipped;
83 }
84
85 // получение флага доставки
86 public boolean getShipped(>
87 {
88 return shipped;
89 }
90
91 // задание списка объектов OrderProductModel
92 public void setOrderProductModels( Collection models )
93 {
94 orderProductModels = models;
95 }
96
97 // получение списка объектов OrderProductModel
98 public Collection getOrderProductModels()
99 {
100 return Collections.unmodifiableCollection(
101 orderProductModels ) ,-
102 }
103
104 // получение XML-представления заказа
105 public Element getXML( Document document. )
106 {
107 // создание элемента order
108 Element order = document.createElement( "order" );
109
110 // создание элемента orderID
111 Element temp = document.createElement( "orderID" );
112 temp.appendchild( document.createTextNode(
113 String.valueOf( getOrderID() ) ) );
114 order.appendChild{ temp );
115
116 // получение формата OateFormat для записи даюшх в XML-документ
117 DateFormat formatter = DateFormat.getDateTimeInstance(
118 DateFormat.DEFAULT, DateFormat.MEDIOM, Locale.US J;
119
120 // создание элемента orderDate
121 temp = document.createElement( "orderDate" };
122 temp,appendChild( document.createTextNode(
Практический пример корпоративного приложения. Бизнес-логика: часть 1 561
123 formatter.format( getOrderDate() ) ) );
124 order.appendChild{ temp );
125
126 NumbexFormat costFormatter =
127 NumberFormat.getCurrencytnstancet Locale.US );
128
129 // создание элемента totalCost
130 temp = document.createElement{ "totalCost" );
131 temp.appendChild( document.createTextNode(
132 costFormatter.format{ getTotalCost() ) ) );
133 order.appendChild( temp );
134
135 // создание элемента shipped
136 temp = document.createElement( "shipped" );
137
138 if ( getShippedO )
139 temp.appendChild(
140 document.createTextNode( "yes" ) );
141 else
142 temp.appendChild(
143 document.createTextNode[ "no" ) };
144
145 order . appendChild ( temp ) ,-
146
147 // создание элемента oirderProducts
148 Element orderProducts =
149 document.createElement( "orderProducts" );
150
151 Iterator iterator = getOrdetProductModels().iterator();
152
153 // добавление элемента orderProduct для каждого объекта
OrderProduct
154 while ( iterator.hasNextО ) {
155 OrderProductModel orderProductModel =
156 ( OrderProductModel } iterator.next();
157
158 orderProducts.appendChild(
159 orderProductModel.getXML( document ) );
160 }
161
162 order.appendChild( orderProducts );
163
164 return order;
165
166 ) // конец метода getXML
167 1
Рис. 11.17. Класс OrderModel для серизлиэации данных EJВ-компонента Order
В таблицах на рис. 11.18, 11.19 и 11.20 представлены параметры
развертывания для EJB-сущности Order. В дополнение к приведенным здесь параметрам
следует установить для типа транзакции Transaction Type значение Required для
всех бизнес-методов.
Глава 11
'вертывания для EJB-компонента Order
:s
■me
■»
Entity
com,deitel.advjhtpl,bookstore,ejb.
com.deitel.advjhtpl.bookstore.ejb.
com,deitel.advjhtpl.bookstore.ejb.
OrderEJB
OrderHome
Order
ы оэзвеотыеания для EJB-компонента Order
ыми и развертыванием для EJB-компонента Order
'■
jry
dse
:
..■t
wit
ГОЛИЦЫ
.merit
^|Я таблицы
cement
Container-Managed Persistence
Java.lang,Integer
orderID
j dbc/Book з tore
SELECT orderlD FROM CustomerOrders WHERE
CustomerID=?l
UPDATE CustomerOrders SET customerID=?,
orderData=?, orderID, shipped»?, Where
orderID=?
INSERT INTO CustomerOrders (customerID,
orderData, orderlD, shipped) VALUES (?, ?,
?, ?)
DELETE FROM Сиз tore rOrdars WHERE ordexXD=?
SELECT orderlD FROM CustomerOrders WHERE
orderID=?
SELECT customerlD, orderData, orderlD,
shipped FROM CustomerOrders WHERE orderID=?
CREATE TABLE CustomerOrders (customerlD
INTEGER, orderData VARCHAR (255), orderlD
INTEGER, shipped BOOLEAN NOT NULL, CONSTRAINT
pk_CustomerOrders PRIMARY KEY (orderlD) }
DROP TABLE CustomerOrders
u, управления данными и развертыванием для EJB-компонента Order
->мпоненты для компонента Order
jded Name
.нтерфейс Home
герфейс Remote
ejb/Product
Entity
com,deitel■advjhtpl.bookstore.ejb.ProductHome
com.deitel■advjhtpl.bookstore.ejb.Product
Практический пример корпоративного приложения. Бизнес-логика: часть 1 563
Ссылки на EJB-компоненты для компонента Order
Имя JNDI Name
Кодовое имя Coded Name
Тип Туре
Собственный интерфейс Ноте
Удаленный интерфейс Remote
Имя JNDI Name
Кодовое имя Coded Name
Тип Туре
Собственный интерфейс Ноте
Удаленный интерфейс Remote
Имя JNDI Name
Кодовое имя Coded Name
Тип Туре
Собственный интерфейс Ноте
Удаленный интерфейс Remote
Имя JNDI Name
Product
ej b/SequenceFactory
Entity
com.deitel.advjhtpl.bookstore,ejb.Sequence-
FactoryHome
com.deitel.advjhtpl.bookstore.ejb.Sequence-
Factory
SequenceFactory
ejb/Customer
Entity
com,deitel.advjhtpl.bookstore.ejb.CuatomerHome
com.deitel.advjhtpl.bookstore.ejb.Customer
Customer
ejb/OrderFroduct
Entity
com.deitel.advjhtpl.bookstore.ejb.OrderProduct-
Home
com.deitel.advjhtpl.bookstore.ejb.OrderProduct
OrderProduct
Рис. 11.20. Ссылки на EJB-компоненты для компонента Order
11.6. Реализация EJB-компонента OrderProduct
Компонент-сущность BJB OrderProduct представляет отношение «многие ко
многим» между заказами и товарами. Каждый EJB-компонент OrderProduct
имеет атрибуты orderlD, ISBN и quantity и представляет один элемент заказа.
11.6,1. Удаленный интерфейс OrderProduct
Удаленный интерфейс OrderProduct {рис. 11.21) определяет бизнес-методы для
EJB-компонента OrderProduct. EJB-компонент OrderProduct устанавливает
соответствие между ISBN-кодом товара и идентификатором заказа, а также
количеством заказываемого товара. Метод getOrderProductModel (строки 18—19)
возвращает объект OrderProductModel, содержащий информацию из записи таблицы
OrderProduct.
1 // OrderProduct.Java
2 // OrderProduct - удаленный интерфейс для EJB-сущности
3 // OrderProduct.
4 package com.deitel.advjhtpl.bookstore.ejb;
5
6 // Набор базовых пакетов Java
7 import Java.rmi.RemoteException;
a
9 // Пакеты расширений Java
564
Глава 11
10 import javax.ejb.*;
11
12 // Пакеты Deitel
13 import com.deltel.advjhtpl.bookstore.model.*;
14
15 public interface OrderProduct extends EJBObjeet {
16
17 // получение сведений о заказе в виде объекта OrderProductModel
18 public OrderProductModel getOrderProductModel0
19 throws RemoteException;
20 }
Рис. 11.21. Удаленный интерфейс OrderProduct для изменения информации,
содержащейся в экземплярах EJ В-компонента OrderProduct
11.6.2. Реализация OrderProductEJB удаленного интерфейса
OrderProduct
Реализация OrderProductEJB удаленного интерфейса OrderProduct (рис. 11.22)
объявляет поля ISBN, orderlD и quantity (строки 22-24), управляемые
контейнером. Метод getOrderProductModel (строки 27-69) возвращает информацию из
записи таблицы OrderProduct в виде объекта OrderProductModel. Метод setOrder-
ProductModel (строки 75-77) задает данные для записи таблицы OrderProduct
с использованием данных, извлеченных из параметра OrderProductModel.
1 // OrderProductEJB.Java
2 // EJB-сущностъ OrderProductEJB устанавливает
3 // соответствие между товаром и заказом м содержит
4 // информацию о количестве данного товара в захаае.
5 package com.deitel.advjhtpl.bookstore.ejb;
6
7 // Набор базовых пакетов Java
8 import java.rmi.RemoteException;
9
10 // Пакеты расширений Java
11 import javax.ejb.*;
12 import j avax.naming.*;
13 import javax.rmi.PortableRemoteObject;
14
15 // Пакеты Deitel
16 import com.deitel.advjhtpl.bookstore.model.*;
17
18 public class OrderProductEJB implements EntityBean {
19 private EntityContext entityContext;
20
21 // поля, управляемые контейнером
22 public String ISBN;
23 public Integer orderlD;
24 public int quantity;
25
26 // получение сведений о заказе в виде объекта OrderProductModel
27 public OrderProductModel getOrderProductModel()
28 throws EJBException
29 {
30 OrderProductModel model = new OrderProductModel();
Практический пример корпоративного приложения. Бизнес-логика: часть 1 565
31
32 // получение объекта ProduetModel для товара в указанном заказе
33 try {
34 Context initialContext = пей InitialContext();
35
36 // поиск EJB-компокекта Product
37 Object object = initialContext.lookup(
38 "java:comp/env/ejb/Product" );
39
40 // получение интерфейса ProductHome
41 ProductHome productHome = ( ProductHome >
42 PortableRemoteObject.narrow( object,
43 ProductHome.class );
44
45 // нахождение книги по ее ISBN-коду
4 6 Product product =
47 productHome.findByPrimaryKey( ISBN );
48
49 // получение объекта ProduetModel
50 ProduetModel productModel =
51 product.getProductModel();
52
53 if установка объекта ProductModel s объекте OrderProductModel
54 model.setProductModel( productModel );
55
56 ) // конец блока try
57
58 // обработка исключения при поиске EJB-компонента Product
59 catch ( Exception exception } {
60 throw лен EJBSxception( exception );
61 }
62
63 // запись идентификатора заказа и количества товаров в объект
OrderProductModel
64 model.setOrderlO( orderlD );
65 model.setQuantity( quantity );
66
67 return model;
68
69 } // конец метода getOrderProductModel
70
71 // задание сведений о заказе с использованием объекта
OrderProductModel
72 private void setOrderProductModel( OrderProductModel model )
73 (
74 ISBN = model.getProductModel().getISBH();
75 orderlD = model.getOrderlD();
76 quantity = model.getQuantity();
77 }
78
7 9 // создание объекта OrderProduct для данного объекта
OrderProductModel
80 public OrderProductPK ejbCreate[ OrderProductModel model )
81 {
82 setOrderProductModel( model ) ;
83 return null;
84 )
566
Глава 11
85
86 (( выполнение необходимых после создания обчехта действий
87 public void ejbPostCreate( OrderProductModel model ) {)
88
89 // задание контекста Entity/Context
90 public void setEntityContext( EntityContext context )
91 {
92 eivtityContext = context;
93 }
94
95 // сброс контекста EntityContext
96 public void unsetEntityContextO
97 {
98 entityCoiitext = mill;
99 }
100
101 // активация экземпляра EJB-компонента OrderProduct
102 public void ejbActivate{)
103 {
104 OrderProductPK primaxyKey =
105 ( OrderProductPK ) entityContext.getPrimaryKey();
106
107 ISBN = primaryKey.getlSBNO ;
108 orderlD = primaryKey,getOrderlD{);
109 }
110
111 // пассивация экземпляра EJB-компонента OrderProduct
112 public void ejbPassivate{)
113 {
114 ISBN = null;
115 orderlD = null;
116 l
117
118 // удаление экземпляра EJB-компонента OrderProduct
119 public void ejbReraove() {}
120
121 // сохранение данных EJB-компонента OrderProduct в базе данных
122 public void ejbStoreO il
123
124 // загрузка данных EJB-компонента OrderProduct из базы данных
125 public void ejbLoad() [}
126 }
Рис. 11.22. Реализация OrderProduttEJB удаленного интерфейса OrderProduct
Контейнер EJB вызывает метод ejbCreate (строки 80-84) для создания новых
экземпляров EJB-компонента OrderProduct. Атрибуты ISBN, orderlD и quantity
для записи таблицы OrderProduct предоставляются в параметре OrderProduct-
Modcl. В строке 82 иы-зываетея метод setOrderProductModd для завершения
создания записи OrderProduct.
11.6.3. Собственный интерфейс OrderProductHome
Интерфейс OrderProductHome (рис. 11.23) предоставляет методы для создания
новых экземпляров ЕЛВ-компонента OrderProduct и нахождения имеющихся
записей в таблице OrderProduct. Метод create (строки 19-20) соответствует методу
Практический пример корпоративного приложения. Бизнес-логика: часть 1 567
ejb Create реализации OrderProductEJB интерфейса OrderProduct (рис. 11.22).
Метод findByOrderlD (строки 23-24) обнаруживает все записи таблицы Order-
Product с заданным идентификатором orderlD и возвращает коллекцию
удаленных ссылок на объекты OrderProduct. Метод fin dBy Primary Key (строки 27-28)
находит запись OrderProduct с использованием класса первичного ключа Order-
ProductPK.
1 // OrderProductHome.java
2 // OrderProductHome - собственный интерфейс для EJB-
3 // сущности OrderProduct.
4 package com.deitel.advjhtpl.bookstore.ejb;
5
6 // Набор базовых пакетов Java
7 import Java.util.Collection;
8 import java.rmi.RemoteException;
9
10 // Пакеты расширений Java
11 import javax.ejb.*;
12
13 // Пакеты Deitel
14 import com.deitel.advjhtpl.bookstore.model.*;
15
16 public interface OrderProductHome extends EJBHome {
17
18 // создание объекта OrderProduct с использованием заданного
объекта OrderProductHodel
19 public OrderProduct create( OrderProductModel model )
20 throws RemoteException, CreateException;
21
22 // нахождение объекта OrderProduct с заданным идентификатором
orderlD
23 public Collection findByOrderlD{ Integer orderlD )
24 throws RemoteException, FinderException;
25
26 // нахождение объекта OrderProduct для заданного первичного ключа
27 public OrderProduct findByPrimaryKey( OrderProductPK key )
28 throws RemoteException, FinderException;
29 )
Рис. 11.23. Интерфейс OrderProductHome для нахождения и создания экземпляров
EJB-компонента OrderProduct
11.6.4. Класс первичного ключа Order ProductPK
Класс OrderProductPK (рис. 11.24) является классом первичного ключа для
EJB-компонента OrderProduct. Поля (атрибуты) orderlD и ISBN необходимы для
уникальной идентификации определенного экземпляра EJB-компонента Order-
Product. Для компонента-сущности EJB, который имеет составной первичный
ключ (т.е. первичный ключ, состоящий из нескольких полей), требуется
нестандартный класс первичного ключа, определяемый разработчиком. Нестандартный
класс первичного ключа должен содержать открытые (public) элементы данных
для кансдого поля в составном первичном ключе. Класс первичного ключа
OrderProductPK имеет два открытых элемента данных (строки 12-13): ISBN
и orderlD, которые соответствуют двум полям первичного ключа EJB-компонента
.OrderProductEJB (рис. 11.22). Нестандартный класс первичного ключа также дол-
568
Глава 11
жен переопределять методы hashCode и equals класса Object. Переопределенные
реализации методов hashCode (строки 38-41) и equals (строки 49-57) дают
возможность контейнеру EJB и клиентам EJB-компонента OrderProduct определять,
являются ли два экземпляра EJB-компонента OrderProduct эквивалентными,
путем сравнения их экземпляров класса первичного ключа.
1
2
3
4
5
6
7
В
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// OrderProduc tPK.java
// OrderProductPK - класс первичного ключа для EJB-
// сущности OrderProduct.
package com.deitel.advjhtpl.bookstore.ejb;
// Набор базовых пакетов Java
import java.io.*;
public class OrderProductPK irnplements Serializable {
// поля в составе первичного клича
public String ISBN;
public Integer orderlD;
// конструктор беэ аргументов
public OrderProductPK() {}
// построение объекта OrderProductPK с ISBN-кодом
и идентификатором orderlD
public OrderProductPK( String isbn. Integer id )
ISBN = isbn;
orderlD = id;
// получение ISBN-кода
public String getISBN{)
return ISBN;
// получение идентификатора orderlD
public Integer getOrderID()
return orderlD;
// вычисление хэш-кода для данного объекта
public int hashCode()
return getISBN().hashCode() * getOrderlD().intValue(>;
// нестандартная реализация метода equals класса Object
public boolean equals( Object object )
{
// проверка, что объект является экземпляром класса
OrderProductPK
if ( object instanceof OrderProductPK ) {
OrderProductPK otherKey =
Практический пример корпоративного приложения. Бизнес-логика: часть 1 569
49 ( OrderProductFK ) object;
50
51 /У сравнение ISBN-кодов и идентификаторов orderID
52 return ( getlSBN().equals( otherKey.getlSBN() )
53 && getOrderlDO .equals ( otherKey.getOrderID() ) );
54 }
55
56 return false;
57 }
58 }
Рис. 11.24. Класс первичного ключа OrderProducPK для EJ В -компонента OrderProduct
11.6.5. Класс OrderProductModel
Класс модели OrderProductModel (рис. 11.25) представляет запись в таблице
OrderProduct. В строке 18 объявляется ссылка на объект ProductModeJ для товара
(компонент Product), ассоциированного с записью таблицы OrderProduct.
Атрибут quantity (строка 19) представляет количество товара в заказе. Заказ
идентифицируется по его номеру orderlD (строка 20). Метод getXML (строки 59-76)
генерирует код элемента XML, представляющего объект OrderProductModel.
1 // OrderProductModel.Java
2 // Класс OrderProductModel представляет товар и его
3 // количество в заказе или в магазинной тележке.
4 package com.deitel.advjhtpl.bookstore.model;
5
6 // Набор базовых пакетов Java
7 import java.io.*;
8 Import java.util.*;
9 import java.text.*;
10
11 // Пакеты сторонних поставщиков
12 import org.w3c.dom.*;
13
14 public class OrderProductModel implements Serializable,
15 XMLGenerator (
16
17 // свойства объекта OrderProductModel
18 private ProductModel productModel;
19 private int quantity;
20 private Integer orderID,-
21
22 // задание объекта ProductModel
23 public void setProduetModel( ProductModel model )
24 {
25 productModel = model;
26 }
27
28 // получение обгьекта ProductModel
29 public ProductModel getProductModel()
30 {
31 return productModel;
32 }
33
570
Глава 11
34
35
36
37
36
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77 )
// задание количества
public void setQuantity( int productQuantity )
quantity = productQuantity;
// получение количества
public int getQuantity()
return quantity;
// задание идентификатора orderlD
public void setOrderlD( Integer id )
orderID = id;
// получение идентификатора orderID
public Integer getOrderlDO
return orderXD;
// получение XML-представления объекта OrderProduct
public Element getXML( Document document )
<
// создание элемента orderProduct
Element OrderProduct =
document.createElenient< "orderProduct" );
// добавление элемента иэ объекта ProductModel
orderProduct.appendChild (
getProductModel(),getXML( document ) );
// создание элемента quantity
Element temp = document.createElement( "quantity" )
temp.appendChild( document.createTextNode(
String.valueOf ( getQuantity() ) ) );
orderProduct.appendChild( temp );
return orderProduct;
Рис. 11.25. Класс OrderProductModel для сериализации данных EJB-компонента
OrderProduct
В таблицах на рис. 11.26, 11.27 и 11.28 представлены параметры
развертывания для EJB-сущеости OrderProduct. В дополнение к этим установкам следует
задать для типа транзакции Transaction Type значение Required для всех
бизнес-методов. Заметим, что поскольку EJB-сущность OrderProduct использует
нестандартный (создаваемый разработчиком) класс первичного ключа, значение для
имени поля первичного ключа Primary Key Field Каше (рис, 11,27) должно быть
оставлено пустым.
Практический пример корпоративного приложения. Бизнес-логика: часть 1 571
^ . ч -
Основные параметры развертывания для EJB-компонента OrderProduct
Тип Bean Type
Класс Enterprise Bean
Собственный интерфейс
Home Interface
Удаленный интерфейс
Remote Interface
Entity
com.deitel.advjhtpl.bookstore.ejb.OrderProductEJB
com.deitel.advjhtpl.bookstore.ejb.OrderProductHame
com.deitel.advjhtpl.bookstore.ejb.OrderProduct
Рис. 11.26. Основные параметры развертызания для EJB-компонента OrderProduct
| Параметры управления данными и развертыванием для EJB-компонента OrderProduct
Управление
п е рс и стентн ость ю
| Persistent Management
Класс первичного ключа
Primary Key Class
Имя поля первичного
ключа Primary Key
Field Name
Имя базы данных JNDI
Database JNDI Name
SQL-оператор SQL
Statement метода
findByCustomerlD
SQL-оператор SQL
Statement метода
ejbStore
SQL-оператор SQL
Statement метода
ejbCreate
SQI -оператор SQL
Statement метода
ejb Remove
SQL-оператор SQL
Statement метода
findByPrimaryKey
SQL-оператор SQL
Statement метода
ejbLoad
SQL-оператор создания
таблицы Table Create
SQL Statement
SQL-оператор удаления
таблицы Table Delete
5QL Statement
Container-Managed Persistence
com.deitel.advjhtpl.bookstore.ejb.OrderProductPK
Нет
jdbc/Bookstore
SELECT ISBN, orderlD, FROM OrderProduct WHERE
orderiD=?l
UPDATE OrderProduct SET quantity=? WHERE ISBN=? AND
orderID=?
INSERT INTO OrderProduct (ISBN, orderlD, quantity)
VALUES {?, ?, ?)
DELETE FROM OrderProduct WHERE ISBN=? AND orderID='>
SELECT ISBN, orderlD FROM OrderProduct WHERE ISBN=?
AND orderID=?
SELECT quantity FROM OrderProduct WHERE ISBN=? AND
orderiD=?
CREATE TABLE OrderProduct (ISBN VARCHAR (2 55),
orderlD INTEGER, quantity INTEGER NOT NULL,
CONSTRAINT pk_OrderProduct PRIMARY KEY (ISBN,
orderlD) )
DROP TABLE OrderProduct
Рис. 11.27. Параметры управления данными и развертыванием для EJB-компонента
OrderProduct
572
Глава 11
Ссылки на EJВ-компоненты для компонента OrderProduct
Кодовое имя Coded Name
Тип Туре
Собственный интерфейс Ноте
Удаленный интеофейс Remote
Имя JNDI Name
ejb/Product
Entity
coin. deitel. advjhtpl. bookstore. e jb. ProductHome
com.deitel.advjhtpl.bookstore.ejb.Product
Product
Рис, 11.28. Ссылки на EJ В-компоненты для компонента OrderProduct
В этой главе была рассмотрена бизнес-логика управления магазинной тележкой
(компонент ShoppingCart) покупателя и модель данных для описания товаров
и заказов в нашем Internet-магазине. Мы также обсудили, каким образом можно
использовать сериализуемые (Serializable) объекты для снижения объема сетевого
трафика при коммуникационном взаимодействии с EJB-компонентами. В
следующей главе будут рассмотрены EJB-компоненты для обслуживания покупателей
в нашем Internet-магазине.
19
Практический пример
корпоративного приложения.
Бизнес-логика: часть 2
Цели
• Познакомиться с моделью
данных для обслуживания
покупателей в приложении
Deitel Bookstore.
• Реализовать EJB-компонент
для хранения информации об
оплате и доставке товара.
• Создать ЕЛЗ-компоненг для
формирования первичных
ключей.
• Узнать о преимуществах,
которые дает декларативная
семантика транзакций.
• Понять, каким образом
осуществляется
развертывание учебного
приложения Deitel Bookstore.
Лучшие инвестиции — это
инвестиции в орудия вашего
труда.
Бенджамин Франклин
Творчество состоит не « том,
чтобы найти предмет, но в том,
чтобы выявить и представить
его лучшие качества.
Джеймс Рассел Лоуэлл
Ш1
Прогнозируемые события не требуют
управления извне. Они управляют собой
сами.
Амелия Барр
Как известно, ключом к смыслу предмета
и явлений является их интерпретация.
Джордж Элиот
574
Глава 12
12.1. Введение
В этой главе будут рассмотрены EJB-компоненты с данными, предназначенные
для обслуживания покупателей. Хранение информации о посетителях
Internet-магазина может сделать процесс приобретения товаров более удобным, поскольку
информация об оплате и доставке сохраняется на сервере. Отдел маркетинга
Internet-магазина также может воспользоваться собранными данными для
распространения рекламных материалов и анализа информации о демографическом
составе покупателей.
Будет также рассмотрен EJB-компонент с данными, который генерирует
уникальные идентификаторы для EJB-компонентов Customer, Order и Address.
Экземпляры этих ЕсШ-компонентов создаются при регистрации нового посетителя,
а также при размещении покупателем новых заказов. Реляционные базы данных
требуют применения уникальных первичных ключей для обеспечения ссылочной
целостности и выполнения запросов. Мы предоставляем EJB-компонент Sequen-
ceFaetory для генерирования этих уникальных идентификаторов, поскольку не
все базы данных способны автоматически генерировать значения первичных
ключей. Наконец, будут предоставлены инструкции по развертыванию учебного
приложения Deitel Bookstore на эталонной реализации cepsepa приложений J2EE Sun
Microsystems.
Практический пример корпоративного приложения. Бизнес-логика: часть 2 575
12.2. Реализация EJB-компонента Customer
EJB-компонент с данными Customer представляет покупателя в главной базе
данных. В следующих подразделах будет рассмотрена реализация
EJB-компонента и соответствующий класс модели.
12.2.1. Удаленный интерфейс Customer
Удаленный интерфейс EJB-компонента Customer (рис. 12,1) определяет методы
бизнес-логики в EJB-компоненте. Метод getCustomerModel (строки 19—20)
формирует сериализуемый (Serializable) объект класса CustomerModel, который содер
жит подробную информацию о покупателе. Метод getOrderHistory (строки 23-24)
возвращает коллекцию объектов OrderModel, которая содержит информацию о
последних сделанных покупателем заказах. Метод getPasswordHint (строка 27)
возвращает строку с подсказкой, помогающей покупателю вспомнить забытый пароль.
12.2.2. Реализация CustomerEJB удаленного интерфейса Customer
Реализация CustomerEJB (рис. 12.2) удаленного интерфейса Customer
(рис. 12.1) содержит переменные экземпляров для каждого из свойств
EJB-компонента Customer (строки 25-36). Эти переменные экземпляров являются
открытыми (public), чтобы контейнер EJB мог синхронизировать их значения со
значениями из соответствующей таблицы базы даннь'х.
1 // Customer.Java
2 // Customer - удаленный интерфейс для компонента EJB Customer.
3 package com.deitel,advjhtpl.bookstore,ejb;
4
5 // Набор базовых библиотек Java
6 import Java.rmi.RemoteException;
7 import java.util.Collection;
8
9 // Стандартные расширение Java
10 import javax.ejb.*;
11
12 // Библиотеки Deitel
13 import com.deitel.advjhtpl.bookstore.model.*;
14 import com.deitel.advjhtpl.bookstore.exceptions.*;
15
16 public interface Customer extends EJBObject {
17
18 // получение сведений о покупателе в виде объекта CustomerModel
19 public CustomerModel getCustomerModel()
20 throws RemoteException;
21
22 // получение архива заказов для покупателя
23 public Collection getOrderHistory{)
24 throws RemoteException, NoOrderHistoryExeeption;
25
26 // получение подсказки пароля для покупателя
27 public String getPasswordHint() throws RemoteException,-
28}
Рис. 12.1. Удаленный интерфейс Customer для изменения информации о покупателе,
получения архива заказов и выдачи подсказки пароля
1 // CustomerEJB.Java
2 // EJB-компонент с данными Customer представляем покупателя и
3 // содержит имя пользователя, пароль, адрес доставки счета,
4 // адрес доставки товара и информацию о кредитной карте.
5 package com.deitel.advjhtpl.books tore. e jb;
6
7 // Набор базовых пакетов Java
fl import j ava.util.*;
Э import java.rmi.RemoteException;
10
11 // Пакеты расширении Java
12 import javax.ejb.*;
13 import javax.naming.*;
14 import javax.rmi.PortableRemoteObject;
15
16 // Пакеты Deitel
17 import com.deitel.advjhtpl.bookstore.model.*;
18 import com.deitel.advjhtpl.bookstore.exceptions,*;
19
20 public class CustomerEJB implements EntityBean {
21 private EntityContext entityContext;
22 private InitialContext initialContext;
23
24 // поля, управляемое контейнером
25 public Integer customerID;
26 public String userlD;
27 public String password;
28 public String passwordHint;
29 public String firstName;
30 public String lastSame;
31 public Integer billingAddressID;
32 public Integer shippingAddresslD;
33
34 public String creditCardHame;
35 public String creditCardNumber;
36 public String creditCardExpirationDate;
37
Зв // получение о&ъехта CustomsrKodel
39 public CustomerModel getCustomerModel() throws EJBException
40 {
41 // построение нового объекта Си stonier Model
42 CustomerModel customer = new CustomerHodel();
43
44 // заполнение объекта CustomerModel данными для этого покупателя
45 customer.setCusfcomerlD{ customerlD );
46 customer.setUserXD( userlD );
47 customer.setPassword( password );
48 customer.setPasswordHint( passwordHint );
49 customer.setFirstName( firstName );
50 customer.setLastName( lastName );
51
52 // использование EJB-компонентa Address для получения
53 // экземпляров компонента Address, содержащих адрес доставки
счета и адрес доставки товара
54 try {
55 initialContext = new InitialContext();
Практический пример корпоративного приложения. Бизнес-логика: часть 2 577
56
57 Object object = initialContext.lookup(
58 "java:corop/env/ejb/Address" );
59
60 AddressHome addressHome = ( AddressHome )
62 PortableRemoteObject.narrow( object,
62 AddressHome.class );
63
64 // получение удаленной сошки на адрес доставки счета
65 Address billingAddress =
66 addressHome.findByPrimaryKey( billingAddressid );
67
68 // добавление объекта AddressModel в объект CustomerModel
69 customer.setBillingAddress(
70 billingAddress.getAddressModel() );
71
72 // получение удаленной ссылки на адрес доставки товара
73 Address shippingAddress =
74 addressHome,findByPrimaryKey( shippingAddressID);
75
7 6 // добавление объекта AddressModel в объект CustomerModel
77 customer.setShippingAddress{
78 shippingAddress.getAddressModel() );
79
80 } // конец блока try
81
82 // обработка исключения при использовании EJB-компонента
Address
83 catch { Exception exception ) {
84 throw new EJBException( exception );
85 }
86
87 // задание информации о кредитной карте в объекте CustomerModel
88 customer.setCreditCardName( creditCardName );
89 customer.setCreditCardNumber( creditCardNumber );
90 customer.setCreditCardExpirationDate(
91 creditCardExpirationDate );
92
93 return customer;
94
95 ] // конец метода getCustomerModel
96
97 // получение архива заказов покупателя
98 public Collection getOrderHistory()
99 throws NoOrderHIstoryException, EJBException
100 {
101 Collection history = new ArrayList {};
102
103 // использование EJB-компонента Order для получении списка
заказов покупателя
104 try t
105 initialContext = new InitialContext() ;
106
107 Object object = initialContext.lookup(
108 "java:comp/env/ejb/Order" );
109
578
Глава 12
110 OrderHome orderHome = ( OrderHome )
111 PortableRemoteObjeet.narrow( object,
11Z OrderHome.class );
113
114 // нахождение заказов для данного покупателя
115 Collection orders =
116 orderHome.findByCustomerlD( customerlD );
117
118 Iterator iterator = orders.itarator () ;,
119
120 // использование списка заказов для создания архива заказов
121 while ( iterator.hasNextO ) (
122 Order order = ( Order ) PortableRemoteObject.narrow{
123 iterator.next(), Order.class );
124
125 // извлечение объекта OrderModel для заказа
126 OrderModel orderModel = order.getOrderModel()/
127
128 // добавление каждого из объектов OrderModel в архив
заказов
129 History.add{ orderModel ).;
130 J
131
132 } // конец блока try
133
134 // обработка исключения при поиске записей для заказов
135 catch ( FinderException f" inderException } {
136 throw new NoOrderHistoryException( "No order " +
137 "history found for the customer with userID " +
138 userlD ) ;
139 J
140
141 // обработка исключения яри вызове методов EJB-компонента Order
142 catch < Exception exception ) {
143 exception.printStackTraceО;
144 throw new EJBExcoption( exception );
145 }
146
147 return history;
148
149 } // конец метода getOrderHistory
150
151 // получение подсказки пароля для данного покупателя
152 public String getPasswordHint()
1ЬЗ (
154 return passwordHint;
155 )
156
157 // задание сведений о покупателе с помощь» объекта CustomerModel
158 private void setCustomerModel( CustomerModel customer )
159 {
160 // установка значений из CustomerModel для элементов данных
Customer
161 userlD = customer.getCserlDO ;
162 password = customer.getPasswordf);
163 passwordHint = customer .getPasswordHint (} ,-
Практический пример корпоративного приложения. Бизнес-логика: часть 2 579
164 firstName = customer.getFirstName();
165 last-Name = customer.getLastName();
166
167 billingAddressID =
168 customer.getBillingAddress().getAddressID();
169
170 shippingAddressID =
171 customer.getShippingAddress().getAddressID();
172
173 creditCardName = customer.getCreditCardName();
174 creditCardNumber = customer.getCraditCardKumber(j;
175
176 creditCardExpirationDate =
177 customer.getCreditCardExpirationDate();
178
179 } // конец метода setCustomerModel
180
181 // создание EJB-компонента Customer с использованием заданного
объекта CustomerModel
182 public Integer ejbCreate( CustomerModel customerModel )
183 throws CreateException
184 {
185 // извлечение уникального значения для первичного ключа
136 //с использованием EJB-компонента SequenceFactory
187 try {
188 initialContext = new InitialContext();
189
190 // поиск EJB-компонента SequenceFactory
191 Object object = initialContext.lookup(
192 "Java:comp/env/ejb/SequenceFactory" );
193
194 SequenceFactoryHome sequenceFactoryHome =
195 ( SequenceFactoryHome ) PortableRemoteObject.narrow(
196 object, SequenceFactoryHome.class );
197
198 // нахождение последовательности для EJB-компонента
Customer
199 SequenceFactory SequenceFactory =
200 sequenceFactoryHome.findByPrimaryKey( "Customer" );
201
202 // извлечение следующего имеющегося идентификатора
customerID
203 cuatomerlD = SequenceFactory.getNextID();
204
205 // создание EJB-компонентов Address для адресов
206 // доставки счета и товара
207 object = initialContext.lookup(
208 "java:comp/env/ejb/Address" };
209
210 AddressHome addressHome = ( AddressHome }
211 PortableRemoteObject.narrow( object,
212 AddressHome. class ) ,-
213
214 // получение адреса доставки счета для покупателя
215 AddressModel billingAddressModel =
216 customerModel.getBillingAddress();
580
Глава 12
217
218 // создание ЕJB-компонента Address для адреса доставки счета
219 Address billingAddress =
220 addressHome.create( billingAddressHbdel );
221
222 // задание идентификатора addressID в объекте AddressModel
223 billingAddressModel.setAddressIDf ( Integer )
224 billingAddress.getPrimaryKey() );
225
226 // получение адреса доставки topара для покупателя
227 AddressModel shippingAddressModel =
228 с u s tome rMode1.getShippingAddress (>;
229
230 // создание EJB-компонента Address для адреса доставки товара
2 31 Address shippingAddress =
232 addressHome.create( ShippingAddressModel ) ;
233
234 // задание идентификатора addressID s объекте AddressModel
235 ShippingAddressModel.setAddzressID( ( Integer )
236 shippingAddress.getPrimaryKeyО );
237
238 // использование объекта CustomerModel для установки
данных для нового покупателя
239 setCustomerModel{ CustomerModel );
240
241 } // конец блока try
242
243 // обработка исключений при поиске, нахождении и использовании
EJB-компонентов
244 catch ( Exception exception ) {
245 throw new CreateException( exception.getMessage{) );
246 }
247
246 II контейнер EJB будет возвращать удаленную ссылку
249 return null;
250
2 51 } // конец метода ejbCreate
252
253 // выполнение необходимых после создания объекта действий
254 public void ejbPostCreate{ CustomerModel customer ) {}
255
256 // установка контекста EntityContext
257 public void setEntityContext( EntityContext context )
258 {
259 entityContext = context;
260 }
261
2 62 // сброс контекста EntityContext
263 public void unsetEntityContextO
264 {
265 entityContext = null;
266 J
267
268 // активация экземпляра EJB-компонента Customer
269 public void ejbActivate()
270 {
Практический пример корпоративного приложения. Бизнес-логика: часть 2 581
271 customer-ID = { Integer ) entityContext. getPrimaryKey () ;
272 )
273
274 // пассивация экземпляра EJB-компонента Customer
275 public void ejbPassivate()
276 {
277 customerlD = null;
278 }
279
280 // удаление экземпляра EJB-компонента Customer
281 public void ejbRercove () {}
282
283 // сохранение данных EJB-компонента Customer в базе данных
284 public void ejbStoret) {}
285
28 6 // загрузка данных EJB-компонента Customer из баз» данных
287 public void ejbLoadO {}
288 )
Рис. 12.2. Реализация CustomerEJВ удаленного интерфейса Customer
Метод getCustomerModel (строки 39-95) формирует объект CustomerModel,
элементы данных которого содержат значения из компонента CustomerEJB. EJB-
компоненты Address хранят информацию об адресах покупателей. EJB-компонент
Customer хранит идентификаторы addressID для каждого из объектов Address
(адрес доставки счета и адрес доставки товара). Метод getCustomerModel
использует интерфейс AddressHoine для получения EJB-компонентов Address для
покупателя (строки 65-78). Для каждого EJB-компонента Address метод
getCustomerModel получает объект AddressModel и добавляет его в объект CustomerModel.
Метод getOrderHistory (строки 98-149) формирует коллекцию, которая
содержит объект OrderModel для каждого заказа, размещенного покупателем. Метод
findByCustomerlD интерфейса OrderHome возвращает коллекцию заказов (EJB-
комноненты Order) для данного покупателя (EJB-компонент Customer) (строка
116). В строках 115-130 извлекается коллекция EJB-компонентов Order и
формируется коллекция объектов OrderModel для представления архива предыдущих
заказов покупателя.
Метод setCustomerModel (строки 158-179) является вспомогательным методом
для метода ejbCreate (строки 182-251). Метод setCustomerModel модифицирует
информацию, содержащуюся в EJB-компоненте CustomerEJB, используя данные
из аргумента CustomerModel. В строках 161-177 извлекаются элементы данных
объекта CustomerModel и устанавливаются значения элементов данных
EJB-компонента CustomerEJB.
Контейнер EJB вызывает метод ejbCreate (строки 182-251) при создании
нового экземпляра EJB-компонента Customer. Метод ejbCreate принимает в качестве
аргумента объект CustomerModel и использует его для вызова метода
setCustomerModel, чтобы осуществить инициализацию элементов данных
EJB-компонента CustomerEJB (строка 239). Метод ejbCreate использует метод
getNextlD класса Sequenceractory для генерирования уникального
идентификатора customerlD для нового покупателя (строка 203). Каждый покупатель имеет
адрес доставки счета и адрес доставки товара, которые хранятся в EJB-компоненте
Address. В строках 215-236 эти EJB-компоненты Address создаются с
использованием данных из объектов AddressModel, задаваемых в аргументе CustomerModel.
582
Глава 12
12.2.3. Собственный интерфейс CustomerHome
Интерфейс CustomerHome (рис. 12.3) предоставляет методы create для создания
новых EJB-компонентов Customer для покупателей и методы поиска для
нахождения имеющихся покупателей. Метод create (строки 17-18) соответствует методу
ejbCreate в реализации CustomcrEJB (рис. 12.2). Метод fmdBy Login (строки 21-22)
возвращает удаленную ссылку на EJB-компонент Customer для покупателя с
заданным идентификатором userlD и паролем password- Этот метод осуществляет
аутентификацию покупателей при входе их в Internet-магазин. Метод findByUseiTD
(строки 25-26) возвращает EJB-компонент Customer для покупателя с заданным
идентификатором пользователя userlD. Метод findByPrimaryKey (строки 29-30)
возвращает EJB-компонент Customer для покупателя с заданным идентификатором
customerlD. Контейнер EJB предоставляет реализации этих методов, используя
RQL-запросы, задаваемые разработчиком при развертывании приложения.
1 // CustomerHome.Java
2 // CustomerHome - собственный интерфейс для ВJB-компонента
с данными Customer.
3 package com.deitel.advjhtpl.bookstore.ejb;
4
5 // Набор базовых пакетов Java
6 import Java.rmi .RemoteException;
7
8 // Пакеты расширений Java
9 import javax.ejb.*;
10
11 // Лахеты Deitel
12 import com.deitel.advjhtpl.bookstore.model.*;
13
14 public interface CustomerHome extends EJBHome {
15
16 // создание EJB-компонента Customer с использованием заданного
объекта CustomerModel
17 public Customer create ( CustomerModel customer-Model )
18 throws RemoteException, CreateException;
19
20 // нахождение покупателя с указанным идентификатором и паролем
21 public Customer findByLogin( String userID, String password )
22 throws RemoteException, FinderException;
23
24 // нахождение покупателя с указанным идентификатором userlD
25 public Customer findByCserlD( String userlD )
26 throws RemoteException, FinderException;
27
28 // нахождение покупателя с указанным идентификатором customerID
29 public Customer findByPrimaryKey( Integer customerID )
30 throws RemoteException, FinderException;
31 }
Рис. 12.3. Интерфейс CustomerHome для создания и нахождения экземпляров
EJ8-компонента Customer
Практический пример корпоративного приложения. Бизнес-логика: часть 2 583
12.2.4. Класс CustomerModel
CustomerModel (рис. 12.4) — это класс модели для EJB-компонента Customer.
Класс CustomerModel содержит частные элементы данных (строки 17-29) для
каждого открытого элемента данных в реализации CustomerEJB (рис. 12.2). Класс
CustomerModel хранит ссылки на объекты AddrcssModel, содержащие адрес
доставки счета и адрес доставки товара для покупателей (строки 24—25). Класс
CustomerModel определяет методы set и get для каждого из своих свойств,
реализует интерфейс XMLGenerator и предоставляет метод getXML (строки 168-233)
для генерирования XML-кода элемента, описывающего покупателя.
1 // CustomerModel.Java
2 // Объект CustomerModel представляет посетителя магазина Deitel
3 // Bookstore и содержит адреса доставки счета и товара, а также
информацию о кредитной карте.
4 package com.deitel.advjhtpl.bookstore.model,-
5
6 // Набор базовых пакетов Java
7 import java.io.*;
8 import java.util.*;
9
10 // Пакеты сторонних поставщиков
11 import org.w3c.dora.*;
12
13 public class CustomerModel implements Serializable,
14 XMLGenerator {
15
16 // свойства объекта CustomerModel
17 private Integer cxistomerlD;
18 private String userlD;
19 private String password;
20 private String passwordHint;
21 private String firstName;
22 private String lastName;
23
24 private AddressModel billingAddress;
25 private AddressModel shippingAddress;
26
27 private String creditCardName;
28 private String creditCardNumber;
29 private String creditCardExpirationDate;
30
31 // создание пустого объекта CustomerModel
32 public CustomerModel {) {}
33
34 // задание идентификатора покупателя
35 public void setCustomerlD( Integer id )
36 {
37 customerID = id;
38 }
39
40 // получение идентификатора покупателя
41 public Integer getCustomerlD()
42 1
43 return customerlD;
44 )
584
Глава 12
// задание идентификатора пользователя
public void setOaerlD( String id >
userlD = id;
// получение идентификатора пользователя
public String getUserlDO
return userlD;
// задание пароля
public void setPassword( String customerPassword )
password = customerPassword;
// получение пароля
public String getPassword{)
return password;
// задание подсказки для пароля
public void setPasswordHint( String hint )
passwordHint = hint;
// получение подсказки для пароля
public String getPasswordHintO
return passwordHint;
// задание имени
public void satFirstName{ String name )
firstName = name;
// получение имени
public String getFirstName()
return firstName;
// зедание фамилии
public void setLastNarae( String name )
lastHame = name;
// получение фамилии
Практический пример корпоративного приложения. Бизнес-логика: часть 2
public String getLastName()
return lastName;
// задание адреса доставки счета
public void setBillingAddress( AddressModel address }
billingAddress = address;
// получение адреса доставки счета
public AddressModel getBillingAddress()
return billingAddress;
// задание адреса доставки товара
public void setShippingAddress( AddressModel address )
shippingAddress = address;
// получение адреса доставки товара
public AddressModel getShippingAddress()
return shippingAddress;
// задание названия кредитной карты
public void setCreditCardName( String name )
creditCardName = name;
// получение названия кредитной карты
public String getCreditCardName()
return creditCardName;
// задание номера кредиткой карты
public void setCreditCardNumber( String number )
creditCardNumber = number;
// получение номера кредитной карты
public String getCreditCardMumberO
return creditCardNumber;
// задание даты истечения срока действия кредиткой карты
public void setCreditCardExpirationDate( String date )
t
586
Глав;
157 creditCardExpirationDate = date;
158 }
159
160 // получение даты истечения срока действия кредитной карты
161 public String getCreditCardExpirationDate()
162 (
163 return creditCardExpirationDate;
164 }
165
166 // формирование XML-представления данных для покупателя
167 //с включением всех открытых свойств в виде узлов
168 public Element getXML( Document document )
169 {
170 // создание элемента customer
171 Element customer =
172 document, createElenient( "customer" ) ;
173
174 // создание элемента customerID
175 Element temp = document.createElement( "customerlD" );
176 temp.appendChild( document.createTextNode{
177 String.valueOf( getCustomerlD() ) ) );
178 customer.appendchild( temp );
179
160 // создание элемента userlD
181 temp = document.createElement( "userlD" );
182 temp.appendChild{
183 document.createTextNode( getUserlDQ ) );
\Bi customer.appendchild{ temp );
185
186 // создание элемента firstName
167 temp = document. createElement ( "irstName" },-
186 temp.appendChild{ document.createTextNode{
189 getFirstName() ) );
190 customer.appendchild{ temp );
191
192 // создание элемента lastName
193 temp = document.createElement( "lasLHame" );
194 temp.appendchild( document,createTextNode(
195 getLastName() ) );
196 customer.appendchild( temp );
197
198 // создание элемента billingAddress
199 temp = document.createElementt "billingAddress" };
200 temp,appendchild( billingAddress.getXML( document ) );
201
202 // создание элемента shippingAddress
203 temp = document.createElement( "shippingAddress" );
204 temp.appendchild( shippingAddress.getXML( document ) );
205
206 // создание элемента creditCardName
207 temp = document.createElement( "creditCardName" );
208 temp.appendchild( document.createTextNode(
209 getCreditCardName0 ) );
210 customer.appendchild( temp };
211
212 // создание элемента creditCardNumber
Практический пример корпоративного приложения. Бизнес-логика: часть 2 587
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234 }
temp = document.createElement( "creditCardNumber" )
temp.appendChild( document.createTextNode(
getCreditCardNumber{) ) ) ;
customer.appendChild{ temp );
// создание элемента creditCardExpirationDate
temp = document.createElement(
"creditCardExpirationDate" );
temp.appendChild{ document.createTextNode(
getCreditCardExpirationDate() ) );
customer.appendChild( temp );
// создание элемента passwordHint
temp = document.createElement( "passwordHint" );
temp.appendChild( document. MeateTextNode(
getPasswordHint() ) );
customer.appendChild( temp );
return customer;
} II конец метода getXML
Рис. 12.4. Класс CustomerModel для сериализации данных о покупателе
В таблицах на рис. 12.5, 12.6 и 12.7 представлен перечень параметров
развертывания для компонента-сущности EJB Customer. В дополнение к
представленным установкам, следует задать для типа транзакции Transaction Type значение
Required для всех бизнес-методов.
Основные параметры развертывания для EJB-компонента Customer
Тип Sean Type
Класс Enterprise Bean Class
Собственный интерфейс
Home Interface
i Удаленный интерфейс
Remote interface
Entity
com.deitel.advjhtpl.bookstore.ejb.CustomerEJB
com.deitel.advjhtpl.bookstore.ejb.CustomerHome
com.deitel.advjhtpl.bookstore.ejb.Customer
Рис. 12.5. Основные параметры развертывания для EJB-компонента Customer
Параметры управления данными и развертыванием для EJB-компонента Customer
Управление персистентностью
Persistent Management
Container-Managed Persistence
Класс первичного ключа
Primary Key Class
Имя поля первичного ключа
Primary Key Field Name
Имя базы данных JNDI
Database JNDI Name
SQL-оператор SQL Statement
метода flndByUseriD
java.lang.Integer
customerlD
jdbc/BooJcstore
SELECT customerlD FROM Customer WHERE userlD = ?1
;88
Глава 12
Параметры управления данными и развертыванием для EJB-кОМПОнента Customer
SQL-оператор SQL Statement
метода findByLogin
SQL-оператор SQL Statement
метода ejbStore
SQL-оператор SQL Statement
метода ejbCreate
SQL-оператор SQL Statement
метода ejbRemove
SQL-оператор SQL Statement
метода findByPrimaryKey
SQL-оператор SQL Statement
метода ejbLoad
SQL-оператор создания
таблицы Table Create SQL
Statement
SQL-оператор удаления
-аблицы Table Delete SQL
statement
SELECT customerlD FROM Customer WHERE userlD = ?1
AHD password = ?2
UPDATE Customer SET billingAddressID = ?,
creditCardExpirationData = ?, creditCardName = ?,
creditCardNumber = ?, firstName = ?, lastName = ?,
password = ?, passwordHint = ?,
shippingAddressID = ?, userID = ? WHERE
customerlD = ?
INSERT INTO Customer { billingAddressID,
creditCardExpirationDate, creditCardName,
creditCardNumber, customerlD, firstName,
lastName, password, passwordHint,
shippingAddressID, userlD ) VALUES ( ?, ?, ?, ?,
■) -3 •> •> ■} ',')
DELETE FROM WHERE customerlD = ?
SELECT customerlD FROM Customer WHERE
customerlD = ?
SELECT billingAddressID,
creditCardExpirationDate, creditCardName,
creditCardNumber, firstName, lastName, password,
passwordHint, shippingAddressID, usarlD FROM
Customer WHERE customerlD = ?
CREATE TABLE Customer ( billingAddressID
INTEGER, creditCardExpirationDate VARCHARf 255 ),
creditCardName VARCHARf 255 ), CreditCardNumber
VARCHARf 255 ), customerlD INTEGER, firstName
VARCHARf 2SS 1, LastMame VARCHARf 255 ),
password VARCHARf 255 }, passwordHint VARCHARf
255 ), ShippingAddressID INTEGER, userlD
VARCHARf 255 ), CONSTRAINT pk_Customer PRIMARY
KEY ( customerlD ) }
DROP TABLE Customer
Чк. 12.6. Параметры управления данными \л развертыванием для EJB-компонента
:Listomer
-
Ссылки на EJB-компоненты для компонента Customer
Кодовое имя Coded
Name
Тип Туре
Собивенный
интерфейс Ноте
Удаленный интерфейс
Remote
Имя JNDI Name
ejb/Order
Entity
com.deitel.advjhtpl,bookstore.ejb.OrderHome
cam.deitel.advjhtpl.bookstore.ejb.Order
Order
Практический пример корпоративного приложения. Бизнес-логика-, часть 2 589
Ссылки на EJB-компоненты для компонента Customer
Кодовое имя Coded
Name
~ил Туре
Собственный
интерфейс Ноше
Удаленный интерфейс
Remote
Имя JNDi Name
Кодовое имя Coded
Name
~ип Туре
Собственный
интерфейс Ноте
Удаленный интерфейс
Remote
Имя JNDI Name
ejb/SequenceFactory
Entity
com.deitel.advjhtpl.bookstore.ejb.SequenceFactoryHotne
com.deitel.advjhtpl.bookstore.ejb.SequenceFectory
SequenceFactory
ejb/Address
Entity
com.deitel.advjhtpl.bookstore.ejb.AddressHome
com.deitel.advjhtpl.bookstore.ejb.Address
Address
Рис. 12.7. Ссылки на EJB-компоненты для компонента Customer
12.3. Реализация EJB-компонента Address
Приложение сохраняет адрес доставки счета и адрес доставки товара для
каждого покупателя. Каждый адрес содержит схожую по структуре информацию (улица,
город, штат, почтовый индекс), поэтому абстракция этих двух видов адресов
помещается в один EJB-компонент Address. Каждый EJB-коыпонент Customer хранит
идентификатор адреса доставки счета и идентификатор адреса доставки товара.
12.3.1. Удаленный интерфейс Address
Удаленный интерфейс Address (рис. 12.8) содержит методы set и get для
обновления данных EJB-компонента Address и извлечения данных из него. Метод
getAddressModel (строки 17-18) создает объект AddrcssModel, который содержит
подробную информацию для конкретного EJB-компонента Address.
123.2. Реализация AddressEJB удаленного интерфейса Address
Класс AddressEJB (рис. 12.9) представляет собой реализацию удаленного
интерфейса Address и содержит открытые, управляемые контейнером элементы
данных для имени и фамилии контактного лица в адресе (EJB-компоненте Address),
а также название улицы, города, штата, почтовый индекс, страну и номер
телефона (строки 24-33).
1 // Address.Java
2 // Address - удаленный интерфейс для EJB-компонента с данными Address.
3 package com.deitel.advjhtpl.bookstore.ejb;
4
5 // Набор базовых пакетов Java
6 impart Java.rmi.RemoteException;
590
Глава 12
7
8 // Пакеты расширений Java
9 import javax.ejb.*;
10
11 // Пакеты Deitel
12 import, com.deitel.advjhtpl.bookstore.model.*;
13
14 public interface Address extends EJBObject {
15
16 // получение сведений об адресе в виде объекта AddressModel
17 public AddressModel getAddressModel(}
18 throws RemoteException;
19 >
Рис 12.8. Удаленный интерфейс Address для изменения информации в адресе
1 // AddressEJB.Java
2 // EJB-кошюкент с данными Address представляет адрес и
3 // содержит названия улицы, города, штата и почтовый индекс.
4 package com.deitel.advjhtpl.bookstore.ejb;
5
6 // Набор базовых пакетов Java
7 import java.util.*;
8 import Java. ntii.RemoteException;
9
10 // Пакеты расширений Java
11 import javax.ejb.*,-
12 import javax.naming.*;
13 import javax.rmi.PortableRemotaObject;
14
15 // Пакеты Deitel
16 import com.deitel.advjhtpl.bookstore.model.*;
17 import con.deitel.advjhtpl.bookstore.exceptions.*;
18
19 public class AddressEJB implements EntityBean {
20 private EntityContext entityContext;
21
22 // поля, управляемые контейнером
23 public Integer addresSlD;
24 public String firstName;
25 public String lastName;
26 public String streetAddressLinel;
27 public String streetAddressLine2;
28 public String city;
29 public String state;
30 public String zipCode;
31 public String country;
32 public String phoneNumber;
33
34 // получение объекта AddressModel
35 public AddressModel getAddressModel()
36 {
37 // формирование нового объекта AddressModel
38 AddressModel address = new AddressModel()•
39
Практический пример корпоративного приложения. Бизнес-логика: часть 2 S91
40 // заполнение объекта AddressModel значениями полей
41 // элементов данных EJB-компонента Address
42 address.setAddressID( addressID );
43 address.setFirstName( firstName };
44 address.setLastName( lastName );
45 address.setStreetAddressLinel( streetAddressLinel );
46 address.setStreetAddressLine2( streetAddressLine2 J;
47 address.setCityf city );
48 address.setstate( state );
49 address.setZipCode( zipCode );
50 address.setCountry( country );
51 address.setPhoneNumber( phoneNumber );
52
53 return address;
54
55 } // конец метода getAddressModel
56
57 // установка данных в EJB-компоненте Address с использованием
объекта AddressModel
5S private void setAddressModel{ AddressModel address )
59 (
60 // обновление элементов данных EJB-компонента Address
61 // Значениями, предоставляемыми объектом AddressModel
62 firstName = address,getFirstMame();
63 lastName = address.getLastName();
64 streetAddressLinel = address.getStreetAddressLinel();
65 streetAddressLine2 = address.getStreetAddressLineJ();
66 city = address.getCity();
67 state = address.getStateО;
68 zipCode = address.getZipCode();
69 country = address.getCountry ();
70 phoneNumber = address.getPhoneNumber();
71
72 } // конец метода setAddressModel
73
74 // создание EJB-компонента Address с использованием заданного
объекта AddressModel
75 public Integer ejbCreate< AddressModel address )
7 6 throws CreateException
77 {
78 // извлечение уникального значения для первичного ключа
7 9 //с использованием EiTB-компонента SequenceFactory
80 try (
81 Context initialContext = new InitialContext();
82
83 // поиск EJB-компонента SequenceFactory
84 Object object = initialContext.lookup(
85 "java:comp/env/ejb/SequenceFactory" );
86
87 SequenceFaсtorуHome sequenceFactoryHome =
88 ( SequenceFactoryHome )
89 PortableRemoteObjeat. narrow (
90 object, SequenceFactoryHome.class );
91
92 // нахождение последовательности для EJB-компонента Address
93 SequenceFactory SequenceFactory =
592
Глава 12
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
не
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
13S
139
140
141
142
143
144
145
146
sequenceFactoryHome.findByPrimaryKey( "Address" );
// извлечение следующего имеющегося идентификатора addressID
addressID = sequenceFactory.getNextID();
// задание идентификатора addressID для адреса
(первичный ключ)
address.setAddressID( addressID );
// использование объекта AddressModel для установки нового
адреса
setAddressModel( address );
} // конец блока try
// обработка исключения при использовании EJB-коМЛОНента
SequenceFactory
catch ( Exception exception ) {
throw пек CreateException( exception.getMessage() );
}
// контейнер EJB будет возвращать удаленную ссылку
return null;
1 // конец метода ejbCreate
// выполнение необходимых после создания объекта действий
public void еjbPostCreate( AddressModel address } {}
// установка контекста EntityContext
public void setEntityContext( EntityContext context )
entityContext = context;
// сброс контекста EntityContext
public void unsetEntityContext()
entityContext = null;
// активация экземпляра EJB-компонента Address
public void ejbActivate()
addressID = ( Integer ) entityContext.getPrimaryKey()
// пассивация экземпляра EJB-компонента Address
public void ejbPassivateО
addressID
null;
// удаление экземпляра EJB-компонента Address
public void ejbRemove() {}
Практический пример корпоративного приложения. Бизнес-логика: часть 2 593
147 // сохранение данных EJB-компонента Address в базе данных
148 public void ejbStoreO {)
149
150 // Загрузка данных EJB-компонента Address us базы данных
151 public void ejbLoadO {}
152 }
Рис, 12.9. Реализация AddressEJB удаленного интерфейса Address
Метод getAddressModel (строки 35-55) создает объект AddressModel,
устанавливает для его свойств значения открытых элементов данных EJB-компонента
Address и возвращает объект AddressModel вызвавшему процессу. Метод setAdd-
ressModel (строки 58-72) представляет собой утилитарный метод, который
принимает в качестве аргумента объект AddressModel и обновляет значения в элементах
данных EJB-компонента AddressEJB.
Контейнер EJB вызывает метод ejbCreate (строки 75-115) для создания нового
EJB -компонента AddressEJB. Каждый EJB--KOMnoHeHT Address должен иметь
уникальный идентификатор addressID для своего первичного ключа. Метод
getNextID (строка 97) генерирует этот уникальный идентификатор. В строке 100
устанавливается значение идентификатора addressID в объекте AddressModel.
В строке 103 объект AddressModel передается методу setAddressModel для
завершения инициализации EJB-компонента AddressEJB.
12.3.3. Собственный интерфейс AddressHome
Интерфейс AddressHome (рис. 12.10) предоставляет методы для создания и
нахождения EJB-компонентов Address. Метод create (строки 18-19) принимает в
качестве аргумента объект AddressModel. Контейнер EJB вызывает метод ejbCreate
(рис, 12.9), когда клиент вызывает метод create. Метод findByPrimaryKey (строки
22-23) находит имеющийся EJB-компонент Address, используя его первичный
ключ addressID, и возвращает удаленную ссылку на EJB-компонент Address.
12.3.4. Класс AddressModel
Класс AddressModel (рис. 12.11) представляет собой класс модели, который
реализует интерфейс XMLGenerator и метод getXML для генерирования
XML-описания адреса. Класс AddressModel содержит свойства (строки 17-149)
для каждого открытого элемента данных в реализации AddressEJB (рис. 12.9).
Метод getXML (строки 152-212) формирует элемент XML, содержащий дочерние
элементы для каждого из свойств класса AddressModel.
1 // AddressHome.java
2 // AddressHome - собственный интерфейс для EJB-компонента с данными
Address.
3 package com.deitel.advjhtpl.bookstore.ejb;
4
5 // Набор базовых пакетов Java
6 import java.rmi. ReпloteException;
7
8 // Пакеты расширений Java
9 import javax.eJb.EJBHome;
10 import javax.ejb.*;
11
12 // Пакеты Deitel
594
Глава 12
13 import com.deifcel.advjhtpl.bookstore.model.*;
14
15 public interface AddressHome extends EJBHome {
16
1"? // создание EJB-компонента Address с использованием заданного
объекта AddressModel
18 public Address create{ AddressModel address }
19 throws RemofceExoeption, Cre&teException;
20
21 // нахождение адреса с взданним Идентификатором addressID
22 public Address findByPrimaryKey( Integer addressID )
23 throws RemoteException, FinderException;
24 } ___ ^____
Рис. 12.10. Интерфейс AddressHome для создания и нахождения экземпляров
EJ В-компонента Address
1 // AddressModel.Java
2 /f Объект AddressModel представляет адрес покупателя и
3 // содержит названия улицы, города, штата и почтовый индекс.
4 package com.deitel.advjhtpl.bookstore.model;
5
6 // Набор базовых пакетов Java
7 import java.io.*;
8 import java^util.*;
9
10 // Пакеты сторонних поставщиков
11 import org.w3c.dom.*;
12
13 public class AddressModel implements Serializable,
14 XMLGenerabor 1
15
16 // свойства объекта AddressModel
17 private Integer addressID;
18 private String firstName;
19 private String lasfcName;
20 private String streetAddressLinel;
21 private String streetAddreasLlne2;
22 private String city;
23 private String state;
24 private String zipCode;
25 private String country;
26 private String phoneNumber ;
27
28 // формирование пустого объекта AddressModel
29 public AddressModel() {)
30
31 // задание идентификатора addressID
32 public void setAddressID( Integer id )
33 {
34 addressID = id;
35 }
36
37 // получение идентификатора addressID
38 public Integer getAddressIDО
Практический пример корпоративного приложения. Бизнес-логика: час
return addressID;
// задание имени
public void setFirstHame( String name )
firstName = name;
// получение имени
public String getFirstName()
return firstName;
// задание фамилии
public void setLastName( String name )
lastName — name;
// получение фамилии
public String getLastKame{)
return lastName;
// задание первой строки названия улицы
public void setStreetAddressLinel ( String address )
streetAddressLinel = address,-
// получение первой строки названия улицы
public String getStreetAddressLinel()
return streetAddressLinel;
,/ задание второй строки названия улицы
public void setStreetAddressLine2( String address )
streetAddressLine2 = address;
// получение второй строки названия улицы
public String getStreetAddressLine2()
return streetAddressLine2;
// задание названия города
public void setcity< String addressCity J
t
city = addressCity;
596
Глава 12
/ получение названия города
public String getCityO
return city;
// задание названия штата
public void setstate( String addressState }
state = addressState;
// получение названия штага
public String getStateO
return state;
// задание почтового индекса
public void setZipCode( String zip )
zipCode = zip;
// получение почтового индекса
public String getZipCode{)
return zipCode;
// задание названия страны
public void setCountry{ String addressCountry )
country = addressCountry;
// получение названия страны
public String getCountry()
return country;
// задание номера телефона
public void setPhoneNumber{ String phone )
phoneNumber = phone;
// получение номера телефона
public String getPhoneNumber{)
return phoneNumber;
Практический пример корпоративного приложения. Бизнес-логика: часть 2
151 // построение XML-представления данных о покупателе
152 public Element getXML{ Document document )
153 {
154 // создание элемента address
155 Element address = document.createElementt "address" );
156
157 // создание элемента firstName
158 Element temp = document.createElement( "firstName" );
159 temp.appendChiId{
160 document.createTextNode( getFirstNamef) ) );
161 address.appendChild( temp );
162
163 // создание элемента lastName
164 temp = document.createElement{ "lastName" );
165 temp.appendChild(
166 document.createTextNode( getLastName() ) );
167 address.appendChild( temp );
168
169 // создание элемента StreetAddressLinel
170 temp = document.createElement( "streetAddressLinel" );
171 temp.appendChild(
172 document.createTextNode( getStreetAddressLinel() ) );
173 address.appendChildf temp );
174
175 // создание элемента streetAddressLine2
176 temp = document.createElement( "streetAddressLine2" );
177 temp.appendChild( '
178 document.createTextNode( getStreetAddressLine2() ) );
179 address.appendChild( temp );
180
161 // создание элемента city
182 temp = document.createElement( "city" );
183 temp.appendChild{ document.createTextNode( city ) );
184 address.appendChild{ temp );
185
186 // создание элемента state
187 temp = document.createElement{ "state" );
188 temp.appendchiId(
189 document.createTextNode( getState() ) };
190 address.appendChiId( temp };
191
192 /J создание элемента zipCode
193 temp = document.createElement( "zipCode" );
194 temp.appendChild(
195 document.createTextNode( getZipCode() ) );
196 address.appendChild( temp );
197
198 // создание элемента country
199 temp = document.createElement( "country" );
200 temp.appendChild(
201 document.createTextNode( getCountry() ) );
202 address.appendChild( temp );
203
204 // создание элемента phoneNumber
205 temp = document. createEl ement ( "phoneNuir.ber" );
206 temp.appendChild(
598
Глава 12
207 document.ereateTextNode( getPhoneNumberП | );
208 address.appendChild( temp };
209
210 return address;
211
212 } // конец метода getXML
213 }
Рис, 12.11. Класс Add re ssM ode I для сериализации данных EJ В -компонента Address
В таблицах на рис. 12,12, 12,13 и 12.14 представлены параметры для
развертывания EJB-компонента с данными Address. В дополнекие к этим установкам,
следует задать для типа транзакции Transaction Type значение Required для всех
бизнес-методов.
Основные параметры для развертывания EJB -компонента Address
Тип Bean Type
Класс Enterprise Bean Class
Собственный интерфейс Home
Interface
Удаленный интерфейс Remote
Interface
Entity
com.deitel.advjhtpl.bookstore. ejb.AdressEJB
com.deitel.advjhtpl.bookstore.ejb.AddressHome
com.deitel.advjhtpl.bookstore.ejb.Address
Рис. 12.12. Основные параметры для развертываний EJB-компонента Address
Параметры управления данными и развертыванием для EJB-компонента Address
^Управление персистентностью
Persistent Management
Класс первичного ключа
Primary Key Class
Имя поля первичного ключд
Primary Key Field Name
Имя базы данных JNDI
Database JNDI Name
SQL-опера юр SQL Statement
метода ejbStore
SQL-оператор SQL Statement
метода ejbCreate
SQL-оператор SQL Statement
метода ejbRemove
Container-Managed Persistence
Java.lang.Integer
addressID
jdbc/Bookatore
UPDATE Address SET city = ?, country = ?,
firstName = ?, lastName = ?, phoneNumber = ?,
state = ?, streetAddressLinel = ?, I
streetAddressLine2 = ?, zipCode = ? WHERE
addressID = ?
INSERT INTO Address { addressID, city, country,
firstName, lastName, phoneNumber, state,
StreetAddressLinel, 8treetAddressLine2, zipCode )
VALUES {?, ?, ? ?, ?, ?, ?, ?,?,?)
DELETE FROM Address WHERE addressID = ?
SQL-оператор SQL Statement ; SELECT addressID FROM Address WHERE addressID = ?
метода findByPrimaryKey <
Практический пример корпоративного приложений. Б из нес-логика: часть 2 599
| Параметры управления данными и развертыванием для EJB-компонента Address
SQL-оператор SQL Statement
метода ejbLoad
SQL-оператор создания
таблицы Table Create SQL
Statement
SQL-оператор удаления
таблицы Table Delete SQL
Statement
SELECT city, country, firstNaire, lasfcJfcuee,
phormNumber, state, streetAddressLinel,
streetAddressLiite2, zipCode FROM Address WHERE
addressID = ?
CREATE TABLE Address ( addressID INTEGER, city
VARCHAR( 255 ), country VARCHAR( 255 ),
firstName VARCHAR( 255 ), lastName VARCHAR( 255 ),
phoneNumber VARCHAR( 255 ), state VARCHAR! 255 ),
streetAddressLinel VARCHAR( 255 ),
streetAddressLine2 VARCHAR( 255 ), zipCode
VARCHAR( 255 ), CONSTRAINT pk_Address PRIMARY
KEY ( addressID ) )
DROP TABLE Address
Рис 12.13. Параметры управления данными и развертыванием для EJB-компонента Address
Ссылки на EJB-компокенты для компонента Address
Кодовое имя Coded
Пате
Тип Туре
Собственный
интерфейс Ноте
Удаленный интерфейс
Remote
Имя JNDI Name
ejb/SequenceFaetory
Entity
com.deitel.advjhtpl.bookstore.ejb.SequenceFactory-Home
com.deitel.advjhtpl.bookstore.ejb.SequenceFactory
SequenceFactory
Рис. 12.14. Ссылки на EJB-компоненты для компонента Address
12.4. Реализация EJB-компонента SequenceFactory
Одной из фундаментальных концепций реляционных баз данных является
применение первичного ключа, уникально идентифицирующего строку в таблице
базы данных. Первичный ключ нужен для определения отношений между
таблицами в базе данных. Например, в нашем учебном приложении устанавливается
связь между каждым заказом Order и покупателем Customer путем помещения
первичного ключа custouierlD таблицы Customer в ноле (его называют внешним
ключом) таблицы Order. Идентификатор customerlD гарантированно является
уникальным, поэтому может быть использован для определения, какой именно
покупатель сделал заказ. EJB-компоненты Customer, Order и Address содержат записи
таблицы SequenceFactory, из которых онн могут получать первичные ключи,
12.4.1. Удаленный интерфейс SequenceFactory
Интерфейс SequenceFactory (рис. 12.15) представляет собой удаленный
интерфейс для EJB-компонента SequenceFactory. Метод getNextID (строка 15)
возвращает следующий имеющийся первичный ключ.
600
Глава 12
1 // SequenceFactory.j ava
2 // SequenceFactory - удаленный интерфейс для EJB-компонента
3 // с данными SequenceFactory.
4 package cam.de ite1.advjhtpl.bookstore.e jb ;
5
6 // Набор базовых пакетов Java
7 import java.rmi.RemoteException;
8
9 // Пакеты расширений Java
10 import javax.ejb.EJBObject;
11
12 public interface SequenceFactory extends EJBObject {
13
14 // получение следующего имеющегося уникального идентификатора
15 public Integer getNextID() throws RemoteException;
16 }
Рис. 12.15. Удаленный интерфейс SequenceFactory для генерирования первичных ключей
12.4.2. Реализация SequenceFactoryEJB удаленного интерфейса
SequenceFactory
На рис. 12,16 представлена реализация SequenceFactoryEJB удаленного
интерфейса SequenceFactory- Контейнер EJB управляет синхронизацией с базой
данных открытых элементов данных (строки 19-22).
1// SequenceFactoryEJB.java
2 // EJB-компонент с данными SequenceFactory генерирует уникальные
первичные ключи.
3 package com.deltel.advjhtpl.bookstore.ejb;
4
5 // Набор базовых пакетов Java
6 impox t java.xmi.RemoteException;
7 import java.util,ArrayList;
8
9 // Пакеты расширений .Java
10 import javax.ejb.*;
11 import javax.naming.*;
12 import j avax.rmi.PortableRemoteObject;
13
14 public class SequenceFactoryEJB implements EntityBean {
15 private EntityContext entityContext;
16
17 // поля, управляемые контейнером
18 public String tableName,- // имя таблицы с последовательностью
идентификаторов
19 public Integer nextXD; // следующий доступный уникальный
идентификатор
20
21 // получение следующего имеющегося идентификатора orderID
22 public Integer getNextID()
23 {
24 // сохранение идентификатора nextID для его возврата
25 Integer ID = new Integer! nextID.intValue() );
26
Практический пример корпоративного приложения. Бизнес-логика: часть 2 601
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65 }
// инкрементирование для получении следующего уникального
идентификатора
nextID = new Integer( ID.intValue{) + 1 );
return ID;
I
// установка контекста элемента данных
public void setEntityContext( EntityContext context )
entityContext = context;
// сброс контекста элемента данных
public void unsetEntityContext()
entityContext = null;
// активация экземпляра EJB-компонента SequenceFactory
public void ejbActivate()
tableName = ( String ) entityContext.getFrimaryKey()
// пассивация экземпляра EJB-компонента SequenceFactory
public void ejbPassivate()
tableName = null;
// удаление экземпляра EJB-компонента SequenceFactory
public void ejbRemove() {}
// сохранение данных EJB-компонента SequenceFactory в баае данных
public void ejbStoret) {)
// загрузка данных EJB-компонента SequenceFactory иа базы данных
public void ejbLoad() {}
Рис. 12.16. Реализация SequenceFactoryEJB удаленного интерфейса SequenceFactory
Метод getNextOrderlD (строки 25-31) вычисляет следующий имеющийся
уникальный идентификатор orderlD, инкрементируя текущее значение orderlD
(строка 27). Это новое значение сохраняется в EJB-компоненте (строка 28) и
возвращается вызвавшему процессу (строка 30). Метод getNextCustomerlD (строки 34-40)
инкрементирует текущее значение поля CustomerlD и возвращает значение
вызвавшему процессу (строка 39). Метод getNextAddressID (строки 43-49)
инкрементирует значение поля addressib и возвращает значение вызвавшему процессу
в строке 48.
Чтобы EJB-компонент SequenceFactory правильно вычислял уникальные
значения orderlD, customerlD и addressID, должен иметься только один экземпляр
EJB-компонента SequenceFactory. Если имеется более одного экземпляра EJB-
компонента SequenceFactory) это может привести к генерированию повторяю-
602
Глава 12
щихся значений order ID, custom erID или addressID (дубликатов). Для получения
нужного экземпляра EJB-компонента SequenceFactory клиентам EJB-компонента
SequenceFactory следует использовать только метод find Sequence Factory
интерфейса SequenceFactory Home. Разработчик должен определить SQL-запрос,
который будет возвращать одну и ту же запись таблицы SequenceFactory при каждом
вызове метода findSequencePactory.
12.4.3. Собственный интерфейс SequenceFactoryHome
Интерфейс SequenceFactoryHome (рис. 12.17) представляет собой собственный
интерфейс для EJB-компонента SequenceFactory. Метод findBy Primary Key
(строки 15-16) возвращает удаленную ссылку на EJE-компоненг SequenceFactory для
таблицы базы данных с указанным именем.
В таблицах на рис. 12.18 и 12.19 представлены параметры развертывания для
EJB-компонента с данными SequenceFactory. В дополнение к, этим установкам,
следует задать для типа транзакции Transaction Type значение Required для всех
бизнес-методов.
1 // SequenceFactoryHome.Java
2 // SequenceFactoryHome - собственный интерфейс для EJB-
3 // компонента с данными SequenceFactory.
4 package com.deitel.advjhtpl.bookstore.ejb;
5
6 // Набор базовых пакетов Java
1 import java.rmi.RemoteException;
8
9 // Пакеты расширений Java
10 import javax.ejb. *,'
11
12 public interface SequenceFactoryHome extends EJBHome {
13
14 // нахождение компонента SequenceFactory с заданным первичным
ключом
15 public SequenceFactory findByPrimaryKey( String tableName )
16 throws RemoteException, FinderException;
17 } , ,
Рис. 12.17. Интерфейс SequenceFactoryHome для нахождения экземпляров
EJB-компонента SequenceFactory
[ Основные параметры для развертывания EJB-компонента SequenceFactory
Тип Bean Type
Класс Enterprise Bean
Class
Собственный
интерфейс Ноше
Interface
Удаленный интерфейс
Remote Interface
Entity
com.deitel.advjhtpl.bookstore.ejb.SequenceFactoryEJB
com.deitel.advjhtpl.bookstore.ejb.SequenceFactoryHome
com.deitel,advjhtpl.bookstore.ejb,SequenceFactory
Рис. 12.18. Основное параметры для развертывания EJB-кок/понентз SequenceFactory
Практический пример корпоративного приложения. Бизнес-логика: часть 2 603
Параметры управления данными и развертыванием для EJB-компонента SequenceFactory
Управление персистентностью
Persistent Management
Класс первичного ключа
Primary Key Class
Container-Managed Persistence
j ava.lang.S tring
Имя поля первичного ключа
Primary Key Field Name
tableName
Имя базы данных JNDI
Database JNDI Name
jdbc/Bookstore
SQL-оператор SQL Statement
метода ejbStore
UPDATE SequenceFactory SET nextID = ?
tableName = ?
SQL-оператор SQL Statement
метода ejbCreate
INSERT INTO SequenceFactory ( nextID, tableName )
VALUES (?,">)
SQL-оператор SQL Statement
метода ejbRemove
DELETE FROM SequenceFactory WHERE tableName
SQL-оператор SQL Statement
метода findByPrimaryKey
SELECT tableName FROM SequenceFactory WHERE
tableName = ?
SQL-оперэтор SQL Statement
метода ejbLoad
SELECT nextID FROM SequenceFactory WHERE
tableName = ?
SQL-оператор создания
тзблицы Table Create SQL
Statement
CREATE TABLE SequenceFactory ( nextID INTEGER,
tableName VARCHARt 255 ), CONSTRAINT
pk_SequenceFactory PRIMARY KEY ( tableName ) )
SQL-оператор удаления
таблицы Table Delete SQL.
Statement
DROP TABLE SequenceFactory
Рис. 12.19. Параметры управления данными и развертыванием для EJB-компонента
SequenceFactory
12.5. Развертывание приложения Deitel Bookstore
средствами J2EE
Для развертывания компонентов приложения Deitel Bookstore на эталонной
реализации Java 2 Enterprise Edition (J2EE) требуется инструментальное средство
Application Deployment Tool. Ниже будут рассмотрены этапы процесса
развертывания EJB-компонента Order. Развертывание других EJB-компонентов
осуществляется аналогично. Общие инструкции по развертыванию сеансовых EJB-компо-
нентов с состоянием, таких как EJB-компонент ShoppingCart, можно найти в
главе 6. Общие инструкции по развертыванию сервлетов приведены в главе 4.
12.5.1. Развертывание компонентов-сущностей EJB
с персистентностью, управляемой контейнером
Чтобы начать развертывание EJB-компонентов с данными для приложения
Deitel Bookstore, выберите пункт New Enterprise Bean... из меню File
инструментального средства развертывания Application Deployment Tool (рис. 12.20). Вы
увидите интерфейс мастера создания JAR-файла EJB-компонента (рис. 12.21).
В поле JAR Display Name содержится текст, который будет появляться в окне
Application Deployment Tool для JAR-файла этого EJB-компонента, но не будет
604
Глава 12
оказывать влияния на развертывание приложения. Щелкните на кнопке Add...
рядом с полем Content, чтобы добавить файлы классов для EJB-компонента
в JAR-архив.
^Дй^: IJK,. ячш^^жт
Рис. 12.20. Добавление EJB-компонента в корпоративное приложение
рЩЩЕЗЩЖ
: (tin птйп№г^'
' tnitfonn
]♦ ВдЫДига. . ,'"-"':!':_, .„, 1, ^ffijjjpisiiisss Logic " |
Рис. 12.21. Создание JAR-файлэ для EJB-компонента
Практический пример корпоративного приложения. Бизнес-логика: часть 2 605
Чтобы добавить файлы классов EJB-компонента в JAR-файл, необходимо задать
корневой каталог Root Directory, который содержит структуру классов в пакете
(рис. 12.22). Например, EJB-компонент Order содержится в пакете com.dei-
tel.advjhtpl.bookstore.ejb. Если откомпилированный файл класса помещен в каталог
D:\BookStore\com\deitel\advjhtpl\bookstore\ejb, выберите в качестве корневого
каталога Root Directory D:\BookStore. Щелкните на кнопке Browse... и воспользуйтесь
диалоговым окном выбора файлов, чтобы выбрать корневой каталог Root Directory.
После выбора надлежащего корневого каталога укажите файлы классов для
удаленного интерфейса, собственного интерфейса, реализации и других классов,
необходимых для EJB-компонента (например, OrderModel.class, XMLGenera-
tor.class, специфичные для приложения классы исключений и т.д.). Удерживая
нажатой клавишу CTRL, можно выбрать несколько файлов за раз. Щелкните на
кнопке Add для добавления выбранных файлов классов в JAR-архив
EJB-компонента и щелкните на ОК (рис. 12.23). На рис. 12.24 показаны результаты
добавления файлов классов в JAR-файл EJB-компонента для EJB-компонента Order.
Ы^ашч 2 nwn ШШ^Шш'
Рис. 12.22. Задание корневого каталога для классов EJB-компонента
Рис. 12.23. Добавление классов EJB в JAR-файл EJB-компонента
606
Глава 12
янимплтеи!!!
„ AiAR.ffe Чыеайь^в" ^. fiofSitfn Щ& «&Htosd Be^nj^mi can:*dd а newJhfi la
Рис. 12.24. Результаты добавления классов EJB в JAR-файл EJB-компонента
После добавления файлов классов в JAR-файл EJB-компонента необходимо
указать файлы классов, которые содержат удаленный интерфейс, собственный
интерфейс и реализацию EJB-компонента (рис. 12.25). Выберите соответствующий
класс из раскрывающегося списка, как показано на рис. 12.25. EJB-компонент
Order представляет собой компонент-сущность, поэтому в разделе Bean Type
выберите опцию Entity (рис. 12.26),
t«?$f 4я mm i ef ей ciass янн. й1Й1*а tafcjwiSii Щят№Я JAR wit
1e Шейка tff EfrJerpuw вэдптмуы: wotfd шЬ create,'
; одса1е№е"^1Я&т1вгрг]»^п№М]ГО1:1М№рш$9&
^*WM>iwid> idispiarnW™, (евСЛрШ.'а.м! ita^iorthi ьтилф^иц»
&Ёйжё!1
iOafitejtiM»
■■Jj авМ^кШ^^ ;
7*4
*B«rt J Ntiit't- [|'::V ffowi .;
Рис. 12.25. Задание классов, собственною интерфейса и удаленного ишерфейса для
EJB-компонента
Практический пример корпоративного приложении. Б из нес-логика: часть 2 607
ESrfpw i ntprpmp tivan yttr&rtf -
ШЩ&Ш^ът н» ores tftjjjsaii fcij.wft bt тыл in v»ii"ejej№ ««t .■ ■
"-fiyii $$& * diBpley-rrame, W£«iplkirt entf itqn* Геи Ihft uuneflLrf othijr*
икмшйФгмфДОкЙо»
;g3|g
итлевйимтФикиМшогыв.Огвя
^■1
■■StfleMW:b-v.;.P.
..- ^ ^^^^;
', 3in)tiliMi(<№l8c , .;^
T Uff£P!lcim(3&f3?£
1ГЛ:
■f :-'
ZJ
Рис. 12.26. Задание Entity в качестве типа Bean Type EJ В-компонента
EJB-компонент с данными Order использует персистентность, управляемую
контейнером, для синхронизации своих данных с соответствующей таблицей базы
данных. На следующем шаге мастера (рис. 12.27) выберите опцию Container-
Managed Persistence и установите флажки рядом с каждым из полей,
управляемых контейнером. Укажите полное имя класса для класса первичного ключа
.■.,BSK?irJHsart<mllNJ>
■ -.ana ы, t4#£Bj* tieii newt..
' TMiltbfeUs^uwoWdlMpyrilfltij;, . '-'"-.'""
'^-A - '■'■' * '" " -*-: --■
KwQMSf
^|avaIan g Integer
; '№uiafy Key ReU Hair»;
"7^--^- C»
§
Рис. 12.27. Настройка полей, управляемых контейнером, и класса первичного ключа
608
Глава 12
(включая имя пакета) В поле Primary Key Class. Для EJB-компонента Order
введите java.Iang.Integer в качестве имени класса первичного ключа. Если ваш
EJB-компонент использует класс первичного ключа, определенный разработчиком
(например, EJB-компонент OrderProduct), необходимо также указать полное имя
пакета (например, com.deitel.advjhtpl.bookstore.ejb.OrderProdiictPK). Выберите
в раскрывающемся списке Primary Key Field Name поле, которое содержит
первичный ключ (например, orderiD).
Если ваш EJB-компонент ссылается на другие EJB-компоненты, эти EJB-kom-
поненты должны быть указаны в инструментальном средстве развертывания
Application Deployment Tool (рис. 12.28). Щелкните на кнопке Add, чтобы
добавить новую ссылку на EJB-компонент. Столбец кодового имени Coded Name
содержит строку, используемую для поиска EJB-компонента в каталоге JNDI.
Например, чтобы найти EJB-компонент Product, используется строка java:comp/eirv/
ejb/Product. Соответствующим кодовым именем будет ejb/Product. Выберите
нужный тип для EJB-компонента (например, Session для сеансового
EJB-компонента или Entity для компонента-сущности EJB) из раскрывающегося списка
Туре. Укажите полные имена классов (включающие имена пакетов) для
собственного и удаленного интерфейсов в столбцах Ноте и Remote. Например, в столбце
Ноте для EJB-компонента Product укажите com.deitel.advjh.tpl.bookstore.ejb.
ProdnctHome. Введите имя JNDI в поле JNDI Name для EJB-компонента, на
который осуществляется ссылка (например, Product).
Рис. 12.28. Задание других EJB-компонентов, на которые ссылается данный EJB-компонент
Серверы приложений J2EE поддерживают управление транзакциями,
семантика которого может быть задана при развертывании приложения. Для каждого из
бизнес-методов задайте соответствующий тип транзакции Transaction Type
(рис. 12.29), как рассказывалось в главе 7.
Па рис. 12.30 представлен листинг ХМL-дескриптора, который был
сформирован в результате предыдущих действий. Этот ХМL-дескриптор может быть
использован при развертывании данного приложения на любом совместимом с J2EE
сервере приложений.
Практический пример корпоративного приложения. Бизнес-логика: часть 2 609
Рис. 12.29. Задание режима контейнерного управления транзакциями для бизнес-методов
EJB-компонента
Рис. 12.30. ХМL-дескриптор, сформированный средством развертывания Application
Deployment Tool
Теперь необходимо настроить баау данных, в которой компонент-сущность
с персистентностью, управляемой контейнером, будет хранить свои данные.
Щелкните на кнопке Deployment Settings... на вкладке Entity (рис. 12.31).
Укажите имя JNDI для базы данных в поле Database JNDI Name (рис. 12.32).
В эталонной реализации J2EE Reference Implementation это значение
соответствует значению, заданному в файле конфигурации default.proper ties (например,
j doc/Book Store). Указав имя JNDI для базы данных, щелкните на кнопке
Generate SQL Now, чтобы создать необходимые операторы SQL для методов поиска
и создания EJB-компонента.
Вам будет предложено указать предложение WHERE для любых
нестандартных методов поиска, определенных в собственном интерфейсе EJB-компонента
(рис. 12.33). Для каждого метода, приведенного в списке EJB Method (например,
610
Глав,
Рис. 12.31. Задание параметров развертывания для EJB-компонента
gran Tin» Q»gjw£y ■■■„_. .... „
''фан, ш'тты*сь**\
-т,*шштл'
nneeycustomerD(rsi
ЗДБЮге
^Create Щ%
elbLMd
Рис. 12.32. Настройка параметров базы данных для EJB-ког^понента
ш._.„т
■t
^t_^
Рис. 12.33. Окно сообщения, указывающее о, что для SQL-запросов требуется
предложение WHERE
Практический пример корпоративного приложения. Бизнес-логика: часть 2 611
findByPriinaryKey, ejbStore, Table Create и т.д.), введите соответствующий текст
SQL-запроса из таблиц, приведенных в главах 11-12. Например, в таблице на
рис. 12.6 представлены соответствующие SQL-запросы для EJB-комповента
Customer.
Рис. 12.34. Задание SQL-запроса для метода flndByCustomerlD
12.5.2. Развертывание сервлетов
Сервлеты в приложении Deitel Bookstore используют параметры контекста
и инициализации, которые разработчик предоставляет при развертывании
приложения. Это позволяет значительно упростить процесс добавления в приложение
поддержки новых типов клиентов. В таблице на рис. 12.35 перечислены все
используемые в приложении сервлеты, значения для параметра инициализации
XSL_FILE сервлета и псевдонимы сервлетов.
Кроме этого, следует установить bookstore в качестве Web-контекста WAR-
фай л а сервлетов (рис. 12.36).
Сер влет
AddToC»rtServlet
RemoveFromCartServlet
OpdateCartServlet
ViewCartServlet
Che cXoutSe rvle t
viewOrderServlet
ViawOrderHistoryServlet
Значение параметра
инициализации XSL_FILE
error.xsl
error.xsl
error.xsl
viewCart.xsl
error.xsl
vi ewOrde r.xa1
viewOrdecHistory.xsl
GetAllProductsServlet 1 products.xsl
GetProductServlet | pjroductD«tails. xsl
Псевдоним сервлета
AddToCart |
RemoveFromCart J
UpdateCart
ViewCart |
Checkout |
ViewOrder j
Vi ewOrderHiatory
GetJU. lFzoduets
GetProduct
612
Глава 12
Сервлет
Produ с tS e archS e rvlet
Regis terServle t
LoginServlet
GetPasswordHinfcServlet
Значение параметра
инициализации XSL_FILE
products.xsl
error.xsl
login.xsl
pssswordHi.nt.xsl
Псевдоним сервлета
ProductSeareh
Register
Login
GetPasswordHint
Рис. 12.35. Параметры развертывания сервлетов приложения Deitel Bookstore
Рис. 12.36. Задание корня контекста Context Root для сервлетов приложения Deitel
Bookstore
Напомним, что сервлеты в приложении Deitel Bookstore получают информацию
о конфигурадии клиента из параметра CLIENT_LIST контекста сервлета. Задайте
значение для этого параметра контекста, как показано на рис. 12.37.
Рис. 12.37. Задание параметра контекста CLIENT J.15T для сервлетов приложения Deitel
Bookstore
Практический пример корпоративного приложения. Бизнес-логика: часть 2 613
Сервлеты в приложении Deitel Bookstore используют бизнес-логику EJB-компо-
нентов для хранения информации о магазинных тележках покупателей, создания
регистрационных записей и т.д. Чтобы дать возможность сервлетам осуществлять
доступ к EJB-компонентам, необходимо указать в инструментальном средстве раз^
вертывания ссылки на EJB-компоненты. На рис. 12.38 представлены все
необходимые ссылки на EJB-компоненты. Не забудьте указать имя JNDI для каждого
EJB-компонента (например, ShoppingCart) и полные имена классов для
собственного и удаленного интерфейсов.
Рис. 12.38. Ссылки на EJB-компоненты для сервлетов
Последний этап развертывания приложения Deitel Bookstore состоит в
добавлении документов таблиц стилей XSL и других вспомогательных файлов
в WAR-файл сервлетов. В таблице на рис. 12,39 перечислены эти вспомогательные
файлы с указанием их относительные маршрутов в WAR-файле сервлетов и
описанием каждого из них.
Имя файла
clients.xml
index.html
login.html
registration.html
index.wml
login.wml
Default.cas
*. jpg
*.xsl
Относительный путь
/
/
/
/
/
/
/styles/
/images/
/XSLT/XHTML/
Описание
Файл конфигурации, позволяющий
осуществлять поддержку клиентов различных
типов
Страница приветствия для XHTML-кпиентов
Форма входа для XHTML-клиентов
Форма регистрации для XHTML-клиентов
Страница приветствия для WML-кшентов
Форма входа для WML-клиентов
Каскадная таблица стилей дли
XHTML-клиентов
Изобрахения облохек для книжной продукции
XSi Т-трянсформации (таблицы стилей) для
XHTML-клиентов
614
Глава 12
Имя файла
navigation.xml
*.xsl
*.xsl
Относительный путь
/XSLT/XHTML/
/XSLT/WML/
/XSLT/cHTML/
Описание
Заголовок со средствами навигации для
XHTML-клиентов
XSLT-трансформации (тэбгицы стилей) для
WML-клиентов
XSLT-трансформации (таблицы стилей) для
cHTML-клиентов |
Рис. 12.39. Вспомогательные файлы, включаемые в WAR-фэйл сервлетов
Закончив настройку EJB-компонентов и сервлетов в инструментальном
средстве Application Deployment Tool, выберите пункт Deploy Application из меню
Tools, чтобы выполнить развертывание приложения на эталонной реализации
сервера приложений J2EE. Доступ к только что развернутому приложению можно
осуществить, указав URL http://locaihost:8000/bookstore/index.html в
Web-браузере или имитаторе i-mode, либо указав URL http://Iocalhost:8000/bookstore/
index.wml в имитаторе WML.
Мы закончили рассмотрение учебного приложения Deitel Bookstore, при
разработке которого был использован ряд корпоративных технологий Java. В главе 13,
«Серверы приложений», мы познакомимся с тремя наиболее популярными
коммерческими серверами приложений, совместимыми с J2EF3: ВЕА WebLngic, IBM
WebSphere и iPlanet Application Server. Затем мы обсудим, каким образом
осуществляется развертывание учебного приложения Deitel Bookstore на серверах ВЕА
WebLogic и IBM WebSphere.
13
Серверы приложений
Цели
• Познакомиться
с несколькими популярными
коммерческими серверами
приложений.
• Познакомиться с открыто
доступными альтернативами
коммерческим серверам
приложений,
• Узнать о требованиях,
предъявляемых к серверам
приложений, совместимым
с J2EE.
• Уяснить различия между
реализациями коммерческих
серверов приложений.
• Осуществить развертывание
учебного приложения
Enterprise Java Deitel
Bookstore на двух ведущих
коммерческих серверах
приложений.
Что может, быть лучше, чем
иметь мало потребностей
и самому их удовлетворять.
Ральф Вальдо Эмерсон
«Наоборот, — продолжил Траляля, — если
бы это было так, это бы еще ничего, а если
бы ничего, оно бы так и было, но так как
это не так, так оно и не этак. Такова
логика вещей».
Льюис Кэрролл
Красноречие — логика вдохновения.
Лаймен Бичер
... бассейн, глубина которого безмерна,
и тропинки, вдоль которых растут цветы
воспоминаний о былом.
Кэтрин Мэнсфилд
616
Глава 13
13.1. Введение
Java 2 Enterprise Edition представляет собой спецификацию для
корпоративных окружений выполнения. Хотя Sun предоставляет эталонную реализацию этой
спецификации, для работы реальной системы необходим коммерческий сервер
приложений. В этой главе будут рассмотрены три популярных коммерческих
сервера приложений, совместимых со спецификацией J2EE: BEA WebLogic, IBM
WebSphere и iPlanet Application Server. Мы также познакомимся с сервером
приложений JBoss с открытым доступом. Мы осуществим развертывание приложения
Deitel Bookstore, рассматриваемого в главах 9-12, чтобы продемонстрировать
переносимость приложений, написанных в соответствии с требованиями
спецификации J2EE. После изучения этой главы вам станет ясна роль сервера приложений
в корпоративных системах. Вы также сможете осуществлять развертывание вя-
ших собственных систем на коммерческих серверах приложений.
13.2. Спецификация J2EE и ее преимущества
Долгое время никакого стандарта для серверов приложений не существовало.
Каждый разработчик сервера приложений предоставлял свой собственный набор
интерфейсов прикладного программирования (API) с различными
функциональными возможностями. Если бы компания пожеляла перенести свои
корпоративные приложения на новую платформу сервера приложений, разработчикам
пришлось бы заново писать большое количество кода, а сам процесс переноса
становился сложным и дорогостоящим. Корпорация Sun Microsystems совместно
с большой группой посталщиков серверов приложений разработала спецификацию
Java 2 Enterprise Edition. Спецификация J2EE определяет платформу сервера
приложений и вспомогательные API для построения корпоративных систем, которые
обладают переносимостью между различными серверами приложений и,
поскольку они используют Java, между различными платформами. Спецификация J2EE
распространяет основной принцип Java «Написано однажды, работает везде»
(«Write Once, Run, Anywhere™») на корпоративные приложения. J2EE облегчает
переносимость между различными серверами приложений, давая возможность
Серверы приложений
617
разработчикам подключать специфичные для сервера функции, такие как
распределенные транзакции и запросы к базе данных, на этапе развертывания.
В спецификации J2EE можно выделить несколько основных разделов, таких
как поддержка API, безопасность, управление транзакциями и развертывание.
Поставщик сервера приложений обязан обеспечить поддернску выполнения API
платформы J2EE. В таблице на рис. 13.1 представлен список API, которые
являются обязательными для версии 1.2 спецификации J2EE1.
Обязательные API
Java Data Base Connectivity (JDBC) 2.0 Exteision
Remote Method Invocation — Internet Inter-Orb
Protocol {RMI - HOP) 1.0
Enterprise Java Bears (EJB) 1.1
Servlets 2.2
Java Server Pages (JSP) 1.1
Java Messaging System (IMS) 1.3
Java Nailing and Directory Interface (JNDI) 1.2
Java Transaction API (JTA) 1.0
JavaMail 1.1
Java Activation Framework (JAF) 1.0
Web-контейнеры
требуются
требуются
требуются
требуются
требуются
тррбуеттт
требуются
требуются
требуются
требуются
EJB-компоненты
требуются
требуются
требуются
нет
нет
требуется
требуется
требуется
требуется
требуется
Рис. 13.1. Интерфейсы прикладного программирования, обязательные для сервера
приложений
13.3. Коммерческие серверы приложений
Чтобы быть сертифицированным как продукт, совместимый с J2EE, сервер
приложений должен реализовывать минимум функциональных возможностей,
которые определены в спецификации J2EE. Поставщики серверов приложений
могут предоставлять функциональные возможности, которые намного превосходят
возможности, определяемые спецификацией J2EE, чтобы каким-то образом
выделить свой продукт среди других. Например, серверы приложений могут
предоставлять усовершенствованные средства развертывания, повышенные меры
безопасности, обеспечивать более высокую производительность, предоставлять средства для
устранения ошибок и т.д. В этом разделе описываются четыре популярных сервера
приложений: ВЕА WebLogic, iPlanet Application Server, IBM "WebSphere и JBoss.
13.3.1. BEA WebLogic 6.02
Компания ВЕА Systems в настоящий момент является ведущим в мире
поставщиком серверов приложений. Популярность сервера WebLogic главным образом
определяется тем, что он первым появился на рынке и заслужил хорошую
репутацию. ВЕА предоставляет сервер приложений общего назначения, стремясь достичь
оптимального соотношения между скоростью и стабильностью работы с одной
стороны, и основательной поддержкой различных функций, определяемых
спецификацией J2EE, с другой стороны.
1 На момент подготовки к изданию перевода книги актуальной была версия 1.3
спецификации J2EE. Версия 1.4 находилась на стадии законченного проекта. — Прим. ред.
г На мгомент подготовки к изданию переведя книги ■текущей била версия WebLogic 7.0. —
Прим. ред.
618
Глава 13
WebLogic предоставляет пулы данных, которые избавляют от необходимости
создавать новые соединения с базой дапных для каждого клиента. Установка
соединений с базами данных сопровождается значительными затратами времени:
для некоторых серверов баз данных на одно соединение затрачивается до
нескольких секунд. Имен пул открытых соединений и выделяя эти соединения клиентам
по мере необходимости, WebLogic также предоставляет механизм для «горячего»
развертывания. Сервер приложений постоянно проверяет указанный каталог на
наличие новых приложений; если сервер приложений находит новое приложение
в этом каталоге, WebLogic автоматически развертывает приложение без
перезапуска сервера. WebLogic также автоматически сворачивает приложение, если
администратор удаляет это приложение из каталога развертывания. Горячее
развертывание увеличивает время активной работы сервера и упрощает процесс
развертывания. Горячее развертывание может быть полезным для разработчиков при
тестировании приложений. Одновременно с этим, ВЕД не рекомендует
использовать горячее развертывание в рабочих окружениях.
WebLogic использует кластеризацию, которая позволяет повысить степень
доступности посредством поддержки режима восстановления после сбоя (failover)
для EJB-компонентов и Web-компонентов. Серверы в составе кластера
способствуют резервированию: если сервер, осуществляющий транзакцию, выходит из строя,
другой сервер может прозрачно для пользователя взять управление на себя, не
прерывая при этом транзакцию. Подобная организация также способствует
оптимальному распределению нагрузки. Б окружении с оптимизацией распределения
нагрузки сервер приложений распределяет запросы по нескольким серверам в
зависимости от их загруженности (т.е. от того, как много запросов каждый из
серверов на данный момент обрабатывает). Оптимальное распределение нагрузки
помогает предотвратить сбои отдельных серверов при большом количестве запросов.
Для окружений с одним сервером WebLogic предоставляет организацию
множественных пулов: сервис, который осуществляет распределение транзакций между
источниками данных.. Б то время, как пул соединений работает с одним
источником данных, множественные пулы дают возможность приложению осуществлять
доступ к нескольким пулам, тем самым, распределяя запросы среди множества
источников данных. Тем самым обеспечивается выравнивание нагрузки для систем
с одним сервером приложений.
Сервер WebLogic 6.0 полностью совместим со спецификацией J2EE 1.2 и
поддерживает многие требования спецификации J2EE 1.31. В составе WebLogic 6.0
имеется Web-сервер, но средство развертывания с графической оболочкой в него не
входит, поэтому разработчикам приходится писать код дескрипторов
развертывания самостоятельно. WebLogic также способен работать с несколькими понуляр-
ными средами разработки Java Development, такими как JBuilder и Visual Cafe.
WebLogic предоставляет расширенные возможности по обеспечению
безопасности. Управление доступом пользователей реализуются в WebLogic через списки
контроля доступа (Access control list — ACL). ACL обеспечивают эффективный
метод контроля полномочий пользователей. В WebLogic также предусмотрена
поддержка SSL и цифровых сертификатов.
13.3.2. iPlanet Application Server 6.0
Сообщество iPlanet E-Commerce Solutions представляет собой альянс между
компаниями Netscape Communications и Sun Microsystems. Альянс iPlanet создал
полностью сертифицированный под спецификацию J2EE сервер приложений
взамен устаревшего сервера приложений Netscape. Основными целями при разработ-
Версия WebLogic 7.0 полностью поддерживает спецификацию J2EE 1.3. — Прим. ред.
Серверы приложений
619
ке сервера приложений iPlanet были производительность, стабильность и полная
совместимость с J2EE. Чтобы создать быстродействующий, масштабируемый
сервер приложений, было решено интегрировать C++ с Java. Сервер iPlanet
предоставляет поддержку режима восстановления после сбоев, организацию пула
соединений и имеет ряд уникальных возможностей.
Web-коннектор управляет оптимальным распределением нагрузки сервера
приложений iPlanet. Web-коннектор управляет коммуникационным
взаимодействием между сервером приложений и Web-сервером. Web-коннектор распределяет
запросы между экземплярами сервера в зависимости от времени, затрачиваемого
сервером на выдачу ответа. Для достижений максимальной управляемости сервер
приложений может реализовывать свое собственное распределение нагрузки.
Запросы распределяются в соответствии с определенным алгоритмом, задаваемым
администратором развертывания в инструментальном средстве настройки.
В iPlanet имеется «закрепленное» распределение нагрузки: если компонент
помечен как «закрепленный», стандартный алгоритм распределения нагрузки не
применяется, и компонент всегда будет выполняться на «закрепленной» машине.
«Закрепленное» распределение нагрузки помогает в ситуациях, когда затраты
времени на создание соединений между серверами для данного EJB-компонента больше,
чем экономия времени за счет распределения нагрузки.
iPlanet использует протокол Lightweight Directory Access Protocol (LDAF) для
управления безопасностью. Пользователям могут назначаться групповые или
индивидуальные полномочия для доступа к отдельным компонентам приложения.
Настройка полномочий LDAP усложняет процесс настройки сервера, но позволяет
лучше управлять полномочиями пользователей. Сервер приложений iPlanet Application
Server интегрирован с серверами iPlanet Directory Server и iPlanet Web Server.
На момент написания этой книги альянс iPlanet собирался выпустить новую
версию сервера iPlanet Application Server.1 Чтобы скопировать последнюю версию,
посетите сайт www.iplanet.coin/ias_deite]. На Web-сайте www.dejtel.com можно
найти полное описание процесса развертывания приложения Deitel Bookstore
(книжный Internet-магазин) на сервере iPlanet.
13.3.3. IBM WebSphere Advanced Application Server 4.0
IBM WebSphere — популярный сервер, который почти столь же распространен
на рынке программных продуктов, как сервер приложений BEA WebLogic. В
версию 4.0 внесены значительные усовершенствования в сравнении с предыдущими
версиями. В частности, упрощен процесс настройки, уменьшено время выдачи
ответа, улучшена система безопасности. Версия 4.0 предоставляет простой
пользовательский интерфейс для администрирования и развертывания, уделяя основное
внимание скорости работы и масштабируемости. В состав WebSphere входит версия
Web-сервера Apache от IBM, поддержка режима восстановления после сбоев,
система организации пулов данных и управление безопасностью на уровне пользователя.
13.3.4. Сервер приложений J Boss 2.2.2
Сервер JBoss, объединенный с контейнером сервлетов Tomcat от Apache
Software Foundation, на данный момент совместим только со спецификацией J2EE
1.2 и представляет собой сервер приложений с открытым исходным кодом.
Программное обеспечение JBoss распространяется в соответствии с открытой лицензи-
На момент подготовки к изданию перевода книги версия 6.0 сервера iPlanet Application
Server называлась Sun™ ONE Application Server Enterprise Edition. Версия 6.5
находилась на стадии опробования. — Прим. ред.
620
Глава 13
ей Lesser General Public License (LGPL). Исходный код JBoss доступен свободно
и может быть использован для построения коммерческих приложений.
Предполагается, что JBoss будет обладать совместимостью с последующими
спецификациями J2EE. В настоящее время JBoss содержит большинство функциональных
возможностей, характерных для коммерческих серверов приложений. JBoss был од-
пим из первых серверов приложений, поддерживающих горячее развертывание.
При выполнении сервер занимает весьма небольшую область памяти, оставляя
значительный объем ресурсов для приложений [3].
В JBoss отсутствует поддержка кластеризации. Это может не иметь особого
значения для малых и средних предприятий, но ограничивает возможности применения
JBoss на коммерческом рынке. JBoss также не предоставляет инструментальных
средств с графическим интерфейсом для осуществления развертывания или
настройки. К счастью, большое сообщество пользователей JBoss поможет быстро справиться
со значительной частью проблем, которые могут возникнуть при настройке.
13.4. Развертывание приложения Deitel Bookstore
на сервере BEA WebLogic
Настройка сервера приложений может оказаться довольно сложной задачей.
В этом разделе будут рассмотрены все необходимые этапы установки и настройки
сервера BEA WebLogic для развертывания учебного приложения Deitel Bookstore.
Инструкции по настройке можно найти в документе e-docs.bea.com/wls/docs60/
install/instprg.html. В этих инструкциях по настройке предполагается, что с;\Ьеа
является основным каталогом, Deitel — именем домена администрирования
WebLogic, a bookstore — именем сервера. Также предполагается, что система
управления базами данных Cloudscape установлена в каталоге c:\cloudscape_3.6,
и что нашей базой данных является c:\cloudscape_3.6\databases\Bookstore. В
зависимости от требований к безопасности, вы можете создать файл с именем
passwordJni с паролем, который задается при установке. Если поместить файл
password.ini в каталог c:\bea\wlserver6.0\config\deitel\, WebLogic не будет
каждый раз запрашивать пароль яри запуске сервера приложений.
Сначала выполним настройку WebLogic, чтобы обеспечить поддержку работы
с базами данных Cloudscape, Откройте файл c:\bea\wlserver6.0\eonfig\
CaseStudyDS\startWebLogic.cmd в текстовом редакторе, таком как Notepad.
Ближе к концу файла замените строку
set CLASSPATH = .;. lib\weblogic_sp.jar; .\lib\weblogic.jar
на строку
set CLASSPATH = .;. lib\weblogic_sp.jar; .\lib\weblogic.jar;
e:\cloudscape_3,6\lib\cloudscape.jar
которая содержит описание пути к пакету Cloudscape в переменной окружения
CLASSPATH. Следующая строка содержит параметры для запуска сервера:
%JAVA_HOME%\bin\java -hotspot -ms64m ~юх64т -classpath
%CLASSPATH% -Dweblogic.Domain=deitel
-Dweblogic.Name=bookstore -Dbea.home=c:\bea
-Djava.security.policy=c:\bea\wlserver6.0\lib\weblogio.pol
icy -Dweblogic.management.password=%WLS_PW% weblogic.Server
Чтобы упростить поиск базы данных, мы устанавливаем значение c:/cloud-
scape_3.6/dat abases/ для свойства cloudscape.system.home в командной строке.
Далее, запустите файл startWeblogic.cmd либо дважды щелкнув на нем
мышью, либо введя его имя с консоли. Если окно консоли загружается, и через
некоторое время появляется текст, подобный следующему:
Серверы приложений
621
<WebLogic Server started>
<Notiee> <WebLogicSeirver> <SSLListThread listening on port 7002>
<Notice> <WebLogicServer> <ListenThread listening on port 7001>
то сервер приложений готов к настройке.
Введите localhost:7001/console в строке адреса вашего Web-браузера. Появится
окно с запросом вашего сетевого пароля. Введите system для User Name и пароль,
который был задан в процессе установки WebLogic. Ваш Web-браузер должен
отобразить страницу, представленную на рис, 13.2. Это консоль администрирования
для управления большинством функций сервера приложений.
Рис. 13.2. Консоль администрирования сервера WebLogic (публикуется с разрешения
ВЕА Systems)
Теперь настроим пул данных JDBC и источник данных. В главной панели
администрирования выберите Connection Pools под заголовком JDBC. В верхней части
правой панели должна иметься ссылка Create new JDBC Connection Pool.
Установите параметры, как показано на рис. 13.3. Укажите Books tor ePool в качестве
имени Name пула. Установите URL jdbc:cloudscape:Bookstore и имя класса
драйвера Driver Classname COM.cloudscape.core.JDBCDriver. Наконец, установите
для свойств password и server значения none. После ввода этих значений выберите
Apply. Перезапускать сервер нужно лишь в том случае, если вам потребовалось
внести дополнительные изменения.
Далее, откройте вкладку Targets, выберите Bookstore из перечня имеющихся
серверов, щелкните на направленной вправо стрелке, чтобы добавить этот пул
соединений к серверу книжного магазина, а затем щелкните на Apply. В правой
панели выберите CaseStudyDS>Service>JDBC>Date Source, а затем Create new
JDBC data source. Задайте имя источника данных (например, BookstoreData-
Source), введите jdbc/Bookstore в качестве имени JNDI, BookstorePool — в каче-
622
Глава 13
стве имени пула и выберите Create. Наконец, выберите Bookstore на вкладке
Targets и щелкните на Apply. Остановите сервер, закройте окно команд WebLogic
и перезапустите сервер с помощью командного файла starWebLogic.cmd,
Рис. 13.3. Параметры пула соединений JDBC
Теперь создадим дескриптор развертывания для нашего приложения.
Извлеките содержимое архива bookstore.eaf в новый каталог. Для извлечения этих файлов
можно использовать любую утилиту: zip или jar. Чтобы извлечь файлы с помощью
утилиты jar, имеющейся в пакете JDK, введите команду
jar xvf bookstore.ear
Далее, извлеките содержимое архива ejb-jar-ic.jar во временный каталог с
именем ejb-jar. Создайте текстовый файл с именем weblogic-ejb-jar.xml и сохраните
его в каталоге ejb-jar\META-INF. Этот файл представляет собой дескриптор
развертывания, специфичный для WebLogic.
Дескриптор развертывания weblogic-ejb-jar.xml (рис. 13.4) определяет опции
кэширования имен, сохраняемости, управления транзакциями и другие опции
для EJB-компонентов в приложении Deitel Bookstore. В строках 6-8 задается DTD
для дескриптора. В данном случае в качестве типа документа (doctype) задается
WebLogic 5Л.0, поскольку наше приложение использует версию EJB 1.1. Элемент
weblogic-ejb-jar (строки 11-356) содержит информацию о развертывании для всех
EJB-компоиентов в JAR-файле. Элемент weblogic-enterprise-bean (строки 13-71)
содержит информацию о развертывании для EJB-комтюнента Customer. Элемент
ejb-name (строка 14) задает имя этого компонента. Элемент ejb-name должен
соответствовать имени, найденному в документе ejb-jar.xmi для WebLogic, чтобы
корректно идентифицировать текущий компонент. В строках 17-21 содержатся
дескрипторы свойств кэширования. Элемент шах-bean-in-caclie (строка 18) определяет
максимальное число активных экземпляров, которые разрешает иметь контейнер.
По достижении этого предела контейнер Е JB будет пассивировать бездействующие
экземпляры EJB-компонента. Элемент cache-strategy (строка 19) определяет,
каким образом EJB-компоненты будут котировать данные. Допустимыми
значениями являются Read-Write или Read-Only. Значение по умолчанию Read-Write дает
возможность клиентам записывать информацию в компонент в составе
стандартной транзакции, а метод ejbStore вызывается контейнером по завершении
транзакции. Значение Read-Only не разрешает вызывать метод ejbStore, по дает
возможность периодически обновлять компонент информацией, содержащейся в ос-
Серверы приложений
623
новном источнике данных. Это может быть полезно, например, для компонента,
который осуществляет передачу котировок ценных бумаг. В строке 19 этот
компонент определяется как компонент, предназначенный для использования в составе
стандартной транзакции. Элемент read-timeout-secotids для компонентов типа
Read-Write но используется, а для компонентов типа Read-Only его значение
задает интервалы времени в секундах между последовательными обновлениями базы
данных. Если значение установлено в 0, компонент типа Read-Only обновляется
только при его создании.
1 <?мп1 version = "1.0" encoding = "OTF-8"?>
2
3<!-- weblogic-ejb-jar.xml - дескриптор развертывания приложения -->
4<!-- Bookstore описывает специфические для weblogic свойства для
каждого компонента -->
5
6 «C'DOCTYPE weblogic-ejb-jar PUBLIC
7 '-//BEA Systems, Inc.//DTD WebLogic 5.1.0 EJB//EM"
8 'http://www .bea.com/servers/wls510/dtd/weblogic-ejb-jar.dtd'>
9
10 <!-' Собственный дескриптор weblogic EJB-коыпонента -->
11 <weblogic-ejb-jar>
12
13 <weblogic-enterprise-b©an>
14 <ejb-name>Customer</ejb-name>
15
16 <!-- задание свойств кэширования -->
17 <caching-descripfcor>
18 <max-beans-in-cache>100</max-beans-in-cache>
19 <cache-strategy>Read-Write</cache-strategy>
20 <read-timeout-seconds>0</read-timeout-seconds>
21 </caching-descriptor>
22
23 <!-- задание карты соответствий типов для компонента -->
24 <persistence-descriptor>
25
26 <persistence-type>
27 <type-identifier>
28 HebLogic_CMP_PDBMS
29 </type-±dentifier>
30 <type-version>5.1.0</type-version>
31 <type-storage>
32 META-INF/weblogic-cmp-rdbms-jar-Customer.xml
33 </type-storage>
34 </persistence-type>
35
36 <persistence-use>
37 <type-identifier>
38 WebLogic_CMP_HDBMS
39 </type-idsntifier>
40 <type-version>5.1.0</type-version>
41 </pers±stence-use>
42 </persistence-descriptor>
43
44 <!-- параметры транзакции -*•>
45 <transaction-descriptor>
4 6 <trans-timeout-seconds>200</trans-timeout-seconda>
624
Глава 13
47 </transaction-descriptor>
4В
49 <!— задание соответствий ссылок и имен ejb-компонентов —>
50 <reference-descriptor>
51 <ejb-reference-description>
52 <ejb-ref-name>ejb/Order</ejb-rer"-name>
53 <jndi-name>ejb/Order</jndi-name>
54 </ejb-reference-description>
55
56 <ejb-reference-description>
57 <ejb-ref-name>ejb/SequenceFaetory</ejb-ref-name>
58 <jndi-najne>ejb/SequenceFactory</jndi-name>
59 </ejb-reference-description>
60
61 <ejb-reference-description>
62 <eib-ref-name>ejb/Address</ejb-ref-riame>
63 <jndi-naine>ejb/Address</jndi-name>
64 </ejb-reference-description>
65
66 </reference-descriptor>
67
68 <!-- присвоение имени JHPI ВJB-компоненту —>
69 <jndi-name>ejb/Cu8tomer</3ndl-name>
70
71 </weblogic-enterprise-bean> О— конец дескриптора Customer -->
72
73 <!-- дескриптор weblogic EJB-компонентов SequenceFactory —>
74 <weblogic-enterprise-bean>
75
76 <ejb-name>SequenceFactory</ejb-naae>
77
78 <!-- управление поведением при кэшировании компонентов -->
79 <caching-descriptor>
80 <max-beans-in-cache>100</max-beans-iiL-cache>
81 <idle-tlmeout-seconds>20</idle-timeout-seconds>
82 <cache-strategy>Read-Write</cache-strategy>
83 <read-timeout-ssconds>0</read-timeout-seconds>
84 </caching-descriptor>
85
86 <!-- задание соответствия между компонентом и дескриптором
смр —>
87 <persistence-descriptor>
88 <persistence-type>
89 <type-identifier>
90 WebLogic_CMP_RDBMS
91 </type-identifier>
92 <type-version>5.1.0</type-vers±on>
93 <type-storage>
94 META-IHF/weblogic-cmp-rdbms-jar-Sequence.xn»l
95 </type-storage>
96 </persistence-type>
97
98 <persistence-use>
99 <type-identifier>
100 WebLogic_CMP_RDBMS '
101 </type-identifier>
Серверы приложений
102 <type-version>5.1.0</type-version>
103 </porsistence-use>
104 </persistence-descriptor>
105
106 <!-- параметры управления транзакцией —>
107 <transaction-doscriptor>
108 <trans-tin\eout-seconds>200</trans-timeout-seconds>
109 </transaction-descriptor>
110
111 <!-- присвоение компоненту имени JHDI -->
112 <jndi-name>ejb/SequenceFactory</jndi-name>
113
114 </weblogic-enterprise-bean>
115 <!-- конец дескриптора SequenceFactory —>
116
117 <!-- дескриптор weblogic для EJB-компонента Order -->
116 <weblogic-enterprise-bean>
119 <ejb-name>Order<l/ejb-name>
120
121 <!-- определяет свойства кэшированияг
установленные по умолчанию —>
122 <caching-descriptor>
123 <max-beans-in-cache>100</max-beans-in-cache>
124 <idle-timeout-seconds>20</idle-timeout-seconds>
125 <eache-strategy>Read-Write</caehe-strategy>
126 <cead-timeout-seconda>u</iead-titfte«yut.-8econds>
127 </eaching-descriptor>
128
129 <!-- задание соответствия между компонентом
и дескриптором СМР -->
130 <persistence-descriptor>
131 <persistence-type>
132 <type-identifi.er>
133 WebLogic_CMP_RDBMS
134 </type-iderttifier>
135 <type-version>5.1.0</type-version>
136 <type-storage>
137 META-INF/weblogic-cmp-rdbms-jar-order.xiril
138 </type-storage>
139 </persistence-type>
140
141 <persistence-use>
142 <type-identifier>
143 WebLogic_CMP_RDBMS
144 </type-ideiitifier>
145 <type-version>5.1.0</type-version>
146 </persistence-use>
147 </persistence-descriptor>
148
149 <!-- определение атрибутов транзакции -->
150 <transaction-descriptor>
151 <trans-timeovit-seconds>200</trans-timeout-seconds>
152 </transaction-descriptor>
153
154 <!-- задание соответствий между ссылками на компоненты
и именами JNDI -->
626
Глава 13
155 <reference-descriptor>
156 <ejb-reference-description>
157 <ejb-ref-name>ejb/SequenceFactory</ejb-ref-name>
15B <jndi-nanie>ejb/SequenceFactory</jndi-name>
159 </ejb-reference-description>
160
161
162 <ejb-reference-description>
163 <ejb-ref-name>ejb/OrderProduct</ejb-ref-name>
164 <jndi-namo>ejb/OrderProd4ict</3nd.i-naine>
165 </ejb-i:eference-description>
166
167 <eib-referenc:e-'description>
168 <ejb-ref-name>ejb/Customer</ejb-ref-name>
169 <jndi-name>ejb/Customer</jndi-name>
170 </ejb-reference-description>
171
172 </reference-descriptor>
173 <jndi-name>ejb/Order</jndi-name>
174
175 </weblogic-enterpr±ae-bean> <!— конец дескриптора Order -->
176
177 <!— дескриптор развертывания weblogic для EJB-компонента
Address —>
178 <weblogic-enterprise-bean>
179 <ejb-name>Address</ejb-name>
180
181 <!— определение свойств кэширования для компонента —>
182 <caching-descriptor>
183 <max-beans-in-cache>l0 0</max-beans-in-cac:he>
184 <idle-timeout-seconds>20</idle-timeout-seconds>
185 <cache-strategy>Read-Write</cache-strategy>
186 <read-timeout-seconds>0</read-timeout-seconds>
187 </caching~descriptor>
186
189 <!-- задание соответствия между EJB-компонентом и определенным
дескриптором amp -->
190 <persistence-descriptor>
191 <persistence-type>
192 <type-identifier>
193 WebLogic_CMP_RDBMS
194 </type-identifier>
195 <type-version>5,1.0</type-version>
196 <type-storage>
197 METft-INF/weblogic-cmp-rdbms-jar-address.xml
198 </type-storage>
199 </persistence-type>
200
201 <persistenco-use>
202 <type-identifier>
203 WebLogic_CMP_RDBMS
204 </type-identifier>
205 <type-version>5.1.0</type-version>
206 </persistence-use>
207 </persistence-descriptor>
208
Серверы приложений
6
209 <!-- определение параметров транзакции—>
210 <transaction-descriptor>
211 <trans-timeout-seconds>200</trans-tinieout-seeonds>
212 </transaction-descriptor>
213
214 <!-- задание карти соответствий для ссыпок на имена JNDI
компонентов —>
215 <reference-descriptor>
216 <ejb-reference-description>
217 <ejb-ref-name>ejb/SequenceFactory</ejb-ref-name>
218 <jndi-name>ejb/SequenceFactory</jndi-name>
219 </ejb-referonce-description>
220 </reference-descriptor>
221
222 <!-- присвоение имени JNDI этому компоненту -->
223 <jndi-name>ejb/Address</jndi-name>
224
225 </weblogic-enterprise-bean> <!-- end Address descriptor -->
226
227 <!— дескриптор развертывания weblogic для EJE-компонента
Order-Product —>
228 <weblogic-enterprise-bean>
229 <ejb-name>OrderFroduct</ejb-naine>
230
231 <!-- задание свойств кэширования по умолчанию -->
232 <caching-descriptor>
233 <max-be-ans-in-cache>100</niax-beans-in-cache>
234 <idle-timeout-seconds>20</idle-timeout-seeonds>
235 <cache-atrategy>Read-Write</cache-strategy>
236 <read-timeout-seconds>0</read-timeout-seconds>
237 </caching-descriptor>
238
239 <!-- задание соответствия этого компонента определенному
дескриптору СМР-->
240 <persistence-descriptor>
241 <persistence-type>
242 <type-identifier>
243 WebLogic_CMP_RDBMS
244 </type-identifier>
245 <type-version>5.1.0</type-version>
246 <type-storage>
247 META-INF/weblogic-cmp-rdbms-jar-orderProduct.xml
248 </type-storage>
249 </persistence-type>
250
251 <persistence-use>
252 <type-identifier>
253 WebLogic_CMP_RDBMS
254 </type-ident-i£ier>
255 <type-version>5.1.0</type-version>
256 </persistence-use>
257 </persistence-descriptor>
258
259 <!-- задание соответствий для ссылок на имена JNDI
компонента -->
260 <reference-descriptor>
628
Глава 13
261 <ejb-reference-description>
262 <ejb-ref-name>ejb/Product</ejb-ref-name>
263 <jndi-name>ejb/Product</3ndi-naioe>
264 </ejb-reference-deBcription>
265 </reference-descriptor>
266
267 <!-- присвоение имени JNDI этому компоненту —>
268 <jndi-name>ejb/OrderProduct</jndi-name>
269
270 </weblogic-enterprise-oean>
271 <l-- конец дескриптора OrderEroduct —>
272
273 <!-- дескриптор развертывания weblogic EJB-компонекта Product —
274 <weblogic-enterprise-bean>
275 <ejb-name>Product</ejb-name>
276
277 <!-- определение свойств кэширования для EJB-компонента -->
278 <caching-descriptor>
279 <idle-timeout-seconds>20</idle-timeoiit-secojnds>
280 <cache-strategy>Read-Write</cache-strategy>
281 <read-timeout-seconds>0</read-timeout-seconds>
282 </eaching-descriptor>
283
284 <!— задание соответствия между этим компонентом и его
дескриптором СМР -->
285 <persistence-descriptor>
286 <persistence-type>
287 <type-identifier>
288 WebLogic_CMP_RDBMS
289 </type-identifier>
290 <type-version>5.1.0</type-version>
291 <type-storage>
292 META-INF/weblogic-cmp-rdbms-jar-product.xml
293 </type-storage>
294 </persistence-type>
295
296 <persistance-use>
297 <type-identifier>
298 WebLogic_CMP_RDBMS
299 </type-identifier>
300 <type-version>5.1.0</type-version>
301 </persistence-use>
302 </persistence-descriptor>
303
304 <!-- определение параметров транзакции —>
305 <transaction-descriptor>
306 <trans-timeout-seconds>200</trans-timeout-aeconds>
307 </transaction-descriptor>
308
309 <!-- присвоение имени JNDI -->
310 <jndi-r»ame>e5b/Product</jndi-name>
311
312 </weblogic-enterprise-bean> <!— конец дескриптора Product —>
313
314 <!-- дескриптор развертывания weblogic EJB-компонекта
ShoppingCart —>
Серверы приложений
629
315 <weblogic-enterprise-bean>
316 <e3b-name>SboppingCart</ejb-name>
317
318 <!— определение свойств кэширования,
установленных по умолчанию —>
319 <caching-dascriptor>
320 <max-beans-in-cache>100</niax-beans-in-cache>
321 <idle-timeout-seconds>20</idle-timeout-seconds>
322 <cache-strategy>Read-Write</cache-strategy>
323 <read-timeciut-seconds>0</read-timeout-seconds>
324 </caching-descriptor>
325
326 <!— назначение каталога для хранения компонента -->
327 <persistence-descriptor>
328 <stateful-session-pexsistent-store-dir>
329 /config/deitel/
330 </stateful-session-persistent-store-dir>
331 </pexsistence-descriptor>
332
333 <!-- определение атрибутов транзакции —>
334 <transaction-descriptor>
335 <trans-timeout-seconds>200</trans-timeout-seconds>
336 </transaction-descriptor>
337
339 <!— задание соответствия между ссылками на EJB-компоненты
и именами JNDI -->
339 <reference-descriptor>
340
341 <ejb-re;fe,rence-descripfcion>
342 <ejb-ref-name>ejb/Product</ejb-ref-name>
343 <j ndi~name>ejb/Produc t</j ndi-name>
344 </ejb-reference-description>
345
346 <ejb-reference-d©scription>
347 <e3b-ref-name>ejb/0rder</ejb-ref-name>
348 <indi-name>ejb/Order</3ndi-name>
349 </ejb-reference-descripti©n>
350
351 </re£erence-descriptor>
352 <jndi-name>ejb/ShoppingCart</jndi-name>
353
354 </weblogic-enterprise~bean>
<!-- конец дескриптора ShoppingCart -->
355
356 </weblogic-ejb-jar> <!— конец дескриптора weblogic —>
Рис. 13.4. Документ weblogic-ejb-jar.xml определяет параметры развертывания для
приложения Bookstore
Элемент persistence-descriptor (строки 24-42) определяет свойства персистент-
ности EJB-компонентов. Компонент Customer использует псрсистсптность,
управляемую контейнером (CMP). WebLogic требует, чтобы каждый компонент с СМР
имел свой собственный XML-дескриптор. При этом блок управления
персистентностью в этом коде просто идентифицирует имя и тип дескриптора управления
персистентностью. Элемент persistence-type (строки 26-34) содержит элементы
type-identifier (строки 27-29), которые должны иметь значение WebLo-
630
Глава 13
gic_CMP_EDBMS. Элемент type-version имеет значение 5.1.0 для EJB 1.1 и 6.0
для EJB 1.2. Номер версии должен соответствовать номеру, указанному в DTD
weblogie-ejb-jar. Элемент type-storage (строки 31-33) задает местоположение
дескриптора СМР: файла, который мы создадим далее. Может быть определено
несколько типов сохраняемости persistence-type. В следующем блоке,
persistence-use, выбирается, какие типы сохраняемости использовать. В строках 36-41
WebLogic предписывается использовать тип сохраняемости, заданный в
предшествующем элементе. На данный момент элемент transaction-descriptor содержит
только одну опцию: trans-timeout-seconds (строка 46). Если транзакция
продолжается свыше указанной длительности, она будет отменена. Элемент reference-
descriptor (строки 50-66) связывает каждую ссылку с корректным именем JNDI.
Каждый блок содержит элементы ejo-ref-name и jndi-name для вычисления
ссылок на другие EJB-компоненты.
Дескрипторы для всех других компонентов-сущностей BJB имеют тот же
формат, что и дескриптор для Customer, Единственными различиями являются
ссылка на файл дескриптора RDBMS и ссылки на каждый из EJB-компонентов.
Сеансовый компонент EJB без состояния ShoppingCart (строки 315-354)
требует задания несколько иной информации о развертывании. Для сеансовых
компонентов без состояния файлы дескрипторов сохраняемости RDBMS не нужны.
Вместо этого для них требуется указать каталог для хранения сохраняемых сеансов
и каталог для хранения пассивированных сеансов. Элемент stateful-session-
persistent-store-dir в составе элемента persistence-descriptor определяет, где
контейнер должен хранить пассивированные сеансы [4].
В таблице на рис. 13.5 приведены некоторые необязательные элементы в
документе weblogic-ejb-jar.xml. Полный перечень можно найти на сайте edocs.bea.com.
Если элемент не определен, WebLogic использует значение но умолчанию.
Дескриптор RDBMS описывает взаимодействие компонента-сущности EJB с
базой данных. Дескриптор задает пул соединений и имя таблицы, устанавливает
соответствие между полями EJB-компонента и полями базы данных., а также
определяет пользовательские средства поиска для методов собственного интерфейса.
ж^ ЫИ11
Родительский
элемент
caching-
descriptos
caching-
descriptor
persistence-
descriptor
persistence-
descriptor
Элемент
max-beans-in-
free-pool
ini tial-bean s-
in-free-pool
i s-modified-
method-name
delay-wpdates-
until-end-of-tx
Описание
Допускается дли сеансовых компоненюи EJB без
состояния. Определяет максимальное число
свободных компонентов, которые могут храниться
в пуле. По умолчанию ограничений нет, j
Допускается для сеансовых компонентов EJB без |
состояния. Определяет число изначальных j
экземпляров компонентов. Значение по умолчанию I
равно 0,
Имя метода, который вызывается при сохранении
EJB-компонечта. Метод должен возвращать булево .
значение. Если метод возвращает true,
EJB-компонент сохранен.
При установке значения false таблица базы данных
компонентов обновляется после выполнения каждого
из методов, Если установлено значение true, база
данных обновляется по завершении транзакции.
Значение по умолчанию - true.
Серверы приложений
631
Родительский
элемент
persistence-
descriptor
persistence-
descriptor
reference-
descriptor
resource-
descriptor
resourcede scrip tor
security-
role -
assignment
security-
role-
assignment
weblogic-
enterprise-
bean
Элемент
£inder-call-
ejbload
db-is-shared
гезоигсе-
deacriptor
res-ref-name
jndi-патае
role-name
principal-name
enable-call-
by- reference
' -
Описание
Допускается для компонентов-сущностей. Значение
true указывает, что компонент загружается после
первого к нему обращения с помощью метода
поиска. Значение false указывает, что компонент
загружается при первом вызове. Значение по
умолчанию - false.
Допускается для компонентов-сущностей. При
значении false предполагается, что компонент имеет
эксклюзивный доступ к базе данных и не
перезагружает данные. При значении true данные
перезагружаются перед каждой транзакцией.
Значение по умолчанию - true.
Содержит описание мастеров ресурсов,
перечисленных в документе ejb-jar.xml
Имя ссылки на ресурс, содержащееся в документе
eyb-jar.xml
Присваивает имя JNDI мастеру ресурсов.
Имя роли а системе безопасности, определенное
в документе ejb-jar.xml.
Связывает имя роли с администратором,
определенным в конфи!урации сервера WebLogic. За
справкой о допустимых именах администраторов
обратитесь к сайту edocs.bea.com.
Если EJB-компоненты, размещенные на одном
сервере, nt;peMdKjicn по ссылке, задание дли з-ют
элемента Значения false приведет к передаче
переменных по значению. |
Рис. 13.5. Необязательные теги документа weblogic-ejb-jar.xml, не используемые
в тексте
Дескриптор weblogic-cmp-rdbms-jar-address.xnil (рис. 13.6) следует
определению DTD для WebLogic версии 5.1. Элемент webLogic-rdbms-bean (строка 11)
является базовым элементом дескриптора. В файле дескриптора может быть только
один элемент weblogic-rdbms-bean. Элемент pool-name в строке 14 должен
содержать имя пула данных, уже определенное в конфигурации WebLogic. Значение
table-name должно соответствовать имени таблицы в источнике данных, которую
использует компонент. В нашем примере EJB-компонент Address осуществляет
запись в таблицу Address (строка 17). Элемент attribute-map содержит карту
соответствий между полями EJB-компонента и полями базы данных, определенными
в документе ejb-jar.xml. В нашем примере имена полей базы данных совпадают
с именами полей EJB-компонента, но это не обязательно. Определение
соответствий в дескрипторе развертывания дает возможность администратору
развертывания настраивать EJB-компоненты под конкретные базы данных, не внося
изменений в код EJB-компонента. Элемент object-link (строки 21-24) содержит
соответствия между полями EJB-компонента и полями базы данных для zipCode. Имена
полей компонента и полей базы данных задаются, соответственно, в элементах
bean-field и dbms-column.
63 2
Глава 13
1 <?xml version = "1.0" encoding = "UTF-8"?>
2
3 <!— weblogic-cntp-rdbms-jar-address.xml - дескриптор развертывании -->
4 <!-- rdbms для EJB-компонента Address, определяющий свойства базы
данных -->
5
6<!DOCTYPE weblogic-rdbms-bean PUBLIC
7 '-//BEA Systems, Inc.//DTD WebLogic 5.1.0 EJB RDBMS Persistence//EH'
8 'http://www.bea.com/servers/wls510/dtd/
weblogic-rdbms-persistence.dtd'>
9
10<'-- основной блок дескриптора rdbms —>
11 <weblogic-rdbms-baan>
12
13 <!-- задание пула данных для BookstorePool -->
14 <pool-name>BookstorePooK/pool-name>
15
16 <!— задание ADDRESS в качестве таблицы данных-->
17 <table-name>Address</table-name>
18
19 <!-- задание соответствий между полями EJB и полями
базы данных -->
20 <attribute-map>
21 <object-link>
22 <bean-field>zipCode</bean-fiald>
23 <dbms-column>zipCode</dbm8-column>
24 </object-link>
25
26 <objact-link>
27 <bean-field>state</bean-field>
28 <dbms-column>state</dbms-column>
29 </object-link>
30
31 <object-link>
32 <bean-field>addressID</bean-field>
33 <dbms-colutnn>addresslD</dbHis-col,umn>
34 </c>bject-link>
35
36 <object-link>
37 <bean-field>streetAddressLine2</bean-field>
38 <dbms-column>streetAddressLine2</dbms-colmnn>
39 </object-link>
40
41 <object-link>
42 <bean-field>country</bean-field>
43 <dbms-coliann>country</dbms-column>
44 </object-link>
45
46 <object-link>
47 <bean-field>streetAddressLinel</bean-field>
■48 <dbms-coltrnin>streetAddressLinel</dbms-column>
49 </object-link>
50
51 <object-link>
52 <bean-field>city</bean-field>
53 <dbni5-colunin>city</dtims-column>
Серверы приложений
633
54 </object-lin)c>
55
56 <object-link>
57 <bean-field>firstName</bean-field>
58 <dbms-colmnn>firstName</dbms-calmnn>
59 </ob3ect-link>
60
61 <object-link>
62 <bean-field>lastName</bean-field>
63 <dbms-eolumn>lastName</dbms-column>
64 </ob3ect-link>
65
66 <object-link>
67 <bean-field>phoneNumber</be an-field>
68 <dbms-column>phoneKumber</dbms-column>
69 </object-link>
70 </attribute-map>
71
72 <options>
73 <use-quoted-names>false</use-quoted-names>
74 </options>
75
7 6 </webloglc-rdbms-bean> <'-- конец дескриптора RDBMS Address -->
Рис. 13.6. Документ Weblogk-cmp-rdbms-jar-address.xml определяет свойства
контейнерного управления персистентносгью базы данных WebLogic для
EJB компонента Address
XML-дескриптор WebLogic weblogic-cmp-rdbms-jar-Customer.xml (рис. 13.7)
представляет собой дескриптор развертывания базы данных для EJВ-компонента
Customer. Дескрипторы развертывания по своей структуре почти идентичны. Для
каждого EJB-компонента необходимо предоставить карту соответствий полей, имя
таблицы базы данных и пользовательские (создаваемые разработчиком) запросы
для методов поиска.
1 <?xml version = "1.0" encoding « "UTF-8"?>
2
3 <!-- weblogic-cmp-rdbms-jar-Custoiuer. xml ejb - дескриптор для —>
4 <!— EJB-компонента CustomerEJB, определяющий свойства rdbms для
WebLogic -->
5
6 <!DOCTYPE weblogic-rdbms-bean PUBLIC
7 '-//ВЕЛ Systems, Inc.//DTD WebLogic 5.1.0 EJB RDBMS Persistence//EN'
8 'http;//www.bea.com/servers/wls510/dtd/
weblogic-rdbms-persistence.dtd'>
Э
10 <?— элемент, содержащий свойства rdbms для EJB-компонента
Customer —>
11 <weblogie-rdbms-bean>
12
13 <!-- назначение этого компонента пулу с именем BookatorePool -->
14 <pool-name>BooJcstorePool</pool-name>
15
16 <!-- назначение этого компонента таблице с именем CDSTOMER —>
17 <table-name>Customer</table-iiame>
18
19 <!-- элемент, содержащий карту соответствия полей -->
20 <attribute-map>
21
22 <!-- соответствие полей для customerlD -->
23 <object-linlt>
24 <bean-field>customerID</bean-field>
25 <dbms-coliHon>customerID</dDbras-colmnn>
26 </object-link>
27
28 <!-- соответствие полей для creditCardExpirationDate -->
29 <object-link>
30 <bean-field>creditCardExpirationDate</bean-field>
31 <dbms-colwm>creditCardExpirationDate</dbms-column>
32 </object-link>
33
34 <!-- соответствие полей для shippingAddressID —>
35 <objeet-link>
36 <bean-field>shippingAddressID</bean-field>
37 <dbms-colmnn>shippingAddressID</dbnia-column>
38 </object-link>
39
40 <\— соответствие полей для billingAddressID —>
41 <object-linJc>
42 <bean-field>billingAddressID</bean-field>
43 <dbms-colujnn>billingAddxessID</dbms-column>
44 </ob;ject-link>
45
4 6 <!-- соответствие полей для passwordHint -->
47 <object-link>
48 <bean-field>paaswordHint</bean-field>
49 <dbms-colujnn>passwordHint</dbma-coluiiiii>
50 </object-link>
51
52 <?-- соответствие полей для creditCardName -->
53 <object-linJc>
54 <bean-field>creditCardName</bean-field>
55 <dbms-column>creditCardHame</dbma-col'UJiin>
56 </object-link>
57
58 <\— соответствие полей для firstName —>
59 <0b3ecfc-li.nk>
60 <bean-field>firstName</bean-field>
61 <dbiBS-column>firstName</dbms-column>
62 </object-link>
63
64 <\— соответствие полей для password -->
65 <object-linlc>
66 <bean-field>password</bean-field>
67 <obms-column>password</dbms-col'umn>
68 </ob3ect-link>
69
70 <!-- соответствие полей для lastName —>
71 <object-link>
72 <bean-field>lastName</bean-£ield>
73 <dbma-column>lastNanie</dbms-columii>
74 </object-link>
75
76 <•-- соответствие полей для userlD —>
Серверы приложений
635
77 <object-linfc>
78 <bean-field>userID</bean-field>
79 <dfoms-column>userID</dbms-column>
80 </ob3ect-link>
81
82 <!-- соответствие полей для creditCardNumber -->
83 <object-link>
84 <bean-field>creditCardNumber</bean-field>
в 5 <dbms-column>credi tCardN«inber</dbms-column>
86 </object-linfc>
87
В 8 </attribute-map>
89
90 <!-- список нестандартных методов поиска -->
91 <fiiwter-list>
92
93 <!--метод поиска для findByUserlD —>
94 <finder>
95 <method-name>f indByUserID</metliocl-name>
96 <method-params>
97 <method-param>Java.lang.String</method-param>
98 </method-params>
99
100 <!-- получение полей, для которых значение userlD
совпадает со строкой параметра -->
101 <finder-query>
102 <![CDATA[< lifce userlD $0 )]]>
103 </finder-query>
104 </finder>
105
106 <!--метод поиска для findByLogin —>
107 <finder>
108 <method-name>find8yLogin</method-name>
109 <method-params>
110 <njethod-param>;java. lang. String</method-param>
111 <nethod-paran>j ava.lang,Str±ng</methQd-param>
112 </method-params>
113
114 <<— поля, для которых значения userlD и password
совпадают со значениями параметров -->
115 <finder-query>
116 <![CDATA[(S ( like userlD $0 )( like password $1 ))]]>
117 </finder-query>
118 </finder>
119
120 </finder-list>
121
122 <!— дополнительные опции -->
123 <options>
124 <use-quoted-names>false</use-quoted-names>
125 </options>
126
127 </weblogic-rdbms-bean>
128 <!— конец дескриптора rdbms для CustomerEJB —>
Рис. 13.7. Документ WebLogic-cmp-rdbmHar~Cust°'r|er.xml определяет свойства
контейнерного управления персистентностью базы данных WebLogic для EJ В-компонента
CustomerEJB
636 Глава 13
Элемент finder-list (строки 91-120) содержит методы поиска для
нестандартного EJB-компонента. WebLogic требует предоставлять элемент finder для каждого
нестандартного метода поиска я собственном интерфейсе. Вы должны определить
имя метода, параметры и запрос* который будет использоваться. Значением
элемента method-value (строка 95) является строка, которая соответствует методу
поиска, определенному в собственном интерфейсе. Элемент method-params (строки
96-98) содержит элемент method-param (строка 97), который определяет полный
тип всех передаваемых параметров (например, Java, la rig. String). Finder-query
представляет собой запрос, записанный на языке Web Logic Query Language
(WQL). Выражения WQL следует помещать в разделы CD ATA, чтобы не
пользоваться специальными символами,
В таблице на рис. 13.8 представлены операторы и примеры синтаксиса WQL.
Операндами выражений могут быть параметры, литералы, поля bean-fields в
составе этого дескриптора или другие выражения. Синтаксис $п означает, что
оператором является параметр, а п соответствует порядковому номеру передаваемого
параметра, начиная с 0. Литералы должны всегда заключаться в одинарные
кавычки. В строках 107-118 определен метод FindByLogin, который принимает два
параметра и возвращает только те поля, которые отвечают этим двум параметрам.
Оператор
=
<
>
<=
>=
i
&
1
like
isNull
isNotNull
orderBy
Функция J Пример синтаксиса
равно
меньше
больше
меньше или равно
больше или равно
не (not)
и (and)
или (ог)
равенство строке, % соответствует
групповому символу замещения
проверка на равенство null
проверка на не равенство null
упорядочение по именам столбцов,
debc указывает на упорядочение по
убыванию
( = ID $0»
{ < price $0)
( > quantity $0)
( <= operandi operandi)
( >= operandi operandi)
( t (=quantity '0'))
(6 (<price $01 txjuantity $Ш
(1 (>quantity '0') (>onOrder $0)
( like ,%Java%')
(ieNuLL bookID)
(isNotNull bookID)
(like '%Java%■ orderBy 'Price')
Рис. 13.8. Некоторые операторы языка WebLogic Query Language и их гримеры
На рис. с 13.9 по 13.12 представлены дескрипторы контейнерного управления
персистейтностью (СМР), которые следуют одной и той же базовой структуре.
1 <?xml version = "1.0й encoding = "OTF-8"?>
2
3<i-- weblogic-cmp-rdbms-jar-order.xml ejb - дескриптор для —>
t <!— ЕЛЗ-компо&енФа OrderEJB, определяющий свойства rdbms для
WebLogic —>
5
6 <!DOCTYPE weblogic-rdbms-bean PUBLIC
7 '-//BEA Systems, Inc.//DTD WebLogic 5.1.0 EJB RDBMS Persistence//EH'
Серверы приложений
637
8 'http://www.bea.com/servers/wls510/dtd/
weblogic-rdbms-persistence.dtd'>
9
10
11<!-- элемент, содержащий свойства rdbms для OrderEJB -->
12 <weblogic-rdbras-bean>
13
14 <!— назначение этого компонента пулу с именем BookstorePool —>
15 <pool-name>BookstorePooi</pool-name>
16
17
18 <!-- назначение этого компонента таблице с именем CUSTCHERORDEKS —>
19 <table-name>CustomerOrders</table-name>
20
21 <!-- элемент, содержащий карту соответствий полей —>
22 <attribute-map> ^
23
24
25 <?-- соответствие полей для orderDate —>
26 <object-link>
27 <bean-field>orderDate</bean-field>
28 <dbras-column>orderDate</dbms-column>
29 </object-link>
30
31
32 <!-- соответствие полей для shipped —>
33 <object-link>
34 <bean-field>shipped</bean-field>
35 <dbms-column>ehipped</dbms-column>
36 </object-link>
37
38
39 <!— соответствие полей для customerID —>
40 <object-link>
41 <bean-field>customerID</bean-field>
42 <dbms-column>customerID</dbnis-column>
43 </ob3ect-link>
44
45 <!— соответствие полей для orderlD -->
4 6 <object-link>
47 <bean-field>orderID</bean-field>
48 <dbms-column>orderID</dbms-column>
49 </object-link>
50
51 </attribute-map>
52
53 <finder-list>
54
55 <!-- метод поиска для findByCustomerlD —>
56 <finder>
57 <jnethod-name>findByCustomerID</mathod-name>
58 <method-params>
59 <method-param>java.ling.Integer</method-param>
60 </method-params>
61
638
Глава 13
62 <<— возвращает перечисление, для которого
cuatomerlD = параметр -->
63 <finder-query>
64 <![СОАГА[( = customerlD $0 )]]>
65 </finder-guery>
66 </finder>
67
68 </finder-list>
69
70 <!— дополнительные опции -->
71 <options>
72 <use-<juoted-naJnes>false</use-quoted-names>
73 </options>
74
75 </weblogic-rdbms-bean>
76 <!-- конец дескриптора rdbms для OrderEJB —>
Рис, 13.9. Документ Weblogk-cmp-rdbms-jar-order.xml определяет свойства контейнерного
управления персистентностъю базы данных WebLogic для EJ В-компонента OrderEJB
1 <?xml version = "1.0" encoding = "UTF-8"?>
2
3 <P— weblogie-cn£>-rdbms-jar-orderProduct. xml ejb - дескриптор для —>
4<!-- EJB-хомпонента OrderProductEJB, определяющий свойства rdbms
для WebLogic -->
5
6 <!DOCTYPE weblogic-rdbms-bean PDBLIC
7 '-//BEA Systems, Inc.//DTD WebLogic 5.1.0 EJB BDBMS Persistence//EN'
8 'http://wwM.bea.com/servers/wls510/dtd/
weblogic-rdbms-persistence.dtd'>
9
10 <!— элемент, содержаний свойства rdbms для OrderProductsEJB —>
11 <weblogic-rdbms-bean>
12
13 <!— назначение этого компонента пулу с именем BookstoreFool —>
14 <pool-name>BooIcstorePool</pool-name>
15
16 <!— назначение этого компонента таблице с именем QRDERPRODUCT —>
17 <table-name>OrderProduct</table-nairie>
18
19 <!-- элемент, содержаний карту соответствий полей -->
20 <attribute-map>
21
22 <!-- соответствие полей для quantity -->
23 <object-link>
24 <bean-field>quantity</bean-field>
25 <dbms-column>quantity</dbms-coliimn>
26 </object-link>
27
28 <!-- соответствие полей для ISBN -->
29 <object-linlc>
30 <bean-field>ISBN</bean-field>
31 <dbms-coltimn>ISBN</dbms-coluran>
32 </object-link>
33
Серверы приложений
639
34 <!— соответствие полей для orderlD —>
35 <object-link>
36 <bean-field>order!D</bean-field>
37 <dbms-column>orderIIX/dbms-column>
38 </obiect-link>
39
40 </attribute-map>
41
42
43 <f±nder-list>
44
45 <!-- метод поиска для findByOrderlD —>
46 <finder>
47 <method-name>findByOrderID</method-name>
48 <method-params>
49 <method-param>java.lang.lnteger</method-param>
50 </method-params>
51
52 <!— выборка полей, для которых значение orderlD совпадает
с параметром -->
53 <finder-query>
54 <![CDATA[( like orderlD $0 )]]>
55 </£inder-query>
56 </finder>
57
58 </finder-list>
59
60 <!— дополнительные опции -->
61 <options>
62 <use-quoted-names>false</uae-quoted-names>
63 </options>
64
65 </weblogic-rdbms-bean> <j— конец дескриптора OrderProduct -->
Рис. 13.10. Документ Weblogk-cmp-rdbms-jar-orderProduct.xml определяет свойства
контейнерного управления перс и стентн остью базы данных WebLogic для
EJB-компонента OrderProduct
1 <?xml version = Э1.0" encoding = ЭГЛТ-8"?>
2
3 <!— weblogic-cmp-rdbms-jar-product.xml - дескриптор для -->
4 <!-- EJB-компонента ProductsJB, определяющий свойства rdbms для
WebLogic —>
5
6 <!DOCTYPE weblogic-rdbms-bean PUBLIC
7 '-//BEA Systems, Inc.//DTD WebLogic 5.1.0 EJB RDBMS Persistence/ZEN1
8 'http://www.bea.com/servers/wls510/dtd/
weblogic-rdbms-persistence.dtd' >
9
10 <!— элемент, содержаний свойства rdbms для ProductEJB -->
11 <weblogic-rdbms-bean>
12
13 <!-- назначение этого компонента пулу с именем BooltstorePool -->
14 <pool-name>Books torePooK/pool-name>
15
642
Глава 13
20 <attribute-map>
21
22 <!— соответствия полей для addressID —>
23 <object-link>
24 <bean-field>tableMame</bean-field>
25 <dbms-column>tableSanie</dbma-column>
26 </o.toject-liiik>
27
28 <!-- соответствия попей Для primaryKey -->
2 9 <obj ect-1ink>
30 <beAn-field>nextlD</bean-field>
31 ^dbms-coluiMi>nextID</dbms-column?-
32 </object-link>
33
34 </attribute-map>
35
36 <options>
37 <use-quoted-namfis>false</use-quoted~names>
38 </options>
39
40 </wefalogic-rdbros-bean>
Рис. 13.12. Документ Weblogic-cmp-rdbms-jar-sequence,xml определяет свойства
контейнерного управления персистентностью бааы данных WebLogic для
EJB-компонента SequenceFactory ,
Наконец, для ссылок, на EJB-xomitohbhtki в сервлетах должны быть
установлены соответствия с именами JNDI для каждого EJB-компонента. Эти соответствия
определены в дескрипторе развертывания weblogie.xml в каталоге WEB-INF
Web-приложения. На рис. 13.13 представлена карта соответствий для сервлетов
приложения Bookstore. Необязательный элемент description содержит краткое
описание Web-яриложения. Элемент reference-descriptor является единственным
обязательным элементом для нашего приложения. Этот элемент устанавливает
соответствия между ссылками, определенными в документе web.xml, и именами
JNDI EJB-компонентов, на которые указывают ссылки. Мы присвоили ссылкам
имена, совпадающие с именами JNDI, но это не является обязательным. Каждый
дескриптор ссылки помещен внутрь элемента ejb-reference-description. При
установке соответствий требуется, чтобы элементы ejb-ref-name и jndi-name
содержались внутри элемента ejb-reference-description. E строках 22-29 устанавливается
соответствие между ссылкой на EJB-компонент ShoppingCart и его именем JNDI.
1 <?xml version = "1.0" encoding = "UTF-8"?>
2
3 <!— Дескриптор развертывания weblogie.xml для сервлетов, —>
4 <!-- устанавливающий соответствия между ссылками на еjb-хомпонеяты
и именами JNDI —>
5
6 <!DOCTYPE weblogic-web-app PUBLIC
7 "-//BEA Systems, Inc.//DTD Web Application 6.0//EM"
8 "http://www.bea.com/servers/wlsfiOO/dtd/weblogic-web-jar.dtd">
9
10<!-- основной блох дескриптора -->
11 <weblogic-web-app>
12
13 <!-- Необязательный блок, содержащий описание war-файла. -->
14 <description>
Серверы приложений 643
15 Bookstore servlets
16 </description>
17
18 <!-- блок, содержащий карту соответствий для ссыпок
на ejb-компоненты —>
19 <reference-descriptor>
20
21 <!-' индивидуальная карта соответствий -->
22 <ejb-reference-description>
23
24 <!--имя ссылки, определенное в web.xml —>
25 <ejb-ref-name>ejb/SlioppingCart</ejb-ref-name>
26
27 <!— имя JNDI, заданное в weblogie-ejb-jar.xml -->
28 <3ndi-name>ejb/ShoppingCart</jndi-name>
29 </ejb-refer/ence-description>
30
31 <!-- индивидуальная карта соответствия —>
32 <ejb-refereace-deacription>
33 <e j b-re £-name>ejb/Product</e j b-ref-name>
34 <jndi-name>ejb/Product</jndi-najBe>
35 </ejb-reference-description>
36
37 <!— индивидуальная карта соответствия -->
38 <ejb-reference-description>
39 <ejb-re£-nanie>ejb/Customer</ejb-ref-name>
40 <jndi-narae>ejb/Customer</jndi-name>
41 </ejb-reference-description>
42
43 <!"" индивидуальная карта соответствия -->
44 <ejb-reference-description>
45 <ejb-re£-name>ejb/Order</ejb-ref-name>
46 <jndi-name>ejb/Order</jndi-name>
47 </ejb-reference-description>
46
49 </reference-descriptor>
50
51 </weblogic-web-app>
52 <!-- конец дескриптора сервлета —>
Рис. 13.13. Дескриптор развертывания Weblogic.xmt Web-приложения
После того как все дескрипторы развертывания для нашего примера
рассмотрены, можно приступать к процессу развертывания. В нашем приложении
используется синтаксический анализатор Xalan версии 2.1, поэтому укажите путь к
файлам xalan.jar и xerces.jar при установке переменной окружения CLASSPATH
в командном файле startWeblogic.cmd. Ваша запись для установки переменной
окружения classpath должна выглядеть следующим образом:
set CIASSPATH=.; c:\xalan-j_2_l_0\xerces,jar; c:\xalan-
j_2_l_0\xalan.jar; ,\lib\weblogXc_sp.jar; .\ejb\weblogic.jar;
с:\cioudscape_3.6\ lib\cloudscape.jar
Следует заменить c:\xalan-j_2_l_0 на фактический путь к JAH-файлам Xalan.
Выполните командный файл startWeblogic.cmd и укажите в адресной строке
вашего Web-браузера URL localhost:7001/bookstore, чтобы получить доступ в
книжный Internet-магазин.
644
Глава 13
13.5. Развертывание приложения Deitel Bookstore
на сервере IBM WebSphere
В этом разделе будет рассмотрен процесс настройки и развертывание
приложения Deitel Bookstore на сервере IBM WebSphere 4.0. Конкретную информацию по
установке сервера приложений в вашей системе можно найти в документации на
сервер WebSphere. Предполагается, что Cloudscape установлен в каталоге
C:\cloudscape_3.6, сервер приложений WebSphere установлен в каталоге c:\Web-
Sphere\AppServer, а сервер IBM HTTP Server (входящий в пакет установки)
установлен в каталоге c:\IBM HTTP Server. Также необходимо скопировать файлы
xalan.jar и xerces.jar в каталог c:\WcbSphere\AppServer\lib\ejit, чтобы ваше
приложение имело доступ к синтаксическому анализатору XML и XSLT-преобра-
зователю.
Далее, выполните сценарий StartServerBasic, содержащийся в каталоге
WebSphere\AppServer\bin, чтобы запустить сервер приложений. По завершении
загрузки сервера укажите URL localhost:9090/admin в Web-браузере. Вы можете
указать имя пользователя на странице входа: это имя используется для
отслеживания изменений, а не для целей безопасности. Чтобы настроить драйвер JDBC
и источник данных, выберите Resources > JDBC Drivers, затем щелкните на
кнопке New в правой панели. Введите e:\eloudseape_3.6\lib\cloudscape.jar в качестве
пути Server Class Path, Bookstore в качестве имени Name и COM.cloudscape.co-
re.LocalConncctionPoolDatflSource в качестве имени класса реализации
Implementation Class Name. Введя эти значения, щелкните на ОК и сохраните
конфигурацию. Раскройте созданный перед этим драйвер (Bookstore) и выберите Data
Source. Щелкните на кнопке New и заполните поля следующим образом. В поле
Name введите Bookstore, в поле JNDI Name введите jdbc/Bookstore, а в поле
Database Name — полный путь к базе данных (например, c:\cloudscape_3.6\data-
bases\bookstore\). Щелкните на ОК и сохраните конфигурацию.
Далее, необходимо сформировать дескрипторы развертывания для сервера
WebSphere. В состав WebSphere входит инструментальное средство для создания
дескрипторов развертывания. Откройте средство Application Assembly Tool,
выполнив сценарий сборки, содержащийся в каталоге WebSphere/ AppServer/bin.
По завершении загрузки инструментального средства откройте вкладку Existing
в окне приветствия. Затем введите полный путь к архиву bookstore.ear, либо
выберите опцию просмотра, чтобы найти этот файл. Щелкните на Open, чтобы открыть
файл и начать настройку параметров развертывания.
В левой панели раскройте Bookstore > EJB modules > EJBs > Session Beans >
Shopping Cart. Выберите EJB-компонент ShoppingCart и откройте вкладку
Bindings в главной панели (в нижней части правой панели). Введите ShoppingCart
в качестве имени JNDI Name. Выберите EJB References из правой верхней
панели. Проверьте, что выбрано ejb/Order, и воспользуйтесь полем с раскрывающимся
списком Link, чтобы выбрать соответствующую ссылку. Вы также должны
открыть вкладку Bindings на главной панели и ввести Order в качестве имени JNDI
Name для ссылки. Выполните аналогичные действия, чтобы установить
соответствие между ссылкой ejb\Product и EJB-компонентом Product.
Выберите Bookstore > EJB modules > EJBs > Entity Beans > Address. Откройте
вкладку Bindings в главной панели, введите Address в качестве имени JNDI Name
и установите jdbc/Bookstore в качестве имени JNDI Name источника даппых.
Выберите EJB References в верхней панели и установите соответствие между ссылкой
ejb/SequenceFactory и компонентом SequenceFactory с именем JNDI Name
SequenceFactory. Настройте ссылки на EJB-компоненты для других EJB таким
же образом.
Серверы приложений
645
Определив имена JNDI и ссылки на EJВ-компоненты для EJB Customer,
выберите Method Extensions. Вы должны определить собственные запросы поиска
(finder) для методов findByUserlD и findByLogin. Чтобы определить собственный
запрос, выберите метод, установите флажок Finder Descriptor и задайте
соответствующее предложение WHERE (рис. 13.14). Предложение WHERE будет
автоматически помещено в операторьг SQL SELECT и FROM: вам нужно лишь определить
его. WebSphere определяет имя таблицы, исходя из имени EJB-компонента,
поэтому для Того, чтобы задать другую таблицу, необходимо переименовать EJB-компо-
нент. При развертывании приложения Deitel Bookstore не забудьте выбрать
EJB-компонент Order и изменить имя EJB-компонента на CustomerOrders,
которое представляет собой имя таблицы, хранящей информацию о заказе.
Компонент
Customer
Customer
Order
OrderPtoduct
Product
ProduCt
Метод
findByLogin
findByUserlD
findByCustomerlD
findByOrderlD
findByAllProductS
findByTitle
Предложение WHERE
userlD = ? AND password « ?
userlD = ?
customerID = ?
orderlD = ? j
1 *= 1
title like ?
Рис. 13.14. Предложения WHERE для методов поиска в приложении Deitel Bookstore
Чтобы настроить сервлеты в WebSphere, необходимо выбрать Bookstore > Web
Modules > Servlets > EJB references и установить соответствия для каждой из
ссылок, как это делалось для EJB-компонентов. Установив соответствия для
ссылок, можно приступить к формированию кода для развергывання. Выберите
Generate code for deployment из меню File. В текстовом поле Deployment module
location задайте путь для сохранения развертываемого EAR-архива. Введите путь
к файлам xalan.jar и xerces.jar в поле Dependent Classpath. В качестве типа базы
данных Database type введите Generic/SQL-92, в качестве имени базы данных
Database Name введите jdbc/Bookstore, а в качестве схемы Schema — АРР.
Заполнив эти поля, нажмите кнопку Generate Now.
Теперь можно выполнить развертывание приложения на Web-странице
инструментального средства администрирования (localhost:9090/admin). В левой панели
выберите Nodes > Computer Name > Enterprise Applications. Щелкните на
кнопке Install, затем воспользуйтесь кнопкой Browse для нахождения
развертываемого EAR-файла (например, DepJoyed_Bookstore.ear) и щелкните на Next, На
следующей странице представлены соответствия имен JNDI. Дважды проверьте
каждое из значений, прежде чем нажать Next. На следующей странице представлены
ссылки на EJB-компоненты. На следующей странице проверяются установленные
соответствия с базой данных. Каждое поле должно содержать значение
jdbc/Bookstore. В поле Database type должно содержаться Generic/SQL-92, a
в поле Schema — АРР. Следующая страница определяет соответствие между серв-
летом и хостом default_host. На следующей странице сбросьте флажок повторного
развертывания приложения. Щелкните на Next, подтвердите введенные Значения
и щелкните на Finish, чтобы осуществить развертывание. После развертывания
приложения щелкните на ссылке в верхней части страницы, чтобы регенерировать
конфигурацию подключаемых модулей Web-сервера. Сохраните конфигурацию
и перезапустите сервер, выполнив сценарий stopServer, а затем сценарий
startServerBasic. Наконец, введите URL localhost/bookstore, чтобы запустить
приложение Deitel Bookstore.
646
Глава 13
13.6. Ресурсы в Internet и во Всемирной паутине
sexverwatch.internet.com/appservers.html
Сайт новостей по серверам приложений, содержащей также обзоры и сравнительный
анализ популярных серверов приложений,
www.app£Qrver~zone,com
"Web-сайт Application Server Zone содержит технические статьи, сравнительный анализ
программных продуктов и другую информацию, относящуюся к серверам приложений.
Java.sun.com/j2ee
Сайт корпорации Sun, посвященный J2EE. Содержит описание спецификации J2EE,
новости, средства загрузки и поддержки SDK,
Резюме
• Java 2 Enterprise Edition — это спецификация для выполнения корпоративных
приложений. Хотя Sun предоставляет эталонную реалиаац»к> згой спецификации, реальные
системы должны использовать сервер приложений от коммерческого поставщика.
• Корпорация Sun Microsystems, совместно с большим сообществом поставщиков серверов
приложений, разработало спецификацию Java 2 Enterprise Edition. J2EE определяет
платформу сервера приложений и API поддержки для построения корпоративных
приложений, которые являются переносимыми между различными серверами приложений, и,
поскольку они используют Java, между платформами.
• В спецификации J2EE можно выделить несколько разделов, таких как поддержка API,
безопасность, управление транзакциями и развертывание. Поставщик сервера
приложений должен обеспечить поддержку выполнения API платформы J2EE,
• Чтобы быть сертифицированным как удовлетворяющий требованиям спецификации
J2EE, сервер приложений должен реализовьгвать минимум функциональных
возможностей, определенных В спецификаций J2EE. Поставщики серверов приложений также
могут предоставлять возможности, которые не входят в спецификацию J2EE, чтобы как-то
выделить свой продукт среди других.
• Компания ВЕА предоставляет сервер приложений общего назначения, который
обеспечивает «золотую середину» между скоростью и стабильностью, а также основательную
поддержку различных функциональных возможностей, не предусмотренных спецификацией
J2EE, таких как организация пулов данных, «горячее* развертывание, кластеризацию,
поддержку режима восстановления после сбоев для EJB-компонентов и
Web-компонентов, а также оптимальное распределение нагрузки.
• Для окружений с одним сервером WebLogic предоставляет множественные пулы-, сервис,
который осуществляет распределение транзакций между источниками данных. В то
время как пул соединений способен работать только с одним источником данных,
множественные пулы дают возможность приложению осуществлять доступ к нескольким пулам,
тем самым распределяя запросы между весколькими источниками данных.
• iPianet E-Coinnierce Solutions — это альянс между компаниями Netscape Communications
и Sun Microsystems. Основная цель iPianet — добиться высокой производительности,
стабильности и полной совместимости со спецификацией J2EE. iPianet предоставляет
поддержку режима восстановления после сбоев, пулы соединений и несколько уникальных
возможностей.
• Web-коннектор управляет распределением нагрузки б сервере приложений iPianet.
Web-коннектор управляет взаимодействием между сервером приложений и
Web-сервером. Web-коннектор распределяет запросы между экземплярами сераера в зависимости
от времени, затрачиваемого сервером ня выдачу ответа.
• В iPianet имеется поддержка «закрепленного? распределения нагрузки: если компонент
помечен как +закрепленный*, стандартный алгоритм распределения нагрузки не
используется, и компонент выполняется на «закрепленной» машине.
• iPianet использует протокол Lightweight Directory Access Protocol (LDAP) для
управления безопасностью. Пользователям могут назначаться групповые и индивидуальные
полномочия для доступа к составным частям приложения. Настройка полномочий LDAP
позволяет более гибко управлять полномочиями пользователей.
■ IBM WebSphere представляет собой популярный сервер приложений, который почти
столь же распространен на рынке, что и сервер ВЕА WebLogic. Версия 4.0 предоставляет
Серверы приложений
647
простой пользовательский интерфейс для администрирования и развертыьания, уделяя
главное внимание скорости и масштабируемости. В состав WebSphere входит версия
Web-сервера Apache от IBM, средства поддержки режима восстановления после сбоев,
средства организации пулов данных и средства управления безопасностью па уровне
пользователя.
Сервер JBoss в сочетании с контейнером сервлетов Tomcat от Apache Software Foundation,
который в настоящее время является совместимым только со спецификацией J2EE 1.2,
представляет собой сервер приложений с открыто доступным кодом. Предполагается, что
JBoss будет совместим с последующими спецификациями J2EE. В настоящее время
в. JB03S реализовано большинство функциональных возможностей коммерческих
серверов приложений.
Терминология
Access Control List (ACL) — список
управления доступом
application server —сервер нрилйженин
attribute-map, элемент
BEA WebLogic, сервер
bean-field, элемент
cache-strategy, элемент
data pool — пул данных
data source — источник данных
dbmj-column, элемент
deployment descriptor — дескриптор
развертывания
description, элемент
ejb-name, элемент
ejh-reference-description, элемент
ejb-ref-name, элемент
ejbStore, метод
failover — восстановление после сбоев
finder, элемент
finder-list, элемент
finder-query, элемент
hot deployment — «горячее» развертывание
ГВМ WebSphere, сервер
iPlanet Application Server, cepRPp
J2EE specification — спецификация J2EE
JBoss, сервер
jndi-name, элемент
life cycle — жизненный цикл
load balancing — балансирование
(распределение) нагрузки
max-beans-in-eache, элемент
method-name, элемент
method-param, элемент
method-params, элемент
multi pool — множественный пул
object-link, элемент
persistence-descriptor, элемент
persistence-type, элемент
persistence-use, элемент
pool-name, элемент
read-timeout-seconds, элемент
reference-descriptor, элемент
stateful-session-persistent-store-dir, элемент
Sticky load balancing1 — «закрепленное»
распределение нагрузка
table-name, элемент
three-tier — трехуровневое (приложение)
transaction management — управление
транзакциями
transaction-descriptor, элемент
trans-timeout-seconds, элемент
type-identifier, элемент
type-storage, элемент
type-version, элемент
Web connector — Weh-коннектор
WebLogic Query Language (WQL), язык
запросов
WebLogic_CMPJKDBMS, элемент
weblogic_ejb_jar, элемент
weblogic-enterprise_bean, элемент
weblogic-rdbms-bean, элемент
weblogic-web-app, элемент
Используемые источники
1. Shannon В, «Java™ 2 Platform Enterprise Edition Specification, vl.2>, 17 декабря
1999 г., <java.sun,com/j2ee/dowiiload.html>.
2. «BEA WebLogic Server® Datasheet», <www.bea.com/products/wcblogic,/serYer/data-
sheet/shtml>,
3. «Frequently Asked Questions», <www.jboss.org/faq.jsp>.
4. «Using WebLogic Server RDBMS Persistence», 2000, <wrww.weblogic.com/docs51/clasa-
docs/API_ejb/EJB_enviranment.html#1022233>.
5. «weblogic-cmp-rdbms-jar.xml Properties», 2000, <www.weMogie.eom/docs51/classdocs/
APl_eib/EJB_reference.html#1026608>,
f4
Введение в Web-сервисы
и SOAP
Цели
• Понять, что собой
представляет протокол Simple
Object Access Protocol (SOAP)
и каким образом он
использует XML.
• Уяснить структуру сообщений
SOAP.
• Научиться создавать
приложения Java, которые
отправляют и принимают
сообщения SOAP,
Ничего не происходит до тех
пор, пока что-нибудь не будет
продано.
Артур Г. Мот л и
Людям пора учиться управлять
в мире, состоящем по большей
части из множества вождей и
одного индейца. Индеец — это,
конечно, компьютер,
Томас А. Уислер
...чудеса всегда объясняются
просто.
Амелия Барр
Сходство есть повторение внешних сеойс
объектов...
Чинг Хао
...если вы понимаете, что вам хочет,
сообщить природа, радуйтесь, ибо душа
ваша жива...
Элеовора Дьюс
650
Глава 14
14.1. Введение
Функциональная совместимость, или беспрепятственное взаимодействие и
обмен информацией между различными программными системами, является
главной целью предприятий и организаций, которые активно используют в своей
деятельности компьютеры и компьютерные сети. Многие приложения используют
для передачи данных Internet. Некоторые из этих приложений выполняются на
клиентских системах, имеющих небольшую вычислительную мощность, поэтому
для обработки данных они обращаются к методам, размещенным на удаленных
машинах. Многие приложения используют собственные форматы данных, что
затрудняет, или даже делает невозможным взаимодействие с другими
приложениями. Большинство приложений также расположено за брандмауэрами (или
межсетевыми экранами): защитными барьерами, которые ограничивают
коммуникационный обмен между сетями. Протокол простого доступа к объектам Simple Object
Access Protocol (SOAP) решает эти проблемы. Объединяя сильные стороны,
присущие HTTP и XML, он обеспечивает полностью расширяемый режим
взаимодействия между программными системами.
Web-сервисы — новое слово в технологии распределенных систем.
Спецификация Open Net Environment (ONE) корпорации Sun Microsystems и инициатива
.NET корпорации Microsoft обеспечивают инфраструктуры для написания и
развертывания Web-сервисов. Имеется несколько определений Web-сервиса. Web-
сервисом может быть любое приложение, имеющее доступ к Web, например,
Web-страница с динамическим содержимым. В более узком смысле Web-сервис —
это приложение, которое предоставляет открытый интерфейс, пригодный для
использования другими приложениями в Web. Спецификация ONE Sun требует,
чтобы Web-сервисы были доступны через HTTP и другие Web-протоколы, чтобы дать
возможность обмениваться информацией посредством XML-сообщений и быть
найденными через сервисы поиска. SOAP предоставляет средства взаимодействия
на базе XML для многих Web-сервисов. Web-сервисы могут обеспечить высокую
степень совместимости между различными системами [1].
Гипотетический Web-сервис, разработанный в соответствии с архитектурой
ONE Sun, может принимать форму, в которой реестр сервисов публикует описание
Web-сервиса в виде документа Universal Description, Discovery and Integration
(UDDI). Клиент, например, Web браузер или клиент GUI Java, ищет службу
каталогов для требуемого Web-сервиса. Клиент использует информацию, полученную
от сервиса поиска, для отправки XML-сообщения через HTTP Web-серверу, на
котором размещен Web-сервис. Сервлет обрабатывает клиентский запрос. После
этого сервлет осуществляет доступ к серверу приложений, который предоставляет
средства Enterprise JavaBeans. Компоненты EJB, в свою очередь, обращаются к
базе данных, которая хранит информацию для Web-сервиса. После обращения к
базе данных EJB-компонент отправляет сервлету запрошенную информацию.
Введение в Web-сервисы и SOAP
651
Сервлет форматирует информацию для представления ее клиенту (например,
создает страницу JavaServer Page). Сервер HTTP отправляет ответ в виде XML
обратно клиенту. Клиент осуществляет синтаксический анализ ответа и отображает
информацию пользователю [1].
-Огромный потенциал Web-сервисов определяется отнюдь не технологией,
используемой для их создания. HTTP, XML и другие протоколы, используемые
Web-сервисами, не новы. Функциональная совместимость и масштабируемость
Web-сервисов подразумевает, что разработчики могут быстро создавать большие
приложения и более крупные Web-сервисы из меньших Web-сервисов.
Спецификация Sun Open Net Environment описывает архитектуру для создания
интеллектуальных Web-сервисов. Интеллектуальные Web-сервисы используют общее
операционное окружение. Совместно используя контекст, интеллектуальные Web-
сервисы могут выполнять стандартную аутентификацию для финансовых
транзакций, предоставлять рекомендации и указания в зависимости от географического
местоположения компаний, участвующих в электронном бизнесе. На момент
написания этой книги на пути разработки интеллектуальных Web-сервисов имелось
два основных препятствия. Во-первых, пока не существует общепринятых
стандартов для совместного использования контекста. Web-сервисами. Во-вторых, пока
не обеспечивается безопасность и конфиденциальность транзакций,
осуществляемых Web-сервисами.
14.2. Простой протокол доступа к объектам (SOAP)
Корпорации IBM, Lotus Development Corporation, Microsoft, Develop-Mentor и
Userland Software разработали протокол SOAP, который представляет собой
протокол, основанный на HTTP-XML. Он позволяет приложениям взаимодействовать
между собой через Internet, используя для этого ХМЬ-дшсументы, называемые
сообщениями SOAP. Протокол SOAP совместим с любой объектной моделью,
поскольку он включает только те функции и методы, которые абсолютно
необходимы для формирования коммуникационной инфраструктуры. Таким образом,
SOAP является независимым от платформы и конкретных приложений, а для его
реализации может быть использован любой язык программирования. SOAP
поддерживает практически любой транспортный протокол. Например, SOAP
привязан к протоколу HTTP и следует модели запрос-ответ HTTP. SOAP также
поддерживает любые методы кодирования данных, которые позволяют приложениям,
основанным на SOAP, посылать в сообщениях SOAP информацию практически
любого типа (например, изображения, объекты, документы и т.д.).
Сообщение SOAP содержит конверт, который описывает содержимое,
предполагаемого получателя сообщения и требования к обработке сообщения.
Необязательный элемент header (заголовок) сообщения SOAP содержит инструкции по
обработке для приложений, которые принимают сообщение. Например, для
реализаций, которые поддерживают транзакции, заголовок может задавать параметры
для данной транзакции. Заголовок также может содержать информацию о
маршрутизации. С помощью заголовка header поверх SOAP могут надстраиваться
более сложные протоколы. Записи в заголовке могут модульно расширять сообщение
для таких задач, как аутентификация, управление транзакциями и проведение
платежей. Тело SOAP-сообщения содержит специфичные для приложения
данные, предназначенные для предполагаемого получателя сообщения,
SOAP можно использовать для осуществления удаленного вызова процедур
Remote Procedure Call (RPC), который представляет собой запрос, посылаемый
другому компьютеру для выполнения определенной задачи. RPC использует словарь
XML для задания вызываемого метода, передаваемых ему параметров и универсаль-
652
Глава 14
ного идентификатора ресурса (URI) целевого объекта. Вызов RPC привязывается к
HTTP-запросу, так что сообщение отправляется через HTTP-запрос post.
Сообщение-ответ SOAP представляет собой документ HTTP-ответа, который содержит
результаты вызова метода (например, возвращаемые значения, сообщения об
ошибках и т.д.)- SOAP также поддерживает асинхронные RPC-вызовы, при которых
программа, инициировавшая RPC-вызов, не ждет ответа от удаленной процедуры.
На момент написания этой книги SOAP еще находится на стадии развития, и
разработка многих технологий, основанных на SOAP, только начинается. Чтобы
реализовать преимущества, предоставляемые SOAP, необходимо установить
высокоуровневые спецификации и стандарты, которые используют эту технологию.
Несмотря на это, SOAP является перспективным стандартом для
XML-распределенных вычислений, предоставляя невиданный ранее уровень расширяемости и
функциональной совместимости.
На рис. 14.1-14.4 представлен пример сервиса SOAP, использующий
реализацию API SOAP Apache, версия 2.2 (доступна по адресу xml.apache.org/soap)1. Для
RPC SOAP требуется средство работы с сервлетами, например, Tomcat (ja-
karta.apache.org) и синтаксический анализатор Apache Xerces для Java (доступен
по адресу xnil.apachc.org/xerces-j/ind.cx.litnil). В документации SOAP (docs/ins-
tall/index.html) содержатся инструкции по установке как для сервера, так и для
клиента.
На рис 14,1 представлен класс SimpleService, который размещается на Сервере
и содержит метод getWelcome. Приложение Java, представленное на рис. 14.4,
вызывает этот метод с использованием RPC.
1 // Рис. 14.1. SimpleService.java
2 // Реализация запрашиваемого метода на сервере
3
4 public class SimpleService {
5
6 public String getWelcome( String message > throws Exception
7 {
6 String text =
9 "Welcome to EOAP!\nHere is your message: " + message;
10
11 return text; // ответ
12 )
13)
Рис. 14.1. Класс SimpleService
Метод getWelcome (строки 6-12) возвращает строковые данные. Чтобы сделать
этот метод доступным для клиентов (т.е. сделать возможным работу с ним через
RPC), необходимо предоставить серверу имя метода, который обрабатывает
запрос, т.е. необходимо осуществить разеертывание сервиса.
Чтобы выполнить развертывание сервиса, сначала скопируйте файл
SimpleService.class в каталог jakarta-tomcat/classes. Если вы создали файл
архива Java (JAR), скопируйте JAR-файл в каталог jakarte-tomcat/lib. Создайте ката
лог classes или lib, если они не существуют, В Jakarta-Tomcat указание на файлы в
этих каталогах включается в описание переменной окружения CLASSPATH.
Выполните развертывание сервиса с помощью инструментального средства
развертывания XML-SOAP, входящего в состав пакета SOAP (в каталоге webapps/
soap). Чтобы выполнить приложение, введите URL Iocalhost:8080/soap/admin
1 На момент подготовки к изданию перевода книги актуальной была версия 2.3.1 API
Apache SOAP. — Прим, ред.
Введение в Web-сервисы и SOAP
653
в адресной строке Web-браузере. На рис. 14.2 и 14.3 представлено средство
администрирования, которое позволяет развертывать, удалять и получать список
сервисов. Поле ID на рис. 14,2 содержит URI (urn:xml-simple-message), который
идентифицирует сервис клиенту. Этот URI определяется программистом. Если
один сервис имеет такой же URI, что и другой сервис, клиент не сможет их
различить; как следствие, возможны ошибки. Поле Scope задает границы
существования объекта, создаваемого (на сервере) для обработки запроса SOAP. Объект может
существовать в пределах запроса (Request), сеанса (Session) или приложения
(Application). Значение Request подразумевает, что сервер удаляет объект после
отправки ответа. Значение Session подразумевает, что объект существует в
течение всего сеанса взаимодействия клиента с сервером. Значение Application
подразумевает, что объект доступен для всех запросов в течение времени жизни
приложения. В поле Method (рис. 14,2) задаются методы, доступные для запроса SOAP,
в данном случае, метод getWelcome. В поле Provider Туре задается язык
реализации сервиса. Поддерживаются языки Java, JavaScript, Perl и Bean Markup
Language (BML). Для примеров, рассматриваемых в этой главе, используется Java.
В поле Provider Class указывается класс, который реализует сервис:
Simple Service, Поля Script Language, Script File и Script используются только
'. Нг Ш Ив* FimifcM 'Т9?&*'"нф
tap: /to«lhMt: SOaO/*™^dmin/triiJe *■ 1"и=гт1
Л - Miceosttft interne* Еюйогег
>ЖГ±
kite'
Ш:
Deploy
'"Ш.
Un-deploy
W ...
Apache SOAP Atlmin ^~
Service Deployment Descriptor Templar
'Л jPropeity |
ilD '[ui
DetaiU
rn ■/m'i-v^efl'^er-seivice
;Scopc ;l deques'. "j
[Methods ,
l[ge Weeth e rid от rti eii a n
iCWhitcspsce ssparat^insi ofrcethod aarne?)
iProvider
iTypt
3
I F&r IIser-Defoed Pre v-.der Type, Enter FULL Class Name:
■N'-niber of Opnons: \
Key
Value
T
:M
I
Java I Provicsr Class prtEl.advjJ'.tpl.soap.weatfier.WealheTSeivice
Providtr :S»abt? I Mo r\ . Ji
ii£ j
glwalWilindt ■ jri"; ^
Рис. 14.2. Инструментальное средстве- администрирования из пакета SOAP
654
Глава 14
для сервисов, реализованных на поддерживаемых языках написания сценариев.
Поле Type Mapping позволяет вручную устанавливать карту соответствий между
типами Java и XML. Реализация Apache SOAP предоставляет отображения по
умолчанию для большинства типов Java и для классов Java, которые следуют
паттернам проектирования JavaBeans. Заполнив форму, щелкните на кнопке Deploy в
нижней части формы, чтобы осуществить развертывание сервиса. Щелкните на
кнопке List, чтобы получить список сервисов, и убедитесь, что развертывание
сервиса завершилось успешно (рис. 14.S). Инструкции относительно других способов
развертывания (например, через командную строку) можно найти по адресу docs\
guide\index.html.
.jjflUJE hSWfaa»m*fln8q(^lai*i»Tpndei: bbnl ~ Z_H z|i<^° jif**1"!
suAPAd ruol Murcisofi; Jrtterotrt twrianaf
ШШШшЗшБт
Apache SOAP Admin
Deployed Service Information
rurit:xnil-$imine-mes&age' Service
Deployment Descriptor
£' I..
Tlxiptity
Petails
Un-dcplOjf
Scope
|uni:xrn3-simp]e-iijcs sage
.-•«i -• =b;
Prouder type
Trovid'r Class
|TJse Staac Class
^Methods
[Typt Mapping?
Java
jSimp3eStrviCff
!£alsc
!gct Welcome
i *
Mapping Registry ClIss
!©**•
~^Т~7:"ШГ^Ш^
ШПШ!ЩШ-<1&«вЩГ
Рис. 14.3. Огисание развернутого сервиса
На рис. 14.4 представлен клиентский код для RPC-вызова, При выполнении
программа посылает запрос SOAP серверу, которым в данном случае является этот
же локальный компьютер. Клиент отправляет сообщение как параметр для
удаленного метода. (Это сообщение может быть задано в командной строке; по
умолчанию приложение использует сообщение Thanhs!) Когда сервер вызывает метод,
тот возвращает клиенту сообщение
Welcome to SOAP!
Неге is your message: Thanks •
// Рис. Id.4 . GetMessage.Java
// Программа, которая создает RPC-выэов SOAP
// икпоря пакетов Java
import java.io.*;
import java.net.*;
import j ava,util.*;
Введение в Web-сервисы и SOAP
8
9 // импорт пакетов сторонних поставщиков
10 import org.apache.soap,*;
11 import org.apache.soap.rpc.*;
12
13 public class GetHessage {
14
15 // метод main
16 public static void main( String args[] ) {
17 String encodingStyleURI = Constants.NS_URX_SGAP_ENC;
18 String message;
19
20 if ( args.length != 0 )
21 message = args[ 0 ];
22 else
23 message = "Thanks'";
24
25 // попытка удаленного вызова процедуры SOAP
26 try {
27 URL url = new URL(
28 "http://localhost:8080/soap/servlet/rpcrouter" );
29
30 // формирование вызова
31 Call reinoteMethod = new Call О ;
32 remoteMetliod. setTargetObjectURI (
33 "urn:xml-simple-message" );
34
35 // задание имени вызываемого удаленного метода
36 remoteMethod.setMethodName( "getWelcome" );
37 remoteMethod.setEneodingStyleURI( encodingStyleURI );
38
39 // задание параметров для удаленного иетода
40 Vector parameters = new Vector ();
41
42 parameters.add£lement( new Parameter{ "message",
43 String.class, message, null ) );
44 remoteMethod.setParams( parameters );
45 Response response;
46
47 // busob удаленного метода
48 response = remoteMethod. invoice ( url, "" );
49
50 // получение ответа
51 if < response.generatedFault() ) {
52 Fault fault = response.gefcFaulfc();
53
54 System.err.println( "CALL FAILED:\nFault Code = "
55 + fault.getFaultCode()+ "\nFault String = "
56 + fault. getFaultStringO )»'
57 }
58
59 else {
60 Parameter result = response.getReturnValue();
61
62 // отображение результатов вызова
63 System.out.println( result,getValue() );
64 }
656
Глава 14
65 )
66
67 // сереасаат исключения при указании неверного URL
€8 catch ( MalformedURLException malformedUKLException ) {
69 malfonriedURLExceptior. .printStackTrace () ;
70 System.exit( 1 );
71 }
72
73 // перехват исключения SOAPException
74 catch ( SOAPException soapException ) {
75 System.err.println( "Error message: " +
76 soapException.getMessage[) );
77 System.exit( 1 };
Рис. 14.4. Клиент, инициирующий запрос SOAP
В строке 10 импортируется пакет SOAP, который обеспечивает API для
реализации SOAP. Пакет org.apache.aoap.rpc в строке 11 предоставляет реализацию
RPC, использующую SOAP. В строке 17 задается стиль кодирования,
используемый для сообщения. Протокол SOAP, поддерживает множество стилей
кодирования, но не имеет стиля кодирования по умолчанию. В данном случае используется
стандартная кодировка ЯРС (WS_UR\_SOAP_ENC). R строках 27-28 яядается
URL сервера, которому клиент отправляет содержимое rpcrouter сообщения
message. Этот документ, который представляет собой сервлет Java, получает конверт
SOAP с помощью HTTP-метода post. Используя URI, заданный в сообщении
SOAP, он ищет сервисы, развернутые на сервере, чтобы реализовать экземпляр
соответствующего объекта, в данном случае, объекта SimpleService.
Объекты класса Call осуществляют вызовы удаленных методов. В строке 31
реализуется экземпляр объекта Call, и ему назначается ссылка remoteMethod. В
строках 32-33 устанавливается URI удаленного метода. В строке 36 задается имя
вызываемого метода, getWelcnm«. Затем в строке 37 задается стиль кодирования для
сообщения. В строках 40-44 формируются параметры, передаваемые удаленному
методу для обработки. Каждый параметр должен содержаться в своем собственном
объекте, а параметрические объекты должны быть помещены в контейнер Veclor.
В строках 42-43 формируется новый параметр для метода путем построения
объекта Parameter. Первым параметром, передаваемым конструктору, является имя
переменной или ссылки (message), вторым параметром является класс, которому
принадлежит объект Parameter (String), третьим параметром является значение
параметра (объект message), а четвертый параметр задает тип кодировки параметра
(null указывает на использование кодировки, принятой по умолчанию). Метод
setParams в строке 44 устанавливает параметры для объекта remoteMethod.
Введение в Web-сервисы и SOAP
657
Удаленный метод активизируется в строке 48 вызовом метода invoke. Метод
принимает два параметра: URL сервера, которому будет посылаться сообщение
SOAP, и значение заголовка SOAPAction, который задает цель запроса. В
качестве второго параметра может быть задано null, если заголовок SOAPAction
использоваться не будет. Метод invoke возбуждает исключение SOAPException (строки
74-78), если при отправке запроса SOAP возникает ошибка. После активизации
метода на сервере, результат возвращается клиенту и сохраняется в объекте,
идентифицируемом ссылкой response (строка 48). Этот объект принимает сообщение об
ошибке, если на сервере возникает проблема, например, не удается обнаружить
соответствующие сервисы. В строках 51-57 определяется, является ли полученное
сообщение сообщением об ошибке. Если ошибок не было, в строках 59-64
результат выводится на экран.
14.3. Служба погоды, реализованная посредством SOAP
В этом разделе описан простой Web-сервис, реализованный с помощью Java и
SOAP, в котором для отправки информации от сервера к клиенту используется
RPC SOAP1. Обязательные программные компоненты такие же, что и в примере из
предыдущего раздела.
Класс WeatherService (рис. 14.5) предоставляет метод getWeatherlnformation,
который класс WeatherServiceCHent вызывает через RPC SOAP. Чтобы вызов был
успешным, класс WeatherService должен присутствовать в каталоге classes
машины сервлетов Tomcat. RMI-версия метода gfetWeatherfnforniation возвращает
список (List) объектов We a the r Bean. SOAP не поддерживает прямую передачу
этих объектов Java, поэтому версия RPC SOAP метода getWeatherlnformation
возвращает контейнер (объект Vector) строк (String).
Метод updateWeatherConditions (строки 16-79) прочитывает информацию о
погоде с Web-страницы прогноза погоды и сохраняет ее в списке List объектов
WeatherBean. В строках 22—23 создается объект URL для Web-страницы прогноза
погоды. В строках 26-27 вызывается метод opcnStream класса URL для открытия
соединения с ресурсом с заданным UEL. Объект этого соединения помещается в
объект-обертку BnfferedRead.
В строках 30-76 выполняется скрэпинг HTML (т.е. извлечение данных с Web-
страницы) для получения информации о прогнозе погоды. В строке 30
определяется строковый разделитель — "TAV12" — который задает начальную точку на
Web-странице, с которой начинается поиск информации о погоде. В строках 33-34
осуществляют данных чтение с Web-сграннцы прогноза погоды, пока не будет
достигнута сигнальная метка. Это позволяет пропускать информацию, не нужную
для этого приложения.
В строках 38-41 определены две строки, которые представляют заголовки
столбцов для информации о погоде. В зависимости от времени дня, заголовки
столбцов будут иметь вид
"CITY WEA HI/LO WEA HI/LO"
после утреннего обновления (обычво около 10:30 по стандартному восточному
времени) или
"CITY WEA LO/HI WEA LO/EI"
после вечернего обновления (обычно около 22:30 по стандартному восточному
времени).
1 В главе 2 (в оригинале это глава 13) книги «Технологии программирования на Java 2.
Книга 2d это приложение реализовано с помощью технологии RMI. — Прим.. ред.
658
Глава 14
В строках 55-74 осуществляется чтение информацию о погоде в каждом городе
и запись этой информации в объекты WeatherBean. Каждый объект WeatherBean
содержит название города, температуру воздуха и описание погоды. В строке 51
создается контейнер Vector для хранения объектов WeatherBean. В строках
66-71 листинга строки, полученные с Web-страницы прогноза погоды,
добавляются в контейнер weatherbiformatura (строка IS). Первые 16 символов в строке
inputLine относятся к названию города, следующие 6 символов описывают погоду
(т.е. содержат прогноз), а следующие 6 символов представляют верхний и нижний
предел температуры. Последние два столбца данных относятся к прогнозу погоды
на следующий день, и в этом примере игнорируются. В строке 76 закрывается
объект BufferedRead и связанный с ним поток ввода lnpntStream.
В строке 99 возвращается контейнер weatherlnformation.'
1 // WeatherService.java
2 // WeatherService предоставляет метод для извлечения
3 // информации о погоде с сайга National Heather Service.
4 package com.deitel.advjhtpl.soap.weather;
5
6 // Набор базовых пакетов Java
7 import Java. io. * ;
8 import j ava.net.URL;
9 import java.util.*;
10
11 public class WeatherService {
12
13 private Vector weatherlnformation; // объекты WeatherBean
14
15 // получение информации о породе с сайта NWS
16 private void updateWeatherConditions(}
17 {
18 try {
19 System.out.printin( "Update weather information..." );
20
21 // страница прогноза погод» Национальной метеослужбы США
22 URX url = new URL(
23 "http://iwin.nws.noaa.gov/iwin/us/traveler.html" );
24
25 // задание текстового потока ввода для чтения содержимого
Web-страницы
26 BufferedReader in = new BufteredRea.de r(
27 new InputstreamReader( url.openStream() ) );
28
29 // определение начала данных на Web-странице
30 String separator = "TAV12";
31
32 // нахождение разделительного символа на Web- странице
33 while { !in.readLineО.startsWith{ separator ) )
34 ; // отсутствие действий
35
36 // строки, представляющие на Web-странице Travelers
37 // Forecast заголовки для погоды в дневное и конное время
38 String dayHeader =
39 "С1ТУ WEA HI/LO WEA HI/L0";
4Q String nightHeader =
41 "CITY WEA Х.О/Н1 WEA LO/BI" ;
42
Введение в Web-сервисы и SOAP
659
43 String inputLine = "";
44
45 // нахождение заголовка, которым начинается информация о
погоде
46 do {
47 inputLine = in.readLine();
48 > while ( !inputLine.equals( dayHeader ) &&
49 !inputLine.equals( nightHeader ) );
50
51 weatherlnformation = new Vector (),- // создаюге колгейнера
Vector
52
53 // создание компонентов WeatherBean, содержащих данные
54 // о погоде, и сохранение их в контейнере weatherlnformation
55 inputLine = in.readLine(); // получение данных
для первого города
56
57 // Часть строки ввода inputLine, содержащая нужные данные,
58 // имеет длину в 28 символов. Если длина строки
59 // превосходит 28 символов, выполнить обработку данных.
60 while ( inputLine.length() > 28 ) {
61
62 // Подготовка строк для компонента WeatherBean для
63 // каждого города. Первые 16 символов относятся
64 // к названию города. Далее, шесть символов относятся
65 // к описанию информации о погоде. Следующие шесть
символов представляют наибольшую и наименьшую
температуру HI/L0 или L0/HI.
66 weatherlnformation.add{
67 inputLine,substring( 0, 16 ) ) ;
68 weatherlnformation.add(
69 inputLine.substring{ 16, 22 ) );
70 weatherlnformation.add(
71 inputLitte.substring( 23, 29 > );
72
73 inputLine = in.readLine(); // получение данных для
следующего города
74 }
75
76 in.close (}; // закрытие соединения с Web-сервером MWS
77
78 System.out.println( "Weather information updated." );
79 }
80
81 // обработка неудачного соединения со службой National
Weather Service
82 catch{ java.net.ConnectException connectException ) {
83 connectException.printStackTrace(>;
84 System.exit( 1 ) ,-
85 }
86
87 // обработка других исключений
BB catch( Exception exception ) {
89 exception.printstackTrace() ;
90 System.exit( 1 );
91 )
660
Глава 14
92 }
93
94 // реализация интерфейсного метода для сервиса WeatherService
95 public Vector getWeatherlnfoxmation()
96 {
97 updateWeatherConditionsО;
98
99 return weatherlnformation;
100 }
101 }
Рис. 14.5. Реализация класса WeatherService с использованием SOAP
Класс WeatherBean (рис. 14,6) хранит данные, которые класс WeatherService
извлекает с Web-сайта прогноза погоды. Этот класс хранит название города,
температуру и текстовое описание погоды. В строках 64—85 предоставляются методы
get для каждого фрагмента информации. В строках 25-45 загружается файл
свойств, который содержит имена файлов с условными изображениями характера
погоды. Этот статический блок обеспечивает доступность файлов изображений
сразу же после того, как виртуальная машина осуществит загрузку класса
WeatherBean в память,
1 // WeatherBean.java
2 // WeatherBean содержит информацию о погоде для одного города.
3 package com.deitel.advjhtpl.rmi.weather;
4
5 // Набор базовых пакетов Java
6 import Java.awt.*;
7 import Java.io.*;
8 import java.net.*;
9 import java.util.*;
10
11 // Пакеты расширений Java
12 import javax.swing.*;
13
14 public class WeatJverBean implements Serializable \
15
16 private String cityName,- // название города
17 private String temperature; // температура в городе
18 private String description; // описание погоды
19 private Imageleon image; // изображение характера погоды
20
21 private static Properties imageNames;
22
23 // инициализация объекта imageNames при загрузке класса
24 // Weatherlnfo в память
25 static {
26 imageNames = new Properties(); // создание таблицы свойств
27
28 // загрузка описаний погоды и имен изображений иэ
2 9 // файла свойств
30 try {
31
32 // получение URL для файла свойств
33 UKL url = WeatherBean.class.getResource(
34 "imagenames.properties" );
Введение в Web-сервисы и SOAP
661
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
£5
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
64
85
86 }
}
// загрузка содержимого файла свойств
imageNames.load( new FileInputStream( url.getFile() ) )
// обработка исключений при открытии файла
Catch ( IOException ioException ) {
ioException.printStackTrace();
I
} // конец блока static
// конструктор WeatherBean
public WeatherBean{ String city, String weatherDescription,
String cityTemperature )
{
cityName = city;
temperature = cityTemperature;
description = weatherDescription. trim() ,-
URL url = WeatherBean.class.getResource( "images/" +
imageNames.getProperty( description, "noinfo.jpg" ) )
// получение имени изображения или использование
// имени noinfo.jpg, если описание погоды не найдено
image = new Imagelconf url j;
}
// получение названия города
public String getCityNameО
return cityName;
I получение температуры
public String getTemperature()
return temperature;
// получение описания погоды
public String getDescription()
return description;
// получение условного изображения характера погоды
public XmageXcon getlmage()
return image;
Рис. 14.6. Класс WeatherBean хранит сведения о погоде в одном городе
662
Глава 14
Класс WeatherServiceClient (рис. 14.7) осуществляет удаленный процедурный
вызов SOAP метода getWeatherlnformation класса WeatherService, В строках
31-32 устанавливается URL сервиса SOAP. В строке 35 создается новый объект
Call, который хранит информацию, необходимую для выполнения вызова
удаленной процедуры. В строках 3G—37 устанавливается UR1, который уникально
идентифицирует сервис метеопрогнозов в машине сервлетов, В строках 40-41 задается
имя метода для удаленного вызова процедуры, В строках 42-43 устанавливается
кодировка, используемая при вызове. В строке 46 создается объект Response и
вызывается метод invoke на объекте Call с указанием URL в качестве параметра.
Объект Response содержит ответ на вызов удаленной процедуры. В строке 49
определяется, не возникла ли в результате ответа ошибка (объект Fault). В этом случае
в строках 52-54 выводится код ошибки. Если ошибок не возникло, в строке 58
извлекается объект, возвращенный вызовом удаленной процедуры. В строках 60-61
осуществляется приведение типа Object к типу Vector. В строках 64-65 создается
список List и вызывается метод createBeans (строки 95-107) с передачей
контейнером (Vector) строк в качестве параметра. Метод createBeans преобразует контейнер
(объект типа Vector) строк в список (List) объектов WeatherBeans. В строках 68-69
создается модель List Model списка объектов WeatherBean. В строках 73-77
создается компонент JList, содержащий информацию, полученную в результате вызова
удаленной процедуры, и список JList отображается в окне JFrame.
1 // WeatherServiceClient.Java
2 // WeatherServiceClient осуществляет доступ к удаленному об'ьекту
3 // WeatherService через SOAP, чтобы извлечь информацию о погоде.
4 package com.deitel.advjhtpl.soap.weather;
5
6 // набор базовых пакетов Java
7 import Java.util.*;
в import java.net,*;
9
10 // Пакеты расширений Java
11 import javax.swing.*;
12
13 // Пакеты сторонних поставщиков
14 import org.apache.soap.*;
15 import org.apache.soap.rpc.*;
1Й
1*? /7 пакеты Deitel
18 import com.deitel.advjhtpl.rmi.weather.*;
19
20 public class WeatherServiceClient extends JFrame {
21
22 // конструктор WeatherServiceClient
23 public WeatherServiceClient( String server }
24 (
25 super ( "SOAP WeatherService Client" );
26
27 // соединение с сервером и получение информации о погоде
28 try (
29
30 // URL удаленного объекта SOAP
31 URL url = new URL{ "http://" + server + ":8080/soap/"
32 + "servlet/rperouter" );
33
34 // формирование удаленного вызова SOAP
Введение в Web-сервисы и SOAP
663
35 Call remoteMethod = new Call();
36 remoteMethod.setTargetObjectURI(
37 "urn:xml-weather-service" );
38
39 // задание имени вызываемого удаленного метода
40 гemo teMe thod.se tMe thodName (
41 "getWeatherlnformation" );
4 2 remoteMe thod.se tEncodingStyleUKI(
43 Constants.NS_URI_SQAP_ENC );
44
45 // вызов удаленного метода
46 Response response = remoteMethod. invoke { url, "" );
47
48 ff получение ответа
4 9 if ( response.generatedFault() } {
50 Fault fault = response.getFault();
51
52 System.err.println( "CALL FAILED:\nFauIt Code = "
53 + fault.getFaultCode0 + "KnFault String = "
54 + fault.getFaultString() );
55 )
56
57 else \
5$ Parameter result = response. getReturnValuef) .'
59
60 Vector weatherStrings = ( Vector )
61 result.getValue();
62
63 // получение информации о погоде из объекта результата
64 List weatherInformation = createBeans(
65 weatherStrings );
66
67 // создание модели WeatherListModel для информации
а погоде
68 ListModel weatherListModel =
69 new WeatherListModel( weatherlnformation );
70
71 // создание списка JList, установка для него
72 // интерфейса CellRenderer и добавление его в макет
73 JList weatherJList = new JList( weatherListModel );
74 weatherJList.setCellRenderer( new
75 WeatherCellRendererO );
76 getContentPane().add( new
77 JScrollPanef weather JList ) ) ;
78 }
79
80 } // конец блока try
81
32 // обработка неверного URL
83 catch ( MalformedURLException malformedURLException ) {
84 malformedURLException.printStaclcTraceO ;
B5 }
86
87 // обработка исключения SOAP
BB catch ( SOAPException soapException ) {
89 soapException.printStackTrace();
664
Глава 14
90 }
91
92 } // конец конструктора WeatherServiceClient
93
94 // создание списка List, объектов WeatherBean из контейнера строк.
weatherStrings
95 public List createBeans( Vector weatherStrings )
96 {
97 List list = new ArrayList();
98 for ( int i = 0; ( weatherStrings.size{) - 1 > > i;
99 1 += 3 ) {
100 list.add( new HeatherBean (
101 ( String ) weatherStrings.elementAt( i ),
102 ( String ) weatherStrings.elementAt( i + 1 )r
103 ( String } weatherStrings.elementAt( i + 2 ) ) );
104 )
105
106 return list;
107 }
108
109 // выполнение приложения WeatherServiceClient
110 public static void main( String args[] )
111 {
112 WeatherServiceClient client = null;
113
114 // если IP-адрес сервера или хост-имя не заданы, использовать
115 // "looalbost"; иначе использовать указанное хост-имя
116 if ( args.length == 0 )
117 client = new WeatherServiceClient{ "localhost" );
11B else
119 client = new WeatherServiceClient( args[ 0 ] );
120
121 // настройка и отображение окна приложении
122 client.setDefaultCloseOperatiOn( JFrame.EXIT_OH_CL0SE );
123 client pack О;
124 client.setResizable( false );
125 client.setVisible ( true );
126 }
127 } ^
Рис. 14,7. Реализаций класса WeatherServiceClient с использованием SOAP
Класс WeatherListModel (рис. 14.8) представляет собой модель типа ListModel,
которая содержит объекты WeatherБеап., отображаемые в списке JList. В этом
примере мы продолжим применять паттерны проектирования, на этот раз, введя
адаптерный паттерн проектирования Adapter, который дает возможность
взаимодействовать друг с другом компонентам, имеющим несовместимые интерфейсы.
Паттерн проектирования Adapter имеет множество аналогий в реальном мире.
Например, электрические вилки для бытовых приборов, используемых в США, не
совместимы с электрическими розетками, применяемыми в Европе. Чтобы
пользоваться американскими электроприборами в Европе, пользователю необходимо
вставить переходник-адаптер между электрической розеткой и вилкой. С одной
стороны, этот адаптер обеспечивает интерфейс, совместимый с американской
электрической вилкой. С другой стороны, адаптер обеспечивает интерфейс,
совместимый с европейской электрической розеткой. Класс WeatherList Model играет роль
Введение а Web-сервисы и SOAP
665
адаптера в паттерне проектирования Adapter. Интерфейс List Java не является
совместимым с интерфейсом класса JList, поскольку компонент JList способен
извлекать элементы только из модели ListModel. В связи с этим мы предоставляем
класс WeatherListModel, который адаптирует интерфейс List к интерфейсу JList.
Когда JList вызывает метод getSize класса WeatherListModel, объект
WeatherListModel вызывает метод size интерфейса List. Когда объект JList вызывает
метод getElemeatAt класса WeatherListModel, модель WeatherListModel вызывает
метод get класса JList и т.д.
1 // WeatherListModel.Java
2 // WeatherListModel расширяет класс AbstractListModel, чтобы предос-
3 /J тавитъ модель ListModel для хранения списка объектов WeatherBeans.
4 package com.deitel.advjhtpl.rmi.weather;
5
6 // Набор базовых пакетов Java
7 import java.util,*;
6
9 // Пакет» расширений Java
10 import ;javax. swing. AbstractListModel;
11
12 public class WeatherListModel extends AbstractListModel {
13
14 У/ список элементов в модели ListModel
15 private List list;
16
17 // конструктор без параметров WeatherListModel
18 public WeatherListModel()
19 1
20 // создание нового списка для объектов WeatherBean
21 list = new ArrayList();
22 }
23
24 // конструктор WeatherListModel
25 public WsatherListModel( List itemList }
26 (
27 list = itemList;
28 }
29
30 // получение разкерв списка
31 public int getSize()
32 {
33 return list.size();
34 }
35
36 // получение ссылки типа. Object на элемент с заданным индексом
37 public Object getSlementAt( int index )
38 {
39 return list.get( index );
40 >
41
42 // добавление элемента в модель WeatherListModel
43 public void add( Object element )
44 {
45 list.add( element );
46 fire!ntervalAdded( this, list.sized, list.size() );
47 J
666
Глава 14
48
49 // удаление элемента из модели WeatherListModel
50 public void remove( Object element )
51 {
52 int index = list. itidexQf ( element );
53
54 if ( index != -1 ) {
55 list.remove( element >;
56 firelntervalRemoved( this, index, index );
57 }
58
59 } // конец метода remove
60
61 // удаление всех элементов из модели WeatherListModel
62 public void clear О
63 I
64 // получение исходного размера списка
65 int size = list.size();
66
67 // очистка всех элементов в списке
68 list, clear О;
69
70 // уведомление слушателей об изменении содержимого
71 fireContentsChangedf this, 0, size );
72 }
73 )
Рис. 14.8. Класс WeatherListModel является реализацией интерфейса ListModel и хранит
информации о погоде
Класс JList использует интерфейс ListCcllRenderer для воспроизведения
каждого элемента, содержащегося в модели ListModel объекта JList. Класс
WeatherCellRenderer (рис. 14.9) является подклассом класса DefaultListCell-
Renderer и используется для отображения объектов WeatherBean в списке JList.
Метод fjetListCellRendererComponent создает и возвращает объект Weatherltem
(рис. 14.10) для заданного объекта WeatherBean.
Класс Weatherltem (рис. 14.10) является подклассом класса JPanel и
используется для отображения информации о погоде, хранящейся в объекте
WeatherBean. Класс WeatherCellRenderer использует экземпляры класса Weatherltem
для отображения информации о погоде в списке JList. В блоке static (строки
22-29) осуществляется загрузка объекта backgroundlmage класса Imagelcon в
память, когда виртуальная машина загружает сам класс Weatherltem. Это
обеспечивает, что объект backgroundlmage будет доступен всем экземплярам класса
Weatherltem. Метод paintComponent (строки 38-56) осуществляет вывод
фонового изображения backgroundlmage (строка 43), названия города (строка 50),
температуры (строка 51) и изображения Imagelcon для объекта WeatherBean, которое
характеризует погодные условии (строка 54).
1 // WeatherCellRenderer.Java
2 // WeatherCellRenderer - специализированный интерфейс
3 // ListCellRenderer для объектов WeatherBean в списке JList.
4 package com.deitel.advjhtpl.rmi.weather;
5
6 // Набор базовых пакетов Java
7 import java.awt.*;
Введение в Web-сервисы и SOAP
667
8
9 // Пакеты расширений Java
10 import javax.swing.*;
11
12 public class WeatherCellRenderer extends DefaultListCellRenderer {
13
14 // возврат объекта Weatherltem, который отображает информацию о
погоде в городе
15 public Component getListCellRendererComponent( JList list,
16 Object value, int index, boolean isSelected, boolean focus )
17 (
18 return new Weatherltem{ ( WeatherBean ) value );
19 }
20 } __ _
Рис. 14.9. Класс WeatherCellRenderer является видоизмененным классом ListCellRenderer
для отображения объектов WeatherBean в списке JList
1 // Weatherltem.Java
2 // Weatherltem отображает информацию о погоде в городе в панели
JPanel.
3 package com,deitel.advjhtpl. rmi.weather;
4
5 // Набор базовых пакетов Java
6 import java.awt,*;
7 import java.net.*;
8 import java.util.*;
9
10 // Пакеты расширений Java
11 import javax.swing.*;
12
13 public class Weatherltem extends JPanel {
14
15 private WeatherBean WeatherBean; // информация о погоде
16
17 // фоновый рисунок rmagelcon
18 private static ImageIcon backgroundlmage;
19
20 // блок инициализации static загружает файл изображения,
21 // когда класс Weatherltem загружается в память
22 static {
23
24 // получение CTRL для фонового изображения
25 URL url = Weatherltem.class.gatResource( "images/back.jpg" )
26
27 // фоновое изображения для информации о погоде в каждом
из городов
28 backgroundlmage = new ImageIcon( url ),-
29 }
30
31 // инициализация компонента Weatherltem
32 public Weatherltem( WeatherBean bean )
33 {
34 WeatherBean = bean;
35 }
36
668
Глава 14
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65 }
// отображение информации о погоде В городе
public void paintComponerit ( Graphics g )
{
super,paintCoraponent( g ) ;
// рисование фока
backgroundlmage.paintlcon( this, q, 0, 0 );
// задание шрифта к цвета для рисования,
// затем отображение названия города и температуры
Font font = new Font{ "SansSerif", Font.BOLD, 12 );
g.setFont{ font );
g.setColor( Color.white );
g.drawStringf weatherBean.getCityName(), 10, 19 );
g.drawstring{ weatherBean-getTemperature(), 130, 19 )•
// вывод условного изображения характера погоды
weatherBean.getlmageО.paintlconС this, g, 253, 1 1;
} // конец метода paintComponent
// установка в качестве желательных размеров окна для
// компонента Weatberltem высоты и ширины фонового изображения
public Dimension getPreferredSize()
{
return new Dimension( backgroundlmage.getlconWidthO ,
backgroundlmage.getlconHeight() );
Рис. 14.10. Класс Westherftem отображзет информацию о погоде в одном городе
Развертывание сервиса службы погоды осуществляется точно так же, как и
развертывание ранее рассмотренного нами сервиса передачи сообщений.
Запустите средство работы с сервлетами Tomcat и откройте инструментальное средство
администрирования SOAP (localhost:8080/soap/admin), как показано на рис, 14.11.
*&щщ%%
1*ьЩЩ
mmTJfwi^H ttM Щ^Щ^^^.. ' $Ш$Й':1ж!'''
*?f * Ж Л\ «МЧИ» «-«ЯВЙвЯ-
tft: ^tuuh^i ^iBcg,«4p/#l4VMaz .itrti
' ■1Щ$&Я&*$?*~т
*.ЫМ№.
- .....,.,....:.:;..
'■■■'&Ш'.
ЗЗЯКг
Apache SOAP Admi
^ЪлЕ do УК wuir to de [od*y"
S}to*,'.j;, ■ •' ,,
ЖШ "'"Ж,:
Рис. 14.11. Страница средства администрирования Apache SOAP Admin
Введение в Web-сервисы и SOAP
669
Щелкните на Deploy и введите информацию в форму, как показано на рис. 14.12.
Следует вводить полное имя пакета для WeatherService. Осуществив
развертывание сервиса, выполните класс WeatherServiceClient. В результате вызова
удаленной процедуры извлекается информацию о погоде, а клиент затем отображает ее
(рис. 14.13).
it * ..У*.'. ***' ■""■'и 'т«й '■ >«»*.
j »»>**■» '^Ща^ряшЪ ai'-aig JSHWtiU-АН- 'el-Si
U **s №utort,9wys«ft'«*T*V™*»* Ня4
шд
**м
Apache SUA I* Admin Л
Deploy л Service
Service Deployment Descriptor Tif
fScft?e IfRaquait *}
'jt'^n + ,-* ;^t. ^ I -(WMespace icp^ated bst *f aictbod aimer;)
s ^JT^
"1
j F ы ^J aer-"tbie^ f'roviitr Ty?eh Enter XJLL Clal S Nmm
Number C-fOptOw [
Key
^Е—^ ~ jprjraier Ui^T
|3impleteM;a
ИГ
-^
Рис. 14.12. Шаблон дескриптора развертывания Service Deployment Descriptor Template
SOAP Apache
^5 DAP weatherSe
ALBANY NY
ANCHORAGE
A1LANTA
ATLANTIC CITY
BOSTON
tvice СПщгА ^||
86'M
f.2'5V
B5 S9
?&гЦ
8066
H^J
L
ft
0
li
■■=131*1
t '21
-1;
Ш
it Д1
&j
fe
-
Л
BURLINGTON VI BL№tj
CHARLESTONVW 86'6J
Ш1
Рис. 14.13. Клиент SOAP WeatherService Client
670
Глава 14
14.4. Ресурсы в Internet и во Всемирной паутине
WMw.siin.com/9oftnaie/siinD-ne/index .html
Сайт корпорации Sun Microsystems, посвященный спецификации Web-сервисов Open
Net Environment (ONE),
xml -apache.org/soap
С этого сайта можно загрузить реализацию SOAP Apache. Здесь также можно найти
документацию и иную информацию.
xml.apache. ocg/xerces-j/index.html
С этого сайта можно загрузить синтаксический анализатор Apache Xerces для Java, a
также документацию.
Jakarta.apache.org
Web-сайт средства работы с сервлетами Apache Jakarta Tomcat.
Резюме
• Web-сервисом может быть любое доступное через Web приложение, например,
Web-страница с динамическим содержимым.
• В более узком смысле Web-сервис — это приложение, предоставляющее общедоступный
интерфейс, который может использоваться другими приложениями в Web.
• Архитектура Open Net Environment (ONE) требует, чтобы Web-сервис был доступен через
протокол HTTP и другие Web-протоколы, осуществлял взаимодействие на баае
XML-сообщений и был зарегистрировал сервисом поиска.
• Web-сервисы могут обеспечивать высокий уровень совместимости между различными
системами. Функциональная совместимость и расширяемость Weh-cepencos
подразумевает, что разработчики могут быстро создавать большие приложения и более крупные
Web-сервисы из более мелких.
• Спецификация Open Net Environment, разработанная корпорацией Sun, описывает
архитектуру для создания интеллектуальных Web-сервисов. Согласно заявлению Sun,
интеллектуальные Web-сервисы совместно используют общее операционное окружение с
другими сервисами.
• SOAP представляет собой протокол па базе HTTP-XML, который дает возможность црило-
жениям взаимодействовать через Internet, используя XML-документы, называемые
XML-сообщениями.
• SOAP является не зависящим от используемой платфоркы протоколом, я может быть
реализован на любом языке программирования. SOAP поддерживает транспортировку с
применением практически любого приемлемого протокола.
• Сообщение SOAP содержит конверт, который описывает содержимое, предполагаемого
получателя и требования к обработке сообщения. Необязательный элемент header
сообщения SOAP содержит дополнительную информацию по обработке для приложений,
которые получают сообщение SOAP.
• С помощью заголовка поверх SOAP могут быть надстроены более сложные протоколы.
Тело сообщения SOAP содержит специфичные для приложения данные,
предназначенные предполагаемому получателю сообщения.
» SOAP может использоваться для удаленного вызова процедур (Remote Procedure Call
RPC), который представляет собой запрос, посылаемый другому компьютеру для
выполнения определенной задачи. RPC использует словарь XML для указания метода,
подлежащего активизации, передаваемых методу параметров и URL целевого объекта.
» Поскольку различные кампании используют различные платформы, приложения и
форматы данных, обмен данными может вызвать затруднения. В связи с этим партнеры но
бизнесу устанавливают такие протоколы к форматы данных, которые способствуют
эффективному ведению электронной коммерции.
Введение в Web-сервисы и SOAP
671
Терминология
application-to-application (A2A) integration Remote Procedure Call (RPC) — удаленный
— интеграция на уровне приложение — вызов процедур
приложение request-response ~ запрос-ответ
asynchronous RPC — асинхронный удален- schema — схема
ный вызов процедур setMethodName, метод класса Call
Call, класс setParams, метод класса Call
deploying a service — развертывание сервиса Simple Object Access Protocol (SOAP) — про-
distributed. object architecture — архитекту- токол простого доступа к объектам
ра распределенных объектов Sun Open Net Environment — спецификация
Fanlt, класс открытого сетевого окружения Sun
firewall — брандмауэр, межсетевой экран synchronous RPC — синхронный удаленный
Hypertext Transfer Protocol (HTTP) вызов процедур
iovoke, метод класса Call Universal Description, Discovery and
loosely coupled messaging — слабосвязанный Integration (UDDI) — универсальное опи-
обмен сообщениями санке, обнаружение и интеграция
messaging — обмен и передача сообщений Web services — Web-сервисы
org.apache.soap.rpc XML-SOAP admin tool — инструментальное
Parameter, класс средство администрирования XML-SOAP
Упражнения для самоконтроля
14.1. Ответьте, является ли каждое из следующих высказываний истинным или ложным.
Если высказывание ложно, объясните, почему.
a) SOAP — это технология, которая способствует передаче данных через сеть,
b) Чтобы протокол SOAP работал, он должен быть привязан к HTTP.
c) Чтобы взаимодействовать посредством SOAP, программные системы должны иметь
одинаковую распределенную объектную архитектуру.
d) Тело сообщения SOAP может содержать удаленный вызов процедуры.
14.2. Заполните пропуски в следующих высказываниях:
a) Удаленный вызов процедуры RPC SOAP требует указания имени вызываемого
метода, его параметров и ,
b) SOAP содержит информацию, которая описывает содержимое,
предполагаемого получателя и требования по обработке сообщения SOAP.
c) SOAP допускает передачу через межсетевое экраны, поскольку он использует
в качестве транспортного механизма.
d) RPC SOAP используют модель HTTP.
Ответы на упражнения для самоконтроля
14.1. а) Истинно.
b) Ложно. SOAP может быть привязан к другим протоколам.
c) Ложно. SOAP является не зависящим от используемой платформы протоколом.
d) Истинно.
14.2. а) требований к обработке сообщения.
b) Конверт.
c) HTTP.
d) запрос-ответ.
Упражнения
14.3. Напишите серверный класс, содержащий метод sort, который может осуществлять
сортировку заданных чисел. Напишите клиентскую программу, которая может
осуществлять удалнный вызов RPC SOAP метода sort, отправляя при этом множество
неотсортированных значений. Отобразите на клиенте результаты сортировки.
672
Глава 14
14.4. Модифицируйте класс WeatherServiceClient, чтобы обновлять информацию в нем
через задаваемые пользователем интервалы времени. Измените настройки Tomcat, чтобы
сделать объект WeatherService постоянно действующим (персистштным). Это
позволит более эффективно осуществлять обновления.
14.5. Создайте клиентскую и серверную составляющие приложения с архитектурой,
подобной той, которая использовалась для службы погоды, но получающие информацию о
ценах с сайта сопоставления цен, такого как shopper.cnet.com..
14.6. Создайте серверный класс, который способен хранить и извлекать строки. Выполните
развертывание класса, чтобы он постоянно присутствовал на сервере (являлся перси-
стентным). После этого создайте клиента, который хранит и извлекает строки с сервера.
14.7. Напишите простой одиоравговый сервис для неотложного обмена сообщениями.
Создайте серверный класс, содержащий метод, который открывает окно с текстом
сообщения при вызове этого метода клиентом. Клиент предоставляет пользователю
возможность вводить текст сообщения и вызывать метод серверного класса для отображения
сообщения на другом компьютере.
Используемые источники
1. D. Savarese, «ONEWeb to Rule Them ALL». Java Pro August 2001: p. 58.
Этот файл был взят с сайта
http://all-ebooks.com
Данный файл представлен исключительно в
ознакомительных целях. После ознакомления с
содержанием данного файла Вам следует его
незамедлительно удалить. Сохраняя данный файл
вы несете ответственность в соответствии с
законодательством.
Любое коммерческое и иное использование кроме
предварительного ознакомления запрещено.
Публикация данного документа не преследует за
собой никакой коммерческой выгоды.
Эта книга способствует профессиональному росту
читателей и является рекламой бумажных изданий.
Все авторские права принадлежат их уважаемым владельцам.
Если Вы являетесь автором данной книги и её распространение
ущемляет Ваши авторские права или если Вы хотите
внести изменения в данный документ или опубликовать
новую книгу свяжитесь с нами по email.