loading gif

CORBA en Java

Auteur : Gokan EKINCI
Date de 1ère publication : 2015-01-12
Date de mise à jour : 2016-01-23
Licence : CC BY-NC-SA

Sommaire :
Avant de démarrer ce tutoriel…
Introduction
Définitions
Informations complémentaires
Qu’est-ce que l’IDL ?
Les types IDL/Java
Les modules
Les interfaces, les prototypes de méthodes, les constantes, et l’héritage
Les structures
Les values type
Les alias et les tableaux
Les énumérations
Les exceptions
Les opérations asynchrones
TP CORBA Chapitre 1 : Génération du fichier IDL
TP CORBA Chapitre 2 : Schéma UML
TP CORBA Chapitre 3 : Le modèle POA (partie serveur seulement)
TP CORBA Chapitre 4 : Implémentation (partie serveur)
TP CORBA Chapitre 5 : Implémentation (partie cliente)
TP CORBA Chapitre 6 : Bonus
Les erreurs récurrentes
Pour ou contre CORBA ?



Avant de démarrer ce tutoriel…

Avant de démarrer ce tutoriel :



Introduction

CORBA, acronyme de Common Object Request Broker Architecture, est une architecture logicielle, pour le développement de composants et d’Object Request Broker ou ORB. Ces composants, qui sont assemblés afin de construire des applications complètes, peuvent être écrits dans des langages de programmation distincts, être exécutés dans des processus séparés, voire être déployés sur des machines distinctes (source : Wikipédia).

Pour qu’il y ait une communication entre un client et un serveur CORBA, il faut que le client et le serveur possède chacun 1 ORB : une application client-serveur possède au minimum 2 ORB.

Un ORB est un ensemble de fonctions (classes Java, bibliothèques C++...) qui implémentent un « bus logiciel » par lequel des objets envoient et reçoivent des requêtes et des réponses, de manière transparente et portable : il s'agit de l'activation ou de l'invocation à distance par un objet, d'une méthode d'un autre objet distribué, en pratique les objets invoqués sont souvent des services (source : Wikipédia).

Etudions le schéma suivant :

Schema Corba

Les étapes :

  1. Le client CORBA invoque une méthode distante.
  2. Le Stub du client CORBA marshalise l’invocation de la méthode distante en requête CORBA.
  3. La requête CORBA est envoyée à travers le réseau à partir l’ORB du client.
  4. La requête CORBA est réceptionnée par l’ORB du serveur.
  5. Le Skeleton du serveur CORBA démarshalise la requête CORBA.
  6. Le serveur CORBA exécute le service lié à la méthode invoquée.
  7. Le Skeleton du serveur CORBA marshalise la réponse CORBA.
  8. La réponse CORBA est envoyée à travers le réseau à partir de l’ORB du serveur.
  9. La réponse CORBA est réceptionnée par l’ORB du client.
  10. Le Stub du client CORBA démarshalise la réponse CORBA.

Ce que l’on peut remarquer dans ce schéma, c’est qu’il ressemble plus ou moins à celui du protocole HTTP (avec un navigateur qui joue le rôle de client et un serveur web)... donc si on fait l’analogie entre HTTP et CORBA, un client HTTP connaît le serveur grâce à une URL mais comment un client CORBA connaît le serveur CORBA dans ce cas ? La réponse est l’"IOR" (voir sa définition dans la partie suivante)... pour faire court l’IOR est un objet contenant plusieurs informations permettant d’identifier un "servant" (la méthode distante). Comment obtenir cet IOR ? Il existe 2 moyens standards :

  1. Le serveur stocke l’IOR dans un fichier et le client trouve un moyen de récupérer ce fichier pour lire son contenu. Il est possible de générer un objet proxy à partir de l’IOR grâce à une opération appelée "narrowing".
  2. Le serveur possède un service appelé "NameService", c’est un programme qui tourne en daemon sur le serveur, il est plus connu sous le nom de "orbd" ou "tnameserv". En utilisant le host & port du serveur et un nom attribué au service le client peut récupèrer l’IOR et génère un objet proxy.

