Сообщений 0 Оценка 10 Оценить |
Взаимодействие с СУБД – важная и существенная часть большинства приложений, и не только в архитектуре клиент-сервер. Использование API для взаимодействия с СУБД характерно и для серверов приложений (даже в случае использования компонентной модели EJB), и для клиентских приложений – для временного хранения данных на стороне клиента, например, в случае невозможности поддержания непрерывного соединения с сервером.
Как и любая другая Java-ориентированная технология, J2EE в качестве основного инструмента взаимодействия с РСУБД полагается на JDBC, точнее, JDBC версии 2.1 и выше.
Для понимания механизма взаимодействия WAS CE c СУБД необходимо знать основы JDBC.
То, что JDBC построен по драйверному принципу, известно всем. К сожалению, те фундаментальные возможности JDBC, которые и позволяют этой технологии успешно взаимодействовать с другими технологиями в составе J2EE, знакомы немногим. Причина проста – эти возможности редко «попадаются на глаза». Как говорилось в эпиграфе к одной из глав книги М. Твена «Простофиля Вильсон», «на поверхности он бесполезен. Он должен находиться под землей и вдохновлять капусту».
JDBC 1.0 поддерживал только физические соединения с серверами БД. Это означало, что установка и разрыв соединения были связаны с существенными затратами ресурсов. Кроме того, не поддерживалось взаимодействие с внешними координаторами объектных транзакций – JDBC 1.0 поддерживал только локальные транзакции – транзакции на уровне одного физического соединения с БД. Наконец, менеджер соединений в JDBC 1.0 невозможно было сделать доступным для других элементов распределенной системы с помощью JNDI. Другими словами, JDBC 1.0 справлялся с задачами, характерными для архитектуры «клиент-сервер», но не удовлетворял требованиям, предъявляемым многозвенными системами.
Ответом на эти требования стала разработка стандарта JDBC 2.0 (точнее, JDBC 2.1). Основными ключевыми изменениями были следующие: менеджер соединений (интерфейс DataSource и производные от него интерфейсы) можно опубликовывать и, соответственно, получать с помощью JNDI и, самое главное, соответствующий JDBC-драйвер должен уметь поддерживать пулы соединений с СУБД, а также сопоставлять соединение с объектной распределенной (глобальной) транзакцией.
Несмотря на возможности, предоставляемые современными JDBC-драйверами, разработчикам J2EE для решения ряда задач требуется поддерживать более тесное взаимодействие с соединениями БД, нежели позволяет JDBC. Примером может служить необходимость координации глобальных (на уровне координатора объектных транзакций распределенной системы) и локальных (на уровне соединения JDBC) транзакций. Поскольку программист может самостоятельно вызывать методы API JDBC, в том числе и методы управления локальными транзакциями (такими, как setAutoCommit() или commit()), возникает возможность рассогласованного поведения при наличии глобальных транзакций. Спецификация J2EE требует, чтобы реализация предоставила возможность управления локальными транзакциями при наличии глобальных, а для этого нужно иметь возможность перехвата на уровне контейнера EJB-компонентов явного – и ошибочного – вызова разработчиком методов управления локальными транзакциями. Кроме того, драйвера JDBC 1.0 еще недавно использовались очень широко, и нужно было предусмотреть возможность их использования при работе J2EE-серверов приложений.
Был найден следующий выход: создание и поддержка пула соединений, выбор нужного вида соединения, сопоставление соединения с контекстом глобальной транзакции в большинстве случаев выполняется не JDBC-драйвером, а специальным программным модулем в составе самого J2EE-сервера приложений. Собственно драйвер используется тогда, когда нужно получить физическое соединение с СУБД, а также для поддержки распределенных транзакций (XA-транзакций).
Поскольку логика работы с пулом соединений в общем случае зависит не от вида базы данных, а от поддерживаемых видов транзакций и соединений, то возможно создать две универсальные реализации такого пула – для локальных и глобальных транзакций. Тем не менее, поскольку каждая СУБД имеет свои особенности, можно для повышения эффективности в случае, если вид СУБД известен, использовать реализацию пула, оптимизированную для работы именно с этой СУБД.
Как следствие, в состав дистрибутива WAS CE 1.0.1 входит несколько реализаций пула соединений и управления транзакциями. Работа со всеми этими реализациями происходит одинаково, поэтому в примерах мы будем использовать универсальную реализацию для поддержки локальных транзакций.
Эта реализация выполнена в виде стандартного ресурсного адаптера J2EE – модуля с расширением RAR. Этот файл имеет имя tranql-connector-1.1.rar и находится (вместе с другими реализациями пула соединений) в каталоге <install_dir>\repository\tranql\rars. Его развертывание на сервере, как и любого другого модуля, требует наличия xml-дескриптора. В этом xml-дескрипторе и указываются все необходимые для нормальной работы пула параметры.
Первый вопрос, на который должен дать ответ разработчик приложения, взаимодействующего с БД, таков: какова должна быть «область видимости» создаваемого пула соединений?
При разработке серверных приложений возможны два ответа:
Создание клиентских приложений для WAS CE – тема для отдельной статьи.
Выбор одного из этих двух вариантов никак не связан с решением проблем безопасности. Это просто выбор той из стратегий, которая наиболее адекватна поставленной задаче, и которую удобнее использовать в том или ином конкретном случае. Основные отличия связаны с самой процедурой создания и регистрации пула. Кроме того, в случае создания пула на уровне приложения он существует только во время работы этого приложения.
Для нормальной работы с БД с использованием WAS CE необходимо иметь соответствующий JDBC-драйвер. JDBC-драйвера бывают разных типов – написанные на Java или с использованием native-кода, поддерживающие двухфазные (XA) транзакции и нет, обеспечивающие сетевое соединение с СУБД или требующие для работы дополнительного ПО. Сейчас в большинстве случаев используются JDBC-драйверы так называемого четвертого типа. Они поставляются в виде jar-файлов.
WAS CE содержит JDBC-драйвера для большого количества распространенных СУБД. В настоящий момент WAS CE «понимает» – т.е. либо содержит нужный драйвер, либо обеспечивает помощь в его поиске, загрузке и настройке – следующие наиболее популярные типы СУБД (это не полный список):
Разработчик может использовать доступные ODBC-драйвера (через мост JDBC-ODBC).
Кроме того, в состав WAS CE входит встроенная СУБД Derby (это клон БД Cloudscape), которая создавалась в рамках open-source проекта.
Многие из перечисленных СУБД поддерживаются как в локальном, так и в XA-режиме, т.е. режиме использования распределенных транзакций.
Несколько слов о поддержке встроенной БД Derby. Она используется сервисами самой WAS CE и готова к работе сразу после установки WAS CE. Derby поддерживается в различных режимах, в том числе режиме использования XA-транзакций. Поскольку она играет особую роль, консоль администратора в панели навигации (левая часть консоли) содержит специальный пункт – «Embedded DB». С помощью средств консоли («DBManager») пользователь может создавать новые БД, интерактивно создавать и выполнять SQL-операторы для выбранной БД Derby, а также получить список параметров настройки этой СУБД («DB Info»).
В приведенном в этой статье примере приложения будет использоваться не Derby, а бесплатная версия СУБД DB2 – DB2 Express C, которую можно бесплатно скачать с сайта IBM (http://www-306.ibm.com/software/data/db2/ udb/db2express/download.html).
JDBC-драйверы (их jar-файлы) должны находиться в каталоге repository (точнее, одном из его подкаталогов) каталога установки WAS CE. Например, JDBC-драйвер для Derby находится в каталоге <install_dir> \repository\org.apache.derby\jars, а драйвер для DB2 ExpressC – в каталоге <install_dir> \repository\com.ibm.db2\ jars.
Если разработчик хочет для создания и настройки использовать JDBC-драйвер, не входящий в комплект поставки WAS CE, то он должен поместить этот драйвер в подкаталог каталога \repository. Обратите внимание, что эксперты консоли администратора WAS CE полагаются на определенное соглашение об именах для jar-файлов. В частности, имя jar-файла обязательно должно содержать номер версии и подверсии драйвера. Например, jar-файл для драйвера DB2 ExpressC имеет имя db2jcc-8.2.jar, а jar-файл для одного из Derby-драйверов – derby-10.1.2.ibm.jar, причем номер версии отделяется от имени дефисом.
Рисунок 1.
Рисунок 2.
Если нужно создать пул соединений уровня сервера – т.е. пул, доступный всем приложениям этого сервера – то наиболее удобно сделать это с помощью консоли администратора, которая содержит специального мастера (wizard) для решения этой задачи. Чтобы вызвать его, в панели навигации консоли нужно выбрать пункт «Database Pools». Можно либо создавать пул соединений «с нуля» (мастер «Using the Geronimo database pool wizard»), либо импортировать готовый пул, созданный при работе с другими серверами – JBoss или Weblogic. В данной статье мы будем создавать пул соединений с DB2 ExpressC.
На первом шаге при использовании данного мастера разработчик должен выбрать имя создаваемого пула и тип используемой БД (рисунок 1).
Имя пула должно быть уникальным на сервере среди имен всех других ресурсов. Это имя появится в специфическом для WAS CE дескрипторе развертывания – там, где необходимо будет сопоставить выбранное разработчиком JNDI-имя и имя реального ресурса.
После выбора одного из поддерживаемых мастером типов БД нужно перейти к следующему этапу (рисунок 2).
Имя класса драйвера определяет сам мастер, хотя пользователь может его изменить «вручную». Jar-файл, содержащий код драйвера, нужно выбрать из списка. Если в этом списке нет соответствующего драйвера, можно попробовать найти его и загрузить с помощью мастеров консоли (кнопка «Download a driver»).
Дальнейшие параметры зависят от того, как был сконфигурирован установленный экземпляр СУБД, к которому выполняется обращение.
Наконец, на последнем шаге работы с мастером разработчик окончательно формирует нужный URL (URL формируется на основе ранее введенных данных, но здесь его можно изменить) и задает параметры – минимальный (0 по умолчанию) и максимальный (10 по умолчанию) размер пула, время ожидания (в мсек, 5000 по умолчанию) до возбуждения исключения в случае, когда пользователь запрашивает соединение, но свободных соединений в пуле нет, и время ожидания (в минутах, 15 по умолчанию) до удаления из пула неиспользуемого соединения и уменьшения его текущего размера.
После задания всех параметров имеет смысл проверить настройку пула – установить соединение с СУБД (кнопка «Test Connection»). Если все хорошо, появится страница с соответствующей информацией (рисунок 3).
Теперь можно выполнить развертывание созданного пула на сервере (кнопка «Deploy» на шаге 4 или кнопка «Skip test and deploy» на предыдущем шаге). Полезной – особенно при создании пула «вручную», о чем речь пойдет в следующем разделе – может оказаться кнопка «Show Plan» (или «Skip Test an Show Plan» на предыдущем шаге), которая приводит к отображению сгенерированного xml-дескриптора для развертываемого коннектора.
Рисунок 3.
Термин «вручную» не надо понимать буквально – с учетом возможностей, предоставляемых мастерами консоли администратора. Здесь имеется в виду создание xml-файла, содержащего текст дескриптора, и некоторые манипуляции с этим файлом.
Для создания пула соединений уровня сервера приложений или корректировки параметров существующего пула всегда имеет смысл использовать консоль оператора – при этом в явном создании xml-файла дескриптора нет необходимости. Разумеется, даже при создании пула уровня сервера можно поступить иначе – создать xml-дескриптор, а затем с помощью командной строки развернуть пул на сервере. Этот подход может быть удобен, например, при использовании нетривиальных командных файлов. Командная строка для выполнения такого развертывания может выглядеть так:
java -jar bin/deployer.jar deploy <имя_файла_дескриптора>.xml \ repository/tranql/rars/tranql-connector-1.1.rar |
Данный вид командной строки подразумевает, что текущим каталогом является каталог установки WAS CE.
Явное создание xml-файла дескриптора и размещение его в EAR-архиве необходимо в случае создания пула соединений на уровне J2EE-приложения, а не сервера. Для создания такого пула нужно выполнить следующие действия:
<application xmlns="http://geronimo.apache.org/xml/ns/j2ee/application-1.0" configId="MyApplication"> <module> <connector>имя_RAR_файла.rar</connector> <alt-dd>имя_XML_дескриптора.xml</alt-dd> </module> </application> |
Теперь осталось только ознакомиться с кодом дескриптора пула соединений (его вид не зависит от области видимости пула – на уровне сервера или на уровне приложения).
Рисунок 4.
Ниже приведен дескриптор, сгенерированный мастером консоли администратора для параметров, которые были указаны при интерактивной работе с консолью.
<?xml version="1.0" encoding="UTF-8"?> <connector configId="console-db-pool-MyDatabaseSource" xmlns="http://geronimo.apache.org/xml/ns/j2ee/connector-1.0"> <dep:dependency xmlns:dep="http://geronimo.apache.org/xml/ns/deployment-1.0"> <dep:uri>com.ibm.db2/db2jcc/8.2/jar</dep:uri> </dep:dependency> <resourceadapter> <outbound-resourceadapter> <connection-definition> <connectionfactory-interface> javax.sql.DataSource </connectionfactory-interface> <connectiondefinition-instance> <name> MyDatabaseSource </name> <config-property-setting name="Password"> adminpassword </config-property-setting> <config-property-setting name="Driver"> com.ibm.db2.jcc.DB2Driver </config-property-setting> <config-property-setting name="UserName"> db2admin </config-property-setting> <config-property-setting name="ConnectionURL"> jdbc:db2://localhost:50000/MYDEMODB </config-property-setting> <connectionmanager> <local-transaction/> <single-pool> <max-size>10</max-size> <min-size>0</min-size> <match-one/> </single-pool> </connectionmanager> </connectiondefinition-instance> </connection-definition> </outbound-resourceadapter> </resourceadapter> </connector> |
Обратить внимание стоит на следующее:
<connectionmanager> ... <single-pool> <max-size>10</max-size> <min-size>0</min-size> ... <blocking-timeout-milliseconds> 10000 </blocking-timeout-milliseconds> <idle-timeout-minutes> 30 </idle-timeout-minutes> </single-pool> </connectionmanager> |
<dep:dependency xmlns:dep="http://geronimo.apache.org/xml/ns/deployment-1.0">
<dep:uri>com.ibm.db2/db2jcc/8.2/jar</dep:uri>
</dep:dependency>
|
Обратите внимание на формат URI, а также на то, что относительный путь к файлу ресурса указан относительно подкаталога repository каталога установки WAS CE.
На формате тега <dependency> стоит остановиться подробнее.
WAS CE позволяет указывать нужный ресурс (jar-файл) двумя различными способами. Обычно используется тег <uri>, который позволяет указать ссылку в виде строки. Тем не менее, в общем виде ссылка состоит из четырех компонентов, которые имеют имена groupId, artifactId, version и type. Такие же имена имеют теги для задания зависимости во втором поддерживаемом формате.
Возьмем для примера строку, задающую URI для требуемого jar-файла JDBC-драйвера:
com.ibm.db2/db2jcc/8.2/jar |
Этот URI «расшифровывается» (как обычный каталог) следующим образом:
<install_dir>/repository/com.ibm.db2/jars/db2jcc-8.2.jar |
Если строку URI представить в виде набора компонентов, то она будет иметь следующий вид:
groupId/artifactId/version/type |
Соответственно, компоненты имеют следующие значения:
Тег <dependency> в нашем примере можно было записать и так:
<dep:dependency xmlns:dep="http://geronimo.apache.org/xml/ns/deployment-1.0">
<dep:groupId>com.ibm.db2</dep:groupId>
<dep:artifactId>db2jcc</dep:artifactId>
<dep:version>8.2</dep:version>
<dep:type>jar</dep:type>
</dep:dependency>
|
Тег <type> со значением “jar” можно не указывать – это значение по умолчанию.
Этой информации вполне достаточно, чтобы создавать пулы соединений.
Поскольку пулы соединений используются только для того, чтобы J2EE-приложения того или иного вида могли их использовать, важнейшим элементом приложения, использующего БД, является установление связи между пулом и приложением.
Идеология создания J2EE-приложений требует, чтобы такая взаимосвязь создавалась не на этапе написания программы, а на уровне настройки уже написанного приложения для конкретного заказчика. Другими словами, программист, пишущий Java-код, не может и не должен знать, с каким видом СУБД должны взаимодействовать создаваемые им компоненты, или где находятся сервера БД. Программист работает с высокоуровневой моделью взаимодействия с СУБД: он получает (обычно с помощью JNDI) доступ к фабрике соединений, роль которой в JDBC играет интерфейс DataSource и производные от него интерфейсы, а затем – с помощью вызова метода getConnection() – устанавливает логическое соединение с сервером. Затем соединение используется для задания команд работы с данными. В JDBC для этого применяется SQL. После того, как соединение использовано, программист логически разрывает его. Все остальные вопросы программиста интересовать не должны. Java-код не содержит никакой информации, относящейся к собственно СУБД, и изменения типа СУБД или URL ее используемого экземпляра не должно приводить к необходимости перекомпиляции Java-кода приложения.
Спецификация J2EE явно говорит, что настройкой должен заниматься не программист, а специалист, реализующий J2EE-роль под названием «deployer». Deployer не имеет и не должен иметь доступ к Java-коду. Вместо этого он работает с xml-дескрипторами модулей, из которых строится J2EE-приложение.
Обычно обращение к пулам соединений с БД выполняются из модулей, которые содержат Web- или EJB-компоненты. Пул соединений трактуется как ресурс J2EE, что приводит к использованию тега <resource-ref> в стандартных xml-дескрипторах.
Вместе с тем нужно отчетливо понимать, что информации в дескрипторах, структура которых задана J2EE-спецификациями, недостаточно для нормальной работы.
Начнем со стандартного дескриптора и тега <resource-ref>:
<?xml version="1.0" encoding="ISO-8859-1"?> <web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4"> <resource-ref> <res-ref-name>jdbc/MySource</res-ref-name> <res-type>javax.sql.DataSource</res-type> <res-auth>Container</res-auth> <res-sharing-scope>Shareable</res-sharing-scope> </resource-ref> </web-app> |
Тег <resource-ref> является основным универсальным тегом для обращения к ресурсам, наиболее употребительными видами которых являются фабрики соединений JDBC и JMS. Тип ресурса определяется значением тега <res-type>. Для пула соединений JDBC всегда нужно указывать приведенное в листинге значение.
Наиболее интересным параметром является значение тега <res-ref-name>. Это имя, под которым ресурс зарегистрирован в службе имен JNDI.
По поводу реализации JNDI в WAS CE необходимо сказать несколько слов. Хранилищем информации в JNDI являются объекты, называемые «контекстами». Ближайшим аналогом контекста JNDI является каталог файловой системы.
Спецификации J2EE оговаривают два вида контекстов JNDI – локальные и глобальные. Кратко их отличие друг от друга можно описать так: в локальный контекст помещается определенная информация из xml-дескрипторов, в то время как глобальный контекст предназначен для хранения произвольной информации, для чего используется универсальный API. Реализация JNDI в WAS CE не поддерживает глобальные контексты (хотя в состав WAS CE входят и другие реализации служб имен). Все контексты являются локальными. Спецификация J2EE вводит фиксированный «начальный» локальный контекст с именем «java:comp/env». Имя, указываемое в теге <res-ref-name>, является именем ресурса в этом стандартном локальном контексте, т.е. полное имя нашего ресурса в JNDI относительно начального контекста службы имен будет «java:comp/env/jdbc/MySource». Часть имени “jdbc” задавать необязательно, хотя это и принятый среди J2EE-разработчиков способ задания имени пула соединений (точнее, интерфейса dataSource).
Тег <res-auth> позволяет задать режим аутентификации пользователя при попытке установки соединения с БД. Значение «Container» говорит, что аутентификацию будет выполнять контейнер на основе предоставленных ему идентификатора и пароля (или сертификата) пользователя (например, реквизиты пользователя указывались в дескрипторе для пула соединений с выбранной СУБД). Другое возможное значение – «Application» – говорит о том, что параметры пользователя должны явно указываться на уровне Java-кода при обращении к фабрике соединений с СУБД.
Наконец, тег <res-sharing-scope> нужен для задания поведения в случае, если несколько модулей обращаются к одному и тому же ресурсу (в нашем случае – пулу соединений с БД) в контексте одной глобальной транзакции. Значение “Shareable” позволяет этим модулям использовать одно и то же соединение. Если указано значение «Unshareable», то каждому модулю будет предоставлено отдельное соединение. Это, в частности, означает, что может возникнуть необходимость в поддержке двухфазных (XA) транзакций.
Это все, что позволяет задать стандартный дескриптор J2EE. О том, каким именно образом устанавливается соответствие между JNDI-именем и реальным ресурсом, спецификация J2EE не говорит ничего. Этот момент зависит от реализации, и именно поэтому (в том числе) возникает необходимость использования дополнительных, специфических для реализации J2EE-сервера дескрипторов.
Рассмотрим следующий специфический дескриптор, именуемый в WAS CE «планом развертывания» («deployment plan»).
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://geronimo.apache.org/xml/ns/web" xmlns:naming="http://geronimo.apache.org/xml/ns/naming" configId="webdb_app"> <context-root>/webdb</context-root> <context-priority-classloader>false</context-priority-classloader> <naming:resource-ref> <naming:ref-name>jdbc/MySource</naming:ref-name> <naming:resource-link>MyDatabaseSource</naming:resource-link> </naming:resource-ref> </web-app> |
Здесь нас пока интересует только тег <resource-ref>.
WAS CE поддерживает несколько форматов ссылки на ресурс (рисунок 5).
Рисунок 5.
Всегда необходимо указывать значение тега <ref-name>. Это значение задает JNDI-имя в основном дескрипторе, с которым сопоставляется конкретный ресурс. Далее возможны варианты.
Самый простой и распространенный из них – использование тега <resource-link>. Значение тега должно соответствовать уникальному имени ресурса в одном из модулей, который представляет собой коннектор Java и который был развернут на данном сервере (соответствие устанавливается по значению тега <connectiondefinition-instance/name> коннектора).
Если этот способ использовать невозможно или нежелательно, предусмотрены другие варианты. Два из них (теги <objectNameGroup> и <target-name>) схожи друг с другом. Оба они основаны на идентификации ресурса на уровне компонентов GBeans и их конфигураций, а не на уровне J2EE.
Формат значения тега <target-name> соответствует требованиям спецификации JSR-77. В одной из последующих статей будет рассмотрено использование JMX в WAS CE, после чего эти форматы станут понятны.
Теги <context-root> и <context-priority-classloader> касаются вопросов создания web-приложений и будут подробно рассмотрены в следующей статье.
Осталось сказать немного об имени файла, который должен содержать код дополнительного дескриптора. В абсолютном большинстве случаев используется фиксированное имя файла дескриптора, и этот файл включается в стандартный каталог соответствующего J2EE-модуля. Например, для web-приложений таким стандартным каталогом WAR-архива является каталог WEB-INF, а стандартный и дополнительный дескрипторы имеют имена web.xml и geronimo-web.xml, соответственно.
Альтернативные способы задания имени дополнительного дескриптора и размещения его в архиве приложения будут рассмотрены в следующей статье, посвященной созданию Web-приложений для WAS CE.
Использование развернутого на сервере пула выполняется стандартным для J2EE образом – программист по имени, заданному в xml-дескрипторе модуля приложения (в нашем примере использовался web-модуль), получает доступ к фабрике соединений, затем вызывает метод getConnection() и использует API JDBC для работы с данными:
... Context envContext = (Context)initContext.lookup("java:comp/env"); DataSource ds = (DataSource) envContext.lookup("jdbc/MySource"); Connection con = ds.getConnection(); ... con.close(); |
WAS CE предоставляет разработчикам гибкий механизм для взаимодействия со всеми наиболее распространенными реляционными СУБД с поддержкой как однофазных, так и двухфазных транзакций.
Сообщений 0 Оценка 10 Оценить |