Разработаем систему заказов для книжного магазина, позволяющую:
Следующий код создает данную таблицу:
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 имеет два метода:
Интерфейс Admin имеет единственный метод updateBook позволяющий добавлять новые книги и редактировать информацию о них.
После описания 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 файлы, в которых описан каркас реализации серванта, и остается лишь реализовать его методы. Полный список сгенерированных файлов при запуске указанной команды:
Клиенты, посылающие запросы к серверу реализованы на языке Java.В качестве ORB взят стандартный ORB, входящий в Java SDK.
Для получения доступа к интерфейсу сервера, клиенту необходимо выполнить следующие действия.
// 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));
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) драйвер. Покажем работу сервера с базой данных на примере запроса получения списка книг.
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);
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);
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;
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
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).