MokaByte Numero 25  -  Dicembre 98
GIOP/IIOP 
l'autostrada CORBA
di 
Alessandro
Bemporad
Entriamo nel dettaglio dell'interazione fra  CORBA e Java

Introduzione

Il cuore della specifica CORBA, o meglio il principale obiettivo che questa specifica si pone, è quello di stabilire una serie di regole standard per la realizzazione di una architettura per oggetti distribuiti. Al centro di questa architettura si trova l'idea di un Object Request Broker (ORB), ovvero la realizzazione di un bus software attraverso il quale possano scorrere i messaggi che gli oggetti distribuiti si scambiano tra loro.
Dato che un'implementazione reale di un ORB è lasciata all'abilità di produttori di software, come ad esempio Inprise e IONA, si pone, tra gli altri, un quesito di questo genere: come è possibile far interagire tra loro due oggetti che si trovano a vivere su due bus differenti? In altre parole, come può un oggetto client istanziato su un ORB Visibroker trovare e utilizzare un oggetto server in esecuzione in un ORB differente? 
La risposta a questa angosciosa domanda giace nascosta tra le pieghe del protocollo che OMG, l'organismo che controlla lo standard CORBA, ha realizzato proprio per stabilire l'interoperabilità tra ORB differenti. Questo protocollo si chiama GIOP (General Inter-ORB Protocol), e definisce il formato dei messaggi e la rappresentazione dei dati per tutte le comunicazioni tra oggetti distribuiti su uno stesso ORB, o su ORB di produttori differenti. Il protocollo GIOP si appoggia a sua volta ad un qualunque protocollo di trasporto, purchè orientato alla connessione, per portare a destinazione i suoi messaggi. Una particolare implementazione del protocollo GIOP, chiamata IIOP (Internet Inter-ORB Protocol), si avvale del protocollo TCP/IP per la realizzazione dello strato di trasporto dei messaggi tra oggetti remoti. 
In questo articolo ci tufferemo all'interno di un Object Request Broker, e daremo un'occhiata da vicino alle regole che regolamentano il traffico dei messaggi in viaggio da un capo all'altro del bus CORBA.

Introduzione a GIOP

La specifica del protocollo GIOP è divisa in tre aree principali:

1) La definizione della Common Data Rapresentation (CDR).
La CDR stabilisce le regole per la traduzione dei tipi di dato IDL in un formato adatto ad essere trasmesso lungo una linea di comunicazione. Vengono introdotte ad esempio le regole per risolvere il problema del byte ordering, ovvero lo scambio di messaggi tra sistemi che hanno un differente meccanismo di ordinamento dei byte (big-endian e little-endian). Inoltre si affrontano le modalità di alignment, ovvero di allineamento dei dati in memoria, ed il mapping di tutti i tipi IDL primitivi e complessi in un formato adatto ad essere serializzato.

2) Il formato dei messaggi GIOP.
Vengono descritti i messaggi che costituiscono il protocollo vero e proprio. Si stabiliscono inoltre le strutture dati relative a ciascun messaggio, nonchè l'ordine corretto nel quale questi messaggi potranno essere inviati tra oggetti client ed oggetti server.

3) Le assunzioni sul trasporto dei messaggi GIOP.
Questa sezione descrive i requisiti che un qualunque protocollo di trasporto deve soddisfare per far scorrazzare i messaggi GIOP in giro per l'ORB. Il protocollo candidato a svolgere questo compito deve essere connection-oriented ed affidabile. Deve inoltre utilizzare byte stream di lunghezza variabile, e deve implementare un meccanismo che consenta di notificare e di gestire opportunamente una interruzione forzata nelle connessioni. Come già accennato, IIOP è quella particolare incarnazione di GIOP che si avvale dei servizi del protocollo TCP/IP per realizzare lo strato di trasporto.

Il formato dei messaggi GIOP

Definizioni

Prima di addentrarci nella definizione del formato dei messaggi GIOP, è importante chiarire subito alcuni aspetti, e definire alcuni termini. Un client è un qualunque oggetto CORBA capace di compiere due azioni ben precise: aprire una connessione e generare richieste verso un oggetto server. Un server, al contrario, è un qualunque oggetto CORBA in grado di accettare una connessione da parte di un client, e di soddisfare le richieste ottenute tramite l'esecuzione di una opportuna serie di operazioni.
Header dei messaggi GIOP
Tutti i messaggi del protocollo GIOP possiedono un header, che stabilisce il tipo di messaggio, il byte-ordering, la versione del protocollo ed altre amenità di questo genere. La struttura dell'header GIOP, definita in IDL, è la seguente:
 

struct MessageHeader_1_1 {

  char  magic[4];

  Version GIOP_version;

  octet flags;

  octet message_type;

  unsigned long message_size;

};

