Опубликован: 18.03.2010 | Доступ: свободный | Студентов: 840 / 85 | Оценка: 4.48 / 4.33 | Длительность: 12:01:00
Лекция 2:

Технология CORBA

Пример "Банковская система"

Общее описание

Разработаем простейшую банковскую систему, поддерживающую следующий набор функций:

  1. хранение информации о счетах клиентов;
  2. выдачу сведений по запросу авторизованных клиентов;
  3. модификация счетов (снятие/зачисление денег, перевод на другой счет) авторизованными клиентами;
  4. открытие и закрытие счетов;
  5. выдачу отчетов о транзакциях за заданный клиентом период времени.

Система должна обеспечивать актуальность данных, отображаемых пользователю, а так же обеспечивать безопасность работы: перевод денег на счет может осуществляться любым клиентом, а снятие денег, перевод со счета и закрытие счета - только его владельцем. В качестве ORB используется Borland \text{\textregistered} Visibroker.

Создание таблиц в базе данных
Мифологическая модель базы данных

Рис. 2.8. Мифологическая модель базы данных

Информация о счетах клиентов и транзакциях будет храниться в базе данных, инфологическая модель которой приведена на Рис. 2.8. Эта база данных состоит из двух основных таблиц:

  • таблицы accounts, хранящей информацию о текущем состоянии счетов клиентов. В таблице хранятся имя клиента ( name ), пароль для доступа к счету ( password ), текущий остаток на счете ( balance ) и признак закрытия счета ( closed );
  • таблицы transactions, хранящей информацию обо всех транзакциях. В таблице хранятся дата и время проведения транзакции ( transaction_date ), сумма ( amount ) и комментарий к транзакции ( transactioncomment ). Номера счетов источника ( source_account_id ) и получателя ( dest_account_id ) транзакции связаны с таблицей accounts по внешнему ключу.

Для того, чтобы создать таблицы в базе данных Oracle 9i,необходимо выполнить следующий SQL-код.Помимо создания таблиц в базе данных, данный SQL-код создает процедуру, сообщающую серверу системы о появлении новых записей в таблице transactions и триггер, инициирующий выполнение этой процедуры. Так же для каждой таблицы определен триггер, автоматически генерирующий поле id для каждой добавленной строки таблицы.

delete from transactions;
drop table transactions;
drop sequence transactions id;

delete from accounts;
drop table accounts;
drop sequence accounts id;

create table accounts   (id number,
name varchar2(12 8), password varchar2(12 8), balance number default 0, closed char(1),
constraint accounts pk primary key   (id));

create table transactions (id number, transaction date number, source account id number, 
	dest account id number, amount number, transaction comment varchar2(12 8), 
	constraint transactions pk primary key (id), 
	constraint source account fk foreign key (source account id) references accounts(id),
	constraint dest account fk foreign key (dest account id)   references accounts(id));

create sequence transactions id start with 1 increment by 1; 
create sequence accounts id start with 1 increment by 1;

create or replace and compile java source named "Trigger" as

import java.net.*;
import java.io.*;
import java.math.BigInteger;

