MokaByte 56 - 8bre  2001
Foto dell'autore non disponibile
di
Gianluca Morello
Corso CORBA
IX Parte
CORBA run-time information
In Java esiste la possibilità di ottenere a run-time informazioni sulla composizione di un qualunque oggetto (costruttori, metodi, attributi, …). Grazie a queste informazioni è possibile utilizzare un’istanza (invocare metodi, impostare attributi, …) pur non conoscendo a priori la sua classe. In maniera simile tutti i CORBA object possono fornire a run-time informazioni sulla propria struttura.
Introduzione
In Java esiste la possibilità di ottenere a run-time una serie d’informazioni sulla composizione di un qualunque oggetto (costruttori, metodi, attributi, …). Grazie a queste informazioni è possibile utilizzare un’istanza (invocare metodi, impostare attributi, …) pur non conoscendo a priori la sua classe. Questa potenzialità consente di realizzare soluzioni molto eleganti e flessibili a problemi storicamente complessi quali la definizione/manipolazione di componenti.

CORBA fornisce meccanismi simili a quelli appena descritti, tutti i CORBA object possono fornire a run-time informazioni sulla propria struttura. Nel gergo CORBA queste informazioni sono dette metadata e pervadono l’intero sistema distribuito. Il repository destinato a contenere i metadata è l’Interface Repository che consente di reperire ed utilizzare un oggetto ottenendo dinamicamente la sua descrizione IDL.

Queste caratteristiche conferiscono una flessibilità notevole al modello CORBA. Nella pratica risultano particolarmente attraenti per i venditori di tool che sviluppano componenti distribuiti. L’invocazione dinamica, presentando minori performance e maggiori difficoltà di sviluppo, è sicuramente meno interessante per un utilizzo medio.
 
 
 

Introspezione CORBA
Ogni oggetto CORBA specializza CORBA::Object, le capacità introspettive di ogni CORBA object derivano da questa superclasse

// IDL
module CORBA {
 // …
 interface Object {
  // …
  IRObject get_interface_def();

  ImplementationDef get_implementation();

  boolean is_nil();

  boolean is_a(in string logical_type_id);

     boolean is_equivalent(in Object other_object);

     boolean non_existent();
 };
};

Il metodo get_interface_def può essere visto come l’equivalente del getClass Java, fornisce i dettagli relativi ad un oggetto (attributi, metodi, …). Il metodo restituisce un oggetto generico di tipo IRObject, per ottenere i dettagli di cui sopra è necessario effettuare il narrow ad InterfaceDef. Fino alle specifiche 2.3 esisteva un metodo get_interface che restituiva direttamente un oggetto di tipo InterfaceDef, molti ORB lo supportano ancora per backward compatibility.

I restanti metodi sono funzioni di test: is_nil è l’equivalente CORBA di “obj == null”, is_a controlla la compatibilità dell’oggetto con l’interfaccia data, is_equivalent verifica l’equivalenza tra due reference e non_existent verifica se l’oggetto non è più valido.

Va notato che le signature viste sopra rappresentano le signature IDL del CORBA::Object, ma nella maggior parte delle implementazioni i metodi visti sopra sono forniti dal CORBA.Object con un ‘_’ davanti (_is_a, _get_implementation, …).
 
 
 

Interface Repository (IR)
L’Interface Repository è il servizio che contiene e fornisce le informazioni sulla struttura IDL degli oggetti. Si è visto nel paragrafo precedente che, attraverso l’invocazione del metodo get_interface_def, è possibile ottenere un oggetto di tipo InterfaceDef che fornisca tutte le informazioni sulla struttura di una interfaccia.

L’IR contiene in forma persistente gli oggetti InterfaceDef che rappresentano le interfacce IDL registrate presso il repository. L’IR può esser utilizzato anche dall’ORB per effettuare il type-checking nell’invocazione dei metodi, per assistere l’interazione tra differenti implementazioni di ORB o per garantire la correttezza del grafo di derivazione.
Nel caso in cui si intenda utilizzare esplicitamente l’IR sarà necessario registrare l’IDL. La registrazione varia nelle varie implementazioni. VisiBroker prevede due comandi: irep (attiva l’IR) ed idl2ir (registra un IDL presso l’IR). Il JDK non fornisce attualmente alcuna implementazione dell’IR.
Interrogando l’IR è possibile ottenere tutte le informazioni descrivibili con un’IDL. La navigazione di queste informazioni avviene tipicamente a partire dalla classe InterfaceDef e coinvolge un complesso insieme di classi che rispecchiano l’intera specifica IDL: ModuleDef, InterfaceDef, OperationDef, ParameterDef, AttributeDef, ConstantDef, ...
 
 
 

Dynamic Invocation Interface (DII)
Negli esempi visti finora, per invocare i metodi sugli oggetti CORBA si è utilizzata l’invocazione statica. Questo modello di invocazione richiede la precisa conoscenza, garantita dalla presenza dello stub, dell’interfaccia dell’oggetto.
In realtà in CORBA è possibile accedere ad un oggetto, scoprire i suoi metodi ed eventualmente invocarli, senza avere  alcuno stub precompilato e senza conoscere a priori l’interfaccia esposta dall’oggetto CORBA.
Questo implica la possibilità di scoprire le informazioni di interfaccia a run-time. Ciò è possibile utilizzando una delle più note caratteristiche CORBA: la Dynamic Invocation Interface (DII).
La DII opera soltanto nei confronti del client, esiste un meccanismo analogo lato server (Dynamic Skeleton Interface, DSI) che consente ad un ORB di dialogare con un’implementazione senza alcuno skeleton precompilato.
Purtroppo, anche se DII fa parte del core CORBA, le sue funzionalità sono disperse su un gran numero di oggetti. Per invocare dinamicamente un metodo su di un oggetto i passi da compiere sono:

  • Ottenere un riferimento all’oggetto: anche con DII per utilizzare un oggetto è necessario ottenere un riferimento valido. Il client otterrà un riferimento generico ma, non avendo classi precompilate, non potrà effettuare un narrow o utilizzare meccanismi quali il bind fornito dall’Helper. 
  • Ottenere l’interfaccia: invocando il metodo get_interface_def sul riferimento si ottiene un IRObject e da questo, con narrow, l’oggetto navigabile di tipo InterfaceDef. Questo oggetto è contenuto nell’IR e consente di ottenere tutte le informazioni di interfaccia. Con i metodi lookup_name e describe di InterfaceDef è possibile reperire un metodo ed una sua completa descrizione.
  • Creare la lista di parametri: per definire la lista di parametri viene usata una struttura dati particolare, NVList (Named Value List). La creazione di una NVList è effettuata o da un metodo dell’ORB (create_operation_list) o da un metodo di Request (arguments). In ogni caso con il metodo add_item è possibile comporre la lista di parametri.
  • Creare la Request: la Request incapsula tutte le informazioni necessarie all’invocazione di un metodo (nome metodo, lista argomenti e valore di ritorno). Comporre la Request è la parte più pesante e laboriosa della DII. Può essere creata invocando sul reference dell’oggetto il metodo create_request o la versione semplificata _request.
  • Invocare il metodo: utilizzando Request esistono più modi di invocare il metodo. Il primo modo è quello di invocarlo in modalità sincrona con invoke. Il secondo modo è quello di invocarlo in modalità asincrona con send_deferred e controllare in un secondo momento la risposta con poll_response o get_response. Il terzo modo è l’invocazione senza response con send_oneway.


Come si può osservare, un’invocazione dinamica può essere effettuata seguendo percorsi differenti. Lo scenario più complesso implica un’interazione con l’Interface Repository (il secondo ed il terzo passo dell’elenco precedente) mediante la quale è possibile ottenere l’intera descrizione di un metodo.
Scenari più semplici non prevedono l’interazione con l’IR e sono ad esempio adottati quando ci si limita a pilotare l’invocazione dinamica con parametri inviati da script.
Poiché l’invocazione dinamica è un argomento complesso e di uso non comune, verrà proposto un semplice esempio che non utilizzi l’IR, ma sia pilotato da linea di comando.
Definiamo una semplice interfaccia come segue

// IDL
module dii {
 interface DynObject {
  string m0();
  string m1(in string p1);
  string m2(in string p1, in string p2);
  string m3(in string p1, in string p2, in string p3);
 }; 
};

Come si è detto in precedenza, l’uso di DII non ha alcuna influenza sul lato server. Il server si limiterà ad istanziare l’oggetto e a registrarlo col nome di DynObject sul Naming Service.

L’implementazione dell’oggetto è molto semplice ed ha come unico scopo quello di consentire il controllo dei parametri e del metodo invocato

package server;

import dii.*;

public class DynObjectImpl extends _DynObjectImplBase {
 
   public DynObjectImpl() { 
    super();
   }

 public String m0() {
  return "Metodo 0 # nessun parametro";
 }

 public String m1(String p1) {
  return "Metodo 1 # " + p1; 
 }

 public String m2(String p1, String p2) {
  return "Metodo 2 # " + p1 + " - " + p2; 
 }

 public String m3(String p1, String p2, String p3) {
  return "Metodo 3 # " + p1 + " - " + p2 + " - " + p3; 
 }
}

Il client deve identificare dall’input fornito da linea di comando il metodo da invocare ed i suoi eventuali parametri; il primo passo significativo da compiere è l’accesso al Naming Service

// …
org.omg.CORBA.Object obj = ncRef.resolve(path);

Poiché stub, skeleton, Helper ed Holder saranno distribuiti solo sul server, non è possibile effettuare un narrow.
A questo punto è possibile iniziare a costruire la Request con il metodo più semplice

org.omg.CORBA.Request request = obj._request(args[0]);

Si noti che il parametro passato args[0] è il nome del metodo che si intende utilizzare, letto da linea di comando.
Ora è possibile costruire la lista di argomenti, nel caso in esame la lista è costruita dinamicamente effettuando un parsing dell’array di input

org.omg.CORBA.NVList arguments = request.arguments();
 
for (int i = 1; i < args.length; i++) {
 org.omg.CORBA.Any par = orb.create_any();
 par.insert_string(args[i]);
 arguments.add_value("p" + 1, par,
        org.omg.CORBA.ARG_IN.value);
}

Ogni valore della NVList è rappresentato da un oggetto di tipo Any, questo è uno speciale tipo CORBA che può incapsulare qualunque altro tipo ed ha un’API che fornisce specifiche operazioni di inserimento ed estrazione di valori (nell’esempio si usano insert_string ed extract_string).
Per invocare il metodo è ancora necessario impostare il tipo di ritorno. Per fare questo si utilizza l’interfaccia TypeCode che è in grado di rappresentare qualunque tipo IDL. Le costanti che identificano i TypeCode dei vari tipi IDL sono fornite da TCKind

request.set_return_type(orb.get_primitive_tc
       (org.omg.CORBA.TCKind.tk_string));

request.invoke();

L’invocazione utilizza la normale chiamata sincrona di metodo data da invoke.
Terminata l’invocazione è possibile ottenere e visualizzare la stringa di ritorno

org.omg.CORBA.Any method_result = request.return_value();
System.out.println(method_result.extract_string());

L’esecuzione dell’esempio è possibile con VisiBroker utilizzando i consueti passi, in particolare si faccia riferimento all’esempio visto in precedenza sul Naming Service ([1]).
DII fornisce un meccanismo estremamente elastico e flessibile che prospetta un sistema assolutamente libero in cui tutti gli oggetti interrogano un Trader Service, individuano ed utilizzano dinamicamente i servizi di cui necessitano. 
Malauguratamente l’invocazione dinamica presenta alcuni limiti che ne condizionano l’utilizzo. In primo luogo l’invocazione dinamica è decisamente più lenta dell’invocazione statica poiché ogni chiamata a metodo implica un gran numero di operazioni remote.
In secondo luogo la costruzione di Request è un compito complesso e richiede uno sforzo supplementare in fase di sviluppo (lo sviluppatore deve implementare i compiti normalmente svolti dallo stub). L’invocazione dinamica è inoltre meno robusta in quanto l’ORB non può effettuare il typechecking prima dell’invocazione, questo può causare anche un crash durante l’unmarshalling.
 
 
 

Conclusioni
In questo articolo abbiamo visto un semplice esempio di invocazione dinamica con CORBA. Il prossimo mese vedremo come realizzare callback con CORBA.
 
 
 

Bibliografia
[1] G. Morello - "Corso CORBA IV Parte: CORBA Naming Service", Mokabyte N. 51, Aprile 2001
 

Allegati
Gli esempi completi si possono scaricare qui

Vai alla Home Page di MokaByte
Vai alla prima pagina di questo mese


MokaByte®  è un marchio registrato da MokaByte s.r.l.
Java®, Jini®  e tutti i nomi derivati sono marchi registrati da Sun Microsystems; tutti i diritti riservati
E' vietata la riproduzione anche parziale
Per comunicazioni inviare una mail a
mokainfo@mokabyte.it