L’actualité de l’OAMP
CORBA

16/10/08

 

CORBA

 

 

Historique

Les premières machines construites étaient très coûteuses, pour une puissance de calcul modeste. Pour les rentabiliser, il fallait les partager.

L'apparition des machines de bureau a produit une révolution : la capacité de calcul est maintenant partagée par les utilisateurs. Le très faible coût des machines, pour une puissance importante, entraîne leur multiplication. La puissance disponible est très peu utilisée (temps morts très longs).

Certaines applications sont coûteuses :

D'où un grand avantage à les acheter/ installer en unique exemplaire.

Cette remarque a amené le Modèle Client-Serveur. L'application est installée en unique exemplaire sur le serveur, et tous les clients s'y connectent pour travailler.

Application de calcul unique =>

L'augmentation de la puissance des machines de bureau a entraîné l'existence de serveurs bon marché, ce qui a popularisé ce système.

Le modèle Client-Serveur est maintenant ancien et bien rodé.

 

Divers modèles

Modèle Client-Serveur à 2 niveaux :

  • interface utilisateur + logique d'accès ;
            client
  • base de données + logique d'accès.
  serveur

La logique d'accès est répartie sur le serveur et le client. Par suite, toute modification de cette logique sur le serveur entraîne l'échange des postes clients.

Modèle Client-Serveur à 3 niveaux :

  • interface utilisateur ; client
            client
  • règles d'accès (logique de l'application) ;
  serveur
  • base de données.
  serveur

Cette seconde forme sépare l'interface client et la logique d'accès. Le niveau 2 est intermédiaire entre les deux autres, qui s'ignorent. Les deux derniers niveaux sont sur le serveur.

 

Applications distribuées

Le modèle Client-Serveur présente des limitations :

Solution alternative :

Pour accéder à une grande puissance de calcul, une autre solution est envisageable : partager le travail.

Ceci entraîne une symétrie de l'architecture du réseau (chaque élément peut demander/donner des infos aux autres).

Une application distribuée est :

 

Séparation entre l'interface et l'implémentation


schéma de l'application

L'implémentation peut changer sans incidence sur les autres constituants si l'interface est conservée. Cette interface réalise une abstraction de la fonction calculée par rapport au calcul lui-même.

Cette notion d'interface s'est répercutée sur les langages de programmation :

extension Interface sous Pascal ; Modula et Modula II Java ...

 

Services

Les modules d'une application répartie proposent des services. Parmi eux :

 

Environnements répartis

Une application répartie est une application découpée en n parties distinctes, compilées séparément, et qui communiquent entre elles par l'intermédiaire d'un réseau.

Difficultés d'écriture

Les 4 premiers sont structurels ; le dernier est dû à la jeunesse du domaine.

Définition

Un environnement réparti est un outil logiciel permettant de développer facilement des applications réparties. Pour y parvenir, des choix sont faits, qui simplifient le problème général :

Depuis peu, des environnements répartis sont disponibles : CORBA, DCE, DCOM, Java RMI

Actuellement, tous les environnements sont basés sur les mêmes principes.

DCE

L'un des plus anciens.
Produit par OSF (Open Software Foundation)
Supporte l'appel de procédure à distance
Peu répandu

DCOM

Offre Microsoft
Tourne sous les systèmes Microsoft, porté sur d'autres.
D'après Microsoft "tourne mieux sous les systèmes Microsoft"...
Passerelle avec CORBA, mais tout les objets ne passent pas

Java RMI

Java Remote Method Invocation
Très proche de CORBA pour l'architecture, presque complet
Possibilité de passer des objets par valeur
Mais tout doit être programmé en Java

CORBA

CORBA est le plus général et le plus souple... mais ce n'est pas un environnement ! Seulement des directives !

 

Architecture