Dans notre cas nous utiliserons la deuxième solution car c’est la plus propre.



Définitions

Glossaire Oracle pour CORBA (version détaillée ici) :



Informations complémentaires

Ils existent de nombreuses implémentations de CORBA, ces implémentations sont interopérables :

Vous devez bien savoir faire les différences entre :



Qu’est-ce que l’IDL ?

L’IDL (Interface Definition Language) est un langage dont la syntaxe est proche de celle du C++, le principe d’un fichier IDL est le même qu’un fichier XSD (XML Schema Definition) ou WSDL : générer du code (Java, C++, Python etc).

Le fichier IDL représente un contrat entre le serveur et ses clients : le serveur et ses clients doivent utiliser le même fichier de contrat pour générer leur code.

L’IDL est indépendant du langage d'implémentation utilisé coté client ou serveur. On peut avoir un client en C++ et un serveur en Java par exemple.

Quelques limitations :

Exemple d'IDL :

IDL Extrait du code Java généré à partir de l’IDL
module fr {
  module ekinci {
    interface CalculationService {
      long factorial(in long num);
    };
  };
};
package fr.ekinci;

public interface CalculationServiceOperations {
  int factorial(int num);
}
package fr.ekinci;

public interface CalculationService
  extends CalculationServiceOperations,
  org.omg.CORBA.Object,
  org.omg.CORBA.portable.IDLEntity
{ }



Les types IDL / Java

Les types :

IDL Java (mode `in`) Java (mode `out` ou `inout`)
void (type de retour) void void
boolean boolean org.omg.CORBA.BooleanHolder
char char org.omg.CORBA.CharHolder
wchar char org.omg.CORBA.CharHolder
octet byte org.omg.CORBA.ByteHolder
short short org.omg.CORBA.ShortHolder
unsigned short short org.omg.CORBA.ShortHolder
long int org.omg.CORBA.IntHolder
unsigned long int org.omg.CORBA.IntHolder
long long long org.omg.CORBA.LongHolder
unsigned long long long org.omg.CORBA.LongHolder
float float org.omg.CORBA.FloatHolder
double double org.omg.CORBA.DoubleHolder
long double Incompatibilité en Java Incompatibilité en Java
string java.lang.String org.omg.CORBA.StringHolder
wstring java.lang.String org.omg.CORBA.StringHolder
Object (différent de java.lang.Object ! Sert à représenter les IOR). org.omg.CORBA.Object org.omg.CORBA.ObjectHolder

Exemple de prototype de méthode IDL :

void meth1();
long meth2(in long param1);
string meth3(in long param1, out octet param2, inout string param3);

Les mots-clés in, out et inout sont obligatoires pour les paramètres de méthode :



Les modules

L’équivalent du package Java est module en CORBA :

IDL Extrait du code Java généré à partir de l’IDL
module fr {
    module ekinci {
        // définir une interface
    };
};
package fr.ekinci;

// interface Java implémentant `IDLEntity`

A savoir :



Les interfaces, les prototypes de méthodes, les constantes, et l’héritage

IDL Extrait du code Java généré à partir de l’IDL
module fr {
  module ekinci {

    interface Parent{

      // Prototypes de méthode
      void meth1();
      long meth2(in long param1);
      string meth3(
        in long param1, 
        out octet param2,
        inout string param3
      );

      // Constantes 
      const long MAX = 10000;
      const float FACTOR = (10.0 - 6.5) * 3.91;  
    };

    // Héritage
    interface Child : Parent{};

  };
};
package fr.ekinci;

public interface ParentOperations 
{
  void meth1 ();
  int meth2 (int param1);
  String meth3 (
    int param1,
    org.omg.CORBA.ByteHolder param2, 
    org.omg.CORBA.StringHolder param3
  );
}
package fr.ekinci;

