Component with remote access (through CORBA)

Principle

The interfaces necessary to “see” an internal object like a remote component are based on the definition of a “contract” that specifies the services proposed by a component and the mode of accessing these services (input and output parameters, exceptions returned).

These declarations are contained in an IDL file that is used as a reference to define the interface code at the component end and at the clients end of the component. Examples are given later. The designer of the component starts from the IDL file and develops a server end interface to set up communication between the network (through CORBA) and the internal object. The designer of each client starts from the IDL file and develops an interface to set up communication between the network (through CORBA) and the component user code.

_images/accesCorbaIDL.png

Access from CORBA

IDL file

The first step is for the component developer to define a list of proposed services and the method of calling them, in a file called the IDL file. This file contains data structures declarations and classes (“interface” in the CORBA terminology) in a language defined by the CORBA (IDL) standard. OMG IDL Syntax and Semantics [IDL] is a reference document on the IDL language syntax.

Note
This language is similar to a sub-set of the C++ and java languages. CORBA defined its own interface language to achieve transparency relative to client and server implementation languages. A client written with a programming language can connect through CORBA to a server implemented in another language. The only condition is that there are two CORBA ORBs (or systems) interfaced with languages used in the server and the client. There are ORBs capable of managing many languages (C, C++, java, python, etc.). A single ORB (omniORB) was chosen within SALOME, making it possible to use clients and servers written in C++ or in python indifferently.

Example 6 (Part 1)

An IDL file is written to access objects (C++) in the alglin class from CORBA (see the alglin.hxx file in Example 1 and other examples). The corresponding IDL file may for example contain:

alglin.idl

module Distant {

  typedef sequence<double> vecteur;
  
  interface AlgLin 
  {
    void    addvec(out vecteur C, in vecteur A, in vecteur B);
    double  prdscl(in vecteur A, in vecteur B);
  };
  
};

