CORBA (Common Object Request Broker Architecture) jest przemysłowym standardem zaproponowanym przez OMG (Object Management Group) w 1991 roku. Jest to konsorcjum ponad 800 firm i instytucji zajmujące się m.in. pracami nad zdefiniowaniem standardowego, niezależnego od platformy systemowej i języka szkieletu rozproszonej komunikacji pomiędzy obiektami. CORBA jest przeznaczona do wspomagania programowania rozproszonego pomiędzy niekompatybilnymi systemami. Nie jest to kolejny język programowania ani też jego właściwość czy narzędzie stworzone w konkretnym języku. Jest to specyfikacja określająca technologię integracji składników programów rozproszonych. Określa standardowe metody dostępu do zdalnych obiektów i komunikacji pomiędzy nimi, niezależnie od systemu operacyjnego, języka programowania oraz położenia w sieci.
System oparty na CORBA jest zestawem obiektów, które oddzielają dostawców usług (serwery) od odbiorców (klienci) przy pomocy dobrze określonego interfejsu programistycznego. Ich zadaniem jest świadczenie różnego rodzaju usług, z których najważniejsze to lokalizowanie obiektu-serwera po nazwie i zapewnienie komunikacji pomiędzy nim a klientem, w szczególności przekazywanie argumentów i rezultatów metod. Funkcję tę spełnia specjalny program-serwer dostarczany wraz z implementacją środowiska CORBA. Musi on być uruchomiony przed rozpoczęciem działania pozostałych składników rozproszonego programu, podobnie jak rejestr RMI.
Implementacja środowiska CORBA, jak również oprogramowanie obiektów świadczących usługi (serwerów) i z nich korzystających (klientów) mogą być wykonane w różnych językach programowania. Aby ich współdziałanie było możliwe, muszą one spełniać specyfikację, która jest definiowana przed ich implementacją. Do tworzenia specyfikacji obiektów służy specjalny język. Z napisanej w nim specyfikacji generuje się kod w języku docelowym wspomagający implementację obiektów. Wygenerowany kod umożliwia odwoływanie się do obiektów zdalnych przy użyciu zwykłych mechanizmów języka implementacji obiektu lokalnego. Jest to tym ciekawsze, że owe obiekty mogą być zaimplementowane w różnych językach programowania.
Wielu różnych dostawców oprogramowania udostępnia swoje implementacje standardu CORBA. Są one między sobą mniej lub bardziej zgodne, ze względu na różnice w kolejnych wersjach specyfikacji. Różnice te objawiają się głównie po stronie serwera, mniej po stronie klienta. Jedna z nich jest dostarczana wraz SDK Javy (od wersji 1.3).
Budowa i zasady działania CORBA są bardzo złożone, dlatego ograniczymy się tu do pobieżnego przedstawienia wyłącznie elementów niezbędnych do zbudowania najprostszych aplikacji.
OMA (Object Management Architecture - Architektura Zarządzania Obiektami) to specyfikacja zaproponowana przez OMG, definiująca warunki, jakie powinny spełniać rozproszone aplikacje obiektowe. Na jej podstawie został zdefiniowany standard CORBA, w którym można wyróżnić 5 zasadniczych elementów:
Zarówno klient, jak i obiekt, do którego się on odwołuje są oddzieleni od ORB warstwą interfejsów języka IDL. Podobnie jak w przypadku zdalnych obiektów RMI, obiekty CORBA muszą być specyfikowane przy pomocy interfejsów. Ponieważ jednak w CORBA obiekty mogą być implementowane w różnych językach, do tworzenia interfejsów stworzono specjalny pośredni język IDL. Jego składnia jest podobna do Javy czy C++, jednak nie jest to zwykły język programowania. Jego przeznaczeniem jest wyłącznie specyfikowanie interfejsów, z których następnie generuje się kod wspomagający (namiastki, szkielety itp.) w języku implementacji. Do wygenerowania tego kodu potrzebny jest specjalny kompilator. Oczywiście musi istnieć odwzorowanie języka definicji interfejsów IDL na język, w którym implementujemy obiekt. Do tej pory OMG zdefiniowało odwzorowania IDL na następujące języki programowania: Ada, C, C++, COBOL, Common Lisp, Java i Smalltalk. Różne organizacje wprowadzają mniej formalne odwzorowania na inne znane języki (Perl, Python, TCL, Eiffel, Haskell, Erlang). Historycznie pierwsze pojawiły się odwzorowania dla języków C i Smalltalk, obecnie najpowszechniejsze są odwzorowania dla Javy i C++.
Interfejsy obiektów mogą być statyczne (znane w czasie kompilacji) lub dynamiczne. Interfejsy statyczne są reprezentowane po stronie klienta przez namiastki (wygenerowane klasy). Interfejsy dynamiczne pozwalają klientom używać obiektów CORBA, których typy nie były znane w czasie kompilacji. Do tego właśnie służy DII.
Do pozyskania dynamicznych interfejsów służy ich przechowalnia - repozytorium interfejsów IR. Interfejsy mogą być dodawane do repozytorium na użytek klientów. Mogą oni w ten sposób uzyskiwać dostęp do obiektów nieznanych w czasie kompilacji (klienta). Na podstawie interfejsu pozyskanego z repozytorium klient może zbudować i wysłać żądanie do obiektu, który ten interfejs implementuje.
Adapter obiektu pośredniczy pomiędzy ORB a implementacją obiektu w dostępie do usług, jakie ten świadczy. Chodzi tu np. o generowanie referencji do obiektów, wywoływanie metod i kwestie bezpieczeństwa z tym związane.
Początkowo był zdefiniowany podstawowy adapter obiektu BOA (Basic Object Adapter). Jednak jego specyfikacja nie była wystarczająco dokładnie określona, co powodowało problemy z przenośnością oprogramowania pomiędzy pośrednikami ORB różnych producentów. Zaowocowało to powstaniem przenośnego adaptera obiektów POA (Portable Object Adapter). W implementacji CORBA zawartej w J2SDK wersji 1.3 używany był jeszcze BOA, podczas gdy w wersji 1.4 dostępne jest już POA. Na tej ostatniej oparty będzie przykład zamieszczony w dalszej części.
Oprócz specyfikacji CORBA, wspomniana architektura zarządzania obiektami OMA definiuje zestaw usług składowych COS (Common Object Services). Mają one ułatwiać stosowanie CORBA dzięki użyciu standardowych interfejsów (napisanych w języku IDL) do ich specyfikacji. Najważniejszą z tych usług jest Object Naming Service, która spełnia zadanie podobne do rejestru RMI: pozwala na kojarzenie obiektów z ich nazwami. Znając nazwę można uzyskać referencję do obiektu. Niektóre z pozostałych usług to:
Tworzenie typowej rozproszonej aplikacji w standardzie CORBA przeważnie wymaga podjęcia następujących kroków:
Poszczególne kroki mogą się nieco różnić w zależności od producenta implementacji środowiska CORBA i jego wersji, jednak podstawowe zasady pozostają podobne.
Zaprogramowanie serwera obejmuje implementację metod określonych w interfejsie IDL
oraz utworzenie kodu inicjującego system CORBA i instalującego usługę.
Przeważnie czynności te są oddelegowane do metody main()
dodatkowej klasy.
Jeśli używamy adaptera POA
(jak w J2SDK 1.4), to czynności te wyglądają następująco:
POAManager
Program klienta musi uzyskać referencję do obiektu serwera. Podobnie jak w przypadku RMI obiekt serwera będzie reprezentowany lokalnie przez namiastkę. Referencję do serwera wydobywa się z usługi nazywania poprzez nazwę. W związku z tym trzeba najpierw uzyskać dostęp do usługi nazywania - podobnie jak po stronie serwera. To z kolei wymaga inicjalizacji pośrednika ORB. Krótko mówiąc klient musi wykonać następujące standardowe czynności:
Po uzyskaniu referencji do obiektu serwera można wywoływać na jego rzecz metody zadeklarowane w interfejsie wygenerowanym ze specyfikacji, który on implementuje.
Język IDL służy wyłącznie do tworzenia specyfikacji, nie ma w nim więc konstrukcji typowych dla zwykłych języków programowania: instrukcji, operatorów, itp. Jego składnia umożliwia deklaracje metod i wprowadzanie nowych typów danych.
Strukturą najwyższego poziomu są moduły wprowadzane słowem kluczowym
module
:
module Example { // interfejsy i typy danych };Moduły są tłumaczone na pakiety Javy. Służą do grupowania interfejsów w obrębie jednej przestrzeni nazw.
Interfejsy służą do grupowania powiązanych ze sobą operacji (funkcji, metod) i atrybutów.
Są wprowadzane słowem kluczowym interface
:
module Example { interface Operations { // deklaracje metod i atrybutów }; };Interfejsy IDL są tłumaczone na interfejsy Javy.
W języku IDL podstawowe typy danych są podobne jak w Javie czy C++. Można też tworzyć własne, złożone typy. Poniżej omówione zostaną ważniejsze z nich.
Typ IDL | Odpowiednik Javy | Opis |
---|---|---|
short , unsigned short |
short |
16 bitowa liczba całkowita |
long , unsigned long |
int |
32 bitowa liczba całkowita |
long long , unsigned long long |
long |
64 bitowa liczba całkowita |
float |
float |
32 bitowa liczba zmiennoprzecinkowa |
double |
double |
64 bitowa liczba zmiennoprzecinkowa |
char , wchar |
char |
znak 8 lub 16 bitowy |
boolean |
boolean |
typ logiczny o wartościach TRUE lub FALSE |
octet |
byte |
typ znakowy niezmienny podczas przekazu |
Oprócz tego występuje typ any
mogący reprezentować dowolny typ pierwotny
lub złożony. Typ octet
nie podlega żadnym konwersjom podczas przekazywania
pomiędzy różnymi systemami (np. kodowanie ASCII na EBCDIC).
Słowo kluczowe sequence<typ>
deklaruje wektor elementów
typu typ
o nieokreślonej długości. Rozmiar można ustalić podając go po
przecinku:
sequence<string> message; sequence<char, 80> line;Odpowiednikiem typu
sequence
w Javie jest tablica.
Typ string
oznacza łańcuch znakowy składający się ze znaków typu char
zakończonych znakiem NULL
. Łańcuch znakowy typu wstring
jest złożony z elementów typu wchar
.
Złożone są z typów podstawowych lub złożonych.
Definiuje się je słowem kluczowym typedef
lub struct
.
typedef
typedef
można wprowadzić nową nazwę na typ, podobnie
jak w językach C/C++.
typedef string słowo; typedef sequence<string> zdanie;
struct
definiuje się struktury służące do grupowania
atrybutów:
struct Osoba { string imie; int wiek; };Struktury są tłumaczone na klasy w Javie.
Wyjątki można deklarować na poziomie modułów lub interfejsów słowem kluczowym
exception
:
exception Wyjątek {};Są one tłumaczone na klasy wyjątków w Javie.
Operacje IDL odpowiadają metodom Javy. Deklaracje operacji mają składnię podobną do metod Javy:
typ nazwa(listaParametrów); typ nazwa(listaParametrów) raises(NazwaWyjątku);
typ
jest typem IDL lub słowem kluczowym void
.
Jeśli metoda zgłasza wyjątek, to ma on być uwzględniony w klauzuli raises
(której odpowiednikiem w Javie jest throws
). Każdy parametr na liście
parametrów jest poprzedzony jednym ze słów kluczowych:
in
- informujący, że parametr jest przekazywany od klienta do serweraout
- informujący, że parametr jest przekazywany od serwera do klientainout
- informujący, że parametr jest przekazywany w obu kierunkach string date(); void set(in int arg) raises(BadValue); void get(out IntHolder val) raises(BadPlace, BadValue); boolean check(inout StringHolder sval);Druga metoda zgłasza wyjątek, trzecia dwa. Parametry typu
out
i
inout
wymagają w Javie dodatkowych klas pojemnikowych do ich transportu.
Parametrom typu in
odpowiadają zwykłe parametry Javy.
Do wygenerowania kodu Javy z pliku specyfikacji IDL służy program idlj. Jest on zawarty w dystrybucji SDK Javy i znajduje się w jej katalogu bin. Jako argument pobiera on nazwę pliku ze specyfikacją. Wynikiem jego działania jest zestaw plików niezbędnych do implementacji i uruchomienia klienta i serwera usługi zdefiniowanej w specyfikacji IDL.
Technologię CORBA zaprezentujemy na przykładzie programu służącego do przesyłania plików ze zdalnego serwera. Całość - zarówno klient jak i serwer - będą napisane w Javie. Korzystać będziemy z wersji 1.4 SDK. Sposób implementacji i uruchamiania we wcześniejszych wersjach jest trochę inny.
Przykład ten pokaże jak implementować serwery nietrwałe (transient) - takie, których działanie kończy się wraz z zakończeniem procesu lub wątku rodzicielskiego (tego, który je utworzył). CORBA umożliwia również tworzenie serwerów trwałych (persistent) - takich, które mogą kontynuować działanie po zakończeniu procesu lub wątku rodzica.
Serwerem świadczącym usługi przesyłania plików będzie obiekt klasy FileServant
,
która będzie realizować zadania zdefiniowane w pliku interfejsu FileTransferFace.idl.
Będzie ona dostarczać usług (za pośrednictwem publicznych metod) takich jak:
listowanie zawartości katalogu, pobranie pliku lub informacji o pliku.
Metoda main()
inicjująca środowisko CORBA znajduje się w
klasie FileServer
, i jest to jedyna metoda tej klasy, bowiem klasa
FileServer
służy wyłącznie do uruchamiania serwisów.
Metoda main()
klienta jest zawarta w klasie FileClient
.
Inicjuje ona środowisko CORBA i pobiera referencję do serwera. Na końcu
tworzy obiekt klasy FileTransferGUI
, który odpowiada za interakcję
z użytkownikiem.
Oprócz powyższych klas, w skład programu serwera wejdzie mnóstwo klas i interfejsów powstałych
podczas generowania kodu Javy z interfejsu IDL. Będą one umieszczone w
pakiecie FileTransfer
i jego podpakiecie FileInterfacePackage
.
ORB używa standardowego portu 900 do komunikacji sieciowej. Ponieważ jest to numer zastrzeżony (wymaga uprawnień administratora do zainstalowania na nim usługi) użyjemy portu 2004. Będzie to wymagało podania dodatkowych opcji uruchomieniowych bądź dodatków w kodzie źródłowym.
Pierwszą rzeczą, którą trzeba wykonać pracując z CORBA jest zdefiniowanie interfejsu obiektu zdalnego serwera. Ma on określać jaki operacje serwer będzie wykonywał na rzecz klientów. Jak już wcześniej wspomniano, interfejs taki zapisuje się w specjalnym języku IDL, który następnie tłumaczy przy pomocy zewnętrznego programu się na kod języka implementacji. Interfejs IDL może zawierać znacznie więcej informacji niż interfejs Javy: mogą się w nim znaleźć oprócz metod i stałych własne typy danych, które po wygenerowaniu kodu implementującego zostaną przetłumaczone na klasy i interfejsy Javy. W szczególności mogą to być nowe wyjątki.
interface
. Takich interfejsów może być wiele w specyfikacji
IDL. Odpowiada on zwykłemu interfejsowi Javy.
Nasz interfejs IDL zawarty w pliku FileTransferFace.idl będzie
zawierał moduł FileTransfer
. Ponieważ moduły IDL odpowiadają
pakietom Javy, wszystko co się w nim znajduje zostanie umieszczone w wygenerowanym
pakiecie FileTransfer
. W module znajdują się:
AccessDenied
wprowadzana słowem kluczowym
exception
.
FInfo
wprowadzana słowem kluczowym struct
.
Odpowiada ona klasie Javy, która zostanie wygenerowana. Zawiera pewne informacje
o pliku, które klient będzie mógł pobrać z serwera.
FileInterface
zawierający deklaracje metod udostępnianych
przez obiekt serwera klientom. Na użytek tych metod zostały wprowadzone nowe typy
danych słowem kluczowym typedef
, którego składnia i znaczenie jest
takie jak w języku C/C++.
module FileTransfer { exception AccessDenied { }; struct FInfo { boolean isDirectory; boolean accessible; long long size; }; interface FileInterface { typedef sequence<octet> Data; typedef sequence<wstring> Dir; void download(in wstring fileName, out Data fileData) raises(AccessDenied); void list(in wstring dirName, out Dir files) raises(AccessDenied); FInfo fstat(in wstring fname); boolean cpath(inout wstring path); }; };
Wyjątek AccessDenied
zostanie przetłumaczony na klasę
AccessDenied
znajdującą się w pakiecie FileTransfer
.
Ze struktury FInfo
również zostanie wygenerowana klasa o tej samej
nazwie (również w pakiecie FileTransfer
).
Będzie ona zawierać jako publiczne atrybuty składniki
określone w deklaracji struktury. Ich typy zostaną przekonwertowane zgodnie z zasadami
podanymi wcześniej w tabelce. W szczególności typ long long
odpowiada
typowi long
Javy.
Interfejs FileInterface
zawiera deklaracje metod udostępnianych
przez obiekt serwera klientowi. Zostanie z niego wygenerowany interfejs Javy,
który klasa serwera obsługującego musi implementować.
Znaczenie metod list()
i download()
powinno być oczywiste -
jest listowanie zawartości katalogu w pierwszym przypadku i przesłanie zawartości
pliku w tablicy bajtów w drugim. Metoda fstat()
zwraca obiekt typu
FInfo
zawierający informacje o podanym pliku: czy jest katalogiem,
czy serwer ma prawa do jego odczytu oraz rozmiar w przypadku plików. Metoda
cpath()
przekazuje serwerowi nazwę ścieżkową bieżącego (w rozumieniu
klienta) katalogu a serwer zamienia ją na nazwę kanoniczną - pozbawioną składników
postaci ".."
i "."
oznaczających
katalog nadrzędny i bieżący. Na przykład ścieżka "/home/./foo/../bar"
zostanie przekształcona na "/home/bar"
.
Takiej zamiany nie może dokonać klient samodzielnie, ponieważ wymaga ona dostępu
do systemu plików.
Zwróćmy uwagę na deklaracje
parametrów tych metod. Te, które są poprzedzone słowem kluczowym in
,
oznaczają wartości przekazywane od obiektu wołającego (klienta) do obiektu, z którego
metoda jest wywoływana (serwera). Są one w Javie traktowane jak zwykłe parametry i
będą przekazywane przez wartość (będzie przekazana kopia obiektu!) do serwera.
Parametry poprzedzone słowem kluczowym out
oznaczają
argumenty przekazywane od obiektu, na rzecz którego metoda została wywołana, do
wołającego. W ciele metody musi nastąpić przypisanie wartości na ten parametr i
po zakończeniu wykonywania metody wołający (klient) będzie mógł tę wartość odczytać.
Java nie udostępnia takiej semantyki przekazywania parametrów, dlatego potrzebne są
dodatkowe mechanizmy, aby ją zrealizować. Do przekazania takiej wartości od serwera
do klienta jest wykorzystana dodatkowa klasa - pojemnik na wartość - która również
zostanie wygenerowana. Szczegóły dalej, przy opisie implementacji interfejsu.
Trzecim rodzajem parametrów są te, poprzedzone słowem kluczowym inout
.
Jest to połączenie obu powyższych typów: taka wartość jest przekazywana od wołającego
metodę klienta do obiektu, z którego metoda jest wywoływana (serwera), a następnie
z powrotem do wołającego. W jej ciele wartość ta może zostać zmodyfikowana a wołający
będzie mógł ją odczytać. Podobnie jak w przypadku parametrów typu out
,
Java nie obsługuje takiej semantyki przekazywania argumentów i wymagane są do jej
realizacji dodatkowe mechanizmy (o czym dalej). Ostatnie dwa sposoby przekazywania
argumentów wykorzystuje się do przekazywania wyników działania metody zamiast lub
jako dodatek do zwracania rezultatu.
W interfejsie FileInterface
zostały wprowadzone nowe typy danych.
Słowo kluczowe sequence<>
oznacza tablicę elementów, których
typ jest podany w nawiasach kątowych. W tym przypadku Data
będzie
tablicą bajtów (octet
odpowiada typowi byte
Javy), a
Dir
będzie tablicą łańcuchów znakowych. Kompilator języka IDL
wygeneruje z tych typów klasy Javy, których obiekty posłużą do przekazywania
argumentów metod list()
i download()
.
Zgłaszanie wyjątku przez metodę jest w IDL deklarowane słowem kluczowym
raises(Exception)
; w nawiasie podaje się typ wyjątku. W Javie jest to
równoważne klauzuli throws
.
Kompilator idlj zawarty w J2SDK wygeneruje z pliku interfejsu IDL zestaw klas i interfejsów Javy niezbędny do implementacji serwera i klienta. Jako argument podaje się nazwę pliku z interfejsem IDL. Dodatkowo użyjemy opcji -fall dzięki której zostaną wygenerowane zarówno pliki dla serwera jak i klienta.
idlj -fall FileTransferFace.idlPo kompilacji w katalogu bieżącym powinien pojawić się katalog FileTransfer a w nim podkatalog FileInterfacePackage - oba zawierają mnóstwo plików. Nazwy tych katalogów są nazwami pakietów, w których są umieszczone wygenerowane klasy i interfejsy (pierwsza nazwa pakietu została określona w pliku interfejsu IDL). Oto zawartość tych katalogów:
Katalog bieżący:
FileTransfer/ FileTransferFace.idlKatalog FileTransfer:
FileInterfacePackage/ AccessDenied.java * AccessDeniedHelper.java AccessDeniedHolder.java FileInterface.java * FileInterfaceHelper.java * FileInterfaceHolder.java FileInterfaceOperations.java FileInterfacePOA.java * FInfo.java * FInfoHelper.java FInfoHolder.java _FileInterfaceStub.javaKatalog FileTransfer/FileInterfacePackage:
DataHelper.java DataHolder.java * DirHelper.java DirHolder.java *Gwiazdką zaznaczono klasy, z których będziemy bezpośrednio korzystać w kodzie serwera lub klienta. Oczywiście pozostałe są również w ogólnym przypadku niezbędne.
Po stronie serwera będziemy mieli dwie klasy: FileServant
i
FileServer
. Druga z nich zawiera jedynie metodę main()
uruchamiającą całe środowisko CORBA i tworzącą właściwy serwer będący
obiektem pierwszej z nich. Klasa Fileservant
dostarcza metod
zadeklarowanych w interfejsie FileInterface
. Formalnie rzecz biorąc
nie implementuje ona tego interfejsu w sensie Javy, lecz dziedziczy abstrakcyjną klasę
FileInterfacePOA
wygenerowaną przez kompilator idlj.
Nazwa
określony w specyfikacji IDL, dziedziczymy klasę
o nazwie NazwaPOA
wygenerowaną przez kompilator idlj.
Uwaga ta dotyczy wyłącznie wersji CORBA korzystających z adaptera POA - Portable Object Adapter. Implementacje dostarczane z poprzednimi wersjami J2SDK używały innego adaptera obiektów (BOA - Basic Object Adapter) i sposób implementacji serwera był tam trochę inny.
Oczywiście musimy wiedzieć, jakie są sygnatury metod zadeklarowanych w specyfikacji IDL,
aby móc je zaimplementować w Javie. Gdzie znajduje się wygenerowany interfejs FileInterface
wyspecyfikowany w pliku FileTransferFace.idl? Ponieważ został umieszczony
w pakiecie FileTransfer
(w IDL jest to moduł), to musi być w
katalogu FileTransfer. Istotnie, ale niestety jest pusty. Wszystkie jego
metody są zadeklarowane w interfejsie (Javy) FileInterfaceOperations
.
Nazwa
,
kompilator idlj umieści w interfejsie (Javy)
NazwaOperations
. Nadklasa serwera NazwaPOA
implementuje
ten interfejs. Nie dostarcza ona jednak ich implementacji, dlatego jest abstrakcyjna.
out
i inout
Zatem projektując klasę serwera musimy zajrzeć do pliku
FileTransfer/FileInterfaceOperations.java aby zobaczyć jak zostały
przetłumaczone parametry metod. Te, poprzedzone słowem kluczowym in
są traktowane jak normalne parametry metod w Javie. Inaczej ma się rzecz z parametrami
typu out
i inout
, ponieważ po powrocie z wywołania wołający
(klient) musi mieć możliwość pobrania wartości takiego argumentu, jaką miał on przed
zakończeniem wykonywania ciała metody po stronie serwera. Java nie dostarcza takiej
możliwości, dlatego potrzebne są dodatkowe obiekty opakowujące argumenty.
Wołający przekazuje jako argument referencję do takiego obiektu. Jednak sama wartość,
która ma być przekazana jest jego publicznym atrybutem. Adresat komunikatu (serwer)
może go odczytywać i robić na ten atrybut przypisania. Po zakończeniu wywołania
zmiany w obiekcie opakowującym zostaną odzwierciedlone po stronie wołającego. W ten
sposób będzie on mógł odczytać jego (zapewne zmodyfikowaną) zawartość.
Holder
.
Pojemnik opakowujący obiekt klasy Nazwa
nazywa się
NazwaHolder
i ma publiczny atrybut
value
, który jest obiektem klasy Nazwa
.
Klasy pojemnikowe dla standardowych typów Javy znajdują się w pakiecie
org.omg.CORBA
. Np. klasa opakowująca łańcuch znakowy String
nazywa się org.omg.CORBA.StringHolder
. Pojemniki na typy tablicowe
zdefiniowane w interfejsie FileInterface
znajdują się w wygenerowanym
pakiecie FileInterfacePackage
(zawartym w pakiecie FileTransfer
).
Są to DataHolder
i DirHolder
. Metoda serwera chcąc przesłać
plik jako tablicę bajtów do klienta wykorzystując do tego argument typu out
metody musi przypisać na atrybut value
argumentu klasy DataHolder
tablicę z zawartością. Tak samo jest z przekazywaniem tablicy napisów z zawartością
katalogu: tu przypisujemy tablicę na atrybut value
argumentu klasy
DirHolder
.
out
lub inout
metody określony w specyfikacji
IDL jako typu Typ
, po kompilacji do kodu Javy będzie typu
TypHolder
. Klasa tego parametru (TypHolder
) albo jest
w pakiecie org.omg.CORBA
, albo zostanie wygenerowana.
Teraz jesteśmy gotowi do przedstawienia kodu obiektu serwera FileServant
.
Jak wyjaśniono wyżej, jego klasa dziedziczy z FileInterfacePOA
,
która z kolei implementuje interfejs FileInterfaceOperations
dostarczający
sygnatury metod określonych w specyfikacji. W klasie FileServant
znajdują
się wyłącznie implementacje tych metod, choć tak być nie musi - mogą się w niej znaleźć
również inne metody i atrybuty, jednak nie będą one dostępne dla klientów.
Importujemy wygenerowany pakiet FileTransfer
, aby móc korzystać z klas
utworzonych na podstawie specyfikacji IDL.
import java.io.*; import org.omg.CORBA.*; import FileTransfer.*; public class FileServant extends FileInterfacePOA { public FInfo fstat(String fName){ File file = new File(fName); FInfo finfo = new FInfo(); finfo.isDirectory = file.isDirectory(); finfo.accessible = file.canRead(); finfo.size = file.length(); return finfo; }Metoda
fstat()
zwraca informacje o pliku podanym jako argument w
obiekcie klasy FInfo
. Jest to klasa wygenerowana ze specyfikacji
IDL, której obiekty mają te informacje przechowywać.
isDirectory
ma wartość false
, to nie oznacza to
automatycznie, że jest zwykłym plikiem. W systemach Unix i podobnych
plik może być np. urządzeniem, gniazdkiem czy kolejką FIFO.
accessible
ma wartość true
jeśli proces serwera
ma prawa do czytania pliku.
size
jest sensownie określony jedynie w przypadku zwykłych plików.
public boolean cpath(StringHolder path){ try { File file = new File(path.value); path.value = file.getCanonicalPath(); } catch(IOException ioe){ System.err.println("IOException in getCPath: " + ioe.getMessage()); return false; } return true; }Metoda
cpath()
zamienia nazwę ścieżkową pliku (lub katalogu) na kanoniczną
- pozbawioną redundantnych elementów postaci "."
lub
".."
. Zgodnie ze specyfikacją IDL argument
path
jest typu inout
: wołający przekazuje na nim napis jak
normalny argument, a po zakończeniu wywołania odbiera wynik. Dlatego jest on typu
pojemnikowego: StringHolder
. Jak widzimy, sam łańcuch określający ścieżkę
jest przekazywany na atrybucie value
obiektu path
.
Jako rezultat metoda zwraca
false
jeśli konwersja się nie powiodła na skutek zgłoszonego wyjątku.
true
oznacza poprawne zakończenie wywołania.
public void list(String dirName, FileTransfer.FileInterfacePackage.DirHolder result ) throws AccessDenied { File dir = new File(dirName); result.value = dir.list(); if (result.value == null) throw new AccessDenied(); }Metoda
list()
przekazuje wołającemu tablicę plików zawartych w katalogu
podanym jako argument dirName
. Zawartość katalogu jest przekazana w tablicy
łańcuchów znakowych będącej atrybutem value
pojemnika DirHolder
.
Jeśli katalogu nie da się wylistować (np. z powodu braku praw dostępu) zostanie
zgłoszony wyjątek AccessDenied
. Klasa tego wyjątku została wygenerowana
ze specyfikacji IDL, bo tam został on zadeklarowany.
public void download(String fileName, FileTransfer.FileInterfacePackage.DataHolder result ) throws AccessDenied { File file = new File(fileName); result.value = new byte[(int)file.length()]; try { BufferedInputStream in = new BufferedInputStream(new FileInputStream(file)); in.read(result.value, 0, result.value.length); in.close(); } catch(FileNotFoundException e){ throw new AccessDenied(); } catch(IOException e){ System.err.println(e.getMessage()); } } }// class FileServant
FileServant
została napisana tak, by jej metody mogły być wywoływane
jednocześnie przez wiele wątków - nie posiada żadnych atrybutów. Może to być przydatne
jeśli polityka dostępu do usługi świadczonej przez obiekt tej klasy będzie zezwalać
na wielodostęp. Kontrolowaniem polityki jednoczesnego dostępu do usługi zajmuje się
specjalny podsystem CORBA, jednak sposób jej definiowania wykracza poza
ramy tego wykładu.
Najważniejsza metoda w tym programie - download()
- pozwala pobrać
zawartość pliku fileName
jako tablicę bajtów. Podobnie jak w poprzednim
przypadku jest ona przekazana w pojemniku typu DataHolder
jako argument
typu out
. Klasa ta została wygenerowana ze specyfikacji IDL.
Jeśli proces serwera nie ma praw do czytania podanego pliku (albo go po prostu nie ma)
zostanie zgłoszony wyjątek FileNotFoundException
, co zaskutkuje zgłoszeniem
u wołającego wyjątku AccessDenied
. Ważna jest kolejność klauzul catch
przechwytujących wyjątki: klasa FileNotFoundException
jest podklasą klasy
IOException
. Umieszczenie klauzul w odwrotnej kolejności spowodowałoby,
przechwytywanie wyjątku FileNotFoundException
przez klauzulę dla
IOException
i tym samym AccessDenied
nie zostałby nigdy
zgłoszony.
Mamy już klasę, której obiekt będzie bezpośrednio świadczył usługi na rzecz klientów, jednak aby do tego doszło potrzebne są jeszcze pewne czynności inicjalizacyjne - podobne jak przy pracy z RMI. Przeznaczymy do tego specjalną klasę.
Klasa FileServer
będzie zajmować się zainicjowaniem składników systemu
CORBA i rejestracją obiektu - serwera. Będzie zawierać wyłącznie metodę
main()
. Instrukcje w niej umieszczone wykonują standardowe dla tego
typu serwisów CORBA czynności przedstawione w p. 2.3.
W związku z tym można poniższy kod łatwo
zmodyfikować w celu instalowania innych usług, czy też zwiększania ich liczby.
Czynności te są podobne do wykonywanych podczas inicjowania serwera RMI,
jednak jest ich więcej i są bardziej złożone, co ilustruje tabelka.
Czynność | CORBA | RMI |
---|---|---|
Inicjalizacja systemu | ORB.init() |
createRegistry() - Utworzenie rejestru (opcjonalne),
możliwe spoza kodu programu.
|
Aktywacja warstwy pośredniej | POA |
- |
Utworzenie obiektu serwera | + | + |
Pobranie referencji do serwera nazw | uzyskanie dostępu do usługi "NameService" |
nie jest konieczne - wykonuje to automatycznie metoda rejestrująca obiekt |
Zarejestrowanie nazwy | + | + |
Podtrzymanie wątku serwera | ORB.run()
| Robi to rejestr |
main()
definiujemy niestandardowy port, na którym
będzie nasłuchiwać podsystem ORB, ponieważ domyślny port 900 może
uniemożliwić zainstalowanie tej usługi użytkownikom bez uprawnień administracyjnych.
Metoda ORB.init()
zwraca referencję do jedynego obiektu klasy ORB
,
który umożliwi dostęp do jej usług.
Jako pierwszy argument tej metody podaje się zwykle argumenty wywołania programu,
którymi można wpływać na jej funkcjonalność (np. numer portu).
Jeśli nie definiujemy żadnych właściwości Properties
, to jako drugi
argument metody ORB.init()
podaje się null
.
import java.io.*; import java.util.*; import org.omg.CORBA.*; import org.omg.CosNaming.*; import org.omg.CosNaming.NamingContextPackage.*; import org.omg.PortableServer.*; import org.omg.PortableServer.POA; import FileTransfer.*; public class FileServer { public static void main(String[] args){ try { // określenie numeru portu ORB Properties orbprop = new Properties(); orbprop.put("org.omg.CORBA.ORBInitialPort", "2004"); // utworzenie i inicjalizacja ORB ORB orb = ORB.init(args, orbprop); // pobranie referencji do głównego POA org.omg.CORBA.Object poaref = orb.resolve_initial_references("RootPOA"); POA rootpoa = POAHelper.narrow(poaref); // aktywacja zarządcy POA rootpoa.the_POAManager().activate(); // utworzenie serwera plików FileServant servant = new FileServant(); // utworzenie referencji do obiektu serwera org.omg.CORBA.Object seref = rootpoa.servant_to_reference(servant); FileInterface fi = FileInterfaceHelper.narrow(seref); // pobranie referencji do serwera nazw org.omg.CORBA.Object nameref = orb.resolve_initial_references("NameService"); NamingContextExt naming = NamingContextExtHelper.narrow(nameref); // powiązanie nazwy z referencją do obiektu NameComponent path[] = naming.to_name("FileTransfer"); naming.rebind(path, fi); System.out.println("Server started..."); // oczekiwanie na zgłoszenia orb.run(); } catch(Exception e){ System.out.println("Server ERROR: " + e.getMessage()); e.printStackTrace(); } System.out.println("Server exiting..."); } }Metoda
resolve_initial_references()
z klasy ORB
zwraca
referencję do obiektu świadczącego usługę o nazwie podanej jako argument.
Typem wyniku jest org.omg.CORBA.Object
, który jest odpowiednikiem
ogólnego typu java.lang.Object
dla obiektów CORBA, dlatego
wymaga rzutowania. Wykonują je metody narrow()
z klas kończących się przyrostkiem
Helper
.
Metoda resolve_initial_references()
jest przede wszystkim potrzebna
do uzyskania dostępu do usługi nazewniczej oraz adaptera obiektu POA,
który pośredniczy w przesyłaniu żądań do obiektu serwera od klientów.
Musi on zostać aktywowany metodą activate()
zanim nadejdą zgłoszenia.
Metoda to_name()
analizuje złożoną nazwę i rozbija ją na nazwy proste
zwracając je w tablicy jako wynik (nazwy, pod którymi rejestruje się usługi CORBA
mogą mieć złożoną, hierarchiczną budowę). Taką tablicę przekazuje się metodzie rebind()
,
która wiąże nazwę z obiektem serwera przekazanym jako argument.
Metoda run()
podtrzymuje wątek w oczekiwaniu na zgłoszenia nadchodzące
do pośrednika ORB. Będzie on aktywny dopóki nie zostanie zewnętrznie zakończony.
Program klienta będzie składał się z dwóch klas:
FileClient
zawiera
wyłącznie metodę main()
, która inicjalizuje system CORBA oraz
pobiera odniesienie do obiektu serwera
FileTransferGUI
stanowi GUI napisane w Swingu, dzięki któremu
możemy przeglądać katalogi serwera i pobierać wybrane pliki
Pierwsze instrukcje metody main()
mają na celu - tak jak w przypadku
serwera - zmianę domyślnego portu na 2004. Można to zrobić również podając argument
-ORBInitialPort 2004 podczas wywoływania maszyny wirtualnej.
import java.io.*; import java.util.*; import org.omg.CORBA.*; import org.omg.CosNaming.*; import org.omg.CosNaming.NamingContextPackage.*; import FileTransfer.*; public class FileClient { public static void main(String[] args){ try { // określenie numeru portu ORB Properties orbprop = new Properties(); orbprop.put("org.omg.CORBA.ORBInitialPort", "2004"); // utworzenie i inicjalizacja ORB ORB orb = ORB.init(args, orbprop); // pobranie referencji do serwera nazw org.omg.CORBA.Object nameref = orb.resolve_initial_references("NameService"); NamingContextExt naming = NamingContextExtHelper.narrow(nameref); // pobranie referencji do obiektu obsługującego org.omg.CORBA.Object seref = naming.resolve_str("FileTransfer"); FileInterface fi = FileInterfaceHelper.narrow(seref); System.out.println("Uzyskano referencję do zdalnego obiektu"); new FileTransferGUI(fi); } catch(Exception e){ System.out.println(e.getMessage()); e.printStackTrace(); } } }
Na końcu metody startowej main()
klienta (zawartej w klasie FileClient
)
tworzymy obiekt klasy FileTransferGUI
przekazując konstruktorowi odniesienie
do zdalnego obiektu serwera (a właściwie jego namiastki). Od tego momentu użytkownik
interaktywnie wywołuje metody serwera za pośrednictwem utworzonego obiektu klasy
FileTransferGUI
.
Po uruchomieniu klienta ukazuje się okno typu JFrame
z listą JList
prezentującą zawartość katalogu uruchomieniowego serwera. Po kliknięciu w nazwę
otwiera się okno dialogowe z informacjami o pliku bądź katalogu i zapytaniem czy
Klasa FileTransferGUI
posiada następujące atrybuty:
list
do przedstawiania zawartości katalogówfin
typu FileInterface
do obiektu serweracdir
określający bieżący katalog na serwerzefin
.
Katalog cdir
będzie przechowywał ścieżkę absolutną (od korzenia) w
postaci kanonicznej dzięki metodzie cpath()
serwera.
Jej początkowa wartość "."
zostanie zamieniona na absolutną
po pierwszym wylistowaniu zawartości katalogu serwera.
import java.io.*; import java.util.*; import java.awt.*; import javax.swing.*; import java.awt.event.*; import org.omg.CORBA.*; import FileTransfer.*; public class FileTransferGUI extends JFrame { private JList list; private FileInterface fin; private String cdir = "."; private MouseAdapter ma = new MouseAdapter(){ public void mouseClicked(MouseEvent me){ String fname = (String)list.getSelectedValue(); String path = cdir + (cdir.charAt(0) == '/' ? "/" : "\\") + fname; FInfo finfo = fin.fstat(path); if (!confstat(path, finfo)) return; if (finfo.isDirectory) ls(path); else download(path, fname); } };
Kliknięcie myszką w listing katalogu powoduje wywołanie metody mouseClicked()
słuchacza, w której:
fname
path
tworzymy absolutną nazwę ścieżkową zaznaczonego
pliku poprzez doklejenie do fname
bieżącego katalogu cdir
i separatora nazw (znak '\' lub '/') obowiązującego w systemie
serwera. Aby go poznać sprawdzamy pierwszy znak ścieżki absolutnej: jeśli jest to
'/' - mamy do czynienia z Unixem, w przeciwnym wypadku
Windows.
fstat()
confstat
wyświetla okno dialogowe pokazane na obrazku.
Wybranie opcji innej niż YES powoduje zwrócenie wartości false
i zakończenie wykonywania metody mouseClicked()
.
ls()
(wywołującą list()
z serwera) lub pobieramy plik metodą download()
(również wywołującą metodę
serwera o tej samej nazwie).
Konstruktor klasy FileTransferGUI
tworzy GUI, czego nie będziemy tu
szczegółowo omawiać. Wywołanie metody ls()
powoduje pobranie z serwera
(metodą list()
) zawartości jego katalogu uruchomieniowego i uwidocznienie
jej na liście list
.
public FileTransferGUI(FileInterface fi){ fin = fi; Container cp = getContentPane(); list = new JList(); JScrollPane spane = new JScrollPane(list); ls("."); list.addMouseListener(ma); list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); spane.setPreferredSize(new Dimension(300, 300)); cp.add(spane); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocation(300, 300); pack(); show(); } private DefaultListModel makeModel(String[] files){ Arrays.sort(files); DefaultListModel dlm = new DefaultListModel(); dlm.addElement(".."); for (int i = 0; i < files.length; i++) dlm.addElement(files[i]); return dlm; }
Metoda makeModel()
przygotowuje model danych dla listy list
(typu JList
). Przekształca ona zawartość katalogu pobraną z serwera
jako tablicę łańcuchów znakowych do postaci obiektu klasy DefaultListModel
,
którą można uwidocznić na liście list
.
Metoda confstat()
wyświetla okno dialogowe z informacjami dotyczącymi
bieżącego pliku/katalogu i zapytaniem o wykonanie akcji zależnej od rodzaju pliku:
jeśli jest to katalog - zostanie pobrana i wyświetlona jego zawartość;
w innym wypadku nastąpi pobranie zawartości pliku.
private boolean confstat(String fpath, FInfo finfo){ String msg = fpath + "\n" + "is" + (finfo.accessible ? " " : " not ") + "readable\n"; if (finfo.isDirectory) msg = "Directory \n" + msg + "list ?"; else msg = "File \n" + msg + "size = " + finfo.size + " bytes\ndownload ?"; int option = JOptionPane.showConfirmDialog(this, msg); if (option == JOptionPane.YES_OPTION) return true; else return false; } private void download(String path, String local){ // utworzenie obiektu przechowującego argument typu out FileTransfer.FileInterfacePackage.DataHolder result = new FileTransfer.FileInterfacePackage.DataHolder(); try { // wywołanie metody obiektu zdalnego fin.download(path, result); // zapisanie wyniku do pliku BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(local)); out.write(result.value, 0, result.value.length); out.flush(); out.close(); } catch(AccessDenied ade){ JOptionPane.showMessageDialog(this, path + ": Acces denied"); } catch(IOException e){ System.out.println(e.getMessage()); } }
Metody ls()
i download()
opakowują metody list()
i download()
serwera, wykonując dodatkowe czynności. Pierwszym argumentem
(path
) powyższej metody download()
jest absolutna nazwa
ścieżkowa pliku na serwerze, drugim (local
) - jego nazwa (bez katalogów
nadrzędnych), pod którą zostanie zapisany lokalnie. Pierwszą czynnością jaką trzeba
wykonać przed wywołaniem metody download()
serwera jest utworzenie
pojemnika na argument typu out
jakim jest drugi argument tej metody.
Będzie nim obiekt result
klasy DataHolder
wygenerowanej
przez kompilator języka IDL. Po powrocie z wywołania tej metody jego
atrybut value
(typu byte[]
) będzie przechowywał zawartość
pliku path
umieszczoną tam przez obiekt serwera. Zapisujemy ją do pliku
local
metodą write()
z klasy BufferedOutputStream
.
Wyjątek AccessDenied
może zostać zgłoszony na serwerze, jeśli nie mamy
praw do czytania podanego pliku. Drugi z przechwytywanych wyjątków może zostać
zgłoszony lokalnie podczas zapisu pliku.
Metoda ls()
wywołuje najpierw metodę cpath()
serwera aby
poznać kanoniczną nazwę ścieżkową pliku, która następnie będzie umieszczona w tytule
głównego okna aplikacji. Metoda ta ma parametr typu inout
. Obiekt, na rzecz
którego metodę wołamy, pobiera na nim jakąś wartość a przy wyjściu z metody zapisuje
tam nową wartość jako wynik. Będzie on dostępny dla wołającego. Aby tak się jednak stało
musimy, podobnie jak w poprzedniej metodzie, przygotować pojemnik na przekazywaną
wartość. Ponieważ chcemy przekazywać łańcuch znakowy typu String
-
korzystamy ze standardowego pojemnika StringHolder
(z pakietu
org.omg.CORBA
zawierającego pojemniki na standardowe typy).
Początkowa wartość przekazywana do serwera jest podawana w konstruktorze.
Jeśli wywołanie metody cpath()
zakończyło się niepowodzeniem (na skutek
zgłoszonego i przechwyconego w niej wyjątku) zwróci ona wartość false
i
na zmiennej cpath
zapamiętamy wartość dirName
(która nie jest
kanoniczna). Jeśli wywołanie się powiodło, to wartość results.value
zapamiętujemy na zmiennej cpath
i przekazujemy jako pierwszy argument
do metody list()
serwera. Drugim argumentem jest utworzony pojemnik
na tablicę napisów typu DirHolder
(ta klasa została wygenerowana przez
kompilator języka IDL). Wywołanie metody list()
może zostać
przerwane zgłoszeniem wyjątku AccessDenied
, dlatego jest ujęte w blok
try-catch
. Po zakończeniu wywołania zapamiętujemy ścieżkę path
na atrybucie cdir
oraz umieszczamy ją w tytule okna i wymieniamy zawartość
listy list
.
private String[] ls(String dirName){ // utworzenie pojemnika na argument typu inout dla cpath() StringHolder pathold = new StringHolder(dirName); String cpath = (fin.cpath(pathold) ? pathold.value : dirName); // utworzenie pojemnika na argument typu out dla list() FileTransfer.FileInterfacePackage.DirHolder results = new FileTransfer.FileInterfacePackage.DirHolder(); try { fin.list(cpath, results); cdir = cpath; setTitle(cpath); list.setModel(makeModel(results.value)); list.setSelectedIndex(0); } catch(AccessDenied ade){ JOptionPane.showMessageDialog(this, cpath + ": Access denied"); } return results.value; } }
Jeśli proces serwera nie ma praw do listowania zawartości katalogu dirName
,
zostanie zgłoszony wyjątek AccessDenied
.
Zakładając, że zawartość bieżącego katalogu jest taka jak określono w p. 3.3 - po wygenerowaniu plików ze specyfikacji - możemy przystąpić do kompilacji. Najpierw oczywiście trzeba skompilować pliki wygenerowane, potem klasy serwera i klienta w dowolnej kolejności.
javac FileTransfer/FileInterfacePackage/*.java javac FileTransfer/*.java javac *.javaZawartość katalogu po kompilacji jest przedstawiona na obrazku.
Napierw uruchamiamy program orbd - Object Request Broker Daemon. Jest to implementacja pośrednika ORB dostarczana wraz z SDK Javy. Ponieważ przyjęliśmy, że pracujemy na porcie 2004, musimy poinformować go o tym odpowiednią opcją:
orbd -ORBInitialPort 2004Następnie uruchamiamy program serwera:
org.omg.CORBA.ORBInitialPort
nie zostanie przekazana do
metody init()
pośrednika ORB, to należy podać numer portu opcją
-ORBInitialPort 2004 zarówno serwerowi jak i klientowi.
java FileServerA potem - z innej konsoli - klienta:
java FileClientW ten sposób zarówno klient jak i serwer działają na tym samym komputerze. Skorzystanie z serwera działającego na innym niż klient komputerze jest bardzo proste: wystarczy podać klientowi jako argument wywołania odpowiednią opcję z jego nazwą.
main()
i dalej do metody ORB.init()
.
java FileClient -ORBInitialHost anyhost.anywhereOczywiście na komputerze anyhost.anywhere musi działać usługa orbd oraz serwer
FileServer
(uwaga na numery portów!).
CORBA powstała w 1991 roku. Obecnie trwają prace nad wersją 3.0 specyfikacji. Implementacja zawarta w J2SDK 1.4 jest zgodna z wersją 2.3.1. Dla uproszczenia w wykładzie ograniczyliśmy się wyłącznie do języka Java. Należy jednak pamiętać, że istotą technologii CORBA jest integracja oprogramowania napisanego w różnych językach. Zarówno klient jak i serwer z przedstawionego przykładu może być zaimplementowany w dowolnym języku, dla którego istnieje odwzorowanie z języka IDL. Aby móc programować korzystając z systemu CORBA musimy dysponować jego implementacją dla danego języka, w szczególności kompilatorem języka IDL oraz pośrednikiem ORB.
Zastosowanie architektury CORBA jest początkowo kosztowniejsze od tradycyjnych technik programowania rozproszonego (gniazda, RPC, RMI) ze względu na jej dużą złożoność. Ale ta złożoność jest spowodowana jej ogromnymi możliwościami i elastycznością. W przypadku dużych lub długoterminowych aplikacji ten narzut zwraca się z nawiązką. Łatwiejsze będą np. modyfikacje działającego oprogramowania czy integracja z dodatkowymi modułami, dodawanie nowych usług.
Wielu producentów oprogramowania dostarcza implementacje systemu CORBA dla różnych języków programowania. W tabelce umieszczono zestawienie niektórych wersji posiadających wsparcie dla języka Java.
Nazwa Producent Java 2 ORB Sun VisiBroker Inprise Corporation OrbixWeb Iona Technologies Netscape Communicator wersja VisiBroker zawarta w przeglądarkach WebSphere IBM
W systemie Linux jest dostępna implementacja standardu CORBA o nazwie ORBit, będąca częścią projektu GNU. Jest używana przez środowisko GNOME. Językiem implementacji jest C.
org.omg.CORBA