public interface Parent 
    extends ParentOperations, 
    org.omg.CORBA.Object,
    org.omg.CORBA.portable.IDLEntity 
{  
  public static final int MAX = (int)(10000);
  public static final float FACTOR = 
   (float)((double)((double)(10.0 - 6.5) * 3.91));
}
public interface ChildOperations  
    extends fr.ekinci.ParentOperations
{  }
package fr.ekinci;

public interface Child
    extends ChildOperations,
    fr.ekinci.Parent,
    org.omg.CORBA.portable.IDLEntity
{  }

A savoir :

La commande idlj.exe -fall myfile.idl va générer 12 fichiers :

Note : L’interface ChildOperations hérite de ParentOperations, ce sont ces interfaces qui contiennent les prototypes de méthode en Java.



Les structures

Les structures et valuetypes permettent de réaliser des types complexes.

IDL Extrait du code Java généré à partir de l’IDL
module fr {
  module ekinci {
    struct Classe {
      short a;
      long b;
      double c;
      string d;
    };
  };
};
package fr.ekinci;

public final class Classe 
    implements org.omg.CORBA.portable.IDLEntity {
  public short a = (short)0;
  public int b = (int)0;
  public double c = (double)0;
  public String d = null;

  public Classe (){ } // ctor

  public Classe (short _a, int _b, double _c, String _d) {
    a = _a;
    b = _b;
    c = _c;
    d = _d;
  } // ctor

} // class Classe

A savoir :



Les values type

IDL Extrait du code Java généré à partir de l’IDL
module fr {
  module ekinci {
    valuetype ParentValue {
      public long a;
      string getToto(in long param1);
    };

    valuetype ChildValue : ParentValue {
      private double b;
    };
  };
};
package fr.ekinci;

public abstract class ParentValue 
implements org.omg.CORBA.portable.StreamableValue {
  public int a = (int)0;

  public abstract String getToto (int param1);

  // Voir fichier Java pour le reste
}
package fr.ekinci;

public abstract class ChildValue 
extends fr.ekinci.ParentValue {
    protected double b = (double)0;

    // Voir fichier Java pour le reste
}

A savoir :

La commande idlj.exe -fall myfile.idl va générer 8 fichiers :



Les alias et les tableaux

Pour créer un attribut de type tableau dans une struct IDL :

IDL Extrait du code Java généré à partir de l’IDL
long tab[10];
public int tab[] = null;

/!\ : La taille 10 est gérée dans la classe xxxHelper

Pour créer un attribut de type tableau sans une limite prédéfini dans une struct IDL il faut utiliser le mot clé sequence :

IDL Extrait du code Java généré à partir de l’IDL
sequence<long> integerList;
public int integerList[] = null;

Pour utiliser un tableau ou sequence en tant que paramètre dans un prototype de méthode il faut obligatoirement passer par un alias grâce au mot-clé typedef :

IDL Extrait du code Java généré à partir de l’IDL
typedef long TabInt[10][10];
TabInt method(in TabInt param);
int[][] method(int[][] param);
typedef sequence<long> TabInt;
TabInt method(in TabInt param);
int[] method(int[] param);
typedef sequence<long> TabInt;
TabInt method(inout TabInt param);
int[] method(fr.ekinci.TabIntHolder param);

/!\ Attention : Il est possible de placer l’instruction typedef à l’extérieur ou à l’intérieur de l’interface contenant le prototype de méthode avec le paramètre de type tableau ou sequence, cependant cette instruction doit être déclarée avant l’utilisation de l’alias.



Les enumerations

IDL Extrait du code Java généré à partir de l’IDL
module fr {
  module ekinci {
    enum Toto {
      UN, DEUX, TROIS
    };
  };
};
package fr.ekinci;

public class Toto implements org.omg.CORBA.portable.IDLEntity
{
  private int __value;
  private static int __size = 3;
  private static fr.ekinci.Toto[] __array = 
    new fr.ekinci.Toto [__size];

