r 20 07 N6VGQT7GLUPJMKBKRLTCSNK2OTOKZH74X4DSWGQ


Rozdział 20. CORBA
CORBA jest skrótem nazwy Common Object Request Broker Architecture (co oznacza ogólną
architekturę pośrednika wywoływania obiektów). Jest to unormowany schemat budowy
rozproszonych aplikacji obiektowych, którego elementy mają możliwość komunikowania się ze
sobą niezależnie od platformy i użytego języka programowania.
Ogólny schemat architektury systemu wykorzystującego CORBA podany jest na poniższym
rysunku. Klient i serwer aplikacji są programami, które można napisać samemu:
W architekturze CORBA wyróżnia się elementy (ang. components) i warstwy (ang. layers).
Język definicji interfejsu (IDL)
Jest to język używany w deklaracjach interfejsów obiektów. Bardzo mocno przypomina on
deklaracje klas w języku C++.
IDL (Interface Definition Language) jest stosowany do deklarowania struktur danych i metod
wykorzystywanych przez CORBA w aplikacjach. Dla danego zestawienia ORB (patrz niżej) i
języka programowania IDL jest zwykle kompilowany do postaci  zrębów (interfejsy statyczne) i
 szkieletów (interfejsy dynamiczne). Zręby (ang. stubs) można następnie dołączać do klienta,