Des services sont ajoutés pour résoudre certains problèmes structurels (répondant aux difficultés d'écriture).

Dans le schéma initial, les transactions, la localisation etc. doivent être pris en charge par chaque module.

 

Principes

Le découpage de l'application en vue de sa répartition peut se faire de très nombreuses façons, en fonction de la taille des fragments.

Pour un découpage particulier, la proportion du nombre d'échanges par rapport au temps de calcul intermédiaire définit la pertinence du découpage.

Découpage fin :   Temps Echanges >> Temps Calcul     regrouper des éléments
Découpage grossier :   Temps Echanges << Temps Calcul     trop centralisé

L'opération de découpage force le concepteur à déterminer quelles sont les relations entre les éléments de l'application et à mieux la structurer.

Pour un volume de calcul déterminé, le rapport Temps Echanges / Temps Calcul est minimal pour l'appel de procédure :

L'éclatement d'une application centralisée pour en faire une application répartie est donc fortement conditionné par la nature des échanges d'information entre ses constituants.

Si on considère la répartition en plaçant le programme principal sur une machine, et la procédure sur une autre, on trouve un modèle particulier :

C'est cette forme particulière qui a été choisie pour définir l'environnement de programmation réparti CORBA.

 

Appel de Procédure

L'appel de procédure à distance a été développé d'abord par Sun, pour le système de gestion de fichiers NFS (Network File System).

En application centralisée, l'appel de procédure utilise la pile de récursivité du système :

Exemple :

// déclaration
   public class C
     { public static int f(int n)
         { // implémentation de la méthode 
           return s;
         }
     }
 
// utilisation
   public C c = new C();


   ...


   int s = c.f(10);

En centralisé, l'opération de liaison est réalisée à la compilation (la méthode est statique, en Pascal ou C la liaison dynamique n'existe pas).

Dans une application répartie, l'opération de liaison (à la compilation) est impossible. Il faut développer un mécanisme de remplacement. Celui-ci utilisera le réseau, donc l'appel devra être mis en forme pour y transiter, puis le calcul effectué sur le serveur, puis le résultat sera à son tour mis en forme pour rejoindre le client.


Forme particulière du modèle Client-Serveur

Appel de Procédure

Un choix de présentation fondamental est fait dès le début : conserver l'appel de procédure normal, sur le serveur et le client. Ceci signifie que l'instruction d'appel d'une méthode sera exactement la même, que la méthode soit locale ou distante.

L'appel par le client sera un véritable appel d'une procédure locale. Il n'est pas question de placer la procédure elle-même sur la machine cliente, aussi on la remplacera par un fantôme qui aura même apparence pour le client, mais qui fera faire le calcul par le serveur.

La procédure fantôme simulera la procédure distante :

Du côté serveur pareillement.

Les deux interfaces (client et serveur) permettront de plus de standardiser la forme de l'appel et du retour :

Exemple :

client
serveur
// appel p = f(n);
   String sn = code(n);
   requête("f", sn, serveur);
   attenteResultat(z);
   int r = decode(z);
   return r
 
// traitement de la requête
   réception Requete(nom, param, client);
   int x = decode(y);
   switch (nom)
     { ...
       case "f" : int r = f(x);
       ...
     }
   String sr = code(r);
   envoi(sr, client);

 

Symétrisation

Chaque machine peut être à la fois client et serveur, donc posséder les deux interfaces.

Interface définitive

Les interfaces client et serveur vont combiner les propriétés :

La structure choisie (modèle Client-Serveur, appel de procédure à distance) est particulière ;

Mais elle permet de résoudre la plupart des problèmes d'applications distribuées ;

Elle offre un cadre de développement rapide ;

Elle est moins efficace qu'une application écrite directement (sans ces outils).

 

Modes d'appel

synchrone attente de la réponse
synchrone différé pas d'attente ; test périodique si réponse prête
asynchrone pas d'attente ; le serveur prévient

 

CORBA

CORBA est un sigle, signifiant :

Common Object Request Broker Architecture

CORBA est produit par le groupe 'Object Management Group' (OMG).

Celui-ci était constitué de 8 sociétés commerciales en 1989, il en compte plus de 800 aujourd'hui. La plupart des compagnies qui développent du logiciel en font partie.

 

But de l'OMG

CORBA n'est pas un logiciel

C'est un ensemble de directives destinées à créer un standard pour assurer la portabilité et l'interaction des applications réparties. L'Object Model défini par l'OMG définit une sémantique commune des objets pour spécifier :

Le principe de CORBA est de créer sur un site client un fantôme de l'objet serveur, et d'appliquer des méthodes sur cet objet. Pour l'utilisateur, tout se passe comme si l'objet serveur était sur sa machine.

Implémentations

Il n'existe pas à l'heure actuelle d'implémentation complète gratuite.

 