  public static final int _UN = 0;
  public static final fr.ekinci.Toto UN = 
    new fr.ekinci.Toto(_UN);
  public static final int _DEUX = 1;
  public static final fr.ekinci.Toto DEUX = 
    new fr.ekinci.Toto(_DEUX);
  public static final int _TROIS = 2;
  public static final fr.ekinci.Toto TROIS = 
    new fr.ekinci.Toto(_TROIS);

  public int value ()
  {
    return __value;
  }

  public static fr.ekinci.Toto from_int (int value)
  {
    if (value >= 0 && value < __size)
      return __array[value];
    else
      throw new org.omg.CORBA.BAD_PARAM ();
  }

  protected Toto (int value)
  {
    __value = value;
    __array[__value] = this;
  }
} // class Toto

La commande idlj.exe -fall myfile.idl va générer 3 fichiers :



Les exceptions

IDL Extrait du code Java généré à partir de l’IDL
module fr {
  module ekinci {

    // Création de l’exception
    exception TransactionException{};


    // raises IDL = throws Java
    interface DistantTransaction {
      void prepare() 
        raises (TransactionException);

      void commit() 
        raises(TransactionException);

      void rollback() 
        raises(TransactionException);
    };

  };
};
package fr.ekinci;

public interface DistantTransaction 
    extends DistantTransactionOperations,
    org.omg.CORBA.Object,
    org.omg.CORBA.portable.IDLEntity 
{  } // interface DistantTransaction
package fr.ekinci;

public final class TransactionException 
    extends org.omg.CORBA.UserException {

  public TransactionException () {
    super(TransactionExceptionHelper.id());
  } // ctor

  public TransactionException (String $reason) {
    super(TransactionExceptionHelper.id() + "  " + $reason);
  } // ctor

} // class TransactionException

A savoir :

La commande idlj -fall myfile.idl va générer 9 fichiers :



Les opérations asynchrones

Les méthodes CORBA sont exécutées de manière synchrone (comme pour les méthodes RMI en Java), cela signifie que si le serveur prend 2 secondes pour exécuter une méthode, le client devra attendre ces 2 secondes + le temps réseau que peut prendre l’échange de requête entre le client et le serveur. Cependant il existe un mot-clé oneway permettant de rendre une méthode asynchrone : le client n’attend pas que le serveur ait terminé d’exécuter la méthode oneway et passe directement à l’instruction qui suit.

Pour mettre en place une méthode asynchrone il faut respecter quelques contraintes :

Exemple :

module fr {
  module ekinci {
    interface Asynchronous {

      oneway void farewell();

    };
  };
};

Liste des fichiers générés suite à l’exécution de idlj -fall myfile.idl : _AsynchronousStub.java, Asynchronous.java, AsynchronousHelper.java, AsynchronousHolder.java, AsynchronousOperations.java, AsynchronousPOA.java

/!\ Attention : Si vous êtes familier avec les environnements multithread et avez connaissance des problèmes de concurrences, vous savez que les méthodes asynchrones sont à manipuler avec prudence.



TP CORBA Chapitre 1 : Génération du fichier IDL

Pour générer des classes Java à partir du fichier IDL, nous allons utiliser le programme "idlj" présent dans le JDK, celui-ci porte l’extension ".exe" sous Windows. Ce programme se trouve dans le répertoire d’installation du JDK "jdkxxx/bin/".

Nous allons utiliser la commande suivante : idlj -fall <Insérer nom fichier idl> exemple : idlj -fall calcul.idl
Avec le paramètre -fall on va générer plusieurs fichiers de code Java pour la partie client et serveur.
Le nombre de fichier généré dépendra des éléments utilisés dans le fichier idl. Par exemple pour chaque énumération (enum) ajoutée dans le fichier idl, idlj va générer 3 fichiers Java supplémentaires.

Autrement :

Créez un fichier IDL avec le contenu suivant :

IDL Extrait du code Java généré à partir de l’IDL
module fr {
  module ekinci {
    interface CalculationService {
      long factorial(in long num); 
    }; 
  };
};
package fr.ekinci;

public interface CalculationService
    extends CalculationServiceOperations, 
    org.omg.CORBA.Object, 
    org.omg.CORBA.portable.IDLEntity 
{  } // interface CalculationService

6 fichiers seront générés si on exécute la commande suivante idlj -fall calcul.idl :

Fichier Description
CalculationServicePOA.java Ce fichier contient une classe abstraite représentant le skeleton du serveur
_CalculationServiceStub.java Ce fichier contient une classe représentant le stub du client
CalculationService.java Ce fichier contient notre interface Java `CalculationService`. L’interface `CalculationService` hérite de `CalculationServiceOperations`, `org.omg.CORBA.Object` et `org.omg.CORBA.portable.IDLEntity`
CalculationServiceHelper.java Ce fichier contient une classe abstraite permettant de faire du narrowing et de gérer le type `org.omg.CORBA.Any`
CalculationServiceHolder.java Ce fichier contient une classe final permettant de gérer les paramètres de méthode en mode `out` ou `inout`
CalculationServiceOperations.java Cette interface contient les prototypes de méthode de `CalculationService`, dans notre cas il contient juste la méthode `factorial()`



TP CORBA Chapitre 2 : Schéma UML

Voici un schéma UML pour avoir une meilleure vision sur la hiérarchie des classes générées :
UML CORBA

A savoir :



TP CORBA Chapitre 3 : Le modèle POA (partie serveur seulement)

Il existe 2 variantes d’implémentation pour le modèle POA pour la partie serveur :

La différence entre ces 2 modèles ?
Si on choisit le Inheritance Model, la classe d’implémentation doit obligatoirement hériter de la classe CalculationServicePOA, tandis que si on choisit le Tie Delegation Model, la classe d’implémentation doit juste implémenter l’interface CalculationServiceOperations. Comme il n’y pas d’héritage multiple Java, le Tie Delegation Model sert à libérer un emplacement pour que la classe d’implémentation puisse hériter d’une autre classe, cependant ce modèle possède aussi un inconvénient : une méthode supplémentaire est appelé à chaque appel de méthode distante.

Citation Oracle (source) :

You might want to use the Tie model instead of the typical Inheritance model if your implementation must inherit from some other implementation. Java allows any number of interface inheritance, but there is only one slot for class inheritance. If you use the inheritance model, that slot is used up. By using the Tie Model, that slot is freed up for your own use. The drawback is that it introduces a level of indirection: one extra method call occurs when invoking a method.

/!\ : L’héritage multiple n’a jamais été une solution incontournable, il est tout à fait possible d’utiliser un design pattern de comportement avec la composition pour s’en passer.

La différence entre ces 2 modèles de POA au niveau de l’implémentation du serveur est relativement faible, et se résume à 2 lignes de codes chacune :

The Inheritance Model The Tie Delegation Model
org.omg.CORBA.Object ref = rootpoa.servant_to_reference(calculImpl);

CalculationService href = CalculationServiceHelper.narrow(ref);
CalculationServicePOATie tie = new CalculationServicePOATie(calculImpl, rootpoa);

CalculationService href = tie._this(orb);

J'utiliserais le Inheritance Model dans le prochain chapitre.

Activation implicite du servant :

Les classes utiles pour :

Le client (option -fclient) Le serveur (option -fserver)
CalculationService, CalculationServiceOperations, CalculationServiceHelper, CalculationServiceHolder, _CalculationServiceStub CalculationService, CalculationServiceOperations, CalculationServicePOA

/!\ Attention : Si on utilise le Inheritance Model le serveur nécessite l’utilisation de la classe CalculationServiceHelper, si on utilise le Tie Delegation Model le serveur nécessite l’utilisation de la classe CalculationServiceHelper et CalculationServicePOATie, or la commande idlj -fserver calcul.idl ne génère ni CalculationServiceHelper ni CalculationServicePOATie, il faudra au choix selon le modèle choisit :

/!\ : Il existe un autre modèle appelé BOA, qui est l’ancêtre du modèle POA pour implémenter le code du serveur CORBA, bien que dépréciée, un tutoriel sur ce modèle appelé "ImplBase" est toujours disponible pour ceux qui utilisent Java 1.3 (lien).



TP CORBA Chapitre 4 : Implémentation (partie serveur)

I. Soit l’IDL créée dans le chapitre 1 du TP :

module fr {
  module ekinci {
    interface CalculationService {
      long factorial(in long n);
    };
  };
};


II. Générer les fichiers nécessaires avec le programme idlj

Pour utiliser le Inheritance Model, exécuter la commande suivante idlj -fall calcul.idl.
Pour utiliser le Tie Delegation Model, exécuter la commande suivante idlj -fall calcul.idl, puis la commande suivante idlj -fallTIE calcul.idl.

La raison pour laquelle il faut exécuter la 2 commandes pour le Tie Delegation Model (source) :

Because it is not possible to generate ties and skeletons at the same time, they must be generated separately.

/!\ : Si vous ne souhaitez pas utiliser le Tie Delegation Model, la seconde commande n’est bien évidemment pas nécessaire.


III. Créer la classe d’implémentation CalculationServiceImpl.java

Le fichier xxxPOA.java (ou xxxPOATie.java pour une implémentation selon le pattern Tie) représente le skeleton du serveur.

Nous allons créer la classe d'implémentation CalculationServiceImpl qui hérite de CalculationServicePOA et implémente toutes les méthodes définies dans l'interface CalculationServiceOperations :

import fr.ekinci.CalculationServicePOA;

public class CalculationServiceImpl extends CalculationServicePOA {

  @Override
  public int factorial(int n){
    if (n < 2) {
      return 1;
    }

    int c = n;
    while(c != 1){
      n *= --c;
    }

    return n; 
  }
}

/!\ Attention : Vous remarquerez que pour faire simple notre implémentation de factorielle ne gère pas les cas où n est inférieur à zéro et retourne 1.


IV. Créer une classe MainServer pour l’initialisation du serveur

import org.omg.CORBA.*;
import org.omg.PortableServer.*;
import org.omg.CosNaming.*;
import fr.ekinci.*;


/**
 * Classe pour lancer le programme du serveur
 * @author Gokan EKINCI
 */
public class MainServer {
    public static void main(String args[]){
        try {
            // Initialisation de l'ORB
            ORB orb = ORB.init(args, null);

            // Récupérer la référence du RootPOA et activer le POAManager
            POA rootpoa = POAHelper.narrow(orb.resolve_initial_references("RootPOA"));
            rootpoa.the_POAManager().activate();

            // Créer un servant (instance de classe d'implémentation) et l'enregistrer avec l'ORB
            CalculationServiceImpl calculImpl = new CalculationServiceImpl();


            /* *** DEBUT INHERITANCE MODEL (vous pouvez vous référer aux chapitres précédents pour utiliser le modèle Tie Delegation Model) *** */

            // Récupérer une référence du servant
            org.omg.CORBA.Object servantRef = rootpoa.servant_to_reference(calculImpl);

            CalculationService service = CalculationServiceHelper.narrow(servantRef);

            /* *** FIN INHERITANCE MODEL *** */

            // Récupérer la référence du service de nommage
            org.omg.CORBA.Object nsRef = orb.resolve_initial_references("NameService");
            NamingContextExt nce = NamingContextExtHelper.narrow(nsRef);

            // Créer un nom pour le service et ajouter le service
            String serviceName = "MathServices";
            NameComponent nc[] = nce.to_name(serviceName);
            nce.rebind(nc, service);

            // Démarrer le service et attendre les requêtes des clients
            System.out.println("On traite les requêtes des clients ...");
            orb.run(); // En attente de nouveaux clients CORBA

        } catch (Exception e){
            System.err.println(e);
        }
    }
}



