1959


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:

0x01 graphic

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ęźle.

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ć:

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ęźle i gnieździe 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 wyraźnie, 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:

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.

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.

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.

CORBA i gniazda sieciowe

W podobny sposób, jak w poprzednim podrozdziale, porównamy teraz architekturę CORBA z gniazdami sieciowymi (ang. sockets):

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.

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:

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. Weźmy 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<string> message;

Można także ograniczyć rozmiar, podając maksymalną liczbę elementów:

sequence<char, 80> 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<char>, 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: