MokaByte 53 - Giugno  2001
Foto dell'autore non disponibile
di
Gianluca Morello
Corso CORBA
VI Parte: Basic Object Adapter
L’Object Adapter è il componente dell’architettura CORBA che assiste l’ORB nel recapitare le richieste ad un’implementazione valida e nelle operazioni di attivazione/disattivazione dei servant.
Introduzione
L’Object Adapter è un componente molto importante dell’architettura CORBA. Uno dei suoi compiti è quello di associare un riferimento ad una specifica implementazione nel momento in cui un oggetto è invocato. Quando un client effettua un’invocazione l’adapter collabora con l’ORB e con l’implementazione per fornire il servizio richiesto.

Se un client chiama un oggetto che non è effettivamente in memoria, l’adapter si occupa anche di attivare l’oggetto affinché questo possa rispondere all’invocazione. In molte implementazioni l’adapter può occuparsi anche di disattivare un oggetto non utilizzato da lungo tempo. Dal punto di vista del client l’implementazione è sempre disponibile e caricata in memoria.

Formalmente le specifiche CORBA individuano per l’adapter sei funzionalità chiave:

  • Generazione ed interpretazione degli object references
  • Invocazione dei metodi attraverso lo skeleton
  • Sicurezza delle interazioni
  • Autenticazione alla chiamata (utilizzando un’entità CORBA detta Principal)
  • Attivazione e disattivazione degli oggetti
  • Mapping tra reference e corrispondente implementazione
  • Registrazione dell’implementazione


Queste funzionalità sono associate al componente logico adapter e nella pratica sono compiute in collaborazione con il core dell’ORB ed eventualmente con altri componenti; alcune funzionalità sono delegate integralmente all’ORB ed allo skeleton. L’adapter è comunque coinvolto in ogni invocazione di metodo.

Le funzionalità dell’adapter rendono disponibile via ORB l’implementazione del CORBA object e supportano l’ORB nella gestione del run-time environment dell’oggetto. Dal punto di vista del client, l’adapter è il componente che garantisce che le sue richieste siano recapitate ad un oggetto attivo in grado di soddisfare la richiesta.

Il meccanismo CORBA opera in modo tale da consentire l’utilizzo contemporaneo di più tipologie di adapter con differenti comportamenti (nelle specifiche gli adapter vengono definiti pluggable). A livello di design, l’idea di individuare un’altra entità come l’adapter nasce dalla necessità di modellare in maniera flessibile alcuni aspetti senza estendere l’interfaccia dell’ORB, ma individuando nuovi moduli pluggable.

Il primo tipo di adapter introdotto da OMG è il Basic Object Adapter o BOA. Poiché le specifiche del BOA erano lacunose (non definivano ad esempio i meccanismi di attivazione e disattivazione degli oggetti), i vari venditori finirono per realizzarne implementazioni proprietarie largamente incompatibili tra loro che minavano di fatto la portabilità lato server di CORBA. Per questa ragione OMG decise di abbandonare il BOA specificando un nuovo tipo di adapter, il Portable Object Adapter o POA.

Nel caso in cui l’ORB scelto supporti il POA sarà sicuramente opportuno utilizzarlo. Esistono tuttavia molti ORB che attualmente non forniscono un’implementazione del POA. Per questa ragione sarà esaminato anche il BOA nonostante il suo utilizzo sia formalmente deprecato.

Per gli esempi di questa sezione non sarà possibile utilizzare l’implementazione SUN che non fornisce BOA e POA.
 
 
 

Basic Object Adapter (BOA)
Le specifiche CORBA elencano come compiti primari del BOA la creazione/distruzione degli object reference ed il reperimento delle informazioni a questi correlate. Nelle varie implementazioni il BOA, per compiere le sue attività, può accedere al componente proprietario Implementation Repository.

Come si è detto in precedenza, BOA va pensato come un’entità logica ed in effetti alcuni dei suoi compiti sono svolti in cooperazione con altri componenti (ad esempio la creazione e la distruzione di object reference sono a carico dello skeleton). Questo ha un impatto sulla sua implementazione che solitamente suddivide i suoi compiti tra il processo ORB, il codice generato dal compilatore IDL e l’effettivo BOA.

Comunque il BOA fornisce l’interfaccia con i metodi necessari a registrare/deregistrare gli oggetti e ad avvertire l’ORB che l’oggetto è effettivamente pronto a rispondere alle invocazioni. 

Si è già visto negli articoli precedenti il concetto di server: il server è un’entità eseguibile separata che attiva l’oggetto e gli fornisce un contesto di esecuzione. Anche il BOA per attivare un oggetto si appoggia ad un server. 

Il server può essere attivato on demand dal BOA (utilizzando informazioni contenute nell’Implementation Repository) oppure da qualche altra entità (ad esempio uno shell script). In ogni caso il server attiverà l’implementazione chiamando il metodo obj_is_ready oppure il metodo impl_is_ready definiti nel seguente modo

// PIDL
module CORBA { 

 interface BOA { 
  
  void impl_is_ready (in ImplementationDef impl); 
  
  void deactivate_impl (in ImplementationDef impl); 
  
  void obj_is_ready (
   in Object obj, in ImplementationDef impl
  ); 

  void deactivate_obj (in Object obj); 

  // … altri metodi di generazione references
  // ed access control
 }; 
}; 

In quasi tutti gli ORB, obj_is_ready è il metodo di registrazione del singolo oggetto all’interno di un server, stabilisce un’associazione tra un’istanza ed un’entità nell’Implementation Repository.

Il metodo impl_is_ready è comunemente implementato come un loop infinito che attende le request del client, il ciclo non cessa fino a quando non viene invocato il deactivate_impl.
 
Esistono molti modi di combinare un server process con l’attivazione di oggetti (un server registra un oggetto, un server registra n oggetti, …). Le specifiche CORBA individuano quattro differenti politiche di attivazione:

  • Shared Server: il processo server inizializza più oggetti invocando per ognuno obj_is_ready. Al termine di queste inizializzazioni il server notifica al BOA, con impl_is_ready, la sua disponibilità e rimane attivo fino all’invocazione di deactivate_impl. Gli oggetti possono essere singolarmente disattivati con deactivate_obj. La disattivazione è quasi sempre automatizzata dal distruttore dello skeleton.
  • Unshared Server: ogni oggetto viene associato ad un processo server differente. L’inizializzazione avviene comunque con le due chiamate obj_is_ready ed impl_is_ready.
  • Server-per-method: un nuovo processo viene creato ad ogni invocazione. Il processo termina al terminare dell’invocazione e, poiché ogni invocazione implica un nuovo processo, non è necessario inviare una notifica al BOA (alcuni ORB richiedono comunque l’invocazione di impl_is_ready).
  • Persistent Server: tipicamente è un processo avviato mediante qualche meccanismo esterno al BOA (shell script o avvio utente) e va registrato mediante impl_is_ready. Dopo la notifica al BOA si comporta esattamente come uno shared server.


A differenza di quanto indicato nelle specifiche, la maggior parte delle implementazioni fornisce un sottoinsieme delle activation policy. In generale l’utilizzo dei metodi legati al BOA è differente tra i vari ORB e genera quasi sempre problemi di portabilità per quanto concerne l’attivazione delle implementazioni.
 
 
 

BOA in pratica
Riscriviamo ora l’applicazione ShoppingCart utilizzando l’implementazione BOA fornita da Visibroker. Per quanto detto sugli adapter si dovrà intervenire sulle classi coinvolte nell’attivazione e nell’invocazione degli oggetti CORBA, andranno quindi modificate le seguenti classi: ShoppingCartServer, ShoppingCartFactoryImpl e ShoppingCartClient.

Utilizziamo il modello di server persistent, una classe Java con metodo main lanciata da linea di comando o da script. La registrazione dell’oggetto Factory sarà effettuata via BOA.

Il BOA è un cosiddetto pseudo-object ed è possibile ottenere un riferimento valido ad esso mediante l’invocazione di un metodo dell’ORB: BOA_init. Nell’implementazione Visibroker esistono due differenti metodi BOA_init che permettono di ricevere un BOA inizializzato con differenti politiche di gestione dei thread (thread pooling o per session) e di trattamento delle comunicazioni (utilizzo di Secure Socket Layer o no).

Invocando il metodo BOA_init senza parametri si otterrà un BOA con le politiche di default (thread pooling senza SSL). Il codice completo della classe server è

package server;

import shopping.*;

public class ShoppingCartServer {
  
  public static void main(String[] args) {
    
 // Inizializza l'ORB.
 org.omg.CORBA.ORB orb =
        org.omg.CORBA.ORB.init(args,null);
            
 // Crea l'oggetto Factory
 ShoppingCartFactoryImpl factory = 
  new ShoppingCartFactoryImpl("ShoppingCartFactory");

    // Inizializza il BOA
    // N.B. Utilizzo classi proprietarie
 com.inprise.vbroker.CORBA.BOA boa = 
   ((com.inprise.vbroker.CORBA.ORB)orb).BOA_init();

 // Esporta l'oggetto factory
 boa.obj_is_ready(factory);
     
    System.out.println(factory + " is ready.");
     
    //  Attende le requests
    boa.impl_is_ready();

  }
}

Si noti che istanziando l’oggetto Factory si è fornita al costruttore la stringa "ShoppingCartFactory". In Visibroker, specificando un object name quando si istanzia l’oggetto, si ottiene un riferimento di tipo persistent. Il costruttore dell’oggetto dovrà comunque notificare il nome alla superclasse

public ShoppingCartFactoryImpl(String name) {
   super(name);
}

L’associazione tra un reference persistent ed il nome viene registrata in un tool proprietario denominato OSAgent o ORB Smart Agent.

Pur essendo uno strumento proprietario OSAgent è molto utilizzato nella pratica. Fornisce una versione semplificata di naming service (con Visibroker viene fornita anche un’implementazione standard del servizio) ed implementa alcuni meccanismi proprietari di fault-tolerance e load-balancing. Per una trattazione completa dell’OSAgent si faccia riferimento alla documentazione Visibroker (www.inprise.com).

Un riferimento persistent rimane vivo nell’OSAgent anche al termine del processo server (può essere verificato utilizzando il tool osfind).  Un riferimento transient è invece rigidamente associato al ciclo di vita del server e non può avvalersi dei meccanismi di load-balancing e fault-tolerance forniti dall’OSAgent.

Nell’esempio l’unico oggetto con riferimento persistent è la Factory. I vari carrelli hanno riferimenti transient, sono registrati solo dalla chiamata obj_is_ready della Factory e sono quindi accessibili solo tramite questa.

Anche utilizzando il BOA, l’oggetto CORBA deve specializzare la classe <Interfaccia>ImlBase. Ecco il codice completo della Factory

package server;

import shopping.*;
import java.util.*;

public class ShoppingCartFactoryImpl extends
        _ShoppingCartFactoryImplBase {

   private Dictionary allCarts = new Hashtable();
 
   // N.B. Registro nell’OSAgent
 public ShoppingCartFactoryImpl(String name) {
    super(name);
   }

 public synchronized ShoppingCart 
       getShoppingCart(String userID) {

     // Cerca il carrello assegnato allo userID...
     shopping.ShoppingCart cart = 
    (shopping.ShoppingCart) allCarts.get(userID);

     // ...se non lo trova...
     if(cart == null) {
      
        // Crea un nuovo carrello...
        cart = new ShoppingCartImpl();

        // Rende l'oggetto disponibile sull'ORB
 // N.B. _boa() è fornito dalla classe 
 // com.inprise.vbroker.CORBA.Object
   _boa().obj_is_ready(cart);
      
        System.out.println("Created " + userID 
          + "'s cart: " + cart);
      
        // Salva il carrello nel dictionary 
   // associandolo allo userID
        allCarts.put(userID, cart);
     }

     // Restituisce il carrello
     return cart;
 }
}

Il client può ottenere un riferimento alla Factory invocando il metodo bind fornito dall’Helper che si preoccupa anche di eseguire l’opportuno narrow.

Ecco il codice completo della classe client

package client;

import shopping.*;
import org.omg.CORBA.*;

public class ShoppingCartClient {
  
 public static void main(String args[]) {
   
    if (args.length != 3) {
      System.err.println("Uso corretto: 
    java ShoppingCartClient userId Autore Titolo");
        return;
     }

     // Crea ed inizializza l'ORB
    ORB orb = ORB.init(args, null);

     //  Localizza l'oggetto Factory
     ShoppingCartFactory factory =
    ShoppingCartFactoryHelper.bind
     (orb, "ShoppingCartFactory");

     // Ottengo dalla Factory un'oggetto ShoppingCart
     ShoppingCart cart = factory.getShoppingCart(args[0]);
      
     // Aggiungo un libro
     cart.addBook(new Book(args[1],args[2]));
      
     // Ottengo la lista dei libri e la stampo
     Book[] list = cart.getBookList();      
     for(int i=0; i<list.length; i++)
        System.out.println("Autore " + list[i].Author 
       + " - Titolo " + list[i].Title);
            
   }
}

Prima di avviare il server si attivi l’OSAgent (nel caso si lavori in ambiente distribuito è necessario attivare l’OSAgent sia sulla macchina client che sulla macchina server). Fatto questo, per l’esecuzione dell’esempio si compiano i soliti passi. I riferimenti persistent registrati nell’OSAgent sono controllabili usando il tool osfind.
Conclusioni
In questo articolo abbiamo introdotto il concetto di Object Adapter ed abbiamo utilizzato il Basic Object Adapter. Il prossimo mese vedremo i benefici di portabilità offerti dal Portable Object Adapter.
 
 

Bibliografia
[1] G. Morello - "Corso CORBA V Parte: Il pattern Factory in uno scenario CORBA", Mokabyte N. 52, Maggio 2001
[2] G. Morello - "Corso CORBA III Parte: un semplice esempio", Mokabyte N. 50, Marzo 2001

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