szkielety (ang. skeletons) do serwera, zaś kod generowany przez kompilator IDL jest
wykorzystywany do zarządzania komunikacją między kodem użytym w kliencie lub serwerze, a
pośrednikiem wywoływania obiektów (ORB). IDL przekazuje do ORB informację o rodzaju
używanego systemu, wskazując rodzaj wymienianych danych oraz rodzaje wywołań wymaganych
przez te dane.
Pośrednik wywołań obiektów (ORB)
Jest to warstwa podstawowa, wykorzystywana przez wszystkie pozostałe.
ORB (Object Request Broker) cechuje się właściwością zdalnego wywoływania. Na swoim
najbardziej elementarnym poziomie pośredniczy między klientami żądającymi jakichś działań oraz
przekazuje parametry między klientem i serwerem. Może on kierować żądania do innego
pośrednika ORB, jeżeli on właśnie zarządza żądaną usługą.
Najważniejszą cechą zapewniającą takie działanie jest możliwość komunikacji między
pośrednikami ORB niezależnie od ich implementacji. CORBA definiuje ogólne zasady tej
komunikacji w postaci protokołu komunikacyjnego (oznaczanego skrótem GIOP od słów General
Inter-ORB Protocol). GIOP określa wspólną reprezentację danych (CDR, czyli Common Data
Representation), wspólne formaty komunikatów i wspólne założenia odnośnie do dowolnej
sieciowej warstwy transportowej, która może być użyta do przekazywania komunikatów GIOP.
Nazwy, które tutaj wymieniliśmy, stanowią zaledwie niewielki ułamek terminologii i skrótów, z
którymi powinni zapoznać się zainteresowani czytelnicy!
Obecnie, przy względnej dominacji sieci wykorzystujących TCP/IP, prawie cała komunikacja
między pośrednikami ORB i obsługiwanymi przez nie procesami korzysta z TCP/IP. Dlatego
właśnie dodano specyfikację transportową do GIOP, definiując internetowy protokół komunikacji
między pośrednikami ORB. Jest on oznaczamy skrótem IIOP pochodzącym od nazwy Internet
Inter-ORB Protocol. Same protokoły w architekturze CORBA są zdefiniowane za pomocą IDL i
właśnie dzięki temu uzyskano duże możliwości współdziałania różnych implementacji ORB. Jeżeli
np. aplikacja ORBit (czyli aplikacja ORB należąca do GNOME) może mieć dostęp do obiektu
ORBit za pomocą protokołu IIOP, to staje się wielce prawdopodobne, że może ona mieć dostęp
także do obiektów zdefiniowanych za pomocą innych pośredników ORB, np. omniORB,
niezależnie od tego, czy są one umieszczone na tym samym serwerze, czy na innym.
Protokół IIOP został znormalizowany, a więc można się spodziewać, że program-klient napisany
dla pośrednika ORBit będzie pomyślnie korzystał z usług zdefiniowanych za pomocą omniORB.
Nie wymaga to od programisty zbytniego wysiłku, a więc jeżeli została zdefiniowana odpowiednia
specyfikacja takiego obiektu (IOR, patrz niżej), to odwołanie może być skierowane do oddalonego
pośrednika ORB i tam przetworzone.
Współdziałające odwołanie do obiektu (IOR)
Żądanie wprowadzenia metody obiektu musi zawierać tzw. współdziałające odwołanie do obiektu
(oznaczane skrótem IOR od nazwy Interoperable Object Reference). Jest to odwołanie do obiektu
zdefiniowanego w CORBA mające postać napisu. Zawiera ono informację o użytym rodzaju IDL,
nazwę węzła przechowującego obiekt, porządek bajtów (ang. endianness) dla tego węzła oraz w
systemach TCP/IP  dane gniazda sieciowego, które może być wykorzystane do dostarczenia
żądania do pośrednika ORB działającego w tym węzle.
Czasami warto podczas uruchamiania aplikacji podjąć próbę zdekodowania części IOR, ale w
zasadzie powinno ono być traktowane jako przezroczyste, czyli jako coś, co powinien
zinterpretować pośrednik ORB.
Zainicjowanie systemu CORBA zazwyczaj wymaga rozesłania jakichś IOR w celu
poinformowania klientów o sposobie dostępu do odpowiednich serwerów. Jest to najbardziej
dokuczliwy proces podczas uruchamiania aplikacji w architekturze CORBA.
Adapter obiektu
Pomiędzy pośrednikiem ORB a implementacją obiektu występuje tzw. adapter obiektu (ang.
Object Adaptor). Zawiera on funkcje wspomagające tworzenie, identyfikację, uaktywnianie i na
końcu zwalnianie obiektu.
Najpierw w architekturze CORBA zdefiniowany został podstawowy adapter obiektu (BOA, od
słów Basic Object Adpator), ale jego specyfikacja nie była dokładnie określona. Różne pośredniki
korzystały z całkowicie odmiennych wywołań, co wyjątkowo silnie ograniczało możliwość
współdziałania aplikacji. Następnie wprowadzono przenośny adapter obiektu (POA, czyli Portable
Object Adaptor), który umożliwiał korzystanie z wielu cykli pracy w znacznie bardziej wygodny
sposób.
Oznacza to, że gdy jakiś pośrednik odwołań do obiektów okaże się nieprzydatny i jeżeli dostępne
jest odwzorowanie użytego języka, to wprowadzając niewielkie zmiany w kodzie programu,
można będzie przystosować go do innego pośrednika.
Serwery
Serwery zapewniają korzystanie z obiektów, zezwalając klientom na wywołania metod tych
obiektów. Usługi CORBA zostały określone za pomocą standardowych definicji języka IDL, dzięki
czemu znane są sposoby wykonywania ogólnodostępnych czynności. Dzięki usługom
nazewniczym można budować rejestr dostępnych obiektów, a więc aplikacje w architekturze
CORBA mogą wyszukiwać wymagane usługi. Obsługa zdarzeń zapewnia umieszczanie zdarzeń w
kolejkach, dzięki czemu aplikacje korzystające ze zdarzeń przy wywołaniach w architekturze
CORBA nie muszą szczegółowo analizować każdej usługi od początku.
Klienty nie muszą znać bezpośrednio danych identyfikacyjnych serwera, z którego usług chcą
skorzystać. Komponenty serwera mogą być przechowywane na różnych maszynach rozrzuconych
po całej sieci i klient nie musi znać ich dokładnej lokalizacji. Przeważnie wysyłają one żądania
wymagające wykonania pewnych operacji na obiekcie, zaś pośrednik ORB kieruje te żądania do
właściwego serwera w odpowiedniej lokalizacji. W architekturze CORBA istnieje kilka usług,
które pozwalają programom rejestrować ich własne usługi. Oznacza to, że aplikacja CORBA może
wyszukiwać dostępne usługi podczas swojej pracy.
Zwróćmy uwagę na to, że CORBA nie jest  czystym systemem typu klient-serwer, bowiem
zezwala również na komunikowanie się węzłów między sobą. Od wielu usług CORBA wymaga
się, aby serwery odwracały swoją rolę, stając się klientami żądającymi usług od innych serwerów.
Jako przykład można podać aplikację obsługującą wypożyczalnię płyt DVD. Serwer tej aplikacji
przetwarzający zapytania może być traktowany jako typowy serwer, ale zmienia się on w klienta
podczas żądania danych od serwera bazy danych lub żądania potwierdzeń autentyczności od
serwera zabezpieczeń.
Usługi nazewnicze i usługi wymiany
Wiele aplikacji używających usług nazewniczych i transakcyjnych (ang. Naming and Trading
Services) w architekturze CORBA może równocześnie korzystać z tego samego  rejestru , to zaś
umożliwia zastosowanie nawet specyficznych programów pomocniczych do zarządzania
systemem śledzenia informacji o wszystkich zarejestrowanych obiektach. Bez unormowanych
zasad działania, takich jak usługi nazewnicze, nie można byłoby utworzyć aplikacji wykonującej
takie zadanie. Usługi nazewnicze umożliwiają także tworzenie powiązań z usługami
katalogowymi, np. z LDAP.
Nie wspomniano tutaj Nigdzie o użyciu UDP zamiast TCP, o nazwach węzłów sieci lub o
stosowaniu określonego języka programowania przy tworzeniu klientów lub serwerów. Podczas
korzystania z RPC lub gniazd sieciowych aplikacja musi zwracać uwagę na stany węzłów sieci i
sposoby uzyskiwania połączeń z serwerami. W architekturze CORBA zagadnienia te są traktowane
jako szczegółowy opis środowiska, który rozwiązuje dana implementacja. Zarządza tym
wszystkim pośrednik ORB, przeważnie nie ingerując w aplikację. Gniazda sieciowe zapewniają
podstawowy format powiązań z określonymi standardowymi protokołami. Dzięki temu aplikacja
musi się troszczyć tylko o protokół oraz o format danych, które mają być przekazywane. W
architekturze CORBA ten format opisuje IDL, zaś protokółem zajmuje się pośrednik ORB  a
więc została tu wyeliminowana konieczność analizy surowych danych. Ścisłe określanie danych
za pomocą IDL umożliwia przekaz tych danych o odpowiedniej strukturze z jednego systemu do
innego, niezależnie od używanego systemu operacyjnego, protokołu sieciowego, języka
programowania oraz pośrednika ORB.
Ogólna ocena architektury CORBA
Zastosowanie architektury CORBA jest początkowo bardziej kosztowne w porównaniu z
bezpośrednim nakładaniem na siebie elementów tworzonego systemu. Zyski widać dopiero przy
długoterminowym korzystaniu z tej architektury. Użycie CORBA do wydzielenia klienta i serwera
z aplikacji w przypadku naszego programu obsługującego wypożyczalnię płyt DVD nie przyniesie
znacznych ulepszeń w porównaniu z zastosowaniem gniazd sieciowych lub RPC. Uzyskujemy
tylko to, że łatwiejsze stanie się zarządzanie następnymi modyfikacjami, które mogą obejmować:
powiązanie aplikacji obsługującej wypożyczalnię z systemem księgującym,
podłączenie czytnika kodu kreskowego umieszczonego w pojemniku na zwroty (zwrot płyty
jest obsługiwany w momencie, gdy klient wsunie płytę do pojemnika odbiorczego,
przesuwając ją nad czytnikiem kodu),
przenoszenie danych do bazy PostgreSQL bez konieczności angażowania interfejsu klienta,
jeżeli nie ma takiego powodu,
zastosowanie języka Java w programie serwera,
rozdzielenie aplikacji w taki sposób, aby baza danych obsługująca klientów wypożyczalni
była zarządzana z jednego komputera, zaś tytuły płyt były obsługiwane z innego, oraz aby
dostęp do pozostałych elementów aplikacji był możliwy z różnych komputerów.
Wszystkie te zmiany środowiskowe można wprowadzić bez konieczności modyfikacji interfejsu
klienta. Jeżeli zdarzy się, że zostaną wprowadzone nowe interfejsy, to jedyną zmianą będzie
dołączenie nowego oprogramowania do istniejących serwerów wypożyczalni DVD. Nie zaburzy
to w najmniejszym stopniu już istniejących programów-klientów.
Jeżeli zajdą zmiany funkcjonalne w serwerze, to prawdopodobnie trzeba będzie odnowić
połączenia klientów w architekturze CORBA. Jest to wymagane do uzyskania informacji o nowych
lokalizacjach usług obiektowych. Przy założeniu, że interfejs się nie zmieni, uzyskanie dostępu
przez klienta do nowego serwera może polegać tylko na jego wylogowaniu i powtórnym
zalogowaniu.
Zupełnie niezauważalnie można dodawać następne usługi. Usługi związane z bezpieczeństwem w
architekturze CORBA mogą obejmować np. weryfikację tożsamości przeprowadzaną dzięki
specjalnemu wspomaganiu ze strony ORB. Mogłoby to zmniejszyć wymagania odnośnie do
zaufania między klientami a serwerami, a alternatywą byłoby dołączanie kodu weryfikującego do
każdego z nich. Taka sytuacja dotyczy również niektórych innych usług CORBA: ich działanie
można ulepszyć, przenosząc niektóre ich cechy funkcjonalne do pośrednika ORB.
Oddzielenie definicji IDL od języka programowania użytego w klientach i serwerach staje się
szczególnie przydatne wtedy, gdy zespoły programistów zaczynają pracować w rozproszeniu.
Najważniejsze jest wówczas utrzymanie dokładnie i jednoznacznie zdefiniowanych interfejsów,
szczególnie zaś wtedy, gdy system ma mieć jakąś postać otwartego standardu, a programiści są
zatrudnieni w różnych organizacjach i nawet w różnych krajach.
Interfejs musi być bardzo dokładnie zdefiniowany, ale przy implementacji usługi mamy
pozostawioną możliwość wyboru. Ważne jest więc to, że zarówno środowisko, jak i
implementacja mogą się zmieniać bez konieczności modyfikacji interfejsów.
W odróżnieniu od wielu rozwiązań alternatywnych, CORBA pozwala na łączenie wszelkich
systemów operacyjnych i wszelkich rodzajów języków programowania. Konsorcjum o nazwie
Object Management Group (OMG) zarządzające standardami CORBA uzgodniło znormalizowane
sposoby odwzorowania konstrukcji IDL w kilku językach programowania: Ada, C, C++, COBOL,
Common Lisp, Java i Smalltalk. Inne organizacje wprowadziły mniej sformalizowane zapisy
dotyczące odwzorowania IDL w innych językach, np. Perl, Python i TCL.
Niektóre języki, jak np. C, nie obsługują bezpośrednio abstrakcji obiektowych, a więc obiekty
muszą w nich być symulowane. Prowadzi to do nieco dziwnie wyglądającego kodu, ale
jednocześnie możliwe jest uzyskanie wyników niedostępnych w inny sposób.
CORBA i RPC
CORBA najbardziej przypomina RPC (patrz rozdział 18), ponieważ umożliwia zdalne
wywoływanie procedur przez klienty. Najważniejszą różnicą jest tu sposób lokalizacji serwerów:
w systemach korzystających z CORBA nie są one wybierane na podstawie informacji o węzle i
gniezdzie utrzymywanej przez klienta, lecz na podstawie tzw.  odwołań obiektowych używanych
przy kierowaniu żądań przez ORB. Dzięki temu to ORB zarządza wymianą informacji między
klientami a serwerami, a nie same klienty.
Podstawowym problemem w alternatywach, takich jak RPC i gniazda sieciowe, nie jest brak
skomplikowanych abstrakcji, ale raczej to, że w miarę wzrostu stopnia złożoności aplikacji
korzystających z takich mechanizmów coraz trudniejsze staje się zarządzanie nimi. Widać to
szczególnie wyraznie, gdy aplikacje muszą korzystać z dodatkowych usług, rejestrów i
pośredników. Z drugiej strony, CORBA zawiera wzorzec dodawania nowych usług, zaś
konsorcjum OMG unormowało wiele użytecznych dodatkowych abstrakcji obiektów oraz pewne
właściwości tej architektury wspomagające aplikacje. Zagadnienia te zostaną omówione bardziej
szczegółowo w następnych częściach książki.
CORBA rozwiązuje kilka znaczących ograniczeń spotykanych w RPC:
RPC wymaga, aby podczas kompilacji funkcje API były deklarowane statycznie.
Zarówno RPC, jak i CORBA zawierają języki definicji interfejsu, określające dozwolone
operacje, czyli tzw.  protokół . CORBA obsługuje dodatkowo tzw. interfejs wezwań
dynamicznych (Dynamic Invocation Interface). Mogą z niego korzystać programy,
sprawdzając podczas pracy w repozytorium interfejsów dostępność metod, które mogą
być następnie użyte w wywołaniach. Stosując RPC, nie uzyskuje się żadnej możliwości
takiego działania.
RPC umożliwia tylko synchroniczną wymianę komunikatów (ang. synchronous
messaging).
W architekturze CORBA komunikacja między klientem a pośrednikiem (ORB), podobnie
jak komunikacja między pośrednikiem a serwerem, odbywa się synchronicznie  w
tradycyjny sposób. Można tu jednak bardzo prosto dołączyć elementy pośredniczące
umożliwiające korzystanie z innych modeli wymiany komunikatów. Połączenia z
pośrednikami mogą więc pozostać synchroniczne, zaś serwer CORBA przyjmujący i
przechowujący żądania może symulować komunikację asynchroniczną w połączeniach z
serwerem faktycznie obsługującym dane żądania.
Istnieje nowy sposób obsługi wykorzystujący tzw. asynchroniczną metodę wezwań
(oznaczaną skrótem AMI od Asynchronous Method Invocation), który umożliwia
prawdziwie asynchroniczny sposób wymiany komunikatów. Zastosowano go w kilku
pośrednikach ORB.
RPC obsługuje tylko procedury, zaś współczesne systemy coraz częściej zaczynają
korzystać z obiektów.
RPC bardzo mocno wiąże się z protokołami TCP/IP.
W architekturze CORBA założono wprawdzie charakterystykę wymiany informacji
podobną do stosowanej przez TCP/IP, ale świadomie rozważa się użycie alternatywnych
systemów transportu informacji, takich jak IPX, ATM, XNS firmy Xerox, SNA firmy IBM,
DECnet, a także współużytkowanej pamięci lokalnej ze względu na jej olbrzymią
szybkość działania.
W szczególności użycie współużytkowanej pamięci lokalnej może ogromnie powiększyć
wydajność i jest nieomal obowiązkowe, jeżeli rozważa się zastosowanie architektury
CORBA w obsłudze abstrakcji graficznych na najniższym poziomie  jak to ma miejsce
w sterowaniu systemami okienkowymi (projekt Berlin, http://www.berlin-
consortium.org/)  albo w systemach okienkowych wykorzystujących architekturę
CORBA jako warstwę komunikacyjną między API a kontrolerem grafiki.
Mechanizmy te charakteryzują się całkowicie odmienną semantyką, a w architekturze
CORBA świadomie starano się uniknąć ścisłego powiązania z jakimkolwiek
mechanizmem. Obecnie  wszyscy posługują się TCP/IP, co było nie do pomyślenia
jeszcze dziesięć lat temu, ale można sobie wyobrazić, że w roku 2010. wszyscy będą
korzystać z ATM w swoich osobistych łączach gigabitowych. CORBA może sprostać tym
wymaganiom bez problemów. Niewielka liczba osób zajmujących się rozwojem RPC nie
zdoła zapewne wprowadzić modyfikacji nadążających za takimi zmianami.
Wiele wysiłku projektanci i programiści poświęcają obecnie architekturze CORBA, zaś
RPC praktycznie nie jest już rozwijane, ponieważ firmy sponsorujące te prace zajęły się
właśnie architekturą CORBA. Jeżeli ktoś próbuje rozwijać RPC firmy ONC, to może
okazać się jedynym użytkownikiem wykorzystującym efekty swojej pracy. Istniał
wprawdzie projekt bezpłatnego udostępnienia implementacji sponsorowanej przez DCE,
ale wydaje się, że wyszedł on już z mody.
CORBA i gniazda sieciowe
W podobny sposób, jak w poprzednim podrozdziale, porównamy teraz architekturę CORBA z
gniazdami sieciowymi (ang. sockets):
Użycie IDL do dokładnego definiowania typów danych umożliwia przekaz danych o
odpowiedniej strukturze z jednego systemu do innego, niezależnie od używanego
systemu operacyjnego, protokołu sieciowego, języka programowania oraz pośrednika
ORB.
Gniazda sieciowe obsługują  surowy format danych powiązany z pewnymi
standardowymi protokołami. Ich zastosowanie wymaga, aby aplikacja troszczyła się
zarówno o protokół, jak i o format danych, które mają być przesłane. IDL w architekturze
CORBA określa format, zaś sprawą protokołu zajmuje się ORB, co eliminuje konieczność
przetwarzania surowych danych w aplikacji.
Gniazda sieciowe nie pozwalają na użycie żadnej z ogólnie przyjętych metod
komunikacji asynchronicznej lub komunikacji z zastosowaniem pośredników.
Pośredniki HTTP są na pewno powszechnie stosowane, ale dotyczą one tylko bardzo
specjalizowanego protokołu wykorzystującego gniazda, a nie wszystkich takich
protokołów.
Systemy podobne do CORBA
Oprócz RPC, który omówiliśmy w rozdziale 18., istnieje jeszcze kilka systemów podobnych do
architektury CORBA i zawierających narzędzia wspomagające tworzenie aplikacji rozproszonych.
DCOM lub COM+
Ten system jest rozpowszechniany przez Microsoft i najbardziej przypomina architekturę CORBA.
Pierwotnie DCOM był rozprowadzany jako wersja COM, zaś ostatnio połączenie COM i DCOM
zostało nazwane DCOM+. Dostępna jest także jego wersja dla Linuksa  ma ona nazwę EntireX,
a sposób jej instalacji i konfiguracji został opisany w książce wydawnictwa Wrox Press pt.
Professional Linux Deployment (ISBN 1861002874).
Podobnie jak w architekturze CORBA, w systemie DCOM występuje IDL prawie niezależny od
użytego języka programowania, który służy do opisu interfejsów. Jednak w odróżnieniu od
CORBA, IDL w wydaniu firmy Microsoft zawiera zazwyczaj bardzo szczegółowe informacje o
konfiguracji systemu, łącznie z tymi, które normalnie są zarządzane za pomocą rejestru Windows.
Firma Microsoft dołączyła także wielką liczbę usług, a w tym:
MTS dla obsługi transakcji,
MSMQ dla obsługi komunikacji asynchronicznej,
DAO i ADO wykorzystywane w dostępie do danych.
Powyższe usługi działają równolegle z usługami występującymi w architekturze CORBA.
Ponieważ COM i DCOM są równocześnie produktem wytwórcy systemów, to wyposażono je w
możliwość korzystania ze wszelkich właściwości Win32, takich jak np. rejestr systemowy lub
jednorodna implementacja dyspozytorów i usług. W odróżnieniu od tego, w architekturze CORBA
nie zakłada się jakiejkolwiek dostępności systemu plików, ponieważ elementy systemu muszą
działać nawet w komputerach wbudowanych w urządzenia, wyposażonych w niewiele więcej niż
jakiś czujnik i urządzenie komunikacyjne.
Niemożność przyjęcia założenia o dostępności rejestrów, systemów baz danych itp. powoduje, że
uruchamianie aplikacji CORBA jest skomplikowane.
Z drugiej strony, firmy używające różnorodnego sprzętu komputerowego muszą w zasadzie
odrzucić stosowanie COM/DCOM  albo z powodu posiadania systemów, na których DCOM nie
działa, albo z powodu konieczności wprowadzenia bardzo dużych zmian w elementach
używanych systemów.
Jeżeli trzeba połączyć obiekty CORBA i DCOM w jednym systemie, to można skorzystać z
pewnych dostępnych  pomostów umożliwiających komunikowanie się w obydwie strony.
Konsorcjum OMG prowadzi intensywne prace nad standardami regulującymi takie
współdziałanie.
Metoda zdalnych wezwań (RMI) w języku Java
Jest to rozproszony system obiektowy przeznaczony dla środowiska Java firmy Sun. Nie zawiera
on tak wymyślnych i złożonych usług jak usługi CORBA. Jedynym wymaganiem jest to, aby
system ten współdziałał z językiem Java i to jest powodem jego znacznego uproszczenia w
porównaniu z architekturą CORBA. Zarówno CORBA, jak i RMI (Remote Method Invocation) są
niezależne od sprzętu, ale, niestety, RMI działa tylko z programami napisanymi w języku Java.
Ostatnio trwają prace nad zastosowaniem w RMI tego samego protokołu sieciowego, którym
posługuje się CORBA w komunikacji między pośrednikami, czyli IIOP. Pokazuje to pewną
zbieżność obydwóch systemów. Powiększenie obszaru zastosowań IIOP może prowadzić do
pewnego zbliżenia tych systemów, ale niekoniecznie oznacza to poprawę ich współdziałania.
Enterprise JavaBeans
Enterprise JavaBeans (EJB) posługuje się podobnym schematem działania, jak CORBA i DCOM,
zapewniając unormowany i bardzo wyszukany sposób wzywania rozproszonych elementów, który
stwarza podbudowę dla szybkiego przetwarzania trwale przechowywanych danych.
JavaBeans, podobnie jak Java RMI, ogranicza się tylko do języka Java i doświadcza wszystkich
problemów z wydajnością, na które okazały się podatne systemy wykorzystujące ten język. Jeżeli
kompilatory języka Java zostaną ulepszone, to wydajność przestanie być najważniejszym
problemem.
Szkielet dla elementów CORBA, które mogą współpracować z Enterprise JavaBeans, znajduje się
w fazie opracowywania i podobnie jak w wypadku Java RMI, obydwa systemy zbliżają się do
siebie. Oprócz tego istnieje kilka wdrożeń EJB, takich jak np. ogólnie dostępny system Enhydra,
który udowadnia, że zastosowanie EJB daje praktyczne korzyści. Może to kontrastować z
wcześniejszymi planami dotyczącymi zapewnienia funkcjonalności  złożonego dokumentu za
pomocą OpenDOC w architekturze CORBA, które bez względu na swoją jakość zostały zarzucone
przez firmy Apple i IBM.
MQSeries firmy IBM
Jest to system asynchronicznej wymiany komunikatów, który ma wielu naśladowców, między
innymi w postaci MSMQ firmy Microsoft lub MQ firmy Falcon. Wykorzystano w nim model
całkowicie odwracający założenia architektury CORBA dotyczące połączeń synchronicznych.
Wśród podstaw architektury CORBA znajduje się synchroniczna wymiana komunikatów, w czasie
której klient zestawia synchroniczny  kanał komunikacyjny z serwerem, korzystając z serwerów
pośredniczących tylko wtedy, gdy wymagana jest komunikacja asynchroniczna. W systemach MQ
(Message Queuing) sytuacja jest dokładnie odwrotna: domyślnie komunikacja odbywa się w
sposób asynchroniczny, zaś zestawienie połączenia synchronicznego wymaga specjalnych działań
ze strony programisty.
Obydwa sposoby są użyteczne; usługa wymiany komunikatów w systemie CORBA umożliwia
integrację tej architektury ze wspomnianymi systemami. Dobrze oceniane systemy operacyjne
QNX i BeOS intensywnie korzystają z kolejkowania komunikatów przy zarządzaniu komunikacją
między procesami (a czasem także wewnątrz wątków). Abstrakcja tego rodzaju jest bardzo
przydatna w systemach korzystających ze zdarzeń.
SOAP
Microsoft promuje tzw. prosty protokół dostępu do obiektów (SOAP, od słów Simple Object
Access Protocol). W tym modelu zdalne wywołania procedur odbywają się za pomocą protokołu
HTML, a dane są przekazywane w formacie XML.
Jest prawdopodobne, że ostatecznie będzie to połączone z MSMQ, który zapewni bardziej pewny
transport, a wiadomości pozostaną w formacie XML.
Jeżeli traktuje się XML jako  rozwlekły format, to łatwo się przekonać, że zastosowanie CDR jest
bardziej wydajne, rozważając zarówno ilość przekazywanych danych, jak i nakład pracy
poświęcany na interpretację treści komunikatów. Sloganowa popularność XML na pewno pomoże
w promocji SOAP, niezależnie od jego ograniczonej wydajności.
Ponieważ do tej pory nie spotkaliśmy się z dużymi projektami korzystającymi z SOAP, to jeszcze
jest zbyt wcześnie, aby ostatecznie przesądzać o wartości tego protokołu. Kompresja danych oraz
inne metody  dostrajania protokołu mogą polepszyć jego wydajność.
SOAP znacznie różni się od rozwiązań stosowanych w architekturze CORBA w tym sensie, że
komunikaty są przekazywane albo jako tekst, albo w postaci łatwo przekształcalnej na tekst.
Oznacza to, że przekazywane dane można łatwo sprawdzać i przekształcać podczas transportu, co
przydaje się przy uruchamianiu systemu i wykrywaniu błędów.
Podsumowując te rozważania, należy stwierdzić, że istnieje dość dużo systemów wspomagających
tworzenie aplikacji rozproszonych. Korzystając z nich, można osiągnąć podobne efekty, a
ponieważ wszystkie oferują użyteczne abstrakcje, to dlatego też trwają prace mające na celu
stworzenie możliwości ich współpracy z architekturą CORBA.
W różnych systemach spotyka się obsługę różnych abstrakcyjnych obiektów programowania i
warto poznać ich podstawy, niezależnie od tego, jaki szkielet zostanie ostatecznie wybrany.
Nawet, gdy nie będzie się korzystać z jakiegoś specyficznego systemu, to można przejrzeć jego
implementacje w celu znalezienia metod przydatnych w innych systemach.
W przypadku oprogramowania tworzonego w systemie Linux i współpracującego z GNOME
jedynym sensownym rozwiązaniem jest CORBA. Aby to pokazać, zajmiemy się pośrednikiem o
nazwie ORBit przeznaczonym dla GNOME.
IDL: definiowanie interfejsu
Interfejsy obiektów w architekturze CORBA są konstruowane za pomocą IDL. Język ten
przypomina deklaracje klas C++ lub Java i pomimo że sam nie jest językiem programowania w
ścisłym znaczeniu, jest używany do tworzenia takich deklaracji. Korzystając z IDL, nie można
utworzyć kodu, który  coś robi .
Moduły
Moduły (ang. modules) są w IDL strukturą najwyższego poziomu i wykorzystywane są do
grupowania komunikujących się ze sobą interfejsów w jednej przestrzeni nazw. Czasami może to
powodować konflikty. Wezmy na przykład fragment IDL zawierający dwa moduły o nazwach
ECHOING i NOTECHOING. W podanym niżej wierszu moduł ECHOING udostępnia przestrzeń nazw
dla swojego interfejsu o nazwie Echo:
module ECHOING {
IDL obsługuje tu definicje typów, wykorzystywane bardzo podobnie jak w C lub w C++. Typ
input jest typem lokalnym dla modułu ECHOING:
typedef string input;
Interfejs Echo i zawarte w nim operacje są lokalne w odniesieniu do przestrzeni nazw ECHOING:
interface Echo {
void echo (in input str);
};
};
Moduł NOTECHOING udostępnia oddzielną przestrzeń nazw dla swojego interfejsu o tej samej
nazwie jak poprzedni, czyli Echo:
module NOTECHOING {
interface Echo {
void noecho (in ECHOING::input str);
};
};
Zwróćmy uwagę na to, że używając typu input (lokalnego dla modułu ECHOING), musimy
wskazać za pomocą prefiksu ECHOING::, że ma on być zaimportowany z przestrzeni nazw.
Odbywa się to w podobny sposób, jak zarządzanie odwołaniami do obcych przestrzeni nazw w
języku C++.
W powyższym przykładzie istnieją dwa interfejsy o nazwach Echo oraz jeden operator o nazwie
echo. Interfejsy te będą traktowane jako różne tylko wtedy, gdy są dokładnie zdefiniowane w
oddzielnych modułach.
Pułapki
Należy bardzo ostrożnie używać wielkich liter. W architekturze CORBA zakłada się, że używane
języki programowania nie rozróżniają wielkości liter, dlatego nie ma ona znaczenia w
identyfikatorach. Jeśli więc utworzymy dwie deklaracje o nazwach różniących się tylko wielkością
liter, to kompilator IDL zasygnalizuje to jako błąd.
Podczas tworzenia kodu należy pamiętać o języku programowania, który będzie użyty do
tworzenia kodu klientów usług CORBA. Jeżeli język ten interpretuje znaki w pewien szczególny
sposób (np. znaki podkreślenia), to najlepszym wyjściem będzie rezygnacja z jego stosowania. W
języku C, który nie ma wbudowanego naturalnego  systemu obiektowego , nazwy obiektów
podczas odwzorowania są budowane z innych nazw połączonych ze sobą znakiem podkreślenia.
Na przykład operator noecho w języku C mógłby być nazwany NOTECHOING_Echo_noecho.
Interfejsy
Interfejsy są przeznaczone do grupowania wiążących się ze sobą struktur i operacji.
W naszej aplikacji do obsługi wypożyczalni płyt DVD w module DVD rozsądnie będzie umieścić
następujący zestaw interfejsów:
module DVD {
interface MEMEBERSHIP { // Metody używane do obsługi klientów wypożyczalni
};
interface TITLING { // Metody używane do przetwarzania tytułów filmów
};
interface DISKS { // Metody używane do przetwarzania danych o płytach DVD
};
interface RENTAL { // Metody używane do obsługi wypożyczeń i zwrotów
};
interface RESERVATIONS { // Metody używane do rezerwacji tytułów
};
interface UTILITIES { // Metody dodatkowe, nie wiążące się z innymi
};
interface FACTORY { // Interfejs podstawowy zapewniający dostęp do innych
};
};
Każdy interfejs zapewnia odwołanie do obiektu. Istnieje możliwość sterowania sposobem
tworzenia tych odwołań i przekazywania ich do klientów w aplikacji, która obsługuje
wypożyczalnię dzięki interfejsowi podstawowemu o nazwie FACTORY. Zawiera on operacje
zwracające odwołania do innych interfejsów. Interfejs FACTORY powinien być używany jako
zabezpieczenie pomocnicze, zwracające odwołania tylko w odpowiedzi na żądania klienta, który
uzyskał potwierdzenie autentyczności żądającego. Nie wolno zagnieżdżać ani interfejsów, ani
modułów.
Podstawowe typy danych
W architekturze CORBA zdefiniowano zestaw podstawowych typów danych, które są
elementarnymi składnikami danych przekazywanych między pośrednikami. Mają one format
ogólnej reprezentacji danych (CDR, Common Data Representation). Wymieniono je w poniższej
tabeli:
Rodzaj IDL Opis
short
16-bitowa liczba całkowita
long
32-bitowa liczba całkowita
long long
64-bitowa liczba całkowita
unsigned short
16-bitowa liczba całkowita bez znaku
unsigned long
32-bitowa liczba całkowita bez znaku
unsigned long long
64-bitowa liczba całkowita bez znaku
float
32-bitowa liczba zmiennoprzecinkowa w
formacie IEEE
double
64-bitowa liczba zmiennoprzecinkowa w
formacie IEEE
char
znak o kodzie 8-bitowym
wchar
znak o kodzie 16-bitowym
boolean
typ logiczny przybierający wartość TRUE lub
FALSE
octet
przezroczysty typ znakowy niezmienny podczas
przekazu
any
może reprezentować dowolny typ podstawowy
lub utworzony
Większość z nich jest bardzo podobna do typów występujących w językach C i C++. Warto
wskazać nieco bardziej unikatowy typ octet oznaczający 8-bitowy typ znakowy. Może on być
wykorzystany podczas transmisji danych binarnych, które podczas przekazu mają pozostać nie
zmienione. Jeżeli np. przekazuje się dane tekstowe między systemem UNIX stosującym kod
ASCII i komputerem AS/400 firmy IBM stosującym kod EBCDIC, a dane te są typu char, to
warto użyć pośrednika ORB do automatycznego tłumaczenia danych z pierwotnej postaci na
postać docelową. Dzięki temu można wyeliminować angażowanie klienta i serwera w te
czynności. Jeżeli dane nie mogą się zmienić podczas transmisji, to należy użyć typu octet.
Typy szablonowe
Są to typy określające dane o zmiennym rozmiarze.
Typ sequence
Typ sequence oznacza wektor o zmiennej długości. Elementy tego wektora mogą być dowolnego
typu. Rozmiar wielkości typu sequence może być nieograniczony i wówczas można go użyć do
przekazania nieograniczonej liczby wartości typu string:
sequence message;
Można także ograniczyć rozmiar, podając maksymalną liczbę elementów:
sequence line;
Typy string i wstring
Typ string, podobnie jak w języku C, oznacza wektor składający się ze znaków (typu char) o
zmiennej długości, zakończony elementem NULL. Jego rozszerzeniem jest typ wstring, który
oznacza wektor elementów typu wchar używanych w językach korzystających z wielobajtowego
kodowania znaków.
Typy te są równoważne typowi sequence, ale wyróżniono je dlatego, że w niektórych
językach programowania istnieją funkcje przeznaczone specjalnie do ich wydajnego
przetwarzania.
Typ fixed
Jest to nowy typ, wprowadzony w wersji 2.3 architektury CORBA, który dotychczas nie znalazł
jeszcze uniwersalnego zastosowania. Jest on przeznaczony do przechowywania ułamków
dziesiętnych zawierających do 31 cyfr znaczących o ustalonej pozycji kropki dziesiętnej. Takie
samo znaczenie ma typ PIC używany w języku COBOL; można go także stosować do
przetwarzania danych typu MONEY występujących czasami w SQL.
Typ fixed jest szczególnie przydatny w aplikacjach finansowych, gdy wymagane są ustalone
sposoby zaokrąglania, traktowania nadmiaru i dokładność danych.
fixed<10,2> totalamt;
W językach COBOL i Ada występuje typ danych dziesiętnych o ustalonej długości, ale w innych
językach wymagane jest odpowiednie odwzorowanie IDL zapewniające odpowiednią
reprezentację danych.
W języku C ta reprezentacja lokalna nie jest zupełnie naturalna. Kompilator ORBit języka IDL
generuje bowiem następujący kod:
typedef struct
{
CORBA_unsigned_short _digits;
CORBA_short_scale;
CORBA_char _value[6];
} CORBA_fixed_10_2;
Reprezentuje on spakowane dane w formacie 2 cyfry na bajt, co oznacza, że potrzebne są
specjalne funkcje przetwarzające takie wartości. Nie można tutaj zastosować zwykłych operacji
arytmetycznych występujących w języku C.
Zbudowane typy danych
Takie typy są zbudowane z typów podstawowych. Mogą one zawierać elementy, które same są
także typami zbudowanymi.
Deklaracja typedef
Deklaracja typedef działa tak samo, jak słowo kluczowe typedef w języku C.
typedef long dvdidt;
typedef fixed<10,2> money;
W powyższych wierszach zadeklarowano typ zbudowany dvdidt służący do przechowywania 32-
bitowych liczb całkowitych oraz typ money, który służy do przechowywania danych
dziesięciocyfrowych z dwoma miejscami po przecinku.
Struktury
Struktury przypominają struktury występujące w języku C, lecz mają nieco odmienną składnię:
struct dvddisks {
dvdidt diskid;
TITLING::titlet titleid;
};
W powyższym przykładzie nazwą struktury jest dvddisks, zaś jej element o nazwie diskid jest
typu dvdidt (tak jak zadeklarowano wcześniej), czyli jest 32-bitową liczbą całkowitą.
Element struktury titleid jest typu titlet, który jest zadeklarowany w przestrzeni nazw
TITLING. Przykład ten pokazuje sposób użycia przestrzeni nazw w odwołaniu do obcego typu
zadeklarowanego w innym interfejsie.
Typy wyliczeniowe
Typy te są podobne do typów wyliczeniowych używanych w językach C i C++.
enum movieratings { G, PG13, NC17, R, NR, XXX };
Zwróćmy jednak uwagę na to, że nie ma tu możliwości określenia wartości początkowej, tak jak w
językach C i C++. Oznacza to, że między klientami a serwerami nie można bezpośrednio
przekazywać wartości porządkowych typu wyliczeniowego. To ograniczenie wprowadzono
celowo, ponieważ w niektórych językach programowania typy wyliczeniowe są obsługiwane bez
korzystania z jakichkolwiek liczb. Jako przykład można podać odwzorowanie w Common Lisp, w
którym typy wyliczeniowe są reprezentowane bezpośrednio jako symbole, czyli:
(setf ratings-list (:G :PG13 :NC17 :R :NR :XXX))
W przykładowej aplikacji nie będziemy korzystać z wyliczeń tego rodzaju, ponieważ mogłoby to
przestawić aplikację na ustalony i niezmienny zestaw rodzajów filmów. Zamiast tego wartości
będą przechowywane w tabeli, a więc można będzie je modyfikować w dowolnym momencie bez
konieczności modyfikacji interfejsu.
Tablice
W skład tablicy wchodzą wektory o ustalonej liczbie elementów. Deklaracja jednowymiarowej
tablicy ma postać:
typedef char line[80];
Deklaracja dwuwymiarowej tablicy ma postać:
typedef double bezier[3][3];
Unie
Unie w języku IDL różnią się od konstrukcji spotykanych w językach C i C++. W IDL muszą one
być dyskryminowane, można w nich stosować etykiety case: wielokrotnie w tym samym
elemencie oraz używać domyślnej alternatywy (default):
union PCode switch(Country) { /* Międzynarodowe kody pocztowe */
case USA: /* Na przykład 75063-2921 */
long main;
short sub;
case Canada: /* Na przykład K0A 2N0 */
char p1;
short p2;
char p3;
short p4;
char p5;
short p6;
default:
string pcode;
};
W powyższym przykładzie zadeklarowano reprezentację międzynarodowych kodów pocztowych,
których format zależy od kraju podanego w polu Country. Określono tu specjalne formaty dla
Kanady i USA, zaś dla pozostałych krajów używany jest po prostu pierwotny napis.
Operacje
Operacje reprezentują metody obiektów i wyglądają podobnie jak wywołania funkcji w językach
C lub C++. Zawierają one:
Opcjonalny atrybut operacji określający semantykę przekazu danych  obsługiwany jest
jedynie atrybut oneway oznaczający przetwarzanie asynchroniczne, przy którym
wywołanie kończy się natychmiast bez zwracania jakiejkolwiek wartości. Ten atrybut jest
już przestarzały w porównaniu z usługami wymiany komunikatów (Messaging Service) w
architekturze CORBA, które zapewniają lepiej określone właściwości oceny jakości
usługi,
Specyfikację typu określającą typ wartości zwracanej przez metodę. Może to być
dowolny typ zadeklarowany w IDL lub typ void
Identyfikator określający nazwę operacji
Listę parametrów określających dane przekazywane do serwera lub zwracane przez
serwer. Parametry te zawierają atrybut parametru określający kierunek przekazu.
Dozwolonymi atrybutami parametrów są:
in
Atrybut informujący, że parametr jest
przekazywany od klienta do serwera.
out
Atrybut informujący, że parametr jest
przekazywany od serwera do klienta.
inout
Atrybut informujący, że parametr jest
przekazywany w obu kierunkach.
Ten atrybut może być obsługiwany w taki sposób, że usuwane są wartości in i
przydzielane nowe wartości out, zatem nie można zakładać, że dane pozostaną w tym
samym miejscu,
Specyfikację typu parametru,
Nazwę parametru określającą jaki parametr ma być wywołany.
Poniżej podano kilka przykładów operacji. Oto najprostsza z nich:
void simplemethod ();
A oto metoda nadająca wartość:
void set(in storemembers recordtoupdate);
Oto metoda wyszukująca wartość memberid i zwracająca wartość recordcomplete:
void get(in memberidt memberid,
out storemembers recordcomplete)
raises (NOSUCHMEMBER);
Jeżeli operacja się nie uda, to serwer zasygnalizuje ten fakt, zwracając wyjątek NOSUCHMEMBER.
Metoda zmieniająca swoje dane wejściowe ma postać:
long modifyrecord(inout memrecordtype member)
raises (TOTALLYMESSEDUP, TOTALLYRADICALDUDE);
Zastosowano tutaj opcjonalne wyrażenie raises określające wyjątki zadeklarowane w aplikacji,
które mogą być zwracane przez daną operację. Kilka pokazanych wyżej operatorów zawiera
deklaracje wyrażeń obsługujących wyjątki, które zostaną omówione w następnym podrozdziale.
Opcjonalne wyrażenie kontekstowe określa informację kontekstową. Bardzo mocno przypomina
to sposób używany przy przekazywaniu zmiennych środowiskowych do procesu w systemie
UNIX. Niestety, wartości kontekstowe nie zachowują typu, co stwarza problemy przy ich
wyszukiwaniu, dlatego ich użycie nie jest polecane.
Wyjątki
Byłoby pięknie, gdyby interfejsy działały w prosty sposób, bez potrzeby interpretacji wyników.
Niestety, nie wszystko przebiega w zamierzonej kolejności. Interfejsy mogą np. żądać od usług
nazewniczych informacji o usłudze logowania, która nie została zarejestrowana. Może się także
zdarzyć, że żądana jest informacja o kliencie, który nie istnieje, albo informacja, do przetwarzania
której brak uprawnień. Aby obsłużyć takie sytuacje, w architekturze CORBA na poziomie
systemowym zdefiniowano wyjątki wykorzystywane w komunikacji z pośrednikiem przy
napotkaniu na błąd. Oto one:
UNKNOWN PERSIST_STORE
BAD_PARAM BAD_INV_ORDER
NO_MEMORY TRANSIENT
IMP_LIMIT FREE_MEM
COMM_FAILURE INV_IDENT
INV_OBJREF INV_FLAG
NO_PERMISSION INTF_REPOS
INTERNAL BAD_CONTEXT
MARSHAL OBJ_ADAPTER
INITIALIZE DATA_CONVERSION
NO_IMPLEMENT OBJECT_NOT_EXIST
BAD_TYPECODE TRANSACTION_REQUIRED
BAD_OPERATION TRANSACTION_ROLLEDBACK
NO_RESOURCES INVALID_TRANSACTION
NO_RESPONSE
W IDL również istnieje możliwość deklaracji wyjątków, które stanowią albo logiczną
reprezentację wskaznika stanu, w którym wystąpił błąd:
exception NOSUCHMEMBER {};
albo mogą być rozwijane szerzej, informując o swojej istocie (w takim wypadku klient może
zareagować w sposób bardziej odpowiedni dla danego problemu):
exception FIELDOVERFLOW {long maxsize, long offendingsize,
string shorttext, string longtext};
Standardowe wyjątki w architekturze CORBA mogą być podnoszone w dowolnym miejscu.
Najczęściej zdarza się to w pośredniku ORB lub w szkieletach funkcji, gdy na poziomie ORB
występują niepowodzenia podczas wzywania operacji. W odróżnieniu od tego, należy jawnie
zadeklarować miejsce, w którym mają być użyte deklaracje wyjątków określone we własnym
kodzie IDL, tak jak poniżej:
void updatesurname (in memberidt memberid, in string surname)
raises (FIELDOVERFLOW);
Atrybuty
Atrybuty pozwalają na zdeklarowanie  za pomocą jednej operacji  zmiennej stanu związanej z
interfejsem, której wartości można zarówno nadawać (set), jak i pobierać (get). Dwa podane
niżej interfejsy IDL są sobie równoważne:
interface SomeDumbInterface{
attribute int count;
};
interface SomeDumbInterface{
int _get_count;
void _set_count(in int c);
};
Można tu także użyć atrybutu readonly, który spowoduje pomijanie funkcji _set_count.
Przykład aplikacji obsługującej wypożyczalnię DVD
Poniżej podano przykładowy kod IDL opisujący API dla aplikacji obsługującej wypożyczalnię płyt
DVD:
module DVD {
typedef string datec;
// IDL jest podzielony na kilka interfejsów:
// MEMBERSHIP - do obsługi zbioru klientów wypożyczalni
// TITLING - do obsługi _dostępnych_ tytułów płyt
// DISKS - do obsługi zbioru fizycznych płyt DVD, które są
// dostępne do wypożyczenia
// RENTAL - obsługa transakcji wypożyczania płyt
// RESERVATIONS - obsługa rezerwacji tytułów na przyszłość
// UTILITIES - niektóre operacje pomocnicze
// FACTORY - interfejs zapewniający możliwość odwoływania się
// do innych interfejsów
// Metody stosowane w obsłudze klientów wypożyczalni
interface MEMBERSHIP {
exception NOSUCHMEMBER {};
// Wewnętrzna reprezentacja jest typu long integer
typedef long memberidt;
// memberidList użyto w tym celu, aby operatory mogły zwracać
// listę klientów wypożyczalni
typedef sequence memberidList;
// Infromacja o każdym kliencie:
struct storemembers {
memberidt memberid; /* wewnętrzny id [1..] */
string memberno; /* numer klienta, którym się on posługuje */
string title; /* Mr Mrs Ms Dr Sir */
string fname; /* imię */
string lname; /* nazwisko */
string houseflatref; /* np. 5 lub 'The birches' itd. */
string address1; /* pierwszy wiersz adresu */
string address2; /* drugi wiersz adresu */
string town; /* miejscowość */
string state; /* stan - wymagany tylko w USA */
string phone; /* nr telefonu w postaci +44(0)123 456789 */
string zipcode; /* LE1 1AA lub kod pocztowy innego rodzaju */
};
Dalej występują operatory pozwalające tworzyć, usuwać, pobierać i nadawać wartości klientom
wypożyczalni:
void set(in storemembers recordtoupdate);
void get(in memberidt memberid,
out storemembers recordtocomplete)
raises (NOSUCHMEMBER);
void create (in storemembers recordtoadd,
out memberidt memberid);
void delete (in memberidt memberid)
raises (NOSUCHMEMBER);
Podczas wyszukiwania nazwiska otrzymujemy listę klientów wypożyczalni o nazwiskach
pasujących do wzorca:
void search (in string lname,
out memberidList resultids);
// idfromnumber przekształaca "publiczny" numer klienta na wartości
// używane wewnętrznie.
void idfromnumber (in string memberno,
out memberidt memberid)
raises (NOSUCHMEMBER);
};
Metody służące do manipulacji tytułami płyt DVD:
interface TITLING {
exception NOSUCHTITLE {};
exception NOSUCHGENRE {};
typedef string classif;
typedef sequence classList;
typedef string genres ;
typedef sequence genreList;
typedef long titlet;
typedef sequence titleList;
struct dvdtitles {
titlet titleid; /* wewnętrzny ID [1..] */
string titletext; /* np. 'Milczenie owiec' */
string asin; /* 10-cyfrowy numer katalogowy */
string director; /* nazwisko reżysera */
genres genre; /* rodzaj, np. 'Horror', 'comedy' itd.
API dla standardowej listy dalej */
classif classification;
string actor1; /* np. 'Jeremy Irons' */
string actor2; /* np. 'Ingmar Bergman' */
datec releasedate; /* data YYYYMMDD plus null */
string rentalcost; /* cena za dzień wypożyczenia
w formacie $$$.cc */
};
Zauważmy, że set, get, create, delete i search są powtórnie użyte w przestrzeni nazw
TITLING. Będą one też ponownie używane w następnych interfejsach:
void set (in dvdtitles recordtoupdate);
void get (in titlet titleid,
out dvdtitles recordtocomplete)
raises (NOSUCHTITLE);
void create (in dvdtitles recordtoadd,
out titlet titleid);
void delete (in titlet titleid)
raises (NOSUCHTITLE);
void search (in string title, in string name,
out titleList resultids);
};
// Metody używane do manipulacji płytami DVD
//
Dotyczy to rzeczywistego, fizycznego egzemplarza płyty DVD znajdującego się w wypożyczalni,
który może być wypożyczony klientowi (np. jedna z siedmiu kopii  Siedmiu samurajów ):
interface DISKS {
exception NOSUCHDISK {};
typedef long dvdidt;
typedef sequence dvdList;
struct dvddisks {
dvdidt diskid; /* wewnętrzny ID [1..] (nie powiązany z title_id) */
TITLING::titlet titleid;
} ;
// Zwróćmy uwagę na powtórne użycie tych samych nazw operatorów:
// set/get/create/delete
void set (in dvddisks recordtoupdate);
void get (in dvdidt diskid,
out dvddisks recordtocomplete)
raises (NOSUCHDISK);
void create (in dvddisks recordtoadd,
out dvdidt diskid);
void delete (in dvdidt diskid)
raises (NOSUCHDISK);
void search (in TITLING::titlet titleid,
out dvdList resultids);
};
Interfejsy RENTAL i RESERVATIONS posługują się bardziej złożonymi operatorami, niż można by
wnioskować z ich opisowych nazw:
interface RENTAL { // Metody używane do obsługi wypożyczeń i zwrotów płyt
exception FORBIDDENRATING {}; // Dzieci nie mogą wypożyczać pornografii
void renttitle (in MEMBERSHIP::memberidt memberid,
in TITLING::titlet titleid,
out DISKS::dvdidt diskid)
raises (DISKS::NOSUCHDISK, MEMBERSHIP::NOSUCHMEMBER,
TITLING::NOSUCHTITLE, FORBIDDENRATING);
void rentdiskinfo (in DISKS::dvdidt diskid,
out MEMBERSHIP::memberidt memberid,
out datec daterented)
raises (DISKS::NOSUCHDISK);
void diskreturn (in DISKS::dvdidt diskid,
in datec returndate,
out MEMBERSHIP::memberidt memberid)
raises (DISKS::NOSUCHDISK);
long titleavailable (in TITLING::titlet titleid, in datec date)
raises (TITLING::NOSUCHTITLE);
void overduedisks (in datec fromdate, in datec todate,
out DISKS::dvdList latedisks);
};
interface RESERVATIONS { // Metody obsługujące rezerwację tytułów
void reservetitle (in datec needed, in TITLING::titlet titleid,
in MEMBERSHIP::memberidt memberid)
raises (TITLING::NOSUCHTITLE, MEMBERSHIP::NOSUCHMEMBER,
RENTAL::FORBIDDENRATING);
void cancelreservation (in MEMBERSHIP::memberidt memberid)
raises (MEMBERSHIP::NOSUCHMEMBER);
void queryreservationbymember (in MEMBERSHIP::memberidt memberid,
out TITLING::titlet titleid)
raises (MEMBERSHIP::NOSUCHMEMBER);
void queryreservationbytitle (in TITLING::titlet titleid,
in datec date,
out MEMBERSHIP::memberidList memberids)
raises (TITLING::NOSUCHTITLE);
};
// Pozostałe metody, których nie można zaklasyfikować gdzie indziej
interface UTILITIES {
void getgenres(out TITLING::genreList genrelist);
void getclassifications (out TITLING::classList classlist);
void errtext(in long errnumber, out string messagetoshow);
void today(out datec date);
// Czas _powinien_ być obsługiwany przez Time Service, ale nasze
// wymagania są znacznie prostsze
};
Interfejs FACTORY jest interfejsem podstawowym, w którym zawarto operatory zwracające
odwołania do wszystkich pozostałych interfejsów. Reprezentuje on swego rodzaju automatyczne
uruchamianie systemu. Można zmienić sposób rozpowszechniania systemu, zmieniając sposób
zwracania tych interfejsów:
interface FACTORY {
MEMBERSHIP MEMBERSHIPFactory ();
TITLING TITLINGFactory ();
DISKS DISKSFactory ();
RENTAL RENTALFactory ();
RESERVATIONS RESERVATIONSFactory ();
UTILITIES UTILITIESFactory ();
};
};
Odwzorowania języków
Ponieważ CORBA ma współpracować z różnymi językami programowania, to trzeba ustalić, w
jaki sposób opisane w IDL funkcje i struktury danych będą reprezentowane w języku użytym do
zaprogramowania procesów klienta i serwera. Każdy użyty tu język wymaga odwzorowania.
Patrząc z perspektywy historycznej, pierwsze pojawiły się odwzorowania języków C i Smalltalk,
zaś dzisiaj najbardziej rozpowszechnione i rozwijane są prawdopodobnie odwzorowania dla
języków C++ i Java.
Nie ma potrzeby tworzenia uniwersalnego wsparcia o pełnej funkcjonalności dla wszystkich
języków, jeśli np. wersja Emacs Lisp może być użyta tylko w programie klienta. Może się okazać,
że łatwiej użyć języków dynamicznych lub skryptowych, takich jak np. Python lub Lisp do obsługi
obiektów w dynamicznie wzywanych interfejsach (DII, czyli Dynamic Invoked Interface),
ponieważ nie rozpoznaje się w nich typów wartości w fazie działania programu  co jest
całkowicie zgodne z DII. Jedno odwzorowanie języka Python o nazwie PyORBit wygląda
całkowicie wydajnie tylko w zastosowaniu dla DII.
Formalne specyfikacje odwzorowań podane przez konsorcjum OMG dotyczą następujących
języków:
Ada,
C,
C++,
COBOL,
Java,
Smalltalk,
Common Lisp
Jeżeli ktoś ma zamiar używać któregokolwiek z powyższych odwzorowań, to warto pobrać pełną
dokumentację ropozpowszechnianą przez konsorcjum OMG w postaci dokumentów w formatach
Adobe PDF i Postscript
(http://www.omg.org/technology/documents/formal/corba_language_mapping_specifica.htm).
Istnieją także odwzorowania dla innych języków, które nie zostały oficjalnie zaakceptowane przez
MOG, a były opracowane w sposób nieco mniej sformalizowany przez zainteresowane
organizacje. Należą do nich:
Odwzorowanie języka Eiffel na IDL w architekturze CORBA,
Odwzorowanie IDL na język Python (http://orbit-python.sault.org/),
Odwzorowanie IDL na język Erlang (http://www.erlang.se/doc/doc-4.7.3/lib/orber-
2.0/doc/html/ch_erl_map.html),
Obsługa języka Haskell w architekturze CORBA
(http://www.cse.unsw.edu.au/~chak/haskell/gnome/gnome-small/node11.html),
Odwzorowanie języka Perl w architekturze CORBA
(http://people.redhat.com/otaylor/corba/orbit.html),
Implementacja architektury CORBA po stronie klienta Emacs o nazwie corba.el
(http://www.lysator.liu.se/~lenst/corba.el).
Składniki odwzorowania języka
Odwzorowanie zapewnia środki służące do wyrażania następujących informacji w danym języku:
wszystkich podstawowych typów danych występujących w IDL,
wszystkich zbudowanych typów danych występujących w IDL,
odwołań do stałych zadeklarowanych w IDL,
odwołań do obiektów zadeklarowanych w IDL,
wezwań operacji, łącznie z transmisją parametrów i odbiorem wyników,
wyjątków, łącznie z tymi, które opisują, co się dzieje przy obsłudze wyjątku, oraz
transmisji parametrów opisujących wyjątki,
dostępu do atrybutów,
sygnatur dla operacji zdefiniowanych w ORB, takich jak np. w DII adapterów obiektów
itp.
Dobre odwzorowanie pozwala programistom na dostęp do wszystkich funkcji ORB i wyrażenie
ich w sposób wygodny dla danego języka programowania. Jeżeli język nie obsługuje sam z siebie
funkcjonalności IDL, tak jak np. obsługa wyjątków w języku C, to odwzorowanie może być w
takim przypadku nieco zagmatwane.
Celem normalizacji odwzorowania jest zapewnienie, że ten sam kod może być stosowany w
dowolnym pośredniku ORB. Oprócz tego, bardzo irytująca byłaby sytuacja, gdyby np. kod w
języku C++ przeznaczony do pośrednika omniORB nie mógł być zastosowany z pośrednikiem
TAO i cała aplikacja wymagałby gruntownej przeróbki.
Odwzorowania dla języka C
W tym podrozdziale zapoznamy się z różnymi odwzorowaniami IDL dla języka C. Wiele z nich
wykorzystuje IDL zastosowany w aplikacji obsługującej wypożyczalnię płyt DVD. W
rzeczywistości poznamy jedynie niewielki ułamek tego, co jest wyrażane za pomocą
standardowego odwzorowania języka C, ale nawet to pokaże sposób działania całości.
Podstawowe odwzorowania typów danych IDL
Podstawowe odwzorowania typów danych występujących w architekturze CORBA na typy języka
C są następujące:
Typ IDL Odwzorowanie typu w C
short CORBA_short
unsigned short CORBA_unsigned_short
long CORBA_long
unsigned long CORBA_unsigned_long
long long CORBA_long_long
unsigned long long CORBA_unsigned_long_long
float CORBA_float
double CORBA_double
long double CORBA_long_double
boolean CORBA_boolean
char CORBA_char
wchar CORBA_wchar
Zamiast korzystać z konstrukcji w rodzaju long some_long_variable, w programach trzeba
używać konstrukcji CORBA_long some_long_variable. Powoduje to użycie specyficznego
typu danych wymaganego w architekturze CORBA, a nie pozostawienie wyboru dla konkretnej
implementacji języka C. Ten problem może nie być aż tak bardzo ważny w przypadku 32-bitowej
architektury wykorzystującej procesory firmy Intel, gdzie większość typów jest w zasadzie zgodna
z typami stosowanymi w CORBA, ale na pewno warto o nim pamiętać przy przejściu do
procesorów 64-bitowych, gdzie typy  rodzime są przeważnie także 64-bitowe.
W interfejsie MEMBERSHIP, zamiast typedef long memberidt, mamy więc następujące
wyrażenie:
typedef CORBA_long DVD_MEMBERSHIP_memberidt;
Dla typedef long titlet w interfejsie TITLING występuje:
typedef CORBA_long DVD_TITLING_titlet;
Zbudowane i szablonowe typy danych
Odwzorowanie zawiera:
Napisy
Rozważmy napis (string) oznaczający datę:
typedef string datec;
Odwzorowanie C dla tej definicji jest następujące:
typedef CORBA_char *DVD_datec;
Uwidacznia to fakt, że napisy w języku C są po prostu tablicami znakowymi.
Struktury
Odwzorowanie w tym przypadku nie wnosi niespodzianek. Konstrukcja IDL dla storemembers
w interfejsie MEMBERSHIP w module DVD jest następująca:
struct storemembers {
memberidt memberid; /* wewnętrzny identyfikator [1..] */
string memberno; /* numer, którym posługuje się klient */
string title; /* Mr Mrs Dr */
string fname; /* imię */
string lname; /* nazwisko */
string houseflatref; /* np. 5 lub 'The birches' itd. */
string address1; /* 1. wiersz adresu */
string address2; /* 2. wiersz adresu */
string town; /* miejscowość */
string state; /* stan - wymagany tylko w USA */
string phone; /* +44(0)123 456789 */
string zipcode; /* LE1 1AA lub jakikolwiek kod pocztowy */
To odwzorowuje się na następującą strukturę w języku C:
typedef struct
{
DVD_MEMBERSHIP_memberidt memberid;
CORBA_char *memberno;
CORBA_char *title;
CORBA_char *fname;
CORBA_char *lname;
CORBA_char *houseflatref;
CORBA_char *address1;
CORBA_char *address2;
CORBA_char *town;
CORBA_char *state;
CORBA_char *phone;
CORBA_char *zipcode;
} DVD_MEMBERSHIP_storemembers;
Nazwa modułu, interfejsu i struktury są łączone ze sobą do postaci stosowanej w języku C, czyli
DVD_MEMBERSHIP_storemembers.
Sekwencje
Sekwencje powstają w wyniku generacji struktury pobierającej składowe do tablicy. Na przykład
w interfejsie MEMBERSHIP zadeklarowano sekwencję memberidList:
typedef sequence memberidList;
Powoduje to generację następującej struktury:
typedef struct
{
CORBA_unsigned_long _maximum,
Dozwolone jest tu użycie _maximum, które określa maksymalną liczbę elementów tworzących
sekwencję:
_length;
Element _length jest obowiązkowy i reprezentuje liczbę wpisów przekazanych do sekwencji:
DVD_MEMBERSHIP_memberidt *_buffer;
Tutaj _buffer jest tablicą wskazników na różne przekazywane wartości, które w tym przypadku
w IDL będą typu memberidt, zaś w języku C będą typu DVD_MEMBERSHIP_memebridt:
CORBA_boolean _release;
Zmienna _release informuje, czy informacja o sekwencji powinna być wydana przez ORB po
zakończeniu transmisji danych, czy nie:
} CORBA_sequence_DVD_MEMBERSHIP_memberidt;
Aby użyć takiej sekwencji w języku C, należy posłużyć się następującym kodem:
/* Ustawienie zmiennej sekwencyjnej */
CORBA_sequence_DVD_MEMBERSHIP_memberidt membs;
/* Przydzielenie wpisów w sekwencji */
membs._buffer = CORBA_alloc_DVD_MEMBERSHIP_memberidt(6);
membs._length = 6; /* Ustalenie liczby wpisów */
membs._buffer[0] = member1;
membs._buffer[1] = member2;
membs._buffer[2] = member3;
membs._buffer[3] = memebr4;
membs._buffer[4] = member5;
membs._buffer[5] = member6;
Uzyskaliśmy w ten sposób membs, czyli wielkość, którą można przekazać jako argument do
serwera lub pobrać jako wynik do klienta.
Typy wyliczeniowe
Podany niżej przykład nie występuje w naszej aplikacji, ale można go tam użyć:
enum emovieratings {G, PG13, NC17, R, NR, XXX};
Można to odwzorować jako:
typedef enum { DVD_G, DVD_PG13, DVD_NC17, DVD_R, DVD_NR, DVD_XXX }
DVD_emovieratings;
Tablice
Mamy następującą definicję:
typedef double memid[8][12];
Uzyskujemy definicje odwzorowane:
typedef CORBA_double DVD_memid[8][12];
typedef CORBA_double DVD_memid_slice[12];
Wielkości o nazwie slice reprezentują wycinki tablicy, które w razie potrzeby mogą być
przydzielane oddzielnie.
Odwołania do stałych
Można zadeklarować stałe różnych typów podstawowych, które w języku C zostaną odwzorowane
za pomocą dyrektyw #define. Mogą one zawierać obliczenia z zakresu podstawowych działań
arytmetycznych i operatory logiczne (czyli np. dodawanie, mnożenie, koniunkcja i przesunięcia
bitowe).
Prawdopodobnie najlepiej sprawdzić to na przykładach, szkicując je w IDL i badając
odpowiedniki w C. Oto niektóre deklaracje stałych różnego typu w IDL; w kilku z nich
wykorzystano obliczenia:
module CONSAMPLES {
enum Colour { Red, Green, Blue, Brown, Fuchsia, Lime};
const Colour FAVORITECOLOUR = Fuchsia;
const long LV = 3;
const long long sdiff = (2500000 << 4) - (254 >> 2) * LV;
const double Pi = 3.14159265358979323846;
const double Piover2 = Pi / 2.0;
IDL zostaje przekształcony w następujący zestaw deklaracji języka C:
typedef enum {
CONSAMPLES_Red, CONSAMPLES_Green, CONSAMPLES_Blue,
CONSAMPLES_Brown, CONSAMPLES_Fuchsia, CONSAMPLES_Lime
} CONSAMPLES_Colour;
#define CONSAMPLES_FAVORITECOLOUR CONSAMPLES_Fuchsia
#define CONSAMPLES_LV 3
#define CONSAMPLES_sdiff 39999811
#define CONSAMPLES_Pi 3.141593
#define CONSAMPLES_Piover2 1.570796
Wzywanie operacji
Stanowi to całą istotę tego, czym zajmuje się CORBA. Oto kilka operacji zadeklarowanych w IDL:
module opsamples {
interface ops {
void op1 (); /* Nie przekazuje żadnych wartości */
void op2 (in long inl); /* Przekazuje jako long */
void op3 (in long inl, out long outl); /* Odbiera i wysyła jako long */
long op4 (inout long iol); /* Zmienia wartość long na long */
};
};
Odwzorowują się one na następujące deklaracje języka C:
/** prototypy **/
void opsamples_ops_op1(opsamples_ops _obj, CORBA_Environment * ev);
void opsamples_ops_op2(opsamples_ops _obj, const CORBA_long inl,
CORBA_Environment * ev);
void opsamples_ops_op3(opsamples_ops _obj, const CORBA_long inl,
CORBA_long * outl, CORBA_Environment * ev);
CORBA_long opsamples_ops_op4(opsamples_ops -obj, CORBA_long * iol,
CORBA_Environment * ev);
Kod serwera używa tych funkcji, zaś kod klienta będzie z nich korzystał, żądając wykonania
jakichś czynności.
Obsługa wyjątków
Jest to jedyny obszar, w którym odwzorowanie w języku C jest szczególnie mocno pogmatwane.
IDL definiuje strukturalny system obsługi wyjątków podobny do stosowanego w C++. Język C
nie dysponuje tego rodzaju właściwościami i w wyniku tego jego odwzorowanie musi zawierać
dodatkowe wartości zwracane przez każdą metodę, która zwraca  środowisko wyjątku i które
następnie musi być interpretowane przez program. Można się o tym przekonać, próbując
zbudować funkcje pomocnicze tak, aby użyć  rodzimego odwzorowania tylko kilkakrotnie.
Załóżmy, że deklarujemy zestaw wyjątków następująco:
module main {
exception foo {}; // Wyjątek BEZ argumentu
interface secondary {
exception bar { //Wyjątek Z argumentem
string msg;
};
};
};
Odwzorowanie w języku C daje wówczas następujące deklaracje:
#define ex_main_foo "IDL:main/foo:1.0"
#define _main_foo_defined 1
typedef struct {
int dummy;
} main_foo;
#define ex_main_secondary_bar "IDL:main/secondary/bar:1.0"
#define _main_secondary_bar_defined 1
typedef struct {
CORBA_char *msg;
} main_secondary_bar;
Po powrocie z wezwania operacji CORBA generowana jest struktura wyjątku w następującej
postaci:
typedef struct CORBA_environment {
CORBA_exception_type _major;
... inne fragmenty zależne od ORB
} CORBA_environment;
Wartość _major może zawierać albo CORBA_NO_EXCEPTION (co oznacza brak wyjątku), albo
CORBA_USER_EXCEPTION (wskazuje to na jeden z wyjątków zadeklarowanych w wywołanym
IDL), albo CORBA_SYSTEM_EXCEPTION (jeżeli został wywołany jeden ze standardowych
wyjątków architektury CORBA). Nazwę wyjątku można określić, wywołując
CORBA_exception_id, zaś jego wartość  wywołując CORBA_exception_value.
Atrybuty
To odwzorowanie najlepiej pokazać na przykładzie. Załóżmy, że deklarujemy zestaw wyjątków:
module attsamp {
interface circle {
attribute float radius;
};
};
Odwzorowanie w języku C daje w wyniku następujące deklaracje:
/** prototypy **/
CORBA_float attsamp_circle__get_radius(attsamp_circle _obj,
CORBA_Environment * ev);
void attsamp_circle__set_radius(attsamp_circle _obj,
const CORBA_float value,
CORBA_Environment * ev);
Pokazane wyżej metody będą używane jako zwykłe operatory, co oznacza, że zmniejszenie
nakładu pracy występuje tylko przy pisaniu kodu IDL, co zazwyczaj nie sprawia kłopotów.
Przykład wprowadzający: prosty system
powiadamiania
Rozpoczniemy od pokazania pełnego kodu zupełnie prostego systemu. Program będzie
przekazywał dwa argumenty: nazwę nadawcy i nazwę odbiorcy, a następnie odczytywał
komunikat ze stdin i dostarczał go do serwera komunikatów CORBA. Pełny kod jest dostępny na
serwerze ftp wydawnictwa Helion (ftp://ftp.helion.pl/przyklady/zaprli.zip).
Rozważany przykład pokazuje następujące zagadnienia:
niewielki, ale kompletny interfejs IDL,
sposób użycia IDL dla pośrednika ORBit do generacji plików szkieletowych,
kod podstawowego programu maskującego potrzebnego w tym celu, aby w aplikacji
można było użyć architektury CORBA,
obsługę i dostarczanie danych do IOR,
możliwość współdziałania z wieloma językami (serwer został napisany w języku Python).
Prosta wymiana komunikatów
Aplikacja pokazana jako przykład służy do przesyłania komunikatów do serwera. Komunikat
(wiadomość) zawiera następujące elementy:
identyfikator  nadawcy ,
identyfikator  odbiorcy , dla którego jest przeznaczony komunikat,
sekwencja wierszy tworzących właściwą treść komunikatu.
Nie jest to aplikacja, którą można bezpośrednio wykorzystać w praktyce, ale jeżeli rozszerzy się
pojęcia  nadawcy i  odbiorcy i dołączy kilka dodatkowych pól, to można znalezć podobieństwo
do bardzo wielu programów zajmujących się przesyłaniem wiadomości z jednego miejsca do
innego.
IDL dla tej aplikacji ma następującą postać:
module MESSAGING {
typedef sequence#&60;string#&62; msgLines;
struct msg {
string fr;
string to;
msgLines body;
};
interface mail {
void submit (in msg message);
};
};
Zastosowanie ORBit z IDL
Pierwotnie w projekcie GNOME używany był pośrednik o nazwie ORBit, a więc nasza aplikacja
właśnie z niego będzie korzystać. Jeżeli w systemie są zainstalowane narzędzia programistyczne
GNOME, to wśród nich powinien być także i ORBit.
Zachowajmy IDL MESSAGES w pliku o nazwie msg.idl. Polecenie orbit-idl msg.idl
spowoduje utworzenie czterech plików, które są zbyt rozwlekłe, aby je tutaj szczegółowo
omawiać:
Msg.h
Zawiera publiczne typy danych i definicje funkcji, które będą używane przez
każdy program korzystający z interfejsów zadeklarowanych w msg.idl.
msg-common.c
Ten plik zawiera zwykłe funkcje i typy danych, które będą używane w
interfejsach zarówno przez klienta, jak i przez serwery. W szczególności są tu
funkcje do przydzielania i uwalniania struktur danych zdefiniowanych w
interfejsach.
msg-skels.c
Ten plik zawiera  szkielety funkcji i inne definicje wymagane przez ORBit
do odbioru żądań. Zadaniem tego pliku jest przechowywanie funkcji
odbierających żądania bezpośrednio od ORBit, rozdzielających je i
przekazujących do funkcji C obsługujących operacje. Oprócz tego są tu
funkcje pobierające wartości przekazywane przez klienty, rozdzielające je i
przekazujące ponownie do pośrednika. Dlatego właśnie powinny one zostać
wkompilowane w kod serwera komunikatów, wykorzystującego ORB.
msg-stubs.c
Ten plik zawiera wyniki konwersji  szkieletów z pliku msg-skels.c,
zapewniając zręby komunikacji między żądaniem generowanym w programie
a pośrednikiem. Zręby te zarządzają rozdzielaniem żądań i ich wysyłaniem, a
następnie ich odbiorem i łączeniem wyników.
Klient komunikatów
Utworzymy program klienta w języku C, dzięki czemu zobaczymy odwzorcowania C:
/* Prosty klient */
#include "stdio.h"
#include "orb/orbit.h" /* Nagłówek ORBit */
#include "msg.h" /* Plik nagłówkowy IDL */
int readlines (void);
#define MAXMSGLEN 2500 /* Maksymalna liczba wierszy w msg */
char *contents[MAXMSGLEN+1];
int main (int argc, char *argv[])
{
/* Zmienne używane do odczytu IOR */
FILE *ifp; char *ior, filebuffer[1024];
/* Zmienne krytyczne dla POA */
CORBA_Environment ev;
CORBA_ORB orb;
CORBA_Object msg_client; /* Aącze do obiektu Message */
/* Zmienne używane do przechowywania danych wiadomości, która
ma być wysłana. */
MESSAGING_msg *ourmessage;
MESSAGING_msgLines *mbody;
/* Różne */
int lines, i;
/* Standardowa inicjacja ORB. Zauważmy, że ORB_init "zjada"
wejście z wiersza poleceń */
CORBA_exception_init(#&46;ev);
orb = CORBA_ORB_init(#&46; argv, "orbit-local-orb", #&46;ev);
Następna część pobiera IOR (odwołanie do obiektu). Powinno ono być wpisane do pliku msg.ior
przez msg-server w postaci napisu. Jeżeli serwer działa w tej samej lokalizacji co klient, to
wszystko powinno się udać. W innym wypadku pojawi się  problem dystrybucji IOR .
ifp = fopen("msg-server.ior","r");
if( ifp == NULL ){
g_error("No msg-server.ior file!");
exit(-1);
}fgets(filebuffer,1024,ifp);
ior = g_strdup(filebuffer);
fclose(ifp);
Teraz znajdzmy łącze z faktycznym obiektem z interfejsu wiadomości:
msg_client = CORBA_ORB_string_to_object(orb, ior, #&46;ev);
if (!msg_client) {
printf("Cannot bind to %s\n", ior);
exit(-2);
}
Następnie utwórzmy prostą wiadomość, najpierw przydzielając strukturę wiadomości:
ourmessage=MESSAGING_msg__alloc();
A teraz dołączając do niej podstawowe pola:
if (argc < 3) {
printf("Need two arguments: from to\n");
exit(-3);
}
ourmessage->fr=argv[1];
ourmessage->to=argv[2];
mbody = MESSAGING_msgLines__alloc(); /* Przydzielenie struktury... */
lines = readlines(); /* Wczytanie zawartości */
mbody->_length = lines;
mbody->_buffer = CORBA_sequence_CORBA_string_allocbuf(lines);
for (i = 0; i < lines; i++) {
mbody->_buffer[i] = contents[i];
}
ourmessage->body = *mbody; /* Włączenie sekwencji do komunikatu */
/* Teraz wysyłka komunikatu... */
MESSAGING_mail_submit(msg_client, ourmessage, &ev);
Przechwytujemy jakieś wyjątki (spowodowane np. niedostępnością sieci):
if(ev._major != CORBA_NO_EXCEPTION) {
printf("we got exception %d from submit!\n", ev._major);
return 1;
}
/* Oczyszczenie zawartości... */
CORBA_Object_release(msg_client, &ev);
CORBA_Object_release((CORBA_Object)orb, &ev);
return 0;
}
Na zakończenie funkcja, która odczytuje zbitkę wierszy i przenosi je do bufora:
int readlines (void) {
char linebuff[4096]; /* Tymczasowy bufor dla wierszy... */
int nlines, llen;
for (nlines = 0; fgets(linebuff, 4096, stdin); !feof(stdin)) {
llen = strlen(linebuff);
contents[nlines] = (char*) malloc(llen+1);
linebuff[llen-1]=0; /* Wymuszenie ogranicznika wiersza */
strcpy (contents[nlines], linebuff);
nlines++;
if (nlines >= MAXMSGLEN) {
return nlines;
}
}
return nlines;
}
Serwer komunikatów
Kod serwera komunikatów pokazuje niezależność od języka programowania, ponieważ napisano
go w języku Python. Widać tu również sposób, w jaki Python umożliwia bardziej czytelne
odwzorowanie niż język C, ponieważ ma wbudowane właściwości obsługujące obiekty, klasy i
wyjątki (tutaj nie używane).
Oprócz tego Python dysponuje możliwością usuwania pozostałości, co oznacza, że nie musimy się
martwić o ubarwianie kodu wywołaniami malloc i free w celu utrzymania pamięci w ryzach.
Pamięć musi być tu również przydzielana i zwalniana, ale nie wymaga to wprowadzania
dodatkowego kodu. Oto zawartość pliku msg-server.py:
#!/usr/bin/env python
import CORBA
class mail:
msgs = 0 # Message counter
def submit(self, msg):
print "Message Received from:", msg.fr
print "Message for:", msg.to
for line in msg.body:
print line
self.msgs = self.msgs + 1
print "Messages: ", self.msgs
CORBA.load_idl("msg.idl")
CORBA.load_idl("/usr/share/idl/name-service.idl")
orb = CORBA.ORB_init((), CORBA.ORB_ID)
poa = orb.resolve_initial_references("RootPOA")
servant = POA.MESSAGING.mail(mail())
poa.activate_object(servant)
ref = poa.servant_to_reference(servant)
open("./msg-server.ior", "w").write(orb.object_to_string(ref))
print "Wrote out IOR: ", orb.object_to_string(ref)
poa.the_POAManager.activate()
orb.run() # Serwer znajduje się w pętli zdarzeń ORBit i oczekuje żądań
Kompilowanie aplikacji ORBit
A oto plik konfiguracyjny makefile dla naszej aplikacji:
### Makefile dla systemu powiadamiania
# Konfiguracja ORBit:
ORBIT_IDL = /usr/bin/orbit-idl
ORBIT_CFLAGS = -I/usr/lib/glib/include -I/usr/include
ORBIT_LIBS = -L/usr/lib -lORBit -lIIOP -lORBitutil -lglib -lm
CFLAGS = $(ORBIT_CFLAGS) -g
LFLAGS = $(ORBIT_LIBS)
all: msg-client msg.hh msgSK.cc
### Kilka przekształceń ORBit IDL
%.h : %.idl
orbit-idl $<
%-common.c : %.idl
orbit-idl $<
%-skels.c : %.idl
orbit-idl $<
%-stubs.c : %.idl
orbit-idl $<
### Zależności omniORB...
%SK.cc: %.idl
omniidl2 $<
%.hh : %.idl
omniidl2 $<
### Teraz nasze ulubione zależności:
msg.h: msg.idl
msg-common.c: msg.idl
msg-stubs.c: msg.idl
msg-skels.c: msg.idl
### Tutaj ustawiono powiązanie z omniORB2 zapewniając dodatkową weryfikację
### czy IDL jest poprawnie zbudowane z perspektywy innego języka,
### ORB i parsera. Jeżeli ten pośrednik nie jest zaistalowany, to
### powiązanie się nie uda, generując błąd.
msg.hh: msg.idl
msgSK.cc: msg.idl
### Kompilacja klienta komunikatów
msg-client: msg-client.o msg-common.o msg-stubs.o
$(CC) -o msg-client msg-client.o msg-stubs.o msg-common.o \
$(LFLAGS)
msg-client.o: msg-client.c
Może nie jest najlepszym pomysłem użycie make do automatycznej generacji zrębów z IDL, ale to
działa. Zauważmy, że makefile tworzy pliki powiązań do C++ dzięki użyciu pośrednika
omniORB. Faktycznie nie korzystamy z tego, lecz taka konstrukcja pomaga w wyszukiwaniu
błędów w plikach IDL, ponieważ omniORB szuka problemów odnoszących się do języka C++.
Uruchamianie przykładowej aplikacji
Najpierw należy się upewnić, czy ORBit, Python oraz ORBit-Python są zainstalowane w systemie.
Pierwsze dwa są prawdopodobnie dostarczone w dystrybucji Linuksa. ORBit-Python
umożliwiający powiązanie języka Python z pośrednikiem ORBit można pobrać ze strony
http://orbit-python.sault.org/.
Załóżmy, że wszystkie wspomniane do tej pory pliki są już w katalogu
/usr/local/erc/ORBitsamples. Przejdzmy do tego katalogu i użyjmy polecenia:
$ make
Zostaną utworzone zręby IDL i msg-client, a także msg.hh oraz msgSK.cc, jeżeli był
zainstalowany pośrednik omniORB.
Musimy teraz uruchomić serwer, aby można było dokonać rozruchu naszej rozproszonej aplikacji:
$ ./msg-server
Dokonuje się to z aktualnie używanego terminala, a więc można uruchomić ten program w tle, aby
dalsze korzystanie z terminala było możliwe.
Polecenie spowodowało uruchomienie serwera i zapis IOR w postaci znakowej do pliku
/usr/local/src/ORBitsamples/msg-server.ior. Można przejrzeć zawartość tego pliku,
aby sprawdzić, jak wygląda IOR. Teraz nadeszła chwila prawdy: przejdzmy na dostępny terminal i
wpiszmy polecenie:
$ ./msg-client me you < msg-server.ior
Terminal, na którym został uruchomiony msg-server, powinien teraz wyświetlić liczbę
komunikatów, informację o każdym komunikacie i zawartość pliku IOR. Doskonale! Mamy więc
pierwszą aplikację działającą w architekturze CORBA. Jeżeli jest dostępny inny komputer
połączony za pomocą protokołu TCP/IP, to można spróbować zestawić zdalne połączenie. Należy
w tym celu wykonać kilka czynności: na tym komputerze trzeba uruchomić msg-client i mieć
dostęp do pliku msg-server.ior. Jednym ze sposobów rozwiązania tego problemu jest
zamontowanie katalogu /usr/local na obydwóch komputerach. Można także skopiować
wspomniane pliki za pomocą FTP lub przenieść je na dyskietce. W przypadku komputera Alpha
firmy Compaq potrzebna będzie ponowna kompilacja programu msg-client.
Wszystko to pokazuje, że można wysyłać komunikaty z klienta do serwera. Serwer napisany w
języku Python był obciążony dla celów testowych wiadomościami przesyłanymi przez dwa inne
serwery i w ciągu 10 minut przetworzył 15000 komunikatów bez zgłaszania błędów.
Materiały zródłowe
Poniżej podajemy użyteczne strony WWW:
Konsorcjum Object Management Group (http://www.omg.org)
ORRBit (http://www.labs.redhat.com/orbit)
Enhydra (http://www.enhydra.org)
CORBA Links dra Douglasa Schmidta (http:/www.cs.wustl.edu/~schmidt/corba.html)
Bezpłatny pośrednik ORB Toma Valesky (http://patriot.net/~tvalesky/freecorba.html)
Open Source Java i XML Application Server z projektu Enhydra
(http://www.enhydra.org)
Simple Object Access Protocol (SOAP) (http://www.w3.org/TR/SOAP/)
Podsumowanie
W tym rozdziale pokazano architekturę CORBA jako sposób budowy rozproszonych aplikacji
obiektowych oraz jej ważniejsze składniki, czyli pośredniki wywołań obiektów (ORB) i język
definicji interfejsu (IDL).
Porównano architekturę CORBA z niektórymi innymi systemami realizującymi podobne zadania.
Pod względem funkcjonalnym zbliżają się one do siebie, ponieważ organizacje wspomagające
CORBA zaczynają wdrażać usługi przypominające usługi występujące w innych systemach lub
pomosty komunikacyjne między systemami.
Zapoznaliśmy się z niektórymi szczegółami struktur IDL oraz ze sposobem definiowania
interfejsów obiektów. Zobaczyliśmy także, jak w aplikacjach CORBA można łączyć IDL
niezależny od języka ze specyficznymi językami programowania, zwracając szczególną uwagę na
odwzorowanie w języku C.
Pokazano tu również kod zródłowy prostego systemu powiadamiania, zawierający fragmenty
napisane w językach C i Python oraz korzystający z pośrednika ORBit.


Wyszukiwarka

Podobne podstrony:
R 20 07 (3)
ustawa z 20 07 2000 r o ogłaszaniu
07 EDC Revised 1 20 03
Lista licencji trenerskich nr 20 z dnia 26 07 2012 (2)
20 Skazenie sal operacyjnych anestetykami lotnymi Mragowo 07 Palaszkiewicz Pid!452
CNC 07 30 206 00 kolo zebate 12 5M 20
Ling Ling Duo instr 09 07 20
20 złotych 15 07 1947

więcej podobnych podstron