Structure de CORBA

OMA : Object Management Architecture

C'est une abstraction d'un système distribué complet. Elle est composée de 4 parties :

Les langages de programmation utilisables pouir programme dans l'environnement CORBA sont nombreux : Java, C++, mais aussi C et même COBOL !

ORB : Object Request Broker

C'est la partie principale de l'OMA

L'ORB est une abstraction du réseau :

Object Services

Service qui permet de créer des objets, de gérer les contrôles d'accès, de suivre les mouvements des objets sur le réseau... Il est utilisé par l'ORB.

 

Programmation

a) Initialisation du serveur

  1. initialisation de l'ORB :
      org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init(args, null);
  2. initialisation de l'adaptateur d'objet : BOA (Basic Object Adaptor)
      org.omg.CORBA.BOA boa = orb.BOA_init(args, null);
  3. création d'un objet serveur :
      soit Srv la classe du serveur ; il est implémenté dans une classe SrvImpl
      SrvImpl srv = new SrvImpl();
  4. enregistrement de l'objet sur le BOA :
      boa.connect(srv);
  5. activation de l'objet :
      boa.obj_is_ready(srv);
  6. attente d'une demande d'un client :
      try 
    	  { boa.impl_is_ready();      // attente demande
         } 
      catch (org.omg.CORBA.SystemException e)
         { e.printStackTrace();
         }

 

POA : Portable Object Adaptor de préférence au BOA.

Common Facilities et Application Objects sont les composants disponibles directement pour le programmeur. Ils utilisent les composants système.

CORBA accepte divers langages de programmation. Il est donc possible d'écrire un serveur en C++ et le client qui l'utilise en Java par exemple. Entre les deux vont circuler des données et des requêtes, qui devront être compréhensibles par les deux. Les formats de données étant différents dans les divers langages, il faudra intercaler entre les deux un code standard, intelligible pour les deux.

A cette fin, CORBA définit un langage de communication entre le client et le noyau. D'autres systèmes existent, et CORBA prévoit des passerelles pour communiquer avec eux (Internet Inter ORB Protocol IIOP par exemple).

 

Partage des objets

On entre dans la difficulté de réalsitaion de CORBA.

L'accès aux objets se fait par le mécanisme de référence en Java. Une référence est un codage permettant l'accès interne à l'objet (elle se traduira au bout du compte par une adresse en mémoire). Ce mécanisme est local ; pour accéder à des objets distants, il faut donc un nouveau mécanisme.

La conservation de la forme d'appel (identique sur le client et le serveur) nous impose une contrainte particulière : programmation standard du client et du serveur (sauf les inits).

Difficulté : le client doit localiser l'objet et la méthode à invoquer sur le serveur. Il dispose pour cela d'un catalogue des méthodes disponibles (serveurs différents, méthodes différentes sur un serveur).

Pour que le client puisse accéder à un objet, il doit en avoir une référence. Cette référence provient du serveur, et doit donc voyager sur le réseau. Il faut pour cela la mettre sous une forme indépendante du langage, du système... Les consepteurs de CORBA ont choisi une forme textuelle. Ce choix est justifié par le fait que les adresses réseau (dil.univ-mrs.fr) sont déjà textuelles.

 

Elaboration des références

Les services offerts sont chez le serveur c'est lui qui élabore les références.

La transmission de la référence au client se fait par le réseau. Il existe plusieurs façons de le faire, que l'on verra plus loin.

Utilisation des références

Pour effectuer un appel de méthode standard, le client doit posséder un objet local. Il doit reconstruire une référence normale à partir du texte, vers un objet du bon type :

L'objet local a plusieurs rôles, lorsqu'une de ses méthodes est invoquée :

 

Langage IDL

Interface Description Language IDL

Description des interfaces indépendante des langages utilisés pour les écrire.

Redondance : réécrire les interfaces dans un langage standard, quel que soit le langage d'implémentation.

On écrit tout d'abord le serveur dans le langage de programmation choisi. Ensuite, on en extrait la partie des déclarations publiques, et on la met en forme dans la syntaxe IDL.