magic è una stringa che deve contenere i caratteri 'GIOP', e GIOP_Version è a sua volta una struttura che riporta la versione del protocollo utilizzata. Gli otto bit di flags contengono una serie di informazioni tipo il byte-ordering utilizzato, mentre message_type indica quale messaggio l'header sta rappresentando, e message_size la lunghezza del messaggio stesso. 
Oltre all'header generale, che è uguale per tutti, ogni messaggio porta con sè un header "personalizzato", contenente informazioni particolari ed aggiuntive, ed un eventuale body, contenente il messaggio vero e proprio. Passiamo adesso a vedere in concreto di quali messaggi è composto il protocollo GIOP.
Il messaggio Request
Quando un oggetto client desidera inviare una richiesta ad un oggetto server, affinchè questo svolga un certo servizio, gli spedisce un messaggio di tipo Request

Un messaggio di questo tipo contiene un header di tipo RequestHeader ed un corpo del messaggio vero e proprio. Nel RequestHeader, il client specifica tanto per cominciare l'oggetto al quale desidera inoltrare la richiesta, e l'operazione che vorrebbe fosse eseguita. Specifica inoltre se si attende un risultato da questa operazione, oppure se l'operazione è da considerarsi oneway. Nel caso di una operazione oneway, il client dichiara di non aspettarsi alcuna risposta dal server. Questo è il modello stabilito dallo standard CORBA per realizzare tipi di elaborazione asincroni, dal punto di vista del client, il quale altrimenti rimane bloccato in attesa della risposta proveniente dall'oggetto server.

Nel corpo del messaggio, il client infila uno dopo l'altro tutti i parametri di tipo in e inout previsti dall'operazione, in modo da farli ricevere al server perchè possa elaborarli.

Il messaggio Reply
L'oggetto server che si vede recapitare un messaggio di Request da parte di un client, tipicamente esegue l'operazione richiesta e poi, fatto salvo il caso di esecuzione oneway, prepara un messaggio di tipo Reply da rispedire indietro all'oggetto client.

Se tutto va bene, ed il server ha potuto eseguire con successo l'operazione, viene generato un header ReplyHeader, ed il corpo del messaggio contiene l'eventuale valore di ritorno nonchè tutti i parametri di tipo out ed inout previsti dall'operazione stessa. A questo punto il messaggio viene fatto recapitare al client in attesa, ed il gioco è fatto.

Purtroppo, però, qualcosa potrebbe andare storto. Ad esempio, l'operazione potrebbe al suo interno sollevare una qualche eccezione, per segnalare un errore o comunque una anomalia riscontrata durante la sua esecuzione. In questo caso, all'interno dell'header ReplyHeader viene acceso un apposito flag che segnala la condizione di errore. In particolare il flag può assumere i valori USER_EXCEPTION, se l'eccezione è una di quelle definite dal programmatore, oppure SYSTEM_EXCEPTION, nel qual caso l'errore è di sistema e siamo decisamente nei guai. Il corpo del messaggio in questo caso riporta le informazioni previste a corredo dell'eccezione, che in CORBA formano sempre una struttura dati.

Ed infine, il server potrebbe voler ritornare un altro tipo di informazione al client, questa volta di tipo completamente diverso. Se imposta il solito flag al valore LOCATION_FORWARDED, allora intende segnalare che la richiesta è arrivata per qualche motivo nel posto sbagliato. In questo caso, nel corpo del messaggio verrà inserito il reference ad un altro oggetto server, capace di esaudire i desideri del client, e rispedisce il tutto al mittente. In un caso del genere, l'ORB in cui vive il client si deve preoccupare di ridirigere la chiamata al nuovo oggetto server in modo del tutto trasparente per il client. 

Il messaggio CancelRequest
E se il client si pente di ciò che ha fatto? E se qualcuno decidesse di terminarlo nel bel mezzo di un dialogo con il server? Supponiamo che un oggetto client abbia inviato un messaggio di Request, e decida di non attendere il Reply dall'oggetto server. In questo caso, viene inviato un messaggio CancelRequest dal client al server. Questo messaggio è formato solamente da un header CancelRequestHeader, senza corpo del messaggio. Con questo, il client intende informare il server che non attenderà alcuna risposta alla richiesta effettuata. Il server, a discrezione di chi implementa il protocollo, potrebbe comunque decidere di inviare ugualmente il messaggio di Reply, anche se questo con ogni probabilità andrebbe perso per sempre nell'ORB.
Il messaggio LocateRequest
Inviando un messaggio di LocateRequest, un client passa un object reference ad un server e formula automaticamente questa serie di domande:
    • questo object reference è valido? 
    • il server destinatario del messaggio LocateRequest è in grado di ricevere richieste indirizzate a questo object reference? 
    • no? e allora per caso potrebbe gentilmente indicare l'indirizzo di un altro server in grado di ricevere richieste dirette a quell'object reference?


    Il messaggio di LocateRequest è composto da un solo header LocateRequestHeader, contenente l'object reference in questione, e non si porta dietro altre informazioni. E' importante notare che queste domande vengono poste anche attraverso il normale messaggio di Request. Abbiamo visto infatti che in casi come questo il client potrebbe ottenere indietro una indicazione di LOCATION_FORWARDED. E allora a che serve tutto ciò? Beh, diciamo che mentre il messaggio Request si porta appresso una serie di informazioni, come ad esempio i parametri per l'operazione, il messaggio LocateRequest è per così dire più "snello", e quindi rappresenta una forma di ottimizzazione della comunicazione, nella gestione degli oggetti client. 