V. Démarrer le serveur : Lancer orbd (processus daemon)

Ce programme se trouve dans le même répertoire que le programme idlj, soit "/jdkxxx/bin".

Commande pour exécuter ordb :

OS Ligne de commande
Linux orbd -ORBInitialPort 1050 -ORBInitialHost localhost&
Windows start orbd -ORBInitialPort 1050 -ORBInitialHost localhost

A savoir :

Aperçu sous Windows :

Launch ORBD

Un message d’avertissement Windows peut alors se lancer, autoriser l’accès :

Windows pop-up

Une nouvelle console se lance automatiquement :

ORBD is running

Rappel : Pour quitter ordb faire Ctrl + C (si vous quittez les clients ne pourront plus accéder au service).



VI. Démarrer le programme serveur MainServer

Démarrer le programme du serveur avec la commande suivante s’il s’agit d’un Runnable JAR :
start java -jar MainServer.jar -ORBInitialPort 1050 -ORBInitialHost localhost

Si vous lancez le programme MainServer à partir de l’IDE Eclipse, allez dans Run Configurations… > Onglet Arguments > Program Arguments et copier/coller ceci : -ORBInitialPort 1050 -ORBInitialHost localhost.



TP CORBA Chapitre 5 : Implémentation (partie cliente)

Créer une classe MainClient pour l’initialisation du client :

import org.omg.CORBA.*;
import org.omg.CosNaming.*;
import fr.ekinci.*;


/**
 * Classe pour lancer le programme du client
 * @author Gokan EKINCI
 */