public class Trigger  
{
private static java.net.Socket s = null; 
private static OutputStream outputStream = null;
public static synchronized void trig(long id,   String host,   int port)   
	{
	try 
		{
		byte[]   b = new byte[8];
		for (int i = 0;  i < 8;  ++i)
			{
			b[i] = ( byte )(id % 256); 
			id >>= 8;
			}
		if (connect(host, port)) 
			{ 
			outputStream.write(b); 
			outputStream.flush();
			}
		}   
	catch   (Exception e)   
		{
		System.out.println("Failed"); e.printStackTrace();
		}
	}
private static boolean connect(String host,   int port)   throws Exception  
	{
	if ( s == null )
		s = new Socket( host,  port  );
	outputStream = s.getOutputStream();
	return true;
	public static void disconnect()   
		{
		try 
			{
			s.close(); 
			s = null; 
			}   
		catch   (Exception e)   
			{}
		}
	}

/
show errors java source "Trigger"
create or replace procedure account changed(accountId number, host varchar2, port number)   as
language java name   'Trigger.trig(long,   java.lang.String,   int)';

/
create or replace procedure disconnect as

language java name   'Trigger.disconnect()';
/

create or replace trigger transactions trigger after insert on transactions
referencing new as new for each row 
begin
account changed( rnew.source account id, 'smal', 12345); 
account changed( rnew.dest account id,  'smal', 12345); 
end;

/

show errors

В качестве параметров процедура accountchanged принимает номер изменившегося счета, имя хоста, на котором работает сервер банковской системы и номер порта, на который должно производиться соединение.

Интерфейсы системы

Клиентским приложениям система доступна посредством интерфейсов Bank и Account и структуры Transaction. Кроме того, посредством интерфейса AccountEvents сервер сообщает клиентам об изменениях на счете. Для сообщения об ошибках добавлены исключения BankException и AccountException, содержащие информацию об ошибках в работе системы.

Интерфейс Bank

Это первый интерфейс, получаемый клиентами, после подключения к серверу системы. Интерфейс описан в IDL-файле следующим образом:

interface Bank 
{
AccountId CreateAccount(  in wstring usrName,     in wstring usrPasswd ) raises(BankException);
Account	OpenAccount ( in AccountId id,	in wstring usrPasswd )
raises(BankException);
void	CloseAccount (in AccountId id,	in wstring usrPasswd )
raises(BankException);
};

Посредством этого интерфейса клиент может залогиниться в систему ( OpenAccount ), открыть новый ( CreateAccount ) или закрыть существующий ( CloseAccount ) счет.

Класс интерфейса Bank

Класс интерфейса Bank существует в единственном экземпляре все время жизни сервера. Следующий код добавляет объект этого класса в POA,делая его доступным внешним приложениям:

CORBA::Object var obj  = orb->resolve initial references("RootPOA"); 
PortableServer::POA var rootPOA = PortableServer::POA::  narrow(obj);

CORBA::PolicyList policies;
policies.length(1);

policies[(CORBA::ULong)0]   = rootPOA->create lifespan policy(PortableServer::PERSISTENT );

// get the POA Manager
PortableServer::POAManager var poa manager = rootPOA->the POAManager();
PortableServer::POA var
bankPOA = rootPOA->create POA(   "banking poa",  poa manager,  policies  );
const int MAXBUF = 1024; char ior[MAXBUF];

// Convert from string to object 
std::ifstream in(argv[1]); 
if   (   !in )
throw std::runtime error(  std::string("Can't open file \"")   + argv[1] + "\""  );
in.getline(ior,  MAXBUF);

// Create the servant
ParamsPtr params = ReadParams();
BankImpl bankServant(orb,   ior,   *params);

// Decide on the ID for the servant
PortableServer::ObjectId var bankId = PortableServer::string_to_ObjectId("Bank");

// Activate the servant with the ID on bankPOA
bankPOA->activate object with id(bankId,   &bankServant);

// Activate the POA Manager 
poa manager->activate();
CORBA::Object var reference = bankPOA->servant to reference(  &bankServant);

// Wait for incoming requests
orb->run();

Класс BankImpl реализует интерфейс Bank, а так же занимается информированием клиентов об изменении их счетов. Для этого в члене channels хранится отображение номеров счетов на открытые в настоящий момент соединения с сервером. Фактически, основную работу выполняет класс database::Bank, который непосредственно модифицирует базу данных. Фактически, объект класса BankImpl хранит указатель на объект класса database::Bank и делегирует ему запросы клиентского приложения на работу со счетом.

::CORBA::LongLong BankImpl::CreateAccount(  const wchar t*    usrName,   const wchar t*    usrPasswd )
{
database::AccountId id;
CheckResult( bank ->CreateAccount(    usrName,    usrPasswd,   &id )   ); 
return id;
}

banking::Account ptr BankImpl::OpenAccount(   ::CORBA::LongLong    id,   const wchar t*    usrPasswd )
{
database::AccountPtr db acc;
CheckResult( bank ->OpenAccount(id, usrPasswd, &db acc ));
AccountsMap::iterator it = accounts.find(id );
if (it == accounts.end())
	{
	VISMutex var lock(channelsMutex);
	std::string const channelIOR  (  CreateChannel( id )); 
	AccountImpl * servant = new AccountImpl( db acc,   channelIOR ); 
	CORBA::Object var ref (accountPOA ->servant to reference(servant));
	servant-> remove ref();
	banking::Account var account = banking::Account:: narrow(ref); 
	it = accounts.insert(  std::make pair(    id,   account  )   ).first;
	}
return banking::Account::  duplicate(it->second);
}

void BankImpl::CloseAccount(::CORBA::LongLong    id,   const wchar t*    usrPasswd)
{
CheckResult( bank ->CloseAccount( id, usrPasswd ));
}

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

void BankImpl::OnDBChanged( database::AccountId id )
{
VISMutex var lock(  channelsMutex    );
EventChannelsMap::const iterator it = channels.find(id );
if (it != channels .end()   )
it->second->add message(  id );
}
Интерфейс Account

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

interface Account
{
readonly attribute wstring	HolderName;
readonly attribute Money	Balance;
readonly attribute AccountId	Id;
readonly attribute string	ChannelIOR;
TransactionList      Transactions();
Transaction	ProcessTransaction(  in AccountId dest, in Money amount,   in wstring comment  )

raises(AccountException)
};
Класс интерфейса Account

Интерфейс Account реализуется объектами класса AccountImpl. Как и в случае класса BankImpl, объекты данного класса не взаимодействуют с базой данных напрямую, а делегируют вызовы объекту промежуточного класса. Кроме того, в момент первичного создания счета на него помещается стартовая сумма в 1000 денежных единиц. Полный код класса BankImpl приведен ниже.

class AccountImpl
:  public virtual POA banking::Account
,  public virtual PortableServer::RefCountServantBase
{
public:
AccountImpl( database::AccountPtr account, std::string const& channelIOR )
:   account	(account)
,   channelIOR_ (channelIOR )
	{
	account_->PutMoney(  1000,   L"Initial balance", 0); 
	std::cout << "Account created" << std::endl;
	}
~AccountImpl() 
	{
	std::cout << "Account destructed" << std::endl;
	}


virtual wchar_t* 			HolderName() 	{ return CORBA::wstring_dup( account_->HolderName() ); }
virtual ::CORBA::Float 		Balance() 	{ return account_->Balance(); }
virtual ::CORBA::LongLong 	Id() 		{ return account_->Id(); }
virtual char * 			ChannelIOR() 	{ return ORBA::string_dup(channelIOR_.c_str() ; }

virtual banking::TransactionList* Transactions() 
{
database::TransactionsListPtr tl = account_->Transactions();
banking::TransactionList_var res = new banking::TransactionList; 
res->length(  tl->size() );
CORBA::ULong i = 0;

for ( database::TransactionsList::const_iterator it = tl->begin();  it != tl->end();  ++it )
	{
	res[i]=CreateTransaction( *it );
	++i;
	return res._retn();
	}

virtual banking::Transaction * ProcessTransaction( ::CORBA::LongLong _dest, ::CORBA::Float amount, const wchar t* comment)
	{
	database::Transaction trans;
	CheckResult(  account_->ProcessTransaction( _dest,  _amount,  _comment, &trans));
	return new banking::Transaction(  CreateTransaction(  trans ));
	}

private:
database::AccountPtr const account_;
std::string	const channelIOR_;
};
Антон Зубеков
Антон Зубеков

Здравствуйте, подскажите пожалуйста где можно достать материалы по курсу Кросс-платформенные и многозвенные технологии, о которых говориться, к примеру, в Лекции 2. Пример "Служба мгновенных сообщений"