Здравствуйте, подскажите пожалуйста где можно достать материалы по курсу Кросс-платформенные и многозвенные технологии, о которых говориться, к примеру, в Лекции 2. Пример "Служба мгновенных сообщений" |
Технология CORBA
Пример "Книжный магазин "BookStore""
Общее описание
Разработаем систему заказов для книжного магазина, позволяющую:
- обновлять и корректировать информацию о книгах, хранящуюся в базе данных (добавление новых книг, изменение цен и т.д.);
- выполнять заказы клиентов и информировать их о произошедших изменениях;
- обновлять информацию о книгах при ее изменении в базе данных.
Создание таблиц в базе данных
Следующий код создает данную таблицу:
create table BookStore ( id int primary key, name varchar(255), author varchar(255), publisher varchar(255), price float );
Интерфейс системы
Для связи СОRBA -объектов необходимо задать IDL -интерфейс, позволяющий выполнять запросы клиента и обеспечивать доступ к книгам и их модификацию.
struct Book { long id; string name; string author; string publisher; float price; } typedef sequence <Book> BookList; interface User { BookList getBookList (); string getAdmin (); // ior }; interface Admin { long updateBook (in Book book ); };
Структура Book позволяет описывать и хранить информацию о книге. Интерфейс User имеет два метода:
- BookList getBookList (), данный метод возвращает список имеющихся на сервере книг, их названия, цену и прочую информацию;
- string getAdmin (), данный метод возвращает строковое представление ior объекта для получения доступа к интерфейсу Admin.
Интерфейс Admin имеет единственный метод updateBook позволяющий добавлять новые книги и редактировать информацию о них.
Обработка IDL-файла
После описания IDL -интерфейс, необходимо сгенерировать вспомогательные классы для целевого языка, позволяющие реализовать методы интерфейса и предоставить их во внешнее пользование.
Для клиента, написанного на Java,используется ORB из стандартной поставки JDK.
idlj.exe -fall Server.idl
Опция -fall говорит приложению сгенерировать помимо оберток над интерфейсами, необходимыми для использования чужих IDL -интерфейсов, шаблоны для реализации сервантов.
Для сервера, написанного на C++,используется ORB из библиотеки ACE/TAO,соответственно, для генерации вспомогательных классов используется специальное приложение:
tao_idl.exe -GI Server.idl
Опция -GI позволяет сгенерировать дополнительные ServerI.h/.cpp файлы, в которых описан каркас реализации серванта, и остается лишь реализовать его методы. Полный список сгенерированных файлов при запуске указанной команды:
- ServerC. h/.cpp - файлы, предоставляющие доступ к IDL -интерфейсам (аналогичные файлы используются в клиенте, реализованном на Java);
- ServerS. h/. cpp - файлы, описывающие абстрактные классы необходимые для реализации сервантов;
- ServerI.h/.cpp - каркас реализации серванта.
Получение доступа к CORBA-интерфейсу
Клиенты, посылающие запросы к серверу реализованы на языке Java.В качестве ORB взят стандартный ORB, входящий в Java SDK.
Для получения доступа к интерфейсу сервера, клиенту необходимо выполнить следующие действия.
- Инициализация ORB и активизация POA -менеджера
// create and initialize the ORB ORB orb = ORB.init(args, null); POA rootpoa = (POA)orb.resolve_initial_references("RootPOA"); rootpoa.the_POAManager().activate();
- Получение доступа к объекту сервиса имен
// get the root naming context org.omg.CORBA.Object objRef = orb.resolve initial references("NameService"); // Use NamingContextExt instead of NamingContext. This is // part of the Interoperable naming Service. NamingContextExt ncRef = NamingContextExtHelper.narrow(objRef);
- Получение доступа к интерфейсу пользователя с помощью сервиса имен
String name = "BookStore.User"; corbaUser = UserHelper.narrow(ncRef.resolve_str(name));
- Получение доступа к интерфейсу администратора с помощью значения ior
String adminlor = corbaUser.getAdmin(); org.omg.CORBA.Object adminObj = orb.string_to_object(adminlor); corbaAdmin = AdminHelper.narrow(adminObj);
Обратная связь. Сервис событий
При изменении данных книги, сервер должен оповестить клиентов. Для этого можно использовать сервис событий (сервис EventService,входящий с состав пакета ACE/TAO).Для запуска сервиса событий необходимо выполнить следующие действия.
- Получение доступа к объекту сервиса событий
CORBA::Object var eventServiceObj = namingContext->resolve str("CosEventService"); EventChannel_var eventChannel = EventChannel::_narrow(eventServiceObj.in());
- Активизация сервиса и получение объекта поставщика событий
SupplierAdmin_var supplierAdmin = eventChannel->for_suppliers(); // Get a ProxyPushConsumer from the SupplierAdmin. ProxyPushConsumer_var consumer =supplierAdmin->obtain_push_consumer(); // Connect to the ProxyPushConsumer as a PushSupplier // (passing a nil PushSupplier object reference to it because // we don't care to be notified about disconnects). consumer->connect_push_supplier( CosEventComm::PushSupplier::_nil());
- Для генерации события необходимо создать объект события и передать его поставщику событий:
const CORBA::String var notificationMsg = CORBA::string_dup("BookStore.Notify"); CORBA::Any any; any <<= notificationMsg; consumer->push(any);
Обработка событий
На стороне клиента необходимо зарегистрировать обработчик событий. Для этого достаточно реализовать вспомогательный класс (в данном проекте это класс EventNotify ), унаследованный от базового класса - обработчика событий CosEventComm.PushConsumerPOA и реализующий методы, необходимые для обработки поступающих сообщений. Для регистрации обработчика событий необходимо:
- Получить доступ к объекту сервиса событий
// Find the EchoEventChannel. org.omg.CORBA.Object eventServiceObj = ncRef.resolve str("CosEventService"); EventChannel echoEC =EventChannelHelper.narrow(eventServiceObj);
- Создать обработчик событий
// Instantiate an EchoEventConsumer_i servant. notifier = new BookStore.Server.EventNotify(orb, this); // Register it with the RootPOA. byte [] oid = rootpoa.activate object(notifier); org.omg.CORBA.Object consumer_obj =rootpoa.id_to_reference(oid); CosEventComm.PushConsumer consumer = CosEventComm.PushConsumerHelper.narrow(consumer_obj);
- Зарегистрировать обработчик событий
// Get a ConsumerAdmin object from the EventChannel. CosEventChannelAdmin.ConsumerAdmin consumerAdmin =echoEC.for_consumers(); // Get a ProxyPushSupplier from the ConsumerAdmin. CosEventChannelAdmin.ProxyPushSupplier supplier = consumerAdmin.obtain_push_supplier(); // Connect to the ProxyPushSupplier, passing our PushConsumer // object reference to it. supplier.connect_push_consumer(consumer);
Связь с базой данных
Для работы с базой данных сервер BookStore использует ODBC (Open Database Connectivity) драйвер. Покажем работу сервера с базой данных на примере запроса получения списка книг.
- Инициализация драйвера и окружения ODBC
SQLHENV henv; SQLAllocHandle(SQL_HANDLE_ENV, NULL, &henv); // tell to use 3rd ODBC version SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, SQL_IS_INTEGER); SQLHDBC hdbc; SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
- Соединение с базой данных
SQLDriverConnect(hdbc, NULL, (SQLCHAR*)dsn, SQL_NTS, outStr, MAX_NUM, &outStrLen, SQL_DRIVER_COMPLETE);
При соединении указывается DSN (Data Source Name) - имя источника данных, установленных на компьютере, либо специфичные для конкретного драйвера параметры соединения.
- Отправка запроса
SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt); static SQLCHAR querySql[] = "select id, name, author, publisher, price from BookStore"; SQLExecDirect(hstmt, querySql, SQL_NTS);
- Обработка результатов запроса
SQLBindCol(hstmt, i + 1, SQL_C_SLONG, (SQLPOINTER)&uid, sizeof(uid)); SQLBindCol(hstmt, i + 1, SQL_C_CHAR, (SQLPOINTER)name, MAXLEN); SQLBindCol(hstmt, i + 1, SQL_C_CHAR,(SQLPOINTER)author, MAXLEN); SQLBindCol(hstmt, i + 1, SQL_C_CHAR, (SQLPOINTER)publisher, MAXLEN); SQLBindCol(hstmt, i + 1, SQL_C_DOUBLE, (SQLPOINTER)&price, sizeof(price)); for (;;) { if (SQLFetch(hstmt) == SQL_NO_DATA) { break; } // process book info }
- Завершение обработки запроса
SQLCloseCursor(hstmt);
Обратная связь: Oracle AQ
- Создадим пользователя с правами на управление AQ
create user BookAdmin identified by "12345" default tablespace USERS temporary tablespace TEMP; grant CONNECT, RESOURCE, CREATE SESSION, aq_administrator_role TO bookadmin; grant EXECUTE on SYS.DBMS_AQ to BookAdmin; exec DBMS_AQADM.GRANT_SYSTEM_PRIVILEGE(privilege => 'ENQUEUE_ANY', grantee =>'BookAdmin', admin option => FALSE); exec DBMS_AQADM.GRANT_SYSTEM_PRIVILEGE(privilege => 'DEQUEUE_ANY', grantee =>'BookAdmin', admin option => FALSE);
- Зайдем в Oracle под этим пользователем
connect BookAdmin/12345;
- Создадим очередь событий
create type EventType as object(type int, text varchar2(2000)); exec DBMS_AQADM.CREATE_QUEUE_TABLE(queue_table => 'EventTable', queue payload type => 'BookAdmin.EventType'); exec DBMS_AQADM.CREATE_QUEUE(queue_name => 'EventQueue', queue_table => 'EventTable'); exec DBMS_AQADM.START_QUEUE(queue_name => 'EventQueue');
- Создадим триггер, помещающий событие в очередь при добавлении в таблицу
create or replace trigger inserttrigger after insert on BookStore declare queue_options DBMS_AQ.ENQUEUE_OPTIONS_T; message_properties DBMS_AQ.MESSAGE_PROPERTIES_T; message id RAW(16); my_message BookAdmin.EventType; begin my_message := BookAdmin.EventType(1, 'insert'); DBMS_AQ.ENQUEUE( queue_name => 'BookAdmin.EventQueue', enqueue_options => queue_options, message_properties => message_properties, payload => my_message, msgid => message_id); end;
- Сгенерируем Java имплементацию для типа BookAdmin.EventType
set ORA_HOME=C:\oracle\ora92\ set CLASSPATH=%ORA_HOME%jdbc\lib\classes12.zip;%ORA_HOME%sqlj\lib\translator.zip ;%ORA_HOME%sqlj\lib\runtime.zip jpub -user=BookAdmin/12345 -sql=EventType -usertypes=oracle -methods=false
- Напишем код на Java,извлекающий события из очереди AQ
String URL = "jdbc:oracle:thin:@server:1521:DB"; String USER = "BookAdmin"; String PASSWORD = "12345"; String QUEUE_NAME = "EventQueue"; // Connect to DB Class.forName("oracle.jdbc.driver.OracleDriver"); Connection connection =DriverManager.getConnection(URL, USER, PASSWORD); connection.setAutoCommit(false); // Connect to AQ Class.forName("oracle.AQ.AQOracleDriver"); AQSession session = AQDriverManager.createAQSession(connection); AQQueue queue = session.getQueue(USER, QUEUE_NAME); // Dequeue AQ message AQMessage message = queue.dequeue(new AQDequeueOption(), EVENTTYPE.getORADataFactory()); EVENTTYPE m =(EVENTTYPE) message.getObjectPayload().getPayloadData();
Работа приложения
Для доступа к базе данных книжного магазина необходимо запустить клиентское приложение r_admin.bat (Рис. 2.15).
В центре окна отображается список книг. Получение списка книг осуществляется посредством использования метода getBookList объекта User, предоставляемого сервером.
После ввода информации добавляемой книги, а так же при изменении уже существующих параметров книги (Рис. 2.16), вызывается метод updateBook объекта Admin, предоставляемого сервером.
На стороне сервера для предоставления соответствующих IDL -интерфейсов необходимо запустить серверное приложение r_server.bat.После запуска необходимо дождаться сообщения о завершении инициализации данных: запуск ORB,регистрация сервантов в сервисе имен, связь с базой данных (Рис. 2.17).