Une description IDL met en communication le client et le serveur (via l'ORB). Elle leur permet de communiquer des objets ; donc la même interface doit être utilisée par les deux :

A partir d'une description, un compilateur produit deux éléments :

 

Description IDL

Commentaires :

// une ligne  
ou bien
  /* plusieurs lignes *

Une description IDL est constituée d'une ou plusieurs interfaces. Chaque interface permet de décrire un nombre illimité de fonctions.

interface <nom>

   { <corps> } ;

Types de base IDL

type IDL desription type Java
boolean TRUE / FALSE boolean
octet 0 .. 255 byte
char caractère ASCII char
wchar caractère sur plusieurs octets char
short entier short
unsigned short entier sans signe short
long entier int
unsigned long entier sans signe int
long long entier long long
unsigned long long   long
double réel double
float réel float
long double réel très long /
string chaîne de char String
wstring chaîne de wchar String
void / void

 

Données structurées

 struct nom_struct
     { type1 nom1;
       type2 nom2;
	     ...
     }  

 

Paramètres

Les paramètres de procédures peuvent être passés de trois façons différentes, distinguées par un mot-clé placé avant :

Exemple : void fonction(in long n, out long m);

 

Première réalisation

Soit à réaliser un serveur de temps, qui donne l'heure. Il comporte une seule méthode getTime().

Voici la description de son interface IDL :

interface Horloge
  { string getTime();
  }; 

à placer dans un fichier : Horloge.idl.

La compilation de ce fichier en produit quelques autres ; en particulier les amorces :

Références d'objets

Un objet serveur est créé. Sa référence est convertie en référence externe :

Communication des références

Il y a plusieurs façons d'échanger la référence d'un objet entre le serveur et le client. Nous allons voir d'abord la première utilisée, qui est la plus simple si ce n'est la plus souple.

Le serveur range cette référence (texte) dans un fichier. Le client va lire ce fichier, puis traduire la référence texte en référence Java d'un objet.

Problème : la classe de l'objet se trouve sur le serveur. Pour traduire la chaîne de caractères en référence, chez le client, il faut une fonction qui retourne un objet du bon type. Celui-ci n'est pas connu lors de la définition de la fonction. Donc elle ne peut pas returner le type exact, mais seulement un sur-type, ancêtre du type nécessaire. Ce sera le type Object de CORBA.

Créer une classe Corba.Object obligerait les programmeurs à la sous-classer, interdisant l'héritage d'une autre classe en java. Pour autoriser l'héritage multiple, il faut que l'un des types soit une interface. Donc, CORBA définit l'interface :

interface org.omg.CORBA.Object;

La traduction de la référene texte se fait alors vers une référence CORBA.Object par :

org.omg.CORBA.Object obj = string_to_object(ref_text);

 

Côté client, on crée une classe conteneur, qui implante cette interface :

 

La conversion peut se faire de la référence texte vers le type conteneur par :

org.omg.CORBA.Object obj = orb.string_to_object(ref_text);

Il reste à transformer ce type vers le type d'origine, en utilisant la classe Helper produite par le compilateur IDL. La compilation du fichier <nom>.idl produit le fichier :

<nom>Helper

Exemple : HorlogeHelper

La classe Helper contient la méthode narrow qui converti :

Horloge horloge = HorlogeHelper.narrow(obj);

Classe Holder

Le paramètre d'une méthode en Java est inchangé :

    public void Add2(int i)
      { i = i + 2;
      }    

Si le paramètre est déclaré out ou inout dans l'interface IDL, il doit changer !

Méthode : l'enrober dans une classe, et créer un objet de cette classe :

    public class conteneur
      { public int val;
      }
    
    public void Add2(conteneur i)
      { i.val = i.val + 2;
      }

Si n est un conteneur, n.Add2() modifie la valeur de n.i.

 

L'exemple complet

 // fichier Horloge.idl :
      interface Horloge
	      { String getTime();
	      };

En plus de l'interface, il faut écrire l'implémentation du servant :

    public class HorlogeImpl extends _HorlogeImplBase
        { public String getTime()
           { java.util.Calendar calendar =
                                   new java.util.GregorianCalendar();
             String str = "[" + calendar.get(java.util.Calendar.HOUR_OF_DAY);
             str = str + ":" + calendar.get(java.util.Calendar.MINUTE) + "]";
             return str;
           }
        }

... puis le serveur :

    public class Serveur
	      {
          public static void main( String[] args)
            { // init ORB 
              org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init(args, null);
              // init BOA voir p 29
              org.omg.CORBA.BOA boa = orb.BOA_init(args, null);
              // création de l'objet de traitement
              HorlogeImpl horloge = new HorlogeImpl();
              // enregistrement et activation de l'objet
              boa.connect(horloge);
              boa.obj_is_ready(horloge);
              // création du fichier référence
      
        try
                { String ref = orb.object_to_string(horloge);
                  java.io.FileOutputStream file =
                                 new java.io.FileOutputStream("ObjectId");
                  java.io.PrintStream pfile = new java.io. PrintStream (file);
                  pfile.println(ref);
                  pfile.close();
                }
              catch (java.io.IOException ex)
                { ex.printStackTrace();
                }
      
        // attente du client...
              try
               { System.out.println("le serveur est prêt");
                 boa.impl_is_ready();
               }
              catch (org.omg.CORBA.SystemException e)
               { e.printStackTrace();
               }
            }
	      }      

et enfin le client :

    public class Client
	      {	public static void main(String[] args)
			      {	// init ORB 
				      org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init(args, null);
				      // création de l'objet
				      org.omg.CORBA.Object obj = null;
				      try
					      {	java.io.FileInputStream file =
                                 new java.io. FileInputStream ("ObjectId");
						      java.io.InputStreamReader myInput =
                                 new java.io.InputStreamReader (file);
						      java.io.BufferedReader reader =
                                 new java.io.BufferedReader (myInput);
						      String ref = reader.readline();
						      obj = orb.string_to_object(ref);
					      }
      
				      catch (java.io.IOException ex)
					      {	ex.printStackTrace();
					      }
				      try
					      {	Horloge horloge = new HorlogeHelper.narrow(obj);
						      System.out.println("Heure = " + horloge.getTime());
					      }
				      catch (org.omg.CORBA.SystemException ex)
					      {	ex.printStackTrace();
					      }
			      }
	      }      

 

Schéma


Schéma simplifié d'une application répartie conforme à CORBA

 

Le bus CORBA

C'est l'ensemble des modules qui permettent l'utilisation d'objets répartis Ses fonctions sont :

Ces fonctions sont assurées par un ensemble de composants :

ORB (Object Request Broker) avec ses sous-composants :

  • GIOP (General Inter Orb Protocol) et
  • IIOP (Internet Inter Orb Protocol) ;
c/s
IFR (Interface Repository ou Référentiel d'interfaces) application qui regroupe les descriptions IDL des objets
c/s
SII (Static Invocation Interface) interface d'invocations statiques, contient les souches
c
DII (Dynamic Invocation Interface) construction d'invocations dynamiques sans utiliser de souche
c
SSI (Skeleton Static interface) interface d'invocations statiques, contient les squelettes
s
DSI (Dynamic Skeleton Interface) correspondant de DII pour le serveur
s
OA (Object Adapter) deux implémentations : BOA et POA
s

 

Schéma détaillé

 

Tout ceci est décrit en IDL.

Implémentations diverses possibles :

GIOP fournit :

GIOP est un protocole abstrait ; IIOP est une implémentation de GIOP.

 

Les références

Dans le protocole GIOP, les références sont codées selon le format IOR (Interoperable Object References).

Le format IOR contient :

Les IOR sont des chaînes de caractères hexadécimaux ; Les IOR sont définies dans un module IDL A la description IDL correspond une implémentation. D'autres formats sont en cours de réalisation.

 

Les services CORBA

Les services sont des appels standardisés à des fonctions du système réparti CORBA.

Naming Service (annuaire) permet de nommer des objets et de les retrouver
Event Service échange d'événements entre objets
Trader Service gestion de l'activité des objets, permettant de retrouver un objet grâce à son activité
Persistent Object Service sauvegarde sur support stable
Life Cycle Service gestion de la création, de la copie, de la destruction des objets
Concurency Service gère les accès concurrents (verrous)
Externalization Service gestion de l'état d'un objet sous forme exportable
Relationship Service gestion de relations entre objets
Transaction Service gestion de transactions (engagement, abandon) de manière fiable
Query Service récupération d'attributs des objets grâce à un langage style SQL
Property Service définition de propriétés des objets, données par le concepteur
Time Service définition d'une horloge logique
Security Service authentification, intégrité et confidentialité
Collection Service gestion de tables de hachage
Versioning Service gestion d'un numéro de version des objets
Licence Service commercial ! permet de tracer l'utilisation des objets pour régler le concepteur
Notification Service complément du service d'événements ; gestion de filtres sur les événements

 

Les références initiales

Le passage de références du serveur au client se fait rarement par un fichier :

On a développé d'autres mécanismes :

Les références initiales sont des références qui font partie de l'ORB. Elles mettent en correspondance un nom et une référence. Elles permettent donc d'accéder par nom à un objet.

On obtient la liste des références initiales par une méthode de l'ORB :

String [] liste = orb. list_initial_services();

On obtient la référence correspondant à un nom par :

    org.omg.CORBA.Object ref = null;
    try
	     {	ref = orb.resolve_initial_reference("nom ");
	     }
    catch (org.omg.CORBA.ORBPackage.InvalidName e)
       {	
	     }

Attention :

 

Naming Service

Ce service met en relation un nom et une référence. Il permet d'accéder aux objets qui n'ont pas de référence initiale.

// module CosNaming
	  {	typedef Istring;
                                                    
		struct NameComponent
			{	Istring id;
				Istring kind;
			}
                                                    
		typedef sequence <NameComponent> Name;

		.......
	  }

 

Dans NameComponent :

Un Name est une collection de NameComponent. Un NameComponent est appellé une liaison.

Les objets étant identifiés par nom, on peut mettre dans la collection un ident de Name. Ceci hiérarchise l'annuaire sous forme arborescente.

On peut accéder à un élément par le chemin qui le représente dans l'arbre, comme dans les directories.

 

Contextes de désignation

Un contexte de désignation regroupe des références. Pour le décrire, il existe l'interface :

org.omg.CosNaming.NamingContext

Il offre toutes les méthodes de gestion :

Création d'un contexte

On crée un contexte par définition d'un objet qui implante l'interface NamingContext, ou bien création d'une implantation standard par la fonction new_context(). On l'ajoute à l'annuaire par bind_context().

Recherche d'une référence

C'est celle qu'utilise le client pour accéder à un objet. Elle se fait par la fonction resolve (Name). C'est l'opération de résolution de nom. Cette fonction s'applique à un contexte ou à un sous-contexte. Elle recherche le nom dans le sous-contexte auquel elle s'applique. Elle retourne soit une référence d'objet, soit une référence de contexte.

Interface NamingContext

    interface NamingContext 
	     { enum NotFoundReason 
			    { missing_node, 
	 			  not_context, 
				  not_object 
			    };
                      
		   exception NotFound 
			    { NotFoundReason why; 
				  Name rest_of_name; 
			    };
                      
		   exception CannotProceed 
			    { NamingContext cxt; 
				  Name rest_of_name;
			    };
                      
		   exception InvalidName { };
                      
		   exception AlreadyBound { };
                      
		   exception NotEmpty { };
                      
		   void bind(in Name n, in Object obj) 
			    raises (Notfound, CannotProceed, InvalidName, AlreadyBound); 
                      
		   void rebind(in Name n, in Object obj) 
			    raises (Notfound, CannotProceed, InvalidName); 
                      
		   void unbind(in Name n) 
			    raises (Notfound, CannotProceed, InvalidName);
                      
		   void bind_context(in Name n, in NamingContext nc) 
			    raises (Notfound, CannotProceed, InvalidName, AlreadyBound);
                       
		   void rebind_context(in Name n, in NamingContext nc) 
			    raises (Notfound, CannotProceed, InvalidName);

                      
		   Object resolve (in Name n) 
			    raises (Notfound, CannotProceed, InvalidName);
                      
		   NamingContext new_contextO; 
                      
		   NamingContext bind_new_context(in Name n)
			    raises (Notfound, AlreadyBound, CannotProceed, InvalidName);
                      
		   void destroy() 
			    raises (NotEmpty);
                      
		   void list(in unsigned long how_many, out BindingList bl, 			
                   out BindingIterator bi);  
	     }

 

Utilisation d'un annuaire

Le serveur crée des fonctions ; il les ajoute à l'annuaire.

Le client doit connaître la désignation de l'objet ; il l'utilise pour résoudre la désignation et obtenir la référence.

L'annuaire est un objet de type NamingContext ; il se nomme NameService ; il fait l'objet d'une référence initiale.

 

Initialisation de l'ORB, du BOA...

Création d'un objet assurant le nouveau service :

    ImprimeImpl imp = new ImprimeImpl();
	  boa.connect(imp);
	  boa.obj_is_ready(imp);

 

Accès à l'annuaire :

    org.omg.CORBA.Object obj = null;
	  org.omg.CosNaming.NamingContext naming = null;
	  try
		 { obl = orb.resolve_initial_reference("NamingService ");
		   naming = org.omg.CosNaming.NamingContextHelper.narrow(obj);
		 }
    catch (org.omg.CORBA.ORBPackage.InvalidName e)
		 { System.out.println("nom erroné");
		   System.exit(0);
		 }
	  if (naming == null)
		 { System.out.println("service indisponible");
		   System.exit(0);
		 }

 

Création de la liaison :

    org.omg.CosNaming.NameComponent[] name = 
		            new org.omg.CosNaming.NameComponent[1];
	  name[0] = new org.omg.CosNaming.NameComponent();
	  name[0].id = "imprime";
	  name[0].kind = "";        

 

Ajout de la liaison :

    try
      { naming.bind(name, imp);
      }
    catch (org.omg.CosNaming.NameContextPackage.NotFound e)
          { System.out.println("objet introuvable");
		      System.exit(0);
		    }
	  catch (org.omg.CosNaming.NameContextPackage.AlreadyBound e)
          { System.out.println("objet déjà inscrit");
		      System.exit(0);
		    }
	  catch (org.omg.CosNaming.NameContextPackage.InvalidName e)
          { System.out.println("nom invalide");
		      System.exit(0);
		    }
	  catch (org.omg.CosNaming.NameContextPackage.CannotProceed e)
          { System.out.println("traitement impossible");
		      System.exit(0);
          }

Enfin, lancement de l'utilisation :

     try
		 { boa.impl_is_ready();
		 }
	   catch (java.lang.Exception e)
		     { e.printStackTrace();
           }

 

Initialisation de l'ORB (pas de BOA sur le client).

Accès à l'annuaire :

    org.omg.CORBA.Object obj = null;
	  org.omg.CosNaming.NamingContext naming = null;
    try
		{ obl = orb.resolve_initial_reference("NamingService ");
		  naming = org.omg.CosNaming.NamingContextHelper.narrow(obj);
		}
    catch (org.omg.CORBA.ORBPackage.InvalidName e)
      { System.out.println("nom erroné");
		  System.exit(0);
      }

 

Création de la liaison :

    org.omg.CosNaming.NameComponent [] 
             name = new org.omg.CosNaming.NameComponent [1];
	  name[0] = new org.omg.CosNaming.NameComponent();
	  name[0].id = "imprime";
	  name[0].kind = "";

try { obj = naming.resolve(name); } catch (org.omg.CosNaming.NameContextPackage.InvalidName e) { System.out.println("nom invalide"); System.exit(0); } catch (org.omg.CosNaming.NameContextPackage.NotFound e) { System.out.println("objet introuvable"); System.exit(0); } catch (org.omg.CosNaming.NameContextPackage.CannotProceed e) { System.out.println("traitement impossible"); System.exit(0); }

Création d'un objet d'accès au service :

    ImprimeImpl imp = ImprimeHelper.narrow(obj);

 

Accès au service

    try
		{ imp.print("texte à transmettre au serveur");
		}
	  catch (org.omg.CORBA.SystemException e)
		{ System.out.println(e.getMessage());
      }

 

Annuaire interopérable

L'annuaire interopérable ajoute une représentation standard sous forme de chaîne des chemins d'accès. Il étend l'annuaire :

interface NamingContextExt : NamingContext

Il définit les URL CORBA.

Méthodes

  to_string()     transforme un type Name en chaîne ;
  to_name()     transforme une chaîne en type Name ;
  resolve_str()     résoud directement une chaîne de caratères en référence
  to_url()     transforme une chaîne en URL CORBA

 

Constitution de la chaîne

id et kind d'un NameComponent sont séparés par un point, s'ils existent tous deux ;

les NameComponent sont séparés par des / ;

les caractères . et / dans les NameComponent sont codé \. et \/

Exemple a.b/c séquence de NameComponent <a, b> et <c>

 

Les URL CORBA

resolve() retourne un référence à un Object. La méthode object_to_string() la transforme en chaîne.

Il existe 6 formes d'URL : IOR, corbaloc, corbaname, ftp, http, file.

 

Le POA Portable Object Adaptor

Ajoute des spécifications par rapport au BOA pour le rendre portabe.

Spécifications

Servant entité qui implante une interface IDL ;
Serveur entité qui loge un ensemble de servants ;
Référence d'objet une interface IDL vue par le client ;
Client entité qui invoque des services auprès de servants.

Tout comme le BOA, le POA est situé du côté serveur. Donc la nouvelle définition ne change rien au client.

Le servant est une abstraction de l'objet tel que nous l'avons utilisé : à un même servant peuvent être associés plusieurs références d'objets. Les divers objets pourront avoir des comportements différents.

 

Arborescence de POA

Le comportement du POA est régi par un ensemble de règles. On peut donc créer plusieurs POA, avec des règles distinctes. Chaque POA gère un ensemble de servants, en fonction des règles édictées. Il existe un POA standard, nommé RootPOA.

Il est possible de créer de nouveaux POA, et de les ranger en arbre. Chacun aura ses propres paramètres.

 

Mécanismes dynamiques

Les mécanismes de référencement que nous venons de voir sont statiques. La suite traite du mécanisme de référencement dynamique.

Pour reconstruire des références vers les bons objets, côté client, on a utilisé les classes Helper. Le mécanisme d'invocation dynamique ne les utilise pas.

Pour pouvoir opérer la liaison, on utilisera le type IDL Any, qui est compatible avec tout type. C'est un type de métadonnée auto-descriptif : il comprend la valeur et sa description.

Le serveur gère, en plus des objets, une base de données comportant les descriptions de toutes les interfaces IDL des objets disponibles sur le bus CORBA : le référentiel d'interface (IFR Interface repository). L'implantation de cette BD est libre ; elle peut être relationelle ou autre.

L'interface d'invocation dynamique (DII, Dynamic Invocation Interface) permet à un client d'invoquer les opérations sur les objets sans passer par les souches IDL prédéfinies. Il découvre la structure de l'interface d'objet à l'exécution. Il est interrogeable par le client pour retrouver la définition des types.

C'est une souche générique.

Réalisation d'un Object Browser.

L'interface de squelette dynamique (DSI, Dynamic Skeleton Interface).

C'est un squelette générique. Il remplace et étend les squelettes statiques. Il a été défini au départ pour réaliser des passerelles avec des ORB propriétaires.

 

Application

A partir de cet ensemble, il est possible au client de construire une requête, sans utiliser le mécanisme statique précédent.

Construction d'une application générique

Consiste à réaliser un programme capable d'invoquer n'importe quelle opération de n'importe quel objet.

La recherche de la référence se fait par string_to_object() ou par le NamingService ; L'accès à l'interface par get_interface() appliquée à la référence précédente ; il fourni des métadonnées de description de l'objet.

L'accès à la description de l'opération se fait :

La signature obtenue permet de construire la liste des arguments : create_list, puis add, add_item et add_value. Création de la requête par _create_request, en donnant le nom de l'opération et la liste des arguments. Invocation de la requête en synchrone, différée ou asynchrone.

 

Avantages de CORBA

Précautions

Comme pour Java : il faut éviter à tout prix les extensions alléchantes proposées par les fournisseurs d'ORB, car elles ne seront pas portables !

Faut-il choisir Corba, ou préférer autre chose ?

Il n'y a pas de règle générale pour le choix d'un environnement réparti : ce sont vos besoins immédiats ou futurs qui guideront votre choix.

Toutefois, le principe d'économie nous incite à choisir l'environnement le plus facile à utiliser. Or Corba est assez complexe. Alors, une bonne règle pourrait être :

CORBA est un outil extrêmement puissant, qui comporte les défauts inhérents à sa puissance. Sachez l'utiliser comme un langage d'assemblage : si vous ne pouvez pas faire autrement, profitez à fond de sa puissance.

La base industrielle de Corba est aussi un argument important en sa faveur : si vous achetez des composants Corba, vous n'êtes pas lié à un éditeur particulier ; vous aurez toujours la possibilité d'acheter le produit équivalent d'iun concurent, ce qui n'est pas négligeable...