Il messaggio LocateReply
Il messaggio LocateReply sta al messaggio LocateRequest, come Reply sta a Request. In altre parole, quando un oggetto server riceve da un client un messaggio LocateRequest, risponde inviandogli un messaggio LocateReply. In questo modo, risponde alle tre domande fatidiche poste dal client in uno dei modi che illustreremo subito. Supponiamo come prima ipotesi che il server non sappia niente a proposito dell'oggetto cercato dal client: in questo caso risponde indicando nell'header LocateReplyHeader uno status UNKNOWN_OBJECT, e la cosa finisce lì. Viceversa, supponiamo che lo conosca eccome, ed anzi sia in grado di accedervi facilmente per gestire le richieste: in questo caso lo status ritornato nell'header è OBJECT_HERE, e quindi il client può inviare tranquillamente la sua Request. Infine, nel caso non possa gestirlo direttamente, ma sappia comunque come arrivarci, imposta lo status nell'header a OBJECT_FORWARD, ed inserisce nel corpo del messaggio il reference all'oggetto in grado di gestire le richieste del client. Quest'ultimo caso è analogo a quello già visto nel caso di Reply con annessa indicazione di LOCATION_FORWARDED.
Il messaggio CloseConnection
Il messaggio CloseConnection è in qualche modo speculare rispetto a quello di CancelRequest. Un server invia un messaggio CloseConnection ad un client quando intende interrompere la connessione senza fornire ulteriori risposte. In questo modo il client può a sua volta chiudere la connessione in modo più "pulito" e dignitoso.
Il messaggio MessageError
Questo messaggio, a differenza dei precedenti, può viaggiare in entrambe le direzioni, dal client al server e viceversa. Viene generato un MessageError quando, a fronte di un qualunque messaggio ricevuto, viene rilevato un errore di qualche tipo nell'header del messaggio stesso, oppure quando l'header segnala un numero di versione errato o non supportato, oppure infine se il tipo di messaggio ricevuto è incomprensibile o sconosciuto.
Il messaggio Fragment
Come abbiamo visto, tutti i messaggi GIOP iniziano sempre con una struttura dati che rappresenta il loro header, la loro intestazione. Abbiamo visto anche che una delle informazioni presenti negli header di un messaggio è la dimensione del messaggio stesso. A volte però non è conveniente o possibile stabilire la lunghezza del messaggio nel momento stesso di creare l'header. Per risolvere questo problema, è stato deciso che i messaggi Request e Reply possano venire spezzati e spediti in una serie di frammenti separati. Il primo frammento ad essere spedito è il messaggio Request o Reply normale, ma con un particolare flag acceso (il flag more fragments). Dopodichè vengono spediti in serie gli ulteriori frammenti incapsulati in messaggi di tipo Fragment. Anche i messaggi Fragment hanno il flag more fragments acceso, tranne l'ultimo. In questo modo è possibile ricostruire facilmente l'intero messaggio, una volta ricevuto con successo l'ultimo frammento.
Conclusione

Al di là delle apparenze, il protocollo GIOP, ed in particolare la sua incarnazione IIOP che sta alla base di tutti gli ORB attualmente in circolazione, è estremamente semplice ma potente. Attraverso l'utilizzo di un piccolo numero di messaggi, è possibile realizzare un bus capace di localizzare dinamicamente oggetti remoti e di far transitare i messaggi in modo estremamente efficace. Forse l'argomento può sembrare un pò troppo teorico, o comunque privo di reali implicazioni in un normale contesto applicativo. Occorre tener presente però che, sulla base di questi semplici messaggi, sono stati realizzati sofisticati meccanismi di fault tolerance, o di load balancing, come ad esempio quelli implementati nell'OSAgent di VisiBroker, senza i quali non sarebbe possibile mettere in produzione alcuna applicazione distribuita degna di questo nome. Ma lasciamo per ora questi argomenti, magari ad un prossimo articolo, ed intanto accontentiamoci di questo piccolo viaggio nell'autostrada CORBA. 


MokaByte Web  1998 - www.mokabyte.it 
MokaByte ricerca nuovi collaboratori. Chi volesse mettersi in contatto con noi può farlo scrivendo a mokainfo@mokabyte.it