Вы находитесь на странице: 1из 650

Руководство по Web-сервисам

Copyright © Sun Microsystems®, Все права защищены.

Аннотация

Это руководство представляет собой учебник для начинающих, посвященный разработке


Web-служб и Web-приложений с использованием Java™ Web Services Developer Pack (Java
WSDP). Java WSDP является самодостаточным набором средств, содержащим ключевые
технологии, упрощающие построение Web-служб с использованием Java 2 Platform. Ниже
мы перечислим все, что вам необходимо знать для наиболее эффективного изучения
данного руководства.

Rendered by www.RenderX.com
2

Оглавление
1. Об этом руководстве...................................................................................................... 1
1.1. Для кого предназначено руководство.........................................................................1
1.2. Как читать это руководство......................................................................................... 1
1.3. О примерах................................................................................................................... 2
1.3.1. Предварительные требования........................................................................... 2
1.3.2. Запуск примеров..................................................................................................2
1.3.2.1. Требуемое программное обеспечение..................................................... 2
1.3.2.2. Построение примеров................................................................................ 3
1.3.2.3. Настройка примеров...................................................................................3
1.4. Как распечатать это руководство................................................................................3
1.5. Типографские соглашения.......................................................................................... 3

2. Введение в Web-службы................................................................................................ 3
2.1. Значение XML и платформы Java™........................................................................... 4
2.2. Что такое XML?.............................................................................................................5
2.2.1. Что обеспечивает переносимость XML?........................................................... 6
2.3. Обзор Java API для XML..............................................................................................7
2.4. JAXP.............................................................................................................................. 8
2.4.1. SAX API................................................................................................................ 8
2.4.2. DOM API............................................................................................................. 10
2.4.2.1. Пространства имен XML.......................................................................... 12
2.4.3. XSLT API.............................................................................................................13
2.4.3.1. Преобразование дерева DOM в XML-документ.....................................13
2.4.3.2. Преобразование XML-документа в HTML-документ..............................14
2.5. JAX-RPC...................................................................................................................... 15
2.5.1. Обзор JAX-RPC..................................................................................................15
2.5.1.1. Способность к взаимодействию.............................................................. 16
2.5.1.2. Простота использования..........................................................................16
2.5.1.3. Дополнительные функции....................................................................... 17
2.5.2. Использование JAX-RPC.................................................................................. 17
2.5.3. Создание Web-службы......................................................................................18
2.5.4. Кодирование клиентской программы...............................................................19
2.5.5. Вызов удаленного метода................................................................................ 20
2.6. JAXM............................................................................................................................21
2.6.1. Установка соединения...................................................................................... 22
2.6.1.1. Установка соединения точка-точка......................................................... 22
2.6.1.2. Установка соединения с поставщиком системы обмена
сообщениями......................................................................................................... 23
2.6.2. Создание сообщения........................................................................................ 23
2.6.3. Заполнение сообщения.................................................................................... 24
2.6.3.1. Заполнение SOAP-части сообщения...................................................... 24

Web-

Rendered by www.RenderX.com
3

2.6.3.2. Заполнение части вложений сообщения................................................25


2.6.4. Передача сообщения........................................................................................ 26
2.7. JAXR............................................................................................................................ 26
2.7.1. Использование JAXR........................................................................................ 27
2.7.1.1. Регистрация субъекта бизнеса................................................................27
2.7.1.2. Поиск в реестре........................................................................................ 28
2.8. Пример сценария....................................................................................................... 29
2.8.1. Сценарий............................................................................................................29
2.8.1.1. Поиск новых поставщиков........................................................................29
2.8.1.2. Запрос прайс-листов................................................................................ 30
2.8.1.3. Сравнение цен и заказ кофе....................................................................30
2.8.1.4. Продажа кофе по сети Интернет.............................................................30
2.8.2. Заключение........................................................................................................ 30

3. Освоение XML...............................................................................................................31
3.1. Введение в XML..........................................................................................................31
3.1.1. Что такое XML?..................................................................................................31
3.1.1.1. Теги и атрибуты........................................................................................ 32
3.1.1.2. Пустые теги............................................................................................... 32
3.1.1.3. Комментарии в XML-файлах................................................................... 33
3.1.1.4. Пролог XML............................................................................................... 33
3.1.1.5. Директивы................................................................................................. 34
3.1.2. В чем важность XML?........................................................................................35
3.1.2.1. Обыкновенный текст................................................................................ 35
3.1.2.2. Идентификация данных........................................................................... 35
3.1.2.3. Стиль отображения.................................................................................. 35
3.1.2.4. Встроенная возможность многократного использования..................... 36
3.1.2.5. Связываемость......................................................................................... 36
3.1.2.6. Простота обработки................................................................................. 36
3.1.2.7. Иерархичность.......................................................................................... 36
3.1.3. Как можно использовать XML?.........................................................................36
3.1.3.1. Традиционная обработка данных........................................................... 37
3.1.3.2. Основанное на документах программирование (DDP)..........................37
3.1.3.3. Связывание............................................................................................... 37
3.1.3.4. Архивация................................................................................................. 38
3.1.3.5. Итог............................................................................................................ 38
3.2. XML и связанные спецификации: освоение алфавитной путаницы...................... 38
3.2.1. Основные стандарты.........................................................................................39
3.2.1.1. SAX............................................................................................................ 39
3.2.1.2. DOM........................................................................................................... 40
3.2.1.3. JDOM и dom4j............................................................................................40
3.2.1.4. DTD............................................................................................................ 40
3.2.1.5. Пространство имен...................................................................................41
3.2.1.6. XSL.............................................................................................................41
3.2.1.7. XSLT (+XPATH)......................................................................................... 41
3.2.2. Стандарты схем.................................................................................................42

Web-

Rendered by www.RenderX.com
4

3.2.2.1. XML Schema.............................................................................................. 43


3.2.2.2. RELAX NG................................................................................................. 43
3.2.2.3. TREX.......................................................................................................... 43
3.2.2.4. SOX............................................................................................................ 43
3.2.2.5. Schematron................................................................................................ 44
3.2.3. Стандарты связывания и представления........................................................44
3.2.3.1. XML Linking................................................................................................44
3.2.3.2. XHTML....................................................................................................... 44
3.2.4. Стандарты знаний............................................................................................. 45
3.2.4.1. RDF............................................................................................................ 45
3.2.4.2. RDF Schema.............................................................................................. 45
3.2.4.3. XTM............................................................................................................ 45
3.2.5. Стандарты, основанные на XML...................................................................... 46
3.2.5.1. Extended Document Standards (стандарты расширенных
документов)............................................................................................................46
3.2.5.1.1. SMIL.................................................................................................. 46
3.2.5.1.2. MathML..............................................................................................46
3.2.5.1.3. SVG................................................................................................... 46
3.2.5.1.4. DrawML............................................................................................. 46
3.2.5.2. Стандарты электронной коммерции (eCommerce)................................ 47
3.2.5.2.1. ICE.....................................................................................................47
3.2.5.2.2. ebXML............................................................................................... 47
3.2.5.2.3. cxml................................................................................................... 47
3.2.5.2.4. CBL....................................................................................................47
3.2.5.2.5. UBL....................................................................................................47
3.2.6. Итоги...................................................................................................................48
3.3. Разработка структуры данных XML.......................................................................... 48
3.3.1. Облегчение своей работы................................................................................ 48
3.3.2. Атрибуты и элементы........................................................................................48
3.3.2.1. Вынужденный выбор................................................................................ 48
3.3.2.2. Стилистический выбор............................................................................. 49
3.3.3. Нормализация данных...................................................................................... 50
3.3.4. Нормализация DTD........................................................................................... 51

4. Начало работы с Tomcat.............................................................................................. 52


4.1. Установка.................................................................................................................... 52
4.1.1. Получение кода примера.................................................................................. 52
4.1.1.1. Схема кода примера.................................................................................52
4.1.2. Установка переменной PATH........................................................................... 53
4.1.3. Создание файла свойств компоновки............................................................. 53
4.2. Краткий обзор............................................................................................................. 54
4.3. Создание первого приложения................................................................................. 55
4.3.1. Компонент ConverterBean................................................................................. 55
4.3.1.1. Кодирование компонента ConverterBean................................................55
4.3.2. Web-клиент ........................................................................................................56
4.3.2.1. Кодирование Web-клиента.......................................................................56

Web-

Rendered by www.RenderX.com
5

4.4. Компоновка первого приложения при помощи ant.................................................. 58


4.4.1. Создание файла компоновки и размещения для ant..................................... 59
4.4.2. Компиляция исходных файлов.........................................................................61
4.5. Размещение приложения.......................................................................................... 61
4.5.1. Запуск Tomcat ................................................................................................... 62
4.5.2. Установка приложения при помощи ant.......................................................... 62
4.5.3. Размещение приложения при помощи deploytool...........................................63
4.5.3.1. Создание WAR-файла и идентификация файлов в Web-
приложении ........................................................................................................... 63
4.5.3.2. Выбор типа компонентов......................................................................... 64
4.5.3.3. Установка свойств компонента................................................................64
4.5.3.4. Размещение приложения.........................................................................64
4.5.3.5. Просмотр дескриптора размещения....................................................... 64
4.6. Выполнение первого приложения.............................................................................65
4.6.1. Выполнение Web-клиента ............................................................................... 65
4.6.2. Остановка Tomcat..............................................................................................66
4.7. Использование admintool...........................................................................................66
4.7.1. Понимание ролей, групп и пользователей...................................................... 67
4.7.2. Добавление ролей при помощи admintool.......................................................67
4.7.3. Добавление пользователей при помощи admintool........................................68
4.8. Модификация приложения........................................................................................ 68
4.8.1. Модификация файла классов.......................................................................... 68
4.8.2. Модификация Web-клиента .............................................................................69
4.9. Общие проблемы и их решение................................................................................69
4.9.1. Ошибки при запуске Tomcat............................................................................. 69
4.9.1.1. Ошибка "Out of Environment Space" ("Не хватает пространства
окружения")............................................................................................................ 69
4.9.1.2. Ошибка "Unable to Locate the Server localhost:8080" ("Невозможно
обнаружить сервер localhost:8080")..................................................................... 69
4.9.2. Ошибки компиляции.......................................................................................... 70
4.9.2.1. Server returned HTTP response code: 401 for URL : (Сервер
возвратил код ответа HTTP: 401 для URL :)....................................................... 70
4.9.2.2. Ant Cannot Locate the Build File (ant не может найти файл
компоновки)............................................................................................................70
4.9.2.3. The Compiler Cannot Resolve Symbols (компилятор не может
разрешить символы)............................................................................................. 70
4.9.2.4. Ошибка "Connection refused" ("В соединении отказано")...................... 71
4.9.2.5. При попытке выполнения задания на установку система, по всем
признакам, виснет..................................................................................................71
4.9.3. Ошибки размещения......................................................................................... 71
4.9.3.1. Failure to run client application (Ошибка при выполнении клиентского
приложения)........................................................................................................... 71
4.9.3.2. The localhost Machine Is Not Found (Компьютер localhost не
найден)................................................................................................................... 72

Web-

Rendered by www.RenderX.com
6

4.9.3.3. The Application Has Not Been Deployed (Приложение не


размещено)............................................................................................................ 72
4.9.3.4. Ошибка "Build Failed: Application Already Exist at Path" (Компоновка
неудачна: приложение уже существует)..............................................................72
4.9.3.5. HTTP 500: No Context Error (HTTP 500: Ошибка: нет контекста).......... 72
4.10. Дополнительная информация.................................................................................73

5. Web-приложения...........................................................................................................74
5.1. Жизненный цикл Web-приложения........................................................................... 74
5.2. Архивы Web-приложения...........................................................................................76
5.2.1. Структура каталога WAR.................................................................................. 76
5.2.2. Структура каталога примера............................................................................ 76
5.2.3. Создание WAR...................................................................................................77
5.3. Настройка Web-приложений......................................................................................78
5.3.1. Пролог................................................................................................................ 79
5.3.2. Пути псевдонимов............................................................................................. 79
5.3.3. Параметры контекста и инициализации.......................................................... 80
5.3.4. Перехватчики событий...................................................................................... 81
5.3.5. Отображения фильтров.................................................................................... 82
5.3.6. Отображение ошибок........................................................................................ 83
5.3.7. Ссылки на элементы окружения, на элементы окружения ресурсов,
или на ресурсы............................................................................................................ 83
5.4. Установка Web-приложений...................................................................................... 84
5.5. Размещение Web-приложений..................................................................................85
5.6. Просмотр списка установленных и размещенных Web-приложений.....................86
5.7. Выполнение Web-приложений.................................................................................. 86
5.8. Обновление Web-приложений.................................................................................. 86
5.8.1. Перезагрузка Web-приложений........................................................................87
5.8.2. Повторное размещение Web-приложений...................................................... 88
5.9. Удаление Web-приложений.......................................................................................89
5.10. Отмена размещения Web-приложений.................................................................. 89
5.11. Интернационализация и локализация Web-приложений......................................89
5.12. Получение доступа к базам данных из Web-приложений.....................................91
5.12.1. Примеры...........................................................................................................91
5.12.2. Установка и запуск сервера баз данных........................................................91
5.12.3. Заполнение базы данных............................................................................... 92
5.12.4. Настройка в Web-приложении ссылки на источник данных.........................92
5.12.5. Определение источника данных в Tomcat.................................................... 93
5.12.6. Настройка в Tomcat отображения JNDI-имени в источник данных............. 93
5.13. Дополнительная информация.................................................................................94

6. Java API for XML Processing.........................................................................................94

Web-

Rendered by www.RenderX.com
7

6.1. JAXP API......................................................................................................................95


6.2. Обзор пакетов.............................................................................................................95
6.3. The Simple API for XML (SAX).................................................................................... 96
6.3.1. Пакеты SAX........................................................................................................97
6.4. The Document Object Model API (DOM)..................................................................... 98
6.4.1. Пакеты DOM.......................................................................................................99
6.5. XML Stylesheet Language Transformations API (XSLT)............................................. 99
6.5.1. Пакеты XSLT.................................................................................................... 100
6.6. Компиляция и выполнение программ..................................................................... 100
6.7. Куда идти дальше?...................................................................................................100

7. Simple API for XML...................................................................................................... 101


7.1. Когда используется SAX.......................................................................................... 102
7.2. Написание простого XML-файла.............................................................................102
7.2.1. Создание файла.............................................................................................. 103
7.2.2. Написание объявления...................................................................................103
7.2.3. Добавление комментария...............................................................................103
7.2.4. Определение корневого элемента.................................................................103
7.2.5. Добавление атрибутов к элементу................................................................ 104
7.2.6. Добавление вложенных элементов............................................................... 104
7.2.7. Добавление HTML-текста .............................................................................. 105
7.2.8. Добавление пустого элемента....................................................................... 106
7.2.9. Законченный продукт...................................................................................... 106
7.3. Дублирование XML-файла при помощи SAX-анализатора.................................. 107
7.3.1. Создание скелета программы........................................................................ 107
7.3.2. Импорт классов................................................................................................108
7.3.3. Настройка ввода/вывода................................................................................ 108
7.3.4. Реализация интерфейса ContentHandler.......................................................109
7.3.5. Настройка анализатора.................................................................................. 110
7.3.6. Вывод результата............................................................................................111
7.3.7. Расстановка разделителей при выводе........................................................ 112
7.3.8. Обработка событий содержимого.................................................................. 112
7.3.8.1. События документа................................................................................ 112
7.3.8.2. События элементов................................................................................113
7.3.8.3. События для символов.......................................................................... 114
7.3.9. Компиляция и выполнение программы......................................................... 117
7.3.10. Проверка выводимой информации..............................................................118
7.3.11. Идентификация событий.............................................................................. 118
7.3.12. Сжатие выводимой информации................................................................. 121
7.3.13. Исследование выходной информации........................................................ 123
7.3.14. Документы и данные..................................................................................... 124
7.4. Добавление дополнительных обработчиков событий.......................................... 124

Web-

Rendered by www.RenderX.com
8

7.4.1. Указание месторасположения документа..................................................... 124


7.4.2. Управление командами обработки................................................................ 126
7.4.3. Резюме............................................................................................................. 127
7.5. Обработка ошибок в неверифицирующем анализаторе...................................... 128
7.5.1. Введение в ошибки..........................................................................................128
7.5.2. Обработка SAXParseException....................................................................... 129
7.5.3. Обработка SAXException................................................................................ 130
7.5.4. Усовершенствование обработчика SAXParseException...............................131
7.5.5. Обработка ParserConfigurationException....................................................... 132
7.5.6. Обработка IOException....................................................................................133
7.5.7. Обработка нефатальных ошибок...................................................................133
7.5.8. Обработка предупреждений........................................................................... 134
7.6. Замена и вставка текста.......................................................................................... 135
7.6.1. Обработка специальных символов................................................................ 135
7.6.1.1. Предопределенные сущности............................................................... 136
7.6.1.2. Символьные ссылки............................................................................... 136
7.6.2. Использование ссылки на сущность в XML-документе................................136
7.6.3. Обработка текста с синтаксисом XML........................................................... 137
7.6.4. Обработка CDATA и других символов...........................................................138
7.7. Создание определения типа документа (DTD)...................................................... 139
7.7.1. Основные DTD-определения..........................................................................139
7.7.2. Определение текста и вложенных элементов.............................................. 140
7.7.3. Ограничения DTD............................................................................................ 140
7.7.4. Значения специальных элементов в DTD..................................................... 141
7.7.5. Ссылка на DTD................................................................................................ 141
7.8. Влияние DTD на неверифицирующий анализатор................................................143
7.8.1. Отслеживание игнорируемых пробелов........................................................144
7.8.2. Очистка.............................................................................................................145
7.8.3. Документы и данные....................................................................................... 145
7.8.4. Пустые элементы, пересмотренный вариант............................................... 146
7.9. Определение атрибутов и сущностей в DTD......................................................... 146
7.9.1. Определение атрибутов в DTD...................................................................... 146
7.9.2. Определение сущностей в DTD..................................................................... 148
7.9.3. Вывод ссылок на сущность.............................................................................149
7.9.4. Дополнительные полезные сущности........................................................... 149
7.9.5. Ссылки на внешние сущности........................................................................ 149
7.9.6. Отображение внешней сущности...................................................................151
7.9.7. Итоговая информация по сущностям............................................................ 151
7.10. Обращение к двоичным сущностям......................................................................151
7.10.1. Использование типа данных MIME.............................................................. 151
7.10.2. Альтернатива: использование ссылок на сущность...................................153
7.11. Выбор вашей реализации анализатора............................................................... 153

Web-

Rendered by www.RenderX.com
9

7.12. Использование верифицирующего анализатора................................................ 153


7.12.1. Конфигурирование генератора.................................................................... 154
7.12.2. Верификация с использованием XML-схемы..............................................154
7.12.2.1. Установка свойств SAX-анализатора................................................. 155
7.12.2.2. Установка соответствующей обработки ошибок................................155
7.12.2.3. Установка связи между документом и схемой................................... 156
7.12.3. Эксперименты с ошибками верификации................................................... 157
7.12.4. Обработка ошибок в верифицирующем анализаторе................................158
7.13. Определение сущностей-параметров и условных секций..................................159
7.13.1. Создание сущностей-параметров и обращение к ним ..............................159
7.13.2. Условные секции........................................................................................... 161
7.14. Анализ параметризованного DTD.........................................................................162
7.14.1. DTD-предупреждения....................................................................................163
7.15. Обработка лексических событий...........................................................................164
7.15.1. Как работает LexicalHandler..........................................................................165
7.15.2. Работа с LexicalHandler.................................................................................165
7.15.2.1. Отображение комментариев................................................................168
7.15.2.2. Отображение другой лексической информации................................ 168
7.16. Использование DTDHandler и EntityResolver....................................................... 171
7.16.1. DTDHandler API..............................................................................................171
7.16.2. EntityResolver API.......................................................................................... 172
7.17. Дополнительная информация...............................................................................173

8. Document Object Model............................................................................................... 173


8.1. Когда используется DOM.........................................................................................173
8.1.1. Документы против данных.............................................................................. 174
8.1.2. Модель смешанного содержимого.................................................................174
8.1.2.1. Типы узлов.............................................................................................. 175
8.1.3. Более простая модель.................................................................................... 175
8.1.4. Увеличение сложности................................................................................... 176
8.1.5. Выбор вашей модели...................................................................................... 178
8.2. Чтение XML-данных в DOM..................................................................................... 179
8.2.1. Создание программы...................................................................................... 179
8.2.1.1. Создание скелета................................................................................... 179
8.2.1.2. Импорт требуемых классов................................................................... 179
8.2.1.3. Объявление DOM................................................................................... 180
8.2.1.4. Обработка ошибок..................................................................................180
8.2.1.5. Создание экземпляра генератора.........................................................182
8.2.1.6. Получение анализатора и анализ файла............................................. 182
8.2.1.7. Выполнение программы.........................................................................182
8.2.2. Дополнительная информация........................................................................ 183
8.2.2.1. Настройка генератора............................................................................ 183
8.2.2.2. Обработка ошибок верификации.......................................................... 183
8.2.3. Забегая вперед................................................................................................ 185

Web-

Rendered by www.RenderX.com
10

8.3. Отображение DOM-иерархии.................................................................................. 185


8.3.1. Отображение узлов дерева............................................................................ 185
8.3.2. Преобразование DomEcho в приложение GUI..............................................185
8.3.2.1. Добавление операторов импорта......................................................... 185
8.3.2.2. Создание GUI-среды.............................................................................. 186
8.3.2.3. Добавление компонентов для отображения........................................ 188
8.3.3. Создание адаптера для отображения DOM в JTree.....................................191
8.3.3.1. Определение класса AdapterNode........................................................ 191
8.3.3.2. Определение адаптера TreeModel........................................................196
8.3.4. Завершение..................................................................................................... 200
8.4. Исследование структуры DOM................................................................................201
8.4.1. Отображение простого дерева.......................................................................201
8.4.2. Отображение более сложного дерева...........................................................202
8.4.2.1. Резюме по лексическим управляющим элементам.............................205
8.4.3. Завершение..................................................................................................... 206
8.5. Создание дружественного пользователю JTree из DOM......................................207
8.5.1. Сжатие просматриваемого дерева................................................................ 207
8.5.1.1. Сделать операции выбираемыми......................................................... 207
8.5.1.2. Идентификация узлов дерева............................................................... 207
8.5.1.3. Управление видимостью узла............................................................... 209
8.5.1.4. Управление доступом к потомку............................................................210
8.5.1.5. Проверка результатов............................................................................211
8.5.1.6. Дополнительные действия.................................................................... 211
8.5.2. Действия с выбранными узлами дерева....................................................... 212
8.5.2.1. Идентификация типов узлов..................................................................212
8.5.2.2. Объединение узлов для определения содержимого элемента..........213
8.5.2.3. Отображение содержимого в JTree ..................................................... 215
8.5.2.4. Соединение JTree и JEditorPane .......................................................... 216
8.5.2.5. Выполнение приложения....................................................................... 217
8.5.2.6. Дополнительное задание.......................................................................219
8.5.3. Обработка изменений..................................................................................... 220
8.5.4. Завершение .................................................................................................... 220
8.6. Создание и управление DOM..................................................................................220
8.6.1. Получение DOM из генератора...................................................................... 220
8.6.1.1. Измененяем код......................................................................................220
8.6.1.2. Создаем узлы Element и Text................................................................ 221
8.6.1.3. Выполните приложение......................................................................... 223
8.6.2. Нормализация DOM........................................................................................ 223
8.6.3. Другие операции..............................................................................................224
8.6.3.1. Обход узлов............................................................................................ 225
8.6.3.2. Поиск узлов............................................................................................. 225
8.6.3.3. Получение содержимого узла................................................................226
8.6.3.4. Создание атрибутов............................................................................... 227
8.6.3.5. Удаление и изменение узлов................................................................ 228
8.6.3.6. Вставка узлов..........................................................................................228
8.6.4. Завершение..................................................................................................... 228

Web-

Rendered by www.RenderX.com
11

8.7. Использование пространства имен........................................................................ 228


8.7.1. Определение пространства имен в DTD....................................................... 228
8.7.2. Ссылка на пространство имен........................................................................229
8.7.3. Определение префикса пространства имен................................................. 229
8.8. Верификация при помощи XML Schema................................................................ 231
8.8.1. Обзор процесса верификации........................................................................231
8.8.2. Настройка генератора DocumentBuilder........................................................ 231
8.8.2.1. Установка связи документа со схемой..................................................232
8.8.3. Верификация при использовании нескольких пространств имен............... 233
8.8.3.1. Объявление схем в наборе XML-данных............................................. 234
8.8.3.2. Объявление схем в приложении........................................................... 234
8.9. Дополнительная информация.................................................................................236

9. XML Stylesheet Language for Transformations........................................................... 236


9.1. Введение в XSLT и XPath........................................................................................ 236
9.1.1. JAXP-пакеты преобразований........................................................................ 237
9.2. Выбор механизма трансформации......................................................................... 238
9.2.1. Анализ производительности...........................................................................238
9.2.2. Анализ функциональности............................................................................. 240
9.2.3. Что выбрать..................................................................................................... 240
9.3. Как работает XPath.................................................................................................. 240
9.3.1. Выражения XPATH.......................................................................................... 241
9.3.2. Модель данных XSLT/XPath........................................................................... 241
9.3.3. Шаблоны и контексты......................................................................................241
9.3.4. Основы XPath-адресации............................................................................... 242
9.3.5. Основы XPath-выражений.............................................................................. 243
9.3.6. Комбинирование индексных адресов............................................................ 243
9.3.7. Групповые символы........................................................................................ 244
9.3.8. Адресация расширенного пути.......................................................................244
9.3.9. Типы данных и операторы XPath................................................................... 244
9.3.10. Строковое значение элемента..................................................................... 245
9.3.11. XPath-функции............................................................................................... 245
9.3.11.1. Функции набора узлов..........................................................................245
9.3.11.2. Функции позиционирования.................................................................245
9.3.11.3. Строковые функции..............................................................................246
9.3.11.4. Логические функции............................................................................. 246
9.3.11.5. Числовые функции............................................................................... 247
9.3.11.6. Функции преобразования.....................................................................247
9.3.11.7. Функции пространства имен................................................................ 247
9.3.12. Резюме........................................................................................................... 247
9.4. Запись DOM в XML-файл.........................................................................................248
9.4.1. Чтение XML...................................................................................................... 248
9.4.2. Создание преобразователя............................................................................250

Web-

Rendered by www.RenderX.com
12

9.4.3. Вывод XML....................................................................................................... 252


9.4.4. Вывод поддерева DOM................................................................................... 253
9.4.4.1. Очистка....................................................................................................254
9.4.5. Резюме............................................................................................................. 254
9.5. Генерирование DOM из произвольной структуры данных....................................254
9.5.1. Создание простого файла.............................................................................. 255
9.5.2. Создание простого анализатора.................................................................... 256
9.5.3. Изменение анализатора для генерирования SAX-событий.........................259
9.5.4. Использование анализатора в качестве SAXSource....................................265
9.5.5. Выполнение преобразования......................................................................... 268
9.6. Преобразование XML-данных при помощи XSLT..................................................268
9.6.1. Определение простого типа документа <article>..........................................268
9.6.2. Создание тестового документа...................................................................... 270
9.6.3. Написание XSLT-преобразования................................................................. 271
9.6.4. Обработка основных структурных элементов...............................................272
9.6.4.1. Обработка элемента <TITLE>............................................................... 273
9.6.4.2. Обработка заголовков............................................................................ 273
9.6.4.3. Генерирование сообщений времени выполнения............................... 274
9.6.5. Написание основной программы....................................................................275
9.6.6. Удаление пробелов......................................................................................... 277
9.6.7. Обработка оставшихся структурных элементов........................................... 279
9.6.7.1. Изменение шаблона <PARA>................................................................280
9.6.7.2. Обработка элементов <LIST> и <ITEM>...............................................280
9.6.7.3. Сортировка шаблонов в таблице стилей............................................. 281
9.6.7.4. Обработка элементов <NOTE>............................................................. 282
9.6.7.5. Выполнение программы.........................................................................283
9.6.8. Обработка встроенных элементов (элементов содержимого).................... 283
9.6.8.1. Выполнение программы.........................................................................287
9.6.9. Распечатка HTML............................................................................................ 287
9.6.10. Что еще может делать XSLT?...................................................................... 287
9.6.10.1. Проблемы с переменными.................................................................. 289
9.7. Преобразование из командной строки................................................................... 289
9.7.1. Компиляция транслета....................................................................................289
9.7.2. Выполнение транслета................................................................................... 290
9.8. Объединение преобразований при помощи цепочки фильтров.......................... 291
9.8.1. Написание программы.................................................................................... 291
9.8.2. Как работает цепочка фильтров.................................................................... 295
9.8.3. Тестирование программы............................................................................... 295
9.8.4. Заключение...................................................................................................... 298
9.9. Дополнительная информация.................................................................................298

10. Java API for XML-based RPC.................................................................................... 298


10.1. Что такое JAX-RPC?...............................................................................................298

Web-

Rendered by www.RenderX.com
13

10.2. Простой пример: HelloWorld.................................................................................. 299


10.2.1. HelloWorld во время выполнения................................................................. 299
10.2.2. Файлы HelloWorld.......................................................................................... 300
10.2.3. Настройка.......................................................................................................300
10.2.4. Создание и размещение службы................................................................. 301
10.2.4.1. Кодирование интерфейса определения службы и класса
реализации...........................................................................................................301
10.2.4.2. Компиляция кода определения службы............................................. 302
10.2.4.3. Пакетирование кода в WAR-файл.......................................................302
10.2.4.4. Генерирование узлов и WSDL-файла.................................................303
10.2.4.5. Размещение службы............................................................................ 304
10.2.4.6. Верификация размещения...................................................................304
10.2.4.7. Отмена размещения службы...............................................................304
10.2.5. Создание и запуск клиентской программы..................................................304
10.2.5.1. Генерирование заглушек..................................................................... 305
10.2.5.2. Кодирование клиентской программы.................................................. 305
10.2.5.3. Компилирование кода клиентской программы...................................306
10.2.5.4. Пакетирование клиентской программы.............................................. 306
10.2.5.5. Выполнение клиентской программы................................................... 307
10.2.6. Итеративная разработка...............................................................................307
10.2.7. Особенности реализации............................................................................. 307
10.3. Типы, поддерживаемые в JAX-RPC......................................................................308
10.3.1. Классы J2SE SDK.......................................................................................... 308
10.3.2. Примитивы..................................................................................................... 309
10.3.3. Массивы......................................................................................................... 309
10.3.4. Прикладные классы.......................................................................................309
10.3.5. Компоненты JavaBeans................................................................................. 309
10.4. Пример клиентской программы динамического прокси...................................... 310
10.4.1. Листинг клиентской программы динамического прокси HelloClient........... 310
10.4.2. Создание и выполнение примера динамического прокси..........................311
10.5. Пример клиентской программы Dynamic Invocation Interface (DII)..................... 311
10.5.1. Листинг DII HelloClient................................................................................... 312
10.5.2. Создание и выполнение примера DII...........................................................313
10.6. Безопасность в JAX-RPC....................................................................................... 314
10.6.1. Базовая аутентификация по SSL................................................................. 314
10.6.1.1. Генерирование сертификатов для базовой аутентификации...........314
10.6.1.2. Добавление SSL-коннектора к Tomcat................................................315
10.6.1.3. Добавление элементов безопасности в web.xml............................... 316
10.6.1.4. Установка свойств безопасности в коде клиентской
программы............................................................................................................316
10.6.1.4.1. Свойство trustStore...................................................................... 317
10.6.1.4.2. Свойство trustStorePassword...................................................... 317
10.6.1.4.3. Свойства username и password.................................................. 317
10.6.1.5. Компоновка и выполнение примера для базовой аутентификации
по SSL...................................................................................................................317

Web-

Rendered by www.RenderX.com
14

10.6.2. Взаимная аутентификация по SSL...............................................................318


10.7. JAX-RPC в J2EE SDK 1.3.1.................................................................................... 318
10.7.1. Предварительные требования..................................................................... 319
10.7.2. Пример программы........................................................................................319
10.7.3. Пакетирование клиентской программы JAX-RPC и Web-службы ............ 320
10.7.4. Настройка J2EE SDK 1.3.1............................................................................320
10.7.5. Размещение сессионного компонента GreetingEJB................................... 321
10.7.6. Размещение JAX-RPC-службы.....................................................................322
10.7.7. Выполнение клиентского приложения JAX-RPC.........................................322
10.7.8. Удаление действий jwsdponj2ee.................................................................. 323
10.8. Создание службы JAX-RPC в deploytool...............................................................323
10.8.1. Компиляция исходного кода......................................................................... 323
10.8.2. Компоновка Web-приложения...................................................................... 324
10.8.3. Размещение Web-приложения..................................................................... 325
10.8.4. Проверка статуса Web-службы.................................................................... 325
10.8.5. Выполнение клиентской программы............................................................ 326
10.9. Дополнительная информация...............................................................................326

11. Java API for XML Messaging..................................................................................... 326


11.1. Введение в JAXM................................................................................................... 327
11.1.1. Сообщения.....................................................................................................327
11.1.1.1. Структура XML-документа................................................................... 328
11.1.1.2. Что такое сообщение?......................................................................... 328
11.1.1.2.1. Сообщения без вложений........................................................... 328
11.1.1.2.2. Сообщения с вложениями.......................................................... 329
11.1.2. Соединения....................................................................................................331
11.1.2.1. SOAPConnection................................................................................... 331
11.1.2.2. ProviderConnection................................................................................ 332
11.1.3. Поставщики службы обмена сообщениями................................................ 333
11.1.3.1. Прозрачность........................................................................................ 333
11.1.3.2. Профили................................................................................................ 333
11.1.3.3. Непрерывная активность..................................................................... 333
11.1.3.4. Промежуточные пункты назначения................................................... 334
11.1.3.5. Когда используется поставщик службы обмена сообщениями........ 334
11.1.3.6. Обмен сообщениями с поставщиком и без него................................335
11.2. Выполнение примеров........................................................................................... 335
11.2.1. Примеры программ........................................................................................336
11.2.2. Исходный код примеров................................................................................337
11.3. Учебник....................................................................................................................337
11.3.1. Клиент, не использующий поставщика службы обмена
сообщениями............................................................................................................. 338
11.3.1.1. Получение объекта SOAPConnection................................................. 338
11.3.1.2. Создание сообщения........................................................................... 339
11.3.1.2.1. Части сообщения......................................................................... 339

Web-

Rendered by www.RenderX.com
15

11.3.1.2.2. Доступ к элементам сообщения................................................. 339


11.3.1.2.3. Добавление содержимого в тело............................................... 340
11.3.1.3. Передача сообщения........................................................................... 343
11.3.1.4. Получение содержимого сообщения.................................................. 343
11.3.2. Клиент, использующий поставщика службы обмена сообщениями......... 344
11.3.2.1. Получение объекта ProviderConnection.............................................. 344
11.3.2.2. Создание сообщения........................................................................... 345
11.3.2.2.1. Получение MessageFactory.........................................................345
11.3.2.2.2. Добавление содержимого в заголовок.......................................346
11.3.2.2.3. Добавление содержимого в SOAP-тело.................................... 347
11.3.2.2.4. Добавление содержимого в объект SOAPPart.......................... 349
11.3.2.3. Передача сообщения........................................................................... 349
11.3.3. Добавление вложений.................................................................................. 350
11.3.3.1. Создание объекта AttachmentPart и добавление содержимого........350
11.3.3.2. Получение доступа к объекту AttachmentPart.................................... 352
11.3.3.3. Резюме.................................................................................................. 352
11.3.4. Ошибки SOAP................................................................................................ 352
11.3.4.1. Обзор..................................................................................................... 352
11.3.4.2. Создание и заполнение объекта SOAPFault...................................... 354
11.3.4.3. Извлечение информации об ошибке.................................................. 355
11.4. Примеры приложений............................................................................................ 356
11.4.1. Request.java................................................................................................... 356
11.4.2. UddiPing.java и MyUddiPing.java................................................................... 358
11.4.2.1. Создание MyUddiPing.java................................................................... 359
11.4.2.1.1. Настройка..................................................................................... 360
11.4.2.1.2. Исследование MyUddiPing.......................................................... 361
11.4.2.2. Добавление нового кода...................................................................... 363
11.4.3. SOAPFaultTest.java........................................................................................366
11.4.3.1. Выполнение SOAPFaultTest.................................................................368
11.4.4. Заключение.................................................................................................... 370
11.5. Дополнительная информация...............................................................................370

12. Java API for XML Registries.......................................................................................370


12.1. Обзор JAXR.............................................................................................................371
12.1.1. Что такое реестр?..........................................................................................371
12.1.2. Что такое JAXR?............................................................................................ 371
12.1.3. Архитектура JAXR......................................................................................... 372
12.2. Реализация JAXR-клиента.................................................................................... 373
12.2.1. Установка соединения.................................................................................. 373
12.2.1.1. Начальные сведения: получение доступа к реестру......................... 374
12.2.1.2. Создание или поиск центра соединений............................................ 374
12.2.1.3. Создание соединения.......................................................................... 374
12.2.1.4. Установка свойств соединения........................................................... 375
12.2.1.5. Получение и использование объекта RegistryService....................... 377
12.2.2. Запрос реестра.............................................................................................. 377
12.2.2.1. Поиск организации по названию......................................................... 378

Web-

Rendered by www.RenderX.com
16

12.2.2.2. Поиск организаций по классификации................................................379


12.2.2.3. Поиск служб и связей со службами.....................................................381
12.2.3. Управление данными реестра......................................................................382
12.2.3.1. Получение авторизации от реестра....................................................382
12.2.3.2. Создание организации......................................................................... 382
12.2.3.3. Добавление классификаций................................................................ 384
12.2.3.4. Добавление служб и связей со службами в организацию................ 384
12.2.3.5. Сохранение организации..................................................................... 385
12.2.3.6. Удаление данных из реестра.............................................................. 386
12.2.4. Использование таксономий в JAXR-клиентах.............................................387
12.2.4.1. Определение таксономии.................................................................... 387
12.2.4.2. Указание почтовых адресов................................................................ 390
12.2.5. Выполнение примеров клиентских приложений......................................... 392
12.2.5.1. Перед компиляцией примеров............................................................ 392
12.2.5.2. Компиляция примеров..........................................................................394
12.2.5.3. Выполнение примеров......................................................................... 394
12.2.5.4. Выполнение примера JAXRPublish..................................................... 395
12.2.5.5. Выполнение примера JAXRQuery ...................................................... 395
12.2.5.6. Выполнение примера JAXRQueryByNAICSClassification...................395
12.2.5.7. Выполнение примера JAXRDelete...................................................... 395
12.2.5.8. Выполнение примера JAXRQueryByWSDLClassification................... 395
12.2.5.9. Публикация классификационной схемы.............................................396
12.2.5.10. Выполнение примеров, работающих с почтовым адресом............ 396
12.2.5.11. Удаление классификационной схемы...............................................397
12.2.5.12. Получение списка ваших объектов реестра.....................................397
12.2.5.13. Другие задания................................................................................... 397
12.3. Дополнительная информация...............................................................................397

13. Технология Java Servlet........................................................................................... 398


13.1. Что такое Сервлет?................................................................................................398
13.2. Примеры сервлетов............................................................................................... 399
13.2.1. Устранение неисправностей.........................................................................402
13.3. Жизненный цикл сервлета.....................................................................................403
13.3.1. Обработка событий жизненного цикла сервлетов......................................403
13.3.1.1. Определение класса-слушателя.........................................................403
13.3.1.2. Определение классов-слушателей событий......................................405
13.3.2. Обработка ошибок.........................................................................................405
13.4. Использование совместного доступа к информации.......................................... 406
13.4.1. Использования объектов области действия............................................... 406
13.4.2. Управление параллельным доступом к общим ресурсам......................... 407
13.4.3. Доступ к базам данных..................................................................................408
13.5. Инициализация сервлета...................................................................................... 409
13.6. Написание методов служб.....................................................................................410
13.6.1. Получение информации из запросов...........................................................410
13.6.2. Построение ответов...................................................................................... 412

Web-

Rendered by www.RenderX.com
17

13.7. Фильтрация запросов и ответов........................................................................... 414


13.7.1. Програмирование фильтров.........................................................................415
13.7.2. Программирование заказных запросов и ответов...................................... 416
13.7.3. Задание отображений фильтров..................................................................418
13.8. Вызов других Web-ресурсов..................................................................................420
13.8.1. Включение других ресурсов в ответ............................................................ 421
13.8.2. Передача управления другому Web-компоненту........................................422
13.9. Организация доступа к web-контексту..................................................................423
13.10. Сохранение состояния клиента.......................................................................... 424
13.10.1. Организация доступа к сессии................................................................... 424
13.10.2. Ассоциирование атрибутов с сессией....................................................... 424
13.10.2.1. Уведомление объектов о том, что они ассоциированы с
сессией................................................................................................................. 425
13.10.3. Управление сессией....................................................................................425
13.10.4. Отслеживание сессии................................................................................. 426
13.11. Завершение работы сервлета.............................................................................427
13.11.1. Отслеживание запросов службы................................................................427
13.11.2. Предупреждение методов о завершении работы.....................................428
13.11.3. Создание "изящных" затянувшихся методов............................................ 429
13.12. Дополнительная информация.............................................................................429

14. Технология JavaServer Pages.................................................................................. 430


14.1. Что такое JSP-страница?.......................................................................................430
14.2. Примеры JSP-страниц........................................................................................... 432
14.3. Жизненный цикл JSP-страницы............................................................................ 434
14.3.1. Трансляция и компиляция............................................................................ 434
14.3.2. Выполнение................................................................................................... 435
14.3.2.1. Использование буферизации.............................................................. 435
14.3.2.2. Обработка ошибок................................................................................435
14.4. Инициализация и завершение работы JSP-страницы........................................ 436
14.5. Создание статического содержимого................................................................... 437
14.6. Создание динамического содержимого................................................................437
14.6.1. Использование объектов в JSP-страницах................................................. 437
14.6.1.1. Неявные объекты................................................................................. 438
14.6.1.2. Объекты-приложения........................................................................... 438
14.6.1.3. Использование совместного доступа к объектам (общие
объекты)............................................................................................................... 438
14.6.2. JSP-элементы сценариев............................................................................. 439
14.6.2.1. Объявления...........................................................................................440
14.6.2.2. Скриптлеты........................................................................................... 440
14.6.2.3. Выражения............................................................................................ 442
14.7. Включение содержимого в JSP-страницу.............................................................443
14.8. Передача управления другому web-компоненту................................................. 444

Web-

Rendered by www.RenderX.com
18

14.8.1. Элемент jsp:param.........................................................................................444


14.9. Включение апплета................................................................................................ 445
14.10. Расширение JSP-языка........................................................................................447
14.11. Дополнительная информация.............................................................................448

15. JavaBean-компоненты в JSP-страницах................................................................. 448


15.1. Правила создания JavaBean-компонентов.......................................................... 449
15.2. Зачем использовать JavaBean-компоненты?.......................................................450
15.3. Создание и использование JavaBean-компонентов............................................ 451
15.4. Установление свойств JavaBean-компонентов....................................................452
15.5. Извлечение свойств JavaBean-компонентов....................................................... 454

16. Заказные теги в JSP-страницах...............................................................................455


16.1. Что такое заказной тег?......................................................................................... 456
16.2. Примеры JSP-страниц........................................................................................... 457
16.3. Использование тегов..............................................................................................459
16.3.1. Объявление библиотек тегов....................................................................... 459
16.3.2. Как сделать реализацию библиотеки тегов доступной.............................. 460
16.3.3. Типы тегов......................................................................................................460
16.3.3.1. Простые теги......................................................................................... 460
16.3.3.2. Теги с атрибутами.................................................................................461
16.3.3.3. Теги с телом.......................................................................................... 461
16.3.3.4. Осуществление выбора между способом передачи информации
в атрибутах или в теле тега................................................................................ 462
16.3.3.5. Теги, определяющие переменные создания сценария..................... 462
16.3.3.6. Взаимодействие тегов..........................................................................462
16.4. Определение тегов................................................................................................ 463
16.4.1. Обработчики тегов.........................................................................................463
16.4.2. Дескрипторы библиотеки тегов.................................................................... 464
16.4.2.1. Элемент listener.................................................................................... 465
16.4.2.2. Элемент tag...........................................................................................465
16.4.3. Простые теги..................................................................................................466
16.4.3.1. Обработчики тегов................................................................................466
16.4.3.2. Элемент body-content........................................................................... 467
16.4.4. Теги с атрибутами......................................................................................... 467
16.4.4.1. Определение атрибутов в обработчике тега..................................... 467
16.4.4.2. Элемент Attribute.................................................................................. 468
16.4.4.3. Проверка атрибутов............................................................................. 469
16.4.5. Теги с телами................................................................................................. 470
16.4.5.1. Обработчики тегов................................................................................470
16.4.5.1.1. Случай, когда обработчик тега не взаимодействует с его
телом.............................................................................................................. 470
16.4.5.1.2. Случай, когда обработчик тега взаимодействует с его
телом.............................................................................................................. 470

Web-

Rendered by www.RenderX.com
19

16.4.5.2. Элемент body-content........................................................................... 471


16.4.6. Теги, определяющие переменные создания сценариев............................ 472
16.4.6.1. Обработчики тегов................................................................................472
16.4.6.2. Предоставление информации о переменной создания
сценария...............................................................................................................472
16.4.6.2.1. Элемент variable.......................................................................... 473
16.4.6.2.2. Класс TagExtraInfo....................................................................... 474
16.4.7. Взаимодействие тегов...................................................................................475
16.5. Примеры..................................................................................................................477
16.5.1. Тег итераций.................................................................................................. 477
16.5.1.1. JSP-страница........................................................................................ 477
16.5.1.2. Обработчик тега....................................................................................478
16.5.1.3. Дополнительный информационный класс тега..................................481
16.5.2. Библиотека тегов шаблона...........................................................................481
16.5.2.1. JSP-страница........................................................................................ 482
16.5.2.2. Обработчики тегов................................................................................483
16.5.3. Как вызывается обработчик тега?................................................................486

17. Стандартная библиотека тегов JavaServer-страниц............................................. 487


17.1. Примеры JSP-страниц........................................................................................... 488
17.2. Использование JSTL.............................................................................................. 490
17.3. Поддержка языка выражений................................................................................ 492
17.3.1. Двойные библиотеки..................................................................................... 493
17.3.2. Язык выражений JSTL...................................................................................494
17.3.2.1. Атрибуты............................................................................................... 494
17.3.2.2. Неявные объекты................................................................................. 494
17.3.2.3. Литералы...............................................................................................495
17.3.2.4. Операторы.............................................................................................496
17.3.3. Взаимодействие тегов...................................................................................496
17.4. Теги ядра.................................................................................................................497
17.4.1. Теги выражений............................................................................................. 497
17.4.2. Теги управления потоками............................................................................499
17.4.2.1. Теги условий......................................................................................... 499
17.4.2.2. Теги итераций....................................................................................... 501
17.4.3. URL-теги......................................................................................................... 502
17.5. XML-теги..................................................................................................................503
17.5.1. Теги ядра........................................................................................................504
17.5.2. Теги управления потоками............................................................................505
17.5.3. Теги преобразований.................................................................................... 506
17.6. Теги интернационализации................................................................................... 506
17.6.1. Установка региона......................................................................................... 507
17.6.2. Теги сообщений............................................................................................. 507
17.6.2.1. Тег bundle.............................................................................................. 507
17.6.2.2. Тег message.......................................................................................... 508

Web-

Rendered by www.RenderX.com
20

17.6.3. Теги форматирования................................................................................... 508


17.7. SQL-теги.................................................................................................................. 509
17.7.1. Конечный интерфейс тега query...................................................................511
17.8. Дополнительная информация...............................................................................513

18. Безопасность Web-приложений...............................................................................513


18.1. Краткий обзор......................................................................................................... 514
18.2. Пользователи, группы и роли................................................................................514
18.2.1. Роли безопасности........................................................................................ 515
18.2.2. Управление ролями и пользователями....................................................... 515
18.2.2.1. Использование Утилиты Администрирования Web-сервера
Tomcat (admintool)................................................................................................516
18.2.2.1.1. Запуск Tomcat.............................................................................. 516
18.2.2.1.2. Запуск admintool...........................................................................517
18.2.2.2. Управление ролями..............................................................................518
18.2.2.3. Управление пользователями...............................................................518
18.2.2.3.1. Рассуждения при изменении профиля пользователя.............. 519
18.2.3. Отображение ролей приложения на роли области.................................... 520
18.3. Безопасность Web-ярусов..................................................................................... 521
18.3.1. Защита Web-ресурсов...................................................................................521
18.3.2. Контроль доступа к Web-ресурсам.............................................................. 521
18.3.3. Установка безопасности без использования deploytool............................. 522
18.3.4. Аутентификация пользователей Web-ресурсов......................................... 524
18.3.4.1. Базовая аутентификация..................................................................... 524
18.3.4.2. Аутентификация, связанная с формой............................................... 524
18.3.4.3. Аутентификация, основанная на сертификации клиента................. 524
18.3.4.4. Профильная (digest) аутентификация................................................ 525
18.3.4.5. Настройка механизма аутентификации Web-ресурсов..................... 525
18.3.4.6. Использование протокола SSL для увеличения
конфиденциальности базовой HTTP-аутентификации и аутентификации,
связанной с формой............................................................................................ 526
18.3.5. Использование программируемой безопасности в Web-ярусах............... 526
18.3.6. Незащищенные Web-ресурсы...................................................................... 527
18.4. Безопасность EIS-ярусов.......................................................................................527
18.4.1. Настройка входа в систему...........................................................................528
18.4.2. Вход, управляемый контейнером.................................................................528
18.4.3. Вход, управляемый компонентом................................................................ 528
18.5. Установка и настройка поддержки SSL для Tomcat............................................ 528
18.5.1. Использование JSSE.....................................................................................529
18.5.2. Настройка сертификата сервера................................................................. 529
18.5.2.1. Получение цифрового сертификата................................................... 531
18.5.3. Настройка SSL-коннектора........................................................................... 532
18.5.3.1. Добавление SSL-коннектора в admintool............................................532
18.5.3.2. Настройка SSL-коннектора в файле server.xml..................................533

Web-

Rendered by www.RenderX.com
21

18.5.4. Проверка поддержки SSL............................................................................. 534


18.5.5. Выявление неисправностей при SSL-соединениях....................................534
18.5.5.1. При загрузке Tomcat возбуждается исключительная ситуация
наподобие "java.io.FileNotFoundException: {some-directory}/{some-file} not
found".................................................................................................................... 534
18.5.5.2. При загрузке Tomcat возбуждается исключительная ситуация
наподобие "java.io.FileNotFoundException: Keystore was tampered with, or
password was incorrect"........................................................................................534
18.5.5.3. Если у вас по прежнему возникают проблемы,..................................534
18.5.6. Общие советы по работе с SSL................................................................... 535
18.5.7. Дополнительная информация о протоколе SSL......................................... 535
18.6. Устранение неисправностей................................................................................. 535
18.6.1. Сервер возвращает код ответа HTTP: 403..................................................535
18.7. Дополнительная информация...............................................................................536

19. Приложение Coffee Break.........................................................................................536


19.1. Обзор приложения Coffee Break........................................................................... 536
19.2. Служба JAX-RPC поставщика............................................................................... 538
19.2.1. Интерфейс службы........................................................................................538
19.2.2. Реализация службы...................................................................................... 538
19.2.3. Публикация службы в реестре..................................................................... 540
19.2.4. Удаление службы из реестра....................................................................... 545
19.3. Служба JAXM поставщика..................................................................................... 547
19.3.1. Клиент JAXM.................................................................................................. 548
19.3.1.1. Отправка зароса................................................................................... 549
19.3.1.2. Извлечение прайс-листа...................................................................... 550
19.3.1.3. Заказ кофе.............................................................................................552
19.3.1.3.1. Создание заказа.......................................................................... 552
19.3.1.3.2. Извлечение подтверждения заказа............................................556
19.3.2. Служба JAXM................................................................................................. 556
19.3.2.1. Возвращение прайс-листа................................................................... 557
19.3.2.2. Возвращение подтверждения заказа..................................................562
19.4. Сервер Coffee Break...............................................................................................564
19.4.1. JSP-страницы.................................................................................................565
19.4.1.1. orderForm...............................................................................................565
19.4.1.2. checkoutForm.........................................................................................565
19.4.1.3. checkoutAck........................................................................................... 565
19.4.2. JavaBean-компоненты................................................................................... 565
19.4.2.1. RetailPriceList.........................................................................................565
19.4.2.1.1. Обнаружение службы JAX-RPC................................................. 565
19.4.2.2. ShoppingCartItem...................................................................................566
19.4.2.3. OrderConfirmation.................................................................................. 566
19.4.2.4. CheckoutFormBean................................................................................566
19.4.3. RetailPriceListServlet...................................................................................... 567
19.5. Построение, установка и исполнение приложения............................................. 567

Web-

Rendered by www.RenderX.com
22

19.5.1. Построение общих классов.......................................................................... 568


19.5.2. Построение и установка службы JAX-RPC..................................................568
19.5.3. Построение и установка службы JAXM....................................................... 569
19.5.4. Построение и установка сервера Coffee Break........................................... 569
19.5.5. Запуск клиента Coffee Break.........................................................................570
19.5.6. Размещение приложения Coffee Break........................................................572

20. Утилита администрирования Tomcat...................................................................... 572


20.1. Запуск admintool..................................................................................................... 573
20.2. Настройка Tomcat...................................................................................................575
20.2.1. Настройка свойств сервера.......................................................................... 576
20.3. Настройка элементов Service................................................................................576
20.3.1. Настройка элементов Connector.................................................................. 577
20.3.1.1. Типы элементов Connector.................................................................. 578
20.3.1.2. Аттрибуты элемента Connector........................................................... 578
20.3.2. Настройка элементов Host........................................................................... 580
20.3.2.1. Атрибуты элемента Host...................................................................... 581
20.3.2.2. Альтернативные имена хоста..............................................................581
20.3.2.3. Настройка элементов Context..............................................................582
20.3.2.3.1. Атрибуты элемента Context ....................................................... 583
20.3.3. Настройка элементов Logger........................................................................585
20.3.3.1. Атрибуты элемента Logger.................................................................. 586
20.3.4. Настройка элементов Realm........................................................................ 587
20.3.4.1. Атрибуты JDBCRealm...........................................................................589
20.3.4.2. Атрибуты JNDIRealm............................................................................ 590
20.3.4.2.1. Определение атрибутов Realm в файле Server.xml................. 593
20.3.4.3. Атрибуты UserDatabaseRealm............................................................. 594
20.3.4.4. Атрибуты MemoryRealm....................................................................... 594
20.3.5. Настройка элементов Valve..........................................................................594
20.3.5.1. Атрибуты элемента Valve.................................................................... 595
20.3.5.1.1. Атрибуты AccessLogValve........................................................... 595
20.3.5.1.2. Значения для атрибута Pattern...................................................596
20.3.5.1.3. Атрибуты RemoteAddrValve........................................................ 597
20.3.5.1.4. Атрибуты RemoteHostValve.........................................................597
20.3.5.1.5. Атрбуты SingleSignOn..................................................................598
20.4. Настройка элемента Resources............................................................................ 598
20.4.1. Атрибуты элемента Data Source.................................................................. 601
20.4.2. Атрибуты элемента Environment Entries......................................................602
20.5. Администрирование ролей, групп и пользователей............................................603
20.6. Дополнительная информация...............................................................................603

21. Менеджер Web-приложений Tomcat (Tomсat Web Application Manager)..............604


21.1. Запуск менеджера Web-приложений.................................................................... 604
21.2. Исполнение команд менеджера с использованием задач Ant........................... 605

Web-

Rendered by www.RenderX.com
23

22. Сервер реестра Java WSDP.................................................................................... 606


22.1. Установка сервера реестра................................................................................... 606
22.2. Использование JAXR API для получения доступа к серверу реестра...............607
22.3. Использование клиентского сценария командной строки с сервером
реестра.............................................................................................................................608
22.3.1. Получение аутентификации......................................................................... 609
22.3.2. Сохранение бизнеса......................................................................................610
22.3.3. Поиск бизнеса................................................................................................ 610
22.3.4. Получение деталей бизнеса.........................................................................611
22.3.5. Удаление бизнеса......................................................................................... 611
22.3.6. Проверка UDDI-сообщений...........................................................................612
22.3.7. Извлечение бизнеса пользователя..............................................................612
22.3.8. Отправка сообщений UDDI-запроса............................................................ 613
22.4. Использование утилиты Indri для получения доступа к базе данных сервера
реестра.............................................................................................................................613
22.4.1. Сохранение бизнеса......................................................................................614
22.4.2. Получение деталей бизнеса.........................................................................614
22.4.3. Поиск бизнеса................................................................................................ 614
22.4.4. Удаление бизнеса......................................................................................... 615
22.4.5. Отображение содержимого базы данных....................................................615
22.5. Добавление в реестр новых пользователей........................................................ 615
22.6. Дополнительная информация...............................................................................616

23. Броузер реестра....................................................................................................... 617


23.1. Запуск броузера..................................................................................................... 617
23.2. Опрос реестра........................................................................................................ 618
23.2.1. Запрос по имени............................................................................................ 618
23.2.2. Запрос по классификации.............................................................................619
23.3. Управление данными реестра.............................................................................. 619
23.3.1. Добавление организации.............................................................................. 619
23.3.2. Добавление информации о службах организации..................................... 620
23.3.3. Добавление привязок к заданной службе................................................... 620
23.3.4. Добавление и удаление классификаций..................................................... 621
23.3.5. Подтверждение введенных данных............................................................. 621
23.4. Удаление организации...........................................................................................622
23.5. Завершение работы броузера...............................................................................622
23.6. Использование броузера реестра JAXR совместно с сервером реестра......... 622
23.6.1. Добавление и удаление организаций.......................................................... 622
23.6.2. Опрос реестра............................................................................................... 622

24. Утилита администрирования поставщика (Provider Administration Tool)..............623


25. Схемы кодировок Java............................................................................................. 624

Web-

Rendered by www.RenderX.com
24

25.1. Схемы кодировок Java........................................................................................... 624


25.1.1. US-ASCII.........................................................................................................624
25.1.2. ISO-8859-1......................................................................................................624
25.1.3. UTF-8.............................................................................................................. 624
25.1.4. UTF-16............................................................................................................ 624
25.2. Дополнительная информация...............................................................................624

26. Обзор HTTP...............................................................................................................625


26.1. Запросы HTTP........................................................................................................ 625
26.2. Ответы HTTP.......................................................................................................... 625

Web-

Rendered by www.RenderX.com
Для кого предназначено руководство Стр. 1 из 626

1. Об этом руководстве
1.1. Для кого предназначено руководство
Данное руководство предназначено для програмистов, интересующихся разработкой и
размещением Web-служб и Web-приложений с использованием Java WSDP.

1.2. Как читать это руководство


Данное руководство состоит из пяти частей:
• Введение
Первые пять глав знакомят с основными понятиями и технологиями. Мы рекомендуем
вам прочитать их перед всеми остальными частями руководства. В частности, многие
примеры Java WSDP используют Java-сервлет Tomcat и JSP-контейнер, а глава "Начало
работы с Tomcat" описывает как запустить, остановить и управлять Tomcat.
• Технология Java XML
В этих главах рассматриваются все Java XML API.
- Java API для обработки XML (JAXP)
- Java API для системы обмена сообщениями XML (JAXM) и Soap with Attachments API
для Java (SAAJ)
- Java API для основанных на XML RPC (JAX-RPC)
- Java API для XML Registries (JAXR) и Registry Server - UDDI-совместимый реестр,
доступный через JAXR
• Web-технология
В этих главах рассматриваются технологии, использующиеся при разработке Web-
приложений, ориентированных на представление информации различного типа.
- Сервлеты Java
- JavaServer Pages
- Пользовательские теги и JSP Standard Tag Library (JSTL)
• Реальный пример
Глава "Приложение Coffee Break" в этом разделе описывает приложение, использующее
большинство из API, рассмотренных в данном руководстве.
• Приложения
В приложениях рассмотрены программные средства, поставляемые вместе с Java
WSDP.
- Программа администрирования сервера Tomcat
- Менеджер Web-приложений Tomcat
- JAXM Provider Admin
- xprcc

Web-

Rendered by www.RenderX.com
Стр. 2 из 626 Об этом руководстве

- Registry Browser
В этой части приведены также схемы кодирования HTTP и Java.

1.3. О примерах
1.3.1. Предварительные требования
Для понимания примеров вы должны хорошо знать язык программирования Java, SQL и
основы реляционных баз данных. Темы, перечисленные в таблице P-1 "Руководства по
Java™" особенно важны:
Таблица P-1. Важные темы в "Руководстве по Java™"
Тема Web-страница
JDBC™ http://java.sun.com/docs/books/tutorial/jdbc
Потоки http://java.sun.com/docs/books/tutorial/essential/threads
JavaBeans™ http://java.sun.com/docs/books/tutorial/javabeans
Безопасность http://java.sun.com/docs/books/tutorial/security1.2

1.3.2. Запуск примеров


В этом разделе рассматривается все, что нужно знать для получения, построения, установки
и запуска примеров.

1.3.2.1. Требуемое программное обеспечение


Если вы просматриваете данное описание в интернете, вам необходимо загрузить
"Руководство по Web-службам Java™" с
http://java.sun.com/webservices/downloads/webservicestutorial.html
После установки пакета с руководством исходные коды примеров находятся в каталоге
JWSDP_HOME/docs/tutorial/examples, в котором расположены подкаталоги для каждой из
технологий, включенных в пакет.
Это руководство описывает Java WSDP 1.0_01. Для построения, размещения и запуска
примеров вам необходимы копии Java WSDP и Java™ 2 Platform, Standard Edition (J2SE™)
SDK 1.3.1 или 1.4. Java WSDP можно загрузить с
http://java.sun.com/webservices/downloads/webservicespack.html
J2SE 1.3.1 SDK можно загрузить с
http://java.sun.com/j2se/1.3/
J2SE 1.4 SDK можно загрузить с
http://java.sun.com/j2se/1.4/
Добавьте каталоги bin установленных Java WSDP и J2SE SDK в начало переменной
окружения PATH, для того чтобы начальные сценарии Java WSDP для Tomcat, Ant,
deploytool, сервера реестра и других программ заменили сценарии из других инсталляций.

Web-

Rendered by www.RenderX.com
Как распечатать это руководство Стр. 3 из 626

1.3.2.2. Построение примеров


Большинство из примеров поставляются с файлом конфигурации для Ant версии 1.4.1,
переносимого средства построения, входящего в Java WSDP. В каждой главе приводятся
рекомендации для построения примеров.

1.3.2.3. Настройка примеров


Большинство из примеров Java WSDP используют Java-сервлет Tomcat и JSP-контейнер.
Для установки, регистрации, перезагрузки и удаления Web-приложений используется
программа manager. Информация по этой программе находится в Приложении В.

1.4. Как распечатать это руководство


Для распечатки данного руководства выполните следующие действия:
1. Убедитесь, что у вас установлен Adobe Acrobat Reader.
2. Откройте PDF-версию этой книги.
3. Щелкните на иконке с принтером в Adobe Acrobat Reader.

1.5. Типографские соглашения


В Таблице Р-2 перечислены типографские соглашения, принятые в данном руководстве.
Таблица Р-2 Типографские соглашения
Стиль шрифта Использование
жирный Выделения, заголовки, первое упоминание терминов
URL, исходные коды примеров, имена файлов, названия
команд, ключевые слова языка программирования
моноширинный Различные имена файлов

Выбор меню обозначается при помощи символа правой стрелки ->, например, First->Second
следует интерпретировать следующим образом: выберите меню First, затем подменю
Second из меню First.

2. Введение в Web-службы
Web-службы, в общем смысле, представляют собой службы, предлагаемые в Web. В
традиционном сценарии Web-служб бизнес-приложение передает запрос в службу по
определенному URL, используя протокол SOAP по HTTP. Служба принимает запрос,
обрабатывает его и возвращает ответ. Часто приводимым примером Web-службы является
служба котировок акций, в которой запрашивается текущая цена определенной акции.
Цена возвращается в ответе. Это одна из простейших форм Web-службы, в которой ответ
формируется практически сразу, запрос и ответ являются частями одного и того же вызова
метода.
Другим примером может являться служба, определяющая эффективный маршрут доставки
товаров. В этом случае передается запрос, указывающий пункт назначения, а процессы
службы определяют наиболее эффективный маршрут доставки. Время передачи ответа

Web-

Rendered by www.RenderX.com
Стр. 4 из 626 Введение в Web-службы

зависит от сложности маршрутов, то есть ответ может быть передан в виде отдельной от
запроса операции.
Web-службы и потребители Web-служб обычно являются бизнес-процессами,
предназначенными преимущественно для так называемых business-to-business (B-to-B)
транзакций. Предприятие может как предоставлять Web-службы, так и являться
потребителем других Web-служб. Например, оптовый торговец специями может выступать
в роли потребителя при использовании Web-службы для проверки доступности ванильных
зерен, а также в роли поставщика Web-службы во время предложения предполагаемым
потребителям различных цен на зерна от разных поставщиков.

2.1. Значение XML и платформы Java™


Web-службы зависят от способности участников взаимодействовать друг с другом даже
при использовании ими различных информационных систем. XML (Extensible Markup
Language), язык разметки, делающий данные переносимыми, является ключевой
технологией, предназначенной для обеспечения такой способности. Корпоративные
пользователи увидели преимущества использования XML для интеграции данных как для
совместного использования существующих данных между подразделениями, так и для
использования их совместно с другими предприятиями. В результате XML все больше
используется в корпоративных интегрированных приложениях, как в сильносвязанных, так
и в слабосвязанных системах. Из-за такой способности к интеграции данных XML стал
фундаментом Web-вычислений.
Web-службы зависят также от способности предприятий использовать различные
вычислительные платформы для взаимодействия друг с другом. Это требование делает
платформу Java™, программы в которой являются переносимыми, естественным выбором
для разработки Web-служб. Этот выбор стал еще более привлекательным после появления
новых Java API для XML, что сделало более легким использование XML в языке
программирования Java. Общая информация по этим API приводится в этом введении
далее, а более детально каждый API рассматривается в соответствующем руководстве.
Кроме переносимости кода и данных Web-службы должны быть масштабируемыми,
безопасными и эффективными. Java™ 2 Platform, Enterprise Edition (J2EE™), специально
разработана для удовлетворения этих потребностей. Она облегчает в действительности
трудную часть разработки Web-служб, а именно программирование инфраструктуры. Эта
инфраструктура включает в себя такие функции как безопасность, управление
распределенными транзакциями и управление пулом соединений, которые имеют
важнейшее значение для производительности Web-служб. А поскольку компоненты
являются повторно используемыми, время разработки существенно уменьшается.
Поскольку XML и платформа Java хорошо работают вместе, они стали играть центральную
роль в Web-службах. Фактически, преимущества, предлагаемые Java API для XML и
платформой J2EE, делают их идеальной комбинацией для размещения Web-служб.
Описанные в этом руководстве API дополняют и располагаются на верхнем уровне J2EE
API. Эти API дают возможность сообществу Java, разработчикам и поставщикам сервисных
программ и контейнеров начать разработку приложений Web-служб и использование
стандартных Java API, составляющих основу принципа технологии Java - Write Once, Run
Anywhere™ (пишется один раз, используется везде). Java Web Services Developer Pack
(Java WSDP) собирает все эти API в один пакет. В Java WSDP входят jar-файлы,

Web-

Rendered by www.RenderX.com
Что такое XML? Стр. 5 из 626

реализующие эти API, а также документация и примеры. Примеры в Java WSDP должны
запускаться в контейнере Tomcat (входящим в пакет Java WSDP), или в J2EE-контейнере
после установки jar-файлов Java WSDP в J2EE SDK. Рекомендации по установка jar-файлов
в J2EE SDK находятся в документации по Java WSDP в каталоге
<JWSDP_HOME>/docs/jwsdponj2ee.html.
В оставшейся части введения дается краткий обзор XML и объясняется, каким образом
эта технология обеспечивает переносимость данных. Затем приводится обзор Java API
для XML, поясняющий, что делают эти API и то, как они облегчают создание Web-
приложений. Каждый API описывается отдельно и затем приводится сценарий, который
демонстрирует их совместную работу.
Последующие руководства дают более детальную информацию и постепенно учат вас
использованию Java API для XML при создании приложений для Web-служб. В них
приводятся также примеры приложений, которые вы можете выполнять.

2.2. Что такое XML?


В этой главе приведено краткое введение в XML и объясняется, каким образом эта
технология обеспечивает переносимость данных. Другими словами, здесь дается основа
для чтения приведенных ниже описаний Java API для XML. В главе 1 дано более полное
и детальное описание XML и методов работы с ним.
XML является отраслевым стандартом системно-независимого способа представления
данных. Подобно HTML (HyperText Markup Language) XML заключает данные в теги, но
между этими двумя языками разметки есть существенные различия. Во-первых, XML-теги
связаны со значением заключенного в них текста, в то время как HTML-теги указывают
лишь способ отображения текста. Следующий пример XML представляет собой прайс-
лист с названием и ценой двух сортов кофе.

<priceList>
<coffee>
<name>Mocha Java</name>
<price>11.95</price>
</coffee>
<coffee>
<name>Sumatra</name>
<price>12.50</price>
</coffee>
</priceList>
Теги <coffee> и </coffee> указывают синтаксическому анализатору, что информация между
ними относится к кофе. Два других тега внутри тегов <coffee> указывают, что заключенная
в них информация является названием кофе и его ценой. Поскольку XML-теги указывают
на структуру и содержимое данных, они дают возможность производить такие операции,
как архивирование и поиск.
Вторым основным отличием между XML и HTML является расширяемость XML. При помощи
XML вы можете определять свои собственные теги для описания содержимого конкретного

Web-

Rendered by www.RenderX.com
Стр. 6 из 626 Введение в Web-службы

типа документа. При использовании HTML вы ограничены только теми тегами, которые
определены в спецификации HTML. Вторым аспектом расширяемости XML является
возможность создания файла, называемого схемой и описывающего структуру конкретного
типа XML-документа. Например, можно написать схему для прайс-листа, указывающую,
какие теги могут быть использованы и где они могут находиться. Любой XML-документ
следующий ограничениям, установленным в схеме, считается соответствующим этой
схеме.
Наверное наиболее широко используемый язык схем - это Document Type Definition (DTD),
поскольку он является одной из составных частей спецификации XML 1.0. Написанная на
этом языке схема обычно называется DTD. Приведенная ниже DTD определяет теги,
используемые в XML-документе - прайс-лист. В ней определяются четыре тега (элемента)
и указывается, какие теги могут (или должны) использоваться внутри других тегов. В DTD
также определяется иерархическая структура XML-документа, в том числе и порядок
написания тегов.

<!ELEMENT priceList (coffee)+>


<!ELEMENT coffee (name, price) >
<!ELEMENT name (#PCDATA) >
<!ELEMENT price (#PCDATA) >
В первой строке примера определяется элемент верхнего уровня - priceList, это значит,
что все остальные теги в документе будут находиться между тегами <priceList> и </priceList>.
Первая строка указывает также на то, что элемент priceList должен содержать один или
более элементов coffee (это указано знаком плюс). Во второй строке указывается, что все
элементы coffee должны содержать элемент name и элемент price в данном порядке. В
третьей и четвертой строке определяется, что данные между тегами <name> и </name> и
между <price> и </price> представляют собой символьные данные. Имя и цена каждого
сорта кофе представляют собой конкретный текст, формирующий прайс-лист.
Другим популярным языком схем является XML Schema, разработанный консорциумом
World Wide Web (W3C). Язык XML Schema значительно мощнее DTD и после его принятия
в W3C Recomendation в мае 2001 года его использование и количество реализаций стало
увеличиваться. Сообщество разработчиков, использующих платформу Java, заметило эту
тенденцию и группа экспертов по Java™ API for XML Processing (JAXP) начала работу над
добавлением поддержки XML Schema в спецификацию JAXP 1.2. Текущая реализация
Java™ Web Services developer Pack включает поддержку XML Schema.

2.2.1. Что обеспечивает переносимость XML?


Переносимость XML-данных обеспечивает схема. DTD priceList, рассмотренная ранее,
является простым примером схемы. Когда приложению передается документ priceList в
формате XML и имеется DTD priceList, оно может обработать документ в соответствии с
правилами, указанными в DTD. Например, имея DTD priceList, синтаксический анализатор
знает структуру и тип содержимого любого XML-документа, сформированного согласно
этой DTD. Если синтаксический анализатор (парсер) является верифицирующим, он знает,
что документ не корректен, если документ содержит не включенные в DTD элементы,
например, элемент <tea>, или если элементы не расположены в определенном порядке,
например, элемент price предшествует элементу name.

Web-

Rendered by www.RenderX.com
Обзор Java API для XML Стр. 7 из 626

Другие особенности делают свой вклад в популярность XML как метода обмена данными.
Одна из них - текстовый формат данных, который легко читается и человеком и текстовыми
редакторами. Приложения могут анализировать и обрабатывать XML-документы, а при
возникновении ошибок при обработке человек может сам прочитать их. Другая особенность
- поскольку XML-документы не содержат инструкций форматирования, они могут быть
отображены различными способами. Хранение данных отдельно от инструкций
форматирования означает, что одни и те же данные могут быть представлены на различных
носителях информации.
XML обеспечивает переносимость документов, но он не может работать в вакууме, то есть,
использующие XML стороны должны согласиться с определенными условиями. Например,
вместе с договоренностью об использовании XML для обмена информацией, два
приложения должны согласовать набор используемых элементов и их значения. При
использовании ими Web-служб, необходимо также согласовать, какие методы Web-служб
будут использоваться, что эти методы будут делать и порядок их вызова в случае
необходимости вызова более одного метода.
Для удовлетворения этих требований предприятиям доступны несколько технологий. Они
могут использовать схемы DTD и XML Schema для описания корректных терминов и XML-
документов, которые будут использоваться при взаимодействии. Средство описания Web-
служб и их методов предоставляют реестры. На более высоком уровне абстракции
предприятия могут использовать партнерские соглашения и схемы рабочих процессов.
Более подробно схемы и реестры рассмотрены ниже.

2.3. Обзор Java API для XML


Java API для XML дают вам возможность писать Web-приложения полностью на языке
программирования Java. Они подразделяются на две большие категории: связанные
непосредственно с обработкой XML-документов и связанные с процедурами.
• Документо-ориентированные
- Java™ API for XML Processing (JAXP) - обрабатывает XML-документы, используя
различные синтаксические анализаторы
• Процедурно-ориентированные
- Java™ API for XML-based RPC (JAX-RPV) - передает вызовы SOAP-методов
удаленным участникам по сети Интернет и получает ответы.
- Java™ API for XML Messaging (JAXM) - передает SOAP-сообщения по сети Интернет
стандартным способом
- Java™ API for XML Registries (JAXR) - предоставляет стандартный способ доступа
к бизнес-реестрам и общей информации
Возможно, самой важной особенностью Java API для XML является поддержка ими
отраслевых стандартов, что гарантирует возможность взаимодействия. Различные группы
по стандартам сетевого взаимодействия, такие как World Wide Web Consortium (W3C) и
Organization for the Advancement of Structured Information Standards (OASIS) (организация
по улучшению стандартов структурированной информации), определили стандартные
способы работы, следуя которым можно организовать совместную работу приложений и
данных.

Web-

Rendered by www.RenderX.com
Стр. 8 из 626 Введение в Web-службы

Еще одной особенностью Java API для XML является их большая гибкость. Для
пользователей - это способы использования API. Например, JAXR-код может использовать
различные средства для обработки XML-документа, а JAXM-код может использовать
различные протоколы обмена сообщениями на верхнем уровне SOAP. Разработчикам
также доступна эта гибкость. Java API для XML определяют жесткие требования по
совместимости для гарантии того, что все реализации предоставляют стандартную
функциональность, но эти API, в то же время, дают разработчикам большую степень
свободы в предоставлении реализаций, адаптированных для конкретных задач.
В следующих главах обсуждаются каждый из этих API.

2.4. JAXP
Java API for XML Processing (JAXP) упрощает обработку XML-данных в приложениях,
написанных на языке программирования Java. JAXP использует особенности стандартов
SAX (Simple API for XML Parsing) и DOM (Document Object Model), то есть вы можете выбрать
между синтаксическим анализом ваших данных как потока событий, или построить
древовидную структуру их представления. Последние версии JAXP поддерживают также
стандарт CSLT (XML Stylesheet Language Transformations), который предоставляет контроль
над способом представления данных и дает возможность преобразования данных в другие
XML-документы или в другие форматы, например HTML. JAXP обеспечивает также
поддержку пространства имен, разрешая работу со схемами, которые могли бы в противном
случае вызвать конфликт имен.
JAXP дает возможность использовать любой XML-совместимый анализатор в вашем
приложении. Это обеспечивается так называемым уровнем подключаемых модулей,
который позволяет вам подключить реализацию SAX или DOM API. Этот уровень позволяет
также подключить XSL-процессор, который может преобразовывать ваши XML-данные
различными способами, в том числе и их внешнее представление.
Последней версией JAXP является JAXP 1.2, в которую добавлена поддержка XML Schema.
Начальная версия JAXP 1.2 включена в данную редакцию Java WSDP и доступна также в
пакете Java XML Pack.

2.4.1. SAX API


Simple API for XML (SAX) определяет API для анализатора, основанного на событиях.
Анализатор такого типа читает XML-документ от начала до конца и каждый раз, когда он
распознает синтаксическую конструкцию, он уведомляет приложение, которое его
использует. SAX-анализатор уведомляет приложение при помощи вызова методов
интерфейса ContentHandler. Например, когда анализатор встречает знак "меньше чем"
(<), он вызывает метод startElement; когда он встречает символьные данные, он вызывает
метод characters; когда встречается знак "меньше чем" со знаком деления (</), вызывается
метод endElement и т.д. Для иллюстрации вышесказанного рассмотрим часть примера
XML-документа из предыдущей главы и опишем, что делает анализатор для каждой строки.
(Для простоты вызовы метода ignorableWhiteSpace не показаны.)

<priceList> [ startElement]
<coffee> [ startElement]

Web-

Rendered by www.RenderX.com
JAXP Стр. 9 из 626

<name>Mocha Java</name> [
startElement,
characters, endElement]
<price>11.95</price> [
startElement,
characters, endElement]
</coffee> [ endElement]
Реализации вызываемых анализатором методов по умолчанию не делают ничего, то есть
вы должны написать подкласс, реализующий соответствующие методы для получения
желаемой функциональности. Например, предположим, что вам нужно получить цену
фунта кофе сорта Mocha Java. Вы должны написать класс, расширяющий DefaultHandler
(реализация ContentHandler по умолчанию), в котором нужно написать ваши собственные
реализации методов startElement и characters.
Прежде всего, необходимо создать объект SAXParser из объекта SAXParserFactory. Вы
будете вызывать его метод parse, передавая прайс-лист и экземпляр вашего нового класса
(с новыми реализациями методов startElement и characters). В данном примере прайс-лист
- это файл, хотя метод parse может также принимать и другие источники входных данных,
включая объект InputStream, URL и объект InputSource.

SAXParserFactory factory = SAXParserFactory.newInstance();


SAXParser saxParser = factory.newSAXParser();
saxParser.parse("priceList.xml", handler);
Результат вызова метода parse зависит прежде всего от того, как реализованы методы в
handler. SAX-анализатор проходит по файлу priceList.xml строка за строкой, вызывая
соответствующие методы. Кроме упомянутых выше методов анализатор вызывает и другие
методы, такие как startDocument, endDocument, ignorableWhiteSpace и procesingInstructions,
но эти методы имеют реализации по умолчанию и ничего не делают.
Приведенные ниже определения методов показывают один из способов реализации
методов characters и startElement для поиска и отображения цены сорта Mocha Java. Из-
за особенностей работы SAX-анализатора эти два метода работают совместно,
осуществляя поиск элемента name, символов "Mocha Java" и элемента price, следующего
сразу после Mocha Java. Эти методы используют три флага для отслеживания того, какие
условия были обнаружены. Обратите внимание, что SAX-анализатор должен будет вызвать
оба метода более одного раза, прежде чем можно будет распечатать цену.

public void startElement(..., String elementName, ...){


if(elementName.equals("name")){
inName = true;
} else if(elementName.equals("price") && inMochaJava ){
inPrice = true;
inName = false;
}

Web-

Rendered by www.RenderX.com
Стр. 10 из 626 Введение в Web-службы

public void characters(char [] buf, int offset, int len) {


String s = new String(buf, offset, len);
if (inName && s.equals("Mocha Java")) {
inMochaJava = true;
inName = false;
} else if (inPrice) {
System.out.println("The price of Mocha Java is: " + s);
inMochaJava = false;
inPrice = false;
}
}
}
При обнаружении анализатором элемента coffee Mocha Java приведенные ниже основные
состояния будут возникать после вызова следующих методов:

startElement -- inName true


characters -- inMochaJava true

startElement -- inPrice true


characters -
SAX-анализатор может выполнять контроль во время анализа XML-данных, что означает
проверку соответствия данных правилам, указанным в схеме XML-документа. SAX-
анализатор будет осуществлять верификацию, если он создан объектом SAXParserFactory,
в котором такая верификация включен. Включение верификации для объекта SAXParser-
Factory factory производится следующим образом:

factory.setValidating(true);
Для того чтобы анализатор знал, какую схему использовать для верификации, XML-документ
должен содержать ссылку на схему в своем объявлении DOCTYPE. Схемой для прайс-
листа является priceList.DTD, то есть, объявление DOCTYPE должно быть примерно таким:

<!DOCTYPE PriceList SYSTEM "priceList.DTD">

2.4.2. DOM API


Document Object Model (DOM), разработанная рабочей группой W3C DOM, представляет
собой набор интерфейсов для построения представления объектов анализируемого XML-
документа в древовидной форме. После построения DOM вы можете манипулировать
документом при помощи методов DOM, например, insert и remove, также как и любым
другим объектом в древовидной структуре данных. Таким образом, в отличие от SAX-

Web-

Rendered by www.RenderX.com
JAXP Стр. 11 из 626

анализатора, DOM-анализатор предоставляет произвольный доступ к отдельным данным


в XML-документе. Еще одним отличием является то, что используя SAX-анализатор можно
только читать XML-документ, а при помощи DOM-анализатора можно сформировать
объектное представление документа и манипулировать им в памяти, добавляя новый
элемент или удаляя существующий.
В предыдущем примере мы использовали SAX-анализатор для поиска только одного
фрагмента данных в документе. Использование DOM-анализатора требует наличия полной
объектной модели документа в памяти, что обычно менее эффективно для поиска
небольшого количества элементов, особенно при большом размере документа. В
следующем примере мы добавим новый сорт кофе, используя DOM-анализатор. SAX-
анализатор использовать нельзя, так как он только читает данные.
Предположим, что вы хотите добавить кофе Kona в прайс-лист. Вы должны прочитать
XML-файл прайс-листа в DOM и затем вставить новый элемент coffee вместе с названием
и ценой. В следующем фрагменте кода создается объект DocumentBuilderFactory, который
затем используется для создания объекта DocumentBuilder builder. Затем вызывается
метод builder parse, которому передается файл priceList.xml.

DocumentBuilderFactory factory =
DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse("priceList.xml");
С этого момента document - это DOM-представление прайс-листа, расположенное в памяти.
В следующем фрагменте кода в прайс-лист добавляется новый сорт кофе (с названием
"Kona" и ценой 13.50). Поскольку мы хотим вставить новый сорт кофе перед сортом "Mocha
Java", первым шагом является получение списка элементов coffee и итерация по списку
для поиска "Mocha Java". При помощи интерфейса Node, включенного в пакет org.w3c.dom,
создается объект Node для нового элемента coffee и новые объекты для элементов name
и price. Элементы name и price содержат символьные данные, поэтому создаются объекты
Text для каждого из них, которые добавляются к объектам Node, представляющим элементы
name и price.

Node rootNode = document.getDocumentElement();


NodeList list = document.getElementsByTagName("coffee");

// .
for (int i=0; i < list.getLength(); i++) {
thisCoffeeNode = list.item(i);
Node thisNameNode = thisCoffeeNode.getFirstChild();
if (thisNameNode == null) continue;
if (thisNameNode.getFirstChild() == null) continue;
if (! thisNameNode.getFirstChild() instanceof
org.w3c.dom.Text) continue;

Web-

Rendered by www.RenderX.com
Стр. 12 из 626 Введение в Web-службы

String data = thisNameNode.getFirstChild().getNodeValue();


if (! data.equals("Mocha Java")) continue;

// Mocha Java.

// .

Node newCoffeeNode = document.createElement("coffee");

Node newNameNode = document.createElement("name");


Text tnNode = document.createTextNode("Kona");
newNameNode.appendChild(tnNode);

Node newPriceNode = document.createElement("price");


Text tpNode = document.createTextNode("13.50");
newPriceNode.appendChild(tpNode);

newCoffeeNode.appendChild(newNameNode);
newCoffeeNode.appendChild(newPriceNode);
rootNode.insertBefore(newCoffeeNode, thisCoffeeNode);
break;
}
Обратите внимание, что данный фрагмент кода является упрощенным, поскольку мы
предположили, что не встретим таких элементов как комментарий, атрибут или незначащий
пробел. Информация по более надежному использованию DOM-анализатора находится
в разделе "Повышение сложности".
Можно указать DOM-анализатору проверять документ, точно так же как и SAX-анализатору.
Перед созданием вашего DOM-анализатора нужно вызвать setValidating(true) и проверить,
что анализируемый XML-документ имеет ссылку на свою схему в объявлении DOCTYPE.

2.4.2.1. Пространства имен XML


Все имена в схеме, включая и имена в DTD, являются уникальными, что исключает
двусмысленность. Однако, если какой-либо XML-документ ссылается на несколько схем,
существует вероятность того, что две или более из этих схем содержат одно и то же имя.
Следовательно, документ должен указать пространство имен для каждой схемы, чтобы
анализатор знал, какое определение использовать во время анализа экземпляра конкретной
схемы.
Существует стандартная система обозначений для объявления пространства имен XML,
которая обычно располагается в корневом элементе XML-документа. В следующем
объявлении пространства имен обозначение xmlns указывает, что nsName объявляет
пространство имен, а nsName указывает URL фактического пространства имен:

Web-

Rendered by www.RenderX.com
JAXP Стр. 13 из 626

<priceList xmlns:nsName="myDTD.dtd"
xmlns:otherNsName="myOtherDTD.dtd">
...
</priceList>
Внутри документа можно указать, к какому пространству имен принадлежит элемент,
например:

<nsName:price> ...
Для того чтобы ваш SAX или DOM-анализатор был способен распознавать пространство
имен, нужно вызвать метод setNamespaceAware(true) вашего экземпляра ParserFactory.
После вызова этого метода любой создаваемый анализатор будет правильно обрабатывать
пространства имен.

2.4.3. XSLT API


XML Stylesheet Language for Transformations (XSLT), разработанный рабочей группой W3C
XSL, определяет язык для преобразования XML-документов в другие XML-документы или
в другие форматы. Для выполнения преобразования необходимо предоставить таблицу
стилей, которая записывается на языке XML Stylesheet Language (XSL). Таблица стилей
XSL определяет форму отображения XML-данных, а XSLT использует инструкции
форматирования таблицы стилей для выполнения преобразования.
Поддержка языка XSLT в JAXP реализована в пакете javax.xml.transform, который дает
возможность подключить XSLT-преобразователь для выполнения преобразования.
Субпакеты содержат SAX-, DOM- и потоковые API, которые позволяют выполнять
преобразования непосредственно из дерева DOM и событий SAX. Два приведенных ниже
примера демонстрируют создание XML-документа из дерева DOM и преобразование
полученного XML-документа в формат HTML при помощи таблицы стилей XSL.

2.4.3.1. Преобразование дерева DOM в XML-документ


Для преобразования созданного в предыдущей главе дерева DOM в XML-документ в
приведенном ниже фрагменте программы сначала создается объект Transformer, который
затем выполнит преобразование.

TransformerFactory transFactory =
TransformerFactory.newInstance();
Transformer transformer = transFactory.newTransformer();
Используя корневой узел дерева DOM, следующая строка кода создает объект DOMSource,
являющийся источником преобразования.

DOMSource source = new DOMSource(document);


Следующий фрагмент программы создает объект StreamResult для получения результатов
преобразования и преобразует дерево в XML-файл.

Web-

Rendered by www.RenderX.com
Стр. 14 из 626 Введение в Web-службы

File newXML = new File("newXML.xml");


FileOutputStream os = new FileOutputStream(newXML);
StreamResult result = new StreamResult(os);
transformer.transform(source, result);

2.4.3.2. Преобразование XML-документа в HTML-документ


XSLT можно также использовать для преобразования нового XML-документа, newXML.xml,
в HTML, используя таблицу стилей. При создании таблицы стилей используйте пространство
имен XML для ссылки на XML-компоненты. Например, каждая таблица стилей содержит
корневой элемент, идентифицирующий язык таблицы стилей, как показано в следующей
строке кода.

<xsl:stylesheet version="1.0" xmlns:xsl=


"http://www.w3.org/1999/XSL/Transform">
При ссылке на конкретный компонент языка таблицы стилей используется префикс
пространства имен, после которого помещается двоеточие и название конкретного
компонента. Например, следующий фрагмент таблицы стилей указывает, что данные name
должны быть вставлены в строку HTML-таблицы.

<xsl:template match="name">
<tr><td>
<xsl:apply-templates/>
</td></tr>
</xsl:template>
В следующей таблице стилей указывается, что XML-данные преобразуются в HTML и
записи о сортах кофе вставляются в строку таблицы.

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="priceList">
<html><head>Coffee Prices</head>
<body>
<table>
<xsl:apply-templates />
</table>
</body>
</html>
</xsl:template>
<xsl:template match="name">
<tr><td>

Web-

Rendered by www.RenderX.com
JAX-RPC Стр. 15 из 626

<xsl:apply-templates />
</td></tr>
</xsl:template>
<xsl:template match="price">
<tr><td>
<xsl:apply-templates />
</td></tr>
</xsl:template>
</xsl:stylesheet>
Для выполнения преобразования необходимо получить XSLT-преобразователь и
использовать его для применения таблицы стилей к XML-данным. В следующем фрагменте
программы создается преобразователь путем создания экземпляра объекта Transformer-
Factory, чтения таблицы стилей и XML-файлов, создания файла для записи HTML и,
наконец, получения объекта Transformer transformer из объекта TransformerFactory tFactory.

TransformerFactory tFactory =
TransformerFactory.newInstance();
String stylesheet = "prices.xsl";
String sourceId = "newXML.xml";
File pricesHTML = new File("pricesHTML.html");
FileOutputStream os = new FileOutputStream(pricesHTML);
Transformer transformer =
tFactory.newTransformer(new StreamSource(stylesheet));
Преобразование завершается вызовом метода transform, в который передаются данные
и выходной поток.

transformer.transform(
new StreamSource(sourceId), new StreamResult(os));

2.5. JAX-RPC
Java API for XML-based RPC (JAX-RPC) - это Java API для разработки и использования
Web-служб.

2.5.1. Обзор JAX-RPC


Web-службы, основанные на RPC, представляют собой набор процедур, которые могут
быть вызваны удаленным клиентом по сети Интернет. Обычным примером Web-службы,
основанной на RPC, является служба биржевых котировок, которая принимает SOAP
(Simple Object Access Protocol) запрос о стоимости указанной акции и возвращает стоимость,
используя SOAP.
Примечание. Спецификация SOAP 1.1, доступная на http://www.w3.org/, определяет среду
для обмена XML-документами. В ней определяется, среди прочего, что необходимо и что

Web-

Rendered by www.RenderX.com
Стр. 16 из 626 Введение в Web-службы

не обязательно в SOAP-сообщении и как данные могут кодироваться и передаваться. JAX-


RPC и JAXM основаны на SOAP.
Web-служба, являющаяся серверным приложением, которое реализует доступные для
вызова клиентами процедуры, размещается в контейнере на стороне сервера. Контейнер
может быть контейнером сервлетов, таким как Tomcat, или контейнером Java™ 2 Platform,
Enterprise Edition(J2EE™), который основывается на технологии Enterprise JavaBeans™
(EJB™).
Web-служба может стать доступной для потенциальных клиентов после описания ее в
документе Web Services Description Language (WSDL). WSDL-описание представляет собой
XML-документ, в котором находится вся необходимая информация о Web-службе,
включающая ее название, операции, которые можно вызывать, параметры этих операций
и место, куда надо передавать запросы. Пользователь (Web-клиент) может использовать
WSDL-документ для изучения того, что предлагает служба и как получить к ней доступ.
Ниже рассматриваются способы использования разработчиком WSDL-документа для
создания Web-службы.

2.5.1.1. Способность к взаимодействию


Наверное, наиболее важным требованием к Web-службе является способность к
взаимодействию между клиентами и серверами. При помощи JAX-RPC клиентское
приложение, написанное на языке, отличном от Java, может получить доступ к Web-службе,
разработанной и размещенной на платформе Java. И наоборот, клиентское приложение,
написанное на языке программирования Java, может взаимодействовать со службой,
разработанной и размещенной при использовании какой-то другой платформы.
Наличие в JAX-RPC поддержки SOAP и WSDL обеспечивает его способность к
взаимодействию. SOAP определяет стандарты для системы обмена сообщениями XML и
для отображения типов данных, при использовании которых приложения могут
взаимодействовать друг с другом. JAX-RPC придерживается стандартов SOAP и,
фактически, основан на системе обмена сообщениями SOAP. То есть, удаленный вызов
процедур JAX-RPC реализован в виде SOAP-сообщений типа запрос-ответ.
Наличие в JAX-RPC поддержки WSDL также обеспечивает его способность к
взаимодействию. WSDL-описание, будучи XML-документом, описывающим Web-службу
стандартным способом, обеспечивает переносимость описания. WSDL-документы и их
использование будут рассмотрены немного позже.

2.5.1.2. Простота использования


Тот факт, что JAX-RPC основан на механизме удаленного вызова процедур (RPC), означает
выдающуюся дружественность для разработчика. RPC имеет сложную инфраструктуру,
но, к счастью, JAX-RPC делает детали внутренней реализации незаметными ни для клиента,
ни для разработчика службы. Например, клиент Web-служб просто вызывает Java-метод,
а маршализация, демаршализация и передача происходят автоматически. На стороне
сервера Web-служба просто реализует предлагаемые клиентам службы и, также как клиент,
не беспокоится о механизмах внутренней реализации.
В основном благодаря простоте использования JAX-RPC является основным API Web-
служб как для клиентских, так и для серверных приложений. JAX-RPC использует главным
образом систему обмена сообщениями SOAP типа точка-точка, основной механизм,
используемый большинством клиентов и Web-служб. JAX-RPC концентрирует внимание

Web-

Rendered by www.RenderX.com
JAX-RPC Стр. 17 из 626

на простоте использования для наиболее общих задач, хотя он может предоставить


асинхронный обмен сообщениями и может быть расширен для обеспечения поддержки
высококачественного обмена. То есть, JAX-RPC - это хороший выбор для тех приложений,
в которых желательно избежать более сложных аспектов системы обмена сообщениями
SOAP, а также для тех, для которых хорошо подходит модель взаимодействия RPC. Более
мощная альтернатива системе обмена сообщениями SOAP - Java™™ API for XML
Messaging (JAXM) - рассматривается ниже.

2.5.1.3. Дополнительные функции


Хотя JAX-RPC основывается на модели RPC, он предлагает функции, выходящие за рамки
RPC. Одной из них является возможность передачи документа полностью, а также его
фрагментов. Кроме того, JAX-RPC поддерживает обработчики системы обмена
сообщениями SOAP, что предоставляет возможность передавать большое разнообразие
типов сообщений. JAX-RPC может быть расширен для одностороннего обмена сообщениями
в дополнение к способу запрос-ответ, обычно используемому в RPC. Еще одной
дополнительной функцией является гибкое отображение типов, что дает JAX-RPC еще
большую универсальность.

2.5.2. Использование JAX-RPC


Рассмотрим стандартный случай. Пользователь может, например, нуждаться в заказе
частей и товаров. Можно самыми разными методами обнаружить их потенциальные
источники, но удобным способом является поиск в бизнес-реестре и службе архивов, таких
как реестр Universal Description, Discovery and Integration (UDDI). Обратите внимание, что
Java API for XML Registries (JAXR), рассматриваемый далее, предлагает легкий метод
поиска Web-служб в бизнес-реестре и архиве. Web-службы обычно регистрируют себя в
бизнес-реестре и сохраняют связанные документы, включая WSDL-описания, в его архиве.
После поиска в бизнес-реестре потенциального источников можно получить несколько
WSDL-документов, по одному для каждой Web-службы, удовлетворяющей критерию поиска.
Бизнес-клиент может использовать WSDL-документы для просмотра того, что предлагают
службы и как с ними связаться.
Еще одной важной областью применения WSDL-документа является его использование
в качестве основы для создания заглушек - низкоуровневых классов, необходимых клиентам
для взаимодействия с удаленной службой. В JAX-RPC RI (reference implementation)
программа, использующая WSDL-документ для генерации заглушек, называется wscompile.
В RI имеется еще одна программа, называемая wsdeploy, которая создает узлы -
низкоуровневые классы, необходимые серверу для взаимодействия с удаленным клиентом.
Заглушки и узлы, следовательно, выполняют аналогичные функции: заглушки - на
клиентском компьютере, а узлы - на сервере. Кроме генерирования заглушек wsdeploy
может использоваться для создания WSDL-документов.
Исполняющая система JAX-RPC, например, входящая в JAX-RPC RI, использует заглушки
и узлы, созданные при помощи wscompile и wsdeploy, неявным образом. Сначала она
преобразует клиентский вызов удаленной процедуры в SOAP-сообщение и передает его
службе в виде HTTP-запроса. На стороне сервера исполняющая система JAX-RPC
принимает запрос, преобразует SOAP-сообщение в вызов метода и вызывает его. После
обработки запроса Web-службой исполняющая система выполняет похожий набор действий
для возврата результата клиенту. Помните, что как бы ни были сложны детали реализации

Web-

Rendered by www.RenderX.com
Стр. 18 из 626 Введение в Web-службы

взаимодействия между клиентом и сервером, они не видимы ни для Web-служб, ни для


их клиентов.

2.5.3. Создание Web-службы


Разработка Web-службы с использованием JAX-RPC является неожиданно простым
процессом. Служба сама по себе - это по существу два файла: интерфейс, объявляющий
удаленные процедуры службы, и класс, реализующий эти процедуры. Кроме этого,
необходимы еще настройка службы и размещение, но, прежде всего, давайте рассмотрим
два главных компонента Web-службы: определение интерфейса и класс реализации.
Следующее определение интерфейса является простым примером, показывающим методы,
которые оптовый продавец кофе возможно бы захотел сделать доступными для своих
вероятных покупателей. Обратите внимание, что интерфейс определения службы
расширяет java.rmi.Remote, а его методы генерируют объект java.rmi.RemoteException.

package coffees;

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface CoffeeOrderIF extends Remote {


public Coffee [] getPriceList()
throws RemoteException;
public String orderCoffee(String coffeeName, int quantity)
throws RemoteException;
}
Метод getPriceList возвращает массив объектов Coffee, каждый из которых содержит поля
name и price. Существует один объект Coffee для каждого сорта кофе, имеющегося в
наличии у продавца. Метод orderCoffee возвращает String, который должен соответствовать
заказу или указывать, что он является возвратом заказа.
В следующем примере показано, как может выглядеть эта реализация (детали реализации
опущены). Вероятно, метод getPriceList будет посылать запрос к базе данных компании
для получения текущей информации и возвращать результат в виде объектов Coffee.
Второй метод, orderCoffee, также должен обращаться к базе данных для проверки наличия
указанного в заказе количества кофе. Если такое количество есть, реализация запустит
внутренний процесс выполнения заказа и пошлет клиенту ответ, подтверждающий принятие
заказа. Если заказанного количества нет, реализация может сформировать свой
собственный заказ на пополнение запасов и проинформирует клиента о том, что его заказ
отложен.

package coffees;

public class CoffeeOrderImpl implements CoffeeOrderIF {


public Coffee [] getPriceList() throws RemoteException; {

Web-

Rendered by www.RenderX.com
JAX-RPC Стр. 19 из 626

. . .
}

public String orderCoffee(String coffeeName, int quantity)


throws RemoteException; {
. . .
}
}
После написания интерфейса службы и класса реализации разработчик должен запустить
программу отображения. Эта программа может использовать интерфейс и его реализацию
в качестве основы для генерации классов stub и tie и, при необходимости, дополнительных
классов. Как было отмечено ранее, разработчик может использовать эту программу также
для создания WSDL-описания для службы.
Последним действием в создании Web-службы является пакетирование и размещение.
Пакетирование определения Web-службы производится при помощи архива приложений
Web (WAR). WAR-файл является JAR-файлом для Web-приложений, то есть, файлом,
содержащим в сжатой форме все необходимые для Web-приложения файлы. Например,
служба CoffeeOrder может быть спакетирована в файле jaxrpccoffees.war, который облегчает
распространение и установку.
Одним из файлов, которые должны быть в каждом WAR-файле, является XML-файл,
называемый дескриптором размещения. Этот файл, называемый условно web.xml, содержит
информацию, необходимую для размещения определения службы. Например, если оно
размещается в контейнере сервлетов, таком как Tomcat, дескриптор размещения будет
включать имя сервлета и описание, класс сервлета, параметры инициализации и другую
информацию для запуска. Одним из файлов, на которые ссылается web.xml, является
файл конфигурации, автоматически генерируемый программой отображения. В данном
примере этот файл назван CoffeeOrder_Config.properties.
Размещение нашего примера Web-службы CoffeeOrder в контейнере Tomcat может быть
завершено простым копированием файла jaxrpc-coffee.war в каталог webapps системы
Tomcat. Размещение в контейнере J2EE упрощается при использовании программы
размещения, предоставляемой поставщиками сервера приложений.

2.5.4. Кодирование клиентской программы


Создание клиентского приложения для Web-службы сводится просто к написанию кода,
вызывающего нужный метод. Конечно же, для создания вызова удаленного метода и
передачи его в Web-службу нужно выполнить значительно больше действий, но все это
происходит неявно и незаметно для клиента.
Приведенное ниже определение класса представляет собой пример клиента Web-службы.
В нем создается экземпляр CoffeeOrderIF, который используется для вызова метода
getPriceList. Затем, для их отображения, осуществляется доступ к полям name и price
каждого объекта Coffee в массиве, который возвращается в методе getPriceList.
Класс CoffeeOrderServiceImpl - это один из классов, сгенерированных программой
отображения. Он является генератором заглушек и его единственный метод - getCoffee-
OrderIF; другими словами, его единственным предназначением является создание

Web-

Rendered by www.RenderX.com
Стр. 20 из 626 Введение в Web-службы

экземпляров CoffeeOrderIF. Экземпляры CoffeeOrderIF, созданные CoffeeOrderServiceImpl,


являются заглушками на стороне клиента, которые могут использоваться для вызова
методов, определенных в интерфейсе CoffeeOrderIF. Таким образом, переменная coffee-
Order представляет собой клиентскую заглушку, которая может быть использована для
вызова getPriceList - одного из методов, определенных в CoffeeOrderIF.
Метод getPriceList заблокирует работу приложения до тех пор, пока не получит ответ и не
возвратит его. Поскольку используется WSDL-документ, исполняющая система JAX-RPC
будет получать из него конечный адрес сужбы. В этом случае класс клиента может не
указывать назначение для удаленного вызова процедуры. Когда необходимо передать
конечный адрес службы, он может быть указан в виде аргумента в командной строке. Вот
как может выглядеть класс клиента:

package coffees;

public class CoffeeClient {


public static void main(String[] args) {
try {
CoffeeOrderIF coffeeOrder = new
CoffeeOrderServiceImpl().getCoffeeOrderIF();
Coffee [] priceList =
coffeeOrder.getPriceList():
for (int i = 0; i < priceList.length; i++) {
System.out.print(priceList[i].getName() + " ");
System.out.println(priceList[i].getPrice());
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}

2.5.5. Вызов удаленного метода


После обнаружения Web-службы клиент может вызвать один из ее методов. В следующем
примере производится удаленный вызов процедуры getPriceList, которая не принимает
аргументов. Как было отмечено ранее, выполняющая система JAX-RPC может определить
конечный адрес службы CoffeeOrder (являющийся его URI) из ее WSDL-описания. Если
WSDL-документ не используется, вы должны обеспечить URI службы в виде аргумента
командной строки. После компиляции файла CoffeeClient.java для вызова метода getPriceList
необходимо лишь ввести следующую командную строку:

java coffees.CoffeClient
Выполняемый этой строкой кода удаленный вызов процедуры является вызовом
статического метода. Другими словами, RPC был определен во время компиляция.

Web-

Rendered by www.RenderX.com
JAXM Стр. 21 из 626

Необходимо отметить, что при помощи JAX-RPC возможно динамически вызывать


удаленный метод во время выполнения. Это можно сделать либо используя Dynamic
Invocation Interface (DII), либо динамический прокси.

2.6. JAXM
Java API for XML Messaging (JAXM) предоставляет стандартный способ передачи XML-
документов по сети Интернет из платформы Java. Он базируется на спецификациях SOAP
1.1 и SOAP with Attachments, которые определяют основную среду для обмена XML-
сообщениями. JAXM может быть расширен для работы с протоколами обмена сообщениями
более высокого уровня, например определенным в ebXML (electronic business XML) Message
Service Specification, путем добавления новой функциональности на верхний уровень
SOAP.
Примечание: ebXML Message Service Specification доступна с http://www.oasis-
open.org/committees/ebxml-msg/. Среди других функций этот протокол обеспечивает более
безопасное средство передачи бизнес-сообщений по сети Интернет, чем спецификации
SOAP.
Обычно, бизнес-клиенты используют службу поставщика системы обмена сообщениями,
которая делает всю скрытую работу, требуемую для доставки и маршрутизации сообщений.
При использовании службы поставщика системы обмена сообщениями все JAXM-сообщения
проходят через нее, то есть при передаче сообщения оно сначала направляется к
поставщику отправителя, затем передается поставщику получателя и, наконец, попадает
к адресату. Также существует возможность направления сообщения промежуточным
получателям, прежде чем оно достигнет конечного адресата.
Поскольку сообщения проходят через поставщика, он должен позаботиться о деталях
таких служебных функций как назначение идентификаторов, хранение сообщений и
отслеживание того, доставлялось ли сообщение ранее. Поставщик службы обмена
сообщениями может также попытаться повторно передать сообщение, которое не достигло
назначения с первой попытки. Преимущество использования службы поставщика системы
обмена сообщениями состоит в том, что клиент, применяющий технологию JAXM ("JAXM-
клиент"), абсолютно не должен беспокоиться о том, какую внутреннюю работу делает
поставщик. JAXM-клиент просто вызывает Java-метод, а поставщик, используя свою
инфраструктуру, делает всю остальную работу незаметно для клиента.
Хотя обычно клиенты используют службу поставщика, возможно также осуществлять обмен
JAXM-сообщениями без поставщика. В этом случае, JAXM-клиент (называемый автономным
клиентом) ограничен передачей сообщений типа точка-точка непосредственно в Web-
службу, которая реализует систему обмена сообщениями типа запрос-ответ. Система
запрос-ответ является синхронной, это означает, что передача запроса и прием ответа
происходят в одной и той же операции. Сообщение типа запрос-ответ передается объектом
SOAPConnection через метод SOAPConnection.call, который передает сообщение и
блокирует работу до приема ответа. Автономный клиент может выступать только в роли
клиента, то есть он может только передавать запросы и принимать ответы на них. В отличие
от него JAXM-клиент, использующий службу обмена сообщениями поставщика, может
выступать и в роли клиента и в роли сервера (службы). В роли клиента он передает запросы,
а в роли сервера он может принимать запросы, обрабатывать их и передавать ответы.

Web-

Rendered by www.RenderX.com
Стр. 22 из 626 Введение в Web-службы

Хотя это и не обязательно, обмен сообщениями JAXM обычно происходит в контейнере,


как правило в контейнере сервлетов или в J2EE-контейнере. Web-служба, использующая
систему обмена сообщениями поставщика и размещенная в контейнере, обладает
способностью к однонаправленному обмену. Это означает, что она может принять запрос
как однонаправленное сообщение и передать ответ позже как другое однонаправленное
сообщение.
Из-за предоставляемых поставщиком системы обмена сообщениями функций JAXM может
иногда быть более удачным выбором системы обмена SOAP, чем JAX-RPC. В следующем
списке перечислены функции, которые может предоставить JAXM и обычно не может RPC
(в т.ч.JAX-RPC):
• Однонаправленный (асинхронный) обмен сообщениями
• Направление сообщения более чем одному адресату
• Надежный обмен сообщениями с такими функциями, как гарантированная доставка
Объект SOAPMessage представляет собой XML-документ, являющийся SOAP-сообщением.
Объект SOAPMessage всегда имеет необходимую SOAP-часть и может иметь одну или
более частей вложений. SOAP-часть всегда должна иметь объект SOAPenvelope, который,
в свою очередь, всегда должен содержать объект SOAPBody. Объект SOAPenvelope может
также содержать объект SOAPHeader, в который могут быть добавлены один или более
заголовков.
Объект SOAPBody может содержать XML-фрагменты в теле передаваемого сообщения.
Если вы хотите передать информацию, не записанную в формате XML или являющуюся
отдельным XML-документом, ваше сообщение должно содержать кроме SOAP-части часть
вложений. Для содержимого части вложений нет ограничений, то есть она может включать
изображения или любой другой тип информации, включая XML-фрагменты и документы.

2.6.1. Установка соединения


Прежде всего, JAXM-клиент должен установить соединение: либо объект SOAPConnection,
либо ProviderConnection.

2.6.1.1. Установка соединения точка-точка


Автономный клиент ограничен использованием объекта SOAPConnection, который является
соединением точка-точка, устанавливаемым непосредственно между отправителем и
получателем. Все JAXM-соединения создаются центром соединений. Для объекта SOAP-
Connection центром соединений является объект SOAPConnectionFactory. Получить
реализацию SOAPConnectionFactory по умолчанию клиент может при помощи следующего
кода:

SOAPConnectionFactory factory =
SOAPConnectionFactory.newInstance();
Клиент может использовать factory для создания объекта SOAPConnection.

SOAPConnection con = factory.createConnection();

Web-

Rendered by www.RenderX.com
JAXM Стр. 23 из 626

2.6.1.2. Установка соединения с поставщиком системы обмена сообщениями


Для того чтобы использовать службу обмена сообщениями поставщика, приложение
должно получить объект ProviderConnection, который является соединением с поставщиком,
а не с указанным получателем. Есть два способа получить объект ProviderConnection.
Первый похож на способ получения объекта SOAPConnection автономным клиентом. Этот
способ включает получение экземпляра реализации ProviderConnectionFactory по
умолчанию, который используется затем для создания соединения.

ProviderConnectionFactory pcFactory =
ProviderConnectionFactory.newInstance();
ProviderConnection pcCon = pcFactory.createConnection();
Переменная pcCon представляет собой соединение с реализацией по умолчанию
поставщика службы обмена сообщениями JAXM.
Вторым способом создания объекта ProviderConnection является получение объекта
ProviderConnectionFactory, который нужен для создания соединения с конкретным
поставщиком службы обмена сообщениями. В следующем фрагменте кода показано
получение такого объекта ProviderConnectionFactory и использование его для создания
соединения. Первые две строки используют Java Naming and Directory Interface™ (JNDI)
API для получения соответствующего объекта ProviderConnectionFactory из службы имен,
где он был зарегистрирован под именем "CoffeeBreakProvider". Если это логическое имя
передается в виде аргумента, метод lookup возвращает объект ProviderConnectionFactory,
с которым это имя было связано. Возвращаемое значение имеет тип Java Object и должно
быть переопределено как объект ProviderConnectionFactory для того, чтобы его можно
было использовать для создания соединения. В третьей строке используется JAXM-метод
для получения соединения.

Context ctx = getInitialContext();


ProviderConnectionFactory pcFactory =
(ProviderConnectionFactory)ctx.lookup("CoffeeBreakProvider");

ProviderConnection con = pcFactory.createConnection();


Экземпляр con ProviderConnection представляет собой соединение с поставщиком службы
обмена сообщениями "The Coffee Break".

2.6.2. Создание сообщения


Так же как и соединения, сообщения создаются центром сообщений. И, подобно центрам
соединений, объекты MessageFactory могут быть получены двумя способами. Первый
способ - получение экземпляра реализации по умолчанию класса MessageFactory. Этот
экземпляр в дальнейшем может быть использован для создания базового объекта
SOAPMessage.

MessageFactory messageFactory = MessageFactory.newInstance();


SOAPMessage m = messageFactory.createMessage();

Web-

Rendered by www.RenderX.com
Стр. 24 из 626 Введение в Web-службы

Все объекты SOAPMesage, которые создает messageFactory, включая m в предыдущей


строке кода, будут базовыми SOAP-сообщениями. Это значит, что они не будут иметь
предопределенных заголовков.
Одним из аспектов гибкости JAXM API является возможность специального использования
SOAP-заголовка. Например, другие протоколы, такие как ebXML, могут быть реализованы
на верхнем уровне системы SOAP для обеспечения реализации дополнительных
заголовков, обеспечивая, таким образом, дополнительную функциональность. Такое
использование SOAP данной группой стандартов или отраслью называется профилем.
(Дополнительная информация по профилям находится в разделе "Профили" "Руководства
по JAXM".)
При втором способе создания объекта MessageFactory используется метод createMessage-
Factory объекта ProviderConnection и передается профиль. Объекты SOAPMessage,
созданные полученным объектом MessageFactory, будут поддерживать указанный профиль.
Например, в следующем фрагменте кода, в котором schemaURI является URI схемы для
желаемого профиля, m2 будет поддерживать профиль, переданный в createMessageFactory.

MessageFactory messageFactory2 =
con.createMessageFactory(<schemaURI>);
SOAPMessage m2 = messageFactory2.createMessage();
Каждый из новых объектов SOAPMessage m и m2 автоматически содержит требуемые
элементы SOAPPart, SOAPEnvelope и SOAPBody, плюс необязательный элемент SOAP-
Header (который включен для удобства). Объекты SOAPHeader и SOAPBody первоначально
пусты. В следующем разделе показаны несколько из стандартных способов добавления
в них содержимого.

2.6.3. Заполнение сообщения


Содержимое может быть добавлено в объект SOAPPart, в один или несколько объектов
AttachmentPart, или в обе части сообщения.

2.6.3.1. Заполнение SOAP-части сообщения


Как утверждалось ранее, все сообщения имеют объект SOAPPart, который в свою очередь,
имеет объект SOAPEnvelope, содержащий объекты SOAPHeader и SOAPBody. Одним из
способов добавления содержимого в SOAP-часть сообщения является создание объекта
SOAPHeaderElement, или объекта SOAPBodyElement, и вставка XML-фрагмента, созданного
методом SOAPElement.addTextNode. В первых трех строках приведенного ниже фрагмента
кода осуществляется доступ к объекту SOAPBody body, который используется для создания
нового объекта SOAPBodyElement и добавления его в body. Передаваемый в метод
createName аргумент является объектом Name, идентифицирующим добавляемый
SOAPBodyElement. В последней строке добавляется XML-строка, передаваемая в метод
addTextNode.

SOAPPart sp = m.getSOAPPart();
SOAPEnvelope envelope = sp.getSOAPEnvelope();
SOAPBody body = envelope.getSOAPBody();

Web-

Rendered by www.RenderX.com
JAXM Стр. 25 из 626

SOAPBodyElement bodyElement = body.addBodyElement(


envelope.createName("text", "hotitems",
"http://hotitems.com/products/gizmo");
bodyElement.addTextNode("some-xml-text");
Другим способом добавления содержимого в объект SOAPPart является передача его в
объект javax.xml.transform.Source, который может быть объектом SAXSource, DOMSource
или StreamSource. Объект Source включает в себя содержимое SOAP-части сообщения,
а также информацию, необходимую для того, чтобы действовать как источник входных
данных. Объект StreamSource будет включать содержимое в виде XML-документа, а
объекты SAXSource или DOMSource будут включать содержимое и инструкции для
преобразования его в XML-документ.
В следующем фрагменте кода показано добавление содержимого в виде объекта
DOMSource. На первом шаге получаем объект SOAPPart из объекта SOAPMesage. Затем
код использует методы из JAXP API для создания добавляемого XML-документа. Для
получения объекта DocumentBuilder используется объект DocumentBuilderFactory. Затем
указанный файл анализируется для создания документа, который будет использоваться
для инициализации нового объекта DOMSource. И наконец, объект DOMSource domSource
передается в метод SOAPPart.setContent.

DocumentBuilderFactory dbf=
DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse("file:///foo.bar/soap.xml");
DOMSource domSource = new DOMSource(doc);

soapPart.setContent(domSource);

2.6.3.2. Заполнение части вложений сообщения


Объект Message может не иметь части вложений, но если он должен содержать что-либо
не в формате XML, это содержимое должно быть включено в часть вложений. Число частей
вложений может быть любым, и они могут содержать все, начиная с неформатированного
текста и заканчивая изображениями. В следующем фрагменте кода содержимым является
изображение из JPEG-файла, URL которого используется для инициализации объекта
javax.activation.DataHandler dh. Объект Message m создает объект AttachmentPart attachPart,
который инициализируется идентификатором данных, содержащим URL изображения. И,
наконец, сообщение добавляет attachPart к себе.

URL url = new URL("http://foo.bar/img.jpg");


DataHandler dh = new DataHandler(url);
AttachmentPart attachPart = m.createAttachmentPart(dh);
m.addAttachmentPart(attachPart);
Объект SOAPMessage может передать содержимое в объект AttachmentPart, передав
Object и тип его содержимого в метод createAttachmentPart.

Web-

Rendered by www.RenderX.com
Стр. 26 из 626 Введение в Web-службы

AttachmentPart attachPart =
m.createAttachmentPart("content-string", "text/plain");
m.addAttachmentPart(attachPart);
Третьей альтернативой является создание пустого объекта AttachmentPart и передача
Object и типа его содержимого в метод AttachmentPart.setContent. В данном фрагменте
кода Object имеет тип ByteArrayInputStream и иницилизирован jpeg-изображением.

AttachmentPart ap = m.createAttachmentPart();
byte[] jpegData = ...;
ap.setContent(new ByteArrayInputStream(jpegData),
"image/jpeg");
m.addAttachmentPart(ap);

2.6.4. Передача сообщения


После заполнения объекта SOAPMessage он готов для передачи. Автономный клиент
использует метод call объекта SOAPConnection для передачи сообщения. Этот метод
передает сообщение и блокируется до получения ответа. Аргументами метода call являются
передаваемое сообщение и объект URL, содержащий URL, который указывает конечный
адресат.

SOAPMessage response =
soapConnection.call(message, endpoint);
Приложение, использующее службу поставщика системы обмена сообщениями, для
передачи сообщения применяет метод send объекта ProviderConnection. Этот метод
передает сообщение в асинхронном режиме. Это означает, что он передает сообщение
и сразу заканчивает свою работу. Ответ, если таковой есть, будет передан как отдельная
операция в другое время. Обратите внимание, что этот метод принимает только один
параметр - передаваемое сообщение. Поставщик системы обмена сообщениями будет
использовать информацию из заголовка для определения адресата.

providerConnection.send(message);

2.7. JAXR
Java API for XML Registries (JAXR) предоставляет удобный способ получения доступа к
стандартным бизнес-реестрам по сети Интернет. Бизнес-реестры часто описываются как
электронные "желтые страницы", поскольку они содержат списки субъектов бизнеса и
предлагаемые ими продукты и службы. JAXR предоставляет разработчикам, пишущим
приложения на языке программирования Java, унифицированный способ использования
бизнес-реестров, основанных на открытых стандартах (таких как ebXML) или на отраслевых
спецификациях (таких как UDDI).

Web-

Rendered by www.RenderX.com
JAXR Стр. 27 из 626

Субъекты бизнеса могут зарегистрировать себя в реестре или найти других субъектов, с
которыми они, возможно, захотят сотрудничать. Кроме того, они могут предложить
материалы для совместного пользования или найти материалы, предложенные другими.
Группы по разработке стандартов разработали схемы для конкретных типов XML-
документов, и два субъекта бизнеса могут, например, согласиться использовать
стандартную отраслевую схему формы заказа на поставку. Поскольку схема хранится в
стандартном бизнес-реестре оба участника могут использовать JAXR для доступа к ней.
Реестры становятся все более важным компонентом Web-служб, поскольку они дают
возможность субъектам бизнеса взаимодействовать друг с другом динамически или
используя схему слабых связей. Следовательно, потребность в JAXR, позволяющем
предприятиям получать доступ к стандартным бизнес-реестрам из языка программирования
Java, также растет.

2.7.1. Использование JAXR


В следующем разделе приводятся примеры двух типичных способов использования бизнес-
реестра. Они предназначены для иллюстрации общих принципов использования JAXR и
не являются завершенными или всесторонними.

2.7.1.1. Регистрация субъекта бизнеса


Организация, использующая платформу Java в своем электронном бизнесе, может
использовать JAXR для регистрации в стандартном реестре. Она должна предоставить
свое имя, описание и некоторый принцип классификации для облегчения поиска. Это
показано в следующем фрагменте кода, в котором сначала создается объект RegistryService
rs, используемый далее для создания объекта BusinessLifeCycleManager lcm и объекта
BusinessQueryManager bqm. Субъект бизнеса, сеть магазинов называемых The Coffee
Break, представляется объектом Organization org, в который добавляется имя, описание
и код классификации по North American Industry Classification System (NAICS). Затем org,
который уже содержит свойства и классификации предприятия, добавляется в объект
Collection orgs. И, наконец, lcm сохраняет orgs и в дальнейшем будет управлять жизненным
циклом объектов Organization, содержащихся в orgs.

RegistryService rs = connection.getRegistryService();

BusinessLifeCycleManager lcm =
rs.getBusinessLifeCycleManager();
BusinessQueryManager bqm =
rs.getBusinessQueryManager();

Organization org = lcm.createOrganization("The Coffee Break");


org.setDescription(
"Purveyor of only the finest coffees. Established 1895");

ClassificationScheme cScheme =
bqm.findClassificationSchemeByName("ntis-gov:naics");

Web-

Rendered by www.RenderX.com
Стр. 28 из 626 Введение в Web-службы

Classification classification =
(Classification)lcm.createClassification(cScheme,
"Snack and Nonalcoholic Beverage Bars", "722213");

Collection classifications = new ArrayList();


classifications.add(classification);

org.addClassifications(classifications);
Collection orgs = new ArrayList();
orgs.add(org);
lcm.saveOrganizations(orgs);

2.7.1.2. Поиск в реестре


Субъект бизнеса может использовать JAXR также для поиска других субъектов. В
следующем фрагменте кода объект BusinessQueryManager bqm используется для поиска
The Coffee Break. Перед тем, как bqm сможет вызвать метод findOrganizations, необходимо
определить используемый критерий поиска. В данном случае, для findOrganizations
предоставляются три из возможных шести параметров поиска; послкольку для третьего,
пятого и шестого параметров используется значение null, эти критерии не используются
для ограничения поиска. Первый, второй и четвертый аргументы являются объектами
Collection, в которых определены параметры findQualifiers и namePatterns. Единственным
элементом в findQualifiers является объект String, указывающий, что не должна
возвращаться никакая организация, пока ее имя не будет соответствовать (регистр
символов имеет значение) одному из имени в параметре namePatterns. Этот параметр,
также являющийся объектом Collection с единственным элементом, указывает, что субъект
бизнеса, имеющий в своем названии "Coffee", соответствует критерию поиска. Другой
объект Collection - это classification, который определяет, когда компания The Coffee Break
зарегистрировала себя. Предыдущий фрагмент кода является примером определения
классификаций.

BusinessQueryManager bqm = rs.getBusinessQueryManager();

//
Collection findQualifiers = new ArrayList();
findQualifiers.add(FindQualifier.CASE_SENSITIVE_MATCH);
Collection namePatterns = new ArrayList();
namePatterns.add("%Coffee%"); // orgs ,

//'Coffee'

// ,

BulkResponse response = bqm.findOrganizations(findQualifiers,

Web-

Rendered by www.RenderX.com
Пример сценария Стр. 29 из 626

namePatterns, null, classifications, null, null);


Collection orgs = response.getCollection();
JAXR также поддерживает использование SQL-запросов для поиска в реестре. Для этого
используется объект DeclarativeQueryManager, как показано в следующем фрагменте кода.

DeclarativeQueryManager dqm = rs.getDeclarativeQueryManager();


Query query = dqm.createQuery(Query.QUERY_TYPE_SQL,
"SELECT id FROM RegistryEntry WHERE name LIKE %Coffee% " +
"AND majorVersion >= 1 AND " +
"(majorVersion >= 2 OR minorVersion >= 3)");
BulkResponse response2 = dqm.executeQuery(query);
Объект BulkResponse response2 будет содержать значение id (uuid) для каждой записи в
RegistryEntry, имеющей в своем имени символы "Coffee" и номер версии 1.3 и выше.
Для обеспечения возможности взаимодействия между JAXR-клиентом и реализацией
реестра обмен сообщениями происходит с использованием JAXM. Это происходит
полностью незаметно, так что вы, как пользователи JAXR, совершенно не должны
беспокоиться об этом.

2.8. Пример сценария


Следующий сценарий представляет собой пример того, как могут использоваться Java
API для XML и как они работают совместно. Одной из сильных сторон Java API для XML
является то, что во многих случаях они предлагают альтернативные способы реализации
чего-либо и, следовательно, позволяют вам адаптировать код под конкретные требования.
В этом разделе будут специально подчеркнуты случаи, когда можно применить различные
API, и будут объяснены причины, по которым тот или иной API может быть лучшим выбором.

2.8.1. Сценарий
Предположим, что владелец сети кофейных магазинов, называемых The Coffee Break,
хочет расширить свою деятельность продажей кофе в интерактивном режиме. Он
приказывает своему бизнес-менеджеру найти несколько новых поставщиков кофе, получить
их оптовые цены и размещать заказы по мере возникновения необходимости. The Coffee
Break может проанализировать цены и принять решение, какие новые сорта кофе
желательно приобретать и у какого из поставщиков.

2.8.1.1. Поиск новых поставщиков


Бизнес-менеджер ставит задачу инженеру-программисту компании найти потенциально
новые источники кофе. Она решает, что лучшим способом найти новых поставщиков
является поиск в реестре Universal Description, Discovery, and Integration (UDDI), в котором
The Coffee Break себя уже зарегистрировала.
Инженер использует JAXR для передачи запроса для поиска оптовых поставщиков кофе.
Реализация JAXR неявно использует JAXM для передачи запроса в реестр, но это является
полностью прозрачным для инженера.

Web-

Rendered by www.RenderX.com
Стр. 30 из 626 Введение в Web-службы

UDDI-реестр примет запрос и применит переданный в JAXR-коде критерий поиска к


имеющейся в нем информации по зарегистрированным организациям. После завершения
поиска реестр передаст обратно информацию о том, как связаться с оптовыми продавцами
кофе, удовлетворяющими указанному критерию поиска. Хотя реестр неявно использует
JAXM для передачи информации, ответ, который получит инженер, представляет из себя
JAXR-код.

2.8.1.2. Запрос прайс-листов


Следующим действием инженера является запрос прайс-листов у каждого поставщика
кофе. Она получила WSDL-описание каждого из них, в котором указывается, какую
процедуру надо вызвать для получения цен, и URI, куда нужно передать запрос. Ее
программа осуществляет соответствующие удаленные вызовы процедур при помощи JAX-
RPC API и получает ответы от поставщиков. Компания The Coffee Break имеет дело с
одним из поставщиков уже долгое время и подписывает соглашение с ним об обмене
JAXM-сообщениями, используя заранее оговоренную XML-схему. Таким образом, для
этого поставщика программа использует JAXM API для запроса текущих цен, а поставщик
возвращает прайс-лист в JAXM-сообщении.

2.8.1.3. Сравнение цен и заказ кофе


После получения ответов на свои запросы о ценах инженер обрабатывает прайс-листы,
используя SAX. Она использует SAX, а не DOM, поскольку для простого сравнения цен
это более эффективно. (Для изменения прайс-листа она выбрала бы DOM.) После того,
как ее приложение получило цены, назначенные разными поставщиками, происходит их
сравнение и отображение результатов.
После того, как владелец и бизнес-менеджер решают, с какими поставщиками иметь дело,
основываясь на сравнении цен, инженер готова для передачи заказов поставщикам. Заказы
новым поставщикам передаются через JAX-RPC, заказы к постоянным поставщикам
передаются через JAXM. Каждый поставщик, как использующий JAX-RPC, так и
использующий JAXM, передаст подтверждение с номером заказа и датой поставки.

2.8.1.4. Продажа кофе по сети Интернет


Между тем, компания The Coffee Break подготовилась для предложения своего
расширенного ассортимента кофе. Ей необходимо опубликовать прайс-лист и форму
заказа в HTML-формате на своем Web-сайте. Но перед этим компания должна определить
цену продажи. Инженер пишет приложение, умножающее оптовую цену на 135%. С
небольшими изменениями список розничных цен размещается на сайте в форме заказа.
Инженер использует технологию JavaServer Pages™ (JSP™) для создания HTML-формы
заказа, которую могут использовать клиенты для интерактивного заказа кофе. Из JSP-
страницы она получает название и цену каждого сорта и вставляет их в HTML-таблицу на
JSP-страницу. Клиент вводит количество каждого желаемого сорта кофе и нажимает кнопку
"Submit" для передачи заказа.

2.8.2. Заключение
Хотя этот сценарий упрощен в целях краткости, он иллюстрирует, как технологии XML
могут быть использованы в мире Web-служб. С появлением Java API для XML и платформы
J2EE создание Web-служб и написание приложений, использующих их, стали проще.

Web-

Rendered by www.RenderX.com
Введение в XML Стр. 31 из 626

В главе 18 приводится простая реализация этого сценария.

3. Освоение XML
В этой главе описывается Extensible Markup Language (XML) и связанные с ним
спецификации.

3.1. Введение в XML


В этом разделе рассмотрены основы XML. Наша цель - предоставить информацию для
начала работы с XML, дать вам понять, что же такое XML. (Вы изучите XML в следующих
разделах руководства.) Далее мы отметим основные особенности, которые делают XML
идеальным средством для хранения и обмена информацией, и рассмотрим главные
принципы использования XML.

3.1.1. Что такое XML?


XML - это текстовый язык разметки, который быстро становится стандартом для обмена
данными в Web. Как и в HTML для определения данных используются теги
(идентификаторы, заключенные в угловые скобки - <...>). Совокупность тегов называется
разметкой.
Но, в отличие от HTML, XML-теги идентифицируют данные, а не способ их отображения.
Если HTML-тег указывает, например, "отобразить эти данные жирным шрифтом" (<b>...</b>),
XML-тег действует как имя поля в вашей программе. Он ставит метку на часть данных,
которые идентифицирует (например: <message>...</message>).
Примечание: Поскольку идентификация данных происходит по их значению (как их
интерпретировать и что необходимо с ними сделать), XML иногда описывается как механизм
для определения семантики (значения) данных.
Так же как при определении имен полей структуры данных вы можете использовать любые
XML-теги, имеющие значение для данного приложения. Естественно, для того чтобы
несколько приложений использовали одни и те же XML-данные, они должны договориться
об именах тегов.
Вот пример XML-данных, которые можно использовать в почтовом приложении:

<message>
<to>you@yourAddress.com</to>
<from>me@myAddress.com</from>
<subject>XML Is Really Cool</subject>
<text>
How many ways is XML cool? Let me count the ways...
</text>
</message>
Примечание: В этом руководстве жирный шрифт используется для выделения текста, на
который мы хотим обратить ваше внимание. XML не использует жирный шрифт!

Web-

Rendered by www.RenderX.com
Стр. 32 из 626 Освоение XML

Теги в этом примере идентифицируют сообщение в целом, адреса отправителя и


получателя, тему и текст сообщения. Так же как и в HTML тег <to> имеет соответствующий
завершающий тег </to>. Данные между тегом и соответствующим ему завершающим тегом
определяют элемент XML-данных. Обратите внимание, что содержимое тега <to> полностью
содержится в области действия тега <message>...</message>. Именно способность одного
тега содержать другие обеспечивает в XML способность представлять иерархические
структуры данных.
И снова, так же как в HTML, пробелы не существенны, так что вы можете форматировать
данные для удобства чтения и в то же время легко обрабатывать их в программе. В отличие
от HTML, однако, в XML вы можете легко искать в наборе данных сообщения, содержащие
в своей теме слово "cool", поскольку XML-теги идентифицируют содержимое данных, а не
их представление.

3.1.1.1. Теги и атрибуты


Теги могут также содержать атрибуты - дополнительную информацию, включаемую как
часть самого тега вовнутрь его угловых скобок. В следующем примере показана структура
сообщения электронной почты, использующего атрибуты для полей "to", "from" и "subject":

<message to="you@yourAddress.com" from="me@myAddress.com"


subject="XML Is Really Cool">
<text>
How many ways is XML cool? Let me count the ways...
</text>
</message>
Как и в HTML, после имени атрибута следуют знак равенства и значение атрибута, а
несколько атрибутов разделяются пробелами. Однако, в отличие от HTML, запятые между
атрибутами в XML не игнорируются; если они существуют, генерируется ошибка.
Поскольку вы можете создать такую структуру данных как <message>, одинаково хорошо
используя как атрибуты, так и теги, принятие решения, какой дизайн лучше подходит для
конкретных целей, может потребовать значительных усилий. В разделе "Проектирование
структуры XML-данных" приведены некоторые рекомендации, которые помогут вам решить,
когда использовать атрибуты, а когда теги.

3.1.1.2. Пустые теги


Одним из действительно больших различий между HTML и XML является то, что XML-
документ всегда обязан быть формально-правильным. Существует несколько правил,
которые определяют корректность документа, но одним из самых важных является
следующее правило - каждый тег должен иметь завершающий тег. То есть, в XML тег </to>
является обязательным. Элемент <to> никогда не завершается каким-либо тегом, отличным
от </to>.
Примечание: Еще одним важным аспектом формально-правильного документа является
полная вложенность всех тегов. То есть вы можете записать
<message>..<to>..</to>..</message>, но никогда не сможете
<message>..<to>..</message>..</to>. Полный список требований находится в XML Frequently

Web-

Rendered by www.RenderX.com
Введение в XML Стр. 33 из 626

Asked Questions (FAQ) на странице http://www.ucc.ie/xml/#FAQ-VALIDWF. (Этот FAQ


находится в списке "Рекомендуется прочитать" организации W3C на htp://www.w3.org/XML/.)
Однако, иногда есть смысл использовать тег, стоящий особняком. Например, вы возможно
захотите добавить тег "flag", который помечает важное сообщение. Такой тег не имеет
никакого содержимого, и называется "пустым тегом". Вы можете создать пустой тег, закончив
его знаками /> вместо >. Например, следующее сообщение содержит такой тег:

<message to="you@yourAddress.com" from="me@myAddress.com"


subject="XML Is Really Cool">
<flag/>
<text>
How many ways is XML cool? Let me count the ways...
</text>
</message>
Примечание: Пустой тег предохраняет вас от необходимости записи <flag></flag> для
сохранения корректности документа. Вы можете контролировать, какие теги могут быть
пустыми, создав Document Type Definition, или DTD. Через некоторое время мы поговорим
об этом. Если DTD не существует, документ может содержать любые типы тегов, какие
вы захотите, пока документ остается формально-правильным.

3.1.1.3. Комментарии в XML-файлах


XML-комментарии выглядят также как и в HTML:

<message to="you@yourAddress.com" from="me@myAddress.com"


subject="XML Is Really Cool">
<!- -- >
<text>
How many ways is XML cool? Let me count the ways...
</text>
</message>

3.1.1.4. Пролог XML


И, в завершение этого введения в XML, обратите внимание, что XML-файл всегда
начинается с пролога. Минимальный пролог содержит объявление, идентифицирующее
документ как XML-документ, например:

<?xml version="1.0"?>
Объявление может также содержать дополнительную информацию, например:

<?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?>

Web-

Rendered by www.RenderX.com
Стр. 34 из 626 Освоение XML

XML-объявление по существу является таким же как и заголовок HTML, <html>, за


исключением того, что в нем используются <?..?> и он может иметь следующие атрибуты:
version - Идентифицирует используемую версию языка разметки XML. Этот атрибут является
обязательным.
encoding - Идентифицирует набор символов, используемый для кодирования данных. "ISO-
8859-1" - это набор символов "Latin-1" для языков Западной Европы и Англии. (По
умолчанию устанавливается сжатый Unicode: UTF-8.)
standalone - Указывает, ссылается или нет документ на внешнюю сущность или на внешнюю
спецификацию типов данных (см. ниже). Если нет внешних ссылок, используется значение
"yes".
Пролог может также содержать определения сущностей (элементов, вставляемых тогда,
когда вы ссылаетесь на них в документе) и спецификаций, указывающих, какие теги
являются разрешенными в документе. Сущности и спецификации объявляются в Document
Type Definition (DTD), определяемом непосредственно в прологе, или через указатель на
внешние файлы спецификаций. Но это - предмет для дальнейшего изучения в этом
руководстве. Более подробная информация об этом и многих других аспектах XML
находится в списке "Рекомендуется прочитать" организации htp://www.w3.org/XML/.
Примечание: На самом деле объявление не обязательно. Но включение его в любой
создаваемый XML-файл является хорошей идеей. Объявление должно содержать номер
версии и, по возможности, систему кодирования. Эта рекомендация упростит жизнь, если
в будущем XML-стандарт расширится, или если когда-нибудь понадобится локализовать
данные для других географических регионов.
Все, что следует за XML-прологом, составляет содержимое документа.

3.1.1.5. Директивы
XML-файл может также содержать директивы, которые передают команды или информацию
приложению, обрабатывающему XML-данные. Директивы имеют следующий формат:

<?target instructions?>
где target - имя приложения, которое, как ожидается, будет осуществлять обработку, а
instructions - это строка символов, заключающая в себе информацию или команды для
обработки приложением.
Поскольку директивы зависят от приложения, XML-файл может иметь несколько директив,
указывающих различным приложениям выполнить похожие действия, хотя и разными
способами. XML-файл для слайдшоу, например, может содержать директивы, позволяющие
докладчику выбрать техническую версию презентации или версию для исполнительного
звена. Если бы использовались несколько программ презентации, в программе, возможно,
понадобилось бы наличие нескольких версий директив (хотя было бы лучше, если бы эти
приложения распознавали стандартные директивы).
Примечание: Имя "XML" (в любой комбинации строчных или прописных букв)
зарезервировано для XML-стандартов. В некотором смысле объявление является
директивой, удовлетворяющей стандарту. (Однако, если вы позже будете работать с
анализатором, вы увидите, что метод для обработки директив никогда не видит
объявления.)

Web-

Rendered by www.RenderX.com
Введение в XML Стр. 35 из 626

3.1.2. В чем важность XML?


Существует большое количество причин всеобщего признания XML. В этом разделе
перечислены наиболее важные из них.

3.1.2.1. Обыкновенный текст


Поскольку XML не является двоичным форматом, вы можете создать и редактировать
файлы при помощи любого средства, начиная от стандартного текстового редактора и
заканчивая визуальной средой разработки. Это облегчает отладку ваших программ и
приносит пользу при хранении небольшого количества данных. С другой стороны, внешний
интерфейс XML к базе данных делает также возможным эффективное хранение большого
количества XML-данных. То есть XML, обеспечивает полную масштабируемость, начиная
от небольших конфигурационных файлов и заканчивая корпоративным хранилищем данных.

3.1.2.2. Идентификация данных


XML указывает типы данных, а не способ их отображения. Поскольку теги разметки
идентифицируют информацию и разбивают данные на части, почтовая программа может
обрабатывать их, программа поиска может искать сообщения, переданные определенному
человеку, а адресная книга может извлекать адрес из оставшейся части сообщения. Короче
говоря, поскольку идентифицируются различные части информации, они могут быть
использованы различными способами различными приложениями.

3.1.2.3. Стиль отображения


Когда способ отображения является важным, стандарт таблицы стилей XSL позволяет
вам указать, как отобразить данные. Например, таблица стилей для:

<to>you@yourAddress.com</to>
может сказать:
1. Начать новую строку.
2. Отобразить "To:" жирным шрифтом с последующим пробелом.
3. Отобразить данные получателя.
Вот что получится:

To: you@yourAddress
Конечно, вы можете сделать тоже самое и в HTML, но вы не сможете обработать данные
программами поиска и программами, извлекающими адреса и т.д. Более важным является
то, что, поскольку XML по своей сути не имеет стиля, вы можете использовать совершенно
разные таблицы стилей для выполнения вывода в форматах postscript, TEX, PDF или
каких-либо новых форматах, которые еще даже не разработаны. Такая гибкость означает
то, что один автор описал как "будущая защита" вашей информации. XML-документы,
созданные сегодня, могут использоваться в будущих, еще не известных системах доставки
документов.

Web-

Rendered by www.RenderX.com
Стр. 36 из 626 Освоение XML

3.1.2.4. Встроенная возможность многократного использования


Одной из замечательных возможностей XML-документов является возможность их
составления из отдельных модулей. Это возможно и в HTML, но только при помощи
указания ссылок на другие документы. В отличие от HTML, XML-модули могут быть
включены в документ "по месту". Подключенные модули выглядят как нормальные части
документа - вы можете производить поиск по всему документу одновременно или загружать
его одной частью. Это дает возможность разбивать документ на модули без помощи
ссылок. Вы можете отделить модуль так, что редактирование в нем отражается везде, где
он используется, а документ, составленный из таких модулей, выглядит для всех состоящим
только из одной части.

3.1.2.5. Связываемость
Благодаря HTML, способность определять связи между документами рассматривается
сегодня как необходимость. В следующем разделе этого руководства, "XML и связанные
спецификации: освоение алфавитной путаницы", обсуждается программа создания
спецификации по ссылкам. Эта программа позволяет вам определять двунаправленные
ссылки, многоцелевые ссылки, "расширяющиеся" ссылки (в которых переход по ссылке
вызывает появление запрошенной информации в текущем месте документа), и ссылки
между двумя существующими документами, определенные в третьем.

3.1.2.6. Простота обработки


Как упоминалось ранее, регулярная и непротиворечивая нотация облегчает создание
программ для обработки XML-данных. Например, в HTML тег <dt> может быть ограничен
тегами </dt>, другим <dt>, <dd> или </dl>. Это несколько затрудняет программирование.
Но в XML тег <dt> должен всегда ограничиваться тегом </dt>, в противном случае он должен
определяться как тег <dt/>. Это правило является критичным среди ограничений, которые
делают XML-документ формально-правильным. (В противном случае XML-анализатор не
будет способен прочитать данные.) А поскольку XML является независимым от
производителей стандартом, вы можете выбрать любой XML-анализатор, и каждый из них
сможет обработать XML-данные.

3.1.2.7. Иерархичность

3.1.3. Как можно использовать XML?


Существует несколько основных областей использования XML:
• Традиционная обработка данных, при которой данные кодируются в XML для
обрабатывающей их программы.
• Основанное на документах программирование, при котором XML-документы являются
контейнерами, создающими интерфейсы и приложения из существующих компонентов.
• Архивация - фундамент основанного на документах программирования, при котором
настроенная версия компонента сохраняется (архивируется) для дальнейшего
использования.
• Связывание, при котором DTD или схема, определяющая структуру XML-данных,
используется для автоматической генерации значительной части приложения, которое
в конечном итоге будет обрабатывать данные.

Web-

Rendered by www.RenderX.com
Введение в XML Стр. 37 из 626

3.1.3.1. Традиционная обработка данных


XML быстро становится стандартом, выбираемым для представления информации в Web.
Это проявляется еще сильнее при использовании его в соединении с сетевыми
программами платформы Java, передающими и принимающими информацию. То есть,
приложение клиент/сервер, например, может передавать XML-данные в обоих направлениях
между клиентом и сервером.
В будущем XML станет потенциальным решением при обмене данными в транзакциях
любого типа, как только обе стороны согласятся использовать одинаковую разметку.
(Например, какие теги почтовая программа должна обрабатывать: <FIRST> и <LAST>, или
<FIRSTNAME> и <LASTNAME>). Необходимость в общих стандартах в ближайшие годы
приведет к разработке многих отраслевых стандартов. Между тем, будут важны механизмы,
позволяющие "транслировать" теги в XML-документе. Такими механизмами являются,
например, инициатива RDF, в которой определяются "базовые теги", и XSL-спецификация,
определяющая трансляцию одних XML-тегов в другие.

3.1.3.2. Основанное на документах программирование (DDP)


Новейшим подходом к использованию XML является создание документа, описывающего
внешний вид страницы приложения. Этот документ, вместо того, чтобы просто быть
отображенным, состоит из ссылок на компоненты пользовательского интерфейса и
компоненты бизнес-логики, которые соединяются вместе для создания приложения на
лету.
Конечно, для таких компонентов имеет смысл использовать платформу Java. Для создания
таких приложений можно использовать и JavaBeans™ для интерфейсов, и JavaBeans™
для бизнес-логики. И хотя ни одно из предпринятых усилий еще не доведено до
коммерческого использования, очень много предварительной работы уже сделано.
Примечание: Язык программирования Java также отлично подходит для создания средств
обработки XML, которые являются такими же переносимыми, как и XML. Для платформы
Java были написаны несколько визуальных редакторов XML. Список редакторов, средств
обработки и других XML-ресурсов находится в разделе "Software" SGML/XML Web-страницы,
созданной Robin Cover, по адресу http://www.oasis-open.org/cover/.

3.1.3.3. Связывание
Как только вы определили структуру XML-данных, используя DTD или один из стандартов
схем, большая часть необходимой обработки уже определена. Например, если в схеме
указано, что текстовые данные в элементе <date> должны соответствовать одному из
распознаваемых форматов даты, один из аспектов критерия верификации данных уже
определен - остается только написать код. Хотя спецификация DTD не может обеспечить
такой же уровень детализации, DTD (как и схема) описывает грамматику, указывающую,
какие структуры данных могут встретиться и в какой последовательности. В этой
спецификации указывается, как написать высокоуровневый код, обрабатывающий элементы
данных.
Но когда структура данных (и возможно схема) полностью определена, код, необходимый
вам для обработки этих данных, может быть легко сгенерирован автоматически. Этот
процесс известен под названием связывание - создание классов, распознающих и
обрабатывающих различные элементы данных при обработке спецификации, которая
определяет эти элементы. Со временем вы обнаружите, что используете спецификацию

Web-

Rendered by www.RenderX.com
Стр. 38 из 626 Освоение XML

данных для генерирования значительной части кода, и сможете сконцентрироваться на


программировании уникальных для вашего приложения задач.

3.1.3.4. Архивация
Чашей Святого Грааля в программировании является создание повторно используемых,
модульных компонентов. В идеальном случае вы хотели бы взять их с полки, настроить и
подключить их для создания приложения, с минимальным дополнительным кодированием
и дополнительной компиляцией.
Основной механизм сохранения информации называется архивированием. Компонент
архивируется путем записи его в выходной поток в форме, которую можно будет
использовать в дальнейшем. Потом можно будет прочитать его и создать экземпляр,
используя сохраненные параметры. (Например, если вы сохранили элемент таблицы, его
параметрами могут быть количество строк и столбцов для отображения.) Архивированные
компоненты можно перемещать по Web и использовать множеством способов.
Когда компоненты архивируются в бинарной форме, существуют некоторые ограничения
в видах изменений, которые можно сделать в классах при желании сохранить совместимость
с предыдущими версиями. Если бы вы могли модифицировать архивную версию так, чтобы
изменения отразились в ней, это решило бы проблему. Но это очень тяжело сделать с
двоичными объектами. Такие рассуждения вызвали большое количество исследований в
использовании XML для архивирования. Но если состояние объекта было сархивировано
в текстовой форме с использованием XML, тогда все в нем может быть изменено так же
легко, как и сказать "найти и заменить".
Текстовый формат XML может также облегчить передачу объектов между приложениями,
написанными на разных языках. По всем этим причинам основанное на XML архивирование
возможно будет иметь большое распространение в не таком уж далеком будущем.

3.1.3.5. Итог
XML является довольно простым и очень гибким языком разметки. Он имеет много
применений, которые еще должны быть открыты - мы только начинаем использовать его
потенциал. Он является фундаментом для многих грядущих стандартов, обеспечивая
общий язык, который могут использовать различные компьютерные системы для обмена
данными друг с другом. Как только каждая отраслевая группа выйдет со своими
стандартами, компьютеры начнут связываться друг с другом способами, которые раньше
трудно было бы представить.
Более подробная информация по предпосылкам и движущим силам XML находится в
прекрасной статье в Scientific American по адресу
http://www.sciam.com/1999/0599issue/0599bosak.html.

3.2. XML и связанные спецификации: освоение алфавитной путаницы


Теперь, когда вы понимаете основы XML, имеет смысл рассмотреть различные имеющие
отношение к XML акронимы и их значение. Большой объем работы над XML продолжается,
так что у нас есть что изучить.
Существующими в настоящее время API для обработки XML-документов в
последовательном режиме или в режиме произвольного доступа являются соответственно
SAX и DOM. Спецификациями для обеспечения корректности XML-документов являются

Web-

Rendered by www.RenderX.com
XML и связанные спецификации: освоение алфавитной путаницы Стр. 39 из 626

DTD (оригинальный механизм, определенный как часть спецификации XML) и различные


предложения стандартов схем (новые механизмы, использующие синтаксис XML для
описания критериев верификации).
Среди других будущих стандартов, близких к завершению является стандарт XSL - механизм
для настройки правил трансляции XML-документов (например, в HTML или другой XML)
и для описания правил визуализации документа. Часть этого стандарта, предназначенная
для преобразований, XSLT(+XPATH) завершена и описана в этом руководстве. Еще один
стандарт, близкий к завершению - спецификация XML Link Language (XML Linking),
разрешающая связи между XML-документами.
Это основные стандарты, с которыми вы должны быть хорошо знакомы. В этом разделе
приведен также обзор нескольких других интересных предложений, включая HTML-подобный
стандарт, XHTML, и мета-стандарт для описания информации, содержащейся в XML-
документе, RDF. Продолжается работа также над стандартами, расширяющими
возможности XML, например Xlink и Xpointer.
Наконец, существует несколько интересных стандартов и предложений стандартов, которые
построены на XML, включая Synchronised Multimedia Integration Language (SMIL), Mathemat-
ical Markup Language (MathML), Scalable Vector Graphics (SVG) и DrawML, а также несколько
стандартов eCommerce.
В остальной части этой главы приведено более детальное описание этих стандартов. Для
ясности они разделены на:
• Основные стандарты
• Стандарты схем
• Стандарты связи и представления
• Стандарты знаний
• Стандарты, построенные на XML
Бегло просмотрите термины один раз для ознакомления и храните копию этого документа
под рукой, чтобы можно было обратиться к нему при встрече этих терминов где-нибудь
еще. Очень скоро все они зафиксируются в вашей памяти, и вы станете "хорошо знакомы"
с XML!

3.2.1. Основные стандарты


Существуют основные стандарты, с которыми вы должны быть хорошо знакомы. Они
присутствуют практически в любом обсуждении XML.

3.2.1.1. SAX
Простой API для XML.
Этот API был фактически продуктом совместной работы участников списка рассылки XML-
DEV, а не продуктом W3C. Он включен сюда потому, что имеет такие же "окончательные"
характеристики, что и рекомендации W3C.
Вы можете рассматривать этот стандарт как протокол "последовательного доступа" к XML.
Он является быстрым механизмом, который можно применить, например, для чтения или
записи XML-данных на сервер. Его называют также протоколом, управляемым событиями,
поскольку техника работы с ним следующая: регистрация вашего обработчика в SAX-
анализаторе, после чего анализатор обращается к вашим методам обратного вызова при

Web-

Rendered by www.RenderX.com
Стр. 40 из 626 Освоение XML

обнаружении нового XML-тега (либо встречает ошибку, либо хочет передать вам что-нибудь
еще).
Более детальная информация по протоколу SAX находится в разделе "Простой API для
XML".

3.2.1.2. DOM
Document Object Model (объектная модель документа).
Протокол Document Object Model преобразует XML-документ в набор объектов вашей
программы. После этого вы можете манипулировать ими любым способом. Этот механизм
известен также под названием - протокол "произвольного доступа", поскольку можно
обратиться к любой части данных в любое время. Вы можете затем модифицировать
данные, удалить их или вставить новые. Более подробная информация по спецификации
DOM находится в разделе Document Object Model.

3.2.1.3. JDOM и dom4j


В то время как Document Object Model (DOM) обеспечивает большие возможности
документо-ориентированной обработки, он не предоставляет чего-нибудь значительного
в области объектно-ориентированного подхода. Java-разработчики, работающие со
структурами, более ориентированными на данные, а не с книгами, статьями или другими
полностью готовыми документами, часто находят, что объектно-ориентированные API,
такие как JDOM и dom4j, легче в использовании и больше подходят под их требования.
Ниже приведены важные различия, которые нужно принимать во внимание при выборе
одного из этих API:
• JDOM немного более понятный, меньший по объему API. Когда важен "стиль
кодирования", JDOM - это хороший выбор.
• JDOM - это разработка Java Community Process (JCP). После завершения он станет
рекомендованным стандартом.
• dom4j - маленькая, быстрая реализация, находящая широкое применение в течение
уже нескольких лет.
• dom4j - является реализацией, основанной на генераторе. Это облегчает использование
в сложных приложениях специального назначения. На момент написания руководства
JDOM еще не использовал генератор для создания экземпляра анализатора (хотя
стандарт, кажется, продвигается в этом направлении). То есть, в JDOM вы всегда
получаете оригинальный анализатор. (Это хорошо для большинства приложений, но
не подходит для приложений специального назначения.)
Более подробная информация по JDOM находится на странице http://www.jdom.org/.
Более подробная информация по dom4j находится на странице http://dom4j.org/.

3.2.1.4. DTD
Document Type Definition (определение типа документа).
Спецификация DTD является фактически частью спецификации XML, а не отдельной
сущностью. С другой стороны, она не обязательна - вы можете написать XML-документ
без нее. И существует несколько альтернативных стандартов схем, предлагающих более
гибкие возможности. Поэтому DTD рассматривается здесь как отдельная спецификация.

Web-

Rendered by www.RenderX.com
XML и связанные спецификации: освоение алфавитной путаницы Стр. 41 из 626

DTD определяет типы тегов, которые можно использовать в XML-документе, и их


правильное расположение. DTD можно использовать для гарантии корректности XML-
структуры. Вы можете использовать ее также для проверки корректности XML-структуры,
которую читаете (или которая передается по сети).
К сожалению, очень трудно сформировать DTD для сложного документа так, чтобы она
предотвращала все неверные комбинации и разрешала все правильные. Создание DTD
представляет собой в какой-то степени искусство. DTD может существовать в самом
документе, как часть пролога. Она может существовать также в виде отдельной сущности,
или может быть разделена между прологом документа и одной или несколькими
дополнительными сущностями.
Однако, хотя механизм DTD был первым методом указания корректной структуры документа,
он не был последним. Было разработано несколько новых спецификаций схем. Очень
скоро вы узнаете про них.
Детальная информация приведена в разделе Создание Document Type Definition (DTD).

3.2.1.5. Пространство имен


Стандарт пространства имен позволяет вам написать XML-документ, использующий два
и более набора XML-тегов модульным способом. Предположим, что вы создали основанный
на XML список деталей, использующий XML-описания деталей, предлагаемых другими
производителями (в режиме онлайн!). Элемент "price" для субкомпонентов может означать
суммарную цену выбранных деталей, в то время как элемент "price" для структуры в целом
может означать что-либо для отображения. Спецификация пространства имен определяет
механизм уточнения имен для устранения двусмысленности. Она позволяет вам писать
программы, использующие информацию из других источников и правильно работающие
с ней.
Последняя информация по пространству имен находится на странице
http://www.w3.org/TR/REC-xml-names.

3.2.1.6. XSL
Extensible Stylesheet Language (расширяемый язык таблиц стилей).
XML-стандарт определяет способ интерпретации данных, а не способ их отображения.
HTML, с другой стороны, определяет, как данные должны быть отображены, без указания
их значения. XSL-стандарт имеет две части, XSLT (стандарт преобразования, описываемый
ниже) и XSL-FO (часть, описывающая объекты форматирования, известные, также, под
названием поток объектов). XSL-FO дает возможность определить различные области на
странице и затем связать их вместе. Когда текстовый поток направляется в такую
коллекцию, текст заполняет полностью первую область, а затем "перетекает" во вторую
область. Такие объекты используются в почтовых рассылках, каталогах и периодических
изданиях.
Последние работы W3C по XSL находятся на странице http://www.w3.org/TR/WD-xsl.

3.2.1.7. XSLT (+XPATH)


Extensible Stylesheet Language for Transformations (расширяемый язык таблиц стилей для
преобразований).

Web-

Rendered by www.RenderX.com
Стр. 42 из 626 Освоение XML

Стандарт преобразований XSLT является по существу механизмом преобразования,


дающим возможность указать, как преобразовать XML-тег так, чтобы его можно было
отобразить, например, в HTML. Различные XSL-форматы могут быть использованы для
отображения тех же самых данных различными способами для различных пользователей.
(Стандарт XPATH является механизмом адресации, используемым при создании инструкций
преобразования для указания частей XML-структуры, которые необходимо преобразовать.)
Дополнительная информация находится в разделе XML Stylesheet Language for Transfor-
mations.

3.2.2. Стандарты схем


DTD дает возможность проверять структуру относительно простых XML-документов, но
это все, на что он способен.
DTD не может ограничить содержимое элементов и не может определить сложные связи.
Например, в DTD невозможно определить, что <heading> для <book> должен иметь и <title>
и <author>, в то время как <heading> для <chapter> должен иметь только <title>. В DTD
можно определить структуру элемента <heading> только один раз. В нем нет
чувствительности к контексту.
Это происходит из-за того факта, что спецификация DTD не является иерархической.
Например, для почтового адреса, содержащего несколько элементов PCDATA ("parsed
character data"), DTD может выглядеть следующим образом:

<!ELEMENT mailAddress (name, address, zipcode)>


<!ELEMENT name (#PCDATA)>
<!ELEMENT address (#PCDATA)>
<!ELEMENT zipcode (#PCDATA)>
Как вы можете заметить, спецификации являются линейными. Этот факт заставляет вас
записывать новые имена похожих элементов в различных местах. Так что если вы захотите
добавить еще один элемент "name" в DTD, содержащий <firstName>, <middleInitial> и
<lastName> вы должны записать другой идентификатор. Вы не можете просто назвать его
"name" без возникновения конфликта с элементом <name>, определенным в <mailAddress>.
Другой проблемой не иерархической природы спецификации DTD является неясность
того, к чему относятся комментарии. Такой комментарий в начале как <!-- Адрес,
используемый для переписки через почтовую систему --> может относиться ко всем
элементам, составляющим почтовый адрес. Но такой комментарий как <!-- Адресат -->
может относиться только к элементу name. С другой стороны, такой комментарий как <!--
Строка из пяти цифр --> может относиться только к части #PCDATA элемента zipcode для
описания правильного формата. Наконец, DTD не позволяет формально определять
критерии верификации полей, такие как, например, ограничение в 5 цифр (или 5 и 4) в
поле zipcode.
И, наконец, DTD использует синтаксис, существенно отличающийся от XML, и поэтому не
может обрабатываться стандартным XML-анализатором. Это означает, что нельзя прочитать
DTD в DOM, например, для его модификации и сохранения.

Web-

Rendered by www.RenderX.com
XML и связанные спецификации: освоение алфавитной путаницы Стр. 43 из 626

Для преодоления этих недостатков были сделаны несколько предложений более похожих
на базы данных иерархических схем, определяющих критерии верификации. Основные
предложения обсуждаются ниже.

3.2.2.1. XML Schema


Это большой комплексный стандарт, имеющий две части. Одна часть определяет
структурные взаимосвязи. (Это самая большая и наиболее сложная часть.) Другая часть
определяет механизмы проверки содержимого XML-элементов при помощи определения
типов данных (возможно очень сложных) для каждого элемента. Хорошей новостью
является то, что XML Schema for Structures позволяет указать любой тип взаимосвязей,
какие можно только представить. Плохой новостью является большой объем работы для
их реализации и некоторое время на изучение. Большинство из альтернатив предоставляют
более простые определения структуры, чем стандарт типов данных XML Schema.
Дополнительная информация по XML Schema находится в спецификации W3C XML Schema
(Structures) и XML Schema (Datatypes) вместе с другой дополнительной информацией на
странице http://www.w3.org/XML/Schema.

3.2.2.2. RELAX NG
Regular Language description for XML (описание языка регулярных выражений для XML).
Более простым, чем XML Structure Schema, является появляющийся стандарт под
протекцией OASIS (Organization for the Advancement of Structured Information Systems) -
Организации по развитию структурированных информационных систем. RELAX NG
использует модель регулярных выражений для указания ограничений структурных
взаимосвязей. Этот стандарт разработан для работы с механизмом определения типов
данных XML Schema и указания ограничений их содержимого. Он использует синтаксис
XML и содержит преобразователь DTD в RELAX. ("NG" обозначает "Next Generation". Это
более новая версия механизма схем RELAX, которая интегрирует в себя TREX.)
Дополнительная информация по RELAX NG находится на странице http://www.oasis-
open.org/committes/relax-ng/.

3.2.2.3. TREX
Tree Regular Expressions for XML (дерево регулярных выражений для XML).
Это средство выражения критериев верификации путем описания шаблона структуры и
содержимого XML-документа. Сейчас является частью спецификации RELAX NG.
Дополнительная информация по TREX находится на странице http://www.thaiopen-
source.com/trex/.

3.2.2.4. SOX
Schema for Object-oriented XML (схема для объектно-ориентированного XML).
SOX является схемой, включающей расширяемые типы данных, пространства имен и
встраиваемую документацию.
Дополнительная информация по SOX находится на странице http://www.w3.org/TR/NOTE-
SOX/.

Web-

Rendered by www.RenderX.com
Стр. 44 из 626 Освоение XML

3.2.2.5. Schematron
Схема для объектно-ориентированного XML.
Механизм схемы, основанный на утверждениях и предлагающий комплексную верификацию.
Дополнительная информация по механизму верификации Schematron находится на
странице http://www.ascc.net/xml/resource/schematron/schematron.html.

3.2.3. Стандарты связывания и представления


Бесспорно, двумя основными преимуществами HTML были способность иметь связи между
документами и способность создавать форматированные документы (и, в конечном итоге,
очень сложные форматированные документы). Следующие стандарты призваны сохранить
преимущества HTML в области XML, а также добавить новую функциональность.

3.2.3.1. XML Linking


Эти спецификации предоставляют разнообразные мощные механизмы связывания и,
конечно же, оказывают большое влияние на то, как используются XML-документы.
XLink
Протокол XLink - это спецификация обработки связей между XML-документами. Эта
спецификация предоставляет возможность организовывать некоторые довольно непростые
связи, включая двунаправленные ссылки, ссылки на несколько документов,
"расширяющиеся" ссылки, которые вставляют связанную информацию в ваш документ, а
не заменяют его новой страницей, ссылки между двумя документами, созданными в
третьем, независимом документе, и непрямые ссылки (так что вы можете указать на
"адресную книгу" а не прямо на нужный документ - обновление адресной книги
автоматически изменит все ссылки, ее использующие).
XML Base
Этот стандарт определяет атрибут XML-документов, в котором указывается "базовый"
адрес, используемый при вычислении указанного в документе относительного адреса.
(Так, например, простое имя файла может быть найдено в указанном в базовом адресе
каталоге.)
XPointer
В общем случае, спецификация XLink ссылается на документ или сегмент документа,
используя его ID (идентификатор). Спецификация XPointer определяет механизмы для
"адресации во внутренние структуры XML-документов" без необходимости указания автором
документа ID для этого сегмента. Цитируя спецификацию, она обеспечивает "обращение
к элементам, строкам символов и другим частям XML-документов, независимо от того,
имеют они или нет явный атрибут ID".
Дополнительная информация по стандартам XML Linking находится на странице
http://www.w3.org/XML/Linking.

3.2.3.2. XHTML
Спецификация XHTML - это способ создания XML-документов, которые выглядят и ведут
себя так же как HTML-документы. Поскольку XML-документ может содержать любые теги,
которые вы позаботились определить, почему бы не определить набор тегов, выглядящих
как HTML? Во всяком случае, именно эта идея стоит за спецификацией XHTML. Результатом
применения этой спецификации является документ, который может отображаться в

Web-

Rendered by www.RenderX.com
XML и связанные спецификации: освоение алфавитной путаницы Стр. 45 из 626

броузерах, а также обрабатываться как XML-данные. Данные могут быть не настолько


идентифицируемыми, как в "чистом" XML, но ими можно будет намного легче
манипулировать, чем в стандартном HTML, поскольку XML предлагает намного большую
регулярность и логичность.
Например, каждый тег в формально-правильном XML-документе должен иметь
завершающий тег или должен заканчиваться знаками />. То есть, вы можете увидеть
<p>...</p> или <p/>, но вы никогда не увидите стоящего отдельно <p>. Из-за этого
требования вы никогда не должны программировать непонятные конструкции, которые
можно увидеть в HTML, где, например, тег <dt> может завершаться тегом </DT>, другим
<DT>, <dd> или </dl>. Это намного облегчает написание кода!
Спецификация XHTML - это перенос HTML 4.0 в XML. Последняя информация находится
на странице http://www.w3.org/TR/xhtml1.

3.2.4. Стандарты знаний


Если оглянуться на весь путь развития информации в Web за последние пять или шесть
лет, можно заметить, что она превратилась в одну огромную базу знаний ("семантика
Web"). Последняя информация по этому вопросу находится на странице
http://www.w3.org/2001/sw/.
Между тем, вот фундаментальные стандарты, о которых вы должны знать:

3.2.4.1. RDF
Resource Description Framework (среда описаний ресурсов).
RDF является стандартом для определения метаданных - информации, описывающей,
чем является элемент данных и как он может быть использован. Используемый совместно
со спецификацией XHTML например, или с HTML-страницами, RDF может применяться
для описания содержимого страниц. Например, если ваш броузер сохранил вашу
идентификационную информацию как FIRSTNAME, LASTNAME и EMAIL, описание RDF
могло бы сделать возможной передачу данных в приложение, нуждающееся в NAME и
EMAILADDRESS. Только представьте: однажды вам не надо будет вводить имя и адрес
на каждом Web-сайте, который вы посещаете!
Последняя информация по RDF находится на странице http://www.w3.org/TR/REC-rdf-syntax.

3.2.4.2. RDF Schema


RDF Schema предоставляет спецификацию логических правил и дополнительную
информацию, описывающую, как должны интерпретироваться операторы в RDF.
Дополнительная информация по рекомендациям RDF Schema находится на странице
http://www.w3.org/TR/rdf-schema.

3.2.4.3. XTM
XML Topic Maps (карты тем XML).
Являясь по многим параметрам более простым, а также более готовым к использованию
представлением знаний, чем RDF, стандарт Topic Maps заслуживает внимания. До
настоящего времени RDF является стандартом W3C для представления знаний, но карты
тем могут, по-видимому, стать "выбором разработчика" среди стандартов представлений
знаний.

Web-

Rendered by www.RenderX.com
Стр. 46 из 626 Освоение XML

Дополнительная информация по XML Topic Maps находится на странице


http://www.topicmaps.org/xtm/index.html. Информацию по картам тем и Web можно найти
на сайте http://www.topicmaps.org/.

3.2.5. Стандарты, основанные на XML


Следующие стандарты и предложения основываются на XML. Поскольку XML является в
основном средством описания языка, эти спецификации используют его для определения
стандартизованных языков специального применения.

3.2.5.1. Extended Document Standards (стандарты расширенных документов)


Эти стандарты определяют механизмы для создания очень сложных документов - книг,
журналов, газет и т.п. - с использованием XML.

3.2.5.1.1. SMIL
Synchronized Multimedia Integration Language (язык синхронной интеграции мультимедиа).
SMIL - это рекомендация W3C, относящаяся к аудио, видео и анимации. Он также
предназначен для решения сложных вопросов синхронизации воспроизведения этих
элементов.
Дополнительная информация по SMIL находится на странице http://www.w3.org/TR/REC-
smil.

3.2.5.1.2. MathML
Mathematical Markup Language (язык математической разметки).
MathML - это рекомендация W3C, имеющая дело с представлением математических
формул.
Дополнительная информация по MathML находится на странице http://www.w3.org/TR/REC-
MathML.

3.2.5.1.3. SVG
Scalable Vector Graphics (масштабируемая векторная графика).
(Векторные графические изображения строятся на основе команд типа "начертить линию
(квадрат, круг) из точки xi в точку m,n", в отличие от кодирования изображений в виде
последовательности бит. Такие изображения легче масштабируются, хотя обычно они
требуют больше времени обработки для визуализации.)
Дополнительная информация по SVG находится на странице http://www.w3.org/TR/WD-
SVG.

3.2.5.1.4. DrawML
Drawing Meta Language (метаязык для рисования).
DrawML - это комментарий W3C, относящийся к 2D-изображениям для технических рисунков.
Он также предназначен для решения проблемы обновления и повышения качества таких
изображений.
Дополнительная информация по DrawML находится на странице http://www.w3.org/TR/NOTE-
drawml.

Web-

Rendered by www.RenderX.com
XML и связанные спецификации: освоение алфавитной путаницы Стр. 47 из 626

3.2.5.2. Стандарты электронной коммерции (eCommerce)


Эти стандарты нацелены на использование XML в мире business-to-business (B2B - бизнес-
бизнес) и business-to-consumer (B2C - бизнес-потребитель) коммерции.

3.2.5.2.1. ICE
Information and Content Exchange (обмен информацией и содержимым).
ICE - это протокол для использования содержимого поставщиками и их подписчиками. Он
сконцентрирован на "автоматизации обмена и повторного использования содержимого,
как в традиционных публикациях, так и B2B-отношениях".
Дополнительная информация по ICE находится на странице http://www.w3.org/TR/NOTE-
ice.

3.2.5.2.2. ebXML
Electronic Business with XML (электронный бизнес с использованием XML).
Этот стандарт предназначен для создания модульной среды электронного бизнеса с
использованием XML. Он является продуктом совместной инициативы организаций United
Nations (UN/CEFACT) и OASIS.
Дополнительная информация по ebXML находится на сайте http://www.ebxml.org/.

3.2.5.2.3. cxml
Commerce XML (коммерческий XML).
cxml - это стандарт RosettaNet (www.rosettanet.org) для настройки интерактивных онлайн-
каталогов для различных покупателей, в которых цены и предложения продуктов зависят
от компании. В него включены механизмы обработки заказов на поставку, изменений
заказов, обновлений состояния и уведомлений о доставке.
Дополнительная информация по cxml находится на сайте http://www.cxml.org/.

3.2.5.2.4. CBL
Common Business Library (библиотека бизнеса общего назначения).
CBL - это библиотека элементов и определений атрибутов, поддерживаемых CommerceNet
(www.commerce.net).
Дополнительная информация по CBL и множеству других инициатив, работающих совместно
в приложениях eCommerce, находится на странице
http://www.commerce.net/projects/currentprojects/eco/wg/eCo_Framework_Specifications.html.

3.2.5.2.5. UBL
Universal Business Library (бизнес-библиотека универсального назначения).
Инициатива OASIS, нацеленная на формирование стандартной библиотеки XML бизнес-
документов (заказов, счетов и т.д.), определяемых в описаниях XML Schema.
Дополнительная информация по UBL находится на странице http://www.oasis-
open.org/committees/ubl.

Web-

Rendered by www.RenderX.com
Стр. 48 из 626 Освоение XML

3.2.6. Итоги
XML становится общепринятым стандартом, используемым в огромном разнообразии
прикладных областей.

3.3. Разработка структуры данных XML


В этом разделе рассмотрены некоторые эвристические подходы к принятию тех или иных
решений при создании XML-документа.

3.3.1. Облегчение своей работы


Когда это возможно, используйте существующее определение схемы. Всегда легче
игнорировать ненужные элементы, чем создавать свои собственные с нуля. Кроме того,
использование DTD делает возможным обмен данными и может сделать возможным
использование программных средств, разработанных другими.
Итак, если существует отраслевой стандарт, используйте ссылку на его DTD. Одним из
мест для поиска отраслевых стандартов DTD является архив, созданный Organization for
the Advancement of Structured Information Standards (OASIS - организацией по развитию
стандартов структурированной информации), находящийся на сайте http://www.XML.org.
Другим местом для проверки является XML Exchange от CommerceOne на сайте
http://www.xmlx.com, который описывается как "архив для создания и совместного доступа
к определениям типов документов".
Примечание: Очень много хороших рекомендаций по созданию XML-структур находится
на странице OASIS http://www.oasis-open.org/cover/elementsAndAttrs.html.

3.3.2. Атрибуты и элементы


Одним из вопросов, с которым вы будете часто сталкиваться при разработке XML-структуры,
является вопрос, как смоделировать какой-либо элемент данных как субэлемент, или как
атрибут существующего элемента. Например, вы можете смоделировать заголовок слайда
либо как:

<slide>
<title>This is the title</title>
</slide>
либо как:

<slide title="This is the title">...</slide>


В некоторых случаях различные характеристики атрибутов и элементов облегчают выбор.
Рассмотрим сначала именно такие случаи, а затем перейдем к таким, где выбор более
неоднозначный.

3.3.2.1. Вынужденный выбор


Иногда выбор между атрибутом и элементом очень прост из-за их природы. Рассмотрим
несколько таких случаев:

Web-

Rendered by www.RenderX.com
Разработка структуры данных XML Стр. 49 из 626

Данные содержат подструктуры


В этом случае данные должны быть представлены как элемент. Он не может быть
представлен как атрибут, поскольку атрибуты могут содержать только простые строки. То
есть, если заголовок может содержать выделенный текст, как, например, The
<em>Best</em> Choice, то заголовок должен быть элементом.
Данные содержат несколько строк
Здесь тоже имеет смысл использовать элемент. Атрибуты должны быть простыми,
короткими строками, иначе они станут нечитаемыми, или вообще непригодными к
использованию.
Возможны несколько экземпляров
Если элемент данных может встретиться несколько раз, например параграфы в строке,
он должен быть представлен как элемент. Элемент, содержащий такие данные, может
иметь только один атрибут данного типа, но может иметь много субэлементов того же
типа.
Нет часто изменяемых данных
Если данные будут часто меняться при помощи редактора, имеет смысл представить их
как элемент. Многие из XML-редакторов легко позволяют модифицировать элементы
данных, в то время как к атрибутам иногда нелегко получить доступ.
Данные представляют собой короткую, простую строку, которая редко или никогда не
меняется
Такие данные могут быть представлены как атрибуты. Однако то, что вы можете сделать,
не означает то, что вы должны это сделать. Прочитайте следующий раздел "Стилистический
выбор", чтобы быть уверенным в своих действиях.
Использование DTD в том случае, когда данные ограничены небольшим количеством
фиксированных вариантов
Это как раз тот случай, когда имеет смысл использовать атрибуты. DTD может защитить
атрибут от получения значения, не входящего в предопределенный список, но не может
таким же образом ограничить элемент. (С другой стороны, схема может ограничить и
атрибуты и элементы.)

3.3.2.2. Стилистический выбор


Очень часто выбор не так тривиален, как описано выше. Когда выбор не является
вынужденным, вам нужно чувство "стиля" для направления хода мыслей. Следовательно,
нужно ответить на вопрос - что создает хороший XML-стиль, и почему.
Определение чувства стиля для XML, к сожалению, такое же неблагодарное дело, как и
определение "стиля" в искусстве или музыке. Однако существует несколько путей для
достижения этого. Цель этого раздела - изложить несколько полезных мыслей по вопросу
"XML-стиля".
Видимость
Один из эвристических подходов к выбору между XML-элементами и атрибутами использует
концепцию видимости. Если данные планируется показывать - отобразить для какого-либо
конечного пользователя - то они должны быть представлены как элемент. С другой стороны,

Web-

Rendered by www.RenderX.com
Стр. 50 из 626 Освоение XML

если информация предназначена для XML-обработки и никогда не показывается


пользователю, то ее лучше представить в виде атрибута. Например, в форме заказа обуви,
размер ее определенно может быть элементом. С другой стороны, кодовый номер
производителя разумно представить атрибутом.
Потребитель/поставщик
Другим подходом является ответ на вопрос - кто является потребителем и/или поставщиком
информации. Размер обуви вводится человеком-продавцом, то есть это - элемент. С другой
стороны кодовый номер производителя для данной модели обуви может быть передан в
приложение или записан в базу данных, то есть, он должен быть атрибутом. (Если бы он
вводился продавцом, он, возможно, был бы элементом.)
Контейнер против содержимого
Возможно, лучшим подходом к размышлению об элементах и атрибутах является
предположение, что элемент - это контейнер. Используя аналогию, содержимое контейнера
(вода или молоко) соответствует XML-данным, представленным как элементы. Такие
данные, по сути, являются переменными. С другой стороны, характеристики контейнера
(белый или синий кувшин) могут быть представлены как атрибуты. Такой тип информации
является более постоянным. Хорошим XML-стилем будет, следовательно, отделение
содержимого каждого контейнера от его характеристик.
Покажем эти эвристические подходы на практике. При показе слайдов тип слайда (для
администрации или для технического персонала) лучше всего представить в виде атрибута.
Это характеристика слайда, позволяющая выбрать или отложить слайд для конкретной
аудитории. Название слайда, с другой стороны, является частью его содержимого. Подход,
основанный на видимости, здесь тоже работает. При показе слайда его название
показывается, а тип слайда нет. Наконец в этом примере потребителем информации о
названии является аудитория, в то время как потребителем информации о типе является
программа презентации.

3.3.3. Нормализация данных


В разделе "Разработка структуры данных XML" показано, как создать внешний объект, на
который вы можете ссылаться в XML-документе. Такой объект обладает всеми
преимуществами модульной подпрограммы - изменение этой одной копии влияет на каждый
документ, к ней обращающийся. Процесс устранения избыточности известен под названием
нормализация, так что определение объектов - это один из хороших методов нормализации
ваших данных.
В HTML-файле единственным способом достичь такого типа модульности является
использование HTML-ссылок - естественно документ становится фрагментированным.
XML-сущности, с другой стороны, не страдают от такой фрагментации. Ссылка на сущность
работает как макроподстановка - содержимое объекта размещается в текущем месте,
создавая цельный документ, а не фрагментированный. И когда сущность определена во
внешнем файле, несколько документов могут ссылаться на нее.
Соображения для определения ссылки на сущность, следовательно, почти такие же, как
и те, какие вы применяете для разбиения на модули программного кода:
• Где бы вы ни обнаружили, что записываете один и тот же фрагмент более чем один
раз, подумайте об отдельной сущности. Это позволит вам записать ее один раз и
поставить ссылки в нескольких местах.

Web-

Rendered by www.RenderX.com
Разработка структуры данных XML Стр. 51 из 626

• Если существует вероятность изменения информации, особенно если она используется


более одного раза, определенно подумайте об определении сущности. Примером
является определение productName в виде сущности, для того чтобы можно было легко
изменить документы при изменении названия продукта.
• Если на сущность никогда не будет ссылок кроме текущего файла, определите ее в
local_subset DTD документа, точно также как вы определяете метод или внутренний
класс в программе.
• Если на сущность есть ссылки из нескольких документов, определите ее как внешнюю
сущность, так же как вы определяете любой общий класс в качестве внешнего класса.
Внешние сущности формируют XML-документ, который меньше по размерам, легче для
обновления и поддержки. Они могут также сделать результирующий документ несколько
более трудным для визуализации, также как и хороший OO-дизайн (объектно-
ориентированный) может быть легким для изменения, если вы понимаете его, но поначалу
тяжелым для просмотра, поскольку нужно мотать головой в разные стороны в поисках
нужных классов.
Вы можете также зайти слишком далеко при использовании сущностей. Впав в крайность,
вы можете создать ссылки на отдельную сущность для слова "the" - это не принесет вам
никакой выгоды, но вы можете сделать это.
Примечание: Чем больше сущность, тем менее вероятно, что ее изменение вызовет
побочные эффекты. При определении внешней сущности, охватывающей всю часть
инструкций по установке, например, при изменении в этой части очень маловероятно
испортить документы, зависящие от нее. Однако маленькие встроенные подстановки могут
быть более проблематичными. Например, если productName определен как сущность,
можно изменить имя в другую часть речи, и это может произойти. Предположим, что имя
продукта, например, "HtmlEdit". Это глагол. То есть вы пишете предложение, которое после
подстановки сущности выглядит так: "You can HtmlEdit your file...". Это предложение читается
нормально, поскольку глагол хорошо подходит в этот контекст. Но если имя случайно
изменить на "HtmlEditor", предложение станет таким: "You can HtmlEditor your file...", которое
явно не верно. Тем не менее, даже если такие подстановки могут иногда вызвать проблемы,
они могут потенциально сохранить много времени. (Одной из альтернатив было бы создание
сущностей с именем productNoun, productVerb, productAdj и productAdverb!)

3.3.4. Нормализация DTD


Также как вы можете нормализовать ваш XML-документ, вы можете нормализовать ваши
DTD-описания путем вынесения общих частей и создания ссылок на них при помощи
параметра. Этот процесс описан в руководства по SAX в разделе "Определение параметров
и условных секций". Выделение частей в DTD (называемое также как модуляризация или
нормализация) дает те же преимущества и недостатки, что и нормализованный XML-
документ - легко меняется, но немного трудно просматривается.
Вы можете также настроить условные DTD, как описано в разделе руководства по SAX
"Условные секции". Если количество и размер условных секций малы по сравнению с
размером целого DTD, вы можете создать "один источник" DTD, который сможете
использовать для разных целей. Если количество условных секций становится большим,
в результате может получиться сложный документ, который трудно редактировать.

Web-

Rendered by www.RenderX.com
Стр. 52 из 626 Начало работы с Tomcat

4. Начало работы с Tomcat


В этом разделе описываются процессы разработки, размещения и запуска простого Web-
приложения, состоящего из компонента JavaBeans™ - конвертора валют и клиентской
Web-страницы, созданной при помощи технологии JavaServer Pages (JSP). Это приложение
будет размещаться и запускаться в Tomcat - сервлете Java и контейнере JSP,
разработанном организацией The Apache Software Foundation (www.apache.org) и входящем
в Java Web Services Developer Pack (Java WSDP). Эта глава представляет собой введение
в использование Tomcat для размещения Web-служб и Web-приложений. Материал этой
главы является основой для других глав этого руководства.

4.1. Установка
Примечание: Перед началом работы с приложениями примера просмотрите инструкции
раздела "Об этом руководстве".

4.1.1. Получение кода примера


Исходный код примера находится в каталоге <JWSDP_HOME>/docs/tutorial/examples/gs/,
созданном при разархивации пакета руководства. Если вы просматриваете это руководство
в интерактивном режиме, этот пакет можно загрузить с:
http://java.sun.com/webservices/downloads/webservicestutorial.html

4.1.1.1. Схема кода примера


В этом примере приложения каталоги с исходным кодом организованы в соответствии со
статьей "Лучший практический подход к программированию Web-служб", находяйшейся в
файле <JSWDP_HOME>/docs/tomcat/appdev/deployment.html. В основном эта статья
рекомендует при создании приложения рассматривать организацию Web-приложения во
время его выполнения. Web-приложение в стандартной схеме определяется как иерархия
каталогов и файлов. Эта иерархия может быть доступна в распакованной форме, в которой
каждый каталог и файл существует в файловой системе отдельно, или в упакованной
форме, известной под названием архив Web-приложения, или WAR-файл (Web Application
Archive). Первый формат более полезен при разработке, в то время как второй формат
используется при распространении вашего приложения для установки.
Чтобы облегчить создание WAR-файла в нужном формате, удобно разместить файлы,
которые использует Tomcat при выполнении приложения, так, как требует сам WAR-формат.
В примере приложения <JWSDP_HOME>/docs/tutorial/examples/gs/ - это корневой каталог
с исходным кодом приложения. Приложение состоит из следующих файлов, находящихся
либо в каталоге /gs, либо его подкаталогах.
• - /src/converterApp/Converterbean.java - Компонент JavaBeans, содержащий методы get
и set для свойств yenAmount и euroAmount, используемых для конвертации долларов
США в йену и йены в евро.
• /web/index.jsp - Web-клиент, являющийся JSP-страницей и принимающий значение для
конвертации, кнопки для подтверждения значения и результат конвертации.
• /web/WEB-INF/web.xml - Дескриптор размещения для этого приложения. В этом простом
примере он содержит описание приложения.

Web-

Rendered by www.RenderX.com
Установка Стр. 53 из 626

• build.xml - Файл компоновки, использующий программу ant для компоновки и размещения


Web-приложения.
Более детальную информацию о WAR-файлах можно найти в разделе "Архивы Web-
приложения".
Ключевой рекомендацией "
Tomcat " является разделение иерархии каталогов, содержащих
исходный код, от иерархии каталогов, содержащих готовое к размещению приложение.
Выполнение этого разделения дает следующие преимущества:
• Содержимое каталогов с исходными кодами можно легко администрировать, перемещать
и копировать, если исполняемая версия приложения не смешивается с ними.
• Контролировать исходный код легче в каталогах, содержащих только файлы с исходным
кодом.
• Файлы, составляющие готовое к установке приложение, намного легче выбрать, когда
они размещены отдельно от остальных каталогов.
Как рассмотрено в разделе "Создание файлов компоновки и размещения для ant",
программа размещения ant упрощает создание и обработку такого типа иерархии каталогов.
В оставшейся части этого документа рассмотрены процессы создания, компоновки,
размещения и выполнения приложения примера. Если вы хотите пропустить информацию
по созданию приложения примера, можете перейти прямо к разделу "Краткий обзор".

4.1.2. Установка переменной PATH


Очень важно добавить каталоги bin установленных Java WSDP и J2SE SDK в начало вашей
переменной окружения PATH, для того чтобы начальные сценарии Java WSDP для Tomcat,
ant и deploytool, заменили сценарии из других инсталляций.
Примечание: Большинство примеров распространяются с конфигурационным файлом для
версии 1.4.1 ant - переносимой программы компоновки, входящей в Java WSDP. Если ваша
переменная PATH не указывает на каталог bin Java WSDP, многие из команд ant не будут
работать, поскольку версия ant, поставляемая с Java WSDP, устанавливает переменную
окружения jwsdp.home.

4.1.3. Создание файла свойств компоновки


Для того чтобы вызвать большинство из задач ant, вы должны поместить файл с именем
build.properties в ваш домашний каталог. В операционной системе Solaris ваш домашний
каталог обычно имеет формат /home/your_login_name. В операционных системах Windows
(например, Windows 2000) вашим домашним каталогом обычно являются C:\Documents и
Settings\yourProfile.
Файл build.properties содержит имя пользователя и пароль в текстовом формате, которые
соответствуют имени пользователя и паролю, указанным во время установки. Имя
пользователя и пароль, указанные вами во время установки Java WSDP, сохраняются в
файле <JWSDP_HOME>/conf/tomcat-users.xml.
В целях безопасности приложение Tomcat Manager проверяет, что именно вы (как
определено в файле build.properties) являетесь пользователем, которому позволено

Web-

Rendered by www.RenderX.com
Стр. 54 из 626 Начало работы с Tomcat

устанавливать и перезагружать приложения (как определено в tomcat-users.xml) перед


предоставлением вам доступа к серверу.
Если вы еще не создали файл build.properties в вашем домашнем каталоге, сделайте это
сейчас. Файл будет выглядеть примерно так:

username=your_username
password=your_password
Примечание: По соображениям безопасности сделайте файл build.properties не доступным
для чтения всем, кроме себя.
Файл tomcat-users.xml, который создается установщиком, выглядит примерно так:

<?xml version='1.0'?>
<tomcat-users>
<role rolename="admin"/>
<role rolename="manager"/>
<role rolename="provider"/>
<user username="your_username" password="your_password"
roles="admin,manager,provider"/>
</tomcat-users>

4.2. Краткий обзор


Теперь, когда вы загрузили приложение и установили ваше программное окружение для
выполнения приложений, мы приведем краткий обзор действий, необходимых для запуска
приложения. Каждое действие обсуждается более детально на указанных страницах.
1. Выполните действия из раздела "Установка".
2. Перейдите в каталог этого приложения - <JWSDP_HOME>/docs/tutorial/examples/gs
(см. раздел "Создание первого приложения").
3. Откомпилируйте исходные файлы, выполнив следующую команду (см. раздел
"Компоновка первого приложения с помощью ant").

ant build
Ошибки компиляции перечислены в разделе "Ошибки компиляции".
4. Запустите Tomcat, набрав в командной строке следующую команду (см. раздел "Запуск
Tomcat"):

<JWSDP_HOME>/bin/statup.sh ( Unix)

<JWSDP_HOME>\bin\startup (Microsoft Windows)

Web-

Rendered by www.RenderX.com
Создание первого приложения Стр. 55 из 626

5. Разместите Web-приложение при помощи ant, набрав в командной строке следующую


команду (см. раздел "Установка приложения при помощи ant"), или разместите Web-
приложение при помощи deploytool, следуя инструкциям раздела "Размещение
приложения при помощи deploytool":

ant install
Ошибки размещения рассматриваются в разделе "Ошибки размещения".
6. Запустите Web-броузер. Введите следующий URL для запуска примера приложения
(см. раздел "Запуск первого приложения"):

http://localhost:8080/GSApp
7. Остановите Tomcat, набрав следующую команду (см. раздел "Остановка Tomcat"):

<JWSDP_HOME>/bin/shutdown.sh ( Unix)

<JWSDP_HOME>\bin\shutdown (Microsoft Windows)

4.3. Создание первого приложения


Приложение примера содержит класс ConverterBean, Web-компонент, файл для компоновки
и запуска приложения и дескриптор размещения. Для этого примера мы создадим каталог
исходных файлов проекта с именем gs/. Все файлы этого примера создаются из этого
корневого каталога.

4.3.1. Компонент ConverterBean


Компонент ConverterBean используется совместно с JSP-страницей. Конечное приложение
является формой, позволяющей конвертировать американские доллары в иену и иену в
евро. Исходный код компонента ConverterBean находится в каталоге
<JWSDP_HOME>/docs/tutorial/examples/gs/src/converterApp/.

4.3.1.1. Кодирование компонента ConverterBean


Компонент ConverterBean этого примера содержит два свойства, yenAmount и euroAmount,
и методы get и set для этих свойств. Ниже приведен исходный код ConverterBean.

//ConverterBean.java
package converterApp;

import java.math.*;

Web-

Rendered by www.RenderX.com
Стр. 56 из 626 Начало работы с Tomcat

public class ConverterBean{

private BigDecimal yenRate;


private BigDecimal euroRate;
private BigDecimal yenAmount;
private BigDecimal euroAmount;

/** ConverterBean */
public ConverterBean() {
yenRate = new BigDecimal ("138.78");
euroRate = new BigDecimal (".0084");
yenAmount = new BigDecimal("0.0");
euroAmount = new BigDecimal("0.0");
}
public BigDecimal getYenAmount () {
return yenAmount;
}
public void setYenAmount(BigDecimal amount) {
yenAmount = amount.multiply(yenRate);
yenAmount = yenAmount.setScale(2,BigDecimal.ROUND_UP);
}
public BigDecimal getEuroAmount () {
return euroAmount;
}
public void setEuroAmount (BigDecimal amount) {
euroAmount = amount.multiply(euroRate);
euroAmount =
euroAmount.setScale(2,BigDecimal.ROUND_UP);
}
}

4.3.2. Web-клиент
Web-клиент содержится в JSP-странице <JWSDP_HOME>/docs/tutorial/exam-
ples/gs/web/index.jsp. JSP-страница представляет собой текстовый документ, содержащий
статическое и динамическое содержимое. Статическое содержимое - это шаблонные
данные, которые могут быть выражены в любом текстовом формате, таком как HTML,
WML, или XML. Динамическое содержимое создают JSP-элементы.

4.3.2.1. Кодирование Web-клиента


JSP-страница, index.jsp, используется для создания формы, появляющейся в Web-броузере
при запуске клиентского приложения. JSP-страница является обычной смесью статической
HTML-разметки и JSP-элементов. Если вы разрабатывали Web-страницы, вы, возможно,

Web-

Rendered by www.RenderX.com
Создание первого приложения Стр. 57 из 626

знакомы со структурными элементами HTML-документа (<head>, <body> и т.д.) и HTML-


командами, создающими форму <form> и меню <select>. Выделенные строки в примере
содержат следующие типы JSP-элементов:
• Директивы (<%@page: %>) импортируют классы в класс ConverterBean и устанавливают
тип содержимого, возвращаемого страницей.
• Элемент jsp:useBean объявляет, что страница будет использовать компонент bean,
записанный внутри и доступный из указанной области действия. Областью действия
по умолчанию является страница, так что вы можете не устанавливать ее явно в этом
примере.
• Элемент jsp:setProperty используется для установки свойств компонента JavaBeans в
JSP-странице.
• Элемент jsp:getProperty используется для извлечения свойств компонента JavaBeans
в JSP-странице.
• Скриптлеты (<% : %>) извлекают значение параметра amount, преобразовывает его в
BigDecimal и конвертирует значение в иену или евро.
• Выражения (<%= : %>) вставляют значение amount в ответ.
Ниже приведен исходный код index.jsp.

<%-- index.jsp --%>


<%@ page import="converterApp.ConverterBean,java.math.*" %>
<%@ page contentType="text/html; charset=ISO-8859-5" %>

<html>
<head>
<title>Currency Conversion Application</title>
</head>

<body bgcolor="white">
"<jsp:useBean id="converter"
class="converterApp.ConverterBean"/>

<h1><FONT FACE="ARIAL" SIZE=12>Currency Conversion Application


</FONT></h1>
<hr>
<p><FONT FACE="ARIAL" SIZE=10>Enter an amount to convert:</p>
</FONT>
<form method="get">
<input type="text" name="amount" size="25">
<br>
<p>
<input type="submit" value="Submit">

Web-

Rendered by www.RenderX.com
Стр. 58 из 626 Начало работы с Tomcat

<input type="reset" value="Reset">


</form>
<%
String amount = request.getParameter("amount");

if ( amount != null && amount.length() > 0 ) {

%>
<p><FONT FACE="ARIAL" SIZE=10><%= amount %> dollars are

<jsp:setProperty name="converter" property="yenAmount"


value="<%= new BigDecimal(amount)%>" />
<jsp:getProperty name="converter" property="yenAmount" /> Yen.

<p><%= amount %> Yen are

<jsp:setProperty name="converter" property="euroAmount"


value="<%= new BigDecimal(amount)%>" />
<jsp:getProperty name="converter" property="euroAmount" />
Euro. </FONT>

<%
}
%>

</body>
</html>

4.4. Компоновка первого приложения при помощи ant


Сейчас пример Web-приложения готов к компоновке.
Текущая редакция Java Web Services Developer Pack включает ant - make-программу,
являющуюся переносимой между платформами и разработанную организацией Apache
Software Foundation (http://www.apache.org). Документация по программе ant находится в
файле index.html в каталоге <JWSDP_HOME>/docs/ant/ вашей установки Java WSDP.
Примечание: Очень важно, чтобы ваша переменная окружения PATH содержала каталог
bin Java WSDP в самом начале. Если это не так, многие из команд ant не будут работать,
поскольку версия ant, поставляемая с Java WSDP, устанавливает переменную окружения
jwsdp.home, а другие версии ant - нет.
В этом примере программа ant используется для управления компиляцией исходных
файлов Java и созданием иерархии размещения. ant работает под управлением файла
компоновки, обычно называемого build.xml, который определяет требуемые действия. Этот
файл хранится в корневом каталоге вашей иерархии исходных файлов.

Web-

Rendered by www.RenderX.com
Компоновка первого приложения при помощи ant Стр. 59 из 626

Как и makefile, файл build.xml обеспечивает выполнение разных заданий, в том числе и
необязательных (таких как удаление домашнего каталога размещения, что бы вы могли
скомпоновать ваш проект с нуля). Этот файл компоновки включает задания по компиляции
приложения, установке приложения на работающем сервере, перезагрузке
модифицированного приложения на работающем сервере и удаление старых копий
приложения для повторной генерации их содержимого.
При использовании файла build.xml в этом примере для компиляции исходных файлов
создается временный каталог /build в корневом каталоге. Этот каталог содержит точный
образ двоичных установочных файлов вашего Web-приложения. Этот каталог удаляется
и создается заново по необходимости в процессе разработки, так что не редактируйте
файлы в этом каталоге.

4.4.1. Создание файла компоновки и размещения для ant


Чтобы использовать программу ant для этого примера, создайте файл build.xml в каталоге
gs/. Его исходный код приведен ниже.

<!-

-->
<project name="gs-example" default="build" basedir=".">
<target name="init">
<tstamp/>
</target>

<!-- PATH
-->
<property name="example" value="GSApp" />
<property name="path" value="/${example}"/>
<property name="build"
value="${jwsdp.home}/docs/tutorial/examples/${example}/build"
/>

<!--
Manager -->
<property name="url" value="http://localhost:8080/manager"/>
<property file="build.properties"/>
<property file="${user.home}/build.properties"/>

<!-- Ant
Manager -->

Web-

Rendered by www.RenderX.com
Стр. 60 из 626 Начало работы с Tomcat

<path id="classpath">
<fileset dir="${jwsdp.home}/common/lib">
<include name="*.jar"/>
</fileset>
</path>
<taskdef name="install"
classname="org.apache.catalina.ant.InstallTask" />
<taskdef name="reload"
classname="org.apache.catalina.ant.ReloadTask" />
<taskdef name="remove"
classname="org.apache.catalina.ant.RemoveTask"/>

<target name="prepare" depends="init" description="Create


build directories.">
<mkdir dir="${build}" />
<mkdir dir="${build}/WEB-INF" />
<mkdir dir="${build}/WEB-INF/classes" />
</target>

<!-- -->

<target name="install" description="Install Web application"


depends="build">
<install url="${url}" username="${username}"
password="${password}" path="${path}"
war="file:${build}"/>
</target>

<target name="reload" description="Reload Web application"


depends="build">
<reload url="${url}" username="${username}"
password="${password}" path="${path}"/>
</target>

<target name="remove" description="Remove Web application">


<remove url="${url}" username="${username}"
password="${password}" path="${path}"/>
</target>

<target name="build" depends="prepare" description="Compile


app Java files and copy HTML and JSP pages" >

Web-

Rendered by www.RenderX.com
Размещение приложения Стр. 61 из 626

<javac srcdir="src" destdir="${build}/WEB-INF/classes">


<include name="**/*.java" />
<classpath refid="classpath"/>
</javac>
<copy todir="${build}/WEB-INF">
<fileset dir="web/WEB-INF" >
<include name="web.xml" />
</fileset>
</copy>
<copy todir="${build}">
<fileset dir="web">
<include name="*.html"/>
<include name="*.jsp" />
<include name="*.gif" />
</fileset>
</copy>
</target>

</project>

4.4.2. Компиляция исходных файлов


Для компиляции компонента JavaBeans (Converter.java) мы будем использовать программу
ant и запустим задние build в файле build.xml. Последовательность действий следующая:
1. В терминальном окне перейдите в каталог gs/, если вы создаете приложение
самостоятельно, или перейдите в каталог <JWSDP_HOME>/docs/tutorial/examples/gs/,
если вы компилируете файлы примеров, загруженные вместе с руководством.
2. Выполните следующую команду для компоновки Java-файлов.

ant build
Эта команда компилирует файлы для ConverterBean. Она размещает создаваемый
файл классов в каталог <JWSDP_HOME>/docs/tutorial/examples/Gapp/build/WEB-
INF/classes/converterApp, как указано в задании build в файле build.xml. Она также
помещает файл index.jsp в каталог GSApp/build и файл web.xml в каталог
GSApp/build/WEB-INF. Tomcat позволяет вам размещать приложение в такой
неупакованной форме. Размещение приложения рассматривается в разделе
"Размещение приложения".

4.5. Размещение приложения


В этой редакции Java WSDP существует два варианта размещения приложения:
использование программы ant и использование Application Deployment Tool. Для этого

Web-

Rendered by www.RenderX.com
Стр. 62 из 626 Начало работы с Tomcat

примера оба варианта требуют запуска Tomcat. Дополнительная информация по


размещению Web-приложений находится в разделе "Размещение Web-приложений".

4.5.1. Запуск Tomcat


Для запуска Tomcat введите следующую команду в терминальном окне.

<JWSDP_HOME>/bin/statup.sh ( Unix)

<JWSDP_HOME>\bin\startup (Microsoft Windows)

Начальный сценарий запускает задачу в фоновом режиме и затем сразу возвращается в


командную строку. Tomcat может запускаться полностью в течение нескольких минут.
Примечание: Начальный сценарий для Tomcat может выполняться несколько минут до
завершения. Для проверки выполнения Tomcat введите в вашем броузере http://local-
host:8080. Если отобразится начальный экран Tomcat, вы можете продолжать. Если
начальный экран не загрузится сразу, подождите несколько минут и повторите попытку.
Если через несколько минут начальный экран Tomcat все же не появился, обратитесь к
разделу по поиску неисправностей "Ошибка Unable to Locate the Server localhost:8080".
Документация по Tomcat находится в файле <JWSDP_HOME>/docs/tomcat/index.html.

4.5.2. Установка приложения при помощи ant


Web-приложение определяется в стандартной схеме как иерархия каталогов и файлов. В
этом примере иерархия доступна в неупакованной форме, при которой каждый каталог и
файл существует в файловой системе отдельно. В этом разделе рассматривается
процедура размещения приложения при помощи программы ant, определенная в разделе
"Создание файла компоновки и размещения для ant".
Контекст представляет собой имя, которое отображается в корневой каталог документов
Web-приложения. Контекстом для приложения GSApp является /GSAPP. Запрос URL
http://localhost:8080/GSApp/index.html извлекает файл index.html из корневого каталога
документов. Для установки приложения в Tomcat нужно проинформировать Tomcat о
доступности нового контекста.
Уведомление Tomcat о новом контексте производится при помощи задачи ant install в
файле build.xml. Задача ant install не требует перезагрузки Tomcat, но установленное
приложение не запоминается после перезагрузки Tomcat. Для постоянного размещения
приложения обратитесь к разделу "Размещение Web-приложений".
Задача ant install указывает менеджеру приложений Tomcat установить приложение с
контекстом, указанным в атрибуте path, и месторасположением, содержащим файлы Web-
приложения. Прочитайте раздел "Установка Web-приложений" для получения
дополнительной информации по этой процедуре. Для размещения этого Web-приложения
нужно выполнить следующие действия:
1. В терминальном окне перейдите в каталог GSApp/.
2. Выполните следующую команду для размещения файлов Web-приложения:

Web-

Rendered by www.RenderX.com
Размещение приложения Стр. 63 из 626

ant install
Эта команда копирует файл Web-клиента index.jsp в каталог
<JWSDP_HOME>/docs/tutorial/examples/GSApp/build/ и копирует файл классов
компонента JavaBeans ConverterBean.class в каталог <JWSDP_HOME>/docs/tutorial/exam-
ples/gs/build/WEB-INF/classes/converterApp/.

4.5.3. Размещение приложения при помощи deploytool


Программа Application Deployment Tool, называемая здесь и далее для краткости deploytool,
входит в данную редакцию Java WSDP. В этом разделе рассматривается использование
deploytool при создании WAR-файла (Web Application aRchive) для размещения вашего
приложения и управления вопросами безопасности. Для размещения приложения при
помощи deploytool выполните следующие действия:
1. Запустите Tomcat (если он еще не работает).
2. Запустите deploytool, программу командной строки, расположенную в каталоге bin
вашей установки Java WSDP.

<JWSDP_HOME>/bin/deploytool
3. В диалоговом окне Set Tomcat Server введите правильное имя пользователя и пароль.
Они устанавливаются при инсталляции Java WSDP или могут быть установлены при
помощи admintool. Информация по установке пользователей при помощи admintool
находится в разделе "Использование admintool".
4. Выберите OK для завершения размещения.
5. Выберите File.
6. Выберите New Web Application.
Отобразится мастер New Web Application. Этот мастер поможет спакетировать Web-
приложение в WAR-файл, определяя индивидуальные Web-компоненты и генерируя
дескриптор размещения для Web-приложения. Мы будем использовать мастер для
идентификации файлов в Web-приложении и для идентификации Web-компонентов в
дескрипторе размещения приложения.

4.5.3.1. Создание WAR-файла и идентификация файлов в Web-приложении


Для создания WAR-файла и указания мастеру New Web Application файлов, которые он
должен содержать, выполните следующие действия:
1. Выберите Next из страницы Introduction.
2. Отобразится раздел Create New Stand-Alone WAR Module на странице WAR File мастера.
3. Выберите кнопку Browse возле поля Module File Name и выберите путь к каталогу, в
котором надо создать этот файл, например, корневой каталог примера приложения,
сгенерированного ant, а именно <JWSDP_HOME>/docs/tutorial/examples/GSApp.
4. Введите имя WAR-файла, например, GSApp.war и нажмите кнопку Choose Module File.
5. Введите значение в поле WAR Display Name, например, GSApp.

Web-

Rendered by www.RenderX.com
Стр. 64 из 626 Начало работы с Tomcat

6. Нажмите кнопку Edit в поле Contents для добавления файлов к WAR-файлу.


7. Выберите ConverterBean.class из каталога <JWSDP_HOME>/docs/tutorial/exam-
ples/GSApp/build/WEB-INF/classes/converterApp, затем нажмите кнопку Add для
добавления этого файла к архиву. Это каталог, в котором был размещен файл в
сценарии build.xml.
8. Выберите index.jsp из каталога <JWSDP_HOME>/docs/tutorial/examples/GSApp/build/WEB-
INF, затем нажмите кнопку Add для добавления этого файла в архив. Это каталог, в
котором был размещен файл в сценарии build.xml.
9. Нажмите кнопку OK для выхода из диалогового окна Edit Contents.
10. Нажмите кнопку Next для продолжения.

4.5.3.2. Выбор типа компонентов


Следующей страницей мастера является страница Choose Component Type. На этой
странице мы выберем JSP-страницу в качестве типа создаваемого компонента:
1. Выберите JSP.
2. Выберите Next.

4.5.3.3. Установка свойств компонента


Следующей страницей мастера является страница Component General Properties. На этой
странице мы выберем JSP-файл:
1. Выберите index.jsp из списка JSP Filename.
2. Выберите Finish.
3. Выберите File, затем Save для сохранения WAR-файла.
Создается WAR-файл и его содержимое отображается на закладке General программы
Application Deployment Tool.

4.5.3.4. Размещение приложения


После создания WAR-файла мы можем разместить приложение. Для этого выполните
следующие действия. При выборе операции deploy созданный WAR-файл копируется в
Tomcat и Tomcat уведомляется о новом контексте. При помощи deploytool можно размещать
приложения только на localhost.
1. Выберите Tools, затем Deploy.
2. Нажмите OK для подтверждения того, что WAR готов к размещению.

4.5.3.5. Просмотр дескриптора размещения


При размещении приложения с использованием deploytool генерируется дескриптор
размещения. Для просмотра дескриптора размещения выберите Tools->Descriptor Viewer
из меню deploytool. Простой дескриптор размещения, сгенерированный для нашего примера
на предыдущих этапах, выглядит примерно так:

<?xmlversion="1.0"encoding="UTF-8"?>

Web-

Rendered by www.RenderX.com
Выполнение первого приложения Стр. 65 из 626

<!DOCTYPEweb-appPUBLIC'-
//SunMicrosystems,Inc.//DTDWebApplication2.3//EN''http://java.
sun.com/dtd/web-app_2_3.dtd'>

<web-app>
<display-name>GSApp</display-name>
<servlet>
<servlet-name>index</servlet-name>
<display-name>index</display-name>
<jsp-file>/index.jsp</jsp-file>
</servlet>
<session-config>
<session-timeout>30</session-timeout>
</session-config>
</web-app>

4.6. Выполнение первого приложения


Для выполнения приложения необходимо проверить, работает ли Tomcat, затем запустить
JSP-страницу в Web-броузере.

4.6.1. Выполнение Web-клиента


Для запуска Web-клиента введите в адресной строке вашего броузера следующий URL:

http://localhost:8080/GSApp
В данной редакции Java WSDP Tomcat требует, чтобы хостом был localhost, который
является компьютером с выполняющимся Tomcat. В данном примере контекстом
приложения является "GSApp". Контекст определяется либо в файле build.xml, либо путем
ввода имени в поле WAR Display Name программы deploytool.
Для проверки приложения:
1. Введите 100 в поле "Enter an amount to convert".
2. Нажмите Submit.
На рисунке 3-1 изображено работающее приложение.

Web-

Rendered by www.RenderX.com
Стр. 66 из 626 Начало работы с Tomcat

Рисунок 3-1 Web-клиент ConverterBean

4.6.2. Остановка Tomcat


После завершения проверки и разработки приложения вы должны остановить Tomcat:

<JWSDP_HOME>/bin/shutdown.sh ( Unix)

<JWSDP_HOME>\bin\shutdown (Microsoft Windows)

4.7. Использование admintool


В пакет JWSDP входит программа Tomcat Web Server Administration Tool, называемый для
краткости здесь и далее admintool. Web-приложение admintool может быть использовано
для управления Tomcat во время его работы. Например, вы можете добавить и/или
настроить контексты, хосты, области и коннекторы, или настроить пользователей и роли
для безопасности, управляемой контейнером.
Для запуска admintool выполните следующие действия:
1. Запустите Tomcat как описано в разделе "Запуск Tomcat".
2. Запустите Web-броузер.
3. В адресной строке Web-броузера введите следующий URL:

http://localhost:8080/admin
Эта команда вызывает Web-приложение admin. Перед тем, как вы сможете использовать
это приложение, вы должны добавить ваши имя и пароль и связать с ними роль с
именем admin. Первоначальные имя пользователя и пароль, необходимые для доступа
к этой программе, устанавливаются во время инсталляции Java WSDP. Если вы забыли
имя пользователя и пароль, их можно посмотреть в файле <JWSDP_HOME>/conf/tomcat-

Web-

Rendered by www.RenderX.com
Использование admintool Стр. 67 из 626

users.xml при помощи любого текстового редактора. Этот файл содержит элемент
<user> для каждого конкретного пользователя и может выглядеть примерно так:

<user name="adeveloper" pasword="secret"


roles="admin, manager" />
4. Зарегистрируйтесь в admintool, используя имя пользователя и пароль, которым
назначена роль admin. Эта информация должна соответствовать имени пользователя
и паролю в файле build.properties.
5. После завершения выйдите из admintool, выбрав Logout в верхней панели.
admintooladmintool
• Сервер Tomcat.
• Службы, выполняющиеся на сервере Tomcat, плюс вложенные в Services элементы,
такие как Hosts, Contexts, Realms, Connectors, Loggers и Valves.
• Такие ресурсы, как Data Sources, Environment Entries и User Database.

4.7.1. Понимание ролей, групп и пользователей


Служба аутентификации сервера Tomcat включает следующие компоненты:
• Роли - абстрактное имя для разрешений доступа к отдельному набору ресурсов. Роль
можно сравнить с ключом, который может открыть замок. Многие люди могут иметь
копию ключа и замку все равно, кто вы, главное, что у вас есть правильный ключ.
• Пользователь - индивидуум (или прикладная программа), который аутентифицирован
(аутентификация рассматривалась в предыдущем разделе). Пользователь может иметь
набор ролей, назначенных ему и дающих право доступа к ресурсам, защищенным этими
ролями.
• Группа - набор аутентифицированных пользователей, классифицированных по общим
особенностям, например названию работы или пользовательскому профилю. Группы
тоже связываются с набором ролей, и каждый пользователь, являющийся членом
группы, наследует все роли, назначенные этой группе.
• Область - полная база данных ролей, пользователей и групп, идентифицирующая
разрешенных пользователей Web-приложения (или набора Web-приложений).
Управление ролями и пользователями

4.7.2. Добавление ролей при помощи admintool


Чтобы настроить роли для управляемой контейнером безопасности выполните следующие
действия. Добавления, удаления и изменения, сделанные в admintool, записываются в
файл tomcat-users.xml.
• Прокрутите вниз левую панель admintool до элемента User and Group Administration.
• Выберите Role Administration.
• Из списка Roles List выберите Create New Role.
• Введите Role Name и Description, например Customer или User.

Web-

Rendered by www.RenderX.com
Стр. 68 из 626 Начало работы с Tomcat

• Выберите Save.

4.7.3. Добавление пользователей при помощи admintool


Чтобы настроить пользователей для управляемой контейнером безопасности, выполните
следующие действия. Добавления, удаления и изменения, сделанные в admintool,
записываются в файл tomcat-users.xml.
• Прокрутите вниз левую панель admintool до элемента User and Group Administration.
• Выберите User Administration.
• Из списка Users List выберите Create New User.
• Введите User Name, Password и выберите Role для нового пользователя. Если вы
выберете роль admin для нового пользователя, он получит доступ к admintool.
• Выберите Save.

4.8. Модификация приложения


Поскольку Java WSDP предназначен для экспериментов, он поддерживает итеративную
разработку. Если вы изменили приложение, вы должны его повторно разместить и
перезагрузить. Задания, которые мы определили в файле build.xml, упрощают размещение
измененных ConverterBean и JSP-страницы.
В файле build.xml настраивается задание для установки приложения на работающем
сервере Tomcat и задание для перезагрузки приложения на работающем сервере Tomcat.
Эти задания назначаются при помощи программы Tomcat Server Manager Tool, называемой
Web-приложением manager. Вы можете использовать комбинацию имя
пользователя/пароль, которую вы установили при инсталляции Java WSDP, поскольку эта
комбинация будет иметь назначенную ей роль manager. Если вы забыли имя пользователя
и пароль, использовавшиеся при инсталляции, можно посмотреть файл
<JWSDP_HOME>/conf/tomcat-users.xml при помощи любого текстового редактора.
Справочная информация по Tomcat, поставляемая вместе с Java WSDP, содержит
документацию по приложению manager.

4.8.1. Модификация файла классов


Для изменения файла классов Java-компонента нужно изменить исходный код,
перекомпилировать его и повторно разместить приложение. При использовании Web-
приложения manager необязательно останавливать и снова запускать Tomcat для
повторного размещения измененного приложения. Например, предположим, что вы хотите
изменить курс валюты в свойстве yenRate компонента ConverterBean.
1. Отредактируйте файл ConverterBean.java в каталоге с исходными кодами.
2. Перекомпилируйте ConverterBean.java при помощи команды ant build.
3. Повторно разместите ConverterBean.java при помощи команды ant reload.
4. Перезагрузите JSP-страницу в Web-броузере.

Web-

Rendered by www.RenderX.com
Общие проблемы и их решение Стр. 69 из 626

4.8.2. Модификация Web-клиента


Для изменения JSP-страницы нужно изменить исходный код и повторно разместить
приложение. При использовании Web-приложения manager необязательно останавливать
и снова запускать Tomcat для повторного размещения измененного Web-клиента. Например,
предположим, что вы хотите изменить шрифт или добавить дополнительный описательный
текст в JSP-страницу. Для изменения Web-клиента:
1. Отредактируйте файл index.jsp в каталоге с исходными кодами.
2. Перезагрузите Web-приложение при помощи команды ant reload.
3. Перезагрузите JSP-страницу в Web-броузере.

4.9. Общие проблемы и их решение


Используйте приведенные ниже рекомендации для поиска неисправностей при
возникновении любой проблемы во время создания, компиляции, установки, размещения
и выполнения приложения примера.

4.9.1. Ошибки при запуске Tomcat


4.9.1.1. Ошибка "Out of Environment Space" ("Не хватает пространства окружения")
Симптом: Ошибка "out of environment space" при выполнении пакетных файлов startup и
shutdown в операционных системах Microsoft Windows 9X/ME.
Решение: В проводнике нажмите правой кнопкой мыши на файлах startup.bat и shutdown.bat.
Выберите Properties, затем закладку Memory. Увеличьте поле Initial Environment до значения,
например, 4096. Выберите Apply.
После выбора Apply в каталоге, используемом для запуска и остановки контейнера,
создадутся файлы для быстрого запуска этих программ.

4.9.1.2. Ошибка "Unable to Locate the Server localhost:8080" ("Невозможно обнаружить


сервер localhost:8080")
Симптом: ошибка "unable to locate the server" при попытке загрузить Web-приложение в
броузер.
Решение: Полная загрузка Tomcat может занять некоторое время, так что убедитесь, что
вы дали серверу по крайней мере 5 минут, прежде чем продолжить исправление
неисправностей. Для проверки выполнения Tomcat введите в ваш броузер URL
http://localhost:8080. После отображения начальной страницы Tomcat можно продолжать
работу. Если начальный экран не загрузился сразу, подождите несколько минут и повторите
попытку. Если Tomcat снова не загружается, проверьте файлы журналов (log-файлы), как
поясняется ниже, для получения дальнейшей информации по неисправности.
При запуске Tomcat он инициализируется и загружает Web-приложения из
<JWSDP_HOME>/webapps. Если вы запускаете Tomcat при помощи сценария startup.sh,
сообщения сервера записываются в файл <JWSDP_HOME>/logs/catalina.out. Процесс
загрузки Web-приложений можно посмотреть в файле
<JWSDP_HOME>/logs/jwsdp_log.<date>.txt.

Web-

Rendered by www.RenderX.com
Стр. 70 из 626 Начало работы с Tomcat

4.9.2. Ошибки компиляции


4.9.2.1. Server returned HTTP response code: 401 for URL : (Сервер возвратил код ответа
HTTP: 401 для URL :)
Симптом: После выполнения команды ant install появляются следующие сообщения:

BUILD FAILED
/home/you/gs/build.xml:44:
java.io.IOException: Server returned HTTP response code: 401
for URL: http://localhost:8080/manager/install?path= ...
Решение: Проверьте, что имя пользователя и пароль в вашем файле build.properties
соответствуют имени пользователя и паролю с назначенной ролью manager в файле
tomcat-users.xml. Дополнительная информация по настройке этой информации находится
в разделе "Создание файла свойств компоновки".

4.9.2.2. Ant Cannot Locate the Build File (ant не может найти файл компоновки)
Симптом: После выполнения команды ant build появляются следующие сообщения:

Buildfile: build.xml does not exist!


Build failed.
Решение: Запустите ant из каталога <JWSDP_HOME>/docs/tutorial/gs/, или из каталога с
созданным вами приложением. Если вы хотите запустить ant из текущего каталога, вы
должны указать файл компоновки в командной строке. Например, вы могли бы выполнить
следующую команду:

ant -buildfile

<JWSDP_HOME>/docs/tutorial/examples/gs/build.xml

build

4.9.2.3. The Compiler Cannot Resolve Symbols (компилятор не может разрешить символы)
Симптом: После выполнения команды ant build компилятор отображает множество ошибок,
в том числе и эти:

cannot resolve symbol


. . .
BUILD FAILED
. . .
Compile failed, messages should have been provided

Web-

Rendered by www.RenderX.com
Общие проблемы и их решение Стр. 71 из 626

Решение: Убедитесь, что вы используете версию ant, поставляемую с Java WSDP. Лучшим
способом дать гарантию, что вы используете нужную версию, является задание полного
пути к файлу ant при построении приложения <JWSDP_HOME>/bin/ant build. Другие версии
могут не иметь функциональности, ожидаемой файлами компоновки приложения примера.

4.9.2.4. Ошибка "Connection refused" ("В соединении отказано")


Симптом: После выполнения команды ant install из командной строки вы получаете
следующее сообщение:

<JWSDP_HOME>/docs/tutorial/examples/gs/build.xml:82:

java.net.ConnectException: Connection refused

Решение: Tomcat не полностью запустился. Подождите несколько минут, затем повторите


попытку установки приложения. Более подробная информация по этой проблеме находится
в разделе "Ошибка Unable to Locate the Server localhost:8080".

4.9.2.5. При попытке выполнения задания на установку система, по всем признакам, виснет.
Симптом: После выполнения команды ant install система зависает.
Решение: Начальный сценарий Tomcat запускает сервер в фоновом режиме и сразу же
возвращает пользователя к приглашению командной строки. Даже если вы вернулись в
командную строку, начальный сценарий может не запустить Tomcat полностью. Если
задание по установке не запускается мгновенно, подождите несколько минут и повторите
установку. Для проверки выполнения Tomcat перейдите в вашем броузере на страницу
http://localhost:8080. Когда отобразится начальный экран Tomcat, вы можете продолжать
работу. Если начальный экран не загрузился сразу, подождите несколько минут и повторите
попытку. Если Tomcat все равно не загружается, проверьте log-файлы, как описано ниже,
для получения дальнейшей информации по неисправности.
При запуске Tomcat он инициализируется и загружает Web-приложения из
<JWSDP_HOME>/webapps. Если вы запускаете Tomcat при помощи сценария startup.sh,
сообщения сервера записываются в файл <JWSDP_HOME>/logs/catalina.out. Процесс
загрузки Web-приложений можно посмотреть в файле
<JWSDP_HOME>/logs/jwsdp_log.<date>.txt.

4.9.3. Ошибки размещения


4.9.3.1. Failure to run client application (Ошибка при выполнении клиентского приложения)
Симптом: Броузер отвечает, что страница не может быть найдена (HTTP 404).
Решение: Начальный сценарий Tomcat запускает сервер в фоновом режиме и сразу же
возвращает пользователя к командной строке. Даже если вы вернулись в командную
строку, начальный сценарий может не запустить Tomcat полностью. Если Web-клиент не
запускается мгновенно, подождите несколько минут и повторите загрузку Web-клиента.
Более подробная информация по этой проблеме находится в разделе "Ошибка Unable to
Locate the Server localhost:8080".

Web-

Rendered by www.RenderX.com
Стр. 72 из 626 Начало работы с Tomcat

4.9.3.2. The localhost Machine Is Not Found (Компьютер localhost не найден)


Симптом: Броузер сообщает, что страница не может быть найдена (HTTP 404).
Решение: Иногда, если вы работаете с прокси, брандмауэр не разрешает вам получить
доступ к localhost. Для устранения этой ошибки измените настройки прокси так, чтобы он
не использовался при доступе к localhost.
Для этого, например, в броузере Netscape Navigator™ выберите Edit->Preferences-
>Advanced->Proxies и отметьте No Proxy for: localhost. В Internet Explorer выберите Tools-
>Internet Options->Connections->LAN Settings.

4.9.3.3. The Application Has Not Been Deployed (Приложение не размещено)


Симптом: Броузер сообщает, что страница не может быть найдена (HTTP 404).
Решение: Разместите приложение. Детально об этом - в разделе "Размещение приложения".

4.9.3.4. Ошибка "Build Failed: Application Already Exist at Path" (Компоновка неудачна:
приложение уже существует)
Симптом: После выполнения команды ant install в терминальном окне отображается такое
сообщение:

[install] FAIL - Application already exists at path /GSApp

BUILD FAILED

<JWSDP_HOME>/docs/tutorial/examples/gs/build.xml:82: FAIL -

Application already exists at path /GSApp

Это приложение уже установлено. Если вы сделали изменения в приложение после его
установки, используйте команду ant reload для обновления приложения в Tomcat.

4.9.3.5. HTTP 500: No Context Error (HTTP 500: Ошибка: нет контекста)
Симптом: Отображается сообщение No Context Error при попытке запуска размещенного
приложения.
Решение: Эта ошибка означает, что Tomcat загружен, но не знает о вашем приложении.
Если вы не разместили приложение - это будет вашим первым действием. Если вы успешно
разместили приложение при помощи команды ant remove, ant build, ant install и все еще
получаете ошибку - продолжайте чтение.

Web-

Rendered by www.RenderX.com
Дополнительная информация Стр. 73 из 626

Если Tomcat загружен, но не загрузил еще все свои существующие контексты, вы тоже
получите эту ошибку. Продолжайте нажимать кнопки Reload или Refresh вашего броузера,
пока приложение не загрузится или вы не получите какое-либо другое сообщение об
ошибке.

4.10. Дополнительная информация


• Средство администрирования Tomcat. Обратитесь к разделу "Программа
администрирования Tomcat" для дополнительной информации по использованию
admintool при конфигурации поведения Tomcat без его остановки и перезапуска.
• Справочник по конфигурации Tomcat. Для дополнительной информации по элементам,
которые можно использовать для настройки поведения Tomcat, обратитесь к Справочнику
по конфигурации Tomcat, который можно найти на странице
<JWSDP_HOME>/docs/tomcat/config/index.html.
• Class Loader How-To. В этом документе обсуждаются решения, которые прикладные
разработчики и установщики должны следовать в вопросе месторасположения файлов
классов и ресурсов, чтобы сделать их доступными для Web-приложений. Этот документ
находится на странице <JWSDP_HOME>/docs/tomcat/class-loader-howto.html.
• JNDI Resources How-To. В этом документе обсуждается конфигурирование JNDI-
ресурсов, Tomcat Standard Resource Factories, JDBC Data Sources и Custom Resource
Factories. Этот документ находится на странице <JWSDP_HOME>/docs/tomcat/jndi-
resources-howto.html.
• Manager Application How-To. В этом документе рассматривается использование Manager
Application для размещения нового Web-приложения, снятия существующего приложения,
или перезагрузки существующего приложения без необходимости остановки и
перезапуска Tomcat. Этот документ находится на странице
<JWSDP_HOME>/docs/tomcat/manager-howto.html.
• Proxy Support How-To. В этом документе рассмотрена работа с прокси-сервером (или
Web-сервером, который настроен на выполнение функций прокси-сервера). В частности,
в этом документе обсуждаются вопросы управления значениями, возвращаемыми
вызовами из Web-приложения, которые запрашивают имя сервера и номер порта, на
которые запрос перенаправляется для обработки. Этот документ может быть найден
на странице <JWSDP_HOME>/docs/tomcat/proxy-howto.html.
• Realm Configuration How-To. В этом документе рассматриваются вопросы настройки
Tomcat для поддержки управляемой контейнером безопасности при помощи соединения
с существующей базой данных имен пользователей, паролей и ролей пользователей.
Этот документ находится на странице <JWSDP_HOME>/docs/tomcat/realm-howto.html.
• Security Manager How-To. В этом документе рассматриваются вопросы использования
SecurityManager при работе Tomcat для защиты сервера от несанкционированных
сервлетов, JSP-страниц, JSP-компонентов и библиотек тегов. Этот документ находится
на <JWSDP_HOME>/docs/tomcat/security-manager-howto.html.
• SSL Configuration How-To. В этом документе рассмотрены вопросы установки и настройка
поддержки SSL в Tomcat. Настройка поддержки SSL в Tomcat при помощи Java WSDP
рассмотрена в разделе "Установка и настройка поддержки SSL в Tomcat". Документация
Tomcat в <JWSDP_HOME>/docs/tomcat/ssl-howto.html тоже обсуждает этот вопрос,

Web-

Rendered by www.RenderX.com
Стр. 74 из 626 Web-приложения

однако, информация в данном руководстве является более новой для версии Tomcat,
поставляемой с Java WSDP.

5. Web-приложения
Web-приложение представляет собой динамическое расширение Web-сервера. Существует
два типа Web-приложений:
• Ориентированные на представление. Ориентированное на представление Web-
приложение в ответ на запрос генерирует динамические Web-страницы, содержащие
различные типы языка разметки (HTML, XML и т.д.).
• Ориентированные на службы. Ориентированное на службы Web-приложение реализует
конечную точку для разделенной на модули Web-службы. Ориентированное на службы
Web-приложение часто вызывается ориентированными на представление приложениями.
В платформе динамическое расширение возможностей Web-сервера Java 2 обеспечивают
Web-компоненты. Web-компонентами являются либо Java-сервлеты, либо JSP-страницы.
Сервлеты представляют собой классы языка программирования Java, которые динамически
обрабатывают запросы и генерируют ответы. JSP-страницы представляют собой текстовые
документы, которые выполняются как сервлеты, но обеспечивают более естественный
подход к созданию статического содержимого. Хотя сервлеты и JSP-страницы
взаимозаменяемы, каждая из этих технологий имеет свои преимущества. Сервлеты больше
подходят для ориентированных на службы Web-приложения и для управления некоторыми
функциями ориентированных на представление приложений, такими как распределение
запросов и обработка нетекстовых данных. JSP-страницы больше подходят для генерации
текстовой разметки, такой как HTML, SVG, WML и XML.
Web-компоненты поддерживаются службами исполняющей платформы, называемой Web-
контейнером. В Java WSDP Web-компоненты выполняются в Web-контейнере Tomcat.
Web-контейнер обеспечивает такие службы как распределение запросов, безопасность,
параллельная работа и управление циклом жизни. Он также предоставляет Web-
компонентам доступ к таким API как служба имен, транзакции и электронная почта.
В этой главе описываются процедуры организации, конфигурации, установки и размещения
Web-приложений. В главах 9 и 10 рассматривается разработка Web-компонентов для
ориентированных на службы Web-приложений. В главах 12 и 13 рассматривается разработка
Web-компонентов для ориентированных на представление Web-приложений. Многие из
возможностей технологии JSP определяются технологией Java Servlet, поэтому вы должны
изучить этот материал, даже если не планируете писать сервлеты.
Большинство Web-приложений используют протокол HTTP, и поддержка HTTP является
важнейшим аспектом Web-компонентов. Краткая сводка возможностей протокола HTTP
приведена в разделе "Обзор HTTP".

5.1. Жизненный цикл Web-приложения


Web-приложение состоит из Web-компонентов, файлов статических ресурсов, таких как
изображения, и вспомогательных классов и библиотек. Java WSDP предоставляет много
служб поддержки, расширяющих возможности Web-компонентов и облегчающих их
разработку. Однако, из-за существования этих служб, процессы создания и выполнения

Web-

Rendered by www.RenderX.com
Жизненный цикл Web-приложения Стр. 75 из 626

Web-приложений отличаются от процессов создания и выполнения обычных автономных


Java-классов.
Некоторые аспекты поведения Web-приложения могут быть настроены во время его
размещения. Информация по конфигурации хранится в текстовом файле в XML-формате,
называемом дескриптором размещения Web-приложения. Дескриптор размещения должен
соответствовать схеме, описанной в спецификации сервлетов Java.
Процесс создания, размещения и выполнения Web-приложения может быть кратко описан
следующим образом:
1. Разработать код Web-компонента (возможно, включая дескриптор размещения)
2. Скомпоновать компоненты Web-приложения вместе со всеми статическими ресурсами
(например, изображениями) и вспомогательными классами, к которым обращается
компонент.
3. Установить, или разместить, приложение в Web-контейнере.
4. Обратиться к URL, ссылающемуся на Web-приложение.
Разработка кода Web-компонента рассматривается в следующих главах. Шаги 2, 3 и 4
более подробно рассматриваются в следующих разделах и иллюстрируются с помощью
ориентированного на представление приложения Hello. Это приложение позволяет
пользователю ввести имя в HTML-форму (рисунок 4-1) и отобразить приветствие после
подтверждения имени (рисунок 4-2):

Рисунок 4-1 Форма приветствия

Рисунок 4-2 Ответ

Web-

Rendered by www.RenderX.com
Стр. 76 из 626 Web-приложения

Приложение "Hello, World" состоит из двух Web-компонентов, генерирующих приветствие


и ответ. В данном руководстве имеется два варианта этого приложения: версия с
сервлетами Hello1, в которой компоненты реализуются двумя классами сервлетов, Greet-
ingServlet.java и ResponseServlet.java, и JSP-версия Hello2, в которой компоненты
реализуются двумя JSP-страницами, greetings.jsp и response.jsp. Эти два варианта
используются также для иллюстрации задач, возникающих при пакетировании, размещении
и выполнении приложения, содержащего Web-компоненты. Если вы просматриваете это
руководство в интерактивном режиме, вам необходимо загрузить полный пакет руководства
для получения исходных кодов примера. Обратитесь к разделу "Запуск примеров".

5.2. Архивы Web-приложения


Для распространения Web-приложения его надо спакетировать в архив Web-приложения
(WAR), похожий на JAR-архив, используемый при пакетировании библиотек Java-классов.
Кроме Web-компонентов архив Web-приложения может содержать другие файлы, включая
следующие:
• Серверные классы утилит (компоненты баз данных, корзины покупок и т.д.). Часто эти
классы соответствуют архитектуре компонентов JavaBeans.
• Статическое содержимое (HTML, изображения, звуковые файлы и т.д.)
• Клиентские классы (апплеты и классы утилит).
Web-компоненты и файлы статического содержимого называются Web-ресурсами.
Web-приложение может запускаться из WAR-файла или из неупакованного каталога,
организованного в таком же формате, что и WAR.

5.2.1. Структура каталога WAR


Каталогом верхнего уровня в WAR является корневой каталог документов приложения.
Корневой каталог документов представляет собой место, где хранятся JSP-страницы,
клиентские классы и архивы, а также статические Web-ресурсы.
Корневой каталог документов содержит подкаталог WEB-INF, который содержит следующие
файлы и каталоги:
• web.xml - дескриптор размещения Web-приложения
• Файлы дескрипторов библиотек тегов (см. раздел "Дескрипторы библиотек тегов")
• clases - каталог, содержащий серверные классы: сервлеты, классы утилит и компоненты
JavaBeans
• lib - каталог, содержащий JAR-архивы и библиотеки (библиотеки тегов и все библиотеки
утилит, вызываемые серверными классами)
Вы можете также создавать свои собственные подкаталоги (то есть, каталоги пакета) либо
в корневом каталоге документов, либо в каталоге WEB-INF/classes.

5.2.2. Структура каталога примера


В целях облегчения итеративной разработки и хранения исходных файлов Web-приложения
отдельно от компилированных файлов исходный код примеров руководства хранится в
каталоге mywebapp каждого приложения следующей структуры:

Web-

Rendered by www.RenderX.com
Архивы Web-приложения Стр. 77 из 626

• build.xml - файл компоновки для ant


• context.xml - необязательный конфигурационный файл приложения
• src - исходный Java-код сервлетов и компонентов JavaBeans
• web - JSP-страницы и HTML-страницы, изображения
Файл компоновки для ant (build.xml), распространяемый с примерами, содержит задания
для создания неупакованной WAR-структуры в подкаталоге build каталога mywebapp,
копирования и компиляции файлов в этот каталог, а также вызова команд manager (см.
раздел "Менеджер Web-приложений Tomcat") при помощи специальных команд ant для
установки, перезагрузки, удаления, размещения и отмены размещения приложений. Такими
заданиями ant для примеров руководства являются:
• prepare - Создает каталог build и подкаталоги WAR.
• build - Компилирует и копирует файлы Web-приложения mywebapp в каталог build.
• install - Указывает Tomcat установить приложение (см. раздел "Установка Web-
приложений") при помощи команды ant install.
• reload - Указывает Tomcat перезагрузить приложение (см. раздел "Обновление Web-
приложений") при помощи команды ant reload.
• deploy - Указывает Tomcat разместить приложение (см. раздел "Размещение Web-
приложений") при помощи команды ant deploy.
• undeploy - Указывает Tomcat отменить размещение приложения (см. раздел "Отмена
размещения Web-приложений") при помощи команды ant undeploy.
• remove - Указывает Tomcat удалить приложение (см. раздел "Удаление Web-
приложений") при помощи команды ant remove.

5.2.3. Создание WAR


Вы можете вручную создать WAR двумя способами:
• При помощи программы JAR, поставляемой с J2SE SDK. Просто выполните следующую
команду в каталоге build примера:

jar cvf mywebapp.war


• При помощи команды ant war
Оба эти метода требуют создания дескриптора размещения Web-приложения.
Можно спакетировать приложение в WAR при помощи deploytool. При использовании
deploytool он создает дескриптор размещения Web-приложения, основываясь на
информации, введенной в мастерах и инспекторах deploytool. Для компоновки и
пакетирования приложения Hello1 в WAR с именем hello1.war:
1. В терминальном окне перейдите в каталог <JWSDP_HOME>/docs/tutorial/exam-
ples/web/hello1.
2. Выполните команду ant build. Задание build вызовет все необходимые компиляции и
скопирует файлы в каталог <JWSDP_HOME>/docs/tutorial/examples/web/hello1/build.

Web-

Rendered by www.RenderX.com
Стр. 78 из 626 Web-приложения

3. Запустите deploytool.
4. Создайте Web-приложение с именем hello1.
A. Выберите File->New Web Application.
B. Выберите Create New Stand-Alone WAR Module.
C. Нажмите Browse и окне выбора файлов перейдите в <JWSDP_HOME>/docs/tuto-
rial/examples/web/hello1.
D. В поле File Name введите hello1.
E. Нажмите Choose Module File.
F. В поле WAR Display Name введите hello1.

5. Добавьте Web-компонент greeting и все содержимое приложения Hello1.


A. Нажмите Edit для добавления файлов содержимого.
B. В диалоговом окне Edit Contents выберите <JWSDP_HOME>/docs/tutorial/exam-
ples/web/hello1/build/duke.waving.gif и нажмите Add. Перейдите в WEB-INF/classes
и выберите greetingServlet.class и ResponseServlet.class и нажмите Add. Нажмите
OK.
C. Нажмите Next.
D. Выберите переключатель Servlet.
E. Нажмите Next.
F. Выберите GreetingServlet из поля списка Servlet Class.
G. Нажмите Finish.

6. Добавьте Web-компонент response.


A. Выберите File->Edit Web Application.
B. Нажмите переключатель Add to Existing WAR Module и выберите hello1 из поля
списка. Поскольку WAR содержит все классы сервлетов, вам больше не нужно
добавлять какое-либо содержимое.
C. Нажмите Next.
D. Выберите переключатель Servlet.
E. Нажмите Next.
F. Выберите ResponseServlet из поля списка Servlet Class.
G. Нажмите Finish.

5.3. Настройка Web-приложений


Web-приложения настраиваются при помощи элементов, содержащихся в дескрипторах
размещения Web-приложения. Вы можете создавать дескрипторы либо вручную при
помощи текстового редактора, либо используя deploytool для их автоматической генерации.

Web-

Rendered by www.RenderX.com
Настройка Web-приложений Стр. 79 из 626

В следующих разделах руководства приведено краткое введение в свойства Web-


приложения, которые обычно нужно настраивать. Несколько настраиваемых параметров
безопасности рассмотрены в разделе "Безопасность Web-приложений". Полный список и
описание свойств приведен на странице "Спецификация сервлетов Java".
В следующих разделах несколько примеров демонстрируют процедуры настройки
приложения "Hello, World". Если это приложение не использует определенные функции
настройки, используются другие примеры для демонстрации элементов дескриптора
размещения и описываются стандартные процедуры для установки свойств при помощи
deploytool. Более сложные примеры, демонстрирующие использование deploytool,
приводятся в разделах "Пример сервлетов" и "Пример JSP-страниц".
Примечание: Элементы дескриптора должны появляться в дескрипторе размещения в
следующем порядке: icon, display-name, description, distributable, context-param, filter, filter-
mapping, listener, servlet, servlet-mapping, session-config, mime-mapping, welcome-file-list,
error-page, taglib, resource-env-ref, resource-ref, security-constraint, login-config, security-role,
env-entry.

5.3.1. Пролог
Поскольку дескриптор размещения представляет собой XML-документ, для него необходим
пролог. Пролог дескриптора размещения Web-приложения выглядит следующим образом:

<?xml version="1.0" encoding="ISO-8859-1"?>


<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web
Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">

5.3.2. Пути псевдонимов


Когда Tomcat получает запрос, он должен определить, какой Web-компонент должен его
обработать. Tomcat делает это путем отображения пути URL, содержащегося в запросе,
в Web-компонент. Путь URL содержит корневой каталог контекста (описанный в разделе
"Установка Web-приложений") и путь псевдонима

http://<host>:8080/context_root/alias_path
Перед тем как к сервлету можно будет получить доступ, Web-контейнер должен иметь по
крайней мере один путь псевдонима для компонента. Путь псевдонима должен начинаться
со знака / и заканчиваться строкой или шаблоном с расширением (*.jsp, например).
Поскольку Web-контейнер автоматически отображает путь псевдонима, который
заканчивается *.jsp, вам не обязательно указывать путь псевдонима для JSP-страницы,
пока вы не захотите обратиться к странице по имени, отличному от имени ее файла. В
примере, рассмотренном в разделе "Обновление Web-приложений", страница приветствия
имеет псевдоним, а обращение к response.jsp происходит по имени файла.
Для настройки отображения сервлет-версии приложения Hello в дескрипторе размещения
вы должны добавить в него следующие элементы servlet и servlet-mapping. Чтобы
определить псевдоним для JSP-страницы вы должны заменить субэлемент servlet-class
субэлементом jsp-file в элементе servlet.

Web-

Rendered by www.RenderX.com
Стр. 80 из 626 Web-приложения

<servlet>
<servlet-name>greeting</servlet-name>
<display-name>greeting</display-name>
<description>no description</description>
<servlet-class>GreetingServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>response</servlet-name>
<display-name>response</display-name>
<description>no description</description>
<servlet-class>ResponseServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>greeting</servlet-name>
<url-pattern>/greeting</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>response</servlet-name>
<url-pattern>/response</url-pattern>
</servlet-mapping>
Для настройки отображения сервлет-версии приложения Hello выполните следующие
действия в deploytool:
1. Выберите WAR hello1.
2. Выберите Web-компонент GreetingServlet.
3. Выберите закладку Aliases.
4. Нажмите Add для добавления нового отображения.
5. Введите /greeting в списке псевдонимов.
6. Выберите Web-компонент ResponseServlet.
7. Нажмите Add.
8. Введите /response в списке псевдонимов.

5.3.3. Параметры контекста и инициализации


Web-компоненты в WAR совместно используют объект, представляющий их контекст
приложения (см. раздел "Получение доступа к Web-контексту"). Вы можете передать
параметры в контекст или Web-компонент. Для этого необходимо добавить элемент context-
param или init-param в дескриптор размещения Web-приложения. context-param является
субэлементом элемента web-app верхнего уровня. init-param является субэлементом
элемента servlet. Ниже приведен элемент, используемый для объявления параметра

Web-

Rendered by www.RenderX.com
Настройка Web-приложений Стр. 81 из 626

контекста, который устанавливает пакет ресурсов для примера, рассматриваемого в главе


16:

<web-app>
<context-param>
<param-name>
javax.servlet.jsp.jstl.fmt.localizationContext
</param-name>
<param-value>messages.BookstoreMessages</param-value>
</context-param>
...
</web-app>
Для добавления параметра контекста в deploytool выполните следующие действия:
1. Выберите WAR.
2. Выберите закладку Context.
3. Нажмите Add.
Для добавления параметра инициализации в deploytool выполните следующие действия:
1. Выберите Web-компонент.
2. Выберите закладку Init Param.
3. Нажмите Add.

5.3.4. Перехватчики событий


Чтобы добавить класс перехватчика событий (описанного в разделе "Обработка событий
жизненного цикла сервлета"), необходимо добавить элемент listener в дескриптор
размещения Web-приложения. Ниже приведен элемент, который объявляет класс
перехватчика событий, используемого в главах 12 и 16:

<listener>
<listener-class>listeners.ContextListener</listener-class>
</listener>
Для добавления перехватчика событий в deploytool выполните следующие действия:
1. Выберите WAR.
2. Выберите закладку Event Listeners.
3. Нажмите Add.
4. Выберите класс перехватчика из нового поля в области Event Listener Classes.

Web-

Rendered by www.RenderX.com
Стр. 82 из 626 Web-приложения

5.3.5. Отображения фильтров


Web-контейнер использует объявления отображений фильтров для принятия решения,
какие фильтры применить к запросу и в какой последовательности (см. раздел "Установка
отображений фильтров"). Контейнер согласует URI запроса с сервлетом, как описано в
разделе "Пути псевдонимов". Для определения того, какие фильтры применить, он согласует
объявления отображений фильтров по имени сервлета или по шаблону URL. Порядок
вызова фильтров определяется порядком, в котором объявления отображений фильтров,
соответствующих URI запроса для сервлета, появляются в списке.
Для установки отображения фильтров необходимо добавить элементы filter и filter-mapping
в дескриптор размещения Web-приложения. Вот элементы, используемые для объявления
порядка фильтров и их отображения в примере ReceiptServlet, обсуждаемом в главе 12:

<filter>
<filter-name>OrderFilter<filter-name>
<filter-class>filters.OrderFilter<filter-class>
</filter>
<filter-mapping>
<filter-name>OrderFilter</filter-name>
<url-pattern>/receipt</url-pattern>
</filter-mapping>
Чтобы добавить фильтр в deploytool, выполните следующие действия:
1. Выберите WAR.
2. Выберите закладку Filter Mapping.
3. Добавьте фильтр.
A. Нажмите Edit Filter List.
B. Нажмите Add.
C. Выберите класс фильтра.
D. Выберите имя фильтра.
E. Добавьте параметры инициализации фильтра.
F. Нажмите OK.

4. Отобразите фильтр.
A. Нажмите Add.
B. Выберите название фильтра.
C. Выберите тип назначения. Фильтр может быть отображен на конкретный сервлет
или на все сервлеты, соответствующие указанному шаблону URL.
D. Укажите назначение. Если назначение является сервлетом, выберите сервлет из
разворачивающегося списка. Если назначение является шаблоном URL, введите
шаблон.

Web-

Rendered by www.RenderX.com
Настройка Web-приложений Стр. 83 из 626

5.3.6. Отображение ошибок


Вы можете установить отображение между кодом состояния, возвращаемым в HTTP-
ответе, или исключительной ситуацией языка программирования Java, возвращаемой
каким-либо Web-компонентом и Web-ресурсом(см. раздел "Обработка ошибок"). Для
настройки отображения необходимо добавить элемент <error-page> в дескриптор
размещения. Вот элемент, используемый для отображения OrderException на страницу
errorpage.html, которая рассматривается в главе 12:

<error-page>
<exception-type>exception.OrderException</exception-type>
<location>/errorpage.html</location>
</error-page>
Чтобы добавить отображение ошибок в deploytool выполните следующие действия:
1. Выберите WAR.
2. Выберите закладку File Refs.
3. Нажмите Add в области Error Mapping.
4. Введите статус кода HTTP (см. раздел "Ответы HTTP") или полностью указанное имя
класса исключительной ситуации в поле Error/Exception.
5. Введите имя ресурса, который должен быть вызван при возврате кода статуса или
исключительной ситуации. Имя должно начинаться с символа /.
Примечание: Вы можете также определить страницы ошибок для JSP-страницы,
содержащейся в WAR. Если страницы ошибок определены и для WAR, и для JSP-страницы,
страница ошибок для JSP-страницы имеет преимущество.

5.3.7. Ссылки на элементы окружения, на элементы окружения ресурсов, или на


ресурсы
Если ваш Web-компонент обращается на элементы окружения, на элементы окружения
ресурсов, или на ресурсы, например базы данных, необходимо объявить ссылки при
помощи элементов <env-entry>, <resource-env-ref> или <resource-ref> в дескрипторе
размещения Web-приложения. Вот элемент, объявляющий ссылку на источник данных,
используемый в главах по технологии Web этого руководства:

<resource-ref>
<res-ref-name>jdbc/BookDB</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
Чтобы добавить ссылку в deploytool выполните следующие действия:
1. Выберите WAR.

Web-

Rendered by www.RenderX.com
Стр. 84 из 626 Web-приложения

2. Выберите закладку Environment, Enterprise Bean Refs, Resource Env. Refs, или Resource
Refs.
3. Нажмите Add для добавления нового ресурса.

5.4. Установка Web-приложений


Контекст - это имя, которое отображается на Web-приложение. Например, контекстом
приложения Hello1 является /hello1. Для установки приложения в Tomcat необходимо
указать ему, что доступен новый контекст.
Уведомить Tomcat о новом контексте можно при помощи команды ant install. Обратите
внимание, что установленное приложение не доступно после перезагрузки Tomcat. Чтобы
разместить приложение постоянно, обратитесь к разделу "Размещение Web-приложений".
Команда ant install указывает программе manager, выполняющейся в указанном в атрибуте
url месте, установить приложение, файлы которого расположены в указанном в атрибуте
war месте, в указанном в атрибуте path контексте. Значением атрибута war может быть
WAR-файл jar:file:/path/to/bar.war!/ или неупакованный каталог file:/path/to/foo.

<install url="url" path="mywebapp" war="file:build"


username="username" password="password" />
Атрибуты username и password обсуждаются в разделе "Менеджер Web-приложений
Tomcat".
Вместо использования атрибута war информацию по конфигурации можно указать в
атрибуте config:

<install url="url"
path="mywebapp" config="file:build/context.xml"
username="username" password="password"/>
Атрибут config указывает файл конфигурации, содержащий элемент context в виде:

<Context path="/bookstore1"
docBase="../docs/tutorial/examples/web/bookstore1/build"
debug="0">
Обратите внимание, что элемент context полностью указывает месторасположение файлов
Web-приложения через свой атрибут docBase.
Файлы компоновки примера руководства содержат задание ant install, которое вызывает
команду ant install:

<target name="install"
description="Install web application" depends="build">
<install url="${url}" path="${mywebapp}"

Web-

Rendered by www.RenderX.com
Размещение Web-приложений Стр. 85 из 626

config="file:build/context.xml"
username="${username}" password="${password}"/>
</target>
Команда ant install требует доступности дескриптора размещения Web-приложения
(web.xml). Все приложения примеров руководства поставляются с дескрипторами
размещения.
Для установки приложения Hello1, описанного в разделе "Жизненный цикл Web-
приложения":
1. В терминальном окне перейдите в каталог <JWSDP_HOME>/docs/tutorial/exam-
ples/web/hello1.
2. Убедитесь, что Tomcat выполняется.
3. Выполните команду ant install. Задание install уведомляет Tomcat о доступности нового
контекста.

5.5. Размещение Web-приложений


Существует несколько способов постоянно разместить контекст в Tomcat во время его
работы:
• При помощи команды ant deploy:

<deploy url="url" path="mywebapp"


war="file:/path/to/mywebapp.war"
username="username" password="password" />
В отличие от команды install, которая может обращаться к неупакованному каталогу,
команда deploy требует WAR-файл. Команда deploy загружает WAR-файл в Tomcat и
запускает приложение. С помощью этой команды вы можете разместить приложение
на удаленном сервере.
• При помощи deploytool. При выборе операции deploy, программа копирует созданный
ею WAR-файл в Tomcat и информирует его о новом контексте.
Для размещения приложения Hello1 с помощью deploytool выполните следующие
действия:
1. Выберите WAR hello1.
2. Выберите Tools->Deploy.
3. Нажмите OK для выбора пути контекста по умолчанию /hello1.
4. Введите имя пользователя и пароль, указанный вами при установке Java WSDP.
5. Нажмите Finish.
6. Закройте окно Deploy Console нажав Close.

Доступны, также, еще два метода размещения, но они требуют перезапуска Tomcat:
• Скопируйте каталог Web-приложения или WAR в <JWSDP_HOME>/webapps.

Web-

Rendered by www.RenderX.com
Стр. 86 из 626 Web-приложения

• Скопируйте конфигурационный файл с именем mywebapp.xml, содержаший элемент


context, в каталог <JWSDP_HOME>/webapps. Формат элемента context описан в
документе "Server Configuration Reference" в
<JWSDP_HOME>/docs/tomcat/config/context.html. Обратите внимание, что элемент
context полностью указывает расположение файлов Web-приложения в атрибуте
docBase. Вот, например, элемент context для приложения, рассматриваемого в главе
12:

<Context path="/bookstore1"
docBase="../docs/tutorial/examples/web/
bookstore1/build" debug="0">
Некоторые файлы компоновки примеров содержат задание ant deploy, которое вызывает
команду ant deploy.

5.6. Просмотр списка установленных и размещенных Web-приложений


Если вы хотите увидеть список всех Web-приложений, доступных на текущий момент в
Tomcat, используйте команду ant list:

<list url="url" username="username" password="password" />


Файлы компоновки примеров руководства содержат задание ant list, которое вызывает
команду ant list.
Вы можете, также, увидеть список приложений, запустив менеджер приложений:

http://<host>:8080/manager/list
И, наконец, вы можете просмотреть список выполняющихся на сервере Web-приложений
при помощи deploytool, выбирая сервер из списка Server на левой панели.

5.7. Выполнение Web-приложений


Web-приложение выполняется при обращении в Web-броузере к URL, который отображается
на компонент. После установки и размещения приложения Hello1 вы можете запустить
Web-приложение указав в адресной строке броузера

http://<host>:8080/hello1/greeting
Замените <host> именем хоста, на котором выполняется Tomcat. Если ваш броузер
выполняется на том же самом хосте, что и Tomcat, вы можете заменить <host> на localhost.

5.8. Обновление Web-приложений


Во время разработки часто необходимо сделать изменения в Web-приложение. После
модификации сервлета вы должны:

Web-

Rendered by www.RenderX.com
Обновление Web-приложений Стр. 87 из 626

1. Перекомпилировать класс сервлета.


2. Обновить приложение на сервере.
3. Перезагрузить URL на клиенте.
После обновления JSP-страницы нет необходимости перекомпилировать или перезагружать
приложение, поскольку Tomcat выполняет это автоматически.
Чтобы попробовать эту возможность, измените сервлет-версию приложения Hello.
Например, вы можете изменить приветствие, возвращаемое сервлетом GreetingServlet,
следующим образом:

<h2>Hi, my name is Duke. What's yours?</h2>


Для обновления файла:
1. Отредактируйте GreetingServlet.java в каталоге с исходными файлами
<JWSDP_HOME>/docs/tutorial/examples/web/hello1/src.
2. Выполните команду ant build. Эта команда перекомпилирует сервлет в каталог build.
Процедура обновления приложения на сервере зависит от того, устанавливали ли вы его
при помощи команды ant install, или размещали при помощи команды ant deploy или в
deploytool.

5.8.1. Перезагрузка Web-приложений


Если вы устанавливали приложение при помощи команды ant install, обновление
приложения на сервере производится путем выполнения команды ant reload:

<reload url="url" path="mywebapp"


username="username" password="password" />
Файлы компоновки примеров содержат задание ant remove, которое вызывает команду
ant remove. Таким образом, для обновления приложения Hello1 на сервере выполните
команду ant reload. Для просмотра обновленного приложения перезагрузите URL Hello1 в
броузере клиента. Обратите внимание, что команда reload замечает изменения только в
классах Java, а не в файле web.xml. Для перезагрузки web.xml удалите приложение (см.
раздел "Удаление Web-приложений") и установите его снова.
В окне броузера вы должны увидеть экран, изображенный на рисунке 4-3.

Web-

Rendered by www.RenderX.com
Стр. 88 из 626 Web-приложения

Рисунок 4-3 Новое приветствие

Чтобы попробовать возможности обновления для JSP-версии примера, прежде всего


скомпонуйте и разместите ее:
1. В терминальном окне перейдите в каталог <JWSDP_HOME>/docs/tutorial/exam-
ples/web/hello2.
2. Выполните команду ant build. Задание build вызовет все необходимые компиляции и
скопирует файлы в каталог <JWSDP_HOME>/docs/tutorial/examples/web/hello2/build.
3. Выполните команду ant install. Задание install скопирует каталог build в каталог
<JWSDP_HOME>/webapps и укажет Tomcat, что доступно новое приложение.
ant buildкопированияdocs/tutorial/examples/web/hello2/buildHello2

5.8.2. Повторное размещение Web-приложений


Если Web-приложение было размещено с использованием deploytool, его можно обновить
следующим образом:
1. Выберите WAR hello1.
2. Выберите Tools->Update Files.
3. Появится диалоговое окно, показывающее измененный файл. Проверьте, что это
GreetingServlet.class, и дважды нажмите OK.
4. Выберите Tools->Update and Redeploy.
5. Появится диалоговое окно. Выберите /hello1 из поля списка Select Webapp to redeploy
и нажмите OK.
6. Закройте окно Redeploy Console, нажав Close.
Если приложение было размещено с использованием команды ant deploy, его можно
обновить при помощи команды ant undeploy, а затем ant deploy.

Web-

Rendered by www.RenderX.com
Удаление Web-приложений Стр. 89 из 626

5.9. Удаление Web-приложений


Для прекращения работы Web-приложения выполните команду ant remove:

<remove url="url" path="mywebapp"


username="username" password="password" />
Файлы компоновки примеров содержат задание ant remove, вызывающее команду ant
remove.

5.10. Отмена размещения Web-приложений


Для отмены размещения Web-приложения используйте команду ant undeploy:

<undeploy url="url" path="mywebapp"


username="username" password="password" />
или команду Undeploy в deploytool. Например, для отмены размещения приложения Hello1
с помощью deploytool:
1. Выберите WAR hello1.
2. Выберите Tools->Undeploy.
3. Появится диалоговое окно. Выберите /hello1 из поля списка Select Webapp to undeploy
и нажмите OK.
4. Закройте окно Undeploy Console, нажав Close.
или
1. Выберите сервер из списка Server в левой панели.
2. Выберите приложение hello1 в панели Deployed Applications.
3. Нажмите Undeploy.
Некоторые из файлов компоновки примеров содержат задание ant undeploy, вызывающее
команду ant undeploy.

5.11. Интернационализация и локализация Web-приложений


Интернационализация - это процесс подготовки приложения к поддержке различных языков
и форматов дат. Локализация - это процесс адаптирования интернационализированного
приложения к поддержке конкретного языка и местности. Хотя все клиентские
пользовательские интерфейсы должны быть интернационализированы и локализованы,
для Web-приложений это особенно актуально из-за природы Web. Хороший обзор процессов
интернационализации и локализации приведен в статье
http://java.sun.com/docs/books/tutorial/i18n/index.html
Существует два подхода к интернационализации Web-приложений:
• Предоставить версию JSP-страницы для каждой местности и дать контроллеру сервлетов
перенаправить запрос к соответствующей странице (в зависимости от местности). Этот

Web-

Rendered by www.RenderX.com
Стр. 90 из 626 Web-приложения

подход полезен при необходимости интернационализации большого количества данных


на странице или Web-приложения полностью.
• Выделить все чувствительные к местности данные на странице (такие как сообщения
об ошибках, строковые константы или метки кнопок) в пакеты ресурсов и обращаться
к данным так, чтобы соответствующие сообщения автоматически извлекались и
вставлялись в страницу. Таким образом, вместо создания строк непосредственно в
коде, вы создаете пакет ресурсов, содержащий переведенный текст, и читаете этот
текст из этого пакета при помощи соответствующего ключа. Пакет ресурсов может
сохраняться как текстовый файл (пакет ресурсов свойств) или класс (пакет ресурсов
списков), содержащий отображения.
В следующих главах по технологии Web пример "Duke's Bookstore" интернационализирован
и локализован для английского и испанского языков. Пары ключ/значение содержатся в
пакете ресурсов списков с именем messages.BookMessage_*.class. Чтобы показать вам,
как выглядят пары ключ/значение в пакете ресурсов, приведем несколько строк из файла
messages.BookMessages.java.

{"TitleCashier", "Cashier"},
{"TitleBookDescription", "Book Description"},
{"Visitor", "You are visitor number "},
{"What", "What We"re Reading"},
{"Talk", " talks about how Web components can transform the way
you develop applications for the Web. This is a must read for
any self respecting Web developer!"},
{"Start", "Start Shopping"},
Для получения нужных строк для данного пользователя Web-компонент извлекает из
запроса значение местности (установленное в настройках языковых предпочтений в
броузере), открывает пакет ресурсов для этой местности и затем сохраняет пакет как
атрибут сессии (см. раздел "Назначение атрибутов сессии"):

ResourceBundle messages = (ResourceBundle)session.


getAttribute("messages");
if (messages == null) {
Locale locale=request.getLocale();
messages = ResourceBundle.getBundle("WebMessages",
locale);
session.setAttribute("messages", messages);
}
Web-компонент извлекает пакет ресурсов из сессии:

ResourceBundle messages =
(ResourceBundle)session.getAttribute("messages");

Web-

Rendered by www.RenderX.com
Получение доступа к базам данных из Web-приложений Стр. 91 из 626

и ищет строку, связанную с ключом TitleCashier следующим образом:

messages.getString("TitleCashier");
Это очень краткое введение в интернационализацию Web-приложений. Для дополнительной
информации по этому вопросу обратитесь к Java BluePrints:
http://java.sun.com/blueprints

5.12. Получение доступа к базам данных из Web-приложений


Данные, которые используются различными Web-компонентами и остающиеся постоянными
между вызовами Web-приложения, обычно хранятся в базе данных. Web-приложения
используют JDBC 2.0 API для доступа к реляционным базам данных. Информация по этому
API находится на странице
http://java.sun.com/docs/books/tutorial/jdbc

5.12.1. Примеры
Примеры, рассматриваемые в главах 12, 13, 15 и 16 используют базу данных. Для этой
редакции мы проверили примеры с базой данных PointBase 4.3 и предоставили файл
компоновки ant для создания таблиц базы данных и заполнения их. Оставшаяся часть
этого раздела описывает как:
• Установить и запустить сервер баз данных PointBase
• Заполнить таблицы примеров
• Настроить Web-приложение для обращения к источнику данных
• Определить источник данных в Tomcat
• Настроить Tomcat для отображения ссылки на источник данных

5.12.2. Установка и запуск сервера баз данных


Вы можете загрузить пробную копию базы данных PointBase 4.3 с
http://www.poinbase.com
Проверьте, что вы выбрали установочный пакет, предназначенный для вашей платформы
(UNIX или Windows). Установите компоненты клиента и сервера. После загрузки и установки
базы данных PointBase выполните следующие действия:
1. Добавьте свойство pb.home в ваш файл build.properties (рассмотрено в разделе
"Управление примерами"), указывающее на каталог с установленной PointBase. В
системе Windows синтаксис этой записи может выглядеть так:

pb.home=drive:\\<PB_HOME>
2. Скопируйте файл <PB_HOME>/lib/pbclient43.jar в каталог <JWSDP_HOME>/common/lib
для того, чтобы библиотека клиента PointBase стала доступна для приложений

Web-

Rendered by www.RenderX.com
Стр. 92 из 626 Web-приложения

примеров. Если Tomcat работает, перезапустите его для загрузки клиентской


библиотеки.
3. В терминальном окне перейдите в каталог <PB_HOME>/tools/server.
4. Запустите сервер PointBase при помощи команды start_server в UNIX или startserver в
Windows.

5.12.3. Заполнение базы данных


1. В терминальном окне перейдите в каталог <JWSDP_HOME>/docs/tutorial/examples/web.
2. Запустите ant. Команда ant по умолчанию, create-book-db, использует консольную
программу PointBase для выполнения SQL-команд в books.sql. В конце ее работы вы
должны увидеть на экране следующую информацию:

[java] ID
[java] ----------
[java] 201
[java] 202
[java] 203
[java] 204
[java] 205
[java] 206
[java] 207
[java]
[java] 7 Rows Selected.
[java]
[java] SQL>
[java]
[java] COMMIT;
[java] OK

5.12.4. Настройка в Web-приложении ссылки на источник данных


Для доступа к базе данных из Web-приложения необходимо объявить ссылку на ресурс в
дескрипторе размещения этого Web-приложения (см. раздел "Ссылки на элементы
окружения, на элементы окружения ресурсов, или на ресурсы"). Ссылка на ресурс объявляет
JNDI-имя, тип ресурса данных и вид аутентификации, используемой при доступе к ресурсу:

<resource-ref>
<res-ref-name>jdbc/BookDB</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>

Web-

Rendered by www.RenderX.com
Получение доступа к базам данных из Web-приложений Стр. 93 из 626

JNDI-имя используется для создания объекта источника данных во вспомогательном


классе базы данных database.BookDB, используемом примерами этого руководства.
Элемент res-auth указывает, что контейнер будет управлять регистрацией в базе данных.
Для указания ссылки на ресурс в deploytool выполните следующие действия:
1. Выберите WAR.
2. Выберите закладку Resource Refs.
3. Нажмите Add.
4. Введите jdbc/BookDB в поле Coded Name.

5.12.5. Определение источника данных в Tomcat


Для использования базы данных необходимо создать источник данных в Tomcat. Источник
данных содержит информацию о классе драйвера и URL, использующихся для подключения
к базе данных, а также параметры регистрации. Для определения источника данных в
Tomcat используйте admintool (см. "Настройка источников данных") следующим образом:
1. Запустите admintool указав в броузере:

http://localhost:8080/admin/index.jsp
2. Зарегистрируйтесь с именем пользователя и паролем, указанными вами при установке
Java WSDP.
3. Выберите запись Data Source в Resources.
4. Выберите Available Actions->Create New Data Source.
5. Введите pointbase в поле JNDI Name.
6. Введите jdbc:pointbase:server://localhost/sample в поле Data Source URL.
7. Введите com.pointbase.jdbc.jdbcUniversalDriver в поле JDBC Driver Class.
8. Введите public в полях User Name и Password.
9. Нажмите кнопку Save.
10. Нажмите кнопку Commit.

5.12.6. Настройка в Tomcat отображения JNDI-имени в источник данных


Поскольку ссылка на ресурс, объявленная в дескрипторе размещения Web-приложения,
использует JNDI-имя для обращения к источнику данных, необходимо соединить имя с
источником данных при помощи элемента resourceLink в конфигурации Tomcat. Ниже
приведена запись, использующаяся в приложениях, рассмотренных в главах по Web-
технологии:

<Context path="/bookstore1"
docBase="../docs/tutorial/examples/web/bookstore1/build"
debug="0">

Web-

Rendered by www.RenderX.com
Стр. 94 из 626 Java API for XML Processing

<ResourceLink name="jdbc/BookDB" global="pointbase"/>


</Context>
Поскольку ResourceLink является подэлементом элемента context, описанного в разделах
"Установка Web-приложений" и "Размещение Web-приложений", добавлять эту запись в
конфигурацию Tomcat нужно также, как добавляют элемент context: путем передачи имени
конфигурационного файла, содержащего запись, в атрибут config команды ant install, или
путем копирования конфигурационного файла с именем mywebapp.xml, содержащего
элемент context, в каталог <JWSDP_HOME>/webapps.
При размещении приложения с использованием команды ant deploy вы должны
спакетировать конфигурационный файл с именем context.xml, содержащий элемент context,
в каталог META-INF файла WAR.
При размещении приложения с использованием deploytool для подключения выполните
следующие действия:
1. Выберите WAR.
2. Выберите закладку Resource Refs.
3. Выберите источник данных, который вы определили в разделе "Настройка в Web-
приложении ссылки на источник данных".
4. Нажмите кнопку Import Data Sources.
5. Закройте диалоговое окно подтверждения.
6. Выберите pointbase из ниспадающего списка.
Примеры, рассмотренные в главах 12, 13, 15 и 16, иллюстрируют два последних механизма
размещения.

5.13. Дополнительная информация


Дополнительная информация по Web-приложениям и Tomcat находится в:
• Спецификация Java Servlet 3.2 - подробности настройки Web-приложений.
• Справочная документация по Tomcat, поставляемая с Java WSDP в
<JWSDP_HOME>/docs/tomcat/index.html.

6. Java API for XML Processing


Java API for XML Processing (JAXP) предназначен для обработки XML-данных в
приложениях, написанных на языке программирования Java. JAXP использует стандарты
анализаторов SAX (Simple API for XML Parsing) и DOM (Document Object Model), так что
вы можете выбирать между анализом данных как потока событий или построением их
объектного представления. JAXP поддерживает также стандарт XSLT (XML Stylesheet
Language Transformations), предоставляющий вам контроль над представлением данных
и позволяющий преобразование данных в другие XML-документы или в другие форматы,
такие как HTML. JAXP обеспечивает также поддержку пространства имен, позволяя вам
работать с DTD, которые могли бы в противном случае вызвать конфликт имен.

Web-

Rendered by www.RenderX.com
JAXP API Стр. 95 из 626

JAXP позволяет вам использовать любой XML-совместимый анализатор в вашем


приложении. Это обеспечивается при помощи так называемого уровня подключений,
который разрешает подключать реализации SAX или DOM API. Уровень подключений
позволяет также подключить XSL-процессор, дающий вам контроль над способом
отображения ваших XML-данных.

6.1. JAXP API


Основные JAXP API определены в пакете javax.xml.parsers. Этот пакет содержит два
независимых от поставщиков класса генераторов: SAXParserFactory и DocumentBuilderFac-
tory, которые предоставляют вам соответственно SAXParser и DocumentBuilder. Document-
Builder, в свою очередь, создает DOM-совместимый объект Document.
Эти API дают вам возможность подключить XML-реализацию, предлагаемую другим
поставщиком, без изменения вашего исходного кода. Какая именно используется
реализация, зависит от настройки системных свойств javax.xml.parsers.SAXParserFactory
и javax.xml.parsers.DocumentBuilderFactory. Значения по умолчанию (пока они не
переопределены во время выполнения) указывают на справочную реализацию.
В оставшейся части этого раздела показано, как работают различные JAXP API в
приложениях.

6.2. Обзор пакетов


SAX и DOM API определяются группой XML-DEV и W3C соответственно. Библиотеками,
определяющими эти API являются:
javax.xml.parsers
Это JAXP API, обеспечивающие общий интерфейс для SAX и DOM-анализаторов различных
поставщиков.
org.w3c.dom
Определяет класс Document (DOM), а также классы для всех компонентов DOM.
org.xml.sax
Определяет основные SAX API.
javax.xml.transform
"Simple API" for XML (SAX) представляет собой основанный на событиях механизм
последовательного доступа, осуществляющий обработку "элемент-за-элементом". API
этого уровня читает и записывает XML в хранилище данных или Web. Для серверных и
высокопроизводительных приложений вы должны полностью понимать этот уровень. Но
для многих приложений достаточно минимальных знаний.
DOM API обычно легче в использовании. Он обеспечивает относительно знакомую
древовидную структуру объектов. Вы можете использовать DOM API для управления
иерархией объектов приложения, которые он объединяет. DOM API является идеальным
для интерактивных приложений, поскольку в памяти присутствует полная модель объектов,
к ней можно получить доступ и пользователь может управлять ею.

Web-

Rendered by www.RenderX.com
Стр. 96 из 626 Java API for XML Processing

С другой стороны, создание DOM требует чтения полной XML-структуры и хранения дерева
объектов в памяти, то есть этот метод использует значительно больше ресурсов CPU и
памяти. По этой причине SAX API более предпочтителен для серверных приложений и
фильтров данных, которые не требуют наличия данных в памяти.
И, наконец, XSL API, определенный в javax.xml.transform, позволяет вам записывать XML-
данные в файл или преобразовывать их в другие форматы. И, как вы увидите в разделе
по XSLT этого руководства, вы можете даже использовать его совместно с SAX API для
преобразования обычных данных в XML.

6.3. The Simple API for XML (SAX)


Основная схема API SAX-анализа приведена ниже. В начале процесса анализа для
генерации экземпляра анализатора используется экземпляр класса SAXParserFactory.

Рисунок 5-1 SAX API

Анализатор содержит в себе объект SAXReader. При вызове метода анализатора parse()
считыватель вызывает один из нескольких методов обратного вызова, реализованных в
приложении. Эти методы определяются интерфейсами ContentHandler, ErrorHandler,
DTDHandler и EntityResolver.
Вот сводка ключевых SAX API:
SAXParserFactory
Объект SAXParserFactory создает экземпляр анализатора, определенного системным
свойством javax.xml.parsers.SAXParserFactory.
SAXParser
Интерфейс SAXParser определяет методы parse() нескольких типов. В обычном случае,
вы передаете источник XML-данных и объект DefaultHandler анализатору, который
обрабатывает XML и вызывает соответствующие методы объекта handler.
SAXReader
SAXParser включает в себя SAXReader. Обычно, вам не надо беспокоиться об этом, но
изредка необходимо использовать метод getXMLReader() объекта SAXParser для настройки

Web-

Rendered by www.RenderX.com
The Simple API for XML (SAX) Стр. 97 из 626

объекта SAXReader. Именно SAXReader осуществляет общение с обработчиками SAX-


событий, которые вы определяете.
DefaultHandler
DefaultHandler, не показанный на рисунке, реализует интерфейсы ContentHandler,
ErrorHandler, DTDHandler и EntityResolver (с пустыми методами), так что вы можете
переопределить только необходимые.
ContentHandler
При распознавании XML-тега вызываются такие методы как startDocument, endDocument,
startElement и endElement. Этот интерфейс также определяет методы characters и
processingInstruction, которые вызываются при встрече текста в XML-элементе или
встроенной команды обработки соответственно.
ErrorHandler
Методы error, fatalError и warning вызываются в ответ на различные ошибки анализа.
Обработчик ошибок по умолчанию генерирует исключительную ситуацию для фатальных
ошибок и игнорирует остальные ошибки (включая ошибки верификации). Это одна из
причин, из-за которой вы должны знать SAX-анализатор, даже если используете DOM.
Иногда приложение может восстановиться после ошибки верификации. Иногда требуется
сгенерировать исключительную ситуацию. Для гарантии корректной обработки вы должны
предоставить для анализатора ваш собственный обработчик ошибок.
DTDHandler
Определяет методы, которые вы обычно никогда не будете вызывать для использования.
Используется при обработке DTD для распознавания и обработки объявлений для
неанализируемой сущности.
EntityResolver
Метод resolveEntity вызывается тогда, когда анализатор должен идентифицировать данные,
указанные в URI. В большинстве случаев URI представляет собой URL, указывающий
расположение документа, но в некоторых случаях документ может идентифицироваться
при помощи URN - общедоступным идентификатором, или именем, уникальным в Web-
пространстве. Общедоступный идентификатор может быть указан как дополнение к URL.
EntityResolver может затем использовать общедоступный идентификатор вместо URL для
обнаружения документа, например, для доступа к локальной копии документа, если она
существует.
Обычное приложение как минимум реализует большинство из методов ContentHandler.
Поскольку реализации интерфейсов по умолчанию игнорируют весь входной поток, за
исключением фатальных ошибок, надежное приложение может также реализовать методы
ErrorHandler.

6.3.1. Пакеты SAX


SAX-анализатор определяется в пакетах, перечисленных в таблице 5-1.
Таблица 5-1 SAX-пакеты

Web-

Rendered by www.RenderX.com
Стр. 98 из 626 Java API for XML Processing

Пакет Описание
org.xml.sax Определяет SAX-интерфейсы. Имя org.xml является
префиксом пакета, который был разработан группой,
определившей SAX API.
org.xml.sax.ext Определяет расширение SAX, использующееся при более
сложной SAX-обработке, например, для обработки
определений типа документа (DTD) или для просмотра
детального синтаксиса файла.
org.xml.sax.helpers Содержит вспомогательные классы, облегчающие
использование SAX - например, определяя обработчик по
умолчанию, который имеет null-методы для всех
интерфейсов, так что вам необходимо переопределить только
те из них, которые действительно нужны.
javax.xml.parsers Определяет класс SAXParserFactory, возвращающий
SAXParser. Также определяет классы исключительных
ситуаций для вывода ошибок.

6.4. The Document Object Model API (DOM)


На рисунке 5-2 показаны JAXP API в действии:

Рисунок 5-2 DOM API

Для получения экземпляра DocumentBuilder используется класс javax.xml.parsers.Document-


BuilderFactory. DocumentBuilder используется для генерации объекта Document (DOM),
соответствующего спецификации DOM. Фактический построитель, получаемый вами,
определяется в системном свойстве javax.xml.parsers.DocumentBuilderFactory, в котором
выбирается реализация генератора. (Значение по умолчанию может быть переопределено
в командной строке.)
Можно использовать также метод newDocument() объекта DocumentBuilder для создания
пустого Document, реализующего интерфейс org.w3c.dom.Document. В качестве
альтернативы для создания объекта Document из существующих XML-данных вы можете
использовать один из методов parse построителя. Результатом является DOM-дерево,
подобное показанному на рисунке.
Примечание: Хотя элементы DOM-дерева называются объектами, фактически они являются
в некоторой степени низкоуровневыми структурами данных. Например, в каждом узле
element (который соответствует XML-элементу) существует узел text, содержащий имя
тега элемента! Этот вопрос будет рассмотрен в разделе DOM данного руководства, но

Web-

Rendered by www.RenderX.com
XML Stylesheet Language Transformations API (XSLT) Стр. 99 из 626

пользователи, ожидающие объекты, обычно удивлены, когда при вызове метода text()
объекта element ничего не возвращается. Действительно объектно-ориентированное
дерево используется в JDOM API, информацию по которому можно найти на сайте
http://www.jdom.org.

6.4.1. Пакеты DOM


Реализация Document Object Model определяется в пакетах, перечисленных в таблице 5-
2.
Таблица 5-2 Пакеты DOM
Пакет Описание
org.w3c.dom Определяет программные интерфейсы DOM для XML-
документов (и, необязательно, HTML), как определено
спецификацией W3C.
javax.xml.parsers Определяет класс DocumentBuilderFactory и класс Document-
Builder, возвращающий объект, который реализует интерфейс
W3C DOM. Генератор, используемый для создания
построителя, определяется в системном свойстве
javax.xml.parsers, которое можно установить из командной
строки или путем переопределения при вызове метода new
Instance. Также определяет класс ParserConfigurationException
для вывода ошибок.

6.5. XML Stylesheet Language Transformations API (XSLT)


На рисунке 5-3 показаны XSLT API в действии.

Рисунок 5-3 XSLT API

Создается экземпляр объекта TransformerFactory, который используется для создания


Transformer. Объект source является входным для процесса преобразования. Объект
source может быть создан из SAXReader, из DOM, или из входного потока.

Web-

Rendered by www.RenderX.com
Стр. 100 из 626 Java API for XML Processing

Объект result представляет собой результат процесса преобразования. Этот объект может
быть обработчиком событий SAX, DOM, или выходным потоком.
Объект transformer может быть создан из набора инструкций преобразования, в этом
случае проводятся указанные преобразования. Если transformer создан без указания каких-
либо инструкций, то он просто копирует source в result.

6.5.1. Пакеты XSLT


XSLT API определены в следующих пакетах:
Таблица 5-3 Пакеты XSLT
Пакет Описание
javax.xml.transform Определяет классы TransformerFactory и Transformer, которые
используются для получения объекта, способного к
выполнению преобразований. После создания объекта
transformer вызывается его метод transform(), которому
указываются входной (source) и выходной (result) объекты.
javax.xml.transform.dom Классы для создания входного (source) и выходного (result)
объектов из DOM.
javax.xml.transform.sax Классы для создания входного (source) объекта из SAX-
анализатора и выходного (result) объекта из обработчика
событий SAX.
javax.xml.transform.stream Классы для создания входного (source) и выходного (result)
объектов из потока ввода/вывода.

6.6. Компиляция и выполнение программ


В Java WSDP JAXP-библиотеки располагаются в каталоге <JWSDP_HOME>/common/lib.
Для компиляции и выполнения программ примеров прежде всего необходимо установить
JAXP-библиотеки в соответствующее место. (Месторасположение зависит от используемой
версии JVM). Дополнительная информация приведена в инструкции по текущей редакции
JAXP в файле <JWSDP_HOME>/docs/jaxp/ReleaseNotes.xml.

6.7. Куда идти дальше?


Теперь вы имеете достаточно информации для начала изучения JAXP-библиотек. Ваше
следующее действие зависит от того, что именно вы хотите изучить. Вы можете перейти
к:
XML-темы
Если вы хотите узнать больше про XML, тратя минимум времени на Java API. Вы увидите
все разделы по XML в курсе руководства. Следуйте по следующим ссылкам, если хотите
пропустить шаги по программным API:
• Освоение XML
• Написание простого XML-файла
• Замена и вставка текста
• Создание DTD
• Определение атрибутов и сущностей в DTD

Web-

Rendered by www.RenderX.com
Куда идти дальше? Стр. 101 из 626

• Обращение к двоичным сущностям


• Определение сущностей-параметров и условных секций
Разработка структуры XML-данных
Если вы создаете структуру XML-данных для приложения и хотите получить несколько
советов по этому процессу.
Simple API for XML
Если структуры данных уже определены, и вы пишете серверное приложение или XML-
фильтр, которые требуют максимально быстрой обработки. Этот раздел также проведет
вас шаг за шагом через процесс создания XML-документа.
Document Object Model
Если вам необходимо построить дерево объектов из XML-данных, для того чтобы можно
было управлять ими из приложения или преобразовать дерево объектов в XML. Эта часть
руководства заканчивается разделом по пространству имен.
XML Stylesheet Language for Transformations
Если вам необходимо преобразовать XML-теги в какую-то другую форму, если вы хотите
сгенерировать выходные данные в XML-формате, или если вы хотите преобразовать
обычные структуры данных в XML.

7. Simple API for XML


В этой главе мы рассмотрим Simple API for XML (SAX) - управляемый событиями механизм
последовательного доступа к XML-документам. Он является протоколом, который будет,
возможно, использоваться в большинстве сервлетов и сетевых программ для передачи и
приема XML-документов, поскольку это наиболее быстрый и наименее требовательный к
памяти механизм, доступный в настоящее время для работы с XML-документами.
SAX-протокол требует гораздо большего объема программирования, чем Document Object
Model (DOM). Это управляемая событиями модель (вы обеспечиваете методы обратного
вызова, а анализатор вызывает их по мере чтения XML-данных), которая затрудняет
визуализацию данных. И, наконец, вы не можете вернуться к предыдущей части документа
или реорганизовать его, точно также, как не можете вернуться к предыдущим данным в
последовательном потоке данных, или реорганизовать символы, прочитанные из потока.
По этим причинам разработчики, создающие ориентированное на пользователя приложение,
которое отображает XML-документ и, возможно, модифицирует его, будут, вероятно,
использовать DOM-механизм, описанный в следующей части этого руководства - "Document
Object Model".
Однако даже если вы планируете работать исключительно с DOM, существует несколько
важных причин знакомства с моделью SAX:
• Одинаковая обработка ошибок
При анализе документа для DOM генерируются те же самые типы исключительных
ситуаций, так что обработка ошибок в JAXP SAX- и DOM-приложениях идентична.
• Обработка ошибок верификации

Web-

Rendered by www.RenderX.com
Стр. 102 из 626 Simple API for XML

По умолчанию спецификация требует, чтобы ошибки верификации (которые вы изучите


в этой части руководства) игнорировались. Если вы хотите генерировать исключительную
ситуацию при ошибке верификации (а возможно вы так и сделаете), то необходимо
понимать обработку ошибок в SAX.
• Преобразование существующих данных
Как вы узнаете при изучении DOM, существует механизм, используемый для
преобразования существующих данных в XML. Однако использование преимуществ
этого механизма требует понимания модели SAX.
Примечание: Примеры этой главы расположены в каталоге <JWSDP_HOME>/docs/tuto-
rial/examples/jaxp/sax/samples.

7.1. Когда используется SAX


Когда нужно быстрое, эффективное чтение XML-данных, SAX трудно превзойти. Он требует
меньше памяти, поскольку не требуется построения внутреннего представления
(древовидной структуры) XML-данных. Вместо этого он просто передает данные в
приложение по мере чтения - ваше приложение может затем делать все что необходимо
с данными, которые ему доступны.
В сущности, SAX API действует как последовательный поток ввода/вывода. Вы видите
данные по мере их поступления, но вы не можете перейти назад к предыдущей части, или
перепрыгнуть вперед на другую позицию. Обычно все работает хорошо, если вам нужно
просто прочитать данные, и приложение должно что-либо выполнить.
Также полезно понимать модель событий SAX при преобразовании существующих данных
в XML. Как вы увидите в разделе "Генерация XML из произвольной структуры данных",
ключевым моментом процесса преобразования является модификация существующего
приложения для приема соответствующих SAX-событий по мере чтения данных.
Но когда необходимо изменить XML-структуру, особенно если это нужно сделать в
интерактивном режиме, больший смысл имеет применение структуры, находящейся в
памяти, такой как DOM.
Однако, в то время как DOM предоставляет много мощных возможностей для больших
документов (таких как книги и статьи), он также требует много сложного кодирования.
(Подробности этого процесса рассмотрены в разделе "Когда используется DOM").
Для более простых приложений такая сложность может быть нежелательной. Для быстрой
разработки и для более простых приложений может иметь смысл использование одного
из объектно-ориентированных стандартов XML-программирования, как описано в разделе
"JDOM и dom4j".

7.2. Написание простого XML-файла


Давайте начнем писать простую версию XML-данных, которые можно применить для показа
слайдов. В этом упражнении при создании данных используйте ваш текстовый редактор,
для того чтобы ознакомиться с основным форматом XML-файла. Вы будете использовать
этот файл и изменять его в следующих упражнениях.

Web-

Rendered by www.RenderX.com
Написание простого XML-файла Стр. 103 из 626

7.2.1. Создание файла


Используя стандартный текстовый редактор, создайте файл под названием slideSample.xml.
Примечание: Здесь расположена готовый файл: slideSample01.xml. (Версия для броузера
- slideSample01-xml.html.) Вы можете использовать эту версию для сравнения с вашей, а
также для просмотра при чтении этого руководства.

7.2.2. Написание объявления


Далее напишите объявление, которое идентифицирует файл как XML-документ. Объявление
начинается с символов "<?", являющихся стандартным XML-идентификатором для
инструкций обработки. (Вы увидите другие инструкции обработки далее в этом руководстве.)

<?xml version='1.0' encoding='utf-8'?>


Эта строка идентифицирует документ как XML-документ, соответствующий версии 1.0
спецификации XML, и указывает, что в нем используется таблица кодировки символов -
8-битный Unicode. (Информация по таблицам кодировки находится в разделе "Таблицы
кодировки для Java".)
Поскольку документ не был определен как "standalone", анализатор считает, что в нем
могут содержаться ссылки на другие документы. Чтобы увидеть, как указать документ
"standalone", обратитесь к разделу "XML-пролог".

7.2.3. Добавление комментария


Комментарии игнорируются XML-анализаторами. И вы в действительности их никогда не
видите до тех пор, пока не активизируете специальные установки в анализаторе. Вы
увидите, как это сделать далее, когда будет обсуждаться тема "Обработка лексических
событий". А сейчас добавьте выделенный ниже текст, чтобы вставить комментарий в файл.

<?xml version='1.0' encoding='utf-8'?>

<!-- A SAMPLE set of slides -->

7.2.4. Определение корневого элемента


После объявления каждый XML-файл определяет ровно один элемент, известный как
корневой элемент. Все другие элементы в файле содержатся внутри этого элемента.
Введите выделенный ниже текст, чтобы определить корневой элемент этого файла -
slideShow:

<?xml version='1.0' encoding='utf-8'?>

<!-- A SAMPLE set of slides -->

Web-

Rendered by www.RenderX.com
Стр. 104 из 626 Simple API for XML

<slideshow>

</slideshow>
Примечание: Имена XML-элементов чувствительны к регистру символов. Завершающий
тег должен в точности соответствовать начальному тегу.

7.2.5. Добавление атрибутов к элементу


Приложение показа слайдов имеет несколько связанных элементов данных, ни один из
которых не требует какой-либо структуры. Так что вполне естественно определить их как
атрибуты элемента slideshow. Добавьте выделенный ниже текст, чтобы установить
некоторые атрибуты:

...
<slideshow

title="Sample Slide Show"


date="Date of publication"
author="Yours Truly"

>
</slideshow>
При создании имени тега или атрибута можно использовать дефисы ("-"), знаки
подчеркивания ("_"), двоеточия (":") и точку (".") в дополнение к символам и цифрам. В
отличие от HTML значения XML-атрибутов всегда находятся в кавычках, а несколько
атрибутов никогда не разделяются запятой.
Примечание: Двоеточия должны использоваться с осторожностью или вообще не
использоваться, поскольку они применяются при определении пространства имен для
XML-документа.

7.2.6. Добавление вложенных элементов


XML позволяет записывать иерархически структурированные данные. Это означает, что
элемент может содержать другие элементы. Добавьте выделенный ниже текст, чтобы
определить элемент slide и элемент title, находящийся внутри его:

<slideshow
...
>

<!-- TITLE SLIDE -->

Web-

Rendered by www.RenderX.com
Написание простого XML-файла Стр. 105 из 626

<slide type="all">
<title>Wake up to WonderWidgets!</title>
</slide>

</slideshow>
Здесь также был добавлен атрибут type к элементу slide. Назначение этого атрибута
следующее - слайды могут быть отмечены для показа технической или исполнительной
аудитории (type="tech" или type="exec"), а также идентифицированы подходящими обеим
аудиториям (type="all").
Более важно, что этот пример иллюстрирует различие между объектами, которые лучше
определить элементами (элемент title), и объектами, которые лучше определить атрибутами
(атрибут type). Здесь работает эвристический подход, основанный на видимости. title - это
то, что аудитория будет видеть. Поэтому он является элементом. type, с другой стороны,
- это то, что никогда не будет показано, поэтому это - атрибут. Другим способом это
различие можно объяснить так - элемент является контейнером, наподобие бутылки. type
- это характеристика контейнера (длинный он или короткий, широкий или узкий). title - это
характеристика содержимого (вода, молоко или чай). Это, естественно, не жесткие, быстро
применимые правила, но они могут помочь вам при разработке своих собственных XML-
структур.

7.2.7. Добавление HTML-текста


Поскольку XML разрешает определение любых тегов, имеет смысл определить набор
тегов, выглядящих как HTML. Фактически, стандарт XHTML делает именно это. Вы
прочитаете подробнее об этом далее в настоящем руководстве по SAX. А сейчас введите
выделенный ниже текст, чтобы определить slide с парами записей элементов списка,
которые используют тег <em> HTML-стиля для выделения (обычно отображаемого в виде
наклонного текста):

...
<!-- TITLE SLIDE -->
<slide type="all">
<title>Wake up to WonderWidgets!</title>
</slide>

<!-- OVERVIEW -->


<slide type="all">
<title>Overview</title>
<item>Why <em>WonderWidgets</em> are great</item>
<item>Who <em>buys</em> WonderWidgets</item>
</slide>

Web-

Rendered by www.RenderX.com
Стр. 106 из 626 Simple API for XML

</slideshow>
Далее мы увидим, что определение элемента title вступает в конфликт с элементом XHTML,
использующим такое же имя. Мы рассмотрим механизм, вызывающий конфликт (DTD), и
несколько возможных решений в разделе "Анализ и параметризованные DTD".

7.2.8. Добавление пустого элемента


Одним из основных отличий между XML и HTML является то, что все XML-документы
должны быть фомально-правильными - это означает, что каждый тег должен иметь
завершающий тег или быть пустым тегом. Вы уже хорошо знакомы с завершающими
тегами. Добавьте выделенный ниже текст, чтобы определить пустой элемент списка без
содержимого:

...
<!-- OVERVIEW -->
<slide type="all">
<title>Overview</title>
<item>Why <em>WonderWidgets</em> are great</item>

<item/>

<item>Who <em>buys</em> WonderWidgets</item>


</slide>

</slideshow>
Обратите внимание, что любой элемент может быть пустым элементом. Все что для этого
необходимо - закончить тег знаками "/>", а не ">". Можно сделать то же самое путем ввода
<item></item>, что является эквивалентом.
Примечание: Другим фактором, делающим XML-файл формально-правильным, является
правильное вложение. То есть, <b><i>some_text</i></b> является формально-правильным,
поскольку последовательность <i>…</i> полностью вложена в тег <b>…</b>. Следующая
последовательность не является формально-правильной: <b><i>some_text</b></i>.

7.2.9. Законченный продукт


Вот полная версия XML-файла:

<?xml version='1.0' encoding='utf-8'?>

<!-- A SAMPLE set of slides -->

Web-

Rendered by www.RenderX.com
Дублирование XML-файла при помощи SAX-анализатора Стр. 107 из 626

<slideshow
title="Sample Slide Show"
date="Date of publication"
author="Yours Truly"
>

<!-- TITLE SLIDE -->


<slide type="all">
<title>Wake up to WonderWidgets!</title>
</slide>

<!-- OVERVIEW -->


<slide type="all">
<title>Overview</title>
<item>Why <em>WonderWidgets</em> are great</item>
<item/>
<item>Who <em>buys</em> WonderWidgets</item>
</slide
</slideshow>
Теперь, когда вы создали файл для работы, вы готовы написать программу, дублирующую
этот файл при помощи SAX-анализатора. Вы сделаете это в следующем разделе.

7.3. Дублирование XML-файла при помощи SAX-анализатора


В реальной жизни вам вряд ли понадобится дублирование XML-файла с использованием
SAX-анализатора. Обычно нужно обработать данные каким-либо образом, для того чтобы
сделать что-то полезное с ними. (Если надо продублировать XML-данные, легче построить
DOM-дерево и использовать его для вывода.) Но дублирование XML-структуры является
хорошим способом увидеть SAX-анализатор в действии и может быть полезно для отладки.
В этом упражнении события SAX-анализатора направляются в System.out. Будем считать
его версией "Hello World" программы XML-обработки. В упражнении демонстрируется
использование SAX-анализатора для получения данных и их отображения.
Примечание: Исходный код, рассматриваемый в этом разделе, находится в файле
Echo01.java. Файл, с которым он работает - slideSample01.xml. (Версия для броузера -
slideSample01-xml.html.)

7.3.1. Создание скелета программы


Начните с создания файла с именем Echo.java и введите скелет приложения:

public class Echo


{
public static void main(String argv[])

Web-

Rendered by www.RenderX.com
Стр. 108 из 626 Simple API for XML

}
Поскольку мы собираемся выполнять ее автономно, нам необходим метод main. И нам
необходимы аргументы командной строки, чтобы указать приложению, в какой файл
осуществлять вывод.

7.3.2. Импорт классов


Далее, добавьте операторы import для классов, которые будет использовать приложение:

import java.io.*;
import org.xml.sax.*;
import org.xml.sax.helpers.DefaultHandler;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;

public class Echo


{
...
Классы в java.io, естественно, нужны для вывода. Пакет org.xml.sax определяет все
используемые для SAX-анализатора интерфейсы. Класс SAXParserFactory создает
используемый нами экземпляр. Он генерирует ParserConfigurationException, если не может
создать анализатор, удовлетворяющий указанным настройкам конфигурации. (Более
детально о настройках конфигурации будет сказано ниже.) Генератор создает SAXParser
для анализа, а DefaultHandler определяет класс, который будет обрабатывать SAX-события,
генерируемые анализатором.

7.3.3. Настройка ввода/вывода


Первоначальный порядок действий - обработать аргумент командной строки, получить
имя файла для вывода и настроить выходной поток. Добавьте выделенный ниже текст,
чтобы выполнить эти задачи, а также некоторые дополнительные служебные действия:

public static void main(String argv[])

if (argv.length != 1) {
System.err.println("Usage: cmd filename");

Web-

Rendered by www.RenderX.com
Дублирование XML-файла при помощи SAX-анализатора Стр. 109 из 626

System.exit(1);
}
try {
//
out = new OutputStreamWriter(System.out, "UTF8");
}
catch (Throwable t) {
t.printStackTrace();
}
System.exit(0);

static private Writer out;

При создании OutputStreamWriter мы выбираем кодировку символов UTF-8. Можно было


выбрать и кодировку US-ASCII или UTF-16, также поддерживаемые платформой Java.
Дополнительная информация по этим кодировкам символов приведена в разделе "Таблицы
кодировок в Java".

7.3.4. Реализация интерфейса ContentHandler


Наиболее важным интерфейсом для наших текущих задач является интерфейс
ContentHandler. Этот интерфейс требует нескольких методов, которые вызывает SAX-
анализатор в ответ на различные события анализа. Основными методами обработки
событий являются: startDocument, endDocument, startElement, endElement и characters.
Самый простой способ реализовать этот интерфейс - расширить класс DefaultHandler,
определенный в пакете org.xml.sax.helpers. Этот класс предоставляет ничего не делающие
методы для всех событий ContentHandler. Введите выделенный ниже код для расширения
класса:

public class Echo extends DefaultHandler

{
...
}

Примечание: DefaultHandler предоставляет также пустые методы для других основных


событий, определенных в интерфейсах DTDHandler, EntityResolver и ErrorHandler. Вы
изучите эти методы по мере продвижения вперед.

Web-

Rendered by www.RenderX.com
Стр. 110 из 626 Simple API for XML

Каждый из этих методов необходим интерфейсу для генерации SAXException. Генерируемая


здесь исключительная ситуация передается назад в анализатор, который передает ее в
код, вызвавший анализатор. В данной программе это означает, что она возвращается в
обработчик исключительных ситуаций Throwable в конце метода main.
При обнаружении начального или завершающего тега его имя передается как String в
метод startElement или endElement соответственно. При обнаружении начального тега все
определенные в нем атрибуты также передаются в списке Attributes. Символы, найденные
внутри элемента, передаются в виде массива символов вместе с их количеством (length)
и смещением в массиве, указывающим на первый символ.

7.3.5. Настройка анализатора


Теперь, наконец, вы готовы настроить анализатор. Для этого добавьте выделенный ниже
текст:

public static void main(String argv[])


{
if (argv.length != 1) {
System.err.println("Usage: cmd filename");
System.exit(1);
}

//
SAX-
DefaultHandler handler = new Echo();

//
( )
SAXParserFactory factory = SAXParserFactory.newInstance();
try {
//
out = new OutputStreamWriter(System.out, "UTF8");

//
SAXParser saxParser = factory.newSAXParser();
saxParser.parse( new File(argv[0]), handler );

} catch (Throwable t) {
t.printStackTrace();
}

Web-

Rendered by www.RenderX.com
Дублирование XML-файла при помощи SAX-анализатора Стр. 111 из 626

System.exit(0);
}
При помощи этих строк кода вы создали экземпляр SAXParserFactory, как определено в
установках системного свойства javax.xml.parsers.SAXParserFactory. Затем вы получили
экземпляр анализатора этого класса для обработки событий анализа и указали входной
файл для обработки.
Примечание: Класс javax.xml.parsers.SAXParser является оболочкой, в которой определяется
несколько удобных методов. Он включает в себя (немного менее дружественный) объект
org.xml.sax.Parser. При необходимости можно получить этот анализатор при помощи метода
getParser() объекта SAXParser.
Теперь вы просто перехватываете любые исключительные ситуации, которые анализатор
может сгенерировать. Более подробно обработку ошибок вы изучите в последнем разделе
этого руководства "Обработка ошибок неверифицирующим анализатором".

7.3.6. Вывод результата


Методы ContentHandler генерируют SAXExcetion, а не IOException, которые могут возникнуть
во время записи. Однако SAXException может включить в себя другую исключительную
ситуацию, так что имеет смысл осуществить вывод в методе, который позаботится о
деталях обработки исключительных ситуаций. Добавьте выделенный ниже код для
определения метода emit, который делает это:

static private Writer out;

private void emit(String s)


throws SAXException
{
try {
out.write(s);
out.flush();
} catch (IOException e) {
throw new SAXException("I/O error", e);
}
}

...
При вызове emit любая ошибка ввода/вывода перехватывается в SAXException вместе с
сообщением, идентифицирующим ее. Затем эта исключительная ситуация передается
назад в SAX-анализатор. Далее вы изучите исключительные ситуации SAX более подробно.
А пока имейте в виду, что emit - это маленький метод, который обрабатывает вывод строк.
(Вы увидите, что он часто вызывается в коде.)

Web-

Rendered by www.RenderX.com
Стр. 112 из 626 Simple API for XML

7.3.7. Расстановка разделителей при выводе


Вот еще небольшой фрагмент кода, необходимый нам перед выполнением какой-либо
реальной обработки. Добавьте выделенный ниже код для определения метода nl(), который
записывает символ конца строки, используемый в данной системе:

private void emit(String s)


...
}

private void nl()


throws SAXException
{
String lineEnd = System.getProperty("line.separator");
try {
out.write(lineEnd);
} catch (IOException e) {
throw new SAXException("I/O error", e);
}

}
Примечание: Немного досадно, но вам придется вызывать nl() много раз. Определение
его сейчас упростит код в дальнейшем. Он также обеспечивает место для вставки отступов
при выводе.

7.3.8. Обработка событий содержимого


И, наконец, напишем код, который действительно обрабатывает события ContentHandler.

7.3.8.1. События документа


Добавьте выделенный ниже код для обработки событий start-document и end-document:

static private Writer out;

public void startDocument()


throws SAXException
{
emit("<?xml version='1.0' encoding='UTF-8'?>");
nl();
}

Web-

Rendered by www.RenderX.com
Дублирование XML-файла при помощи SAX-анализатора Стр. 113 из 626

public void endDocument()


throws SAXException
{
try {
nl();
out.flush();
} catch (IOException e) {
throw new SAXException("I/O error", e);
}
}

private void echoText()


...
Здесь вы дублируете XML-объявление при обнаружении анализатором начала документа.
Поскольку вы настроили OutputStreamWriter на использование кодировки UTF-8, вы
включаете эту спецификацию как часть объявления.
Примечание: IO-классы не понимают имен кодировок с дефисом, поэтому вы указали
"UTF8", а не "UTF-8".
В конце документа вы просто выводите последнюю newline и сбрасываете выходной поток.
Не так уж много чего здесь происходит.

7.3.8.2. События элементов


А теперь - интересная вещь. Добавьте выделенный ниже код для обработки событий start-
element и end-element:

public void startElement(String namespaceURI,


String sName, //
String qName, //
Attributes attrs)
throws SAXException
{
String eName = sName; //
if ("".equals(eName)) eName = qName; // not namespaceAware
emit("<"+eName);
if (attrs != null) {
for (int i = 0; i < attrs.getLength(); i++) {
String aName = attrs.getLocalName(i); //
if ("".equals(aName)) aName = attrs.getQName(i);
emit(" ");
emit(aName+"=\""+attrs.getValue(i)+"\"");

Web-

Rendered by www.RenderX.com
Стр. 114 из 626 Simple API for XML

}
}
emit(">");
}

public void endElement(String namespaceURI,


String sName, //
String qName //
)
throws SAXException
{
String eName = sName; //
if ("".equals(eName)) eName = qName; // not namespaceAware
emit("<"+eName+">");
}

private void emit(String s)


...
При помощи этого кода вы дублируете теги элементов, включая все атрибуты,
определенные в начальном теге. Обратите внимание на то, что когда вызывается метод
startElement(), простое имя ("локальное имя") элементов и атрибутов можно считать пустой
строкой, если обработка пространства имен не разрешена. Код обрабатывает этот случай,
используя полное имя, когда простое имя представляет собой пустую строку.

7.3.8.3. События для символов


Для завершения обработки событий содержимого необходимо обработать символы,
которые анализатор передает в ваше приложение.
От анализаторов не требуется возвращать какое-то конкретное количество символов за
какое-то время. Анализатор может вернуть все, начиная от одного символа до нескольких
тысяч, и все-таки быть соответствующей стандартам реализацией. Таким образом, если
в приложении нужно обработать символы, которые оно видит, желательно собрать символы
в буфер, и работать с ними только в случае полной уверенности в том, что все они были
найдены.
Добавьте выделенный ниже код для определения текстового буфера:

public class Echo01 extends DefaultHandler


{

StringBuffer textBuffer;

Web-

Rendered by www.RenderX.com
Дублирование XML-файла при помощи SAX-анализатора Стр. 115 из 626

public static void main(String argv[])


{

...
Затем добавьте выделенный ниже код для сбора символов, переданных анализатором, в
буфер:

public void endElement(...)


throws SAXException
{
...
}

public void characters(char buf[], int offset, int len)


throws SAXException
{
String s = new String(buf, offset, len);
if (textBuffer == null) {
textBuffer = new StringBuffer(s);
} else {
textBuffer.append(s);
}
}

private void emit(String s)


...
Далее, добавьте метод, выделенный ниже, для передачи содержимого буфера в выходной
поток.

public void characters(char buf[], int offset, int len)


throws SAXException
{
...
}

private void echoText()


throws SAXException
{

Web-

Rendered by www.RenderX.com
Стр. 116 из 626 Simple API for XML

if (textBuffer == null) return;


String s = ""+textBuffer
emit(s);
textBuffer = null;
}

private void emit(String s)


...
После вызова этого метода дважды на строку (что будет происходить вовремя, как мы
увидим далее), буфер станет нулевым. В этом случае метод просто возвращает управление.
Если буфер не пуст, его содержимое передается в выходной поток.
И, наконец, добавьте выделенный ниже код для дублирования содержимого буфера в
начале элемента и в конце:

public void startElement(...)


throws SAXException
{
echoText();
String eName = sName; //
...
}

public void endElement(...)


throws SAXException
{
echoText();
String eName = sName; //
...
}
Сбор текста завершается, когда заканчивается элемент. То есть, в этот момент времени
вы выводите его в выходной поток, и буфер очищается перед началом следующего
элемента.
Но вы хотите выводить собранный текст и в начале элемента! Это необходимо для данных,
составляющих документ, которые могут содержать XML-элементы, смешанные с текстом.
Например, как в этом фрагменте документа:

<para>This paragraph contains <bold>important</bold>


ideas.</para>
В начале текст "This paragraph contains" завершается начальным элементом <bold>. Текст
"important" заканчивается завершающим тегом </bold>, а оставшийся текст "ideas."
заканчивается тегом </para>.

Web-

Rendered by www.RenderX.com
Дублирование XML-файла при помощи SAX-анализатора Стр. 117 из 626

Примечание: В большинстве случаев собранный текст будет выводиться при возникновении


события endElement(). Когда после этого возникает событие startElement(), буфер еще
пуст. В первой строке метода echoText() проверяется эта ситуация и производится возврат
из метода.
Поздравляем! Вы написали полное приложение SAX-анализатора. Следующим действием
является его компиляция и выполнение.
Примечание: Чтобы быть абсолютно точным, обработчик символов должен просматривать
буфер в поисках символов амперсанда ("&") и левой угловой скобки ("<") и заменять их
строками "&amp;" или "&lt;" соответственно. Более подробно об этом типе обработки мы
расскажем в разделе "Замена и вставка текста".

7.3.9. Компиляция и выполнение программы


В Java WSDP JAXP-библиотеки находятся в каталоге <JWSDP_HOME>/common/lib. Для
компиляции программы необходимо прежде всего установить JAR-файлы JAXR в
соответствующее место. (Названия JAR-файлов зависят от используемой версии JAXP,
а их месторасположение зависит от используемой версии платформы Java. Для получения
самой последней информации обратитесь к файлу
<JWSDP_HOME>/docs/jaxp/ReleaseNotes.html.)
Примечание: Поскольку JAXP 1.1 встроен в версию 1.4 платформы Java 2, вы можете,
также, выполнить большинство примеров разделов JAXP этого руководства (SAX, DOM и
XSLT) без какой-либо специальной установки JAR-файлов. Однако, для использования
дополнительных возможностей в JAXP - XML Schema и компилирующего транслятора
XSLTC вам необходимо установить JAXP 1.2.
В версиях 1.2 и 1.3 платформы Java 2 для компиляции и запуска программы можно
выполнить следующие команды:

javac -classpath jaxp-jar-files Echo.java


java -cp jaxp-jar-files Echo slideSample.xml
В качестве альтернативы, можно разместить JAR-файлы в каталоге расширений платформы
Java и использовать более простые команды:

javac Echo.java
java Echo slideSample.xml
В версии 1.4 платформы Java 2 необходимо идентифицировать JAR-файлы как более
новые версии "рекомендованных стандартов", которые встроены в платформу Java 2. Для
этого разместите JAR-файлы в каталоге рекомендованных стандартов - jre/lib/endorsed.
(Скопируйте все JAR-файлы, кроме jaxp-api.jar. Его можно проигнорировать, поскольку
JAXP API уже встроен в платформу Java 2 версии 1.4.)
Затем можно откомпилировать и запустить программу при помощи следующих команд:

javac Echo.java
java Echo slideSample.xml

Web-

Rendered by www.RenderX.com
Стр. 118 из 626 Simple API for XML

Примечание: Можно, также, установить системное свойство java.endorced.dirs из командной


строки таким образом, чтобы он указывал на каталог, содержащий необходимые JAR-
файлы, используя следующий параметр командной строки: -D"java.endorsed.dirs=somePath".

7.3.10. Проверка выводимой информации


Вот часть выводимой программой информации, содержащей некоторые непонятные
разделители:

...
<slideshow title="Sample Slide Show" date="Date of publication"
author="Yours Truly">

<slide type="all">
<title>Wake up to WonderWidgets!</title>
</slide>
...
Примечание: Выводимая программой информация находится в файле Echo01-01.txt.
(Версия для броузера - Echo01.01.html.)
При взгляде на эту информацию возникает несколько вопросов. А именно, откуда
появляются лишние пустые строки? И почему элементы располагаются с правильными
отступами, хотя в коде для этого ничего не делалось? Мы ответим на эти вопросы. Прежде
всего, необходимо сделать несколько замечаний о выводимой информации:
• Комментарии, определенные в начале файла
<!-- A SAMPLE set of slides -->
не появляются в листинге. Комментарии будут игнорироваться до тех пор, пока вы не
реализуете LexicalHandler. Более подробно этот вопрос рассмотрен ниже.
• Атрибуты элемента перечисляются все вместе на одной строке. Если ваше окно
недостаточно широко, вы можете их всех не увидеть.
• Определенный вами пустой элемент (<item/>) выводится как пустой элемент из двух
тегов (<item></item>). Они в любых случаях идентичны. (Просто первый легче набирать
и он занимает меньше места.)

7.3.11. Идентификация событий


Данная версия программы дублирования может быть полезна для отображения XML-
файла, но она не много расскажет вам о том, что происходит в анализаторе. Следующим
шагом является изменение программы таким образом, чтобы можно было определить,
откуда появляются пробелы и вертикальные линии.
Примечание: Исходный код, приведенный в этом разделе, находится в файле Echo02.java.
Выходная информация, которую он отображает, находится в файле Echo02-01.txt. (Версия
для броузера - Echo02-01.html.)

Web-

Rendered by www.RenderX.com
Дублирование XML-файла при помощи SAX-анализатора Стр. 119 из 626

Сделайте выделенные ниже изменения для идентификации событий по мере их


возникновения:

public void startDocument()


throws SAXException
{
nl();
nl();
emit("START DOCUMENT");
nl();
emit("<?xml version='1.0' encoding='UTF-8'?>");
nl();
}

public void endDocument()


throws SAXException
{
nl();
emit("END DOCUMENT");
try {
...
}

public void startElement(...)


throws SAXException
{
echoText();
nl();
emit("ELEMENT: ");
String eName = sName; //
if ("".equals(eName)) eName = qName; // not namespaceAware
emit("<"+eName);
if (attrs != null) {
for (int i = 0; i < attrs.getLength(); i++) {
String aName = attrs.getLocalName(i); //
if ("".equals(aName)) aName = attrs.getQName(i);
emit(" ");
emit(aName+"=\""+attrs.getValue(i)+"\"");
nl();
emit(" ATTR: ");
emit(aName);

Web-

Rendered by www.RenderX.com
Стр. 120 из 626 Simple API for XML

emit("\t\"");
emit(attrs.getValue(i));
emit("\"");
}
}
if (attrs.getLength() > 0) nl();
emit(">");
}

public void endElement(...)


throws SAXException
{
echoText();
nl();
emit("END_ELM: ");
String eName = sName; //
if ("".equals(eName)) eName = qName; // not namespaceAware
emit("<"+eName+">");
}

...

private void echoText()


throws SAXException
{
if (textBuffer == null) return;
nl();
emit("CHARS: |");
String s = ""+textBuffer
emit(s);
emit("|");
textBuffer = null;
}
Откомпилируйте и выполните эту версию программы для создания более информативного
выходного листинга. Атрибуты показываются по одному на строку, и это уже неплохо. Но,
что более важно, такие строки как, например:

CHARS: |

Web-

Rendered by www.RenderX.com
Дублирование XML-файла при помощи SAX-анализатора Стр. 121 из 626

показывают, что и отступы и символы новой строки, разделяющие атрибуты, происходят


от данных, которые анализатор передает в метод characters().
Примечание: Спецификация XML требует, чтобы все разделители строки были приведены
к одинарному символу новой строки. Символ новой строки определяется также, как в
языках Java, С и системах UNIX, но в системах Windows известен под псевдонимом "перевод
строки".

7.3.12. Сжатие выводимой информации


Чтобы сделать выводимую информацию более удобной для чтения, измените программу
так, чтобы она выводила только символы, отличные от пробелов.
Примечание: Исходный код, приведенный в этом разделе, находится в файле Echo03.java.
Сделайте выделенные ниже изменения для подавления вывода пробелов:

public void echoText()


throws SAXException
{
nl();
emit("CHARS: |");
emit("CHARS: ");
String s = ""+textBuffer;
if (!s.trim().equals("")) emit(s);
emit("|");
}
Далее, добавьте выделенный ниже код для вывода каждого набора символов, переданных
анализатором:

public void characters(char buf[], int offset, int len)


throws SAXException
{
if (textBuffer != null) {
echoText();
textBuffer = null;
}
String s = new String(buf, offset, len);
...
}
Если выполнить программу сейчас, можно увидеть, что табуляции также были удалены,
поскольку они являются частью пробелов перед началом элемента. Добавьте выделенный
ниже код для управления табуляцией:

static private Writer out;

Web-

Rendered by www.RenderX.com
Стр. 122 из 626 Simple API for XML

private String indentString = " "; //

private int indentLevel = 0;

...

public void startElement(...)


throws SAXException
{
indentLevel++;
nl();
emit("ELEMENT: ");
...
}

public void endElement(...)


throws SAXException
{
nl();
emit("END_ELM: ");
emit("</"+sName+">");
indentLevel--;
}
...
private void nl()
throws SAXException
{
...
try {
out.write(lineEnd);
for (int i=0; i < indentLevel; i++)
out.write(indentString);
} catch (IOException e) {
...
}
В этом фрагменте устанавливается строка с пробелами для табуляции, отслеживается
текущий уровень табуляции и эта строка выводится в каждом методе n1. Если установить
строку табуляции в "", то информация будет выводиться без табуляций. (Попробуйте. Вы
увидите, что добавление табуляции было не лишней работой.)

Web-

Rendered by www.RenderX.com
Дублирование XML-файла при помощи SAX-анализатора Стр. 123 из 626

Вы, наверно, будете счастливы узнать, что вы достигли конца "механического" кода,
который нужно было добавить в программу Echo. С этого момента вы будете выполнять
работу, которая позволит понять внутреннюю работу анализатора, хотя действия, которые
вы уже выполнили, дали понимание того, как анализатор видит обрабатываемые XML-
данные. Вы также научились использовать полезное средство отладки для просмотра
того, что видит анализатор.

7.3.13. Исследование выходной информации


Вот часть выходной информации этой версии программы:

ELEMENT: <slideshow
...
>
CHARS:
CHARS:
ELEMENT: <slide
...
END_ELM: </slide>
CHARS:
CHARS:
Примечание: Выходная информация в полном виде находится в файле Echo03-01.txt.
(Версия для броузера - Echo03-01.html).
Обратите внимание на то, что метод characters вызывается дважды для каждой строки.
Исследование исходного файла slideSample01.xml показывает, что перед первым слайдом
есть комментарий. Первый вызов characters приходит перед этим комментарием. Второй
вызов - после. (Далее вы увидите, как можно получить уведомление о встрече анализатором
комментария, хотя в большинстве случаев оно вам не нужно.)
Обратите внимание, что метод characters вызывается после первого элемента slide, так
же как и перед ним. Если рассуждать с точки зрения иерархически структурированных
данных - это кажется странным. В конце концов, вы ожидали, что элемент slideshow
содержит элементы slide, а не текст. Дальше вы увидите, как ограничить элемент slideshow
при помощи DTD. После этого метод characters не будет больше вызываться.
При отсутствии DTD анализатор должен предположить, что любой элемент, который он
видит, содержит текст, как первый элемент item в рассматриваемом примере:

<item>Why <em>WonderWidgets</em> are great</item>


Вот как примерно выглядит иерархическая структура:

ELEMENT: <item>
CHARS: Why
ELEMENT: <em>
CHARS: WonderWidgets

Web-

Rendered by www.RenderX.com
Стр. 124 из 626 Simple API for XML

END_ELM: </em>
CHARS: are great
END_ELM: </item>

7.3.14. Документы и данные


В данном примере видно, что символы смешаны с иерархической структурой элементов.
Тот факт, что текст может окружать элементы (если это не запрещено в DTD или в схеме)
помогает объяснить, почему вы иногда слышите разговоры о "XML-данных", а иногда - о
"XML-документах". XML прекрасно справляется и со структурированными данными и с
текстовыми документами, содержащими разметку. Единственным различием между ними
является то, разрешен или нет текст между элементами.
Примечание: В следующем разделе руководства вы будете работать с методом ignorable-
Whitespace интерфейса ContentHandler. Этот метод может быть вызван только при наличии
DTD. Если в DTD указано, что slideshow не содержит текст, то все пробелы, окружающие
элементы slide, игнорируются по определению. С другой стороны, если slideshow может
содержать текст (что должно предполагаться истинным при отсутствии DTD), то анализатор
должен предположить, что видимые им пробелы и линии между элементами slide являются
важными частями документа.

7.4. Добавление дополнительных обработчиков событий


Кроме метода ignorableWhitespace существуют еще два метода ContentHandler, которые
могут найти применение даже в простых приложениях: setDocumentLocator и processingIn-
struction. В этом разделе руководства вы реализуете эти два обработчика событий.

7.4.1. Указание месторасположения документа


Локатор - это объект, содержащий информацию, необходимую для поиска документа.
Класс Locator инкапсулирует либо системный ID (URL), либо общедоступный идентификатор
(URN), либо оба сразу. Вам может понадобиться эта информация при поиске чего-нибудь
относительно текущего документа (точно так же, как HTML-броузер обрабатывает атрибут
href= "anotherFile" в теге anchor - броузер использует месторасположение текущего
документа для поиска anotherFile).
Также вы могли бы использовать локатор для распечатки хороших диагностических
сообщений. Кроме месторасположения документа и общедоступного идентификатора
локатор содержит методы, выдающие номер строки и столбца самого последнего
обработанного события. Метод setDocumentLocator вызывается только один раз в начале
анализа. Для получения номеров текущей строки и столбца вы должны сохранить локатор
при вызове setDocumentLocator и затем использовать его в других методах обработки
событий.
Примечание: Обсуждаемый в этом разделе код находится в файле Echo04.java. Выходная
информация - в Echo04-01.txt. (Версия для броузера - Echo04-01.html).
Начните с удаления дополнительного кода вывода символов, добавленного вами в
последнем примере:

public void characters(char buf[], int offset, int len)

Web-

Rendered by www.RenderX.com
Добавление дополнительных обработчиков событий Стр. 125 из 626

throws SAXException
{
if (textBuffer != null) {
echoText();
textBuffer = null;
}
String s = new String(buf, offset, len);
...
}
Затем добавьте выделенный ниже метод в программу Echo для получения локатора
документа и используйте его для вывода системного ID документа:

...
private String indentString = " "; //

private int indentLevel = 0;

public void setDocumentLocator(Locator l)


{
try {
out.write("LOCATOR");
out.write("SYS ID: " + l.getSystemId() );
out.flush();
} catch (IOException e) {
//
}
}

public void startDocument()


...
Примечания:
• Этот метод, в отличие от любого другого метода ContentHandler, не возвращает
SAXException. Так что вместо использования emit для вывода, этот код производит
запись непосредственно в System.out. (Этот метод обычно используется просто для
сохранения Locator для дальнейшего использования, а не для какой-либо обработки,
генерирующей исключительную ситуацию, как в данном случае.)
• Эти методы записываются через "Id", не "ID", то есть, getSystemId и getPublicId.
После компиляции и выполнения программы с файлом slideSample01.xml на экране среди
прочей информации отобразится:

Web-

Rendered by www.RenderX.com
Стр. 126 из 626 Simple API for XML

LOCATOR
SYS ID: file:<path>/../samples/slideSample01.xml

START DOCUMENT
<?xml version='1.0' encoding='UTF-8'?>
...
Отсюда видно, что setDocumentLocator вызывается перед startDocument. Это может иметь
значение в случаях, когда вы проводите какую-либо инициализацию в коде обработки
событий.

7.4.2. Управление командами обработки


Иногда имеет смысл закодировать команды обработки, зависящие от приложения, в XML-
данных. В этом упражнении вы добавите команду обработки в ваш файл slideSample.xml
и затем измените программу Echo для ее отображения.
Примечание: Код, обсуждаемый в этом разделе находится в файле Echo05.java. Данные
- в файле slideSample02.xml. Выходная информация - в файле Echo05-02.txt. (Версии для
броузера - slideSample02-xml.html и Echo05-02.html.)
Как вы увидели в разделе "Освоение XML", формат команд обработки: <?target data?>,
где "target" является приложением, которое должно выполнить обработку, а "data" - это
команда обработки или информация для нее. Добавьте выделенный ниже текст, чтобы
добавить команду обработки для мифической программы презентации слайдов,
запрашивающую пользователя, какие слайды показывать (для технического персонала,
для дирекции, или все):

<slideshow
...
>

<!-- PROCESSING INSTRUCTION -->


<?my.presentation.Program QUERY="exec, tech, all"?>

<!-- TITLE SLIDE -->


Примечания:
• Часть "data" команды обработки может содержать пробелы, или даже может быть
пустой. Но не должно быть каких-либо пробелов между начальным <? и
идентификатором target.
• Данные начинаются после первого пробела.
• Имеет смысл полное задание программы назначения с полным уникальным для Web
префиксом пакета, чтобы предотвратить какие-либо конфликты с другими программами,
которые могут обрабатывать те же самые данные.

Web-

Rendered by www.RenderX.com
Добавление дополнительных обработчиков событий Стр. 127 из 626

• Для улучшения читаемости хорошей идеей является помещение двоеточия (:) после
имени приложения, например:

<?my.presentation.Program: QUERY="..."?>
Двоеточие подчеркивает, что имя назначения является "меткой", указывающей
получателя команды. Однако, хотя спецификация w3c разрешает использование
двоеточия в имени назначения, некоторые версии IE5 считают это ошибкой. В данном
руководстве мы будем стараться не использовать двоеточия в имени назначения.
Теперь, когда вы имеете команду обработки, с которой можно работать, добавьте
выделенный ниже код в приложение Echo:

public void characters(char buf[], int offset, int len)


...
}

public void processingInstruction(String target, String data)


throws SAXException
{
nl();
emit("PROCESS: ");
emit("<?"+target+" "+data+"?>");
}

private void echoText()


...
Откомпилируйте и выполните программу. Интересующая нас часть выводимой информации
должна выглядеть примерно так:

ELEMENT: <slideshow
...
>
PROCESS: <?my.presentation.Program QUERY="exec, tech, all"?>
CHARS:
...

7.4.3. Резюме
За исключением метода ignorableWhitespace вы использовали большинство методов
ContentHandler, которые были необходимы для управления наиболее полезными SAX-
событиями. Вы изучите использование ignorableWhitespace немного позднее. Далее вы
получите более глубокие знания в обработке ошибок, возникающих в процессе SAX-анализа.

Web-

Rendered by www.RenderX.com
Стр. 128 из 626 Simple API for XML

7.5. Обработка ошибок в неверифицирующем анализаторе


Эта версия программы Echo использует неверифицирующий анализатор. Поэтому нельзя
определить, содержит ли XML-документ правильные теги, или в правильной ли
последовательности они расположены. Другими словами, нельзя определить, правильно
ли составлен документ. Однако можно определить, является ли документ формально-
правильным.
В этом разделе руководства вы измените файл программы презентации слайдов для
генерации различных типов ошибок и увидите, как анализатор обрабатывает их. Вы узнаете,
также, при каких условиях ошибки игнорируются и как их обработать в этих случаях.

7.5.1. Введение в ошибки


Анализатор может генерировать три типа ошибок: фатальные ошибки, ошибки и
предупреждения. В этом упражнении вы сделаете простое изменение в XML-файл для
генерации фатальной ошибки. Затем вы увидите, как она обрабатывается в приложении
Echo.
Примечание: XML-структура для этого упражнения находится в файле slideSampleBad1.xml.
Выводимая информация - в файле Echo05-Bad1.txt. (Версии для броузера - slideSampleBad1-
xml.html и Echo05-Bad1.html.)
Одним из простых способов получения фатальной ошибки является удаление
завершающего символа "/" из пустого элемента item для создания тега, не имеющего
соответствующего завершающего тега. Это создает фатальную ошибку, поскольку все
XML-документы должны быть формально-правильными. Выполните следующие действия:
1. Скопируйте slideSample.xml в badSample.xml.
2. Отредактируйте badSample.xml и удалите символ, как показано ниже:

...
<!-- OVERVIEW -->
<slide type="all">
<title>Overview</title>
<item>Why <em>WonderWidgets</em> are great</item>
<item/>
<item>Who <em>buys</em> WonderWidgets</item>
</slide>
...
в результате получится:

...
<item>Why <em>WonderWidgets</em> are great</item>
<item>
<item>Who <em>buys</em> WonderWidgets</item>
...

Web-

Rendered by www.RenderX.com
Обработка ошибок в неверифицирующем анализаторе Стр. 129 из 626

3. Выполните программу Echo с новым файлом.


На экране должно отобразиться сообщение об ошибке, которое выглядит примерно так
(отформатировано для лучшей читаемости):

org.xml.sax.SAXParseException:
The element type "item" must be terminated by the
matching end-tag "</item>".
...
at org.apache.xerces.parsers.AbstractSAXParser...
...
at Echo.main(...)
Примечание: Приведенное выше сообщение сгенерировано библиотеками JAXP 1.2. Если
вы используете другой анализатор, сообщение об ошибке, возможно, будет другим.
При возникновении фатальной ошибки анализатор не сможет продолжать работу. Поэтому,
если приложение не генерирует исключительную ситуацию (вы вскоре увидите, как это
сделать), то ее сгенерирует обработчик ошибок событий по умолчанию. Стек трассировки
генерируется обработчиком исключительных ситуаций Throwable в вашем методе main:

...
} catch (Throwable t) {
t.printStackTrace();
}
Этот стек трассировки не очень полезен. Далее вы увидите, как генерировать более
хорошую диагностику при возникновении ошибок.

7.5.2. Обработка SAXParseException


Когда анализатор встречает ошибку, он генерирует SAXParseException - подкласс
SAXException, которая указывает файл и место, где произошла ошибка.
Примечание: Код этого упражнения находится в файле Echo06.java. Выводимая информация
- в файле Echo06-Bad1.txt. (Версия для броузера - Echo06-Bad1.html.)
Добавьте выделенный ниже код для генерации более подробного диагностического
сообщения при возникновении исключительной ситуации:

...
} catch (SAXParseException spe) {
// ,
System.out.println("\n** Parsing error"
+ ", line " + spe.getLineNumber()
+ ", uri " + spe.getSystemId());
System.out.println(" " + spe.getMessage() );

Web-

Rendered by www.RenderX.com
Стр. 130 из 626 Simple API for XML

} catch (Throwable t) {
t.printStackTrace();
}
Сейчас при выполнении программы выдается немного более полезная информация об
ошибке:

** Parsing error, line 22, uri file:<path>/slideSampleBad1.xml


The element type "item" must be ...
Примечание: Текст сообщения об ошибке зависит от используемого анализатора.
Приведенное выше сообщение генерируется JAXP 1.2.
Примечание: Перехват всех исключительных ситуаций Throwable обычно не является
хорошей идеей при разработке приложений. Мы делаем это сейчас для того, чтобы
постепенно построить полную систему обработки ошибок. Кроме того, при таком решении
перехватываются все исключительные ситуации нулевого указателя, которые могут быть
сгенерированы при передаче анализатору значения null.

7.5.3. Обработка SAXException


Иногда анализатор может сгенерировать более общую SAXException, но чаще встречаются
случаи, когда ошибка в приложении возникает в одном из методов обработки событий.
Например, сигнатура метода stsrtDocument в интерфейсе ContentHandler определяется с
указанием возврата SAXException:

public void startDocument() throws SAXException


Все методы ContentHandler (за исключением setDocumentLocator) содержат это объявление
в сигнатуре.
SAXException может быть составлена с использованием сообщения, другой исключительной
ситуации или и того, и другого. Так, например, когда Echo.satrtDocument выводит строку в
методе emit, любая возможная исключительная ситуация ввода/вывода заключается в
SAXException и передается назад анализатору:

private void emit(String s)


throws SAXException
{
try {
out.write(s);
out.flush();
} catch (IOException e) {
throw new SAXException("I/O error", e);
}
}

Web-

Rendered by www.RenderX.com
Обработка ошибок в неверифицирующем анализаторе Стр. 131 из 626

Примечание: Если вы при вызове setDocumentLocator сохранили объект Locator, вы можете


использовать его для генерации SAXParseException, указывая документ и место, вместо
генерации SAXException.
Когда анализатор передает исключительную ситуацию назад в вызвавший его код, имеет
смысл использовать оригинальную исключительную ситуацию для генерации стека
трассировки. Для этого добавьте выделенный ниже код:

...
} catch (SAXParseException err) {
System.out.println("\n** Parsing error"
+ ", line " + err.getLineNumber()
+ ", uri " + err.getSystemId());
System.out.println(" " + err.getMessage());

} catch (SAXException sxe) {


// ,
// ( )
Exception x = sxe;
if (sxe.getException() != null)
x = sxe.getException();
x.printStackTrace();

} catch (Throwable t) {
t.printStackTrace();
}
Этот код проверяет, содержит ли SAXException в себе другую исключительную ситуацию.
Если да, то генерирует стек трассировки, начиная с места, где произошла исключительная
ситуация, для точного определения кода, вызвавшего ошибку. Если исключительная
ситуация содержит только сообщение, код распечатывает стек трассировки начиная с
места, где была сгенерирована исключительная ситуация.

7.5.4. Усовершенствование обработчика SAXParseException


Поскольку SAXParseException может содержать в себе другую исключительную ситуацию,
добавьте выделенный ниже код, чтобы использовать вложенную исключительную ситуацию
для стека трассировки:

...
} catch (SAXParseException err) {
System.out.println("\n** Parsing error"
+ ", line " + err.getLineNumber()
+ ", uri " + err.getSystemId());
System.out.println(" " + err.getMessage());

Web-

Rendered by www.RenderX.com
Стр. 132 из 626 Simple API for XML

//
// ,
Exception x = spe;
if (spe.getException() != null)
x = spe.getException();
x.printStackTrace();

} catch (SAXException sxe) {


// ,

// ( )
Exception x = sxe;
if (sxe.getException() != null)
x = sxe.getException();
x.printStackTrace();

} catch (Throwable t) {
t.printStackTrace();
}
Теперь программа готова к обработке любой возможной исключительной ситуации при
SAX-анализе. Вы увидели, что для фатальных ошибок анализатор генерирует
исключительные ситуации. Но для не фатальных ошибок и предупреждений обработчик
ошибок по умолчанию никогда не генерирует исключительных ситуаций, и никакие
сообщения не выводятся. Через минуту вы изучите более подробно ошибки и
предупреждения, а также узнаете, как создавать обработчик ошибок для их обработки.

7.5.5. Обработка ParserConfigurationException


И, наконец, вспомните, что класс SAXParserFactory может генерировать исключительную
ситуацию при невозможности создания анализатора. Такая ошибка может произойти, если
генератор не может найти класс, необходимый для создания анализатора (ошибка "класс
не найден"), не имеет разрешения для доступа к нему (исключительная ситуация
"несанкционированный доступ"), или не может создать экземпляр анализатора (ошибка
создания экземпляра).
Добавьте выделенный ниже код для обработки таких ошибок:

} catch (SAXException sxe) {


Exception x = sxe;
if (sxe.getException() != null)
x = sxe.getException();
x.printStackTrace();

Web-

Rendered by www.RenderX.com
Обработка ошибок в неверифицирующем анализаторе Стр. 133 из 626

} catch (ParserConfigurationException pce) {


//

pce.printStackTrace();

} catch (Throwable t) {
t.printStackTrace();
Конечно, существует довольно много обработчиков ошибок. Но, по крайней мере, вы знаете
типы исключительных ситуаций, которые могут произойти.
Примечание: Если класс генератора, указанный в системном свойстве, не может быть
найден или создан, может сгенерироваться также javax.xml.parsers.FactoryConfigurationError.
Это неперехватываемая ошибка, поскольку программа не может продолжать выполнение
после нее.

7.5.6. Обработка IOException


Наконец, добавим обработчик для IOExceptions:

} catch (ParserConfigurationException pce) {


//

pce.printStackTrace();

} catch (IOException ioe) {


// I/O
ioe.printStackTrace();
}

} catch (Throwable t) {
...
Мы выйдем из обработчика для Throwables для перехвата ошибок нулевых указателей,
но обратите внимание, что в этом месте он делает то же самое, что и обработчик IOExcep-
tion. Здесь мы просто демонстрируем типы исключительных ситуаций, которые могут
произойти, в случае если после них ваше приложение способно продолжать выполнение.

7.5.7. Обработка нефатальных ошибок


Нефатальная ошибка возникает тогда, когда в XML-документе не выполняются ограничения
на правильность. Если анализатор обнаруживает, что документ является неправильным,
генерируется событие ошибки. Такие ошибки генерируются верифицирующим анализатором
при наличии DTD либо схемы, когда документ содержит неверный тег, либо тег обнаружен
в месте, где он не должен быть, либо (в случае схемы) если элемент содержит
неправильные данные.

Web-

Rendered by www.RenderX.com
Стр. 134 из 626 Simple API for XML

С вопросами верификации вы столкнетесь дальше в этом руководстве. Но, поскольку мы


обсуждаем обработку ошибок, вы напишете код обработки ошибок сейчас.
Наиболее важной чертой нефатальных ошибок, которую нужно понимать, является то, что
они по умолчанию игнорируются.
Но если в документе происходит ошибка верификации, вы, возможно, не захотите
продолжать обрабатывать его. Возможно, вы захотите считать такие ошибки фатальными.
В следующем коде вы настроите обработчик ошибок именно на такую работу.
Примечание: Код программы этого упражнения находится в файле Echo07.java.
Для контроля обработки ошибок вы переопределяете методы DefaultHandler, которые
обрабатывают фатальные ошибкит, нефатальные ошибки и предупреждения, как часть
интерфейса ErrorHandler. SAX-анализатор передает SAXParseException каждому из этих
методов, так что сгенерировать исключительную ситуацию при возникновении ошибки так
же легко, как и передать ее обратно.
Добавьте выделенный ниже код для переопределения обработчика ошибок:

public void processingInstruction(String target, String data)


throws SAXException
{
...
}

//
public void error(SAXParseException e)
throws SAXParseException
{
throw e;
}
Примечание: Полезно проанализировать методы обработки ошибок, определенные в
org.xml.sax.helpers.DefaultHandler. Вы увидите, что методы error() и warning() не делают
ничего, а fatalError() генерирует исключительную ситуацию. Кончено, вы всегда можете
переопределить метод fatalError() для генерации другой исключительной ситуации. Но
если ваш код не генерирует исключительную ситуацию при возникновении фатальной
ошибки, это сделает SAX-анализатор - так требует спецификация XML.

7.5.8. Обработка предупреждений


Предупреждения тоже по умолчанию игнорируются. Предупреждения содержат информацию
и требуют наличия DTD. Например, если элемент в DTD определен дважды, генерируется
предупреждение - это не является не правильным и не вызывает проблем, но возможно,
это может привести не к тем результатам, которых вы ожидали.
Добавьте выделенный ниже код для генерации сообщения при возникновении
предупреждения:

Web-

Rendered by www.RenderX.com
Замена и вставка текста Стр. 135 из 626

//
public void error(SAXParseException e)
throws SAXParseException
{
throw e;
}

//
public void warning(SAXParseException err)
throws SAXParseException
{
System.out.println("** Warning"
+ ", line " + err.getLineNumber()
+ ", uri " + err.getSystemId());
System.out.println(" " + err.getMessage());
}
Поскольку не существует нормального способа генерации сообщения без DTD или схемы,
вы пока еще ничего не сможете увидеть. Но как только это произойдет, вы уже будете
готовы!

7.6. Замена и вставка текста


На следующем шаге мы немного переделаем анализатор, так чтобы вы могли получить
информацию, которую он обычно игнорирует. Но перед этим вам необходимо изучить
несколько важных концепций XML. В этом разделе вы ознакомитесь с:
• Обработкой специальных символов ("<", "&" и т.д.)
• Обработкой текста с синтаксисом XML

7.6.1. Обработка специальных символов


В XML сущностью называется XML-структура (или простой текст), имеющая имя. Обращение
к сущности по имени приводит к вставке ее в то место документа, откуда производится
обращение. Для создания ссылки на сущность ее имя заключается между символами
амперсанда и точки с запятой, например:

&entityName;
Позже, при изучении DTD, вы узнаете, что можно определять свои собственные сущности,
так чтобы &yourEntityName; расширялся полностью в текст, определенный вами для этой
сущности. А сейчас мы рассмотрим предопределенные сущности и символьные ссылки,
не требующие какого-либо специального определения.

Web-

Rendered by www.RenderX.com
Стр. 136 из 626 Simple API for XML

7.6.1.1. Предопределенные сущности


Такая сущность, как, например, &amp; содержит имя (в данном случае "amp") между
начальным и конечным ограничителями. Текст, к которому ведет ссылка (&), замещает
имя подобно макросу в программах С или C++. В таблице 6-1 перечислены
предопределенные сущности для специальных символов.
Таблица 6-1 Предопределенные сущности
Символ Ссылка
& &amp;
< &lt;
> &gt;
" &quot;
' &apos;

7.6.1.2. Символьные ссылки


Символьная ссылка &#147; содержит символ "решетки" ("#"), после которого следует число.
Число представляет значение одного символа в кодировке Unicode, как, например, 65 для
символа "A", 147 для левых кавычек или 148 для правых кавычек. В данном случае "name"
сущности - знак решетки с числом, определяющим символ.
Примечание: XML считает, что значения записаны в десятичной системе. Однако, таблица
кодировки Unicode на сайте http://www.unicode.org/charts/ указывает значения в
шестнадцатиричном формате! Поэтому вам необходимо преобразовывать их для получения
правильного значения при вставке в XML-данные.

7.6.2. Использование ссылки на сущность в XML-документе


Предположим, что вы хотите вставить следующую строку в ваш XML-документ:

Market Size < predicted


Проблема при вставке этой строки прямо в XML-файл заключается в том, что когда
анализатор встречает знак левой угловой скобки (<), он начинает искать имя тега, что
нарушает анализ. Для решения этой проблемы вы помещаете в файл &lt; вместо "<".
Примечание: Результаты приведенной ниже модификации находятся в файле slideSam-
ple03.xml. Результаты обработки показаны в файле Echo07-03.txt. (Версии для броузера
- slideSample03-xml.html и Echo07-03.html.)
Если вы следуете указаниям руководства, добавьте выделенный ниже текст в ваш файл
slideSample.xml:

<!-- OVERVIEW -->


<slide type="all">
<title>Overview</title>
...
</slide>

Web-

Rendered by www.RenderX.com
Замена и вставка текста Стр. 137 из 626

<slide type="exec">
<title>Financial Forecast</title>
<item>Market Size &lt; predicted</item>
<item>Anticipated Penetration</item>
<item>Expected Revenues</item>
<item>Profit Margin </item>
</slide>

</slideshow>
После выполнения программы Echo с вашим XML-файлом вы увидите следующую
информацию на экране:

ELEMENT: <item>
CHARS: Market Size < predicted
END_ELM: </item>
Анализатор преобразовал ссылку на сущность в ее значение и передал в приложение.

7.6.3. Обработка текста с синтаксисом XML


При обработке больших частей XML или HTML, включающих большое количество
специальных символов, очень неудобно заменять каждый из них соответствующей ссылкой
на сущность. В этих ситуациях можно использовать секцию CDATA.
Примечание: Результаты приведенных ниже изменений находятся в файле slideSam-
ple04.xml. Результаты обработки - в файле Echo07-04.txt. (Версии для броузера -
slideSample04-xml.html и Echo07-04.html.)
Секция CDATA работает подобно <pre>…</pre> в HTML, только немного иначе - пробелы
в CDATA являются значимыми и символы внутри секции не интерпретируются как XML.
Секция CDATA начинается символами и завершается . Добавьте выделенный ниже текст
в ваш файл slideSample.xml, чтобы определить секцию CDATA для воображаемого
технического слайда:

...
<slide type="tech">
<title>How it Works</title>
<item>First we fozzle the frobmorten</item>
<item>Then we framboze the staten</item>
<item>Finally, we frenzle the fuznaten</item>
<item><![CDATA[Diagram:
frobmorten <--------------- fuznaten
| <3> ^
| <1> | <1> = fozzle

Web-

Rendered by www.RenderX.com
Стр. 138 из 626 Simple API for XML

V | <2> = framboze
Staten--------------------+ <3> = frenzle
<2>
]]></item>
</slide>
</slideshow>
После выполнения программы Echo с новым файлом вы увидите следующую информацию
на экране:

ELEMENT: <item>
CHARS: Diagram:

frobmorten <--------------fuznaten
| <3> ^
| <1> | <1> = fozzle
V | <2> = framboze
staten----------------------+ <3> = frenzle
<2>

END_ELM: </item>
Вы можете заметить, что текст в секции CDATA пришел в том же виде, в каком он был
написан. Поскольку анализатор не рассматривал угловые скобки как символы XML, не
были сгенерированы фатальные ошибки, что произошло бы в противном случае. (Если
бы угловых скобок не было бы в секции CDATA, документ не был бы формально-
правильным.)

7.6.4. Обработка CDATA и других символов


Существование секции CDATA несколько усложняет правильный вывод XML в программе
дублирования. Если выводимый текст не находится в секции CDATA, то любые угловые
скобки, амперсанды и другие специальные символы в тексте должны быть заменены
соответствующей ссылкой на сущность. (Замены левой угловой скобки и амперсанда
являются наиболее важными, другие символы будут интерпретироваться правильно без
введения анализатора в заблуждение.)
Но если выводимый текст находится в секции CDATA, то для вывода текста, подобного
приведенному выше, подстановок быть не должно. В простой программе, как в приложении
Echo, это не имеет большого значения. Но многие приложения XML-фильтров для
правильной трактовки специальных символов должны будут отслеживать, находится ли
текст в секции CDATA.
Необходимо также обратить внимание на атрибуты. Текст атрибута тоже может содержать
угловые скобки и точки с запятыми, которые необходимо заменять ссылками на сущности.
(Текст атрибута никогда не может быть в секции CDATA, поэтому не возникает никаких
вопросов при такой подстановке.)

Web-

Rendered by www.RenderX.com
Создание определения типа документа (DTD) Стр. 139 из 626

Далее в этом руководстве вы увидите, как используется LexicalHandler для определения


того, обрабатываете ли вы секцию CDATA. Затем вы узнаете, как определить DTD.

7.7. Создание определения типа документа (DTD)


После XML-объявления пролог документа может включать DTD, позволяющий указать
типы тегов, которые может содержать ваш XML-документ. Кроме указания
верифицирующему анализатору типов правильных тегов и порядка их следования DTD
может указать как верифицирующему, так и неверифицирующему анализаторам место,
где может находиться текст, что дает возможность анализатору определить, являются ли
встреченные им пробелы значимыми, или их можно проигнорировать.

7.7.1. Основные DTD-определения


При анализе программы презентации слайдов, например, вы видели, что метод characters
вызывается несколько раз перед и после комментариев и элементов slide. В том случае
пробелы состояли из концов строки и табуляции, окружающих разметку. Целью такой
разметки было повышение читаемости документа - пробелы никоим образом не были
частью содержимого документа. Перед началом изучения DTD-определений укажем
анализатору места, где пробелы нужно проигнорировать.
Примечание: Определенный в этом разделе DTD содержится в файле slideshow1a.dtd.
(Версия для броузера - slideshow1a-dtd.html.)
Сначала создадим файл с именем slideshow.dtd. Введите XML-объявление и комментарий
для идентификации файла, как показано ниже:

<?xml version='1.0' encoding='utf-8'?>

<!--
DTD for a simple "slide show".
-->
Затем добавьте выделенный ниже текст для указания того, что элемент slideshow содержит
элементы slide и ничего более:

<!-- DTD for a simple "slide show". -->

<!ELEMENT slideshow (slide+)>


Как видите, DTD-тег начинается с символов <!, после которых следует имя тега (ELEMENT).
После имени тега идет имя определяемого элемента (slideshow) и, в круглых скобках, один
или более элементов, определяющих правильное содержимое для этого элемента. В
данном случае запись указывает, что slideshow содержит один или более элементов slide.
Без знака плюс определение указывало бы, что slideshow состоит из одного элемента slide.
В таблице 6-2 перечислены квалификаторы, которые можно добавить в определение
элемента.
Таблица 6-2 Квалификаторы DTD-элемента

Web-

Rendered by www.RenderX.com
Стр. 140 из 626 Simple API for XML

Квалификатор Название Значение


? Знак вопроса Необязателен (ноль или один)
* Звездочка Ноль или более
+ Знак плюс Один или более

Вы можете заключить несколько элементов в круглые скобки, разделив их запятой, и


использовать квалификатор с каждым элементом, чтобы указать, сколько экземпляров
соответствующего элемента может встретиться. Разделенный запятыми список определяет,
какие элементы допустимы и в какой последовательности они должны быть расположены.
Вы можете также вкладывать круглые скобки для группировки нескольких элементов.
Например, после определения элемента image, вы можете объявить, что каждый элемент
image должен быть сгруппирован с элементом title, указав ((image, title)+). Здесь знак плюс
относится к паре image/title и указывает, что может встретиться одна или более таких пар.

7.7.2. Определение текста и вложенных элементов


Теперь, когда вы указали анализатору, где не должно быть текста, укажем, где текст может
быть. Добавьте выделенный ниже текст для определения элементов slide, title, item и list:

<!ELEMENT slideshow (slide+)>


<!ELEMENT slide (title, item*)>
<!ELEMENT title (#PCDATA)>
<!ELEMENT item (#PCDATA | item)* >
В первой добавленной строке указывается, что slide содержит элемент title, после которого
идет ноль или более элементов item. Ничего нового. Следующая строка указывает, что
title полностью состоит из анализируемых символьных данных (PCDATA). Они называются
текстом в большинстве случаев, но в XML они называются "анализируемые символьные
данные". (Это отличает их от секций CDATA, которые содержат неанализируемые
символьные данные.) Знак "#" перед PCDATA указывает, что далее следует специальное
слово, а не имя элемента.
|PCDATAitemPCDATAitemмодели со смешанным содержимымitem#PCDATA|*

7.7.3. Ограничения DTD


Было бы неплохо, если бы мы могли указать, что item содержит либо только текст, либо
текст, за которым следует один или более элементов item. Но такой тип спецификации
тяжело определить в DTD. Например, вы можете соблазниться определить item так:

<!ELEMENT item (#PCDATA | (#PCDATA, item+)) >


Это было бы довольно точно, но как только анализатор увидит #PCDATA и вертикальную
линию, он будет ожидать конец определения для соответствия модели со смешанным
содержимым. Эта спецификация такой модели не соответствует, так что вы получите
ошибку: Illegal mixed content model for 'item'. Foun?#x28;… (Неверная модель со смешанным
содержимым для 'item'. Найден &#28;…), где шестнадцатиричный символ 28 является
угловой скобкой в конце определения.

Web-

Rendered by www.RenderX.com
Создание определения типа документа (DTD) Стр. 141 из 626

Попытка определить элемент item дважды тоже не работает. Спецификация, подобная


этой:

<!ELEMENT item (#PCDATA) >


<!ELEMENT item (#PCDATA, item+) >
выводит предупреждение "duplicate definition" ("двойное определение") при работе
верифицирующего анализатора. Фактически, второе определение игнорируется. По-
видимому, определение модели со смешанным содержимым (позволяющей элементам
item быть разбросанными по тексту) является хорошим настолько, насколько мы можем
его сделать.
Кроме ограничений модели со смешанным содержимым, упомянутых выше, не существует
способа дальнейшей квалификации типа текста, который может встретиться там, где было
указано PCDATA. Должно ли оно содержать только числа? Должно ли быть в формате
даты или денежном формате? Это невозможно указать в контексте DTD.
И, наконец, отметим, что DTD не предлагает никакой иерархии. Определение элемента
title одинаково относится и к заголовку элемента slide и к заголовку элемента item. При
расширении DTD для разрешения HTML-разметки в дополнение к обычному тексту могло
бы иметь смысл ограничение размера заголовка item по сравнению с заголовком slide,
например. Но единственным способом это сделать было бы назначение одному из них
другого имени, например, "item-title". Результатом отсутствия иерархии является то, что
DTD заставляет вас использовать "иерархию дефисов" (или ее эквивалент) в вашем
пространстве имен. Все эти ограничения являются основной движущей силой разработки
стандартов спецификации схем.

7.7.4. Значения специальных элементов в DTD


Вместо указания окруженного скобками списка элементов определение элемента может
использовать одно из двух специальных значений: ANY или EMPTY. Спецификация ANY
обозначает, что элемент может содержать любой другой определенный элемент, или
PCDATA. Эта спецификация обычно используется для корневого элемента XML-документа
общего назначения, такого как, например, создаваемого в текстовом редакторе. Текстовые
элементы могут встречаться в таком документе в любом порядке, поэтому имеет смысл
указывать ANY.
Спецификация EMPTY означает, что элемент не имеет содержимого. То есть DTD для
сообщений электронной почты, разрешающее вам "отмечать" сообщение при помощи тега
<flag/>, может иметь примерно такую строку:

<!ELEMENT flag EMPTY>

7.7.5. Ссылка на DTD


В данном случае определение DTD расположено в отдельном файле, а не в XML-документе.
Это значит, что вы должны поставить ссылку на DTD в таком документе, что сделает DTD-
файл частью внешнего подмножества полного DTD для XML-файла. Как вы увидите позже,
можно включить части DTD в документ. Такие определения составляют локальное
подмножество DTD.

Web-

Rendered by www.RenderX.com
Стр. 142 из 626 Simple API for XML

Примечание: XML-документ, написанный в этом разделе, содержится в файле slideSam-


ple05.xml. (Версия для броузера - slideSample05-xml.html.)
Для ссылки на только что созданный DTD добавьте выделенную ниже строку в ваш файл
slideSample.xml:

<!-- A SAMPLE set of slides -->

<!DOCTYPE slideshow SYSTEM "slideshow.dtd">

<slideshow
Опять DTD-тег начинается с "<!". В данном случае имя тега, DOCTYPE, указывает, что
документом является slideshow. Это означает, что документ состоит из элемента slideshow
и всего остального внутри его:

<slideshow>
...
</slideshow>
Этот тег определяет элемент slideshow в качестве корневого элемента документа. XML-
документ должен иметь только один корневой элемент. Именно здесь указывается этот
элемент. Другими словами, этот тег идентифицирует содержимое документа как slideshow.
Тег DOCTYPE располагается после XML-определения и перед корневым элементом.
Идентификатор SYSTEM указывает расположение DTD-файла. Поскольку он не начинается
с префикса типа http:/ или file:/, путь к расположению XML-документа является
относительным. Вспомнили метод setDocumentLocator? Анализатор использует эту
информацию для поиска DTD-файла, так же как делало бы ваше приложение для поиска
файла относительно XML-документа. Мог бы использоваться также идентификатор PUBLIC
для указания того, что DTD-файл использует уникальное имя, но анализатор должен был
быть способен разрешить это имя.
Спецификация DOCTYPE могла бы, также, содержать DTD-определения внутри XML-
документа, а не обращаться к внешнему DTD-файлу. Такие определения должны
заключаться в квадратные скобки, например:

<!DOCTYPE slideshow SYSTEM "slideshow1.dtd" [


...local subset definitions here...
]>
Вы будете использовать преимущества этой возможности позже, при определении
некоторых сущностей, которые могут использоваться в документе.

Web-

Rendered by www.RenderX.com
Влияние DTD на неверифицирующий анализатор Стр. 143 из 626

7.8. Влияние DTD на неверифицирующий анализатор


В последнем разделе вы определили элементарный тип документа и использовали его в
вашем XML-файле. В этом разделе вы будете использовать программу Echo для того,
чтобы увидеть, как данные представляются SAX-анализатору при использовании DTD.
Примечание: Выводимая информация содержится в файле Echo07-05.txt. (Версия для
броузера - в Echo07-05.html.)
Выполнение программы Echo с вашей последней версией slideSample.xml показывает, что
многие лишние вызовы метода characters исчезли.
Там где раньше вы видели:

...
>
PROCESS: ...
CHARS:
ELEMENT: <slide
ATTR: ...
>
ELEMENT: <title>
CHARS: Wake up to ...
END_ELM: </title>
END_ELM: </slide>
CHARS:
ELEMENT: <slide
ATTR: ...
>
...
Сейчас вы увидите:

...
>
PROCESS: ...
ELEMENT: <slide
ATTR: ...
>
ELEMENT: <title>
CHARS: Wake up to ...
END_ELM: </title>
END_ELM: </slide>
ELEMENT: <slide
ATTR: ...

Web-

Rendered by www.RenderX.com
Стр. 144 из 626 Simple API for XML

>
...
Очевидно, что символы пробела, которые раньше выводились вокруг элементов slide,
больше не передаются анализатором, поскольку DTD объявляет, что slideshow состоит
только из элементов slide:

<!ELEMENT slideshow (slide+)>

7.8.1. Отслеживание игнорируемых пробелов


Теперь из-за присутствия DTD анализатор больше не вызывает метод characters с
несущественными пробелами. С точки зрения приложения, которое занимается
исключительно обработкой XML-данных, это замечательно. Приложение никто не беспокоит
пробелами, которые существуют только для повышения читаемости XML-файла.
С другой стороны, если бы вы писали приложение, фильтрующее файл XML-данных, и
хотели бы выводить такую же читаемую версию файла, то эти пробелы уже не были бы
несущественными - они были бы важными. Для получения этих символов вы должны
добавить метод ignorableWhitespace в ваше приложение. Что мы сейчас и сделаем.
Примечание: Написанный в этом разделе код находится в файле Echo08.java. Выводимая
информация - в файле Echo08-05.txt. (Версия для броузера - в Echo08-05.html.)
Чтобы обработать обычно игнорируемые пробелы, которые встречаются анализатору,
добавьте выделенный ниже код для реализации обработчика события ignorableWhitespace
в вашу версию программы Echo:

public void characters (char buf[], int offset, int len)


...
}

public void ignorableWhitespace char buf[], int offset, int Len)


throws SAXException
{
nl();
emit("IGNORABLE");
}

public void processingInstruction(String target, String data)


...
Этот код просто генерирует сообщение, чтобы дать вам знать, что встретился игнорируемый
пробел.
Примечание: Опять же, не все анализаторы сделаны одинаково. Спецификация SAX не
требует, чтобы этот метод вызывался. Реализация Java XML делает это тогда, когда в
DTD указана такая возможность.

Web-

Rendered by www.RenderX.com
Влияние DTD на неверифицирующий анализатор Стр. 145 из 626

Если вы выполните приложение Echo сейчас, вы увидите примерно следующую


информацию:

ELEMENT: <slideshow
ATTR: ...
>
IGNORABLE
IGNORABLE
PROCESS: ...
IGNORABLE
IGNORABLE
ELEMENT: <slide
ATTR: ...
>
IGNORABLE
ELEMENT: <title>
CHARS: Wake up to ...
END_ELM: </title>
IGNORABLE
END_ELM: </slide>
IGNORABLE
IGNORABLE
ELEMENT: <slide
ATTR: ...
>
...
Отсюда очевидно, что ignorableWhitespace вызывается перед и после комментариев и
элементов slide тогда, когда вызывался метод characters при отсутствии DTD.

7.8.2. Очистка
Теперь, когда вы увидели отображение игнорируемых пробелов, удалите этот код из вашей
версии программы Echo - для следующих упражнений он вам больше не нужен.
Примечание: Это изменение сделано в файле Echo09.java.

7.8.3. Документы и данные


Раннее вы узнали, что одной из причин, по которой вы слышите то об XML-документах,
то об XML-данных, является то, что XML хорошо справляется с обоими, в зависимости от
того, разрешен или нет текст между элементами структуры.
В файле примера, с которым вы работаете, элемент slideshow является примером элемента
данных - он содержит только субэлементы без текста. Элемент item, с другой стороны,
может быть назван элементом документа, поскольку он может содержать и текст и
субэлементы.

Web-

Rendered by www.RenderX.com
Стр. 146 из 626 Simple API for XML

По мере работы с руководством вы увидите, как расширить определение элемента title


для включения HTML-разметки, что превратит его тоже в элемент документа.

7.8.4. Пустые элементы, пересмотренный вариант


Теперь, когда вы понимаете, как определенные экземпляры пробелов могут быть
проигнорированы, наступило время пересмотреть определение "пустого" элемента. Это
определение сейчас может быть расширено включением

<foo> </foo>
в котором существуют пробелы между тегами, а DTD определяет эти пробелы
игнорируемыми.

7.9. Определение атрибутов и сущностей в DTD


Определенный вами DTD пригоден для использования с неверифицирующим анализатором.
Он показывает, где ожидается текст, а где нет. Больше ни на что анализатор не обращает
внимания. Но для применения с верифицирующим анализатором DTD должен указать
правильные атрибуты для различных элементов. Вы сделаете это в данном разделе, после
чего определите одну внутреннюю сущность и одну внешнюю, к которым вы сможете
обратиться из XML-файла.

7.9.1. Определение атрибутов в DTD


Давайте определим атрибуты в программе презентации слайдов.
Примечание: Написанный в данном разделе XML содержится в файле slideshow1b.dtd.
(Версия для броузера - slideshow1b-dtd.html.)
Добавьте выделенный ниже текст, чтобы определить атрибуты для элемента slideshow:

<!ELEMENT slideshow (slide+)>


<!ATTLIST slideshow
title CDATA #REQUIRED
date CDATA #IMPLIED
author CDATA "unknown"
>
<!ELEMENT slide (title, item*)>
DTD-тег ATTLIST начинает последовательность определений атрибутов. Имя, следующее
после ATTLIST, указывает элемент, для которого определяются атрибуты. В данном случае
таким элементом является slideshow. (Обратите еще раз внимание на отсутствие иерархии
в спецификации DTD.)
Каждый атрибут определяется последовательностью из трех разделенных пробелами
значений. Запятые и другие разделители не разрешены, так что форматирование
определений, как показано выше, полезно для повышения читаемости. Первый элемент
в каждой строке - это имя атрибута: title, date или author в данном случае. Второй элемент
указывает тип данных: CDATA - это символьные данные, неанализируемые данные, в

Web-

Rendered by www.RenderX.com
Определение атрибутов и сущностей в DTD Стр. 147 из 626

которых левая угловая скобка (>) никогда не будет истолкована как часть XML-тега. В
таблице 6-3 перечислены правильные варианты для типов атрибутов.
Таблица 6-3 Типы атрибутов
Тип атрибута Указывает…
(value1 | value2 | … ) Список значений, разделенных вертикальными линиями.
(Пример находится ниже).
CDATA "Неанализируемые символьные данные". (Для нормальных
людей - текстовая строка.)
ID Имя, которое не имеет ни один другой атрибут ID.
IDREF Ссылка на ID, определенный где-то в документе.
IDREFS Разделенный пробелами список, содержащий один или более
ID-ссылок.
ENTITY Имя сущности, определенной в DTD.
ENTITIES Разделенный пробелами список сущностей.
NMTOKEN Правильное XML-имя, состоящее из букв, цифр, дефисов,
знаков подчеркивания и двоеточий.
NMTOKENS Разделенный пробелами список имен.
NOTATION Название DTD-определенной нотации, описывающей формат
не XML-данных, таких как файлы изображений.*

* Она является быстро отживающей спецификацией, которая будет рассмотрена подробно


в конце этого раздела.
Если тип атрибута состоит из заключенного в скобки списка вариантов, разделенных
вертикальными чертами, атрибут должен использовать одно из специфицированных
значений. Например, добавьте выделенный ниже текст в DTD:

<!ELEMENT slide (title, item*)>


<!ATTLIST slide
type (tech | exec | all) #IMPLIED
>
<!ELEMENT title (#PCDATA)>
<!ELEMENT item (#PCDATA | item)* >
Эта спецификация указывает, что атрибут type элемента slide должен быть записан как
tye="tech", type="exec" или type="all". Другие значения неприемлемы. (Использующие DTD
редакторы XML могут использовать такие спецификации для представления выпадающего
списка вариантов.)
В последней записи спецификации атрибута определяется значение атрибута по
умолчанию, если оно есть, и указывается, игнорируется либо нет атрибут. Таблица 6-4
показывает возможные варианты.
Таблица 6-4 Параметры спецификации атрибутов
Спецификация Указывает…
#REQUIRED Значение атрибута должно быть указано в документе.
#IMPLIED Значение не должно быть указано в документе. Если оно не
указано, приложение будет использовать значение по
умолчанию.

Web-

Rendered by www.RenderX.com
Стр. 148 из 626 Simple API for XML

Спецификация Указывает…
"defaultValue" Значение по умолчанию, если значение не указано в
документе.
#FIXED "fixedValue" Значение для использования. Если документ указывает любое
значение, оно должно быть таким же.

7.9.2. Определение сущностей в DTD


До настоящего времени мы рассматривали только предопределенные сущности, например
&amp;, и вы видели, что атрибут может ссылаться на сущность. Теперь наступило время
изучить, как определить свои собственные сущности.
Примечание: Определенный здесь XML находится в файле slideSample06.xml. Выводимая
информация - в файле Echo09-06.txt. (Версии для броузера - slideSample06-xml.html и
Echo09-06.html.)
Добавьте выделенный ниже текст в тег DOCTYPE вашего XML-файла.

<!DOCTYPE slideshow SYSTEM "slideshow.dtd" [


<!ENTITY product "WonderWidget">
<!ENTITY products "WonderWidgets">
]>
Имя тега ENTITY указывает, что вы определяете сущность. Далее идет имя сущности и
ее определение. В данном случае вы определяете сущность с именем "product", которая
будет замещать название продукта. В дальнейшем, когда название продукта изменится
(что определенно произойдет), вы должны будете лишь изменить название в одном месте,
и все ваши слайды будут отображать новое значение.
Последней частью является строка подстановки, которая замещает имя сущности везде,
где на нее встречается ссылка в XML-документе. Строка подстановки определяется в
кавычках, которые не включаются при вставке текста в документ.
Только лишь для удобства мы определили две версии: одну - для единственного числа,
другую - для множественного. Так что при использовании, например, в качестве названия
продукта "Wally" вы должны быть готовы ввести "Wallies" для множественного числа, и оно
будет подставлено корректно.
Примечание: По правде говоря, это тип сущности, который используется главным образом
во внешнем DTD. При этом все ваши документы могут обращаться к новому названию,
когда оно изменится. А это только пример…
Теперь, когда вы определили сущности, следующим шагом будет обращение к ним из
программы презентации слайдов. Выполните выделенные ниже изменения для этого:

<slideshow
title="WonderWidget&product; Slide Show"
...

<!-- TITLE SLIDE -->

Web-

Rendered by www.RenderX.com
Определение атрибутов и сущностей в DTD Стр. 149 из 626

<slide type="all">
<title>Wake up to WonderWidgets&products;!</title>
</slide>

<!-- OVERVIEW -->


<slide type="all">
<title>Overview</title>
<item>Why <em>WonderWidgets&products;</em> are
great</item>
<item/>
<item>Who <em>buys</em> WonderWidgets&products;</item>
</slide>
Обратите внимание, что на созданные вами сущности ссылаются при помощи такого же
синтаксиса (&entityName;), что и для предопределенных сущностей, и что на сущность
можно ссылаться в значении атрибута, так же как и в содержимом элемента.

7.9.3. Вывод ссылок на сущность


Если вы выполните программу Echo с новой версией файла, вот что вы увидите:

ELEMENT: <title>
CHARS: Wake up to WonderWidgets!
END_ELM: </title>
Обратите внимание, что название продукта было замещено ссылкой на сущность.

7.9.4. Дополнительные полезные сущности


Вот несколько других примеров определений сущностей, которые вы, возможно, найдете
полезными при написании XML-документа:

<!ENTITY ldquo "&#147;"> <!-- Left Double Quote -->


<!ENTITY rdquo "&#148;"> <!-- Right Double Quote -->
<!ENTITY trade "&#153;"> <!-- Trademark Symbol (TM) -->
<!ENTITY rtrade "&#174;"> <!-- Registered Trademark (R) -->
<!ENTITY copyr "&#169;"> <!-- Copyright Symbol -->

7.9.5. Ссылки на внешние сущности


Также можно использовать идентификаторы SYSTEM или PUBLIC для именования
сущности, определенной во внешнем файле. Сделаем это.
Примечание: Определенные здесь XML находятся в файлах slideSample07.xml и copy-
right.xml. Выводимая информация - в файле Echo09-07.txt. (Версии для броузера -
slideSample07-xml.html, copyright-xml.html и Echo09-07.html.)

Web-

Rendered by www.RenderX.com
Стр. 150 из 626 Simple API for XML

Добавьте выделенный ниже текст в предложение DOCTYPE вашего XML-файла для ссылки
на внешнюю сущность:

<!DOCTYPE slideshow SYSTEM "slideshow.dtd" [


<!ENTITY product "WonderWidget">
<!ENTITY products "WonderWidgets">
<!ENTITY copyright SYSTEM "copyright.xml">
]>
Это определение обращается к информации об авторских правах, содержащейся в файле
с именем copyright.xml. Создайте этот файл и запишите в него какой-то интересный текст,
возможно такой:

<!-- A SAMPLE copyright -->

This is the standard copyright message that our lawyers


make us put everywhere so we don'0t have to shell out a
million bucks every time someone spills hot coffee in their
lap...
И наконец, добавьте выделенный ниже текст в ваш файл slideSample.xml для ссылки на
внешнюю сущность:

<!-- TITLE SLIDE -->


...
</slide>

<!-- COPYRIGHT SLIDE -->


<slide type="all">
<item>&copyright;</item>
</slide>
Вы можете также объявить внешнюю сущность для получения доступа к сервлету, который
генерирует текущую дату, используя, например, такое определение:

<!ENTITY currentDate SYSTEM


"http://www.example.com/servlet/CurrentDate?fmt=dd-MMM-
yyyy">
Теперь вы можете обратиться к этой сущности, так же как и к любой другой:

Today's date is &currentDate;.

Web-

Rendered by www.RenderX.com
Обращение к двоичным сущностям Стр. 151 из 626

7.9.6. Отображение внешней сущности


После выполнения программы Echo с последней версией файла презентации слайдов вы
увидите следующую информацию:

...
END_ELM: </slide>
ELEMENT: <slide
ATTR: type "all"
>
ELEMENT: <item>
CHARS:
This is the standard copyright message that our lawyers
make us put everywhere so we don't have to shell out a
million bucks every time someone spills hot coffee in their
lap...
END_ELM: </item>
END_ELM: </slide>
...
Обратите внимание, что новая строка, за которой следует комментарий, отображается как
символ, но сам комментарий проигнорирован. По этой причине сообщение об авторских
правах начинается с новой строки после метки CHARS: вместо того, чтобы идти сразу
после метки - первый отображаемый символ является символом новой строки, после
которого следует комментарий.

7.9.7. Итоговая информация по сущностям


Внешняя или внутренняя сущность, на которую есть ссылка в содержимом документа,
называется сущностью общего назначения. Сущность, содержащая спецификацию DTD,
на которую есть обращение из DTD, называется сущностью-параметром. (Более подробно
об этом мы расскажем далее.)
Сущность, содержащая XML и являющаяся, таким образом, анализируемой, называется
анализируемой сущностью. Сущность, содержащая двоичные данные (например,
изображения), называется неанализируемой сущностью. (По своей природе она должна
быть внешней.) Мы рассмотрим ссылки на неанализируемые сущности в следующем
разделе руководства.

7.10. Обращение к двоичным сущностям


Этот раздел не содержит упражнений для программирования. В нем обсуждаются ссылки
на двоичные файлы, такие как изображения и файлы с мультимедийными данными.

7.10.1. Использование типа данных MIME


Существует два способа обратиться к неанализируемой сущности, например, к файлу
изображения. Первый использует механизм NOTATION спецификации DTD. Однако этот

Web-

Rendered by www.RenderX.com
Стр. 152 из 626 Simple API for XML

механизм является сложным, запутанным и существующим в основном для совместимости


с документами SGML. Мы будем иметь возможность рассмотреть его более детально при
обсуждении DTDHandler API, а сейчас достаточно сказать, что комбинация недавно
определенного стандарта пространства имен XML и типов данных MIME, определенных
для вложений электронных сообщений, обеспечивает намного более полезный, понятный
и расширяемый механизм для обращения к неанализируемым внешним сущностям.
Примечание: Описанный здесь XML находится в файле slideshow1b.xml. В действительности
мы не будем отображать никаких изображений. Это не входит в функции программы Echo.
В этом разделе просто рассмотрено, как такие ссылки можно сделать. Мы предполагаем,
что приложение, которое будет обрабатывать XML-данные, знает, как использовать эти
ссылки.
Добавьте выделенный ниже текст в ваш файл slideshow.dtd для настройки программы
slideshow на использование файлов изображений:

<!ELEMENT slide (image?, title, item*)>


<!ATTLIST slide
type (tech | exec | all) #IMPLIED
>
<!ELEMENT title (#PCDATA)>
<!ELEMENT item (#PCDATA | item)* >
<!ELEMENT image EMPTY>
<!ATTLIST image
alt CDATA #IMPLIED
src CDATA #REQUIRED
type CDATA "image/gif"
>
Эти изменения определяют image как необязательный элемент в slide, определяют его
как пустой элемент и определяют необходимые для него атрибуты. Тег image похож на
тег img HTML 4.0 с дополнением спецификатора типа изображения type. (Тег img определен
в спецификации HTML 4.0.)
Атрибуты тега image определяются в записи ATTLIST. Атрибут alt, определяющий
альтернативный текст для отображения при неудачном поиске изображения, принимает
символьные данные (CDATA). Он имеет "предполагаемое" значение, то есть является
необязательным, и программа, обрабатывающая данные, знает достаточно, чтобы
заместить его чем-то вроде "image not found" ("изображение не найдено"). С другой стороны,
атрибут src, который содержит имя изображения для вывода, является необходимым.
Атрибут type предназначен для спецификации типа данных MIME, как определено в
ftp://ftp.isi.edu/in-notes/iana/assignments/mediatypes/. Он имеет значение по умолчанию:
image/gif.
Примечание: Отсюда понятно, что символьные данные (CDATA), используемые для
атрибута type, должны быть одним из MIME-типов данных. Двумя наиболее общими
форматами являются: image/gif и image/jpeg. Основываясь на этом, хорошо было бы указать
список атрибутов в этом месте, используя что-либо наподобие: type("image/gif", "image/jpeg").

Web-

Rendered by www.RenderX.com
Выбор вашей реализации анализатора Стр. 153 из 626

Однако это работать не будет. Прямой слеш не является частью установленного набора
символов для меток имени, поэтому такое объявление является ошибочным. Кроме того,
создание списка атрибутов в DTD ограничило бы доступные MIME-типы до определенных
на сегодняшний день. Использование же CDATA оставляет вопрос открытым, то есть
объявление будет продолжать оставаться правильным и при определении дополнительных
типов.
В документе ссылка на изображение с именем "intro-pic" может выглядеть примерно так:

<image src="image/intro-pic.gif", alt="Intro Pic",


type="image/gif" />

7.10.2. Альтернатива: использование ссылок на сущность


Использование типов данных MIME в качестве атрибута элемента является гибким и
расширяемым механизмом. Для создания ссылки на внешнюю сущность ENTITY с
использованием механизма нотаций вам необходимы элементы DTD NOTATION для
данных jpeg и gif. Они, конечно, могут быть получены из какого-нибудь центрального
хранилища. Но в дальнейшем вам нужно было бы определить отдельный элемент ENTITY
для каждого изображения, к которому вы хотели бы обратиться! Другими словами,
добавление нового изображения к вашему документу всегда бы требовало определения
новой сущности в DTD и ссылки на нее в документе. Принимая во внимание повсеместность
спецификации HTML 4.0, более новый стандарт должен использовать типы данных MIME
и объявление подобное image, которое предполагает, что приложение знает, как
обрабатывать такие элементы.

7.11. Выбор вашей реализации анализатора


Если не указан какой-либо другой класс генератора, по умолчанию используется класс
SAXParserFactory. Для использования анализатора другого производителя вы должны
изменить значение переменной окружения, указывающей на него. Это можно сделать из
командной строки, например:

java -Djavax.xml.parsers.SAXParserFactory=yourFactoryHere ...


Имя генератора должно быть полностью определенным именем класса (имеются все
префиксы пакета). Дополнительная информация находится в документации по методу
newInstance() класса SAXParserFactory.

7.12. Использование верифицирующего анализатора


До настоящего времени вы провели много экспериментов с неверифицирующим
анализатором. Теперь наступило время познакомиться с верифицирующим анализатором
и с его работой во время анализа нашего примера приложения.
Прежде всего необходимо понимать две особенности верифицирующего анализатора:
• Схема или DTD обязательны.

Web-

Rendered by www.RenderX.com
Стр. 154 из 626 Simple API for XML

• Поскольку присутствует схема или DTD, метод ignorableWhitespace вызывается везде,


где возможно.

7.12.1. Конфигурирование генератора


Сначала модифицируем программу Echo для использования верифицирующего анализатора
вместо неверифицирующего.
Примечание: Код этого раздела находится в файле Echo10.java.
Сделайте выделенные ниже изменения:

public static void main(String argv[])


{
if (argv.length != 1) {
...
}
//
( )
//
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setValidating(true);
try {
...
Здесь вы сконфигурировали генератор для создания верифицирующего анализатора при
вызове newSAXParser. Можно также сконфигурировать его для возврата анализатора,
использующего пространство имен, при помощи метода setNamespaceAware(true). Данная
реализация поддерживает любую комбинацию конфигурационных параметров. (Если
комбинация не поддерживается какой-либо конкретной реализацией, должна возникнуть
ошибка конфигурации генератора.)

7.12.2. Верификация с использованием XML-схемы


Хотя полное описание XML-схемы выходит за рамки данного руководства, в этом разделе
будут рассмотрены действия, необходимые для верификации XML-документа с
использованием существующей схемы, написанной на языке XML Schema. (Вы, также,
можете исследовать примеры программ, которые является частью загрузочного пакета
JAXP. Они используют простое определение XML-схемы для верификации персональных
данных, записанных в XML-файле.)
Примечание: Существует несколько языков определения схем, включая RELAX NG,
Schematron и стандарт W3C "XML Schema". (Даже DTD называется "схемой", хотя эта
спецификация является единственной, которая не использует синтаксис XML для описания
ограничений схемы.) Однако "XML Schema" представляет терминологическую проблему.
Хотя фраза "Схема XML Schema" была бы точной, мы будем использовать фразу
"определение XML Schema" для устранения избыточности.

Web-

Rendered by www.RenderX.com
Использование верифицирующего анализатора Стр. 155 из 626

Для того чтобы быть информированным об ошибках верификации в XML-документе,


генератор анализатора должен быть сконфигурирован на создание верифицирующего
анализатора, как показано в предыдущем разделе. Кроме того:
1. Соответствующие свойства должны быть установлены в SAX-анализаторе.
2. Должен быть установлен соответствующий обработчик ошибок.
3. Документ должен быть связан со схемой.

7.12.2.1. Установка свойств SAX-анализатора


Начнем с определения констант, которые вы будете использовать при установке свойств:

static final String JAXP_SCHEMA_LANGUAGE =


"http://java.sun.com/xml/jaxp/properties/schemaLanguage";

static final String W3C_XML_SCHEMA =


"http://www.w3.org/2001/XMLSchema";
Затем вы должны сконфигурировать генератор анализатора на создание анализатора,
использующего пространство имен, а также верификацию:

...
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setNamespaceAware(true);
factory.setValidating(true);
Более подробно о пространстве имен вы узнаете в разделе "Использование пространства
имен". Сейчас вы должны понимать, что верификация схемы - это ориентированный на
пространство имен процесс. Поскольку JAXP-совместимые анализаторы по умолчанию
не используют пространство имен, необходимо установить это свойство для работы
верификации схемы.
Последним действием является настройка анализатора на использование определенного
языка схемы. Для этого используются константы, определенные ранее для установки языка
W3C XML Schema:

saxParser.setProperty(JAXP_SCHEMA_LANGUAGE, W3C_XML_SCHEMA);
Во время работы необходимо, также, обрабатывать дополнительную ошибку. Рассмотрим
эту ошибку.

7.12.2.2. Установка соответствующей обработки ошибок


Кроме обработки уже изученных вами ошибок существует одна ошибка, которая может
возникнуть при конфигурировании анализатора на основанную на схеме верификацию.
Если анализатор не является совместимым с версией 1.2 и, таким образом, не
поддерживает XML Schema, он может сгенерировать SAXNotRecognizedException.

Web-

Rendered by www.RenderX.com
Стр. 156 из 626 Simple API for XML

Для обработки этой ситуации вы должны заключить оператор setProperty() в блок try/catch,
как показано в выделенном ниже коде.

...
SAXParser saxParser = factory.newSAXParser();
try {
saxParser.setProperty(JAXP_SCHEMA_LANGUAGE, W3C_XML_SCHEMA);
}
catch (SAXNotRecognizedException x) {
// ,
JAXP 1.2
...
}
...

7.12.2.3. Установка связи между документом и схемой


Теперь, когда программа готова к верификации данных с использованием определения
XML Schema, осталось только проверить, что XML-документ связан со схемой. Существует
два способа сделать это:
• При помощи объявления схемы в XML-документе.
• При помощи указания схемы, используемой в приложении.
Примечание: Когда приложение указывает схему для использования, она аннулирует
действие схемы, объявленной в документе.
Чтобы указать определение схемы в документе, вы должны создать примерно такой XML-
файл:

<documentRoot
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation='YourSchemaDefinition.xsd'
>
...
Первый атрибут указывает префикс пространства имен XML (xmlns), "xsi", где "xsi"
обозначает "XML Schema Instance" ("Экземпляр XML-схемы"). Вторая строка указывает
используемую схему для элементов документа, которые не имеют префикса пространства
имен - то есть, для элементов, обычно определяемых вами в простых, не составных XML-
документах.
Примечание: Вы изучите пространство имен в разделе "Использование пространства
имен". А сейчас рассматривайте эти атрибуты как "магическую формулу", используемую
для верификации простого XML-файла, не использующего пространство имен. Как только
вы более близко познакомитесь с пространством имен, вы узнаете, как использовать XML
Schema для верификации сложных документов, использующих его. Эти вопросы
обсуждаются в разделе "Верификация при использовании нескольких пространств имен".

Web-

Rendered by www.RenderX.com
Использование верифицирующего анализатора Стр. 157 из 626

Можно, также, указать в приложении файл со схемой при помощи, например, следующего
кода:

static final String JAXP_SCHEMA_SOURCE =


"http://java.sun.com/xml/jaxp/properties/schemaSource";

...
SAXParser saxParser = spf.newSAXParser();
...
saxParser.setProperty(JAXP_SCHEMA_SOURCE,
new File(schemaSource));
Теперь, когда вы узнали, как использовать определение XML Schema, мы обратим ваше
внимание на типы ошибок, которые можно увидеть при верификации в приложении
входящих данных. Для этого будем использовать DTD.

7.12.3. Эксперименты с ошибками верификации


Для того чтобы увидеть, что происходит, если в XML-документ не указана DTD, удалите
оператор DOCTYPE из XML-файла и выполните программу Echo с ним.
Примечание: Выводимая информация находится в файле Echo10-01.txt. (Версия для
броузера - Echo10-01.html.)
Вы увидите примерно такой результат:

<?xml version='1.0' encoding='UTF-8'?>


** Parsing error, line 9, uri .../slideSample01.xml
Document root element "slideshow", must match DOCTYPE root
"null"
Примечание: Приведенное выше сообщение генерируется библиотеками JAXP версии
1.2. Если вы используете другой анализатор, сообщение, возможно, будет другим.
Это сообщение говорит о том, что корневой элемент документа должен соответствовать
элементу, указанному в объявлении DOCTYPE. Это объявление указывает DTD документа.
Поскольку вы еще не имеете его, значение равно "null". Другими словами, сообщение
говорит, что вы пытаетесь верифицировать документ, но DTD не был указан, поскольку
отсутствует объявление DOCTYPE.
Теперь вы знаете, что DTD является обязательным для правильных документов. И это
имеет смысл. Что произойдет, если вы запустите анализатор с вашей текущей версией
программы презентации слайдов с указанием DTD?
Примечание: Показанная здесь информация, генерируемая из slideShow07.xml, находится
в файле Echo10-07.txt. (Версия для броузера - Echo10-07.html.)
Сейчас анализатор выдает другое сообщение об ошибке:

** Parsing error, line 29, uri file:...

Web-

Rendered by www.RenderX.com
Стр. 158 из 626 Simple API for XML

The content of element type "slide" must match


"(image?,title,item*)
Примечание: Приведенное выше сообщение генерируется библиотеками JAXP версии
1.2. Если вы используете другой анализатор, сообщение, возможно, будет другим.
Это сообщение говорит о том, что элемент, найденный в строке 29 (<item>) не соответствует
определению элемента <slide> в DTD. Ошибка возникает из-за того, что в определении
элемента slide указано обязательное наличие title. Этот элемент является обязательным,
а слайд с информацией об авторских правах его не имеет. Для решения проблемы добавьте
знак вопроса, выделенный ниже, чтобы сделать элемент title не обязательным:

<!ELEMENT slide (image?, title?, item*)>


Что теперь произойдет при выполнении программы?
Примечание: Вы можете удалить также слайд с информацией об авторских правах, что
приведет к такому же результату. Эта информация находится в файле Echo10-06.txt.
(Версия для броузера - Echo10-06.html.)
Ответ на вопрос: все работает прекрасно, пока анализатор не встретит тег <em>,
содержащийся в слайде overview. Поскольку этот тег не определен в DTD, попытка
верифицировать документ заканчивается неудачно. Отобразится следующая информация:

...
ELEMENT: <title>
CHARS: Overview
END_ELM: </title>
ELEMENT: <item>
CHARS: Why ** Parsing error, line 28, uri: ...
Element "em" must be declared.
org.xml.sax.SAXParseException: ...
...
Примечание: Приведенное выше сообщение генерируется библиотеками JAXP версии
1.2. Если вы используете другой анализатор, сообщение, возможно, будет другим.
Сообщение об ошибке указывает часть DTD, вызвавшую сбой при верификации. В данном
случае - это строка, определяющая элемент item как (#PCDATA | item).
Упражнение: Сделайте копию файла и удалите из него все теги <em>. Выполнится ли
верификация файла теперь? (В следующем разделе вы узнаете, как определить параметры
так, чтобы можно было использовать XHTML в элементах, которые мы определяем как
часть презентации слайдов.)

7.12.4. Обработка ошибок в верифицирующем анализаторе


Важно понимать, что единственной причиной генерирования исключительной ситуации
при ошибке верификации является код обработки ошибок, написанный вами в предыдущих
разделах этого руководства. Этот код воспроизведен ниже:

Web-

Rendered by www.RenderX.com
Определение сущностей-параметров и условных секций Стр. 159 из 626

public void error(SAXParseException e)


throws SAXParseException
{
throw e;
}
Если эта исключительная ситуация не генерируется, ошибки верификации просто
игнорируются.
Упражнение: Попробуйте закомментировать строку, генерирующую исключительную
ситуацию. Что теперь произойдет при работе анализатора?
В общем случае ошибка SAX-анализа является ошибкой верификации, хотя мы видели,
что ошибка может возникнуть и при указании в файле версии XML, для обработки которой
анализатор не подготовлен. Запомните, что ваше приложение не будет генерировать
исключительные ситуации при верификации, пока вы не предоставите обработчик ошибок,
подобный описанному выше.

7.13. Определение сущностей-параметров и условных секций


Также как сущность общего назначения позволяет вам повторно использовать XML-данные,
сущность-параметр позволяет вам повторно использовать части DTD. В данном разделе
руководства вы узнаете, как определять и использовать сущности-параметры. Вы также
увидите, как используются сущности-параметры с условными секциями в DTD.

7.13.1. Создание сущностей-параметров и обращение к ним


Вспомните, что существующая версия файла презентации слайдов не могла быть
верифицирована, поскольку документ использовал тег <em>, который не являлся частью
DTD. В общем случае мы бы хотели использовать полное разнообразие HTML-тегов в
тексте слайда, а не только один или два, поэтому имеет больший смысл использование
существующего DTD для XHTML, так как в нем определяются все теги, которые нам когда-
нибудь могут понадобиться. Именно для этой цели предназначены сущности-параметры.
Примечание: Показанная здесь спецификация DTD находится в файле slideshow2.dtd.
XML-файл находится в файле slideSample08.xml. (Версии для броузера - slideshow2-dtd.html
и slideSample08-xml.html.)
Откройте ваш DTD-файл для презентации слайдов и добавьте выделенный ниже текст
для определения сущности-параметра, обращающейся к внешнему DTD-файлу:

<!ELEMENT slide (image?, title?, item*)>


<!ATTLIST slide
...
>

<!ENTITY % xhtml SYSTEM "xhtml.dtd">


%xhtml;

Web-

Rendered by www.RenderX.com
Стр. 160 из 626 Simple API for XML

<!ELEMENT title ...


Здесь вы использовали тег <!ENTITY> для определения сущности-параметра, точно так
же, как для сущности общего назначения, но используя немного другой синтаксис. При
определении сущности перед ее именем ставится знак процента (%), и этот знак процента
используется вместо амперсанда при обращении к сущности.
Обратите внимание также, что всегда существует два этапа при использовании сущности-
параметра. Первый - определить имя сущности. Второй - обратиться по имени к сущности,
что фактически вставляет внешние определения в текущий DTD. Поскольку URI для
внешней сущности могут содержать слеши (/) или другие символы, не являющиеся
допустимыми в XML-именах, на этапе определения можно связать корректное XML-имя с
фактическим документом. (Такая же самая техника используется в определении
пространства имен и везде, где XML-компонент требует обращения к внешним документам.)
Примечания:
• DTD-файлом, к которому происходит обращение в данном определении, является
xhtml.dtd. Вы можете либо скопировать этот файл в вашу систему, либо модифицировать
идентификатор SYSTEM в теге <!ENTITY> для указания корректного URL.
• Этот файл является небольшим подмножеством спецификации XHTML, созданным из
проекта Modularized XHTML, целью которого является разбиение DTD for XHTML на
отдельные части, которые затем могут быть скомбинированы для создания различных
подмножеств XHTML для различных целей. В настоящее время данной версии
достаточно для наших целей.
Конечной целью использования основанного на XHTML DTD было получение доступа к
определяемой в нем сущности, описывающей HTML-теги <em> и <b>. При просмотре
xhtml.dtd можно обнаружить следующую сущность, являющуюся именно тем, чего мы
хотим:

<!ENTITY % inline "#PCDATA|em|b|a|img|br">


Эта сущность является более простой версией сущности, определенной в проекте Modu-
larized XHTML. Она определяет наиболее часто используемые HTML-теги - emphasis, bold
и break, плюс несколько других для изображений и анкеров, которые мы можем
использовать в презентации слайдов. Для использования сущности inline сделайте
выделенные ниже изменения в вашем DTD-файле:

<!ELEMENT title (#PCDATA %inline;)*>


<!ELEMENT item (#PCDATA %inline; | item)* >
Эти изменения замещают простой элемент #PCDATA сущностью inline. Необходимо
отметить, что #PCDATA является первым в сущности inline, и что inline является первой
везде, где мы используем ее. Это требование определения XML для модели со смешанным
содержимым. Чтобы соответствовать этой модели, необходимо, также, добавить знак
звездочки в конце определения title. (В следующих двух разделах вы увидите, что наше
определение элемента title конфликтует с версией, определенной в xhtml.dtd, и мы
рассмотрим различные способы решения этой проблемы.)

Web-

Rendered by www.RenderX.com
Определение сущностей-параметров и условных секций Стр. 161 из 626

Примечание: Modularized XHTML DTD определяет обе сущности - как inline, так и Inline, и
делает это немного по-разному. Вместо указания #PCDATA|em|b|a|img|Br ее определения
больше выглядят как (#PCDATA|em|b|a|img|Br)*. Использование одного из этих определений,
таким образом, больше выглядит как: <!ELEMENT title %Inline; >.

7.13.2. Условные секции


Перед работой со следующими упражнениями стоит рассмотреть использование сущностей-
параметров для управления условными секциями. Хотя вы не можете использовать условия
в содержимом XML-документа, вы можете определить условные секции в DTD, которые
становятся частью DTD только при указании include. С другой стороны, если вы укажете
ignore, то условная секция включена в документ не будет.
Предположим, например, что вы хотите использовать немного разные версии DTD, в
зависимости от того, обрабатываете ли вы документ как XML-документ, или как SGML-
документ. Вы можете сделать это при помощи следующего определения DTD:

someExternal.dtd:
<![ INCLUDE [
... XML-only definitions
]]>
<![ IGNORE [
... SGML-only definitions
]]>
... common definitions
Условные секции начинаются с символов "<![", за которыми следует ключевое слово
INCLUDE или IGNORE и еще один символ "[". После этого идет содержимое условной
секции, заканчивающееся символами: "]]>". В данном случае XML-определения включены,
а SGML-определения исключены. Это хорошо для XML-документов, но вы не сможете
использовать DTD для SGML-документов. Вы, конечно, можете изменить ключевые слова,
но это только поменяет проблему.
Решением является использование ссылок на параметры-сущности вместо ключевых слов
INCLUDE и IGNORE:

someExternal.dtd:
<![ %XML; [
... XML-only definitions
]]>
<![ %SGML; [
... SGML-only definitions
]]>
... common definitions
Теперь каждый документ, использующий DTD, может установить определения
соответствующей сущности:

Web-

Rendered by www.RenderX.com
Стр. 162 из 626 Simple API for XML

<!DOCTYPE foo SYSTEM "someExternal.dtd" [


<!ENTITY % XML "INCLUDE" >
<!ENTITY % SGML "IGNORE" >
]>
<foo>
...
</foo>
Эта процедура позволит каждому документу контролировать DTD. Она, также, заменяет
ключевые слова INCLUDE и IGNORE именами переменных, которые более точно отражают
назначение условной секции, делая версию DTD более читаемой и самодокументируемой.

7.14. Анализ параметризованного DTD


Для того чтобы увидеть, что происходит при ссылке в slideshow.dtd на xhtml.dtd, в этом
разделе используется программа Echo. Также здесь рассматриваются некоторые типы
предупреждений, генерируемых SAX-анализатором при наличии DTD.
Примечание: Описанная в этом разделе выводимая информация содержится в файле
Echo10-08.txt. (Версия для броузера - Echo10-08.html.)
Когда вы попытаетесь выполнить программу Echo, вы увидите, что появилась новая ошибка.
Интересующая нас часть выводимой информации показана ниже (в форматированном
виде для улучшения читаемости):

<?xml version='1.0' encoding='UTF-8'?>


** Parsing error, line 22, uri: .../slideshow.dtd
Element type "title" must not be declared more than once.
Примечание: Приведенное выше сообщение генерируется библиотеками JAXP версии
1.2. Если вы используете другой анализатор, сообщение, возможно, будет другим.
Кажется, что xhtml.dtd определяет элемент title, который полностью отличается от элемента
title, определенного в DTD программы презентации слайдов. Поскольку в DTD не существует
иерархии, эти два определения конфликтуют между собой.
Примечание: Modularized XHTML DTD также определяет элемент title, который является
заголовком документа, так что мы не можем избежать конфликта изменением xhtml.dtd -
позднее проблема возникнет снова.
Вы можете, также, использовать пространство имен XML для устранения конфликта, или
использовать одну из более иерархических схем, описанных в разделе "Стандарты схем".
А сейчас просто переименуем элемент title в slideshow.dtd.
Примечание: Показанный здесь XML находится в файлах slideshow3.dtd и slideSample09.xml,
которые обращаются к copyright.xml и xhtml.dtd. Результат обработки показан в Echo10-
09.txt. (Версии для броузера - slideshow3-dtd.html, slideSample09-xml.html, copyright-xml.html,
xhtml-dtd.html и Echo10-09.html.)

Web-

Rendered by www.RenderX.com
Анализ параметризованного DTD Стр. 163 из 626

Для разделения двух элементов title мы прибегнем к "иерархии дефисов". Сделайте


выделенные ниже изменения для замены имени элемента title в slideshow.dtd на slide-title:

<!ELEMENT slide (image?, slide-title?, item*)>


<!ATTLIST slide
type (tech | exec | all) #IMPLIED
>

<!-- Defines the %inline; declaration -->


<!ENTITY % xhtml SYSTEM "xhtml.dtd">
%xhtml;

<!ELEMENT slide-title (%inline;)*>


Следующее действие - изменение XML-файла для использования нового имени элемента.
Для этого сделайте выделенные ниже изменения:

...
<slide type="all">
<slide-title>Wake up to ... </slide-title>
</slide>

...

<!-- OVERVIEW -->


<slide type="all">
<slide-title>Overview</slide-title>
<item>...
Теперь выполните программу Echo на этой версии файла презентации слайдов. Она
должна выполниться полностью и вывести на экран информацию, подобную находящейся
в файле Echo10-09.
Поздравляем! Вы только что прочитали полностью верифицированный XML-документ.
Выполненные вами изменения привели к помещению вашего элемента DTD title в
"пространство имен" slideshow, которое вы искусственно создали при помощи добавления
дефиса в имя. Теперь элемент title в "пространстве имен slideshow" (фактически slide-title)
больше не конфликтует с элементом title в xhtml.dtd. В следующем разделе руководства
вы увидите, как сделать это без переименования определения. В завершение этого раздела
мы рассмотрим типы предупреждений, которые верифицирующий анализатор может
выдать при обработке DTD.

7.14.1. DTD-предупреждения
Как упоминалось ранее в этом руководстве, предупреждения генерируются только во
время обработки SAX-анализатором DTD. Некоторые предупреждения генерируются

Web-

Rendered by www.RenderX.com
Стр. 164 из 626 Simple API for XML

только верифицирующим анализатором. Главная задача неверифицирующего анализатора


- работать как можно быстрее, но он тоже генерирует некоторые предупреждения.
(Следующие далее объяснения расскажут, какой анализатор какие предупреждения
генерирует.)
Спецификация XML указывает, что предупреждения должны генерироваться в результате:
• Предоставления дополнительных объявлений для сущностей, атрибутов или нотаций.
(Такие объявления игнорируются. Используется только первое. Обратите внимание,
также, на то, что дублирование определений элементов всегда генерирует фатальную
ошибку при верификации. Вы видели это ранее.)
• Обращения к необъявленному типу элемента.
(Ошибка верификации возникает только тогда, когда необъявленный тип действительно
используется в XML-документе. Предупреждение генерируется при ссылке на
необъявленный элемент в DTD.)
• Объявления атрибутов для необъявленных типов элементов.
SAX-анализатор Java XML также генерирует предупреждения и в других ситуациях, таких
как:
• Нет <!DOCTYPE…> при верификации.
• Обращение к неопределенной сущности-параметру при отсутствии верификации.
(При верификации генерируется ошибка. Хотя неверифицирующие анализаторы не
должны читать сущности-параметры, анализатор Java XML читает их. Поскольку это
не является требованием, анализатор Java XML генерирует предупреждение, а не
ошибку.)
• Определенные случаи, когда символьное объявление не выглядит правильным.
К этому моменту вы усвоили многие концепции XML, включая DTD и внешние сущности.
Вы, также, изучили SAX-анализатор. Оставшаяся часть руководства по SAX посвящена
расширенным темам, которые вам необходимо знать только для написании приложений,
основанных на SAX. Если вашей первичной целью является создание приложений,
основанных на DOM, вы можете сразу перейти к главе "Document Object Model".

7.15. Обработка лексических событий


Ранее вы видели, что если вы отображаете текст как XML, вы должны знать, находитесь
ли в секции CDATA. Если да, то угловые скобки (<) и амерсанды (&) должны выводиться
неизмененными. Но если нет - они должны быть заменены предопределенными сущностями
&lt; и &amp;. Но как вам узнать, обрабатывается ли секция CDATA?
И далее, если вы фильтруете XML каким-либо способом, вы хотели бы передавать
комментарии. Обычно анализатор игнорирует комментарии. Как вы можете получить
комментарии для их вывода?
И, наконец, существуют определения анализируемых сущностей. Если приложение XML-
фильтр видит &myEntity;, оно должно вывести такую же строку - не текст, вставляемый на
ее место. Как это сделать?

Web-

Rendered by www.RenderX.com
Обработка лексических событий Стр. 165 из 626

В этом разделе руководства даются ответы на эти вопросы. Здесь показано, как
использовать org.xml.sax.ext.LexicalHandler для идентификации комментариев, секций
CDATA и ссылок на анализируемые сущности.
Комментарии, секции CDATA и ссылки на анализируемые сущности составляют лексическую
информацию, то есть, информацию, относящуюся собственно к тексту XML, а не к
содержимому XML. Большинство приложений, конечно, имеют дело только с содержимым
XML-документа. Такие приложения не будут использовать LexicalEventListener API. Но
приложения, выводящие XML-текст, найдут этот API бесценным.
Примечание: Обработка лексических событий является необязательной возможностью
анализатора. От реализации анализатора не требуется ее поддержки. (Данная реализация
поддерживает ее.) Это обсуждение полагает, что ваш анализатор поддерживает обработку
лексических событий.

7.15.1. Как работает LexicalHandler


Для получения уведомления о том, что SAX-анализатор встретил лексическую информацию,
надо настроить XmlReader, который лежит в основе анализатора с LexicalHandler.
Интерфейс LexicalHandler определяет следующие методы обработки событий:

comment(String comment)
Передает комментарии в приложение.

startCDATA(), endCDATA()
Указывает, когда секция CDATA начинается и заканчивается. При этом ваше приложение
будет знать, какой тип символов ожидается при следующем вызове characters().

startEntity(String name), endEntity(String name)


Передает имя анализируемой сущности.

startDTD(String name, String publicId, String systemId), endDTD()


Указывает, когда обрабатывается DTD, и идентифицирует его.

7.15.2. Работа с LexicalHandler


В оставшейся части этого раздела вы преобразуете приложение Echo в лексический
обработчик и поэкспериментируете с его возможностями.
Примечание: Приведенный в этом разделе код находится в файле Echo11.java. Выводимая
информация приведена в файле Echo11-09.txt. (Версия для броузера - Echo11-09.html.)
Для начала добавьте выделенный ниже код для реализации интерфейса LexicalHandler и
добавления соответствующих методов.

import org.xml.sax.*;

Web-

Rendered by www.RenderX.com
Стр. 166 из 626 Simple API for XML

import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.ext.LexicalHandler;
...
public class Echo extends HandlerBase
implements LexicalHandler
{
public static void main(String argv[])
{
...
//
SAX-
DefaultHandler handler = new Echo();
Echo handler = new Echo();
...
С этого момента класс Echo расширяет один класс и реализует дополнительный интерфейс.
Соответственно, вы изменили и класс переменной-обработчика, так что можете
использовать один и тот же экземпляр, как DefaultHandler, так и LexicalHandler.
Далее, добавьте выделенный ниже код для получения XMLReader, к которому обращается
анализатор, и для настройки его на передачу лексических событий в ваш обработчик:

public static void main(String argv[])


{
...
try {
...
//
SAXParser saxParser = factory.newSAXParser();
XMLReader xmlReader = saxParser.getXMLReader();
xmlReader.setProperty(
"http://xml.org/sax/properties/lexical-handler",
handler
);
saxParser.parse( new File(argv[0]), handler);
} catch (SAXParseException spe) {
...
Здесь вы настроили XMLReader при помощи метода setProperty(), определенного в классе
XMLReader. Имя свойства, определенного как часть стандарта SAX, -
http://xml.org/sax/properties/lexical-handler.
И, наконец, добавьте выделенный ниже код для определения соответствующих методов,
реализующих интерфейс.

Web-

Rendered by www.RenderX.com
Обработка лексических событий Стр. 167 из 626

public void warning(SAXParseException err)


...
}

public void comment(char[] ch, int start, int length)throws SAX-


Exception
{
}

public void startCDATA()


throws SAXException
{
}

public void endCDATA()


throws SAXException
{
}

public void startEntity(String name)


throws SAXException
{
}

public void endEntity(String name)


throws SAXException
{
}

public void startDTD(


String name, String publicId, String systemId)
throws SAXException
{
}

public void endDTD()


throws SAXException
{
}

Web-

Rendered by www.RenderX.com
Стр. 168 из 626 Simple API for XML

private void echoText()


...
Вы превратили класс Echo в лексический обработчик. В следующем разделе вы начнете
экспериментировать с лексическими событиями.

7.15.2.1. Отображение комментариев


Следующее действие - сделать что-нибудь с одним из новых методов. Добавьте в XML-
файл выделенный ниже код для вывода комментариев:

public void comment(char[] ch, int start, int length)


throws SAXException
{
String text = new String(ch, start, length);
nl();
emit("COMMENT: "+text);
}
После компиляции и выполнения программы Echo с вашим XML-файлом отобразится
примерно следующая информация:

COMMENT: A SAMPLE set of slides


COMMENT: FOR WALLY / WALLIES
COMMENT:
DTD for a simple "slide show".

COMMENT: Defines the %inline; declaration


COMMENT: ...
Окончания строк в комментариях передаются как часть строк комментариев, еще раз
нормализованных к символам новой строки. Вы можете также увидеть, что комментарии
в DTD передаются вместе с комментариями из файла. (Это может привести к проблемам,
если вы хотите отображать только комментарии из файла данных. Для решения этой
проблемы вы можете использовать методы startDTD и endDTD.)

7.15.2.2. Отображение другой лексической информации


В завершение этого раздела давайте поупражняемся с оставшимися методами LexicalHan-
dler.
Примечание: Приведенный в этом разделе код находится в файле Echo12.java. Файл, с
которым работает программа - slideSample10.xml. (Версия для броузера - slideSample10-
xml.html.) Результаты обработки находятся в файле Echo12-10.
Сделайте выделенные ниже изменения для удаления вывода комментариев (вам это
больше не понадобится) и добавления вывода других событий вместе со всеми символами,
которые были накоплены к моменту наступления события:

Web-

Rendered by www.RenderX.com
Обработка лексических событий Стр. 169 из 626

public void comment(char[] ch, int start, int length)


throws SAXException
{
String text = new String(ch, start, length);
nl();
emit("COMMENT: "+text);
}

public void startCDATA()


throws SAXException
{
echoText();
nl();
emit("START CDATA SECTION");
}

public void endCDATA()


throws SAXException
{
echoText();
nl();
emit("END CDATA SECTION");
}

public void startEntity(String name)


throws SAXException
{
echoText();
nl();
emit("START ENTITY: "+name);
}

public void endEntity(String name)


throws SAXException
{
echoText();
nl();
emit("END ENTITY: "+name);
}

public void startDTD(String name, String publicId, String

Web-

Rendered by www.RenderX.com
Стр. 170 из 626 Simple API for XML

systemId)
throws SAXException
{
nl();
emit("START DTD: "+name
+" publicId=" + publicId
+" systemId=" + systemId);
}

public void endDTD()


throws SAXException
{
nl();
emit("END DTD");
}
При обработке DTD отобразится следующая информация:

START DTD: slideshow


publicId=null
systemId=file:/..../samples/slideshow3.dtd
START ENTITY: ...
...
END DTD
Примечание: Для просмотра событий, происходящих при обработке DTD, используйте
org.xml.sax.ext.DeclHandler.
Следующая дополнительная информация отобразится, когда с последней версией
программы будет обрабатываться определенная внутри сущность products:

START ENTITY: products


CHARS: WonderWidgets
END ENTITY: products
А вот дополнительная информация, отображающаяся при обработке внешней сущности
copyright:

START ENTITY: copyright


CHARS:
This is the standard copyright message that our lawyers
make us put everywhere so we don't have to shell out a
million bucks every time someone spills hot coffee in their
lap...

Web-

Rendered by www.RenderX.com
Использование DTDHandler и EntityResolver Стр. 171 из 626

END ENTITY: copyright


И, наконец, при обработке секции CDATA отобразится следующая информация:

START CDATA SECTION


CHARS: Diagram:

frobmorten <--------------fuznaten
| <3> ^
| <1> | <1> = fozzle
V | <2> = framboze
staten----------------------+ <3> = frenzle
<2>

END CDATA SECTION


Подведем итог. LexicalHandler уведомляет вас о событиях, требуемых для создания точного
отображения оригинального XML-текста.
Примечание: Для точного вывода входной информации вы должны модифицировать метод
characters() для дублирования видимого им текста соответствующим образом, в зависимости
от того, находится или нет программа в режиме CDATA.

7.16. Использование DTDHandler и EntityResolver


В этом разделе мы кратко рассмотрим два оставшихся обработчика событий SAX:
DTDHandler и EntityResolver. DTDHandler вызывается при обнаружении в DTD
неанализируемой сущности или объявления нотации. EntityResolver начинает работать
тогда, когда URN (общедоступный ID) должен быть преобразован в URL (системный ID).

7.16.1. DTDHandler API


В разделе "Обращение к двоичным сущностям" вы видели метод для ссылки на файл,
содержащий двоичные данные, например, файлы изображений, использующие типы
данных MIME. Это самый простой, наиболее гибкий механизм. Для совместимости со
старыми SGML-данными можно, также, определить неанализируемую сущность.
Ключевое слово NDATA определяет неанализируемую сущность, например:

<!ENTITY myEntity SYSTEM "..URL.." NDATA gif>


Ключевое слово NDATA указывает, что данные в этой сущности являются не
анализируемыми XML-данными, а данными, использующими какую-то другую нотацию. В
данном случае нотация называется "gif". DTD должен затем содержать объявление для
этой нотации, которое может выглядеть примерно так:

Web-

Rendered by www.RenderX.com
Стр. 172 из 626 Simple API for XML

<!NOTATION gif SYSTEM "..URL..">


Когда анализатор видит неанализируемую сущность или объявление нотации, он ничего
не делает с информацией, за исключением передачи ее в приложение при помощи
интерфейса DTDHandler. В этом интерфейсе определены два метода:

notationDecl(String name, String publicId, String systemId)

unparsedEntityDecl(String name, String publicId,


String systemId, String notationName)
В метод notationDecl передается имя нотации и либо общедоступный, либо системный
идентификатор, либо оба, в зависимости от того, что объявляется в DTD. В метод
unparseEntityDecl передается имя сущности, соответствующие идентификаторы и имя
используемой нотации.
Примечание: Интерфейс DTDHandler реализуется в классе DefaultHandler.
Нотации могут также использоваться в объявлениях атрибутов. Например, следующее
объявление требует нотации для форматов файлов-изображений GIF и PNG:

<!ENTITY image EMPTY>


<!ATTLIST image
...
type NOTATION (gif | png) "gif"
>
Здесь type объявляется как gif или png. По умолчанию, если ничего не указано, используется
gif.
Где бы ни использовалась ссылка на нотацию, либо для описания неанализируемой
сущности, либо для описания атрибута, именно приложение отвечает за выполнение
соответствующей обработки. Анализатор не знает ничего о семантике нотаций. Он только
передает ее в объявлениях.

7.16.2. EntityResolver API


EntityResolver API позволяет вам преобразовывать общедоступный ID (URN) в системный
ID (URL). Ваше приложение может нуждаться в этом, например, для преобразования
href="urn:/someName" в "http://someURL".
Интерфейс EntityResolver определяет один метод:

resolveEntity(String publicId, String systemId)


Этот метод возвращает объект InputSource, который может использоваться для доступа
к содержимому сущности. Преобразование URL в InputSource достаточно легкий процесс.
Но передаваемый в качестве системного ID URL будет являться месторасположением
оригинального документа, который, весьма вероятно, расположен где-нибудь в Web. Для

Web-

Rendered by www.RenderX.com
Дополнительная информация Стр. 173 из 626

доступа к локальной копии, если таковая существует, вы должны где-то в системе создать
каталог, который отобразит имена (общедоступные ID) в локальные URL.

7.17. Дополнительная информация


Дополнительная информация по стандарту Simple API for XML processing (SAX) находится
на:
• странице стандарта SAX: http://www.saxproject.org/
Дополнительную информацию по основанным на схеме механизмам верификации можно
найти:
• Стандартный механизм верификации W3C, XML Schema: http://www.w3c.org/XML/Schema
• Механизм верификации, основанный на регулярных выражениях, RELAX NG:
http://www.oasis-open.org/committees/relax-ng/
• Механизм верификации, основанный на утверждениях, Schematron:
http://www.ascc.net/xml/resource/schematron/schematron.html

8. Document Object Model


В главе, посвященной SAX, вы создали XML-файл, содержащий описание слайдов для
презентации. Затем вы использовали SAX API для отображения XML на вашем мониторе.
В этой главе вы будете использовать Document Object Model (DOM) для создания
небольшого приложения SlideShow. Вы начнете с построения DOM и исследования ее,
затем узнаете, как написать DOM в виде XML-структуры, отобразить ее в GUI и управлять
структурой дерева.
Document Object Model (объектная модель документа) является искусственной древовидной
структурой, в которой каждый узел содержит один из компонентов XML-структуры. Двумя
наиболее общими типами узлов являются узлы элементов и текстовые узлы. Использование
функций DOM позволяет вам создавать узлы, удалять узлы, изменять их содержимое и
проходить по иерархии узлов.
В этой главе вы проанализируете существующий XML-файл для построения DOM,
отобразите и исследуете DOM-иерархию, преобразуете DOM в дружественный
пользователю Jtree и исследуете синтаксис пространства имен. Вы также создадите DOM
с нуля и узнаете, как использовать некоторые, из зависящих от реализации, возможности
Sun JAXP для преобразования существующего набора данных в XML.
Но прежде всего мы убедимся, что DOM - это наиболее подходящий выбор для нашего
приложения. Мы сделаем это в следующем разделе "Когда используется DOM".
Примечание: Примеры этой главы расположены в каталоге <JWSDP_HOME>/docs/tuto-
rial/examples/jaxp/dom/samples.

8.1. Когда используется DOM


Document Object Model (DOM) представляет собой стандарт, который прежде всего
проектировался для документов (например, статей и книг). Кроме того, реализация JAXP

Web-

Rendered by www.RenderX.com
Стр. 174 из 626 Document Object Model

1.2 поддерживает стандарт XML Schema, который может быть важным для любого
приложения.
С другой стороны, если вы имеете дело с простыми структурами данных, и XML Schema
не играет большой роли в ваших планах, то вы, возможно, найдете, что один из более
объектно-ориентированных стандартов, таких как JDOM и dom4j, больше подходит для
ваших целей.
С самого начала DOM разрабатывался нейтральным к языку. Поскольку он был
предназначался для использования с языками, подобными С или Perl, DOM не использует
преимуществ объектно-ориентированных особенностей Java. Этот факт, в дополнение к
различиям документ/данные, также помогает объяснить различия в обработке структур
DOM и JDOM или dom4j.
В этом разделе мы исследуем различия между моделями, лежащими в основе этих
стандартов, что поможет вам выбрать тот, который наиболее подходит для вашего
приложения.

8.1.1. Документы против данных


Основные отличия между моделью документов, используемой в DOM, и моделью данных,
используемой в JDOM или dom4j, состоят в:
• Типе узлов, существующих в иерархии.
• Возможности "смешанного содержимого".
Именно различие в том, что составляет "узел" в иерархии данных, главным образом влияет
на различия в программировании при помощи этих двух моделей. Однако, именно
возможность смешанного содержимого, в большей степени чем что-либо еще, отвечает
за различие в том, как стандарты определяют "узел". Поэтому мы начнем с исследования
"модели смешанного содержимого" DOM.

8.1.2. Модель смешанного содержимого


Вспомните из обсуждения "Основанное на документах программирование (DDP)", что текст
и элементы могут свободно смешиваться в иерархии DOM. Такой тип структуры в модели
DOM называется "смешанное содержимое".
Смешанное содержимое часто встречается в документах. Например, для представления
этой структуры:

<sentence>This is an <bold>important</bold> idea.</sentence>

Иерархия DOM-узлов будет выглядеть примерно так (каждая строка представляет один
узел):

ELEMENT: sentence
+ TEXT: This is an
+ ELEMENT: bold

Web-

Rendered by www.RenderX.com
Когда используется DOM Стр. 175 из 626

+ TEXT: important
+ TEXT: idea.
Обратите внимание, что элемент sentence содержит текст, за которым следует субэлемент
и далее дополнительный текст. Это и есть то смешение текста и элементов, которое и
определяет "модель со смешанным содержимым"

8.1.2.1. Типы узлов


Предоставляя возможность смешанного содержимого, узлы DOM в действительности
очень просты. В приведенном выше упражнении, например, "содержимое" первого элемента
(его значение) просто идентифицирует тип узла.
Новички в DOM часто не учитывают этот факт. После перемещения к узлу <sentence> они
запрашивают "содержимое" и ожидают получить что-нибудь полезное. Вместо этого все,
что они получают - это имя элемента, "sentence".
Примечание: DOM Node API определяет методы nodeValue(), node.nodeType() и nodeName().
Для первого узла элемента nodeName() возвращает "sentence", а nodeValue() возврщает
null. Для первого текстового узла nodeName() возвращает "#text", а nodeValue() - "This is
an ". Важное замечание - значение элемента не равно его содержимому.
Получение интересующего содержимого при работе DOM означает проверку списка
субэлементов, которые содержит узел, игнорирование ненужных вам субэлементов и
обработку нужных.
Например, что означает запрос "text" у узла sentence в приведенном выше примере? Любое
значение из следующего списка, в зависимости от вашего приложения:
This is an
This is an idea.
This is an important idea.
This is an <bold>important</bold> idea.

8.1.3. Более простая модель


При использовании DOM вы свободно можете создавать необходимую семантику. Однако
вы должны также выполнить обработку для реализации этой семантики. С другой стороны,
такие стандарты как JDOM и dom4j значительно облегчают реализацию простых задач,
поскольку каждый узел в иерархии является объектом.
Хотя JDOM и dom4j позволяют использовать элементы со смешанным содержимым, они
не были разработаны специально для этих ситуаций. Эти стандарты предназначены для
приложений, в которых XML-структура содержит данные.
Как указывалось в разделе "Традиционная обработка данных", элементы в структуре
данных обычно содержат либо текст, либо другие элементы, но не и то и другое
одновременно. Например, вот XML, представляющий простую адресную книгу:

<addressbook>
<entry>
<name>Fred</name>

Web-

Rendered by www.RenderX.com
Стр. 176 из 626 Document Object Model

<email>fred@home</email>
</entry>
...
</addressbook>
Примечание: Для очень простых XML-структур, таких как эта, вы можете также использовать
пакет регулярных выражений (java.util.regex), встроенный в версию 1.4 платформы Java.
В JDOM и dom4j после перемещения к элементу, содержащему текст, для получения его
содержимого вызывается метод text(). В DOM вы должны проверить список субэлементов
для составления текста узла, даже если, как вы уже видели, этот список содержит только
один элемент (узел TEXT).
Таким образом, для таких простых структур данных как приведенная выше адресная книга
вы можете сохранить ваше время, используя JDOM или dom4j. Также имеет смысл
использовать одну из этих моделей даже при технически "смешанных" данных, но если
всегда существует один (и только один) сегмент текста для конкретного узла.
Вот пример структуры такого типа, которая могла бы легко обрабатываться в JDOM или
dom4j:

<addressbook>
<entry>Fred
<email>fred@home</email>
</entry>
...
</addressbook>
Здесь каждая запись содержит небольшой текст, за которым следуют другие элементы.
С этой структурой программа могла бы переместиться к записи, вызвать text() для
определения адресата и обработать субэлемент <email> нужного узла.

8.1.4. Увеличение сложности


Для полного понимания типа обработки, необходимого при поиске в DOM или при ее
обработке, важно знать типы узлов, которые DOM может содержать.
Рассмотрим следующий пример, который поможет разобраться в этом вопросе:
<sentence>
The &projectName;
<![CDATA[<i>project</i>]]> is
<?editor: red><bold>important</bold><?editor: normal>.
</sentence>
Это предложение содержит ссылку на сущность - указатель на "сущность", определенную
где-то в другом месте. В данном случае сущность содержит имя проекта. Пример содержит
также секцию CDATA (неинтерпретируемые данные, как <pre> в HTML), и инструкции
обработки (<?…?>), указывающие редактору, каким цветом отображать текст.

Web-

Rendered by www.RenderX.com
Когда используется DOM Стр. 177 из 626

Вот DOM-структура этих данных. Она четко представляет тип структуры, к обработке
которой должно быть готово приложение:

+ ELEMENT: sentence
+ TEXT: The
+ ENTITY REF: projectName
+ COMMENT: The latest name we're using
+ TEXT: Eagle
+ CDATA: <i>project</i>
+ TEXT: is
+ PI: editor: red
+ ELEMENT: bold
+ TEXT: important
+ PI: editor: normal
Этот пример показывает типы узлов, которые могут встретиться в DOM. Хотя ваше
приложение может в большинстве случаев игнорировать большинство из них, по-
настоящему устойчивое приложение должно распознавать и обрабатывать каждый тип
узла.
Подобным же образом процесс перемещения к узлу включает в себя обработку
субэлементов, то есть, игнорирование ненужных и проверку требуемых до тех пор, пока
не будет найден искомый узел.
Часто в таких случаях необходимо найти узел, содержащий конкретный текст. Например,
в разделе "DOM API" вы искали узел <coffee>, элемент <name> которого содержал текст
"Mocha Java". Для такого поиска программе необходимо было работать со списком
элементов <coffee> и для каждого элемента: а) получить элемент <name>; б) проверить
узел TEXT этого элемента.
Однако в этом примере были сделаны некоторые упрощения. Предполагалось, что в
структуре данных не могло содержаться инструкций обработки, комментариев, узлов
CDATA и ссылок на сущности. Многие простые приложения могут тоже делать такие
предположения. Но по-настоящему устойчивые приложения должны быть готовы иметь
дело со всеми типами XML-данных.
("Простое" приложение будет работать только в тех случаях, когда входные данные
содержат ожидаемые им упрощенные XML-структуры. Нет механизмов проверки того, что
не будут существовать более сложные структуры. В конце концов, XML создавался
специально для таких структур.)
Для более устойчивой работы код примера, описанного в разделе "DOM API", должен
выполнить следующее:
1. При поиске элемента <name>:
2.
A. Игнорировать комментарии, атрибуты и инструкции обработки.
B. Учитывать возможность того, что субэлементы <coffee> могут встречаться в разном
порядке.

Web-

Rendered by www.RenderX.com
Стр. 178 из 626 Document Object Model

C. Пропустить узлы TEXT, содержащие игнорируемые пробелы, при отсутствии


верификации.
3. При получении текста из узла:
4.
A. Извлечь текст из узлов CDATA как из текстовых узлов.
B. Игнорировать комментарии, атрибуты и инструкции обработки при сборке текста.
C. Если встретилась ссылка или другой узел выполнить рекурсию. (То есть, применить
процедуру извлечения текста для каждого из субэлементов.)
Примечание: Анализатор JAXP 1.2 не вставляет узлы с ссылками на сущность в DOM.
Вместо них он вставляет узлы TEXT с содержимым ссылки. Анализатор JAXP 1.1,
встроенный в платформу 1.4 вставляет узлы с ссылками на сущность. Поэтому устойчивое
приложение, не зависящее от анализатора, должно быть готово к обработке таких узлов.
Многие приложения, естественно, не должны беспокоиться об этих вещах, поскольку тип
данных, с которыми они работают, будет строго контролироваться. Но если данные могут
прийти из различных внешних источников, приложение, по всей видимости, должно
учитывать все возможности.
Код, который необходим для учета этих функций, приведен в конце руководства по DOM
в разделах "Поиск узлов" и "Получение содержимого узла". А сейчас нашей целью является
просто определение того, подходит ли DOM для вашего приложения.

8.1.5. Выбор вашей модели


Как вы видите, при использовании DOM даже такая простая операция, как получение
текста из узла, может потребовать некоторого программирования. Поэтому, если ваше
приложение будет обрабатывать простые структуры данных, возможно более подходящими
будут модели JDOM, dom4j или даже пакет регулярных выражений версии 1.4
(java.util.regex).
С другой стороны, для сложных документов и сложных приложений DOM предоставляет
большую гибкость. DOM тоже может применяться, если вам необходимо использовать
XML Schema.
Если в разрабатываемых вами приложениях вы будете обрабатывать и документы и
данные, DOM также будет лучшим выбором. В конце концов, если вы написали код,
проверяющий и обрабатывающий DOM-структуру, очень легко настроить его для конкретных
задач. Поэтому, выполнение всей обработки в DOM означает, что вы будете иметь дело
только с одним набором API, а не с двумя.
Кроме того, стандарт DOM - это стандарт. Он устойчивый и завершенный и имеет много
реализаций. Это - решающий фактор для многих больших приложений - в частности для
производственных приложений, где нужно защититься от больших переделок кода в случае
изменения API.
И, наконец, даже если текст в адресной книге в настоящее время может не содержать
жирного и наклонного шрифтов, цветного выделения и различных размеров шрифта, когда-
нибудь вы, вероятно, захотите использовать эти возможности. Поскольку DOM будет
обрабатывать практически все, что вы ему предоставите, выбор DOM облегчит работу
над приложением в будущем.

Web-

Rendered by www.RenderX.com
Чтение XML-данных в DOM Стр. 179 из 626

8.2. Чтение XML-данных в DOM


В этом разделе руководства вы построите Document Object Model (DOM) читая
существующий XML-файл. В следующих разделах вы узнаете, как отобразить XML в
компонент Swing и потренируетесь в обработке DOM.
Примечание: В следующей части руководства "XML Stylesheet Language for Transformations"
вы узнаете, как писать DOM в XML-файл. (Вы также увидите как относительно легко
преобразовывать существующие данные в XML.)

8.2.1. Создание программы


DOM предоставляет API, позволяющий создавать узлы, изменять, удалять и
реорганизовывать их. То есть, создать DOM относительно легко, что вы и увидите далее
в разделе 5 этого руководства - "Создание и управление DOM."
Перед тем, как вы попытаетесь создать DOM, полезно понять, как она структурирована.
Эти упражнения сделают видимыми внутренности DOM при помощи отображения ее в
Swing Jtree.

8.2.1.1. Создание скелета


Теперь, когда вы кратко познакомились с тем, как создать DOM, давайте разработаем
простую программу чтения XML-документа в DOM и последующего сохранения этой DOM
в XML-документ.
Примечание: Код этого раздела содержится в файле DomEcho01.java. Файл, с которым
он работает, называется slideSample01.xml. (Версия для броузера - slideSample01-xml.html.)
Начнем с обычной для приложения задачи - проверим наличие аргументов, переданных
в командной строке:

public class DomEcho {


public static void main(String argv[])
{
if (argv.length != 1) {
System.err.println(
"Usage: java DomEcho filename");
System.exit(1);
}
}// main
}// DomEcho

8.2.1.2. Импорт требуемых классов


В этом разделе вы увидите, что все классы перечислены индивидуально. То есть вы
можете увидеть, откуда берется каждый класс для ссылки на документацию по API. В
ваших собственных приложениях вы, возможно, замените приведенные ниже операторы
import на более короткую форму, например: javax.xml.parsers.*.

Web-

Rendered by www.RenderX.com
Стр. 180 из 626 Document Object Model

Добавьте эти строки для импорта JAXP API:

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.ParserConfigurationException;
Добавьте следующие строки для возможных исключительных ситуаций, генерируемых
при анализе XML-документа:

import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
Добавьте эти строки для чтения XML-файла и идентификации ошибок:

import java.io.File;
import java.io.IOException;
Наконец, импортируйте W3C-определение для DOM и исключительных ситуаций DOM:

import org.w3c.dom.Document;
import org.w3c.dom.DOMException;
Примечание: DOMException генерируется только при обходе или управлении DOM. Ошибки,
возникающие при анализе, используют другой механизм вывода, объясняемый ниже.

8.2.1.3. Объявление DOM


Класс org.w3c.dom.Document является названием W3C для DOM. И при анализе XML-
документа, и при его создании будет существовать экземпляр Document. В дальнейшем
мы захотим использовать ссылку на этот объект из другого метода, поэтому определим
его как глобальный объект:

public class DomEcho


{
static Document document;

public static void main(String argv[])


{
Он должен иметь спецификатор static, поскольку мы будем генерировать его содержимое
из метода main.

8.2.1.4. Обработка ошибок


Напишем логику обработки ошибок. Эта логика в основном такая же, как и увиденная вами
в разделе "Обработка ошибок в неверифицирующем анализаторе" руководства по SAX,

Web-

Rendered by www.RenderX.com
Чтение XML-данных в DOM Стр. 181 из 626

поэтому мы не будем сейчас вдаваться в детали. Главный момент, который стоит


упоминания, - необходим генератор JAXP-совместимых документов для выдачи
исключительных ситуаций SAX при возникновении проблем во время анализа XML-
документа. DOM-анализатор не должен в действительности использовать SAX-анализатор,
но поскольку стандарт SAX уже существует, имеет смысл использовать его для выдачи
ошибок. Поэтому код обработки ошибок в приложениях DOM и SAX очень похож:

public static void main(String argv[])


{
if (argv.length != 1) {
...
}

try {

} catch (SAXParseException spe) {


// ,
System.out.println("\n** Parsing error"
+ ", line " + spe.getLineNumber()
+ ", uri " + spe.getSystemId());
System.out.println(" " + spe.getMessage() );

//

Exception x = spe;
if (spe.getException() != null)
x = spe.getException();
x.printStackTrace();

} catch (SAXException sxe) {


// ,
Exception x = sxe;
if (sxe.getException() != null)
x = sxe.getException();
x.printStackTrace();

} catch (ParserConfigurationException pce) {


//

pce.printStackTrace();

Web-

Rendered by www.RenderX.com
Стр. 182 из 626 Document Object Model

} catch (IOException ioe) {


// I/O
ioe.printStackTrace();
}

}// main

8.2.1.5. Создание экземпляра генератора


Теперь добавьте выделенный ниже код для получения генератора документа:
public static void main(String argv[])
{
if (argv.length != 1) {
...
}
DocumentBuilderFactory factory =
DocumentBuilderFactory.newInstance()
try {

8.2.1.6. Получение анализатора и анализ файла


А сейчас добавьте выделенный ниже код для получения экземпляра анализатора и
использования его для анализа указанного файла:

try {
DocumentBuilder builder = factory.newDocumentBuilder();
document = builder.parse( new File(argv[0]) );
} catch (SAXParseException spe) {
Сохраните этот файл!
Сейчас вы, возможно, подумали о том, что каждое JAXP-приложение начинается примерно
одинаково. Вы правы! Сохраните эту версию файла в виде шаблона. Вы будете
использовать этот шаблон в дальнейшем как основу приложения XSLT-преобразований.

8.2.1.7. Выполнение программы


Во всем руководстве по DOM вы будете использовать пример презентации слайдов, с
которым вы встречались в разделах по SAX. В частности, slideSample01.xml - простой
XML-файл, а slideSample10.xml - более сложный пример, включающий DTD, инструкции
обработки, ссылки на сущности и секцию CDATA.
Инструкции по компиляции и выполнению программы вы найдете в разделе "Компиляция
и выполнение программы" руководства по SAX. Замените "Echo" на "DomEcho" в имени
программы.
А теперь выполните программу с файлом slideSample01.xml. Если она выполнится без
ошибок - это значит, что вы успешно проанализировали документ и построили DOM.
Поздравляем!

Web-

Rendered by www.RenderX.com
Чтение XML-данных в DOM Стр. 183 из 626

Примечание: Вы должны пока верить на слово, поскольку никак не можете отобразить


результаты. Но вскоре такая возможность предоставится…

8.2.2. Дополнительная информация


Теперь, когда вы успешно прочитали в DOM, существует несколько моментов, которые вы
должны знать для эффективного использования DocumentBuilder. В частности, вы должны
знать о:
• Настройке генератора
• Обработке ошибок верификации

8.2.2.1. Настройка генератора


По умолчанию генератор возвращает неверифицирующий анализатор, который ничего не
знает о пространстве имен. Для получения верифицирующего анализатора, и/или
анализатора, работающего с пространствами имен, вы настраиваете генератор,
устанавливая свойства при помощи выделенных ниже команд:

public static void main(String argv[])


{
if (argv.length != 1) {
...
}
DocumentBuilderFactory factory =
DocumentBuilderFactory.newInstance();
factory.setValidating(true);
factory.setNamespaceAware(true);
try {
...
Примечание: От JAXP-совместимых анализаторов не требуется поддержка всех комбинаций
этих свойств, хотя наш анализатор поддерживает их. Если вы укажете неверную
комбинацию свойств, генератор вызовет ParserConfigurationException при попытке получения
экземпляра генератора.
Использование пространств имен

8.2.2.2. Обработка ошибок верификации


Вспомнили, что когда вы прорабатывали руководство по SAX, вы хотели создать DOM?
Хорошо, сейчас эта информация пригодится.
В стандарте SAX предусмотрено, что при возникновении ошибки верификации по умолчанию
ничего не делается. Стандарт JAXP требует генерировать исключительные ситуации SAX,
поэтому вы используете точно такой же механизм обработки ошибок, что и в SAX-
приложении. В частности, вам необходимо использовать метод setErrorHandler Document-
Builder для предоставления объекта, реализующего интерфейс SAX ErrorHandler.

Web-

Rendered by www.RenderX.com
Стр. 184 из 626 Document Object Model

Примечание: DocumentBuilder имеет, также, метод setEntityResolver, который вы можете


использовать.
В приведенном ниже коде используется анонимный внутренний класс для определения
ErrorHandler. Выделенный ниже код обеспечивает генерацию исключительной ситуации
при ошибках верификации.

builder.setErrorHandler(
new org.xml.sax.ErrorHandler() {
//
( )
public void fatalError(SAXParseException exception)
throws SAXException {
}
//

public void error(SAXParseException e)


throws SAXParseException
{
throw e;
}

//
public void warning(SAXParseException err)
throws SAXParseException
{
System.out.println("** Warning"
+ ", line " + err.getLineNumber()
+ ", uri " + err.getSystemId());
System.out.println(" " + err.getMessage());
}

);
Этот код использует анонимный класс для генерации экземпляра объекта, реализующего
интерфейс ErrorHandler. Поскольку он не имеет имени класса, он является "анонимным".
Вы можете рассматривать его как экземпляр "ErrorHandler", хотя технически он является
не имеющим имени экземпляром, реализующим указанный интерфейс. Код в большей
части такой же, как и описанный в разделе "Обработка ошибок неверифицирующим
анализатором". Более подробная информация по вопросам верификации находится в
разделе "Использование верифицирующего анализатора".

Web-

Rendered by www.RenderX.com
Отображение DOM-иерархии Стр. 185 из 626

8.2.3. Забегая вперед


В следующем разделе вы отобразите DOM-структуру в JTree и начнете исследование
структуры. Например, вы увидите, как выглядят ссылки на сущность и секции CDATA в
DOM. И, что наиболее важно, вы увидите, как текстовые узлы (которые содержат
фактические данные) располагаются под узлами элементов в DOM.

8.3. Отображение DOM-иерархии


При создании или управлении DOM полезно иметь ясное представление, как
структурированы узлы в DOM. В данном разделе руководства вы отобразите внутреннюю
структуру DOM.

8.3.1. Отображение узлов дерева


Что вам необходимо на данном этапе - это способ отображения узлов в DOM так, чтобы
можно было увидеть их содержимое. Для этого вы преобразуете DOM в JTreeModel и
отобразите всю DOM в JTree. На это придется потратить немного времени, но в конечном
итоге вы получите средство диагностики, которое можно использовать в будущем, а также
сейчас для изучения DOM-структуры.

8.3.2. Преобразование DomEcho в приложение GUI


Поскольку DOM является деревом, а компонент Swing JTree предназначен для отображения
деревьев, имеет смысл преобразовать DOM в JTree, чтобы посмотреть на нее. Первым
действием для этого является переделка программы DomEcho в приложение GUI.
Примечание: Код этого раздела находится в файле DomEcho02.java.

8.3.2.1. Добавление операторов импорта


Начнем с импорта компонентов GUI, необходимых для настройки приложения и отображения
JTree:

// GUI
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
Затем мы модифицируем программу для генерации дружественного пользователю
отображения JTree. При выборе пользователем элемента в дереве вы отобразите
субэлементы в смежной панели. Поскольку мы сейчас делаем подготовительную работу,
импортируйте компоненты для разделения области просмотра (JSplitPane) и отображения
текста субэлементов (JEditorPane):

import javax.swing.JSplitPane;
import javax.swing.JEditorPane;

Web-

Rendered by www.RenderX.com
Стр. 186 из 626 Document Object Model

Добавьте несколько классов поддержки:

// GUI
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.WindowEvent;
import java.awt.event.WindowAdapter;
И, наконец, импортируйте некоторые классы для создания рамки:

//
import javax.swing.border.EmptyBorder;
import javax.swing.border.BevelBorder;
import javax.swing.border.CompoundBorder;
(Они не обязательны. Вы можете, если хотите, для упрощения пропустить их и код, их
использующий.)

8.3.2.2. Создание GUI-среды


Следующим действием является преобразование приложения в GUI-приложение. Для
этого статический метод main создает экземпляр класса main, который станет GUI-панелью.
Начните преобразование класса в GUI-панель путем расширения класса Swing JPanel:

public class DomEcho02 extends JPanel


{
// ,

static Document document;


...
Добавьте несколько констант для управления размерами окна:

public class DomEcho02 extends JPanel


{
// ,

static Document document;

static final int windowHeight = 460;


static final int leftWidth = 300;

Web-

Rendered by www.RenderX.com
Отображение DOM-иерархии Стр. 187 из 626

static final int rightWidth = 340;


static final int windowWidth = leftWidth + rightWidth;
Теперь в методе main вызовите метод, который создаст внешнюю рамку вокруг GUI-панели:

public static void main(String argv[])


{
...
DocumentBuilderFactory factory ...
try {
DocumentBuilder builder = factory.newDocumentBuilder();
document = builder.parse( new File(argv[0]) );
makeFrame();

} catch (SAXParseException spe) {


...
Затем необходимо определить сам метод makeFrame. Он содержит стандартный код для
создания рамки, нормально обрабатывает условие выхода, передавая его в экземпляр
основной панели, задает размеры панели, размещает на экране и делает ее видимой:

...
} // main

public static void makeFrame()


{
// GUI-
JFrame frame = new JFrame("DOM Echo");
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e)
{System.exit(0);}
});

// ,

final DomEcho02 echoPanel = new DomEcho02();


frame.getContentPane().add("Center", echoPanel );
frame.pack();
Dimension screenSize =
Toolkit.getDefaultToolkit().getScreenSize();
int w = windowWidth + 10;
int h = windowHeight + 10;

Web-

Rendered by www.RenderX.com
Стр. 188 из 626 Document Object Model

frame.setLocation(screenSize.width/3 - w/2,
screenSize.height/2 - h/2);
frame.setSize(w, h);
frame.setVisible(true)
} // makeFrame

8.3.2.3. Добавление компонентов для отображения


Единственной оставшейся работой для преобразования программы в GUI-приложение
является создание конструктора класса и создание им содержимого панели. Вот этот
конструктор:

public class DomEcho02 extends JPanel


{
...
static final int windowWidth = leftWidth + rightWidth;

public DomEcho02()
{
} //
Используйте импортированные ранее классы для создания красивой рамки (необязательно):

public DomEcho02()
{
//
EmptyBorder eb = new EmptyBorder(5,5,5,5);
BevelBorder bb = new BevelBorder(BevelBorder.LOWERED);
CompoundBorder cb = new CompoundBorder(eb,bb);
this.setBorder(new CompoundBorder(cb,eb));

} //
Затем, создайте пустое дерево и разместите его в JScrollPane, чтобы пользователь видел
его содержимое по мере его роста:

public DomEcho02(
{
...

//
JTree tree = new JTree();

Web-

Rendered by www.RenderX.com
Отображение DOM-иерархии Стр. 189 из 626

//
JScrollPane treeView = new JScrollPane(tree);
treeView.setPreferredSize(
new Dimension( leftWidth, windowHeight ));

} //
Теперь создайте нередактируемую JEditPane, в которой будет отображаться содержимое
выбранных узлов JTree:

public DomEcho02(
{
....

//
JEditorPane htmlPane = new JEditorPane("text/html","");
htmlPane.setEditable(false);
JScrollPane htmlView = new JScrollPane(htmlPane);
htmlView.setPreferredSize(
new Dimension( rightWidth, windowHeight ));

} //
Для хранения построенных JTree и JEditorPane создайте JSplitPane:

public DomEcho02()
{
....

//

JSplitPane splitPane =
new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
treeView, htmlView );
splitPane.setContinuousLayout( true );
splitPane.setDividerLocation( leftWidth );
splitPane.setPreferredSize(
new Dimension( windowWidth + 10, windowHeight+10 ));

} //

Web-

Rendered by www.RenderX.com
Стр. 190 из 626 Document Object Model

При помощи этого кода вы создали JSplitPane с вертикальным разделителем. Сделано


"горизонтальное разделение" между деревом и панелью редактора. (Фактически -
горизонтальная схема.) Вы, также, установили делитель так, чтобы ширина левой части
зависела от ширины дерева, а в оставшейся части окна располагалась панель редактора.
Наконец, определим схему панели и добавим разделительную рамку:

public DomEcho02()
{
...

// GUI-
this.setLayout(new BorderLayout());
this.add("Center", splitPane );

} //
Поздравляем! Программа стала GUI-приложением. Вы можете выполнить ее сейчас, чтобы
посмотреть ее общий вид на экране. Для справки, вот полный конструктор:

public DomEcho02()
{
//
EmptyBorder eb = new EmptyBorder(5,5,5,5);
BevelBorder bb = new BevelBorder(BevelBorder.LOWERED);
CompoundBorder CB = new CompoundBorder(eb,bb);
this.setBorder(new CompoundBorder(CB,eb));

//
JTree tree = new JTree();

//
JScrollPane treeView = new JScrollPane(tree);
treeView.setPreferredSize(
new Dimension( leftWidth, windowHeight ));

//
JEditorPane htmlPane = new JEditorPane("text/html","");
htmlPane.setEditable(false);
JScrollPane htmlView = new JScrollPane(htmlPane);
htmlView.setPreferredSize(
new Dimension( rightWidth, windowHeight ));

Web-

Rendered by www.RenderX.com
Отображение DOM-иерархии Стр. 191 из 626

//

JSplitPane splitPane =
new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
treeView, htmlView )
splitPane.setContinuousLayout( true );
splitPane.setDividerLocation( leftWidth );
splitPane.setPreferredSize(
new Dimension( windowWidth + 10, windowHeight+10 ));

// GUI-
this.setLayout(new BorderLayout());
this.add("Center", splitPane );

} //

8.3.3. Создание адаптера для отображения DOM в JTree


Теперь, когда у вас есть GUI-среда для отображения JTree, следующим действием является
получение JTree для отображения DOM. Но JTree требует для отображения TreeModel.
DOM является деревом, но не TreeModel. Поэтому вы должны создать класс адаптера,
который сделает DOM похожим на TreeModel для JTree.
При передаче узлов из TreeModel в JTree, JTree использует функцию toString этих узлов
для получения текста, отображающегося в дереве. Стандартная функция toString не очень
симпатичная, поэтому вам необходимо преобразовать DOM-узлы в AdapterNode,
возвращающие нужный нам текст. Следовательно, в действительности TreeModel передает
в JTree объекты AdapterNode, заключающие в себе DOM-узлы.
Примечание: Следующие далее классы определяются как внутренние классы. Если вы
кодируете для платформы версии 1.1, они должны определяться как внешние классы.

8.3.3.1. Определение класса AdapterNode


Начните с импорта классов дерева, событий и утилит, необходимых для выполнения
работы:

// TreeModel
import javax.swing.tree.*;
import javax.swing.event.*;
import java.util.*;

public class DomEcho extends JPanel


{

Web-

Rendered by www.RenderX.com
Стр. 192 из 626 Document Object Model

Переместившись в конец программы, определите набор строк для типов узловых элементов:

...
} // makeFrame

// DOM-
// ( = nodeType().)
static final String[] typeName = {
"none",
"Element",
"Attr",
"Text",
"CDATA",
"EntityRef",
"Entity",
"ProcInstr",
"Comment",
"Document",
"DocType",
"DocFragment",
"Notation",
};

} // DomEcho
Это строки, отображаемые в JTree. Спецификация этих типов узлов находится в Document
Object Model (DOM) Level 2 Core Specification в http://www.w3.org/TR/2000/REC_DOM/Level-
2-Core-20001113. Ниже приведена эта таблица с измененными для ясности заголовками
и с добавленным столбцом nodeType():
Таблица 1 Типы узлов
Узел nodeName() nodeValue() attributes nodeType()
Attr имя атртибута значение атрибута null 2
CDATASection #cdata-section содержимое CDATASec- null 4
tion
Comment #comment содержимое null 8
комментария
Document #document null null 9
DocumentFragment #document-fragment null null 11
DocumentType название типа null null 10
документа
Element имя тега null NamedNodeMap 1
Entity имя сущности null null 6
EntityReference имя ссылаемой null null 5
сущности

Web-

Rendered by www.RenderX.com
Отображение DOM-иерархии Стр. 193 из 626

Узел nodeName() nodeValue() attributes nodeType()


Notation имя нотации null null 12
ProcessingInstruction назначение полное содержимое за null 7
исключением
назначения
Text #text содержимое текстового null 3
узла

Предложения:
Распечатайте эту таблицу и держите ее под рукой. Вам она понадобится при работе с
DOM, поскольку все эти типы смешаны в DOM-дереве. То есть, ваш код постоянно
спрашивает: "Это ли тип узла, который меня интересует?".
Теперь определим AdapterNode, оболочку для DOM-узлов, как внутренний класс:

static final String[] typeName = {


...
};

public class AdapterNode


{
org.w3c.dom.Node domNode;

// AdapterNode DOM-
public AdapterNode(org.w3c.dom.Node node) {
domNode = node;
}

// ,

//
public String toString() {
String s = typeName[domNode.getNodeType()];
String nodeName = domNode.getNodeName();
if (! nodeName.startsWith("#")) {
s += ": " + nodeName;
}
if (domNode.getNodeValue() != null) {
if (s.startsWith("ProcInstr"))
s += ", ";
else
s += ": ";

Web-

Rendered by www.RenderX.com
Стр. 194 из 626 Document Object Model

//

//
String t = domNode.getNodeValue().trim();
int x = t.indexOf(");
if (x >= 0) t = t.substring(0, x);
s += t;
}
return s;
}

} // AdapterNode

} // DomEcho
Этот класс объявляет переменную для хранения DOM-узла и требует указания его в
аргументе конструктора. Затем он определяет операцию toString, которая возвращает тип
узла из массива String, и добавляет к нему дополнительную информацию из узла для
дальнейшей его идентификации.
Как видно из таблицы типов узлов в org.w3c.dom.Node, каждый узел имеет тип, имя и
значение, которые могут быть пустыми. В тех случаях, когда имя узла начинается с "#",
это поле дублирует тип узла, поэтому нет смысла включать его. Это объясняет присутствие
строк:

if (! nodeName.startsWith("#")) {
s += ": " + nodeName;
}
Оставшаяся часть метода toString также заслуживает некоторого внимания. Например,
следующие строки:

if (s.startsWith("ProcInstr"))
s += ", ";
else
s += ": ";
просто обеспечивают немного "синтаксических украшений". Поле типа для инструкций
обработки заканчивается двоеточием (:), поэтому эти строки предотвращают дублирование
двоеточий.
Другими интересными строчками являются:

String t = domNode.getNodeValue().trim();
int x = t.indexOf(");

Web-

Rendered by www.RenderX.com
Отображение DOM-иерархии Стр. 195 из 626

if (x >= 0) t = t.substring(0, x);


s += t;
Эти строки сокращают значение поля до первого символа новой строки (перевода строки).
Если оставить эти строки неизменными, вы увидите странные символы в JTree (обычно
квадратики).
Примечание: Вспомните, что стандарт XML предусматривает, что все окончания строк
нормализуются в символы новой строки, независимо от системы, из которой пришли
данные. Это упрощает программирование.
Оформление DomNode и возврат желаемой строки являются основными функциями
AdapterNode. Но, поскольку адаптер TreeModel должен отвечать на такие вопросы как
"Сколько потомков имеет данный узел?" и выполнять такие команды как "Получить N-го
потомка данного узла", полезно определить несколько дополнительных методов. (Адаптер
всегда может получить доступ к узлу DOM и получить эту информацию самостоятельно,
но поступая таким образом, мы в большей степени придерживаемся принципов
инкапсуляции.)
Добавьте выделенный ниже код для возврата индекса указанного потомка, потомка,
соответствующего заданному индексу, и количества потомков:

public class AdapterNode


{
...
public String toString() {
...
}

public int index(AdapterNode child) {


//System.err.println("Looking for index of " + child);
int count = childCount();
for (int i=0; i<count; i++) {
AdapterNode n = this.child(i);
if (child == n) return i;
}
return -1; // .
}

public AdapterNode child(int searchIndex) {


// : JTree
.
org.w3c.dom.Node node =
domNode.getChildNodes().item(searchIndex);
return new AdapterNode(node);

Web-

Rendered by www.RenderX.com
Стр. 196 из 626 Document Object Model

public int childCount() {


return domNode.getChildNodes().getLength();
}

} // AdapterNode

} // DomEcho
Примечание: Во время разработки только после написания адаптера TreeModel я понял,
что это необходимо, и добавил этот код. Через некоторое время вы увидите, почему.

8.3.3.2. Определение адаптера TreeModel


Теперь, наконец, вы готовы к рзработке адаптера TreeModel. Одним из действительно
хороших моментов в модели JTree является легкость преобразования существующего
дерева для отображения. Одной из причин этого является четкое разделение между
объектом отображения, который используется в JTree, и объектом модификации, который
используется в приложении. Более подробно о таком разделении рассказано в статье
"Understanding the TreeModel", которая расположена по адресу http://java.sun.com/prod-
ucts/jfc/tsc/articles/jtree/index.html. А сейчас важно отметить, что для интерфейса TreeModel
нам необходимо только (a) предоставить методы для доступа к потомкам и отчета по ним
и (b) зарегистрировать соответствующий перехватчик JTree, так чтобы он знал, когда
обновить просматриваемую информацию при изменении модели.
Добавьте выделенный ниже код для создания адаптера TreeModel и определения методов
обработки потомков:

...
} // AdapterNode

// Document
(DOM)
// JTree.
public class DomToTreeModelAdapter implements
javax.swing.tree.TreeModel
{
// TreeModel
public Object getRoot() {
//System.err.println("Returning root: " +document);
return new AdapterNode(document);
}

public boolean isLeaf(Object aNode) {

Web-

Rendered by www.RenderX.com
Отображение DOM-иерархии Стр. 197 из 626

// , .
// true ,

AdapterNode node = (AdapterNode) aNode;


if (node.childCount() > 0) return false;
return true;
}

public int getChildCount(Object parent)


AdapterNode node = (AdapterNode) parent;
return node.childCount();
}

public Object getChild(Object parent, int index) {


AdapterNode node = (AdapterNode) parent;
return node.child(index);
}

public int getIndexOfChild(Object parent, Object child) {


AdapterNode node = (AdapterNode) parent;
return node.index((AdapterNode) child);
}

public void valueForPathChanged(


TreePath path, Object newValue)
{
// Null.
GUI
// ,
,
//

// TreeNodesChanged.
}

} // DomToTreeModelAdapter

} // DomEcho

Web-

Rendered by www.RenderX.com
Стр. 198 из 626 Document Object Model

В этом коде метод getRoot возвращает корневой узел DOM, оформленный как объект
AdapterNode. После этого все узлы, возвращаемые адаптером, будут иметь тип
AdapterNodes, который включает в себя DOM-узлы. По этому признаку, когда бы мы ни
спросили JTree о потомках данного узла, о количестве потомков и т.д., JTree будет
передавать нам AdapterNode. Мы знаем это, поскольку контролируем каждый узел в JTree,
начиная с корневого узла.
JTree использует метод isLeaf для определения необходимости отобразить иконку
открытия/закрытия слева от узла. Этот метод возвращает true только если узел не имеет
потомков. Здесь мы выполняем преобразование типа оригинального объекта JTree в объект
AdapterNode, каким он и должен быть. Мы знаем, что передается объект адаптера, но
интерфейс, вообще говоря, определяет объекты, поэтому мы должны выполнить
преобразование.
Следующие три метода возвращают количество потомков указанного узла, потомка,
имеющего заданный индекс, и индекс указанного потомка соответственно. Это все
достаточно просто.
Последний метод вызывается тогда, когда пользователь меняет значение, сохраненное
в JTree. В этом приложении мы не будем поддерживать это. Но если бы мы включили
такую возможность, приложение должно было бы сделать изменение в модели и затем
проинформировать какой-либо перехватчик событий об этом изменении. (JTree может
быть не единственным перехватчиком. Во многих приложениях это в действительности
так.)
Для информирования перехватчиков событий об изменении, необходимо зарегистрировать
их. Это ведет нас к рассмотрению последних двух методов для реализации в интерфейсе
TreeModel. Добавьте выделенный ниже код для их определения:

public class DomToTreeModelAdapter ...


{
...
public void valueForPathChanged(
TreePath path, Object newValue)
{
...
}
private Vector listenerList = new Vector();
public void addTreeModelListener(
TreeModelListener listener ) {
if ( listener != null
&& ! listenerList.contains(listener) ) {
listenerList.addElement( listener );
}
}

public void removeTreeModelListener(

Web-

Rendered by www.RenderX.com
Отображение DOM-иерархии Стр. 199 из 626

TreeModelListener listener )
{
if ( listener != null ) {
listenerList.removeElement( listener );
}
}

} // DomToTreeModelAdapter
Поскольку приложение не будет делать изменений в дереве, эти методы пока не будут
использоваться. Однако вы можете использовать их в будущем при необходимости.
Примечание: Этот пример использует Vector, так что будет работать с приложениями
версии 1.1. Если работать с версиями 1.2 или более поздними, я бы использовал вместо
них отличную коллекцию: private LinkedList listenerList=new LinkedList();
Операциями над List являются add и remove. Для прохода по списку можно использовать
следующие операции:

Iterator it = listenerList.iterator();
while ( it.hasNext() ) {
TreeModelListener listener = (TreeModelListener) it.next();
...
}
Приведем, также, несколько необязательных методов, которые вы не будете использовать
в этом приложении. К этому времени вы построили работающий шаблон для адаптера
TreeModel. В интересах полноты вы, возможно, захотите добавить выделенный ниже код.
Вы можете затем вызывать его всегда, когда необходимо будет проинформировать
перехватчики JTree об изменениях:

public void removeTreeModelListener(


TreeModelListener listener)
{
...
}

public void fireTreeNodesChanged( TreeModelEvent e ) {


Enumeration listeners = listenerList.elements();
while ( listeners.hasMoreElements() ) {
TreeModelListener listener =
(TreeModelListener) listeners.nextElement();
listener.treeNodesChanged( e );
}
}

Web-

Rendered by www.RenderX.com
Стр. 200 из 626 Document Object Model

public void fireTreeNodesInserted( TreeModelEvent e ) {


Enumeration listeners = listenerList.elements();
while ( listeners.hasMoreElements() ) {
TreeModelListener listener =
(TreeModelListener) listeners.nextElement();
listener.treeNodesInserted( e );
}
}

public void fireTreeNodesRemoved( TreeModelEvent e ) {


Enumeration listeners = listenerList.elements();
while ( listeners.hasMoreElements() ) {
TreeModelListener listener =
(TreeModelListener) listeners.nextElement();
listener.treeNodesRemoved( e );
}
}

public void fireTreeStructureChanged( TreeModelEvent e ) {


Enumeration listeners = listenerList.elements();
while ( listeners.hasMoreElements() ) {
TreeModelListener listener =
(TreeModelListener) listeners.nextElement();
listener.treeStructureChanged( e );
}
}

} // DomToTreeModelAdapter
Примечание: Эти методы взяты из класса TreeModelSupport, описанного в статье "Under-
standong the TreeModel". Эта архитектура разработана Tom Santos и Steve Wilson, и является
намного более элегантной, чем описанная здесь. Поэтому стоит поместить эти методы
сюда, чтобы они были всегда под рукой, когда возникнет необходимость.

8.3.4. Завершение
К данному моменту вы в основном сделали все. Все что вам нужно - возвратиться назад
к конструктору и добавить код для построения адаптера и передачи его в JTree как
TreeModel:

//
JTree tree = new JTree(new DomToTreeModelAdapter());

Web-

Rendered by www.RenderX.com
Исследование структуры DOM Стр. 201 из 626

8.4. Исследование структуры DOM


В этом разделе вы будете использовать GUI-приложение DomEcho, созданное в
предыдущем разделе, для визуального исследования DOM. Вы увидите, какие узлы
составляют DOM и как они располагаются. После того как вы разберетесь со структурой,
вы будете хорошо подготовлены к созданию и модификации структур DOM в будущем.

8.4.1. Отображение простого дерева


Мы начнем с отображения простого файла, чтобы дать вам общее представление о
структуре DOM. Затем мы посмотрим на структуру, которая получается при использовании
более сложных XML-элементов.
Примечание: Код, используемый в данном разделе, находится в файле DomEcho02.java.
Отображаемый файл - slideSample01.xml. (Версия для броузера - slideSample01-xml.html.)
На рисунке 1 изображено дерево, которое вы увидите после выполнения программы
DomEcho с первым, созданным вами, XML-файлом.

Рисунок 1 Отображаемые узлы Document, Comment и Element.

Вспомните, что первой частью отображаемого текста для каждого узла является тип
элемента. Затем идет имя элемента, если оно есть, и его значение. Для узла Comment
отображается атрибут value, тогда как для узла Element отображается атрибут name -
"slideshow".
Сравните рисунок 1 с кодом в методе AdapterNode toString, чтобы увидеть, имя или значение
отображается для конкретного узла. Если вы хотите сделать выводимую информацию
более понятной, можно указывать отображаемое свойство (например, N: name, V: value).
При расширении элемента slideshow отобразится информация, показанная на рисунке 2.

Web-

Rendered by www.RenderX.com
Стр. 202 из 626 Document Object Model

Рисунок 2 Узел Element расширен, узлы атрибутов не показаны.

Здесь вы можете увидеть узлы Text и Comment, разбросанные между элементами Slide.
Пустые узлы Text появляются потому, что нет DTD для информирования анализатора об
отсутствии текста. (Вообще говоря, подавляющим большинством узлов DOM-дерева будут
узлы Element и Text.)
Это важно!
Текстовые узлы существуют в DOM под узлами элементов, а данные всегда хранятся в
текстовых узлах. Возможно, наиболее общая ошибка при обработке DOM является
перемещение на узел элемента и ожидание того, что он содержит хранимые в нем данные.
Это не так! Даже самый простой узел элемента имеет свой текстовый узел. Например, в
<size>12</size> имеется узел элемента (size) и текстовый узел в нем, содержащий
фактические данные (12).
На этом рисунке совершенно отсутствуют узлы Attribute. Из таблицы org.w3c.dom.Node
видно, что существует тип узла Attribute. Но они не включены как потомки в иерархию DOM.
Доступ к ним можно получить при помощи вызова метода getAttributes интерфейса Node.
Примечание: Отображение текстовых узлов является причиной включения приведенных
ниже строк кода в методе toString AdapterNode. Если вы удалите их, вы увидите непонятные
символы (обычно квадратики), генерируемые символами новой строки, находящимися в
тексте.

String t = domNode.getNodeValue().trim();
int x = t.indexOf(");
if (x >= 0) t = t.substring(0, x);
s += t;

8.4.2. Отображение более сложного дерева


Сейчас вы отобразите пример XML-файла, созданного вами в конце руководства по SAX,
для того чтобы посмотреть, как выглядят в DOM ссылки на сущность, инструкции обработки
и секции CDATA.

Web-

Rendered by www.RenderX.com
Исследование структуры DOM Стр. 203 из 626

Примечание: Обрабатываемый в этом разделе файл называется slideSample10.xml.


slideSample10.xml обращается к slideshow3.dtd, который, в свою очередь, обращается к
copyright.xml и (очень упрощенно) к xhtml.dtd. (Версии для броузера - slideSample10-xml.html,
slideshow3-dtd.html, copyright-xml.html и xhtml-dtd.html.)
На рисунке 3 показан результат выполнения приложения DomEcho с файлом slideSam-
ple10.xml, который имеет запись DOCTYPE для указания DTD документа.

Рисунок 3 Отобразился узел DocType.

Интерфейс DocType является фактически расширением w3c.org.dom.Node. Он определяет


метод getEntities, который вы можете использовать для получения узлов Entity - узлов,
определяющих такие сущности, как product, которая имеет значение "WonderWidgets".
Подобно узлам Attribute, узлы Entity не появляются как потомки узлов DOM.
При расширении узла slideshow отобразится информация, показанная на рисунке 4.

Рисунок 4 Отобразился узел инструкции обработки.

Web-

Rendered by www.RenderX.com
Стр. 204 из 626 Document Object Model

Здесь выделен узел инструкции обработки. Свойство name содержит спецификацию


назначения, которая идентифицирует приложение, для которого предназначена инструкция.
Свойство value содержит текст инструкции.
Обратите внимание, что пустые текстовые узлы также показаны, хотя в DTD указано, что
slideshow может содержать только элементы slide и никогда текст. Логически вы можете
думать, что такие узлы никогда не появятся. (При запуске этого файла с SAX-анализатором
эти элементы генерируют события ignorableWhitespace, а не character.)
При расширении второго элемента slide и открытии элемента item в нем отображается
информация, показанная на рисунке 5.

Рисунок 5 JAXP 1.2 DOM - Текстовый элемент, возвращенный из ссылки на сущность.

Вы можете заметить, что в DOM был вставлен текстовый узел, содержащий информацию
об авторских правах, а не ссылка на сущность, указывающая на нее.
Для большинства приложений вставка текста - это именно то что вам нужно. При этом во
время просмотра текста узла вам не нужно беспокоиться о ссылках на сущность, которые
он может содержать.
Для других приложений, однако, вам может понадобиться способность восстанавливать
оригинальный XML. Например, редактор должен сохранять результаты пользовательских
изменений без замены ссылок на сущность.
Различные DocumentBuilderFactory API дают вам контроль над типом создаваемой DOM-
структуры. Например, добавьте выделенный ниже код для создания DOM-структуры,
показанной на рисунке 6.

public static void main(String argv[])


{
...
DocumentBuilderFactory factory =
DocumentBuilderFactory.newInstance();
factory.setExpandEntityReferences(true);
...

Web-

Rendered by www.RenderX.com
Исследование структуры DOM Стр. 205 из 626

Рисунок 6 JAXP 1.1 на платформе 1.4 - отобразился узел ссылки на сущность.

Здесь выделен узел ссылки на сущность. Обратите внимание, что ссылка на сущность
содержит несколько узлов. Этот пример показывает только узлы комментариев и текстовые
узлы, но сущность может потенциально содержать также другие узлы элементов.
Наконец, при перемещении вниз к последнему элементу item в последнем slide отобразится
информация, показанная на рисунке 7.

Рисунок 7 Отобразился узел CDATA.

Здесь выделен узел CDATA. Обратите внимание, что он не содержит узлов. Поскольку
секция CDATA полностью неинтерпретируемая, все ее содержимое содержится в свойстве
value.

8.4.2.1. Резюме по лексическим управляющим элементам


Лексическая информация представляет собой информацию, необходимую для
восстановления оригинального синтаксиса XML-документа. Как было рассмотрено ранее,
сохранение лексической информации является очень важным для приложений - редакторов,
в которых нужно сохранять документ, являющийся точным отражением оригинала

Web-

Rendered by www.RenderX.com
Стр. 206 из 626 Document Object Model

(полностью с комментариями, ссылками на сущности и всеми секциями CDATA, которые


могут в нем содержаться).
Большинство приложений, однако, имеют дело только с содержимым XML-структур. Они
могут игнорировать комментарии и им все равно, закодированы ли данные в секции CDATA
как обычный текст, или встретилась ссылка на сущность. Для таких приложений желателен
минимум лексической информации, поскольку это упрощает количество и тип DOM-узлов,
которые приложение может встретить при обработке.
Следующие методы DocumentBuilderFactory дают вам контроль над лексической
информацией, которую вы видите в DOM:
• setCoalescing()
Для преобразования узлов CDATA в узел Text и добавления его в смежный узел Text
(если он существует).
• setExpandEntityReferences()
Для расширения узлов ссылок на сущность.
• setIgnoringComments()
Для игнорирования комментариев.
• setIgnoringElementContentWhitespace()
Для игнорирования пробелов в содержимом элемента.
Значением по умолчанию для всех этих свойств является false. В таблице 2таблице 2
показаны установки, необходимые для сохранения всей лексической информации,
требуемой для восстановления оригинального документа. В ней также показаны настройки
для обработки простейшей DOM, чтобы приложение могло сконцентрироваться на
семантическом содержимом данных, не заботясь о лексических деталях.
Таблица 2 Настройка DocumentBuilderFactory
API Сохранение лексической информации Концентрация на содержимом
setCoalescing() false true
setExpandedEntityReferences() true false
setIgnoringComments() false true
setIgnoringElementContentWhitespace() false true

8.4.3. Завершение
Вы увидели большинство узлов, которые можно встретить в дереве DOM. Существует
еще один или два, которые мы обсудим в следующем разделе, но вы уже знаете все, что
вам необходимо для создания или изменения DOM-структуры. В следующем разделе вы
узнаете, как преобразовать DOM в подходящий для интерактивного GUI объект JTree.
Или, если вы хотите, можете пропустить следующие разделы до 5-ой главы руководства
по DOM "Создание и управление DOM", где вы научитесь создавать DOM с нуля.

Web-

Rendered by www.RenderX.com
Создание дружественного пользователю JTree из DOM Стр. 207 из 626

8.5. Создание дружественного пользователю JTree из DOM


Теперь, когда вы знаете, как выглядит DOM изнутри, вы лучше подготовлены для изменения
DOM или создания ее с нуля. Но перед этим в данном разделе представлены некоторые
изменения в JTreeModel, которые позволят вам создать более дружественную пользователю
версию JTree, подходящую для использования в GUI.

8.5.1. Сжатие просматриваемого дерева


Отображение DOM в форме дерева очень хорошо подходит для экспериментирования и
изучения работы DOM. Но оно не очень подходит на роль "дружественного" экрана, который
хотело бы видеть большинство пользователей в JTree. Необходимо некоторое количество
изменений для превращения адаптера TreeModel в что-то более дружественное
пользователю. В этом разделе вы сделаете эти изменения.
DomEcho03.javaslideSample01.xmlslideSample01-xml.html

8.5.1.1. Сделать операции выбираемыми


При изменении адаптера мы сожмем область просмотра DOM, удаляя все узлы, кроме
тех, которые мы действительно хотим отобразить. Начнем с определения логической
переменной, которая управляет тем, расширенная или сжатая информация должна
появляться на экране:

public class DomEcho extends JPanel


{
static Document document;
boolean compress = true;
static final int windowHeight = 460;
...

8.5.1.2. Идентификация узлов дерева


Следующим действием является идентификация узлов, которые мы хотим отобразить в
дереве. Для этого добавьте выделенный ниже код:

...
import org.w3c.dom.Document;
import org.w3c.dom.DOMException;
import org.w3c.dom.Node;

public class DomEcho extends JPanel


{
...

public static void makeFrame() {


...

Web-

Rendered by www.RenderX.com
Стр. 208 из 626 Document Object Model

// DOM
static final String[] typeName = {
...
};

static final int ELEMENT_TYPE = Node.ELEMENT_NODE;

//

static String[] treeElementNames = {


"slideshow",
"slide",
"title", // slideshow #1
"slide-title", // slideshow #10
"item",
};

boolean treeElement(String elementName) {


for (int i=0; i<treeElementNames.length; i++) {
if ( elementName.equals(treeElementNames[i]) )
return true;
}
return false;
}
С помощью этого кода вы установили константу, которую можно использовать для
идентификации типа узлов ELEMENT, объявили названия элементов, которые нужно
отобразить в дереве, и создали метод, определяющий, является или нет указанный элемент
"элементом дерева". Поскольку slideSample01.xml имеет элементы title и slideSample10.xml
имеет элементы slide-title, вы установили содержимое этих массивов, так что программа
будет работать с обоими файлами данных.
Примечание: Создаваемый вами механизм рассчитан на то, что узлы структуры, такие как
slideshow и slide никогда не будут содержать текст, в то время как текст обычно появляется
в таких узлах содержимого как item. Хотя эти узлы "содержимого" могут содержать
субэлементы в slideShow10.xml, DTD указывает, чтобы эти субэлементы были XHTML-
узлами. Поскольку они являются XHTML-узлами (XML-версия HTML, являющаяся
формально-правильной), полная подструктура узла item может быть скомбинирована в
одну строку и отображена в htmlPane, составляющей вторую половину окна приложения.
Во второй части этого раздела вы сделаете это объединение, отобразив текст и XHTML
как содержимое в htmlPane.

Web-

Rendered by www.RenderX.com
Создание дружественного пользователю JTree из DOM Стр. 209 из 626

Хотя вы можете просто обращаться к типам узлов, определенным в классе


org.w3c.dom.Node, определение константы ELEMENT_TYPE сохраняет код немного более
читаемым. Каждый узел в DOM имеет имя, тип и (потенциально) список субузлов.
Функциями, возвращающими эти значения, являются getNodeName(), getNodeType() и
getChildNodes(). Определение нашей собственной константы позволит нам написать такой
код:

Node node = nodeList.item(i);


int type = node.getNodeType();
if (type == ELEMENT_TYPE) {
....
Дополнительные константы поясняют читателю (и нам самим!), что же мы делаем. Здесь
же совершенно ясно, когда мы имеем дело с объектом узла, а когда с константой типа. В
противном случае мог бы существовать соблазн написать код, например if
(node==ELEMENT_NODE), который, естественно, вообще работать не будет.

8.5.1.3. Управление видимостью узла


Следующее действие - изменение функции AdapterNode childCount, которая считала бы
узлы "элементов дерева" (узлы, которые будут отображаться в Jtree). Для этого выполните
выделенные ниже изменения:

public class DomEcho extends JPanel


{
...
public class AdapterNode
{
...
public AdapterNode child(int searchIndex) {
...
}
public int childCount() {
if (!compress) {
//
return domNode.getChildNodes().getLength();
}
int count = 0;
for (int i=0;
i<domNode.getChildNodes().getLength(); i++)
{
org.w3c.dom.Node node =
domNode.getChildNodes().item(i);
if (node.getNodeType() == ELEMENT_TYPE

Web-

Rendered by www.RenderX.com
Стр. 210 из 626 Document Object Model

&& treeElement( node.getNodeName() ))


{
++count;
}
}
return count;
}
} // AdapterNode
Единственной хитростью в этом коде является проверка принадлежности узла узлу
элемента перед их сравнением. Узел DocType делает это необходимым, поскольку имеет
такое же имя "slideshow", что и элемент slideshow.

8.5.1.4. Управление доступом к потомку


Наконец, необходимо изменить функцию AdapterNode Child для возврата N-ого элемента
из списка отображаемых узлов, а не из полного списка узлов. Для этого добавьте
выделенный ниже код:

public class DomEcho extends JPanel


{
...
public class AdapterNode
{
...
public int index(AdapterNode child) {
...
}
public AdapterNode child(int searchIndex) {
// : JTree index
.
org.w3c.dom.Node node =
domNode.getChildNodes()Item(searchIndex);
if (compress) {
// N-
int elementNodeIndex = 0;
for (int i=0;
i<domNode.getChildNodes().getLength(); i++)
{
node = domNode.getChildNodes()Item(i);
if (node.getNodeType() == ELEMENT_TYPE
&& treeElement( node.getNodeName() )
&& elementNodeIndex++ == searchIndex) {

Web-

Rendered by www.RenderX.com
Создание дружественного пользователю JTree из DOM Стр. 211 из 626

break;
}
}
}
return new AdapterNode(node);
} //
} // AdapterNode
Здесь нет ничего особенного. Это немного измененная версия той же логики, которую вы
использовали для возврата количества потомков.

8.5.1.5. Проверка результатов


После компиляции и выполнения этой версии приложения с slideSample01.xml и расширения
дерева вы увидите результаты, показанные на рисунке 8. Единственными узлами,
оставшимися в дереве, являются высокоуровневые "структурные" узлы.

Рисунок 8 Дерево со сжатой иерархией.

8.5.1.6. Дополнительные действия


В данном примере информация, указывающая приложению, как сжимать дерево для
просмотра, является "жестко закодированной". Вот некоторые советы по расширению
приложения:
Используйте аргументы командной строки
Сжимать дерево или нет можно задавать в аргументе командной строки, а не в логической
переменной. С другой стороны, список отображаемых в дереве элементов все еще задан
жестко в программе, поэтому этот совет, возможно, не имеет большого смысла, пока вы
не выполните следующий совет.
Читайте список treeElement из файла
Если вы читаете список отображаемых в дереве элементов из внешнего файла, это может
сделать все приложение управляемым командами. Это было бы неплохо. Но не будет ли
действительно прекрасно получить эту информацию из DTD или схемы? Так что вы, может
быть, захотите выполнить следующий совет.

Web-

Rendered by www.RenderX.com
Стр. 212 из 626 Document Object Model

Автоматически создавайте список


Будьте осторожны! При сегодняшнем состоянии дел нет стандартных DTD-анализаторов!
Следовательно, если вы используете DTD, для разбора его несколько загадочного
синтаксиса вы должны написать свой собственный анализатор. Вам, возможно, больше
повезет при использовании схемы, а не DTD. Приятным моментом в схемах является
использование ими синтаксиса XML, то есть, вы можете использовать XML-анализатор
для чтения схемы также как и для чтения любого другого файла.
Как только вы проанализировали схему, обратите внимание, что отображаемые в JTree
узлы структуры не имеют текста, а узлы содержимого могут содержать текст и,
необязательно, XHTML-субузлы. Это различие работает для данного примера, и, возможно,
будет работать для больших реальных приложений. Довольно легко создать условия,
которые приведут к проблеме, так что вам придется искать спецификации схемы/DTD,
которые содержат не XHTML-элементы в текстовых элементах, и выполнять
соответствующие действия.

8.5.2. Действия с выбранными узлами дерева


Теперь, когда дерево правильно отображается, следующее действия - объединить части
дерева в выбранных узлах для отображения их в htmlPane. После этого вы будете
использовать объединенный текст для помещения идентификационной информации об
узле обратно в JTree.
Примечание: Код этого раздела находится в файле DomEcho04.java.

8.5.2.1. Идентификация типов узлов


При объединении всех узлов элемента его обработка зависит от типа узла. Поэтому первым
действием должно быть определение констант для оставшихся типов узлов. Добавьте
выделенный ниже код:

public class DomEcho extends JPanel


{
...
// DOM
static final String[] typeName = {
...
};
static final int ELEMENT_TYPE = 1;
static final int ATTR_TYPE = Node.ATTRIBUTE_NODE;
static final int TEXT_TYPE = Node.TEXT_NODE;
static final int CDATA_TYPE = Node.CDATA_SECTION_NODE;
static final int ENTITYREF_TYPE =
Node.ENTITY_REFERENCE_NODE;
static final int ENTITY_TYPE = Node.ENTITY_NODE;
static final int PROCINSTR_TYPE =
Node.PROCESSING_INSTRUCTION_NODE;

Web-

Rendered by www.RenderX.com
Создание дружественного пользователю JTree из DOM Стр. 213 из 626

static final int COMMENT_TYPE = Node.COMMENT_NODE;


static final int DOCUMENT_TYPE = Node.DOCUMENT_NODE;
static final int DOCTYPE_TYPE = Node.DOCUMENT_TYPE_NODE;
static final int DOCFRAG_TYPE =
Node.DOCUMENT_FRAGMENT_NODE;
static final int NOTATION_TYPE = Node.NOTATION_NODE;

8.5.2.2. Объединение узлов для определения содержимого элемента


Далее вы должны определить метод, объединяющий текст и подчиненные узлы элемента
и возвращающий его как "содержимое" элемента. Для создания метода content вы должны
добавить большой фрагмент кода, выделенного ниже, но это последний большой фрагмент
в руководстве по DOM!

public class DomEcho extends JPanel


{
...
public class AdapterNode
{
...
public String toString() {
...
}
public String content() {
String s = "";
org.w3c.dom.NodeList nodeList =
domNode.getChildNodes();
for (int i=0; i<nodeList.getLength(); i++) {
org.w3c.dom.Node node = nodeList.item(i);
int type = node.getNodeType();
AdapterNode adpNode = new AdapterNode(node);
if (type == ELEMENT_TYPE) {
if ( treeElement(node.getNodeName()) )
continue;
s += "<" + node.getNodeName() + ">";
s += adpNode.content();
s += "</" + node.getNodeName() + ">";
} else if (type == TEXT_TYPE) {
s += node.getNodeValue();
} else if (type == ENTITYREF_TYPE) {
// TEXT

s += adpNode.content();

Web-

Rendered by www.RenderX.com
Стр. 214 из 626 Document Object Model

} else if (type == CDATA_TYPE) {


StringBuffer sb = new StringBuffer(
node.getNodeValue() );
for (int j=0; j<sb.length(); j++) {
if (sb.charAt(j) == '<') {
sb.setCharAt(j, '&');
sb.insert(j+1, "lt;");
j += 3;
} else if (sb.charAt(j) == '&') {
sb.setCharAt(j, '&');
sb.insert(j+1, "amp;");
j += 4;
}
}
s += "<pre>" + sb + "</pre>";
}
}
return s;
}
...
} // AdapterNode
Примечание: Этот код расширяет узлы EntityRef, которые вставляет анализатор JAXP 1.1,
включенный в платформу Java 1.4. При использовании JAXP 1.2 эта часть кода не нужна,
поскольку ссылки на сущность преобразуются анализатором в текстовые узлы
автоматически. Другие анализаторы, однако, могут также вставлять такие узлы, поэтому
включение этого кода защищает ваше приложение для будущих применений, если вы
решите использовать другой анализатор.
Хотя этот код не самый эффективный - он работает и подходит для наших целей. В этом
коде вы работаете со следующими типами данных:
Element
Для элементов подобных узлу XHTML "em" вы возвращаете содержимое узла послойно
между соответствующими тегами <em> и </em>. Однако при обработке, например,
содержимого элемента slideshow вы не включаете теги для элементов slide, которые он
содержит, то есть, при возврате содержимого узла вы пропускаете все субэлементы,
которые сами отображаются в дереве.
Text
Здесь никаких неожиданностей. Для текстовых узлов вы просто возвращаете value узла.
Entity Reference
В отличие от узлов CDATA, ссылки на сущность могут содержать несколько субэлементов.
Поэтому стратегия заключается в объединении этих субэлементов.
CDATA

Web-

Rendered by www.RenderX.com
Создание дружественного пользователю JTree из DOM Стр. 215 из 626

Так же как и для текстового узла, вы возвращаете value узла. Однако, поскольку текст в
данном случае может содержать угловые скобки и амперсанды, вы должны преобразовать
их в форму, правильно отображающую их в HTML-области. В отличие от тега XML CDATA
тег HTML <pre> не гарантирует отсутствие анализа тегов форматирования, разделителей
и т.д. Поэтому вы должны преобразовать левые угловые скобки (<) и амперсанды (&) для
их корректного отображения.
С другой стороны, существует небольшое количество типов, которые вы не обрабатываете
в вышеприведенном коде. Стоит потратить немного времени на их рассмотрение, чтобы
понять почему:
Attribute
Эти узлы не появляются в DOM, а возвращаются при вызове getAttribute узлов элемента.
Entity
Эти узлы тоже не появляются в DOM, а возвращаются при вызове getEntities узлов DocType.
Processing Instruction
Эти узлы не содержат отображаемых данных.
Comment
То же самое. Ничего нет, чтобы вы хотели отображать.
Document
Это корневой элемент DOM. Нет данных для отображения.
DocType
Узел DocType содержит спецификацию DTD с внешними указателями, или без них. Он
появляется только в корневом узле и не имеет данных для отображения в дереве.
Document Fragment
Этот узел эквивалентен узлу Document. Это - корневой элемент, который спецификация
DOM использует для хранения промежуточных результатов, например, при операциях
вырезания/вставки. Подобно узлу Document не содержит отображаемых данных.
Notation
Мы игнорируем этот узел. Он предназначен для включения двоичных данных в DOM. Как
обсуждалось ранее в разделах "Ссылка на двоичные сущности" и "Использование
DTDHandler и EntityResolver" типы MIME (вместе с пространствами имен) предлагают
лучший механизм для этого.

8.5.2.3. Отображение содержимого в JTree


Осталось совсем мало действий по программированию. Прежде всего, измените toString,
чтобы в нем использовалось содержимое узла для идентификационной информации.
Добавьте выделенный ниже текст:

public class DomEcho extends JPanel


{
...

Web-

Rendered by www.RenderX.com
Стр. 216 из 626 Document Object Model

public class AdapterNode


{
...
public String toString() {
...
if (! nodeName.startsWith("#")) {
s += ": " + nodeName;
}
if (compress) {
String t = content().trim();
int x = t.indexOf(");
if (x >= 0) t = t.substring(0, x);
s += " " + t;
return s;
}
if (domNode.getNodeValue() != null) {
...
}
return s;
}

8.5.2.4. Соединение JTree и JEditorPane


Возвращаясь к конструктору приложения, создайте перехватчик выбора дерева и
используйте его для соединения JTree и JEditorPane:

public class DomEcho extends JPanel


{
...
public DomEcho()
{
...
//
JEditorPane htmlPane = new JEditorPane("text/html","");
htmlPane.setEditable(false);
JScrollPane htmlView = new JScrollPane(htmlPane);
htmlView.setPreferredSize(
new Dimension( rightWidth, windowHeight ));

tree.addTreeSelectionListener(
new TreeSelectionListener() {
public void valueChanged(TreeSelectionEvent e)

Web-

Rendered by www.RenderX.com
Создание дружественного пользователю JTree из DOM Стр. 217 из 626

{
TreePath p = e.getNewLeadSelectionPath();
if (p != null) {
AdapterNode adpNode =
(AdapterNode)
p.getLastPathComponent();
htmlPane.setText(adpNode.content());
}
}
}
);
Теперь при выборе узла JTree его содержимое передается в htmlPane.
Примечание: TreeSelectionListener в этом примере создается при помощи анонимного
внутреннего класса адаптера. При программировании для платформы версии 1.1
необходимо использовать внешний класс.
При компиляции этой версии приложения вы сразу обнаружите, что htmlPane должна быть
указана как final для ссылки во внутреннем классе, поэтому добавьте выделенное ниже
ключевое слово:

public DomEcho04()
{
...
//
final JEditorPane htmlPane = new
JEditorPane("text/html","");
htmlPane.setEditable(false);
JScrollPane htmlView = new JScrollPane(htmlPane);
htmlView.setPreferredSize(
new Dimension( rightWidth, windowHeight ));

8.5.2.5. Выполнение приложения


После компиляции и выполнения приложения с файлом slideSample10.xml (версия для
броузера - slideSample10-xml.html), отобразится информация, показанная на рисунке 9.
Расширение иерархии показывает, что JTree сейчас включает идентифицирующий текст
для узла везде, где это возможно.

Web-

Rendered by www.RenderX.com
Стр. 218 из 626 Document Object Model

Рисунок 9 Сжатая иерархия с отображением текста в узлах.

Выбор элемента, включающего подчиненные XHTML-элементы, вызывает отображение


информации, показанной на рисунке 10:

Рисунок 10 Узел с выбранным тегом <em>.

Выбор элемента, включающего ссылку на сущность, вызывает отображение информации,


показанной на рисунке 11:

Web-

Rendered by www.RenderX.com
Создание дружественного пользователю JTree из DOM Стр. 219 из 626

Рисунок 11 Узел с выбранной ссылкой на сущность.

Наконец, выбор элемента, включающего секцию CDATA, вызывает отображение


информации, показанной на рисунке 12:

Рисунок 12 Узел с выбранным компонентом CDATA.

8.5.2.6. Дополнительное задание


Теперь, когда вы имеете работающее приложение, есть несколько моментов, над которыми
можно подумать, чтобы улучшить приложение в будущем:
Используйте текст для идентификации слайдов
При обработке элемента slide содержимое его узла node можно использовать как
идентифицирующий текст. При его выборе преобразуйте содержимое узла title в
отцентрированный тег H1 и игнорируйте элемент title при создании дерева.
Преобразуйте элементы item в список
Удалите элементы item из JTree и преобразуйте их в HTML-список при помощи тегов <ul>,
<li>, </ul>, включая их в содержимое слайда при его выборе.

Web-

Rendered by www.RenderX.com
Стр. 220 из 626 Document Object Model

8.5.3. Обработка изменений


Полное обсуждение механизмов изменения JTree выходит за рамки данного учебника.
Однако несколько слов по этому предмету мы выскажем.
Самое главное, обратите внимание на то, что если вы разрешите пользователю изменять
структуру при помощи изменения JTree, вы должны учитывать сжатие для определения
того, где применить эти изменения. Например, если вы отображаете текст в дереве, и
пользователь изменяет его, изменения должны быть применены к текстовым субэлементам
и, возможно, будет необходимо перерисовать поддерево XHTML.
Когда вы проводите эти изменения, вы должны понимать взаимодействия между JTree,
его TreeModel и лежащей в их основе модели данных. Эта тема глубоко затрагивается в
статье Swing Connection "Unerstanding the TreeModel" по адресу http://java.sun.com/prod-
ucts/jfc/tsc/articles/jtree/index.html.

8.5.4. Завершение
Теперь вы понимаете достаточно много в структуре DOM и знаете, как адаптировать DOM
для создания дружественного пользователю приложения. Это потребовало немного
кодирования, но в результате вы получили ценное средство для отображения структуры
DOM и шаблон GUI-приложения. В следующем разделе вы сделаете несколько изменений
в коде, которые превратят приложение в средство для экспериментов, а затем
поэкспериментируете с созданием и управлением DOM.

8.6. Создание и управление DOM


Вы уже понимаете структуру узлов, составляющих DOM. В действительности DOM создать
легко. В этом разделе руководства вы сделаете намного меньше работы, чем ранее.

8.6.1. Получение DOM из генератора


В этой версии приложения вы все еще будете создавать генератор построителя
приложений, но теперь укажете ему создать новый DOM вместо анализа существующего
XML-документа. Вы сохраните всю существующую функциональность и добавите новую
таким образом, что сможете легко переключиться обратно к анализу.
Примечание: Код этого раздела находится в файле DomEcho05.java.

8.6.1.1. Измененяем код


Начнем с отключения возможности сжатия. При работе с DOM в этом разделе мы будем
отображать все узлы:

public class DomEcho05 extends JPanel


{
...
boolean compress = true;
boolean compress = false;
Далее вам надо создать метод buildDom, в котором создается объект document. Самый
простой путь для этого - создать метод и скопировать часть по построению DOM из метода

Web-

Rendered by www.RenderX.com
Создание и управление DOM Стр. 221 из 626

main для создания buildDom. Показанные ниже изменения необходимы, чтобы настроить
этот код для метода buildDom.

public class DomEcho05 extends JPanel


{
...
public static void makeFrame() {
...
}
public static void buildDom()
{
DocumentBuilderFactory factory =
DocumentBuilderFactory.newInstance();
try {
DocumentBuilder builder =
factory.newDocumentBuilder();
document = builder.parse( new File(argv[0]) );
document = builder.newDocument();
} catch (SAXException sxe) {
...
} catch (ParserConfigurationException pce) {
//

pce.printStackTrace();
} catch (IOException ioe) {
...
}
}
В этом коде вы заменили строку, делающую анализ, строкой, создающей DOM. Затем,
поскольку код больше не анализирует существующий файл, вы удалили исключительные
ситуации, которые больше не генерируются: SAXException и IOException.
И поскольку вы собираетесь работать с объектами Element, добавьте операторы импорта
этого класса в начало программы:

import org.w3c.dom.Document;
import org.w3c.dom.DOMException;
import org.w3c.dom.Element;

8.6.1.2. Создаем узлы Element и Text


Теперь, в качестве вашего первого эксперимента, добавьте операции Document для
создания корневого элемента и нескольких потомков:

Web-

Rendered by www.RenderX.com
Стр. 222 из 626 Document Object Model

public class DomEcho05 extends JPanel


{
...
public static void buildDom()
{
DocumentBuilderFactory factory =
DocumentBuilderFactory.newInstance();
try {
DocumentBuilder builder =
factory.newDocumentBuilder();
document = builder.newDocument();
//
Element root =
(Element)
document.createElement("rootElement");
document.appendChild(root);
root.appendChild(
document.createTextNode("Some") );
root.appendChild(
document.createTextNode(" ") );
root.appendChild(
document.createTextNode("text") );
} catch (ParserConfigurationException pce) {
//

pce.printStackTrace();
}
}
Наконец, измените код проверки списка аргументов в начале метода main для вызова
buildDom и makeFrame вместо генерирования ошибки, как показано ниже:

public class DomEcho05 extends JPanel


{
...
public static void main(String argv[])
{
if (argv.length != 1) {
System.err.println("...");
System.exit(1);
buildDom();

Web-

Rendered by www.RenderX.com
Создание и управление DOM Стр. 223 из 626

makeFrame();
return;
}
Пока это все! Теперь, если вы укажете аргумент, этот файл будет анализироваться, если
нет - будет выполняться экспериментальный код, строящий DOM.

8.6.1.3. Выполните приложение


Откомпилируйте и выполните приложение без аргументов. При этом отобразится
информация, показанная на рисунке 13:

Рисунок 13 Создались узлы Element и Text.

8.6.2. Нормализация DOM


В этом эксперименте вы будете изменять созданный вами DOM путем нормализации его
после построения.
Примечание: Код этого раздела находится в файле DomEcho06.java.
Добавьте выделенный ниже код для нормализации DOM:

public static void buildDom()


{
DocumentBuilderFactory factory =
DocumentBuilderFactory.newInstance();
try {
...
root.appendChild( document.createTextNode("Some") );
root.appendChild( document.createTextNode(" ") );
root.appendChild( document.createTextNode("text") );
document.getDocumentElement().normalize();

Web-

Rendered by www.RenderX.com
Стр. 224 из 626 Document Object Model

} catch (ParserConfigurationException pce) {


...
В этом коде getDocumentElement возвращает корневой узел документа, а операция
normalize манипулирует с деревом этого узла.
На рисунке 14 показана информация, которая отобразится на экране после выполнения
приложения:

Рисунок 14 Узлы Text после нормализации.

Здесь вы можете заметить, что смежные текстовые узлы были соединены в один узел.
Операция нормализации является одной из операций, которую вы обычно будете
использовать после проведения изменений в DOM, чтобы гарантировать как можно большую
компактность результата.
Примечание: Теперь, когда у вас есть программа для экспериментов, посмотрите, что
произойдет с другими комбинациями узлов CDATA, ссылок на сущности и текстовых узлов
при нормализации дерева.

8.6.3. Другие операции


В завершение этого раздела бегло рассмотрим другие операции, которые вы, возможно,
захотите применить к DOM, включая:
• Обход узлов
• Поиск узлов
• Получение содержимого узла
• Создание атрибутов
• Удаление и изменение узлов
• Вставку узлов

Web-

Rendered by www.RenderX.com
Создание и управление DOM Стр. 225 из 626

8.6.3.1. Обход узлов


Интерфейс org.w3c.dom.Node определяет несколько методов, которые вы можете
использовать для обхода узлов, включая getFirstChild, getLastChild, getNextSibling, getPre-
viousSibling и getParentNode. Этих операций достаточно для перемещения из любого места
дерева в любое другое.

8.6.3.2. Поиск узлов


Однако, при поиске узла с конкретным именем, нужно принимать во внимание следующее
обстоятельство. Можно попытаться получить первого потомка и проверить является ли
он искомым. Первый потомок в списке может быть комментарием или инструкцией
обработки. Если XML-данные неверифицированы, он может быть даже текстовым узлом,
содержащим игнорируемые пробелы.
По существу, вы должны просмотреть список потомков, игнорируя не имеющие отношения
к делу и проверяя необходимые. Вот пример процедуры, которую вы должны написать
для поиска узлов в иерархии DOM. Она приведена полностью (с комментариями), так что
вы можете использовать ее как шаблон в ваших приложениях.

/**
* .
* <li>
.
* <li> TEXT (

* ,
.
* <li> CDATA EntityRef.
* <li>
.
* </ul>
* @param name
* @param node ,

* @return
*/
public Node findSubNode(String name, Node node) {
if (node.getNodeType() != Node.ELEMENT_NODE) {
System.err.println("Error: Search node not of element
type");
System.exit(22);
}

Web-

Rendered by www.RenderX.com
Стр. 226 из 626 Document Object Model

if (! node.hasChildNodes()) return null;

NodeList list = node.getChildNodes();


for (int i=0; i < list.getLength(); i++) {
Node subnode = list.item(i);
if (subnode.getNodeType() == Node.ELEMENT_NODE) {
if (subnode.getNodeName() == name) return subnode;
}
}
return null;
}
Более подробное объяснение этого кода приведено в разделе "Повышение сложности" в
"Когда используется DOM".
Обратите внимание также, что вы можете использовать API, описанный в разделе "Резюме
по лексическим элементам управления", для изменения типа DOM, которую строит
анализатор. Этот код будет работать с большинством других DOM.

8.6.3.3. Получение содержимого узла


Если вы хотите получить текст, который содержит узел, вы опять должны просмотреть
список потомков, игнорируя несущественные записи и собирая текст, найденный в узлах
TEXT, CDATA и EntityRef.
Вот пример процедуры, выполняющей эту работу:

/**
* , .
:<ul>
* <li>
.
* <li> TEXT, CDATA,

* EntityRef.
* <li>
.
* (

*
*
.)
* </ul>

Web-

Rendered by www.RenderX.com
Создание и управление DOM Стр. 227 из 626

* @param node DOM


* @return String,

*/
public String getText(Node node) {
StringBuffer result = new StringBuffer();
if (! node.hasChildNodes()) return "";

NodeList list = node.getChildNodes();


for (int i=0; i < list.getLength(); i++) {
Node subnode = list.item(i);
if (subnode.getNodeType() == Node.TEXT_NODE) {
result.append(subnode.getNodeValue());
}
else if (subnode.getNodeType() ==
Node.CDATA_SECTION_NODE)
{
result.append(subnode.getNodeValue());
}
else if (subnode.getNodeType() ==
Node.ENTITY_REFERENCE_NODE)
{
//

// ( )
result.append(getText(subnode));
}
}
return result.toString();
}
Более подробное объяснение этого кода приведено в разделе "Повышение сложности" в
"Когда используется DOM".
Опять же, вы можете упростить этот код, используя API, описанный в разделе "Резюме по
лексическим элементам управления", для изменения типа DOM, которую строит анализатор.
Этот код будет работать с большинством других DOM.

8.6.3.4. Создание атрибутов


Интерфейс org.w3c.dom.Element, расширяющий Node, определяет метод setAttribute,
который добавляет атрибут к этому узлу. (Более подходящее имя для платформы Java
должно было быть addAttribute, поскольку атрибут не является свойством класса, а
создается новый объект.)

Web-

Rendered by www.RenderX.com
Стр. 228 из 626 Document Object Model

Вы можете использовать также метод createAttribute для создания экземпляра Attribute и


использовать перегруженную версию setAttribute для его добавления.

8.6.3.5. Удаление и изменение узлов


Для удаления узла используется метод removeChild родительского Node. Для изменения
его вы можете либо использовать метод replaceChild родительского Node, либо метод
setNodeValue узла.

8.6.3.6. Вставка узлов


При создании новых узлов необходимо помнить, что единственными указываемыми
данными является их имя. На самом деле этот узел дает вам крючок для навешивания
всего остального. Вы вешаете элемент на крючок путем добавления к нему списка потомков.
Например, вы можете добавить текстовый узел, узел CDATA или узел атрибута. При
построении вспомните структуру, которую вы исследовали в упражнениях этого руководства.
Помните: каждый узел в иерархии является очень простым, содержащим только один
элемент данных.

8.6.4. Завершение
Поздравляем! Вы изучили структуру DOM и способы управления ею. Вы имеете приложение
DomEcho, которое можете использовать для отображения структуры DOM. Вы можете
собрать его в GUI-приложение и проводить эксперименты с этой структурой для того,
чтобы увидеть, как различные действия влияют на структуру. Развлекайтесь!

8.7. Использование пространства имен


Как вы уже видели, так или иначе необходимо разрешить конфликт между элементом title,
определенном в slideshow.dtd, и элементом title, определенном в xhtml.dtd, когда одно и
то же имя используется для различных целей. В предыдущем примере вы использовали
дефис в имени, для того чтобы переместить его в другое "пространство имен". В этом
разделе вы узнаете, как использовать стандарт XML по пространству имен для решения
этой же проблемы без переименования элемента.
Основной целью спецификации пространства имен является разрешение автору документа
указывать анализатору, какие DTD или схему использовать при анализе данного элемента.
Анализатор может посмотреть соответствующие DTD или схему для определения элемента.
Естественно, важно также защитить анализатор от остановки при обнаружении
"дублированного" определения и обеспечить генерирование ошибки при ссылке документа
на элемент, например, title, без его определения (без указания DTD или схемы,
используемых для определения).
Примечание: Пространство имен действует также и на атрибуты. В этом разделе мы
рассмотрим только элементы. Более подробная информация по атрибутам находится в
спецификации пространства имен на http://www.w3.org/TR/REC-xml-names/.

8.7.1. Определение пространства имен в DTD


В DTD вы определяете пространство имен, к которому принадлежит элемент, путем
добавления атрибута в определение элемента. Именем атрибута является xmlns ("xml
namespace"). Например, вы могли бы определить его в slideshow.dtd, добавив следующую
запись в определение списка атрибутов элемента title:

Web-

Rendered by www.RenderX.com
Использование пространства имен Стр. 229 из 626

<!ELEMENT title (%inline;)*>


<!ATTLIST title
xmlns CDATA #FIXED "http://www.example.com/slideshow"
>
Объявление атрибута как FIXED имеет несколько важных особенностей:
• Он защищает документ от указания любых неподходящих значений атрибута xmlns (как
описано в разделе "Определение атрибутов в DTD").
• Элемент, определенный в таком DTD, становится уникальным (потому что анализатор
понимает атрибут xmlns), так что он не конфликтует с элементом, имеющим то же самое
имя в другом DTD.
• Это позволяет нескольким DTD использовать одинаковые имена элементов без
возникновения ошибки анализатора.
• Когда документ указывает для тега атрибут xmlns, документ выбирает определение
элемента с подходящим атрибутом.
Каждое имя элемента в вашем DTD могло бы иметь одинаковый атрибут с одинаковым
значением. (Здесь мы говорим об элементе title.) Обратите внимание также, что вы
используете строку CDATA для предоставления URI. В данном случае мы указали URL.
Но вы можете также указать URN, возможно при помощи префикса urn: вместо http:. (URN
сейчас исследуются. В настоящее время они не выполняют много действий, но эта ситуация
в будущем может измениться.)

8.7.2. Ссылка на пространство имен


Если документ использует имя элемента, который существует только в одном из DTD или
схем, к которым он обращается, имя не обязательно должно быть уточненным. Но если
имя элемента имеет несколько определений, необходимо явное уточнение.
Примечание: Фактически, имя элемента всегда определяется его пространством имен по
умолчанию, определенным по имени файла DTD, в котором находится элемент. До тех
пор, пока имеется только одно определение имени, определение является неявным.
Уточнить ссылку на имя элемента можно, как показано ниже, указанием атрибута xmlns:

<title xmlns="http://www.example.com/slideshow">
Overview
</title>
Указанное пространство имен относится к этому элементу и ко всем элементам,
находящимся внутри него.

8.7.3. Определение префикса пространства имен


Если вам нужна только одна такая ссылка на пространство имен, это не так уж страшно.
Но если вам необходимо сделать такие же ссылки несколько раз, добавление атрибутов
xmlns увеличивает объем работы и затрудняет изменение имени пространства имен в
будущем.

Web-

Rendered by www.RenderX.com
Стр. 230 из 626 Document Object Model

Альтернативным подходом является определение префикса пространства имен, а именно


указание xmlns, двоеточия (:) и имени префикса перед значением атрибута, как показано
ниже:

<SL:slideshow xmlns:SL='http:/www.example.com/slideshow'
...>
...
</SL:slideshow>
Это определение устанавливает SL как префикс, который может быть использован для
уточнения имени текущего элемента и всех элементов внутри него. Поскольку префикс
может использоваться с любыми подчиненными элементами, имеет смысл, как показано
здесь, определить его в корневом элементе XML-документа.
Примечание: URI пространства имен может содержать символы, которые недопустимы в
именах XML, то есть, он не может использоваться в качестве префикса непосредственно.
Определение префикса связывает XML-имя с URI, что позволяет вместо него использовать
префикс. Это также облегчает изменение ссылок на URI в будущем.
Если префикс используется для уточнения имени элемента, завершающий тег тоже
включает префикс, как выделено ниже:

<SL:slideshow xmlns:SL='http:/www.example.com/slideshow'
...>
...
<slide>
<SL:title>Overview</SL:title>
</slide>
...
</SL:slideshow>
Наконец, обратите внимание, что несколько префиксов могут быть определены в одних и
тех же элементах, как показано ниже:

<SL:slideshow xmlns:SL='http:/www.example.com/slideshow'
xmlns:xhtml='urn:...'>
...
</SL:slideshow>
При таком расположении все определения префиксов собраны в одном месте и вы можете
использовать их везде, где этого потребует документ. Этот пример также предлагает
использовать URN для определения префикса xhtml вместо URL. Такое определение
потенциально смогло бы позволить приложению ссылаться на локальную копию XHTML
DTD, или на какую-либо зеркальную версию, с потенциально большим влиянием на
производительность.

Web-

Rendered by www.RenderX.com
Верификация при помощи XML Schema Стр. 231 из 626

8.8. Верификация при помощи XML Schema


Теперь, когда вы знаете достаточно о пространстве имен, вы готовы глубже взглянуть на
процесс верификации XML Schema. Хотя полное описание XML Schema выходит за рамки
данного пособия, в этом разделе мы покажем вам основные действия, необходимые для
верификации XML-документа при помощи определения XML Schema. (Вы можете, также,
исследовать примеры программ загрузочного пакета JAXP. Они используют простое
определение XML Schema для верификации данных XML-файла.)
Примечание: Существует несколько языков определения схем, включая RELAX NG,
Schematron и стандарт W3C "XML Schema". (Даже DTD называется "схемой", хотя этот
стандарт является единственным, не использующим синтаксис XML для описания
ограничений схемы.) "XML Schema" представляет нам некоторую терминологическую
путаницу. Хотя фраза "Схема XML Schema" была бы точной, мы будем использовать фразу
"Определение XML Schema" во избежание повторения.
В конце данного раздела вы научитесь также использовать определение XML Schema для
верификации документа, содержащего элементы из нескольких пространств имен.

8.8.1. Обзор процесса верификации


Для того чтобы в XML-документе проверялись ошибки верификации:
1. Должен быть настроен генератор и установлен соответствующий обработчик ошибок.
2. Документ должен быть связан по крайней мере с одной схемой, а возможно и с
несколькими.

8.8.2. Настройка генератора DocumentBuilder


Полезно начать с определения констант, которые вы будете использовать при настройке
генератора. (Это такие же константы, которые вы определяли при использовании XML
Schema для SAX-анализа.)

static final String JAXP_SCHEMA_LANGUAGE =


"http://java.sun.com/xml/jaxp/properties/schemaLanguage";

static final String W3C_XML_SCHEMA =


"http://www.w3.org/2001/XMLSchema";
Затем необходимо настроить DocumentBuilderFactory на генерирование верифицирующего
анализатора, работающего с пространством имен и использующего XML Schema:

...
DocumentBuilderFactory factory =
DocumentBuilderFactory.newInstance()
factory.setNamespaceAware(true);
factory.setValidating(true);
try {

Web-

Rendered by www.RenderX.com
Стр. 232 из 626 Document Object Model

factory.setAttribute(JAXP_SCHEMA_LANGUAGE, W3C_XML_SCHEMA);
}
catch (IllegalArgumentException x) {
// ,
JAXP 1.2
...
}
Поскольку JAXP-совместимые анализаторы по умолчанию не работают с пространством
имен, необходимо установить это свойство для работы верификации схемы. Также вы
устанавливаете атрибут генератора, указывающий используемый язык анализатора. (Для
SAX-анализа вы устанавливаете свойство анализатора, сформированного генератором.)

8.8.2.1. Установка связи документа со схемой


После подготовки программы к верификации с использованием XML Schema необходимо
проверить, что XML-документ связан, по крайней мере, с одной из схем. Существуют два
способа для установки этой связи:
1. При помощи объявления схемы в XML-документе.
2. Указав схему для использования в приложении.
Примечание: Когда приложение указывает используемую схему, это аннулирует объявление
схемы в документе.
Для указания определения схемы в документе вы должны создать примерно такой XML:

<documentRoot
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation='YourSchemaDefinition.xsd'
>
...
Первый атрибут определяет префикс XML-пространства имен (xmlns) - "xsi", где "xsi"
означает "XML Schema Instance". Вторая строка указывает схему, которая будет
использоваться для тех элементов документа, которые не имеют префикса пространства
имен - то есть, для элементов, которые вы обычно определяете в простом, обычном XML-
документе. (Вы узнаете, как работать с несколькими пространствами имен в следующем
разделе.)
Вы можете, также, указать файл схемы в приложении, например:

static final String schemaSource = "YourSchemaDefinition.xsd";


static final String JAXP_SCHEMA_SOURCE =
"http://java.sun.com/xml/jaxp/properties/schemaSource";
...
DocumentBuilderFactory factory =
DocumentBuilderFactory.newInstance()

Web-

Rendered by www.RenderX.com
Верификация при помощи XML Schema Стр. 233 из 626

...
factory.setAttribute(JAXP_SCHEMA_SOURCE,
new File(schemaSource));
В вашем распоряжении есть механизмы, позволяющие указывать несколько схем. Мы
узнаем про них далее.

8.8.3. Верификация при использовании нескольких пространств имен


Пространства имен дают возможность комбинировать элементы, предназначенные для
разных задач, в одном и том же документе без необходимости беспокоиться о совпадении
имен.
Примечание: Материал, рассматриваемый в этом разделе, относится также к верификации
в SAX-анализаторе. Этот материал расположен здесь, поскольку вы уже достаточно знаете
о пространствах имен, чтобы его рассмотрение имело смысл.
Для примера рассмотрим набор XML-данных, хранящих персональные данные. Набор
данных может содержать информацию из налоговой формы w2, а также информацию из
формы по найму работника, в соответствующих схемах которых есть элементы с именем
<form>.
Если префикс определяется для пространства имен "tax", а второй префикс определяется
для пространства имен "hiring", то персональные данные могут содержать следующие
сегменты:

<employee id="...">
<name>....</name>
<tax:form>
...w2 tax form data...
</tax:form>
<hiring:form>
...employment history, etc....
</hiring:form>
</employee>
Очевидно, что содержимое элемента tax:form будет отличаться от содержимого hiring:form
и должно верифицироваться по-разному.
Обратите внимание также, что в этом примере есть пространство имен "по умолчанию",
к которому принадлежат элементы employee и name. Для правильной верификации
документа схема для этого пространства имен должна быть объявлена, также как и для
пространства имен tax и hiring.
Примечание: Пространство имен "по умолчанию" в действительности является
специфическим пространством имен. Оно определяется как "пространство имен без имени".
То есть, вы не можете просто использовать одно пространство имен по умолчанию на этой
неделе, а другое на следующей. Это "непоименованное пространство имен" или "нулевое
пространство имен" похоже на цифру ноль. Она не имеет какого-то значения, но все-таки

Web-

Rendered by www.RenderX.com
Стр. 234 из 626 Document Object Model

точно определена. Поэтому пространство имен, имеющее имя, никогда не может быть
использовано как пространство имен по умолчанию.
Во время анализа каждый элемент в наборе данных будет верифицироваться с
использованием соответствующей схемы, если эти схемы были объявлены. Опять же,
схемы могут быть объявлены как часть XML-данных, или в программе. (Возможно, также,
смешивать объявления, хотя, в общем случае, хорошей идеей является хранение всех
объявлений в одном месте.)

8.8.3.1. Объявление схем в наборе XML-данных


Для объявления схем в наборе данных, использующихся в приведенном выше примере,
XML-код может выглядеть примерно так:

<documentRoot
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="employeeDatabase.xsd"
xsi:schemaLocation=
"http://www.irs.gov/ fullpath/w2TaxForm.xsd
http://www.ourcompany.com/ relpath/hiringForm.xsd"
xmlns:tax="http://www.irs.gov/"
xmlns:hiring="http://www.ourcompany.com/"
>
...
Объявление noNamespaceSchemaLocation похоже на то, что вы видели ранее, то же самое
можно сказать про последние две записи, определяющие префиксы пространств имен tax
и hiring. Новые записи находятся в середине и определяют месторасположение схем,
используемых для каждого пространства имен, на которые есть ссылки в документе.
Объявление xsi:schemaLocation состоит из пар записей, в которых первым элементом
записи является полностью указанный URI пространства имен, а второй элемент содержит
полный или относительный путь к определению схемы. (В общем случае рекомендуется
полный путь, поскольку при этом существует только одна копия схемы.)
Еще одно замечание: префиксы пространства имен не могут быть использованы при
определении месторасположения схемы. Объявление xsi:schemaLocation понимает только
имена пространств имен, не префиксы.

8.8.3.2. Объявление схем в приложении


Чтобы объявить эквивалентные схемы в приложении, код может выглядеть следующим
образом:

static final String employeeSchema = "employeeDatabase.xsd";


static final String taxSchema = "w2TaxForm.xsd";
static final String hiringSchema = "hiringForm.xsd";

static final String[] schemas = {

Web-

Rendered by www.RenderX.com
Верификация при помощи XML Schema Стр. 235 из 626

employeeSchema,
taxSchema,
hiringSchema,
};

static final String JAXP_SCHEMA_SOURCE =


"http://java.sun.com/xml/jaxp/properties/schemaSource";

...
DocumentBuilderFactory factory =
DocumentBuilderFactory.newInstance()
...
factory.setAttribute(JAXP_SCHEMA_SOURCE, schemas);
Здесь массив строк, указывающих на определения схем (.xsd-файлы), передается как
аргумент в метод factory.setAttribute. Обратите внимание на отличия в объявлении схем в
наборе XML-данных:
• Нет специального объявления схемы "по умолчанию" (непоименованной).
• Вы не указываете имя пространства имен, а только определяете указатели на .xsd-
файлы.
Для установки связи с пространством имен анализатор читает .xsd-файлы и находит в них
имя target namespace (целевое пространство имен). Поскольку файлы указаны с URI,
анализатор может использовать EntityResolver (если он определен) для поиска локальной
копии схемы.
Если в определении схемы целевое пространство имен не указано, она связывается с
пространством имен "по умолчанию" (непоименованным, или нулевым). То есть, в
приведенном выше примере вы могли бы увидеть следующие объявления пространств
имен в схемах:
• employeeDatabase.xsd - none
• w2TaxForm.xsd - http://www.irs.gov/
• hiringForm.xsd - http://www.ourcompany.com
На данный момент вы увидели два возможных значения свойства источника схемы при
вызове метода factory.setAttribute(), объект File в factory.setAt-
tribute(JAXP_SCHEMA_SOURCE, new File(schemaSource)) и массив строк в factory.setAt-
tribute(JAXP_SCHEMA_SOURCE, schemas). Вот полный список возможных значений этого
аргумента:
• Строка, указывающая URI схемы
• InputStream с содержимым схемы
• SAX InputSource
• Файл
• Массив объектов, каждый из которых имеет тип, указанный выше.

Web-

Rendered by www.RenderX.com
Стр. 236 из 626 XML Stylesheet Language for Transformations

Примечание: Массив объектов может использоваться только тогда, когда язык схемы
(например, http://java.sun.com/xml/jaxp/properties/schemaLanguage) обладает способностью
ассемблировать схему во время выполнения. И еще: при передаче массива объектов
нельзя иметь две схемы, использующие одно и тоже пространство имен.

8.9. Дополнительная информация


Дополнительную информацию по TreeModel можно найти в:
• Understanding the TreeModel: http://java.sun.com/products/jfc/tsc/articles/jtree/index.html.
Дополнительную информацию по W3C Document Object Model (DOM) можно найти на:
• Странице стандарта DOM http://www.w3.org/DOM/.
Дополнительную информацию по основанным на схеме механизмам верификации можно
найти здесь:
• Стандартный механизм верификации W3C, XML Schema: http://www.w3c.org/XML/Schema.
• Механизм верификации, основанный на регулярных выражениях, RELAX NG:
http://www.oasis-open.org/committees/relax-ng/.
• Механизм верификации, основанный на утверждениях, Schematron:
http://www.ascc.net/xml/resource/schematron/schematron.html.

9. XML Stylesheet Language for Transformations


XML Stylesheet Language for Transformations (язык таблиц стилей XML для преобразований)
- XSLT - определяет механизмы адресации XML-данных (XPath) и указания преобразований
данных в другие форматы. JAXP включает две реализации XSLT - интерпретатор (Xalan)
и компилятор (XSLTC), который дает возможность сохранять предварительно
откомпилированные версии желаемых преобразований в виде транслетов для последующей
наиболее эффективной их обработки во время выполнения.
В этой главе вы познакомитесь и с Xalan, и с XSLTC. Вы напишете Document Object Model
(DOM) в виде XML-файла и узнаете, как генерировать DOM из произвольно выбранного
файла данных для преобразования в XML. И, в завершение, вы преобразуете XML-данные
в другую форму, по пути раскрывая тайны механизма адресации XPath.
Примечание: Примеры этой главы расположены в каталоге <JWSDP_HOME>/docs/tuto-
rial/examples/jaxp/xslt/samples.

9.1. Введение в XSLT и XPath


XML Stylesheet Language (XSL) имеет три основных компонента:
XSL-FO
Стандарт "flow object" ("объект потока"). Являясь самым большим компонентом, этот
стандарт определяет механизмы описания размеров шрифтов, схем страниц и способ
"перетекания" информации из одной страницы в другую. Этот компонент не входит в JAXP
и не рассматривается в данном руководстве.

Web-

Rendered by www.RenderX.com
Введение в XSLT и XPath Стр. 237 из 626

XSLT
Это язык преобразований, позволяющий определять преобразования из XML в какой-либо
другой формат. Например, XSLT можно использовать для генерирования HTML или другой
XML-структуры. Его можно использовать даже для генерирования обычного текста или
для сохранения информации в каком-нибудь другом формате. (И, как вы узнаете из раздела
"Генерирование XML из произвольной структуры данных", "умное" приложение может
также манипулировать и не XML-данными.)
XPath
В своей основе XSLT - это язык, позволяющий указать, что делать при обнаружении
конкретного элемента. Но при написании программы для различных частей структуры
XML-данных необходимо иметь возможность в любое время указать ту часть структуры,
которая обрабатывается в данный момент времени. Таким языком указания является
XPath. Это механизм адресации, дающий возможность указать путь к элементу, так чтобы,
например, <article><title> можно было отличить от <person><title>. То есть, вы можете
описать различные типы преобразований для различных элементов <title>.
В оставшейся части этого раздела рассматриваются пакеты, составляющие JAXP Trans-
formation API. Затем обсуждаются конфигурационные параметры генератора, используемые
для выбора механизма преобразования Xalan или XSLTC.

9.1.1. JAXP-пакеты преобразований


Ниже приведено описание пакетов, составляющих JAXP Transformation API:
javax.xml.transform
Этот пакет определяет класс генератора, используемого для получения объекта Transformer.
В дальнейшем вы можете настроить преобразователь на входной (Source) и выходной
(Result) объекты и вызвать метод transform() для проведения преобразования. Входной и
выходной объекты создаются с использованием классов одного из трех других пакетов.
(Какой именно преобразователь вы создадите, Xalan-интерпретатор или XSLTC-компилятор,
определяется конфигурационными настройками генератора, которые будут рассмотрены
очень скоро.)
javax.xml.transform.dom
Определяет классы DOMSource и DOMResult, позволяющие использовать DOM в качестве
входного или выходного объекта преобразования.
javax.xml.transform.sax
Определяет классы SAXSource и SAXResult, позволяющие использовать генератор событий
SAX в качестве входного или выходного объекта преобразования или передавать SAX-
события в процессор SAX-событий.
javx.xml.transform.stream
Определяет классы StreamSource и StreamResult, позволяющие использовать поток I/O в
качестве входного или выходного объекта преобразования.

Web-

Rendered by www.RenderX.com
Стр. 238 из 626 XML Stylesheet Language for Transformations

9.2. Выбор механизма трансформации


В данном разделе приведена информация, необходимая для того, чтобы помочь вам
сделать правильный выбор между интерпретирующим преобразователем (Xalan) и
компилирующим преобразователем (XSLTC).

9.2.1. Анализ производительности


Для однопроходной трансляции интерпретирующий преобразователь (Xalan) является
более быстрым, чем компилирующий (XSLTC), поскольку он не генерирует и не сохраняет
байт-коды в небольших Java-классах, выполняющихся как транслеты.
Но если преобразование будет использоваться несколько раз, имеет смысл использовать
механизм преобразований XSLTC, поскольку XSLTC является несомненным лидером в
вопросах по требованиям к памяти и производительности.
Транслет XSLTC является небольшим, поскольку он реализует только те преобразования,
которые действительно выполняет таблица стилей. Он является быстрым, и потому что
небольшой, и потому что необходимая для интерпретации таблицы стилей лексическая
обработка уже выполнена. Наконец, транслеты загружаются быстрее и используют меньше
системных ресурсов из-за своих малых размеров.
Например, сервлет, который будет выполняться длительный период времени, выиграет
от использования XSLTC. Также, преобразование, запускаемое из командной строки,
выполняется быстрее при использовании XSLTC. Более подробно этот процесс описан в
разделе "Преобразование из командной строки".
В дополнение к возможности кэширования транслетов, XSLTC предоставляет несколько
других возможностей, повышающих производительность:
• Управление встраиванием
По умолчанию, XSLTC "встраивает" код преобразований. Это означает, что код,
отвечающий за преобразование элемента, содержит код преобразования для всех
возможных субэлементов этого элемента.
Для маленьких или средних таблиц стилей такая реализация генерирует наиболее
быстрый код. Однако для сложных таблиц стилей транслеты становятся очень большими.
Для решения этой проблемы XSLTC дает возможность запретить встраивание. Для
этого используется ключ -n при компиляции XSLTC-транслетов из командной строки.
При генерации XSLTC-преобразователя при помощи класса генератора JAXP
используется метод setAttribute() для установки параметра "disable-inlining":

TransformerFactory tf = new TransformerFactory();


tf.setAttribute("disable-inlining", Boolean.TRUE);
• Кэширование модели документа
Когда XSLTC обрабатывает XML-данные он создает свою собственную внутреннюю
Document Object Model (похожую на уже известную вам W3C DOM, но более простую).
Так как построение модели документа занимает определенное время, XSLTC
предоставляет возможность кэшировать модель для увеличения скорости
последовательных преобразований.

Web-

Rendered by www.RenderX.com
Выбор механизма трансформации Стр. 239 из 626

Эта возможность может пригодиться, например, при обработке сервлетом XML-


документов. Если они преобразовываются в HTML во время доступа к Web, то
кэширование внутреннего представления документа может оказать большое влияние
на производительность. Вот пример кода, который вы могли бы использовать:

final SAXParser parser = factory.newSAXParser();


final XMLReader reader = parser.getXMLReader();

XSLTCSource source = new XSLTCSource();


source.build(reader, xmlfile);
Объект source можно теперь использовать в нескольких преобразованиях без повторного
чтения файла.
• Кэширование компилированных таблиц стилей
XSLTC позволяет сохранять откомпилированные версии таблиц стилей, которые можно
использовать для более быстрого создания нескольких объектов Transformer. Например,
эта возможность может уменьшить время загрузки многопоточного сервлета. Если
сервлет генерирует сотню потоков для обслуживания входящих запросов, он может
откомпилировать таблицу стилей один раз и использовать откомпилированную версию
для генерации преобразователя для каждого потока.
Предварительно откомпилированные таблицы стилей сохраняются в объекте Templates.
Для непосредственного создания объекта Transformer (без использования объекта
Templates) можно использовать следующий код:

TransformerFactory factory =
TransformerFactory.newInstance();
Transformer xformer = factory.newTransformer(myStyleSheet);
xformer.transform(myXmlInput,
new StreamResult(System.out));
Можно также создать промежуточный объект Templates, который можно сохранить и
использовать повторно, например:

TransformerFactory factory =
TransformerFactory.newInstance();
Templates templates = factory.newTemplates(myStyleSheet);
Transformer xformer = templates.newTransformer();
xformer.transform(myXmlInput,
new StreamResult(System.out));
Примечание: Существуют, также, правила создания таблиц стилей, которые объясняют,
что нужно сделать, а чего следует избегать для получения максимальной
производительности в XSLT. Более подробную информацию по этому вопросу можно
найти на http://xml.apache.org/xalan-j/xsltc/xsltc_performance.html.

Web-

Rendered by www.RenderX.com
Стр. 240 из 626 XML Stylesheet Language for Transformations

9.2.2. Анализ функциональности


В то время когда XSLTC предлагает более высокую производительность во многих
приложениях, Xalan имеет некоторые преимущества в функциональности. Среди этих
преимуществ - поддержка стандартного языка запросов SQL.

9.2.3. Что выбрать


Механизм преобразования (Xalan или XSLTC) определяется настройками конфигурации
генератора. По умолчанию JAXP-генератор создает преобразователь Xalan. Для получения
преобразователя XSLTC предпочтительным способом является установка системного
свойства TransformationFactory, например:

javax.xml.transform.TransformerFactory=
org.apache.xalan.xsltc.trax.TransformerFactoryImpl
Хотя иногда невозможно установить это свойство - например, из-за того, что приложение
является сервлетом и изменение системного свойства будет влиять на остальные сервлеты,
выполняющиеся в том же контейнере. В этом случае вы можете создать экземпляр XSLTC-
преобразователя непосредственно:

new org.apache.xalan.xsltc.trax.TransformerFactoryImpl(..)
Вы можете, также, передать значение генератора в приложение и использовать ClassLoader
для создания его экземпляра во время выполнения.
Примечание: Для явного указания Xalan-преобразователя можно использовать значение
org.apache.xalan.processor.TransformerFactoryImpl вместо org.apache.xalan.xsltc.trax.Trans-
formerFactoryImpl.
Существует, также, "умный преобразователь", использующий механизм преобразований
Xalan во время создания объекта Transformer и механизм преобразования XSLTC во время
создания промежуточных объектов Templates. Для получения экземпляра такого
преобразователя используйте значение org.apache.xalan.xsltc.trax.SmartTransformerImpl
для установки системного свойства генератора или для использования этого класса при
прямом создании экземпляра анализатора.

9.3. Как работает XPath


Спецификация XPath является основой нескольких спецификаций, включая XSLT и
спецификации связывания/адресации, например XPointer. Поэтому понимание XPath
является фундаментом профессионального использования XML. Этот раздел предоставляет
полное введение в XPath в контексте XSLT; вы можете обращаться к нему при
необходимости.
Примечание: В этом руководстве вы не будете использовать XPath до раздела
"Преобразование XML-данных при помощи XSLT". Поэтому, если хотите, вы можете
пропустить этот раздел и перейти к разделу "Запись DOM в XML-файл". (В конце раздела
помещено примечание, которое отошлет вас сюда, так что вы не забудете!)

Web-

Rendered by www.RenderX.com
Как работает XPath Стр. 241 из 626

9.3.1. Выражения XPATH


В общем случае XPath-выражение определяет образец для выбора XML-узлов. XSLT-
шаблоны в дальнейшем используют эти образцы при преобразованиях. (XPointer добавляет
механизмы определения точки или диапазона, так что XPath-выражения могут
использоваться для адресации.)
Узлами в XPath-выражении считаются не только элементы. Ими, помимо прочего, являются
также текст и атрибуты. По сути, спецификация XPath определяет абстрактную модель
документа, имеющую семь различных типов узлов:
• корень
• элемент
• текст
• атрибут
• комментарий
• инструкция обработки
• пространство имен
Примечание: Корневой элемент XML-данных моделируется как узел элемента. Корневой
элемент XPath содержит корневой элемент документа, а также другую информацию,
относящуюся к документу.

9.3.2. Модель данных XSLT/XPath


Как и DOM, модель данных XSLT/XPath состоит из дерева, содержащего разнообразные
узлы. Каждый узел элемента может содержать узлы текста, узлы атрибутов, узлы
элементов, узлы комментариев и узлы инструкций обработки.
В этой абстрактной модели синтаксические различия устраняются, и вам предоставляются
нормализованные данные. Например, в текстовом узле не делается различий, определен
ли он в секции CDATA, или включен в ссылках на сущность. Текстовый узел будет содержать
нормализованные данные после завершения полного анализа. То есть, текст будет
содержать символ < независимо от того, использовалась ли для него ссылка на сущность
&lt; или секция CDATA. (Также, текст будет содержать символ & независимо от того, был
он передан с использованием &amp; или секции CDATA.)
В данном разделе мы будем иметь дело в основном с узлами элементов и текстовыми
узлами. Информацию по другим механизмам адресации можно найти в "Спецификации
XPath".

9.3.3. Шаблоны и контексты


XSLT-шаблон представляет собой набор инструкций форматирования, применяемых к
узлам, выбранным XPATH-выражением. В таблице стилей XSLT-шаблон будет выглядеть
примерно так:

<xsl:template match="//LIST">

Web-

Rendered by www.RenderX.com
Стр. 242 из 626 XML Stylesheet Language for Transformations

...
</xsl:template>
Выражение //LIST выбирает набор узлов LIST из входного потока. Дополнительные
инструкции в шаблоне указывают системе, что делать с этими узлами.
Выбранный таким выражением набор узлов определяет контекст, в котором другие
выражения шаблона выполняются. Этот контекст может рассматриваться как полный
набор - например, при определении количества его узлов.
Контекст может рассматриваться, также, как простой член набора, поскольку каждый член
обрабатывается один за другим. Например, в шаблоне LIST выражение @type относится
к атрибуту type текущего узла LIST. (Подобным же образом, выражение @* относится ко
всем атрибутам текущего элемента LIST.)

9.3.4. Основы XPath-адресации


XML-документ представляет собой древовидный набор узлов. Как и для иерархической
структуры каталогов, имеет смысл указывать путь, определяющий конкретный узел в
иерархии. (Oтсюда и имя спецификации: XPath.) Фактически, многие соглашения о путях
каталогов применяются и здесь:
• Прямой слеш / используется как разделитель.
• Абсолютный путь от корня документа начинается с /.
• Относительный путь от данного места начинается с чего-либо другого.
• Две точки указывают предка текущего узла.
• Одна точка указывает текущий узел.
Например, в XHTML-документе (XML-документ, выглядящий как HTML, но являющийся
формально-правильным согласно правилам XML) путь /h1/h2 указывает на элемент h2 в
элементе h1. (Вспомните, что в XML имена элементов чувствительны к регистру, поэтому
этот тип спецификации лучше работает в XHTML, чем в обычном HTML, так как HTML
является не чувствительным к регистру.)
В такой спецификации соответствий образцу, как XSLT, указание /h1/h2 выбирает все
элементы h2, находящиеся под элементом h1. Для выбора конкретного элемента h2
используются квадратные скобки для индексации (как в массивах). Таким образом, путь
/h1[4]/h2[5] выберет пятый элемент р2 в четвертом элементе h1.
Примечание: В XHTML все имена элементов записываются в нижнем регистре. Это
общепринятое соглашение для XML-документов. Однако, имена, записанные в верхнем
регистре, легче читаются в таком руководстве, как наше. Поэтому в оставшейся части
руководства по XSLT все имена XML-элементов будут записаны в верхнем регистре.
(Имена атрибутов останутся в нижнем регистре.)
Указанное в XPath-выражении имя относится к элементу. Например, "h1" в /h1/h2 относится
к элементу h1. Для ссылки на атрибут необходимо добавить к его имени префикс - символ
@. Например, @type ссылается на атрибут type элемента. Предположив, что вы имеете
XML-документ с элементами LIST, выражение LIST/@type выбирает атрибут type элемента
LIST.

Web-

Rendered by www.RenderX.com
Как работает XPath Стр. 243 из 626

Примечание: Поскольку выражение не начинается с /, ссылка указывает на узел list


относительно текущего контекста, где бы ни была текущая позиция в документе.

9.3.5. Основы XPath-выражений


Полный набор XPath-выражений использует групповые символы, операторы и функции,
определенные в XPath. Вы вскоре их изучите. А сейчас мы для ознакомления рассмотрим
несколько наиболее общих XPath-выражений.
Выражение @type= "unordered" определяет атрибут с именем type, чье значение равно
"unordered". Как вы уже знаете, выражение LIST/@type определяет атрибут type элемента
LIST.
Вы можете комбинировать эти две записи для получения чего-то интересного! В XPath
квадратные скобки ([]), обычно связываемые с индексацией, расширены для указания
критерия выбора. То есть, выражение LIST[@type="unordered"] выбирает все элементы
LIST, чье значение type равно "unordered".
Подобные выражения существуют для элементов, в которых каждый элемент имеет
связанное с ним строковое значение. (Вскоре вы увидите, как строковое значение
определяется для составного элемента. А сейчас мы рассматриваем простые элементы,
имеющие одну текстовую строку.)
Предположим, что вы моделируете деятельность вашей компании в XML-структуре,
содержащей элементы PROJECT и ACTIVITY, имеющие текстовую строку с названием
проекта, несколько элементов PERSON, представляющих список вовлеченных в проект
людей, и необязательный элемент STATUS, хранящий статус проекта. Вот несколько
примеров, в которых используется расширенное значение квадратных скобок:
• /PROJECT[.="MyProject"] - выбирает PROJECT с именем "MyProject".
• /PROJECT[STATUS] - выбирает все проекты, имеющие дочерний элемент STATUS.
• /PROJECT[STATUS= "Critical"] - выбирает все проекты, имеющие дочерний элемент
STATUS со строковым значением "Critical".

9.3.6. Комбинирование индексных адресов


Спецификация XPath определяет немало механизмов адресации, и они могут быть
скомбинированы многими различными способами. В результате XPath предлагает очень
большие возможности для относительно простой спецификации. Приведем еще две
интересные комбинации:
• LIST[@type="ordered"][3] - выбирает все элементы LIST с типом "ordered" и возвращает
третий.
• LIST[3][@type="ordered"] - выбирает третий элемент LIST, но только если его тип равен
"ordered".
Примечание: Значительно больше комбинаций операторов адресации приведено в разделе
2.5 "XPath Specification". Это, без сомнения, наиболее полезная часть спецификации для
определения XSLT-преобразования.

Web-

Rendered by www.RenderX.com
Стр. 244 из 626 XML Stylesheet Language for Transformations

9.3.7. Групповые символы


По определению неполное XPath-выражение выбирает набор XML-узлов, соответствующих
указанному образцу. Например, /HEAD соответствует всем записям HEAD верхнего уровня,
а /HEAD[1] соответствует только первому. В таблице 1 перечислены групповые символы,
которые могут быть использованы в выражениях XPath для расширения области действия
соответствия образцов.
Таблица 1 Групповые символы XPath
Групповой символ Значение
* Соответствует любому узлу элемента (не узлу атрибута или
текста).
node() Соответствует любому узлу любого типа: узлу элемента, узлу
атрибута, узлу инструкции обработки, узлу пространства имен
или узлу комментария.
@* Соответствует любому узлу атрибута

Например, в проекте базы данных /*/PERSON[.="Fred"] соответствует любому элементу


PROJECT или ACTIVITY, содержащему имя Fred.

9.3.8. Адресация расширенного пути


До настоящего времени все увиденные нами образцы указывали точное количество уровней
иерархии. Например, /HEAD указывает любой элемент HEAD на первом уровне иерархии,
тогда как /*/* указывает любой элемент на втором уровне иерархии. Для указания
неопределенного уровня иерархии используется двойной прямой слеш (//). Например,
выражение //PARA выбирает все элементы paragraph в документе, где бы они ни были
найдены.
Образец // может также быть использован внутри пути. То есть, выражение
/HEAD/LIST//PARA указывает все элементы параграфа в поддереве, начинающемся с
/HEAD/LIST.

9.3.9. Типы данных и операторы XPath


Выражения XPath выдают набор узлов, строку, логическую переменную (значение true/false)
или число. В таблице 2таблице 2 перечислены операторы, которые могут быть
использованы в выражении XPath.
Таблица 2 Операторы XPath
Оператор Значение
| Альтернатива. Например, PARA|LIST выбирает все элементы
PARA или LIST.
or, and Возвращает результат операции or/and над двумя
логическими значениями.
=, != Равно, или не равно, для логических значений, строк и чисел.
<, >, <=, >= Меньше, больше, меньше или равно, больше или равно - для
чисел.
+, -, *, div, mod Добавить, вычесть, умножить, разделить (плавающая запятая)
и разделить по модулю (остаток) (например 6 mod 4=2).

Наконец, выражения могут быть сгруппированы в скобках, чтобы не беспокоиться о


приоритете операций.

Web-

Rendered by www.RenderX.com
Как работает XPath Стр. 245 из 626

Примечание: "Приоритет операций" - это понятие, отвечающее на вопрос, "Если указать


a+b*c, это означает (a+b)*c или a+(b*c)?". (Приоритет операций приблизительно такой же,
как указано в таблице.)

9.3.10. Строковое значение элемента


Перед тем как двигаться дальше, необходимо понять, как определяется строковое значение
более сложного элемента.
Строковое значение элемента представляет собой объединение всех подчиненных
текстовых узлов. То есть, для такого элемента "смешанной модели" XML-данных как:

<PARA>This paragraph contains a <B>bold</B> word</PARA>


строковым значением <PARA> является "This paragraph contains a bold word". Обратите
внимание, что <B> является потомком <PARA>, а текст, содержащийся во всех потомках,
объединяется в строковое значение.
Также необходимо понимать, что текст в абстрактной модели данных, определенной в
XPath, является полностью нормализованным. То есть, чтобы ни содержала XML-структура
- ссылку на сущность &lt; или знак "<" в секции CDATA, - строковое значение элемента
будет содержать символ "<". Следовательно, при генерировании HTML или XML с таблицей
стилей XSLT все экземпляры "<" должны быть преобразованы в &lt; или заключены в
секцию CDATA. Аналогично, все экземпляры "&" должны быть преобразованы в &amp;.

9.3.11. XPath-функции
Данный раздел завершается обзором XPath-функций. Вы можете использовать их для
выбора набора узлов, точно также как использовали бы спецификацию элементов, которую
вы уже видели. Другие функции возвращают строку, число или логическое значение.
Например, выражение /PROJECT/text() выдает строковое значение узлов PROJECT.
Многие функции зависят от текущего контекста. В приведенном выше примере контекстом
для каждого вызова функции text() является выбранный в данное время узел PROJECT.
Есть много XPath-функций - слишком много для их детального описания. Здесь приведен
их список вместе с кратким описанием их действия.
Примечание: Просмотрите список функций для ознакомления. Более подробная
информация находится в "XPath Specification".

9.3.11.1. Функции набора узлов


Многие XPath-выражения выбирают набор узлов. По существу, они возвращают тип node-
set. Одна из функций тоже делает это.
• id(…) - возвращает узел с указанным id.
(Элементы имеют ID, только если документ имеет DTD, указывающий, какой атрибут имеет
тип ID.)

9.3.11.2. Функции позиционирования


Эти функции возвращают числовые значения, основанные на позиции элементов.
• last() - возвращает индекс последнего элемента.

Web-

Rendered by www.RenderX.com
Стр. 246 из 626 XML Stylesheet Language for Transformations

Например: /HEAD[last()] выбирает последний элемент HEAD.


• position() - возвращает индексную позицию.
Например: /HEAD[position()<=5] выбирает первые пять элементов HEAD.
• count(…) - возвращает количество элементов.
Например: /HEAD[count(HEAD)= 0] выбирает все элементы HEAD, не имеющие
подзаголовков.

9.3.11.3. Строковые функции


Эти функции оперируют над строками или возвращают строки.
• concat(string, string, ...) - объединяет строковые значения
• starts-with(string1, string2) - возвращает true если string1 начинается с string2
• contains(string1, string2) - возвращает true если string1 содержит string2
• substring-before(string1, string2) - возвращает начало string1 перед вхождением в нее
string2
• substring-after(string1, string2) - возвращает остаток string1 после вхождения в нее
string2
• substring(string, idx) - возвращает подстроку, начиная с указанного индекса до конца,
нумерация индексов начинается с 1
• substring(string, idx, len) - возвращает подстроку указанной длины, начиная с указанного
индекса
• string-length() - возвращает размер строкового значения узла контекста
Узел контекста представляет собой выбранный в данный момент узел, то есть узел,
выбранный XPath-выражением, в котором используется функция, подобная string-length().
• string-length(string) - возвращает размер указанной строки
• normalize-space() - возвращает нормализованное строковое значение текущего узла
(нет начальных и конечных пробелов, последовательности пробелов заменены одним
пробелом)
• normalize-space(string) - возвращает нормализованное строковое значение указанной
строки
• translate(string1, string2, string3) - преобразует string1, заменяя символы из string2
соответствующими символами из string3
Примечание: В XPath определены три способа получения текста элемента: text(),
string(object) и строковое значение, выраженное именем элемента, например:
/PROJECT[PERSON="Fred"].

9.3.11.4. Логические функции


Эти функции работают с логическими значениями или возвращают логическое значение:
• not(...) - инвертирует указанное логическое значение
• true() - возвращает true
• false() - возвращает false

Web-

Rendered by www.RenderX.com
Как работает XPath Стр. 247 из 626

• lang(string) - возвращает true, если язык узла контекста (указанный в атрибуте xml:Lang)
такой же (или подчиненный ему), как указанный язык. Например: Lang("en") возвращает
true для <PARA_xml:Lang="en">...</PARA>

9.3.11.5. Числовые функции


Эти функции работают с числовыми значениями или возвращают числовое значение:
• sum(...) - возвращает сумму числовых значений всех узлов в указанном наборе узлов
• floor(N) - возвращает наибольшее целое, которое не больше чем N
• ceiling(N) - возвращает наименьшее целое, которое больше чем N
• round(N) - возвращает целое, ближайшее к N

9.3.11.6. Функции преобразования


Эти функции преобразуют один тип данных в другой.
• string(...) - возвращает строковое значение числа, логического значения или набора
узлов
• boolean(...) - возвращает логическое значение числа, строки или набора узлов (не
нулевое число, не пустой набор узлов и не пустая строка - все имеют значение true)
• number(...) - возвращает числовое значение логического значения, строки или набора
узлов (true равно 1, false равно 0, строка, содержащая число, становится этим числом,
строковое значение набора узлов преобразуется в число)

9.3.11.7. Функции пространства имен


Эти функции дают возможность определить характеристики пространства имен для узла.
• local-name() - возвращает имя текущего узла минус префикс пространства имен
• local-name(...) - возвращает имя первого узла в указанном наборе узлов минус префикс
пространства имен
• namespace-uri() - возвращает URI пространства имен текущего узла
• namespace-uri(...) - возвращает URI пространства имен первого узла в указанном наборе
узлов
• name() - возвращает расширенное имя (URI плюс локальное имя) текущего узла
• name(...) - возвращает расширенное имя (URI плюс локальное имя) первого узла в
указанном наборе узлов

9.3.12. Резюме
Операторы, функции, групповые имена и механизмы адресации узлов в XPath могут
комбинироваться различными способами. Это введение должно было дать вам хорошие
начальные знания для определения образца, необходимого при решении любой конкретной
задачи.

Web-

Rendered by www.RenderX.com
Стр. 248 из 626 XML Stylesheet Language for Transformations

9.4. Запись DOM в XML-файл


После создания DOM - то ли после анализа XML-файла, то ли после программного ее
построения - часто нужно сохранить ее в XML-файле. В этом разделе показано, как это
сделать, используя пакет преобразований Xalan.
Используя этот пакет, вы создадите объект преобразователя для соединения DomSource
с StreamResult. Затем вы вызовете метод преобразователя transform() для записи DOM в
виде XML-данных.

9.4.1. Чтение XML


Первым действием является создание DOM в памяти путем анализа XML-файла. Вы
должны быть хорошо знакомы с этой процедурой.
Примечание: Код этого раздела находится в файле TransformationApp01.java.
Приведенный ниже код обеспечивает основной шаблон, с которого можно начинать работу.
(Он должен быть вам знаком. Это, в основном, тот же код, который вы писали в начале
руководства по DOM. Если вы тогда его сохранили, он должен быть практически
эквивалентен приведенному ниже.)

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.ParserConfigurationException;

import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

import org.w3c.dom.Document;
import org.w3c.dom.DOMException;

import java.io.*;

public class TransformationApp


{
static Document document;

public static void main(String argv[])


{
if (argv.length != 1) {
System.err.println (
"Usage: java TransformationApp filename");
System.exit (1);
}

Web-

Rendered by www.RenderX.com
Запись DOM в XML-файл Стр. 249 из 626

DocumentBuilderFactory factory =
DocumentBuilderFactory.newInstance();
//factory.setNamespaceAware(true);
//factory.setValidating(true);

try {
File f = new File(argv[0]);
DocumentBuilder builder =
factory.newDocumentBuilder();
document = builder.parse(f);

} catch (SAXParseException spe) {


// ,
System.out.println("\n** Parsing error"
+ ", line " + spe.getLineNumber()
+ ", uri " + spe.getSystemId());
System.out.println(" " + spe.getMessage() );

//
( )
Exception x = spe;
if (spe.getException() != null)
x = spe.getException();
x.printStackTrace();

} catch (SAXException sxe) {


// ,

// (
)
Exception x = sxe;
if (sxe.getException() != null)
x = sxe.getException();
x.printStackTrace();

} catch (ParserConfigurationException pce) {


//

pce.printStackTrace();

Web-

Rendered by www.RenderX.com
Стр. 250 из 626 XML Stylesheet Language for Transformations

} catch (IOException ioe) {


// I/O
ioe.printStackTrace();
}
} // main
}

9.4.2. Создание преобразователя


Следующим действием является создание преобразователя, который вы можете
использовать для передачи XML в System.out.
Примечание: Рассматриваемый в данном разделе код находится в файле Transformation-
App02.java. Этот файл выполняется с slideSample01.xml. Выходная информация находится
в файле TransformationLog02.txt. (Версии для броузера - slideSample01-xml.html и Transfor-
mationLog02.html.)
Начнем с добавления операторов import:

import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerConfigurationException;

import javax.xml.transform.dom.DOMSource;

import javax.xml.transform.stream.StreamResult;

import java.io.*;
Здесь вы добавили набор классов, которые должны теперь быть стандартным шаблоном:
сущность (Transformer), генератор для его создания (TransformerFactory) и исключительные
ситуации, которые могут быть сгенерированы каждым из них. Поскольку преобразование
всегда имеет источник и результат, далее добавляются классы, необходимые для
использования DOM в качестве источника (DomSource) и выходного потока в качестве
результата (StreamResult).
Далее, добавим код для выполнения преобразования:

try {
File f = new File(argv[0]);
DocumentBuilder builder = factory.newDocumentBuilder();
document = builder.parse(f);

// Transformer

Web-

Rendered by www.RenderX.com
Запись DOM в XML-файл Стр. 251 из 626

TransformerFactory tFactory =
TransformerFactory.newInstance();
Transformer transformer = tFactory.newTransformer();

DOMSource source = new DOMSource(document);


StreamResult result = new StreamResult(System.out);
transformer.transform(source, result);
Здесь вы создали объект преобразователя, использовали DOM для создания объекта
source и использовали System.out для создания объекта result.
Примечание: В данном случае "преобразователь" в действительности ничего не изменяет.
По терминологии XSLT вы используете тождественное преобразование, это означает, что
"преобразователь" генерирует неизмененную копию источника.
Наконец, добавьте выделенный ниже код для перехвата новых ошибок, которые могут
возникнуть:

} catch (TransformerConfigurationException tce) {


// ,
System.out.println ("* Transformer Factory error");
System.out.println(" " + tce.getMessage() );

//
( )
Throwable x = tce;
if (tce.getException() != null)
x = tce.getException();
x.printStackTrace();

} catch (TransformerException te) {


// ,
System.out.println ("* Transformation error");
System.out.println(" " + te.getMessage() );

//
( )
Throwable x = te;
if (te.getException() != null)
x = te.getException();
x.printStackTrace();

Web-

Rendered by www.RenderX.com
Стр. 252 из 626 XML Stylesheet Language for Transformations

} catch (SAXParseException spe) {


...
Примечания:
• TransformerExceptions выдаются объектом transformer.
• TransformerConfigurationExceptions выдаются генератором
• Для сохранения настройки DOCTYPE XML-документа необходимо добавить следующий
код:

import javax.xml.transform.OutputKeys;
...
if (document.getDoctype() != null){
String systemValue = (new

File(document.getDoctype().getSystemId())).getName();
transformer.setOutputProperty(
OutputKeys.DOCTYPE_SYSTEM, systemValue
);
}

9.4.3. Вывод XML


Инструкции по компиляции и выполнению программы вы найдете в разделе "Компиляция
и выполнение программы" руководства по SAX. (Если вы работаете самостоятельно,
замените "TransformApp" на "Echo" в имени программы. Если вы компилируете код примера,
используйте "TransformationApp02".) После выполнения программы с файлом slideSam-
ple01.xml вы должны увидеть следующую информацию:

<?xml version="1.0" encoding="UTF-8"?>


<!-- A SAMPLE set of slides -->
<slideshow author="Yours Truly" date="Date of publication"
title="Sample Slide Show">

<!-- TITLE SLIDE -->


<slide type="all">
<title>Wake up to WonderWidgets!</title>
</slide>

<!-- OVERVIEW -->


<slide type="all">
<title>Overview</title>
<item>Why <em>WonderWidgets</em> are great</item>

Web-

Rendered by www.RenderX.com
Запись DOM в XML-файл Стр. 253 из 626

<item/>
<item>Who <em>buys</em> WonderWidgets</item>
</slide>

</slideshow>
Примечание: Порядок атрибутов может меняться в зависимости от используемого
анализатора.
Дополнительная информация по настройке генератора и обработке ошибок верификации
находится в разделах "Чтение XML-данных в DOM", "Дополнительная информация".

9.4.4. Вывод поддерева DOM


Можно, также, работать с поддеревом DOM. В данном разделе руководства вы проведете
эксперимент с ним.
Примечание: Рассматриваемый в данном разделе код находится в файле Transformation-
App03.java. Выводимая информация - в файле TransformationLog03.txt. (Версия для броузера
- TransformationLog03.html.)
Единственным различием в процессе является то, что вы создадите DOMSource, используя
узел в DOM, а не полную DOM. Первым шагом будет импорт классов, необходимых для
получения нужного узла. Для этого добавьте выделенный ниже код:

import org.w3c.dom.Document;
import org.w3c.dom.DOMException;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
Следующее действие - найти хороший узел для эксперимента. Добавьте выделенный ниже
код для выбора первого элемента <slide>:

try {
File f = new File(argv[0]);
DocumentBuilder builder = factory.newDocumentBuilder();
document = builder.parse(f);

// <slide> DOM
NodeList list = document.getElementsByTagName("slide");
Node node = list.item(0);
Наконец, сделайте показанные ниже изменения для построения объекта source, состоящего
из поддерева, начинающегося с этого узла:

DOMSource source = new DOMSource(document);


DOMSource source = new DOMSource(node);

Web-

Rendered by www.RenderX.com
Стр. 254 из 626 XML Stylesheet Language for Transformations

StreamResult result = new StreamResult(System.out);


transformer.transform(source, result);
Теперь запустите приложение. Должна отобразиться следующая информация:

<?xml version="1.0" encoding="UTF-8"?>


<slide type="all">
<title>Wake up to WonderWidgets!</title>
</slide>

9.4.4.1. Очистка
Выполните показанные ниже изменения для отмены добавлений, сделанных вами в этом
разделе. (Эти изменения содержатся в файле TransformationApp04.java.)

Import org.w3c.dom.DOMException;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
...
try {
...
// <slide> DOM
NodeList list = document.getElementsByTagName("slide");
Node node = list.item(0);
...
DOMSource source = new DOMSource(node);
StreamResult result = new StreamResult(System.out);
transformer.transform(source, result);

9.4.5. Резюме
Вы узнали, как использовать преобразователь для записи DOM и как использовать
поддерево DOM в качестве объекта source для преобразования. В следующем разделе
вы увидите, как используется преобразователь для создания XML из любой структуры
данных, которую можно проанализировать.

9.5. Генерирование DOM из произвольной структуры данных


В этом разделе вы будете использовать XSLT для преобразования произвольной структуры
данных в XML.
Основные действия:
1. Вы измените существующую программу чтения данных так, чтобы она генерировала
SAX-события. (Сейчас не важно, является ли эта программа реальным анализатором,
или просто фильтром данных определенного вида.)

Web-

Rendered by www.RenderX.com
Генерирование DOM из произвольной структуры данных Стр. 255 из 626

2. Затем вы будете использовать "SAX-анализатор" для построения SAXSource для


преобразования.
3. Далее вы будете использовать для просмотра результатов точно такой же объект
StreamResult, какой вы создали в последнем упражнении. (Но заметьте, что вы также
легко могли бы создать объект DOMResult для построения DOM в памяти.)
4. В завершение вы соедините source с result, используя объект transformer для выполнения
преобразования.
Для начала вам необходим набор данных, которые вы хотите преобразовать, и программа,
способная читать данные. В следующих двух разделах вы создадите простой файл данных
и читающую его программу.

9.5.1. Создание простого файла


Начнем с создания набора данных для адресной книги. Вы можете, если хотите, повторить
процесс, или просто использовать данные, записанные в PersonalAddressBook.ldif.
Показанный ниже файл был сгенерирован путем создания новой адресной книги в Netscape
Messenger, добавления в нее некоторых фиктивных данных (одна визитка) и
экспортирования ее в формат LDIF.
Примечание: LDIF означает LDAP Data Interchange Format (формат обмена данными LDAP).
LDAP, в свою очередь, означает Lightweight Directory Access Protocol (упрощенный протокол
доступа к каталогам). Я предпочитаю думать о LDIF как о "Line Delimited Interchange Format"
("формат обмена с разделением строк"), поскольку это точно отражает его сущность.
На рисунке 1 показана созданная запись в адресной книге.

Web-

Rendered by www.RenderX.com
Стр. 256 из 626 XML Stylesheet Language for Transformations

Рисунок 1 Запись в адресной книге

Экспорт адресной книги создает показанный ниже файл. Интересующая нас часть файл
выделена жирным шрифтом.

dn: cn=Fred Flintstone,mail=fred@barneys.house


modifytimestamp: 20010409210816Z
cn: Fred Flintstone
xmozillanickname: Fred
mail: Fred@barneys.house
xmozillausehtmlmail: TRUE
givenname: Fred
sn: Flintstone
telephonenumber: 999-Quarry
homephone: 999-BedrockLane
facsimiletelephonenumber: 888-Squawk
pagerphone: 777-pager
cellphone: 555-cell
xmozillaanyphone: 999-Quarry
objectclass: top
objectclass: person
Обратите внимание, что каждая строка файла содержит имя переменной, двоеточие и
пробел, за которым следует значение переменной. Переменная sn содержит фамилию
человека, а переменная cn - поле DisplayName из записи адресной книги.

9.5.2. Создание простого анализатора


Следующее действие - создание программы, анализирующей данные.
Примечание: Рассматриваемый в данном разделе код находится в файле Address-
BookReader01.java. Выводимая информация - в AddressBookReaderLog01.txt.
Текст программы приведен ниже. Это очень простая программа, в которой нет даже цикла
для нескольких записей, потому что, в конце концов, это всего лишь демонстрационная
программа!

import java.io.*;

public class AddressBookReader


{

public static void main(String argv[])


{
//
if (argv.length != 1) {

Web-

Rendered by www.RenderX.com
Генерирование DOM из произвольной структуры данных Стр. 257 из 626

System.err.println (
"Usage: java AddressBookReader filename");
System.exit (1);
}
String filename = argv[0];
File f = new File(filename);
AddressBookReader01 reader = new AddressBookReader01();
reader.parse(f);
}

/** */
public void parse(File f)
{
try {
//

FileReader r = new FileReader(f);


BufferedReader br = new BufferedReader(r);

//
.
String line = br.readLine();
while (null != (line = br.readLine())) {
if (line.startsWith("xmozillanickname: "))
break;
}
output("nickname", "xmozillanickname", line);
line = br.readLine();
output("email", "mail", line);
line = br.readLine();
output("html", "xmozillausehtmlmail", line);
line = br.readLine();
output("firstname","givenname", line);
line = br.readLine();
output("lastname", "sn", line);
line = br.readLine();
output("work", "telephonenumber", line);
line = br.readLine();
output("home", "homephone", line);
line = br.readLine();
output("fax", "facsimiletelephonenumber",

Web-

Rendered by www.RenderX.com
Стр. 258 из 626 XML Stylesheet Language for Transformations

line);
line = br.readLine();
output("pager", "pagerphone", line);
line = br.readLine();
output("cell", "cellphone", line);

}
catch (Exception e) {
e.printStackTrace();
}
}

void output(String name, String prefix, String line)


{
int startIndex = prefix.length() + 2;
// 2=length of ": "
String text = line.substring(startIndex);
System.out.println(name + ": " + text);
}
}
Эта программа содержит три метода:
main
Mетод main получает имя файла из командной строки, создает экземпляр анализатора и
настраивает его для работы с анализируемым файлом. Этот метод будет удален после
преобразования программы в SAX-анализатор. (Это одна из причин выделения кода
анализа в отдельный метод.)
parse
Этот метод работает с объектом File, переданным ему процедурой main. Как можно увидеть,
он настолько прост, насколько может быть. Единственный поклон в сторону
производительности - использование BufferedReader, что может стать важным при
обработке больших файлов.
output
Метод output содержит логику для структуры строки. Начиная справа, он принимает три
аргумента. Первый аргумент передает имя для отображения, так что мы можем вывести
в качестве имени переменой "html" вместо "xmozillausehtmlmail". Второй аргумент передает
имя переменной, хранимой в файле (xmozillausehtmlmail). Третий аргумент передает строку,
содержащую данные. Процедура удаляет имя переменной из начала строки и выводит
желаемое имя плюс данные.
Выполнение программы с PersonalAddressBook.ldif выдает следующий результат:

nickname: Fred

Web-

Rendered by www.RenderX.com
Генерирование DOM из произвольной структуры данных Стр. 259 из 626

email: Fred@barneys.house
html: TRUE
firstname: Fred
lastname: Flintstone
work: 999-Quarry
home: 999-BedrockLane
fax: 888-Squawk
pager: 777-pager
cell: 555-cell
Думаю, что можно согласиться с тем, что это немного более читаемо.

9.5.3. Изменение анализатора для генерирования SAX-событий


Следующее действие - изменить анализатор для генерирования SAX-событий, так чтобы
вы могли использовать его как основу объекта SAXSource в XSLT transform.
Примечание: Рассматриваемый код находится в файле AddressBookReader02.java.
Начнем с импорта необходимых дополнительных классов:

import java.io.*;

import org.xml.sax.*;
import org.xml.sax.helpers.AttributesImpl;
Затем, измените приложение так, чтобы оно расширяло XmlReader. Эти изменения
преобразуют приложение в анализатор, генерирующий соответствующие SAX-события.

public class AddressBookReader


implements XMLReader
{
А сейчас удалите метод main. Он вам больше не понадобится.

public static void main(String argv[])


{
//
if (argv.length != 1) {
System.err.println ("Usage: Java AddressBookReader
filename");
System.exit (1);
}
String filename = argv[0];
File f = new File(filename);

Web-

Rendered by www.RenderX.com
Стр. 260 из 626 XML Stylesheet Language for Transformations

AddressBookReader02 reader = new AddressBookReader02();


reader.parse(f);
}
Добавьте глобальные переменные, которые нам вскоре пригодятся:

public class AddressBookReader


implements XMLReader
{
ContentHandler handler;

//

// .
String nsu = ""; // NamespaceURI
Attributes atts = new AttributesImpl();
String rootElement = "addressbook";

String indent = "\n "; // for readability!


SAX ContentHandler является объектом, который будет получать SAX-события,
генерируемые анализатором. Для преобразования приложения в XmlReader вы определите
метод setContentHandler. Переменная handler будет содержать ссылку на объект,
переданный при вызове setContentHandler.
Когда анализатор генерирует события SAX-элемента, необходимо предоставить
информацию о пространстве имен и атрибуте. Поскольку это простое приложение, вы
определяете нулевые значения для каждого из них.
Вы, также, определяете корневой элемент для структуры данных (addressbook) и
устанавливаете строку табуляции для повышения читаемости выводимой информации.
Затем, измените метод parse так, чтобы он принимал InputSource (а не File) в качестве
аргумента, и перечислите исключительные ситуации, которые он может сгенерировать:

public void parse(File f)InputSource input)


throws IOException, SAXException
Теперь сделайте показанные ниже изменения для получения считывателя,
инкапсулированного в объект InputSource.

try {
//

FileReader r = new FileReader(f);

Web-

Rendered by www.RenderX.com
Генерирование DOM из произвольной структуры данных Стр. 261 из 626

java.io.Reader r = input.getCharacterStream();
BufferedReader Br = new BufferedReader(r);
Примечание: В следующем разделе вы создадите входной объект source и все, что вы
поместите в него, фактически и будет BufferedReader. Но AddressBookReader мог бы когда-
нибудь использоваться кем-то еще. Это действие дает гарантию того, что обработка будет
эффективной независимо от используемого считывателя.
Следующее действие - изменить метод parse для генерирования SAX-событий в начале
документа и корневого элемента. Для этого добавьте выделенный ниже код:

/** */
public void parse(InputSource input)
...
{
try {
...
//
.
String line = br.readLine();
while (null != (line = br.readLine())) {
if (line.startsWith("xmozillanickname: ")) break;
}

if (handler==null) {
throw new SAXException("No content handler");
}

handler.startDocument();
handler.startElement(nsu, rootElement,
rootElement, atts);

output("nickname", "xmozillanickname", line);


...
output("cell", "cellphone", line);

handler.ignorableWhitespace("\n".toCharArray(),
0, //
1 //
);
handler.endElement(nsu, rootElement, rootElement);
handler.endDocument();
}

Web-

Rendered by www.RenderX.com
Стр. 262 из 626 XML Stylesheet Language for Transformations

catch (Exception e) {
...
Здесь вы, прежде всего, проверили, правильно ли настроен анализатор на ContentHandler.
(В данном приложении мы больше ни о чем не беспокоимся.) Затем вы сгенерировали
события для начала документа и корневого элемента, и, в завершение, послали конечные
события для корневого элемента и для документа.
Некоторые моменты достойны внимания:
• Мы не беспокоимся о посылке события setDocumentLocator, поскольку оно не
обязательно. Там, где это важно, оно могло бы посылаться непосредственно перед
событием startDocument.
• Мы сгенерировали событие ignorableWhitespace перед концом корневого элемента. Это
тоже не обязательно, но значительно улучшает читаемость выводимой информации,
что вы вскоре увидите. (В данном случае пробел состоит из одного символа новой
строки, который передается также, как символы передаются в метод characters: в виде
массива символов, начального индекса и длины.)
Теперь, когда для документа и корневого элемента генерируются SAX-события, изменим
метод output для генерирования соответствующих событий элемента для каждого элемента
данных. Выполните следующие изменения:

void output(String name, String prefix, String line)


throws SAXException
{
int startIndex = prefix.length() + 2; // 2= ": "
String text = line.substring(startIndex);
System.out.println(name + ": " + text);

int textLength = line.length() - startIndex;


handler.ignorableWhitespace(indent.toCharArray(),
0, //
indent.length()
);
handler.startElement(nsu, name, name /*"qName"*/, atts);
handler.characters(line.toCharArray(),
startIndex,
textLength);
handler.endElement(nsu, name, name);
}
Поскольку методы ContentHandler могут передавать SAXException обратно анализатору,
анализатор должен быть готов их обработать. В данном случае мы их не ожидаем, поэтому
мы просто позволим приложению закончить работу по ошибке при их возникновении.

Web-

Rendered by www.RenderX.com
Генерирование DOM из произвольной структуры данных Стр. 263 из 626

Затем вы вычисляете длину данных и опять генерируете несколько игнорируемых пробелов


для повышения читаемости. В данном случае существует только один уровень данных,
поэтому мы можем использовать строку с фиксированной длиной табуляции. (Если бы
данные были более структурированы, нам бы пришлось вычислять размеры табуляции в
зависимости от вложенности данных.)
Примечание: Строка табуляции не влияет на данные, но облегчает чтение выводимой
информации. После того как все заработает, попробуйте сгенерировать результат без
этой строки! Все элементы будут соединены один с другим, например: <addressbook><nick-
name>Fred</nickname><email>…
Далее, добавьте метод, настраивающий анализатор на ContentHandler, который должен
принимать генерируемые им события:

void output(String name, String prefix, String line)


throws SAXException
{
...
}

/**
. */
public void setContentHandler(ContentHandler handler) {
this.handler = handler;
}

/**
. */
public ContentHandler getContentHandler() {
return this.handler;
}
Существует еще несколько методов, которые должны быть реализованы для интерфейса
XmlReader. Для задач этого приложения мы создадим нулевые методы для них всех. Для
рабочего приложения вы, возможно, решите реализовать методы обработчика ошибок
для создания более надежного приложения. А сейчас просто добавьте выделенный ниже
код для создания нулевых методов:

/**
. */
public void setErrorHandler(ErrorHandler handler)
{ }

/** .

Web-

Rendered by www.RenderX.com
Стр. 264 из 626 XML Stylesheet Language for Transformations

*/
public ErrorHandler getErrorHandler()
{ return null; }
Наконец, добавьте выделенный ниже код, создающий нулевые методы для оставшегося
интерфейса XmlReader. (Большинство из них имеют значение для реального SAX-
анализатора, но не имеют смысла для похожего на наше приложение по преобразованию
данных.)

/** XML-
(URI). */
public void parse(String systemId)
throws IOException, SAXException
{ }

/**
DTD- . */
public DTDHandler getDTDHandler()
{ return null; }

/**
. */
public EntityResolver getEntityResolver()
{ return null; }

/**
. */
public void setEntityResolver(EntityResolver resolver)
{ }

/**
DTD- . */
public void setDTDHandler(DTDHandler handler)
{ }

/** . */
public Object getProperty(String name)
{ return null; }

/** . */
public void setProperty(String name, Object value)

Web-

Rendered by www.RenderX.com
Генерирование DOM из произвольной структуры данных Стр. 265 из 626

{ }

/** . */
public void setFeature(String name, boolean value)
{ }

/** . */
public boolean getFeature(String name)
{ return false; }
Поздравляем! Вы имеете анализатор, который можно использовать для генерации SAX-
событий. В следующем разделе вы будете использовать его для построения объекта
SAXSource, который позволит вам преобразовать данные в XML.

9.5.4. Использование анализатора в качестве SAXSource


Используя SAX-анализатор в качестве источника событий, вы можете (легко!) создать
преобразователь для генерирования результата. В этом разделе вы модифицируете
TransformerApp, с которым вы работали, для создания выходного потока result, хотя вы
можете также легко создать DOM result.
Примечание: Рассматриваемый в этом разделе код находится в файле Transformation-
App04.java. Выводимая информация находится в файле TransformationLog04.txt.
Важно!
Убедитесь, что вы отложили AddressBookReader и открыли TransformationApp. В этом
разделе мы работаем с TransformationApp! (Эти приложения очень похожи, поэтому легко
ошибиться.)
Начнем с показанных ниже изменений в операторах импорта классов, необходимых для
создания объекта SAXSource. (Вам сейчас не понадобятся классы DOM, поэтому они
вычеркнуты, хотя вы не причините никакого вреда, оставив их.)

import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource;
import org.w3c.dom.Document;
import org.w3c.dom.DOMException;
...
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stream.StreamResult;
Затем, удалите несколько других пережитков наших упражнений с DOM и добавьте код
для создания экземпляра AddressBookReader:

Web-

Rendered by www.RenderX.com
Стр. 266 из 626 XML Stylesheet Language for Transformations

public class TransformationApp


{
// ,

static Document document;

public static void main(String argv[])


{
...
DocumentBuilderFactory factory =
DocumentBuilderFactory.newInstance();
//factory.setNamespaceAware(true);
//factory.setValidating(true);

// sax " ".


AddressBookReader saxReader = new AddressBookReader();

try {
File f = new File(argv[0]);
DocumentBuilder builder =
factory.newDocumentBuilder();
document = builder.parse(f);
Знаете что! Вы почти у цели. Еще совсем немного действий. Добавьте выделенный ниже
код для создания объекта SAXSource:

// Transformer
...
Transformer transformer = tFactory.newTransformer();

//
SAX
FileReader fr = new FileReader(f);
BufferedReader br = new BufferedReader(fr);
InputSource inputSource = new InputSource(br);
SAXSource source = new SAXSource(saxReader, inputSource);

StreamResult result = new StreamResult(System.out);


transformer.transform(source, result);

Web-

Rendered by www.RenderX.com
Генерирование DOM из произвольной структуры данных Стр. 267 из 626

Здесь вы создали BufferedReader (который упоминался ранее) и заключили его во входной


объект source. Затем вы создали объект SAXSource, передав в него считыватель и объект
InputSource, а также передали его в преобразователь.
Во время выполнения приложения преобразователь настроит себя как ContentHandler для
SAX-анализатора (AddressBookReader) и укажет анализатору работать с объектом input-
Source. События, генерируемые анализатором, будут затем передаваться преобразователю,
который выполнит соответствующие действия и передаст данные в объект result.
Наконец, удалите не нужные больше исключительные ситуации, поскольку Transformation-
App больше не генерирует их:

catch (SAXParseException spe) {


// Error generated by the parser
System.out.println("\n** Parsing error"
+ ", line " + spe.getLineNumber()
+ ", uri " + spe.getSystemId());
System.out.println(" " + spe.getMessage() );

//
,
Exception x = spe;
if (spe.getException() != null)
x = spe.getException();
x.printStackTrace();

} catch (SAXException sxe) {


//

// ( )
Exception x = sxe;
if (sxe.getException() != null)
x = sxe.getException();
x.printStackTrace();

} catch (ParserConfigurationException pce) {


// Parser with specified options can't be built
pce.printStackTrace();

} catch (IOException ioe) {


...
Все! Вы создали преобразователь, который будет использовать SAXSource в качестве
источника и создавать StreamResult для выводимой информации.

Web-

Rendered by www.RenderX.com
Стр. 268 из 626 XML Stylesheet Language for Transformations

9.5.5. Выполнение преобразования


Теперь запустите приложение с файлом адресной книги. Должна отобразиться примерно
такая информация:

<?xml version="1.0" encoding="UTF-8"?>


<addressbook>
<nickname>Fred</nickname>
<email>fred@barneys.house</email>
<html>TRUE</html>
<firstname>Fred</firstname>
<lastname>Flintstone</lastname>
<work>999-Quarry</work>
<home>999-BedrockLane</home>
<fax>888-Squawk</fax>
<pager>777-pager</pager>
<cell>555-cell</cell>
</addressbook>
Вы успешно преобразовали существующую структуру данных в XML. И это было совсем
не трудно. Поздравляем!

9.6. Преобразование XML-данных при помощи XSLT


XML Stylesheet Language for Transformation (XSLT) может использоваться для многих целей.
Например, с достаточно интеллектуальной таблицей стилей вы можете сгенерировать
выходные данные формата PDF или PostScript из XML-данных. Но обычно XSLT
используется для генерирования форматированного HTML-вывода, или для создания
альтернативного XML-представления данных.
В этом разделе руководства вы будете использовать XSLT-преобразование для трансляции
XML-данных в HTML.
Примечание: Спецификация XSLT большая и сложная. Поэтому это руководство может
только затронуть основы. Здесь вы получите достаточно базовых знаний и сможете решать
простые задачи XSLT-преобразований. Вы должны, также, получить хороший старт для
дальнейшего изучения XSLT. Более глубокие основы по предмету вы можете получить в
хорошем справочном руководстве, таком как XSLT Programmer's Reference от Michael Kay.

9.6.1. Определение простого типа документа <article>


Мы начнем с определения простого типа документа, который мог бы быть использован
для написания статей. Наши документы <article> будут содержать следующие структурные
теги:
• <TITLE> - Название статьи
• <SECT> - Секция, содержащая заголовок и тело
• <PARA> - Параграф

Web-

Rendered by www.RenderX.com
Преобразование XML-данных при помощи XSLT Стр. 269 из 626

• <LIST> - Список
• <ITEM> - Запись в списке
• <NOTE> - Отступление, которое может быть смещением от основного текста
Немного необычным в этой структуре является то, что мы не хотим создавать отдельный
тег элемента для заголовка секции. Такие элементы обычно создаются для того, чтобы
отличить текст заголовка (и любых тегов, которые он содержит) от тела секции (то есть,
любых структурных элементов, расположенных ниже заголовка).
Вместо этого мы разрешаем заголовку плавно переходить в тело секции. Это добавляет
некоторую сложность в таблицу стилей, но дает нам шанс исследовать механизмы выбора
шаблонов XSLT. Это также соответствует нашему интуитивному представлению структуры
документа, где текст заголовка непосредственно предшествует структурным элементам,
что может упростить структурное редактирование.
Примечание: Однако такую структуру верифицировать не легко, поскольку XML-модель
смешанного содержимого позволяет тексту находиться в любом месте секции, тогда как
мы хотим ограничить текст и встроенные элементы так, чтобы они появлялись только
перед первым структурным элементом тела секции. Основанный на утверждениях
верификатор (Schematron) может сделать это, но большинство других механизмов
верификации - нет. Поэтому мы обойдемся без определения DTD для типа документов.
В этой структуре секции могут быть вложенными. Глубина вложения будет определять
тип HTML-форматирования, используемого для заголовка секций (например, h1 или h2).
Использование простого тега SECT (вместо пронумерованных секций) также полезно при
структурном редактировании, потому что дает возможность перемещать секции по желанию
без необходимости беспокоится об изменении нумерации для этой секции или для любых
других секций, которые могут быть затронуты перемещением.
Для списков мы будем использовать атрибут type для указания следующих видов списка:
unordered (список с буллитами), alpha (пронумерованный маленькими буквами), ALPHA
(пронумерованный большими буквами), numbered.
Мы, также, разрешим использование некоторых встроенных тегов, изменяющих внешний
вид текста:
• <B> - жирный
• <I> - наклонный
• <U> - подчеркнутый
• <DEF> - определение
• <LINK> - ссылка на URL
Примечание: Встроенный тег не генерирует конец строки, поэтому изменение стиля,
вызванное встроенным тегом, не влияет на поток текста на странице (хотя влияет на его
внешний вид). С другой стороны, структурный тег разделяет новый сегмент текста, поэтому
как минимум он всегда генерирует конец строки в дополнение к другим изменениям
формата.
Тег <DEF> будет использоваться для терминов, определенных в тексте. Такие термины
будут отображаться наклонным шрифтом, как принято в документах. Но использование
специального тега в XML позволит программе индексации найти такие определения и

Web-

Rendered by www.RenderX.com
Стр. 270 из 626 XML Stylesheet Language for Transformations

добавить их к индексу вместе с ключевыми словами в заголовке. Например, в Note


определения встроенных и структурных тегов могут быть отмечены тегом <DEF> для
дальнейшей индексации.
Наконец, тег LINK выполняет две задачи. Во-первых, он позволит нам создать ссылку на
URL без необходимости указывать его дважды, то есть, мы можем записать
<link>http//…</link> вместо <a href="http//…">http//…</a>. Мы также захотим, чтобы форма
выглядела так: <link target="…">…name…</link>. Это вторая задача тега <link> - он даст
нам возможность применять условные выражения в XSLT.
Примечание: Хотя структура статьи чрезвычайно проста (состоит только из 11 тегов), она
поднимает достаточно интересные проблемы и дает хороший обзор основных возможностей
XSLT. Но мы, все-таки, оставим без внимания большую часть спецификации. В последнем
разделе этого руководства мы отметим основные пропущенные нами функции.

9.6.2. Создание тестового документа


Создадим простой тестовый документ, используя элементы <SECT>, несколько элементов
<PAPA>, элемент <NOTE>, <LINK> и <LIST type="unordered">. Идея состоит в создании
документа, использующего все теги, для того чтобы мы смогли исследовать наиболее
интересные механизмы трансляции.
Примечание: Пример данных находится в файле article1.xml (Версия для броузера - article-
xml.html.)
Для создания тестового документа создадим файл с именем article.xml и введем показанные
ниже XML-данные.

<?xml version="1.0"?>
<ARTICLE>
<TITLE>A Sample Article</TITLE>
<SECT>The First Major Section
<PARA>This section will introduce a subsection.</PARA>
<SECT>The Subsection Heading
<PARA>This is the text of the subsection.
</PARA>
</SECT>
</SECT>
</ARTICLE>
Обратите внимание на то, что в XML-файле подсекции полностью содержатся внутри
основной секции. (В HTML, с другой стороны, заголовки не содержат тела секции.) В
результате получается структура, которую тяжело редактировать в обычной форме, но
намного легче редактировать с помощью структурного редактора.
Имея ориентированный на древовидную структуру XML-редактор, понимающий встроенные
теги, подобные <B> и <I>, будет возможно редактировать статью этого типа в
табулированной форме без необходимости сложной таблицы стилей. (Такой редактор
позволил бы писателю сконцентрироваться на структуре статьи, оставляя создание схемы

Web-

Rendered by www.RenderX.com
Преобразование XML-данных при помощи XSLT Стр. 271 из 626

на более поздний срок.) В таком редакторе вышеприведенный фрагмент статьи выглядел


бы примерно так:

<ARTICLE>
<TITLE>A Sample Article
<SECT>The First Major Section
<PARA>This section will introduce a subsection.
<SECT>The Subheading
<PARA>This is the text of the subsection. Note that
...
Примечание: В настоящее время редакторы, понимающие древовидную структуру,
существуют, но они интерпретируют встроенные теги, подобные <B> и <I>, так же как и
остальные структурные теги, что может сделать "табулированную" структуру немного
трудной для чтения.

9.6.3. Написание XSLT-преобразования


В этом разделе руководства вы начнете писать XSLT-преобразование, которое будет
преобразовывать XML-статью и отображать ее в HTML.
Примечание: Рассматриваемое в этом разделе преобразование находится в файле arti-
cle1a.xsl. (Версия для броузера - article1a-xsl.html.)
Начнем с создания обычного XML-документа:

<?xml version="1.0" encoding="ISO-8859-1"?>


Затем добавьте выделенные ниже строки для создания таблицы стилей XSL:

<?xml version="1.0" encoding="ISO-8859-1"?>


<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0"
>

</xsl:stylesheet>
А сейчас настройте ее для генерирования HTML-совместимого вывода:

<xsl:stylesheet
...
>
<xsl:output method="html"/>

...

Web-

Rendered by www.RenderX.com
Стр. 272 из 626 XML Stylesheet Language for Transformations

</xsl:stylesheet>
Мы детально объясним необходимость этой записи позже. А сейчас отметим, что если вы
хотите выводить что-либо, кроме формально-правильного XML, подобный тег <xsl:output>,
указывающий либо "text", либо "html", необходим. (Значение по умолчанию - "xml".)
Примечание: При указании XML-вывода вы можете добавить атрибут indent для
генерирования красивого протабулированного XML-вывода. Спецификация выглядит так:
<xsl:output method="xml" indent="yes"/>.

9.6.4. Обработка основных структурных элементов


Вы начнете заполнять таблицу стилей обработкой элементов, которые участвуют в создании
содержания документа - корневого элемента, элемента названия и заголовков. Вы, также,
обработаете элемент PARA, определенный в тестовом документе.
Примечание: Если при первом чтении вы пропустили раздел этого руководства,
обсуждающий механизм адресации XPath - "Как работает XPath", - сейчас самое время
вернуться назад и прочитать его.
Начните с добавления основной команды, обрабатывающей корневой элемент:

<xsl:template match="/">
<html><body>
<xsl:apply-templates/>
</body></html>
</xsl:template>

</xsl:stylesheet>
Новые XSL-команды выделены жирным шрифтом. (Обратите внимание, что они определены
в пространстве имен "xsl".) Команда <xsl:apply-templates> обрабатывает потомков текущего
узла. В данном случае текущим узлом является корневой узел.
Несмотря на свою простоту, этот пример иллюстрирует некоторое количество важных
идей, поэтому стоит его внимательно рассмотреть. Первым моментом является то, что
таблица стилей содержит несколько шаблонов, определенных тегом <xsl:template>. Каждый
шаблон содержит атрибут match, который выбирает элементы, к которым относится шаблон,
используя механизм адресации XPath, описанный в разделе "Как работает XPath".
Внутри шаблона теги, не начинающиеся с префикса пространства имен xsl:, просто
копируются. Следующие за ними символы новой строки и пробелы тоже копируются, что
делает выводимый результат более читаемым.
Примечание: Если отсутствует символ новой строки, пробел обычно игнорируется. Чтобы
в данном случае включить пробел в вывод, или включить другой текст, можно использовать
тег <xsl:text>. По существу таблица стилей XSLT ожидает обработки тегов. Поэтому все,
что она видит, должно быть либо тегом <xsl:..>, либо каким-то другим тегом, либо пробелом.

Web-

Rendered by www.RenderX.com
Преобразование XML-данных при помощи XSLT Стр. 273 из 626

В данном случае не XSL-теги являются тегами HTML. Поэтому при совпадении корневого
тега XSLT выводит начальный тег HTML, обрабатывает все шаблоны, относящиеся к
потомкам корневого тега, и затем выводит завершающий тег HTML.

9.6.4.1. Обработка элемента <TITLE>


Добавьте шаблон для обработки названия статьи:

<xsl:template match="/ARTICLE/TITLE">
<h1 align="center"> <xsl:apply-templates/> </h1>
</xsl:template>

</xsl:stylesheet>
В данном случае вы указываете полный путь к элементу TITLE и выводите несколько
HTML-тегов для оформления текста названия в большой, отцентрированный заголовок.
В данном случае тег apply-templates гарантирует, что если название содержит какой-либо
встроенный тег, например наклонный шрифт, ссылки или подчеркивания, они тоже будут
обработаны.
Более важно, что команда apply-templates вызывает обработку текста названия. Аналогично
модели данных DOM, модель данных XSLT основана на концепции содержания текстовых
узлов в узлах элементов (которые, в свою очередь, могут содержаться в других узлах
элементов и т.д.). Эта иерархическая структура составляет исходное дерево. Существует
также дерево результата, содержащее выводимые данные.
XSLT работает, преобразуя исходное дерево в дерево результата. Для визуализации
результата XSLT-операций полезно понимать структуру этих деревьев и их содержимое.
(Более подробная информация по этому предмету приведена в разделе "Модель данных
XSLT/XPath".)

9.6.4.2. Обработка заголовков


Для продолжения обработки основных структурных элементов добавьте шаблон обработки
заголовков верхнего уровня:

<xsl:template match="/ARTICLE/SECT">
<h2> <xsl:apply-templates
select="text()|B|I|U|DEF|LINK"/> </h2>
<xsl:apply-templates select="SECT|PARA|LIST|NOTE"/>
</xsl:template>

</xsl:stylesheet>
Здесь вы указали путь к самым верхним элементам SECT. Но сейчас вы применили
шаблоны в два шага, используя атрибут select. На первом шаге вы выбрали текстовые
узлы при помощи функции XPath text(), так же как и встроенные элементы, такие как B и
I. (Символ вертикальной черты (|) используется для соответствия нескольких элементов

Web-

Rendered by www.RenderX.com
Стр. 274 из 626 XML Stylesheet Language for Transformations

- текста, или тега bold, или тега italics и т.д.) На втором шаге вы выбрали другие структурные
элементы, содержащиеся в файле - секции, параграфы, списки и узлы.
Использование атрибута select позволяет разместить текст и встроенные элементы между
тегами <h2>…</h2>, гарантируя, что все структурные теги в секции будут обработаны
после них. Другими словами, вы гарантируете, что вложение заголовков в XML-документе
не отражается на HTML-форматировании, что важно для вывода в формате HTML.
В общем случае использование предложения select позволяет вам применить все шаблоны
к набору информации, доступному в текущем контексте. Например, этот шаблон выбирает
все атрибуты текущего узла:

<xsl:apply-templates select="@*"/></attributes>
Далее, добавьте практически идентичный шаблон для обработки подзаголовков, вложенных
на один уровень глубже:

<xsl:template match="/ARTICLE/SECT/SECT">
<h3> <xsl:apply-templates
select="text()|B|I|U|DEF|LINK"/> </h3>
<xsl:apply-templates select="SECT|PARA|LIST|NOTE"/>
</xsl:template>

</xsl:stylesheet>

9.6.4.3. Генерирование сообщений времени выполнения


Вы, также, можете добавить шаблоны для заголовков более глубокого уровня, но в какой-
то момент вы должны остановиться, поскольку HTML поддерживает пять уровней. Но для
данного примера мы остановимся на двух уровнях заголовков секций. А если исходный
XML будет содержать третий уровень, мы передадим пользователю сообщение об ошибке.
В этом разделе показано, как это сделать.
Примечание: Мы могли бы продолжить обработку элементов SECT более низких уровней
путем выбора их выражением /SECT/SECT//SECT. Символы // выбирают любые элементы
SECT на любой глубине, как определено механизмом адресации XPath. Но вместо этого
мы воспользуемся возможностью поработать с сообщениями.
Добавьте следующий шаблон для генерирования ошибки при обнаружении секции,
вложенной слишком глубоко:

<xsl:template match="/ARTICLE/SECT/SECT/SECT">
<xsl:message terminate="yes">
Error: Sections can only be nested 2 deep.
</xsl:message>
</xsl:template>

</xsl:stylesheet>

Web-

Rendered by www.RenderX.com
Преобразование XML-данных при помощи XSLT Стр. 275 из 626

Предложение terminate= "yes" вызывает остановку процесса преобразования после


генерирования сообщения. Без этого обработка могла бы продолжаться, а все в этой
секции было бы проигнорировано.
В качестве дополнительного упражнения вы могли бы расширить таблицу стилей для
обработки секций, вложенных до четвертого уровня, генерируя теги <h2>…<h5>.
Генерируйте ошибку при любой секции, вложенной на более чем пятый уровень.
Наконец, завершите таблицу стилей, добавив шаблон для обработки тега PARA:

<xsl:template match="PARA">
<p><xsl:apply-templates/></p>
</xsl:template>

</xsl:stylesheet>

9.6.5. Написание основной программы


В этой части руководства вы модифицируете программу, которая использовала XSLT для
вывода неизмененного XML-файла, так, чтобы она использовала вашу таблицу стилей.
Примечание: Код, содержащийся в данном разделе, находится в файле Stylizer.java.
Результат работы - в stylizer1a.html. (Версия для броузера - sttylizer1a-src.html.)
Начните с копирования TransformationApp02, который анализирует XML-файл и записывает
результат в System.out. Сохраните его под именем Stylizer.java.
Затем, измените имя класса и секцию usage в программе:

public class TransformationAppStylizer


{
if (argv.length != 1 2) {
System.err.println (
"Usage: java TransformationApp filename");
"Usage: java Stylizer stylesheet xmlfile");
System.exit (1);
}
...
Далее, измените программу для использования таблицы стилей при создании объекта
Transformer.

...
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamSource;
import javax.xml.transform.stream.StreamResult;
...

Web-

Rendered by www.RenderX.com
Стр. 276 из 626 XML Stylesheet Language for Transformations

public class Stylizer


{
...
public static void main (String argv[])
{
...
try {
File f = new File(argv[0]);
File stylesheet = new File(argv[0]);
File datafile = new File(argv[1]);

DocumentBuilder builder =
factory.newDocumentBuilder();
document = builder.parse(f datafile);
...
StreamSource stylesource =
new StreamSource(stylesheet);
Transformer transformer =
Factory.newTransformer(stylesource);
...
Этот код использует файл для создания объекта StreamSource и передает исходный объект
в класс генератора для создания преобразователя.
Примечание: Вы можете немного упростить код, удалив полностью класс DOMSource.
Вместо создания объекта DOMSource для XML-файла создайте для него объект Stream-
Source, так же как и для таблицы стилей.
Откомпилируйте и выполните программу, используя article1a.xsl с article1.xml. Результат
работы должен выглядеть примерно так:

<html>
<body>

<h1 align="center">A Sample Article</h1>

<h2>The First Major Section

</h2>

<p>This section will introduce a subsection.</p>


<h3>The Subsection Heading

Web-

Rendered by www.RenderX.com
Преобразование XML-данных при помощи XSLT Стр. 277 из 626

</h3>

<p>This is the text of the subsection.

</p>

</body>
</html>
В выводе существует порядочное число лишних пробелов. В следующем разделе вы
узнаете, как удалить большинство из них.

9.6.6. Удаление пробелов


Если вы помните, при изучении структуры DOM мы видели, что в ней было много текстовых
узлов, не содержащих ничего, кроме игнорируемых пробелов. Большинство из лишних
пробелов в выводе возникают из-за этих узлов. К счастью, XSL предоставляет способ
удаления их. (Более подробная информация о структуре узла находится в разделе "Модель
данных XSLT/XPath".)
Примечание: Описанная здесь таблица стилей находится в файле article1b.xsl. Результат
работы - в stylizer1b.html. (Версии для броузера - article1b-xsl.html и stylizer1b-src.html.)
Для удаления некоторых лишних пробелов добавьте выделенную ниже строку в таблицу
стилей.

<xsl:stylesheet ...
>
<xsl:output method="html"/>
<xsl:strip-space elements="SECT"/>
...
Эта команда указывает XSL удалить любые текстовые узлы под элементом SECT, не
содержащим ничего, кроме пробелов. Узлы, содержащие текст не из пробелов, затронуты
не будут, как и другие типы узлов.
Теперь после выполнения программы результат будет выглядеть примерно так:

<html>
<body>

<h1 align="center">A Sample Article</h1>

<h2>The First Major Section


</h2>
<p>This section will introduce a subsection.</p>
<h3>The Subsection Heading

Web-

Rendered by www.RenderX.com
Стр. 278 из 626 XML Stylesheet Language for Transformations

</h3>
<p>This is the text of the subsection.
</p>

</body>
</html>
Это действительно улучшение. Еще остались символы новой строки и пробелы после
заголовков, но их выводит сам XML:

<SECT>The First Major Section


____<PARA>This section will introduce a subsection.</PARA>
^^^^
Здесь вы можете увидеть, что заголовок секции заканчивается символом новой строки и
пробелами табуляции перед началом записи PARA. Это не большая проблема, поскольку
броузеры, которые будут обрабатывать HTML, сжимают и игнорируют лишние пробелы.
Но есть еще одно средство форматирования в нашем распоряжении.
Примечание: Рассмотренная здесь таблица стилей находится в файле article1c.xsl.
Результат работы - в stylizer1c.html. (Версии для броузера - article1c-xsl.html и stylizer1c-
src.html.)
Для избавления от этого последнего пробела добавьте следующий шаблон в таблицу
стилей:

<xsl:template match="text()">
<xsl:value-of select="normalize-space()"/>
</xsl:template>

</xsl:stylesheet>
Результат работы теперь выглядит примерно так:

<html>
<body>
<h1 align="center">A Sample Article</h1>
<h2>The First Major Section</h2>
<p>This section will introduce a subsection.</p>
<h3>The Subsection Heading</h3>
<p>This is the text of the subsection.</p>
</body>
</html>

Web-

Rendered by www.RenderX.com
Преобразование XML-данных при помощи XSLT Стр. 279 из 626

Уже лучше. Конечно, было бы еще лучше, если бы присутствовала табуляция, но ее


сделать труднее, чем можно было бы ожидать! Вот некоторые возможные подходы вместе
с их проблемами:
Свойство indent
К сожалению, свойство indent= "yes", которое может быть применено к XML-выводу,
недоступно для HTML-вывода. Если бы даже это свойство было доступно, это бы не
помогло, поскольку HTML-элементы редко бывают вложенными! Хотя HTML-документ
часто табулирован для отображения неявной структуры, теги HTML сами по себе не
являются вложенными для создания реальной структуры.
Переменные табуляции
Функция <xsl:text> позволяет вам добавить любой желаемый текст, включая пробелы.
Поэтому, его потенциально можно было бы использовать для вывода пробелов для
табуляции. Проблемой является изменение количества пробелов. Использование XSLT-
переменных само по себе кажется хорошей идеей, но они здесь не работают. Причина -
при присваивании значения переменной в шаблоне это значение известно только внутри
этого шаблона (статическое значение, или значение времени компиляции). Даже если
переменная объявлена глобальной, присвоенное значение не хранится способом,
позволяющим динамически узнавать это значение в других шаблонах во время выполнения.
Как только <apply-templates/> вызывает другие шаблоны, любые установки переменных,
сделанных в других шаблонах, становятся недоступными.
Параметризованные шаблоны
Использование "параметризованного шаблона" - еще один способ изменения поведения
шаблона. Но определение количества пробелов табуляции для передачи в качестве
параметра остается основной проблемой!
Следовательно, в настоящее время, кажется, нет хорошего способа управлять табуляцией
HTML-вывода. Это будет проблемой при необходимости отображения или редактирования
HTML-файла как обычного текста. Но это не является проблемой, если вы для
редактирования используете XML-форму, а HTML используете только для отображения
в броузере. (При просмотре, например, stylizer1c.html, вы увидите ожидаемые результаты.)

9.6.7. Обработка оставшихся структурных элементов


В этом разделе вы будете обрабатывать элементы LIST и NOTE, которые добавляют
дополнительную структуру в статью.
Примечание: Рассматриваемый здесь пример документа находится в файле article2.xml,
а таблица стилей для его обработки находится в файле article2.xsl. Результат - в styl-
izer2.html. (Версии для броузера - article2-xml.html, article2-xsl.html и stylizer20src.html.)
Начните с добавления некоторых тестовых данных в пример документа:

<?xml version="1.0"?>
<ARTICLE>
<TITLE>A Sample Article</TITLE>
<SECT>The First Major Section
...

Web-

Rendered by www.RenderX.com
Стр. 280 из 626 XML Stylesheet Language for Transformations

</SECT>
<SECT>The Second Major Section
<PARA>This section adds a LIST and a NOTE.
<PARA>Here is the LIST:
<LIST type="ordered">
<ITEM>Pears</ITEM>
<ITEM>Grapes</ITEM>
</LIST>
</PARA>
<PARA>And here is the NOTE:
<NOTE>Don't forget to go to the hardware store
on your way to the grocery!
</NOTE>
</PARA>
</SECT>
</ARTICLE>
Примечание: Хотя список и примечание в XML-файле содержатся в соответствующих
параграфах, в действительности нет различий, содержатся они там или нет - в любом
случае генерируемый HTML будет одним и тем же. Но такая структура облегчит работу с
ними в структурном редакторе.

9.6.7.1. Изменение шаблона <PARA>


Затем, измените шаблон <PARA> для учета того, что мы разрешаем некоторым структурным
элементам находиться внутри параграфа:

<xsl:template match="PARA">
<p><xsl:apply-templates/></p>
<p> <xsl:apply-templates select="text()|B|I|U|DEF|LINK"/>
</p>
<xsl:apply-templates select="PARA|LIST|NOTE"/>
</xsl:template>
Это изменение использует ту же технику, которая применялась вами в заголовках секций.
Единственное отличие - элементы SECT не ожидаются внутри параграфа. (Однако параграф
мог бы легко существовать внутри другого параграфа.)

9.6.7.2. Обработка элементов <LIST> и <ITEM>


Теперь вы готовы добавить шаблон для обработки элементов LIST:

<xsl:template match="LIST">
<xsl:if test="@type='ordered'">
<ol>
<xsl:apply-templates/>

Web-

Rendered by www.RenderX.com
Преобразование XML-данных при помощи XSLT Стр. 281 из 626

</ol>
</xsl:if>
<xsl:if test="@type='unordered'">
<ul>
<xsl:apply-templates/>
</ul>
</xsl:if>
</xsl:template>

</xsl:stylesheet>
Тег <xsl:if> использует атрибут test="" для указания логического условия. В данном случае,
проверяется значение атрибута type и генерируемый список изменяется в зависимости от
значения (ordered или unordered).
Два важных замечания:
• Не существует предложения else, а также операторов return или exit, поэтому мы
используем два тега <xsl:if> для записи двух свойств. (Либо мог бы использоваться тег
<xsl:choose>, обеспечивающий функцию выбора.)
• Вокруг значений атрибутов требуется наличие одинарных кавычек. В противном случае
XSLT-процессор пытается интерпретировать слово ordered как XPath функцию, а не
строку.
А сейчас завершим обработку LIST, добавив обработку элементов ITEM:

<xsl:template match="ITEM">
<li><xsl:apply-templates/>
</li>
</xsl:template>

</xsl:stylesheet>

9.6.7.3. Сортировка шаблонов в таблице стилей


К этому времени вы должны были заметить, что шаблоны не зависят друг от друга, поэтому
обычно не имеет значения, в каком порядке они появляются в файле. С этого момента мы
будем только показывать шаблоны, которые вы должны добавить. (Для сравнения они
всегда добавляются в конец примера таблицы стилей.)
Порядок имеет значение, если два шаблона могут влиять на один и тот же узел. В данном
случае находится и обрабатывается тот шаблон, который определен последним. Например,
для изменения формата табулированного списка в формат с маленькими буквами вы
могли бы указать шаблон, например //LIST//LIST. В этом шаблоне вы могли бы использовать
HTML-атрибут для генерирования алфавитной нумерации вместо числовой.
Но этот же элемент мог бы также быть идентифицирован как //LIST. Для гарантирования
правильной обработки шаблон, указывающий //LIST, должен появляться перед шаблоном,
указывающим //LIST//LIST.

Web-

Rendered by www.RenderX.com
Стр. 282 из 626 XML Stylesheet Language for Transformations

9.6.7.4. Обработка элементов <NOTE>


Последним оставшимся структурным элементом является <NOTE>. Добавьте показанный
ниже шаблон для его обработки.

<xsl:template match="NOTE">
<blockquote><b>Note:</b><br/>
<xsl:apply-templates/>
</p></blockquote>
</xsl:template>

</xsl:stylesheet>
Этот код поднимает один интересный вопрос, возникающий из-за включения тега <br/>.
Для того, чтобы XML был формально-правильным, тег должен быть указан в таблице
стилей как <br/>, но этот тег не распознается многими броузерами. И хотя многие броузеры
распознают последовательность <br></br>, они считают ее разрывом параграфа, а не
одной строки.
Другими словами, преобразование должно генерировать тег <br>, а таблица стилей должна
указывать <br/>. Это основная причина добавления специального тега output в таблице
стилей, которое мы сделали ранее:

<xsl:stylesheet ... >


<xsl:output method="html"/>
...
</xsl:stylesheet>
Эта спецификация преобразует при выводе пустые теги, такие как <br/> в их HTML-форму
- <br>. Такое преобразование является важным, поскольку большинство броузеров не
распознают пустые теги. Вот список таких тегов:

area frame isindex


base hr link
basefont img meta
br input param
col
Итак, по умолчанию XSLT генерирует на выходе формально-правильный XML. А поскольку
таблица стилей XSL является формально-правильным XML-документом, то вы не можете
просто поместить такой тег, как <br>, в ее середину. Тег "<xsl:output method="html"/>" решает
эту проблему, так что вы можете записать <br/> в таблицу стилей, но получить на выходе
<br>.
Другой основной причиной указания <xsl:output method="html"/> является то, что как и при
указании спецификации <xsl:output method= "text"/>, генерируемый текст не использует
escape-последовательности. Например, если таблица стилей содержит ссылку на сущность

Web-

Rendered by www.RenderX.com
Преобразование XML-данных при помощи XSLT Стр. 283 из 626

&lt;, она в генерируемом тексте будет выглядеть как символ <. А при генерировании XML
ссылка на сущность &lt; в таблице стилей осталась бы неизмененной и появлялась бы как
&lt; в генерируемом тексте.
Примечание: Если вы действительно хотите, чтобы &lt; генерировалась как часть вывода
HTML, вам необходимо закодировать ее так: &amp;lt; - эта последовательность
преобразуется в &lt; поскольку только &amp; преобразуется в символ &.

9.6.7.5. Выполнение программы


Вот HTML, который генерируется для второй части при выполнении программы:

...
<h2>The Second Major Section</h2>
<p>This section adds a LIST and a NOTE.</p>
<p>Here is the LIST:</p>
<ol>
<li>Pears</li>
<li>Grapes</li>
</ol>
<p>And here is the NOTE:</p>
<blockquote>
<b>Note:</b>
<br>Don't forget to go to the hardware store on your way to the
grocery!
</blockquote>

9.6.8. Обработка встроенных элементов (элементов содержимого)


Единственными оставшимися тегами в типе ARTICLE являются встроенные теги - теги,
которые не создают разрывов строки на выходе, а интегрированы в поток текста, частью
которого они и являются.
Встроенные элементы отличаются от структурных - они являются частью содержимого
тега. Если представить элемент как узел в дереве документа, то каждый узел имеет и
содержимое, и структуру. Содержимое состоит из текста и встроенных тегов. Структура
состоит из других элементов (структурных элементов) тега.
Примечание: Рассмотренный в этом разделе пример документа, находится в файле arti-
cle3.xml, а используемая им таблица стилей - в article3.xsl. Результат - в stylizer3.html.
(Версии для броузера - article3-xml.html, article3-xsl.html и stylizer3-src.html.)
Для начала добавьте тестовые данные в пример документа:

<?xml version="1.0"?>
<ARTICLE>
<TITLE>A Sample Article</TITLE>
<SECT>The First Major Section

Web-

Rendered by www.RenderX.com
Стр. 284 из 626 XML Stylesheet Language for Transformations

...
</SECT>
<SECT>The Second Major Section
...
</SECT>
<SECT>The <I>Third</I> Major Section
<PARA>In addition to the inline tag in the heading,
this section defines the term <DEF>inline</DEF>,
which literally means "no line break". It also
adds a simple link to the main page for the Java
platform (<LINK>http://java.sun.com</LINK>),
as well as a link to the
<LINK target="http://java.sun.com/xml">XML</LINK>
page.
</PARA>
</SECT>
</ARTICLE>
Теперь обработайте встроенные в параграф элементы <DEF>, переименовывая их в теги
HTML для наклонного шрифта:

<xsl:template match="DEF">
<i> <xsl:apply-templates/> </i>
</xsl:template>
Затем, закомментируйте нормализацию текстового узла. Она выполнила свою задачу, и
сейчас вам необходимо сохранить важные пробелы:

<!--
<xsl:template match="text()">
<xsl:value-of select="normalize-space()"/>
</xsl:template>
-->
Это изменение защищает нас от потери пробелов перед такими тегами, как <I> и <DEF>.
(Попробуйте выполнить эту программу без такого изменения и посмотрите на результат.)
А теперь обработаем основные встроенные HTML-элементы, такие как <B>, <I>, <U> для
выделения текста жирным, наклонным и подчеркнутым шрифтами.

<xsl:template match="B|I|U">
<xsl:element name="{name()}">
<xsl:apply-templates/>

Web-

Rendered by www.RenderX.com
Преобразование XML-данных при помощи XSLT Стр. 285 из 626

</xsl:element>
</xsl:template>
Тег <xsl:element> позволяет вам вычислить элемент, который вы хотите сгенерировать.
Здесь вы генерируете соответствующий встроенный тег, используя имя текущего элемента.
В частности, обратите внимание на использование фигурных скобок ({}) в выражении
name= "..". Эти фигурные скобки указывают, чтобы текст внутри них был обработан как
XPath-выражение, а не как символьная строка. В данном случае вызывается функция
XPath name(), возвращающая имя текущего узла.
Фигурные скобки распознаются везде, где может встретиться шаблон значения атрибута.
(Шаблоны значения атрибута определены в разделе 7.6.2 Спецификации XSLT и
появляются в нескольких местах в определении шаблона.) В таких выражениях фигурные
скобки могут, также, использоваться для ссылки на значение атрибута, {@foo} или на
содержимое элемента {foo}.
Примечание: Вы можете, также, генерировать атрибуты, используя <xsl:attribute>. Для
дополнительной информации обратитесь к разделу 7.1.3 Спецификации XSLT.
Последним оставшимся элементом является тег LINK. Простейшим способом обработки
этого тега является установка именованного шаблона, который мы можем использовать
с параметром:

<xsl:template name="htmLink">
<xsl:param name="dest" select="UNDEFINED"/>
<xsl:element name="a">
<xsl:attribute name="href">
<xsl:value-of select="$dest"/>
</xsl:attribute>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
Основным отличием этого шаблона от использовавшихся ранее является то, что вместо
указания предложения match вы дали шаблону имя при помощи предложения name="".
Поэтому этот шаблон выполняется только при его вызове.
Внутри шаблона вы также указали параметр с именем dest, используя тег <xsl:param>.
Для некоторой защиты от ошибок вы использовали предложение select для присвоения
этому параметру значения по умолчанию UNDEFINED. Для ссылки к переменной в теге
<xsl:value-of> вы указали "$dest".
Примечание: Вспомните, что запись в кавычках интерпретируется как выражение, если
она не заключается далее в одинарные кавычки. Вот почему одинарные кавычки были
ранее необходимы в "@type='ordered'" - для интерпретации ordered как строки.
Тег <xsl:element> генерирует элемент. Ранее мы могли просто указать желаемый элемент,
кодируя, например, <html>. Но здесь вы динамически генерируете содержимое анкера
HTML (<a>) в теле тега <xsl:element>, а также динамически генерируете атрибут href,
используя тег <xsl:attribute>.

Web-

Rendered by www.RenderX.com
Стр. 286 из 626 XML Stylesheet Language for Transformations

Последней важной частью шаблона является тег <apply-template>, который вставляет


текст из текстового узла элемента LINK. Без него в генерируемой HTML-ссылке не было
бы текста.
Добавьте шаблон для тега LINK и вызовите именованный шаблон из него:

<xsl:template match="LINK">
<xsl:if test="@target">
<!--Target attribute specified.-->
<xsl:call-template name="htmLink">
<xsl:with-param name="dest" select="@target"/>
</xsl:call-template>
</xsl:if>
</xsl:template>

<xsl:template name="htmLink">
...
Предложение test="@target" возвращает true, если в теге LINK существует атрибут target.
То есть, этот тег <xsl-if> генерирует HTML-ссылки, когда текст ссылки и назначение,
определенное для нее, различны.
Тег <xsl:call-template> вызывает именованный шаблон, в то время как <xsl:with-param>
указывает параметр, используя предложение name, и его значение, используя предложение
select.
В качестве последнего действия в процессе создания таблицы стилей добавьте
приведенный ниже тег <xsl-if> для обработки тегов LINK, не имеющих атрибута target.

<xsl:template match="LINK">
<xsl:if test="@target">
...
</xsl:if>

<xsl:if test="not(@target)">
<xsl:call-template name="htmLink">
<xsl:with-param name="dest">
<xsl:apply-templates/>
</xsl:with-param>
</xsl:call-template>
</xsl:if>
</xsl:template>
Предложение not(…) инвертирует предыдущий тест (вспомните, что не существует
предложения else). То есть, эта часть шаблона интерпретируется тогда, когда атрибут

Web-

Rendered by www.RenderX.com
Преобразование XML-данных при помощи XSLT Стр. 287 из 626

target не указан. Здесь значение параметра приходит не из предложения select, а из


содержимого элемента <xsl:with-param>.
Примечание: Значения параметров и переменных (которые будут вскоре рассмотрены в
разделе "Что еще может сделать XSLT?") могут быть указаны либо при помощи
предложения select, позволяющего использовать XPath-выражения, либо при помощи
содержимого элемента, позволяющего использовать XSLT-теги.
Содержимое параметра в данном случае генерируется тегом <xsl:apply-templates/>, который
вставляет содержимое текстового узла элемента LINK.

9.6.8.1. Выполнение программы


Если вы выполните программу сейчас, вы получите следующие результаты:

...
<h2>The <I>Third</I> Major Section
</h2>
<p>In addition to the inline tag in the heading, this section
defines the term <i>inline</i>, which literally
means
"no line break". It also adds a simple link to the
main page for the Java platform (<a

href="http://java.sun.com">http://java.sun.com</a>),
as well as a link to the
<a href="http://java.sun.com/xml">XML</a> page.
</p>
Хорошая работа! Вы сейчас преобразовали довольно сложный XML-файл в HTML.
(Кажущийся простым на первый взгляд, он определенно предоставил много возможностей
для исследования.)

9.6.9. Распечатка HTML


Вы преобразовали XML-файл в HTML. Однажды кто-нибудь создаст механизм печати,
распознающий HTML, который вы сможете найти и использовать в Java Printing Service
API. Тогда вы имели бы возможность распечатать произвольный XML-файл, генерируя
HTML. Все, что вам нужно было бы сделать, - настроить таблицу стилей и использовать
ваш броузер.

9.6.10. Что еще может делать XSLT?


Каким бы длинным ни был этот раздел руководства, в нем затронута только поверхность
возможностей XSLT. Многие дополнительные возможности ждут вас в Спецификации
XSLT. Вот несколько материалов для поиска:
import (раздел 2.6.2) и include (раздел 2.6.1)
Используйте эти операторы для разбиения таблиц стилей XSLT на модули и их
комбинирования. Оператор include просто вставляет все определения из включаемого

Web-

Rendered by www.RenderX.com
Стр. 288 из 626 XML Stylesheet Language for Transformations

файла. Оператор import позволяет переопределять определения в импортируемом файле


определениями в вашей собственной таблице стилей.
Циклы for-each (раздел 8)
Организация цикла по набору элементов и обработка каждого из них.
Выбор (оператор case) для условной обработки (раздел 9.2)
Переход к одному из нескольких ветвей обработки в зависимости от входного значения.
Генерирование чисел (раздел 7.7)
Динамическое генерирование пронумерованных секций, элементов и числовых литералов.
XSLT предоставляет три режима нумерации:
• single: Нумерует элементы в одном заголовке, например, отсортированный список в
HTML.
• multiple: Генерирует многоуровневую нумерацию, например "A.1.3".
• any: Последовательно нумерует элементы, где бы они не встретились, например сноски
в главе.
Форматирование чисел (раздел 12.3)
Управление форматированием нумерации. Вы можете использовать числа (format="1"),
символы в верхнем регистре (format= "A"), символы в нижнем регистре (format= "a") или
составные номера, например "A.1". Также управление форматом чисел и валют для
соответствия местным требованиям.
Сортировка выхода (раздел 10)
Генерирование выходных данных с желаемой сортировкой.
Основанные на режимах шаблоны (раздел 5.7)
Обрабатывают элементы несколько раз, каждый раз в различном "режиме". Вы добавляете
атрибут mode в шаблон и указываете <apply-templates mode="…"> для применения только
тех шаблонов, которые соответствуют режиму. Комбинируйте с атрибутом <apply-templates
select="…"> для использования основанной на режимах обработки подмножества входных
данных.
Переменные (раздел 11)
Переменные, как и параметры, позволяют вам управлять поведением шаблонов. Но они
не настолько ценны, как вы бы могли подумать. Значение переменной известно только
внутри области действия текущего шаблона или тега <xsl:if> (например), в которых она
определена. Вы не можете передавать значение переменной из одного тега в другой и
даже из одной заключенной в скобки части шаблона в другую часть этого же шаблона.
Эти утверждения истинны даже для "глобальной" переменной. Вы можете изменить ее
значение в шаблоне, но это изменение действует только в этом шаблоне. Вычисление
выражения, используемого для определения глобальной переменной, происходит в
контексте корневого узла структуры. Другими словами, глобальные переменные по существу
являются константами времени выполнения. Эти константы могут быть полезны для
изменения поведения шаблона, особенно при использовании совместно с операторами
include и import. Но переменные не являются механизмом управления данными общего
назначения.

Web-

Rendered by www.RenderX.com
Преобразование из командной строки Стр. 289 из 626

9.6.10.1. Проблемы с переменными


Было бы заманчиво создать один шаблон и установить переменную для назначения ссылки,
а не порождать проблему при установке параметризованного шаблона и вызова его двумя
различными способами. Идея может заключаться в том, чтобы установить переменную в
значение по умолчанию (скажем, текст тега LINK) и затем, если существует атрибут target,
установить переменную назначения в значение атрибута target.
Это была бы не плохая идея - если бы она работала. Но опять же, проблема в том, что
переменные известны только в той области действия, где они были определены. Поэтому
при кодировании тега <xsl:if> для изменения значения переменной ее значение известно
только внутри контекста тега <xsl:if>. Как только встречается тег </xsl:if>, любые изменения
значения переменной теряются.
Такая же заманчивая идея - возможность замены спецификации text()|B|I|U|DEF|LINK на
переменную ($inline). Но, поскольку значение переменной определяется в месте ее
объявления, значение глобальной переменной inline состоит из текстовых узлов, узлов
<B> и т.д., что справедливо только для уровня корневого узла. Другими словами, значение
такой переменной в данном случае равно null.

9.7. Преобразование из командной строки


При выполнении преобразования из командной строки использование XSLTC имеет
большой смысл. Хотя интерпретирующий преобразователь Xalan тоже имеет механизм
командной строки, он не сохраняет предварительно откомпилированные байт-коды в виде
транслетов для дальнейшего использования, как это делает XSLTC.
Для выполнения XSLTC из командной строки необходимы два действия:
1. Откомпилируйте транслет.
2. Запустите откомпилированный транслет с данными.
Примечание: Детальную информацию по этому предмету вы можете найти в отличном
руководстве http://xml.apache.org/xalan-j/xsltc_usage.html.

9.7.1. Компиляция транслета


Для компиляции таблицы стилей article3.xsl в транслет выполните следующую команду:

java org.apache.xalan.xsltc.cmdline.Compile article3.xsl


Примечание: Для версии 1.3 платформы Java вам необходимо включить соответствующие
установки classpath, как описано в разделе "Компиляция и выполнение программы".
В результате создается файл класса (транслет) с именем article3.class.
Ниже приведены аргументы, которые могут быть указаны при компиляции транслета:

java org.apache.xalan.xsltc.cmdline.Compile
-o transletName -d directory -j jarFile
-p packageName {-u stylesheetURI | stylesheetFile }

Web-

Rendered by www.RenderX.com
Стр. 290 из 626 XML Stylesheet Language for Transformations

где
• -o transletName
Указывает имя генерируемого класса транслета (выходной класс). Суффикс .class не
обязателен. Если он отсутствует, то автоматически добавляется к имени, указанному
в аргументе stylesheet.
• -d directory
Указывает каталог назначения. (По умолчанию - текущий каталог.)
• -j jarFile
Выводит генерируемые файлы класса транслета в JAR-файл с именем jarFile.jar. При
использовании этого аргумента создается только JAR-файл.
• -p packageName
Указывает имя пакета для генерируемых классов транслета.
• -u stylesheetURI
Указывает таблицу стилей при помощи URI, например http://meserver/stylesheet1.xsl.
• stylesheetFile
(Не флаг) Путь к файлу таблицы стилей.

9.7.2. Выполнение транслета


Для запуска транслета с файлом примера article3.xml выполните следующую команду:

java org.apache.xalan.xsltc.cmdline.Transform
article3.xml article3
Примечание: Опять установите classpath, как описано в разделе "Компиляция и выполнение
программы", если вы работаете на версии 1.3 платформы Java.
Эта команда добавляет текущий каталог к переменной classpath, для того чтобы транслет
мог быть найден. Вывод осуществляется на System.out.
Ниже приведены возможные аргументы, которые могут быть указаны при запуске транслета:

java org.apache.xalan.xsltc.cmdline.Transform
{-u documentURI | documentFilename}
className [name=value...]
где
• -u documentURI
Указывает URI входного XML-документа.
• documentFilename
Указывает имя файла входного XML-документа.
• className
Транслет, выполняющий преобразование. (Здесь вы не можете указать суффикс .class,
так же как вы пропускаете его при запуске java-приложения.)

Web-

Rendered by www.RenderX.com
Объединение преобразований при помощи цепочки фильтров Стр. 291 из 626

• name=value…
Необязательный набор из одного или более параметров таблицы стилей, указанных в
виде пар имя-значение.

9.8. Объединение преобразований при помощи цепочки фильтров


Иногда полезно создать цепочку фильтров - соединение XSLT-преобразований, в которых
выходная информация одного преобразования становится входной для следующего. В
этом разделе показано, как это сделать.

9.8.1. Написание программы


Начнем с написания программы, осуществляющую фильтрацию. Этот пример показан с
полным исходным кодом, но вы можете использовать одну из программ, над которыми
работали, в качестве основы.
Примечание: Рассматриваемый здесь код находится в файле FilterChain.java.
Пример программы включает операторы import, которые идентифицируют
месторасположение пакетов для каждого класса:

import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;
import org.xml.sax.XMLFilter;

import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerConfigurationException;

import javax.xml.transform.sax.SAXTransformerFactory;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.sax.SAXResult;

import javax.xml.transform.stream.StreamSource;
import javax.xml.transform.stream.StreamResult;

import java.io.*;

Web-

Rendered by www.RenderX.com
Стр. 292 из 626 XML Stylesheet Language for Transformations

Программа включает в себя также стандартные обработчики ошибок, которые вы уже


использовали. Они собраны вместе и перечислены ниже:

}
catch (TransformerConfigurationException tce) {
// ,
System.out.println ("* Transformer Factory error");
System.out.println(" " + tce.getMessage() );

//
,
Throwable x = tce;
if (tce.getException() != null)
x = tce.getException();
x.printStackTrace();
}
catch (TransformerException te) {
// ,
System.out.println ("* Transformation error");
System.out.println(" " + te.getMessage() );

//
,
Throwable x = te;
if (te.getException() != null)
x = te.getException();
x.printStackTrace();
}
catch (SAXException sxe) {
// ,

// (or a parser-initialization error)


Exception x = sxe;
if (sxe.getException() != null)
x = sxe.getException();
x.printStackTrace();
}
catch (ParserConfigurationException pce) {
//

Web-

Rendered by www.RenderX.com
Объединение преобразований при помощи цепочки фильтров Стр. 293 из 626

pce.printStackTrace();
}
catch (IOException ioe) {
// I/O
ioe.printStackTrace();
}
Между операторами import и обработчиками ошибок располагается основная часть
программы:

public static void main (String argv[])


{
if (argv.length != 3) {
System.err.println (
"Usage: java FilterChain style1 style2 xmlfile");
System.exit (1);
}

try {
//
File stylesheet1 = new File(argv[0]);
File stylesheet2 = new File(argv[1]);
File datafile = new File(argv[2]);

//
BufferedInputStream bis = new

BufferedInputStream(newFileInputStream(datafile));
InputSource input = new InputSource(bis);

// ( .
#1)
SAXParserFactory spf = SAXParserFactory.newInstance();
spf.setNamespaceAware(true);
SAXParser parser = spf.newSAXParser();
XMLReader reader = parser.getXMLReader();

// ( . #2)
SAXTransformerFactory stf =
(SAXTransformerFactory)
TransformerFactory.newInstance();

Web-

Rendered by www.RenderX.com
Стр. 294 из 626 XML Stylesheet Language for Transformations

XMLFilter filter1 = stf.newXMLFilter(


new StreamSource(stylesheet1));
XMLFilter filter2 = stf.newXMLFilter(
new StreamSource(stylesheet2));

// filter1
( . #3)
// filter1 filter2
filter1.setParent(reader);
filter2.setParent(filter1);

//
StreamResult result = new StreamResult(System.out);

//
SAX- ,
//

Transformer transformer = stf.newTransformer();


SAXSource transformSource = new SAXSource(
filter2, input);
transformer.transform(transformSource, result);
} catch (...) {
...
Примечания:
1. Механизм преобразования Xalan в настоящее время требует использования
распознающего пространство имен SAX-анализатора. XSLTC этого не требует.
2. Присутствие такого немного запутанного кода объясняется тем, что SAXTransformer-
Factory расширяет TransformerFactory, добавляя методы получения объектов фильтров.
Метод newInstance() является статическим методом, определенным в TransformerFactory,
который (обычно этого достаточно) возвращает объект TransformerFactory. В
действительности он возвращает SAXTransformerFactory. Поэтому для получения
дополнительных методов, определенных в SAXTransformerFactory, возвращаемое
значение должно быть преобразовано в фактический тип.
3. Объект XMLFilter является и SAX-считывателем, и обработчиком SAX-содержимого.
Как SAX-считыватель он генерирует SAX-события для любого объекта,
зарегистрированного для их приема. Как обработчик содержимого он перехватывает
SAX-события, генерируемые его "родительским" объектом, которым является SAX-
считыватель. (Термин "родительский" имеет смысл для внутренней архитектуры. С
точки общей структуры это имя не кажется вполне подходящим.) Тот факт, что фильтры

Web-

Rendered by www.RenderX.com
Объединение преобразований при помощи цепочки фильтров Стр. 295 из 626

как генерируют SAX-события, так и перехватывают их дает им возможность


объединяться в цепочку.

9.8.2. Как работает цепочка фильтров


Приведенная ниже программа показывает, как настроить информацию. Рисунок 2 должен
помочь вам понять, что происходит во время выполнения этой программы.

Рисунок 2 Действия в цепочке фильтров

При создании преобразователя вы передаете ему объект SAXSource, который


инкапсулирует считыватель (в данном случае filter2) и входной поток. Вы также передаете
ему указатель на выходной поток, в который он направляет свой вывод. На диаграмме
показано, что происходит при вызове метода transform() преобразователя. Вот объяснение
действий:
1. Преобразователь настраивает внутренний объект как обработчик содержимого для
filter2 и указывает ему анализировать входную информацию.
2. filter2, в свою очередь, настраивает себя как обработчик содержимого для filter1 и
указывает ему анализировать входную информацию.
3. filter1, в свою очередь, указывает объекту анализатора анализировать входную
информацию.
4. Анализатор выполняет это, генерирует SAX-события, который передает в filter1.
5. filter1, действуя как обработчик содержимого, обрабатывает события и выполняет свои
преобразования. Затем, действуя как SAX-считыватель (XMLReader), он предает SAX-
события в filter2.
6. filter2 делает то же самое, передавая события в обработчик содержимого
преобразователя, который генерирует выходной поток.

9.8.3. Тестирование программы


Для испытания программы вы создадите XML-файл с маленьким фрагментом формата
XML DocBook и преобразуете его в формат ARTICLE? определенный здесь. Затем вы
примените таблицу стилей ARTICLE для генерирования HTML-версии.

Web-

Rendered by www.RenderX.com
Стр. 296 из 626 XML Stylesheet Language for Transformations

Примечание: Данный пример обрабатывает файл small-docbook-article.xml, используя


docbookToArticle.xsl и article1c.xsl. Pезультат записан в файл filterout.html. (Версии для
броузера - small-docbook-article-xml.html, docbookToArticle-xsl.html, article1c-xsl.html и filterout-
src.html.) Посмотрите на Web-странице O'Reilly хорошее описание формата DocBook.
Начнем с создания маленькой статьи, использующей подмножество формата XML DocBook:

<?xml version="1.0"?>
<Article>
<ArtHeader>
<Title>Title of my (Docbook) article</Title>
</ArtHeader>
<Sect1>
<Title>Title of Section 1.</Title>
<Para>This is a paragraph.</Para>
</Sect1>
</Article>
Затем создадим таблицу стилей для преобразования ее в формат ARTICLE:

<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0"
>
<xsl:output method="xml"/> ( . #1)

<xsl:template match="/">
<ARTICLE>
<xsl:apply-templates/>
</ARTICLE>
</xsl:template>

<!-- Lower level titles strip element tag --> ( .


#2)

<!-- Top-level title -->


<xsl:template match="/Article/ArtHeader/Title"> ( .
#3)
<TITLE> <xsl:apply-templates/> </TITLE>
</xsl:template>

<xsl:template match="//Sect1"> ( . #4)

Web-

Rendered by www.RenderX.com
Объединение преобразований при помощи цепочки фильтров Стр. 297 из 626

<SECT><xsl:apply-templates/></SECT>
</xsl:template>

<xsl:template match="Para">
<PARA><xsl:apply-templates/></PARA> ( .
#5)
</xsl:template>

</xsl:stylesheet>
Примечания:
1. На этот раз таблица стилей генерирует XML-формат.
2. Следующий шаблон (для элемента title самого верхнего уровня) соответствует только
главному заголовку. Для заголовков секций используется тег TITLE. (Поскольку нет
шаблона преобразования, относящегося к этим элементам title, - они игнорируются.
Однако текстовые узлы, которые они содержат, передаются как результат XSLT
встроенных правил шаблонов - то есть игнорируется только тег, а не текст. Более
подробно об этом ниже.)
3. Элемент title из заголовка DocBook становится элементом title ARTICLE.
4. Нумерованные теги секции преобразуются в обычные теги SECT.
5. Этот шаблон выполняет преобразование регистра, то есть Para становится PARA.
Хотя об этом не было сказано отдельно, XSLT определяет несколько встроенных (по
умолчанию) правил шаблонов. Их полный набор перечислен в разделе 5.8 спецификации.
В основном они предназначены для автоматического копирования текстовых узлов и узлов
атрибутов, а также для пропуска комментариев и инструкций обработки. Они, также,
обеспечивают обработку внутренних элементов даже в том случае, когда содержащие их
теги не имеют шаблонов. Это является причиной того, что текстовый узел обрабатывается
в заголовке секции, хотя этот заголовок не описан ни в одном из шаблонов.
Теперь выполните программу FilterChain, передав ей приведенную выше таблицу стилей
(docbookToArticle.xsl), таблицу стилей ARTICLE (article1c.xsl) и небольшой DocBook-файл
(small-docbook-article.xml) в этом порядке. Результат должен выглядеть примерно так:

<html>
<body>
<h1 align="center">Title of my (Docbook) article</h1>
<h2>Title of Section 1.</h2>
<p>This is a paragraph.</p>
</body>
</html>
Примечание: Этот результат был сгенерирован при использовании JAXP 1.0. Однако
первый фильтр в цепочке сейчас не транслирует ни одного тега во входном файле. Пока
этот дефект не будет устранен, результат, который вы увидите, будет состоять из

Web-

Rendered by www.RenderX.com
Стр. 298 из 626 Java API for XML-based RPC

соединенного обычного текста в HTML-выводе, например: "Title of my (Docbook) article Title


of Section 1. This is a paragraph.".

9.8.4. Заключение
Поздравляем! Вы закончили руководство по XSLT. Вы немало поработали с XML и XSLT
и готовы к изучению многих других захватывающих возможностей.

9.9. Дополнительная информация


Дополнительную информацию по таблицам стилей XSL, XSLT и механизмам
преобразования можно найти в следующих источниках:
• Oтличное введение в XSLT, которое начинается с простой HTML-страницы и
последовательно использует XSLT для ее настройки: http://www.xfront.com/rescuing-
xslt.html
• Extensible Stylesheet Language (XSL): http://www.w3.org/Style/XSL/
• XML Path Language: http://www.w3.org/TR/xpath
• Механизм преобразования Xalan: http://xml.apache.org/xalan-j/
• Механизм преобразования XSLTC: http://xml.apache.org/xalan-j/
• Советы по использованию XSLTC: http://xml.apache.org/xalan-j/xsltc_usage.html
• Проектирование таблиц стилей для достижения максимальной производительности с
XSLTC: http://xml.apache.org/xalan-j/xsltc/xsltc_performance.html

10. Java API for XML-based RPC


Если вы новичок в Java API for XML-based RPC (JAX-RPC) - эта глава даст вам начальные
знания. После короткого описания JAX-RPC здесь будет показано, как построить простую
Web-службу и Web-клиент. Глава основана на примерах, в ней представлены листинги
программ и пошаговые инструкции для создания динамических клиентов, аутентификации
по SSL и размещения Web-служб на J2EE SDK 1.3.1.

10.1. Что такое JAX-RPC?


JAX-RPC - это сокращение от Java API for XML-based RPC (прикладной программный
интерфейс Java для основанных на XML удаленных вызовах процедур). Это API для
создания Web-служб и клиентских приложений, использующих вызов удаленных процедур
(RPC) и XML. Часто используемый в распределенной модели клиент/сервер, механизм
RPC позволяет клиентам выполнять процедуры на других системах.
В JAX-RPC удаленный вызов процедуры представляется основанным на XML протоколом,
таким как SOAP. Спецификация SOAP определяет структуру конверта, правила кодирования
и соглашения для представления вызовов удаленных процедур и ответов. Эти вызовы и
ответы передаются в виде SOAP-сообщений по HTTP. В данной редакции JAX-RPC основан
на SOAP 1.1 и HTTP 1.1.

Web-

Rendered by www.RenderX.com
Простой пример: HelloWorld Стр. 299 из 626

Хотя JAX-RPC базируется на сложных протоколах, API скрывает эту сложность от


разработчика приложения. На стороне сервера разработчик указывает удаленные
процедуры, определяя методы в интерфейсе на языке программирования Java. Разработчик
также кодирует один или более классов, которые реализуют эти методы. Клиентские
программы тоже создаются легко. Клиент создает прокси, локальный объект представляет
службу и затем просто вызывает методы в прокси.
С помощью JAX-RPC клиенты и Web-службы получают большое преимущество -
платформонезависимость языка программирования Java. Кроме того, JAX-RPC не
накладывает ограничений: клиент JAX-RPC может получить доступ к Web-службе, которая
не выполняется на Java-платформе, и наоборот. Такая гибкость возможна потому, что
JAX-RPC использует технологии, определенные организацией World Wide Web Consortium
(W3C): HTTP, SOAP и Web Service Description Language (WSDL). WSDL определяет XML-
формат для описания службы как набора конечных элементов, оперирующих сообщениями.

10.2. Простой пример: HelloWorld


Этот пример демонстрирует использование JAX-RPC для создания Web-службы с именем
HelloWorld. Удаленный клиент службы HelloWorld может вызвать метод sayHello, который
принимает строковый параметр и возвращает строку.

10.2.1. HelloWorld во время выполнения


На рисунке 9-1 показана упрощенная схема службы HelloWorld после ее размещения. Вот
более детальное описание событий, происходящих во время ее выполнения:
1. Вызывая удаленную процедуру, программа HelloClient вызывает метод в заглушке -
локальном объекте, представляющем удаленную службу.
2. Заглушка вызывает процедуры в среде времени исполнения JAX-RPC.
3. Среда времени исполнения преобразовывает вызов удаленного метода в SOAP-
сообщение и передает это сообщение как HTTP-запрос.
4. Когда сервер принимает HTTP-запрос, среда времени исполнения JAX-RPC извлекает
SOAP-сообщение из запроса и транслирует его в вызов метода.
5. Среда времени исполнения JAX-RPC вызывает метод связывающего объекта.
6. Связывающий объект вызывает метод в реализации службы HelloWorld.
7. Среда времени исполнения на сервере преобразовывает ответы метода в SOAP-
сообщение и передает сообщение обратно клиенту в виде HTTP-ответа.
8. На клиентской машине среда времени исполнения JAX-RPC извлекает SOAP-сообщение
из HTTP-ответа и транслирует его в ответ метода для программы HelloClient.

Web-

Rendered by www.RenderX.com
Стр. 300 из 626 Java API for XML-based RPC

Рисунок 9-1 Пример HelloWorld во время выполнения

Разработчик приложения обеспечивает только верхние уровни в стеке, избраженном на


рисунке 9-1. В таблице 9-1 показаны источники этих уровней.
Таблица 9-1 Кто (или что) предоставляет уровни
Уровень Источник
Программа HelloClient Обеспечивается разработчиком приложения
Cлужба HelloWorld (интерфейс определения и класс
реализации)
Заглушки Генерируются служебной программой wscompile, которая
запускается разработчиком приложения
Узлы Генерируются служебной программой wsdeploy, которая
запускается разработчиком приложения
Среда времени исполнения JAX-RPC Включена в Java WSDP

10.2.2. Файлы HelloWorld


Для создания службы в JAX-RPC разработчик приложения должен предоставить несколько
файлов. Файлы примера HelloWorld находятся в каталоге <JWSDP_HOME>/docs/tuto-
rial/examples/jaxrpc/hello:
• HelloIF.java - интерфейс определения службы
• HelloImpl.java - класс реализации определения службы, он реализует интерфейс HelloIF
• HelloClient.java - удаленный клиент, который соединяется со службой и затем вызывает
метод sayHello
• config.xml - конфигурационный файл для служебной программы wscompile
• jaxrpc-ri.xml - конфигурационный файл для служебной программы wsdeploy
• web.xml - дескриптор размещения Web-компонента (сервлет), который распределяет
запросы к службе

10.2.3. Настройка
Если вы этого еще не сделали, выполните следующие инструкции в главе "Начало работы
с Tomcat":
• Установка переменной PATH
• Создание и компоновка файла свойств
• Запуск Tomcat

Web-

Rendered by www.RenderX.com
Простой пример: HelloWorld Стр. 301 из 626

10.2.4. Создание и размещение службы


Основные действия при разработке Web-службы JAX-RPC следующие:
1. Кодирование интерфейса определения службы и класса реализации.
2. Компиляция кода определения службы из пункта 1.
3. Пакетирование кода в WAR-файл.
4. Генерирование узлов и WSDL-файла.
5. Размещение службы.
Следующие разделы описывают каждый шаг более детально.

10.2.4.1. Кодирование интерфейса определения службы и класса реализации.


Интерфейс определения службы объявляет методы, которые удаленный клиент может
вызвать в службе. Интерфейс должен следовать нескольким правилам:
• Он расширяет интерфейс java.rmi.Remote.
• Он не должен иметь объявлений констант, таких как public final static.
• Методы должны генерировать java.rmi.RemoteException или один из его подклассов.
(Эти методы могут также генерировать специфичные для службы исключительные
ситуации.)
• Параметры метода и типы возвращаемого значения должны поддерживаться в JAX-
RPC. См. раздел "Типы, поддерживаемые JAX-RPC".
В данном примере интерфейсом определения службы является HelloIF.java:

package hello;

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface HelloIF extends Remote {


public String sayHello(String s) throws RemoteException;
}
Кроме интерфейса вы должны закодировать класс, реализующий интерфейс. В данном
примере класс реализации называется HelloImpl:

package hello;

public class HelloImpl implements HelloIF {

public String message ="Hello";

public String sayHello(String s) {

Web-

Rendered by www.RenderX.com
Стр. 302 из 626 Java API for XML-based RPC

return message + s;
}
}

10.2.4.2. Компиляция кода определения службы


Чтобы откомпилировать HelloIF.java и HelloImpl.java перейдите в каталог
<JWSDP_HOME>/docs/tutorial/examples/jaxrpc/hello и выполните следующую команду:

ant compile-server
Это команда помещает создающиеся файлы классов в подкаталоге build/shared.

10.2.4.3. Пакетирование кода в WAR-файл.


Для создания WAR-файла, содержащего код службы, выполните эти команды:

ant setup-web-inf
ant package
Задание setup-web-inf копирует файлы классов и XML-файлы в подкаталог build/WEB-INF.
Задание package выполняет команду jar и группирует файлы в WAR-файл с именем
dist/hello-portable.war. Этот WAR-файл не готов для размещения, поскольку он не содержит
классов узлов. В следующем разделе вы узнаете, как создать WAR-файл, готовый для
размещения. hello-portable.war содержит следующие файлы:

WEB-INF/classes/hello/HelloIF.class
WEB-INF/classes/hello/HelloImpl.class
WEB-INF/jaxrpc-ri.xml
WEB-INF/web.xml
Файлы классов были созданы заданием compile-server, как показано в предыдущем разделе.
Файл web.xml является дескриптором размещения для Web-приложения, реализующего
службу. В отличие от web.xml, файл jaxrpc-ri.xml не является частью спецификации и
зависит от реализации. Файл jaxrpc-ri.xml для данного примера выглядит так:

<?xml version="1.0" encoding="UTF-8"?>


<webServices
xmlns="http://java.sun.com/xml/ns/jax-rpc/ri/dd"
version="1.0"
targetNamespaceBase="http://com.test/wsdl"
typeNamespaceBase="http://com.test/types"
urlPatternBase="/ws">

<endpoint
name="MyHello"

Web-

Rendered by www.RenderX.com
Простой пример: HelloWorld Стр. 303 из 626

displayName="HelloWorld Service"
description="A simple web service"
interface="hello.HelloIF"
implementation="hello.HelloImpl"/>

<endpointMapping
endpointName="MyHello"
urlPattern="/hello"/>

</webServices>
Некоторые из атрибутов webServices, такие как targetNamespaceBase, используются в
WSDL-файле, который вы создадите в следующем разделе. (WSDL-файлы могут быть
сложными и не рассматриваются в данном руководстве. См. раздел "Дополнительная
информация".) Обратите внимание, что значение urlPattern (/hello) является частью URL
службы, и рассматривается в разделе "Верификация размещения").
Дополнительную информацию о синтаксисе файла jaxrpc-ri.xml можно найти в файле XML
Schema: <JWSDP_HOME>/docs/tutorial/examples/jaxrpc/common/jax-rpc-ri-dd.xsd.

10.2.4.4. Генерирование узлов и WSDL-файла.


Для генерирования узлов и WSDL-файла выполните следующую команду:

ant process-war
Эта команда запускает программу wsdeploy следующим образом:

wsdeploy -tmpdir build/wsdeploy-generated


-o dist/hello-deployable.war dist/hello-portable.war
Служебная программа wsdeploy выполняет следующие задачи:
• Читает файл dist/hello-portable.xml
• Получает информацию из файла jaxrpc-ri.xml, который находится в файле hello-
portable.war
• Генерирует классы узла для службы
• Генерирует WSDL-файл с именем MyHello.wsdl
• Пакетирует классы узла в файл Hello.wsdl и содержимое файла hello-portable.war в
готовый для размещения WAR-файл с именем dist/hello-jaxrpc.war
Параметр -tmpdir определяет каталог, в котором wsdeploy сохраняет генерируемые файлы,
включая WSDL-файл, классы узла и файлы промежуточного кода. Если вы укажете
параметр -keep, эти файлы не будут удалены.
Существует несколько способов получения доступа к WSDL-файлу, сгенерированному
программой wsdeploy:

Web-

Rendered by www.RenderX.com
Стр. 304 из 626 Java API for XML-based RPC

• Запустите wsdeploy с параметром -keep и найдите WSDL-файл в каталоге, указанном


в параметре -tmpdir.
• Распакуйте (jar -x) WAR-файл, созданный программой wsdeploy, и найдите WDL-файл
в каталоге WEB-INF.
• Разместите и верифицируйте службу, как описано в следующих разделах. Ссылка на
WSDL-файл находится на HTML-странице URL, как описано в разделе "Верификация
размещения".
Обратите внимание, что программа wsdeploy не размещает службу; она создает WAR-
файл, готовый к размещению. В следующем разделе вы разместите службу, находящуюся
в файле hello-jaxrpc.war, созданном программой wsdeploy.

10.2.4.5. Размещение службы.


Для размещения службы выполните следующую команду:

ant deploy
Для повторного размещения выполните ant redeploy, как описано в разделе "Итеративная
разработка".

10.2.4.6. Верификация размещения


Для проверки того, что служба была размещена успешно, откройте окно броузера и укажите
полный URL службы:

http://localhost:8080/hello-jaxrpc/hello
Броузер должен отобразить страницу под названием Web Services, которая содержит имя
порта MyHello со статусом ACTIVE. Эта страница также содержит URL к WSDL-файлу
службы.
Часть URL hello-jaxrpc.war является путем контекста сервлета, реализующего службу
HelloWorld. Эта часть соответствует префиксу файла hello-jaxrpc.war. Строка /hello в URL
соответствует значению атрибута urlPattern файла jaxrpc-ri.xml. Обратите внимание, что
прямой слеш в значении /hello в urlPattern необходим. Полный листинг файла jaxrpc-ri.xml
приведен в разделе "Пакетирование WAR-файла".

10.2.4.7. Отмена размещения службы


Пока не отменяйте размещение службы. Когда вы закончите работать с этим примером,
вы можете отменить размещение службы, выполнив команду:

ant undeploy

10.2.5. Создание и запуск клиентской программы


Для разработки клиентской программы JAX-RPC выполните следующие действия:
• Сгенерируйте заглушки.
• Напишите код клиентской программы.

Web-

Rendered by www.RenderX.com
Простой пример: HelloWorld Стр. 305 из 626

• Откомпилируйте код.
• Спакетируйте клиентские классы в JAR-файл.
• Запустите клиентскую программу.
Следующие разделы описывают эти шаги более детально.

10.2.5.1. Генерирование заглушек


Перед генерированием заглушек проверьте, что файл Hello.wsdl установлен в соответствии
с инструкциями раздела "Размещение службы". Для создания заглушек перейдите в каталог
<JWSDP_HOME>/docs/tutorial/examples/jaxrpc/hello и выполните следующую команду:

ant generate-stubs
Эта команда запускает служебную программу wscompile следующим образом:

wscompile -gen:client -d build/client


-classpath build/shared config.xml
Параметр -gen:client указывает программе wscompile создать клиентские классы, такие
как заглушки. Параметр -d указывает каталог, в который генерируются файлы.
Служебная программа wscompile генерирует файлы, основываясь на информации,
прочитанной из файлов Hello.wsdl и config.xml. Файл Hello.wsdl был установлен в Tomcat
при размещении службы. Месторасположение файла Hello.wsdl указывается элементом
<wsdl> в файле config.xml, который приведен ниже:

<?xml version="1.0" encoding="UTF-8"?>


<configuration
xmlns="http://java.sun.com/xml/ns/jax-rpc/ri/config">
<wsdl location=
"http://localhost:8080/hello-jaxrpc/hello?WSDL"
packageName="hello"/>
</configuration>
Задачи, выполняемые программой wscompile, зависят от содержимого файла config.xml.
Дополнительную информацию о синтаксисе файла config.xml можно найти в файле XML
Schema: <JWSDP_HOME>/docs/tutorial/examples/jaxrpc/common/jax-rpc-ri-dd.xsd.

10.2.5.2. Кодирование клиентской программы


HelloClient является автономной программой, вызывающей метод sayHello службы
HelloWorld. Она выполняет этот вызов через заглушку, локальный объект, который действует
как прокси для удаленной службы. Поскольку заглушки создаются перед временем
выполнения (при помощи wscompile), они обычно называются статическими заглушками.
Для создания заглушки HelloClient вызывает private метод с именем createProxy. Обратите
внимание, что код в этом методе является зависимым от реализации и может быть
непереносимым, поскольку зависит от объекта MyHello_Impl. (Класс MyHello_Impl был

Web-

Rendered by www.RenderX.com
Стр. 306 из 626 Java API for XML-based RPC

сгенерирован программой wscompile в предыдущем разделе.) После создания заглушки


клиентская программа преобразует тип заглушки в тип HelloIF - интерфейс определения
службы.
Исходный код HelloClient следующий:

package hello;

import javax.xml.rpc.Stub;

public class HelloClient {


public static void main(String[] args) {
try {
Stub stub = createProxy();
HelloIF hello = (HelloIF)stub;
System.out.println(hello.sayHello("Duke!"));
} catch (Exception ex) {
ex.printStackTrace();
}
}

private static Stub createProxy() {


// : MyHello_Impl
.
return (Stub)(new MyHello_Impl().getHelloIFPort());
}
}

10.2.5.3. Компилирование кода клиентской программы


Поскольку клиентский код обращается к классам заглушки, выполните указания раздела
"Генерирование заглушек" перед компилированием клиентской программы. Для
компилирования перейдите в каталог <JWSDP_HOME>/docs/tutorial/examples/jaxrpc/hello
и выполните следующую команду:

ant compile-client

10.2.5.4. Пакетирование клиентской программы


Для пакетирования клиентской программы в JAR-файл выполните следующую команду:

ant jar-client
Эта команда создает файл dist/hello-client.jar.

Web-

Rendered by www.RenderX.com
Простой пример: HelloWorld Стр. 307 из 626

10.2.5.5. Выполнение клиентской программы


Для запуска программы HelloClient выполните следующую команду:

ant run
Программа должна отобразить следующую строку:

Hello Duke!
Задание ant run выполняет следующую команду:

java -classpath <cpath> hello.HelloClient


Переменная classpath содержит файл hello-client.jar, созданный в предыдущем разделе,
а также несколько JAR-файлов, принадлежащих Java WSDP. Для удаленного запуска
клиентской программы все эти файлы должны находиться на удаленном клиентском
компьютере.

10.2.6. Итеративная разработка


Для того чтобы показать вам каждый шаг разработки, в предыдущих разделах давались
указания выполнить несколько команд ant. Однако было бы неудобно набирать все эти
команды во время итеративной разработки. Для экономии времени после первоначального
размещения службы вы можете повторять следующие действия:
1. Протестируйте приложение.
2. Отредактируйте исходные файлы.
3. Выполните команду ant build для создания готового к размещению WAR-файла.
4. Выполните команду ant redeploy для отмены размещения и повторного размещения
службы.
5. Выполните команду ant build-static для создания JAR-файла клиентских статических
заглушек.
6. Выполните команду ant run.

10.2.7. Особенности реализации


Для реализации спецификации JAX-RPC Java WSDP требует наличия некоторых функций,
не описанных в спецификации. Эти функции являются специфичными для Java WSDP и
могут быть не совместимы с реализациями других поставщиков. Для JAX-RPC зависящие
от реализации особенности Java WSDP таковы:
• config.xml - См. раздел "Генерирование заглушек" для примера.
• jaxrpc-ri.xml - См. раздел "Пакетирование WAR-файла" для примера.
• Узлы - в предыдущем примере узлы находятся в файле hello-jaxrpc.war, который зависит
от реализации. (Файл hello-portable.war, однако, не зависит от реализации.)

Web-

Rendered by www.RenderX.com
Стр. 308 из 626 Java API for XML-based RPC

• Заглушки - заглушки находятся в файле hello-client.jar. Обратите внимание, что программа


HelloClient создает экземпляр MyHelloImpl - статический класс заглушки, см. разделы
"Пример клиентской программы динамического прокси" и "Пример клиентской программы
Dynamic Invocation Interface (DII)".
• Служебные программы - утилиты wsdeploy, wscompile и deploytool.
• Поддержка коллекций - см. таблицу 9-1.

10.3. Типы, поддерживаемые в JAX-RPC


Неявно JAX-RPC отображает типы языка программирования Java в определения XML/WSDL.
Например, JAX-RPC отображает класс java.lang.String в тип XML-данных xsd:string.
Разработчикам приложений нет необходимости знать детали такого отображения, но они
должны знать, что не каждый класс в Java 2 Standard Edition (J2SE™) может быть
использован в качестве параметра метода или типа возвращаемого значения в JAX-RPC.

10.3.1. Классы J2SE SDK


JAX-RPC поддерживает следующие классы J2SE SDK:

java.lang.Boolean
java.lang.Byte
java.lang.Double
java.lang.Float
java.lang.Integer
java.lang.Long
java.lang.Short
java.lang.String

java.math.BigDecimal
java.math.BigInteger

java.util.Calendar
java.util.Date
Данная редакция JAX-RPC поддерживает также несколько классов реализации интерфейса
java.util.Collection. См. таблицу 9-2.
Таблица 9-2 Поддерживаемые классы коллекций Java
Субинтерфейсы java.util.Collection Классы реализации
List ArrayList
LinkedList
Stack
Vector
Map HashMap
Hashtable
Properties
TreeMap

Web-

Rendered by www.RenderX.com
Типы, поддерживаемые в JAX-RPC Стр. 309 из 626

Субинтерфейсы java.util.Collection Классы реализации


Set HashSet
TreeSet

10.3.2. Примитивы
JAX-RPC поддерживает следующие элементарные типы языка программирования Java:

boolean
byte
double
float
int
long
short

10.3.3. Массивы
JAX-RPC поддерживает также массивы с членами, имеющими поддерживаемые JAX-RPC
типы. Примерами поддерживаемых массивов являются int[] и String[]. Поддерживаются
также многомерные массивы, такие как BigDecimal[][].

10.3.4. Прикладные классы


JAX-RPC, также, поддерживает классы, который вы написали для вашего приложения.
Для работы приложения, например, вы могли бы предоставить классы с именем Order,
ListItem и Product. Спецификация JAX-RPC ссылается на такие классы как значимые типы,
поскольку их значения (или состояния) могут быть переданы между клиентами и
удаленными службами как параметры метода или типы возвращаемых значений.
Чтобы быть поддерживаемым в JAX-RPC, класс приложения должен соответствовать
следующим правилам:
• Он должен иметь конструктор по умолчанию со спецификатором доступа public.
• Он не должен реализовывать (прямо или косвенно) интерфейс java.rmi.Remote.
• Его поля должны иметь поддерживаемые в JAX-RPC типы.
Класс может содержать поля public, private или protected. Для того чтобы их значения могли
быть переданы (или возвращены) во время удаленного вызова, поля должны удовлетворять
следующим требованиям:
• Поле public не должно быть final или transient.
• Не public поля должны иметь соответствующие методы getter и setter.

10.3.5. Компоненты JavaBeans


JAX-RPC поддерживает также компоненты JavaBeans, которые должны соответствовать
тому же набору правил, что и классы приложения. Кроме того, компонент JavaBeans должен
иметь метод getter и setter для каждого свойства компонента. Тип свойства компонента

Web-

Rendered by www.RenderX.com
Стр. 310 из 626 Java API for XML-based RPC

должен поддерживаться в JAX-RPC. Пример компонента JavaBeans находится в разделе


"Служба распределения в JAX-RPC".

10.4. Пример клиентской программы динамического прокси


Клиентская программа в разделе "Простой пример: HelloWorld" использовала статическую
заглушку для прокси. В отличие от нее клиентская программа этого раздела вызывает
удаленную процедуру через динамический прокси, класс, создаваемый во время
выполнения. Перед созданием класса прокси клиент получает информацию о службе
путем просмотра WSDL-документа.

10.4.1. Листинг клиентской программы динамического прокси HelloClient


Ниже приведен полный листинг файла HelloClient.java, находящегося в каталоге
<JWSDP_HOME>/docs/tutorial/examples/jaxrpc/proxy.

package proxy;

import java.net.URL;
import javax.xml.rpc.Service;
import javax.xml.rpc.JAXRPCException;
import javax.xml.namespace.QName;
import javax.xml.rpc.ServiceFactory;

public class HelloClient {

public static void main(String[] args) {


try {

String UrlString =
"http://localhost:8080/ProxyHelloWorld.wsdl";
String nameSpaceUri = "http://proxy.org/wsdl";
String serviceName = "HelloWorld";
String portName = "HelloIFPort";

URL helloWsdlUrl = new URL(UrlString);

ServiceFactory serviceFactory =
ServiceFactory.newInstance();

Service helloService =
serviceFactory.createService(helloWsdlUrl,
new QName(nameSpaceUri, serviceName));

Web-

Rendered by www.RenderX.com
Пример клиентской программы Dynamic Invocation Interface (DII) Стр. 311 из 626

HelloIF myProxy = (HelloIF) helloService.getPort(


new QName(nameSpaceUri, portName),
proxy.HelloIF.class);

System.out.println(myProxy.sayHello("Buzz"));

} catch (Exception ex) {


ex.printStackTrace();
}
}
}

10.4.2. Создание и выполнение примера динамического прокси


Выполните следующие действия:
1. Если вы этого еще не сделали, выполните указания раздела "Настройка".
2. Перейдите в каталог <JWSDP_HOME>/docs/tutorial/examples/jaxrpc/proxy.
3. Выполните следующие команды:
ant build
ant deploy
ant build-dynamic
ant run
Должна отобразиться следующая строка:

A dynamic proxy hello to Buzz!

10.5. Пример клиентской программы Dynamic Invocation Interface (DII)


При помощи интерфейса динамического вызова (DDI) клиент может вызвать удаленную
процедуру даже если сигнатура удаленной процедуры или имя службы неизвестны до
времени выполнения.
Из-за своей гибкости DII-клиент может использоваться как брокер служб, который
динамически обнаруживает службы, настраивает удаленные вызовы и выполняет эти
вызовы. Например, приложение для интерактивного магазина одежды может обращаться
к брокеру службы, специализирующемуся на доставке. Этот брокер мог бы использовать
Java API for XML Registries (JAXR) для поиска служб экспедиторских компаний,
удовлетворяющих определенному критерию, такому как низкая стоимость или быстрая
доставка. Во время выполнения брокер использует DII для вызова удаленных процедур
в Web-службах экспедиторских компаний. Как посредник между магазином одежды и
экспедиторскими компаниями брокер предлагает экономический эффект всем участникам.
Для магазина одежды он упрощает процесс доставки, а для экспедиторских компаний он
находит клиентов.

Web-

Rendered by www.RenderX.com
Стр. 312 из 626 Java API for XML-based RPC

10.5.1. Листинг DII HelloClient


Ниже приведен полный листинг файла HelloClient.java, расположенного в каталоге
<JWSDP_HOME>/docs/tutorial/examples/jaxrpc/dynamic.

package dynamic;

import javax.xml.rpc.Call;
import javax.xml.rpc.Service;
import javax.xml.rpc.JAXRPCException;
import javax.xml.namespace.QName;
import javax.xml.rpc.ServiceFactory;
import javax.xml.rpc.ParameterMode;

public class HelloClient {

private static String endpoint =


"http://localhost:8080/dynamic-jaxrpc/dynamic";
private static String qnameService = "Hello";
private static String qnamePort = "HelloIF";

private static String BODY_NAMESPACE_VALUE =


"http://dynamic.org/wsdl";
private static String ENCODING_STYLE_PROPERTY =
"javax.xml.rpc.encodingstyle.namespace.uri";
private static String NS_XSD =
"http://www.w3.org/2001/XMLSchema";
private static String URI_ENCODING =
"http://schemas.xmlsoap.org/soap/encoding/";

public static void main(String[] args) {


try {

ServiceFactory factory =
ServiceFactory.newInstance();
Service service =
factory.createService(new QName(qnameService));

QName port = new QName(qnamePort);

Call call = service.createCall(port);


call.setTargetEndpointAddress(endpoint);

Web-

Rendered by www.RenderX.com
Пример клиентской программы Dynamic Invocation Interface (DII) Стр. 313 из 626

call.setProperty(Call.SOAPACTION_USE_PROPERTY,
new Boolean(true));
call.setProperty(Call.SOAPACTION_URI_PROPERTY,"");
call.setProperty(ENCODING_STYLE_PROPERTY,
URI_ENCODING);
QName QNAME_TYPE_STRING =
new QName(NS_XSD, "string");
call.setReturnType(QNAME_TYPE_STRING);

call.setOperationName(
new QName(BODY_NAMESPACE_VALUE "sayHello"));
call.addParameter("String_1", QNAME_TYPE_STRING,
ParameterMode.IN);
String[] params = { "Duke!" };

String result = (String)call.invoke(params);


System.out.println(result);

} catch (Exception ex) {


ex.printStackTrace();
}
}
}

10.5.2. Создание и выполнение примера DII


Выполните следующие действия:
1. Если вы этого еще не сделали, выполните указания раздела "Настройка".
2. Перейдите в каталог <JWSDP_HOME>/docs/tutorial/examples/jaxrpc/dynamic.
3. Выполните следующие команды:

ant build
ant deploy
ant build-dynamic
ant run
Должна отобразиться следующая строка:

A dynamic hello to Duke!

Web-

Rendered by www.RenderX.com
Стр. 314 из 626 Java API for XML-based RPC

10.6. Безопасность в JAX-RPC


В этом разделе вы научитесь создавать JAX-RPC приложения Web-служб, использующих
HTTP/SSL для базовой или взаимной аутентификации. Если тема аутентификации для
вас нова, обратитесь, пожалуйста, к главе "Безопасность Web-приложений".
Примечание: Инструкции в этом разделе относятся только к версии 1.4 J2SE SDK.
Существуют определенные действия, которые вы должны выполнить для конфигурации
конечной точки JAX-RPC Web-службы для базовой и взаимной аутентификации HTTP/S:
• Используйте программу keytool, являющуюся частью J2SE SDK, для генерирования
сертификатов и хранилищ ключей.
• Добавьте SSL-коннектор к Tomcat при помощи программы admintool, являющейся частью
Java WSDP.
• Перезапустите Tomcat.
• Добавьте элементы безопасности в дескриптор размещения web.xml.
• Установите некоторые свойства в коде клиентской программы.
• Скомпонуйте и запустите Web-службу.
Далее следуют детальные инструкции для выполнения этих действий.

10.6.1. Базовая аутентификация по SSL


Здесь описаны действия по настройке Web-службы для базовой аутентификации в HTTP/S.
Обратитесь к разделу "Взаимная аутентификация по SSL" для инструкций по настройке
этой же службы для взаимной аутентификации.

10.6.1.1. Генерирование сертификатов для базовой аутентификации


Для генерирования SSL-сертификатов и экспортирования их в соответствующие хранилища
ключей сервера и клиента используется программа keytool. Помните, что хранилища
ключей сервера и клиента создаются в каталоге, из которого вы запускаете keytool.
1. Перейдите в каталог <JWSDP_HOME>/docs/tutorial/examples/jaxrpc/security.
2. Запустите keytool для генерирования хранилищ ключа сервера с паролем по умолчанию
changeit.
UNIX:
Укажите имя сервера, например localhost, и идентификационную информацию о
пользователе как аргументы keytool. Введите следующее:

$JAVA_HOME/bin/keytool -genkey -alias tomcat-server -dname


"CN=<server name>, OU=<organizational unit>, O=<organization>,
L=<locality>, S=<state>, C=<country code>", -keyalg RSA -
keypass changeit -storepass changeit -keystore server.keystore
Windows:
Программа keytool запросит вас ввести имя сервера, подразделение организации,
организацию, местность, штат и код страны. Обратите внимание, что вы должны ввести

Web-

Rendered by www.RenderX.com
Безопасность в JAX-RPC Стр. 315 из 626

имя сервера в ответ на первый запрос, который спросит имя и фамилию. Введите
следующее:

%JAVA_HOME%\bin\keytool -genkey -alias tomcat-server -keyalg


RSA -keypass changeit -storepass changeit -keystore
server.keystore
3. Экспортируйте сгенерированный сертификат сервера.
Команда keytool одинакова для UNIX и Windows. В UNIX введите следующее:

$JAVA_HOME/bin/keytool -export -alias tomcat-server -storepass


changeit -file server.cer -keystore server.keystore
4. Сгенерируйте хранилище ключей клиента.
UNIX:

$JAVA_HOME/bin/keytool -genkey -alias tomcat-client -dname


"CN=<client name>, OU=<organizational unit>, O=<organization>,
L=<locality>, S=<state>, C=<country code>", -keyalg RSA -
keypass changeit -storepass changeit -keystore client.keystore
Windows:
Программа keytool запросит вас ввести имя сервера клиента, подразделение
организации, организацию, местность, штат и код страны. Обратите внимание, что вы
должны ввести имя сервера в ответ на первый запрос, который спросит имя и фамилию.
Введите следующее:

%JAVA_HOME%\bin\keytool -genkey -alias tomcat-client -keyalg


RSA -keypass changeit -storepass changeit -keystore
client.keystore
5. Импортируйте сертификат сервера в хранилище ключей клиента.
Для базовой аутентификации необходимо только импортировать сертификат сервера
в хранилище ключей клиента. Команда keytool одинакова для UNIX и Windows. В UNIX
введите следующее:

$JAVA_HOME/bin/keytool -import -v -trustcacerts -alias tomcat-


server -file server.cer -keystore client.keystore -keypass
changeit -storepass changeit

10.6.1.2. Добавление SSL-коннектора к Tomcat


В этом разделе вы добавите SSL-коннектор при помощи admintool, программы, включенной
в Java WSDP. Дополнительную информацию по этой программе можно найти в приложении
"Средство администрирования Tomcat".

Web-

Rendered by www.RenderX.com
Стр. 316 из 626 Java API for XML-based RPC

1. Выполните инструкции, приведенные в разделе "Добавление SSL-коннектора в


admintool". В правой панели, отображенной программой admintool, введите значения,
показанные в таблице 9-3.
Таблица 9-3 Значения SSL-коннектора в admintool
Поле Значение
Type HTTPS
Port 8443
Keystore Name <JWSDP_HOME>/docs/tutorial/examples/jaxrpc/secu-
rity/server.keystore
Keystore Password changeit

2. Перезапустите Tomcat.
3. Убедитесь, что SSL-коннектор был добавлен, следуя указаниям раздела "Верификация
SSL-поддержки".

10.6.1.3. Добавление элементов безопасности в web.xml


Файлы для этого примера находятся в каталоге <JWSDP_HOME>/docs/tutorial/exam-
ples/jaxrpc/security. Для аутентификации по SSL файл web.xml содержит элементы <security-
constraint> и <login-config>:

<security-constraint>
<web-resource-collection>
<web-resource-name>SecureHello</web-resource-name>
<url-pattern>/security</url-pattern>
<http-method>GET</http-method>
<http-method>POST</http-method>
</web-resource-collection>
<auth-constraint>
<role-name>manager</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>BASIC</auth-method>
</login-config>
Обратите внимание, что элемент <role-name> указывает manager, роль, которая уже была
указана в файле <JWSDP_HOME>/conf/tomcat-users.xml. Чтобы узнать, как обновить файл
tomcat-users.xml при помощи admintool, обратитесь к разделу "Управление ролями".

10.6.1.4. Установка свойств безопасности в коде клиентской программы


Исходный код клиентской программы находится в файле HelloClient.java в каталоге
<JWSDP_HOME>/docs/tutorial/examples/jaxrpc/security. Для базовой аутентификации по
SSL клиентская программа должна установить несколько относящихся к безопасности
свойств.

Web-

Rendered by www.RenderX.com
Безопасность в JAX-RPC Стр. 317 из 626

10.6.1.4.1. Свойство trustStore


Значением свойства trustStore является полностью указанное имя файла client.keystore:

<JWSDP_HOME>/docs/tutorial/examples/jaxrpc/security/client.key
store
В предыдущем разделе "Генерирование сертификатов для базовой аутентификации" вы
создали файл client.keystore при помощи выполнения программы keytool. Клиент указывает
свойство trustStore следующим образом:

System.setProperty("javax.net.ssl.trustStore", trustStore);

10.6.1.4.2. Свойство trustStorePassword


Свойство trustStorePassword является паролем хранилища ключей J2SE SDK. В
предыдущем разделе вы указали значение по умолчанию для этого пароля (changeit) при
выполнении keytool. Клиент указывает свойство trustStorePassword следующим образом:

System.setProperty("javax.net.ssl.trustStorePassword",
trustStorePassword);

10.6.1.4.3. Свойства username и password


Значения свойств username и password соответствуют роли manager, которая указана в
файле <JWSDP_HOME>/conf/tomcat-users.xml. (См. раздел "Управление ролями и
пользователями".) Программа установки Java WSDP автоматически добавляет имя
пользователя и пароль в файл tomcat-users.xml.
Клиент устанавливает свойства username и password следующим образом:

stub._setProperty(javax.xml.rpc.Stub.USERNAME_PROPERTY,
username);
stub._setProperty(javax.xml.rpc.Stub.PASSWORD_PROPERTY,
password);

10.6.1.5. Компоновка и выполнение примера для базовой аутентификации по SSL


Выполните следующие действия:
1. Если вы этого еще не сделали, выполните указания раздела "Настройка".
2. Выполните инструкции, приведенные в разделах "Генерирование сертификатов для
базовой аутентификации" и "Добавление SSL-коннектора к Tomcat"/ Не забудьте
перезапустить Tomcat.
3. Перейдите в каталог <JWSDP_HOME>/docs/tutorial/examples/jaxrpc/security.
4. Выполните следующие команды:

Web-

Rendered by www.RenderX.com
Стр. 318 из 626 Java API for XML-based RPC

ant build
ant deploy
ant build-static
ant run-security
Должна отобразиться следующая строка:

Hello Duke (secure)

10.6.2. Взаимная аутентификация по SSL


Для настройки и создания JAX-RPC-службы со взаимной аутентификацией выполните все
действия, описанные в предыдущем разделе ("Базовая аутентификация по SSL"), до
команды ant build-static. Затем выполните следующие действия:
1. Экспортируйте сгенерированный сертификат клиента.
Команда keytool одинакова для UNIX и Windows. В UNIX выполните следующее:

$JAVA_HOME/bin/keytool -export -alias tomcat-client -storepass


changeit -file client.cer -keystore client.keystore
2. Импортируйте сертификат клиента в хранилище ключей сервера.
Команда keytool одинакова для UNIX и Windows. В UNIX выполните следующее:

$JAVA_HOME/bin/keytool -import -v -trustcacerts -alias tomcat-


client -file client.cer -keystore server.keystore -keypass
changeit -storepass changeit
3. Выполните приложение:

ant run-security
Должна отобразиться следующая строка:

Hello Duke (secure)


Признательность: Этот раздел включает материал из белых страниц "Web Services Security
Configuration", написанных Rahul Sharma и Beth Stearns.

10.7. JAX-RPC в J2EE SDK 1.3.1


В примере этого раздела автономный JAX-RPC-клиент выполняет удаленный вызов JAX-
RPC-службы, размещенной в виде сервлета на J2EE SDK. Сервлет определяет не имеющий
состояния сессионный компонент и затем вызывает метод компонента.
Примечание: Инструкции этого раздела относятся только к версии 1.3.1 J2EE SDK.

Web-

Rendered by www.RenderX.com
JAX-RPC в J2EE SDK 1.3.1 Стр. 319 из 626

10.7.1. Предварительные требования


Этот раздел предназначен для опытных пользователей, которые знакомы со следующими
понятиями:
• Переменная окружения PATH (для чего она и как ее установить)
• Технологии EJB и J2EE
• Программа deploytool из J2EE SDK.
Материалы по технологиям EJB и J2EE можно найти в руководстве по J2EE:

http://java.sun.com/j2ee/tutorial/index.html
Для выполнения этого примера вы должны загрузить и установить J2EE SDK, который
доступен по следующему URL:

http://java.sun.com/j2ee/sdk_1.3/index.html
Примечание: На странице предыдущего URL обязательно прочитайте раздел
"Поддерживаемые операционные системы и необходимое программное обеспечение".
J2EE SDK не поддерживает Windows 95, 98, ME или XP.

10.7.2. Пример программы


Файлы примера расположены в каталоге <JWSDP_HOME>/docs/tutorial/examples/jaxrpc/toejb.
Подкаталог greeting содержит исходный код не имеющего состояния сессионного компонента
с именем GreetingEJB. Вам не обязательно компилировать или пакетировать компонент,
поскольку он уже спакетирован в архив J2EE-приложения под именем GreetingApp.ear.
Этот EAR-файл находится в подкаталоге provided-jars.
Во время выполнения JAX-RPC-клиент с именем HelloClient выполняет удаленный вызов
метода sayHello JAX-RPC Web-службы:

System.out.println(stub.sayHello("Buzz!"));
Затем, метод sayHello класса HelloImpl вызывает метод sayHey компонента GreetingEJB:

public String sayHello(String name) {

String result = null;

try {
Context initial = new InitialContext();
Context myEnv =
(Context)initial.lookup("java:comp/env");
Object objref = myEnv.lookup("ejb/SimpleGreeting");

Web-

Rendered by www.RenderX.com
Стр. 320 из 626 Java API for XML-based RPC

GreetingHome home =
(GreetingHome)PortableRemoteObject.narrow
(objref, GreetingHome.class);

Greeting salutation = home.create();


result = salutation.sayHey(name);

} catch (Exception ex) {


System.out.println("Exception in sayHello: "
+ ex.getMessage());
}

return result;

}
Вот метод sayHey класса GreetingBean не имеющего состояния сессионного компонента
GreetingEJB:

public String sayHey(String name) {


return "Hey " + name + "!";
}

10.7.3. Пакетирование клиентской программы JAX-RPC и Web-службы


1. Если ваша переменная окружения PATH содержит <J2EE_HOME>/bin, измените PATH
так, чтобы <JWSDP_HOME>/bin предшествовал <J2EE_HOME>/bin.
2. В терминальном окне перейдите в каталог <JWSDP_HOME>/docs/tutorial/exam-
ples/jaxrpc/toejb.
3. В текстовом редакторе откройте файл build.xml и установите значение j2ee.home в
месторасположение вашей установки J2EE SDK.
4. Выполните следующие команды:

ant build
ant build-static
Эти команды создадут файлы toejb-jaxrpc.war и toejb-client.jar в подкаталоге dist.

10.7.4. Настройка J2EE SDK 1.3.1


1. Если Tomcat выполняется, остановите его.
2. Если j2ee-сервер выполняется, остановите его.
3. Измените переменную окружения PATH так, чтобы <J2EE_HOME>/bin предшествовал
<JWSDP_HOME>/bin.

Web-

Rendered by www.RenderX.com
JAX-RPC в J2EE SDK 1.3.1 Стр. 321 из 626

Примечание: Во всех последующих действиях <J2EE_HOME>/bin должен


предшествовать <JWSDP_HOME>/bin в переменной окружения PATH.
4. В терминальном окне выполните следующую команду:
UNIX:

<JWSDP_HOME>/bin/jwsdponj2ee.sh $J2EE_HOME
Windows:

<JWSDP_HOME>\bin\jwsdponj2ee.bat %J2EE_HOME%
Сценарий jwsdponj2ee добавляет библиотеки Java WSDP к J2EE SDK и затем меняет
порт Web-службы J2EE SDK с 8000 в 8080. После завершения выполнения этого
примера вы, возможно, захотите выполнить инструкции раздела "Удаление действий
jwsdponj2ee".
5. В терминальном окне запустите j2ee-сервер:

j2ee -verbose
6. И Java WSDP, и J2EE SDK имеют служебные программы, называемые deploytool. Для
следующих действий вы должны запускать deploytool из J2EE SDK. Для проверки того,
что ваша переменная окружения PATH указывает на deploytool из J2EE SDK, выполните
следующую команду:

deploytool -version
Программа должна отобразить следующую строку:

The deployment tool version is 1.3.1


7. Запустите deploytool из J2EE SDK:

deploytool

10.7.5. Размещение сессионного компонента GreetingEJB


1. В программе deploytool откройте файл GreetingApp.ear, который расположен в каталоге
<JWSDP_HOME>/docs/tutorial/examples/jaxrpc/toejb/provided-jars.
2. В древовидном списке разверните GreetingApp. Обратите внимание, что он содержит
корпоративный компонент с именем GreetingEJB и клиентское J2EE-приложение с
именем GreetingClient. Эта клиентская программа была создана для тестирования
компонента и не будет запускаться в данном примере.
3. Разместите приложение GreetingApp.

Web-

Rendered by www.RenderX.com
Стр. 322 из 626 Java API for XML-based RPC

10.7.6. Размещение JAX-RPC-службы


1. В deploytool создайте новое приложение с именем HelloApp.
2. Добавьте файл toejb-jaxrpc.war в HelloApp. WAR-файл находится в каталоге
<JWSDP_HOME>/docs/tutorial/examples/jaxrpc/toejb/dist.
3. Укажите ссылку на GreetingEJB.
A. В древовидном списке выберите HelloWorldApplication.
B. На закладке EJB Refs добавьте запись со значениями, приведенными в .
Таблица 9-4 Закладка EJB Refs приложения HelloWorldApplication
Поле Значение
Coded Name ejb/SimpleGreeting
Type Session
Interfaces Remote
Home Interface toejb.greeting.GreetingHome
Local/Remote Interface toejb.greeting.Greeting

4. Укажите JNDI-имя GreetingEJB.


A. В древовидном списке выберите HelloApp.
B. На закладке JNDI Names введите MyGreeting в поле JNDI Name внизу.

5. Установите корень контекста для сервлета.


A. В древовидном списке выберите HelloApp.
B. На закладке Web Context введите toejb-jaxrpc в поле Context-Root.

6. Разместите приложение HelloApp.


Если вы имеете проблемы с размещением этого приложения, может быть полезным
сравнение созданного вами файла HelloApp.ear с файлом CompareHelloApp.ear в
подкаталоге provided-jars. HelloWorldApplication в файле CompareHelloApp.ear содержит
корректные установки и может быть размещен без дополнительных действий.

10.7.7. Выполнение клиентского приложения JAX-RPC


1. В терминальном окне перейдите в каталог <JWSDP_HOME>/docs/tutorial/exam-
ples/jaxrpc/toejb.
2. Выполните следующую команду:

ant run
Должна отобразиться следующая строка:

Hey Buzz!!

Web-

Rendered by www.RenderX.com
Создание службы JAX-RPC в deploytool Стр. 323 из 626

10.7.8. Удаление действий jwsdponj2ee


В разделе "Установка J2EE SDK 1.3.1" вы запустили сценарий jwsdponj2ee, который внес
некоторые изменения в установку J2EE SDK. Для отмены этих изменений выполните
следующие действия:
1. Остановите j2ee-сервер.
2. В текстовом редакторе откройте файл <J2EE_HOME>/config/web.properties и измените
значение свойства http.port с 8080 на 8000.
3. В текстовом редакторе откройте сценарий userconfig из каталога <J2EE_HOME>/bin и
закомментируйте операторы, устанавливающие переменную J2EE_HOME.

10.8. Создание службы JAX-RPC в deploytool


В примерах предыдущих разделов вы выполняли задания ant для компоновки и установки
служб. В этом разделе вы создадите службу, используя deploytool, а не ant. Служебная
программа deploytool автоматически выполняет следующие задачи:
• Создает файл web.xml
• Создает WAR-файл
• Устанавливает Web-приложение
• Устанавливает WSDL-документ для службы в Tomcat
Клиентская программа в этом примере является динамическим прокси, похожим на
программу, описанную в разделе "Пример клиентской программы динамического прокси".
Во время выполнения клиент обращается к WSDL-документу, установленному при помощи
deploytool. Исходный код для этой программы HelloClient находится в каталоге
<JWSDP_HOME>/docs/tutorial/examples/jaxrpc/dephello.
Перед работой с примером этого раздела вы должны быть знакомы с основными функциями
программы deploytool. Краткое введение можно найти в разделе "Размещение приложения
с использованием deploytool".

10.8.1. Компиляция исходного кода


1. Если вы этого еще не сделали, выполните указания раздела "Настройка".
2. В терминальном окне перейдите в каталог <JWSDP_HOME>/docs/tutorial/exam-
ples/jaxrpc/dephello.
3. Выполните следующую команду:

ant build
Задание build выполняет следующие задачи:
• Компилирует интерфейс службы и класс реализации
• Компилирует клиентское приложение
• Пакетирует клиентское приложение в файл dist/dephello-client.jar

Web-

Rendered by www.RenderX.com
Стр. 324 из 626 Java API for XML-based RPC

10.8.2. Компоновка Web-приложения


В этом разделе вы спакетируете службу в WAR-файл при помощи помощника New Web
Application программы deploytool. После запуска помощника (File->New Web Application)
появится следующие диалоговые окна:
1. Диалоговое окно Introduction
A. Прочитайте информацию в этом окне, чтобы больше узнать о помощнике.
B. Нажмите Next.

2. Диалоговое окно WAR File


A. В поле Module File Name введите myHello.war.
B. В поле War Display Name введите MyHelloWAR.
C. Нажмите Edit.

3. Диалоговое окно Edit Contents


A. Перейдите к следующему каталогу, содержащему интерфейс службы и класс
реализации.

<JWSDP_HOME>/docs/tutorial/examples/jaxrpc/dephello/build/shared.
B. Добавьте HelloIF.class и HelloImpl.class в поле Contents of MyHelloWAR.
C. Нажмите OK.
D. Нажмите Next.

4. Диалоговое окно Choose Component Type


A. Выберите переключатель JAX-RPC Endpoint.
B. Нажмите Next.

5. Диалоговое окно JAX-RPC Default Settings


A. В поле WSDL Target Namespace Base String введите следующее:

http://wombat.com/wsdl/
B. В поле Schema Target Namespace Base String введите следующее:

http://wombat.com/xsd/
C. В поле Endpoint Alias Base String введите /jaxrpc.
D. Нажмите Next.

6. Диалоговое окно JAX-RPC Endpoint


A. В поле комбинированного списка EndPoint Interface выберите dephello.HelloIF.

Web-

Rendered by www.RenderX.com
Создание службы JAX-RPC в deploytool Стр. 325 из 626

B. В поле комбинированного списка EndPoint Class выберите dephello.HelloImpl.


C. В поле Endpoint Name введите MyHelloWorld.
D. В поле Endpoint Display Name введите MyHelloWorld.
E. Оставьте поле Alias пустым.
F. Нажмите Next.

7. Диалоговое окно JAX-RPC Model


A. Выберите переключатель Use Default Settings.
B. Нажмите Next.

8. Диалоговое окно Review Settings


A. Взгляните на два XML-файла, отображенные в этом диалоговом окне. Файл в
верхней части окна - web.xml, который будет спакетирован в WAR-файл. Файл в
нижней части - jaxrpc-ri.xml, также будет спакетирован в WAR-файл. Файл jaxrpc-
ri.xml зависит от реализации и не определен в спецификациях.
B. Нажмите Finish.

Теперь программа deploytool создает файл MyHello.war.

10.8.3. Размещение Web-приложения


1. Из основного меню deploytool выберите Tools->Deploy.
2. В диалоговом окне Text Input введите /jaxrpc-dephello для Web context.
3. Появится диалоговое окно Deployment Console, в котором отобразится следующая
строка:

OK - Installed application at context path /jaxrpc-dephello


4. Нажмите Close.

10.8.4. Проверка статуса Web-службы


В броузере перейдите к URL:

http://localhost:8080/jaxrpc-dephello/jaxrpc/MyHelloWorld
В броузере отобразится страница, показывающая статус имени порта (или конечной точки)
MyHelloWorld. Эта страница также показывает URL для WSDL-документа:

http://localhost:8080/jaxrpc-dephello/jaxrpc/MyHelloWorld?WSDL
Этот URL программа HelloClient будет использовать для определения WSDL-документа,
созданного во время размещения.

Web-

Rendered by www.RenderX.com
Стр. 326 из 626 Java API for XML Messaging

Примечание: Если у вас возникли проблемы при размещении Web-службы, может быть
полезным сравнение вашего файла MyHello.war с файлом CompareMyHello.war из
подкаталога dephello/provided-jars.

10.8.5. Выполнение клиентской программы


1. В терминальном окне перейдите в каталог <JWSDP_HOME>/docs/tutorial/exam-
ples/jaxrpc/dephello.
2. Выполните следующую команду:

ant run
На клиентском компьютере должна отобразиться следующая строка:

A dynamic proxy hello to Murphy!

10.9. Дополнительная информация


Ниже приведены дополнительные источники информации по JAX-RPC и связанным
технологиям:
• Спецификация Java API for XML-based RPC 1.0 http://java.sun.com/xml/down-
loads/jaxrpc.html
• Домашняя страница JAX-RPC http://java.sun.com/xml/jaxrpc/index.html
• Simple Object Access Protocol (SOAP) 1.1 W3C Note http://www.w3.org/TR/SOAP/
• Web Services Description Language (WSDL) 1.1 W3C Note http://www.w3.org/TR/wsdl

11. Java API for XML Messaging


Java API for XML Messaging (JAXM) предоставляет разработчикам возможность
осуществлять обмен XML-сообщениями используя платформу Java. Простым вызовом
метода при помощи JAXM API вы можете создавать и передавать XML-сообщения по сети
Интернет. Эта глава поможет вам научиться пользоваться JAXM API.
JAXM API соответствует спецификации SOAP (Simple Object Access Protocol - простой
протокол доступа к объектам) 1.1 и спецификации "SOAP with Attachements" (SOAP с
вложениями). Полный JAXM API представлен двумя пакетами:
• javax.xml.soap - пакет, определенный в спецификации "SOAP with Attachements API for
Java" (SAAJ) 1.1. Это основной пакет для обмена сообщениями в SOAP, содержащий
API для создания и заполнения SOAP-сообщения. Этот пакет имеет все API,
необходимые для передачи сообщений типа запрос-ответ. (Сообщения типа запрос-
ответ рассматриваются в разделе "SOAPConnection".) Текущей версией является SAAJ
1.1_02.
• javax.xml.messaging - пакет, определенный в спецификации JAXM 1.1. Этот пакет
содержит API, необходимые для использования поставщика системы обмена

Web-

Rendered by www.RenderX.com
Введение в JAXM Стр. 327 из 626

сообщениями и, таким образом, для применения однонаправленных сообщений.


(Однонаправленные сообщения рассматриваются в разделе "ProviderConnection".)
Текущей версией является JAXM 1.1_01.
Первоначально оба пакета были определены в спецификации JAXM 1.0. Пакет
javax.xml.soap был отделен и расширен в спецификацию SAAJ 1.1, так что сейчас он никак
не связан с пакетом javax.xml.messaging и, следовательно, может использоваться
независимо. SAAJ API также облегчает создание XML-фрагментов, что особенно полезно
при разработке реализаций JAX-RPC.
Пакет javax.xml.messaging, определенный в спецификации JAXM 1.1, сохраняет свою
зависимость от пакета java.xml.soap, поскольку пакет soap содержит API, использующийся
для создания и управления SOAP-сообщениями. Другими словами, клиент, передающий
сообщения вида запрос-ответ, может использовать только javax.xml.soap API. Web-служба
или клиент, использующие однонаправленный обмен сообщениями, должны использовать
API из обоих пакетов - javax.xml.soap и javax.xml.messaging.
Примечание: В данном документе ссылка на "JAXM 1.1_01 API" равносильна ссылке на
API в пакете javax.xml.messaging; ссылка "SAAJ API" равносильна ссылке на API в пакете
javax.xml.soap. "JAXM API" - это более общее понятие, относящееся ко всем API,
используемым для обмена SOAP-сообщениями, то есть к API в обоих пакетах.
Кроме объяснения принципов использования JAXM API в этой главе приводятся инструкции
для выполнения примеров JAXM-приложений, включенных в Java WSDP. Вы можете
сначала прочитать введение и учебник перед выполнением примеров для того, чтобы
понимать, что именно делают приложения, либо вы, возможно, захотите сначала
исследовать примеры. Введение объясняет концептуальную основу, стоящую за JAXM
API, и помогает вам понять, что происходит и как. Учебник рассматривает принципы
использования JAXM API, приводя примеры и объяснения большинства используемых
функциональных возможностей. Наконец, примеры приложений в последней части
руководства объясняют, как создать приложение.

11.1. Введение в JAXM


Это введение представляет обобщенный взгляд на работу JAXM-системы обмена
сообщениями и в общих терминах объясняет ее концепции. Цель этой главы - познакомить
вас с терминологией и со средой для дальнейшей работы с учебником и примерами
приложений.
Введение рассматривает JAXM с трех точек зрения:
• Сообщения
• Соединения
• Поставщики служб обмена сообщениями

11.1.1. Сообщения
JAXM-сообщения соответствуют стандарту SOAP, который описывает формат сообщений,
а также указывает обязательные, не обязательные и недопустимые элементы. При помощи
JAXM API вы можете создавать XML-сообщения, соответствующие спецификациям SOAP,
простым вызовом Java API.

Web-

Rendered by www.RenderX.com
Стр. 328 из 626 Java API for XML Messaging

11.1.1.1. Структура XML-документа


Примечание: Более полная информация по XML-документам приведена в разделах "Основы
XML" и "Java API for XML Processing".
XML-документ представляет собой иерархическую структуру с элементами, субэлементами,
субсубэлементами и т.д. Вы увидите, что многие SOAP-классы и интерфейсы представляют
XML-элементы в SOAP-сообщении и содержат в своих названиях слова element, SOAP,
либо то и другое.
Элемент также называется узлом. Соответственно, SAAJ API имеет интерфейс Node,
который является базовым классом для всех классов и интерфейсов, представляющих
XML-элементы в SOAP-сообщении. Существуют также методы, такие как SOAPEle-
ment.addTextNode, Node.detachNode и Node.getValue, способы использования которых вы
рассмотрите в учебнике.

11.1.1.2. Что такое сообщение?


Существует два основных типа SOAP-сообщений - имеющие вложения и не имеющие.

11.1.1.2.1. Сообщения без вложений


Следующий список показывает очень обобщенную структуру SOAP-сообщения без
вложений. За исключением SOAP-заголовка все перечисленные части обязательны.
1. SOAP-сообщение
A. SOAP-часть
i. SOAP-конверт
a) SOAP-заголовок (не обязателен)
b) SOAP-тело

SAAJ API предоставляет класс SOAPMessage для представления SOAP-сообщения,


SOAPPart для представления SOAP-части, SOAPEnvelope для представления SOAP-
конверта и т.д.
При создании нового объекта SOAPMessage он автоматически будет иметь части, которые
должны быть в SOAP-сообщении. Другими словами, новый объект SOAPMessage имеет
объект SOAPPart, который содержит объект SOAPEnvelope. Объект SOAPEnvelope, в свою
очередь, автоматически содержит пустой объект SOAPHeader, за которым следует объект
SOAPBody. Если вам не нужен объект SOAPHeader, являющийся необязательным, вы
можете удалить его. Логическим объяснением его автоматического включения является
то, что он чаще нужен, чем нет.
Объект SOAPHeader может содержать один или более заголовков с информацией о
передающей и принимающей сторонах и о промежуточных назначениях сообщения.
Заголовки могут также выполнять такие задачи, как установка соотношений с предыдущими
сообщениями, указание уровня службы и содержать маршрутную информацию и
информацию о доставке. Объект SOAPBody, который всегда следует за объектом SOAP-
Header, если он существует, предоставляет простой способ передачи произвольной
информации, предназначенной для конечного адресата. Если существует объект SOAPFault
(см. раздел "Ошибки SOAP"), он должен находиться в объекте SOAPBody.

Web-

Rendered by www.RenderX.com
Введение в JAXM Стр. 329 из 626

Рисунок 10-1 Объект SOAPMessage без вложений

11.1.1.2.2. Сообщения с вложениями


SOAP-сообщение может содержать одну или более частей вложений в дополнение к SOAP-
части. SOAP-часть может содержать только XML-содержимое; поэтому, если какое-либо
содержимое сообщения не записано в XML-формате, оно должно находиться в части
вложения. То есть, если вы, например, хотите передать в сообщении изображение или
обычный текст, ваше сообщение должно иметь часть вложений. Обратите внимание, что
часть вложений может содержать любой тип содержимого, то есть он может также
содержать данные в XML-формате. На рисунке 10-2 показана высокоуровневая структура
SOAP-сообщения, которое имеет два вложения.

Web-

Rendered by www.RenderX.com
Стр. 330 из 626 Java API for XML Messaging

Рисунок 10-2 Объект SOAPMessage с двумя объектами AttachmentPart

SAAJ API предоставляет класс AttachmentPart для представления части вложения SOAP-
сообщения. Объект SOAPMessage автоматически содержит объект SOAPPart и его
необходимые подчиненные элементы, но поскольку объект AttachmentPart не обязателен,
вы должны создать и добавить его самостоятельно. Раздел "Учебник" покажет вам процесс
создания и заполнения сообщений с вложениями и без них.
Объект SOAPMessage может иметь одно или более вложений. Каждый объект Attachment-
Part имеет MIME-заголовок для обозначения типа содержащихся в нем данных. Он может
также содержать дополнительные MIME-заголовки для обозначения типа или для указания
месторасположения, которые могут быть полезны при наличии нескольких вложений. Когда
объект SOAPMessage имеет один или более объектов AttachmentPart, его объект SOAPPart
может иметь содержимое сообщения, а может и не иметь.
На систему обмена сообщениями SOAP можно взглянуть еще с одной стороны. А именно
- используется или нет поставщик службы обмена сообщениями. Этот вопрос обсуждается
в разделе "Поставщики службы обмена сообщениями".

Web-

Rendered by www.RenderX.com
Введение в JAXM Стр. 331 из 626

11.1.2. Соединения
Все SOAP-сообщения передаются и принимаются по соединению. Соединение может быть
установлено непосредственно с конкретным адресатом, или с поставщиком службы обмена
сообщениями. (Поставщик службы обмена сообщениями представляет собой службу,
которая управляет передачей и маршрутизацией сообщений и предоставляет возможности,
недоступные при использовании соединения непосредственно с конечным адресатом.
Поставщики службы обмена сообщениями рассмотрены более детально далее.)
JAXM API предоставляет следующие классы и интерфейсы для представления этих двух
типов соединений:
1. javax.xml.soap.SOAPConnection - соединение между отправителем и непосредственно
с адресатом (соединение точка-точка).
2. javax.xml.mesaging.ProviderConnection - соединение с поставщиком службы обмена
сообщениями.

11.1.2.1. SOAPConnection
Объект SOAPConnection, представляющий соединение точка-точка, создать и использовать
просто. Одна из причин этого - вы не должны выполнять какую-либо настройку для
использования объекта SOAPConnection, поскольку он не должен работать в контейнере
сервлетов (например Tomcat) или в J2EE-контейнере. Он является единственным типом
соединения, доступным клиенту, не использующему поставщика службы обмена
сообщениями.
В следующем фрагменте кода создается объект SOAPConection и, затем, после создания
и заполнения сообщения, используется соединение для передачи сообщения. Параметр
request представляет собой передаваемое сообщение, а endpoint - пункт назначения.

SOAPConnectionFactory factory =
SOAPConnectionFactory.newInstance();
SOAPConnection con = factory.createConnection();

. . .// request

SOAPMessage response = con.call(request, endpoint);


При использовании объекта SOAPConnection единственный способ передать сообщение
- метод call, который передает сообщение и блокирует работу до получения ответа.
Поскольку метод call требует ответа, этот тип обмена сообщениями называется обмен
сообщениями типа запрос-ответ.
Реализованная для системы обмена сообщениями типа запрос-ответ Web-служба должна
возвращать ответ на любое сообщение, которое она принимает. Если сообщение является
обновлением, ответ - это подтверждение того, что оно было получено. Такое подтверждение
предполагает, что обновление было успешным. Некоторые сообщения могут вообще не
требовать ответа. Служба, принимающая такие сообщения, все равно должна передать
ответ, поскольку он необходим для разблокирования метода call. В этом случае ответ

Web-

Rendered by www.RenderX.com
Стр. 332 из 626 Java API for XML Messaging

никак не связан с содержимым сообщения; это просто сообщение для разблокирования


метода call.
Поскольку сигнатура метода javax.xml.soap.SOAPConnection.call изменилась в спецификации
SAAJ 1.1, реализация JAXM может не реализовывать метод call. Для решения этой
проблемы существует новая исключительная ситуация в классе SOAPConnectionFactory,
указывающая, что SOAPConnection не реализован, что дает возможность нормально
завершить работу.
В отличие от клиента, не использующего поставщика службы обмена сообщениями, который
ограничен использованием только объекта SOAPConnection, клиент, использующий
поставщика службы обмена сообщениями, может свободно использовать объект SOAP-
Connection или объект ProviderConnection. Ожидается, что объекты ProviderConnection
будут использоваться в большинстве случаев.

11.1.2.2. ProviderConnection
Объект ProviderConnection представляет соединение с поставщиком службы обмена
сообщениями. (В следующем разделе поставщики службы обмена сообщениями
рассматриваются более подробно.) При передаче сообщения через объект ProviderCon-
nection, оно приходит поставщику службы обмена сообщениями. Поставщик службы обмена
сообщениями перенаправит это сообщение указанному адресату и затем возвратит ответ
при его наличии. Интервал между передачей запроса и получением ответа может быть
очень коротким, а может измеряться днями. При таком типе обмена сообщениями
оригинальное сообщение передается как однонаправленное сообщение, и любой ответ
передается позже как однонаправленное сообщение. Не удивительно, что этот тип обмена
сообщениями называется однонаправленным.

Рисунок 10-3 Системы обмена сообщениями - запрос-ответ и однонаправленная

Web-

Rendered by www.RenderX.com
Введение в JAXM Стр. 333 из 626

11.1.3. Поставщики службы обмена сообщениями


Поставщик службы обмена сообщениями представляет собой службу, которая управляет
передачей и маршрутизацией сообщений. Он работает неявно, прослеживая прохождение
сообщений и гарантируя их доставку нужному адресату или адресатам.

11.1.3.1. Прозрачность
Одним из основных преимуществ поставщика службы обмена сообщениями является то,
что вы даже не замечаете его. Вы просто пишете ваше JAXM-приложение, а все остальное
произойдет само. Например, при использовании поставщика службы обмена сообщениями
и передаче сообщения при помощи вызова метода ProviderConnection.send, этот поставщик
принимает сообщение и работает с другими частями коммуникационной инфраструктуры
для выполнения различных задач в зависимости от содержимого заголовка сообщения и
реализации службы поставщика. Сообщение прибывает к конечному адресату без знания
вами деталей процесса доставки.

11.1.3.2. Профили
JAXM предоставляет возможность подключать дополнительные протоколы, построенные
на верхнем уровне SOAP. От реализации поставщика JAXM не требуется поддержка
функциональности, выходящей за рамки требований спецификаций SOAP 1.1 и SOAP with
Attachments. Но этой реализации разрешено поддерживать другие стандартные протоколы,
называемые профилями, которые строятся на верхнем уровне SOAP. Например,
спецификация "ebXML Message Service Specification" (доступная на http://www.oasis-
open.org/committees/ebxml-msg/) определяет уровни службы, не включенные в спецификации
SOAP. О поставщике службы обмена сообщениями, который включает возможности ebXML
на верхнем уровне SOAP, говорят, что он поддерживает ebXML-профиль. Поставщик
службы обмена сообщениями может поддерживать несколько профилей, но приложение
может использовать только один в данный момент времени, и нужно иметь предварительное
соглашение с каждым из участников, которому передаются сообщения, об используемом
профиле.
Профили влияют на заголовки сообщения. Например, в зависимости от профиля, новый
объект SOAPMessage создается с определенными, уже установленными заголовками.
Также, реализация профиля может предоставить API, облегчающие создание заголовка
и установку его содержимого. Справочная реализация JAXM включает API и для профиля
ebXML, и для профиля SOAP-RP. Документация для этих профилей в формате Javadoc
находится на странице <JWSDP_HOME>/docs/jaxm/profiles/index.html. Ссылки на Javadoc-
документацию по JAXM API (javax.xml.soap и javax.xml.messaging) находятся на странице
<JWSDP_HOME>/docs/api/index.html.

11.1.3.3. Непрерывная активность


Поставщик службы обмена сообщениями работает непрерывно. JAXM-клиент может
получить соединение со своим поставщиком, передать одно или больше сообщений и
закрыть соединение. Поставщик сохранит сообщение и затем передаст его. В зависимости
от того, как поставщик был сконфигурирован, он будет повторять передачу не доставленного
сообщения до тех пор, пока оно не будет успешно доставлено или пока не будет достигнут
лимит количества повторений. Поставщик будет оставаться в состоянии ожидания, готовый
принять любые предназначенные для клиента сообщения. Поставщик сохранит входящие
сообщения и, после того как клиент снова подключится к нему, передаст эти сообщения.

Web-

Rendered by www.RenderX.com
Стр. 334 из 626 Java API for XML Messaging

Кроме того, поставщик при необходимости генерирует сообщения об ошибках и ведет


журнал, в котором хранятся сообщения и соответствующие им сообщения об ошибках.

11.1.3.4. Промежуточные пункты назначения


При использовании поставщика службы обмена сообщениями сообщение может быть
передано одному или нескольким промежуточным пунктам назначения перед доставкой
к конечному адресату. Эти промежуточные пункты назначения, называемые актерами,
указываются в объекте SOAPHeader сообщения. Например, предположим, что сообщение
является входящим заказом на поставку. Заголовок может направить это сообщение в
отдел приема заказов, в отдел подтверждения заказов, в транспортный отдел и отдел
выписки счетов. Каждый из этих пунктов назначения является актером, который выполнит
соответствующее действие, удалит из заголовка относящуюся к нему информацию и
передаст сообщение следующему актеру. Актером по умолчанию является конечный пункт
назначения, то есть, если актеры не указаны, сообщение направляется к конечному
адресату.
Для указания промежуточного адресата используется атрибут actor. Связанным с ним
атрибутом является mustUnderstand; его значение true означает, что актер должен понимать,
каких действий от него ждут, и успешно их выполнить. Объект SOAPHeader использует
метод addAttribute для добавления этих атрибутов, а интерфейс SOAPHeaderElement
предоставляет методы для установки и получения значений этих атрибутов.

Рисунок 10-4 Однонаправленное сообщение с промежуточными пунктами назначения

11.1.3.5. Когда используется поставщик службы обмена сообщениями


JAXM-клиент может использовать поставщика службы обмена сообщениями, а может и
не использовать. Вообще говоря, если вы просто собираетесь быть потребителем Web-
служб, вам не нужен поставщик службы обмена сообщениями. Следующий список
перечисляет преимущества при отказе от поставщика:
• Приложение может быть написано с использованием платформы J2SE.
• Приложение не должно быть размещено в контейнере сервлетов или J2EE-контейнере.
• Не требуется настройки.
Ограничения при отказе от поставщика таковы:
• Клиент может передавать только сообщения вида запрос-ответ

Web-

Rendered by www.RenderX.com
Выполнение примеров Стр. 335 из 626

• Клиент может выполнять только клиентскую роль


Отсюда следует, что если вы хотите предоставить Web-службу, способную получать и
сохранять переданные вам в любое время запросы, вы должны использовать поставщика
службы обмена сообщениями. Вы должны, также, размещать приложение в контейнере,
который предоставляет инфраструктуру обмена сообщениями, используемую поставщиком.
Поставщик дает вам возможность выполнять роль и клиента, и службы, а также позволяет
вам передавать однонаправленные сообщения. Кроме того, если ваш поставщик
поддерживает такие протоколы как ebXML или SOAP-RP на верхнем уровне SOAP, вы
можете воспользоваться преимуществами дополнительного качества обслуживания,
которые ими предоставляются.

11.1.3.6. Обмен сообщениями с поставщиком и без него


JAXM-клиенты могут быть классифицированы в зависимости от того, используют они
поставщика службы обмена сообщениями, или нет. Те, которые поставщика не используют,
могут далее подразделяться на тех, кто выполняется в контейнере, и тех, кто нет. JAXM-
клиент, не использующий поставщика и не выполняющийся в контейнере, называется
автономным клиентом.

11.2. Выполнение примеров


Java WSDP содержит несколько примеров приложений JAXM. В Java WSDP включены,
также, различные реализации, которые дают вам возможность выполнять примеры
приложений. Эти реализации, которые составляют справочную реализацию JAXM,
следующие:
• реализация JAXM API
• реализация поставщика службы обмена сообщениями
• базовые реализации профилей ebXML и SOAP-RP, выполняющиеся на верхнем уровне
SOAP
Все примеры приложений используют JAXM API, а некоторые, естественно, используют
и другие реализации. Например, приложение Remote использует реализации поставщика
службы обмена сообщениями и профиль ebXML; пример SOAP-RP использует реализации
поставщика службы обмена сообщениями и профиль SOAP-RP. В следующем разделе
("Примеры программ") приводится более подробная информация о примерах приложений
и о том, что они делают.
Большинство из примеров выполняются в контейнере, поэтому перед их выполнением вы
должны запустить Tomcat (см. раздел "Запуск Tomcat").
Если Tomcat работает, вы можете выполнить JAXM-примеры следующим образом:
1. Откройте окно броузера и укажите в адресной строке

http://localhost:8080/index.html
2. На появившейся странице нажмите на один из перечисленных примеров приложений.
Затем следуйте инструкциям в новом окне.

Web-

Rendered by www.RenderX.com
Стр. 336 из 626 Java API for XML Messaging

11.2.1. Примеры программ


Примеры программ демонстрируют различные типы приложений, которые вы можете
писать при помощи JAXM API. Обратите внимание, что примеры Simple, Translator и SAAJ
Simple ведут журнал передаваемых и принимаемых сообщений в каталоге вашей установки
Java WSDP, откуда вы запустили Tomcat. Поэтому, если, например, вы запустили Tomcat
из каталога <JWSDP_HOME>/bin, журнал сообщений будет вестись в нем. Эти сообщения
являются XML-сообщениями, которые передаются по сети, что вы поймете по завершении
изучения данного учебника.
• Simple - Простой пример передачи и приема сообщения с использованием локального
поставщика. Обратите внимание, что локального поставщика нельзя путать с
поставщиком службы обмена сообщениями. Локальный поставщик - это простой
механизм для возврата ответа на сообщение, которое было передано при помощи
метода SOAPConnection.call. Заметьте, что переданное этим методом сообщение всегда
будет сообщением типа запрос-ответ. При выполнении этого примера генерируются
файлы sent.msg и reply.msg в каталоге, из которого вы запустили Tomcat.
• SAAJ Simple - Приложение, подобное примеру Simple, за исключением того, что оно
написано при использовании только SAAJ API. В этом примере метод call принимает
Java Object, а не объект URLEndpoint, для назначения адресата, и, таким образом,
использует только пакет javax.xml.soap. При выполнении этого примера генерируются
файлы sent.msg и reply.msg в каталоге, из которого вы запустили Tomcat.
• Translator - Приложение, которое использует простую службу перевода, которая
переводит указанное слово на различные языки. Если вы укажете правильный хост и
порт прокси, указанное вами слово будет переведено на французский, немецкий и
итальянский языки. При выполнении этого примера генерируются файлы request.msg
и reply.msg в каталоге, из которого вы запустили Tomcat. Проверьте reply.msg после
получения ответа в теле SOAP и после получения ответа в виде вложения для сравнения
полученной информации.
• JAXM Tags - Пример, использующий JSP-теги для генерирования и потребления SOAP-
сообщения.
• Remote - Пример двунаправленного сообщения, который использует поставщика службы
обмена сообщениями JAXM, поддерживающего базовый профиль ebXML для передачи
и приема сообщения.
• SOAP-RP - Пример двунаправленного сообщения, который использует поставщика
службы обмена сообщениями JAXM, поддерживающего базовый профиль SOAP-RP
для передачи и приема сообщения.
Есть еще два других примера программ - jaxm-uddiping и jaxm-standalone, которые не
выполняются в Tomcat. Для их запуска, перейдите в каталог <JWSDP_HOME>/samples/jaxm,
в котором вы найдете каталоги uddiping и standalone. Каждый каталог содержит файл
README, объясняющий, что делать.
В разделе "Примеры" руководства по JAXM (UddiPing.java и MyUddiPing.java) вы найдете
приложение, которое модифицирует код в UddiPing.java и детально объясняет, как его
запустить. Возможно, вы найдете более удобным подождать с запуском примеров jaxm-
uddiping и jaxm-standalone до завершения изучения этого раздела.

Web-

Rendered by www.RenderX.com
Учебник Стр. 337 из 626

Предыдущий список представил примеры приложений в соответствии с тем, что они


делают. Вы можете, также, посмотреть на примеры приложений с точки зрения трех
возможных типов JAXM-клиентов:
• Не использующие поставщика службы обмена сообщениями и не выполняющиеся в
контейнере. Они называются автономными приложениями. Примерами автономных
приложений являются программы jaxm-standalone и jaxm-udiping.
• Не использующие поставщика службы обмена сообщениями и выполняющиеся в
контейнере. Примерами этого типа являются программы Simple, SAAJ Simple, Translator
и JAXM Tags.
• Использующие поставщика службы обмена сообщениями и выполняющиеся в
контейнере. Примерами этого типа являются программы Remote и SOAP-RP.

11.2.2. Исходный код примеров


Исходный код примеров приложений находится в каталоге

<JWSDP_HOME>/docs/tutorial/examples/jaxm/samples/
Вы найдете шесть подкаталогов, по одному на каждый пример, выполняющийся в Tomcat.
Подкаталог jaxmtags содержит несколько .jsp-файлов. Другие подкаталоги содержат два
файла SendingServlet.java и ReceivingServlet.java. Кроме этих двух файлов подкаталог
translator содержит файл TranslationSevice.java.
Если вы хотите увидеть все файлы, которые составляют Web-приложение, вы можете
перейти в каталог <JWSDP_HOME>/webapps и распаковать .war-файлы. Например, для
примера Simple вы должны сделать следующее:

cd <JWSDP_HOME>/webapps
jar -xvf jaxm-simple.war
Кроме исходных файлов и файлов классов для примера Simple вы найдете файлы web.xml
и build.xml.
Файл web.xml, называемый дескриптором размещения, связывает конечную точку,
переданную в метод SOAPConnection.call или ProviderConnection.Send с конкретным
классом сервлета. Когда контейнер встречает конечную точку, являющуюся обычно URI,
он использует файл web.xml для определения соответствующего класса сервлета и его
выполнения. Пример и объяснение этого процесса находится в конце раздела "Передача
запроса".
Файл build.xml является файлом для программы ant, используемым при запуске приложения.

11.3. Учебник
В этом разделе рассмотрены основы передачи SOAP-сообщения с помощью JAXM API.
К концу этой главы вы узнаете, как выполнить следующее:
• Получить соединение

Web-

Rendered by www.RenderX.com
Стр. 338 из 626 Java API for XML Messaging

• Создать сообщение
• Добавить содержимое в сообщение
• Передать сообщение
• Извлечь содержимое из ответного сообщения
• Создать и извлечь элемент SOAP-ошибки
Прежде всего, мы выполним действия для передачи сообщения типа запрос-ответ для
клиента, не использующего поставщика службы обмена сообщениями. Затем мы исследуем
клиентское приложение, использующее поставщика службы обмена сообщениями для
передачи однонаправленного сообщения. Оба типа клиентов могут добавить в сообщения
вложения, поэтому добавление вложений выделено в отдельную тему. Наконец, вы узнаете,
что такое SOAP-ошибки и как с ними работать.
Раздел "Примеры кода" содержит фрагменты кода, которые вы будете использовать в
исполняемых приложениях и сможете протестировать самостоятельно. JAXM-часть
исследования ("JAXM-служба распределения") демонстрирует использование JAXM-кода
в Web-службе, показывая код клиента и сервера.

11.3.1. Клиент, не использующий поставщика службы обмена сообщениями


Приложение, не использующее поставщика службы обмена сообщениями, ограничено
ролью клиента и может передавать только сообщения типа запрос-ответ. Но и с такими
ограничениями его можно использовать для Web-службы, реализованной для обмена
сообщениями типа запрос-ответ.

11.3.1.1. Получение объекта SOAPConnection


Первое действие, которое должен сделать JAXM-клиент - установить соединение, используя
либо объект SOAPConnection, либо объект ProviderConnection. В разделе "Соединения"
рассмотрены эти два типа соединений и то, как они используются.
Клиент, не использующий поставщика службы обмена сообщениями, имеет только один
выбор для установки соединения - создать объект SOAPConnection. Этот тип соединения
является соединением точка-точка, то есть, устанавливается непосредственно между
отправителем и пунктом назначения (обычно URL), который указан отправителем.
Прежде всего, нужно получить объект SOAPConnectionFactory, который можно использовать
для создания соединения. SAAJ API облегчает это, предоставляя класс SOAPConnection-
Factory с реализацией по умолчанию. Вы можете получить экземпляр этой реализации
при помощи следующей строки кода:

SOAPConnectionFactory scFactory =
SOAPConnectionFactory.newInstance();
Обратите внимание, что поскольку newInstance является статическим методом, вы всегда
будете использовать имя класса SOAPConnectionFactory при вызове его метода newInstance.
Теперь вы можете использовать scFactory для создания объекта SOAPConnection.

SOAPConnection con = scFactory.createConnection();

Web-

Rendered by www.RenderX.com
Учебник Стр. 339 из 626

Вы будете использовать con позже для передачи сообщения, создаваемого в следующем


разделе.

11.3.1.2. Создание сообщения


Следующий шаг - создание сообщения, используя объект MessageFactory. При разработке
автономного клиента вы можете использовать реализацию по умолчанию класса
MessageFactory, предоставляемую SAAJ API. Следующий фрагмент кода демонстрирует
получение экземпляра этого генератора сообщений по умолчанию и использование его
для создания сообщения.

MessageFactory factory = MessageFactory.newInstance();


SOAPMessage message = factory.createMessage();
Аналогично методу newInstance в SOAPConnectionFactory, метод newInstance в Message-
Factory является статическим методом, поэтому вы вызываете его с указанием имени
класса: MessageFactory.newInstance. Обратите внимание, что возможно написание вашей
собственной реализации генератора сообщений и подключение его через системные
свойства, но обычно будет использоваться генератор сообщений по умолчанию.
Другим способом получения объекта MessageFactory является извлечение его из службы
имен, в которой он был зарегистрирован. Этот способ доступен только для приложений,
использующих поставщика службы обмена сообщениями, и будет рассмотрен далее (в
разделе "Создание сообщения").

11.3.1.2.1. Части сообщения


Объект SOAPMessage должен иметь определенные элементы, и SAAJ API облегчает вам
работу, возвращая новый объект SOAPMessage, который уже содержит эти элементы. То
есть message, созданный в предыдущей строке кода, автоматически содержит следующее:
1. Объект SOAPPart, который содержит
A. Объект SOAPEnvelope, который содержит
i. Пустой объект SOAPHeader
ii. Пустой объект SOAPBody

Объект SOAPHeader, являющийся необязательным, включен для удобства, поскольку


большинство сообщений содержат его. Объект SOAPBody может иметь содержимое
сообщения, (а также сообщение об ошибке), в котором указана информация о состоянии
или детальная информация о проблеме с сообщением. Раздел "Ошибки SOAP"
рассматривает использование объекта SOAPFaults.

11.3.1.2.2. Доступ к элементам сообщения


Следующим шагом при создании сообщения является получение доступа к его частям
для добавления содержимого. Используем объект SOAPMessage message, созданный в
предыдущем фрагменте кода, для получения объекта SOAPPart.

SOAPPart soapPart = message.getSOAPPart();

Web-

Rendered by www.RenderX.com
Стр. 340 из 626 Java API for XML Messaging

Затем можно использовать soapPart для получения объекта SOAPEnvelope.

SOAPEnvelope envelope = soapPart.getEnvelope();


Теперь можно использовать envelope для получения пустых объектов SOAPHeader и
SOAPBody.

SOAPHeader header = envelope.getHeader();


SOAPBody body = envelope.getBody();
Наш пример автономного клиента не использует SOAP-заголовок, поэтому вы можете
удалить его. Поскольку все объекты SOAPElement, включая объекты SOAPHeader,
порождены из интерфейса Node, для удаления header используется метод
Node.detachNode.

header.detachNode();

11.3.1.2.3. Добавление содержимого в тело


Для добавления содержимого в тело необходимо создать объект SOAPBodyElement, в
котором хранится содержимое. При создании любого нового элемента необходимо, также,
создавать связанный с ним объект Name для его идентификации. Одним из способов
создания объектов Name является использование методов SOAPEnvelope, то есть, вы
можете использовать envelope из предыдущего фрагмента кода для создания объекта
Name для вашего нового элемента.
Примечание: SAAJ API расширяет пакет javax.xml.soap, добавляя класс SOAPFactory,
который позволяет создать объекты Name без использования объекта SOAPEnvelope. Эта
возможность полезна при создании XML-элементов тогда, когда вы не создаете сообщение
полностью. Например, реализации JAX-RPC находят эту способность полезной. Если вы
не работаете с объектом SOAPMessage, вы не имеете доступа к объекту SOAPEnvelope
и, следовательно, нуждаетесь в альтернативном средстве создания объектов Name. Кроме
методов для создания объектов Name, класс SOAPFactory предоставляет методы для
создания объектов Detail и SOAP-фрагментов. Объекты Detail рассматриваются в разделах
по ошибкам SOAP "Обзор" и "Создание и заполнение объекта SOAPFault".
Объекты Name, связанные с объектами SOAPBody и SOAPHeader, должны быть указаны
полностью; то есть, они должны быть созданы с локальным именем, префиксом
используемого пространства имен и URI пространства имен. Указание пространства имен
обеспечивает их идентификацию при наличии нескольких элементов с одинаковым
локальным именем.
Следующий фрагмент кода извлекает объект SOAPBody body из envelope, создает объект
Name для добавляемого элемента и добавляет новый объект SOAPBodyElement к body.

SOAPBody body = envelope.getBody();


Name bodyName = envelope.createName(""GetLastTradePrice"",

Web-

Rendered by www.RenderX.com
Учебник Стр. 341 из 626

""m"", ""http://wombat.ztrade.com"");
SOAPBodyElement gltp = body.addBodyElement(bodyName);
С этого момента body содержит объект SOAPBodyElement, идентифицируемый объектом
Name bodyName, но содержимого в gltp все еще нет. Предположим, что вы хотите получить
котировки акций Sun Microsystems, Inc. Вы должны создать дочерний элемент для символа,
используя метод addChildElement. Затем необходимо получить символ акции при помощи
метода addTextNode. Объект Name для нового объекта SOAPElement symbol
инициализируется только с локальным именем, которое допустимо для дочерних элементов.

Name name = envelope.createName("symbol");


SOAPElement symbol = gltp.addChildElement(name);
symbol.addTextNode(""SUNW"");
Вы должны вспомнить, что заголовки и содержимое в объекте SOAPPart должны иметь
XML-формат. JAXM API заботится об этом вместо вас, создавая соответствующие XML-
конструкции автоматически при вызове таких методов, как addBodyElement, addChildElement
и addTextNode. обратите внимание, что вы можете вызвать метод addTextNode только с
таким элементом, как bodyElement или любыми дочерними элементами, добавляемыми
к нему. Вы не можете вызвать addTextNode с объектами SOAPHeader или SOAPBody,
поскольку они содержат элементы, а не текст.
Содержимое, только что добавленное вами в объект SOAPBody, будет выглядеть примерно
следующим образом при передаче по сети:

<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
<SOAP-ENV:Body>
<m:GetLastTradePrice xmlns:m=
"http://wombat.ztrade.com">
<symbol>SUNW</symbol>
</m:GetLastTradePrice>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Давайте проанализируем этот XML-фрагмент строка за строкой, для того чтобы увидеть,
как он связан с вашим JAXM-кодом. Отметим, что XML-анализатор не обращает внимания
на табуляцию, но она обычно используется для указания уровня элементов и облегчения,
таким образом, чтения кода человеком.
JAXM-код:

SOAPPart soapPart = message.getSOAPPart();


SOAPEnvelope envelope = soapPart.getEnvelope();

Web-

Rendered by www.RenderX.com
Стр. 342 из 626 Java API for XML Messaging

XML, который он генерирует:

<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
. . . . . . (intervening elements omitted)
</SOAP-ENV:Envelope>
Самый внешний элемент в этом примере XML является элементом SOAP-конверта,
указанным SOAP-ENV:Envelope. Envelope является именем элемента, а SOAP-ENV -
префиксом пространства имен. Интерфейс SOAPEnvelope представляет SOAP-конверт.
Первая строка сигнализирует о начале элемента SOAP-конверта, а последняя строка
сигнализирует о его конце; все, что внутри - это часть SOAP-конверта. Вторая строка
содержит атрибут для элемента SOAP-конверта. xmlns обозначает "XML namespace"
(пространство имен XML) и его значение - это URI пространства имен, связанного с Enve-
lope. Этот атрибут включается автоматически.
JAXM-код:

SOAPBody body = envelope.getBody();


XML, который он генерирует:

<SOAP-ENV:Body>
. . . . . .
</SOAP-ENV:Body>
Эти две строки отмечают начало и конец SOAP-тела, представленного в JAXM объектом
SOAPBody.
JAXM-код:

Name bodyName = envelope.createName("GetLastTradePrice",


"m", "http://wombat.ztrade.com");
SOAPBodyElement gltp = body.addBodyElement(bodyName);
XML, который он генерирует:

<m:GetLastTradePrice xmlns:m=
"http://wombat.ztrade.com">

. . . .
</m:GetLastTradePrice>
Эти две строки отображают то, что в вашем коде представляет SOAPBodyElement gltp.
"GetLastTradePrice" - это его локальное имя, "m" - префикс пространства имен,
"http://wombat.ztrade.com" - это его URI пространства имен.

Web-

Rendered by www.RenderX.com
Учебник Стр. 343 из 626

JAXM-код:

Name name = envelope.createName("symbol");


SOAPElement symbol = gltp.addChildElement(name);
symbol.addTextNode("SUNW");
XML, который он генерирует:

<symbol>SUNW</symbol>
Строка "SUNW" - это содержимое сообщения, которое принимает ваш адресат - служба
котировок акций.

11.3.1.3. Передача сообщения


Автономный клиент использует объект SOAPConnection и, следовательно, должен
использовать метод SOAPConnection call для передачи сообщения. Этот метод принимает
два аргумента - передаваемое сообщение и пункт назначения, куда сообщение нужно
передать. Это сообщение передается службе котировок акций, указанной в объекте URL
endpoint.

java.net.URL endpoint = new URL(


"http://wombat.ztrade.com/quotes";

SOAPMessage response = con.call(message, endpoint);


Ваше сообщение передает символ акции SUNW; объект SOAPMessage response должен
содержать последнюю цену акции Sun Microsystems, которую вы получите в следующем
разделе.
Соединение использует изрядное количество ресурсов, поэтому хорошей идеей является
закрытие соединения после его использования.

con.close();

11.3.1.4. Получение содержимого сообщения


Первые действия при извлечении содержимого сообщения такие же, как и при передаче
содержимого в сообщение: сначала получить доступ к объекту SOAPBody, используя
сообщение для получения конверта и конверт для получения тела. Затем вы обращаетесь
к объекту SOAPBodyElement, поскольку в этот элемент было добавлено содержимое в
этом примере. (Далее вы узнаете, как добавить содержимое непосредственно в объект
SOAPBody, в этом случае нет необходимости обращаться к объекту SOAPBodyElement
для добавления содержимого или извлечения его.) Для получения содержимого,
добавленного при помощи метода SOAPElement.addTextNode, вызывается метод
Node.getValue. Обратите внимание, что getValue возвращает значение прямого потомка
элемента, вызывающего метод. Следовательно, в следующем фрагменте кода метод
getValue вызывается с bodyElement; с этим элементом вызывался метод addTextNode.

Web-

Rendered by www.RenderX.com
Стр. 344 из 626 Java API for XML Messaging

Для получения доступа к bodyElement необходимо вызвать метод body getChildElement.


Передача bodyName в getChildElement возвращает объект java.util.Iterator, содержащий
все дочерние элементы, указанные объектом Node bodyName. Вы уже знаете, что
существует только один вызов метода next, который возвратит желаемый SOAPBodyEle-
ment. Обратите внимание, что метод Iterator.next возвращает Java Object, поэтому
необходимо преобразовать тип возвращаемого объекта в SOAPBodyElement перед
присвоением его переменной bodyElement.

SOAPPart sp = response.getSOAPPart();
SOAPEnvelope env = sp.getEnvelope();
SOAPBody sb = env.getBody();
java.util.Iterator it = sb.getChildElements(bodyName);
SOAPBodyElement bodyElement = (SOAPBodyElement)it.next();
String lastPrice = bodyElement.getValue();
System.out.print("The last price for SUNW is ");
System.out.println(lastPrice);
Если бы существовало больше одного элемента с именем bodyElement, вы должны были
бы использовать цикл while и метод Iterator.hasNext для перебора их всех.

while (it.hasNext()) {
SOAPBodyElement bodyElement = (SOAPBodyElement)it.next();
String lastPrice = bodyElement.getValue();
System.out.print("The last price for SUNW is ");
System.out.println(lastPrice);
}
Вы узнали, как передавать сообщение типа запрос-ответ в автономном клиенте. Вы, также,
узнали, как получить содержимое из ответа. Следующий раздел покажет вам, как передать
сообщение, используя поставщика службы обмена сообщениями.

11.3.2. Клиент, использующий поставщика службы обмена сообщениями


Использование поставщика службы обмена сообщениями дает вам большую гибкость,
чем автономный клиент, поскольку можно воспользоваться преимуществами
дополнительной функциональности, которую предлагает поставщик.

11.3.2.1. Получение объекта ProviderConnection


В отличие от объекта SOAPConnection, являющегося соединением точка-точка
непосредственно с конкретным URL, объект ProviderConnection - это соединение с
поставщиком службы обмена сообщениями. При этом типе соединения все передаваемые
и принимаемые вами сообщения проходят через этого поставщика.
Как и при получении объекта SOAPConnection, первым шагом является получение
генератора соединений, но в этом случае он является объектом ProviderConnectionFactory.
Вы можете извлечь объект ProviderConnectionFactory из службы имен. Это возможно, если
ваше приложение использует поставщика службы обмена сообщениями и размещено в

Web-

Rendered by www.RenderX.com
Учебник Стр. 345 из 626

контейнере сервлетов или J2EE-контейнере. При помощи ProviderConnectionFactory вы


можете установить соединение с конкретным поставщиком и, следовательно, использовать
возможности профиля, который он поддерживает.
Для получения объекта ProviderConnectionFactory прежде всего нужно предоставить
логическое имя вашего поставщика службы обмена сообщениями в контейнер во время
размещения. Это имя, которое было зарегистрировано службой имен, основанной на JNDI
(Java Naming and Directory Interface™). Затем вы можете использовать это имя для поиска,
чтобы получить объект ProviderConnectionFactory, который создаст соединение с вашим
поставщиком. Например, если ваш поставщик зарегистрирован с именем "ProviderABC",
вы можете использовать "ProviderABC" для поиска. Именно это делается в следующем
фрагменте кода. Первые две строки используют методы из JNDI API для получения объекта
ProviderConnectionFactory, а последняя строка использует метод из JAXM API для создания
соединения с поставщиком службы обмена сообщениями. Обратите внимание, что поскольку
JNDI-метод lookup возвращает тип Java Object, вы должны преобразовать его тип в
ProviderConnectionFactory перед присвоением его переменной pcFactory.

Context ctx = new InitialContext();


ProviderConnectionFactory pcFactory =
(ProviderConnectionFactory)ctx.lookup("ProviderABC");

ProviderConnection pcCon = pcFactory.createConnection();


Вы будете использовать pcCon для получения информации о вашем поставщике службы
обмена сообщениями и передачи сообщения, которое вы создадите в следующем разделе.

11.3.2.2. Создание сообщения


Все JAXM-сообщения создаются путем получения объекта MessageFactory и использования
его для создания объекта SOAPMessage. В примере автономного клиента вы просто
использовали объект Messagefactory по умолчанию, получаемый при помощи метода
MessageFactory.newInstance. Но при использовании поставщика службы обмена
сообщениями вы получаете объект MessageFactory другим способом.

11.3.2.2.1. Получение MessageFactory


При использовании поставщика службы обмена сообщениями объект MessageFactory
создается при помощи метода ProviderConnection.createMessageFactory. Кроме того,
передается String, указывающая профиль, который вы хотите использовать. Для
определения профилей, поддерживаемых вашим поставщиком, необходимо получить
объект ProviderMetaData с информацией о вашем поставщике. Это делается путем вызова
метода getMetaData. Затем необходимо вызвать метод getSupportProfiles для получения
массива поддерживаемых профилей. Предположив, что вы хотите использовать профиль
ebXML, вы должны определить, соответствует ли какой-нибудь профиль в массиве строке
"ebxml". Если такой профиль существует, он присваивается переменной profile, которая
затем передается в метод createMesageFactory.

ProviderMetaData metaData = pcCon.getMetaData();


String[] supportedProfiles = metaData.getSupportedProfiles();

Web-

Rendered by www.RenderX.com
Стр. 346 из 626 Java API for XML Messaging

String profile = null;

for (int i=0; i < supportedProfiles.length; i++) {


if (supportedProfiles[i].equals("ebxml")) {
profile = supportedProfiles[i];
break;
}
}

MessageFactory factory = pcCon.createMessageFactory(profile);


Теперь вы можете использовать factory для создания объекта SOAPMessage,
соответствующего профилю ebXML. Этот пример использует минимальную реализацию
профиля ebXML, включенную в Java WSDP. Обратите внимание, что следующая строка
кода использует класс EbXMLMesageImpl, определенный в реализации профиля ebXML
и не являющийся частью JAXM API.

EbXMLMessageImpl message = (EbXMLMessageImpl)factory.


createMessage();
Для этого профиля, вместо использования объектов Endpoint, вы указываете объекты
Party для отправителя и адресата. Эта информация появится в заголовке сообщения, а
поставщик службы обмена сообщениями будет использовать ее для определения пункта
назначения. В следующих строках кода используются методы setSender и setReceiver,
которые определены в реализации EbXMLMessageImpl. Эти методы не только создают
объект SOAPHeader, но и дают ему содержимое. Вы можете использовать эти методы,
поскольку ваш объект SOAPMessage является объектом EbXMLMessageImpl, что дает вам
доступ к методам, определенным в EbXMLMessageImpl.

message.setSender(new Party("http://grand.products.com"));
message.setReceiver(new Party("http://whiz.gizmos.com"));
Вы можете посмотреть комментарии в формате Javadoc по реализациям профилей ebXML
и SOAP-RP, предоставленные в Java WSDP, в следующем каталоге:

<JWSDP_HOME>/docs/jaxm/profile/com/sun/xml/messaging/
Если вы не используете профиль или хотите установить содержимое заголовка, не
обеспечиваемое реализацией профиля, вы должны следовать инструкциям следующего
раздела.

11.3.2.2.2. Добавление содержимого в заголовок


Для добавления содержимого в заголовок необходимо создать объект SOAPHeaderElement.
Как и остальные новые элементы, он должен иметь связанный с ним объект Name, который
вы создаете при помощи объекта SOAPEnvelope сообщения.

Web-

Rendered by www.RenderX.com
Учебник Стр. 347 из 626

В следующем фрагменте кода объект SOAPHeader извлекается из envelope и в него


добавляется новый объект SOAPHeaderElement.

SOAPHeader header = envelope.getHeader();


Name headerName = envelope.createName("Purchase Order",
"PO", "http://www.sonata.com/order");
SOAPHeaderElement headerElement =
header.addHeaderElement(headerName);
С этого момента header содержит объект SOAPHeaderElement headerElement,
идентифицируемый объектом Name headerName. Обратите внимание, что метод
addHeaderElement и создает headerElement, и добавляет его в header.
Теперь, когда вы идентифицировали headerElement при помощи headerName и добавили
его в header, следующее действие - добавить содержимое в headerElement, что и делается
в следующей строке кода в методе addTextNode.

headerElement.addTextNode("order");
Теперь вы имеете объект SOAPHeader header, содержащий объект SOAPHeaderElement,
содержимым которого является слово "order".

11.3.2.2.3. Добавление содержимого в SOAP-тело


Процесс добавления содержимого в объект SOAPBody является одинаковым для клиентов,
использующих поставщика службы обмена сообщениями, и для автономных клиентов. Он
похож на процесс добавления содержимого в объект SOAPHeader. Вы обращаетесь к
объекту SOAPBody, добавляете к нему объект SOAPBodyElement и добавляете текст в
объект SOAPBodyElement. Возможно добавление дополнительных объектов SOAPBodyEle-
ment субэлементов в объекты SOAPBodyElement при помощи метода addChildElement.
Для каждого элемента или дочернего элемента вы добавляете содержимое при помощи
метода addTextNode.
В разделе по автономному клиенту было продемонстрировано добавление объекта
SOAPBodyElement, добавление дочернего элемента и добавление в него текста. Следующий
пример показывает добавление более чем одного SOAPBodyElement и добавление текста
в каждый из них.
В коде сначала создается объект SOAPBodyElement purchaseLineItems, который имеет
связанное с ним полностью указанное пространство имен. То есть, объект Name для него
содержит локальное имя, префикс пространства имен и URI пространства имен. Как вы
видели ранее, объект SOAPBodyElement должен иметь полностью указанное пространство
имен, но дочерние элементы могут иметь объекты Name только с локальным именем.

SOAPBody body = envelope.getBody();


Name bodyName = envelope.createName("PurchaseLineItems", "PO",
"http://sonata.fruitsgalore.com");
SOAPBodyElement purchaseLineItems =
body.addBodyElement(bodyName);

Web-

Rendered by www.RenderX.com
Стр. 348 из 626 Java API for XML Messaging

Name childName = envelope.createName("Order");


SOAPElement order =
purchaseLineItems.addChildElement(childName);

childName = envelope.createName("Product");
SOAPElement product = order.addChildElement(childName);
product.addTextNode("Apple");

childName = envelope.createName("Price");
SOAPElement price = order.addChildElement(childName);
price.addTextNode("1.56");

childName = envelope.createName("Order");
SOAPElement order2 =
purchaseLineItems.addChildElement(childName);

childName = envelope.createName("Product");
SOAPElement product2 = order2.addChildElement(childName);
product2.addTextNode("Peach");

childName = envelope.createName("Price");
SOAPElement price2 = order2.addChildElement(childName);
price2.addTextNode("1.48");
JAXM-код в предыдущем примере генерирует следующий XML в SOAP-теле:

<PO:PurchaseLineItems
xmlns:PO="http://www.sonata.fruitsgalore/order">
<Order>
<Product>Apple</Product>
<Price>1.56</Price>
</Order>

<Order>
<Product>Peach</Product>
<Price>1.48</Price>
</Order>
</PO:PurchaseLineItems>

Web-

Rendered by www.RenderX.com
Учебник Стр. 349 из 626

11.3.2.2.4. Добавление содержимого в объект SOAPPart


Если содержимое, которое вы хотите передать, находится в файле, JAXM предоставляет
простой способ добавить его непосредственно в объект SOAPPart. Это значит, что вы не
обращаетесь к объекту SOAPBody и не создаете XML-содержимое самостоятельно, как
делали в предыдущем разделе.
Для добавления файла непосредственно в объект SOAPPart используется объект
javax.xml.transform.Source из JAXP (Java API for XML Processing). Существует три типа
объектов Source: SAXSource, DOMSource и StreamSource. Объект StreamSource хранит
содержимое в виде XML-документа. Объекты SAXSource и DOMSource хранят содержимое
вместе с командами для его преобразования в XML-документ.
В следующем фрагменте кода используется JAXP API для построения объекта DOMSource,
который передается в метод SOAPPart.setContent. В первых двух строках вы получаете
объект DocumentBuilderFactory и используете его для создания объекта DocumentBuilder
builder. builder анализирует содержимое файла для генерации объекта Document, который
используется для инициализации нового объекта DOMSource.

DocumentBuilderFactory dbFactory = DocumentBuilderFactory.


newInstance();
DocumentBuilder builder = dbFactory.newDocumentBuilder();
Document doc = builder.parse("file:///music/order/soap.xml");
DOMSource domSource = new DOMSource(doc);
Следующие две строки кода обращаются к объекту SOAPPart (используя объект
SOAPMessage message) и устанавливают новый объект DOMSource в качестве его
содержимого. Метод SOAPPart.setContent не только устанавливает содержимое объекта
SOAPBody, но также устанавливает соответствующий заголовок для объекта SOAPHeader.

SOAPPart soapPart = message.getSOAPPart();


soapPart.setContent(domSource);
Вы увидите другие способы добавления содержимого в сообщение в разделе по объектам
AttachmentPart. Одно главное отличие, которое нужно помнить, - это то, что объекты
SOAPPart должны содержать только XML-данные, в то время как объекты AttachmentPart
могут содержать данные любого типа.

11.3.2.3. Передача сообщения


Если соединение является объектом ProviderConnection, сообщения должны передаваться
при помощи метода ProviderConnection.send. Этот метод передает сообщение и сразу
возвращает управление. В отличие от метода SOAPConnection call, он не должен
блокировать работу до приема ответа, что дает возможность приложению выполнять
другие задачи.
Метод send принимает только один аргумент - передаваемое сообщение. Он не нуждается
в пункте назначения, поскольку поставщик службы обмена сообщениями может
использовать информацию в заголовке для определения пункта назначения сообщения.

Web-

Rendered by www.RenderX.com
Стр. 350 из 626 Java API for XML Messaging

pcCon.send(message);
pcCon.close();

11.3.3. Добавление вложений


Добавление объектов AttachmentPart аналогично для всех клиентов, независимо от того,
используют они поставщика службы обмена сообщениями или нет. Как отмечалось ранее,
в объект AttachmentPart вы можете поместить содержимое любого типа, в том числе XML.
А поскольку SOAPPart может содержать только XML-содержимое, вы должны использовать
объект AttachmentPart для любого содержимого, не представленного в XML-формате.

11.3.3.1. Создание объекта AttachmentPart и добавление содержимого


Объект SOAPMessage создает объект AttachmentPart, и сообщение тоже должно добавить
к себе вложение после добавления содержимого. Класс SOAPMessage имеет три метода
создания объекта AttachmentPart.
Первый метод создает вложение без содержимого. В этом случае метод AttachmentPart
используется позже для добавления содержимого к вложению.

AttachmentPart attachment = message.createAttachmentPart();


Содержимое к attachment добавляется при помощи метода AttachmentPart setContent. Этот
метод принимает два параметра, Java object для содержимого и String для указания типа
содержимого. Содержимое в части сообщения SOAPBody автоматически имеет заголовок
Content-Type со значением "text/xml", поскольку содержимое должно иметь XML-формат.
В отличие от этого тип содержимого объекта AttachmentPart должен быть указан, поскольку
оно может быть любого типа.
Каждый объект AttachmentPart имеет один или более заголовков, связанных с ним. Когда
вы указываете тип для метода setContent, этот тип используется для заголовка Content-
Type. Content-Type является единственным необходимым заголовком. Можно установить
другие необязательные заголовки, такие как Content-Id и Content-Location. Для удобства
JAXM предоставляет методы get и set для заголовков Content-Type, Content-Id и Content-
Location. Эти заголовки могут быть полезны при обращении к конкретному вложению при
наличии в сообщении нескольких вложений. Например, для обращения к вложениям,
имеющим конкретные заголовки, вы вызываете метод SOAPMessage getAttachment и
передаете в него интересующий вас заголовок или заголовки.
В следующем фрагменте кода показан один из способов использования метода setContent.
Добавляемый Java Object имеет тип String, являющийся обычным текстом, поэтому второй
аргумент должен иметь тип "text/plain". В коде также устанавливается содержимое
идентификатора, которое может использоваться для идентификации этого объекта,
AttachmentPart. После добавления содержимого к attachment вы должны добавить attachment
к объекту SOAPMessage, как показано в следующей строке.

String stringContent = "Update address for Sunny Skies " +


"Inc., to 10 Upbeat Street, Pleasant Grove, CA 95439";

Web-

Rendered by www.RenderX.com
Учебник Стр. 351 из 626

attachment.setContent(stringContent, "text/plain");
attachment.setContentId("update_address");

message.addAttachmentPart(attachment);
Переменная attachment теперь представляет объект AttachmentPart, который содержит
String stringContent и имеет заголовок, содержащий String "text/plain". Он также имеет
заголовок Content-Id со значением "update_address". И сейчас attachment является частью
message.
Предположим, что вы хотите вложить jpeg-изображение, показывающее, как красива новая
местность. В этом случае второй аргумент, переданный в setContent, должен быть равен
"image/jpeg" для соответствия добавляемому содержимому. Код для добавления
изображения может выглядеть примерно так, как показано ниже. Для первого вложения
Object, переданный в метод setContent, имел тип String. В данном случае, это - stream.

AttachmentPart attachment2 = message.createAttachmentPart();

byte[] jpegData = . . .;
ByteArrayInputStream stream = new ByteArrayInputStream(
jpegData);

attachment2.setContent(stream, "image/jpeg");

message.addAttachmentPart(attachment);
Два других метода SOAPMessage.createAttachment создают объект AttachmentPart с
содержимым. Один очень похож на метод AttachmentPart.setContent в том, что принимает
такие же параметры и делает по существу то же самое. Он принимает Java Object с
содержимым и String с его типом. Как и в AttachmentPart.setContent, Object может быть
объектами String, stream, javax.xml.Transform.Source или javax.activation.DataHandler. Вы
уже видели пример использования объекта Source в качестве содержимого. Следующий
пример покажет использование объекта DataHandler для содержимого.
Другой метод создания объекта AttachmentPart с содержимым принимает объект DataHan-
dler, являющийся частью JavaBeans™ Activation Framework (JAF). Использовать объект
DataHandler довольно просто. Сначала вы создаете объект java.net.URL для файла, который
вы хотите добавить как содержимое. Затем создаете объект DataHandler,
инициализированный объектом URL, и передаете его в метод createAttachmentPart.

URL url = new URL("http://greatproducts.com/gizmos/img.jpg");


DataHandler dh = new DataHandler(url);
AttachmentPart attachment = message.createAttachmentPart(dh);
attachment.setContentId("gyro_image");

message.addAttachmentPart(attachment);

Web-

Rendered by www.RenderX.com
Стр. 352 из 626 Java API for XML Messaging

Вы, возможно, заметили две вещи в предыдущем фрагменте кода. Во-первых, он


устанавливает заголовок Content_ID при помощи метода setContent. Этот метод принимает
параметр типа String, который может содержать все что угодно для идентификации
вложения. Во-вторых, в отличие от других методов установки содержимого, в нем не
принимается параметр типа String для Content-Type. Этот метод сам заботится об установке
заголовка Content-Type, что возможно, поскольку одной из задач, которые должен выполнить
объект DataHandler, является определение типа данных, содержащихся в файле.

11.3.3.2. Получение доступа к объекту AttachmentPart


Если вы получаете сообщение с вложениями или хотите изменить вложение в сообщении,
которое создаете, вы должны получить доступ к вложению. При отсутствии аргумента
метод SOAPMessage.getAttachment возвращает объект java.util.Iterator со всеми объектами
AttachmentPart в сообщении. В следующем коде выполняется распечатка каждого объекта
AttachmentPart объекта SOAPMessage message.

java.util.Iterator it = message.getAttachments();
while (it.hasNext()) {
AttachmentPart attachment = (AttachmentPart)it.next();
Object content = attachment.getContent();
String id = attachment.getContentId();
System.out.print("Attachment " + id + " contains: " +
content);
System.out.println("");
}

11.3.3.3. Резюме
В этом разделе вы познакомились с основами JAXM API. Вы узнали, как создавать и
передавать SOAP-сообщения в автономном клиенте и в клиенте, использующем поставщика
службы обмена сообщениями. Вы рассмотрели процесс добавления содержимого в
заголовок и тело SOAP-сообщения, а также рассмотрели процесс создания вложений и
добавления в них содержимого. Кроме того, вы узнали, как извлечь содержимое из SOAP-
части и из вложений. Другими словами, вы рассмотрели использование основных
возможностей JAXM API.

11.3.4. Ошибки SOAP


Этот раздел расширяет ваши базовые знания JAXM API, показывая как использовать API
для создания элементов SOAP-ошибок и доступа к ним в XML-сообщении.

11.3.4.1. Обзор
Если вы передаете сообщение, которое по каким-либо причинам не является правильным,
вы можете получить ответ, содержащий элемент SOAP-ошибки, который передает вам
информацию о статусе, информацию об ошибке, или и то и другое. Может быть только
один элемент SOAP-ошибки в сообщении, и он должен быть записью в теле SOAP-
сообщения. Спецификация SOAP 1.1 определяет только одну запись в теле, которая
является элементом SOAP-ошибки. Естественно, тело SOAP-сообщения может содержать
другие записи, но элемент SOAP-ошибки является единственным определенным.

Web-

Rendered by www.RenderX.com
Учебник Стр. 353 из 626

Объект SOAPFault, представляющий элемент SOAP-ошибки в JAXM API, похож на объект


Exception в том, что он передает информацию о проблеме. Однако объект SOAPFault
отличается тем, что является элементом объекта SOAPBody сообщения, а не частью
механизма try/catch, использующегося для объектов Exception. Также как часть объекта
SOAPBody, который предоставляет простое средство передачи произвольной информации,
предназначенной для конечного адресата, объект SOAPFault только информирует о статусе
или ошибке. Он не останавливает выполнение приложения так, как это может делать
объект Exception.
Различные стороны могут предоставлять объект SOAPFault в сообщении. Если вы являетесь
автономным клиентом, использующим SAAJ API, и, следовательно, передаете сообщения
типа точка-точка, адресат вашего сообщения может добавить в ответ объект SOAPFault
для предупреждения вас о проблеме. Например, если вы передаете заказ с неполным
адресом получателя, служба, принимающая заказ, может поместить объект SOAPFault в
обратное сообщение, сообщая вам, что часть адреса была пропущена.
В другом сценарии, если вы применяете JAXM 1.1_01 API для использования поставщика
службы обмена сообщениями, этот поставщик может поддерживать объект SOAPFault.
Например, если поставщик не может передать сообщение из-за недоступности сервера,
он может передать вам сообщение с объектом SOAPFault, содержащим эту информацию.
В этом случае ничего ошибочного в самом сообщении не было, поэтому вы можете позже
попытаться передать его снова без каких-либо изменений. В предыдущем примере вы
должны добавить отсутствующую информацию перед повторной передачей сообщения.
Объект SOAPFault содержит следующие элементы:
• код ошибки - требуется всегда. Спецификация SOAP 1.1 определяет набор значений
кодов ошибок в разделе 4.4.1, которые разработчик может расширить для описания
других проблем. Коды ошибок по умолчанию, определенные в спецификации, связаны
с JAXM API следующим образом:
- VersionMismatch - пространство имен объекта SOAPEnvelope не верно.
- MustUnderstand - прямой потомок объекта SOAPHeader имеет атрибут mustUnderstand,
установленный в "1", а обрабатывающая сторона не понимает элемент или не
обрабатывает его.
- Client - объект SOAPMessage не был сформирован правильно и не содержит
информацию, требующуюся для доставки.
- Server - объект SOAPMessage не мог быть обработан из-за ошибки обработки, а не
из-за проблем в самом сообщении.

• строка ошибки - требуется всегда. Предназначенное для человека описание ошибки.


• актер ошибки - требуется, если объект SOAPHeader содержит один или более атрибутов
actor; не обязательно при отсутствии указания актеров, при этом единственным актером
является конечный пункт назначения. Актер ошибки, на которого указывает URI,
идентифицирует того, кто вызвал ошибку. Более подробно об актерах рассказывается
в разделе "Промежуточные пункты назначения".
• объект Detail - требуется, если ошибка связана с объектом SOAPBody. Если, например,
код ошибки - "Client", указывающий на то, что сообщение не может быть обработано
из-за проблемы в объекте SOAPBody, объект SOAPFault должен содержать объект

Web-

Rendered by www.RenderX.com
Стр. 354 из 626 Java API for XML Messaging

Detail, в котором находится детальная информация о проблеме. Если объект SOAPFault


не содержит объекта Detail, можно предположить, что объект SOAPBody был обработан
успешно.

11.3.4.2. Создание и заполнение объекта SOAPFault


Вы уже знаете, как добавить содержимое в объект SOAPBody; в этом разделе вы увидите,
как добавить объект SOAPFault в объект SOAPBody и как добавить в него составляющие
его части.
Так же как при добавлении содержимого первым действием является получение доступа
к объекту SOAPBody.

SOAPEnvelope envelope =
msg.getSOAPPart().getEnvelope();
SOAPBody body = envelope.getBody();
Имея объект SOAPBody body, вы можете использовать его для создания объекта SOAPFault
при помощи следующей строки кода:

SOAPFault fault = body.addFault();


В следующем коде используется удобный метод добавления элементов и их значений в
объект SOAPFault fault. Например, метод setFaultCode создает элемент, добавляет его к
fault и добавляет узел Text со значением "Server".

fault.setFaultCode("Server");
fault.setFaultActor("http://gizmos.com/orders");
fault.setFaultString("Server not responding");
Объект SOAPFault fault, созданный в предыдущих строках кода, указывает, что причиной
проблемы является недоступность сервера и что актер в "http://gizmos.com/orders" имеет
проблемы. Если бы сообщение было направлено только к конечному адресату, не было
бы необходимости устанавливать актера ошибки. Также обратите внимание, что fault не
имеет объекта Detail, поскольку ошибка не относится к объекту SOAPBody.
В следующем фрагменте кода создается объект SOAPFault, включающий объект Detail.
Обратите внимание, что объект SOAPFault может иметь только один объект Detail, который
является простым контейнером объектов DetailEntry, но объект Detail может иметь несколько
объектов DetailEntry. Объект Detail в следующих строках кода имеет два объекта
DetailEntry, добавленных к нему.

SOAPFault fault = body.addFault();

fault.setFaultCode("Client");
fault.setFaultString("Message does not have necessary info");

Web-

Rendered by www.RenderX.com
Учебник Стр. 355 из 626

Detail detail = fault.addDetail();

Name entryName = envelope.createName("order", "PO",


"http://gizmos.com/orders/");
DetailEntry entry = detail.addDetailEntry(entryName);
entry.addTextNode("quantity element does not have a value");

Name entryName2 = envelope.createName("confirmation", "PO",


"http://gizmos.com/confirm");
DetailEntry entry2 = detail.addDetailEntry(entryName2);
entry2.addTextNode("Incomplete address: no zip code");

11.3.4.3. Извлечение информации об ошибке


Интерфейс SOAPFault предоставляет удобные методы для добавления информации, а
также - удобные методы для извлечения этой информации. В следующем фрагменте кода
показано, как можно извлечь информацию об ошибке из полученного сообщения. В нем
newmsg является объектом SOAPMessage, который был вам передан. Поскольку объект
SOAPFault должен быть частью объекта SOAPBody, первым действием должно быть
получение доступа к объекту SOAPBody. Затем код проверяет, содержится ли в объекте
SOAPBody объект SOAPFault. Если да, код извлекает объект SOAPFault и использует его
для извлечения его содержимого. Удобные методы getFaultCode, getFaultString и
getFaultActor облегчают извлечение значений.

SOAPBody body =
newmsg.getSOAPPart().getEnvelope().getBody();
if ( body.hasFault() ) {
SOAPFault newFault = body.getFault();
String code = newFault.getFaultCode();
String string = newFault.getFaultString();
String actor = newFault.getFaultActor();
В следующем коде полученные значения распечатываются. Актер ошибки может
присутствовать не во всех сообщениях, поэтому в коде проверяется его наличие. Проверка
того, не равна ли переменная actor значению null, возможна потому, что метод getFaultActor
возвращает null, если актер не был установлен.

System.out.println("SOAP fault contains: ");


System.out.println(" fault code = " + code);
System.out.println(" fault string = " + string);

if ( actor != null ) {
System.out.println(" fault actor = " + actor);

Web-

Rendered by www.RenderX.com
Стр. 356 из 626 Java API for XML Messaging

}
}
Последней задачей является извлечение объекта Detail и его объектов DetailEntry. В коде
используется объект SOAPFault newFault для извлечения объекта Detail newDetail, а затем
используется newDetail для вызова метода getDetailEntries. Этот метод возвращает объект
java.util.Iterator it, который содержит все объекты DetailEntry в newDetail. Объект Detail не
должен быть во всех объектах SOAPDetail, поэтому в коде проверяется, равно ли newDetail
значению null. Если нет, в коде распечатываются значения объекта (объектов) DetailEntry.

Detail newDetail = newFault.getDetail();


if ( newDetail != null) {
Iterator it = newDetail.getDetailEntries();
while ( it.hasNext() ) {
DetailEntry entry = (DetailEntry)it.next();
String value = entry.getValue();
System.out.println(" Detail entry = " + value);
}
}
Итак, вы изучили процесс добавления объекта SOAPFault и его содержимого в сообщение,
а также процесс извлечения информации из объекта SOAPFault. Объект SOAPFault,
являющийся необязательным, добавляется в объект SOAPBody для передачи информации
о статусе или ошибке. Он должен всегда содержать код ошибки и строку с объяснением
ошибки. Объект SOAPFault должен указывать актера, который являлся источником ошибки,
только при наличии нескольких актеров; в противном случае, он не обязателен. Аналогично,
объект SOAPFault должен содержать объект Detail с одним или более объектами
DetailEntry только тогда, когда содержимое объекта SOAPBody не могло быть обработано
успешно.

11.4. Примеры приложений


В первой части руководства использовались фрагменты кода для ознакомления вас с
основами использования JAXM API. В этом разделе вы будете использовать некоторые
из этих фрагментов для создания приложения. Сначала вы рассмотрите программу
Request.java. Затем вы увидите, как создать и запустить приложение MyUddiPing.java.
Наконец, вы узнаете, как создать и запустить SOAPFaultTest.java.
Примечание: <JWSDP_HOME> - это каталог, в котором вы установили Java Web Services
Developer Pack.

11.4.1. Request.java
Класс Request.java объединяет вместе фрагменты кода, используемые в разделе "Клиент,
не использующий поставщика службы обмена сообщениями", и добавляет все, что
необходимо для создания законченного примера клиента, передающего сообщения типа
запрос-ответ. Кроме объединения кода в него добавлены операторы import, метод main и
блок try/catch с обработкой исключительных ситуаций. Файл Request.java, показанный

Web-

Rendered by www.RenderX.com
Примеры приложений Стр. 357 из 626

здесь полностью, является автономным клиентом, который использует SAAJ API (пакет
javax.xml.soap). Нет необходимости использовать пакет javax.xml.messaging, поскольку
приложение не использует поставщика службы обмена сообщениями.

import javax.xml.soap.*;
import java.util.*;
import java.net.URL;

public class Request {


public static void main(String[] args) {
try {
SOAPConnectionFactory scFactory =
SOAPConnectionFactory.newInstance();
SOAPConnection con = scFactory.createConnection();

MessageFactory factory =
MessageFactory.newInstance();
SOAPMessage message = factory.createMessage();

SOAPPart soapPart = message.getSOAPPart();


SOAPEnvelope envelope = soapPart.getEnvelope();
SOAPHeader header = envelope.getHeader();
SOAPBody body = envelope.getBody();
header.detachNode();

Name bodyName = envelope.createName(


"GetLastTradePrice", "m",
"http://wombats.ztrade.com");
SOAPBodyElement gltp =
body.addBodyElement(bodyName);

Name name = envelope.createName("symbol");


SOAPElement symbol = gltp.addChildElement(name);
symbol.addTextNode("SUNW");

URL endpoint = new URL


("http://wombat.ztrade.com/quotes";
SOAPMessage response = con.call(message, endpoint);

con.close();

SOAPPart sp = response.getSOAPPart();

Web-

Rendered by www.RenderX.com
Стр. 358 из 626 Java API for XML Messaging

SOAPEnvelope se = sp.getEnvelope();
SOAPBody sb = se.getBody();

Iterator it = sb.getChildElements(bodyName);
SOAPBodyElement bodyElement =
(SOAPBodyElement)it.next();
String lastPrice = bodyElement.getValue();

System.out.print("The last price for SUNW is ");


System.out.println(lastPrice);

} catch (Exception ex) {


ex.printStackTrace();
}
}
}
Для того чтобы программа Requext.java была выполняемой, второй аргумент, передаваемый
в метод call, должен быть корректным существующим URI, что не верно в данном случае.
Посмотрите JAXM-код в руководстве, который вы можете запустить ("JAXM-клиент").
Приложение, рассматриваемое в следующем разделе, также является запускаемым
приложением.

11.4.2. UddiPing.java и MyUddiPing.java


Пример программы UddiPing.java является еще одним примером автономного приложения.
Служба UDDI (Universal Description, Discovery and Inegration) является бизнес-реестром и
хранилищем, из которого вы можете получать информацию о субъектах бизнеса,
зарегистрировавших себя при помощи службы реестра. Для этого примера приложение
UddiPing в действительности не обращается в службу UDDI, а является демонстрационной
версией. По этой причине число объектов бизнеса, о которых вы можете получить
информацию, ограничено. Тем не менее, UddiPing демонстрирует передачу запроса и
прием ответа. Приложение распечатывает возвращенное сообщение полностью, то есть,
полный XML-документ, который выглядит так, как передается по сети. Позже в этом разделе
вы увидите, как переписать UddiPing.java так, чтобы кроме вывода полного XML-документа,
он распечатывал только текстовое содержимое ответа, что намного облегчает просмотр
нужной вам информации.
Для получения представления о запуске примера UddiPing, взгляните на каталог
<JWSDP_HOME>/samples/jaxm/uddiping. Этот каталог содержит подкаталог src и файлы
run.sh (или run.bat), uddi.properties, UddiPing.class и README. Файл README объясняет
все, что необходимо сделать для выполнения приложения. Далее это рассмотрено более
подробно.
Файл README советует вам изменить файл uddi.properties, содержащий URL пункта
назначения (тестовый UDDI-реестр), прокси-хост и прокси-порт отправителя. Если вы
находитесь в каталоге uddiping, в котором вызываете сценарий run.sh (или run.bat),
информация в uddi.properties должна уже быть корректной. Если вы находитесь вне

Web-

Rendered by www.RenderX.com
Примеры приложений Стр. 359 из 626

брандмауэра Sun Microsystem, вы должны предоставить ваш прокси-хост и прокси-порт.


Если вы не знаете этих значений, проконсультируйтесь с администратором вашей сети
или другим человеком, имеющим эту информацию.
Основная работа сценария run - выполнить UddiPing. Если файл uddi.properties имеет
корректные значения прокси-хоста и прокси-порта, вы можете вызвать соответствующий
сценарий run, как показано ниже. Обратите внимание, что вы должны указать два аргумента,
uddi.properties и название субъекта бизнеса, который хотите найти.
Unix:

cd <JWSDP_HOME>/samples/jaxm/uddiping
run.sh uddi.properties Microsoft
Windows:

cd <JWSDP_HOME>\samples\jaxm\uddiping
run.bat uddi.properties Microsoft
На вашем экране отобразится примерно следующая информация:

Received replyfrom:
http://www3.ibm.com/services/uddi/testregistry/inquiryapi<?xml
version="1.0" encoding="UTF-8" ?><Envelope
xmlns="http://schemas.xmlsoap.org/soap/envelope/"><Body><busin
essList generic="1.0" xmlns="urn:uddi-org:api"
operator="www.ibm.com/services/uddi"
truncated="false"><businessInfos><businessInfo
businessKey="D7475060-BF58-11D5-A432-
0004AC49CC1E"><name>Microsoft Corporation</name><description
xml:lang="en">Computer Software and Hardware
Manufacturer</description><serviceInfos></serviceInfos></busin
essInfo></businessInfos></businessList></Body></Envelope>
Если указанное вами название субъекта бизнеса есть в тестовом реестре, распечатается
XML-документ с именем и описанием этого субъекта бизнеса. Однако они будут встроены
в XML-документ, что затрудняет их просмотр. В следующем разделе в программу UddiP-
ing.java добавляется код, извлекающий содержимое для облегчения просмотра.

11.4.2.1. Создание MyUddiPing.java


Для того чтобы сделать ответ в UddiPing.java более легким для чтения, вы создадите
новый файл с именем MyUddiPing.java, который извлекает содержимое и распечатывает
его. Вы увидите, как написать новый файл, позже в этом разделе после настройки нового
каталога с необходимыми подкаталогами и файлами.

Web-

Rendered by www.RenderX.com
Стр. 360 из 626 Java API for XML Messaging

11.4.2.1.1. Настройка
Поскольку имя нового файла - MyUddiPing, создайте каталог myuddiping в каталоге
<JWSDP_HOME>/samples/jaxm.

cd <JWSDP_HOME>/samples/jaxm
mkdir myuddiping
Этот новый каталог myuddiping будет основным каталогом для всех последующих команд,
относящихся к приложению MyUddiPing.java.
Вместо сценариев run.sh или run.bat, использовавшихся для запуска UddiPing, вы будете
использовать файл для программы ant - build.xml для настройки каталогов и файлов и для
запуска MyUddiPing. Преимущество использования файла Ant состоит в том, что он является
платформно-независимым и, следовательно, может применяться и для платформ Unix, и
для Windows. Таким образом, вы должны скопировать файл build.xml в каталог exam-
ples/jaxm руководства в ваш новый каталог myuddiping. (Команда для копирования должна
быть в одной строке. Обратите внимание, что нет пробела между "myuddiping/" и "build",
а также есть символ "." в конце командной строки.
Unix:

cd myuddiping
cp <JWSDP_HOME>/docs/tutorial/examples/jaxm/myuddiping/
build.xml .
Windows:

cd myuddiping
copy <JWSDP_HOME>\docs\tutorial\examples\jaxm\myuddiping\
build.xml .
Как только вы имеете файл build.xml в вашем каталоге myuddiping, вы можете вызвать его
для выполнения остальной работы по настройке и для запуска MyUddiPing. Файл компоновки
для ant является XML-файлом, разделенным на задания, каждое из которых является
элементом, содержащим атрибуты и одну или более задач. Например, элемент задания
с именем атрибута prepare создает каталоги build и src и копирует файл MyUddiPing.java
из каталога <JWSDP_HOME>/docs/tutorial/examples/jaxm/myuddiping/src в новый каталог
src. Затем он копирует файл uddi.properties из каталога uddiping в созданный вами каталог
myuddiping.
Чтобы выполнить эти задачи, введите в командной строке:

ant prepare
Задание, называемое build, компилирует исходный файл MyUddiPing.java и помещает
.class файл результата в build каталог. Чтобы выполнить эти задачи, введите в командной
строке:

Web-

Rendered by www.RenderX.com
Примеры приложений Стр. 361 из 626

ant build
Теперь, когда вы выполнили все настройки, рассмотрим детальнее код программы.

11.4.2.1.2. Исследование MyUddiPing


Мы будем исследовать файл MyUddiPing построчно. Обратите внимание, что большая
часть класса MyUddiPing.java основана на UddiPing.java. Мы добавим фрагмент в конец
MyUddiPing.java, который обращается только к нужному нам содержимому ответа,
возвращенного методом call.
Первые четыре строки кода импортируют пакеты, используемые в приложении.

import javax.xml.soap.*;
import javax.xml.messaging.*;
import java.util.*;
import java.io.*;
Следующие несколько строк начинают определение класса MyUddiPing, а именно,
определение метода main. Первое выполняемое действие - проверка наличия двух
аргументов. Если они отсутствуют, программа выводит сообщение о правилах
использования и завершает работу.

public class MyUddiPing {


public static void main(String[] args) {
try {
if (args.length != 2) {
System.err.println("Usage: MyUddiPing " +
"properties-file business-name");
System.exit(1);
}
В следующих строках создается файл java.util.Properties, содержащий системные свойства
и свойства из файла uddi.properties, находящегося в каталоге myuddiping.

Properties myprops = new Properties();


myprops.load(new FileInputStream(args[0]));
Properties props = System.getProperties();
Enumeration it = myprops.propertyNames();
while (it.hasMoreElements()) {
String s = (String) it.nextElement();
props.put(s, myprops.getProperty(s));
}

Web-

Rendered by www.RenderX.com
Стр. 362 из 626 Java API for XML Messaging

В следующих четырех строках создается объект SOAPMessage. Сначала код получает


экземпляр SOAPConnectionFactory, который используется для создания соединения. Затем
создается экземпляр MessageFactory, который используется для создания сообщения.

SOAPConnectionFactory scf =
SOAPConnectionFactory.newInstance();
SOAPConnection connection =
scf.createConnection();
MessageFactory msgFactory =
MessageFactory.newInstance();
SOAPMessage msg = msgFactory.createMessage();
В новый объект SOAPMessage msg автоматически включается объект SOAPPart, который
содержит объект SOAPEnvelope. Объект SOAPEnvelope содержит объект SOAPBody,
являющийся элементом, в который мы хотим добавить содержимое. В следующих строках
кода мы получаем объект SOAPPart, объект SOAPEnvelope и объект SOAPBody.

SOAPEnvelope envelope =
msg.getSOAPPart().getEnvelope();
SOAPBody body = envelope.getBody();
В следующих строках добавляется элемент с полностью указанным именем, и в него
добавляются два атрибута. Первый атрибут называется "generic" и имеет значение "1.0".
Второй аргумент называется "maxRows" и имеет значение "100". Затем добавляется
дочерний элемент с именем name, и в него добавляется некоторый текст при помощи
метода addTextNode. Добавляемый текст является названием субъекта бизнеса, которое
указывается вами при запуске приложения.

SOAPBodyElement findBusiness =
body.addBodyElement(
envelope.createName("find_business",
"", "urn:uddi-org:api"));
findBusiness.addAttribute(
envelope.createName("generic", "1.0");
findBusiness.addAttribute(
envelope.createName("maxRows", "100");
SOAPElement businessName =
findBusiness.addChildElement(
envelope.createName("name"));
businessName.addTextNode(args[1]);
В следующей строке кода создается Java Object, представляющий пункт назначения
сообщения. Он получает значение свойства с названием "URL" из файла системных
свойств.

Web-

Rendered by www.RenderX.com
Примеры приложений Стр. 363 из 626

Object endpoint =
System.getProperties().getProperty("URL");
В следующей строке сохраняются сделанные в сообщении изменения. Этот метод будет
вызываться автоматически при передаче сообщения, но ничего страшного не произойдет
и при его явном вызове.

msg.saveChanges();
Затем сообщение msg передается в пункт назначения, представленный в endpoint, которым
является UDDI-реестр. Метод call блокируется до получения объекта SOAPMessage назад,
после чего возвращает ответ.

SOAPMessage reply = connection.call(msg, endpoint);


В следующих двух строках первая выводит строку с URL отправителя (тестовый реестр),
а вторая выводит возвращенное сообщение в виде XML-документа.

System.out.println("Received reply from: " + endpoint);


reply.writeTo(System.out);
Код все еще основан на UddiPing.java. В следующем разделе вы добавите код для создания
MyUdiPing.java.

11.4.2.2. Добавление нового кода


Код, который мы собираемся добавить в UddiPing, будет отображать ответ в более
читаемом виде. В нем мы получим содержимое из некоторых элементов, вместо вывода
всего XML-документа в том виде, в котором он передается по сети. Поскольку содержимое
находится в объекте SOAPBody, прежде всего вам необходимо получить к нему доступ,
как показано в следующих строках кода. Вы можете обратиться к каждому элементу в
отдельных вызовах метода, как было показано ранее, или можете обратиться к объекту
SOAPBody, используя короткую версию.

SOAPBody replyBody =
reply.getSOAPPart().getEnvelope().getBody();
Затем вы можете вывести две пустых строки для отделения вашего результата от XML-
сообщения и третью строку, описывающую последующий текст.

System.out.println("");
System.out.println("");
System.out.print(
"Content extracted from the reply message: ");
Теперь вы можете начать процесс получения всех дочерних элементов из элемента,
получения дочерних элементов от каждого из них, и так далее до тех пор, пока не достигнете

Web-

Rendered by www.RenderX.com
Стр. 364 из 626 Java API for XML Messaging

текстового элемента, который можно распечатать. К сожалению, используемый для этого


примера реестр является всего лишь тестовым реестром и не всегда последователен.
Количество субэлементов иногда отличается, что затрудняет определение числа уровней,
на которое должен перейти код. И, в некоторых случаях, существует несколько записей
для одного и того же названия компании. Отметим, что в противоположность этому записи
в стандартном корректном реестре являются непротиворечивыми.
Код, который вы добавите, спускается по субэлементам в SOAP-теле и извлекает имя и
описание субъекта бизнеса. Для извлечения дочерних элементов используется метод
SOAPElement getChildElements. Если вы не предаете в этот метод аргументы, он извлекает
все дочерние элементы элемента, с которым вызывается. Если вам известен объект Name,
используемый для именования элемента, вы можете передать его в метод getChildElements
и извлечь только дочерний элемент с этим именем. В этом примере, однако, вы должны
извлечь все элементы и продолжать спускаться ниже, пока не получите элементы,
содержащие текстовое содержимое.
Вот основной образец, который используется для перемещения вниз по субэлементам:

Iterator iter1 = replyBody.getChildElements();


while (iter1.hasNext()) {
SOAPBodyElement bodyElement =
(SOAPBodyElement)iter1.next();
Iterator iter2 =
bodyElement.getChildElements();
while (iter2.hasNext()) {
Метод getChildElements возвращает элементы в форме объекта java.util.Iterator. Вы
получаете доступ к дочерним элементам, вызывая метод next объекта Iterator. В цикле
while может использоваться метод Iterator.hasNext, потому что он возвращает true, когда
метод next возвратит дочерний элемент. Цикл заканчивается, когда больше не останется
элементов для извлечения.
Прямым потомком объекта SOAPBody является объект SOAPBodyElement, вот почему
вызов iter1.next возвращает объект SOAPBodyElement. Потомок объектов SOAPBodyElement
и все последующие дочерние элементы являются объектами SOAPElement. Например,
вызов iter2.next возвращает объект SOAPElement child2. Обратите внимание, что метод
Iterator.next возвращает Object, который должен быть преобразован (приведен) в конкретный
тип извлекаемого вами объекта. Таким образом, результат вызова iter1.next приводится
к типу SOAPBodyElement, в то время как результаты вызовов iter2.next, iter3.next и т.д.
приводятся в тип SOAPElement.
Вот код, который нужно добавить для получения доступа, а также вывода имени и описания
субъекта бизнеса:

Iterator iter1 = replyBody.getChildElements();


while (iter1.hasNext()) {
SOAPBodyElement bodyElement =
(SOAPBodyElement)iter1.next();

Web-

Rendered by www.RenderX.com
Примеры приложений Стр. 365 из 626

Iterator iter2 =
bodyElement.getChildElements();
while (iter2.hasNext()) {
SOAPElement child2 =
(SOAPElement)iter2.next();
Iterator iter3 =
child2.getChildElements();
String content = child2.getValue();
System.out.println(content);
while (iter3.hasNext()) {
SOAPElement child3 =
(SOAPElement)iter3.next();
Iterator iter4 =
child3.getChildElements();
content = child3.getValue();
System.out.println(content);
while (iter4.hasNext()) {
SOAPElement child4 =
(SOAPElement)iter4.next();
content = child4.getValue();
System.out.println(content);
}
}
}
}
connection.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
Для компиляции MyUddiPing.java выполните следующую команду:

ant build
После компиляции вы готовы запустить MyUddiPing. Следующая команда вызовет
программу java с .class-файлом для MyUddiPing, который принимает два аргумента. Первый
аргумент - это файл uddi.properties, который предоставляется установкой свойства в
build.xml. Второй аргумент - это имя бизнеса, описание которого вы хотите получить. Вы
должны указывать этот аргумент в командной строке. Обратите внимание, что установка
любого свойства в командной строке переопределяет значение, установленное для этого
свойства в файле build.xml. Последний аргумент, предоставляемый ant, всегда является
заданием, которой в данном случае является run.

Web-

Rendered by www.RenderX.com
Стр. 366 из 626 Java API for XML Messaging

cd <JWSDP_HOME>/samples/jaxm/myuddiping
ant -Dbusiness-name="Oracle" run
После полного XML-сообщения отобразится следующая информация. Она генерируется
кодом, добавленным в MyUddiPing.java.

Content extracted from the reply message:

Oracle
oracle powers the internet

Oracle Corporation
Oracle Corporation provides the software and services for e-
business.
Выполнение ant со свойством business-name Microsoft вместо Oracle генерирует следующую
информацию:

Received reply from: http://www-


3.ibm.com/services/uddi/testregistry/inquiryapi
<?xml version="1.0" encoding="UTF-8" ?><Envelope
xmlns="http://schemas.xmlsoap.org/soap/envelope/"><Body><busin
essList generic="1.0" xmlns="urn:uddi-org:api"
operator="www.ibm.com/services/uddi"
truncated="false"><businessInfos><businessInfo
businessKey="D7475060-BF58-11D5-A432-
0004AC49CC1E"><name>Microsoft Corporation</name><description
xml:lang="en">Computer Software and Hardware
Manufacturer</description><serviceInfos></serviceInfos></busin
essInfo></businessInfos></businessList></Body></Envelope>

Content extracted from the reply message:

Microsoft Corporation
Computer Software and Hardware Manufacturer

11.4.3. SOAPFaultTest.java
Код SOAPFaultTest.java, основанный на фрагментах кода из предыдущего раздела ("Ошибки
SOAP"), создает сообщение с объектом SOAPFault. Он, также, извлекает содержимое
объекта SOAPFault и распечатывает его. Код приложения SOAPFaultTest находится в
каталоге

Web-

Rendered by www.RenderX.com
Примеры приложений Стр. 367 из 626

<JWSDP_HOME>/docs/tutorial/examples/jaxm/fault/src
Ниже приведен файл SOAPFaultTest.java.

import javax.xml.soap.*;
import java.util.*;

public class SOAPFaultTest {

public static void main(String[] args) {


try {
MessageFactory msgFactory =
MessageFactory.newInstance();
SOAPMessage msg = msgFactory.createMessage();
SOAPEnvelope envelope =
msg.getSOAPPart().getEnvelope();
SOAPBody body = envelope.getBody();
SOAPFault fault = body.addFault();

fault.setFaultCode("Client");
fault.setFaultString(
"Message does not have necessary info");
fault.setFaultActor("http://gizmos.com/order");

Detail detail = fault.addDetail();

Name entryName = envelope.createName("order", "PO",


"http://gizmos.com/orders/");
DetailEntry entry = detail.addDetailEntry(entryName);
entry.addTextNode(
"quantity element does not have a value");

Name entryName2 = envelope.createName("confirmation",


"PO", "http://gizmos.com/confirm");
DetailEntry entry2 = detail.addDetailEntry(entryName2);
entry2.addTextNode("Incomplete address: no zip code");

msg.saveChanges();

// SOAPFault

Web-

Rendered by www.RenderX.com
Стр. 368 из 626 Java API for XML Messaging

//

if ( body.hasFault() ) {
fault = body.getFault();
String code = fault.getFaultCode();
String string = fault.getFaultString();
String actor = fault.getFaultActor();

System.out.println("SOAP fault contains: ");


System.out.println(" fault code = " + code);
System.out.println(" fault string = " + string);
if ( actor != null) {
System.out.println(" fault actor = " + actor);
}

detail = fault.getDetail();
if ( detail != null) {
Iterator it = detail.getDetailEntries();
while ( it.hasNext() ) {
entry = (DetailEntry)it.next();
String value = entry.getValue();
System.out.println(" Detail entry = " + value);
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}

11.4.3.1. Выполнение SOAPFaultTest


Для запуска SOAPFaultTest используется файл build.xml для ant, который находится в
каталоге

<JWSDP_HOME>/docs/tutorial/examples/jaxm/fault.
Этот ant-файл выполняет за вас много действий, включая создание каталога build, где
будут размещаться файлы классов, создание переменной classpath, необходимой для
выполнения SOAPFaultTest, компиляцию SOAPFaultTest.java, помещение выходных .class-
файлов в каталог build и выполнение SOAPFaultTest.
Для запуска SOAPFaultTest выполните следующие действия:
1. Перейдите в каталог, в котором находится соответствующий файл build.xml.

Web-

Rendered by www.RenderX.com
Примеры приложений Стр. 369 из 626

cd <JWSDP_HOME>/docs/tutorial/examples/jaxm/fault
2. Из командной строки выполните следующую команду:

ant prepare
Эта команда создаст каталог build - каталог, в который будут помещаться файлы
классов.
3. Из командной строки выполните следующую команду:

ant build
Эта команда запустит программу javac с SOAPFaultTest.java, используя переменную
classpath, установленную в файле build.xml. Выходной .class-файл будет помещен в
каталог build, созданный в предыдущем задании.
4. Из командной строки выполните следующую команду:

ant run
Эта команда выполнит программу java SOAPFaultTest.
Обратите внимание, что для ускорения набора вы можете просто ввести ant run.
Необходимые задания будут выполнены в соответствующей последовательности, поскольку,
если задание указывает, что оно зависит от одного или более других заданий, эти задания
будут выполнены перед выполнением указанного задания. В данном случае задание run
зависит от задания build, которое, в свою очередь, зависит от задания prepare, то есть
задания prepare, build и run будут выполнены именно в этом порядке. Для еще большего
ускорения набора вы можете просто ввести ant. Заданием по умолчанию для файла
build.xml является run, то есть, выполнение ant имеет тот же эффект, что и выполнение
ant run.
Если вы хотите запустить SOAPFaultTest повторно, имеет смысл удалить каталог build и
.class-файлы, которые он содержит. Вы можете сделать это, выполнив следующую команду:

ant clean
После выполнения SOAPFaultTest вы увидите примерно следующее:

Here is what the XML message looks like:

<?xml version="1.0" encoding="UTF-8"?>


<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/
soap/envelope/"><soap-env:Header/><soap-env:Body><soap-env:
Fault><soap-env:faultcode>Client</soap-env:faultcode><soap-
env:faultstring>Message does not have necessary info</soap-

Web-

Rendered by www.RenderX.com
Стр. 370 из 626 Java API for XML Registries

env:faultstring><soap-env:faultactor>http://gizmos.com/order
</soap-env:faultactor><soap-env:Detail><PO:order xmlns:PO=
"http://gizmos.com/orders/">quantity element does not have a
value</PO:order><PO:confirmation xmlns:PO="http://gizmos.com/
confirm">Incomplete address: no zip code</PO:confirmation>
</soap-env:Detail></soap-env:Fault></soap-env:Body></soap-env:
Envelope>

Here is what the SOAP fault contains:


fault code = Client
fault string = Message does not have necessary info
fault actor = http://gizmos.com/order
Detail entry = quantity element does not have a value
Detail entry = Incomplete address: no zip code

11.4.4. Заключение
JAXM предоставляет Java API, который упрощает написание и передачу XML-сообщений.
Вы узнали, как писать код клиентского приложения для сообщений типа запрос-ответ и
однонаправленных сообщений. Вы также узнали, как получить содержимое из ответного
сообщения. Эти знания были применены при написании и выполнении примеров MyUddiPing
и SOAPFaultTest. Кроме того, дополнительный раздел "Приложение The Coffee Break"
предоставляет детальные примеры JAXM-кода как для клиента, так и для сервера.
Теперь вы хорошо представляете себе, как JAXM упрощает обмен XML-сообщениями.

11.5. Дополнительная информация


Вы можете почерпнуть дополнительную информацию по JAXM из следующих источников:
• Документация, включенная в справочную реализацию JAXM в каталоге
<JWSDP_HOME>/docs/jaxm/
• Спецификация SAAJ 1.1, доступная на http://java.sun.com/xml/downloads/saaj.html
• Спецификация JAXM 1.1, доступная на http://java.sun.com/xml/downloads/jaxm.html
• Web-сайт JAXM: http://java.sun.com/xml/jaxm/
• Примеры JAXM-приложений (см. "Выполнение примеров")

12. Java API for XML Registries


Java API for XML Registries (JAXR) предоставляет унифицированный и стандартный Java
API для доступа к различным типам XML-реестров.
Реализация JAXR, которая является частью Java WSDP (Java Web Services Developer
Pack), содержит несколько примеров программ, а также броузер реестра, которые

Web-

Rendered by www.RenderX.com
Обзор JAXR Стр. 371 из 626

демонстрируют, как писать клиентские JAXR-программы. Раздел "Броузер реестра"


содержит дополнительную информацию по этой программе.

12.1. Обзор JAXR


В этом разделе приведен краткий обзор JAXR.

12.1.1. Что такое реестр?


XML-реестр представляет собой инфраструктуру, которая позволяет создать, разместить
и обнаружить Web-службы. Он является нейтральной третьей стороной, поддерживающей
динамические и слабосвязанные business-to-business (B2B) взаимодействия. Реестр
доступен для организаций как совместный ресурс, часто в форме Web-службы.
В текущий момент существует несколько спецификаций XML-реестров.
• Стандарт ebXML Registry and Repository, который финансируется организациями OASIS
(Organization for the Advancement of Structured Information Standards - организация по
развитию стандартов структурированной информации) и U.N./CEFACT (the United Nations
Centre for the Facilitation of Procedures and Practices in Administration, Commerce and
Transport - центр Объединенных Наций по поддержке мероприятий и деятельности в
управлении, коммерции и на транспорте)
• Проект UDDI (Universal Description, Discovery and Integration - универсальное описание,
обнаружение и интеграция), который разрабатывается консорциумом производителей
Поставщик реестра - это реализация бизнес-реестра, соответствующая спецификации
XML-реестров.

12.1.2. Что такое JAXR?


JAXR предоставляет возможность Java-программистам использовать простой, легкий в
использовании абстрактный API для доступа к различным XML-реестрам. Унифицированная
информационная модель JAXR описывает содержимое и метаданные в XML-реестрах.
JAXR позволяет разработчикам создавать клиентские программы реестра, являющиеся
переносимыми для различных реестров. JAXR также предоставляет дополнительные
возможности, отсутствующие в поддерживаемых реестрах.
Текущая версия спецификации JAXR включает тесные взаимосвязи между информационной
моделью JAXR и обеими спецификациями реестров ebXML и UDDI версии 2. Последняя
версия спецификации находится на странице

http://java.sun.com/xml/downloads/jaxr.html
В данной редакции Java WSDP JAXR реализует профиль возможностей нулевого уровня,
определенный в спецификации JAXR. Этот уровень разрешает доступ к реестрам UDDI и
ebXML на базовом уровне. В данной редакции JAXR поддерживает только реестры UDDI
версии 2.
В настоящее время существует несколько реестров UDDI версии 2. Сервер реестра Java
WSDP предоставляет реестр UDDI версии 2, который вы можете использовать для

Web-

Rendered by www.RenderX.com
Стр. 372 из 626 Java API for XML Registries

тестирования ваших JAXR-приложений в персональном окружении. Детальная информация


приведена в разделе "Сервер реестра Java WSDP".
Несколько реестров ebXML находятся в разработке и один доступен в Center for E-Commerce
Infrastructure Development (CECID), Department of Computer Science Information Systems,
The University of Hong Kong (HKU) (Центр разработки инфраструктуры электронной
коммерции, отделение компьютерных информационных систем, Гонгконгский Университет.)
Информацию по этому проекту можно найти на странице
http://www.cecid.hku.hk/Release/PR09APR2002.html.
http://ebxmlrr.sourceforge.net

12.1.3. Архитектура JAXR


Абстрактная архитектура JAXR состоит из следующих частей:
• JAXR-клиент: клиентская программа, использующая JAXR API для доступа к бизнес-
реестру через JAXR-поставщика.
• JAXR-поставщик: реализация JAXR API, которая обеспечивает доступ к определенному
поставщику реестра или к классу поставщиков реестра, основывающихся на общей
спецификации.
JAXR-поставщик реализует два основных пакета:
• javax.xml.registry, который состоит из интерфейсов и классов API, определяющих
интерфейс доступа к реестру.
• javax.xml.registry.infomodel, который содержит интерфейсы, определяющие
информационную модель JAXR. Основным интерфейсом в этом пакете является
интерфейс RegistryObject. Его субинтерфейсами являются Organization, Service и
ServiceBinding.
Самыми главными интерфейсами в пакете javax.xml.registry являются
• Connection. Интерфейс Connection представляет сессию клиента с поставщиком реестра.
Клиент должен создать соединение с JAXR-поставщиком для использования реестра.
• RegistryService. Клиент получает объект RegistryService из соединения. Объект
RegistryService, в свою очередь, позволяет клиенту получить интерфейсы,
использующиеся для доступа к реестру.
Основными интерфейсами, тоже входящими в пакет javax.xml.registry, являются
• BusinessQueryManager, который дает возможность клиенту осуществлять поиск
информации в реестре в соответствии с интерфейсами javax.xml.registry.infomodel.
Необязательный интерфейс DeclarativeQueryManager дает возможность клиенту
использовать для запросов синтаксис SQL. (JAXR в Java WSDP не реализует Declara-
tiveQueryManager.)
• BusinessLifeCycleManager, который дает возможность клиенту модифицировать
информацию в реестре, либо сохраняя (обновляя), либо удаляя ее.
При возникновении ошибки методы JAXR API генерируют JAXRException или один из его
подклассов.

Web-

Rendered by www.RenderX.com
Реализация JAXR-клиента Стр. 373 из 626

Многие методы в JAXR API используют объект Collection в качестве аргумента или типа
возвращаемого значения. Использование объекта Collection разрешает проводить
одновременно операции с несколькими объектами реестра.
На рисунке 11-1 изображена архитектура JAXR. В Java WSDP, JAXR-клиент использует
интерфейсы нулевого уровня возможностей JAXR API для доступа к JAXR-поставщику.
JAXR-поставщик, в свою очередь, обращается к реестру. Java WSDP реализует поддержку
JAXR-поставщика для UDDI-реестров.

Рисунок 11-1 JAXR-архитектура

12.2. Реализация JAXR-клиента


В этом разделе описаны основные действия для реализации JAXR-клиента, который
выполняет запросы и обновления в UDDI-реестре. JAXR-клиент является клиентской
программой, которая обращается к реестрам, используя JAXR API.
В этом руководстве не описывается, как реализовать JAXR-поставщика. JAXR-поставщик
обеспечивает реализацию спецификации JAXR, которая позволяет получить доступ к
существующему поставщику реестра, такому как UDDI или ebXML. Реализация JAXR в
Java WSDP сама является примером JAXR-поставщика.
В это руководство включено несколько примеров клиентских приложений, которые
описываются в разделе "Выполнение примеров клиентских приложений".
JAXR тоже содержит несколько примеров JAXR-клиентов, самым полным из которых
является броузер реестра, который включает графический пользовательский интерфейс
(GUI). Детальная информация по этому броузеру находится в разделе "Броузер реестра".

12.2.1. Установка соединения


Первым действием, которое должен выполнить JAXR-клиент, является установка
соединения с реестром.

Web-

Rendered by www.RenderX.com
Стр. 374 из 626 Java API for XML Registries

12.2.1.1. Начальные сведения: получение доступа к реестру


Любой пользователь программы JAXR-клиента может выполнять запросы к реестру. Но
для добавления данных в реестр или обновления данных в реестре пользователь должен
получить разрешение на доступ к нему. Для регистрации в одном из публичных UDDI-
реестров версии 2 зайдите на один из следующих Web-сайтов и следуйте инструкциям:
• http://udi.microsoft.com/ (Microsoft)
• http://uddi.ibm.com/testregidtry/regidtry.html (IBM)
• http://udditest.sap.com/ (SAP)
Эти UDDI-реестры версии 2 предназначены для тестирования. При регистрации вы получите
имя пользователя и пароль. Вы будете указывать эту информацию в некоторых примерах
программ JAXR-клиентов.
Примечание: JAXR API протестирован с реестрами Microsoft и IBM, но не с реестром SAP.

12.2.1.2. Создание или поиск центра соединений


Клиент создает соединение при помощи центра соединений. JAXR-поставщик может
предоставлять один или более предварительно настроенных центров соединений, которые
клиент может получить путем поиска, используя JNDI (Java Naming and Directory Interface™)
API.
В данной редакции Java WSDP JAXR не поддерживает предварительно настроенные
центры соединений. Вместо них клиент создает экземпляр абстрактного класса Connec-
tionFactory:

import javax.xml.registry.*;
...
ConnectionFactory connFactory =
ConnectionFactory.newInstance();

12.2.1.3. Создание соединения


Для создания соединения клиент сначала создает набор свойств, указывающих URL или
несколько URL реестра или реестров, к которым нужно получить доступ. Например,
следующий код предоставляет URL службы запросов и URL службы публикации для
тестового реестра IBM. (Строки не должны содержать символов разрывов строки.)

Properties props = new Properties();


props.setProperty("javax.xml.registry.queryManagerURL",
"http://uddi.ibm.com/testregistry/inquiryapi");
props.setProperty("javax.xml.registry.lifeCycleManagerURL",
"https://uddi.ibm.com/testregistry/protect/publishapi");
В реализации JAXR в Java WSDP при обращении клиента к реестру, находящемуся за
пределами брандмауэра, он должен также указать информацию о хосте и порте прокси
для сети, в которой работает. Для запросов может быть необходим только хост и порт
HTTP-прокси; для обновлений необходимо указывать хост и порт HTTPS-прокси.

Web-

Rendered by www.RenderX.com
Реализация JAXR-клиента Стр. 375 из 626

props.setProperty("com.sun.xml.registry.http.proxyHost",
"myhost.mydomain");
props.setProperty("com.sun.xml.registry.http.proxyPort",
"8080");
props.setProperty("com.sun.xml.registry.https.proxyHost",
"myhost.mydomain");
props.setProperty("com.sun.xml.registry.https.proxyPort",
"8080");
Затем клиент устанавливает свойства центра соединений и создает соединение:

connFactory.setProperties(props);
Connection connection = connFactory.createConnection();
Метод makeConnection в примере программы показывает шаги, выполняемые при создании
JAXR-соединения.

12.2.1.4. Установка свойств соединения


Реализация JAXR в Java WSDP позволяет вам установить несколько свойств JAXR-
соединения. Некоторые из них являются стандартными свойствами, определенными в
спецификации JAXR. Другие свойства специфичны для реализации JAXR в Java WSDP.
В таблицах 11-1 и 11-2 перечислены и описаны эти свойства.
Таблица 11-1 Стандартные свойства JAXR-соединения
Название и описание свойства Тип данных Значение по умолчанию
javax.xml.registry.queryManagerURL String Нет
Указывает URL службы управления
запросами у заданного поставщика
реестра
javax.xml.registry.lifeCycleManagerURL String Такое же, как и указанное значение
Указывает URL службы управления queryManagerURL
циклом жизни у заданного поставщика
реестра (для обновлений реестра)
javax.xml.registry.semanticEquivalences String Нет
Указывает семантические
эквивалентности концепций как один или
несколько кортежей значений ID двух
эквивалентных концепций, разделенных
запятыми; кортежи разделяются
вертикальными линиями: id1, id2| id3, id4
javax.xml.registry.security.authentication- String Нет; единственное поддерживаемое
Method значение - UDI_GET_AUTHTOKEN
Предоставляет рекомендацию JAXR-
поставщику о методе аутентификации,
используемому при аутентификации у
поставщика реестра
javax.xml.registry.uddi.maxRows Integer Нет
Максимальное число строк,
возвращаемых операциями поиска.
Предназначено для UDDI-поставщиков

Web-

Rendered by www.RenderX.com
Стр. 376 из 626 Java API for XML Registries

Название и описание свойства Тип данных Значение по умолчанию


javax.xml.registry.postalAddressScheme String Нет
ID ClassificationScheme, используемой
схемы почтовых адресов по умолчанию.
Примеры находятся в разделе "Указание
почтовых адресов"

Таблица 11-2 Свойства JAXR-соединения, зависящие от реализации


Название и описание свойства Тип данных Значение по умолчанию
com.sun.xml.registry.http.proxyHost String Значение хоста прокси, указанное в
Указывает хост HTTP-прокси, <JWSDP_HOME>/conf/jwsdp.properties
используемый для доступа к внешним
реестрам. Если вы указывали хост и порт
прокси во время установки Java WSDP,
указанные вами значения находятся в
файле
<JWSDP_HOME>/conf/jwsdp.properties
com.sun.xml.registry.http.proxyPort String Значение порта прокси, указанное в
Указывает порт HTTP-прокси, <JWSDP_HOME>/conf/jwsdp.properties
используемый для доступа к внешним
реестрам; обычно 8080
com.sun.xml.registry.https.proxyHost String Такое же, как и значение хоста HTTP-
Указывает хост HTTPS-прокси, прокси
используемый для доступа к внешним
реестрам
com.sun.xml.registry.https.proxyPort String Такое же, как и значение порта HTTP-
Указывает порт HTTPS-прокси, прокси
используемый для доступа к внешним
реестрам; обычно 8080
com.sun.xml.registry.http.proxyUserName String Нет
Указывает имя пользователя для хоста
прокси при HTTP-прокси
аутентификации, если это необходимо
com.sun.xml.registry.http.proxyPassword String Нет
Указывает пароль для хоста прокси при
HTTP-прокси аутентификации, если это
необходимо
com.sun.xml.registry.useCache Boolean, передаваемое как String True
Указывает реализации JAXR искать
объекты реестра сначала в кэше, а
потом, если они не найдены, - в реестре
com.sun.xml.registry.useSOAP Boolean, передаваемое как String False
Указывает реализации JAXR
использовать Apache SOAP вместо Java
API for XML Messaging; может быть
полезно при отладке

Вы можете установить эти свойства следующим образом:


• Большинство из этих свойств может быть установлено в программе JAXR-клиента.
Например:

Properties props = new Properties();


props.setProperty("javax.xml.registry.queryManagerURL",
"http://uddi.ibm.com/testregistry/inquiryapi");
props.setProperty("javax.xml.registry.lifeCycleManagerURL",

Web-

Rendered by www.RenderX.com
Реализация JAXR-клиента Стр. 377 из 626

"https://uddi.ibm.com/testregistry/protect/publishapi");
ConnectionFactory factory = ConnectionFactory.newInstance();
factory.setProperties(props);
connection = factory.createConnection();
• Свойства postalAdressScheme, useCache и useSOAP могут быть установлены в теге
<sysproperty> в файле build.xml для программы ant. Например:

<sysproperty key="useSOAP" value="true"/>


Эти свойства могут быть, также, установлены при помощи параметра -D в командной
строке java.
Дополнительным системным свойством, специфичным для реализации JAXR в Java WSDP,
является com.sun.xml.registry.userTaxonomyFilenames. Детальная информация по
использованию этого свойства приведена в разделе "Определение таксономии".

12.2.1.5. Получение и использование объекта RegistryService


После создания соединения клиент использует его для получения объекта RegistryService,
а затем интерфейса, или интерфейсов, которые будет использовать:

RegistryService rs = connection.getRegistryService();
BusinessQueryManager bqm = rs.getBusinessQueryManager();
BusinessLifeCycleManager blcm =
rs.getBusinessLifeCycleManager();
Обычно клиент получает оба объекта - и объект BusinesQueryManager, и объект BusinessLife-
CycleManager из объекта RegistryService. Если он использует реестр только для простых
запросов, ему необходимо получить только объект BusinessQueryManager.

12.2.2. Запрос реестра


Простейшим способом использования реестра клиентом является запрос информации об
организации, которая предоставила данные реестру. Интерфейс BusinessQueryManager
поддерживает несколько методов поиска, позволяющих клиентам искать данные, используя
информационную модель JAXR. Многие из этих методов возвращают BulkResponse
(коллекцию объектов), которая удовлетворяет набору критериев, указанных в аргументах
метода. Наиболее полезными из этих методов являются:
• findOrganizations, который возвращает список организаций, удовлетворяющих указанному
критерию - часто шаблону названия или классификации в схеме классификации
• findServices, который возвращает набор служб, предлагаемых указанной организацией
• findServiceBindings, который возвращает связи со службой (информацию о том, как
получить доступ к службе), поддерживаемые указанной службой
Программа JAXRQuery демонстрирует выполнение запроса к реестру по названию
организации и отображение возвращаемых данных. Программы JAXRQueryByNAICSClas-
sification и JAXRQueryByWSDLClassification демонстрируют выполнение запросов к реестру

Web-

Rendered by www.RenderX.com
Стр. 378 из 626 Java API for XML Registries

с использованием классификаций. Все JAXR-поставщики поддерживают как минимум


следующие таксономии для классификации:
• The North American Industry Classification System (NAICS). Более подробно:
http://www.census.gov/epcd/www/naics.html.
• The Universal Standard Product and Services Classification (UNSPSC). Более подробно -
http://www.eccma.org/unspsc/.
• Система классификации кодов стран ISO 3166, поддерживаемая организацией ISO
(International Organization for Standardization). Более подробно -
http://www.iso.org/iso/en/prods-services/iso3166ma/index.html.
В следующих разделах описывается, как выполнить некоторые распространенные запросы.

12.2.2.1. Поиск организации по названию


Для поиска организации по названию обычно используется комбинация квалификаторов
поиска (которые влияют на сортировку и сопоставление с образцом) и образцов названия
(которые указывают искомые строки). Метод findOrganization принимает коллекцию объектов
findQualifier в качестве первого аргумента и коллекцию объектов namePattern в качестве
второго аргумента. В следующем фрагменте показано, как найти в реестре все организации,
чьи названия начинаются с указанной строки qString, и отсортировать их в алфавитном
порядке.

//

Collection findQualifiers = new ArrayList();


findQualifiers.add(FindQualifier.SORT_BY_NAME_DESC);
Collection namePatterns = new ArrayList();
namePatterns.add(qString);

//
BulkResponse response =
bqm.findOrganizations(findQualifiers,
namePatterns, null, null, null, null);
Collection orgs = response.getCollection();
Клиент может использовать знак процента (%) для указания того, что строка запроса может
встретиться в любом месте названия организации. Например, в следующем фрагменте
программы выполняется поиск организаций (учитывается регистр символов), чьи названия
содержат qString:

Collection findQualifiers = new ArrayList();


findQualifiers.add(FindQualifier.CASE_SENSITIVE_MATCH);
Collection namePatterns = new ArrayList();
namePatterns.add("%" + qString + "%");

Web-

Rendered by www.RenderX.com
Реализация JAXR-клиента Стр. 379 из 626

// ,
qString
BulkResponse response =
bqm.findOrganizations(findQualifiers, namePatterns, null,
null, null, null);
Collection orgs = response.getCollection();

12.2.2.2. Поиск организаций по классификации


Для поиска организации по классификации необходимо установить классификацию в
конкретной схеме классификации, и затем указать ее в качестве аргумента метода findOr-
ganizations.
В следующем фрагменте программы выполняется поиск всех организаций, соответствующих
конкретной классификации в таксономии NAICS. (вы можете найти NAICS-коды на странице
http://www.census.gov/epcd/naics/naicscod.txt, а также в файле
<JWSDP_HOME>/docs/jaxr/taxonomies/naics.xml.)

ClassificationScheme cScheme =
bqm.findClassificationSchemeByName(null,
"ntis-gov:naics");
Classification classification =
blcm.createClassification(cScheme,
"Snack and Nonalcoholic Beverage Bars", "722213");
Collection classifications = new ArrayList();
classifications.add(classification);
// JAXR-
BulkResponse response = bqm.findOrganizations(null,
null, classifications, null, null, null);
Collection orgs = response.getCollection();
Вы можете использовать классификацию для поиска организаций, предлагающих службы,
основанные на технических спецификациях в форме WSDL-документов (Web Services
Description Language). В JAXR в качестве прокси для хранения информации о спецификации
используется концепт. Процедура поиска немного более сложная, чем в предыдущем
примере, поскольку клиент должен сначала найти концепт спецификации, а затем
организации, использующие этот концепт.
В следующем фрагменте программы выполняется поиск всех экземпляров WSDL-
спецификаций, используемых в данном реестре. Как вы можете заметить, программа
похожа на фрагмент кода запроса NAICS, за исключением того, что он заканчивается
вызовом findConcepts вместо findOrganizations.

String schemeName = "uddi-org:types";


ClassificationScheme uddiOrgTypes =
bqm.findClassificationSchemeByName(null, schemeName);

Web-

Rendered by www.RenderX.com
Стр. 380 из 626 Java API for XML Registries

/*
* , ,
* ,
WSDL-
* UDDI.
*/
Classification wsdlSpecClassification =
blcm.createClassification(uddiOrgTypes,
"wsdlSpec", "wsdlSpec");

Collection classifications = new ArrayList();


classifications.add(wsdlSpecClassification);

//
BulkResponse br = bqm.findConcepts(null, null,
classifications, null, null);
Для сужения поиска вы могли бы использовать другие аргументы метода findConcepts
(квалификаторы поиска, названия, внешние идентификаторы или внешние ссылки).
Следующий шаг - просмотреть концепты, найти WSDL-документы, которым они
соответствуют, и отобразить организации, использующие каждый документ:

//

Collection specConcepts = br.getCollection();


Iterator iter = specConcepts.iterator();
if (!iter.hasNext()) {
System.out.println("No WSDL specification concepts found");
} else {
while (iter.hasNext()) {
Concept concept = (Concept) iter.next();

String name = getName(concept);

Collection links = concept.getExternalLinks();


System.out.println("\nSpecification Concept:\n\tName: " +
name + "\n\tKey: " +
concept.getKey().getId() +
"\n\tDescription: " +
getDescription(concept));

Web-

Rendered by www.RenderX.com
Реализация JAXR-клиента Стр. 381 из 626

if (links.size() > 0) {
ExternalLink link =
(ExternalLink) links.iterator().next();
System.out.println("\tURL of WSDL document: '" +
link.getExternalURI() + "'");
}

// ,

Collection specConcepts1 = new ArrayList();


specConcepts1.add(concept);
br = bqm.findOrganizations(null, null, null,
specConcepts1, null, null);

//
...
}
Если вы нашли организации, предлагающие нужную вам службу, вы можете вызвать эту
службу, используя JAX-RPC API.

12.2.2.3. Поиск служб и связей со службами


После определения организации клиент может выполнить поиск служб организации и
связей с этими службами.

Iterator orgIter = orgs.iterator();


while (orgIter.hasNext()) {
Organization org = (Organization) orgIter.next();
Collection services = org.getServices();
Iterator svcIter = services.iterator();
while (svcIter.hasNext()) {
Service svc = (Service) svcIter.next();
Collection serviceBindings =
svc.getServiceBindings();
Iterator sbIter = serviceBindings.iterator();
while (sbIter.hasNext()) {
ServiceBinding sb =
(ServiceBinding) sbIter.next();
}
}
}

Web-

Rendered by www.RenderX.com
Стр. 382 из 626 Java API for XML Registries

12.2.3. Управление данными реестра


Клиент может подавать данные в реестр, изменять и удалять их, если он авторизован для
таких действий. Для выполнения этих задач используется интерфейс BusinessLifeCycleM-
anager.
Реестры обычно разрешают клиенту изменение или удаление данных только в том случае,
если он является тем же самым пользователем, который в первый раз предоставил эти
данные.

12.2.3.1. Получение авторизации от реестра


Перед получением возможности записать данные клиент должен передать свои имя и
пароль в реестр в наборе рекомендаций. В следующем фрагменте программы показано,
как это сделать.

String username = "myUserName";


String password = "myPassword";

//
PasswordAuthentication passwdAuth =
new PasswordAuthentication(username,
password.toCharArray());

Set creds = new HashSet();


creds.add(passwdAuth);
connection.setCredentials(creds);

12.2.3.2. Создание организации


Клиент создает организацию и заполняет ее данными перед сохранением.
Объект Organization является одним из наиболее сложных элементов данных в JAXR API.
Обычно он включает следующие объекты:
• Объект Name.
• Объект Description.
• Объект Key, представляющий ID, под которым организация известна в реестре. Этот
ключ создается реестром, а не пользователем, и возвращается после подачи
организации в реестр.
• Объект PrimaryContact, который является объектом User, ссылающимся на
авторизованного пользователя реестра. Объект User обычно включает в себя объект
PersonName и коллекции объектов TelephoneNumber, EmailAddress и/или PostalAddress.
• Коллекция объектов Classification.
• Объект Service и связанные с ним объекты ServiceBinding.
Например, в следующем фрагменте программы создается организация и указывается ее
имя, описание и контактная информация. При создании организации ее ключ не

Web-

Rendered by www.RenderX.com
Реализация JAXR-клиента Стр. 383 из 626

указывается; реестр возвращает новый ключ после подтверждения вновь созданной


организации. Объект blcm в этом фрагменте является объектом BusinessLifeCycleManager,
возвращенным в разделе "Получение и использование объекта RegistryService". Для
строковых значений, которые могут нуждаться в локализации, используется объект Inter-
nationalString.

//
Organization org =
blcm.createOrganization("The Coffee Break");
InternationalString s =
blcm.createInternationalString("Purveyor of " +
"the finest coffees. Established 1895");
org.setDescription(s);

// ,

User primaryContact = blcm.createUser();


PersonName pName = blcm.createPersonName("Jane Doe");
primaryContact.setPersonName(pName);

//

TelephoneNumber tNum = blcm.createTelephoneNumber();


tNum.setNumber("(800) 555-1212");
Collection phoneNums = new ArrayList();
phoneNums.add(tNum);
primaryContact.setTelephoneNumbers(phoneNums);

//

EmailAddress emailAddress =
blcm.createEmailAddress("jane.doe@TheCoffeeBreak.com");
Collection emailAddresses = new ArrayList();
emailAddresses.add(emailAddress);
primaryContact.setEmailAddresses(emailAddresses);

//

org.setPrimaryContact(primaryContact);

Web-

Rendered by www.RenderX.com
Стр. 384 из 626 Java API for XML Registries

12.2.3.3. Добавление классификаций


Организации обычно соответствуют одной или более классификациям, основанным на
одной или более классификационных схем (таксономий). Для установки классификации
для организации с использованием таксономии клиент сначала определяет таксономию,
которую хочет применить. Он использует BusinessQueryManager для поиска таксономии.
Метод findClassificationSchemeByName принимает набор объектов FindQualifier в качестве
первого аргумента, но этот аргумент может иметь значение null.

// NAICS
ClassificationScheme cScheme =
bqm.findClassificationSchemeByName(null, "ntis-gov:naics");
Затем клиент создает классификацию, используя классификационную схему и концепт
(элемент таксономии) в классификационной схеме. Например, в следующем коде для
организации устанавливается классификация в NAICS-таксономии. Второй и третий
аргументы метода createClassification являются названием и значением концепта.

//
Classification classification =
blcm.createClassification(cScheme,
"Snack and Nonalcoholic Beverage Bars", "722213");
Collection classifications = new ArrayList();
classifications.add(classification);
org.addClassifications(classifications);
Службы также используют классификации, поэтому вы можете использовать похожий код
для добавления классификации в объект Service.

12.2.3.4. Добавление служб и связей со службами в организацию


Большинство организаций добавляют себя в реестр, для того чтобы предложить службы,
поэтому JAXR API имеет возможности добавления служб и связей со службами к
организации.
Аналогично объекту Organization, объект Service имеет имя и описание. Также, аналогично
объекту Organization, он имеет уникальный ключ, который генерируется реестром при
регистрации службы. Он может также иметь связанные с ним классификации.
Служба обычно содержит связи со службой, которые предоставляют информацию о том,
как получить доступ к службе. Объект ServiceBinding обычно имеет описание, URI доступа
и ссылку на спецификацию, которая обеспечивает соединение между связью со службой
и технической спецификацией. Эта спецификация описывает, как использовать службу с
применением этой связи.
В следующем фрагменте кода показано, как создать коллекцию служб, добавить связи со
службами к службе и добавить службы к организации. В нем указан URI доступа, а ссылка
на спецификацию не указана. Поскольку URI доступа не является реальным, а JAXR по
умолчанию проверяет достоверность всех указываемых URI, связь со службой
устанавливает свое свойство validateURI в значение false.

Web-

Rendered by www.RenderX.com
Реализация JAXR-клиента Стр. 385 из 626

// services service
Collection services = new ArrayList();
Service service = blcm.createService("My Service Name");
InternationalString is =
blcm.createInternationalString("My Service Description");
service.setDescription(is);

//
Collection serviceBindings = new ArrayList();
ServiceBinding binding = blcm.createServiceBinding();
is = blcm.createInternationalString("My Service Binding " +
"Description");
binding.setDescription(is);
// URL

binding.setValidateURI(false);
binding.setAccessURI("http://TheCoffeeBreak.com:8080/sb/");
serviceBindings.add(binding);

//
service.addServiceBindings(serviceBindings);

// service services,
services
//
services.add(service);
org.addServices(services);

12.2.3.5. Сохранение организации


Основным методом, используемым клиентом для добавления или изменения данных об
организации, является метод saveOrganizations, который создает в реестре одну или более
организаций, если они не существовали. Если какая-либо из организаций существует, но
некоторые данные были изменены, метод saveOrganizations обновляет и заменяет данные.
После заполнения организации информацией, которую клиент хочет сделать
общедоступной, он сохраняет организацию. Реестр в ответ возвращает ключ, и клиент
извлекает его.

//
//

Web-

Rendered by www.RenderX.com
Стр. 386 из 626 Java API for XML Registries

Collection orgs = new ArrayList();


orgs.add(org);
BulkResponse response = blcm.saveOrganizations(orgs);
Collection exceptions = response.getException();
if (exceptions == null) {
System.out.println("Organization saved");

Collection keys = response.getCollection();


Iterator keyIter = keys.iterator();
if (keyIter.hasNext()) {
javax.xml.registry.infomodel.Key orgKey =
(javax.xml.registry.infomodel.Key) keyIter.next();
String id = orgKey.getId();
System.out.println("Organization key is " + id);
org.setKey(orgKey);
}
}

12.2.3.6. Удаление данных из реестра


Реестр позволяет вам удалить из него любые данные, которые вы ему передали. Для этого
используется ключ, возвращенный реестром, в качестве аргумента одного из методов
BusinessLifeCycleManager для удаления: deleteOrganizations, deleteServices, deleteService-
Bindings и других.
Пример программы JAXRDelete удаляет организацию, созданную программой JAXRPublish.
Он удаляет организацию, соответствующую указанному ключу, а затем отображает ключ
для того, чтобы пользователь мог убедиться, что был удален корректный ключ.

String id = key.getId();
System.out.println("Deleting organization with id " + id);
Collection keys = new ArrayList();
keys.add(key);
BulkResponse response = blcm.deleteOrganizations(keys);
Collection exceptions = response.getException();
if (exceptions == null) {
System.out.println("Organization deleted");
Collection retKeys = response.getCollection();
Iterator keyIter = retKeys.iterator();
javax.xml.registry.infomodel.Key orgKey = null;
if (keyIter.hasNext()) {
orgKey =
(javax.xml.registry.infomodel.Key) keyIter.next();
id = orgKey.getId();

Web-

Rendered by www.RenderX.com
Реализация JAXR-клиента Стр. 387 из 626

System.out.println("Organization key was " + id);


}
}
Клиент может использовать похожий механизм для удаления служб и связей со службой.

12.2.4. Использование таксономий в JAXR-клиентах


В JAXR API таксономия представлена объектом ClassificationScheme.
В этом разделе описывается использование реализации JAXR в Java WSDP:
• для определения ваших собственных таксономий
• для указания почтовых адресов организации

12.2.4.1. Определение таксономии


Спецификация JAXR требует, чтобы JAXR-поставщик был способен добавить
пользовательские таксономии для использования JAXR-клиентами. Механизмы, которые
используют клиенты для добавления и администрирования этих таксономий, зависят от
реализации.
Реализация JAXR в Java WSDP использует простой, основанный на файлах подход к
предоставлению пользовательских таксономий JAXR-клиентам. Эти файлы читаются во
время выполнения после запуска JAXR-поставщика.
Структура таксономии для Java WSDP определяется в JAXR Predefined Concept DTD,
которая объявляется в двух файлах - jaxrconcepts.dtd и jaxrconcepts.xsd (в форме XML-
схемы). Файл jaxrconcepts.xml содержит таксономии для реализации JAXR в Java WSDP.
Все эти файлы содержатся в архиве <JWSDP_HOME>/common/lib/jaxr-ri.jar, но вы можете
найти их копии в каталоге <JWSDP_HOME>/docs/jaxr/taxonomies. Этот каталог содержит
также копии XML-файлов, которые использует реализация JAXR в Java WSDP для
определения широко-известных таксономий: naics.xml, iso3166.xml и unspsc.xml. Вы можете
использовать их как примеры для конструирования XML-файла таксономий.
Записи в файле jaxrconcepts.xml выглядят следующим образом:

<PredefinedConcepts>
<JAXRClassificationScheme id="schId" name="schName">
<JAXRConcept id="schId/conCode" name="conName"
parent="parentId" code="conCode"></JAXRConcept>
...
</JAXRClassificationScheme>
</PredefinedConcepts>
Структура таксономии является структурой, основанной на включениях. Элемент Prede-
finedConcepts является корнем структуры и должен присутствовать. Элемент JAXRClasifi-
cationScheme является родительским элементом структуры, а элементы JAXRConcept
являются детьми и внуками. Элемент JAXRConcept может иметь детей, но это не
обязательно.

Web-

Rendered by www.RenderX.com
Стр. 388 из 626 Java API for XML Registries

В определениях всех элементов порядок и регистр атрибутов важны.


Для добавления пользовательской таксономии выполните следующие действия:
1. Опубликуйте элемент JAXRClassificationScheme для таксономии как объект Classifica-
tionScheme в реестре, к которому вы будете обращаться. Например, вы можете
опубликовать объект ClassificationScheme в сервере реестра Java WSDP. Для его
публикации вы должны установить имя объекта. Вы, также, даете схеме классификацию
в известной классификационной схеме, такую как uddi-org:types. В следующем
фрагменте программы имя является первым аргументом метода LifeCycleManager.create-
ClassificationScheme.

ClassificationScheme cScheme =
blcm.createClassificationScheme("MyScheme",
"A Classification Scheme");
ClassificationScheme uddiOrgTypes =
bqm.findClassificationSchemeByName(null,
"uddi-org:types");
if (uddiOrgTypes != null) {
Classification classification =
blcm.createClassification(uddiOrgTypes,
"postalAddress", "categorization" );
postalScheme.addClassification(classification);
ExternalLink externalLink =
blcm.createExternalLink("http://www.mycom.com/myscheme.html",
"My Scheme");
postalScheme.addExternalLink(externalLink);
Collection schemes = new ArrayList();
schemes.add(cScheme);
BulkResponse br =
blcm.saveClassificationSchemes(schemes);
}
Объект BulkResponse, возвращенный методом saveClassificationSchemes, содержит
ключ для классификационной схемы, которую вы должны извлечь:

if (br.getStatus() == JAXRResponse.STATUS_SUCCESS) {
System.out.println("Saved ClassificationScheme");
Collection schemeKeys = br.getCollection();
Iterator keysIter = schemeKeys.iterator();
while (keysIter.hasNext()) {
javax.xml.registry.infomodel.Key key =
(javax.xml.registry.infomodel.Key)
keysIter.next();

Web-

Rendered by www.RenderX.com
Реализация JAXR-клиента Стр. 389 из 626

System.out.println("The postalScheme key is " +


key.getId());
System.out.println("Use this key as the scheme" +
" uuid in the taxonomy file");
}
}
2. В XML-файле определите структуру таксономии, совместимую с JAXR Predefined
Concepts DTD. Введите элемент ClassificationScheme в вашем XML-файле таксономии,
указав возвращенное значение ID ключа в качестве атрибута id и имя в качестве
атрибута name. Для приведенного выше фрагмента программы, например, открывающий
тег для элемента JAXRClassificationScheme выглядит примерно так (все в одной строке):

<JAXRClassificationScheme id="uuid:nnnnnnnn-nnnn-nnnn-nnnn-
nnnnnnnnnnnn" name="MyScheme">
ClassificationScheme id дожен быть UUID.
3. Введите каждый элемент JAXRConcept в ваш XML-файл таксономии, указывая
следующие четыре атрибута в следующем порядке:
A. id - это значение JAXRClassificationScheme id, за которым идет разделитель /, и код
элемента JAXRConcept
B. name - это имя элемента JAXRConcept
C. parent - это id прямого родителя (либо ClassificationScheme id, либо id родительского
JAXRConcept)
D. code - это значение кода элемента JAXRConcept
Первый элемент JAXRConcept в файле naics.xml выглядит следующим образом (все
в одной строке):

<JAXRConcept id="uuid:C0B9FE13-179F-413D-8A5B-5004DB8E5BB2/11"

name="Agriculture, Forestry, Fishing and Hunting"


parent="uuid:C0B9FE13-179F-413D-8A5B-5004DB8E5BB2"
code="11"></JAXRConcept>
4. Для добавления структуры пользовательской таксономии к JAXR-поставщику при
запуске клиентской программы укажите системное свойство com.sun.xml.registry.user-
TaxonomyFilenames. Командная строка будет выглядеть следующим образом (все в
одной строке). Вертикальная черта (|) является разделителем файлов.

java myProgram
-DuserTaxonomyFilenames=c:\myfile\xxx.xml|c:\myfile\xxx2.xml
Вы можете использовать тег <sysproperty> для установки этого свойства в файле
build.xml. Или вы можете установить свойство в вашей программе следующим образом:

Web-

Rendered by www.RenderX.com
Стр. 390 из 626 Java API for XML Registries

System.setProperty
("com.sun.xml.registry.userTaxonomyFilenames",
"c:\myfile\xxx.xml|c:\myfile\xxx2.xml");

12.2.4.2. Указание почтовых адресов


Спецификация JAXR определяет почтовый адрес как структурированный интерфейс с
атрибутами для улицы, города, страны и т.д. Спецификация UDDI, с другой стороны,
определяет почтовый адрес как коллекцию строк адреса в свободной форме, каждой из
которых может быть тоже присвоено значение. Для отображения JAXR-формата PostalAd-
dress в известный адресный формат UDDI вы должны указать UDDI-формат как объект
ClassificationScheme, а затем указать семантические эквиваленты между концептами в
классификационной схеме UDDI-формата и элементами в классификационной схеме JAXR
PostalAddress. Классификационная схема JAXR PostalAddress предоставляется реализацией
JAXR в Java WSDP.
В JAXR API объект PostalAddress имеет поля streetNumber, street, city, state, postalCode и
country. В реализации JAXR в Java WSDP они являются предопределенными концептами
в файле jaxrconcepts.xml внутри ClassificationScheme с именем PostalAddressAttributes.
Для указания отображения между форматом почтового адреса JAXR и другим форматом
необходимо установить два свойства соединения:
• Свойство javax.xml.registry.postalAddressScheme, указывающее классификационную
схему почтового адреса для соединения.
• Свойство javax.xml.registry.semanticEquivalences, указывающее семантические
эквиваленты между JAXR-форматом и другими форматами.
Например, предположим, что вы хотите использовать схему, опубликованную в IBM-реестре
с известным UUID uuid:6eaf4b50-4196-11d6-9e2b-000629dc0a2b. Эта схема уже существует
в файле jaxrconcepts.xml под именем IBMDefaultPostalAddressAttributes.

<JAXRClassificationScheme id="uuid:6EAF4B50-4196-11D6-9E2B-
000629DC0A2B" name="IBMDefaultPostalAddressAttributes">
Прежде всего, вы указываете схему почтового адреса, используя значение id из элемента
JAXRClassificationScheme (UUID). Регистр не имеет значения:

props.setProperty("javax.xml.registry.postalAddressScheme",
"uuid:6eaf4b50-4196-11d6-9e2b-000629dc0a2b");
Затем, вы указываете отображение id каждого элемента JAXRConcept в схеме почтового
адреса JAXR по умолчанию в id его эквивалента в IBM-схеме:

props.setProperty("javax.xml.registry.semanticEquivalences",
"urn:uuid:PostalAddressAttributes/StreetNumber," +
"urn:uuid:6eaf4b50-4196-11d6-9e2b-

Web-

Rendered by www.RenderX.com
Реализация JAXR-клиента Стр. 391 из 626

000629dc0a2b/StreetAddressNumber|" +
"urn:uuid:PostalAddressAttributes/Street," +
"urn:uuid:6eaf4b50-4196-11d6-9e2b-
000629dc0a2b/StreetAddress|" +
"urn:uuid:PostalAddressAttributes/City," +
"urn:uuid:6eaf4b50-4196-11d6-9e2b-000629dc0a2b/City|" +
"urn:uuid:PostalAddressAttributes/State," +
"urn:uuid:6eaf4b50-4196-11d6-9e2b-000629dc0a2b/State|" +
"urn:uuid:PostalAddressAttributes/PostalCode," +
"urn:uuid:6eaf4b50-4196-11d6-9e2b-000629dc0a2b/ZipCode|" +
"urn:uuid:PostalAddressAttributes/Country," +
"urn:uuid:6eaf4b50-4196-11d6-9e2b-000629dc0a2b/Country");
После создания соединения с использованием этих свойств вы можете создать почтовый
адрес и присвоить его контактной информации организации перед опубликованием этой
организации:

String streetNumber = "99";


String street = "Imaginary Ave. Suite 33";
String city = "Imaginary City";
String state = "NY";
String country = "USA";
String postalCode = "00000";
String type = "";
PostalAddress postAddr =
blcm.createPostalAddress(streetNumber, street, city, state,
country, postalCode, type);
Collection postalAddresses = new ArrayList();
postalAddresses.add(postAddr);
primaryContact.setPostalAddresses(postalAddresses);
Затем JAXR-запрос может извлечь почтовый адрес, используя метод PostalAddress, если
схема почтового адреса и семантические эквиваленты запроса и приложения аналогичны.
Для извлечения почтовых адресов при неизвестной схеме, использовавшейся при их
публикации, вы можете извлечь их как коллекцию объектов Slot. Пример программы
JAXRQueryPostal.java показывает, как это сделать:
В общем случае вы можете создать пользовательскую таксономию почтового адреса для
любых моделей tModel postalAddress, использующих широко-известную категоризацию в
таксономии uddi-org:types, которая имеет tModel UUID uuid:c1acf26d-9672-4404-9d70-
39b756e62ab4 со значением postalAddress. Вы можете извлечь tModel overviewDoc, которая
указывает на технические детали в спецификации схемы, содержащей определение
структуры таксономии. (JAXR-эквивалентом overviewDoc является ExternaLink.)

Web-

Rendered by www.RenderX.com
Стр. 392 из 626 Java API for XML Registries

12.2.5. Выполнение примеров клиентских приложений


Простые клиентские программы, поставляемые с этим руководством, могут быть выполнены
из командной строки. Вы можете менять их для ваших потребностей. Они позволяют
указывать IBM-реестр, Microsoft-реестр или сервер реестра для запросов и обновлений;
вы также можете указать любой другой UDDI-реестр версии 2.
Примерами клиентских приложений, расположенными в каталоге
<JWSDP_HOME>/docs/tutorial/examples/jaxr, являются:
• JAXRQuery.java показывает, как создать реестр для организаций
• JAXRQueryByNAICSClassification.java показывает, как выполнить поиск в реестре с
использованием обычной классификационной схемы
• JAXRQueryByWSDLClassification.java показывает, как найти в реестре Web-службу,
которая описывает себя средствами WSDL-документа
• JAXRPublish.java показывает, как опубликовать организацию в реестре
• JAXRDelete.java показывает, как удалить организацию из реестра
• JAXRSaveClassificationScheme.java показывает, как опубликовать классификационную
схему (в частности схему почтового адреса) в реестре
• JAXRPublishPostal.java показывает, как опубликовать организацию с почтовым адресом
для контактов
• JAXRQueryPostal.java показывает, как извлечь данные о почтовом адресе организации
• JAXRDeleteScheme.java показывает, как удалить классификационную схему из реестра
• JAXRGetMyObjects.java выводит список всех объектов, которыми вы владеете в реестре
Каталог <JWSDP_HOME>/docs/tutorial/examples/jaxr содержит также:
• Файл build.xml для примеров
• Файл JAXRExamples.properties, обеспечивающий строковые значения для примеров
программ
• Файл, называемый postalconcepts.xml, используемый в примерах с почтовым адресом

12.2.5.1. Перед компиляцией примеров


Перед компиляцией примеров отредактируйте файл JAXRExamples.properties следующим
образом. (См. раздел "Использование JAXR API для доступа к серверу реестра" для
получения детальной информации по редактированию файла для обращения к серверу
реестра.)
1. Отредактируйте следующие строки файла JAXRExamples.properties для указания
нужного реестра. Для обоих присваиваний - queryURL и publishURL - закомментируйте
все строки, кроме строки с реестром, к которому вы хотите обратиться. Реестром по
умолчанию является сервер реестра, поэтому при использовании сервера реестра
ничего изменять не надо.

## Uncomment one pair of query and publish URLs.

Web-

Rendered by www.RenderX.com
Реализация JAXR-клиента Стр. 393 из 626

## IBM:
#query.url=http://uddi.ibm.com/testregistry/inquiryapi
#publish.url=https://uddi.ibm.com/testregistry/protect/publish
api
## Microsoft:
#query.url=http://uddi.microsoft.com/inquire
#publish.url=https://uddi.microsoft.com/publish
## Registry Server:
query.url=http://localhost:8080/registry-
server/RegistryServerServlet
publish.url=http://localhost:8080/registry-
server/RegistryServerServlet
Оба реестра - и IBM, и Microsoft - содержат огромное количество данных, к которым
вы можете выполнять запросы. Кроме того, вам не нужно регистрироваться, если вы
собираетесь только лишь выполнять запросы. Мы не включили URL SAP-реестра;
можете добавить его самостоятельно. Если вы хотите опубликовать данные в любом
из публичных реестров, процесс регистрации для получения доступа к ним не является
трудным (см. раздел "Начальные сведения: получение доступа к реестру"). Однако
каждый из них позволяет вам одновременно иметь только одну зарегистрированную
организацию. Если вы опубликовали организацию в один из них, вы должны удалить
ее перед тем, как сможете опубликовать другую. Поскольку организации, публикуемые
примером JAXRPublish, являются вымышленными, вы должны удалить их сразу же в
любом случае. (Очень важно удалить эти организации сразу же, поскольку публичные
реестры тиражируют данные друг друга, и ваша вымышленная организация может
появиться не в том реестре, в который вы ее публиковали, и из него вы уже не сможете
ее удалить.) Сервер реестра дает вам больше свободы для экспериментов с JAXR.
Вы можете опубликовать столько организаций, сколько захотите. Однако этот реестр
поставляется с пустой базой данных, поэтому вы должны опубликовать организации
в нем самостоятельно перед тем, как сможете выполнять запросы к данным.
2. Отредактируйте следующие строки файла JAXRExamples.properties для указания имени
пользователя и пароля, полученного вами при регистрации в реестре. По умолчанию
указан пароль к серверу реестра.

## Specify username and password if needed


## testuser/testuser are defaults for Registry Server
registry.username=testuser
registry.password=testuser
3. Если вы будете использовать публичный реестр, отредактируйте следующие строки
в файле JAXRExamples.properties, который содержит пустые строки для хостов прокси,
и укажите ваши собственные настройки прокси. Хостом прокси является система в
вашей сети, через которую вы получаете доступ в Интернет; вы обычно указываете
его в настройках вашего броузера. Вы можете оставить это значение пустым при
использовании сервера реестра.

Web-

Rendered by www.RenderX.com
Стр. 394 из 626 Java API for XML Registries

## HTTP and HTTPS proxy host and port;


## ignored by Registry Server
http.proxyHost=
http.proxyPort=8080
https.proxyHost=
https.proxyPort=8080
Порты прокси обычно имеют значение 8080; измените эту строку, если ваш прокси
использует другой порт. Для публичных реестров ваши записи обычно подчиняются
следующей схеме:

http.proxyHost=proxyhost.mydomain
http.proxyPort=8080
https.proxyHost=proxyhost.mydomain
https.proxyPort=8080
4. Свободно меняйте любые данные по организации в оставшейся части файла. Эти
данные используются примерами публикации и примерами, работающими с почтовыми
адресами.

12.2.5.2. Компиляция примеров


Для компиляции программ перейдите в каталог <JWSDP_HOME>/docs/tutorial/examples/jaxr.
Файл build.xml позволяет вам использовать команду

ant build
для компиляции всех примеров. Программа ant создает подкаталог build и помещает в нем
файлы классов.
Вы увидите, что установка переменной classpath в файле build.xml включает содержимое
каталогов common/lib и common/endorsed. Все примеры JAXR-клиентов требуют такой
установки classpath.

12.2.5.3. Выполнение примеров


Некоторые задания build.xml для выполнения примеров содержат закомментированные
теги <sysproperty>, которые устанавливают уровень протоколирования JAXR для отладки,
а также устанавливают другие свойства соединения. Эти теги предоставляются для
демонстрации установки свойств соединения. Вы можете свободно менять или удалить
эти теги.
Если вы выполняете примеры с сервером реестра, запустите Tomcat и базу данных Xindice.
Более подробная информация по этому вопросу находится в разделе "Настройка сервера
реестра". Вам не обязательно запускать Tomcat для выполнения примеров с публичными
реестрами.

Web-

Rendered by www.RenderX.com
Реализация JAXR-клиента Стр. 395 из 626

12.2.5.4. Выполнение примера JAXRPublish


Для запуска программы JAXRPublish используйте задание run-publish без аргументов
командной строки:

ant run-publish
Программа отобразит строковое значение ключа новой организации под названием "The
Coffee Break".
После выполнения программы JAXRPublish, но перед запуском JAXRDelete, вы можете
выполнить программу JAXRQuery для поиска организации, которую опубликовали. Вы
можете, также, использовать броузер реестра для ее поиска.

12.2.5.5. Выполнение примера JAXRQuery


Для запуска примера JAXRQuery используйте задание run-query программы ant. Укажите
аргумент query-string в командной строке для поиска в реестре организации, чье название
содержит строку. Например, следующая командная строка используется для поиска
организаций, чьи названия содержат строку "coff" (поиск не зависит от регистра символов):

ant run-query -Dquery-string=coff

12.2.5.6. Выполнение примера JAXRQueryByNAICSClassification


После выполнения программы JAXRPublish вы можете, также, запустить пример JAXR-
QueryByNAICSClassification, который ищет организации, использующие классификацию
"Snack and Nonalcoholic Beverage Bars", использовавшейся для организации, созданной в
JAXRPublish. Для этого выполните задание run-query-naics программы ant:

ant run-query-naics

12.2.5.7. Выполнение примера JAXRDelete


Для запуска программы JAXRDelete укажите ключевую строку, возвращенную программой
JAXRPublishб в качестве входной информации для задания run-delete:

ant run-delete -Dkey-string=keyString

12.2.5.8. Выполнение примера JAXRQueryByWSDLClassification


Вы можете запустить пример JAXRQueryByWSDLClassification в любое время. Используйте
задание run-query-wsdl программы ant:

ant run-query-wsdl
Этот пример возвращает много результатов из публичных реестров и может выполняться
несколько минут.

Web-

Rendered by www.RenderX.com
Стр. 396 из 626 Java API for XML Registries

12.2.5.9. Публикация классификационной схемы


Для публикации организаций с почтовыми адресами в публичные реестры, вы должны
сначала опубликовать классификационную схему для почтового адреса.
Для запуска программы JAXRSaveClassificationScheme используйте задание run-save-
scheme:

ant run-save-scheme
Программа возвращает строку UUID, которая будет использоваться в следующем разделе.
Вы не должны запускать эту программу, если используете сервер реестра, поскольку она
не работает с этими объектами.
Публичные реестры позволяют вам владеть более чем одной классификационной схемой
одновременно (предел обычно устанавливается равным 10 для всех схем классификации
и концептов вместе).

12.2.5.10. Выполнение примеров, работающих с почтовым адресом


Перед запуском примеров, работающих с почтовым адресом, откройте в редакторе файл
postalconcepts.xml. Везде, где вы увидите строку uuid-from-save, замените ее строкой UUID,
возвращенной заданием run-save-scheme. Для сервера реестра вы можете использовать
любую строку, которая отформатирована как UUID.
Для данного реестра вам необходимо только сохранить классификационную схему и
отредактировать один раз файл postalconcepts.xml. После выполнения этих двух задач вы
можете запустить программы JAXRPublishPostal и JAXRQueryPostal несколько раз.
1. Запустите программу JAXRPublishPostal. Обратите внимание, что в файле build.xml
задание run-publish-postal содержит тег <sysproperty>, который устанавливает свойство
userTaxonomyFilenames в месторасположение файла postalconcepts.xml в текущем
каталоге:

<sysproperty key="com.sun.xml.registry.userTaxonomyFilenames"
value="postalconcepts.xml"/>
Укажите строку, введенную в файл postalconcepts.xml, как входную для задания run-
publish-postal:

ant run-publish-postal -Duuid-string=uuidstring


Программа отобразит строковое значение ключа новой организации.
2. Запустите программу JAXRQueryPostal. Задание run-query-postal содержит такой же
тег <sysproperty>, как и задание run-publish-postal.
В качестве входной информации для задания run-query-postal укажите в командной
строке оба аргумента - аргумент query-string и аргумент uuid-string - для поиска в реестре
организации, опубликованной в задании run-publish-target:

Web-

Rendered by www.RenderX.com
Дополнительная информация Стр. 397 из 626

ant run-query-postal -Dquery-string=coffee


-Duuid-string=uuidstring
Почтовый адрес контактной информации будет выглядеть корректно с методами JAXR
PostalAddress. Любой найденный почтовый адрес, использующий другую схему, будет
выглядеть как строки Slot.
3. Если вы используете публичный реестр, выполните инструкции раздела "Выполнение
примера JAXRDelete" для удаления опубликованной организации.

12.2.5.11. Удаление классификационной схемы


Для удаления опубликованной вами классификационной схемы после завершения ее
использования запустите программу JAXRDeleteScheme, используя задание run-delete-
scheme:

ant run-delete-scheme -Duuid-string=uuidstring


Для UDDI-реестра при удалении классификационной схемы она удаляется логически, а
не физически. Вы больше не можете использовать классификационную схему, но все
равно будете видеть ее, если, например, вызовете метод QuerryManager.getRegisteredOb-
jects. Поскольку публичные реестры позволяют вам иметь до 10 таких объектов, это обычно
не является проблемой.

12.2.5.12. Получение списка ваших объектов реестра


Для получения списка объектов, которыми вы владеете в реестре, - и организациями, и
классификационными схемами - запустите программу JAXRGetMyObjects, используя
задание run-get-objects:

ant run-get-objects

12.2.5.13. Другие задания


Для удаления каталога build и файлов классов используйте команду:

ant clean
Для получения вспомогательной информации по синтаксису заданий используйте команду:

ant -projecthelp

12.3. Дополнительная информация


Дополнительную информацию по JAXR, реестрам и Web-службам можно найти в следующих
источниках:
• Java Specification Request (JSR) 93:JAXR 1.0:
http://jcp.org/jsr/detail/093.jsp

Web-

Rendered by www.RenderX.com
Стр. 398 из 626 Технология Java Servlet

• Домашняя страница JAXR:


http://java.sun.com/xml/jaxr/index.html
• Проект Universal Description, Discovery, and Integration(UDDI):
http://www.uddi.org/
• ebXML:
http://www.ebxml.org/
• Open Source JAXR Provider for ebXML Registries:
https://sourceforge.net/forum/forum.php?forum_id=197238
• Java Web Services Developer Pack (Java WSDP):
http://java.sun.com/webservices/webservicespack.html
• Технологии Java и XML:
http://java.sun.com/xml/
• Технологии Java и Web-службы:
http://java.sun.com/webservices/index.htm

13. Технология Java Servlet


Стефани Бодофф
С тех пор, как Web начали использовать для служб доставки, поставщики служб распознали
необходимость использования динамического содержимого. Аплеты являлись одной из
ранних попыток разрешить эту проблему. Внимание было сфокусировано на опыте
использования платформы клиента для доставки динамического содержимого. В то же
время, разработчики исследовали использование для этих целей серверной платформы.
Вначале, основной технологией для производства динамического содержимого были CGI-
скрипты (Common Gateway Interface - общий шлюзовой интерфейс). Однако, несмотря на
широкое использование, технология CGI имеет ряд недостатков, включающих в себя
платформозависимость и отсутствие масштабируемости. Предназначением Java Servlet-
технологии было упрощение создания динамического содержимого, ориентированного на
пользователя.

13.1. Что такое Сервлет?


Сервлет является Java-классом, используемым для расширения функциональных
возможностей серверов, на которых размещаются приложения, доступ к которым
осуществляется посредством программной модели запрос-ответ. Несмотря на то, что
сервлеты могут отвечать на любой тип запроса, они используются для расширения
приложений, находящихся на Web-серверах. Для таких приложений технология Java Servlet
определяет характерные для HTTP классы сервлетов.
Пакеты javax.servlet и javax.servlet.http обеспечивают интерфейсы и классы для написания
сервлетов. Все сервлеты должны реализовывать интерфейс Servlet, который определяет
методы жизненного цикла.
При реализации службы generic, можно использовать или расширять класс GenericServlet,
поставляемый с интерфейсом API для Java Servlet. Класс HttpServlet обеспечивает для
обработки служб HTTP такие методы, как doGet и doPost.

Web-

Rendered by www.RenderX.com
Примеры сервлетов Стр. 399 из 626

В данной главе собрана информация о написании сервлетов, генерирующих ответы на


HTTP-запросы. Подразумевается, что вы уже обладаете некоторыми знаниями о протоколе
HTTP. Если вы не знакомы с данным протоколом, вы можете получить вводную
информацию, ознакомившись с разделом Обзор HTTP.

13.2. Примеры сервлетов


Данная глава использует приложение Duke's Bookstore для иллюстрирования задач,
связанных с программированием сервлетов. В Tаблице 12- 1 отображаются сервлеты,
которые обрабатывают каждую функцию, используемую в данном книжном магазине.
Каждая программируемая задача иллюстрируется одним или более сервлетами. К примеру,
BookDetailsServle t иллюстрирует обработку HTTP-запросов GET, BookDetailsServlet и
CatalogServlet показывают, как конструировать ответы, а CatalogServlet иллюстрирует, как
отслеживать информацию о сессии.
Таблица 12-1 Сервлеты примера Duke's Bookstore
Выполняемая функция Сервлет
Вход в книжный магазин BookStoreServlet
Создание шапки книжного магазина BannerServlet
Осмотр книг, выставленных на продажу CatalogServlet
Помещение книги в корзину покупателя CatalogServlet,BookDetailsServlet
Получение дополнительной информации о заданной книге BookDetailsServlet
Отображение корзины покупателя ShowCartServlet
Удаление одной или нескольких книг из корзины покупателя ShowCartServlet
Покупка книг, находящихся в корзине покупателя CashierServlet
Получение подтверждения заказа ReceiptServlet

Данные для приложения "книжный магазин" содержатся в базе данных и доступны


посредством вспомогательного класса database.BookDB. Пакет database содержит также
класс BookDetails, создающий представление книги. Корзина покупателя и позиции в ней
представлены, соответственно, классами cart.ShoppingCart и cart.ShoppingCartItem.
Исходный код приложения "книжный магазин" находится в директории
<JWSDP_HOME>/docs/tutorial/examples/web/bookstore1, создаваемой при распаковке
руководства (см. Запуск примеров).
Для построения, установки и запуска примера выполните следующие действия:
1. В окне терминала перейдите в директорию <JWSDP_HOME>/docs/tutorial/exam-
ples/web/bookstore1
2. Запустите ant build. Исполнитель build вызовет все необходимые компиляции и скопирует
файлы в директорию <JWSDP_HOME>/docs/tutorial/examples/web/bookstore1/build
3. Убедитесь, что Tomcat запущен.
4. Запустите ant install. Исполнитель install оповестит Tomcat о появлении нового контекста.
5. Запустите сервер баз данных PointBase и заполните базу данных, если вы этого еще
не сделали (см. Получение доступа к базам данных из Web-приложений).
6. Для запуска приложения откройте URL-адрес http://localhost:8080/bookstore1/enter

Web-

Rendered by www.RenderX.com
Стр. 400 из 626 Технология Java Servlet

Размещение приложения при помощи задачи Ant deploy:


1. Запустите ant package. Задача package создаст WAR-файл, содержащий классы
приложения в директории WEB-INF/classes, а также файл context.xml в директории
META-INF.
2. Убедитесь, что Tomcat запущен.
3. Запустите ant deploy. Исполнитель deployскопирует WAR-файл в Tomcat и оповестит
его о появлении нового контекста.
Размещение приложения при помощи утилиты deploytool:
1. Убедитесь, что Tomcat запущен.
2. Запустите утилиту deploytool.
3. Создайте Web-приложение с названием bookstore1.
4.
A. Выберите File _ New Web Application.
B. Нажмите Browse.
C. В меню выбора файла перейдите в директорию <JWSDP_HOME>/docs/tutorial/exam-
ples/web/bookstore1/build
D. В поле File Name введите bookstore1
E. Нажмите Choose Module File.
F. В поле WAR Display Name введите bookstore1
G. В диалоговом окне Edit Archive Contents перейдите в
директорию<JWSDP_HOME>/docs/tutorial/examples/web/bookstore1/build
H. Выберите файлы errorpage.html и duke.books.gif и нажмите Add.</p></item>
<item><p>Перейдите в директорию WEB-INF/classes и выберите классы
BannerServlet.class, BookStoreServlet.class, BookDetailsServlet.class, Cata-
logServlet.class, ShowCartServlet.class, CashierServlet.class, и ReceiptServlet.class, а
также пакеты cart, database, exception, filters, listeners, messagesи util. Нажмите Add,
а затем OK.
I. Нажмите Next.
J. Выберите переключатель Servlet.
K. Нажмите Next.
L. В комбинированном окне Servlet Class выберите BannerServlet.
M. Нажмите Finish.
5. Добавьте каждый из Web-компонентов, описанных в Таблице 12-2. Для каждого
сервлета:
6.
A. Выберите File _ Edit Web Application.
B. Нажмите переключатель Add to Existing WAR Module. WAR-файл содержит все
классы сервлетов, поэтому вам больше не потребуется добавлять какое-либо
содержимое.

Web-

Rendered by www.RenderX.com
Примеры сервлетов Стр. 401 из 626

C. Нажмите Next.
D. Выберите переключатель Servlet.
E. Нажмите Next.
F. В комбинированном окне Servlet Class выберите сервлет.
G. Нажмите Finish.
H. Таблица 12-2 Web-компоненты Duke's Bookstore
Имя Web-компонента Класс сервлета Псевдоним компонента
BookStoreServlet BookStoreServlet /enter
CatalogServlet CatalogServlet /catalog
BookDetailsServlet BookDetailsServlet /bookdetails
ShowCartServlet ShowCartServlet /showcart
CashierServlet CashierServlet /cashier
ReceiptServlet ReceiptServlet /receipt

7. Добавьте псевдонимы для каждого компонента.


8.
A. Выберите компонент.
B. Выберите закладку Aliases.
C. Нажмите Add а затем введите псевдоним в поле Aliases.
9. Добавьте класс-слушатель listeners.ContextListener (см. Обработка событий жизненного
цикла сервлетов).
10.
A. Выберите закладку Event Listeners.
B. Нажмите Add.
C. Выберите класс listeners.ContextListener из ниспадающего списка в панели Event
Listener Classes.
11. Добавьте страницу ошибки (см. Обработка Ошибок.)
12.
A. Выберите закладку File Refs.
B. Нажмите Add в панели Error Mapping.
C. В поле Error/Exception введите exception.BookNotFoundException
D. В поле Resource to be Called введите /errorpage.html
E. Повторите то же самое для исключений exception.BooksNotFoundException и
javax.servlet.UnavailableException.
13. Добавьте фильтры filters.HitCounterFilter и filters.OrderFilter (см. Фильтрация Запросов
и Ответов).
14.
A. Выберите закладку Filter Mapping.
B. Нажмите Edit Filter List.
C. Нажимте Add.

Web-

Rendered by www.RenderX.com
Стр. 402 из 626 Технология Java Servlet

D. Выберите filters.HitCounterFilter из колонки Filter Class. Утилита deploytool


автиматически введет HitCounterFilter в колонке Display Name.
E. Нажмите Add.
F. Из колонки Filter Class выберите filters.OrderFilter. Утилита deploytool автоматически
введет OrderFilter в колонке Display Name.
G. Нажмите OK.
H. Нажмите Add.
I. Из колонки Filter Name выберите HitCounterFilter.
J. Из колонки Target Type выберите Servlet.
K. Из колонки Target выберите BookStoreServlet.
L. Повторите это для OrderFilter. Где типом цели является Servlet, а целью - Receipt-
Servlet.
15. Добавьте ссылку на ресурс для базы данных.
16.
A. Выберите WAR.
B. Выберите закладку Resource Refs.
C. Нажмите Add.
D. Из колонки Type выберите javax.sql.DataSource
E. В поле Coded Name введите jdbc/BookDB.
F. Нажмите кнопку Import Data Sources.
G. Пропустите диалог подтверждения.
H. Из ниспадающего списка выберите pointbase.
17. Разместите приложение.
18.
A. Выберите Tools _ Deploy.
B. нажмите OK, чтобы выбрать путь контекста /bookstore1, заданный по-умолчанию.
C. Нажмите Finish.

13.2.1. Устранение неисправностей


В разделе Общие проблемы и их решения приведено несколько причин, по которым Web-
клиент может выйти из строя. Кроме того, Duke's Bookstore возвращает следующие
сообщения об ошибках:
• BookNotFoundException- возвращается, если книга не может быть найдена в базе данных.
Это происходит, если вы не загрузили базу данных путем запуска комманды ant create-
book-db или если сервер базы данных не запущен или неисправен.
• BooksNotFoundException- возвращается, если данные "книжного магазина" не могут
быть получены. Это происходит, если вы не загрузили базу данных путем запуска
комманды ant create-book-db или если сервер базы данных не запущен или неисправен.

Web-

Rendered by www.RenderX.com
Жизненный цикл сервлета Стр. 403 из 626

• UnavailableException- возвращается, если сервлет не может извлечь атрибут Web-


контекста, представляющий "книжный магазин". Данное событие может произойти в
трех случаях: (1) если вы не скопировали библиотеку PointBase, находящуюся в файле
<PB_HOME>/lib/pbclient42.jar, в директорию <JWSDP_HOME>/common/lib, (2) если
сервер PointBase не загружен или (3) если вы не определили в Tomcat источник, который
ссылается на базу данных PointBase (см. Определение источника данных в Tomcat).
Вследствие того, что мы уже определили страницу ошибок, вы увидите сообщение "The
application is unavailable. Please try later". Если страница ошибок не определена, Web-
контейнер по-умолчанию создаст страницу, содержащую сообщение "A Servlet Exception
Has Occurred"и содержимое стека, что поможет диагностировать причину ошибки. Если
вы используете страницу errorpage.html, то для определения причины ошибки необходимо
будет просмотреть log-файл Web-контейнера. Log-файлы содержатся в директории
JWSDP_HOME/logs и называются jwsdp_log.<date>.txt.

13.3. Жизненный цикл сервлета


Жизненный цикл сервлета управляется контейнером, в котором был размещен сервлет.
При отображении запроса сервлету, контейнер выполняет следующие действия:
1. Если экземпляра сервлета не существеует, Web-контейнер
2.
A. Загружает класс сервлета.
B. Создает экземпляр класса сервлета.
C. Инициализирует экземпляр сервлета путем вызова метода init. Инициализация
рассматривается в разделе Инициализация сервлета.
3. Вызывает метод service, передавая объект запроса и ответа. Методы служб
обсуждаются в разделе Написание методов служб.
Если контейнеру необходимо удалить сервлет, он завершает работу сервлета путем вызова
его метода destroy. Данная тема обсуждается в разделе Завершение работы сервлета.

13.3.1. Обработка событий жизненного цикла сервлетов


Отслеживание и реагировать на события жизненного цикла сервлетов можно, путем
определения объектов-слушателей. Когда в жизненном цикле сервлета происходит событие,
вызываются методы объектов-слушателей. Для их использования необходимо определить
класс-слушатель.

13.3.1.1. Определение класса-слушателя


Класс-слушатель определяется, как реализация интерфейса слушателя. В разделе События
жизненного цикла сервлето в перечислены события, которые могут быть отслежены, а
также соответствующий интерфейс, который необходимо реализовать. При вызове метода
listener, он передает событие, содержащее информацию, соответствующую данному
событию. К примеру, методы в интерфейсе HttpSessionListene r передаются событию
HttpSessionEvent, содержащему HttpSession.
Таблица 12-3 События жизненного цикла сервлетов

Web-

Rendered by www.RenderX.com
Стр. 404 из 626 Технология Java Servlet

Объект Событие Интерфейс слушателя и класс события


Web-контекст (см. Получение доступа к Инициализация и разрушение Javax.servlet.ServletContextListener и
Web-контексту) ServletContextEvent
Добавление, удаление или заменена Javax.servlet.ServletContextAttributeLis-
атрибута tener и ServletContextAttributeEvent
Сессия (см. Поддерживание состояния Создание, аннулирование и тайм-аут javax.servlet.http.HttpSessionListener и
клиента) HttpSessionEvent
Добавление, удаление или заменена javax.servlet.http.HttpSessionAttributeLis-
атрибута tener и HttpSessionBindingEvent

Класс listeners.ContextListener создает и удаляет вспомогательный объект базы данных и


объект-счетчик, используемые в приложении Duke's Bookstore. Методы извлекают объект
Web-контекста из события ServletContextEvent после чего хранят (и удаляют) объекты как
аттрибуты контекста сервлета.

import database.BookDB;
import javax.servlet.*;
import util.Counter;

public final class ContextListener


implements ServletContextListener {
private ServletContext context = null;
public void contextInitialized(ServletContextEvent event) {
context = event.getServletContext();
try {
BookDB bookDB = new BookDB();
context.setAttribute("bookDB", bookDB);
} catch (Exception ex) {
System.out.println(
"Couldn't create database: "
+ ex.getMessage());
}
Counter counter = new Counter();
context.setAttribute("hitCounter", counter);
context.log("Created hitCounter"
+ counter.getCounter());
counter = new Counter();
context.setAttribute("orderCounter", counter);
context.log("Created orderCounter"
+ counter.getCounter());
}

public void contextDestroyed(ServletContextEvent event) {


context = event.getServletContext();

Web-

Rendered by www.RenderX.com
Жизненный цикл сервлета Стр. 405 из 626

BookDB bookDB = context.getAttribute(


"bookDB");
bookDB.remove();
context.removeAttribute("bookDB");
context.removeAttribute("hitCounter");
context.removeAttribute("orderCounter");
}
}

13.3.1.2. Определение классов-слушателей событий


Для определения класса-слушателя события добавьте элемент listener в дескриптор
размещения Web-приложения. Далее представлен элемент listener для приложения Duke's
Bookstore:

<listener>
<listener-class>listeners.ContextListener</listener-class>
</listener>
Определите класс-слушатель для WAR-файла при помощи инспектора Event Listeners в
утилите deploytool (см. Слушатели событий).

13.3.2. Обработка ошибок


При запуске сервлета возможно появление некоторого количества ошибок. В случае ошибки
Web-контейнер создает страницу, определенную по-умолчанию, в которой содержится
сообщение "A Servlet Exception Has Occurred". Однако вы можете также определить, чтобы
контейнер возвращал специальную страницу ошибок для конкретной исключительной
ситуации. Для определения такой страницы, добавьте элемент error-page в дескриптор
размещения Web-приложения. Данные элементы отображают исключительные ситуации,
возвращенные приложением Duke's Bookstore в страницу errorpage.html:

<error-page>
<exception-type>
exception.BookNotFoundException
</exception-type>
<location>/errorpage.html</location>
</error-page>
<error-page>
<exception-type>
exception.BooksNotFoundException
</exception-type>
<location>/errorpage.html</location>
</error-page>
<error-page>

Web-

Rendered by www.RenderX.com
Стр. 406 из 626 Технология Java Servlet

<exception-type>exception.OrderException</exception-type>
<location>/errorpage.html</location>
</error-page>
Определите страницы ошибок для WAR-файла в инспекторе File Refs утилиты deploytool
(см. Отображения ошибок).

13.4. Использование совместного доступа к информации


Web-компоненты для выполнения своих задач часто используют другие объекты. Для этого
существует несколько способов. Можно использовать частные вспомогательные объекты
(к примеру, JavaBean-компоненты), или же совместно использовать объекты, являющиеся
атрибутами области действия public. Web-компоненты могут также использовать базу
данных и вызывать другие Web-ресурсы. Механизмы технологии Java Servlet, позволяющие
Web-компонентам вызывать другие Web-ресурсы, описаны в разделе Вызов других Web-
ресурсов.

13.4.1. Использования объектов области действия


Взаимодействующие Web-компоненты используют общую информацию посредством
применения объектов, поддерживаемых как атрибуты объектов четырех областей действия.
Доступ к этим атрибутам производится при помощи методов [get|set]Attribut e класса,
представляющего область действия. В Таблице 12-4 перечислены объекты области
действия.
Таблица 12-4 Объекты области действия
Объект области действия Класс Откуда доступен
Web context (Web-контекст) javax.servlet.ServletContext Web-компонент внутри Web-контекста.
См. Получение доступа к Web-контексту.
Session (Сессия) javax.servlet.http.HttpSession Web-компоненты, обрабатывающие
запрос, относящийся к сессии. См.
Поддержание состояния клиента.
Request (Запрос) Подтип javax.servlet.ServletRequest Web-компоненты, обрабатывающие
запрос.
Page (Страница) javax.servlet.jsp.PageContext JSP-страница, создающая объект. См.
Неявные объекты.

На Рисунке 12-1 показаны атрибуты области действия, поддерживаемые приложением


Duke's Bookstore.

Web-

Rendered by www.RenderX.com
Использование совместного доступа к информации Стр. 407 из 626

Рисунок 12-1 Атрибуты области действия, используемые в приложении Duke's Bookstore

13.4.2. Управление параллельным доступом к общим ресурсам


В многопоточном сервере возможен совместный доступ к общим ресурсам. Кроме атрибутов
объекта области действия, общие ресурсы включают в себя данные, находящиеся в памяти,
такие как переменная экземпляра или переменные класса, а также внешние объекты,
такие как файлы, соединения с базами данных и сетевые соединения. Совместный доступ
может осуществляться в различных ситуациях:
• Множество Web-компонентов, получающих доступ к объектам, хранящимся в Web-
контексте
• Множество Web-компонентов, получающих доступ к объектам, хранящимся в сессии
• Множество потоков внутри Web-компонента, получающих доступ к переменным
экземпляров. Web-контейнер, обычно создает поток для обработки каждого запроса.
Для гарантии того, что экземпляр сервлета обрабатывает одновременно только один
запрос, сервлет может реализовывать интерфейс SingleThreadModel. Если сервлет
реализует данный интерфейс, то гарантируется, в методе службы сервлета
одновременно будет выполняться не более одного потока. В Web-контейнере это
реализуется путем синхронизации доступа к отдельному экземпляру сервлета или
путем поддержания пула экземпляров Web-компонентов и перенаправления каждого
нового запроса к свободному экземпляру. Данный интерфейс не ограждает от проблем
связанных с синхронизацией, которые возникают в результате доступа Web-компонентов
к общим ресурсам, таким как статические переменные класса или к внешние объекты.
При совместном доступе к ресурсам, они могут быть использованы в противоречащих
действиях. Для того чтобы избежать данной ситуации, необходимо контролировать доступ,
используя методы синхронизации, описанные в уроке Потоки руководства The Java Tutorial
.
В предыдущем разделе мы представили пять атрибутов области действия, являющихся
общими для более чем одного сервлета: bookDB, cart, currency, hitCounter и orderCounter.
Атрибут bookDB обсуждается в следующем разделе. Атрибуты cart, currency и counters
могут быть установлены и считаны многочисленными многопоточными сервлетами. Для
предотвращения возникновения противоречий при использовании данных объектов доступ
контролируется при помощи синхронизированных методов. Примером может служить
класс util.Counter:

Web-

Rendered by www.RenderX.com
Стр. 408 из 626 Технология Java Servlet

public class Counter {


private int counter;
public Counter() {
counter = 0;
}
public synchronized int getCounter() {
return counter;
}
public synchronized int setCounter(int c) {
counter = c;
return counter;
}
public synchronized int incCounter() {
return(++counter);
}
}

13.4.3. Доступ к базам данных


Данные, которые являются общими для Web-компонентов и сохраняются между вызовами
Web-приложения, обычно поддерживаются базой данных. Web-компоненты используют
JDBC 2.0 API для доступа к реляционным базам данных. Данные приложения "книжный
магазин" содержатся в базе данных, причем доступ к ним осуществляется при помощи
вспомогательного класса database.BookDB. К примеру, когда пользователь совершает
покупку, сервлет ReceiptServlet вызывает метод BookDB.buyBooks с целью обновления
хранилища. Метод buyBooks вызывает buyBook для каждой из книг, содержащихся в
корзине покупателя. Для уверенности в том, что заказ обрабатыватся полноценно, вызов
к buyBook должен быть "завернут" в одну транзакцию JDBC. Использование совместного
соединения с базой данных синхронизируется посредством методов [get|release]Connection.

public void buyBooks(ShoppingCart cart) throws OrderException {


Collection items = cart.getItems();
Iterator i = items.iterator();
try {
getConnection();
con.setAutoCommit(false);
while (i.hasNext()) {
ShoppingCartItem sci = (ShoppingCartItem)i.next();
BookDetails bd = (BookDetails)sci.getItem();
String id = bd.getBookId();
int quantity = sci.getQuantity();
buyBook(id, quantity);
}

Web-

Rendered by www.RenderX.com
Инициализация сервлета Стр. 409 из 626

con.commit();
con.setAutoCommit(true);
releaseConnection();
} catch (Exception ex) {
try {
con.rollback();
releaseConnection();
throw new OrderException("Transaction failed: " +
ex.getMessage());
} catch (SQLException sqx) {
releaseConnection();
throw new OrderException("Rollback failed: " +
sqx.getMessage());
}
}
}

13.5. Инициализация сервлета


Web-контейнер инициализирует сервлет после того, как он загружает и создает экземпляр
класса сервлета и перед доставкой запросов от клиентов. Вы можете самостоятельно
настроить данный процесс, позволив сервлету считывать постоянные данные конфигурации,
а также инициализировать ресурсы и обеспечивать любые другие единовременные действия
путем переопределения метода init интерфейса Servlet. Сервлет, который не в состоянии
завершить процесс инициализации, должен возбуждать ошибку UnavailableException.
Все сервлеты, получающие доступ к базе данных "книжного магазина" (BookStoreServlet,
CatalogServlet, BookDetailsServlet и ShowCartServlet) инициализируют переменную в своем
методе init, которая указывает на вспомогательный объект базы данных, созданный
слушателем Web-контекста:

public class CatalogServlet extends HttpServlet {


private BookDB bookDB;
public void init() throws ServletException {
bookDB = (BookDB)getServletContext().
getAttribute("bookDB");
if (bookDB == null) throw new
UnavailableException("Couldn't get database.");
}
}

Web-

Rendered by www.RenderX.com
Стр. 410 из 626 Технология Java Servlet

13.6. Написание методов служб


Служба, обеспеченная сервлетом, реализована в методе service сервлета GenericServlet,
методе doMethod (где Method может принимать значения Get, Delete, Options, Post, Put,
Trace) сервлета HttpServlet или любых других, определенных протоколом методов, которые
определены классом, реализующим интерфейс Servlet. В конце данного раздела, термин
метод службы будет использован для любого метода класса сервлета, обеспечивающего
службу клиенту.
Общим принципом действия метода службы является извлечение информации из запроса,
доступ к внешним ресурсам и последующее заполнение ответа, базирующегося на данной
информации.
Для сервлетов HTTP, корректной процедурой заполнения ответа будет следующая
последовательность действий: заполнение заголовков ответа, получение входящего потока
из ответа и запись содержимого тела в исходящий поток. Заголовки ответов всегда должны
устанавливаться перед получением PrintWriter или ServletOutputStream. Это необходимо
по причине того, что HTTP-протокол ожидает получения всех заголовков перед перед
получением содержимого тела. В следующих двух разделах описывается, как получать
информацию из запросов и создать ответы.

13.6.1. Получение информации из запросов


Запрос содержит данные, передаваемые между клиентом и сервлетом. Все запросы
реализуют интерфейс ServletRequest. Данный интерфейс определяет методы для доступа
к следующей информации:
• Параметрам, которые обычно используются для передачи информации между клиентами
и сервлетами
• Объектно-значимым атрибутам, которые обычно используются для передачи
информации между контейнером сервлета и сервлетом, или между взаимодействующими
сервлетами
• Информации о протоколе, использованном для соединения, а также о клиенте и о
сервере, которые вовлечены в запрос
• Информации, относящейся к локализации
К примеру, в сервлете CatalogServlet идентификатор книги, которую желает приобрести
покупатель, включен как параметр запроса. Следующий фрагмент кода иллюстрирует
использование метода getParameter для извлечения данного идентификатора:

String bookId = request.getParameter("Add");


if (bookId != null) {
BookDetails book = bookDB.getBookDetails(bookId);
Входящий поток можно также получать из запроса, а затем вручную обрабатывать данные.
Для чтения символьных данных необходимо использовать объект BufferedReader,
возвращенный методом запроса getReader. Для чтения бинарных данных используется
ServletInputStream, возвращенный getInputStream.

Web-

Rendered by www.RenderX.com
Написание методов служб Стр. 411 из 626

HTTP-сервлеты передаются объекту запроса HTTP HttpServletRequest, содержащему URL


запроса, HTTP-заголовки, строку запроса и т.д.
URL запроса HTTP состоит из следующих частей:

http://[host]:[port][request path]?[query string]


Путь запроса, в свою очередь, состоит из следующих элементов:
• Путь контекста: сочетание косой черты / с корнем контекста Web-приложения сервлета.
• Путь сервлета: участок пути, соответствущий псевдониму компонента, который
активирует данный запрос. Этот участок начинаеся с косой черты /.
• Информация о пути: участок пути запроса, не являющийся частью пути контекста или
пути сервлета.
Для тех случаев, когда путем контекста является /catalo g, а также для псевдонимов из
Таблицы 12-5, в Таблице 12-6 представлены примеры разбиения URL на составные части.
Таблица 12-5 Псевдонимы
Шаблон Сервлет
/lawn/* LawnServlet
/*.jsp JSPServlet

Таблица 12-6 Элементы пути запроса


Путь запроса Путь сервлета Информация о пути
/catalog/lawn/index.html /lawn /index.html
/catalog/help/feedback.jsp /help/feedback.jsp null

Строки запросов состоят из набора параметров и значений. Индивидуальные параметры


получены из запроса при помощи метода getParameter. Существует два собсоба создания
строк запроса:
• Строка запроса может появляться явным образом в Web-странице. К примеру, HTML-
страница, созданная сервлетом CatalogServlet может содержать ссылку <a
href="/bookstore1/catalog?Add=101">Add To Cart</a>. Сервлет CatalogServlet извлекает
параметр с именем Add следующим образом:

String bookId = request.getParameter("Add");


• Строка запроса добавляется к URL при подтверждении формы с HTTP-методом GET.
В приложении Duke's Bookstore сервлет CashierServlet генерирует форму, затем
введенное в форме имя пользователя при помощи URL отображается сервлету
ReceiptServlet и, наконец, ReceiptServlet извлекает имя пользователя при помощи метода
getParameter.

Web-

Rendered by www.RenderX.com
Стр. 412 из 626 Технология Java Servlet

13.6.2. Построение ответов


Ответ содержит данные, передаваемые между сервером и клиентом. Все ответы реализуют
интерфейс ServletResponse. Данный интерфейс определяет методы, которые позволяют
вам выполнять следующие действия:
• Получать исходящий поток, используемый в отправке данных клиенту. Для отправки
данных в текстовом виде используйте PrintWriter, возвращенный методом ответа
getWriter. Для отправки бинарных данных в теле MIME ответа, используйте ServletOut-
putStream, возвращенный getOutputStream. Для совмещения бинарных и текстовых
данных, к примеру, при создании ответа, сотоящего из нескольких частей, используйте
ServletOutputStream, управляя секциями символов вручную.
• Обозначать тип содержимого (к примеру, text/html), возвращаемого в данном ответе.
Реестр имен типов содержимого организация IANA (Internet Assigned Numbers Authority
- Агентство по выделению имен и уникальных параметров протоколов Internet) хранит
по адресу: ftp://ftp.isi.edu/in-notes/iana/assignments/media-types
• Указывать, осуществляется ли вывод в буфер. По-умолчанию, любое содержимое,
записываемое в исходящий поток, немедленно отправляется клиенту. Буферизация
позволяет записывать содержимое перед отправкой клиенту. Таким образом, сервлету
предоставляется больше времени для установки соответствующих кодов статуса и
заголовков, а также переотправки содержимого другому Web-ресурсу.
• Устанавливать информацию о регионе.
• Объекты HTTP-ответа HttpServletResponse имеют поля, отображающие такие заголовки
HTTP как
• Коды статуса (Status codes), используются для обозначения причины, по которой не
был удовлетворен запрос.
• Файлы сookie (Cookies), используются для сохранения у клиента информации, связанной
с приложением. Иногда файлы cookie используются для сохранения идентификатора
сессии пользователя (см. Отслеживание сессии).
В приложении Duke's Bookstore, сервлет BookDetailsServlet генерирует HTML-страницу,
отображающую информацию о книге, которую сервлет извлекает из базы данных. Вначале
сервлет устанавливает заголовки ответа: тип его содержимого и размер буфера. Сервлет
буферизирует содержимое страницы. Это необходимо для того, чтобы сохранить
содержимое страницы, если доступ к базе данных вызвал ошибку с последующим
отображением станицы ошибок. В случае возникновения ошибки клиенту отобразиться
только страница ошибок, при этом содержимое предыдущей страницы будет сохранено в
буфере. После этого метод doGet извлечет PrintWriter из ответа.
Для заполнения ответа, сервлет перенаправляет запрос к BannerServlet, который генерирует
общую шапку для всех сервлетов приложения. Данный процесс обсуждается в разделе
Включение других ресурсов в ответ. Затем сервлет извлекает идентификатор книги из
параметра запроса и использует его для извлечения информации о книге из базы данных.
И наконец, сервлет генерирует разметку HTML-страницы, в которой описана информация
о книге, и передает ответ клиенту, вызывая метод close в PrintWriter.

public class BookDetailsServlet extends HttpServlet {

Web-

Rendered by www.RenderX.com
Написание методов служб Стр. 413 из 626

public void doGet (HttpServletRequest request,


HttpServletResponse response)
throws ServletException, IOException {
//
Writer
response.setContentType("text/html");
response.setBufferSize(8192);
PrintWriter out = response.getWriter();

//
out.println("<html>" +
"<head><title>+
messages.getString("TitleBookDescription")
+</title></head>");

// dispatcher,

RequestDispatcher dispatcher =
getServletContext().
getRequestDispatcher("/banner");
if (dispatcher != null)
dispatcher.include(request, response);

//

String bookId = request.getParameter("bookId");


if (bookId != null) {
// try {
BookDetails bd =
bookDB.getBookDetails(bookId);
...
//
out.println("<h2>" + bd.getTitle() + "</h2>" +
...
} catch (BookNotFoundException ex) {
response.resetBuffer();
throw new ServletException(ex);
}
}
out.println("</body></html>");

Web-

Rendered by www.RenderX.com
Стр. 414 из 626 Технология Java Servlet

out.close();
}
}
Сервлет BookDetailsServlet генерирует страницу, которая выглядит следующим образом:

Рисунок 12-2 Подробная информация о книге

13.7. Фильтрация запросов и ответов


Фильтр - это объект, который может преобразовывать заголовок и содержимое (или оба
из них) запроса и ответа. Фильтры отличаются от Web-компонентов тем, что они обычно
сами не создают ответ. Вместо этого, фильтр обеспечивает функциональные возможности,
который могут быть присоединены к Web-ресурсу любого рода. Как следствие, фильтр не
должен зависеть от Web-ресурса, для которого он действует как фильтр. То есть, он может
быть компонуем с более чем одним типом Web-ресурса. Основными задачами, которые
может выполнять фильтр, являются:
• Соответствующий опрос запросов и действий
• Блокировка любой дальнейшей передачи запросов и ответов.
• Изменение заголовков и данных запроса. Это можно сделать, обеспечив заказную
версию запроса.
• Изменение заголовков и данных ответа. Это можно сделать, обеспечив заказную версию
ответа.
• Взаимодействие с внешними ресурсами.

Web-

Rendered by www.RenderX.com
Фильтрация запросов и ответов Стр. 415 из 626

Приложения фильтров включают в себя аутентификацию, запись показаний, конвертацию


изображений, сжатие данных, шифрование, преобразование потоков в символы, XML-
преобразования и т.д.
К Web-ресурсу можно применить цепочку из нуля, одного или более фильтров, заданных
в определенном порядке. Данная цепочка задается во время размещения Web-приложения,
содержащего компонент. Когда Web-контейнер загружает компонент, создается экземпляр
данной цепочки.
Задачи, связанные с применением фильтров включают в себя следующее:
1. Программирование фильтра
2. Программирование заказных запросов и ответов
3. Определение цепочки фильтров для каждого Web-ресурса

13.7.1. Програмирование фильтров


API фильтрования определяется интерфейсами Filter, FilterChain и FilterConfig в пакете
javax.servlet. Фильтр определяется путем реализации интерфейса Filter. Наиболее важным
методом в данном интерфейсе является метод doFilter, в котором передаются объекты
запроса, ответа и цепочки фильтров. Данный метод может выполнять следующие действия:
1. Изучать заголовки запросов.
2. Настраивать объект запроса, если тот желает модифицировать заголовки или данные
запроса.
3. Настраивать объект ответа, если тот желает модифицировать заголовки или данные
ответа.
4. Вызывать следующий элемент из цепочки фильтров. Если текущий фильтр является
последним в цепочке фильтров, заканчивающейся на целевом Web-компоненте или
статическом ресурсе, следующим элементом цепочки будет ресурс в конце цепочки
или же он будет, следующим фильтром, настроенным в WAR. Он вызывает следующий
элемент цепочки путем вызова метода doFilter для объекта цепочки. С другой стороны,
он может предпочесть блокировку запроса, запретив вызов следующего элемента
цепочки. В последнем случае, фильтр несет ответственность за заполнение ответа.
5. Изучать заголовки ответа после вызова следующего фильтра в цепочке
6. Возбуждать исключение для индикации ошибки в обработке
Кроме doFilter, необходимо реализовать методы init и destroy. Метод init вызывается
контейнером после создания экземпляра фильтра. Если вы желаете передать фильтру
параметры инициализации, получите их из объекта FilterConfig, передаваемого методу
init.
Приложение Duke's Bookstore использует фильтры HitCounterFilter и OrderFilter для
приращения и отслеживания значения счетчика во время получения доступа к сервлетам
entry и receipt.
В методе doFilter оба фильтра извлекают контекст сервлета из объекта конфигурации
фильтра так, что они могут получать доступ к счетчикам, хранящимся как атрибуты
контекста. После того, как фильтры завершают обработку, связанную с приложением, они

Web-

Rendered by www.RenderX.com
Стр. 416 из 626 Технология Java Servlet

вызывают метод doFilter в объекте цепочки фильтров, передаваемом в оригинальный


метод doFilter. Часть кода была опущена, она обсуждается в следующем разделе.

public final class HitCounterFilter implements Filter {


private FilterConfig filterConfig = null;

public void init(FilterConfig filterConfig)


throws ServletException {
this.filterConfig = filterConfig;
}
public void destroy() {
this.filterConfig = null;
}
public void doFilter(ServletRequest request,
ServletResponse response, FilterChain chain)
throws IOException, ServletException {
if (filterConfig == null)
return;
StringWriter sw = new StringWriter();
PrintWriter writer = new PrintWriter(sw);
Counter counter = (Counter)filterConfig.
getServletContext().
getAttribute("hitCounter");
writer.println();
writer.println("===============");
writer.println("The number of hits is: " +
counter.incCounter());
writer.println("===============");
//
writer.flush();
filterConfig.getServletContext().
log(sw.getBuffer().toString());
...
chain.doFilter(request, wrapper);
...
}
}

13.7.2. Программирование заказных запросов и ответов


Фильтр обеспечивает множество способов для модификации запроса и ответа. К примеру,
он может добавлять атрибут к запросу или вставлять данные в ответ. В примере Duke's
Bookstore фильтр HitCounterFilter вставляет значение счетчика в ответ.

Web-

Rendered by www.RenderX.com
Фильтрация запросов и ответов Стр. 417 из 626

Фильтр, модифицирующий ответ, обычно перехватывает ответ до его возвращения клиенту.


Для этого необходимо передать дублированый поток сервлету, генерирующему ответ.
Дублированный поток запрещает сервлету закрывать первоначальный потока запроса,
когда тот завершен, и позволяет фильтру модифицировать ответ сервлета.
Для передачи сервлету дублированного потока, фильтр создает упаковщик ответа, который
переопределяет метод getWriter или getOutputStream для возвращения данного
дублированного потока. Упаковщик передается методу doFilter цепочки фильтров. Методы
упаковщика (по-умолчанию) вызываются через упакованные объекты запроса и ответа.
Данный подход соответствует общеизвестной модели Wrapper or Decorator, описанной в
книге Design Patterns, Elements of Reusable Object-Oriented Software (Addison-Wesley, 1995).
В следующих разделах описано использование упаковщика фильтров счетчиком посещений
и другими типами фильтров.
Для переопределения мотодов запроса, запрос упаковывается в объект, который расширяет
ServletRequestWrapper или HttpServletRequestWrapper. Для переопределения методов
ответа, ответ упаковывается в объект, который расширяет ServletResponseWrapper или
HttpServletResponseWrapper.
HitCounterFilter упаковывает ответ в CharResponseWrapper. Упакованный ответ передается
следующему объекту в цепочке фильтров, которым является BookStoreServlet. Сервлет
BookStoreServlet записывает свой ответ в поток, созданный CharResponseWrapper. Когда
возвращается chain.doFilter, фильтр HitCounterFilter извлекает ответ сервлета из PrintWriter
и записывает его в буфер. Фильтр вставляет значение счетчика в буфер, сбрасывает
содержимое заголовка ответа, после чего записывает содержимое буфера в поток ответа.

PrintWriter out = response.getWriter();


CharResponseWrapper wrapper = new CharResponseWrapper(
(HttpServletResponse)response);
chain.doFilter(request, wrapper);
CharArrayWriter caw = new CharArrayWriter();
caw.write(wrapper.toString().substring(0,
wrapper.toString().indexOf("</body>")-1));
caw.write("<p>\n<center>" +
messages.getString("Visitor") + "<font color='red'>" +
counter.getCounter() + "</font></center>");
caw.write("\n</body></html>");
response.setContentLength(caw.toString().length());
out.write(caw.toString());
out.close();

public class CharResponseWrapper extends


HttpServletResponseWrapper {
private CharArrayWriter output;
public String toString() {
return output.toString();

Web-

Rendered by www.RenderX.com
Стр. 418 из 626 Технология Java Servlet

}
public CharResponseWrapper(HttpServletResponse response){
super(response);
output = new CharArrayWriter();
}
public PrintWriter getWriter(){
return new PrintWriter(output);
}
}
На Рисунке 12-3 показана вступительная страница Duke's Bookstore со счетчиком
посещений.

Рисунок 12-3 Приложение Duke's Bookstore

13.7.3. Задание отображений фильтров


Web-контейнер использует отображения фильтров для определения того, как применять
фильтры к Web-ресурсам. Отображение фильтров сравнивает фильтр с Web-компонентом
по имени или с Web-ресурсом по его URL-структуре. Фильтры вызываются в том порядке,
в котором отображения фильтров появляются в списке отображений фильтров WAR-файла.
Определить список отображений фильтров для WAR-файла можно в инспекторе Filter
Mapping утилиты deploytool (см. Отображения фильтров) или путем кодирования их
напрямую в дескрипторе размещения Web-приложения:
• Обьявите фильтр, используя элемент <filter>. Данный элемент создает имя для фильтра
и обьявляет класс реализации фильтра, а также параметры инициализации.

Web-

Rendered by www.RenderX.com
Фильтрация запросов и ответов Стр. 419 из 626

• Отобразите фильтр на Web-ресурс путем определения элемента <filter-mapping>.


Данный элемент отображает имя фильтра в Web-ресурс при помощи имени или его
URL-структуры.
Следующие элементы показывают, как определить фильтры счетчика посещений и заказа.
Для определения фильтра вы должны предоставить ему имя, класс, который реализует
фильтр и, при необходимости, некоторые параметры инициализации.

<filter>
<filter-name>OrderFilter</filter-name>
<filter-class>filters.OrderFilter</filter-class>
</filter>
<filter>
<filter-name>HitCounterFilter</filter-name>
<filter-class>filters.HitCounterFilter</filter-class>
</filter>
Элемент filter-mapping отображает фильтр заказа в URL-адрес/receipt. Отображение могло
также определить сервлет ReceiptServlet. Обратите внимание, что элементы filter, filter-
mapping, servlet и servlet-mapping должны появляться в дескрипторе размещения Web-
приложения в следующем порядке.

<filter-mapping>
<filter-name>OrderFilter</filter-name>
<url-pattern>/receipt</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>HitCounterFilter</filter-name>
<url-pattern>/enter</url-pattern>
</filter-mapping>
Если вы желаете вести журнал запросов к Web-приложению, необходимо отобразить
фильтр счетчика посещений в URL-структуру /*. В Таблице 12-7 представлен список
отображений фильтров приложения Duke's Bookstore. Фильтры совпадают по URL-структуре,
а каждая цепочка фильтров содержит только один фильтр.
Таблица 12-7 Список отображения фильтров приложения Duke's Bookstore
URL Фильтр
/enter HitCounterFilter
/receipt OrderFilter

Фильтр можно отобразить на один или более Web-ресурсов. Можно также отобразить
более одного фильтра на один Web-ресурс. Данный процесс проиллюстрирован на Рисунке
12-4, где фильтр F1 отображается на сервлеты S1, S2 и S3, Фильтр F2 отображается на
сервлет S2, а фильтр F3 отображается на сервлеты S1 и S2.

Web-

Rendered by www.RenderX.com
Стр. 420 из 626 Технология Java Servlet

Рисунок 12-4 Отображение фильтров на сервлеты

Вспомните, что цепочка фильтров является одним из объектов, передаваемых методу


doFilter фильтра. Цепочка формируется косвенно посредством отображений фильтров.
Порядок расположения фильтров в цепочке совпадает с порядком отображения их в
дескрипторе размещения Web-приложения.
При отображении фильтра на сервлет S1, Web-контейнер вызывает метод doFilter фильтра
F1. Метод doFilter каждого фильтра в цепочке фильтров S1 вызывается через
предшествующий фильтр в цепочке посредством метода chain.doFilter. Цепочка фильтров
S1 будет содержать фильтры F1 и F3, поэтому вызов F1 метода chain.doFilter приведет к
вызову метода doFilter фильтра F3. После завершения метода doFilter фильтра F3,
управление возвращается к методу doFilter фильтра F1.

13.8. Вызов других Web-ресурсов


Web-компоненты могут вызывать другие Web-ресурсы двумя способами: напрямую и
косвенно. При косвенном вызове Web-ресурса, Web-компонент встраивает в содержимое,
возвращаемое клиенту, URL-адрес, указывающий на другой Web-компонент. В приложении
Duke's Bookstore большинство Web-компонентов содержит встроенный URL, указывающий
на другие Web-компоненты. К примеру, сервлет ShowCartServlet косвенным образом
вызывает сервлет CatalogServlet используя встроенный URL-адрес /bookstore1/catalog.
Web-компонент может вызывать другой ресурс напрямую, пока тот выполняется. Для
выполнения данной задачи существуют две способа: включение содержимого другого
ресурса или отправка запроса другому ресурсу.
Для того чтобы вызвать ресурс, доступный на сервере, на котором запускается Web-
компонент, вначале вы должны получить объект RequestDispatcher, используя для этого
метод getRequestDispatcher("URL").
Объект RequestDispatcher можно получить или из запроса, или из Web-контекста. Однако
поведение обоих методов различно. Метод получает путь к запрашиваемому ресурсу в
качестве аргумента. Запрос может получать относительный путь (то есть, не начинающийся
с /), между тем, Web-контекст требует предоставления ему абсолютного пути. Если ресурс
недоступен или, если сервер не реализовал объект RequestDispatcher для данного типа

Web-

Rendered by www.RenderX.com
Вызов других Web-ресурсов Стр. 421 из 626

ресурса, getRequestDispatcher возвратит в качестве значение нуль. Вам следует подготовить


свой сервлет к подобным ситуациям.

13.8.1. Включение других ресурсов в ответ


Иногда необходимо включить в ответ, полученный от Web-компонента, другой Web-ресурс.
К примеру, содержимое шапки или информацию об авторских правах. Для того чтобы
включить другой ресурс, вызовите метод include объекта RequestDispatcher:

include(request, response);
Если ресурс является статическим, метод include позволяет использовать програмный
процесс включения ресурсов на стороне сервера. Если ресурс является Web-компонентом,
включаемому Web-компоненту отправляется запрос, затем Web-компонент выполненяется,
после чего результат запуска будет включен в ответ. Включенный Web-компонент имеет
доступ к объекту запроса, однако он ограничен в своих действиях относительно объекта
ответа:
• Он может производить запись в тело ответа и фиксировать его.
• Он не может устанавливать заголовки или вызывать любые методы (к примеру,
setCookie), влияющие на заголовки ответа.
Шапка приложения Duke's Bookstore генерируется при помощи сервлета BannerServlet.
Обратите внимание, что реализуются оба метода doGet и doPost, так как сервлет
BannerServlet может быть отправлен из любого метода вызываюшего сервлета.

public class BannerServlet extends HttpServlet {


public void doGet (HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {

PrintWriter out = response.getWriter();


out.println("<body bgcolor=\"#ffffff\">" +
"<center>" + "<hr> <br> &nbsp;" + "<h1>" +
"<font size=\"+3\" color=\"#CC0066\">Duke's </font>" +
<img src=\"" + request.getContextPath() +
"/duke.books.gif\">" +
"<font size=\"+3\" color=\"black\">Bookstore</font>" +
"</h1>" + "</center>" + "<br> &nbsp; <hr> <br> ");
}
public void doPost (HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {

PrintWriter out = response.getWriter();


out.println("<body bgcolor=\"#ffffff\">" +

Web-

Rendered by www.RenderX.com
Стр. 422 из 626 Технология Java Servlet

"<center>" + "<hr> <br> &nbsp;" + "<h1>" +


"<font size=\"+3\" color=\"#CC0066\">Duke's </font>" +
<img src=\"" + request.getContextPath() +
"/duke.books.gif\">" +
"<font size=\"+3\" color=\"black\">Bookstore</font>" +
"</h1>" + "</center>" + "<br> &nbsp; <hr> <br> ");
}
}
Каждый сервлет приложения Duke's Bookstore включает в себя результат выполнения
сервлета BannerServlet при помощи следующего кода:

RequestDispatcher dispatcher =
getServletContext().getRequestDispatcher("/banner");
if (dispatcher != null)
dispatcher.include(request, response);
}

13.8.2. Передача управления другому Web-компоненту


В некоторых приложениях, вам может потребоваться, чтобы один Web-компонент совершал
предварительную обработку запроса, а другой - генерировал ответ. К примеру, вы можете
частично обработать запрос, а затем передать его другому компоненту, в зависимости от
природы запроса.
Для передачи управления другому Web-компоненту, вызовите метод forward объекта
RequestDispatcher. При перенаправлении запроса в качестве URL запроса устанавливается
путь к перенаправленной странице. Если для обработки требуется исходный URL, его
можно сохранить в качестве атрибута запроса. Сервлет Dispatcher, используемый в версии
приложения Duke's Bookstore, которая описана в разделе Примеры JSP-страниц, сохраняет
информацию о пути из исходного URL, извлекает RequestDispatcher из запроса, а затем
отправляет ее JSP-странице template.jsp

public class Dispatcher extends HttpServlet {


public void doGet(HttpServletRequest request,
HttpServletResponse response) {
request.setAttribute("selectedScreen",
request.getServletPath());
RequestDispatcher dispatcher = request.
getRequestDispatcher("/template.jsp");
if (dispatcher != null)
dispatcher.forward(request, response);
}
public void doPost(HttpServletRequest request,

Web-

Rendered by www.RenderX.com
Организация доступа к web-контексту Стр. 423 из 626

...
}
Метод forward следует использовать для передачи другому ресурсу ответственности за
создание ответа пользователю. Если вы уже получили доступ к объектам ServletOutput-
Stream или PrintWriter внутри сервлета, использование данного метода будет невозможным.
Он будет возбуждать исключение IllegalStateException.

13.9. Организация доступа к web-контексту


Контекст, в котором выполняются web-компоненты, является объектом, реализующим
интерфейс ServletContext. Web-контекст извлекается при помощи метода getServletContext.
Web-контекст обеспечивает методы для организации доступа к:
• Параметрам инициализации
• Ресурсам, связанным с web-контекстом
• Объектно-значимым атрибутам
• Возможностям ведения журнала
Web-контекст используется фильтрами filters.HitCounterFilter и OrderFilter примера Duke's
Bookstore, которые обсуждались в разделе Фильтрование запросов и ответов. Счетчик
хранится в фильтрах, как атрибут контекста. Вспомните из раздела Управление
параллельным доступом к общим ресурсам, что методы доступа счетчика синхронизированы
таким образом, чтобы избежать несовместимых операций в сервлетах, исполняемых
одновременно. Фильтр извлекает объект-счетчик при помощи метода getAttribute контекста.
Значение прироста счетчика записывается при помощи метода log контекста.

public final class HitCounterFilter implements Filter {


private FilterConfig filterConfig = null;
public void doFilter(ServletRequest request,
ServletResponse response, FilterChain chain)
throws IOException, ServletException {
...
StringWriter sw = new StringWriter();
PrintWriter writer = new PrintWriter(sw);
ServletContext context = filterConfig.
getServletContext();
Counter counter = (Counter)context.
getAttribute("hitCounter");
...
writer.println("The number of hits is: " +
counter.incCounter());
...
context.log(sw.getBuffer().toString());
...

Web-

Rendered by www.RenderX.com
Стр. 424 из 626 Технология Java Servlet

}
}

13.10. Сохранение состояния клиента


Множество приложений требует, чтобы наборы запросов клиента были взаимосвязаны.
Например, приложение Duke's Bookstore сохраняет состояние корзины покупателя в период
получения запросов. За сохранение такого состояния, называемого сессионным, отвечают
Web-приложения, так как HTTP-протокол состояния не имеет. Для того чтобы осуществлять
поддержку приложений, требующих сохранения своего состояния, технология Java Servlet
обеспечивает специальный API для управления сессиями, что позволяет некоторым
механизмам реализовывать данные сессии.

13.10.1. Организация доступа к сессии


Сессии представляются при помощи объекта HttpSession. Доступ к сессии можно получить,
вызывая метод getSession объекта запроса. Данный метод возвращает текущую сессию,
ассоциированную с запросом или, если у запроса нет сессии, он создает ее. Так как метод
getSession может изменить заголовок запроса (если файлами cookie является механизм
для отслеживания сессии), необходимо вызвать его до того, как вы извлечете PrintWriter
или ServletOutputStream.

13.10.2. Ассоциирование атрибутов с сессией


Вы можете ассоциировать объектно-значимые атрибуты с сессией при помощи имен.
Доступ к таким атрибутам может получить любой web-компонент, принадлежащий к тому
же web-контексту, и обрабатывающий запрос, являющийся частью той же сессии.
Приложение Duke's Bookstore хранит корзину покупателя как сессионный атрибут. Это
позволяет сохранять корзину покупателя между получением запросов, а также позволяет
ей быть доступной для взаимодействующих сервлетов. CatalogServlet добавляет предметы
в корзину, ShowCartServlet - отображает корзину, удаляет из нее требуемые предметы, а
также может полностью ее очистить, а CashierServlet извлекает общую стоимость всех
книг в корзине.

public class CashierServlet extends HttpServlet {


public void doGet (HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {

//
HttpSession session = request.getSession();
ShoppingCart cart =
(ShoppingCart)session.
getAttribute("cart");
...
//

Web-

Rendered by www.RenderX.com
Сохранение состояния клиента Стр. 425 из 626

double total = cart.getTotal();

13.10.2.1. Уведомление объектов о том, что они ассоциированы с сессией


Вспомните, что ваше приложение может уведомить web-контекст и объекты-слушатели
сессии о событиях жизненного цикла сервлета (см. Обработка событий жизненного цикла
сервлета). Кроме того, вы можете уведомить объекты о конкретных событиях, имеющих
отношение к их ассоциации с сессией, в следующих случаях:
• Добавление или удаление объекта из сессии. Чтобы получить данное уведомление,
ваш объект должен реализовать интерфейс javax.http.HttpSessionBindingListener.
• Пассивация или активация сессии, к которой прикреплен объект. Сессия пассивируется
или активируется в случае перемещения между виртуальными машинами или, когда
ее сохраняют или восстанавливают из постоянного хранилища. Для того чтобы получить
данное уведомление, ваш объект должен реализовать интерфейс javax.http.HttpSession-
ActivationListener.

13.10.3. Управление сессией


У HTTP-клиента нет возможности сообщить об отсутствии потребности в сессии. По этой
причине для каждой сессии существует определенный лимит времени существования, а
ее ресурсы можно при необходимости востребовать повторно. Доступ к значению этого
времени можно получить при помощи методов [get|set]MaxInactiveInterval сессии. Его также
можно установить в утилите deploytool:
1. Выберите WAR
2. Выберите закладку General.
3. Введите значение временного лимита в окне Advanced.
Для того чтобы временной лимит сессии не истек, следует периодически осуществлять к
ней доступ посредством методов служб. Это обнуляет значение счетчика времени.
После завершения определенных взаимодействий клиента используйте метод
сессииinvalidate. Он позволяет "отключить" сессию на стороне сервера и удалить все
сессионные данные.
Последним сервлетом, осуществляющим доступ к клиентской сессии приложения "книжный
магазин", является ReceiptServlet. Поэтому именно ему принадлежит право закрытия
сессии:

public class ReceiptServlet extends HttpServlet {


public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
//
HttpSession session = request.getSession();
// -

Web-

Rendered by www.RenderX.com
Стр. 426 из 626 Технология Java Servlet

session.invalidate();
...

13.10.4. Отслеживание сессии


Web-контейнер может использовать некоторые методы для ассоциирования сессии с
пользователем, каждый из которых включает передачу идентификатора между клиентом
и сервером. Можно сохранять идентификатор на стороне клиента как cookie-файл; или же
web-компонент может включить требуемый идентификатор в каждый URL-адрес,
возвращаемый клиенту.
Если ваше приложение использует сессионные объекты, необходимо настроить его таким
образом, чтобы при каждом отключении функции сохранения cookie-файлов, URL-адреса
перезаписывались. При этом гарантируется способность отслеживания сессии. Это
осуществляется при помощи вызова метода ответаencodeURL(URL) во всех URL-адресах,
возвращаемых сервлетом. Данный метод включает сессионный ID в URL-адрес только в
том случае, если cookie-файлы отключены. В противном случае, он возвращает URL
неизмененным.
Метод doGet сервлета ShowCartServlet шифрует три URL в нижней части отображающейся
страницы корзины покупателя следующим образом:

out.println(" &nbsp; <strong><a href=\"" +


response.encodeURL(request.getContextPath() + "/catalog") +
"\">" + messages.getString("ContinueShopping") +
"</a> &nbsp; &nbsp; &nbsp;" +
"<a href=\"" +
response.encodeURL(request.getContextPath() + "/cashier") +
"\">" + messages.getString("Checkout") +
"</a> &nbsp; &nbsp; &nbsp;" +
"<a href=\"" +
response.encodeURL(request.getContextPath() +
"/showcart?Clear=clear") +
"\">" + messages.getString("ClearCart") +
"</a></strong>");
Если cookie-файлы отключены, сессия шифруется в Check Out URL следующим образом:

http://localhost:8080/bookstore1/cashier;
jsessionid=c0o7fszeb1
Если cookie-файлы включены, URL простой:

http://localhost:8080/bookstore1/cashier

Web-

Rendered by www.RenderX.com
Завершение работы сервлета Стр. 427 из 626

13.11. Завершение работы сервлета


Когда контейнер сервлета определяет, что сервлет должен быть удален из службы
(например, при необходимости восстановить ресурсы памяти или при завершении работы),
он вызывает метод destroy интерфейса Servlet. В данном методе, вы освобождаете все
ресурсы, используемые сервлетом, и сохраняете персистентное состояние. Следующий
метод destroy освобождает объект базы данных, созданный в методе init, который описан
в разделе Инициализация сервлета:

public void destroy() {


bookDB = null;
}
При удалении сервлета все его методы служб должны быть завершены. Сервер попытается
проверить это, вызвав метод destroy после возврата все запросов служб, или же после
заданного сервером периода отсрочки, в зависимости от того, что наступит первым. Если
ваш сервлет выполняет операции, занимающие большое количество времени (то есть,
операции, которые протекают дольше периода отсрочки сервера), при вызове метода
destroy даные операции могут все еще выполняться. Вы должны удостовериться, что все
потоки, обрабатывающие запросы клиентов завершены. В заключении данного раздела
описано то, как:
• Отслеживать количество потоков, выполняемых в данный момент методом service
• Обеспечить чистое завершение работы путем предупреждения метода destroy о
затянувшихся потоках и ожидания их завершения
• Периодически опрашивать затянувшиеся методы на предмет завершения их работы.
При необходимости остановить работу, выполнить очистку и возвратить в исходное
состояние.

13.11.1. Отслеживание запросов службы


Для отслеживания запросов службы включите в ваш класс сервлета поле, которое будет
подсчитывать количество выполняющихся методов службы. Данное поле должно иметь
методы синхронизированного доступа для приращения, уменьшения и возврата значения.

public class ShutdownExample extends HttpServlet {


private int serviceCounter = 0;
...
// serviceCounter
protected synchronized void enteringServiceMethod() {
serviceCounter++;
}
protected synchronized void leavingServiceMethod() {
serviceCounter--;
}

Web-

Rendered by www.RenderX.com
Стр. 428 из 626 Технология Java Servlet

protected synchronized int numServices() {


return serviceCounter;
}
}
Метод service должен увеличивать значение счетчика службы при каждой регистрации
метода и уменьшать его значение, когда метод возвращается. Это один из тех случаев,
когда ваш подкласс HttpServlet должен переопределить метод service. Новый метод должен
вызвать super.service для сохранения всех первоначальных выполняемых функций метода
service:

protected void service(HttpServletRequest req,


HttpServletResponse resp)
throws ServletException,IOException {
enteringServiceMethod();
try {
super.service(req, resp);
} finally {
leavingServiceMethod();
}
}

13.11.2. Предупреждение методов о завершении работы


Для обеспечения чистого завершения работы, метод destroy не должен освобождать
никаких общеиспользуемых ресурсов, пока все запросы служб не будут завершены. Первой
частью выполнения данной задачи является проверка счетчика службы. Во второй части
затянувшиеся методы предупреждаются о том, что настало время завершить работу. Для
этого предупреждения необходимо еще одно поле. Данное поле должно иметь обычные
методы доступа:

public class ShutdownExample extends HttpServlet {


private boolean shuttingDown;
...
// shuttingDown
protected synchronized void setShuttingDown(boolean flag) {
shuttingDown = flag;
}
protected synchronized boolean isShuttingDown() {
return shuttingDown;
}
}
Пример метода destroy, использующего данные поля для обеспечения чистого завершения
работы:

Web-

Rendered by www.RenderX.com
Дополнительная информация Стр. 429 из 626

public void destroy() {


/* Check to see whether there are still service methods /*
/* running, and if there are, tell them to stop. */
if (numServices() > 0) {
setShuttingDown(true);
}

/* Wait for the service methods to stop. */


while(numServices() > 0) {
try {
Thread.sleep(interval);
} catch (InterruptedException e) {
}
}
}

13.11.3. Создание "изящных" затянувшихся методов


Для полного обеспечиния чистого завершения работы необходимо, чтобы все затянувшиеся
методы действовали "изящно". Методы, которые могут затянуться на долгий период,
должны проверять значение поля, предупреждающего их о завершении работы, и при
необходимости прерывать свою работу.

public void doPost(...) {


...
for(i = 0; ((i < lotsOfStuffToDo) &&
!isShuttingDown()); i++) {
try {
partOfLongRunningOperation(i);
} catch (InterruptedException e) {
...
}
}
}

13.12. Дополнительная информация


Для получения дополнительной информации о технологии Java Servlet ознакомьтесь с:
• Ресурсами, представленными на Web-сайте http://java.sun.com/products/servlet.
• Java Servlet 2.3 Specification.

Web-

Rendered by www.RenderX.com
Стр. 430 из 626 Технология JavaServer Pages

14. Технология JavaServer Pages


Стефани Бодофф
Технология JSP-страниц (JavaServer Pages - JSP) позволяет вам без труда создавать web-
содержимое, у которого есть как статическая, так и динамическая компоненты. JSP-
технология воплощает все динамические возможности Java Servlet-технологии, однако
обеспечивает при этом более естественный подход к созданию постоянного содержимого.
Основными характеристиками JSP-технологии являются:
• Язык разработки JSP-страниц, являющихся текстовыми документами, которые описывают
процесс обработки запроса и конструирование ответа
• Конструкции для получения доступа к объектам на стороне сервера
• Механизмы, определяющие расширения для JSP-языка
JSP-технология также содержит API, используемый разработчиками web-контейнеров,
однако данный API в этой главе не рассматривается.

14.1. Что такое JSP-страница?


JSP-страницей является документ с текстовой основой, содержащий два типа текста:
статические шаблонные данные, выражаемые при помощи любого формата на текстовой
основе, такого как HTML, SVG, WML, и XML, а также JSP-элементы, которые создают
динамическое содержимое. Синтаксическая карта и ссылка для JSP-элементов доступны
по адресу http://java.sun.com/products/jsp/technical.html#syntax
Web-страница на Рисунке 13-1 является формой, позволяющей вам выбрать регион, и
отобразить дату характерным для него способом.

Рисунок 13-1 Локализованная форма даты

Исходный код для этого примера находится в директории docs/tutorial/examples/web/date,


создаваемой после распаковки руководства. JSP-страница index.jsp, использованная для
создания формы, появится ниже. Она представляет собой типичную композицию из
статической HTML-разметки и JSP-элементов. Если вы уже занимались разработкой web-
страниц, то, возможно, вы сталкивались со структурными выражениями HTML-документов
(<head>, <body>, и т.д.), а также с HTML-выражениями, создающими форму <form> и меню
<select>. Строки, выделенные в примере кода жирным начертанием, содержат следующие
типы JSP конструкций:

Web-

Rendered by www.RenderX.com
Что такое JSP-страница? Стр. 431 из 626

• Директивы (<%@page ... %>) импортируют классы пакета java.util и класс MyLocales, а
также устанавливают тип содержания, возвращаемый страницей.
• Элемент jsp:useBean создает объект, содержащий набор регионов, и инициализирует
переменную, указывающую на этот объект.
• Скриптлеты (<% ... %> ) извлекают значение параметра запроса locale, производят
итерацию в наборе названий регионов и условно вставляют HTML-текст в вывод.
• Выражения (<%= ... %>) вставляют значение названия региона в ответ.
• Элемент jsp:include посылает запрос другой странице (date.jsp) и включает ответ в ответ
вызывающей страницы.

<%@ page import="java.util.*,MyLocales" %>


<%@ page contentType="text/html; charset=ISO-8859-5" %>
<html>
<head><title>Localized Dates</title></head>
<body bgcolor="white">
<jsp:useBean id="locales" scope="application"
class="MyLocales"/>
<form name="localeForm" action="index.jsp" method="post">
<b>Locale:</b>
<select name=locale>
<%
String selectedLocale = request.getParameter("locale");
Iterator i = locales.getLocaleNames().iterator();
while (i.hasNext()) {
String locale = (String)i.next();
if (selectedLocale != null &&
selectedLocale.equals(locale)) {
%>
<option selected><%=locale%></option>
<%
} else {
%>
<option><%=locale%></option>
<%
}
}
%>
</select>
<input type="submit" name="Submit" value="Get Date">
</form>

Web-

Rendered by www.RenderX.com
Стр. 432 из 626 Технология JavaServer Pages

<jsp:include page="date.jsp"/>
</body>
</html>
Для того чтобы создать, разместить и осуществить выполнение данной JSP-страницы:
1. В окне терминала перейдите в директорию docs/tutorial/examples/web/date.
2. Запустите ant build. Исполнитель build вызовет все необходимые компиляции и скопирует
файлы в директорию docs/tutorial/examples/web/date/build.
3. Запустите ant install. Исполнитель install оповестит Tomcat о том, что стал доступен
новый контекст.
4. Откройте URL даты http://localhost:8080/date.
Вы увидите комбинированное окно, чьими записями являются названия регионов. Выберите
требуемый регион и нажмите Get Date. После этого появится дата, выраженная способом,
характерным для данного региона.

14.2. Примеры JSP-страниц


Для того чтобы лучше продемонстрировать возможности JSP-технологии, в данной главе
каждый сервлет из приложения Duke's Bookstore представлен в виде JSP-страницы:
Таблица 13-1 Примеры JSP-страниц из приложения Duke's Bookstore
Выполняемая функция JSP-страница
Вход в книжный магазин bookstore.jsp
Создание шапки книжного магазина banner.jsp
Осмотр книг, выставленных на продажу catalog.jsp
Помещение книги в корзину покупателя catalog.jsp и bookdetails.jsp
Получение дополнительной информации о заданной книге bookdetails.jsp
Отображение корзины покупателя showcart.jsp
Удаление одной или нескольких книг из корзины покупателя showcart.jsp
Покупка книг, находящихся в корзине покупателя cashier.jsp
Получение подтверждения заказа receipt.jsp

Данные для приложения "книжный магазин" все еще хранятся в базе данных. Однако, со
вспомогательным объектом database.BookDB базы данных были произведены два
изменения:
• Вспомогательный объект базы данных был переписан для соответствия шаблонам
проектирования JavaBean-компонентов, как описано в разделе Правила создания
JavaBean-компонентов. Это изменение сделано для того, чтобы JSP-страницы могли
иметь доступ к вспомогательным объектам, используя элементы JSP-языка, характерные
для JavaBean-компонентов.
• Вместо того чтобы напрямую получать доступ к базе данных книжного магазина,
вспомогательный объект проходит через объект доступа к данным database.BookDAO.
Далее представлена реализация вспомогательного объекта базы данных. У компонента
имеются две переменные экземпляров: текущая книга и ссылка на корпоративный компонент
базы данных.

Web-

Rendered by www.RenderX.com
Примеры JSP-страниц Стр. 433 из 626

public class BookDB {


private String bookId = "0";
private BookDBEJB database = null;

public BookDB () throws Exception {


}
public void setBookId(String bookId) {
this.bookId = bookId;
}
public void setDatabase(BookDBEJB database) {
this.database = database;
}
public BookDetails getBookDetails()
throws Exception {
try {
return (BookDetails)database.
getBookDetails(bookId);
} catch (BookNotFoundException ex) {
throw ex;
}
}
...
}

Наконец, данная версия примера содержит апплет, генерирующий в шапке динамические


цифровые часы. Смотрите раздел Включение аплета, в котором описан JSP-элемент,
генерирующий HTML для загрузки апплета.
Исходный код данного приложения расположен в директории docs/tutorial/examples/web/book-
store2, созданной при распаковке руководства (см. Запуск примеров). Для того чтобы
создать, разместить и запустить пример:
1. В окне терминала перейдите в директорию docs/tutorial/examples/web/bookstore2.
2. Запустите ant build. Исполнитель build вызовет все необходимые компиляции и скопирует
файлы в директорию docs/tutorial/examples/web/bookstore2/build.
3. Удостоверьтесь, что Tomcat запущен.
4. Запустите ant install. Исполнительinstall оповестит Tomcat о том, что стал доступен
новый контекст.
5. Запустите сервер базы данных PointBase и заполните базу данных, если вы еще этого
не сделали (см. Организация доступа к базам данных из web-приложений).
6. Откройте URL книжного магазина http://localhost:8080/bookstore2/enter.

Web-

Rendered by www.RenderX.com
Стр. 434 из 626 Технология JavaServer Pages

Если вы хотите лучше разбираться в выявлении распространенных ошибок, обратитесь


к разделам Распространенные проблемы и их решения и Устранение неисправностей.

14.3. Жизненный цикл JSP-страницы


JSP-страница обслуживает запросы также, как это делает сервлет. Таким образом,
жизненный цикл и большинство возможностей JSP-страниц (в частности, динамические
аспекты) определяются технологией Java Servlet. Большая часть обсуждаемого в данной
главе материала обращается к описываемым здесь функциям.
Когда запрос отображается на JSP-страницу, он обрабатывается специальным сервлетом,
который вначале проверяет, не является ли сервлет JSP-страницы старше, чем сама JSP-
страница. Если это так, он транслирует JSP-страницу в класс сервлета и компилирует
класс. Одним из преимуществ процесса разработки JSP-страниц по сравнению с
разработкой сервлетов является то, что процесс создания JSP-страниц осуществляется
автоматически.

14.3.1. Трансляция и компиляция


Во время фазы трансляции каждый тип данных в JSP-странице обрабатывается отдельно.
Шаблонные данные преобразованы в программный код, который будет отправлять данные
в поток, возвращающий данные клиенту. JSP-элементы обрабатываются следующим
образом:
• Директивы используются для управления Web-контейнером в процессе трансляции и
выполнения им JSP-страницы.
• Элементы сценариев вставляются в класс сервлета JSP-страницы. См. раздел JSP-
элементы сценариев.
• Элементы формы <jsp:XXX ... /> конвертируются в вызовы метода JavaBean-компонентов
или вызовы Java Servlet API.
Для JSP-страницы с именем pageName, источник сервлета JSP-страницы хранится в
файле:

<JWSDP_HOME>/work/Standard
Engine/localhost/context_root/pageName$jsp.java

Например, источник страницы index (index.jsp) для примера "локализация даты",


обсуждаемого в начале главы, будет назван:

<JWSDP_HOME>/work/Standard Engine/localhost/date/index$jsp.java

Обе фазы, как трансляции, так и компиляции, могут вызывать ошибки, которые
прослеживаются только при первом запросе к странице. Если ошибка встречается во
время трансляции страницы (например, в случае, когда транслятор встречает плохо

Web-

Rendered by www.RenderX.com
Жизненный цикл JSP-страницы Стр. 435 из 626

сформированный JSP-элемент), сервер возвращает исключение ParseException, а исходный


файл класса сервлета будет пустым или неполным. Последняя неполная строка укажет
на неправильный JSP-элемент.
Если ошибка встречается во время компиляции JSP-страницы (например, в случае
синтаксической ошибки в скриптлете), сервер возвратит исключение JasperException и
сообщение, включающее имя сервлета JSP-страницы со строкой, где была найдена ошибка.
Если страница была транслирована и откомпилирована, сервлет JSP-страницы в большей
степени придерживается жизненного цикла сервлета, который описан в разделе Жизненный
цикл сервлета:
1. Если экземпляра сервлета JSP-страницы не существует, контейнер
2.
A. Загружает класс сервлета JSP-страницы
B. Создает экземпляр класса сервлета
C. Инициализирует экземпляр сервлета, путем вызова метода jspInit
3. Контейнер вызывает метод _jspService, передавая объект запроса и ответа.
Если контейнеру требуется удалить сервлет JSP-страницы, он вызывает метод jspDestroy.

14.3.2. Выполнение
Вы можете контролировать различные параметры выполнения JSP-страниц при помощи
директив page. Директивы, имеющие отношение к буферизации вывода и обработке ошибок
обсуждаются далее. Рассмотрение других директив встречается в данной главе в контексте
задач разработчика конкретных страниц.

14.3.2.1. Использование буферизации


При выполнении JSP-страницы вывод, записанный в объект ответа, автоматически
заносится в буфер. Вы можете установить размер буфера при помощи следующей
директивы page:

<%@ page buffer="none|xxxkb" %>

Больший размер буфера позволяет записать больший объем содержимого. Таким образом,
JSP-странице обеспечивается больше времени для установки соответствующих кодов и
заголовков статуса или же для отправки другому web-ресурсу. Меньший размер буфера
уменьшает загрузку памяти сервера и позволяет клиенту быстрее получать данные.

14.3.2.2. Обработка ошибок


Во время выполнения JSP-страницы может возникнуть любое количество исключительных
ситуаций. Можно задать, чтобы web-контейнер в случае возникновения исключительной
ситуации передавал управление странице ошибок. Для этого необходимо включить
следующую директиву page в начало вашей JSP-страницы:

Web-

Rendered by www.RenderX.com
Стр. 436 из 626 Технология JavaServer Pages

<%@ page errorPage="file_name" %>

Страница initdestroy.jsp приложения Duke's Bookstore содержит следующую директиву

<%@ page errorPage="errorpage.jsp"%>

В начале страницы при помощи следующей директивы pageуказано, что errorpage.jsp


служит страницей ошибок:

<%@ page isErrorPage="true|false" %>

Данная директива делает доступным для страницы ошибок объект исключения (типа
javax.servlet.jsp.JspException) таким образом, чтобы вы могли извлекать, интерпретировать
и возможно даже отображать информацию о причине возникновения исключительной
ситуации на странице ошибок.
Примечание: Вы можете также определять страницы ошибок для WAR-файла, который
содержит JSP-страницу. Если страницы ошибок определены как для WAR, так и для JSP-
страницы, вначале появляется страница ошибок JSP-страницы.

14.4. Инициализация и завершение работы JSP-страницы


Вы можете настроить процесс инициализации путем переопределения метода jspInit
интерфейса JspPage. Это позволит JSP-странице считывать постоянные данные
конфигурации, инициализировать ресурсы и осуществлять любую другую единовременную
деятельность. Освободите ресурсы, используя метод jspDestroy. Методы, определенные
с использованием JSP-объявления, обсуждаются в разделе Объявления.
Страница "книжного магазина" initdestroy.jspопределяет метод jspInit для извлечения
объекта database.BookDBAO, который имеет доступ к базе данных магазина и хранит
ссылку на компонент в bookDBAO.

private BookDBAO bookDBAO;


public void jspInit() {
bookDBAO =
(BookDBAO)getServletContext().getAttribute("bookDB");
if (bookDBAO == null)
System.out.println("Couldn't get database.");
}

Web-

Rendered by www.RenderX.com
Создание статического содержимого Стр. 437 из 626

Когда JSP-страница удаляется из службы, метод jspDestroy освобождает переменную


BookDBAO.

public void jspDestroy() {


bookDBAO = null;
}

В виду того, что корпоративный компонент совместно используется всеми JSP-страницами,


его необходимо инициализировать при запуске приложения, а не в каждой отдельной JSP-
странице. Технология Java Servlet обеспечивает для этой цели события жизненного цикла
приложения и классы-слушатели. В качестве упражнения вы можете переместить
программный код, управляющий созданием корпоративного компонента в класс-слушатель
контекста. Дополнительную информацию о слушателе контекста, который инициализирует
версию Java Servlet приложения "книжный магазин", можно получить в разделе Обработка
событий жизненного цикла сервлета.

14.5. Создание статического содержимого


Статическое содержимое создается в JSP-странице так же, как если бы это была обычная
страница, содержащая только текстовый формат данных. Статическое содержимое может
быть выражено в любом формате, предназначенном для форматирования текстов, к
примеру, HTML, WML и XML. Форматом, определенным по-умолчанию, является HTML.
При желании можно использовать и другой формат текста. Для этого включите в начало
вашей JSP-страницы директиву page с атрибутом contentType, которому в качестве значения
следует установить тип формата. Например, если вы хотите, чтобы ваши данные были
выражены в формате WML (wireless markup language - язык разметки для беспроводных
систем), требуется включить следующую директиву:

<%@ page contentType="text/vnd.wap.wml"%>

Реестр имен типов содержимого определяется организацией IANA (Агентство по выделению


имен и уникальных параметров протоколов Internet) и хранится по адресу: ftp://ftp.isi.edu/in-
notes/iana/assignments/media-types

14.6. Создание динамического содержимого


Динамическое содержимое создается путем обращения к объектам языка программирования
Java из элементов сценариев.

14.6.1. Использование объектов в JSP-страницах


Из JSP-страницы можно получить доступ к различным объектам, включая корпоративные
компоненты и JavaBean-компоненты. JSP-технология автоматически организует доступ к

Web-

Rendered by www.RenderX.com
Стр. 438 из 626 Технология JavaServer Pages

определенным объектам так, что можно создавать объекты-приложения и обращаться к


ним из JSP-страницы.

14.6.1.1. Неявные объекты


Неявные объекты создаются web-контейнером и содержат информацию, относящуюся к
конкретному запросу, странице или приложению. Многие из таких объектов определяются
технологией Java Servlet, лежащей в основе JSP-технологии. В дальнейшем это
обсуждается более детально. В Таблице 13-2 представлены неявные объекты.
Таблица 13-2 Неявные объекты
Переменная Класс
application javax.servlet.ServletContext
config javax.servlet.ServletConfig
exception java.lang.Throwable
out javax.servlet.jsp.JspWriter
page java.lang.Object
pageContext javax.servlet.jsp.PageContext
request подтип javax.servlet.ServletRequest
response подтип javax.servlet.ServletResponse
session javax.servlet.http.HttpSession

14.6.1.2. Объекты-приложения
По возможности, режим работы приложения следует инкапсулировать в объекты так, чтобы
создатели страниц могли сфокусироваться на аспектах представления. Объекты могут
создаваться разработчиками, которые являются профессионалами в программировании
Java и в получении доступа к базам данных и другим службам. Существует четыре способа
для создания и использования объектов в JSP-странице.
• Переменные экземпляра и класса, относящиеся к классу сервлета JSP-страницы,
создаются в объявлениях и доступны в скриптлетах и выражениях.
• Локальные переменные класса сервлета JSP-страницы создаются и используются в
скриптлетах и выражениях.
• Атрибуты объектов области действия (см. Использование объектов области действия)
создаются и используются в скриптлетах и выражениях.
• JavaBean-компоненты могут создаваться и быть доступными при помощи
модернизированных JSP-элементов. Данные элементы обсуждаются в главе JavaBean-
компоненты в JSP-страницах. Вы также можете создавать JavaBean-компонент в
объявлении или скриптлете и вызывать методы JavaBean-компонента в скриптлете или
выражении.
Объявления, скриптлеты и выражения описаны в разделе JSP-элементы сценариев.

14.6.1.3. Использование совместного доступа к объектам (общие объекты)


Условия, оказывающие воздействие на параллельный доступ к общим объектам, описаны
в разделе Управление параллельным доступом к общим объектам. Из JSP-страниц,
запускаемых как многопоточные сервлеты можно получить доступ к объектам, к которым

Web-

Rendered by www.RenderX.com
Создание динамического содержимого Стр. 439 из 626

применимы данные условия. Вы можете указать web-контейнеру, каким образом следует


отправлять многочисленные запросы клиентов, используя следующую директиву page:

<%@ page isThreadSafe="true|false" %>

Когда значением isThreadSafe является true, web-контейнер устанавливает более высокий


приоритет отправки многочисленных параллельных клиентских запросов к JSP-странице.
Это значение используется по-умолчанию. Используя true, вы должны гарантировать, что
вы должным образом синхронизировали доступ ко всем общим объектам, определенным
на уровне страницы. Этот уровень включает в себя объекты, созданные в объявлениях,
JavaBean-компоненты со страницей в качестве области действия и атрибуты объекта
области действия page.
Если значением isThreadSafe является false, запросы отправляются поочередно (то есть,
по одному) в том порядке, в котором они были получены, а контролировать доступ к
объектам уровня страницы при этом не требуется. Тем не менее, вы должны гарантировать
правильную синхронизацию доступа к атрибутам объектов области действия application
или session, а также к JavaBean-компонентам с приложением или сессией, указанными в
качестве области действия.

14.6.2. JSP-элементы сценариев


JSP-элементы сценариев используются для создания объектов и организации к ним
доступа, определения методов и управления потоком контроля. В виду того, что одной из
целей JSP-технологии является отделение статических шаблонных данных от программного
кода, требуемого для динамической генерации содержимого, рекомендуется умеренное
использование сценариев JSP. Большую часть работы, требующей использование
сценариев, можно устранить путем использования заказных тегов, описанных в главе
Заказные теги в JSP-страницах.
JSP-технология позволяет контейнеру поддерживать любой язык написания сценариев,
имеющий возможность вызывать Java-объекты. Если вместо языка java, установленного
по-умолчанию для создания сценариев, вы желаете использовать другой язык, задайте
его в директиве page в начале JSP-страницы:

<%@ page language="scripting language" %>

По причине того, что элементы сценариев конвертируются в выражения языка


программирования в классе сервлета JSP-страницы, вам необходимо импортировать все
классы и пакеты, используемые данной JSP-страницей. Если страница написана на языке
java, импортируйте класс или пакет при помощи директивы page:

Web-

Rendered by www.RenderX.com
Стр. 440 из 626 Технология JavaServer Pages

<%@ page import="packagename.*, fully_qualified_classname" %>

К примеру, страница showcart.jsp приложения "книжный магазин" импортирует классы,


необходимые для реализации корзины покупателя, при помощи данной директивы:

<%@ page import="java.util.*, cart.*" %>

14.6.2.1. Объявления
JSP-объявление используется для объявления переменных и методов на языке создания
сценариев страницы. Синтаксис для объявления следующий:

<%! scripting language declaration %>

Когда языком создания сценариев является Java, переменные и методы в JSP-объявлениях


становятся объявлениями класса сервлета JSP-страницы.
В следующем объявлении страница initdestroy.jsp примера "книжный магазин" определяет
переменную экземпляра с названием bookDBAO, а также методы инициализации и
завершения работыjspInit и jspDestory, обсуждаемые ранее:

<%!
private BookDBAO bookDBAO;

public void jspInit() {


...
}
public void jspDestroy() {
...
}
%>

14.6.2.2. Скриптлеты
JSP-скриптлет используется для хранения какого-либо фрагмента кода, действительного
для языка сценариев, используемого в данной странице. Синтаксис для скриптлета выглядит
следующим образом:

Web-

Rendered by www.RenderX.com
Создание динамического содержимого Стр. 441 из 626

<%
scripting language statements
%>

Когда языком сценариев является java, скриптлет трансформируется во фрагмент


выражения языка Java и вставляется в метод service сервлета JSP-страницы. Переменная
языка программирования, созданная в скриптлете, доступна из любой точки JSP-страницы.
JSP-страница showcart.jsp содержит скриптлет, извлекающий итератор из коллекции
позиций, содержащихся в корзине покупателя, а также скриптлет, устанавливающий
конструктор для циклического прохода по всем позициям корзины. Во время цикла JSP-
страница извлекает свойства объектов-книг и форматирует их, использую при этом HTML-
разметку. Блок открывает цикл while, поэтому HTML-разметка предшествует скриптлету,
закрывающему блок.

<%
Iterator i = cart.getItems().iterator();
while (i.hasNext()) {
ShoppingCartItem item =
(ShoppingCartItem)i.next();
BookDetails bd = (BookDetails)item.getItem();
%>

<tr>
<td align="right" bgcolor="#ffffff">
<%=item.getQuantity()%>
</td>
<td bgcolor="#ffffaa">
<strong><a href="
<%=request.getContextPath()%>/bookdetails?bookId=
<%=bd.getBookId()%>"><%=bd.getTitle()%></a></strong>
</td>
...
<%
// while
}
%>

Результат показан на Рисунке 13-2.

Web-

Rendered by www.RenderX.com
Стр. 442 из 626 Технология JavaServer Pages

Рисунок 13-2 Корзина покупателя магазина Duke's Bookstore

14.6.2.3. Выражения
JSP-выражение используется для вставки в поток данных, возвращаемый клиенту, значения
выражения языка сценариев, конвертированного в строку. Когда языком написания
сценариев является Java, выражение преобразовывается в оператор, конвертирующий
значение выражения в объект String и вставляющий его в неявный объект out.
Синтаксис такого выражения выглядит следующим образом:

<%= scripting language expression %>

Обратите внимание, что использование точки с запятой в JSP-выражениях запрещено,


даже в тех случаях, когда у такого же выражения, используемого в скриптлете, она
присутствует.
Следующий скриптлет извлекает количество позиций, находящихся в корзине покупателя:

<%
//
int num = cart.getNumberOfItems();
if (num> 0) {
%>

Позднее выражения используются для вставки значения num в выходной поток и задания
подходящей строки, которую требуется вставить после данного числа:

Web-

Rendered by www.RenderX.com
Включение содержимого в JSP-страницу Стр. 443 из 626

<font size="+2">
<%=messages.getString("CartContents")%><%=num%>
<%=(num==1 ? <%=messages.getString("CartItem")%> :
<%=messages.getString("CartItems"))%></font>

14.7. Включение содержимого в JSP-страницу


Существует два механизма для включения другого web-ресурса в JSP-страницу:
использование директивы include и элемента jsp:include
Директива include обрабатывается во время трансляции JSP-страницы в класс сервлета.
Действием данной директивы является вставка текста, содержащегося в другом файле
(либо в статическом содержимом, либо же в другой JSP-странице) в требуемую JSP-
страницу. Вероятно, вы будете использовать директиву include для включения в другую
страницу содержимого шапки, авторской информации или любой другой порции
информации, которую вы бы хотели использовать повторно. Синтаксис для директивыinclude
следующий:

<%@ include file="filename" %>

Например, все страницы приложения "книжный магазин" включают в себя файл banner.jsp,
в котором находится содержимое шапки. Это осуществляется при помощи следующей
директивы:

<%@ include file="banner.jsp" %>

Кроме того, страницы bookstore.jsp, bookdetails.jsp, catalog.jsp, и showcart.jsp включают в


себя JSP-элементы, создающие и разрушающие компонент базы данных при помощи
данной директивы:

<%@ include file="initdestroy.jsp" %>

У данного подхода нет ограничений, так как директива include статически помещается в
каждый файл, повторно использующий ресурс, на который ссылается директива. Для того
чтобы узнать о более гибком подходе создания страниц данного типа, обратитесь к разделу
Библиотека тегов шаблона.

Web-

Rendered by www.RenderX.com
Стр. 444 из 626 Технология JavaServer Pages

Элемент jsp:include обрабатывается во время выполнения JSP-страницы. Действие include


позволяет вам включать в JSP-файл как статические, так и динамические ресурсы.
Результаты, полученные при включении статических и динамических ресурсов, несколько
отличаются. Если ресурс статический, его содержимое вставляется в вызывающий JSP-
файл. Если же ресурс является динамическим, включаемому ресурсу отправляется запрос,
включаемая страница выполняется, а затем результат включается в ответ вызывающей
JSP-страницы. Синтаксис элемента jsp:include выглядит следующим образом:

<jsp:include page="includedPage" />

Примечание: Tomcat не будет перегружать статически включенную страницу, подвергшуюся


модификациям до тех пор, пока включающая ее страница также не будет изменена.
Приложение date, представленное в начале данной главы, включает в себя страницу,
генерирующую отображение локализованной даты, при помощи следующего выражения:

<jsp:include page="date.jsp"/>

14.8. Передача управления другому web-компоненту


Механизм передачи управления другому web-компоненту из JSP-страницы использует
выполняемые функции, обеспечиваемые Java Servlet API. Это описано в разделе Передача
управления другому web-компоненту. Доступ к этим функциям осуществляется из JSP-
страницы при помощи элемента jsp:forward:

<jsp:forward page="/main.jsp" />

Обратите внимание, что если клиенту уже возвратили какие-либо данные, у элемента
jsp:forward возникнет исключительная ситуация IllegalStateException.

14.8.1. Элемент jsp:param


При вызове элемента include или forward, целевой странице предоставляется
первоначальный объект запроса. Если необходимо поместить в эту страницу
дополнительные данные, при помощи элемента jsp:param вы можете добавить в объект
запроса и другие параметры:

<jsp:include page="...">
<jsp:param name="param1" value="value1"/>

Web-

Rendered by www.RenderX.com
Включение апплета Стр. 445 из 626

</jsp:include>

14.9. Включение апплета


Включать апплет или JavaBean-компонент в JSP-страницу можно при помощи элемента
jsp:plugin. Данный элемент генерирует HTML-код, в котором содержатся соответствующие
конструкции (<object> or <embed>). Они зависят от взаимодействия между клиентом и
броузером и приводят, при необходимости, к загрузке программного обеспечения Java
Plug-in и компонента на стороне клиента, а также к последующему выполнению любого
компонента на стороне сервера. Синтаксис элемента jsp:plugin выглядит следующим
образом:

<jsp:plugin
type="bean|applet"
code="objectCode"
codebase="objectCodebase"
{ align="alignment" }
{ archive="archiveList" }
{ height="height" }
{ hspace="hspace" }
{ jreversion="jreversion" }
{ name="componentName" }
{ vspace="vspace" }
{ width="width" }
{ nspluginurl="url" }
{ iepluginurl="url" } >
{ <jsp:params>
{ <jsp:param name="paramName" value= paramValue" /> }+
</jsp:params> }
{ <jsp:fallback> arbitrary_text </jsp:fallback> }
</jsp:plugin>

Тег jsp:plugin заменяется либо тегом <object>, либо тегом <embed>. Это зависит от клиента,
осуществляющего запрос. Атрибуты тега jsp:plugin обеспечивают данные конфигурации
для представления элемента, как того требует версия plug-in. Атрибуты nspluginurl и
iepluginurl задают URL-адрес, с которого этот plug-in можно загрузить.
Элементы jsp:param задают параметры апплета или JavaBean-компонента. Если plug-in
нельзя запустить (или теги <object> и <embed> не поддерживаются клиентом, или по какой-
либо другой причине), элемент jsp:fallback указывает на то, чтобы содержимое
просматривалось в броузере клиента.

Web-

Rendered by www.RenderX.com
Стр. 446 из 626 Технология JavaServer Pages

Если же plug-in запускается, но апплет или JavaBean-компонент не могут быть найдены


или же не запускаются, пользователю отобразится специальное сообщение, встроенное
в plug-in (вероятнее всего, это будет всплывающее окно, докладывающее об
исключительной ситуации ClassNotFoundException).
Далее представлена страница banner.jspприложения Duke's Bookstore, которая создает
шапку, отображающую динамические цифровые часы, сгенерированные апплетом Digital-
Clock:

Рисунок 13-3 Приложение Duke's Bookstore, использующее апплет

Элемент jsp:plugin, используемый для загрузки апплета, выглядит следующим образом:

<jsp:plugin
type="applet"
code="DigitalClock.class"
codebase="/bookstore2"
jreversion="1.3"
align="center" height="25" width="300"
nspluginurl="http://java.sun.com/products/plugin/1.3.0_01
/plugin-install.html"
iepluginurl="http://java.sun.com/products/plugin/1.3.0_01
/jinstall-130_01-win32.cab#Version=1,3,0,1">

Web-

Rendered by www.RenderX.com
Расширение JSP-языка Стр. 447 из 626

<jsp:params>
<jsp:param name="language"
value="<%=request.getLocale().getLanguage()%>" />
<jsp:param name="country"
value="<%=request.getLocale().getCountry()%>" />
<jsp:param name="bgcolor" value="FFFFFF" />
<jsp:param name="fgcolor" value="CC0066" />
</jsp:params>
<jsp:fallback>
Unable to start plugin.
</jsp:fallback>
</jsp:plugin>

14.10. Расширение JSP-языка


При помощи JavaBean-компонентов, используемых совместно со скриптлетами, можно
выполнить множество различных, динамическим образом обрабатываемых задач. Они
включают в себя организацию доступа к базам данных, использование корпоративных
служб, таких как e-mail и каталоги, а также управление потоками. Однако одним из
недостатков скриптлетов является то, что они усложняют поддержку JSP-страниц. С другой
стороны, JSP-технология обеспечивает механизм, называемый заказные теги, который
позволяет инкапсулировать динамически выполняемые функции в объекты, доступные
через расширения JSP-языка. Заказные теги передают JSP-страницам преимущества
другого уровня компонентного представления.
К примеру, вспомните скриптлет, используемый для циклического прохождения через
содержимое корзины покупателя Duke's Bookstore и ее отображения:

<%
Iterator i = cart.getItems().iterator();
while (i.hasNext()) {
ShoppingCartItem item =
(ShoppingCartItem)i.next();
...
%>
<tr>
<td align="right" bgcolor="#ffffff">
<%=item.getQuantity()%>
</td>
...
<%
}

Web-

Rendered by www.RenderX.com
Стр. 448 из 626 JavaBean-компоненты в JSP-страницах

%>

Заказной тег iterate устраняет логику кода и управляет переменной создания сценария
item, ссылающейся на элементы в корзине покупателя:

<logic:iterate id="item"
collection="<%=cart.getItems()%>">
<tr>
<td align="right" bgcolor="#ffffff">
<%=item.getQuantity()%>
</td>
...
</logic:iterate>

Заказные теги упаковываются и распределяются в единице, называемой библиотекой


тегов. Синтаксис заказных тегов не отличается от синтаксиса, используемого для JSP-
элементов, то есть <prefix:tag> является формой написания заказного тега. Тем не менее,
элемент тега prefix определяется пользователем библиотеки тегов, а tag - разработчиком
самого тега. В главе Заказные теги в JSP-страницах объясняется, как следует использовать
и разрабатывать заказные теги.

14.11. Дополнительная информация


Для получения дополнительной информации по технологии JavaServer Pages, обратитесь
к следующим источникам:
• Ресурсы, перечисленные на web-сайте http://java.sun.com/products/jsp.
• В документе Спецификация JavaServer Pages, версия 1.2 представлено полное описание
синтаксиса и семантики JSTL.

15. JavaBean-компоненты в JSP-страницах


Стефани Бодофф
JavaBean-компоненты - это Java-классы многократного использования, которые можно
объединять в приложения. Любой Java-класс, который удовлетворяет определенным
правилам создания, можно считать JavaBean-компонентом.
Технология страниц JavaServer поддерживает использование JavaBean-компонентов с
элементами языка JSP. Вы можете легко создавать и инициализировать компоненты, а
также получать и устанавливать значения их свойств. В данной статье представлена
основная информация о JavaBean-компонентах и элементах языка JSP, необходимая для
осуществления доступа к JavaBean-компонентам в JSP-страницах. Дополнительую

Web-

Rendered by www.RenderX.com
Правила создания JavaBean-компонентов Стр. 449 из 626

информацию о модели JavaBean-компонентов можно получить, обратившись к странице


http://java.sun.com/products/javabeans.

15.1. Правила создания JavaBean-компонентов


Правила создания JavaBean-компонентов управляют свойствами класса и методами public,
которые дают доступ к свойствам.
В качестве свойств JavaBean-компонентов могут быть:
• Чтение/запись, только чтение или только запись
• Простой (JavaBean-компонент), в этом случае он содержит одно значение или
индексированный, то есть представляющий массив значений
Реализация свойства при помощи переменной экземпляра не является необходимым. К
свойству просто должен осуществляться доступ с помощью методов public,
удовлетворяющих определенным условиям:
• Для каждого читаемого свойства у компонента должен быть метод, вида:

PropertyClass getProperty() { ... }

Для каждого записываемого свойства у компонента должен быть метод, вида:

setProperty(PropertyClass pc) { ... }

Кроме методов свойств, JavaBean-компонент должен определять конструктор, у которого


нет параметров.
JSP-страницы enter.jsp, bookdetails.jsp, catalog.jsp, и showcart.jsp приложения Duke's
Bookstore используют JavaBean-компоненты database.BookDB и database.BookDetails.
BookDB обеспечивает внешний интерфейс JavaBean-компонентов для доступа к объекту
BookDBAO. Оба компонента в значительной степени используются компонентно-
ориентированными заказными тегами (см. Заказные теги в JSP-страницах). JSP-страницы
showcart.jsp и cashier.jsp используют cart.ShoppingCart для отображения корзины покупателя.
JSP-страницы catalog.jsp, showcart.jsp, и cashier.jsp используют JavaBean-компонент
util.Currency для форматирования валюты, в соответствии с региональными стандартами.
У компонента есть два записываемых свойства, это - locale и amount, а также свойство
для чтения format. Свойство format не соответствует никакой переменной экземпляра, зато
возвращает функцию свойств locale и amount.

public class Currency {


private Locale locale;

Web-

Rendered by www.RenderX.com
Стр. 450 из 626 JavaBean-компоненты в JSP-страницах

private double amount;


public Currency() {
locale = null;
amount = 0.0;
}
public void setLocale(Locale l) {
locale = l;
}
public void setAmount(double a) {
amount = a;
}
public String getFormat() {
NumberFormat nf =
NumberFormat.getCurrencyInstance(locale);
return nf.format(amount);
}
}

15.2. Зачем использовать JavaBean-компоненты?


JSP-страница может создавать и использовать в пределах объявления или скриптлета
любой тип объекта языка программирования Java. Следующий скриптлет создает корзину
покупателя магазина и хранит ее, как сессионный атрибут:

<%
ShoppingCart cart = (ShoppingCart)session.
getAttribute("cart");
// ,

if (cart == null) {
cart = new ShoppingCart();
session.setAttribute("cart", cart);
}
%>

Если объект корзины соответствует правилам JavaBean-компонентов, JSP-страницы могут


использовать JSP-элементы для создания объекта и доступа к нему. Например, страницы
Duke's Bookstore bookdetails.jsp, catalog.jsp, и showcart.jsp заменяют скриптлет гораздо
более лаконичным JSP-элементом useBean:

Web-

Rendered by www.RenderX.com
Создание и использование JavaBean-компонентов Стр. 451 из 626

<jsp:useBean id="cart" class="cart.ShoppingCart"


scope="session"/>

15.3. Создание и использование JavaBean-компонентов


Объявите, что ваша JSP-страница будет использовать JavaBean-компонент, при помощи
одного из следующих форматов:

<jsp:useBean id="beanName"
class="fully_qualified_classname" scope="scope"/>

или

<jsp:useBean id="beanName"
class="fully_qualified_classname" scope="scope">
<jsp:setProperty .../>
</jsp:useBean>

Второй формат используется, если для инициализации свойств компонента вы желаете


включить выражения jsp:setProperty, описанные в следующем разделе.
Элемент jsp:useBean объявляет, что в странице будет использован компонент, который
хранится, и к которому есть доступ из определенной области действия. Такой областью
действия может быть приложение, сессия, запрос или страница. Если такого компонента
не существует, выражение само создает компонент и хранит его как атрибут объекта
области действия (см. Использование объектов области действия). Значение атрибута id
определяет имя компонента в области действия и идентификатор, используемый для того,
чтобы ссылаться на компонент из других JSP-элементов и скриптлетов.
Примечание: В разделе Элементы JSP-сценариев мы упоминали, что вы должны
импортировать все классы и пакеты, используемые JSP-страницей. Данное правило немного
изменяется в случае, когда на класс ссылаются только из элементов useBean. В таких
случаях вы должны только импортировать класс, если он находится в пакете без названия.
Например, в разделе Что такое JSP-страница? страница index.jsp импортирует класс
MyLocales. Тем не менее, в примере Duke's Bookstore все классы содержатся в пакетах,
вследствие чего не могут импортироваться явным образом.
Следующий элемент создает экземпляр Currency, если такового не существует, хранит
его, как атрибут объекта session и делает компонент доступным в течение сессии,
посредством идентификатора currency:

Web-

Rendered by www.RenderX.com
Стр. 452 из 626 JavaBean-компоненты в JSP-страницах

<jsp:useBean id="currency" class="util.Currency"


scope="session"/>

15.4. Установление свойств JavaBean-компонентов


Существует два способа, позволяющих устанавливать свойства JavaBean-компонентов в
JSP-странице: при помощи элемента jsp:setProperty или при помощи скриптлета

<% beanName.setPropName(value); %>

Синтаксис элемента jsp:setProperty зависит от источника значения свойства. В Таблице


14-1 показаны различные способы для установки свойств JavaBean-компонентов, используя
элемент jsp:setProperty.
Таблица 14-1 Установка свойств JavaBean-компонентов
Источник значения Синтаксис элемента
Строковая константа <jsp:setProperty name="beanName" property="propName"
value="string constant"/>
Параметр запроса <jsp:setProperty name="beanName" property="propName"
param="paramName"/>
Имя параметра запроса, соответствующее свойству <jsp:setProperty name="beanName" property="prop-
компонента Name"/><jsp:setProperty name="beanName" property="*"/>
Выражение <jsp:setProperty name="beanName" property="propName"
value="<%= expression %>"/>
1. beanName должен совпадать с тем, который определен для атрибута id элемента useBean.
2. В JavaBean-компоненте должен существовать метод setPropName.
3. paramName должен быть именем параметра запроса.

Набор свойств из строковой константы или параметра запроса должен иметь тип из списка,
представленного в Таблице14-2. Так как и константа, и параметр запроса являются
строками, Web-контейнер автоматически конвертирует значение в тип свойства. Сам
процесс преобразования отражен в таблице. Значения String могут использоваться для
присваивания значений свойству класса PropertyEditor. В этом случае используется метод
setAsText(String). Если метод бросает исключение IllegalArgumentException, возникает
ошибка преобразования. Значением, присваиваемым индексированному свойству, должен
быть массив, а только что описанные правила применяются к его элементам.
Таблица 14-2 Правильные значения свойств
Тип свойства Преобразование строкового значения
Свойство компонента Использует setAsText(string-literal)
boolean или Boolean Как показано в java.lang.Boolean.valueOf(String)
byte или Byte Как показано в java.lang.Byte.valueOf(String)
char или Character Как показано в java.lang.String.charAt(0)
double или Double Как показано в java.lang.Double.valueOf(String)

Web-

Rendered by www.RenderX.com
Установление свойств JavaBean-компонентов Стр. 453 из 626

Тип свойства Преобразование строкового значения


int или Integer Как показано в java.lang.Integer.valueOf(String)
float или Float Как показано в java.lang.Float.valueOf(String)
long или Long Как показано в java.lang.Long.valueOf(String)
short или Short Как показано в java.lang.Short.valueOf(String)
Object Новое String(string-literal)

Для установки значения свойства сложного типа языка программирования Java вам следует
использовать выражение рабочего цикла. Вспомните из раздела Выражения, что JSP-
выражение используется для внедрения в поток, возвращаемый клиенту, значения из
выражения языка создания сценариев, конвертированного в строку. Используя в элементе
setProperty, выражение просто возвращает свое значение без какого-либо автоматического
преобразования. Как следствие, тип, возвращаемый из выражения, должен соответствовать
типу свойства или сводиться к нему.
Приложение Duke's Bookstore демонстрирует, как необходимо использовать элемент
setProperty и скриптлет с целью установки текущей книги для вспомогательного компонента
базы данных. Например, bookstore3/web/bookdetails.jsp использует форму:

<jsp:setProperty name="bookDB" property="bookId"/>

не смотря на то, что bookstore2/web/bookdetails.jsp использует форму:

<% bookDB.setBookId(bookId); %>

Следующие фрагменты со страницы bookstore3/web/showcart.jsp иллюстрируют, как


компонент-валюта инициализируется вместе с объектом Locale и суммой, заданными
путем оценки выражений периода запроса. Так как первая инициализация вложена в
элемент useBean, она выполняется только когда создается компонент.

<jsp:useBean id="currency" class="util.Currency"


scope="session">
<jsp:setProperty name="currency" property="locale"
value="<%= request.getLocale() %>"/>
</jsp:useBean>

<jsp:setProperty name="currency" property="amount"


value="<%=cart.getTotal()%>"/>

Web-

Rendered by www.RenderX.com
Стр. 454 из 626 JavaBean-компоненты в JSP-страницах

15.5. Извлечение свойств JavaBean-компонентов


Существует несколько способов, позволяющих извлекать свойства JavaBean-компонентов.
В двух из них (с использованием элемента jsp:getProperty и выражения) значение свойства
конвертируется в String и значение вставляется в текущий неявный объект out:
• <jsp:getProperty name="beanName" property="propName"/>
• <%= beanName.getPropName() %>
В обоих случаях beanName должен совпадать с именем, которое было определено для
атрибута id в элементе useBean, кроме этого, в JavaBean-компоненте должен быть метод
getPropName.
Если вам требуется извлечь значение свойства без его конвертирования и вставки во
внешний объект, необходимо использовать скриптлет:

<% Object o = beanName.getPropName(); %>

Обратите внимание на различия между выражением и скриптлетом. В выражении есть


знак "=" после открытия % и нет точки с запятой в конце, которая есть у скриптлета.
В приложении Duke's Bookstore демонстрируется, как использовать обе формы для
извлечения форматированной валюты из компонента-валюта и вставки ее в страницу.
Например, в странице bookstore3/web/showcart.jsp использована форма:

<jsp:getProperty name="currency" property="format"/>

не смотря на это, в странице bookstore2/web/showcart.jsp использована форма:

<%= currency.getFormat() %>

В странице приложения Duke's Bookstore для извлечения количества книг из компонента-


корзина и открытия условной вставки текста в выходной поток использован следующий
скриптлет:

<%
//
int num = cart.getNumberOfItems();
if (num > 0) {

Web-

Rendered by www.RenderX.com
Извлечение свойств JavaBean-компонентов Стр. 455 из 626

%>

Хотя скриптлеты очень полезны при динамической обработке, в качестве наилучшего


подхода для доступа к свойствам объектов и осуществления управления потоками
рассматривается использование заказных тегов (см. Заказные теги в JSP-страницах).
Например, страница bookstore3/web/showcart.jsp заменяет скриптлет на следующие
заказные теги:

<bean:define id="num" name="cart" property="numberOfItems" />

<logic:greaterThan name="num" value="0" >

На Рисунке 14-1 показано, где хранятся различные типы объектов и как можно получить
доступ к этим объектам из JSP-страницы. Объекты, созданные при помощи тега jsp:useBean,
хранятся как атрибуты области действия объектов и доступны в скриптлетах и выражениях
при помощи тегов jsp:[get|set]Property. Объекты, созданные в объявлениях и скриптлетах
хранятся как переменные класса сервлета JSP-страницы и могут быть доступны в
скриптлетах и выражениях.

Рисунок 14-1 Организация доступа к объектам из JSP-страницы

16. Заказные теги в JSP-страницах


Стефани Бодофф
Стандартные JSP-теги для вызова операторов в JavaBean-компонентах и управления
запросами упрощают разработку и поддержку JSP-страницы. Кроме этого JSP-технология
обеспечивает механизм инкапсуляции других типов динамической функциональности в
заказных тегах, которые являются дополнением к языку JSP. Заказные теги обычно

Web-

Rendered by www.RenderX.com
Стр. 456 из 626 Заказные теги в JSP-страницах

распространяются в форме библиотеки тегов, которая определяет набор связанных


заказных тегов и содержит объекты, реализующие эти теги.
Некоторые примеры задач, которые могут выполняться при помощи заказных тегов,
включают в себя следующие операции: операции над неявными объектами, обработку
форм, организацию доступа к базе данных и другим корпоративным службам, таким как
электронная почта и директории, а также осуществляют управление потоками. Библиотеки
JSP-тегов создаются разработчиками, являющимися специалистами в программировании
Java и экспертами в организации доступа к службам. Используются же библиотеки
создателями web-приложений, которым следует сконцентрировать свое внимание скорее
на аспекте представления, чем на организации доступа к корпоративным службам. Кроме
принесения пользы в разделении труда между разработчиками библиотек и их
пользователями, использование заказных теги также увеличивает производительность.
Это осуществляется путем инкапсуляции повторяющихся задач для того, чтобы можно
было позднее использовать их более чем в одном приложении.
В сообществе JSP-технологий библиотекам тегов уделяется огромнейшее внимание.
Дополнительную информацию о библиотеках тегов, а также ссылки на некоторые доступные
бесплатно библиотеки можно получить на странице http://java.sun.com/prod-
ucts/jsp/taglibraries.html.
Стандартные JSP-теги для вызова операторов в JavaBean-компонентах и управления
запросами упрощают разработку и поддержку JSP-страницы. Кроме этого JSP-технология
обеспечивает механизм инкапсуляции других типов динамической функциональности в
заказных тегах, которые являются дополнением к языку JSP. Заказные теги обычно
распространяются в форме библиотеки тегов, которая определяет набор связанных
заказных тегов и содержит объекты, реализующие эти теги.
Некоторые примеры задач, которые могут выполняться при помощи заказных тегов,
включают в себя следующие операции: операции над неявными объектами, обработку
форм, организацию доступа к базе данных и другим корпоративным службам, таким как
электронная почта и директории, а также осуществляют управление потоками. Библиотеки
JSP-тегов создаются разработчиками, являющимися специалистами в программировании
Java и экспертами в организации доступа к службам. Используются же библиотеки
создателями web-приложений, которым следует сконцентрировать свое внимание скорее
на аспекте представления, чем на организации доступа к корпоративным службам. Кроме
принесения пользы в разделении труда между разработчиками библиотек и их
пользователями, использование заказных теги также увеличивает производительность.
Это осуществляется путем инкапсуляции повторяющихся задач для того, чтобы можно
было позднее использовать их более чем в одном приложении.
В сообществе JSP-технологий библиотекам тегов уделяется огромнейшее внимание.
Дополнительную информацию о библиотеках тегов, а также ссылки на некоторые доступные
бесплатно библиотеки можно получить на странице http://java.sun.com/prod-
ucts/jsp/taglibraries.html.

16.1. Что такое заказной тег?


Заказной тег - это элемент языка JSP, определенный пользователем. Когда JSP-страница,
содержащая заказной тег, транслируется в сервлет, тег конвертируется в операции над

Web-

Rendered by www.RenderX.com
Примеры JSP-страниц Стр. 457 из 626

объектом под названием обработчик тега. Позднее, во время исполнения сервлета JSP-
страницы Web-контейнер вызывает данные операции.
У заказных тегов имеется большой набор характеристик. Они могут
• Настраиваться через атрибуты, передаваемые от вызывающей страницы.
• Организовывать доступ ко всем объектам, доступным для JSP-страниц.
• Изменять ответ, созданный вызывающей страницей.
• Взаимодействовать друг с другом. Вы можете создать и инициализировать JavaBean-
компонент, а также переменную, которая ссылается на этот компонент в одном теге, а
затем использовать этот компонент в другом теге.
• Быть вложенными друг в друга, позволяя осуществлять сложные взаимодействия в
пределах одной JSP-страницы.

16.2. Примеры JSP-страниц


В данной главе описаны задачи, связанные с использованием и определением тегов. А
также иллюстрируются задачи с выдержками из JSP-версии приложения Duke's Bookstore,
которая обсуждалась в разделе Примеры JSP-страниц. Приложение было переписано для
получения возможности использования преимуществ двух библиотек: Struts и tutorial-
template (шаблон руководства). В третьем разделе данной главы под названием Примеры
в подробностях описаны два тега: тег iterate из библиотеки Struts и набор тегов из
библиотеки tutorial-template.
Библиотека тегов Struts обеспечивает основу для создания интернационализированных
web-приложений, которые реализуют шаблон проектирования Model-View-Controller. Struts
включает в себя обширный набор полезных заказных тегов для оперирования:
• HTML-формами
• Шаблонами
• JavaBean-компонентами
• Обработкой логики
В приложении Duke's Bookstore используются теги из подбиблиотек Struts, которые
называются bean и logic.
Библиотека тегов tutorial-template определяет набор тегов для создания шаблона
приложения. Шаблоном является JSP-страница с "заполнителями" вместо частей, которые
требуется изменить при каждом отображении экрана. Каждый из таких "заполнителей"
называется параметром шаблона. Например, для отображения заказного содержимого
страницы простой шаблон может включать в себя параметр названия для шапки общего
экрана и параметр тела, ссылающийся на JSP-страницу. Шаблон создается при помощи
набора вложенных тегов (definition, screen, и parameter), которые используются для создания
таблицы определений экрана приложения Duke's Bookstore, а также тега insert, который
используется для вставки параметров из таблицы в экран.
На Рисунке 15-1 показан поток запросов через данные web-компоненты приложения Duke's
Bookstore:

Web-

Rendered by www.RenderX.com
Стр. 458 из 626 Заказные теги в JSP-страницах

• template.jsp, определяет структуру каждого экрана. Использует тег insert для


формирования экрана из субкомпонентов.
• screendefinitions.jsp, определяет субкомпоненты, используемые каждым экраном. Все
экраны имеют одинаковую шапку, но различные заголовки и содержание тела
(определяемое колонкой "JSP-страницы" в Таблице 13-1).
• Dispatcher, сервлет, который обрабатывает запросы и пересылает их template.jsp.

Рисунок 15-1 Поток запросов через компоненты приложения Duke's Bookstore

Исходный код для приложения Duke's Bookstore расположен в директории docs/tutorial/exam-


ples/web/bookstore3, созданной при распаковке руководства (см. Запуск примеров). Для
того чтобы создать, разместить и запустить приложение:
1. Загрузите библиотеку Struts версии 1.0 из каталога http://jakarta.apache.org/builds/jakarta-
struts/release/v1.0/
2. Распакуйте Struts и скопируйте файлы struts-bean.tld, struts-logic.tld, и struts.jar из
директории jakarta-struts-1.0/lib в директорию <JWSDP_HOME>/docs/tutorial/exam-
ples/web/bookstore.
3. В окне терминала перейдите в директорию <JWSDP_HOME>/docs/tutorial/examples/book-
store3.
4. Запустите ant build. Исполнитель build вызовет все необходимые компиляции и скопирует
файлы в директорию <JWSDP_HOME>/docs/tutorial/examples/web/bookstore3/build.
5. Удостоверьтесь, что Tomcat запущен.
6. Запустите ant install. Исполнитель install оповестит Tomcat о том, что доступен новый
контекст.
7. Запустите сервер базы данных PointBase и заполните базу данных, если вы этого еще
не сделали (см. Организация доступа к базам данных из web-приложений).

Web-

Rendered by www.RenderX.com
Использование тегов Стр. 459 из 626

8. Откройте URL-адрес "книжного магазина" http://localhost:8080/bookstore3/enter.


Если вы хотите лучше разбираться в выявлении распространенных ошибок, обратитесь
к разделам Распространенные проблемы и их решения и Устранение неисправностей.

16.3. Использование тегов


В данном разделе описывается использование тегов в JSP-странице, а также представлены
различные типы тегов.
Для того чтобы можно было использовать тег, автор страницы должен выполнить две
операции:
• Объявить библиотеку тегов, содержащую данный тег
• Сделать реализацию библиотеки тегов доступной для web-приложения

16.3.1. Объявление библиотек тегов


Перед использованием заказного тега объявите, что в JSP-странице будут использоваться
теги, определенные библиотекой тегов, путем включения в страницу директивы taglib:

<%@ taglib uri="/WEB-INF/tutorial-template.tld" prefix="tt" %>

Атрибут uri ссылается на URI, который уникально идентифицирует дескриптор библиотеки


тегов (tag library descriptor - TLD), описанный в разделе Дескрипторы библиотеки тегов.
Этот URI может быть как прямым, так и косвенным. Атрибут prefix определяет префикс,
который отличает теги, определенные заданной библиотекой тегов от тех, которые
обеспечиваются другими библиотеками.
Дескриптор библиотеки тегов должен иметь расширение .tld. TLD-файлы хранятся в
директории WEB-INF архива WAR или поддиректории WEB-INF. Вы можете ссылаться на
TLD прямым или косвенным образом.
Следующая директива taglib ссылается на TLD-файл прямым образом:

<%@ taglib uri="/WEB-INF/tutorial-template.tld" prefix="tt" %>

Данная директива taglib использует короткое логическое имя для косвенной ссылки на
TLD-файл:

<%@ taglib uri="/tutorial-template" prefix="tt" %>

Web-

Rendered by www.RenderX.com
Стр. 460 из 626 Заказные теги в JSP-страницах

Отобразите логическое имя на абсолютную локацию в дескрипторе размещения Web-


приложения. Чтобы отобразить логическое имя /tutorial-template на абсолютную локацию
/WEB-INF/tutorial-template.tld, добавьте элемент taglib в дескриптор web.xml:

<taglib>
<taglib-uri>/tutorial-template</taglib-uri>
<taglib-location>
/WEB-INF/tutorial-template.tld
</taglib-location>
</taglib>

16.3.2. Как сделать реализацию библиотеки тегов доступной


Сделать реализацию библиотеки тегов доступной для web-приложения можно двумя
основными способами. Классы, реализующие обработчиков тега, могут храниться в
распакованной виде в поддиректории WEB-INF/classes, принадлежащей web-приложению.
С другой стороны, если библиотека распространяется как JAR-файл, она хранится в
директории WEB-INF/lib. Библиотека тегов, используемая более чем в одном приложении,
хранится в директории <JWSDP_HOME>/common/lib Java WSDP.

16.3.3. Типы тегов


Заказные JSP-теги написаны с использованием синтаксиса XML. У них есть открывающий
и закрывающий теги, а возможно и тело:

<tt:tag>
body
</tt:tag>

Заказной тег без тела выражается следующим образом:

<tt:tag />

16.3.3.1. Простые теги


В простом теге не существует ни тела, ни атрибутов:

Web-

Rendered by www.RenderX.com
Использование тегов Стр. 461 из 626

<tt:simple />

16.3.3.2. Теги с атрибутами


Заказной тег может иметь атрибуты. Атрибуты перечисляются в открывающем теге и
имеют синтаксис attr="value". Значения атрибутов служат для настройки поведения заказного
тега так же, как параметры используются для настройки режима работы метода. Типы
атрибутов тегов определяются в дескрипторе библиотеки тегов (см. раздел Теги с
атрибутами).
Вы можете установить значение атрибута из константы String или же из выражения рабочего
цикла. Процесс преобразования между константами, выражениями рабочего цикла и
типами атрибутов выполняется по правилам, описанным для свойств JavaBean-компонентов
в разделе Установка свойств JavaBean-компонентов.
Атрибуты тега logic:present библиотеки Struts определяют, должно ли оцениваться тело
тега. В следующем примере атрибут определяет параметр запроса с именем Clear:

<logic:present parameter="Clear">

На странице catalog.jsp приложения Duke's Bookstore выражение рабочего цикла


используется для установки значения атрибута, задающего коллекцию книг, в которой тег
logic:iterate библиотеки Struts реализует процесс итерации:

<logic:iterate collection="<%=bookDB.getBooks()%>"
id="book" type="database.BookDetails">

16.3.3.3. Теги с телом


В теле заказного тега (то есть в промежутке между открывающим и закрывающим тегами)
могут находиться другие заказные теги, теги ядра, элементы создания сценариев, HTML-
текст, а так же содержимое, зависящее от этого или другого тегов.
В следующем примере представлена страница showcart.jsp приложения Duke's Bookstore.
На этой странице используется тег logic:present из библиотеки Struts для очистки корзины
покупателя и вывода сообщения в том случае, если в запросе имеется параметр с именем
Clear:

<logic:present parameter="Clear">
<% cart.clear(); %>
<font color="#ff0000" size="+2"><strong>
You just cleared your shopping cart!

Web-

Rendered by www.RenderX.com
Стр. 462 из 626 Заказные теги в JSP-страницах

</strong><br>&nbsp;<br></font>
</logic:present>

16.3.3.4. Осуществление выбора между способом передачи информации в атрибутах или


в теле тега
Как показано в последних двух разделах, существует возможность передавать часть
данных двумя способами: в атрибуте тега или же в его теле. Вообще, любые данные,
которые выражены простой строкой или могут быть сгенерированы путем оценки простого
выражения, лучше всего передавать, как атрибут.

16.3.3.5. Теги, определяющие переменные создания сценария


Заказной тег позволяет определить переменную, которая может быть использована в
сценариях страницы. В следующем примере проиллюстрировано, как определить и
использовать переменную создания сценария, которая содержит объект, возвращаемый
в результате JNDI-поиска. Примеры таких объектов включают в себя корпоративные
компоненты, транзакции, базы данных, записи окружения и так далее:

<tt:lookup id="tx" type="UserTransaction"


name="java:comp/UserTransaction" />
<% tx.begin(); %>

Некоторые страницы приложения Duke's Bookstore используют компонентно-


ориентированные теги из библиотеки Struts для определения переменных создания
сценария. Например, bookdetails.jsp использует тег bean:parameter для создания переменной
создания сценария bookId и установки ему значения параметра запроса bookId. Выражение
jsp:setProperty также устанавливает свойству bookId объекта bookDB значение параметра
запроса bookId. Тег bean:define извлекает значение свойства bookDetails базы данных книг
и определяет результат как переменную создания сценария book:

<bean:parameter id="bookId" name="bookId" />


<jsp:setProperty name="bookDB" property="bookId"/>
<bean:define id="book" name="bookDB" property="bookDetails"
type="database.BookDetails"/>
<h2><jsp:getProperty name="book" property="title"></h2>

16.3.3.6. Взаимодействие тегов


Заказные теги могут взаимодействовать друг с другом, используя совместный доступ к
объектам.
В данном примере tag1 создает объект с именем obj1, который затем используется тегом
tag2.

Web-

Rendered by www.RenderX.com
Определение тегов Стр. 463 из 626

<tt:tag1 attr1="obj1" value1="value" />


<tt:tag2 attr1="obj1" />

В следующем примере объект, созданный закрывающим тегом группы вложенных тегов,


доступен всем внутренним тегам. Потенциальная возможность конфликта имен при этом
уменьшается, так как объект не называется. Пример иллюстрирует то, как набор
взаимодействующих вложенных тегов должен отобразиться на JSP-странице.

<tt:outerTag>
<tt:innerTag />
</tt:outerTag>

Страница template.jsp приложения Duke's Bookstore использует набор взаимодействующих


тегов для определения экранов приложения. Данные теги описаны в разделе Библиотека
тегов шаблона.

16.4. Определение тегов


Для того чтобы определить тег, вам необходимо:
• Разработать обработчик тега и вспомогательные классы для данного тега
• Объявить тег в дескрипторе библиотеки тегов
В данном разделе описаны свойства обработчиков тегов и TLD. Кроме того, объясняется,
как разрабатываются обработчики тегов и элементы дескриптора библиотеки для каждого
типа тегов, представленных в предыдущем разделе.

16.4.1. Обработчики тегов


Обработчик тега - объект, вызываемый web-контейнером для оценки заказного тега во
время исполнения JSP-страницы, ссылающейся на данный тег. Обработчики тегов должны
реализовывать или интерфейс Tag, или интерфейс BodyTag. Интерфейсы могут
использоваться с целью преобразования существующего Java-объекта в обработчик тега.
Для вновь созданных обработчиков в качестве основных классов можно использовать
классы TagSupport и BodyTagSupport. Данные классы и интерфейсы содержатся в пакете
javax.servlet.jsp.tagext.
Методы обработки тегов, определенные при помощи интерфейсов Tag и BodyTag,
вызываются сервлетом JSP-страницы в различные моменты во время оценки тега. Когда
встречается открывающий тег заказного тега, сервлет JSP-страницы вызывает методы,
инициализирующие соответствующий обработчик, а затем вызывает метод обработчика
doStartTag. Когда встречается закрывающий тег, вызывается метод обработчика doEndTag.
В промежутке, когда обработчику тега требуется осуществить взаимодействие с телом
тега, вызываются дополнительные методы. Для получения дополнительной информации

Web-

Rendered by www.RenderX.com
Стр. 464 из 626 Заказные теги в JSP-страницах

по данному вопросу обратитесь к разделу Теги с телами. Для того, чтобы обеспечить
реализацию обработчика тега, вы должны реализовать методы, представленные в Таблице
15-1, которые вызываются на различных стадиях процесса обработки тега.
Таблица 15-1 Методы обработчиков тегов
Тип обработчика тега Методы
Простой doStartTag, doEndTag, release
Атрибуты doStartTag, doEndTag, set/getAttribute1...N, release
Тело, оценка без взаимодействия doStartTag, doEndTag, release
Тело, повторяющаяся оценка doStartTag, doAfterBody, doEndTag, release
Тело, взаимодействие doStartTag, doEndTag, release, doInitBody, doAfterBody, release

У обработчика тега есть доступ к API, благодаря которому он может взаимодействовать


с JSP-страницей. Точкой входа в API является объект контекста страницы
(javax.servlet.jsp.PageContext), через который обработчик тега может извлекать все
остальные неявные объекты (запрос, сессию и приложение) доступные из JSP-страницы.
Неявные объекты могут иметь ассоциированные с ними названные атрибуты. Такие
атрибуты доступны при помощи методов [set|get]Attribute.
Если тег является вложенным, то у обработчика тега кроме этого имеется доступ к
обработчику (называемому родительским) родительского тега.
Набор связанных классов обработчика тега (библиотека тега) обычно пакуется и
размещается как JAR-архив.

16.4.2. Дескрипторы библиотеки тегов


Дескриптор библиотеки тегов (tag library descriptor - TLD) - это XML-документ, описывающий
библиотеку тегов. TLD содержит информацию о библиотеке, как о едином целом, и о
каждом теге библиотеки в отдельности. TLD используются web-контейнерами для проверки
тегов, а также средствами разработки JSP-страниц.
Имена TLD-файлов должны иметь расширение .tld. TLD-файлы хранятся в директории
WEB-INF WAR-файла или в поддиректории WEB-INF.
TLD должен начинаться с пролога XML-документа, задающего версию XML и определение
типа документа (DTD):

<?xml version="1.0" encoding="ISO-8859-1" ?>


<!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag
Library 1.2//EN"
"http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">

Tomcat поддерживает версии DTD 1.1 и 1.2. Тем не менее, в примерах данной главы
использована версия 1.2, так как при разработке любых библиотек тегов вы должны
использовать самую последнюю версию DTD-файла. TLD библиотеки tutorial-
templatetutorial-template.tld соответствует версии 1.2. TLD библиотеки Struts соответствует

Web-

Rendered by www.RenderX.com
Определение тегов Стр. 465 из 626

версии 1.1 DTD-файла, который содержит меньшее количество элементов и использует


для некоторых элементов отличающиеся имена.
Корневым элементом в TLD является taglib. Подэлементы taglib перечислены в Таблице
15-2:
Таблица 15-2 Подэлементы taglib
Элемент Описание
tlib-version Версия библиотеки тегов
jsp-version Версия JSP-спецификации, которую требует библиотека тегов
short-name Имя (используется при необходимости), которое может
использоваться инструментарием JSP-страницы для создания
имен с мнемоническим значением
uri URL, уникально идентифицирующий библиотеку тегов
display-name Имя (используется при необходимости), предназначенное
для отображения утилитами
small-icon Маленькая иконка (используется при необходимости), которая
может быть использована в утилитах
large-icon Большая иконка (используется при необходимости), которая
может быть использована в утилитах
description Информация о теге (используется при необходимости)
listener См. Элемент listener
tag См. Элемент tag

16.4.2.1. Элемент listener


Библиотека тегов может определять некоторые классы, которые являются слушателями
событий (см. Обработка событий жизненного цикла сервлета). Слушатели перечислены
в TLD как элементы listener, а web-контейнер создает экземпляры классов-слушателей и
регистрирует их способом, аналогичным для слушателей определенных на WAR-уровне.
В отличие от слушателей WAR-уровня, порядок, в котором регистрируются слушатели
библиотеки тегов, не задается. Единственным подэлементом элемента listener является
listener-class, который должен содержать полностью пригодное имя класса-слушателя.

16.4.2.2. Элемент tag


Описание каждого тега библиотеки задается его именем и классом его обработчика,
информацией о переменных создания сценария, созданных тегом, а также информацией
об атрибутах тега. Информация о переменных создания сценария может передаваться
непосредственно в TLD или же через дополнительный информационный класс тега (см.
Теги, определяющие переменные создания сценария). Каждое объявление атрибута
содержит указатель, определяющий, требуется ли атрибут и может ли его значение
задаваться выражениями периода запроса, а также тип данного атрибута (см. Элемент
атрибут).
Определение каждого тега в TLD вложено в элемент tag. Подэлементы tag перечислены
в Таблице 15-3:
Таблица 15-3 Подэлементы tag
Элемент Описание
name Уникальное имя тега
tag-class Полностью пригодное имя класса обработчика тега

Web-

Rendered by www.RenderX.com
Стр. 466 из 626 Заказные теги в JSP-страницах

Элемент Описание
tei-class Подкласс (используется при необходимости)
javax.servlet.jsp.tagext.TagExtraInfo. См. Обеспечение
информации о переменной скрипта.
body-content Тип содержания тела. См. Элемент body-content и Элемент
body-content.
display-name Имя (используется при необходимости), предназначенное
для отображения утилитами
small-icon Маленькая иконка (используется при необходимости), которая
может быть использована в утилитах
large-icon Большая иконка (используется при необходимости), которая
может быть использована в утилитах
description Информация о теге (используется при необходимости)
variable Информация о переменной создания сценария (используется
при необходимости). См. Обеспечение информации о
переменной скрипта.
attribute Информация об атрибуте тега. См. Элемент атрибут.

В следующих разделах описаны методы и TLD-элементы, которые вам потребуются при


разработке каждого тега, представленного в разделе Типы тегов.

16.4.3. Простые теги


16.4.3.1. Обработчики тегов
Обработчик простого тега должен реализовывать методы doStartTag и doEndTag
интерфейса Tag. Метод doStartTag вызывается, когда встречается открывающий тег. Этот
метод возвращает SKIP_BODY, так как у простого тега нет тела. Метод doEndTag
вызывается, когда встречается закрывающий тег. Метод doEndTag возвращает EVAL_PAGE,
если необходимо оценить остаток страницы, в противном случае он возвращает
SKIP_PAGE.
Простой тег, обсуждаемый в первом разделе,

<tt:simple />

реализуется при помощи следующего обработчика тегов:

public SimpleTag extends TagSupport {


public int doStartTag() throws JspException {
try {
pageContext.getOut().print("Hello.");
} catch (Exception ex) {
throw new JspTagException("SimpleTag: " +
ex.getMessage());
}

Web-

Rendered by www.RenderX.com
Определение тегов Стр. 467 из 626

return SKIP_BODY;
}
public int doEndTag() {
return EVAL_PAGE;
}
}

16.4.3.2. Элемент body-content


Теги без тела должны объявлять об отсутствии тела при помощи элемента body-content:

<body-content>empty</body-content>

16.4.4. Теги с атрибутами


16.4.4.1. Определение атрибутов в обработчике тега
Для каждого атрибута тега в его обработчике необходимо определить свойство и методы
get и set, согласно условным соглашениям JavaBean-архитектуры. Например, обработчик
тега из библиотеки Struts

<logic:present parameter="Clear">

содержит следующие объявления и методы:

protected String parameter = null;


public String getParameter() {
return (this.parameter);
}
public void setParameter(String parameter) {
this.parameter = parameter;
}

Обратите внимание, что если атрибут называется id, а обработчик тега наследуется от
класса TagSupport, определять свойство и методы set и get не требуется, так как они уже
определены в классе TagSupport.
Атрибут тега, значением которого является String, может присваивать имя атрибуту одного
из неявных объектов, доступных обработчикам тега. Доступ к атрибуту неявного объекта
можно получить, передав значение атрибута тега методу [set|get]Attribute неявного объекта.

Web-

Rendered by www.RenderX.com
Стр. 468 из 626 Заказные теги в JSP-страницах

Этот достаточно хороший способ позволяет передавать имена переменных создания


сценариев обработчику тега, в котором они ассоциируются с объектами, содержащимися
в контексте страницы (см. Неявные объекты).

16.4.4.2. Элемент Attribute


Для каждого тега attribute вы должны определить, требуется ли ему атрибут, может ли
значение быть задано выражением, а также (при необходимости) тип атрибута в элементе
attribute. Для постоянных значений типом всегда является java.lang.String. Если значением
элемента rtexprvalue является true или yes, тогда элемент type определяет возвращаемый
тип, ожидаемый от любого выражения, определенного как значение атрибута.

<attribute>
<name>attr1</name>
<required>true|false|yes|no</required>
<rtexprvalue>true|false|yes|no</rtexprvalue>
<type>fully_qualified_type</type>
</attribute>

Если тег attribute не является необходимым, обработчик тега должен обеспечить значение
по-умолчанию.
Элемент tag для тега logic:present объявляет, что использование атрибута parameter не
является необходимым (так как тег может также проверять наличие других сущностей,
таких как свойства компонента) и, что его значение может быть установлено при помощи
выражения рабочего цикла.

<tag>
<name>present</name>
<tag-class>org.apache.struts.taglib.
logic.PresentTag</tag-class>
<body-content>JSP</body-content>
...
<attribute>
<name>parameter</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
...
</tag>

Web-

Rendered by www.RenderX.com
Определение тегов Стр. 469 из 626

16.4.4.3. Проверка атрибутов


В документацию библиотеки тегов должны быть внесены правильные значения атрибутов
тегов. При трансляции JSP-страницы web-контейнер применяет все ограничения TLD-
элементов к каждому его атрибуту.
Атрибуты, передаваемые тегу, могут также проверяться во время трансляции при помощи
метода isValid класса, производного от TagExtraInfo. Этот класс также используется для
обеспечения информации о переменных создания сценария, определенных тегом (см.
Обеспечение информации о переменной создания сценария).
Метод isValid передает информацию об атрибуте в объект TagData, который содержит
кортежи (набор взаимосвязанных величин) атрибут-значение для каждого атрибута тега.
Так как проверка осуществляется в период трансляции, в качестве значения атрибута,
вычисленного во время запроса, будет установлено TagData.REQUEST_TIME_VALUE.
В теге <tt:twa attr1="value1"/> содержится следующий TLD-элемент attribute:

<attribute>
<name>attr1</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>

В данном объявлении указано, что значение attr1 может быть задано во время рабочего
цикла.
Следующий метод isValid проверяет, является ли значением attr1 правильное значение
типа Boolean. Обратите внимание на то, что значение attr1 может быть вычислено во время
рабочего цикла, поэтому метод isValid должен проверить, выбрал ли пользователь тега
обеспечение значения рабочего цикла.

public class TwaTEI extends TagExtraInfo {


public boolean isValid(Tagdata data) {
Object o = data.getAttribute("attr1");
if (o != null && o != TagData.REQUEST_TIME_VALUE) {
if (((String)o).toLowerCase().equals("true") ||
((String)o).toLowerCase().equals("false") )
return true;
else
return false;
}
else
return true;

Web-

Rendered by www.RenderX.com
Стр. 470 из 626 Заказные теги в JSP-страницах

}
}

16.4.5. Теги с телами


16.4.5.1. Обработчики тегов
Обработчик тега с телом реализуется различными путями в зависимости от того, требуется
ли обработчику взаимодействовать с телом тега или нет. Под взаимодействием мы
подразумеваем, что обработчик тега считывает или модифицирует содержание тела.

16.4.5.1.1. Случай, когда обработчик тега не взаимодействует с его телом


Если обработчику тега не требуется взаимодействовать с его телом, первый должен
реализовать интерфейс Tag (или быть производным от класса TagSupport). Если телу тега
требуется оценка, метод doStartTag должен возвратить EVAL_BODY_INCLUDE, в противном
случае он возвратит SKIP_BODY.
Если обработчик тега должен многократно оценить тело, он должен реализовать интерфейс
IterationTag (или быть производным от класса TagSupport). Если обработчик определит,
что тело нуждается в дополнительной оценке, после выполнения методов doStartTag и
doAfterBody он должен возвратить EVAL_BODY_AGAIN.

16.4.5.1.2. Случай, когда обработчик тега взаимодействует с его телом


Если обработчику тега требуется взаимодействовать с телом, он должен реализовать
интерфейс BodyTag (или быть производным от класса BodyTagSupport). Подобные
обработчики обычно реализуют методы doInitBody и doAfterBody. Данные методы
взаимодействуют с содержимым тела, передаваемым обработчику тега при помощи
сервлета JSP-страницы.
В теле тега поддерживается несколько методов для чтения и записи своего содержимого.
Обработчик тега может использовать методы содержания getString или getReader для
извлечения информации из тела, а также метод writeOut(out) для записи содержимого тела
в исходящий поток. Writer, поставляемый с методом writeOut получен в результате
использования метода обработчика getPreviousOut. Данный метод используется для
подтверждения того, что результаты обработчика тега доступны обработчику родительского
тега.
Если требуется оценить тело тега, метод doStartTag должен возвратить
EVAL_BODY_BUFFERED, в противном случае он возвратит SKIP_BODY.
Метод doInitBody
Метод doInitBody вызывается после установки содержимого тела, но перед его оценкой.
Обычно данный метод используют для выполнения любого процесса инициализации,
зависимого от содержимого тела.
Метод doAfterBody
Метод doAfterBody вызывается после оценки содержимого тела.
Так же, как и метод doStartTag, метод doAfterBody должен возвратить указание, следует
ли продолжать оценку тела. Таким образом, если тело необходимо оценить снова, как в

Web-

Rendered by www.RenderX.com
Определение тегов Стр. 471 из 626

случае реализации тега итерации, doAfterBody возвратит EVAL_BODY_BUFFERED, в


противном же случае он возвратит SKIP_BODY.
Метод release
Обработчик тега должен вернуть свое начальное состояние и освободить все личные
ресурсы в методе release.
В следующем примере содержимое тела (которое содержит SQL-запрос) считывается и
передается объекту, выполняющему запрос. Дополнительной оценки тела не требуется,
поэтому метод doAfterBody возвращает SKIP_BODY.

public class QueryTag extends BodyTagSupport {


public int doAfterBody() throws JspTagException {
BodyContent bc = getBodyContent();
// bc
String query = bc.getString();
//
bc.clearBody();
try {
Statement stmt = connection.createStatement();
result = stmt.executeQuery(query);
} catch (SQLException e) {
throw new JspTagException("QueryTag: " +
e.getMessage());
}
return SKIP_BODY;
}
}

16.4.5.2. Элемент body-content


Для тегов с телом необходимо определить тип содержимого тела при помощи элемента
body-content:

<body-content>JSP|tagdependent</body-content>

Содержимое тела, в котором имеются заказные теги, теги ядра, элементы создания
сценариев и HTML-текст, относится к категории JSP. Это значение объявлено для тега
logic:present библиотеки Struts. Все остальные типы содержимого тела (например, SQL-
выражения, передаваемые тегу запроса) будут обозначаться как tagdependent.

Web-

Rendered by www.RenderX.com
Стр. 472 из 626 Заказные теги в JSP-страницах

Обратите внимание, что значение элемента body-content не влияет на интерпретацию


тела обработчиком его тега. Элемент используется только для отображения содержимого
тела в инструментальной среде.

16.4.6. Теги, определяющие переменные создания сценариев


16.4.6.1. Обработчики тегов
Обработчик тега отвечает за создание и установку в контекст, доступный из страницы,
объекта, на который ссылается переменная создания сценария. Это выполняется путем
использования методов pageContext.setAttribute(name, value, scope) или pageContext.setAt-
tribute(name, value). Обычно атрибут, передаваемый заказному тегу, задает имя объекта
переменной создания сценария. Это имя можно извлекать путем вызова метода атрибута
get, описанного в разделе Использование объектов области действия.
Если значение переменной создания сценария зависит от объекта, находящегося в
контексте обработчика тега, можно извлечь объект, используя метод pageContext.getAt-
tribute(name, scope).
В обычную процедуру входит извлечение обработчиком тега переменной создания
сценария, выполнение определенных операций над объектом, а затем установка значения
переменной создания сценария при помощи метода pageContext.setAttribute(name, object).
Области действия, которые могут иметь объекты, показаны в Таблице 15-4. Область
действия ограничивает источники доступа и жизненный цикл объекта.
Таблица 15-4 Области действия объектов
Имя Откуда доступен Жизненный цикл
page Данная страница До тех пор, пока ответ не будет
отправлен обратно к пользователю или
запрос не будет передан на другую
страницу
request Данная страница, а также любые До тех пор, пока ответ не будет
включенные или отправленные страницы отправлен обратно к пользователю
session Данный запрос и любые последующие Жизненный цикл сессии пользователя
запросы от того же броузера (если
сессия относится к жизненному циклу)
application Данный и все последующие запросы от Жизненный цикл приложения
того же web-приложения

16.4.6.2. Предоставление информации о переменной создания сценария


Пример, описанный в разделе Теги, определяющие переменные создания сценария,
определяет переменную создания сценария book, используемую для получения доступа
к информации о книге:

<bean:define id="book" name="bookDB" property="bookDetails"


type="database.BookDetails"/>
<font color="red" size="+2">
<%=messages.getString("CartRemoved")%>
<strong><jsp:getProperty name="book"

Web-

Rendered by www.RenderX.com
Определение тегов Стр. 473 из 626

property="title"/></strong>
<br>&nbsp;<br>
</font>

Когда транслируется JSP-страница, содержащая данный тег, web-контейнер генерирует


программный код для синхронизации переменной создания сценария с объектом, на
который ссылается переменная. Для того чтобы сгенерировать код, web-контейнеру
потребуется определенная информация о переменной создания сценария. А именно:
• Имя переменной
• Класс переменной
• Ссылается ли переменная на новый или на существующий объект
• Доступность переменной
Существует два способа для обеспечения данной информации: путем задания TLD-
подэлемента variable, или определением дополнительного информационного класса тега
и включением в TLD элемента tei-class. Использовать элемент variable проще, однако этот
способ является менее гибким.

16.4.6.2.1. Элемент variable


У элемента variable есть следующие подэлементы:
• name-given - имя переменной, константа</p></item> <item><p> name-from-attribute - имя
атрибута, чье значение в период трансляции даст имя переменной
• Требуется или name-given или name-from-attribute. Следующие элементы являются
необязательными:
• variable-class - полностью пригодное имя класса переменной. По-умолчанию
определяется значение java.lang.String.</p></item> <item><p> declare - ссылается ли
переменная на новый объект. По-умолчанию определяется значение True.</p></item>
<item><p> scope - область действия заданной переменной создания сценария. По-
умолчанию определяется значение NESTED. В Таблице 15-5 описано, где переменная
создания сценария является доступной, а также методы, в которых должно быть
установлено или переустановлено значение переменной.
Таблица 15-5 Доступность переменной создания сценария
Значение Где доступна переменная Методы
NESTED Между открывающим и закрывающим В doInitBody и doAfterBody для
тегами обработчика тега, реализующего Body-
Tag, в противном случае в doStartTag
AT_BEGIN В промежутке между открывающим В doInitBody, doAfterBody, и doEndTag
тегом и концом страницы для обработчика тега, реализующего
BodyTag, в противном случае в doStart-
Tag и doEndTag
AT_END После закрывающего тега и до конца В doEndTag
страницы

Реализация тега bean:define библиотеки Struts соответствует JSP-спецификации версии


1.1, которая требует от вас определения дополнительного информационного класса тега.

Web-

Rendered by www.RenderX.com
Стр. 474 из 626 Заказные теги в JSP-страницах

В JSP-спецификации версии 1.2 добавлен элемент variable. Вы можете определить


следующий элемент variable для тега bean:define:

<tag>
<variable>
<name-from-attribute>id</name-from-attribute>
<variable-class>database.BookDetails</variable-class>
<declare>true</declare>
<scope>AT_BEGIN</scope>
</variable>
</tag>

16.4.6.2.2. Класс TagExtraInfo


Определите дополнительный информационный класс тега путем расширения класса
javax.servlet.jsp.TagExtraInfo. TagExtraInfo должен реализовать метод getVariableInfo для
возвращения массива объектов VariableInfo, содержащих следующую информацию:
• Имя переменной
• Класс переменной
• Ссылается ли переменная на новый объект
• Доступность переменной
Web-контейнер передает параметр с названием data методу getVariableInfo, который
содержит кортежи атрибут-значение для каждого из атрибутов тега. Эти атрибуты могут
быть использованы для обеспечения объекта VariableInfo именем и классом переменной
создания сценария.
Библиотека тегов Struts предоставляет в дополнительном информационном классе тега
DefineTei информацию о переменных создания сценария, созданных тегом bean:define.
Имя (book) и класс (database.BookDetails) переменной создания сценария передаются как
атрибуты тегов, поэтому их можно извлечь при помощи метода data.getAttributeString и
использовать для заполнения конструктора VariableInfo. Для того чтобы переменную
создания сценарияbook можно было использовать в оставшейся части страницы, в качестве
области действия book устанавливается значение AT_BEGIN.

public class DefineTei extends TagExtraInfo {


public VariableInfo[] getVariableInfo(TagData data) {
String type = data.getAttributeString("type");
if (type == null)
type = "java.lang.Object";
return new VariableInfo[] {
new VariableInfo(data.getAttributeString("id"),

Web-

Rendered by www.RenderX.com
Определение тегов Стр. 475 из 626

type,
true,
VariableInfo.AT_BEGIN)
};
}
}

Полностью пригодное имя дополнительного информационного класса тега, определенное


для переменной создания сценария, должно быть объявлено в TLD в подэлементе tei-class
элемента tag. Таким образом, элемент tei-class для DefineTei будет выглядеть следующим
образом:

<tei-class>
org.apache.struts.taglib.bean.DefineTagTei
</tei-class>

16.4.7. Взаимодействие тегов


Теги взаимодействуют, используя совместный доступ к объектам. JSP-технология
поддерживает два стиля организации совместного доступа.
В первом из них требуется, чтобы общий объект был назван и хранился в контексте
страницы (один из неявных объектов, доступный как JSP-страницам, так и обработчикам
тегов). Для того чтобы получить доступ к объектам, созданным и названным другим тегом,
обработчик тегов использует метод pageContext.getAttribute(name, scope).
Во втором стиле организации совместного доступа объект, созданный обработчиком
родительского тега группы вложенных тегов, доступен всем обработчикам внутренних
тегов. Данная форма организации совместного доступа имеет преимущество, то есть
использует индивидуальное пространство имен для объектов, уменьшая, таким образом,
потенциальную возможность конфликта имен.
Для того чтобы получить доступ к объекту, созданному родительским тегом, обработчик
тега вначале должен получить родительский тег объекта при помощи статического метода
TagSupport.findAncestorWithClass(from, class) или метода TagSupport.getParent. Первый
метод следует использовать в тех случаях, когда невозможно гарантировать точное
вложение обработчиков тегов. Если предок был извлечен, обработчик тега может получить
доступ к любым объектам, созданным статическим или динамическим образом. Статическим
образом созданными объектами являются члены родительских объектов. Некоторые
объекты могут также быть созданы динамично. Подобные объекты могут храниться в
обработчике тега, используя метод setValue, и извлекаться при помощи метода getValue.
В следующем примере представлен обработчик тега, поддерживающий, как названные,
так и частные объектные подходы к организации совместного доступа. В примере
обработчик для тега запроса проверяет, установлен ли атрибут с именем connection в
методе doStartTag. Если атрибут connection установлен, обработчик извлекает объект

Web-

Rendered by www.RenderX.com
Стр. 476 из 626 Заказные теги в JSP-страницах

соединения из контекста страницы. В противном случае обработчик тега сначала извлекает


обработчик родительского тега и затем уже извлекает объект соединения из основного
обработчика.

public class QueryTag extends BodyTagSupport {


private String connectionId;
public int doStartTag() throws JspException {
String cid = getConnection();
if (cid != null) {
// id ,

connection =(Connection)pageContext.
getAttribute(cid);
} else {
ConnectionTag ancestorTag =
(ConnectionTag)findAncestorWithClass(this,
ConnectionTag.class);
if (ancestorTag == null) {
throw new JspTagException("A query without
a connection attribute must be nested
within a connection tag.");
}
connection = ancestorTag.getConnection();
}
}
}

Тег запроса, реализованный данным обработчиком тега, может быть использован одним
из нижеприведенных способов:

<tt:connection id="con01" ....> ... </tt:connection>


<tt:query id="balances" connection="con01">
SELECT account, balance FROM acct_table
where customer_number = <%= request.getCustno()%>
</tt:query>

<tt:connection ...>
<x:query id="balances">
SELECT account, balance FROM acct_table

Web-

Rendered by www.RenderX.com
Примеры Стр. 477 из 626

where customer_number = <%= request.getCustno()%>


</x:query>
</tt:connection>

TLD для обработчика тега при помощи следующего объявления должен указывать на то,
что атрибут connection является необязательным:

<tag>
...
<attribute>
<name>connection</name>
<required>false</required>
</attribute>
</tag>

16.5. Примеры
Заказные теги, описанные в данном разделе, демонстрируют решения двух часто
встречающихся проблем в разработке JSP-приложений. Ими являются минимизация
объема Java-программирования на JSP-страницах и гарантия единого восприятия всех
приложений. При этом данные теги иллюстрируют множество стилей тегов, которые
обсуждались в первой части главы.

16.5.1. Тег итераций


Конструирование содержимого страницы, зависящего от динамически генерируемых
данных, часто требует использования инструкций сценария управления потоками. Путем
перемещения логики управления потоками в обработчики тегов, теги управления потоками
уменьшают количество сценариев, требуемых JSP-странице.
Тег logic:iterate библиотеки Struts извлекает объекты из коллекции, хранящейся в JavaBean-
компоненте, и присваивает их переменной создания сценария. Тело тега извлекает
информацию из переменной создания сценария. Пока элементы остаются в коллекции,
тег iterate вызывает переоценку тела.

16.5.1.1. JSP-страница
Страницы catalog.jsp и showcart.jsp приложения Duke's Bookstore используют тег logic:iterate
для выполнения процесса итерации в коллекции объектов. Выдержка из страницы catalog.jsp
представлена ниже. JSP-страница инициализирует тег iterate с коллекцией (названной
атрибутом property) компонента bookDB. Тег iterate устанавливает переменную создания
сценарияbook при каждой итерации всей коллекции. Свойство bookId переменной book
используется как другая переменная создания сценария. Свойства обеих переменных
используются для динамического генерирования таблицы, содержащей ссылки на другие
страницы и информацию книжного каталога.

Web-

Rendered by www.RenderX.com
Стр. 478 из 626 Заказные теги в JSP-страницах

<logic:iterate name="bookDB" property="books"


id="book" type="database.BookDetails">
<bean:define id="bookId" name="book" property="bookId"
type="java.lang.String"/>

<tr>
<td bgcolor="#ffffaa">
<a href="<%=request.getContextPath()%>
/bookdetails?bookId=<%=bookId%>">
<strong><jsp:getProperty name="book"
property="title"/>&nbsp;</strong></a></td>

<td bgcolor="#ffffaa" rowspan=2>


<jsp:setProperty name="currency" property="amount"
value="<%=book.getPrice()%>"/>
<jsp:getProperty name="currency" property="format"/>
&nbsp;</td>

<td bgcolor="#ffffaa" rowspan=2>


<a href="<%=request.getContextPath()%>
/catalog?Add=<%=bookId%>">
&nbsp;<%=messages.getString("CartAdd")%>
&nbsp;</a></td></tr>

<tr>
<td bgcolor="#ffffff">
&nbsp;&nbsp;<%=messages.getString("By")%> <em>
<jsp:getProperty name="book"
property="firstName"/>&nbsp;
<jsp:getProperty name="book"
property="surname"/></em></td></tr>
</logic:iterate>

16.5.1.2. Обработчик тега


Реализация тега logic:iterate библиотеки Struts соответствует возможностям JSP-
спецификации версии 1.1, которая требует от вас расширения класса BodyTagSupport. В
JSP-спецификацию версии 1.2 добавлены свойства (описанные в разделе Случай, когда
обработчик тега не взаимодействует с телом), упрощающие программные теги, которые
многократно оценивают их тело. Дальнейшее обсуждение базируется на реализации,
использующей данные свойства.

Web-

Rendered by www.RenderX.com
Примеры Стр. 479 из 626

Тег logic:iterate поддерживает инициализацию коллекции несколькими способами: из


коллекции, представленной как атрибут тега или же из коллекции, являющейся компонентом
или его свойством. В нашем примере используется последний способ. Большая часть
программного кода в doStartTag связана с конструированием итератора объекта коллекции.
Метод вначале проверяет, установлено ли свойство коллекции обработчика и, если это
не так, продолжает проверять атрибуты компонента и свойства. Если установлены и
атрибут name, и property, тогда метод doStartTag вызывает метод utility, который использует
методы интроинспекции JavaBean-компонентов для извлечения коллекции. Если объект
коллекции задан, метод конструирует итератор.
Если в итераторе содержится больше элементов, метод doStartTag устанавливает значение
переменной создания сценария следующему элементу и затем указывает на то, что тело
должно подвергнуться оценке. В противном случае метод прекращает итерацию, возвращая
значение SKIP_BODY.
После оценки тела метод doAfterBody извлекает содержимое тела и записывает его в
исходящий поток. Затем в процессе подготовки для другой оценки тела объект содержимого
тела очищается. Если в итераторе содержится больше элементов, doAfterBody снова
устанавливает значение переменной создания сценария следующему элементу и
возвращает EVAL_BODY_AGAIN, что указывает на необходимость в повторной оценке
тела. Это приводит к повторному выполнению метода doAfterBody. Когда элементов не
остается, метод doAfterBody завершит процесс, возвращая при этом значение SKIP_BODY.

public class IterateTag extends TagSupport {


protected Iterator iterator = null;
protected Object collection = null;
protected String id = null;
protected String name = null;
protected String property = null;
protected String type = null;
public int doStartTag() throws JspException {
Object collection = this.collection;
if (collection == null) {
try {
Object bean = pageContext.findAttribute(name);
if (bean == null) {
... throw an exception
}
if (property == null)
collection = bean;
else
collection =
PropertyUtils.
getProperty(bean, property);

Web-

Rendered by www.RenderX.com
Стр. 480 из 626 Заказные теги в JSP-страницах

if (collection == null) {
... throw an exception
}
} catch
... catch exceptions thrown
by PropertyUtils.getProperty
}
}
//

if (collection instanceof Collection)


iterator = ((Collection) collection).iterator();
else if (collection instanceof Iterator)
iterator = (Iterator) collection;
...
}
//
// ,
if (iterator.hasNext()) {
Object element = iterator.next();
pageContext.setAttribute(id, element);
return (EVAL_BODY_AGAIN);
} else
return (SKIP_BODY);
}
public int doAfterBody() throws JspException {
if (bodyContent != null) {
try {
JspWriter out = getPreviousOut();
out.print(bodyContent.getString());
bodyContent.clearBody();
} catch (IOException e) {
...
}
}
if (iterator.hasNext()) {
Object element = iterator.next();
pageContext.setAttribute(id, element);
return (EVAL_BODY_AGAIN);
} else
return (SKIP_BODY);

Web-

Rendered by www.RenderX.com
Примеры Стр. 481 из 626

}
}
}

16.5.1.3. Дополнительный информационный класс тега


Информация о переменной создания сценария представлена в дополнительном
информационном классе тега IterateTei. Имя и класс переменной создания сценария
передаются как атрибуты тега и используются для заполнения конструктора VariableInfo.

public class IterateTei extends TagExtraInfo {


public VariableInfo[] getVariableInfo(TagData data) {
String type = data.getAttributeString("type");
if (type == null)
type = "java.lang.Object";

return new VariableInfo[] {


new VariableInfo(data.getAttributeString("id"),
type,
true,
VariableInfo.AT_BEGIN)
};
}
}

16.5.2. Библиотека тегов шаблона


Шаблон обеспечивает способ отделения общих элементов, являющихся частью каждого
экрана, от элементов, изменяющихся при каждом экране приложения. Размещение всех
общих элементов в одном файле упрощает поддержку и создание единого восприятия
всех экранов приложения. Это также упрощает разработку индивидуальных экранов, так
как сам разработчик может сфокусировать свое внимание на отдельных элементах,
характерных для данного экрана, в то время как шаблон "позаботится" об остальных.
Шаблоном является JSP-страница с "заполнителями" для частей, требующих изменений
в каждом экране. Каждый из таких заполнителей называют параметром шаблона. Например,
для отображения заказного содержимого страницы простой шаблон может включать в
себя параметр названия для шапки общего экрана и параметр тела, ссылающийся на JSP-
страницу.
В шаблоне используется набор вложенных тегов (definition, screen, и parameter) для задания
таблицы определений экранов, а также тег insert для вставки параметров из определения
экрана в отдельное окно приложения.

Web-

Rendered by www.RenderX.com
Стр. 482 из 626 Заказные теги в JSP-страницах

16.5.2.1. JSP-страница
Далее представлен шаблон template.jsp для примера Duke's Bookstore. Данная страница
включает в себя JSP-страницу, создающую определение экрана, а затем использует тег
insert для вставки параметров из определения в окно приложения.

<%@ taglib uri="/tutorial-template.tld" prefix="tt" %>


<%@ page errorPage="errorpage.jsp" %>
<%@ include file="screendefinitions.jsp" %><html>
<head>
<title>
<tt:insert definition="bookstore"
parameter="title"/>
</title>
</head>
<tt:insert definition="bookstore"
parameter="banner"/>
<tt:insert definition="bookstore"
parameter="body"/>
</body>
</html>

Страница screendefinitions.jsp создает определение экрана на основе атрибута запроса


selectedScreen:

<tt:definition name="bookstore"
screen="<%= (String)request.
getAttribute(\"selectedScreen\") %>">
<tt:screen id="/enter">
<tt:parameter name="title"
value="Duke's Bookstore" direct="true"/>
<tt:parameter name="banner"
value="/banner.jsp" direct="false"/>
<tt:parameter name="body"
value="/bookstore.jsp" direct="false"/>
</tt:screen>
<tt:screen id="/catalog">
<tt:parameter name="title"
value="<%=messages.getString("TitleBookCatalog")%>"
direct="true"/>

Web-

Rendered by www.RenderX.com
Примеры Стр. 483 из 626

...
</tt:definition>

Экземпляр шаблона создается сервлетом Dispatcher, который первым получает


запрошенное окно и хранит его как атрибут запроса. Это необходимо по причине того, что,
когда запрос пересылается странице template.jsp, URL запроса не содержит первоначальный
запрос (например, /bookstore3/catalog). Вместо этого он отражает путь (/book-
store3/template.jsp) отправленной страницы. Наконец, сервлет пересылает запрос странице
template.jsp:

public class Dispatcher extends HttpServlet {


public void doGet(HttpServletRequest request,
HttpServletResponse response) {
request.setAttribute("selectedScreen",
request.getServletPath());
RequestDispatcher dispatcher =
request.getRequestDispatcher("/template.jsp");
if (dispatcher != null)
dispatcher.forward(request, response);
}
public void doPost(HttpServletRequest request,
HttpServletResponse response) {
request.setAttribute("selectedScreen",
request.getServletPath());
RequestDispatcher dispatcher =
request.getRequestDispatcher("/template.jsp");
if (dispatcher != null)
dispatcher.forward(request, response);
}
}

16.5.2.2. Обработчики тегов


В шаблоне библиотеки тегов содержится четыре обработчика тегов (DefinitionTag,
ScreenTag, ParameterTag, и InsertTag), которые демонстрируют пользу взаимодействия
тегов. DefinitionTag, ScreenTag, и ParameterTag включают в себя набор обработчиков
вложенных тегов, которые разделяют объекты public и private. DefinitionTag создает объект
public с названием definition, используемый тегом InsertTag.
В методе doStartTag, обработчик DefinitionTag создает объект public с названием screens,
который содержит хэш-таблицу определений экранов. Определение экрана состоит из
идентификатора экрана и набора параметров, ассоциированных с экраном.

Web-

Rendered by www.RenderX.com
Стр. 484 из 626 Заказные теги в JSP-страницах

public int doStartTag() {


HashMap screens = null;
screens = (HashMap) pageContext.getAttribute("screens",
pageContext.APPLICATION_SCOPE);
if (screens == null)
pageContext.setAttribute("screens", new HashMap(),
pageContext.APPLICATION_SCOPE);
return EVAL_BODY_INCLUDE;
}

Таблица определений экранов заполнена при помощи тегов ScreenTag и ParameterTag из


текста, представленного как атрибуты данных тегов. В Таблице 15-6 показано содержание
хэш-таблицы определений экранов для приложения Duke's Bookstore.
Таблица 15-6 Определения экранов
Id экрана Заголовок Шапка Тело
/enter Duke's Bookstore /banner.jsp /bookstore.jsp
/catalog Book Catalog (Каталог книг) /banner.jsp /bookstore.jsp
/bookdetails Book Description (Описание /banner.jsp /bookdetails.jsp
книги)
/showcart Shopping Cart (Корзина /banner.jsp /showcart.jsp
покупателя)
/cashier Cashier (Кассир) /banner.jsp /cashier.jsp
/receipt Receipt (Квитанция) /banner.jsp /receipt.jsp

В методе doEndTag, обработчик DefinitionTag создает объект public класса Definition,


выбирает определение экрана из объекта screens, базирующегося на URL-адресе,
переданном в запросе, а затем использует его для инициализации объекта Definition.

public int doEndTag()throws JspTagException {


try {
Definition definition = new Definition();
HashMap screens = null;
ArrayList params = null;
TagSupport screen = null;
screens = (HashMap)
pageContext.getAttribute("screens",
pageContext.APPLICATION_SCOPE);
if (screens != null)
params = (ArrayList) screens.get(screenId);
else

Web-

Rendered by www.RenderX.com
Примеры Стр. 485 из 626

...
if (params == null)
...
Iterator ir = null;
if (params != null)
ir = params.iterator();
while ((ir != null) && ir.hasNext())
definition.setParam((Parameter) ir.next());
//

pageContext.setAttribute(
definitionName, definition);
} catch (Exception ex) {
ex.printStackTrace();
}
return EVAL_PAGE;
}

Если URL-адресом, переданным в запросе, является /enter, объект Definition содержит


пункты из первой строки Таблицы 15-6:
Определение для URL /enter показано в Таблице 15-7. В нем указано, что значение
параметра Title приложения Duke's Bookstore должно вставляться прямо в исходящий
поток, однако значения Banner и Body следует включить динамически.
Таблица 15-7 Определение экрана для URL /enter
Имя параметра Значение параметра isDirect
title Duke's Bookstore true

InsertTag использует объект Definition для вставки параметров определения экрана в ответ.
В методе doStartTag он извлекает объект определения из контекста страницы.

public int doStartTag() {


//

definition = (Definition) pageContext.


getAttribute(definitionName);
//
if (parameterName != null && definition != null)
parameter = (Parameter)definition.
getParam(parameterName);
if (parameter != null)

Web-

Rendered by www.RenderX.com
Стр. 486 из 626 Заказные теги в JSP-страницах

directInclude = parameter.isDirect();
return SKIP_BODY;
}

Метод doEndTag вставляет значение параметра. Если параметр прямой, он напрямую


вставляется в ответ, в противном случае, запрос посылается параметру, а ответ
динамически включается в полный ответ.

public int doEndTag()throws JspTagException {


try {
if (directInclude && parameter != null)
pageContext.getOut().print(parameter.getValue());
else {
if ((parameter != null) &&
(parameter.getValue() != null))
pageContext.include(parameter.getValue());
}
} catch (Exception ex) {
throw new JspTagException(ex.getMessage());
}
return EVAL_PAGE;
}

16.5.3. Как вызывается обработчик тега?


Интерфейс Tag определяет основной протокол между обработчиком тега и сервлетом
JSP-страницы. Он определяет жизненный цикл и методы, которые требуется вызвать при
обнаружении открывающего и закрывающего тегов.
Перед вызовом метода doStartTag сервлет JSP-страницы вызывает setPageContext,
setParent и методы установки атрибута. Сервлет JSP-страницы также гарантирует, что
метод release будет вызван в обработчике тега до окончания страницы.
Типичная последовательность вызова метода обработчика тега выглядит следующим
образом:

ATag t = new ATag();


t.setPageContext(...);
t.setParent(...);
t.setAttribute1(value1);
t.setAttribute2(value2);

Web-

Rendered by www.RenderX.com
Примеры Стр. 487 из 626

t.doStartTag();
t.doEndTag();
t.release();

Интерфейс BodyTag расширяет Tag путем задания дополнительных методов, позволяющих


обработчику тега получить доступ к своему телу. Интерфейс обеспечивает три новых
метода:
• SetBodyContent - создает содержимое тела и добавляет его в обработчик тега
• DoInitBody - вызывается до оценки тела тега
• DoAfterBody - вызывается после оценки тела тега
Типичная последовательность вызова:

t.doStartTag();
out = pageContext.pushBody();
t.setBodyContent(out);
// ,

t.doInitBody();
t.doAfterBody();
// doAfterBody EVAL_BODY_BUFFERED
//
...
t.doAfterBody();
t.doEndTag();
t.pageContext.popBody();
t.release();

17. Стандартная библиотека тегов JavaServer-страниц


Стефани Бодофф
Стандартная библиотека тегов JavaServer-страниц (The JavaServer Pages Standard Tag
Library - JSTL) включает в себя центральные функции, которые являются общими для
многих JSP-приложений. Например, вместо того, чтобы выполнять процесс итерации в
нескольких списках при помощи скриптлета или различных тегов итерации для каждого
поставщика в отдельности, JSTL определяет единый стандартный тег итерации,
работающий везде одинаково. При данном подходе можно знать лишь один стандартный

Web-

Rendered by www.RenderX.com
Стр. 488 из 626 Стандартная библиотека тегов JavaServer-страниц

тег для заданной операции и использовать его в различных JSP-контейнерах. Кроме этого,
когда теги являются стандартными, контейнеры могут оптимизировать их реализацию.
У JSTL есть поддержка общих структурных задач, таких как итерация и задачи с условиями,
есть также теги управления XML-документами, теги интернационализации и теги для
организации доступа к базам данных, использующих язык SQL. Кроме этого, вводится
концепция языка выражений, упрощающая разработку страниц. JSTL, к тому же,
обеспечивает основу для интеграции существующих библиотек тегов с библиотекой JSTL.
В данной главе при рассмотрении библиотеки JSTL используются выдержки из JSP-версии
приложения Duke's Bookstore. Допускается, что вы уже знакомы с материалом раздела
Использование тегов главы 15.

17.1. Примеры JSP-страниц


В данной главе при рассмотрении библиотеки JSTL используются выдержки из JSP-версии
приложения Duke's Bookstore, которое обсуждалось в главе 15 и было переписано
следующим образом:
• Теги структурной логики были заменены на центральные JSTL-теги.
• Скриптлеты, имеющие доступ к хранилищам сообщений, были заменены на теги,
форматирующие сообщения.
• Вспомогательный объект базы данных JavaBean-компонентов был заменен на прямые
обращения к базе данных посредством SQL-тегов библиотеки JSTL. Для большинства
приложений лучше всего инкапсулировать вызовы к базе данных в компоненты. JSTL
включает в себя SQL-теги для ситуаций, когда новое приложение уже моделируется,
а определить затраты на создание компонента не представляется возможным.
Исходный код приложения Duke's Bookstore расположен в директории
<JWSDP_HOME>/docs/tutorial/examples/web/bookstore4, созданной при распаковке
руководства (см. Запуск примеров).
Для того чтобы создать, установить и запустить пример:
1. В окне терминала перейдите в директорию <JWSDP_HOME>/docs/tutorial/exam-
ples/web/bookstore4.
2. Запустите ant build. Исполнитель build вызовет все необходимые компиляции и скопирует
файлы в директорию <JWSDP_HOME>/docs/tutorial/examples/web/bookstore4/build.
3. Удостоверьтесь, что Tomcat запущен.
4. Запустите ant install. Исполнительinstall оповестит Tomcat о том, что стал доступен
новый контекст.
5. Запустите сервер базы данных PointBase и заполните базу данных, если вы еще этого
не сделали (см. Организация доступа к базам данных из Web-приложений).
6. Откройте URL-адрес магазина http://localhost:8080/bookstore4/enter.
Для размещения приложения используйте утилиту deploytool:
1. Удостоверьтесь, что Tomcat запущен
2. Запустите утилиту deploytool.

Web-

Rendered by www.RenderX.com
Примеры JSP-страниц Стр. 489 из 626

3. Создайте Web-приложение с названием bookstore4.


4.
A. Выберите File --> New Web Application.
B. Нажмите Browse.
C. В меню выбора файлов перейдите в директорию <JWSDP_HOME>/docs/tutorial/exam-
ples/web/bookstore4/build.
D. В поле File Name введите bookstore4.
E. Нажмите на Choose Module File.
F. В поле WAR Display Name введите bookstore4.
G. Для добавления файлов содержания нажмите Edit. В диалоговом окне Edit Contents
перейдите в директорию <JWSDP_HOME>/docs/tutorial/examples/web/web/book-
store4/build. Выберите JSP-страницы banner.jsp, bookstore.jsp, bookdetails.jsp, cata-
log.jsp, showcart.jsp, cashier.jsp, receipt.jsp, template.jsp, screendefinitions.jsp, и error-
page.jsp, а также файл duke.books.gif, после чего нажмите Add. Перейдите в WEB-
INF. Добавьте Dispatcher.class, а также пакеты cart, database, messages, taglib и util
из classes. Добавьте JSTL TLD и библиотеки из lib. Нажмите OK.
H. Нажмите Next.
I. Выберите переключатель Servlet.
J. Нажмите Next.
K. Выберите из комбинированного окна класса Servlet пункт Dispatcher.
L. Нажмите Finish.
5. Добавьте псевдонимы.
6.
A. Выберите Dispatcher
B. Выберите закладку Aliases.
C. Нажмите Add, затем введите /enter в поле Aliases. Продолжайте добавлять
псевдонимы /catalog, /bookdetails, /showcart, /cashier, и /receipt.
7. Добавьте параметр контекста базового имени JSTL.
8.
A. Выберите закладку Context.
B. Нажмите Add.
C. Введите в качестве Coded Parameter следующее: javax.servlet.jsp.jstl.fmt.localization-
Context.
D. Введите в качестве Value следующее: messages.BookstoreMessages.
9. Добавьте ссылку на ресурс базы данных.
10.
A. Выберите WAR.
B. Выберите закладку Resource Refs.
C. Нажмите Add.

Web-

Rendered by www.RenderX.com
Стр. 490 из 626 Стандартная библиотека тегов JavaServer-страниц

D. Выберите из колонки Type javax.sql.DataSource.


E. В поле Coded Name введите jdbc/BookDB.
F. Нажмите кнопку Import Data Sources.
G. Появится диалоговое окно с подтверждением.
H. Выберите из ниспадающего списка pointbase.
11. Добавьте URL-адрес библиотеки тегов список адресов (см. Объявление библиотек
тегов):
12.
A. Выберите закладку File Refs.
B. Нажмите на кнопку Add в закладке JSP Tag Libraries.
C. В поле Coded Reference введите локальный URI /tutorial-template.
D. В поле Tag Library введите полный URL /WEB-INF/tutorial-template.tld.
E. Повторите это от /jstl-c до /WEB-INF/c.tld, от /jstl-fmt до /WEB-INF/fmt.tld, и от /jstl-sql
до /WEB-INF/sql.tld.
13. Разместите приложение.
14.
A. Выберите Tools --> Deploy.
B. Нажмите OK, чтобы выбрать путь контекста /bookstore4 по умолчанию.
C. Нажмите Finish.
Если вы хотите лучше разбираться в выявлении распространенных ошибок, обратитесь
к разделам Распространенные проблемы и их решения и Устранение неисправностей.

17.2. Использование JSTL


JSTL включает в себя большое разнообразие тегов, которые естественным образом
подходят для конкретных функциональных областей. Следовательно, JSTL раскрывается
посредством многочисленных дескрипторов библиотек тегов (Tag Library Descriptor - TLD).
Это позволяет чисто отображать функциональные области, которые он покрывает, и
присваивать каждой такой области собственные пространства имен. В Таблице 16-1
показаны подобные функциональные области вместе с их логическими TDL-именами и
префиксами, использующимися в данной главе и в приложении Duke's Bookstore.
Таблица 16-1 Теги JSTL

Web-

Rendered by www.RenderX.com
Использование JSTL Стр. 491 из 626

Область Функция Теги TLD Префикс


Ядро Поддержка языка catch /jstl-c c
выражений out </p><p> remove
set
Управление потоками choose
when
otherwise
forEach
forTokens
if
Управление URL import
paramredirect
param
url
param
XML Ядро out /jstl-x x
parse
set
Управление потоками choose when otherwise
forEach
if
Преобразование transform
param
I18n Установка информации setLocale /jstl-fmt fmt
о регионе
Форматирование bundle
сообщений message
param
setBundle
Форматирование чисел formatNumber
и дат formatDate
parseDate
parseNumber
setTimeZone
timeZone
База данных setDataSource /jstl-sql sql
SQL query
dateParam
param
transaction
update dateParam
param

К примеру, для того чтобы использовать центральные теги JSTL и JSP-странице, объявите
библиотеку, используя директиву taglib, которая ссылается на TLD:

<%@ taglib uri="/jstl-core" prefix="c" %>

Библиотеки тегов JSTL выпускаются в двух версиях (см. Двойные библиотеки). TLD для
библиотеки JSTL-EL называются prefix.tld. TLD для библиотеки JSTL-RT называются prefix-
rt.tld. В виду того, что примеры, обсуждаемые в данной главе, используют логические TLD-
имена, мы отображаем логические имена в реальные TLD-локации с элементами taglib в
дескрипторе размещения Web-приложения. Далее представлен код, отображающий
центральное логическое TLD-имя библиотеки /jstl-c в свое местоположение /WEB-INF/c.tld:

Web-

Rendered by www.RenderX.com
Стр. 492 из 626 Стандартная библиотека тегов JavaServer-страниц

<taglib>
<taglib-uri>/jstl-c</taglib-uri>
<taglib-location>/WEB-INF/c.tld</taglib-location>
</taglib>

В Java WSDP JSTL TLD хранятся в директории <JWSDP_HOME>/tools/jstl/tlds. При создании


приложения Duke's Bookstore данные TLD автоматически копируются в директорию
<JWSDP_HOME>/docs/tutorial/examples/web/bookstore4/build/WEB-INF.
Вы также можете сослаться на TLD в директиве taglib с помощью полного URL-адреса:
• Ядро: http://java.sun.com/jstl/core
• XML: http://java.sun.com/jstl/xml
• Интернационализация: http://java.sun.com/jstl/fmt
• SQL: http://java.sun.com/jstl/sql
При использовании полного URL-адреса вам не требуется добавлять элемент taglib в
web.xml. JSP-контейнер автоматически определяет TLD внутри реализации библиотеки
JSTL.
Кроме объявления библиотеки тегов, необходимо также сделать доступными для Web-
приложения JSTL API и реализацию. Они распределены, как архивы jstl.jar в директории
<JWSDP_HOME>/tools/jstl и standard.jar в директории <JWSDP_HOME>/tools/jstl/standard.
Во время создания приложения Duke's Bookstore данные библиотеки автоматически
копируются в директорию <JWSDP_HOME>/docs/tutorial/examples/web/bookstore4/build/WEB-
INF/lib.

17.3. Поддержка языка выражений


Основным качеством, характеризующим JSTL, является его поддержка языка выражений
(expression language - EL). Язык выражений в сочетании с JSTL тегами помогает
осуществлять более простой доступ к данным приложений и более простое управление,
не требующее использования скриптлетов или выражений периода запроса. Теперь автору
страницы для осуществления доступа к значению системы или определенному
пользователем JavaBean-компоненту требуется использовать выражение <%= aName %>.
Например:

<x:aTag att="<%= pageContext.getAttribute("aName") %>">

Ссылки на вложенные свойства компонента являются еще более сложными:

Web-

Rendered by www.RenderX.com
Поддержка языка выражений Стр. 493 из 626

<%= aName.getFoo().getBar() %>

Это усложняет процесс создания страниц.


Язык выражений позволяет автору страницы получить доступ к объекту, используя для
этого упрощенный синтаксис, такой как

<x:atag att="${aName}">

для простой переменной или

<x:aTag att="${aName.foo.bar}">

для вложенного свойства.


Язык выражений JSTL предлагает атрибуты области действия JSP в качестве стандартного
способа для связи информации из бизнес-логики с JSP-страницами. Например, атрибут
test данного условного тега поставляется вместе с выражением, которое сравнивает число
элементов атрибута cart области действия сессии с нулем:

<c:if test="${sessionScope.cart.numberOfItems > 0}">


...
</c:if>

В следующей версии JSP-спецификации язык выражений будет являться стандартным


для всех библиотек заказных тегов. В данной версии JSTL включен только отрывок языка
выражений.

17.3.1. Двойные библиотеки


Библиотеки тегов JSTL поставляются в двух версиях, которые отличаются только способами
поддержки использования выражений рабочего цикла для значений атрибутов.
В библиотеке тегов JSTL выражения задаются на языке сценариев страницы. Именно так
сейчас функционируют современные библиотеки тегов.
В библиотеке тегов JSTL-EL выражения задаются на языке выражений JSTL. В синтаксисе
EL выражение является литералом String.
Используя библиотеку тегов EL, нельзя передать выражение языка сценариев в качестве
значения атрибута. Благодаря данному правилу становится возможным утвердить синтаксис
выражения во время трансляции.

Web-

Rendered by www.RenderX.com
Стр. 494 из 626 Стандартная библиотека тегов JavaServer-страниц

17.3.2. Язык выражений JSTL


Язык выражений JSTL отвечает за обработку, как выражений, так и литералов. Выражения
заключены в символы ${ }. Например:

<c:if test="${bean1.a < 3}" />

Любое значение, не начинающееся с ${, рассматривается как литерал, который


анализируется согласно ожидаемому типу. Для этого используется PropertyEditor:

<c:if test="true" />

Для значений литералов, которые содержат символ ${, следует применить переход:

<mytags:example attr1="an expression is ${'${'}true}" />

17.3.2.1. Атрибуты
Доступ к атрибутам осуществляется по имени с дополнительной областью действия. К
свойствам атрибутов, которые могут быть вложены в любом порядке, доступ
осуществляется при помощи оператора точка (.).
EL объединяет обработку операторов . и []. Таким образом expr-a.expr-b эквивалентен
expr-a[expr-b]. Чтобы вычислить expr-a[expr-b], переведите значение expr-a в value-a, а
значение expr-b в value-b.
• Если value-a является Map, возвратите value-a.get(value-b).
• Если value-a является List массива, переведите value-b на int и возвратите value-
a.get(value-b) или Array.get(value-a, value-b), по обстоятельствам.
• Если же value-a является JavaBean-объектом, переведите value-b в String. Если value-
b - это читаемое свойство value-a, возвратится вызов.
EL оценивает идентификатор путем поиска его значения как атрибута, в соответствии с
поведением PageContext.findAttribute(String). Например, ${product} будет искать атрибут с
названием product в области действия страницы, запроса, сессии и приложения, после
чего возвратит его значение. Если атрибут не найден, возвратится null. Обратите внимание,
что идентификатор, который соответствует одному из неявных объектов, описанных в
следующем разделе, возвратит сам неявный объект вместо значения атрибута.

17.3.2.2. Неявные объекты


Язык выражений JSTL определяет набор неявных объектов:
• PageContext - объект PageContext

Web-

Rendered by www.RenderX.com
Поддержка языка выражений Стр. 495 из 626

• pageScope - Map, отображающий имена атрибутов области действия страницы на их


значения
• requestScope - Map, отображающий имена атрибутов области действия сессии на их
значения
• sessionScope - Map, отображающий имена атрибутов области действия запроса на их
значения
• applicationScope - Map, отображающий имена атрибутов области действия приложения
на их значения
• param - Map, отображающий имена параметров на отдельное значение параметра
String (полученное путем вызова ServletRequest.getParameter(String))
• paramValues - Map, отображающий имена параметров на String[] всех значений для
этого параметра (полученного путем вызова ServletRequest.getParameterValues(String))
• header - Map, который отображает имена заголовков на отдельное значение заголовка
String (полученное путем вызова ServletRequest.getheader(String))
• headerValues - Map, который отображает имена заголовков на String[] всех значений
для этого параметра (получен путем вызова ServletRequest.getHeaders(String))
• cookie - Map, который отображает имена файлов-cookie в отдельный Cookie (полученный
путем вызова HttpServletRequest.getCookie(String))
• initParam - Map, который отображает имена параметров на значение отдельного
параметра String (полученное путем вызова ServletRequest.getInitParameter(String))
Когда выражение ссылается на один из этих объектов по имени, вместо атрибута
возвращается соответствующий объект. Например: ${pageContext} возвращает объект
PageContext, даже если имеется существующий атрибут pageContext, содержащий какое-
либо другое значение. В Таблице 16-2 показаны некоторые примеры использования
подобных неявных объектов.
Таблица 16-2 Примеры JSTL-выражений
Выражение Результат
${pageContext.request.contextPath} Путь контекста (полученный из HttpServletRequest)
${sessionScope.cart.numberOfItems} Свойство numberOfItems атрибута cart области действия
сессии
${param["mycom.productId"]} Значение String параметра mycom.productId

17.3.2.3. Литералы
• Boolean: true и false
• Long: как в Java
• Floating point: как в Java
• String: с одиночными и двойными кавычками. " получено из \", ' получено из \', и \ получено
из \\.
• Null: null

Web-

Rendered by www.RenderX.com
Стр. 496 из 626 Стандартная библиотека тегов JavaServer-страниц

17.3.2.4. Операторы
EL обеспечивает следующие операторы:
• Арифметические: +, -, *, / и div, % и mod, -
• Логические: and, &&, or, ||, not, !
• Операторы сравнений: ==, eq, !=, ne, <, lt, >, gt, <=, ge, >=, le. Сравнивать можно и с
другими значениями или с литералами boolean, string, integer или floating point.
• Empty: Оператор empty является префиксной операцией, с помощью которой можно
определить, является ли значением null или empty.
Чтобы узнать больше о других возможностях данных операторов обратитесь к документу
Спецификация JSTL 1.0.

17.3.3. Взаимодействие тегов


Теги обычно взаимодействуют со своим окружением явным и неявным образом. Неявное
взаимодействие осуществляется посредством вполне определенного интерфейса,
позволяющего вложенным тегам работать единым образом со своим предком,
задействованным в данном интерфейсе. JSTL-теги литералов поддерживают данный
режим взаимодействия.
Явные связи встречаются, когда тег предоставляет информацию для своего окружения.
Традиционно это делается путем раскрытия переменной создания сценария (реальный
объект обеспечивается атрибутом области действия JSP-страницы). Вследствие того, что
JSTL имеет язык выражений, потребность в переменных создания сценария уменьшается.
Таким образом, JSTL-теги (и EL и RT версии) раскрывают информацию только как атрибуты
области действия JSP-страницы, переменные создания сценария не используются. Обычно
JSTL использует имя var для любого атрибута тега, который экспортирует информацию о
теге. Например, тег forEach представляет текущую позицию корзины покупателя в процессе
итерации всего списка следующим образом:

<c:forEach var="item" items="${sessionScope.cart.items}">


...
</c:forEach>

Имя var было выбрано для акцентирования внимания на том факте, что открытая
переменная области действия не является переменной создания сценария (что является
нормальным для атрибутов с именем id).
В ситуациях, когда теги представляют более одного блока информации, имя var
используется для первичного экспортируемого блока информации, причем соответствующее
имя выбирается и для любого другого вторичного представляемого блока информации. К
примеру, информация о статуте итерации экспортируется тегом forEach посредством
атрибута status.

Web-

Rendered by www.RenderX.com
Теги ядра Стр. 497 из 626

17.4. Теги ядра


Теги ядра связаны с выражениями, управлением потоками и общим способом для
осуществления доступа к URL-ресурсам, содержание которых включено в JSP-страницу
или обрабатывается в ней.
Таблица 16-3 Теги ядра
Область Функция Теги TLD Префикс
Ядро Поддержка языка catchoutremoveset /jstl-c c
выражений
Управление потоками choose when other-
wiseforEachforTokensif
Управление URL import paramredirect
paramurl param

17.4.1. Теги выражений


Тег out оценивает выражение и выводит результат оценки в текущий объект JspWriter. Это
является эквивалентом JSP-синтаксиса <%= expression %>. К примеру, showcart.jsp
отображает количество позиций в корзине покупателя следующим образом:

<c:out value="${sessionScope.cart.numberOfItems}"/>

Тег set устанавливает значение атрибута в любой области действия JSP (странице, запросе,
сессии, приложении). Если атрибут не существует, он создается.
Атрибут области действия JSP-страницы может быть установлен двумя способами: из
значения атрибута

<c:set var="foo" scope="session" value="..."/>

или из тела тега

<c:set var="foo">
...
</c:set>

К примеру, устанавливается атрибут области действия страницы с названием bookID со


значением параметра запроса с именем Remove:

Web-

Rendered by www.RenderX.com
Стр. 498 из 626 Стандартная библиотека тегов JavaServer-страниц

<c:set var="bookId" value="${param.Remove}"/>

Если вы использовали RT-версию библиотеки, выражение будет таким:

<c_rt:set var="bookId"
value="<%= request.getParameter("Remove") %>" />

Чтобы удалить атрибут области действия, используйте тег remove. Когда вызывается JSP-
страница receipt.jsp магазина, сессия покупки завершается, следовательно, сессионный
атрибут cart удаляется следующим образом:

<c:remove var="cart" scope="session"/>

Язык выражения JSTL уменьшает потребность в написании сценариев. Тем не менее,


авторам страниц все равно потребуется иметь дело с ситуациями, когда некоторые
атрибуты, не принадлежащие JSTL-тегам должны быть определены, как выражения на
языке сценариев страницы. Стандартный элемент jsp:useBean используется для объявления
переменной создания сценариев, которая может быть использована в выражении на языке
сценариев или скриптлете. Например, showcart.jsp удаляет книгу из корзины покупателя,
используя скриптлет. ID книги, которую требуется удалить, передается как параметр
запроса. Значение параметра запроса в первую очередь устанавливается как атрибут
страницы (чтобы использовать его позже в JSTL-теге sql:query), после чего оно объявляется
как переменная создания сценариев и передается методу cart.remove:

<c:set var="bookId" value="${param.Remove}"/>


<jsp:useBean id="bookId" type="java.lang.String" />
<% cart.remove(bookId); %>
<sql:query var="books"
dataSource="${applicationScope.bookDS}">
select * from PUBLIC.books where id = ?
<sql:param value="${bookId}" />
</sql:query>

Тег catch обеспечивает дополнение к механизму страницы ошибок JSP. Оно позволяет
авторам страницы оперативно разрешать ситуации сбоя, которые находятся у них под
контролем. Действия первостепенной важности для страницы не должны быть
инкапсулированы в catch, таким образом, их исключения будут распространяться на
страницу ошибок. Действия второстепенной важности для страницы должны быть
"завернуты" в catch, поэтому они никогда не вызовут механизма страницы ошибок.

Web-

Rendered by www.RenderX.com
Теги ядра Стр. 499 из 626

Возбужденная исключительная ситуация хранится в переменной области действия,


идентифицированной при помощи атрибута var, областью действия которого всегда
является страница. В тех случаях, когда ошибки не возникают, переменная области
действия, идентифицированная var удаляется (если она существует). Если var отсутствует,
исключительная ситуация просто перехватывается и не сохраняется.

17.4.2. Теги управления потоками


Для применения логики управления потоками автор страницы должен прибегнуть к помощи
скриптлетов. Следующий пример показывает использование скриптлета для выполнения
процесса итерации в корзине покупателя:

<%
Iterator i = cart.getItems().iterator();
while (i.hasNext()) {
ShoppingCartItem item =
(ShoppingCartItem)i.next();
...
%>
<tr>
<td align="right" bgcolor="#ffffff">
<%=item.getQuantity()%>
</td>
...
<%
}
%>

Теги управления потоками исключают потребность в скриптлетах. В следующих двух


разделах представлены примеры, демонстрирующие теги условий и итераций.

17.4.2.1. Теги условий


Тег if позволяет выполнять условия для своего тела в соответствии со значением атрибута
test. В следующем примере из страницы catalog.jsp проверяется, является ли параметр
запроса Add пустым. Если оценка принимает значение true, страница запрашивает базу
данных на предмет записи книги, идентификатором которой является параметр запроса,
и добавляет эту книгу в корзину покупателя.

<c:if test="${!empty param.Add}">


<c:set var="bid" value="${param.Add}"/>
<jsp:useBean id="bid" type="java.lang.String" />
<sql:query var="books"

Web-

Rendered by www.RenderX.com
Стр. 500 из 626 Стандартная библиотека тегов JavaServer-страниц

dataSource="${applicationScope.bookDS}">
select * from PUBLIC.books where id = ?
<sql:param value="${bid}" />
</sql:query>
<c:forEach var="bookRow" begin="0" items="${books.rows}">
<jsp:useBean id="bookRow" type="java.util.Map" />
<jsp:useBean id="addedBook"
class="database.BookDetails" scope="page" />
...
<% cart.add(bid, addedBook); %>
...
</c:if>

Тег choose добавляет условия в выполнение блока путем вставки субтегов when. Он
отображает тело первого тега when, чьим тестовым условием является true. Если ни одним
из тестовых условий вложенных тегов when не является true, оценивается тело тега
otherwise, если таковой существует.
Примером может служить программный код, показывающий как отображать текст,
основываясь на категории покупателя.

<c:choose>
<c:when test="${customer.category == 'trial'}" >
...
</c:when>
<c:when test="${customer.category == 'member'}" >
...
</c:when>
<c:when test="${customer.category == 'preferred'}" >
...
</c:when>
<c:otherwise>
...
</c:otherwise>
</c:choose>

Теги choose, when и otherwise могут применяться для создания инструкции if-then-else
следующим образом:

<c:choose>

Web-

Rendered by www.RenderX.com
Теги ядра Стр. 501 из 626

<c:when test="${count == 0} >


No records matched your selection.
</c:when>
<c:otherwise>
<c:out value="${count}"/> records matched your selection.
</c:otherwise>
</c:choose>

17.4.2.2. Теги итераций


Тег forEach позволяет вам осуществлять процесс итерации в коллекции объектов. Коллекция
задается атрибутом items, а текущая позиция доступна через переменную области действия,
которая была названа атрибутом item.
Большинство типов коллекций поддерживается тегом forEach, включая все реализации
java.util.Collection и java.util.Map. Если атрибут items принадлежит типу java.util.Map, тогда
текущая позиция будет принадлежать типу java.util.Map.Entry, у которого имеются
следующие свойства:
• key - ключ, под которым позиция хранится в основном Map
• value - значение, соответствующее ключу
Поддерживаются также и массивы объектов, равно как и массивы примитивных типов
(например int). Для массива примитивных типов текущая позиция итерации автоматически
связана со своим стандартным порождающим классом (например, Integer для int, Float для
float и т.д.).
Реализации .util.Iterator и java.util.Enumeration также поддерживаются, но их следует
использоваться с осторожностью. Объекты Iterator и Enumeration нельзя переустановить,
поэтому их не следует использовать более чем в одном теге итерации. И, наконец, объекты
java.lang.String могут подвергаться процессу итерации, если строка содержит список
значений, перечисляемых через запятую (например: понедельник, вторник, среда, четверг,
пятница).
Ниже представлен процесс итерации корзины покупателя, описанный в предыдущем
разделе, в котором применяется тег forEach:

<c:forEach var="item" items="${sessionScope.cart.items}">


...
<tr>
<td align="right" bgcolor="#ffffff">
<c:out value="${item.quantity}"/>
</td>
...
</c:forEach>

Web-

Rendered by www.RenderX.com
Стр. 502 из 626 Стандартная библиотека тегов JavaServer-страниц

Тег forTokens используется для итерации набора символов, перечисленных через


разделитель.

17.4.3. URL-теги
Элемент jsp:include обеспечивает включение статических и динамических ресурсов в тот
же контекст, что и текущая страница. Тем не менее, jsp:include не может получить доступ
к ресурсам, которые находятся вне Web-приложения. Это вызовет бесполезное
использование буфера, если включенный ресурс используется другим элементом.
В следующем примере элемент transform использует содержимое включенного ресурса,
как данные на входе его преобразования. Элемент jsp:include считывает содержимое
ответа, записывает его в содержимое тела закрывающего элемента преобразования,
который затем перечитывает то же содержимое. Было бы эффективнее, если бы элемент
transform мог получить доступ к входящему ресурсу напрямую и избежать использования
буфера для содержимого тела тега преобразования.

<acme:transform>
<jsp:include page="/exec/employeesList"/>
<acme:transform/>

Из этого следует, что тег import является простым и распространенным способом для
доступа к URL-ресурсам, чье содержимое может быть включено в JSP-страницу и/или
обработано в ней. Например, в разделе XML-теги import используется для чтения XML-
документа, содержащего информацию о книге, а также присваивания содержимого
переменной области действия xml:

<c:import url="/books.xml" var="xml" />


<x:parse xml="${xml}" var="booklist"
scope="application" />

Тег param аналогичный тегу jsp:param (см. Элемент jsp:param) может использоваться
вместе с import для определения параметров запроса.
В разделе Отслеживание сессии мы обсуждали то, как приложение должно перезаписывать
URL-адреса для запуска механизма отслеживания сессии каждый раз, когда клиент
запрещает использование файлов cookie. Вы можете использовать тег url для перезаписи
URL-адресов, возвращаемых из JSP-страницы. Тег включает ID сессии в URL только в том
случае, когда файлы cookie отключены. В противном случае он возвращает URL без
изменений. Обратите внимание, что данное свойство требует, чтобы URL-адрес был
относительным. Например, catalog.jsp переписывает URL, используемый для добавления
книги в корзину покупателя, следующим образом:

Web-

Rendered by www.RenderX.com
XML-теги Стр. 503 из 626

<c:url var="url"
value="/catalog" >
<c:param name="Add" value="${bookId}" />
</c:url>
<p><strong><a href="<c:out value='${url}'/>">

Тег redirect отсылает перенаправленный HTTP клиенту. Тег redirect принимает субтеги
param с целью включения параметров в возвращаемый URL.

17.5. XML-теги
Ключевым подходом в работе с XML-документами является возможность осуществления
более легкого доступа к их содержимому. XPath (рекомендуемый W3C с 1999 года)
обеспечивает простой вид записи для определения и выбора объектов в XML-документе.
Набор тегов XML библиотеки JSTL, перечисленный в Таблице 16-4, базируется на XPath
(см. раздел Как функционирует XPath).
Таблица 16-4 XML-теги
Область Функция Теги TLD Префикс
XML Ядро outparseset /jstl-x x
Управление потоками choose when other-
wiseforEachif
Преобразование transform param

XML-теги используют XPath как локальный язык выражений. XPath-выражения всегда


задаются атрибутом select. Это означает, что, используя язык выражений XPath,
оцениваются только значения, определенные для атрибутов select. Все остальные атрибуты
оцениваются, используя правила, ассоциированные с глобальным языком выражений.
В дополнение к стандартному синтаксису XPath, устройство XPath JSTL поддерживает
следующие области действия для доступа к данным Web-приложения в пределах
выражения XPath:
• $foo
• $param:
• $header:
• $cookie:
• $initParam:
• $pageScope:
• $requestScope:
• $sessionScope:
• $applicationScope:
Данные области действия определены точно таким же образом, как их двойники в языке
выражений JSTL, обсуждаемые в разделе Неявные объекты. В Таблице 16-5 показано
несколько примеров использования областей действия.

Web-

Rendered by www.RenderX.com
Стр. 504 из 626 Стандартная библиотека тегов JavaServer-страниц

Таблица 16-5 Примеры XPath-выражений


XPath-выражение Результат
$sessionScope:profile Атрибуту области действия сессии присваивается имя profile
$initParam:mycom.productId Значение String параметра контекста mycom.productId

XML-теги иллюстрируются в другой версии (bookstore5) приложения Duke's Bookstore. В


данной версии обычная база данных книг заменяется ее XML-представлением (books.xml).
Для того чтобы создать и установить данную версию приложения, следуйте инструкциям
раздела Примеры JSP-страниц, заменив при этом bookstore4 на bookstore5.
В виду того, что XML-теги требуют использование анализатора XPath, Java WSDP включает
в себя анализатор XPath под названием Jaxen в двух библиотеках, jaxen-full.jar и saxpath.jar,
находящихся в директории <JWSDP_HOME>/tools/jstl/standard. Когда вы создаете
приложение Duke's Bookstore, эти библиотеки автоматически копируются в директорию
<JWSDP_HOME>/docs/tutorial/examples/web/bookstore5/build/WEB-INF/lib.

17.5.1. Теги ядра


XML-теги ядра обеспечивают основные функциональные возможности для более легкого
анализа и доступа к XML-данным.
Тег parse анализирует XML-документ и сохраняет получившийся объект в атрибуте области
действия, определенном атрибутом var. В версии bookstore5 XML-документ анализируется
и сохраняется в атрибуте контекста страницы parseBooks.jsp, включаемой во все JSP-
страницы, которым необходим доступ к документу:

<c:if test="${applicationScope:booklist == null}" >


<c:import url="/books.xml" var="xml" />
<x:parse xml="${xml}" var="booklist" scope="application" />
</c:if>

Теги out и set соответствуют поведению, описанному в разделе Теги выражений, локального
языка выражений XPath. Тег out оценивает XPath-выражение в текущем контекстном узле
и выводит результат оценки в текущий объект JspWriter.
Тег set оценивает XPath-выражение и устанавливает результат в атрибут области действия
JSP-страницы, определенный атрибутом var.
JSP-страница bookdetails.jsp выбирает элемент-книга, чей атрибут id соответствует
параметру запроса bookId и устанавливает атрибут abook. Затем, тег out выбирает элемент-
название книги и выводит результат.

<x:set var="abook"
select="$applicationScope.booklist/
books/book[@id=$param:bookId]" />

Web-

Rendered by www.RenderX.com
XML-теги Стр. 505 из 626

<h2><x:out select="$abook/title"/></h2>

Как вы могли убедиться, тег x:set хранит внутреннее XML-представление узла, извлеченного
с помощью XPath-выражения. Он не конвертирует выбранный узел в String и не хранит
его. Таким образом, тег x:set прежде всего полезен для хранения частей документов с
целью последующего восстановления.
Если вы желаете хранить String, необходимо в теге c:set использовать тег x:out. Тег x:out
конвертирует узел в String, а затем c:set хранит String, как атрибут области действия.
Например, страница bookdetails.jsp хранит атрибут области действия, содержащий цену,
который позднее направляется в тег fmt. Это выглядит следующим образом:

<c:set var="price">
<x:out select="$abook/price"/>
</c:set>
<h4><fmt:message key="ItemPrice"/>:
<fmt:formatNumber value="${price}" type="currency"/>

Другой вариант, который является более прямым, но требует большего знания XPath
пользователя, предназначен для навязывания узлу типа String вручную, используя функцию
string языка XPath.

<x:set var="price" select="string($abook/price)"/>

17.5.2. Теги управления потоками


XML-теги управления потоками соответствуют поведению, описанному в разделе Теги
управления потоками для языка выражений XPath.
JSP-страница catalog.jsp использует тег forEach для отображения всех книг, хранящихся
в списке booklist, следующим образом:

<x:forEach var="book"
select="$applicationScope:booklist/books/*">
<tr>
<c:set var="bookId">
<x:out select="$book/@id"/>
</c:set>=
<td bgcolor="#ffffaa">
<c:url var="url"

Web-

Rendered by www.RenderX.com
Стр. 506 из 626 Стандартная библиотека тегов JavaServer-страниц

value="/bookdetails" >
<c:param name="bookId" value="${bookId}" />
<c:param name="Clear" value="0" />
</c:url>
<a href="<c:out value='${url}'/>">
<strong><x:out select="$book/title"/>&nbsp;
</strong></a></td>
<td bgcolor="#ffffaa" rowspan=2>
<c:set var="price">
<x:out select="$book/price"/>
</c:set>
<fmt:formatNumber value="${price}" type="currency"/>
&nbsp;
</td>
<td bgcolor="#ffffaa" rowspan=2>
<c:url var="url" value="/catalog" >
<c:param name="Add" value="${bookId}" />
</c:url>
<p><strong><a href="<c:out value='${url}'/>">&nbsp;
<fmt:message key="CartAdd"/>&nbsp;</a>
</td>
</tr>
<tr>
<td bgcolor="#ffffff">
&nbsp;&nbsp;<fmt:message key="By"/><em>
<x:out select="$book/firstname"/>&nbsp;
<x:out select="$book/surname"/></em></td></tr>
</x:forEach>

17.5.3. Теги преобразований


Тег transform применяет к XML-документу, определенному атрибутом xml, процесс
преобразования, заданный таблицей стилей XSLT, которая установлена при помощи
атрибута xslt. Если атрибут xml не задан, входящий XML-документ считывается из
содержимого тела тега.
Для установки параметров преобразования тег transform можно использовать с субтегом
param. Для определения параметра применяются атрибуты name и value. Если значение
не определено, оно извлекается из тела тега.

17.6. Теги интернационализации


В разделе Интернационализации и локализации Web-приложений мы обсуждали способ
адаптации web-приложений к языку и правилам форматирования, характерных для региона

Web-

Rendered by www.RenderX.com
Теги интернационализации Стр. 507 из 626

клиента. В данном разделе описаны теги, поддерживающие так называемую


интернационализацию JSP-страниц.
JSTL определяет теги для:
• Установки региона, в котором отображается страница
• Создания сообщений, зависимых от региона клиента
• Форматирование и анализ элементов данных, таких как числа, валюта, даты и время.
Это осуществляется посредством автоматического переключения на стандарты
требуемого региона или при помощи ручной настройки параметров.
Таблица 16-6 Теги интернационализации
Область Функция Теги TLD
I18n Установка региона setLocalerequestEncoding /jstl-fmt
Отправка сообщений bundlemessage paramsetBun-
dle
Форматирование чисел и дат formatNumberformatDateparse-
DateparseNumbersetTime-
ZonetimeZone

17.6.1. Установка региона


Тег setLocale используется для переопределения региона страницы конкретного клиента.
Тег requestEncoding используется для установки кодировки символов запроса. Это
необходимо для того, чтобы правильно расшифровывать значения параметров запросов,
чья кодировка отличается от ISO-8859-1.

17.6.2. Теги сообщений


По-умолчанию функция броузера для определения информации о регионах включена. Это
означает, что клиент определяет (посредством настроек его броузера), для какого региона
должна быть настроена его страница, что позволяет авторам страницы удовлетворять
языковые требования любых своих клиентов.

17.6.2.1. Тег bundle


Тег bundle используется для определения пакета ресурсов страницы.
Чтобы определить пакет ресурсов для web-приложения, определите параметр контекста
javax.servlet.jsp.jstl.fmt.localizationContext в дескрипторе размещения web-приложения.
Здесь представлено объявление из дескриптора Duke's Bookstore:

<context-param>
<param-name>
javax.servlet.jsp.jstl.fmt.localizationContext
</param-name>
<param-value>messages.BookstoreMessages</param-value>

Web-

Rendered by www.RenderX.com
Стр. 508 из 626 Стандартная библиотека тегов JavaServer-страниц

</context-param>

17.6.2.2. Тег message


Тег message используется для вывода локализованных (то есть предназначенных для
региона клиента) строк. Следующий тег из страницы catalog.jsp

<h3><fmt:message key="Choose"/></h3>

используется для вывода строки, приглашающей заказчиков выбрать книгу из каталога.


Субтег param устанавливает в своем родительском теге message отдельный аргумент
(для параметрической замены) сложного сообщения или модели. Для каждой переменной
в сложном сообщении или модели должен быть определен лишь один тег param.
Параметрическая замена осуществляется в порядке размещения тегов param.

17.6.3. Теги форматирования


JSTL обеспечивает набор тегов для анализа и форматирования чисел и дат, вид которых
определяется регионом клиента.
Тег formatNumber используется для вывода локализованных чисел. Следующий тег из
страницы showcart.jsp

<fmt:formatNumber value="${book.price}" type="currency"/>

используется для отображения локализованной цены книги. Обратите внимание на то, что
цена сохраняется в базе данных в долларах, поэтому локализация в некоторой степени
упрощена (тег formatNumber не "подозревает" о валютных курсах). Тег форматирует валюту,
но не конвертирует ее.
Кроме этого доступны и другие аналогичные теги для форматирования дат (formatDate),
и анализа чисел и дат (parseNumber, parseDate). Тег timeZone устанавливает временную
полосу (определенную атрибутом value), которая используется всеми вложенными тегами
formatDate.
В странице receipt.jsp, создается эталонная дата поставки, которая затем форматируется
при помощи тега formatDate:

<jsp:useBean id="now" class="java.util.Date" />


<jsp:setProperty name="now" property="time"
value="<%= now.getTime() + 432000000 %>" />
<fmt:message key="ShipDate"/>

Web-

Rendered by www.RenderX.com
SQL-теги Стр. 509 из 626

<fmt:formatDate value="${now}" type="date"


dateStyle="full"/>.

17.7. SQL-теги
JSTL SQL-теги сконструированы для быстрого моделирования и создания простых
приложений. Для производства приложений операции баз данных обычно инкапсулируются
в JavaBean-компоненты.
Таблица 16-7 SQL-теги
Область Функция Теги TLD Префикс
База данных setDataSource /jstl-sql sql
SQL query dateParam param-
transactionupdate
dateParam param

Тег setDataSource позволяет вам установить информацию об источнике данных в базу


данных. Чтобы установить информацию об источнике данных, вы можете обеспечить имя
JNDI или параметры DriverManager. Все страницы Duke's Bookstore, у которых имеется
более чем один SQL-тег, используют следующее выражение для установки источника
данных:

<sql:setDataSource dataSource="jdbc/BookDB" />

Тег query используется для осуществления SQL-запроса, который возвращает


результирующее множество. Для параметризованных SQL-запросов, используйте
вложенный тег param внутри тега query.
В странице catalog.jsp значение параметра запроса Add определяет информацию о книге,
которую необходимо извлечь из базы данных. Этот параметр сохраняется как имя атрибута
bid, и передается тегу param. Обратите внимание, что тег query получает свой источник
данных из атрибута контекста bookDS, установленного в слушателе контекста.

<c:set var="bid" value="${param.Add}"/>


<sql:query var="books" >
select * from PUBLIC.books where id = ?
<sql:param value="${bid}" />
</sql:query>

Тег update используется для обновления строки базы данных. Тег transaction используется
для автоматического выполнения последовательности SQL-выражений.

Web-

Rendered by www.RenderX.com
Стр. 510 из 626 Стандартная библиотека тегов JavaServer-страниц

JSP-страница receipt.jsp использует для каждого заказа оба тега, обновляющих хранилище
базы данных. Из-за того, что в корзине покупателя может находиться более одной книги,
тег transaction используется для заключения в него большого количества запросов и
обновлений. Вначале страница устанавливает, является ли достаточным существующее
хранилище, после чего она обновляется.

<c:set var="sufficientInventory" value="true" />


<sql:transaction>
<c:forEach var="item" items="${sessionScope.cart.items}">
<c:set var="book" value="${item.item}" />
<c:set var="bookId" value="${book.bookId}" />

<sql:query var="books"
sql="select * from PUBLIC.books where id = ?" >
<sql:param value="${bookId}" />
</sql:query>
<jsp:useBean id="inventory"
class="database.BookInventory" />
<c:forEach var="bookRow" begin="0"
items="${books.rowsByIndex}">
<jsp:useBean id="bookRow" type="java.lang.Object[]" />
<jsp:setProperty name="inventory" property="quantity"
value="<%=(Integer)bookRow[7]%>" />

<c:if test="${item.quantity > inventory.quantity}">


<c:set var="sufficientInventory" value="false" />
<h3><font color="red" size="+2">
<fmt:message key="OrderError"/>
There is insufficient inventory for
<i><c:out value="${bookRow[3]}"/></i>.</font></h3>
</c:if>
</c:forEach>
</c:forEach>

<c:if test="${sufficientInventory == 'true'}" />


<c:forEach var="item" items="${sessionScope.cart.items}">
<c:set var="book" value="${item.item}" />
<c:set var="bookId" value="${book.bookId}" />

<sql:query var="books"
sql="select * from PUBLIC.books where id = ?" >

Web-

Rendered by www.RenderX.com
SQL-теги Стр. 511 из 626

<sql:param value="${bookId}" />


</sql:query>

<c:forEach var="bookRow" begin="0"


items="${books.rows}">
<sql:update var="books" sql="update PUBLIC.books set
inventory = inventory - ? where id = ?" >
<sql:param value="${item.quantity}" />
<sql:param value="${bookId}" />
</sql:update>
</c:forEach>
</c:forEach>
<h3><fmt:message key="ThankYou"/>
<c:out value="${param.cardname}" />.</h3><br>
</c:if>
</sql:transaction>

17.7.1. Конечный интерфейс тега query


Интерфейс Result используется для того, чтобы извлекать информацию из объектов,
возвращаемых от тега query.

public interface Result


public String[] getColumnNames();
public int getRowCount()
public Map[] getRows();
public Object[][] getRowsByIndex();
public boolean isLimitedByMaxRows();

Для получения более полной информации о данном интерфейсе прочтите документацию


API для пакета javax.servlet.jsp.jstl.sql.
Атрибут var установленный тегом query относится к типу Result. Метод getRows возвращает
массив отображений, которые могут поставляться для атрибута items каждого тега forEach.
Язык выражений JSTL конвертирует синтаксис ${result.rows} в вызов result.getRows.
Выражение ${books.rows}, представленное в следующем примере, возвращает массив
отображений.
Когда вы обеспечиваете массив отображений на тег forEach, атрибут var, установленный
тегом, относится к типу Map. Чтобы извлечь информацию из строки, используйте метод
get("colname") для получения значения колонки. Язык выражений JSTL конвертирует
синтаксис ${map.colname} в вызов map.get("colname"). Например, выражение ${book.title}
возвращает значение записи заголовка отображения книги.

Web-

Rendered by www.RenderX.com
Стр. 512 из 626 Стандартная библиотека тегов JavaServer-страниц

Страница Duke's Bookstore bookdetails.jsp извлекает значения колонки из отображения


book следующим образом.

<c:forEach var="book" begin="0" items="${books.rows}">


<h2><c:out value="${book.title}"/></h2>
&nbsp;<fmt:message key="By"/><em><c:out
value="${book.firstname}"/><c:out
value="${book.surname}"/></em>&nbsp;&nbsp;
(<c:out value="${book.year}"/>)<br> &nbsp; <br>
<h4><fmt:message key="Critics"/></h4>
<blockquote><c:out value="${book.description}"/>
</blockquote>
<h4><fmt:message key="ItemPrice"/>:
<fmt:formatNumber value="${book.price}" type="currency"/>
</h4>
</c:forEach>

Следующая выдержка из страницы catalog.jsp использует интерфейс Row для извлечения


значений из колонок строки книги при помощи выражений языка сценариев. Вначале из
базы данных извлекается строка книги, которая соответствует параметру запроса (bid).
Объекты bid и bookRow объявляются, как переменные создания сценария с помощью тега
jsp:useBean. Это происходит по причине того, что они (bid и bookRow) позднее используются
тегами, которые используют выражения языка сценариев для установки значений атрибутов,
а также скриптлетами, добавляющими книгу в корзину. Страница создает компонент,
который описывает книгу, а выражения языка сценариев используются для установки
свойств книги из значений колонки строки данной книги. В итоге книга добавляется в корзину
покупателя.
При желании, вы можете сравнить данную версию страницы catalog.jsp с версией,
используемой в разделах Технология JavaServer-страниц и Заказные теги в JSP-страницах,
которые используют JavaBean-компонент базы данных книг.

<sql:query var="books"
dataSource="${applicationScope.bookDS}">
select * from PUBLIC.books where id = ?
<sql:param value="${bid}" />
</sql:query>
<c:forEach var="bookRow" begin="0"
items="${books.rowsByIndex}">
<jsp:useBean id="bid" type="java.lang.String" />
<jsp:useBean id="bookRow" type="java.lang.Object[]" />

Web-

Rendered by www.RenderX.com
Дополнительная информация Стр. 513 из 626

<jsp:useBean id="addedBook" class="database.BookDetails"


scope="page" />
<jsp:setProperty name="addedBook" property="bookId"
value="<%=bookRow[0]%>" />
<jsp:setProperty name="addedBook" property="surname"
value="<%=bookRow[1]%>" />
<jsp:setProperty name="addedBook" property="firstName"
value="<%=bookRow[2]%>" />
<jsp:setProperty name="addedBook" property="title"
value="<%=bookRow[3]%>" />
<jsp:setProperty name="addedBook" property="price"
value="<%=((Double)bookRow[4]).floatValue()%>" />
<jsp:setProperty name="addedBook" property="year"
value="<%=(Integer)bookRow[5]%>" />
<jsp:setProperty name="addedBook"
property="description" value="<%=bookRow[6]%>" />
<jsp:setProperty name="addedBook" property="inventory"
value="<%=(Integer)bookRow[7]%>" />
</jsp:useBean>
<% cart.add(bid, addedBook); %>
...
</c:forEach>

17.8. Дополнительная информация


Дополнительную информацию по JSTL можно получить из документов:
• Справочная документация по Java WSDP, которая находится в файле:
<JWSDP_HOME>/docs/jstl/index.html.
• Примеры JSTL в Java WSDP. Если Tomcat запущен, можно получить доступ к примерам
по адресу http://localhost:8080/jstl-examples/index.html.
• Список ресурсов на Web-сайте http://java.sun.com/products/jsp/jstl.
• Cпецификация JSTL 1.0, в которой представлено полное описание синтаксиса и
семантики JSTL.

18. Безопасность Web-приложений


Дэбби Карсон
Модель безопасности Web-служб основана на спецификации Java Servlet. Данная модель
изолирует разработчиков от технических деталей реализации безопасности приложения.
Java WSDP обеспечивает подобную изоляцию путем усовершенствования портативности
приложений, что позволяет им быть размещенными в различных окружениях безопасности.

Web-

Rendered by www.RenderX.com
Стр. 514 из 626 Безопасность Web-приложений

Материал данной главы подразумевает, что вы имеете представление о концепции


безопасности. Чтобы узнать больше о данной концепции, перед началом данной главы
настоятельно рекомендуется изучить раздел Security в документе The Java™ Tutorial (см
http://java.sun.com/docs/books/tutorial/security1.2/index.html).

18.1. Краткий обзор


Java WSDP определяет декларативные соглашения между разработчиками компонентов
приложений и теми, кто настраивает их в операционной среде. В контексте безопасности
приложения поставщикам приложений необходимо объявлять требования безопасности
их приложений в том виде, в котором эти требования могут быть удовлетворены во время
настройки приложения. Механизм декларативной безопасности, используемый в
приложениях, выражается в декларативном синтаксисе документа под названием
дескриптор размещения. Разместитель приложения использует инструментарий контейнера
для отображения требований приложения (которые находятся в дескрипторе размещения)
на механизмы безопасности, реализуемые данным Web-компонентом.
Программируемая безопасность относится к решениям безопасности, принятыми
приложениями, осведомленными о безопасности. Использование программируемой
безопасности полезно в тех случаях, когда для выражения модели безопасности приложения
одной декларативной безопасности не достаточно. Примером может послужить приложение,
которое должно принимать решения об авторизации, базирующиеся на времени суток,
параметрах вызова или внутреннем состоянии Web-компонента. В других случаях
приложение может ограничивать доступ, базируясь на информации о пользователе,
хранящейся в базе данных.
Приложения Web-служб Java собраны из компонентов, которые могут быть размещены в
различных контейнерах. Данные компоненты используются для построения многоярусного
приложения. Задачей архитектуры безопасности Java WSDP является достижение сквозной
безопасности, путем организации защиты каждого яруса.
Ярусы могут содержать как защищенные, так и незащищенные ресурсы. Обычно для
гарантии того, что только авторизированные пользователи имеют доступ, необходимо
защитить ресурсы. Авторизация обеспечивает контролируемый доступ к защищенным
ресурсам. Однако авторизация базируется на аутентификации и контроле доступа.
Аутентификация является процессом, который контролирует идентификацию пользователя,
устройства или другого объекта в компьютерной системе. Обычно она является
необходимым условием для позволения доступа к ресурсам. Контроль доступа - это
процесс, который определяет, имеет ли право аутентифицированный пользователь получать
доступ к отдельному запрошенному ресурсу.
Авторизация не является необходимой для доступа к незащищенным ресурсам. В следствие
того, что авторизация базируется на аутентификации, послежняя также не является
необходимой для доступа к незащищенным ресурсам. Доступ к ресурсам без
аутентификации называется неаутентифицированным или анонимным доступом.

18.2. Пользователи, группы и роли


Пользователь Web-службы подобен пользователю операционной системы. Обычно,
пользователями обоих типов являются люди. Однако данные типы пользователей не
одинаковы. При входе в операционную систему служба аутентификаци сервера Tomcat

Web-

Rendered by www.RenderX.com
Пользователи, группы и роли Стр. 515 из 626

не осведомлена о пользователе и назначенном ему пароле. Данная служба не связяна с


механизмом безопасности операционной системы. Обе службы безопасности управляют
пользователями, которые относятся к разным областям.
Служба аутентификации сервера Tomcat включает в себя следующие компоненты:
• Role(Роль) - абстрактное название для разрешения доступа к отдельному набору
ресурсов. Роль можно сравнить с ключом, отпирающим дверь. Копия ключа может быть
у многих, однако замку все равно, кто вы такой, главное, чтобы у вас был нужный ключ.
• User (Пользователь) - отдельная идентичность (или приложение), прошедшая
аутентификацию (аутентификация обсуждалась в предыдущем разделе). Пользователь
может иметь набор ролей, ассоциированных с данной идентичностью, который дает
им право получать доступ ко всем ресурсам, защищенным этими ролями.
• Group (Группа) - набор аутентифицированных пользователей классифицированных по
общим признакам, таким как должность или профиль клиента. Группы также
ассоциируются с набором ролей, где каждый пользователь, являющийся членом группы,
наследует роли, назначанные данной группе. В большинстве случаев, пользователи
отображаются прямо на роли, группу определять не требуется.
• Realm (Область) - полная база данных ролей, пользователей и групп, которые
идентифицируют действительных пользователей Web-приложения (или набора Web-
приложений).

18.2.1. Роли безопасности


При разработке Web-компонента, необходимо помнить о различных типах пользователей,
которые будут получать доступ к компоненту. К примеру, Web-приложение для кадрового
отдела должно иметь различные URL запросов для пользователей, которым назначена
роль admin и пользователей, которым назначена роль director. Роль admin позволяет
просматривать некоторые данные о служащих, однако роль director позволяет
просматривать данные о зарплате. Каждые из данных ролей безопасности являются
абстрактным логическим группированием пользователей, которое определяется
разработчиком приложения. При размещении приложения, разместитель отображает роли
идентичности безопасности в операционном окружении.
Для создания роли для приложения Web-служб, вначале необходимо установить
пользователей и роли, используя утилиту admintool, а затем объявить их для файла WAR,
содержащегося в приложении.

18.2.2. Управление ролями и пользователями


Для управления информацией, находящейся в файле пользователей, рекомендуется
использовать утилиту admintool. Для использования admintool, запустите Tomcat, после
чего установите в броузере адрес http://localhost:8080/admin и войдите в утилиту, используя
имя пользователя и пароль, назначенные ролью admin, то есть под именем и паролем,
которые вы ввели при инсталляции.
Из соображений безопасности, утилита admintool (называемая также Утилита
Администрирования Web-сервера Tomcat) прежде чем разрешить доступ к серверу,
проверяет, являетесь ли вы (как определено в информации, введенной вами при входе)

Web-

Rendered by www.RenderX.com
Стр. 516 из 626 Безопасность Web-приложений

пользователем, который наделен полономочиями устанавливать и перезагружать


приложения (определено как пользователь с ролью admin в файле tomcat-users.xml).
При инсталляции создается файл JWSDP_HOME/conf/tomcat-users.xml. Он содержит в
текстовом виде имя пользователя и пароль, созданные при инсталляции Java WSDP.
Первоначально данное имя ассоциируется с определенными ролями admin, manager и
provider. Можно отредактировать файл пользователей непосредственно, добавив или
удалив пользователей, или же изменив роли. Для этой цели можно также воспользоваться
утилитой admintool.
Файл tomcat-users.xml выглядит следующим образом:

<?xml version='1.0'?>
<tomcat-users>
<role rolename="admin"/>
<role rolename="manager"/>
<role rolename="provider"/>
<user username="your_name" password="your_password"
roles="admin,manager,provider"/>
</tomcat-users>

В следующем разделе описывается, как добавлять роли и пользователей, используя


утилиту admintool. При изменениях, произведенных с помощью admintool, файл
JWSDP_HOME/conf/tomcat-users.xml обновляется.

18.2.2.1. Использование Утилиты Администрирования Web-сервера Tomcat (admintool)


Для использования утилиты admintoolнеобходимо запустить Tomcat.

18.2.2.1.1. Запуск Tomcat


Для запуска Tomcat, наберите в окне терминала следующие команды.

<JWSDP_HOME>/bin/startup.sh ( Unix- )
<JWSDP_HOME>\bin\startup.bat ( Microsoft Windows)

Сценарий загрузки запускает задачу в фоновом режиме, после чего сразу же возвращает
пользователя к командной строке. Для полной загрузки Tomcat загрузочному сценарию
требуется некоторое время.
Примечание: Сценарий загрузки Tomcat выполняется в течение нескольких минут. Для
проверки полной загрузки Tomcat откройте в броузере http://localhost:8080. После появления
заставки Tomcat можно продолжить работу. Если заставка не загружается сразу, подождите
несколько минут, а затем повторите процесс. Если по прошествии нескольких минут

Web-

Rendered by www.RenderX.com
Пользователи, группы и роли Стр. 517 из 626

заставка Tomcat не отобразится, обратитесь к разделу, описывающему устранение


неисправностей ("Unable to Locate the Server localhost:8080" Error).
Документацию по Tomcat можно найти в файле JWSDP_HOME/docs/tomcat/index.html.

18.2.2.1.2. Запуск admintool


После загрузки Tomcat, для запуска admintool выполните следующие действия:
1. Запустите Web-броузер.
2. В Web-броузере откройте URL:

http://localhost:8080/admin

3. Войдите в утилиту admintool, используя имя и пароль, назначенные ролью admin.


4. Утилита admintool отобразится в окне Web-броузера:

Рисунок 17-1 Утилита администрирования Web-сервера Tomcat

5. После окончания работы, выйдите из admintool, выбрав Log Out.


В следующих разделах показано, как использовать утилиту admintoolдля выполнения
следующих действий:
• Отображение всех ролей в области, заданной по-умолчанию
• Добавление роли в область, заданную по-умолчанию
• Удаление роли из области, заданной по-умолчанию
• Отображение всех пользователей области, заданной по-умолчанию

Web-

Rendered by www.RenderX.com
Стр. 518 из 626 Безопасность Web-приложений

• Добавление пользователя в область, заданную по-умолчанию


• Удаление пользователя
Данный раздел использует в качестве примера приложение Getting Started, обсуждаемое
в главе Начало работы с Tomcat. Данные модификации произведены для запуска сервера
Tomcat - они не являются необходимыми для остановки и перезагрузки Tomcat.

18.2.2.2. Управление ролями


Для просмотра всех существующих ролей области выберите в левом окне Roles из секции
User Definition.
В правой панели отобразится Roles List (список ролей) и список Available Actions (доступные
действия). По-умолчанию, отобразятся роли, определенные при инсталляции Java WSDP.
Данные роли включают в себя admin, manager и provider.
Для того чтобы добавить новую роль в область, установленную по-умолчанию, выполните
следующую процедуру:
1. Из списка Roles List выберите Create New Role
2. В качестве названия роли введите user
3. В качестве описания данной роли введите Getting Started App Security Role
4. После окончания выполнения действий выберите Save. В списке отобразится новая
роль.
Для удаления роли из области, установленной по-умолчанию, выполните следующее:
1. В списке Roles List, выберите Delete Existing Roles из списка Available Actions.
2. В окне Roles, выберите роль, которую вы хотите удалить.
3. Выберите Save.

18.2.2.3. Управление пользователями


Для просмотра всех существующих пользователей области выберите в левом окне Users
из секции User Definition.
В правом окне отобразятся списки User List и Available Actions. По-умолчанию отобразится
имя пользователя, определенное при инсталляции Java WSDP.
Для редактирования свойств пользователя:
1. Выберите Users из секции User Definition в левом окне.
2. Выберите для редактирования профиль пользователя в правом окне.
3. Отредактируйте существующие свойства пользователя.
Для добавления нового пользователя в область, установленную по-умолчанию
1. Из списка Users List выберите Create New User.
2. В качестве имени добавляемого пользователя введите Duke
3. В качестве пароля для данного пользователя введите javarocks
4. В качестве полного имени пользователя введите Duke the Java programming wiz

Web-

Rendered by www.RenderX.com
Пользователи, группы и роли Стр. 519 из 626

5. Выберите роль user для данного пользователя.


6. После окончания выберите Save. Новый пользователь отобразится в списке.
Для удаления пользователя из области, установленной по-умолчанию
1. В списке Users List выберите Delete Existing Users из списка Available Actions.
2. В окне Delete Existing Users выберите пользователя, которого вы желаете удалить.
3. Выберите Save.
Добавление новой роли и пользователя, описанные в предыдущем разделе, отображаются
в обновленном файле tomcat-users.xml. Теперь он будет содержать следующие данные:

<?xml version='1.0'?>
<tomcat-users>
<role rolename="admin"/>
<role rolename="user" description="Getting Started
App Security Role"/>
<role rolename="manager"/>
<role rolename="provider"/>
<user username="your_name" password="your_password"
roles="admin,manager,provider"/>
<user username="Duke" password="javarocks"
fullName="Duke the Java Programming wiz"
roles="user"/>
</tomcat-users>

18.2.2.3.1. Рассуждения при изменении профиля пользователя


При добавлении пользователя или изменения его имени или пароля при помощи утилиты
admintool данные изменения записываются в файл tomcat-users.xml, как описано в разделе
Управление пользователями. При запуске, Tomcat считывает информацию из файла tomcat-
users.xml. Если для изменений вы используете утилиту admintool, а затем сохраняете
изменения, последние производятся и в работающем сервере Tomcat (нет необходимости
перегружать его).
Однако если вы производите изменения при помощи admintool, а затем желаете запустить
утилиту deploytool для манипуляций с новыми или измененными профилями пользователей,
Tomcat должен быть остановлен и перезагружен. Перезагрузка Tomcat необходима, так
как исполняемые одновременно deploytool и admintool, требуют, чтобы имя пользователя
и пароль в файле build.properties совпадали с соответствующими именем пользователя и
паролем, заданными ролью в файле tomcat-users.xml. Если вы желаете войти в утилиту
deploytool, используя новый или измененный профиль пользователя, выполните следующие
действия:
1. Испольщуя admintool, присвойте новому пользователю роли admin и manager. Роль
admin необходима для admintool и deploytool. Роль manager необходима для deploytool.

Web-

Rendered by www.RenderX.com
Стр. 520 из 626 Безопасность Web-приложений

2. Выйдите из admintool.
3. Закройте Tomcat.
4. Отредактируйте файл build.properties таким образом, чтобы новые или
модифицированные имя пользователя и пароль совпадали. Подробнее о файле
build.properties читайте в разделе Создание файла BuildProperties.
5. Загрузите Tomcat (дождитесь полной загрузки).
6. Запустите deploytool. Введите имя пользователя и пароль, которому в файле tomcat-
users.xml назначены роли admin и manager, и которые совпадают с именем пользователя
и паролем в файле build.properties.

18.2.3. Отображение ролей приложения на роли области


При разработке приложения Web-служб, вам известны роли, которые вы использовали в
приложении, однако вы можете не знать наверняка, какие роли определены для области.
В Java WSDP этим вопросом занимается архитектура безопасности Web-служб. После
размещения приложения, администратор сервера Tomcat будет отображать роли
приложения на роли области, назначенной по-умолчанию.
В Java WSDP роль для приложения Web-служб создается в два этапа. Вначале при помощи
admintool устанавливаются роли и пользователи, как описано в разделе Управление ролями
и пользователями. Затем, заданные роли импортируются при помощи утилиты deploytool,
после чего вы выбираете, какие из них являются авторизированными для файла WAR,
содержащегося в приложении.
Администратор может авторизировать роли для доступа к данному Web-приложению,
выбрав их в deploytool. Однако перед тем как авторизировать роль для Web-приложения,
необходимо установить ограничения безопасности. Для получения более подробной
информации обратитесь к разделу Контроль доступа к Web-ресурсам.
В следующем примере авторизируется роль user, установленная в разделе Использование
Утилиты Администрирования Web-сервера Tomcat для приложения Getting Started. Данный
пример использует архив gs.war, созданный в приложении Getting Started, что обсуждалось
в разделе Размещение приложения с использованием утилиты deploytool.
1. Убедитесь, что Tomcat запущен.
2. Запустите deploytool. Утилита deploytool выполняется из коммандной строки, и находится
в директории bin, установленного вами Java WSDP. Для ее запуска откройте окно
терминала или командную строку и введите:

<JWSDP_HOME>/bin/deploytool

3. В диалоге Set Tomcat Server введите имя пользователя и пароль, которому назначена
роль admin.
4. Выберите или откройте файл WAR Web-приложения, <JWSDP_HOME>/docs/tutorial/exam-
ples/gs/gs.war.

Web-

Rendered by www.RenderX.com
Безопасность Web-ярусов Стр. 521 из 626

5. Выберите окно Security.


6. Добавьте Security Constraint, выбрав кнопку Add рядом с полем Security Constraints.
7. Для добавления к приложению авторизированной роли выберите кнопку Edit, которая
находится под Authorized Roles.
8. Для импорта ролей, определенных ранее при помощи admintool, выберите Import Roles.
9. Выберите User, Add, а затем OK для закрытия диалога.

18.3. Безопасность Web-ярусов


В следующих разделах рассматривается защита ресурсов и аутентификация пользователей
в Web-ярусах.

18.3.1. Защита Web-ресурсов


Web-ресурсы можно защитить, определив ограничения безопасности. Ограничения
безопасности определяют, кто авторизирован для доступа к набору Web-ресурсов, список
шаблонов URL и методов HTTP, которые описывают набор защищаемых ресурсов.
Ограничения безопасности могут быть определены при помощи утилиты deploytool, как
описано в разделе Контроль доступа к Web-ресурсам.
Если вы попытаетесь получить доступ к защищенному Web-ресурсу как
неаутентифицированный пользователь, Web-контейнер попытается вас аутентифицировать.
Он будет принимать запрос только после того, как вы докажете вашу подлинность, после
чего, вы будете допущены для доступа к ресурсу.
Ограничения безопасности используют только оригинальные URI запроса, а не вызовы,
сделанные посредством RequestDispatcher (который включает в себя <jsp:include> и
<jsp:forward>). Внутри приложения это подразумевает, что само приложение выполнит
доступ ко всем ресурсам и не будет отправлять запрос пользователя, пока не установит,
что запрашиваемый пользователь тоже имеет право доступа.

18.3.2. Контроль доступа к Web-ресурсам


Для определения ограничений безопасности для доступа к Web-ресурсу используйте
следующие процедуры в утилите deploytool:
1. Выберите файл WAR, содержащий Web-ресурс.
2. Выберите окно с закладками Security.
3. Выберите Basic для типа User Authentication. Методы типа User Authentication описаны
в разделе Аутентификация пользователей Web-ресурсов.
4. В секции Security Constraints выберите кнопку Add. Тем самым в приложение
добавляются ограничения безопасности.
5. Выберите кнопку Add рядом с полем Web Resource Collections для добавления к
ограничению безопасности набора Web-ресурсов. Набор Web-ресурсов описывает
пару шаблон URL/метод HTTP, которая относится к защищаемому ресурсу.
6. Выберите пункт Get (для примера Getting Started).

Web-

Rendered by www.RenderX.com
Стр. 522 из 626 Безопасность Web-приложений

7. Выберите кнопку Edit под пунктом Get. Перейдите к Web-клиенту (в файле WAR примера
Getting Started им являтся index.jsp). Чтобы закрыть диалог, выберите Add, затем OK.
8. Для добавления роли user к ограничению безопасности выберите кнопку Edit рядом с
полем Authorized Roles. Этим вы определите набор ролей, которым позволено получать
доступ к набору Web-ресурсов.
9. Выберите Import Roles для импорта ролей, добавленных к файлу tomcat-users.xml при
помощи утилиты admintool.
10. Выберите слева роль user. Выберите Add. Роль добавится в список Authorized Roles
List.
11. Нажмите OK. Выбранная роль отобразится в списке Authorized Roles для данного
приложения. Для получения информации о создании пользователей и ролей при помощи
admintool обратитесь к разделу Управление ролями и пользователями.
Теперь, когда ограничения безопасности добавлены в приложение, запустим его для
проверки.
1. Выберите Tools _ Deploy для того, чтобы убедиться, что используется последняя версия
файла WAR. Во всех появляющихся диалогах выбирайте OK.
2. После завершения размещения закройте консоль Deploy.
3. Откройте приложение Getting Started в Web-броузере, используя следующий URL:

http://localhost:8080/GSApp

4. Введите имя пользователя (Duke) и пароль (javarocks), установленные ранее.


Приложение Getting Started загрузится в броузере.

18.3.3. Установка безопасности без использования deploytool


В разделе Создание приложения Getting Started мы обсуждали "лучший метод подхода к
организации программирования Web-служб", который более детально описывается в
файле <JWSDP_HOME>/docs/tomcat/appdev/deployment.html. Одним из файлов, который
всегда будет присутствовать в приложении Web Services, является файл web.xml. Он
должен быть создан в директории /web/WEB-INF/ вашего Web-приложения. Файл web.xml
является дескриптором размещения для приложения. В начале, в данном примере, мы
обеспечили описание приложения. В файле web.xml можно определить те же установки
для безопасности Web-приложений, которые мы создали при помощи утилиты deploytool.
Для примера, показанного в предыдущем разделе, deploytool записывает файл web.xml,
который выглядит следующим образом:

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE web-app PUBLIC '-//Sun Microsystems, Inc.//DTD Web

Web-

Rendered by www.RenderX.com
Безопасность Web-ярусов Стр. 523 из 626

Application 2.3//EN' 'http://java.sun.com/dtd/web-


app_2_3.dtd'>

<web-app>
<display-name>GSApp</display-name>
<servlet>
<servlet-name>index</servlet-name>
<display-name>index</display-name>
<jsp-file>/index.jsp</jsp-file>
</servlet>
<session-config>
<session-timeout>30</session-timeout>
</session-config>
<security-constraint>
<web-resource-collection>
<web-resource-name>WRCollection</web-resource-name>
<url-pattern>/index.jsp</url-pattern>
<http-method>GET</http-method>
</web-resource-collection>
<auth-constraint>
<role-name>user</role-name>
</auth-constraint>
<user-data-constraint>
<transport-guarantee>NONE</transport-guarantee>
</user-data-constraint>
</security-constraint>
<login-config>
<auth-method>BASIC</auth-method>
<realm-name></realm-name>
</login-config>
<security-role>
<role-name>admin</role-name>
</security-role>
<security-role>
<description>Getting Started App User</description>
<role-name>user</role-name>
</security-role>
<security-role>
<role-name>manager</role-name>
</security-role>
<security-role>
<role-name>provider</role-name>

Web-

Rendered by www.RenderX.com
Стр. 524 из 626 Безопасность Web-приложений

</security-role>
</web-app>

18.3.4. Аутентификация пользователей Web-ресурсов


При попытке получения доступа к защищенному Web-ресурсу, Web-контейнер активирует
механизм аутентификации, который настроен для данного ресурса. Для Web-ресурса
можно настроить следующие механизмы аутентификации:
• None (без аутентификации)
• HTTP basic authentication (базовая аутентификация HTTP)
• Form-based authentication (аутентификация, связанная с формой)
• Client-certificate authentication (аутентификация, основанная на сертификации клиента)
• Digest authentication (профильная аутентификация)

18.3.4.1. Базовая аутентификация


Если вы определили базовую аутентификацию HTTP, Web-сервер будет аутентифицировать
пользователя, используя имя пользователя и пароль, полученные от Web-клиента. В
качестве примера, в котором к программе Getting Started применяется базовая
аутентификация, смотрите раздел Контроль доступа к Web-ресурсам.

18.3.4.2. Аутентификация, связанная с формой


Если вы определяете аутентификацию, связанную с формой, вы можете настроить экран
ввода имени/пароля и страницы с сообщениями об ошибках, которые будут отображатся
пользователю HTTP-броузером.
Ни базовая HTTP-аутентификация, ни аутентификация, связанная с формой не являются
в высокой степени безопасными. В аутентификации, связанной с формой содержимое
диалога пользователя передается, как простой текст, и сервер назначения не является
аутентифицированным. При базовой аутентификации имена пользователей и пароли
отправляются через Internet как текст uuencode, но не как зашифрованный текст. Данная
форма аутентификации (использующая шифрование Base64) может раскрывать имена
пользователей и пароли, если только передача не совершается посредством протокола
SSL. Если кто-нибудь сможет перехватить передачу, имена пользователей и пароли могут
быть без труда расшифрованы.

18.3.4.3. Аутентификация, основанная на сертификации клиента


Аутентификация, основанная на сертификации клиента, является более безопасным
методом аутентификации, чем базовая или аутентификация, связанная с формой. Она
использует HTTP посредством протокола SSL, в котором сервер и (при необходимости)
клиент, аутентифицируется при помощи сертификатов открытых ключей (Public Key
Certificates). Secure Sockets Layer (SSL) обеспечивает шифровку данных, аутентификацию
сервера, целостность сообщений и, при необходимости, аутентификацию клиента для
соединения TCP/IP. Считайте, что сертификат открытого ключа является цифровым
эквивалентом паспорта. Он публикуется доверенной организацией, которая называется
certificate authority (CA), и обеспечивает идентификацию для его носителя. Если вы

Web-

Rendered by www.RenderX.com
Безопасность Web-ярусов Стр. 525 из 626

определяете аутентификацию, основанную на сертификации клиента, Web-сервер будет


аутентифицировать клиента, используя сертификат X.509 certificate, т.е. сертификат
открытого ключа, который согласован со стандартом, определенным X.509 Public Key
Infrastructure (PKI). Перед запуском приложения, использующего протокол SSL, необходимо
настроить поддержку SSL на сервере (см. раздел Установка и настройка поддержки SSL
для Tomcat).

18.3.4.4. Профильная (digest) аутентификация


Профильная (digest) аутентификация поддерживает концепцию классификации паролей
пользователя. Это приводит к тому, что сохраненная версия паролей шифруется (в том
виде, который сложно расшифровать), однако сервер Tomcat сможет использовать ее для
аутентификации.
С точки зрения пользователя, действие профильной аутентификации почти идентично
действию базовой аутентификации, в которой инициируется диалог входа в систему.
Разница между базовой и профильной аутентификацией заключается в сетевом соединении
между броузером и сервером, где пароль шифруется, даже при использовании не SSL-
соединения. В сервере пароль может сохраняться как в виде простого текста, так и в
зашифрованном виде. Он является достоверным для всех методов входа в систему и не
зависит от выбора, который сделает разместитель приложения.

18.3.4.5. Настройка механизма аутентификации Web-ресурсов


Для настройки механизма аутентификации в файле WAR, который будут использовать
Web-ресурсы:
1. Выберите WAR, содержащий Web-приложение.
2. Выберите закладку Security.
3. Выберите один из следующих механизмов аутентификации из ниспадающего меню
User Authentication Method:
4.
• None
• Basic
• Form Based
• Client-Certificate
• Digest
5. Если вы выберите аутентификацию, связанную с формой необходимо будет выбрать
Settings и заполнить поля Login Page и Error Page. Если пользователю будет отказано
в доступе, ему отобразится страница ошибки.
6. Если вы выберите базовую или профильную аутентификацию, необходимо будет
выбрать Settings и ввести отображаемое имя WAR-файла в поле Application Label,
отображающееся как часть всплывающего диалогового окна. В дескрипторе размещения,
данное значение отображается на имя области. По-умолчанию, оно установлено в
поле WAR Display Name, как показано в закладке General в утилите deploytool.

Web-

Rendered by www.RenderX.com
Стр. 526 из 626 Безопасность Web-приложений

18.3.4.6. Использование протокола SSL для увеличения конфиденциальности базовой


HTTP-аутентификации и аутентификации, связанной с формой
В базовой HTTP-аутентификации и аутентификации, связанной с формой пароли не
защищены. Чтобы преодолеть это ограничение, вы можете запускать эти протоколы
аутентификации через SSL-защищенную сессию и тем самым гарантировать, что все
содержимое сообщения будет защищено для конфиденциальности.
Конфигурация Tomcat, установленная по-умолчанию, не поддерживает использование
протокола SSL. Для этого необходимо настроить Tomcat, или любой другой Web-сервер,
который вы используете, на работу с SSL-коннектором. Для получения более подробной
информации обращайтесь к разделу Установка и настройка поддержки SSL для Tomcat.
Для настройки работы базовой HTTP-аутентификации или аутентификации, связанной с
формой через протокол SSL выполните следующие действия:
1. Выберите компонент Web.
2. В панели закладок Security, убедитесь, что в ниспадающем меню User Authentication
Method выбраны Basic или Form Based
3. Нажмите кнопку Add в секции Security Constraint.
4. Нажмите на Security Constraint, который добавлен.
5. В ниспадающем меню Network Security Requirement выберите CONFIDENTIAL.
Если в ограничении безопасности вы выбираете CONFIDENTIAL или INTEGRAL, данный
тип ограничения безопасности будет применяться для всех запросов, которые соответствуют
шаблонам URL в наборе Web-ресурсов, а не только в диалоге входа в систему.
Примечание: Хорошее правило безопасности: Если вы используете сессии, то после
переключения на протокол SSL вы больше не должны принимать ни один запрос к данной
сессии, если он сделан не через SSL. К примеру, сайт, на котором делаются покупки, может
не использовать SSL до тех пор, пока не понадобится переход к странице расчета с
покупателем. В этом случае с целью принятия номера кредитной карты возможен переход
на использование протокола SSL. После переключения на SSL, необходимо запретить
прослушивание запросов к данной сессии, совершаемых не через SSL-соединение. Это
необходимо сделать по-причине того, что ID сессии не был зашифрован для предыдущих
соединений. Стоит только информации о кредитной карте сохраниться в сессии,
потенциальному злоумышленнику станет доступна информация о вашей кредитной карте,
после чего он сможет ей воспользоваться. Данное правило может быть легко реализовано
путем использования фильтра.

18.3.5. Использование программируемой безопасности в Web-ярусах


Программируемая безопасность используется приложениями, осведомленными о
безопасности, когда для выражения модели безопасности приложения одной декларативной
безопасности не достаточно. Программируемая безопасность состоит из следующих
методов интерфейса HttpServletRequest:
• getRemoteUser
• isUserInRole
• getUserPrincipal

Web-

Rendered by www.RenderX.com
Безопасность EIS-ярусов Стр. 527 из 626

Для определения имени пользователя, с которым был аутентифицирован клиент, можно


использовать метод getRemoteUser. Метод isUserInRole используется для определения
того, назначена ли пользователю определенная роль безопасности. Метод getUserPrincipal
возвращает объект java.security.Principal.
Данные API позволяют сервлетам принимать решения бизнес-логики, базирующиеся на
логической роли удаленного пользователя. Они также позволяют сервлетам определять
имя principal текущего пользователя

18.3.6. Незащищенные Web-ресурсы


Многие приложения предоставляют небезопасное Web-содержимое, к которому может
получить доступ любой посетитель без прохождения аутентификации. В Web-ярусе при
установлении неограниченного доступа для отдельного URL запроса отсутствуют настройки
ограничений безопасности. Наличие одновременно незащищенных и защищенных ресурсов
является общепринятым. В подобной ситуации будут использоваться определенные
ограничения безопасности, а также метод для входа в систему, но они не будут
использоваться для контроля доступа к незащищенным ресурсам. У пользователя не будет
запрошен логин и пароль до тех пор, пока тот не будет запрашивать защищенный URI.
Universal Resource Identifier (URI) - Унифицированный Идентификатор Ресурсов является
глобальным уникальным идентификатором ресурса. Universal Resource Locator (URL) -
Унифицированный Указатель Ресурса - URI, который определяет протокол доступа (http
или https для Web-приложений) и физическое местонахождения ресурса (имя хоста и
относительный путь).
В спецификации Java Servlet, URI запроса является частью URL, стоящей после имени
хоста и номера порта. К примеру, в URL http://localhost:8080/myapp/jsp/hello.jsp, URI запроса
будет /jsp/hello.jsp. Кроме этого, URI запроса разбивается на путь контекста (который
определяет, какое Web-приложение должно обрабатьвать запрос) и оставшуюся часть
пути, используемую для выбора целевого сервлета.
Представьте себе сайт электронной коммерции с просматриваемым каталогом, к которому
вы хотите предоставить доступ для каждого, и областью, в которой находятся корзины
покупателя, предназначенной только для покупателей. В Web-приложении можно установить
два пути таким образом, чтобы защищался только шаблон /cart/*. Представьте себе
приложение, установленное в путь контекста /myapp,
• http://localhost:8080/myapp/index.jsp является незащищенным
• http://localhost:8080/myapp/cart/index.jsp является защищенным
Пользователь не будет опрошен, пока он не запросит доступ к ресурсам поддиректории
cart.

18.4. Безопасность EIS-ярусов


В ярусе EIS, компонент приложения запрашивает соединение к ресурсу EIS. Являясь
частью данного соединения, EIS может потребовать предъявления пароля для доступа к
ресурсу. Для создания аутентификации в EIS существует два подхода:
• Вход в систему, управляемый контейнером. Компонент приложения позволяет контейнеру
взять на себя ответственность о настройке и управлении входа в EIS. Контейнер

Web-

Rendered by www.RenderX.com
Стр. 528 из 626 Безопасность Web-приложений

определяет имя пользователя и пароль для установления соединения с экземпляром


EIS.
• Вход в систему, управляемый компонентом. Код компонента приложения управляет
процессом входа в EIS, и содержит код, который осуществляет процесс входа в EIS.
Для выбора типа входа в систему поставщик компонентов может использовать утилиту
deploytool.

18.4.1. Настройка входа в систему


Для настройки типа входа в систему при помощи утилиты deploytool
1. Выберите компонент.
2. Выберите закладку Resource Refs.
3. Нажмите Add.
4. В комбинированном окне Authentication, выберите из:
5.
A. Container - для входа в систему, управляемого контейнером
B. Application - для входа в систему, управляемого компонентом

18.4.2. Вход, управляемый контейнером


При входе, управляемым контейнером, компонент приложения не должен передавать
никакой информации безопасности методу getConnection() ресурса. Информация о
безопасности предоставляется контейнером. В этой ситуации можно использовать
javax.sql.DataSource, который предлагает два пути получения текущего java.sql.Connection,
в зависимости от того, используете вы вход, управляемый контейнером, или нет. Если вы
используете вход, управляемый контейнером, вам необходимо использовать метод
getConnection(). При использовании входа, управляемого компонентом, воспользуйтесь
методом getConnection(String username, String password).

18.4.3. Вход, управляемый компонентом


При входе, управляемом компонентом, последний несет ответственность за передачу
информации безопасности методу getConnection() ресурса. Информация о безопасности
должна содержать имя пользователя и пароль. Для получения java.sql.Connection при
помощи getConnection(String username, String password) можно использовать
javax.sql.DataSource.

18.5. Установка и настройка поддержки SSL для Tomcat


Secure Socket Layer (SSL) является технологией, позволяющей Web-броузерам и Web-
серверам связываться посредством безопасного соединения. При таком соединении,
отправляемые данные шифруются еще до отправки, затем дешифруются при получении
и до их обработки. И сервер, и броузер, шифруют весь траффик перед передачей данных.
Еще одним важным аспектом протокола SSL является аутентификация. Во время первой
попытки соединиться с Web-сервером через безопасное соединение, сервер предоставит
вашему Web-броузеру набор удостоверений в форме сертификата сервера. Сертификат

Web-

Rendered by www.RenderX.com
Установка и настройка поддержки SSL для Tomcat Стр. 529 из 626

сервера позволяет определить сайт и требования, которые он предьявляет. В некоторых


случаях, сервер может запрашивать сертификат, определяющий идентичность пользователя
и предьявляемые им требования (аутентификацию пользователя).
Для того чтобы воспользоваться преимуществом поддержки SSL, вам необходимо настроить
Tomcat, если вы планируете запускать его в качестве автономного Web-сервера. Если вы
планируете запускать Tomcat, главным образом как контейнер JSP и Java Servlet, на фоне
другого Web-сервера (Apache или Microsoft IIS), обычно необходимо настроить первичный
Web-сервер для обработки соединений пользователей через SSL. Соединения между
первичным Web-сервером и Tomcat шифровать не требуется.
Для установки и настройки поддержки SSL для Tomcat вам необходимы:
• Java Secure Socket Extension (JSSE), см. Использование JSSE.
• Хранилище сертификата сервера, см. Настройка сертификата сервера.
• Коннектор HTTPS, см. Настройка SSL-коннектора.
Для проверки доступности поддержки SSL, смотрите раздел Проверка поддержки SSL.

18.5.1. Использование JSSE


Для того чтобы Tomcat мог использовать SSL, необходимо установить Java™ Secure Socket
Extension (JSSE). JSSE интегрирован в Java WSDP 1.0. Он является набором пакетов Java,
которые позволяют выполнять безопасные Internet-соединения. Данные пакеты реализуют
Java-версию протоколов SSL (Secure Sockets Layer) и TLS (Transport Layer Security) и
включают в себя функции для шифрования данных, аутентификации сервера, целостности
сообщений и (при необходимости) аутентификации клиента. Используя JSSE, разработчики
могут обеспечить безопасное прохождение данных через TCP/IP между клиентом и
сервером, работающими с любыми протоколами приложений (такими, как HTTP, Telnet,
NNTP и FTP).
По-умолчанию, местонахождением файла jsse.jar является директория
JWSDP_HOME/common/lib/jsse.jar. Для получения подробной информации о JSSE,
воспользуйтесь ссылкой http://java.sun.com/products/jsse/index-102.html.

18.5.2. Настройка сертификата сервера


Для реализации SSL, Web-сервер должен обладать ассоциированным сертификатом для
каждого внешнего интерфейса или адреса IP, которые принимают безопасные соединения.
Теория, лежащая в основе разработки, заключается в том, что сервер должен
предоставлять определенного рода обоснованное заверение того, что его владелец именно
тот, о ком вы думаете, особенно перед получением любой точной информации. Это может
пригодиться для размышления о сертификате, как о "цифровой лицензии водителя" для
Internet-адреса. Согласно основной контактной информации о владельце сайта или его
администраторе он определяет, с какой компанией связан данный сайт.
Сертификат криптографическим образом подписан его владельцем, причем его трудно
подделать кому-нибудь другому. Для сайтов, связанных с электронной коммерцией, или
другими бизнес-транзакциями, для которых аутентификация очень важна, сертификат
может быть получен от известной вам организации Certificate Authority (CA), к примеру,
Verisign или Thawte.

Web-

Rendered by www.RenderX.com
Стр. 530 из 626 Безопасность Web-приложений

Если аутентификация не является крайней необходимостью, как если бы администратор


просто желал обеспечить приватность передаваемых и получаемых сервером данных и
избежать "прослушивания" соединения, вы можете просто сберечь время и расходы на
получение сертификата от CA и использовать собственный подписанный сертификат.
Сертификаты используются с протоколом HTTPS для аутентификации Web-клиентов.
Служба HTTPS сервера Tomcat не запустится до тех пор, пока не будет установлен
сертификат сервера. Для установки сертификата, используемого Tomcat для поддержки
SSL, используйте процедуру, описанную ниже.
Для установки сертификата сервера Tomcat может использоваться инструмент keytool,
являющийся утилитой для управления ключами и сертификатами. Она позволяет
пользователям администрировать свои собственные пары открытых/секретных ключей и
ассоциированные сертификаты для использования при самоаутентификации (когда
пользователь аутентифицирует себя с другими пользователями/службами), а также
использовать неприкосновенность данных и служб аутентификации, применяя цифровые
подписи. Это также позволяет пользователям кэшировать открытые ключи (в виде
сертификатов) взаимодействующих одноранговых узлов.
Сертификат является цифровым образом подписанным заявлением от одной сущности
(человека, компании, и.т.д.), сообщающей о том, что открытый ключ (и другая информация)
какой-либо другой сущности имеет частное значение. Когда данные подписываются
цифровым образом, подпись может быть верифицирована для проверки целостности и
аутентичности данных. Целостность означает то, что данные не были изменены или
подделаны, а аутентичность означает, что данные в самом деле поступили о того, кто их
создал и подписал.
Утилита keytool сохраняет ключи и сертификаты в, так называемом, keystore (хранилище
ключей). Реализация keystore по-умолчанию обеспечивает его как файл. Он защищаетt
секретные ключи при помощи пароля. Для более подробной информации о keytool, прочтите
документацию http://java.sun.com/j2se/1.4/docs/tooldocs/solaris/keytool.html.
1. Создание пары ключей и сертификата, подписанного самостоятельно.
Утилита keytool позволяет создавать сертификат. Она поставляется в J2SE SDK-версии,
в которую программно добавлен поставщик Java Cryptographic Extension, имеющий
реализации алгоритмов RSA. Данный поставщик позволяет вам импротировать
сертификаты, подписанные RSA.
Для создания сертификата, запустите утилиту keytool как указано далее, где
keystore_filename> - имя вашего файла keystore:

keytool -genkey -keyalg RSA -alias tomcat


-keystore <keystore_filename>

Примечание: Для того чтобы получить имя .keystore, Tomcat производит поиск файла
keystore в "домашней" директории компьютера, на котором запущен Tomcat.

Web-

Rendered by www.RenderX.com
Установка и настройка поддержки SSL для Tomcat Стр. 531 из 626

2. Утилита keytool запросит у вас следующую информацию:


3.
A. Keystore password - Введите пароль. (можно использовать changeit, для
совместимости с паролем, установленным по-умолчанию в файле keystore J2SE
SDK.)
B. First and last name - Введите полностью пригодное имя вашего сервера. Такое имя
включает в себя имя хоста и домен. Для тестирования на отдельном компьютере
таким именем будет localhost.
C. Organizational unit - (Подразделение организации) Введите соответствующее
значение.
D. Organization - (Организация) Введите соответствующее значение.
E. City or locality - (Город или местность) Введите соответствующее значение.
F. State or province - (Штат или область) Введите полное имя.
G. Two-letter country code - (Двухбуквенный код страны) Для России, двухбуквенный
код страны - RU.
H. Проверьте еще раз введенную информацию и нажмите Yes, если все верно.
I. Key password for Tomcat - (ключевой пароль для Tomcat) Ничего не указывайте,
нажмите Enter.
Сертификат, подписанный самостоятельно, является приемлемым для большинства
соединений SSL. Если вы используете подобный сертификат, перейдите к разделу
Настройка SSL-коннектора. Если же вы желаете использовать сертификат, подписанный
CA, перейдите к разделу Получение цифрового сертификата.

18.5.2.1. Получение цифрового сертификата


Для получения сертификата, подписанного CA, выполните следующее:
1. Создайте Certificate Signing Request (CSR).

keytool -certreq -alias tomcat -keyalg RSA


-file <csr_filename> -keystore <keystore_filename>

2. Отправьте содержимое <csr_filename> для подписи.


3. Если вы используете Verisign CA, откройте сайт http://digitalid.verisign.com/. Verisign
перешлет вам подписанный сертификат по e-mail. Сохраните данный сертификат в
файл.
4. Импортируйте сертификат в сервер:

keytool -import -alias root -trustcacerts -file

Web-

Rendered by www.RenderX.com
Стр. 532 из 626 Безопасность Web-приложений

<signed_cert_file> -keystore <keystore_filename>

5. Импортируйте сертификат (если вы используете сертификат, подписанный CA).


Если ваш вертификат подписан Certification Authority (CA), необходимо его импортировать.
При использовании сертификата, подписанного самостоятельно или сертификата,
подписанного CA, который ваш броузер не распознает, при первой попытке пользователя
получения доступа к серверу запускается диалог. Пользователь может предпочесть
доверять данному сертификату только для данной сессии или постоянно.
Для импортирования сертификата выполните следующие действия:
1. Запросите сертификат CA от вашего CA. Сохраните сертификат в файл.
2. Для инсталляции сертификата CA в Java 2 Platform, Standard Edition, запустите утилиту
keytool,как указано далее (вы должны обладать необходимыми полномочиями для
изменения файла $JAVA_HOME/jre/lib/security/cacerts)

keytool -import -trustcacerts -alias tomcat


-file <ca-cert-filename> -keystore <keystore-filename>

18.5.3. Настройка SSL-коннектора


По-умолчанию, коннектор SSL HTTPS недоступен. Вы можете сделать его доступным и
настроить его на порт 8443, используя любой из следующих методов:
• Добавьте коннектор при помощи утилиты admintool. См. раздел Добавление SSL-
коннектора в admintool.
• Убрать комментарий SSL-коннектора в файле JWSDP_HOME/conf/server.xml. См. раздел
Настройка SSL-коннектора в файле server.xml.

18.5.3.1. Добавление SSL-коннектора в admintool


Для настройки SSL-коннектора при помощи утилиты admintool необходимо вначале создать
файл keystore, как описано в разделе Установка сертификата сервера. Tomcat производит
поиск файла keystore с именем .keystore в "домашней" директории компьютера, на котором
запущен Tomcat. Когда вы убедитесь, что файл keystore создан, выполните следующие
действия:
1. Запустите Tomcat, если он еще не запущен.
2. Запустите admintool, введя строку http://localhost:8080/admin в Web-броузере.
3. Введите комбинацию имени и пароля пользователя, которому назначена роль admin
4. Выберите Service (Java Web Services Developer Pack) в левом окне.
5. Выберите Create New Connector из ниспадающего списка в правом окне.
6. В поле Type выберите HTTPS.

Web-

Rendered by www.RenderX.com
Установка и настройка поддержки SSL для Tomcat Стр. 533 из 626

7. В поле Port введите 8443 (или тот порт, который требуется). Это определяет номер
порта TCP/IP, который Tomcat будет использовать для безопасных соединений.
8. Введите Keystore Name и Keystore Password, если вы создали файл keystore с именем
отличным от .keystore, или же если .keystore находится в директории, отличной от
"домашней" директории машины, на которой запущен Tomcat, или пароль имеет
значение, отличное от changeit. Если вы используете ожидаемые значения, оставьте
это поле пустым.
9. "Домашней" директорией обычно является /home/user_name в системах Unix и Linux,
и C:\Documents and Settings\user_name в системах Microsoft Windows.
10. Выберите Save для сохранения нового элемента Connector для данной сессии.
11. Выберите Commit Changes для записи информации о новом элементе Connector в
файл server.xml. Коннектор будет доступен при следующей перезагрузке Tomcat.
Для того чтобы просмотреть и/или отредактировать новый элемент Connector, разверните
узел Service (Java Web Services Developer Pack) и выберите Connector (8443).

18.5.3.2. Настройка SSL-коннектора в файле server.xml


Пример элемента Connector для SSL-коннектора включен в файл server.xml. Данный
элемент Connector по-умолчанию закомментирован. Для того чтобы сделать его доступным
для Tomcat, уберите тэги комментария вокруг элемента SSL Connector:
1. Закройте Tomcat, если он запущен. Изменения в файле JWSDP_HOME/conf/server.xml
будут прочитаны Tomcat, когда он перезагрузится.
2. Откройте файл JWSDP_HOME/conf/server.xml с помощью текстового редактора.
3. Найдите следующую часть кода в файле (попробуйте поискать "SSL Connector"). Уберите
тэги комментария вокруг записи Connector. Тэги комментарии, которые необходимо
убрать, выделены ниже жирным шрифтом.

<!-- SSL Connector on Port 8443 -->

<!--
<Connector
className="org.apache.coyote.tomcat4.CoyoteConnector"
port="8443" minProcessors="5"
maxProcessors="75"
enableLookups="false"
acceptCount="10"
connectionTimeout="60000" debug="0"
scheme="https" secure="true">
<Factory
className="org.apache.coyote.tomcat4.
CoyoteServerSocketFactory"
clientAuth="false" protocol="TLS" />

Web-

Rendered by www.RenderX.com
Стр. 534 из 626 Безопасность Web-приложений

</Connector>
-->

4. Сохраните и закройте файл.


5. Запустите Tomcat.
Атрибуты данного элемента Connector описаны подробнее в главе Утилита
администрирования Tomcat.

18.5.4. Проверка поддержки SSL


Для тестирования и проверки того, что поддержка SSL была корректно установлена в
Tomcat, загрузите вступительную страницу Tomcat, используя следующий URL:

https://localhost:8443/

Протокол https в данном URL указывает на то, что броузер использует SSL-протокол. Порт
8443,является портом, в котором в предыдущем разделе был создан SSL-Connector.
При первой загрузке пользователем данного приложения отобразится диалог New Site
Certificate. Выберите Next для прохождения ряда диалогов New Site Certificate, выберите
Finish, когда достигнете последнего диалога.

18.5.5. Выявление неисправностей при SSL-соединениях


18.5.5.1. При загрузке Tomcat возбуждается исключительная ситуация наподобие
"java.io.FileNotFoundException: {some-directory}/{some-file} not found".
Вероятнее всего Tomcat не может найти файл keystore. По-умолчанию Tomcat ожидает
файл keystore с именем .keystore в "домашней" директории компьютера, на котором запущен
Tomcat (она может совпадать или не совпадать с вашей). Если файл keystore находится
в другом месте, необходимо добавить атрибут keystoreFile в элемент <Factory> в файле
конфигурации Tomcat или определить местонахождение файла в узле Connector (8443),
используя утилиту admintool.

18.5.5.2. При загрузке Tomcat возбуждается исключительная ситуация наподобие


"java.io.FileNotFoundException: Keystore was tampered with, or password was incorrect"
Представьте, что на самом деле никто не подделывал ваш файл keystore, скорее всего
Tomcat использует пароль, отличный от того, который вы использовали при создании
файла keystore. Для исправления этого, необходимо заново создать файл keystore или
добавить (или обновить) атрибут keystorePass элемента <Factory> в конфигурации Tomcat
или узла Connector (8443), использовав утилиту admintool. ПОМНИТЕ - Пароли
чувствительны к регистру!

18.5.5.3. Если у вас по прежнему возникают проблемы,


Если у вас по прежнему возникают проблемы, вы можете воспользоваться в качестве
источника информации списком почтовой рассылки TOMCAT-USER. На странице

Web-

Rendered by www.RenderX.com
Устранение неисправностей Стр. 535 из 626

http://jakarta.apache.org/site/mail.html вы сможете найти указатели на архивы предыдущих


сообщений, а также информацию о подписке.

18.5.6. Общие советы по работе с SSL


• Протокол SSL разработан для эффективного обеспечения безопасности. Однако процесс
шифрования/дешифрования является, с точки зрения производительности, более
требовательным к вычислительным ресурсам. Нет абсолютной необходимости запускать
всё Web-приложение через SSL. Определение того, какие страницы требуют безопасного
соединения, а какие нет, является индивидуальным решением разработчика. Страницы,
которые могут потребовать безопасного соединения, включают себя страницы логинов,
страницы личной информации, страницы расчета с клиентом или любые другие
страницы, где возможна передача информации о кредитной карточке клиента. Любая
страница в пределах приложения может быть запрошена через безопасный сокет путем
простой установки префикса в адресе: https: вместо http:. Любые страницы, которые
полностью требуют безопасного соединения, должны проверять тип протокола,
связанный со страницей запроса, и выполнять соответствующие действия, если https:
не определен.
• Использование безопасного соединения на виртуальных хостах, базирующихся на
именах, может быть проблематичным. Это является ограничением разработки самого
протокола SSL. Квитирование SSL, когда броузер клиента принимает сертификат
сервера, должно происходить перед получением доступа к HTTP запроса. В результате
запрошенная информация, содержащая имя виртуального хоста, не может быть
определена до аутентификации, следовательно, определить множество сертификатов
на одном IP-адресе не представляется возможным. Если все виртуальные хосты на
одном IP-адресе нуждаются в аутентификации относительно одного и того же
сертификата, добавление множества виртуальных хостов не должно служить
препятствием для нормальной работы SSL на сервере. Однако знайте, что большинство
броузеров клиентов будет сравнивать доменное имя сервера с доменным именем,
указанным в сертификате, если таковой имеется (применяется, в основном, к
официальным сертификатам, подписанным CA). Если доменные имена не совпадают,
броузеры будут отображать клиенту соответствующее предупреждение. В общем, в
производственной среде с протоколом SSL, как правило, используются только
виртуальные хосты, основанные на адресах.

18.5.7. Дополнительная информация о протоколе SSL


Для получения дополнительной информации о протоколе SSL, прочтите документ Tomcat
"SSL Configuration HOW-TO", который находится в файле <JWSDP_HOME>/docs/tomcat/ssl-
howto.html.

18.6. Устранение неисправностей


18.6.1. Сервер возвращает код ответа HTTP: 403
Симптом: При входе в утилиту deploytool при загрузке или, выбрав File _ Set Tomcat Server
из меню deploytool, вы получаете следующую ошибку:

Web-

Rendered by www.RenderX.com
Стр. 536 из 626 Приложение Coffee Break

Unable to add Server URL 'http://localhost:8080/manager'

Server returned HTTP response code: 403 for URL:


http://localhost;8080/manager/list

Решение: Имени пользователя, которое вы используете для входа в deploytool, не была


назначена роль manager. Используйте утилиту admintool для редактирования профиля
пользователя и назначьте пользователю роль manager. Информация об использовании
admintool для редактирования профиля пользователя находится в разделе Управление
пользователями.

18.7. Дополнительная информация


• Основные понятия о концепции безопасности в JSP и технологии Java Servlet
описываются в спецификации Java Servlet, доступной в Internet:
http://java.sun.com/products/servlet/download.html
• Для получения информации, относящейся к установке поддержки SSL для Tomcat
прочтите документ Tomcat "SSL Configuration HOW-TO", который находится в файле
<JWSDP_HOME>/docs/tomcat/ssl-howto.html
• Для получения информации, относящейся к настройке Tomcat для поддержки
безопасности, основанной на контейнерах, путем соединения с существующей базой
данных имен, паролей и ролей, прочтите документ Tomcat "Realm Configuration HOW-
TO", который находится в файле <JWSDP_HOME>/docs/tomcat/realm-howto.html

19. Приложение Coffee Break


Стефани Бодофф, Мэйдэн Фишер, Дэйл Грин, Ким Хаас
Введение в данное руководство представляет сценарий, в котором Web-приложение (Coffee
Break) построено с использованием Web-служб. Теперь, когда мы обсудили все технологии,
необходимые для построения Web-приложений и Web-служб, данная глава описывает
реализацию сценария, описанного в Главе 1.

19.1. Обзор приложения Coffee Break


Приложение Coffee Break используется для продажи кофе через Internet. Для того чтобы
заказать кофе покупатель связывается с сервером Coffee Break и в режиме online указывает
ему необходимое количество различных видов кофе, который он желает приобрести.
Заполнив форму заказа, пользователь нажимает кнопку Submit и отправляет его серверу.
Сервер Coffee Break состоит из Java-сервлетов, JSP-страниц и JavaBean-компонентов.
В приложение Coffee Break не содержится хранилища. Оно обрабатывает запросы
пользователя и управляет заказами и процессом оплаты. Каждый заказ выполняется путем

Web-

Rendered by www.RenderX.com
Обзор приложения Coffee Break Стр. 537 из 626

отправки запросов одному или более поставщикам кофе. Данный процесс


проиллюстрирован на Рисунке 18-1.

Рисунок 18-1 Потоки приложения Coffee Break

При запуске, а также по требованию сервер Coffee Break опрашивает поставщиков кофе
с целью получения от них информации о наличии в продаже разных сортов кофе и о его
ценах.
1. Сервер Coffee Break использует для связи с одним из поставщиков обмен сообщениями
JAXM.
2. Необходимыми условиями для осуществления обмена сообщениями JAXM в виде
запрос-ответ являются работа с поставщиком и наличие уже происходивших ранее
сделок. Обе стороны соглашаются обмениваться четырьмя видами XML-сообщений и
должны установить определения DTD для каждого из них.
3. Сервер Coffee Break использует JAXR для отправки запросов с целью поиска
поставщиков кофе, которые поддерживают JAX-RPC для сервера реестра.
4. Сервер Coffee Break запрашивает прайс-листы у каждого из поставщиков кофе. Сервер
осуществляет соответствующую процедуру удаленного вызова и ожидает ответа,
который JavaBean-компонент представляет в виде прайс-листа. Поставщик JAXM
возвращает прайс-листы в виде XML-документов.
5. Получив ответы, сервер Coffee Break обрабатывает прайс-листы из JavaBean-
компонентов, возвращенных от поставщиков.
6. Сервер Coffee Break создает локальную базу данных поставщиков.
7. После того как заказ осуществлен, одному или более поставщикам отправляются
подзаказы, при этом используется протокол, предпочитаемый поставщиком.

Web-

Rendered by www.RenderX.com
Стр. 538 из 626 Приложение Coffee Break

19.2. Служба JAX-RPC поставщика


Сервер Coffee Break одновременно является и клиентом - он осуществляет удаленный
вызов службы JAX-RPC поставщика. Программный код службы состоит из интерфейса
службы, класса реализации службы и некоторых JavaBean-компонентов, которые
использутся для параметров метода и возвращаемых типов.

19.2.1. Интерфейс службы


Интерфейс службы SupplierIF определяет методы, которые могут вызываться удаленными
клиентами. Параметры и возвращаемые типы данных методов являются JavaBean-
компонентами:
• AddressBean - информация об адресе доставки покупателю
• ConfirmationBean - идентификатор заказа и дата поставки
• CustomerBean - контактная информация о покупателе
• LineItemBean - предмет заказа
• OrderBean - id заказа, заказчик, адрес, список позиций заказа, итоговая стоимость
• PriceItemBean - запись прайс-листа (название кофе и оптовая цена)
• PriceListBean - прайс-лист
В виду того, что данные компоненты используются другими программами, их исходный
код находится в директории JWSDP_HOME/docs/tutorial/examples/cb/common/src. Исходный
код интерфейса SupplierIF, соответственно, находится в директории
JWSDP_HOME/docs/tutorial/examples/cb/jaxrpc/src.

package com.sun.cb;

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface SupplierIF extends Remote {

public ConfirmationBean placeOrder(OrderBean order)


throws RemoteException;
public PriceListBean getPriceList() throws RemoteException;
}

19.2.2. Реализация службы


Класс SupplierImpl реализует методы placeOrder и getPriceList, которые определяются
интерфейсом SupplierIF. Теперь вы можете сфокусировать свое внимание на программном
коде, относящемся к JAX-RPC, так как эти методы являются короткими и простыми. В
реальном приложении, данные методы будут получать доступ к базам данных и

Web-

Rendered by www.RenderX.com
Служба JAX-RPC поставщика Стр. 539 из 626

взаимодействовать с другими службами, такими, как доставка, произведение расчетов и


хранилище.
Метод placeOrder принимает введенную информацию о заказе и возвращает подтверждение
для данного заказа. Для упрощения, метод placeOrder подтверждает каждый заказ и
устанавливает в подтверждении дату отправки на следующий день. Эта дата расчитывается
при помощи класса utilityDateHelper, который находится в поддиректории cb/common.
Исходный код метода placeOrder выглядит следующим образом:

public ConfirmationBean placeOrder(OrderBean order) {

Date tomorrow =
com.sun.cb.DateHelper.addDays(new Date(), 1);
ConfirmationBean confirmation =
new ConfirmationBean(order.getId(), tomorrow);
return confirmation;
}

Метод getPriceList возвращает объект PriceListBean, отображающий название и цену для


каждого типа кофе, который может быть заказан при помощи данной службы. Метод
getPriceList создает объект PriceListBean путем вызова метода private с именем loadPrices.
В промышленном варианте приложения метод loadPrices будет извлекать цены из базы
данных. Однако в нашем случае, метод loadPrices получает цены из файла Supplier-
Prices.properties. Далее представлены методы getPriceList и loadPrices:

public PriceListBean getPriceList() {

PriceListBean priceList = loadPrices();


return priceList;
}

private PriceListBean loadPrices() {

String propsName = "com.sun.cb.SupplierPrices";


Date today = new Date();
Date endDate = DateHelper.addDays(today, 30);

PriceItemBean[] priceItems =
PriceLoader.loadItems(propsName);
PriceListBean priceList =
new PriceListBean(today, endDate, priceItems);

Web-

Rendered by www.RenderX.com
Стр. 540 из 626 Приложение Coffee Break

return priceList;
}

19.2.3. Публикация службы в реестре


Для того чтобы покупатели смогли отыскать нашу службу, мы будем публиковать ее в
реестре. Программы, выполняющие публикацию и удаление службы в реестре, называются,
соответственно OrgPublisher и OrgRemover. Данные программы не являются частью Web-
приложения службы. Они являются независимыми программами, которые запускаются
при помощи комманды ant set-up-service (см. раздел Создание и установка службы JAX-
RPC.) Сразу же после инсталляции служба публикуется в реестре. И равно, сразу же перед
тем, как служба удаляется, она удаляется и из реестра.
Программа OrgPublisher начинает свою работу с загрузки значений String из файла
CoffeeRegistry.properties. Затем она создает экземпляр класса utility под названием JAXR-
Publisher. OrgPublisher подключается к реестру путем вызова метода makeConnection
класса JAXRPublisher. Для публикации службы OrgPublisher вызывает метод executePublish,
который принимает в качестве входящих данных значения username, password и endpoint.
Значения username и password являются необходимыми значениями для сервера реестра.
Значением endpoint является URL-адрес, который будет использован удаленным клиентом
для контакта с нашей службой JAX-RPC. Метод executePublish класса JAXRPublisher
возвращает ключ, который однозначно идентифицирует службу в реестре. Приложение
OrgPublisher сохраняет данный ключ в текстовом файле под названием orgkey.txt.
Программа OrgRemover считывает ключ из файла orgkey.txt, после чего она может удалить
службу из реестра (см. раздел Удаление службы из реестра.) Исходный код программы
OrgPublisher выглядит следующим образом.

package com.sun.cb;

import javax.xml.registry.*;
import java.util.ResourceBundle;
import java.io.*;

public class OrgPublisher {

public static void main(String[] args) {

ResourceBundle registryBundle =
ResourceBundle.getBundle
("com.sun.cb.CoffeeRegistry");

String queryURL =
registryBundle.getString("query.url");

Web-

Rendered by www.RenderX.com
Служба JAX-RPC поставщика Стр. 541 из 626

String publishURL =
registryBundle.getString("publish.url");
String username =
registryBundle.getString("registry.username");
String password =
registryBundle.getString("registry.password");
String endpoint = registryBundle.getString("endpoint");
String keyFile = registryBundle.getString("key.file");

JAXRPublisher publisher = new JAXRPublisher();


publisher.makeConnection(queryURL, publishURL);
String key = publisher.executePublish
(username, password, endpoint);

try {
FileWriter out = new FileWriter(keyFile);
out.write(key);
out.flush();
out.close();
} catch (IOException ex) {
System.out.println(ex.getMessage());
}
}

Класс JAXRPublisher является почти идентичным программе-примеру JAXRPublish.java,


описанному в разделе Управление данными реестра.
Вначале метод makeConnection устанавливает соединение с сервером реестра. Для
получения более детальной информации обратитесь к разделу Установка соединения.
Для того чтобы сделать это, вначале определяется набор свойств соединения с
использованием запроса и публикации URL-адресов, переданных из файла CoffeeReg-
istry.properties. Для сервера реестра, запрос и публикация URL-адресов являются, в
сущности, одинаковыми.

Properties props = new Properties();


props.setProperty("javax.xml.registry.queryManagerURL",
queryUrl);
props.setProperty("javax.xml.registry.lifeCycleManagerURL",
publishUrl);

Web-

Rendered by www.RenderX.com
Стр. 542 из 626 Приложение Coffee Break

Затем, метод makeConnection устанавливает соединение, используя свойства соединения:

ConnectionFactory factory = ConnectionFactory.newInstance();


factory.setProperties(props);
connection = factory.createConnection();

Метод executePublish принимает три аргумента: имя пользователя, пароль и конечную


точку. Процесс начинается с получения объекта RegistryService, затем объекта Business-
QueryManager и объекта BusinessLifeCycleManager, которые позволяют осуществить
запросы и управлять данными:

rs = connection.getRegistryService();
blcm = rs.getBusinessLifeCycleManager();
bqm = rs.getBusinessQueryManager();

В виду того, что для выполнения публикации данных требуется подтверждение пароля,
для получения мандата безопасности используются аргументы username и password:

PasswordAuthentication passwdAuth =
new PasswordAuthentication(username,
password.toCharArray());
Set creds = new HashSet();
creds.add(passwdAuth);
connection.setCredentials(creds);

После этого создается объект Organization с именем "JAXRPCCoffeeDistributor", а затем


объект User, который будет служить в качестве первичного контакта. Данные получаются
из пакета ресурсов, минуя жесткое программирование данных как строк, однако во всем
остальном данный код почти идентичен представленному в главе JAXR.

ResourceBundle bundle =
ResourceBundle.getBundle("com.sun.cb.CoffeeRegistry");

//
Organization org =
blcm.createOrganization(bundle.getString("org.name"));

Web-

Rendered by www.RenderX.com
Служба JAX-RPC поставщика Стр. 543 из 626

InternationalString s =
blcm.createInternationalString
(bundle.getString("org.description"));
org.setDescription(s);

// ,

User primaryContact = blcm.createUser();


PersonName pName =
blcm.createPersonName(bundle.getString("person.name"));
primaryContact.setPersonName(pName);

В коде добавляется номер телефона и email-адрес пользователя, после чего тот


определяется, как первичный контакт:

org.setPrimaryContact(primaryContact);

Это определяется классификацию JAXRPCCoffeeDistributor, используя Систему


классификации северо-американской промышленности (North American Industry Classification
System - NAICS). В данном случае используется классификация "Other Grocery and Related
Products Wholesalers".

Classification classification = (Classification)


blcm.createClassification(cScheme,
bundle.getString("classification.name"),
bundle.getString("classification.value"));
Collection classifications = new ArrayList();
classifications.add(classification);
org.addClassifications(classifications);

После этого добавляется служба JAX-RPC, называемая "JAXRPCCoffee Service", и ее


привязки. URI доступа, используемый привязками службы, содержит конечный URL, который
будут использовать удаленные клиенты для связи с нашей службой:

http://localhost:8080/jaxrpc-coffee-supplier/jaxrpc/SupplierIF

Web-

Rendered by www.RenderX.com
Стр. 544 из 626 Приложение Coffee Break

Если перед запуском данной программы служба JAX-RPC не была установлена, то


возбуждаются исключения, так как JAXR проверяет каждый URI.

Collection services = new ArrayList();


Service service =
blcm.createService(bundle.getString("service.name"));
InternationalString is =
blcm.createInternationalString
(bundle.getString("service.description"));
service.setDescription(is);

//
Collection serviceBindings = new ArrayList();
ServiceBinding binding = blcm.createServiceBinding();
is = blcm.createInternationalString
(bundle.getString("service.binding"));
binding.setDescription(is);
try {
binding.setAccessURI(endpoint);
} catch (JAXRException je) {
throw new JAXRException("Error: Publishing this " +
"service in the registry has failed because " +
"the service has not been installed on Tomcat.");
}
serviceBindings.add(binding);

//
service.addServiceBindings(serviceBindings);

// ,
-
services.add(service);
org.addServices(services);

После этого организация сохраняется в реестре:

Collection orgs = new ArrayList();


orgs.add(org);

Web-

Rendered by www.RenderX.com
Служба JAX-RPC поставщика Стр. 545 из 626

BulkResponse response = blcm.saveOrganizations(orgs);

Объект BulkResponse возвращенный методом saveOrganizations включает в себя объект


Key, содержащй уникальное значение ключа организации. Метод executePublish вначале
проверяет успешность выполнения вызова saveOrganizations.
Если вызов произведен успешно, метод извлекает значение из объекта Key и отображает
его:

Collection keys = response.getCollection();


Iterator keyIter = keys.iterator();
if (keyIter.hasNext()) {
javax.xml.registry.infomodel.Key orgKey =
(javax.xml.registry.infomodel.Key) keyIter.next();
id = orgKey.getId();
System.out.println("Organization key is " + id);
}

И, наконец, метод возвращает строку id так, что программа OrgPublisher может сохранить
ее в файл для использования программой OrgRemover.

19.2.4. Удаление службы из реестра


Программа OrgRemover удаляет службу из сервера реестра непосредственно перед тем,
как удаляется сама служба. Как и OrgPublisher, программа OrgRemover запускается путем
извлечения значений из файла CoffeeRegistry.properties. Одно из этих значений, keyFile,
является именем файла, содержащего ключ, который уникальным образом идентифицирует
службу. OrgPublisher считывает ключ из файла, устанавливает соединение с сервером
реестра путем вызова метода makeConnection, а затем удаляет службу из реестра при
помощи executeRemove. Далее представлен исходный код программы OrgRemover:

package com.sun.cb;

import java.util.ResourceBundle;
import javax.xml.registry.*;
import javax.xml.registry.infomodel.Key;
import java.io.*;

public class OrgRemover {

Connection connection = null;

Web-

Rendered by www.RenderX.com
Стр. 546 из 626 Приложение Coffee Break

public static void main(String[] args) {

String keyStr = null;

ResourceBundle registryBundle =
ResourceBundle.getBundle
("com.sun.cb.CoffeeRegistry");

String queryURL =
registryBundle.getString("query.url");
String publishURL =
registryBundle.getString("publish.url");
String username =
registryBundle.getString("registry.username");
String password =
registryBundle.getString("registry.password");
String keyFile = registryBundle.getString("key.file");

try {
FileReader in = new FileReader(keyFile);
char[] buf = new char[512];
while (in.read(buf, 0, 512)>= 0) { }
in.close();
keyStr = new String(buf).trim();
} catch (IOException ex) {
System.out.println(ex.getMessage());
}

JAXRRemover remover = new JAXRRemover();


remover.makeConnection(queryURL, publishURL);
javax.xml.registry.infomodel.Key modelKey = null;
modelKey = remover.createOrgKey(keyStr);
remover.executeRemove(modelKey, username, password);
}
}

Созданный при помощи программы OrgRemover, экземпляр класса JAXRRemover содержит


методы makeConnection, createOrgKey и executeRemove. Это идентично программе-примеру
JAXRDelete.java, описанному в разделе Удаление данных из реестра.
Метод makeConnection является идентичным методу JAXRPublisher с таким же именем.

Web-

Rendered by www.RenderX.com
Служба JAXM поставщика Стр. 547 из 626

Метод createOrgKey является методом utility, принимающим только один аргумент -


строковое значение, извлекаемое из файла ключа. Он получает объект RegistryService и
объект BusinessLifeCycleManager, а затем создает объект Key из строкового значения.
Метод executeRemove принимает три аргумента: имя пользователя (username), пароль
(password) и объект Key, возвращенный методом createOrgKey. Он использует аргументы
username и password для установления мандатов безопасности в сервере реестра, так
же, как это делает метод executePublish.
Затем метод "заворачивает" объект Key в Collection и использует метод deleteOrganizations
объекта BusinessLifeCycleManager для удаления организации.

Collection keys = new ArrayList();


keys.add(key);
BulkResponse response = blcm.deleteOrganizations(keys);

Метод deleteOrganizations возвращает ключи удаленных им организаций так, чтобы метод


executeRemove мог затем проверить корректность выполнения операции и отобразить
ключ удаленной организации.

Collection retKeys = response.getCollection();


Iterator keyIter = retKeys.iterator();
javax.xml.registry.infomodel.Key orgKey = null;
if (keyIter.hasNext()) {
orgKey =
(javax.xml.registry.infomodel.Key) keyIter.next();
id = orgKey.getId();
System.out.println("Organization key was " + id);
}

19.3. Служба JAXM поставщика


Служба JAXM поставщика является всего лишь соглашениями, которые поставщик и Coffee
Break должны выполнять относительно обмена XML-документами. Данные соглашения
включают в себя то, какими типами сообщений они будут обмениваться, форму этих
сообщений и то, какого рода обмен сообщений JAXM будет происходить. Если обе стороны
договариваются об односторонних сообщениях, они также должны будут использовать
поставщиков сообщений, которые будут переговариваются между собой и использовать
один и тот же профиль. В данном сценарии, стороны условились использовать обмен
сообщениями типа запрос-ответ, таким образом, поставщик сообщений им не потребуется.

Web-

Rendered by www.RenderX.com
Стр. 548 из 626 Приложение Coffee Break

Сервер Coffee Break отправляет два вида сообщений:


• Запросы текущей оптовой цены на кофе
• Заказы покупателей
Поставщик кофе JAXM отвечает двумя видами сообщений:
• Текущие прайс-листы
• Подтверждения заказа
Все сообщения, которые они отправляют, соответствуют утвержденной XML-структуре,
которая определяется в DTD для каждого вида сообщений. Это позволяет им обмениваться
собщениями, даже если они используют различные форматы внутренних документов.
Четыре вида сообщений, которыми обмениваются между собой сервер Coffee Break и
поставщик JAXM, определены при помощи следующих DTD:
• request-prices.dtd
• price-list.dtd
• coffee-order.dtd
• confirm.dtd
Данные DTD находятся в директории JWSDP_HOME/docs/tutorial/examples/cb/jaxm/dtds.
Директория dtds содержит также пример того, как должен выдеглять XML-документ,
определенный соответствующим DTD. Ниже представлены XML-файлы, соответствующие
каждому из DTD:
• request-prices.xml
• price-list.xml
• coffee-order.xml
• confirm.xml
Обе стороны знают заранее, чего ожидать в каждом отдельном сообщении так как они
используют DTD, следовательно они могут извлекать содержимое, используя JAXM API.
Програмный код приложений клиента и сервера находится в следующей директории:

JWSDP_HOME/docs/tutorial/examples/cb/jaxm/src/com/sun/cb

19.3.1. Клиент JAXM


Сервер Coffee Break, который в данном сценарии является клиентом JAXM, отправляет
запросы поставщику JAXM. В виду того, что применяется запросно-ответная форма
сообщений JAXM, приложения клиента используют для отправки сообщений метод call
класса SOAPConnection.

Web-

Rendered by www.RenderX.com
Служба JAXM поставщика Стр. 549 из 626

SOAPMessage response = con.call(request, endpoint);

Таким образом, программный код клиента состоит из двух основных задач. Первая -
создание и отправка запроса, вторая - извлечение содержимого из ответа. Данные задачи
обрабатываются классами PriceListRequest и OrderRequest.

19.3.1.1. Отправка зароса


В данном разделе представлен программный код, используемый для создания и отправки
запросов для обновления прайс-листа. Это выполняется в методе getPriceList класса
PriceListRequest, что следует из DTD price-list.dtd.
Начало метода getPriceList установливает соединение, которое будет использоваться для
отправки запроса. Затем метод получает объект MessageFactory (по-умолчанию) таким
образом, чтобы можно было создать объект msg объектаSOAPMessage.

SOAPConnectionFactory scf =
SOAPConnectionFactory.newInstance();
SOAPConnection con = scf.createConnection();

MessageFactory mf = MessageFactory.newInstance();
SOAPMessage msg = mf.createMessage();

Следующим шагом является доступ к объекту SOAPEnvelope данного сообщения, который


будет использоваться для создания объекта Name для каждого нового созданного им
элемента. Он также используется для доступа к объекту SOAPBody, к которому будет
добавляться содержимое сообщения.

SOAPPart part = msg.getSOAPPart();


SOAPEnvelope envelope = part.getEnvelope();
SOAPBody body = envelope.getBody();

Файл price-list.dtd задает, что самым крайним элементом тела является элемент request-
prices, содержащий элемент request. Текстовый узел, добавленный к request является
текстом отправленного запроса. У каждого нового элемента, добавленного к сообщению,
должен быть объект Name (необходимый для его идентификации), который создается при
помощи метода createName класса Envelope. Представленные далее строки программного
кода создают крайний элемент объекта body в объекте SOAPBody. Первым элементом,
созданным в объекте SOAPBody всегда является объект SOAPBodyElement.

Name bodyName = envelope.createName("request-prices",

Web-

Rendered by www.RenderX.com
Стр. 550 из 626 Приложение Coffee Break

"RequestPrices", "http://sonata.coffeebreak.com");
SOAPBodyElement requestPrices =
body.addBodyElement(bodyName);

В следующих строках кода к элементу request-prices (представленному SOAPBodyElement


requestPrices.) добавляется элемент request. Затем добавляется текстовый узел,
содержащий текст запроса. После этого для сообщения вызывается метод saveChanges,
позволяющий сохранить все, что было сделано ранее, так как других элементов в запросе
нет.

Name requestName = envelope.createName("request");


SOAPElement request =
requestPrices.addChildElement(requestName);
request.addTextNode("Send updated price list.");

msg.saveChanges();

Когда создание сообщения запроса завершено, отправляется сообщение для JAXM


поставщика кофе. Отправленное сообщение является объектом msg объекта SOAPMessage,
к которому добавлены отрывки элементов, созданных в предыдущем коде. Конечной точкой
является URI для JAXM поставщика кофе. Объект con класса SOAPConnection используется
для отправки сообщения. Затем по причине ненадобности он закрывается.

URL endpoint = new URL(


"http://localhost:8080/jaxm-coffee-supplier/getPriceList");
SOAPMessage response = con.call(msg, endpoint);
con.close();

После выполнения метода call, Tomcat выполняет сервлет PriceListServlet. Данный сервлет
создает и возвращает объект SOAPMessage, чьим содержимым является прайс-лист JAXM
поставщика (PriceListServlet обсуждается в разделе Возвращение прайс-листа.) Tomcat
"знает" о том, что необходимо выполнить сервлет PriceListServlet, так как файл web.xml,
находящийся в директории <JWSDP>/docs/tutorial/examples/cb/jaxm/web/ отображает
заданную конечную точку для данного сервлета.

19.3.1.2. Извлечение прайс-листа


Данный раздел демонстрирует (1) извлечение прайс-листа, содержащегося в response,
объекте SOAPMessage возвращенном методом call, и (2) возвращение прайс-листа в виде
PriceListBean.

Web-

Rendered by www.RenderX.com
Служба JAXM поставщика Стр. 551 из 626

Программный код создает пустой объект Vector, который будет содержать элементы coffee-
name и price, извлеченные из response. Затем код использует response для доступа к его
объекту SOAPBody, который содержит содержимое сообщения. Обратите внимание на
то, что отдельный доступ к объекту SOAPEnvelope не производится, в силу того, что он
не является необходимым для создания объектов Name, как это было в предыдущем
разделе.

Vector list = new Vector();

SOAPBody responseBody = response.getSOAPPart().


getEnvelope().getBody();

Следующий шаг - получение объекта SOAPBodyElement. Метод getChildElements


возвращает объект Iterator, содержащий все вложенные элементы того элемента, который
он вызывает. Таким образом, в дальнейших строках кода, it1 содержит объект bodyEl
объекта SOAPBodyElement, представляющего элемент price-list.

Iterator it1 = responseBody.getChildElements();


while (it1.hasNext()) {
SOAPBodyElement bodyEl = (SOAPBodyElement)it1.next();

Объект it2 объекта Iterator содержит вложенные элементы объекта bodyEl, который
представляет элементы coffee. Вызов метода next к объекту it2 позволяет получить первый
элемент coffee в объекте bodyEl. До тех пор, пока объект it2 содержит другой элемент,
метод next будет возвращать следующий элемент coffee.

Iterator it2 = bodyEl.getChildElements();


while (it2.hasNext()) {
SOAPElement child2 = (SOAPElement)it2.next();

В последующих строках кода обрабатываются другие уровни для получения элементов


coffee-name и price, содержащихся в объекте it3. Затем собщение getValue извлекает текст
(название кофе или его цену), который JAXM поставщика кофе при выдаче содержимого
в response добавляет к элементам coffee-name и price. Конечная строка данного фрагмента
кода добавляет название кофе или его цену к объекту list объекта Vector. Обратите
внимание, что элементы во время прохождения цикла являются вложенными. Поэтому
для каждого элемента coffee, извлекаются также оба его вложенных элемента (элементы
coffee-nameи price).

Web-

Rendered by www.RenderX.com
Стр. 552 из 626 Приложение Coffee Break

Iterator it3 = child2.getChildElements();


while (it3.hasNext()) {
SOAPElement child3 = (SOAPElement)it3.next();
String value = child3.getValue();
list.addElement(value);
}
}
}

В последнем фрагменте кода к ArrayList priceItems добавляется название кофе и его цена
(в виде PriceListItem), после чего они попарно выводятся в отдельную строку. В заключение
он конструирует и возвращает PriceListBean.

ArrayList priceItems = new ArrayList();

for (int i = 0; i <list.size(); i = i + 2) {


priceItems.add(
new PriceItemBean(list.elementAt(i).toString(),
new BigDecimal(list.elementAt(i + 1).toString())));
System.out.print(list.elementAt(i) + " ");
System.out.println(list.elementAt(i + 1));
}

Date today = new Date();


Date endDate = DateHelper.addDays(today, 30);
PriceListBean plb =
new PriceListBean(today, endDate, priceItems);

19.3.1.3. Заказ кофе


Еще одним видом сообщений, которые сервер Coffee Break может отправлять JAXM
поставщика, является заказ на кофе. Это выполненяется в методе placeOrder OrderRequest,
который соответствует DTD coffee-order.dtd.

19.3.1.3.1. Создание заказа


Так же, как и в коде запроса прайс-листа, метод placeOrder начинается с создания объекта
SOAPConnection, затем создается объект SOAPMessage и производится доступ к объектам
сообщенияSOAPEnvelope и SOAPBody.

Web-

Rendered by www.RenderX.com
Служба JAXM поставщика Стр. 553 из 626

SOAPConnectionFactory scf =
SOAPConnectionFactory.newInstance();
SOAPConnection con = scf.createConnection();
MessageFactory mf = MessageFactory.newInstance();
SOAPMessage msg = mf.createMessage();

SOAPPart part = msg.getSOAPPart();


SOAPEnvelope envelope = part.getEnvelope();
SOAPBody body = envelope.getBody();

После этого в форму заказа создаются и добавляются XML-элементы. Первым элементом


является SOAPBodyElement, который в данном случает является coffee-order.

Name bodyName = envelope.createName("coffee-order", "PO",


"http://sonata.coffeebreak.com");
SOAPBodyElement order = body.addBodyElement(bodyName);

Затем приложение добавляет следующий уровень элементов, первым из которых будет


orderID. Значение, данное ordered, извлечено из объекта OrderBean, переданного методу
OrderRequest.placeOrder.

Name orderIDName = envelope.createName("orderID");


SOAPElement orderID = order.addChildElement(orderIDName);
orderID.addTextNode(orderBean.getId());

Следующий элемент customer имеет несколько вложенных элементов, которые


предоставляют информацию о покупателе. Данная информация также извлекается из
компонента Customer объекта OrderBean.

Name childName = envelope.createName("customer");


SOAPElement customer = order.addChildElement(childName);

childName = envelope.createName("last-name");
SOAPElement lastName = customer.addChildElement(childName);
lastName.addTextNode(orderBean.getCustomer().

Web-

Rendered by www.RenderX.com
Стр. 554 из 626 Приложение Coffee Break

getLastName());

childName = envelope.createName("first-name");
SOAPElement firstName = customer.addChildElement(childName);
firstName.addTextNode(orderBean.getCustomer().
getFirstName());

childName = envelope.createName("phone-number");
SOAPElement phoneNumber = customer.addChildElement(childName);

phoneNumber.addTextNode(orderBean.getCustomer().
getPhoneNumber());

childName = envelope.createName("email-address");
SOAPElement emailAddress =
customer.addChildElement(childName);
emailAddress.addTextNode(orderBean.getCustomer().
getEmailAddress());

Элемент address, добавляемый далее, содержит вложенные элементы street (улица), city
(город), state (штат) и zip code (почтовый индекс). Данная информация извлечена из
компонента Address, объекта OrderBean.

childName = envelope.createName("address");
SOAPElement address = order.addChildElement(childName);

childName = envelope.createName("street");
SOAPElement street = address.addChildElement(childName);
street.addTextNode(orderBean.getAddress().getStreet());

childName = envelope.createName("city");
SOAPElement city = address.addChildElement(childName);
city.addTextNode(orderBean.getAddress().getCity());

childName = envelope.createName("state");
SOAPElement state = address.addChildElement(childName);
state.addTextNode(orderBean.getAddress().getState());

childName = envelope.createName("zip");
SOAPElement zip = address.addChildElement(childName);

Web-

Rendered by www.RenderX.com
Служба JAXM поставщика Стр. 555 из 626

zip.addTextNode(orderBean.getAddress().getZip());

Элемент line-item содержит три вложенных элемента: coffeeName, poundsи price. Данная
информация извлекается из списка LineItems, содержащегося в объекте OrderBean.

for (Iterator it = orderBean.getLineItems().iterator();


it.hasNext(); ; ) {
LineItemBean lib = (LineItemBean)it.next();

childName = envelope.createName("line-item");
SOAPElement lineItem =
order.addChildElement(childName);

childName = envelope.createName("coffeeName");
SOAPElement coffeeName =
lineItem.addChildElement(childName);
coffeeName.addTextNode(lib.getCoffeeName());

childName = envelope.createName("pounds");
SOAPElement pounds =
lineItem.addChildElement(childName);
pounds.addTextNode(lib.getPounds().toString());

childName = envelope.createName("price");
SOAPElement price =
lineItem.addChildElement(childName);
price.addTextNode(lib.getPrice().toString());

// childName = envelope.createName("total");
SOAPElement total =
order.addChildElement(childName);
total.addTextNode(orderBean.getTotal().toString());
}

Когда заказ выполнен, приложение отправляет сообщение и закрывает соединение.

Web-

Rendered by www.RenderX.com
Стр. 556 из 626 Приложение Coffee Break

URL endpoint = new URL(


"http://localhost:8080/jaxm-coffee-supplier/orderCoffee");
SOAPMessage reply = con.call(msg, endpoint);
con.close();

Файл web.xml отображает заданную конечную точку на сервлет ConfirmationServlet, поэтому


Tomcat выполняет этот сервлет (как описано в разделе Возвращение пожтверждения
заказа) для создания и возвращения объекта reply объекта SOAPMessage.

19.3.1.3.2. Извлечение подтверждения заказа


Оставшаяся часть метода placeOrder извлекает информацию, возвращенную в reply. Клиент
знает, какие элементы в нем находятся, так как они заданы в confirm.dtd. После получения
доступа к объекту SOAPBody, код извлекает элемент confirmation и получает текст
элементов orderID и ship-date. В итоге он создает и возвращает компонент ConfirmationBean
с данной информацией.

SOAPBody sBody = reply.getSOAPPart().getEnvelope().getBody();


Iterator bodyIt = sBody.getChildElements();
SOAPBodyElement sbEl = (SOAPBodyElement)bodyIt.next();
Iterator bodyIt2 = sbEl.getChildElements();

SOAPElement ID = (SOAPElement)bodyIt2.next();
String id = ID.getValue();

SOAPElement sDate = (SOAPElement)bodyIt2.next();


String shippingDate = sDate.getValue();

SimpleDateFormat df = new
SimpleDateFormat("EEE MMM dd HH:mm:ss z yyyy");
Date date = df.parse(shippingDate);
ConfirmationBean cb = new ConfirmationBean(id, date);

19.3.2. Служба JAXM


JAXM поставщика кофе, в данном сценарии - сервер JAXM, обеспечивает ответную часть
парадигмы запрос-ответ. При использовании обмена сообщениями JAXM, кодом сервера
является сервлет. Ядро каждого такого сервлета построено из трех методов
javax.servlet.HttpServlet (init, doPost и onMessage). Методы init и doPost устанавливают
сообщение ответа, а метод onMessage предоставляет сообщению его содержимое.

Web-

Rendered by www.RenderX.com
Служба JAXM поставщика Стр. 557 из 626

19.3.2.1. Возвращение прайс-листа


Данный раздел описывает сервлет PriceListServlet. Этот сервлет создает сообщение с
текущим прайс-листом, которое возвращается к методу call, вызванному в PriceListRequest.
Любой сервлет расширяет класс javax.servlet. Будучи частью Web-приложения, этот сервлет
расширяет HttpServlet. Вначале он создает статический объект MessageFactory, который
будет использоваться позднее для создания возвращаемого им объекта SOAPMessage.
Затем он объявляет объект msgFactory объекта MessageFactory, который будет
использоваться для создания объекта SOAPMessage, имеющего заголовки и содержимое
оригинального сообщения запроса.

public class PriceListServlet extends HttpServlet {


static MessageFactory fac = null;
static {
try {
fac = MessageFactory.newInstance();
} catch (Exception ex) {
ex.printStackTrace();
}
};

MessageFactory msgFactory;

Каждый сервлет имеет метод init. Данный метод init инициализирует сервлет с информацией
о конфигурации, которую передает для него Tomcat. Затем он просто инициализирует
объект msgFactory при помощи реализации (используемой по-умолчанию) класса
MessageFactory.

public void init(ServletConfig servletConfig)


throws ServletException {
super.init(servletConfig);
try {
// -
msgFactory = MessageFactory.newInstance();
} catch (SOAPException ex) {
throw new ServletException(
"Unable to create message factory" + ex.getMessage());
}
}

Web-

Rendered by www.RenderX.com
Стр. 558 из 626 Приложение Coffee Break

Следующий метод, определенный в сервлете PriceListServlet - метод doPost, который и


осуществляет реальную работу сервлета путем вызова метода onMessage (метод
onMessage обсуждается в следующем разделе). Tomcat передает методу doPost два
аргумента. Первый из них - объект req интерфейса HttpServletRequest, хранящий
содержимое сообщения, отправленного в PriceListRequest. Метод doPost получает
содержимое из объекта req и помещает его в объект msg объекта SOAPMessage так, что
теперь он может передать его методу onMessage. Второй аргумент, объект resp интерфейса
HttpServletResponse, будет содержать сообщение, созданное путем выполнения метода
onMessage.
В дальнейшем фрагменте кода, для чтения и записи заголовков в объекте req, метод
doPost вызывает методы getHeaders и putHeaders, определенные сразу после doPost.
Затем он получает содержимое объекта req в виде потока и передает заголовки и входящий
поток методу MessageFactory.createMessage. В результате, объект msg объекта
SOAPMessage содержит запрос прайс-листа. Обратите внимание, что в данном случае
объект msg не имеет никаких заголовков, так как сообщение, отправленное в PriceListRe-
quest также не имело никаких заголовков.

public void doPost( HttpServletRequest req, HttpServletResponse


resp) throws ServletException, IOException {
try {
//
HTTP- .
MimeHeaders headers = getHeaders(req);

// HTTP- .
InputStream is = req.getInputStream();

// HTTP-
// SOAPMessage
SOAPMessage msg = msgFactory.createMessage(headers, is);

Далее в коде объявляется объект reply объекта SOAPMessage и заполняет его путем
вызова метода onMessage.

SOAPMessage reply = null;


reply = onMessage(msg);

Если в reply что-то содержится, его содержимое сохраняется, значение статуса resp
устанавливается как OK, а заголовки и содержимое reply записываются в resp. Если reply
пуст, статус resp будет указывать на отсутствие содержимого.

Web-

Rendered by www.RenderX.com
Служба JAXM поставщика Стр. 559 из 626

if (reply != null) {
// saveChanges, . .

// MimeHeaders
HTTP- .
// MimeHeaders
.

if (reply.saveRequired()) {
reply.saveChanges();
}

resp.setStatus(HttpServletResponse.SC_OK);

putHeaders(reply.getMimeHeaders(), resp);
// .
OutputStream os = resp.getOutputStream();
reply.writeTo(os);
os.flush();
} else
resp.setStatus(HttpServletResponse.SC_NO_CONTENT);

} catch (Exception ex) {


throw new ServletException( "JAXM POST failed " +
ex.getMessage());
}
}

Методы getHeaders и putHeaders являются нестандартными для сервлете, в отличие от


методов init, doPost и onMessage. Метод doPost вызывает getHeaders и передает его
объекту req интерфейса HttpServletRequest, который передал ему Tomcat. Он возвращает
объект MimeHeaders, заполненный заголовками из объекта req.

static MimeHeaders getHeaders(HttpServletRequest req) {

Enumeration enum = req.getHeaderNames();


MimeHeaders headers = new MimeHeaders();

Web-

Rendered by www.RenderX.com
Стр. 560 из 626 Приложение Coffee Break

while (enum.hasMoreElements()) {
String headerName = (String)enum.nextElement();
String headerValue = req.getHeader(headerName);

StringTokenizer values = new StringTokenizer(


headerValue, ",");
while (values.hasMoreTokens()) {
headers.addHeader(headerName,
values.nextToken().trim());
}
}

return headers;
}

Метод doPost вызывает putHeaders и передает его объекту headers объектаMimeHeaders,


который был возвращен методом getHeaders. Метод putHeaders записывает заголовки из
объекта headers в объект res, которому передается второй аргумент. Результатом будет
то, что объект res (ответ, который Tomcat возвратит методу call) будет содержать заголовки,
находящиеся в оригинальном запросе.

static void putHeaders(MimeHeaders headers,


HttpServletResponse res) {
Iterator it = headers.getAllHeaders();
while (it.hasNext()) {
String[] values = headers.getHeader(header.getName());
if (values.length == 1)
res.setHeader(header.getName(),
header.getValue());
else {
StringBuffer concat = new StringBuffer();
int i = 0;
while (i <values.length) {
if (i != 0) concat.append(',');
concat.append(values[i++]);
}
res.setHeader(header.getName(), concat.toString());
}
}

Web-

Rendered by www.RenderX.com
Служба JAXM поставщика Стр. 561 из 626

Метод onMessage является кодом приложения для ответов на сообщение, отправленное


PriceListRequest и встроенное в объект msg. Он использует статический объект fac объекта
MessageFactory для создания объекта message объекта SOAPMessage, а затем заполняет
его текущими ценами на кофе, полученными от поставщика.
Метод doPost вызывает onMessage и передает его объекту msg. В данном случае,
onMessage не нужно использовать объект msg, так как он просто создает сообщение,
содержащее прайс-лист поставщика. Метод onMessage в сервлете ConfirmationServlet(см.
Возвращение подтверждения заказа), с другой стороны, использует сообщение,
передаваемое ему, для получения ID заказа.

public SOAPMessage onMessage(SOAPMessage msg) {


SOAPMessage message = null;
try {
message = fac.createMessage();

SOAPPart part = message.getSOAPPart();


SOAPEnvelope envelope = part.getEnvelope();
SOAPBody body = envelope.getBody();

Name bodyName = envelope.createName("price-list",


"PriceList", "http://sonata.coffeebreak.com");
SOAPBodyElement list = body.addBodyElement(bodyName);

coffee Name coffeeN = envelope.createName("coffee");


SOAPElement coffee = list.addChildElement(coffeeN);

Name coffeeNm1 = envelope.createName("coffee-name");


SOAPElement coffeeName =
coffee.addChildElement(coffeeNm1);
coffeeName.addTextNode("Arabica");

Name priceName1 = envelope.createName("price");


SOAPElement price1 = coffee.addChildElement(priceName1);
price1.addTextNode("4.50");

Name coffeeNm2 = envelope.createName("coffee-name");


SOAPElement coffeeName2 =
coffee.addChildElement(coffeeNm2);
coffeeName2.addTextNode("Espresso");

Web-

Rendered by www.RenderX.com
Стр. 562 из 626 Приложение Coffee Break

Name priceName2 = envelope.createName("price");


SOAPElement price2 = coffee.addChildElement(priceName2);
price2.addTextNode("5.00");

Name coffeeNm3 = envelope.createName("coffee-name");


SOAPElement coffeeName3 =
coffee.addChildElement(coffeeNm3);
coffeeName3.addTextNode("Dorada");

Name priceName3 = envelope.createName("price");


SOAPElement price3 = coffee.addChildElement(priceName3);
price3.addTextNode("6.00");

Name coffeeNm4 = envelope.createName("coffee-name");


SOAPElement coffeeName4 =
coffee.addChildElement(coffeeNm4);
coffeeName4.addTextNode("House Blend");

Name priceName4 = envelope.createName("price");


SOAPElement price4 = coffee.addChildElement(priceName4);
price4.addTextNode("5.00");

message.saveChanges();

} catch(Exception e) {
e.printStackTrace();
}
return message;
}
}

19.3.2.2. Возвращение подтверждения заказа


Сервлет ConfirmationServlet создает сообщение подтверждения, которое возвращается
методу call, вызываемому в OrderRequest. Это очень похоже на код сервлета PriceListServlet,
за исключением того, что вместо создания прайс-листа, его метод onMessage создает
подтверждение с номером заказа и датой поставки.
Метод onMessage данного сервлета использует объект SOAPMessage, передаваемый ему
методом doPost для получения номера заказа, отправленного в OrderRequest. Затем он
создает сообщение подтверждения с ID заказа и датой поставки. Дата поставки вычисляется
как сегодняшний день, плюс два дня.

Web-

Rendered by www.RenderX.com
Служба JAXM поставщика Стр. 563 из 626

public SOAPMessage onMessage(SOAPMessage message) {

SOAPMessage confirmation = null;

try {

// orderID

SOAPBody sentSB = message.getSOAPPart().


getEnvelope().getBody();
Iterator sentIt = sentSB.getChildElements();
SOAPBodyElement sentSBE =
(SOAPBodyElement)sentIt.next();
Iterator sentIt2 = sentSBE.getChildElements();
SOAPElement sentSE = (SOAPElement)sentIt2.next();

// orderID

String sentID = sentSE.getValue();

//
confirmation = fac.createMessage();
SOAPPart sp = confirmation.getSOAPPart();
SOAPEnvelope env = sp.getEnvelope();
SOAPBody sb = env.getBody();
Name newBodyName = env.createName("confirmation",
"Confirm", "http://sonata.coffeebreak.com");
SOAPBodyElement confirm =
sb.addBodyElement(newBodyName);

// orderID

Name newOrderIDName = env.createName("orderId");


SOAPElement newOrderNo =
confirm.addChildElement(newOrderIDName);
newOrderNo.addTextNode(sentID);

// ship-date Name shipDateName

Web-

Rendered by www.RenderX.com
Стр. 564 из 626 Приложение Coffee Break

= env.createName("ship-date");
SOAPElement shipDate =
confirm.addChildElement(shipDateName);

// Date today = new


Date();
long msPerDay = 1000 * 60 * 60 * 24;
long msTarget = today.getTime();
long msSum = msTarget + (msPerDay * 2);
Date result = new Date();
result.setTime(msSum);
String sd = result.toString();
shipDate.addTextNode(sd);

confirmation.saveChanges();

} catch (Exception ex) {


ex.printStackTrace();
}
return confirmation;
}

19.4. Сервер Coffee Break


Сервер Coffee Break использует сервлеты, JSP-страницы и JavaBean-компоненты,
позволяющие динамически создавать HTML-страницы, отображаемые Web-броузером
клиента. Для минимизации использования сценариев при создании JSP-страниц
применяется библиотека тегов шаблона (см. раздел Библиотека тегов шаблона), а также
множество заказных тегов библиотеки JSTL, описываемых в Главе 16. При этом достигается
общее восприятие внешнего вида HTML-страниц.
Реализация сервера Coffee Break организована по принципу шаблона проектирования
Model-View-Controller. Сервлет Dispatcher является контроллером. Он изучает URL запроса,
создает и инициализирует модель JavaBean-компонентов и направляет запросы для
просмотра JSP-страниц. JavaBean-компоненты содержат бизнес-логику для приложения
- они вызывают Web-службы и выполняют вычисления данных, возвращенных от служб.
JSP-страницы форматируют данные, содержащиеся в JavaBean-компонентах. Связи между
JavaBean-компонентами и страницами обобщены в Таблице18-1.
Таблица 18-1 Компоненты Model и View
Функция JSP-страница JavaBean-компонент
Обновление данных заказа orderForm ShoppingCart
Обновление данных о доставке и оплате checkoutForm CheckoutFormBean
Отображение подтверждения заказа checkoutAck OrderConfirmations

Web-

Rendered by www.RenderX.com
Сервер Coffee Break Стр. 565 из 626

19.4.1. JSP-страницы
19.4.1.1. orderForm
Форма orderForm отображает текущее содержимое корзины покупателя. При первом
запросе страницы, количество всего кофе в заказе равняется нулю. Каждый раз, когда
покупатель заносит кофе в корзину и нажимает кнопку Update, запрос заносится в orderForm.
Сервлет Dispatcher обновляет значения в корзине покупателя, которые затем отображаются
заново в orderForm. Когда процесс заказа завершен, покупатель направляется на страницу
checkoutForm путем открытия ссылки Checkout.

19.4.1.2. checkoutForm
Форма checkoutForm используется для сбора информации для покупателя о доставке и
об оплате. После нажатия кнопки Submit запрос заносится в страницу checkoutAck. Однако
он вначале обрабатывается при помощи Dispatcher, который вызывает метод validate
компонента checkoutFormBean. Если проверка не прошла успешно, возвращается страница
checkoutForm с предупреждениями об ошибке в каждом поле, где она была допущена.
Если проверка подтверждена, checkoutFormBean подтверждает подзаказы для каждого
поставщика и сохраняет результат в JavaBean-компоненте OrderConfirmations области
действия запроса, а управление передается странице checkoutAck.

19.4.1.3. checkoutAck
Форма checkoutAck попросту отображает содержимое JavaBean-компонента OrderConfir-
mations, который является списком подзаказов, содержащихся в заказе, и дат поставки
для каждого подзаказа.

19.4.2. JavaBean-компоненты
19.4.2.1. RetailPriceList
RetailPriceList - список позиций прайс-листа. Позиция прайс-листа содержит название кофе,
оптовую цену за фунт, розничную цену за фунт и название поставщика. Эти данные
используются в двух целях: они содержат прайс-лист, представленный конечному
пользователю, а также используются компонентом CheckoutFormBean, когда он создает
подзаказы, передающиеся поставщикам кофе.
Вначале для определения конечных точек службы JAX-RPC выполняется поиск JAXR.
Затем для получения прайс-листа на кофе запрашивается каждая служба JAX-RPC. И
наконец, для той же цели запрашивается служба JAXM. Оба прайс-листа комбинируются,
и розничная цена за фунт определяется путем добавления наценки в 35% от оптовой
цены.

19.4.2.1.1. Обнаружение службы JAX-RPC


Созданный при помощи RetailPriceList, экземпляр JAXRQueryByName устанавливает
соединение с сервером реестра и производит поиск поставщиков кофе, зарегистрированных
с именем JAXRPCCoffeeDistributor в методе executeQuery. Метод возвращает коллекцию
организаций, которые содержат надлежащие службы.
Каждая служба доступна посредством ее привязки или URI. RetailPriceList выполняет
запросы JAX-RPC к каждому URI.

Web-

Rendered by www.RenderX.com
Стр. 566 из 626 Приложение Coffee Break

19.4.2.2. ShoppingCartItem
ShoppingCart является списком позиций в корзине покупателя. В каждой позиции содержится
розничная цена товара, количество фунтов товара и итоговая стоимость.

19.4.2.3. OrderConfirmation
OrderConfirmations является списком объектов подтверждения заказа. Подтверждение
заказа содержит объекты заказа и его подтверждение, как уже обсуждалось в разделе
Интерфейс службы.

19.4.2.4. CheckoutFormBean
CheckoutFormBean проверяет завершенность информации, введенной в checkoutForm.
Если информация неполная, компонент порождает сообщения об ошибке, а Dispatcher
заново отображает страницу checkoutForm с соответствующими сообщениями об ошибках.
Если информация полная, из корзины покупателя конструируется запросы заказа, а
информация предоставляемая checkoutForm отправляется каждому поставщику. При
получении каждого подтверждения, создается подтверждение заказа, которое затем
добавляется к OrderConfirmations.

if (allOk) {
String orderId = CCNumber;

AddressBean address = new AddressBean(street, city,


state, zip);
CustomerBean customer = new CustomerBean(firstName, lastName,
"(" + areaCode+ ") " + phoneNumber, email);

for(Iterator d = rpl.getDistributors().iterator();
d.hasNext(); ) {
String distributor = (String)d.next();
System.out.println(distributor);
ArrayList lis = new ArrayList();
BigDecimal price = new BigDecimal("0.00");
BigDecimal total = new BigDecimal("0.00");
for(Iterator c = cart.getItems().iterator();
c.hasNext(); ) {
ShoppingCartItem sci = (ShoppingCartItem) c.next();
if ((sci.getItem().getDistributor()).
equals(distributor) &&
sci.getPounds().floatValue()> 0) {
price = sci.getItem().
getWholesalePricePerPound().
multiply(sci.getPounds());

Web-

Rendered by www.RenderX.com
Построение, установка и исполнение приложения Стр. 567 из 626

total = total.add(price);
LineItemBean li = new LineItemBean(
sci.getItem().getCoffeeName(), sci.getPounds(),
sci.getItem().getWholesalePricePerPound());
lis.add(li);
}
}

if (!lis.isEmpty()) {
OrderBean order = new OrderBean(orderId,
customer, lis, total, address);

String JAXMOrderURL =
"http://localhost:8080/
jaxm-coffee-supplier/orderCoffee";

if (distributor.equals(JAXMOrderURL)) {
OrderRequest or = new OrderRequest(JAXMOrderURL);
confirmation = or.placeOrder(order);
} else {
OrderCaller ocaller = new OrderCaller(distributor);
confirmation = ocaller.placeOrder(order);
}
OrderConfirmation oc = new OrderConfirmation(order,
confirmation);
ocs.add(oc);
}
}
}

19.4.3. RetailPriceListServlet
RetailPriceListServlet отвечает на запросы для перезагрузки прайс-листа посредством URL
/loadPriceList. Он просто создает новый RetailPriceList и новый ShoppingCart.
Пока сервлет используется администраторами сервера Coffee Break, он является
защищенным Web-ресурсом. Для загрузки прайс-листа, пользователь должен пройти
аутентификацию (используя базовую аутентификацию) и быть в роли admin.

19.5. Построение, установка и исполнение приложения


Исходный код приложения Coffee Break находится в директории
<JWSDP_HOME>/docs/tutorial/examples/cb. В директории cb находятся поддиректории для
каждого Web-приложения (jaxm, jaxrpc, server), а также директория common, для классов

Web-

Rendered by www.RenderX.com
Стр. 568 из 626 Приложение Coffee Break

используемых Web-приложениями совместно. Каждая поддиректория содержит файлы


build.xml и build.properties. Поддиректории Web-приложений, в свою очередь, содержат
поддиректорию src для Java-классов и поддиректорию web для Web-ресурсов, а также
дескриптор размещения Web-приложения.
Примечание: Web-приложения установлены в Tomcat при помощи задачи ant install. Перед
использованием задачи install необходимо в вашей домашней директории создать файл
с именем build.properties, который будет содержать имя пользователя и пароль,
определенные вами при инсталляции Java WSDP. См. Выполнение команд менеджера с
использованием задач Ant.

19.5.1. Построение общих классов


Для построения общих классов:
1. В окне терминала, перейдите в директорию <JWSDP_HOME>/docs/tutorial/exam-
ples/cb/common.
2. Запустите ant build.

19.5.2. Построение и установка службы JAX-RPC


Для построения службы JAX-RPC с библиотекой клиента и ее установки выполните
следующие действия:
1. В окне терминала перейдите в директорию <JWSDP_HOME>/docs/tutorial/exam-
ples/cb/jaxrpc.
2. Запустите ant build. Данная задача генерирует связи и заглушки JAX-RPC, создает
JAXR и библиотеки клиента, компилирует классы сервера и копирует их в необходимое
место для инсталляции.
3. Запустите Tomcat и Xindice, если они не загружены. При этом запустится сервер реестра.
4. Запустите set-up-service. Данная задача инсталлирует службу JAX-RPC в Tomcat и
регистрирует ее в сервере реестра. Процесс регистрации может занять некоторое
время, поэтому перед переходом к следующему шагу дождитесь момента, пока вы не
увидите следующие строки:

run-jaxr-publish:
[echo] Running OrgPublisher.
[echo] Note: Remember to start the registry server before running
this program.
[java] Created connection to registry
[java] Got registry service, query manager, and life cycle
manager
[java] Established security credentials
[java] Organization saved

Web-

Rendered by www.RenderX.com
Построение, установка и исполнение приложения Стр. 569 из 626

[java] Organization key is edeed14d-5eed-eed1-31c2-aa789a472fe0

5. Можно проверить корректность установки службы JAX-RPC, запустив одну из или обе
тестовые программы: выполните ant run-test-price или ant run-test-order. При запуске
ant run-test-price вы должны увидеть следующее:

run-test-price:
run-test-client:
[java] 05/21/02 06/20/02
[java] Kona 6.50
[java] French Roast 5.00
[java] Wake Up Call 5.50
[java] Mocca 4.00

Позднее вы сможете удалить службу JAX-RPC путем выполнения ant take-down-service.


Данная команда удаляет службу из сервера реестра, после чего деинсталлирует ее из
Tomcat. Не удаляйте службу в этот раз.

19.5.3. Построение и установка службы JAXM


Для построения службы JAXM с библиотекой клиента и ее установки выполните следующие
действия:
1. В окне терминала перейдите в директорию <JWSDP_HOME>/docs/tutorial/exam-
ples/cb/jaxm
2. Запустите ant build. Данная задача создает библиотеку клиента, компилирует классы
сервера и копирует их в необходимое место для установки.
3. Убедитесь, что Tomcat запущен.
4. Запустите ant install. Данная задача инсталлирует службу JAXM в Tomcat.
5. Можно проверить корректность установки службы JAXM, запустив одну из или обе
тестовые программы: выполните ant run-test-price или ant run-test-order.

19.5.4. Построение и установка сервера Coffee Break


Для построения и установки сервера Coffee Break выполните следующие действия:
1. В окне терминала перейдите в директорию <JWSDP_HOME>/docs/tutorial/exam-
ples/cb/server
2. Запустите ant build. Данная задача компилирует классы сервера и копирует классы,
JSP-страницы, библиотеки клиента, а также библиотеки тегов в необходимое место
для инсталляции. Обратите внимание, что сервер Coffee Break зависит от библиотек
клиента, созданных процессами построения JAX-RPC (jaxrpc-client.jar) и JAXM (jaxm-
client.jar).

Web-

Rendered by www.RenderX.com
Стр. 570 из 626 Приложение Coffee Break

3. Убедитесь, что Tomcat запущен.


4. Запустите ant install.

19.5.5. Запуск клиента Coffee Break


После инсталляции всех Web-приложений убедитесь, что все приложения запущены.
Запустите ant list в окне терминала или откройте http://localhost:8080/manager/list. В окне
броузера отобразится примерно следующее:

OK - Listed applications for virtual host localhost


/manager:running:0:../server/webapps/manager
/jaxm-translator:running:0:D:\jwsdp-1_0\webapps\jaxm-translator.war
/jaxm-coffee-supplier:running:0:D:/jwsdp-1_0/docs/tuto-rial/examples/cb/jaxm/build
/jaxm-soaprp:running:0:D:\jwsdp-1_0\webapps\jaxm-soaprp.war
/saaj-simple:running:0:D:\jwsdp-1_0\webapps\saaj-simple.war
/jaxm-remote:running:0:D:\jwsdp-1_0\webapps\jaxm-remote.war
/jstl-examples:running:0:D:\jwsdp-1_0\webapps\jstl-examples.war
/registry-server:running:0:D:\jwsdp-1_0\webapps\registry-server.war
/jaxmtags:running:0:D:\jwsdp-1_0\webapps\jaxmtags.war
/jaxm-simple:running:0:D:\jwsdp-1_0\webapps\jaxm-simple.war
/jaxrpc-coffee-supplier:running:0:D:/jwsdp-1_0/docs/tuto-rial/examples/cb/jaxrpc/build
/cbserver:running:1:D:/jwsdp-1_0/docs/tuto-rial/examples/cb/server/build
/:running:0:D:\jwsdp-1_0\webapps\ROOT
/admin:running:0:../server/webapps/admin

Приложения, выделенные жирным начертанием: сервер Coffee Break и службы JAX-RPC


и JAXM.
Затем, для запуска клиента Coffee Break откройте сервер Coffee Break, установив для
этого в Web-броузере следующий URL-адрес:

http://localhost:8080/cbserver/orderForm

Вы должны увидеть страницу, показанную на Рисунке 18-2.

Web-

Rendered by www.RenderX.com
Построение, установка и исполнение приложения Стр. 571 из 626

Рисунок 18-2 Форма заказа

После прохождения всех этапов приложения, вы получите подтверждение заказа, как


показано на Рисунке 18-3.

Web-

Rendered by www.RenderX.com
Стр. 572 из 626 Утилита администрирования Tomcat

Рисунок 18-3 Подтверждение заказа

19.5.6. Размещение приложения Coffee Break


Инструкции, представленные в предыдущем разделе, описывают процесс установки и
запуска приложения Coffee Break. Однако после перезагрузки Tomcat установленное
приложение будет недоступно. Для постоянного размещения приложения выполните
следующие действия:
1. Удалите службы JAXRPC и JAXM, а также сервер Coffee Break путем выполнения
задачи ant remove в каждой директории Web-приложения.
2. Запакуйте приложения в WAR-файлы, используя задачу ant package в каждой
директории Web-приложения.
3. Разместите приложение, выполнив задачу ant deploy в каждой директории Web-
приложения.

20. Утилита администрирования Tomcat


Дэбби Карсон
Данное приложение содержит информацию об утилите администрирования Web-сервера
Tomcat (Tomcat Web Server Administration Tool). Для простоты использования в данном
документе она носит название admintool.
Утилита admintool используется для настройки поведения Java Servlet/JSP контейнера
сервера Tomcat в период его работы. Изменения, сделанные в Tomcat при использовании

Web-

Rendered by www.RenderX.com
Запуск admintool Стр. 573 из 626

admintool могут быть сохранены независимо, то есть изменения остаются при перезапуске
Tomcat или они могут быть сохранены только для текущей сессии.

20.1. Запуск admintool


Web-приложение admintool может быть использовано для манипуляции с Tomcat во время
его работы. К примеру, вы можете добавить контекст или установить пользователей и
роли для безопасности, управляемой контейнером.
Для того чтобы запустить admintool выполните следующие действия:
1. Запустите Tomcat, вызвав его сценарий запуска из коммандной строки следующим
образом:

<JWSDP_HOME>/bin/startup.sh (
Unix- )
<JWSDP_HOME>\bin\startup ( Microsoft Windows)

2. Запустите Web-броузер.
3. В Web-броузере откройте следующий URL:

http://localhost:8080/admin

Данная команда вызывает Web-приложение с контекстом admin.


4. Войдите в admintool, используя комбинацию имени и пароля, определенную вами при
инсталляции Java WSDP.
Данная комбинация имени и пароля назначена по-умолчанию ролями admin, manager,
и provider. Для того чтобы использовать admintool, вы должны войти в него, используя
комбинацию имя/пароль, которая была назначена для роли admin. Данные имя
пользователя и пароль должны совпадать с именем/паролем, содержащимися в файле
build.properties (для получения более подробной информации о файле build.properties
обратитесь к разделу Создание файла Build Properties).
Если вы забыли имя пользователя и пароль, их можно найти в файле
<JWSDP_HOME>/conf/tomcat-users.xml, который доступен для просмотра с помощью
любого текстового редактора. Данный файл содержит элемент <user> для каждого
индивидуального пользователя, он должен выглядеть следующим образом:

<user name="your_name" password="your_password"


roles="admin, manager,provider" />

Web-

Rendered by www.RenderX.com
Стр. 574 из 626 Утилита администрирования Tomcat

В окне Web-броузера отобразится Web-приложение admintool:

Рисунок A-1 Утилита администрирования сервера Tomcat

5. Выполните действия по администрированию.


После того, как вы произвели изменения в Tomcat, для сохранения аттрибутов текущего
процесса Tomcat выберите кнопку Save на данной странице. Выберите кнопку Commit
Changes для записи изменений в файл <JWSDP_HOME>/conf/server.xml, теперь эти
изменения в Tomcat являются постоянными и останутся после перезагрузки Tomcat.
Предыдущая версия файла конфигурации server.xml находится в той же директории с
расширением, указывающим на дату его создания, к примеру, server.xml.2002-06-15.12-
11-54. Для восстановления предыдущей конфигурации закройте Tomcat, переименуйте
нужный файл в server.xml и перегрузите Tomcat.
6. После завершения работы выйдите из admintool, выбрав Log Out.
7. Закройте Tomcat, вызвав из коммандной строки сценарий закрытия следующим образом:

<JWSDP_HOME>/bin/shutdown.sh ( Unix- )
<JWSDP_HOME>\bin\shutdown ( Microsoft Windows)

Данный документ содержит информацию об использовании admintool для настройки


поведения Tomcat. Для получения более детальной информации об элементах настройки,
обратитесь к документу Tomcat Configuration Reference, который находится в файле
<JWSDP_HOME>/docs/tomcat/config/index.html.

Web-

Rendered by www.RenderX.com
Настройка Tomcat Стр. 575 из 626

В этом документе нет описания того, какие конфигурации должны быть использованы для
выполнения специфических задач. Для получения информации подобного рода обратитесь
к одному из документов, указанных ниже:
• Class Loader How-To. В данном документе обсуждаются решения, которые должны
принимать разработчики и разместители приложений, определяя, где помещать классы
и исходные файлы, чтобы сделать их доступными для Web-приложений. Документ
находится в файле: <JWSDP_HOME>/docs/tomcat/class-loader-howto.html.
• Здесь обсуждается настройка JNDI-ресурсов, Tomcat Standard Resource Factories, JDBC
Data Sources, и Custom Resource Factories. Документ находится в файле:
<JWSDP_HOME>/docs/tomcat/jndi-resources-howto.html.
• Manager Application How-To. Этот документ описывает использование менеджера
приложений для размещения нового Web-приложения, сворачивания существующего
приложения или перезагрузки существующего приложения без закрытия и перезагрузки
Tomcat. Документ находится здесь: <JWSDP_HOME>/docs/tomcat/manager-howto.html.
• Proxy Support How-To. В документе обсуждается запуск вне прокси сервера (или если
web-сервер настроен так, что ведет себя как прокси-сервер). Частным образом в
документе описывается, как управлять значениями, возвращенными вызовами из Web-
приложений, запрашиваюих имя сервера и номер порта, в который был направлен
запрос. Вы можете найти этот документ в файле<JWSDP_HOME>/docs/tomcat/proxy-
howto.html.
• Realm Configuration How-To. В данном документе обсуждается то, как настроить Tomcat
для поддержки безопасности, управляемой контейнером, путем соединения с
существующей базой данных имен пользователей, паролей и ролей пользователей.
Он находится в файле <JWSDP_HOME>/docs/tomcat/realm-howto.html.
• Security Manager How-To. В документе обсуждается использование SecurityManager при
работе с Tomcat для защиты вашего сервера от неавторизированных сервлетов, JSP-
страниц, JSP-компонентов и библиотек тэгов. Вы можете найти его в файле:
<JWSDP_HOME>/docs/tomcat/security-manager-howto.html.
• SSL Configuration How-To. В нем обсуждается вопрос установки и настройки поддержки
протокола SSL для Tomcat. Настройка поддержки SSL для Tomcat с использованием
Java WSDP обсуждается в разделе Установка и настройка поддержки SSL для Tomcat.
Документация Tomcat, которая находится в файле <JWSDP_HOME>/docs/tomcat/ssl-
howto.html, также обсуждает эту тему, однако, информация в данном руководстве
является более свежей по сравнению с версией Tomcat, поставляемой с Java WSDP.

20.2. Настройка Tomcat


Как видно из рисунка A-1, утилита admintool отображает иерархию элементов, которые
могут быть настроены для изменения контейнера JSP/Servlet сервера Tomcat
соответственно вашим потребностям. Элемент Server отображает характеристики всего
контейнера JSP/Servlet.

Web-

Rendered by www.RenderX.com
Стр. 576 из 626 Утилита администрирования Tomcat

20.2.1. Настройка свойств сервера


В левом окне выберите Tomcat Server. В правом окне отобразятся свойства сервера (Server
Properties). Элемент Server отображает содержимое всего контейнера JSP/Servlet. Свойства
сервера показаны в Таблице A-2.
Таблица A-2 Свойства сервера
Свойство Описание
Port Number Номер порта TCP/IP, в котором данный сервер ожидает
команду закрытия (shutdown). Данное соединение должно
быть произведено с того же компьютера, на котором запущен
Tomcat. Значением по-умолчанию является 8005. Значения,
меньшие чем 1024, приводят к появлению сигнала
предупреждения, так как для использования данного порта
необходимы специальные программные возможности.
Debug Level Уровень детальности отладки событий, записывающихся в
журнал данного сервера. Большие номера означают вывод
более детальной информации. Если не определено, по-
умолчанию значение равно нулю (0).
Shutdown Коммандная строка, которая должна быть получена
посредством соединения TCP/IP в заданный порт с целью
завершения работы сервера Tomcat. Значение для этого
свойства должно содержать как минимум 6 символов. По-
умолчанию таким значением является SHUTDOWN.

20.3. Настройка элементов Service


Элементы Service вложены в элемент Server. Элемент Service представляет собой
комбинацию из одного или более компонентов Connector, которые для обработки входящих
запросов используют совместно одиночный компонент устройства. В конфигурации,
определенной по-умолчанию, Tomcat содержит две службы: Internal Service и Java Web
Services Developer Pack Service.
• Служба Internal Service использует порт 8081. Данная служба используется внутренним
образом Web-приложениями Tomcat, такими, как контексты JAXM provider и JAXM
provideradmin. Данные контексты используются контекстами web-приложений JAXM в
службе JWSDP.
• Служба Java Web Services Developer Pack Service использует порт 8080, стандартный
порт, посредством которого пользователи могут размещать свои Web-приложения.
Данная служба используется разработчиками Java Servlet и JSP-страниц.
Существует возможность использования admintool для добавления других служб,
использующих другой порт. Для создания новой службы,
1. В левом окне выберите Tomcat Server.
2. Из ниспадающего списка в правом окне выберите Create New Service.
3. Введите значения Service Name, Engine Name, Debug Level и Default Hostname.
Service Name - отображаемое имя данной службы, которое будет включено в сообщения
журнала, если вы выберите элемент Logger (см. Настройка элементов Logger).
Примечание: Имя каждой службы, ассоциируемое с отдельным сервером, должно быть
уникальным.

Web-

Rendered by www.RenderX.com
Настройка элементов Service Стр. 577 из 626

Для каждого определенного элемента Service можно создать или удалить следующие
элементы:
• Элементы Connector, символизирующие интерфейс между службой и внешними
клиентами, которые отправляют запросы и получают от нее ответы. Для получения
более детальной информации обратитесь к разделу Настройка элементов Connector.
• Элементы Host, символизирующие виртуальный хост, который ассоциируется с сетевым
именем сервера (к примеру, www.mycompany.com), являющимся частью сервера, на
котором запущен Tomcat. Для получения более детальной информации обратитесь к
разделу Настройка элементов Host.
• Элементы Logger, символизирующие адресата для записи журнала, отладки и сообщений
об ошибках (включая содержимое стека) для Tomcat (Engine, Host или Context). Для
получения детальной информации обратитесь к разделу Настройка элементов Logger.
• Элементы Realm, символизирующие базу данных имен пользователей, паролей и ролей,
назначенных этим пользователям. Подробнее читайте об этом в разделе Настройка
элементов Realm.
• Элементы Valve, символизирующие компонент, который будет включен в поток обработки
запроса для ассоциированного с ним контейнера (Engine, Host, или Context). Более
подробная информация находится в документе Настройка элементов Valve.

20.3.1. Настройка элементов Connector


Элементы Connector отображают интерфейс между внешними клиентами, отправляющими
запрос отдельной службе и получающими от нее ответы.
Для того чтобы отредактировать элемент Connector,
1. Раскройте элемент Service в левом окне.
2. Выберите Connector, который вы хотите отредактировать.
3. Отредактируйте значения в правом окне.
4. Выберите Save для сохранения изменений в данной сессии. Выберите Commit Changes
для сохранения изменений при перезагрузке Tomcat.
Для того чтобы создать новый коннектор для службы,
1. В левом окне выберите элемент Service. Настоятельно рекомендуется модифицировать
только службу Java Web Services Developer Pack Service, или службу, которую вы
создали.
2. Из списка Available Actions выберите Create New Connector.
3. Введите необходимые значения для элемента Connector. Для получения более
подробной информации обратитесь к разделу Атрибуты элемента Connector.
4. Выберите Save для сохранения изменений в данной сессии. Выберите Commit Changes
для сохранения изменений при перезагрузке Tomcat.
Для получения более детальной информации об элементах Connector прочтите
документацию под названием Coyote HTTP/1.1 Connectors, которая находится в файле
<JWSDP_HOME>/docs/tomcat/config/coyote.html или документ под названием JK 2
Connectors, находящийся в файле <JWSDP_HOME>/docs/tomcat/config/jk2.html.

Web-

Rendered by www.RenderX.com
Стр. 578 из 626 Утилита администрирования Tomcat

20.3.1.1. Типы элементов Connector


Используя утилиту admintool, можно создавать следующие типы коннекторов:
• HTTP
Выбирая HTTP, вы создаете компонент Connector, поддерживающий протокол HTTP/1.1.
Это позволяет Tomcat, кроме его способности выполнять Java-сервлеты и JSP-страницы,
функционировать в качестве автономного Web-сервера. Отдельный экземпляр данного
компонента отслеживает соединения с сервером в определенному порту TCP. Один
или более подобных компонентов Connector могут быть настроены, как часть отдельной
службы Service, причем каждый из них для обеспечения обработки запроса и создания
ответа пересылает данные к ассоциированному элементу Engine.
• HTTPS
Выбор HTTPS позволяет создавать SSL HTTP/1.1 Connector. Технология безопасных
соединений SSL (Secure Socket Layer) позволяет Web-броузерам и Web-серверам
устанавливать безопасное соединение. Для того чтобы реализовать протокол SSL,
Web-сервер должен иметь ассоциированный сертификат ключей для каждого из внешних
интерфейсов (IP-адресов), которые участвуют в безопасных соединениях. Детальные
инструкции о настройках коннектора HTTPS находятся в разделе Установка и настройка
поддержки SSL для Tomcat.
• AJP
Выбор AJP позволяет создавать компонент Connector, который связывается с Web-
коннектором посредством протокола Apache JServ ("AJP"). Это используется в случае,
если вы желаете незаметно интегрировать Tomcat в существующую (или новую)
инсталляцию Apache, или же, чтобы Apache обрабатывал статическое содержимое,
находящееся в Web-приложении, и/или утилизировал обработку SSL. Использование
протокола AJP позволит увеличить общую производительность во многих окружениях
приложений по сравнению с тем, если бы вы запускали их с помощью Tomcat автономно,
используя коннектор HTTP/1.1. Однако, полностью быть уверенным в этом можно лишь,
обеспечив использование обоих способов.

20.3.1.2. Аттрибуты элемента Connector


При создании или модификации любого типа коннектора используются аттрибуты,
представленные в Таблице A-3.
Таблица A-3 Общие аттрибуты элемента Connector
Атрибут Описание
Accept Count Максимальная длина очереди для входящих запросов на
соединение, когда все возможные запросы уже
обрабатываются. Любые запросы получаемые, когда очередь
полна, будут отвергнуты. Значением по-умолчанию является
10.
Connection Timeout Время в миллисекундах после установления соединения, в
течение которого коннектор будет ожидать предоставления
URI запроса. Значением по-умолчанию является 60000 (60
секунд).
Debug Level Уровень детальности отладки сообщений журнала. Большие
номера означают вывод более детальной информации. Если
не определено, по-умолчанию значение равно нулю (0).

Web-

Rendered by www.RenderX.com
Настройка элементов Service Стр. 579 из 626

Атрибут Описание
Default Buffer Size Размер буфера (в байтах) для входящих потоков, созданных
данным коннектором. По-умолчанию обеспечивается буфер,
размером 2048 байт.
Enable DNS Lookups Если вы желаете, чтобы при помощи вызовов к методу
request.getRemoteHost() осуществлялся поиск в DNS с целью
возвращения имени хоста удаленного клиента, установите
в качестве значения аттрибута True. В противном случае,
для того, чтобы пропустить поиск в DNS и ввести напрямую
IP адрес в форму String (это позволит увеличить
производительность) установите значение False.
IP Address Определяет, какой адрес для серверов, имеющих более чем
один адрес IP, будет использоваться для прослушивания
заданного порта. По-умолчанию данный порт будет
использоваться на всех адресах IP, связанных с сервером.
Port Number Номер порта TCP, на котором Connector создаст сокет
сервера и будет ожидать входящие соединения. Ваша
операционная система позволит только одному приложению
сервера прослушивать отдельный порт на отдельном IP-
адресе.
Redirect Port Number Номер порта, в который Tomcat автоматически будет
перенаправлять запрос, если Connector поддерживает
запросы через небезопасные соединения, а также получать
запрос, для которого соответствующие ограничения
безопасности требуют передачу посредством протокола SSL.
Minimum Количество потоков запросов для обработки, которые будут
созданы после запуска данного коннектора. Значение данного
атрибута должно быть меньше значения атрибута Maximum.
По-умолчанию оно равно 5.
Maximum Максимальное количество запросов для обработки,
создаваемых данным коннектором. Атрибут также определяет
максимальное количество одновременных запросов, которые
могут быть обработаны. Если значение не определено, по-
умолчанию оно установлено на 75.

В случае использования коннектора типа HTTP или HTTPS возможна настройка


дополнительных атрибутов, как указано в Таблице A-4.
Таблица A-4 Атрибуты коннекторов HTTP/HTTPS
Атрибут Описание
Proxy Name Имя сервера, возвращаемое в ответ на запросы
request.getServerName(), если данный коннектор использует
конфигурацию прокси.
Proxy Port Number Порт сервера, возвращаемый в ответ на запросы
request.getServerPort(), если данный коннектор использует
конфигурацию прокси.

Таблице A-5
Таблица A-5 Атрибуты HTTPS
Атрибут Описание
Client Authentication Если вы хотите, чтобы перед установкой соединения стэк
SSL требовал правильной цепочки сертификатов от клиента,
установите значение True. В противном случае, установите
False (определенное по-умолчанию), при этом цепочка
сертификатов не потребуется, пока клиент не запросит доступ
к ресурсу, защищенному ограничениями безопасности, для
чего потребуется его аутентификация.

Web-

Rendered by www.RenderX.com
Стр. 580 из 626 Утилита администрирования Tomcat

Атрибут Описание
Keystore Filename Путь и имя загружаемого файла ключей в котором хранится
сертификат клиента. По-умолчанию, именем файла является
.keystore. Он находится в домашней директории пользователя
операционной системы, который запустил Tomcat. Если вы
используете для имени файла и его пути значения,
определенные по-умолчанию, оставьте это поле пустым.Если
же вы определии имя файла ключей без назначения пути к
нему, утилита admintool будет искать его в директории
JWSDP_HOME>.
Keystore Password Пароль, используемый для получения доступа к сертификату
сервера из заданного файла ключей. По-умолчанию
установлено значение changeit.

Примечание: При использовании коннектора SSL, для создания файла ключей необходимо
использовать утилиту keytool. Если вы сгенерировали файл ключей с именем .keystore и
паролем changeit (определенными по-умолчанию) в домашней директории пользователя,
можно оставить атрибуты Keystore Filename и Keystore Password пустыми. Когда эти два
свойства не определены, admintool будет искать файл ключей со значениями имени и
пароля, установленными по-умолчанию (соответственно .keystore и changeit), в директории,
определенной по-умолчанию (см. выше). Если же вы опредилили имя файла ключей, не
указав пути к нему, admintool будет искать файл в директории <JWSDP_HOME>. В разделе
Установка и настройка поддержки SSL для Tomcat содержится детальная информация по
установке коннектора HTTPS.

20.3.2. Настройка элементов Host


Элемент Host символизирует виртуальный хост, который является ассоциацией сетевого
имени сервера (к примеру, www.mycompany.com) с отдельным сервером, на котором
запущен Tomcat. Для эффективности использования данное имя должно быть
зарегистрировано в сервере DNS, который управляет вашим доменом Internet.
Во многих случаях системные администраторы желают ассоциировать более одного
сетевого имени (к примеру www.mycompany.com и company.com) с одним и тем же
виртуальным хостом. Это может быть осуществлено с помощью свойства Host Name
Aliases, описанного в разделе Альтернативные имена хоста.
В элемент Service вложен один или более элементов Host. Точнее, один из элементов
Host, ассоциированый с каждым элементом Service ДОЛЖЕН иметь имя, совпадающее с
атрибутом defaultHost данного элемента Service. В элемент Host могут быть вложены
следующие элементы:
• Элементы Context, описанные в разделе Настройка элементов Context.
• Элементы Logger, описанные в разделе Настройка элементов Logger.
• Элементы Valve, описанные в разделе Настройка элементов Valve.
• Host Aliases, описанные в разделе Альтернативные имена хоста.
Для того чтобы отредактировать элемент Host выполните следующие действия:
1. Раскройте элемент Service в левом окне.
2. Раскройте элемент Host в левом окне.

Web-

Rendered by www.RenderX.com
Настройка элементов Service Стр. 581 из 626

3. Выберите для редактирования Host или любой из элементов: Contexts, Valves, Loggers
или Aliases.
4. Отредактируйте значения в правом окне.
5. Выберите Save для сохранения изменений в данной сессии. Выберите Commit Changes
для сохранения изменений при перезагрузке Tomcat.
Для создания нового элемента Host,
1. В левой панели выберите элемент Service. Настоятельно рекомендуется
модифицировать только службу Java Web Services Developer Pack Service или службу,
которую вы создали.
2. Выберите Create New Host из списка Available Actions.
3. Введите необходимые значения для элемента Host. Для получения детальной
информации по настройкам смотрите раздел Атрибуты элемента Host.
4. Выберите Save для сохранения изменений в данной сессии. Выберите Commit Changes
для сохранения изменений при перезагрузке Tomcat.
Для получения более подробной информации об элементах Host, ознакомьтесь с
документом "Host Container", который находится в файле
<JWSDP_HOME>/docs/tomcat/config/host.html.

20.3.2.1. Атрибуты элемента Host


Для настройки элемента Host доступны аттрибуты, представленые в Таблице A-6.
Таблица A-6 Атрибуты элемента Host
Атрибут Описание
Name Сетевое имя данного виртуального хоста, зарегистрированное
в вашем сервере DNS. Один из элементов Host, вложенных
внутри Engine ДОЛЖЕН иметь имя, совпадающее со
значением defaultHost для данного Engine.
Application Base Базовая директория приложения для данного виртуального
хоста. Является именем пути к директории, размещаемой на
данном виртуальном хосте, в которой могут содержаться
Web-приложения. Можно определить абсолютный путь
доступа к данной директории или путь относительно
директории, в которой установлен Tomcat.
Debug Level Уровень детальности отладки сообщений журнала. Большие
номера означают вывод более детальной информации. Если
не определено, по-умолчанию значение равно нулю (0).
Unpack WARs Если вы хотите, чтобы Web-приложения, размещенные в
данном виртуальном хосте из WAR-архива, были распакованы
в определенную директорию на диске, установите значение
True. В противном случае, для того, чтобы приложение
запускалось напрямую из файла WAR, установите значение
False (установлено по-умолчанию)

20.3.2.2. Альтернативные имена хоста


Во множестве окружений серверов сетевые администраторы настраивают более одного
сетевого имени (в сервере DNS), которые переводятся в IP-адрес того же сервера. Обычно,
каждое сетевое имя настраивается, как отдельный элемент Host со своим собственным
набором Web-приложений.

Web-

Rendered by www.RenderX.com
Стр. 582 из 626 Утилита администрирования Tomcat

Тем не менее, при запуске одного и того же набора приложений в некоторых случаях
предпочтительнее преобразовывать два или более сетевых имени на одном виртуальном
хосте. Общим примером использования данного сценария может послужить корпоративный
Web-сайт, в котором пользователи должны иметь возможность использовать как
www.mycompany.com так и company.com для доступа к одному и тому же содержимому и
набору приложений.
Tomcat поддерживает виртуальные хосты, являющиеся, множеством комбинаций "хосты
+ доменные имена", отображаемых в одиночный IP. Обычно, каждое имя хоста
отображается на хост сервера Tomcat, к примеру, www.foo.com отображается на localhost,
или www.foo1.com - на localhost1. В некоторых случаях различные имена хостов могут быть
отображены на один и тот же хост, к примеру, www.foo.com и www.foo1.com могут оба
отображаться на localhost. В данной ситуации вы увидите, что оба эти альтернативных
имени ассоциированы с localhost в утилите admintool.
Для использования альтернативных имен сервер DNS должен иметь имена хостов,
зарегистрированные в IP сервера, на котором будет запускаться Tomcat.
To create a new Host alias,
Для создания нового альтернативного имени хоста,
1. В левой панели выберите элемент Host.
2. Из списка Available Actions выберите Create New Alias.
3. Введите имя Alias.
4. Выберите Save для сохранения изменений в данной сессии. Выберите Commit Changes
для сохранения изменений при перезагрузке Tomcat.

20.3.2.3. Настройка элементов Context


Элемент Context отображает Web-приложение, которое запускается в пределах отдельного
виртуального хоста. Каждое Web-приложение базируется на WAR-файле или директории,
содержащей Web-приложение в распакованном виде. Для получения более подробной
информации о WAR-файлах, рекомендуется прочесть раздел Архивы Web-приложения.
При получении HTTP-запроса Tomcat выбирает Web-приложение, которое будет
использоваться для его обработки. Для того чтобы выбрать Web-приложение, Tomcat
сравнивает самый длинный префикс запрошенного URI с путем контекста для каждого
определенного элемента Context. Когда элемент Context выбран, он выбирает
соответствующий элемент Servlet для обработки входящего запроса. Данный запрос
основан на отображении элемента Servlet, определенного в дескрипторе размещения
Web-приложения, который должен находится в файле <web_app_root>/WEB-INF/web.xml.
Можно определить столь много элементов Context внутри элемента Host, сколько вам
необходимо, однако каждый из них должен иметь уникальный путь контекста. По крайней
мере один элемент Context должен включать в себя путь контекста равный строке нулевой
длины. Данный элемент Context становится "Web-приложением по-умолчанию" для данного
виртуального хоста и используется для обработки всех запросов, которые не совпадают
ни с одним другим путем контекста элемента Context.
Для того чтобы отредактировать элемент Context выполните следующие действия:
1. Раскройте элемент Service в левом окне.

Web-

Rendered by www.RenderX.com
Настройка элементов Service Стр. 583 из 626

2. Раскройте элемент Host в левом окне.


3. Выберите Context для редактирования.
4. В правом окне отредактируйте значения.
5. Выберите Save для сохранения изменений в данной сессии. Выберите Commit Changes
для сохранения изменений при перезагрузке Tomcat.
Для того чтобы создать новый элемент Context для службы
1. В левом окне выберите элемент Service.
2. Выберите в левом окне элемент Host, в который вы хотите добавить элемент Context.
3. Выберите Create New Context из списка Available Actions.
4. Введите необходимые значения для элемента Context. Для получения детальной
информации обратитесь к разделу Атрибуты элемента Сontext.
5. Выберите Save для сохранения изменений в данной сессии. Выберите Commit Changes
для сохранения изменений при перезагрузке Tomcat.
Для получения болеее подробной иформации об элементе Context обратитесь к документу
"The Context Container", который находися в файле
<JWSDP_HOME>/docs/tomcat/config/context.html.

20.3.2.3.1. Атрибуты элемента Context


Страница элемента Context содержит три типа свойств:
• Свойства элемента Context, описанные в Таблице A-7.
• Свойства элемента Loader, описанные в Таблице A-8.
• Свойства Session Manager, описанные в Таблице A-9.
Для настройки элемента Context доступны аттрибуты, представленые в Таблице A-7.
Таблица A-7 Свойства элемента Context
Атрибут Описание
Cookies Установите значение True, если вы хотите, чтобы для
идентификации сессии использовались файлы cookie (если
клиент их поддерживает). Установите значение False, если
вы желаете, чтобы идентификация сессии определялась
путем перезаписи URL. Значением по-умолчанию является
True.
Cross Context Установите значение True, если вы желаете вызвать в
приложении метод ServletContext.getContext() для успешного
возврата диспечтера запроса для других Web-приложений,
запускаемых на данном виртуальном хосте. Установите
значение False в защищенном окружении для того, чтобы
метод getContext() возвращал значение null. Значением по-
умолчанию является False.
Debug Level Уровень детальности отладки сообщений журнала. Большие
номера означают вывод более детальной информации. Если
не определено, по-умолчанию значение равно нулю (0).

Web-

Rendered by www.RenderX.com
Стр. 584 из 626 Утилита администрирования Tomcat

Атрибут Описание
Document Base Базовой директорией документа (также известной, как Context
Root) для данного Web-приложения является путь к WAR-
файлу (если данное приложение выполняется
непосредственно из WAR-файла). Можно обозначить
абсолютное имя пути для данной директории или WAR-
файла, или имя пути относительно базовой директории
приложения.
Override Установите значение True для того, чтобы указанные явно
установки в данном элементе Context переопределяли любые
соответствующие установки из элемента DefaultContext,
связанного с вашим хостом. По-умолчанию используются
установки из элемента DefaultContext, а значением атрибута
является False.
Path Путь контекста данного Web-приложения, совпадающего с
началом запрашивамого URI для выбора Web-приложения,
предназначенного для обработки. Все пути контекста в
пределах отдельного хоста должны быть уникальны. Если
вы определяете путь контекста как пустую строку (""), этим
самым вы определяете Web-приложение, используемое по-
умолчанию для данного хоста, которое будет обрабатывать
все запросы, не относящиеся к другим элементам Context.
Reloadable Установите значение True, если вы желаете, чтобы Tomcat
отслеживал изменения в классах /WEB-INF/classes/ и /WEB-
INF/lib и в случае обнаружения изменений автоматически
перезагружал Web-приложение. Данная особенность
чрезвычайно полезна во время разработки приложения,
однако требует больших затрат ресурсов и не рекомендована
к использованию в размещенных приложениях. Для
инициации перезагрузок размещенных приложений по
требованию можно использовать приложение Manager Web.
Значением по-умолчанию является False.
Use Naming Для того чтобы Tomcat мог использовать JNDI InitialContext
для данного Web-приложения, установите значение True
(совместимо с условиями платформы J2EE). Значением по-
умолчанию является False.
Working irectory Путь к временной директории, используемой сервлетами для
временного чтения и записи в пределах соответствующего
Web-приложения. Данная директория будет видима для
сервлетов в Web-приложении при помощи аттрибута
контекста Servlet (типа java.io.File) под названием
javax.servlet.context.tempdir, как описано в спецификации
сервлетов. Если значение не определено, будет
предоставлена подходящая директория
<JWSDP_HOME>/work.

Информация из раздела Свойства элемента Loader позволяет вам настраивать класс


loader для Web-приложения, который будет использоваться для загрузки сервлета и
JavaBean-классов. Обычно, стандартной конфигурации класса loader достаточно. Для
настройки элемента Loader доступны аттрибуты, представленые в Таблице A-8.
Таблица A-8 Свойства элемента Loader
Атрибут Описание
Check Interval Интервал в секундах между проверками на предмет
модифицирования классов и ресурсов, если значение
атрибута Reloadable установлено, как True. По-умолчанию
значением атрибута является 15 (сек).
Debug Level Уровень детальности отладки сообщений журнала. Большие
номера означают вывод более детальной информации. Если
не определено, по-умолчанию значение равно нулю (0).

Web-

Rendered by www.RenderX.com
Настройка элементов Service Стр. 585 из 626

Атрибут Описание
Reloadable Установите значение True, если вы желаете, чтобы Tomcat
отслеживал изменения в классах /WEB-INF/classes/ и /WEB-
INF/lib и в случае обнаружения изменений автоматически
перезагружал Web-приложение. Данная особенность
чрезвычайно полезна во время разработки приложения,
однако требует больших затрат ресурсов и не рекомендована
к использованию в размещенных приложениях. Для
инициации перезагрузок размещенных приложений по
требованию можно использовать приложение Manager Web.
Значением по-умолчанию является False.

Свойства Session Manager позволяют вам настраивать менеджер сессии, который будет
использоваться для создания, уничножения и сохранения HTTP-сессий данного Web-
приложения. Обычно стандартной конфигурации менеджера сессии достаточно. Для
настройки элемента Session Manager доступны аттрибуты, представленые в Таблице A-8.
Таблица A-9 Свойства элемента Session Manager
Атрибут Описание
Check Interval Интервал в секундах между проверками сессии данного
менеджера. на предмет истечения Значением по-умолчанию
является 60.
Debug Level Уровень детальности отладки сообщений журнала. Большие
номера означают вывод более детальной информации. Если
не определено, по-умолчанию значение равно нулю (0).
Session ID Initializer Tomcat обеспечивает две стандартные реализации Session
Manager.org.apache.catalina.session.StandardManager хранит
активные сессии. org.apache.catalina.session.PersistentManager
персистентно хранит активные сессии, которые
свопировались (кроме этого сохраняет сессии при
перезагрузке Tomcat) в хранилище, обозначенном
посредством соответствующего вложенного элемента Store.
Кроме обычных операций создания и удаления сессий
PersistentManager способен свопировать активные (но
неиспользуемые) сессии в персистентный механизм
хранилища, равно как и сохранять все сессии в случае
перезагрузок Tomcat. Используемый персистентный механизм
хранилища выбирается из элементов Store, вложенных в
элемент Manager (это необходимо для использования
PersistentManager).
Maximum Active Sessions Максимальное количество активных сессий, которые будут
создаваться данным менеджером. По-умолчанию количество
сессий не ограничено, значением атрибута является -1.

20.3.3. Настройка элементов Logger


Элемент Logger символизирует адресата для записи журнала, отладки и сообщений об
ошибках (включая содержимое стека) для Tomcat.
Если вы желаете вести журнал доступов, как это делает Web-сервер (например, при запуске
программы анализатора посещений), необходимо настроить компонент Access Log Valve
в ваших элементах Engine, Host или Context.
Используя утилиту admintool, можно создавать три типа элементов Logger:
• SystemOutLogger

Web-

Rendered by www.RenderX.com
Стр. 586 из 626 Утилита администрирования Tomcat

Стандартный Output Logger, который записывает все сообщения журнала в поток,


направленный в файл logs/catalina.out, относящийся к директории, где установлен
Tomcat.
• SystemErrLogger
Стандартный Error Logger, который записывает все сообщения об ошибках в поток,
направленный в файл logs/catalina.out, относящийся к директории, где установлен
Tomcat.
• FileLogger
File Logger записывает все сообщения в файл(ы) на диске в определенную директорию.
Действительные имена log-файлов создаются из определенного настройками префикса,
где текущая дата указана в формате YYYY-MM-DD, а также определенного настройками
суффикса. Каждый день после полуночи, в момент получения первого сообщения,
текущий log-файл будет закрыт, после чего будет создан новый log-файл с новой датой,
при этом перезагрузка Tomcat не потребуется.
Для того чтобы отредактировать элемент Logger,
1. Раскройте элемент Service в левом окне.
2. Выберите элемент Logger, который вы желаете отредактировать.
3. Отредактируйте значение в правом окне.
4. Выберите Save для сохранения изменений в данной сессии. Выберите Commit Changes
для сохранения изменений при перезагрузке Tomcat.
Для того чтобы создать новый элемент Logger для службы,
1. Выберите элемент Service в левом окне. Настоятельно рекомендуется редактировать
только службу Java Web Services Developer Pack Service, или службу, которую вы
создали.
2. Из списка Available Actions выберите Create New Logger.
3. Введите требуемые значения для элемента Logger. Для получения более подробной
информации обратитесь к разделу Атрибуты элемента Logger.
4. Выберите Save для сохранения изменений в данной сессии. Выберите Commit Changes
для сохранения изменений при перезагрузке Tomcat.
Для получения более подробной информации об элементах Logger, ознакомьтесь с
документом "Logger Component", который находится в файле
<JWSDP_HOME>/docs/tomcat/config/logger.html.

20.3.3.1. Атрибуты элемента Logger


Общие атрибуты для всех типов элемента Logger представлены в Таблице A-10.
Таблица A-10 Атрибуты элемента Logger
Атрибут Описание
Type Тип создаваемого элемента Logger.
Debug Level Уровень детальности отладки сообщений журнала. Большие
номера означают вывод более детальной информации. Если
не определено, по-умолчанию значение равно нулю (0).

Web-

Rendered by www.RenderX.com
Настройка элементов Service Стр. 587 из 626

Атрибут Описание
Verbosity Level Уровень словесного наполнения для данного элемента
Logger. Сообщения с уровнем словесного наполнения более
высоким, чем установлено, будут игнорироваться. Доступные
уровни: 0 (только сообщения о фатальных ошибках), 1
(ошибки), 2 (предупреждения), 3 (информация), и 4
(информация об отладке). Если атрибут не установлен, его
значением по-умолчанию является нуль (0) (только
сообщения о фатальных ошибках).

Если вы используете элемент Logger типа FileLogger, вам будет доступна установка
дополнительных атрибутов, как показано в Таблице A-11.
Таблица A-11 Атрибуты элемента Logger типа FileLogger
Атрибут Описание
Directory Абсолютное или относительное имя пути к директории, в
которой создаются log-файлы. Если установлен
относительный путь, он интерпретируется как путь к
директории, в которой установлен Tomcat. Если атрибут не
определен, значением по-умолчанию является logs
(относительно директори, в которую установлен Tomcat).
Prefix Префикс, который будет добавляться к началу каждого имени
log-файла. По-умолчанию значение определено как catalina.
Для того чтобы запретить использование префикса,
установите в качестве значения атрибута строку нулевой
длины.
Suffix Суффикс, добавляемый к концу каждого log-файла. По-
умолчанию значением данного атрибута является .log. Для
того чтобы запретить использование суффикса, установите
в качестве значения атрибута строку нулевой длины.
Timestamp В любом случае, все сообщения журнала будут отмечаться
датой и временем создания. Если вы хотите использовать
отметку о дате, установите значение True. В противном
случае установите значение False.

20.3.4. Настройка элементов Realm


Элемент Realm символизирует базу данных имен пользователей, паролей и их ролей
(подобно группам в Unix), назначенных данным пользователям. Различные реализации
элемента Realm позволяют серверу Tomcat быть интегрированным в окружения, где такая
информация об аутентификации уже создана и утверждена, а затем использовать данную
информацию для реализации безопасности, управляемой контейнером. Информацию по
данному вопросу можно получить из спецификации Java Servlet: http://java.sun.com/prod-
ucts/servlet/download.html.
Элемент Realm, созданный внутри элемента Service, относящегося к службе, в которой
запущен Tomcat, не может быть добавлен или удален. В Java WSDP, таким элементом
Service является Java Web Services Developer Pack. Вы можете создавать элемент Realm
внутри Service, который вы определили и добавили к Tomcat.
Для того чтобы отредактировать элемент Realm
1. Раскройте элемент Service в левом окне.
2. Выберите для редактирования элемент Realm.
3. Отредактируйте значение в правом окне.

Web-

Rendered by www.RenderX.com
Стр. 588 из 626 Утилита администрирования Tomcat

4. Выберите Save для сохранения изменений в данной сессии. Выберите Commit Changes
для сохранения изменений при перезагрузке Tomcat.
Элемент Realm может быть создан внутри любого контейнера - Engine, Host или Context.
У каждого из них может быть только один интерфейс элемента Realm. Элементы Realm,
ассоциированные с Engine или Host автоматически наследуются контейнерами более
низкого уровня, пока не будут переназначены явным образом.
Для того что бы создать новый элемент Realm,
1. Выберите службу, хост или контекст, в которых вы желаете создать новый элемент
Realm.
2. Из списка Available Actions выберите опцию Create New Realm. Выберите тип элемента
Realm. Атрибуты могут варьироваться в зависимости от типа элемента Realm, который
вы выбрали.
Существует несколько стандартных реализаций элемента Realm, включая следующие:
• JDBCRealm
Элемент Realm базы данных JDBC для обеспечения поиска имен, паролей и ролей,
ассоциированных с ними, подключает Tomcat к реляционной базе данных, доступ к
которой осуществляется посредством соответствующего драйвера JDBC. Поиск
производится каждый раз, когда он необходим, поэтому изменения в базе данных будут
немедленно отражаться на информации, используемой для аутентификации новых
логинов. Атрибуты для реализации элемента Realm в базе данных JDBC представлены
в разделе Атрибуты JDBCRealm.
• JNDIRealm
Элемент Realm базы данных JDBC для обеспечения поиска имен, паролей и ролей,
ассоциированных с ними, подключает Tomcat к директории LDAP, доступ к которой
осуществляется посредством соответствующего драйвера JNDI. Поиск производится
каждый раз, когда он необходим, поэтому изменения в базе данных будут немедленно
отражаться на информации, используемой для аутентификации новых логинов. Атрибуты
для реализации элемента Realm в базе данных JNDI представлены в разделе Атрибуты
JNDIRealm.
• MemoryRealm
Элемент Realm, базирующийся на использовании памяти (Memory Based), является
простой реализацией элемента Realm, которая считывает XML-файл для конфигурации
действительных пользователей, паролей и ролей. Формат файла и его местонахождение
идентичны тем файлам, которые поддерживаются Tomcat версии 3.x. Данная реализация
предназначена исключительно для организации работы с безопасностью, управляемой
контейнером - она НЕ предназначена для использовании в производстве. По существу,
нет никаких механизмов для модернизации набора пользователей, находящегося в
памяти, когда содержимое основного файла данных подверглось изменениям. Атрибуты
реализации элемента Memory Realm представлены в разделе Атрибуты элемента
MemoryRealm.
• UserDatabaseRealm
UserDatabaseRealm является реализацией элемента Realm, основанной на реализации
UserDatabase, которая доступна через глобальные ресурсы JNDI, настроенные для

Web-

Rendered by www.RenderX.com
Настройка элементов Service Стр. 589 из 626

привязки Tomcat. Параметр Resource Name устанавливается экземпляром UserDatabase


с использованием JNDI ресурсов имен. Атрибуты для реализации User Database Realm
представлены в разделе Атрибуты UserDatabaseRealm.
Для получения дополнительной информации об элементах Realm, прочтите документ под
названием "Realm Component", который находится в файле
<JWSDP_HOME>/docs/tomcat/config/realm.html или документ "Realm Configuration How To",
находящийся в файле <JWSDP_HOME>/docs/tomcat/realm-howto.html.

20.3.4.1. Атрибуты JDBCRealm


Реализация JDBC Database Realm подключает Tomcat к реляционной базе данных, доступ
к которой производится посредством соответсвующего драйвера JDBC, для обеспечения
поиска использования имен, паролей и связанных с ними ролей. Поиск производится тогда
при необходимости, поэтому изменения в базе данных будут немедленно отражаться на
информации, используемой для аутентификации новых логинов. Атрибуты для реализации
JDBC Database Realm представлены в Таблице A-12.
Таблица A-12 Атрибуты JDBCRealm
Атрибут Описание
Database Driver Полностью пригодное имя Java-класса драйвера JDBC,
используемого для связи с аутентификационной базой
данных.
Database Password Пароль, используемый при установке соединения с JDBC.
Database URL URL, передаваемый драйверу JDBC при установке
соединения с базой данных.
Database User Name Имя пользователя базы данных, используемое при установке
соединения с JDBC.
Debug Level Уровень детальности отладки сообщений журнала. Большие
номера означают вывод более детальной информации. Если
не определено, по-умолчанию значение равно нулю (0).
Digest Algorithm Имя алгоритма MessageDigest, используемого для
шифрования паролей пользователей, хранящихся в базе
данных. Если значение не определено, подразумевается,
что пароли будут храниться в текстовом виде.
Password Column Название колонки, в которой содержится пароль
пользователя. Если определено значение атрибута Digest
Algorithm, компонент будет считать, что пароли были
закодированы специфическим алгоритмом. В противном
случае, данные будут приняты, как простой текст.
Role Name Column Название колонки в таблице ролей пользователей, которая
содержит имя роли, назначенной соотвествующему
пользователю.
User Name Column Название колонки в таблице ролей пользовател(я)ей, (которая
содержит имя пользователя.
User Role Table Название таблицы ролей пользователя, которая должна
содержать колонки, обозначенные атрибутами User Name
Column и Role Name Column.
User Table Название таблицы пользователей, которая должна содержать
колонки, обозначенные атрибутами User Name Column и
Password Column.

Web-

Rendered by www.RenderX.com
Стр. 590 из 626 Утилита администрирования Tomcat

20.3.4.2. Атрибуты JNDIRealm


Реализация JNDI Directory Realm для обеспечения поиска имен пользователей, их паролей
и назначенных ролей подключает Tomcat к директории LDAP Directory, доступ к которой
производится посредством соответствующего драйвера JNDI. Поиск производится каждый
раз при необходимости, поэтому изменения в директории будут немедленно отражаться
на информации, используемой при аутентификации пользователей.
Множество атрибутов позволит вам настроить необходимое соединение к основной
директории, кроме этого элемент и имена атрибутов используются для извлечения
требуемой информации. Атрибуты для реализации JNDI Directory Realm представлены в
Таблице A-14.
Таблица A-13 Атрибуты элемента JNDIRealm
Атрибут Описание
Connection Name Название директории пользователя, использующееся при
соединении с JNDI. Данный атрибут является необходимым,
если вы определили атрибут User Password, в противном
случае он не используется.
Connection Password Пароль директории, используемый во время соединения с
JNDI. Данный атрибут является необходимым, если вы
определили атрибут User Password, в противном случае он
не используется.
Connection URL URL соединения, передаваемый драйверу JNDI при
соединении с директорией.
Context Factory Полностью пригодное имя Java-класса класса factory,
используюемое для получения JNDI InitialContext. По-
умолчанию подразумевается, что будет использоваться
стандартный поставщик JNDI LDAP.
Debug Level Уровень детальности отладки сообщений журнала. Большие
номера означают вывод более детальной информации. Если
не определено, по-умолчанию значение равно нулю (0).
Digest Algorithm Название алгоритма MessageDigest, используемого для
шифрования паролей пользователей, хранящихся в базе
данных. Если не значение определено, подразумевается,
что пароли будут храниться в текстовом виде.
Role Base Element Элемент базовой директории для выполнения поиска
пользовательских ролей.
Role Name Название атрибута директории, извлекаемого при выборе
назначенных ролей для пользователя. Если не определено,
используйте атрибут User Role Name для определения имени
атрибута в списке пользователей, который содержит ноль
или более имен ролей, назначенных данному пользователю.
Role Search Pattern Выражение поиска LDAP, используемое при выборе ролей
для отдельного пользователя, с отметкой {0}, где должно
вводиться действительное имя пользователя. Для получения
подробной информации обратитесь к разделу Значения для
атрибута шаблона.
Search Role Subtree Установите значение True для поиска в поддеревьях
элементов, выбранных выражением Role Search Pattern.
Установите значение False для отмены поиска в поддеревьях.
Значением по умоляанию является False.
User Role Name Имя атрибута директории в записи пользователя, содержащее
ноль или более значений имен ролей, назначенных данному
пользователю. Если атрибут не задан, используйте атрибут
Role Name для определения имени отдельного атрибута,
полученного из индивидуальных записей ролей, связанных
с данным пользователем.

Web-

Rendered by www.RenderX.com
Настройка элементов Service Стр. 591 из 626

Атрибут Описание
* User Base Element (userBase) Список, являющийся основой поддерева, содержащего
пользователей. Если не определено, поиск производится в
самом высоком уровне контекста. Данная опция не
применяется при использовании выражения userPattern (User
Pattern). Этот атрибут (userBase) недоступен в утилите
admintool. Смотрите раздел Определение Realm Attributes в
Server.xml для получения информации об установках данного
атрибута.
*User Pattern (userPattern) Выражение поиска LDAP, используующееся при получении
атрибутов отдельного пользователя, с отметкой {0}, где
должно вводиться имя пользователя. Если вы желаете
выбрать отдельную запись, базирующуюся на имени
пользователя, используйте данный атрибут вместо User
Search Pattern. Данный атрибут (userPattern) не доступен в
утилите admintool. Смотрите раздел Определение Realm
Attributes в Server.xml для получения подробной информации
об установках атрибута.
Search User Subtree Установите значение True, если вы используете User Search
Pattern для поиска аутентифицированных пользователей и
вам необходимо найти в поддеревьях элемент, определенный
User Base Element. Установка значения (по-умолчанию) False
приводит к поиску в определенном уровне. Не применяется,
если вы используете выражение User Pattern.
User Password Имя элемента LDAP, содержащего пароль пользователя.
Если вы определили данное значение, JNDIRealm будет
привязываться к директории с использованием значений,
определенных атрибутами Connection Name и Connection
Password и извлекать соответствующий атрибут для
сравнения значений, определенных пользователем, который
прошел аутентификацию. В противном случае, JNDIRealm
будет привязываться к директории, используя имя
пользователя и пароль, определенные пользователем, где
правильная привязка интерпретируется как аутентификация
пользователя.
User Search Pattern Выражение поиска LDAP использующееся при получении
атрибутов отдельного пользователя, с отметкой {0}, где
должно вводится имя пользователя. Используйте данный
атрибут вместо User Pattern для поиска по всей директории
(вместо извлечения отдельной записи) в дополнение к
контролю над атрибутами User Base Element и Search User
Subtree.

Таблица A-14 Атрибуты элемента JNDIRealm


Атрибут Описание
Connection Name Название директории пользователя, использующееся при
соединении с JNDI. Данный атрибут является необходимым,
если вы определили атрибут User Password, в противном
случае он не используется.
Connection Password Пароль директории, используемый во время соединения с
JNDI. Данный атрибут является необходимым, если вы
определили атрибут User Password, в противном случае он
не используется.
Connection URL URL соединения, передаваемый драйверу JNDI при
соединении с директорией.
Context Factory Полностью пригодное имя Java-класса класса factory,
используюемое для получения JNDI InitialContext. По-
умолчанию подразумевается, что будет использоваться
стандартный поставщик JNDI LDAP.

Web-

Rendered by www.RenderX.com
Стр. 592 из 626 Утилита администрирования Tomcat

Атрибут Описание
Debug Level Уровень детальности отладки сообщений журнала. Большие
номера означают вывод более детальной информации. Если
не определено, по-умолчанию значение равно нулю (0).
Digest Algorithm Название алгоритма MessageDigest, используемого для
шифрования паролей пользователей, хранящихся в базе
данных. Если не значение определено, подразумевается,
что пароли будут храниться в текстовом виде.
Role Base Element Элемент базовой директории для выполнения поиска
пользовательских ролей.
Role Name Название атрибута директории, извлекаемого при выборе
назначенных ролей для пользователя. Если не определено,
используйте атрибут User Role Name для определения имени
атрибута в списке пользователей, который содержит ноль
или более имен ролей, назначенных данному пользователю.
Role Search Pattern Выражение поиска LDAP, используемое при выборе ролей
для отдельного пользователя, с отметкой {0}, где должно
вводиться действительное имя пользователя. Для получения
подробной информации обратитесь к разделу Значения для
атрибута шаблона.
Search Role Subtree Установите значение True для поиска в поддеревьях
элементов, выбранных выражением Role Search Pattern.
Установите значение False для отмены поиска в поддеревьях.
Значением по умоляанию является False.
User Role Name Имя атрибута директории в записи пользователя, содержащее
ноль или более значений имен ролей, назначенных данному
пользователю. Если атрибут не задан, используйте атрибут
Role Name для определения имени отдельного атрибута,
полученного из индивидуальных записей ролей, связанных
с данным пользователем.
Search User Subtree Установите значение True, если вы используете User Search
Pattern для поиска аутентифицированных пользователей и
вам необходимо найти в поддеревьях элемент, определенный
User Base Element. Установка значения (по-умолчанию) False
приводит к поиску в определенном уровне. Не применяется,
если вы используете выражение User Pattern.
User Password Имя элемента LDAP, содержащего пароль пользователя.
Если вы определили данное значение, JNDIRealm будет
привязываться к директории с использованием значений,
определенных атрибутами Connection Name и Connection
Password и извлекать соответствующий атрибут для
сравнения значений, определенных пользователем, который
прошел аутентификацию. В противном случае, JNDIRealm
будет привязываться к директории, используя имя
пользователя и пароль, определенные пользователем, где
правильная привязка интерпретируется как аутентификация
пользователя.
User Search Pattern Выражение поиска LDAP использующееся при получении
атрибутов отдельного пользователя, с отметкой {0}, где
должно вводится имя пользователя. Используйте данный
атрибут вместо User Pattern для поиска по всей директории
(вместо извлечения отдельной записи) в дополнение к
контролю над атрибутами User Base Element и Search User
Subtree.
User Search Pattern Выражение поиска LDAP использующееся при получении
атрибутов отдельного пользователя, с отметкой {0}, где
должно вводится имя пользователя. Используйте данный
атрибут для поиска по всей директории вместо извлечения
отдельной записи.

Web-

Rendered by www.RenderX.com
Настройка элементов Service Стр. 593 из 626

20.3.4.2.1. Определение атрибутов Realm в файле Server.xml


При использовании утилиты admintool для определения JNDI Realm являются атрибуты
User Base Element (userBase) и User Pattern (userPattern). Атрибут User Base Element
(userBase) является базовым элементом для поиска пользователей, осуществляемого при
помощи User Search. Атрибут User Base Element может быть установлен в базе поддерева,
содержащего пользователей. Если данный элемент не определен, поиск производится в
самом высоком уровне контекста. Атрибуты User Search Pattern и Search User Subtree
будут по прежнему зависеть от атрибута User Base Element, используя для данного атрибута
значение пустой строки ("").
Используйте атрибут User Pattern (userPattern) чтобы определить выражение поиска LDAP
при получении атрибутов отдельного пользователя, с отметкой {0}, где вводится
действительное имя пользователя. Используйте данный атрибут вместо User Search
Pattern, если вы желаете выбрать отдельную запись, базирующуюся на имени пользователя.
Для использования любого из этих атрибутов
1. Отредактируйте <JWSDP_HOME>/conf/server.xml.
2. Найдите или создайте секцию, озаглавленную JNDIRealm.
3. Добавье код для userPattern, как показано далее:

<Realm className="org.apache.catalina.realm.JNDIRealm"
debug="99"
connectionName="cn=Manager,dc=mycompany,dc=com"
connectionPassword="secret"
connectionURL="ldap://localhost:389"
roleBase="dc=roles,dc=person,dc=net"
roleName="cn"
roleSearch="(uniqueMember={0})"
roleSubtree="false"
userPassword="userPassword"
userPattern="cn={0},dc=mycompany,dc=com"
/>

4. Или код для userBase, как показано далее:

<Realm className="org.apache.catalina.realm.JNDIRealm"
debug="99"
connectionURL="ldap://localhost:389"
userBase="ou=people,dc=mycompany,dc=com"
userSearch ="(mail={0})"
userRoleName ="memberOf"

Web-

Rendered by www.RenderX.com
Стр. 594 из 626 Утилита администрирования Tomcat

roleBase="ou=groups,dc=mycompany,dc=com"
roleName="cn"
roleSearch="(uniqueMember={0})"
/>

20.3.4.3. Атрибуты UserDatabaseRealm


UserDatabaseRealm является реализацией элемента Realm, базирующейся на реализации
UserDatabase, доступной через глобальные ресурсы JNDI и настроенной для нужд Tomcat.
Параметр Resource Name устанавливается экземпляром UserDatabase с использованием
JNDI ресурсов имен. Атрибуты для реализации User Database Realm представлены в
Таблице A-15.
Таблица A-15 Атрибуты UserDataBaseRealm
Атрибут Описание
Debug Level Уровень детальности отладки сообщений журнала. Большие
номера означают вывод более детальной информации. Если
не определено, по-умолчанию значение равно нулю (0).
Resource Name Имя глобального ресурса JNDI, при помощи которого
производится настройка экземпляра UserDatabase.

20.3.4.4. Атрибуты MemoryRealm


Элемент Realm, базирующийся на использовании памяти (Memory Based), является простой
реализацией элемента Realm, которая считывает XML-файл для конфигурации
действительных пользователей, паролей и ролей. Формат файла и его местонахождение
идентичны тем файлам, которые поддерживаются Tomcat версии 3.x. Данная реализация
предназначена исключительно для организации работы с безопасностью, управляемой
контейнером - она НЕ предназначена для использовании в производстве. По существу,
нет никаких механизмов для модернизации набора пользователей, находящегося в памяти,
когда содержимое основного файла данных подверглось изменениям. Атрибуты реализации
элемента Memory Realm представлены в Таблице A-16.
Таблица A-16 Атрибуты MemoryRealm
Атрибут Описание
Debug Level Уровень детальности отладки сообщений журнала. Большие
номера означают вывод более детальной информации. Если
не определено, по-умолчанию значение равно нулю (0).
Path Name Абсолютное или относительно директории <JWSDP_HOME>
имя пути к XML-файлу, содержащему информацию о
пользователе. Если путь не указан, значением по-умолчанию
является <JWSDP_HOME>/conf/tomcat-users.xml.

20.3.5. Настройка элементов Valve


Элемент Valve символизирует компонент, который будет внедрен в канал обработки
запросов для сервера Tomcat. Отдельные элементы Valve имеют различные возможности
для обработки, описанные ниже.
Для редактирования элемента Valve

Web-

Rendered by www.RenderX.com
Настройка элементов Service Стр. 595 из 626

1. В левом окне разверните элемент Service.


2. Выберите тот элемент Valve, который вы желаете отредактировать.
3. Отредактируйте значения в правом окне.
4. Выберите Save для сохранения изменений в данной сессии. Выберите Commit Changes
для сохранения изменений при перезагрузке Tomcat.
Для создания нового элемента Valve для службы
1. Выберите элемент Service в левом окне. Настоятельно рекомендуется модифицировать
только службу Java Web Services Developer Pack Service, или службу, которую вы
создали.
2. Из списка Available Actions выберите Create New Valve.
3. Введите необходимые значения для элемента Valve. Для получения подробной
информации по данным установкам см. раздел Атрибуты элемента Valve.
4. Выберите Save для сохранения изменений в данной сессии. Выберите Commit Changes
для сохранения изменений при перезагрузке Tomcat.
Для получения более подробной информации прочтите документ под названием "Valve
Component", расположенный в файле <JWSDP_HOME>/docs/tomcat/config/valve.html

20.3.5.1. Атрибуты элемента Valve


В данном релизе существуют пять типов элементов Valve, причем каждый из них имеет
собственный набор атрибутов, как описано в следующих разделах.

20.3.5.1.1. Атрибуты AccessLogValve


Элемент Access Log Valve создает log-файлы в таком же формате, в котором его создают
стандартные Web-серверы. Эти записи позднее могут быть проанализированы при помощи
стандартных инструментов анализа для отслеживания количества посещений, активности
сессии пользователя и т. д. Access Log Valve совместно использует множество
конфигураций и характеристик поведения File Logger, включая автоматическое продление
log-файлов каждую полночь, путем создания новых. Access Log Valve может быть связан
с любым контейнером сервера Tomcat, и будет записывать ВСЕ запросы, обработанные
данным контейнером. Атрибуты AccessLogValve представлены в Таблице A-17.
Таблица A-17 Атрибуты AccessLogValve
Атрибут Описание
Debug Level Уровень детальности отладки сообщений журнала. Большие
номера означают вывод более детальной информации. Если
не определено, по-умолчанию значение равно нулю (0).
Directory Абсолютный или относительный путь к директории, в которую
будут помещаться log-файлы, созданные данным элементом
valve. Если определен относительный путь, он
интерпретируется по отношению к <JWSDP_HOME>. Если
атрибут директории не определен, значением по-умолчанию
является logs (относительно <JWSDP_HOME>).
Pattern Схема форматирования, определяющая различные
информационные поля из запроса и ответа для записи в
журнал. Для выбора стандартного формата используются
значения common или combined. Для более подробной
информации см. раздел Значения для атрибута Pattern.

Web-

Rendered by www.RenderX.com
Стр. 596 из 626 Утилита администрирования Tomcat

Атрибут Описание
Prefix Префикс, добавляемый к началу каждого имени log-файла.
Ели не определено, значением по-умолчанию является
access_log. Для установки отстутствия префикса в имени
файла используйте строку нулевой длины.
Resolve Hosts Определяет, преобразовывать или нет IP-адреса удаленного
хоста в соответствующее имя хоста через поиск в DNS.
Установите значение True для преобразования IP-адреса
удаленного хоста в соответствующее имя хоста, используя
поиск в DNS. Установите значение False для отмены поиска
и выдачи удаленного IP-адреса.
Suffix Суффикс, добавляемый к концу каждого имени log-файла.
Если не определено, значением по-умолчанию является
строка нулевой длины, что указывает на отсутствие
суффикса.

20.3.5.1.2. Значения для атрибута Pattern


Значения для атрибута Pattern состоят из буквенных строк, комбинированных с
идентификаторами схемы, начинающимися с символа "%", для исключения замены
соответствующих значений переменных из данного запроса и ответа. Поддерживаются
следующие коды схемы:
• %a - Удаленный IP-адрес
• %A - Локальный IP-адрес
• %b - Отправлено байт, кроме заголовков HTTP, или '-', если ноль
• %B - Отправленный байт, кроме заголовков HTTP
• %h - Имя удаленного хоста (или IP-адрес, если значением атрибута resolveHosts является
false)
• %H - Протокол запроса
• %l - Удаленное логическое имя пользователя из identd (всегда возвращает '-')
• %m - Метод запроса (GET, POST и т.д.)
• %p - Локальный порт, в котором был получен запрос
• %q - Строка запроса (если существует, приставляется '?')
• %r - Первая линия запроса (метод и URI запроса)
• %s - Код статуса HTTP-запроса
• %S - ID пользователя сессии
• %t - Дата и время, в общем формате
• %u - Удаленный пользователь, прошедший аутенитификацию (если таковой имеется),
иначе '-'
• %U - Запрошенный путь URL
• %v - Имя локального сервера
Стенографическая структура, именуемая common (установлена по-умолчанию)
соответствует %h %l %u %t "%r" %s %b. Структура под названием combined добавляет к

Web-

Rendered by www.RenderX.com
Настройка элементов Service Стр. 597 из 626

структуре common значения заголовков Referrer и User-Agent, каждое из которых стоит в


двойных кавычках.

20.3.5.1.3. Атрибуты RemoteAddrValve


Remote Address Valve позволяет сравнивать IP-адрес клиента, который представил запрос,
с одним или более регулярными выражениями, а также позволяет продолжить запрос или
отказать клиенту, представившему запрос, в его обработке. Remote Address Valve должен
принимать любые запросы, представленные данному контейнеру перед тем, как он передаст
их далее.
Атрибуты данного элемента представлены в Таблице A-18.
Таблица A-18 Атрибуты RemoteAddrValve
Атрибут Описание
Allow IP Addresses Список схем регулярных выражений (разделенный запятыми)
с которым сравнивается IP-адрес удаленного клиента. Если
данный атрибут определен, то для того чтобы принять запрос,
удаленный адрес ДОЛЖЕН совпадать с выражением из
списка. Если атрибут не определен, будут приниматься все
запросы, КРОМЕ запросов с адресов, совпадающих со схемой
запрещенных адресов.
Deny IP Addresses Список схем регулярных выражений (разделенный запятыми)
с которым сравнивается IP-адрес удаленного клиента. Если
данный атрибут определен, то для того чтобы принять запрос,
удаленный адрес НЕ ДОЛЖЕН совпадать с выражением из
списка. Если атрибут не определен, прием запросов будет
регулироваться только атрибутом Allow IP Addresses.

20.3.5.1.4. Атрибуты RemoteHostValve


Remote Host Valve позволяет вам сравнить имя хоста клиента, который представил запрос,
с одним или более регулярными выражениями, а также позволяет продолжить запрос или
отказать в обработке запроса данному клиенту. Remote Host Valve должен принимать
любые запросы, представленные данному контейнеру перед тем, как он их передаст далее.
Атрибуты RemoteHostValve представлены в Таблице A-19.
Таблица A-19 Атрибуты RemoteHostValve
Атрибут Описание
Allow these Hosts Список схем регулярных выражений (разделенный запятыми),
с которым сравнивается имя хоста удаленного клиента. Если
данный атрибут определен, то для того чтобы принять запрос,
имя удаленного хоста ДОЛЖНО совпадать с выражением из
списка. Если атрибут не определен, будут приниматься все
запросы, КРОМЕ запросов с хостов, совпадающих со схемой
запрещенных имен хостов.
Deny these Hosts Список схем регулярных выражений (разделенный запятыми)
с которым сравнивается имя хоста удаленного клиента. Если
данный атрибут определен, то для того чтобы принять запрос,
имя хоста НЕ ДОЛЖНО совпадать с выражением из списка.
Если атрибут не определен, прием запросов будет
регулироваться только атрибутом Allow These Hosts.

Атрибуты RequestDumperValveRequest Dumper Valve является полезным инструментом


при отладке взаимодействий с приложением клиента (или броузером), которое отправляет
HTTP-запросы вашему серверу, базирующемуся на Tomcat. Если его настроить, он

Web-

Rendered by www.RenderX.com
Стр. 598 из 626 Утилита администрирования Tomcat

предоставит детали о каждом запросе, обработанном, ассоциированными с ним Engine,


Host или Context, для записи в журнал соответствующего элемента Logger. Данный элемент
Valve не имеет характерных атрибутов.

20.3.5.1.5. Атрбуты SingleSignOn


Single Sign On Valve используется для предоставления пользователям возможности
зарегистрироваться в любом из Web-приложений, связанных с вашим виртуальным хостом,
после чего их идентичности признаются всеми Web-приложениями данного виртуального
хоста. Данный элемент Valve имеет атрибут Debug Level.

20.4. Настройка элемента Resources


Узел Resources символизирует компонент Global Naming Resources. Элементы, находящиеся
в данном узле, отображают глобальные ресурсы JNDI, которые определены для сервера.
Для настройки менеджера ресурсов (или фэктори объекта) использующегося для возврата
объектов, когда Web-приложение выполняет операцию поиска JNDI в соответствующем
имени ресурса, могут быть использованы следующие ресурсы:
• Data Sources (Источники данных)
• Environment Entries (Записи окружения)
• User Databases (Базы данных пользователей)
Для получения более подробной информации о настройках Global Naming Resources,
прочтите документ под названием "GlobalNamingResources Component", который находится
в файле <JWSDP_HOME>/docs/tomcat/config/globalresources.html.
Настройка Data SourcesМногие Web-приложения для поддержки функциональности,
требуемой данным приложением, нуждаются в доступе к базе данных посредством драйвера
JDBC. Согласно спецификации платформы J2EE, для того чтобы сделать реализацию
Data Source (то есть связной пул для соединений JDBC) доступной для данных целей,
требуются сервера приложений J2EE. Tomcat предлагает подобную поддержку,
осуществляемую таким образом, что приложения баз данных, разработанные в Tomcat,
и, использующие данный сервис, будут запускаться без изменений на любом сервере.
Для редактирования Data Source
1. В левом окне раскройте элемент Resources.
2. Выберите для редактирования Data Source.
3. Отредактируйте значения в правом окне.
4. Выберите Save для сохранения изменений в данной сессии. Выберите Commit Changes
для сохранения изменений при перезагрузке Tomcat.
Для создания нового Data Source для Tomcat
1. Запустите утилиту deploytool. Выберите или создайте WAR-файл для вашего
приложения.
2. Выберите страницу с закладками Resource Refs. Нажмите кнопку Add для добавления
Resource Ref.

Web-

Rendered by www.RenderX.com
Настройка элемента Resources Стр. 599 из 626

3. Введите нужное вам имя в таблицу. Внизу страницы выберите Import Data Sources,
затем выберите из ниспадающего списка JNDI Name. Именем JNDI Name должно быть
имя Global Resource, который вы желаете добавить при помощи утилиты admintool.
4. Для размещения приложения выберите Tools _ Deploy. Когда приложение будет
размещено, в файле server.xml создастся Context и в него будет добавлен Resource
Link.
5. В утилите admintool выберите в левом окне элемент Data Source.
6. Из списка Available Actions выберите Create New Data Source.
7. Установите атрибуты Data Source. Для получения более подробной информации см.
раздел Атрибуты Data Source. Используйте имя JNDI Name, которое вы определили в
утилите deploytool. Добавьте Driver Name, URL, User Name и Password.
8. Выберите Save для сохранения изменений в данной сессии. Выберите Commit Changes
для сохранения изменений при перезагрузке Tomcat.
Если вы выберете Commit Changes, файл <JWSDP_HOME>/conf/server.xml будет обновлен
с записью для Resource и Resource Params в элементе GlobalNamingResources. Resource,
созданный здесь, связан с контекстом через элемент Resource Link.
Дальнейший пример является частью файла server.xml, который отображает Resource и
связанный с ним элемент ResourceParams в GlobalNamingResources, а также ссылку на
Resource внутри контекста.

<GlobalNamingResources>
<Environment description="Absolute Path name of the JWSDP
Installation" name="jwsdp.home"
override="true" type="java.lang.String"
value="/home/your_name/jwsdp-1_0"/>
<Resource auth="Container" description="Users and Groups
Database" name="UserDatabase"
scope="Shareable"
type="org.apache.catalina.UserDatabase"/>
<Resource name="jdbc/ActivityDB" scope="Shareable"
type="javax.sql.DataSource"/>
<ResourceParams name="UserDatabase">
<parameter>
<name>factory</name>

<value>org.apache.catalina.users.MemoryUserDatabaseFactory</value>
</parameter>
<parameter>
<name>pathname</name>
<value>conf/tomcat-users.xml</value>

Web-

Rendered by www.RenderX.com
Стр. 600 из 626 Утилита администрирования Tomcat

</parameter>
</ResourceParams>
<ResourceParams name="jdbc/ActivityDB">
<parameter>
<name>validationQuery</name>
<value></value>
</parameter>
<parameter>
<name>user</name>
<value>your_user_name</value>
</parameter>
<parameter>
<name>maxWait</name>
<value>5000</value>
</parameter>
<parameter>
<name>maxActive</name>
<value>4</value>
</parameter>
<parameter>
<name>password</name>
<value>your_password</value>
</parameter>
<parameter>
<name>url</name>
<value>jdbc:pointbase:server://localhost/ActivityDB</value>

</parameter>
<parameter>
<name>driverClassName</name>
<value>com.pointbase.jdbc.jdbcUniversalDriver</value>
</parameter>
<parameter>
<name>maxIdle</name>
<value>2</value>
</parameter>
</ResourceParams>
</GlobalNamingResources>
<Context
className="org.apache.catalina.core.StandardContext"
cachingAllowed="true"charsetMapperClass="org.apache.
catalina .util.CharsetMapper" cookies="true"

Web-

Rendered by www.RenderX.com
Настройка элемента Resources Стр. 601 из 626

crossContext="false" debug="0"displayName="JSTLActTrack"
docBase="/home/your_name/work/Standard
Engine_ocalhost\manager\JSTLActTrack.war"
mapperClass="org.apache.catalina.core.
StandardContextMapper" path="/JSTLActTrack"
privileged="false"reloadable="false" useNaming="true"
wrapperClass="org.apache.catalina.core.StandardWrapper">
<ResourceLink global="jdbc/ActivityDB" name="ActivityDB"/>
</Context>

20.4.1. Атрибуты элемента Data Source


Примечание: Для использования Data Source, вам необходимо иметь установленный и
настроенный драйвер JDBC.
Настройки атрибутов Data Source представлены в Таблице A-20.
Таблица A-20 Атрибуты элемента Data Source
Атрибут Описание
JNDI Name Имя JNDI, под которым вы будете искать предустановленные
источники данных. По-договоренности, все подобные имена
разбиваются на подконтекст jdbc (относительно стандартного
контекста наименований java:comp/env, являющегося корнем
всех предоставляемых фэктори ресурсов). К примеру, данная
запись должна выглядеть как jdbc/EmployeeDB.
Data Source URL URL соединения, передаваемый драйверу JDBC. К примеру:
jdbc:HypersonicSQL:database.
JDBC Driver Class Полностью пригодное имя Java-класса используемого
драйвера JDBC. К примеру: org.hsql.jdbcDriver.
User Name Имя пользователя базы данных, передаваемое драйверу
JDBC.
Password Пароль базы данных, передаваемый драйверу JDBC.
Max. Active Connections Максимальное количество активных запросов, которые могут
быть одновременно распределены из данного пула.
Значением по-умолчанию является 4.
Max. Idle Connections Максимальное количество соединений, которые могут
одновременно оставаться незанятыми в данном пуле.
Значением по-умолчанию является 2.
Max. Wait for Connections Время в миллисекундах перед возбуждением исключения, в
течение которого пул будет ожидать (при отсутствии
доступных соединений) возвращения соединения. Значением
по-умолчанию является 5000.
Validation Query SQL-запрос, который может использоваться пулом для
проверки соединений, перед тем, как они возвратятся
приложению. Если значение установлено, данный запрос
ДОЛЖЕН быть выражением SQL SELECT, которое
возвращает хотя бы одну строку.

Настройка элемента Environment EntriesИспользуйте данный элемент для настройки или


удаления названных значений, которые будут видимы для Web-приложений в качестве
ресурсов записей окружения. Примером записи окружения может служить абсолютный

Web-

Rendered by www.RenderX.com
Стр. 602 из 626 Утилита администрирования Tomcat

путь к директории, в которую был установлен Java WSDP, который уже определен как
Environment Entry.
Для редактирования Environment Entry
1. В левом окне разверните элемент Resources.
2. Выберите Environment Entries.
3. Выберите в правом окне Environment Entry для редактирования. По-умолчанию,
отобразится запись окружения для абсолютного пути к директории, в которую был
установлен Java WSDP.
4. Отредактируйте значения в правом окне.
5. Выберите Save для сохранения изменений в данной сессии. Выберите Commit Changes
для сохранения изменений при перезагрузке Tomcat.
Для создания нового Environment Entry для Tomcat
1. В левом окне выберите элемент Environment Entries.
2. Из списка Available Actions выберите Create New Env Entry.
3. Установите атрибуты Environment Entries. Для получения более подробной информации
см. раздел Атрибуты элемента Environment Entries.
4. Выберите Save для сохранения изменений в данной сессии. Выберите Commit Changes
для сохранения изменений при перезагрузке Tomcat.

20.4.2. Атрибуты элемента Environment Entries


Доступные атрибуты элемента Environment представлены в Таблице A-21.
Таблица A-21 Атрибуты элемента Environment Entries
Атрибут Описание
Name Имя создаваемой записи окружения, относительно контекста
java:comp/env. К примеру: jwsdp.home.
Type Польностью пригодное имя Java-класса, ожидаемое Web-
приложением для данной записи окружения: java.lang.Boolean,
java.lang.Byte, java.lang.Character, java.lang.Double,
java.lang.Float, java.lang.Integer, java.lang.Long, java.lang.Short,
или java.lang.String.
Value Значение параметра, который будет предоставлен
приложению при запросе от контекста JNDI. Данное значение
должно быть преобрауемо в Java-тип, определенной типом
атрибута. К примеру: <path_to_home_directory>/jwsdp-1_0.
Override Application Level Entries Если вы не хотите, чтобы Environment Entry использовался
для переопределения значения подобного имени записи
окружения, найденного в дескрипторе размещения Web-
приложения, установите значение False. По-умолчанию
переопределения разрешены.
Description При необходимости, предоставляет описание данной записи
окружения удобное для чтения.

Настройка User Databases. Для настройки и редактирования базы данных пользователей


данного сервера используйте элемент Resource. По-умолчанию определена база данных
<JWSDP_HOME>/conf/tomcat-users.xml

Web-

Rendered by www.RenderX.com
Администрирование ролей, групп и пользователей Стр. 603 из 626

Для редактирования элемента User Database


1. В левом окне разверните элемент Resources.
2. В левом окне выберите User Databases.
3. В правом окне выберите для редактирования User Database. По-умолчанию отобразится
база данных пользователей для Tomcat.
4. Отредактируйте значения в правом окне.
5. Выберите Save для сохранения изменений в данной сессии. Выберите Commit Changes
для сохранения изменений при перезагрузке Tomcat.
Чтобы создать новой элемент User Database для сервера Tomcat, выполните следующие
действия:
1. В левом окне выберите элемент User Databases.
2. Из списка Available Actions выберите Create New User Database.
3. Установите атрибуты элемента User Database. Для получения подробной информации
смотрите раздел Атрибуты User Database.
4. Выберите Save для сохранения изменений в данной сессии. Выберите Commit Changes
для сохранения изменений при перезагрузке Tomcat.
Атрибуты User DatabaseНастройка элемента User Database при помощи атрибутов описана
в Таблице A-22.
Таблица A-22 Атрибуты элемента User Database
Атрибут Описание
Name Имя создаваемой базы данных пользователей, к примеру,
UserDatabase.
Location Место, где должна создаваться база данных пользователей,
к примеру, conf/tomcat-users.xml.
Factory Тип фэктори, используемый с этой базой данных, обычно
org.apache.catalina.users.MemoryUserDatabaseFactory.
Description Текстовое описание содержимого базы данных, к примеру:
Users and Groups Database.

20.5. Администрирование ролей, групп и пользователей


Информация о понимании, добавлении, удалении и редактировании ролей, групп и
пользователей находится в главе Безопасность Web-приложения в разделе Пользователи,
группы и роли.

20.6. Дополнительная информация


• Tomcat Configuration Reference. Для получения дополнительной информации об
элементах, которые могут быть использованы для настройки поведения Tomcat, прочтите
документ Tomcat Configuration Reference, который находится в файле
<JWSDP_HOME>/docs/tomcat/config/index.html.

Web-

Rendered by www.RenderX.com
Стр. 604 из 626 Менеджер Web-приложений Tomcat (Tomсat Web Application Manager)

• JNDI Resources How-To. В данном документе обсуждается настройка JNDI Resources,


Tomcat Standard Resource Factories, JDBC Data Sources и Custom Resource Factories.
Он находится в файле <JWSDP_HOME>/docs/tomcat/jndi-resources-howto.html.
• Manager Application How-To. В данном документе описывается использование Manager
Application для размещения нового Web-приложения, сворачивания существующего
приложения или перезагрузки существующего приложения без необходимости закрытия
и перезагрузки Tomcat. Документ находится в файле
<JWSDP_HOME>/docs/tomcat/manager-howto.html.
• Proxy Support How-To. В данном документе обсуждается запуск на фоне прокси-сервера
(или web-сервера, который настроен как прокси-сервер). В частности, в документе
обсуждается то, как управлять значениями, возвращенными вызовами из Web-
приложений, которые запрашивают имя сервера и номер порта, в который запрос будет
отправлен на обработку. Документ можно найти в файле
<JWSDP_HOME>/docs/tomcat/proxy-howto.html.
• Realm Configuration How-To. В документе обсуждается настройка Tomcat для поддержки
безопасности, управляемой контейнером, путем соединения с существующей базой
данных имен пользователей, паролей и ролей. Документ находится в файле
<JWSDP_HOME>/docs/tomcat/realm-howto.html.
• Security Manager How-To. В документе обсуждается использование SecurityManager при
запущенном Tomcat для защиты вашего сервера от неавторизированных сервлетов,
JSP-страниц, JSP-компонентов и библиотек тэгов. Документ находится в файле
<JWSDP_HOME>/docs/tomcat/security-manager-howto.html.
• SSL Configuration How-To. Обсуждается инсталляция и настройка поддержки протокола
SSL для сервера Tomcat. Настройка поддержки SSL для Tomcat с использованием
WSDP обсуждается в разделе Установка и настройка поддержки SSL для Tomcat. В
документации Tomcat, которая находится в файле <JWSDP_HOME>/docs/tomcat/ssl-
howto.html также обсуждается данный вопрос, однако информация, представленная в
данном руководстве, более свежая по отношению к документации Tomcat, поставляемой
с Java WSDP.

21. Менеджер Web-приложений Tomcat (Tomсat Web


Application Manager)
Менеджер Web-приложений Tomcat используется для составления списков, установки,
перезагрузки, размещения и удаления Web-приложений из Tomcat. Менеджер Web-
приложений Tomcat для простоты использования в данном разделе называется просто
manager.

21.1. Запуск менеджера Web-приложений


Manager является web-приложением, которое предварительно установлено в Tomcat.
Следовательно, для того, чтобы Tomcat мог его использовать, он сам должен быть запущен.
Вызовите команду manager, обратившись к одному из URL-адресов, перечисленных в
Таблице B-1.

Web-

Rendered by www.RenderX.com
Исполнение команд менеджера с использованием задач Ant Стр. 605 из 626

Таблица B-1 Команды менеджера web-приложения Tomcat


Функция Команда
Составить список http://<host>:8080/manager/list
Установить http://<host>:8080/manager/install? path=/mywebapp&
war=file:/path/to/mywebapphttp://<host>:8080/manager/install?
path=/mywebapp& war=jar:file:/path/to/mywebapp.war!/
Перезагрузить http://<host>:8080/manager/reload?path=/mywebapp
Удалить http://<host>:8080/manager/remove?path=/mywebapp

Так как страницы manager являются защищенными Web-ресурсами, при первом вызове
команды manager появится диалоговое окно для аутентификации. При входе в manager
необходимо воспользоваться теми именем пользователя и паролем, которые были
использованы при установке Java WSDP.
Документ "Manager App HOW-TO", распространяемый вместе с Java WSDP, который можно
найти в файле JWSDP_HOME>/docs/tomcat/manager-howto.html, содержит техническую
информацию о менеджере приложения.

21.2. Исполнение команд менеджера с использованием задач Ant


Версия Ant, распространяемая с Java WSDP, поддерживает задачи, которые вызывают
команды manager. Это позволяет вам запускать команды из окна терминала. Все эти задачи
представлены в Таблице B-2.
Таблица B-2 Задачи менеджера web-приложения Ant
Функция Синтаксис задачи Ant
Составить список <list url="url" username="username" password="password" />
Установить <install url="url" path="mywebapp" war="" username="username"
password="password" />Значением атрибута war может быть
WAR-файл (jar:file:/path/to/mywebapp.war!/) или
нераспакованная директория (file:/path/to/mywebapp).
Перезагрузить <reload url="url" path="mywebapp" username="username"
password="password" />
Разместить <deploy url="url" path="mywebapp" war="file:/path/to/mywe-
bapp.war" username="username" password="password" />
Свернуть <undeploy url="url" path="mywebapp" username="username"
password="password" />
Удалить <remove url="url" path="mywebapp" username="username"
password="password" />

Примечание: Установленное приложение будет недоступно после перезагрузки Tomcat.


Для того чтобы сделать приложение постоянно доступным, используйте задачу deploy.
В виду того, что пользователь manager должен быть обязательно аутентифицирован,
задача Ant кроме URL-адреса использует атрибуты username и password. Вместо того
чтобы внедрять их в build-файл Ant, можно использовать подход, использовавшийся в
других примерах руководства. Установите свойства username и password в файле с
названием build.properties в своей домашней директории следующим образом:

username=ManagerName

Web-

Rendered by www.RenderX.com
Стр. 606 из 626 Сервер реестра Java WSDP

password=ManagerPassword

Замените ManagerName and ManagerPassword на значения, которые вы определили в


качестве имени пользователя и пароля при установке Java WSDP.
Примечание: В системе Windows вашей домашней директорией является директория, где
хранится Windows-профиль. Например, в системе Windows 2000 это будет директория
C:\Documents and Settings\yourProfile.
Build-файлы Ant импортируют эти свойства при помощи следующего элемента:

<property file="${user.home}/build.properties"/>

22. Сервер реестра Java WSDP


Ким Хаас
Реестр предлагает пользователям или приложениям механизм, позволяющий
рекламировать и обнаруживать Web-службы. Сервер реестра пакета разработчика Web-
служб Java (Java Web Services Developer Pack - Java WSDP) реализует версию 2 проекта
"Универсальное обнаружение и интеграция описаний" (Universal Description, Discovery and
Integration project - UDDI) для предоставления реестра UDDI для Web-служб в частном
окружении. Вы можете использовать его для разработки приложений Web-служб совместно
с Java WSDP API в качестве тестового реестра.
Вы можете использовать сервер реестра для тестирования разработанных вами
приложений, которые используют Java API для XML-реестров (JAXR), как описано в разделе
Java API для XML-реестров. Кроме того, для обеспечения запросов и обновлений данных
реестра вы можете использовать броузер реестра JAXR, представленный в Java WSDP.
Релиз сервера реестра, являющийся частью Java WSDP, включает в себя следующее:
• Базу данных, основанную на родной базе данных XML Xindice, которая является частью
XML-проекта Apache. Данная база данных обеспечивает репозиторий для данных
реестра.
• Утилиту под названием Indri, которая позволяет вам создавать и инспектировать базу
данных с использованием графического интерфейса.
Сервер реестра не поддерживает сообщения, определенные в Спецификации репликаций
UDDI версии 2.0

22.1. Установка сервера реестра


Прежде чем использовать сервер реестра Java WSDP, вы должны запустить Tomcat и базу
данных Xindice, причем не важно, в какой последовательности вы это сделаете.
Перейдите в директорию <JWSDP_HOME>/bin (или добавьте ее в PATH).

Web-

Rendered by www.RenderX.com
Использование JAXR API для получения доступа к серверу реестра Стр. 607 из 626

Для получения информации о том, как запустить Tomcat обратитесь к разделу Запуск
Tomcat
Для запуска базы данных Xindice используйте команду:

xindice-start ( Microsoft Windows)


xindice-start.sh ( UNIX)

Команда выполняется в фоновом режиме. Для запуска базы данных может потребоваться
некоторое время.
Для того чтобы узнать, как завершить работу Tomcat обратитесь к разделу Завершение
работы Tomcat.
Для завершения работы базы данных используйте команду:

xindice-stop ( Microsoft Windows)


xindice-stop.sh ( UNIX)

22.2. Использование JAXR API для получения доступа к серверу реестра


Вы можете получить доступ к серверу реестра путем использования программ-примеров,
которые находятся в директории <JWSDP_HOME>/docs/tutorial/examples/jaxr. Для детального
рассмотрения работы данных примеров воспользуйтесь информацией из раздела Запуск
примеров.
Перед тем, как скомпилировать данные примеры вам необходимо отредактировать файл
JAXRExamples.properties следующим образом.
1. При необходимости, отредактируйте следующие строки в файле JAXRExamples.prop-
erties для того, чтобы определить сервер реестра. По-умолчанию реестром является
Registry Server, то есть, если вы используете примеры впервые, вам не нужно выполнять
этот шаг. Данные строки должны выглядеть примерно так:

## Uncomment one pair of query and publish URLs.


## IBM:
#query.url=http://uddi.ibm.com/testregistry/inquiryapi
#publish.url=https://uddi.ibm.com/testregistry/protect/publish
api
## Microsoft:
#query.url=http://uddi.microsoft.com/inquire

Web-

Rendered by www.RenderX.com
Стр. 608 из 626 Сервер реестра Java WSDP

#publish.url=https://uddi.microsoft.com/publish
## Registry Server:
query.url=http://localhost:8080/registry-
server/RegistryServerServlet
publish.url=http://localhost:8080/registry-
server/RegistryServerServlet

Если сервер реестра исполняется не в вашей системе, определите подходящее имя


хоста взамен localhost. При этом для publishURL не следует использовать https:.
2. При необходимости можно определить имя пользователя и пароль для дальнейшего
использования, отредактировав соответствующие строки в файле JAXRExamples.prop-
erties. По-умолчанию таким паролем является пароль сервера реестра:

## Specify username and password if needed


## testuser/testuser are defaults for Registry Server
registry.username=testuser
registry.password=testuser

3. Следующие строки в файле JAXRExamples.properties вы можете оставить без изменений.


По причине того, что в данном примере для доступа к серверу реестра прокси не
используется, эти значения также не используются. Если вы ранее установили значения
хоста, можете оставить их такими, какие они есть.

## HTTP and HTTPS proxy host and port;


## ignored by Registry Server
http.proxyHost=
http.proxyPort=8080
https.proxyHost=
https.proxyPort=8080

4. Не стесняйтесь менять данные организации, которые находятся в остальной части


файла. Эти данные используются для публикации и примеров почтовых адресов.

22.3. Использование клиентского сценария командной строки с сервером


реестра
Оболочка сценариев находится в директории <JWSDP_HOME>/samples/registry-server,
для систем UNIX это файл с именем registry-server-test.sh , для систем Microsoft Windows,
соответственно registry-server-test.bat.

Web-

Rendered by www.RenderX.com
Использование клиентского сценария командной строки с сервером Стр. 609 из 626
реестра

Для отправки сообщений серверу реестра сценарий использует XML-файлы, которые


находятся в поддиректории xml.
Для того чтобы использовать сценарий, перейдите в директорию, где находится этот
сценарий. Удостоверьтесь, что сценарий является исполняемым (или сделайте его таким).
Сценарий можно использовать для выполнения следующих задач:
• Получение аутентификации
• Сохранение бизнеса
• Поиск бизнеса
• Получение деталей бизнеса
• Удаление бизнеса
• Подтверждение сообщений
• Извлечение конкретного бизнеса пользователя
• Отправка различных типов UDDI-сообщений

22.3.1. Получение аутентификации


Перед выполнением других задач, вы должны получить аутентификацию в качестве
пользователя сервера реестра.
Для получения аутентификации используете файл GetAuthToken.xml, который находится
в поддиректории xml. По-умолчанию, реестр разрешает доступ пользователю с именем
testuser и паролем testuser. Для создания других пользователей вначале обратитесь к
инструкции Добавление новых пользователей в реестр, а затем, чтобы определить новые
имя пользователя и пароль, отредактируйте соответствующим образом файл GetAuthTo-
ken.xml.
Для получения аутентификации наберите следующую команду в одной строке:
для Windows:

registry-server-test run-cli-request
-Drequest=xml\GetAuthToken.xml

для UNIX:

registry-server-test.sh run-cli-request
-Drequest=xml/GetAuthToken.xml

При запуске сценарий возвратит тег <authToken>, который содержит тег <authInfo>. Вы
будете использовать значение этого тега в следующем шаге.

Web-

Rendered by www.RenderX.com
Стр. 610 из 626 Сервер реестра Java WSDP

Значение тега действительно в течение одного часа. После окончания действия сценария
его можно перезапустить.

22.3.2. Сохранение бизнеса


Для того чтобы сохранить (то есть добавить) бизнес, используйте файл SaveBusiness.xml
из поддиректории xml. Прежде чем запустить сценарий, отредактируйте тег <authInfo> в
данном файле и замените существующее содержимое содержимым тега <authInfo> ,
возвращенным в предыдущем шаге. Вы можете также менять и другие значения,
определенные в файле.
Для сохранения бизнеса наберите следующую команду в одной строке:
для Windows:

registry-server-test run-cli-request
-Drequest=xml\SaveBusiness.xml

для UNIX:

registry-server-test.sh run-cli-request
-Drequest=xml/SaveBusiness.xml

Результат появится в окне терминала, в котором вы запускаете команду.

22.3.3. Поиск бизнеса


Для того чтобы найти бизнес по имени, используйте файл FindBusiness.xml , который
находится в поддиректории xml.
Прежде чем запустить сценарий в этот раз, отредактируйте файл, изменив значение в
теге <name> на имя, которое вы указали в файле SaveBusiness.xml.
Для поиска бизнеса используйте следующие команды:
для Windows:

registry-server-test run-cli-request
-Drequest=xml\FindBusiness.xml

для UNIX:

Web-

Rendered by www.RenderX.com
Использование клиентского сценария командной строки с сервером Стр. 611 из 626
реестра

registry-server-test.sh run-cli-request
-Drequest=xml/FindBusiness.xml

Результат появится в окне терминала. Обратите внимание на значение businessKey,


возвращенное в теге <businessEntity>. Оно понадобится вам при выполнении следующего
шага.

22.3.4. Получение деталей бизнеса


Для получения деталей о бизнесе используйте файл GetBusinessDetail.xml из поддиректории
xml.
Перед запуском сценария отредактируйте этот файл, скопировав в тег <businessKey>
значение businessKey, полученное в предыдущем шаге.
Для получения деталей о бизнесе, сохраненном вами, используйте следующую команду:
для Windows:

registry-server-test run-cli-request
-Drequest=xml\GetBusinessDetail.xml

для UNIX:

registry-server-test.sh run-cli-request
-Drequest=xml/GetBusinessDetail.xml

Результат появится окне в терминала.

22.3.5. Удаление бизнеса


Для удаления сохраненного вами бизнеса используйте файл DeleteBusiness.xml из
поддиректории xml.
Перед запуском сценария отредактируйте файл следующим образом:
1. Замените значение тега <authInfo> на значение, которое вы использовали в SaveBusi-
ness.xml.
2. Замените значение тега <businessKey> на значение бизнес-ключа того бизнеса, который
вы хотите удалить.
Для удаления бизнеса используйте следующую команду:для Windows:

registry-server-test run-cli-request

Web-

Rendered by www.RenderX.com
Стр. 612 из 626 Сервер реестра Java WSDP

-Drequest=xml\DeleteBusiness.xml

для UNIX:

registry-server-test.sh run-cli-request
-Drequest=xml/DeleteBusiness.xml

22.3.6. Проверка UDDI-сообщений


Для проверки перед отправкой соответствия UDDI-сообщения с XML-схемой UDDI V2.0
используйте следующую команду:

registry-server-test run-validate -Dinstance=XML_file_name


Если файл содержит ошибки, сообщения об ошибках будут иметь следующий формат:

file:line:column:message

22.3.7. Извлечение бизнеса пользователя


Для того чтобы получить все позиции, опубликованные пользователем, воспользуйтесь
файлом GetRegisteredInfo.xml, находящимся в поддиректории xml.
В данном случае, перед запуском сценария вам необходимо отредактировать файл
GetRegisteredInfo.xml путем копирования в тег <authInfo> строки <authInfo>, которую вы
ввели в файл SaveBusiness.xml или DeleteBusiness.xml.
Для получения деталей о сохраненном вами бизнесе используйте команду:
для Windows:

registry-server-test run-cli-request
-Drequest=xml\GetRegisteredInfo.xml

для UNIX:

registry-server-test.sh run-cli-request

Web-

Rendered by www.RenderX.com
Использование утилиты Indri для получения доступа к базе данных Стр. 613 из 626
сервера реестра

-Drequest=xml/GetRegisteredInfo.xml

22.3.8. Отправка сообщений UDDI-запроса


Для отправки любого UDDI-запроса серверу, используйте команду:

registry-server-test run-cli-request -Drequest=name_of_file

где name_of_file - это имя XML-файла, содержащего UDDI-сообщение. Это хорошая идея
- проверять сообщения перед их отправкой.
Поддиректория xml содержит несколько дополнительных сообщений, которые вы можете
отредактировать и использовать. Кроме того, вы можете создавать свои собственные
сообщения.

22.4. Использование утилиты Indri для получения доступа к базе данных


сервера реестра
Утилита Indri обеспечивает графический интерфейс (GUI), который позволяет вам получать
доступ к базе данных сервера реестра напрямую. Данную утилиту можно использовать
для:
• Сохранения бизнеса
• Получения деталей о бизнесе
• Поиска бизнеса
• Удаления бизнеса
• Отображения содержимого базы данных
Примечание: Индри - это большой лемур. Первое сообщение о нем появилось, когда
европейцы впервые прибыли на Мадагаскар. Они услышали крик, доносящийся из деревьев,
и поинтересовались, что производит такой шум? Ответом было "Индри! Индри!", что на
Малагаси означает "Смотри! Ищи!" Похоже, это подходящее название для утилиты,
производящей поиск в базе данных.
Утилита Indri вызывается при помощи сценария registry-server-test, который находится в
директории <JWSDP_HOME>/samples/registry-server.
Используйте следующую команду:

registry-server-test.sh run-indri (UNIX systems)


registry-server-test run-indri (Microsoft Windows systems)

Для завершения работы с утилитой Indri, выберите Exit в меню File.

Web-

Rendered by www.RenderX.com
Стр. 614 из 626 Сервер реестра Java WSDP

22.4.1. Сохранение бизнеса


Для того чтобы сохранить (создать) бизнес, выполните следующие действия.
В меню File выберите Open и откройте файл SaveBusiness.xml, который находится в
поддиректории xml. Содержимое файла появится в большой текстовой области, помеченной
как Node. Отредактируйте содержимое по вашему усмотрению.
1. В меню Process выберите Check Content и удостоверьтесь, что отформатированный
документ сообщения появился в области статуса в нижней части окна Indri.
2. Убедитесь, что в панели Collection в левой верхней части окна Indri, выбран uddi.
3. В текстовом поле owner введите testuser и нажмите Enter.
4. В меню Database выберите Create Node. В области статуса появится сообщение "node
'nid' in collection 'uddi' created".

22.4.2. Получение деталей бизнеса


Для получения деталей о бизнесе выполните следующие действия:
1. Для очистки текстовой области Node выберите Clear Text Area из меню Database.
2. Выберите Get Node из меню Database. В текстовой области появится XML-код,
утвержденный при сохранении бизнеса.

22.4.3. Поиск бизнеса


Для того чтобы найти бизнес по имени, выполните следующие действия:
1. Для очистки текстовой области выберите Clear Text Area из меню Database.
2. Скопируйте следующую строку в поле запроса XPath. Если необходимо, замените
"Alter" строкой, которая имеется в имени сохраненного вами бизнеса.

//uddi:businessEntity/uddi:name[contains(text(),"Alter")]

Нажмите Find.
Проверьте область статуса на наличие сообщения вида:

query complete: 1 matches.

3. Если совпадений не найдено, выберите узел из панели XNodes в нижней левой части
окна Indri. Содержимое узла появится в области Node.

Web-

Rendered by www.RenderX.com
Добавление в реестр новых пользователей Стр. 615 из 626

22.4.4. Удаление бизнеса


Для того чтобы удалить бизнес выполните следующие действия:
1. Выберите бизнес, созданный вами и убедитесь, что его содержимое отобразилось в
текстовом окне Node.
2. В меню Database выберите Delete Node.
3. Для того чтобы удостовериться, что узел отсутствует, выберите Index Collection.
Удалите созданный вами узел, используя содержимое файла SaveBusiness.xml. По причине
того, что вы создали данное сообщение напрямую в базе данных, минуя его отправку в
сервер реестра и получение ответа, сообщение нуждается в бизнес-ключе и, следовательно,
будет генерировать ошибки, если для поиска вы используете JAXR API или броузер реестра.

22.4.5. Отображение содержимого базы данных


Для получения списка всех бизнесов в базе данных выполните следующие действия:
1. В панели Collection верхней левой части окна Indri выберите uddi.
2. В меню Database выберите Index Collection. В панели XNodes появится список всех
текущих узлов. Для отображения содержимого выберите нужный вам узел.

22.5. Добавление в реестр новых пользователей


Чтобы добавить нового пользователя в базу данных сервера реестра используйте сценарий
registry-server-test для генерирования хэш-пароля и запуска утилиты Indri.
1. Перейдите в директорию <JWSDP_HOME>/samples/registry-server/xml.
2. Откройте файл UserInfo.xml с помощью редактора и измените значения в тегах <fname>,
<lname>, и <uid> соответствующих фамилии, имени и уникальному ID нового
пользователя (UID). Тег <uid> обычно используется в качестве логина для пользователя.
Он должен быть уникальным.
3. Вернитесь в директорию <JWSDP_HOME>/samples/registry-server.
4. Создайте хэш-пароль для пользователя путем определения действительного пароля
(аргумент value) в следующей командной строке:

registry-server-test run-md5 -Dpassword=value

5. К примеру, если вы определили значение пароля как mypass, результат будет


следующим:

D:\jwsdp-1_0\samples\registry-server>registry-server-test run-
md5 -Dpassword=mypass
Buildfile: D:\jwsdp-1_0\samples\registry-server\test-build.xml

Web-

Rendered by www.RenderX.com
Стр. 616 из 626 Сервер реестра Java WSDP

run-md5:
[echo] -- Running md5 for auth --
[java]
[java] The Value of the MD5 Hash is: a029d0df84eb5549

6. Введите значение хэш как значение тега <passwd> в файле UserInfo.xml. Теги <token-
Expiration> или <authInfo> оставьте без изменений.
7. Сохраните файл, но не выходите из редактора.
8. Запустите Indri:

registry-server-test run-indri

9. В панели Collection, в верхней левой части окна Indri, выберите authinfo.


10. В поле Node ID введите UID, который вы определили для нового пользователя.
11. В поле владельца введите testuser.
12. В текстовом редакторе выберите и скопируйте все содержимое файла UserInfo.xml,
затем вставьте его в текстовую область Node.
13. В меню Database выберите Create Node.
14. Для того чтобы проверить, был ли создан новый пользователь, выберите Clear Text
Area из меню Database, а затем заново введите UID в поле Node ID. Появятся данные
нового пользователя. Для просмотра данных пользователя, установленного по-
умолчанию, в поле Node ID введите testuser.
15. Закройте файл UserInfo.xml и выйдите из Indri.

22.6. Дополнительная информация


Для получения дополнительной информации о реестрах UDDI, JAXR, и Web-службах
воспользуйтесь следующими ссылками:
• Universal Description, Discovery, and Integration (UDDI) project:
http://www.uddi.org/
• Домашняя страница JAXR:
http://java.sun.com/xml/jaxr/index.html
• Java Web Services Developer Pack (Java WSDP):
http://java.sun.com/webservices/webservicespack.html
• Java Technology and XML:
http://java.sun.com/xml/

Web-

Rendered by www.RenderX.com
Запуск броузера Стр. 617 из 626

• Java Technology and Web Services:


http://java.sun.com/webservices/index.html

23. Броузер реестра


Ким Хаас
Броузер реестра является одновременно, как работающим примером JAXR-клиента, так
и GUI-утилитой, позволяющей вам осуществлять поиск в реестрах и предоставлять им
данные.
Исходный код броузера реестра находится в директории <JWSDP_HOME>/samples/jaxr/jaxr-
browser . Большая часть программного кода реализует GUI-интерфейс. JAXR-код находится
в файле JAXRClient.java.
Броузер реестра позволяет осуществлять доступ к любому реестру, однако включает в
качестве настройки URL-адреса тестовых реестров сервера реестра, а так же реестров
UDDI IBM и Microsoft.

23.1. Запуск броузера


Для запуска броузера перейдите в директорию bin основной директории, в которой
установлен Java WSDP, или создайте эту директорию по нужному адресу.
Следующие команды показывают, как запустить броузер в системах UNIX и Microsoft
Windows, соответственно:

jaxr-browser.sh
jaxr-browser
Для получения доступа к серверу реестра посредством броузера перед выполнением
каких-либо запросов или других действий с броузером необходимо удостовериться в том,
что Tomcat и база данных Xindice запущены. Дополнительную информацию можно получить
из раздела Установка сервера реестра.
Для того чтобы можно было получить доступ к внешним реестрам, броузеру требуется
знать ваши установки прокси. По-умолчанию броузер использует установки, определенные
во время инсталляции Java WSDP. Они представлены в файле
<JWSDP_HOME>/conf/jwsdp.properties . Если вы хотите переопределить эти установки,
можно отредактировать этот файл или определить информацию о прокси в командной
строке броузера.
Для того чтобы использовать один прокси сервер для доступа HTTP и HTTPS, определите
новый хост прокси и его порт следующим образом. Обычно используется порт 8080.
Следующая команда показывает, как запустить броузер в системе UNIX.

jaxr-browser.sh httpHost httpPort


К примеру, если именем хоста является websys и он находится на субдомене south , вам
необходимо набрать следующее:

Web-

Rendered by www.RenderX.com
Стр. 618 из 626 Броузер реестра

jaxr-browser.sh websys.south 8080


Для использования различных прокси-серверов для доступа HTTP и HTTPS, определите
хосты и порты как показано далее (если вы не уверены, нужны ли вам два различных
сервера, определите только один, потребность в двух серверах не распространена). В
системе Microsoft Windows синтаксис выглядит следующим образом:

jaxr-browser httpHost httpPort httpsHost httpsPort


После запуска броузера введите в комбинированном окне Registry Location URL-адрес
реестра, который вы хотите использовать или выберите URL из ниспадающего меню в
комбинированном окне. Меню позволит вам выбрать между реестрами IBM и Microsoft и
сервером реестра.
Может возникнуть задержка в несколько секунд, во время которой курсор примет вид
"занято".
После того как курсор снова примет свой нормальный вид, будет установлено соединение
с указанным URL. Однако JAXR не доложит о неверном URL, пока вы не установите связь
с самим реестром и не выполните запрос или не обновите его.
В броузере содержится два окна - Browse и Submissions.

23.2. Опрос реестра


Для опроса реестра используйте окно Browse.
Примечание: Для того чтобы выполнять запросы в реестре Microsoft, необходимо связаться
с URL inquire . Для выполнения запросов в реестре IBM необходимо соединение или с
URL inquiryapi , или с URL publishapi .

23.2.1. Запрос по имени


Для поиска организаций по имени, выполните следующие действия.
1. Нажмите закладку Browse, если еще не на ней.
2. В панели Find By в левой части окна Registry Browser выполните следующее:
3.
A. Выберите Name в комбинированном окне Find By, если он еще не выделен.
B. Введите строку в текстовом поле
C. Нажмите Enter или кнопку Search в панели инструментов.
Спустя несколько секунд, организации, чьи имена совпадают с текстом, введенным вами
в строку, появятся в правой части окна Registry Browser. Если не удастся найти никакой
информации, появится информационное диалоговое окно, оповещающее об этом.
При вводе запроса регистр символов не учитывается. В реестрах IBM и Microsoft имена
организаций подходят для поиска, если они начинаются со строки, которую вы ввели. В
сервере реестра имена организаций подходят для поиска, если они содержат строку поиска.

Web-

Rendered by www.RenderX.com
Управление данными реестра Стр. 619 из 626

При двойном щелчке на названии организации отобразится дополнительная информация


о ней. Появится диалоговое окно Organization. В этом окне можно нажать Show Services,
при этом появятся диалоговое окно Services соответствующее данной организации. В
появившемся окне можно нажать Show ServiceBindings и вы увидите окно ServiceBindings
для данной службы.

23.2.2. Запрос по классификации


Для запроса реестра по классификации выполните следующие действия:
1. В комбинированном окне Find By выберите Classification.
2. В окне Classifications, появившемся ниже комбинированного окна, щелкните два раза
на схеме классификации.
3. Продолжайте щелкать кнопкой мыши, пока вы не достигнете желаемой точки поиска.
4. Нажмите кнопку Search на панели инструментов.
Спустя несколько секунд в правой части окна Registry Browser появится одна или несколько
организаций, соответствующих данному параметру классификации. Если ничего не будет
найдено, появится информационное диалоговое окно, сообщающее об этом.

23.3. Управление данными реестра


Для добавления организаций в реестр используется окно Submissions.
Для перехода в окно Submissions нажмите закладку Submissions.

23.3.1. Добавление организации


Для того чтобы добавить организацию используйте панель Organization в левой части окна
Submissions.
Используйте поля Organization Information (информация об организации) следующим
образом:
• Name: введите имя организации.
• Id: данные в этом поле не подлежат вводу или изменениям; значение ID возвращается
реестром при подтверждении данных.
• Description: Введите описание организации.
Используйте поля Primary Contact Information (первичная контактная информация)
следующим образом:
• Name: Введите имя персоны для первичного контакта с организацией.
• Phone: Введите номер телефона.
• Email: Введите e-mail адрес.
Примечание: В сервере реестра не требуется заполнение ни одного из вышеперечисленных
полей. Возможно (хотя и не рекомендуется) добавление организации и без введения
дополнительной информации. В реестрах IBM и Microsoft организации обязательно должно
быть присвоено имя.

Web-

Rendered by www.RenderX.com
Стр. 620 из 626 Броузер реестра

За получением информации о добавлении и удалении классификаций обращайтесь к


разделу Добавление и удаление классификаций.

23.3.2. Добавление информации о службах организации


Для того чтобы добавить информацию о службах конкретной организации, используйте
панель Services в правой части окна Submissions.
Для добавления службы нажмите кнопку Add Services в панели инструментов. В панели
Service появится подпанель для новой службы. Для добавления большего количества
служб нажмите кнопку Add Services необходимое число раз.
В каждой подпанели службы имеются данные компоненты:
• Поля Name, Id, и Description
• Кнопки Edit Bindings и Remove Service
• Панель Classifications
Используйте данные компоненты следующим образом:
• Name field: введите название службы.
• Id field: вы не сможете ввести или изменить данные в этом поле для поставщика JAXR
нулевого уровня.
• Description field: введите описание службы.
Для добавления к службе привязок, нажмите кнопку Edit Bindings. Появится диалоговое
окно Edit ServiceBindings. За дополнительной информацией обращайтесь к разделу
Добавление привязок к заданной службе.
• Для удаления службы из организации нажмите кнопку Remove Service. При этом
подпанель службы исчезнет с общей панели Service.
• Добавить или удалить классификации можно с помощью панели Classifications. Для
получения детальной информации обратитесь к разделу Добавление и удаление
классификаций.

23.3.3. Добавление привязок к заданной службе


Для того чтобы добавить привязку к заданной службе нажмите на кнопку Edit Bindings в
подпанели service окна Submissions. Появится диалоговое окно ServiceBindings.
Если при первом появлении диалогового окна привязок служб не существует, в нем
содержится только пустая панель Service Bindings и две кнопки: Add Binding и Done. В том
случае, когда у службы уже есть привязки, в основной панели будут находиться подпанели
для каждой из таких привязок.
Для добавления новой привязки к службе нажмите кнопку Add Binding. Если вам нужно
добавить к службе более чем одну привязку, нажмите эту кнопку необходимое число раз.
После каждого нажатия на кнопку Add Binding появится новая подпанель service binding.
В ней будут находиться три текстовых поля, а так же кнопка Remove Binding (удалить
привязку).

Web-

Rendered by www.RenderX.com
Управление данными реестра Стр. 621 из 626

Используйте текстовые поля следующим образом:


• Description: Введите описание привязки службы.
• Access URI: Введите URL, используемый для доступа к службе.
• Используйте кнопку Remove Binding для удаления привязки у данной службы.
• Для закрытия диалогового окна после удаления и добавления всех необходимых
привязок служб нажмите кнопку Done.

23.3.4. Добавление и удаление классификаций


Для добавления классификации в службу или организацию, а также для ее удаления
воспользуйтесь панелью Classifications. Панель Classifications появляется в панели Orga-
nization или подпанели service.
Для добавления классификации:
1. Нажмите Add.
2. В диалоговом окне Select Classifications сделайте двойной щелчок мышью на одной из
схем классификации.
3.
• Если вы нажали на ntis-gov:naics:1997 или на unspsc-org:unspsc:3-1, можно добавить
классификацию на любой уровень систематической иерархии. Достигнув желаемого
уровня иерархии, нажмите Add.
• Если же вы выбрали iso-ch:3166:1999 (geography), расположите соответствующий
краевой узел (страну) и нажмите Add.
Классификация появится в таблице, расположенной под кнопками в панели Classifications.
Для добавления большого числа классификаций в организацию или службу можно повторить
эту процедуру необходимое количество раз. Вы также можете выбрать схемы
классификации, нажав на клавишу control или shift, после чего нажать Add.
По окончанию работы для закрытия окна нажмите Close.
Удалить классификацию можно, выбрав соответствующую строку таблицы в панели Clas-
sifications и нажав Remove, после чего классификация исчезнет.

23.3.5. Подтверждение введенных данных


По окончанию ввода данных нажмите кнопку Submit в панели инструментов.
Появится диалоговое окно для аутентификации. Чтобы продолжить процесс подтверждения
наберите имя пользователя, пароль и нажмите OK. Чтобы закрыть окно без подтверждения
нажмите Cancel.
Если подтверждение прошло успешно, появится информационное диалоговое окно с
ключом организации. Для продолжения нажмите OK. Ключ организации также появится в
поле ID окна Submissions.
Примечание: Если вы подтвердили ввод организации, вернитесь к окну Browse, после чего
вернитесь в окно Submissions, вы обнаружите, что организация все еще там. Если снова
нажать на кнопку Submit, будет создана новая организация, независимо от того изменяли
ли вы данные организации или нет.

Web-

Rendered by www.RenderX.com
Стр. 622 из 626 Броузер реестра

23.4. Удаление организации


Чтобы удалить организацию:
1. Используйте окно Browse, чтобы обозначить организацию, которую вы желаете удалить.
2. Свяжитесь с тем URL-адресом, который позволяет вам публиковать данные. Если вы
ранее использовали URL, позволяющий выполнять только запросы, смените его на
новый - позволяющий осуществлять публикацию данных.
3. Нажмите правой кнопкой мыши на организацию и выберите Delete RegistryObject из
всплывающего меню.
4. В появившемся аутентификационном диалоговом окне введите имя пользователя,
пароль и нажмите OK. Чтобы закрыть окно, не удалив при этом организацию, нажмите
Cancel.

23.5. Завершение работы броузера


Чтобы завершить работу броузера реестра в меню File выберите Exit.

23.6. Использование броузера реестра JAXR совместно с сервером


реестра
Можно использовать броузер реестра JAXR для получения доступа к серверу реестра.
Для получения базовой информации о сервере реестра обратитесь к разделу Сервер
реестра Java WSDP.
Для получения доступа к серверу реестра выберите соответствующий URL в
комбинированном окне Registry Location (все в одной строке):

http://localhost:8080/registry-server/RegistryServerServlet
Оставьте настройки хоста как localhost , если сервер реестра находится в вашей системе.
В противном случае измените localhost на полное имя хоста системы, где запущен сервер
реестра. При неправильном вводе имени сообщение об ошибке не появится до тех пор,
пока вы не попробуете осуществить запрос или процесс обновления.
Определите http: как для запросов, так и для обновлений.

23.6.1. Добавление и удаление организаций


При добавлении или удалении организации в появившемся диалоговом окне в полях
Username и Password введите testuser (эти значения действительны до тех пор, пока вы
сами не измените имя и пароль, см. раздел Добавление новых пользователей в реестр).

23.6.2. Опрос реестра


Для опроса сервера реестра по имени введите строку в текстовом поле Name. Поисковые
системы в сервере реестра чувствительны к регистру символов. В поиске будут найдены
все организации, чьи имена содержат введенную вами строку.

Web-

Rendered by www.RenderX.com
Стр. 623 из 626

24. Утилита администрирования поставщика (Provider


Administration Tool)
Мэйдэн Фишер
Утилита администрирования поставщика является удобным средством для настройки
поставщика сообщений. Поставщик сообщений - служба третьей стороны - управляет
"закулисными" деталями маршрутов и передач JAXM-сообщений. Руководство по JAXM
представляет более подробную информацию о поставщике сообщений, которая находится
в разделе Поставщики сообщений.
Обратите внимание, что для использования утилиты администрирования поставщика
необходимо, чтобы Tomcat был запущен. Выполните следующие действия:
1. При работающем Tomcat в окне вашего броузера перейдите в

http://localhost:8080/index.html

2. Нажмите на ссылку "JAXM Provider Administration Tool"


В окне появятся текстовые поля для ввода имени и пароля. Введите имя и пароль,
определенные вами при инсталляции данной версии Java WSDP.
3. Следуйте инструкциям, появившимся на странице
Утилита администрирования поставщика обычно используется системными
администраторами, однако ей может воспользоваться любой. Изучение данного
программного обеспечения позволит вам яснее представить себе, что должен знать
поставщик сообщений. Например, поставщик сообщений поддерживает список конечных
точек, куда можно отправить сообщения. С помощью утилиты администрирования
поставщика можно добавить в этот список новые конечные точки. Если при первой попытке
сообщение не удалось доставить, поставщик сообщений будет продолжать осуществлять
новые попытки. Вы можете определить количество попыток, которые будет предпринимать
поставщик для доставки сообщения, установив значение предела повторов. Установка
данного предела - это еще одна операция, которую можно проделать с помощью утилиты
администрирования поставщика.
Далее перечислены возможности данного инструментария при настройке параметров
поставщика сообщений.
• Добавить, изменить или удалить конечную точку
• Изменить количество попыток (количество попыток отправки сообщения)
• Изменить интервал повторов (период времени между очередными попытками отправить
сообщение)
• Изменить директорию, где поставщик ведет журнал записей о сообщениях
• Установить количество сообщений в одном log-файле

Web-

Rendered by www.RenderX.com
Стр. 624 из 626 Схемы кодировок Java

25. Схемы кодировок Java


25.1. Схемы кодировок Java
В данном приложении описаны схемы кодировок символов, поддерживаемые Java-
платформой.

25.1.1. US-ASCII
US-ASCII - это 7-ми битная схема кодировки, которая включает в себя англоязычный
алфавит. Она недостаточно велика, чтобы в нее могли войти символы других языков. По
этой причине данная схема не очень полезна для интернационализации.

25.1.2. ISO-8859-1
Набор символов для западноевропейских языков. 8-ми битовая схема кодировки, в которой
каждому кодируемому символу отводится ровно 8-бит (с другой стороны, из оставшегося
набора символов некоторые коды зарезервированы для обозначения начала мульти-
байтного символа).

25.1.3. UTF-8
UTF-8 - это 8-ми битная схема кодировки. Все символы из англоязычного алфавита
кодируются с использованием 8-ми битовых байтов. Символы других языков кодируются
при помощи 2-х, 3-х или даже 4-х байтов. Следовательно, при использовании UTF-8
создаются компактные документы на английском языке, но документы на других языках
будут в полтора раза больше, чем, если бы для них использовалась схема кодировки UTF-
16. Если большинство текста в документе написано на западноевропейском языке, то
схема UTF-8, в общем-то, является хорошим выбором в качестве кодировки, так как она
позволяет расширить доступ к документу при минимальных затратах на кодирование.

25.1.4. UTF-16
UTF-16 - это 16-ти битная схема кодировки. Ее достаточно для охвата всех символов из
всех алфавитов мира. Для большинства символов этой кодировки используется по 16 бит,
однако для символов-иероглифов (например, в китайском языке) зарезервировано по 32
бита. Документы на западноевропейских языках в кодировке UTF-16 будут вдвое больше,
чем в кодировке UTF-8. Но документы на дальневосточных языках в кодировке UTF-16
будут гораздо меньше.
Примечание: UTF-16 зависит от условий организации байт в системе. И хотя в большинстве
систем в 16-битном или 32-битном слове старшие байты следуют за младшими, в некоторых
других системах используется обратный порядок. Обмен документами в кодировке UTF-
16, не подвергшихся дополнительным преобразованиям, между такими системами
недопустим.

25.2. Дополнительная информация


Полный список кодировок, поддерживаемых платформой Java 2, можно найти в документе:
http://java.sun.com/j2se/1.3/docs/guide/intl/encoding.doc.html

Web-

Rendered by www.RenderX.com
Запросы HTTP Стр. 625 из 626

26. Обзор HTTP


Стефани Бодофф
Большинство Web-клиентов используют протокол HTTP для связи с J2EE-сервером. HTTP
определяет запросы, которые клиент может посылать серверу и ответы, которые сервер
может посылать в ответ. Каждый запрос содержит URL, который является строкой,
определяющей Web-компонент или статический объект, к примеру, страницу HTML или
файл с изображением.
J2EE сервер преобразует запрос HTTP в объект запроса HTTP и доставляет его Web-
компоненту, определенному с помощью URL запроса. Web-компонент заполняет объект
запроса HTTP, который сервер преобразует в ответ HTTP, и отправляет его клиенту.
Данное приложение представляет собой некоторый вводный материал по протоколу HTTP.
Для получения более детальной информации по данному протоколу обращайтесь к
документам RFC: HTTP/1.0 - RFC 1945, HTTP/1.1 - RFC 2616, которые находятся на сайте
http://www.rfc-editor.org/rfc.html.

26.1. Запросы HTTP


Запрос HTTP включает в себя метод запроса, URL запроса, поля заголовка и тело. HTTP
1.1 определяет следующие методы запроса:
• GET - извлекает ресурс, определенный при помощи URL запроса.
• HEAD - возвращает заголовки, определенные при помощи URL запроса.
• POST - отправляет данные (без ограничений по размеру) Web-серверу.
• PUT - хранит ресурс в URL запроса.
• DELETE - удаляет ресурс, определенный при помощи URL запроса.
• OPTIONS - возвращает методы HTTP, поддерживаемые сервером.
• TRACE - возвращает поля заголовка, отправленные с запросом TRACE.
HTTP 1.0 включает в себя только методы GET, HEAD и POST. И хотя для поддержки HTTP
1.0 необходимы только серверы J2EE, на практике многие серверы, включая Java WSDP,
поддерживают HTTP 1.1.

26.2. Ответы HTTP


Ответ HTTP состоит из результирующего кода, полей заголовка и тела.
Протокол HTTP ожидает возврат результирующего кода и всех полей заголовка до возврата
содержимого тела документа.
Далее представлены несколько часто используемых кодов состояния:
• 404 - указывает на то, что запрошенный ресурс недоступен.
• 401 - указывает на то, что запрос требует аутентификации HTTP.
• 500 - указывает на ошибку HTTP-сервера, которая мешает выполнить запрос

Web-

Rendered by www.RenderX.com
Стр. 626 из 626 Обзор HTTP

• 503 - указывает на то, что HTTP-сервер временно перегружен и не может обработать


запрос.

Web-

Rendered by www.RenderX.com

Вам также может понравиться