2000 г
Распределенные вычисления и технологии Inprise
Часть 5. Cоздание CORBA-серверов, переносимых на другие платформы, с помощью С++Builder 4
Наталия Елманова
Компьютер Пресс - CD, 1999, N 5
Copyright N.Elmanova & ComputerPress Magazine.
Данная статья посвящена возможностям C++Builder 4, связанным с созданием распределенных
многоплатформенных систем. Она же является второй частью опубликованного в предыдущем
выпуске журнала обзора, посвященного возможностям С++Builder 4.
- Постановка задачи
- Создание переносимого сервера функциональности
- Создание клиента с графическим пользовательским интерфейсом
- Создание переносимого клиентского приложения
- Несколько слов о серверах доступа к данным
Организация распределенных вычислений за последние два года стала весьма "модным"
направлением в области проектирования информационных систем. Весьма популярны
распределенные системы на основе COM и MIDAS (возможность создания которых появилась
в предыдущей версии С++Builder). Однако нередко (особенно в случае повышенных
требований к надежности подобного рода систем) возникает потребность использовать
в качестве серверной части такой распределенной системы приложения, функционирующие
на платформе, отличной от Windows (что исключает использование COM как специфичной
для Windows технологии).
В этой связи следует заметить, что C++Builder позволяет генерировать код для
таких переносимых приложений (естественно, за счет отказа от использования COM,
VCL и Windows API). Соответственно в качестве технологии распределенных вычислений
в этом случае используется многоплатформенная технология, не использующая специфику
Windows. В последнее время наиболее часто для этой цели используется CORBA (Common
Object Request Broker Architecture), спецификация которой создана консорциумом
Object Management Group. В комплект поставки C++Builder Enterprise входит набор
утилит и сервисов, предстаавляющих собой одну из реализаций этой спецификации
- Inprise VisiBroker.
Данная часть обзора возможностей C++Builder 4 описывает создание сервера функциональности
с использованием VisiBroker, переносимого на другие платформы, а также создание
клиентских приложений для него (в том числе переносимых на другип платформы.
Постановка задачи
Создадим приложение, производящее, к примеру, сложные научные вычисления (в
нашем примере - вычисление значения некоторой функции, представляющей собой
полином третьей степени). В реальной жизни вместо этого расчета данная функция
может реализовывать любую другую функциональность - доступ к данным, обработку
сигналов, поступающих с аппаратуры, генерацию отчетов, и др.
Пусть наше приложение имеет главную форму следующего вида (рис. 1):
Рис. 1. Интерфейс приложения, подлежащего разбиению на сервер
и клиента
В нашем примере компонент TChart содержит одну серию (ее следует добавить вручную
при создании формы).
Создадим обработчик события, связанный с нажатием на кнопку с надписью "График",
и в этом же модуле создадим реализацию нашей функции:
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::BitBtn1Click(TObject *Sender)
{
int i; double x1,y;
for (i=1;i<60;i++)
{
x1=0.1*float(i-13);
y=fun1(x1);
Chart1->Series[0]->AddXY(x1,y,FloatToStr(x1),clWhite);
}
}
double fun1(double x)
{
double r=x*x*x-5*x*x+3*x+5;
return(r);
}
//---------------------------------------------------------------------------
Результатом работы такого приложения будет график нашего полинома, появляющийся
при нажатии на кнопку "График" (рис. 2):
Рис. 2. То же приложение на этапе выполнения
Это обычное Windows-приложение, содержащее в себе и пользовательский интерфейс,
и функциональность, связанную с проведением расчетов (вычисление значений функции).
Теперь попробуем разбить это приложение на две части, отделив пользовательский
интерфейс (кнопки и компонент TChart) от функциональности. Иными словами, создадим
сервер функциональности, вычисляющий значение функции, и клиентское приложение,
использующее этот сервер как поставщика результатов расчетов.
Создание переносимого сервера функциональности
Итак, создадим CORBA-сервер, осуществляющий проведение расчетов. Сразу же заметим,
что подобного рода серверы, как правило, не нуждаются в пользовательском интерфейсе.
Поэтому мы вполне можем создать его как консольное приложение. Если при этом
мы откажемся от использования VCL, код полученного сервера может быть скомпилирован
любым другим компилятором C++ (в том числе, естественно, и компилятором для
другой платформы). Консольные приложения есть единственный тип приложений, существующий
для любых платформ, в отличие от приложений с графическим интерфейсом пользователя
- последние требуют графических библиотек или операционных систем с GUI, и на
все эти графические библиотеки и функции API подобных операционных систем нет
никаких стандартов, в отличие от самого языка С++.
Иными словами, сервер может быть создан переносимым на другие платформы, что
и будет сделано.
Для создания сервера выберем из репозитария объектов со страницы Multitier
пиктограмму CORBA server (рис. 3):
Рис. 3. Выбор пиктограммы CORBA Server из репозитария объектов
После выбора пиктограммы в диалоговой панели CORBA Server Wizard выберем в
качетсве типа приложения Console Application и отключим возможность использования
VCL. Это позволит нам создать переносимый на другие платформы код (fig. 4):
Рис. 4. CORBA Server Wizard
Первое, с чего следует начинать создание распределенной системы, - это описание
интерфейсов сервера. С этой целью используется язык IDL (Interface Definition
Language), являющийся, по существу, стандартом для подобного рода описаний,
не зависящим от языков программирования и платформ. Отметим, что существует
несколько диалектов IDL (COM IDL, CORBA IDL, DCE IDL), имеющих некоторые различия.
Естественно, мы будем использовать CORBA IDL. Так как мы еще не создали никакого
IDL-описания, выберем опцию Add New IDL File. После нажатия на кнопку OK в окне
редактора кода появится пустая страница для ILD-описания, куда можно ввести
описание интерфейса нашего сервера.
interface b1
{
double fun1(in double x);
} ;
Далее следует скомпилировать IDL-файл. В действительности это не компиляция,
а кодогенерация, в результате которой мы получим два модуля, fun1_s.cpp and
fun1_c.cpp, содержащие stub-код и skeleton-код.
Что представляют собой stub-объект и skeleton-объект?
Все способы взаимодействия между серверами функциональности и их клиентами основаны на механизме вызовов удаленных процедур (RPC - Remote Procedure Calls) и маршалинга, представляющего солой обмен пакетами данных между объектом внутри клиента (stub-объектом) и объектом внутри сервера (skeleton-объектом). Skeleton-объект является представителем клиента внутри адресного пространства сервера. Обращаясь к нему, сервер "думает", что он имеет дело с локальным объектом. Stub-объект является представителем сервера внутри адресного пространства клиентского приложения, поэтому последнее также "думает", что вызывает методы локального объекта. В действительности так оно и есть, однако вместо истинной реализации (в нашем случае - вычисления значений функции) методы этого объекта осуществляют вызовы удаленных процедур и обмен пакетами данных с сервером (рис. 5):
Рис. 5. Взаимодействие сервера и клиента
Теперь можно вспомнить о реализации нашей функции. Для этого выберем со страницы
Multitier репозитария объектов пиктограмму CORBA Object Implementation. В диалоговой
панели CORBA Object Implementation Wizard выберем имя интерфейса (b1), а также
определим имя модуля, содержащего реализацию, и имя класса CORBA-объектов. В
данном примере мы описшем объект, экземпляр которого создается в момент старта
сервера, так что сервер сразу же сможет принимать обращения клиентов (рис. 6):
Рис. 6. CORBA Object Implementation Wizard
Мы можем просмотреть изменения, внесенные в наше приложение этим экспертом.
В частности, имеет смысл внести в код реализации нашего метода строки, ответственные
за проведение расчетов (рис. 7):
Рис. 7. Добавление кода рализации функции в сгенерированный
модуль
Теперь можно скомпилировать проект и закрыть его.
Перед созданием клиента мы должны запустить VisiBroker Smart Agent - службу
каталогов CORBA, позволяющую осуществить доступ к CORBA-серверам. Теперь можно
запустить наш сервер (желательно отдельно от среды разработки).
Теперь можно создавать клиенские приложения.
Создание клиента с графическим пользовательским интерфейсом
Для начала создадим клиентское приложение, представляющее собой обычное Windows-приложение
с интерфейсом, похоржим на представленный на рис.2. Для этого выберем пиктограмму
CORBA Client со страницы Multitier репозитария объектов C++Builder.
В этом примере мы будем использовать раннее связывание клиента с сервером
(в этом случае можно достичь максимального быстродействия). Для этого выберем
из меню среды разработки опцию "Edit/Use CORBA object". В соответствующем эксперте
мы должны добавить к проекту тот же самый IDL-файл, что был создан при создании
сервера, а также ответить на вопросы об именах объектов и переменных, в частности,
об имени нового свойства формы, к которому следует обращаться, если нужно вызвать
метод сервера (рис. 8):
Рис. 8. Определение свойств для доступа к CORBA-объекту
в клиентском приложении
Далее можно создать интерфейс, сходный с приведенным на рис. 1, и создать обработчик
события, связанного с нажатием на кнопку "График":
void __fastcall TForm1::BitBtn1Click(TObject *Sender)
{
int i; double x1,y;
for (i=1;i<60;i++)
{
x1=0.1*float(i-13);
y=b1_1->fun1(x1);
Chart1->Series[0]->AddXY(x1,y,FloatToStr(x1),clWhite);
}
}
В результате получим тот же график, что и на рис.2.
Создание переносимого клиентского приложения
Теперь создадим клиентское приложение, переносимое на другие платформы. Это
должно быть консольное приложение, не использующее VCL. В этом случае при создании
клиента мы должны выбрать опцию "Console Application" и отменить опцию "Enable
VCL" .
Все остальные действия похожи на предыдущие, за исключением создания пользовательского
интерфейса. Простейший способ создания пользовательского интерфейса в данном
случае - вывести результаты расчетов на экран и поместить этот код непостредственно
в файл проекта:
//---------------------------------------------------------------------------
#include <corbapch.h>
#pragma hdrstop
//---------------------------------------------------------------------------
#include "fun1_c.hh"
#include <corba.h>
#include <condefs.h>
USEIDL("corba\corba_rus\fun1.idl");
USEUNIT("corba\corba_rus\fun1_c.cpp");
USEUNIT("corba\corba_rus\fun1_s.cpp");
//---------------------------------------------------------------------------
#pragma argsused
int main(int argc, char* argv[])
{
try
{
// Initialize the ORB and BOA
CORBA::ORB_var orb = CORBA::ORB_init(argc, argv);
CORBA::BOA_var boa = orb->BOA_init(argc, argv);
a1_var a1_1 = a1::_bind("a1Obj");
cout<<"Our function table \n";
int i; double x1,y;
for (i=1;i<271;i++)
{
x1=0.1*float(i);
y=a1_1->fun1(x1);
cout<<x1<<" "<<y<<"\n";
}
}
catch(const CORBA::Exception& e)
{
cerr << e << endl;
return(1);
}
return 0;
}
//---------------------------------------------------------------------------
В этом случае мы получим таблицу с результатами расчетов непосредственно на
экране консольного приложения.
Отметим, что данное приложение можно скомпилировать любым компилятором С++,
в том числе и компилятором для платформы, отличной от Windows.
Несколько слов о серверах доступа к данным
Серверы доступа к данным являются одним из наиболее популярных типов серверов
middleware. Как создать переносимый сервер доступа к данным?
Наиболее популярный способ создания таких серверов - использование MIDAS и
создание удаленных модулей данных как COM- или CORBA-объектов. В этом случае
мы получим COM или CORBA-сервер, но это будет Windows-приложение, так как оно
использует VCL, основанную на Windows API, и BDE, которая также представляет
собой набор Windows-библиотек. Создание таких серверов более подробно будет
рассмотрено в следующей статье данного цикла.
Если нам нужен переносимый сервер доступа к данным, он не должен использовать
ни VCL, ни BDE. Как быть в этом случае?
Вспомним, что вся функциональность сервера доступа к данным - это набор SQL-запросов,
и ничего более. Для MIDAS-серверов генератором запросов является BDE. Но выполнить
запрос можно и другим способом! Например, всегда есть возможность использования
низкоуровневого API клиентских частей серверных СУБД (например, Oracle Call
Interface). Для большинства таких СУБД такие API существуют для многих платформ
и содержат одни и те же функции. Использование таких API может показаться несколько
утомительным, но в целом оно ненамного сложнее, чем написание других функций.
Российское представительство Inprise:
Тел. 7(095)238-36-11
e-mail: info@inprise.ru
http://www.inprise.ru