Comments

  1. The IDL file begins with the module Distant { line. Whenever possible, it is recommended that interface declarations should be contained within a module.
  2. The vecteur class of the C++ version does not have to be declared in IDL. The IDL language has a base type (sequence <double>) that can manage a simple vector.

Example 7 (Part 1)

An IDL file is constructed to access (python) objects in the FreeFem class from CORBA (see FreeFem.py, Example 4 and subsequent examples). The corresponding IDL file may for example contain:

FreeFem.idl

module Solveur {

  struct Bord {
    string X;
    string Y;
    long   n;
  };
  typedef sequence<Bord> seqBords;

  interface FreeFem {
    
    void Bords(in seqBords B);
    void Flux(in string u1, in string u2);

    void Convection(in string cond_init, in double dt, in long n);

  };

};

Server end interface

A server end interface has to be developed from the IDL file. The principle consists of defining an object communicating firstly with the internal object and secondly with the CORBA communication layer. It will be written in a manner that will depend on the implementation language of the internal object (C++ or python).

C++ server interface

_images/objCorbaCpp.png

CORBA C++ interface generation

When the internal object is written in C++ (or it has a C++ higher layer), a C++ implementation class will have to be defined at the server end that

  • is derived from the POA_<module name>::<idl class name> class (class generated by the system starting from the IDL file)
  • is derived from the PortableServer::ServantBase class (class provided by the system, that provides a reference counter to the implementation class)
  • defines methods corresponding to the methods and attributes of the IDL class
  • has a pointer to the internal object as an attribute.
Note
The final point above can be replaced by:
is derived from the class of the internal object

The first version is recommended in preference, because it is easier to implement.

Important: Most CORBA implementations can generate skeletons of implementation classes. It is strongly recommended that this feature should be used to facilitate writing implementation classes.

In the case of omniORB:

omniidl -bcxx -Wbexample <nom>.idl

(note the –Wbexample option) generates a <name>_i.cc file that contains the implementation class and an example of the main program at the server end. This file then has to be “cleaned” (keeping the method call prototypes) and completed. Methods for implementation classes receive and return CORBA objects. Therefore, if necessary, each method must:

  • convert CORBA objects in input into C++ objects (or into simple types)
  • call the method(s) of the internal object
  • convert the C++ objects resulting from the methods of the internal object into CORBA objects

Example 6 (continued)

Consider an example consisting of a server end implementation class that calls objects in the alglin class from CORBA:

alglin_i.hxx

#ifndef ALGLIN_I_
#define ALGLIN_I_

#include <alglin.hh>
#include "alglin.hxx"

class AlgLin_i : public POA_Distant::AlgLin,
                 public PortableServer::RefCountServantBase {

private:

  alglin A_interne;

public:

  AlgLin_i();
  virtual ~AlgLin_i();

  void addvec(Distant::vecteur_out C, 
              const Distant::vecteur& A, 
              const Distant::vecteur& B);

  CORBA::Double prdscl(const Distant::vecteur& A, 
                       const Distant::vecteur& B);

};

#endif

alglin_i.cxx

#include "alglin_i.hxx"
#include <iostream>

AlgLin_i::AlgLin_i()
{
}

AlgLin_i::~AlgLin_i()
{
}

void AlgLin_i::addvec(Distant::vecteur_out C,
                      const Distant::vecteur& A, 
                      const Distant::vecteur& B)
{
  long i, n = A.length();

  vecteur A_(n);
  vecteur B_(n);
  vecteur C_(n);

  double *xA = A_.x();
  double *xB = B_.x();
  double *xC = C_.x();

  for (i=0; i<n; i++) {
    xA[i] = A[i];
    xB[i] = B[i];
  }

  A_interne.addvec(&C_, &A_, &B_);

  C = new Distant::vecteur;
  C->length(n);
  for (i=0; i<n; i++) {
    (*C)[i] = xC[i];
  }
}

CORBA::Double AlgLin_i::prdscl(const Distant::vecteur& A, 
                               const Distant::vecteur& B)
{
  long i, n = A.length();

  vecteur A_(n);
  vecteur B_(n);

  double *xA = A_.x();
  double *xB = B_.x();

  for (i=0; i<n; i++) {
    xA[i] = A[i];
    xB[i] = B[i];
  }

  return A_interne.prdscl(&A_, &B_);  
}


Note
Note that the create_vector and destroy_vector functions are not exported in IDL, because a standard CORBA type is used (sequence<double>).

Python server interface

This case is similar to the previous case. It can be simpler (it is not always necessary to include input and output parameter conversion phases).

_images/objCorbapy.png

Generation of the python CORBA interface

Note
This is due to the fact that python is not as strongly typed as C++: a python function can be executed if the input parameters possess all methods and attributes called in the function. This is the case if the methods in the implementation class have the same signatures as methods in the internal object class, as in example 7 below.

Example 7 (continued)

We will consider an example consisting of the python implementation class at the server end used to call objects in the FreeFem class from CORBA:

FreeFem_i.py

import Solveur__POA
import FreeFem

class FreeFem_i(Solveur__POA.FreeFem):

    def __init__(self):
        self.F = FreeFem.FreeFem();

    def Bords(self, b):
        self.F.Bords(b);

    def Flux(self, u1, u2):
        self.F.Flux(u1, u2);

    def Convection(self, cond_init, dt, n):
        self.F.Convection(cond_init, dt, n)

Client interface

The client end interface code can be written in any language (provided that there is a CORBA implementation in this language), independently of the language(s) used at the server end.

Essentially, it is necessary:

  • during compilation, to have the CORBA interface generated at the client end (generally, CORBA generates the two interfaces at the client and server ends simultaneously) and to integrate it into the client code,
  • during execution, retrieve the CORBA reference of the server end component,
  • call component methods on this CORBA reference.

Example 8

In the context of SALOME, there is no need to write clients for user components. However, consider a C++ client example and a python client example in the CORBA AlgLin class:

client.cxx

#include <CORBA.h>
#include <fstream>
#include <string>
#include "alglin.hh"

int main(int argc,char **argv)
{
  CORBA::ORB_ptr orb = CORBA::ORB_init (argc, argv);

  string s;
  ifstream f("AlgLin.ior");
  f >> s;
  f.close();

  CORBA::Object_ptr O = orb->string_to_object(s.c_str());
  Distant::AlgLin_var A = Distant::AlgLin::_narrow(O);

  long n = 5;

  Distant::vecteur V1;
  V1.length(n);

  Distant::vecteur V2;
  V2.length(n);

  for (long i=0; i<n; i++) {
    V1[i] = i*i;
    V2[i] = 2*i;
  }

  CORBA::Double S = A->prdscl(V1, V2);
  cerr << S << endl;
}

client.py

import CORBA

orb = CORBA.ORB_init();

f = open('AlgLin.ior');
s = f.read();
f.close();

o = orb.string_to_object(s);

import Distant
o = o._narrow(Distant.AlgLin)


v1 = [ 1, 2, 3 ]
v2 = [ 3, 4, 5 ]

v3 = o.addvec(v1, v2)
print(v3)

print(o.prdscl(v1, v3))