public class MainClient {
    public static void main(String args[]){
        try {
            // Initialisation de l'ORB
            ORB orb = ORB.init(args, null);

            // Récupérer la référence du service de nommage
            org.omg.CORBA.Object nsRef = orb.resolve_initial_references("NameService");
            NamingContextExt nce = NamingContextExtHelper.narrow(nsRef);

            // Générer un objet proxy
            String serviceName = "MathServices";
            CalculationService service = CalculationServiceHelper.narrow(nce.resolve_str(serviceName));

            System.out.println("Réponse du serveur : " + service.factorial(5)); // 120
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Lancez le programme client à partir de votre IDE, OU BIEN compilez le programme avec javac et lancer le programme java, OU BIEN utilisez la commande suivante si vous avez transformé votre projet en JAR :
java -jar MainClient.jar -ORBInitialPort 1050 -ORBInitialHost localhost

Rappel : Il est nécessaire de lancer le programme Serveur (MainServer) avant le programme Client (MainClient).



TP CORBA Chapitre 6 : Bonus

Dans les chapitres précédents nous avons vu à quel point le code d’une implémentation CORBA peut-être long à écrire... et ce procédé fastidieux sera toujours aussi répétitif. Si la mise en place du code n’était pas aussi difficile CORBA n’aurait pas perdu sa popularité, qui sait ?

Dans cette partie nous allons réaliser le même exercice avec seulement quelques instructions.

Téléchargez le projet corba-wrapper à partir de ce lien GitHub. Vous pouvez utiliser une commande Git ou tout simplement faire un copier/coller des classes CORBAServer et CORBAClient dans votre projet.

UML diagram CORBA Sever

Créez un projet serveur et utilisez la classe CORBAServer :

import fr.ekinci.*;
import fr.ekinci.corbawrapper.CORBAServer;

public class MainServer {
    public static void main(String args[]) throws Exception {
        CORBAServer server = new CORBAServer("127.0.0.1", 1050); 

        // Ajouter un service       
        server.addService("MathServices", new CalculationServiceImpl(), CalculationServiceHelper.class); 

        // Démarrer le service
        server.run();                                                          
    }
}

/!\ : CalculationServiceImpl hérite de CalculationServicePOA (cf : utilisez le Inheritance Model, PAS le Tie Delegation Model).

UML diagram CORBA Client

Créez un projet client et utilisez la classe CORBAClient :

import fr.ekinci.*;
import fr.ekinci.corbawrapper.CORBAClient;

public class MainClient {
    public static void main(String args[]) throws Exception {
        CORBAClient client = new CORBAClient("127.0.0.1", 1050);

        // Récupérer l'objet proxy    
        CalculationService calcul = client.<CalculationService, CalculationServiceHelper>lookup("MathServices", CalculationServiceHelper.class);

        // Invoquer des méthodes distantes 
        System.out.println("Réponse du serveur : " + calcul.factorial(5)); // 120
    }
}

Lancer orbd en premier, puis le projet du serveur, puis le projet du client, le résultat obtenu sera le même que dans les chapitres précédents. Simple à mettre en place, n’est-ce pas ?



Les erreurs récurrentes

Il y a fort à parier que le programme client/serveur CORBA que nous avons mis en place fonctionne en localhost sans aucun problème, mais ce ne sera pas forcément le cas entre 2 machines distinctes :

WARNING: "IOP00410201: (COMMFAILURE) Connection failure: socketType:
IIOP
CLEAR_TEXT; hostname: 127.0.0.1;

... il suffit d’aller sur la machine du serveur et de remplacer l’adresse du localhost par l’adresse IP réelle de la machine dans le fichier /etc/hosts, enfin redémarrez votre machine pour que les modifications soient prises en compte. Notez que ce type de problème est aussi rencontré par ceux qui font du RMI sous Linux, la solution au problème est identique.



Conclusion : pour ou contre CORBA ?

Listons quelques avantages et inconvénients dans l’utilisation de CORBA...

Avantages Inconvénients
CORBA utilise un protocole binaire (GIOP/IIOP), de ce fait c’est un système plus performant (léger et rapide) que les protocoles basé sur les textes (ex : les web services, cf benchmark lire §3.4). A l’heure où j’écris ce tutoriel, la dernière spécification de la norme CORBA est 3.3 (novembre 2012), mais la plupart des dernières implémentations dans les langages informatiques reposent sur la norme 2.3.1 (octobre 1999). A l’époque CORBA était considéré comme une bonne alternative au DCOM de Microsoft, mais depuis d’autres technologies comme les services Web ont supplantés CORBA.
Contrairement à RMI, CORBA a été conçu pour être une solution hétérogène, il est possible d’avoir un serveur en Java et un client en C++ par exemple. Contrairement aux services Web qui utilisent le protocole HTTP adapté au WAN (Wide Area Network), CORBA peut être bloqué par les pare-feu à cause du numéro de port qui change dynamiquement par le serveur CORBA. On limitera l’utilisation de CORBA au LAN.
La spécification 1.0 de CORBA date d’août 1991, c’est une norme mature. La volonté de l’OMG est de rendre CORBA hétérogène, cependant le fichier IDL peut contenir des incompatibilités avec les langages d'implémentations (Java, C++ etc). Un même IDL générant du C++ peut rencontrer des problèmes pour générer du Java par exemple (ex : impossible de retranscrire le type `long double` IDL en Java).
Ecrire le code d’une implémentation client/serveur CORBA est très long, très répétitif, très "old-school", mais nous avons vu qu’il suffit d’un simple wrapper comme le projet corba-wrapper pour outrepasser ces difficultés.

Aujourd’hui il existe un autre projet open source concurrent de CORBA : gRPC. Ce dernier est un protocole créé par Google, interopérable, possède une implémentation officielle pour de nombreux langages (C++, Java, Python, C# etc), protocole binaire (HTTP/2)... son défaut ? Qu’il ne soit pas assez connu pour le moment.
Aimeriez-vous avoir un tutoriel gRPC ? N’hésitez pas à me le dire dans les commentaires.