Back to TILs

CORBA

Date: 2019-12-27Last modified: 2023-02-17

Table of contents

Introdução

O que é CORBA?

O que é a OMG?

CORBA vs SOAP/XML-RPC

CORBA é pesado?

ORB - Object Request Broker

Clientes

Servidores

IDL

IDL Features

Tipos básicos

Direções dos parâmetros

Exceções

CORBA define dois tipos de exceções:

Interface Bank {
  exception Reject {
    string reason; // membro da exceção
  };
  exception TooMany {}; // exceção sem membros

  Account newAccount( in string name ) raises (Reject, TooMany);
};

Operações oneway

Interface Account {
  oneway void notice( in string notice );
};

The OMG CORBA specification defines the following exceptions:

Estruturas

struct PersonalDetails {
  string name;
  short age;
};

interface Bank {
  PersonalDetails getPerDet(in string name);
};

Arrays

Account bankAccounts[100];
short matrix[10][20]; // 2D array

Constantes

Interface Bank {
  const long MaxAccounts = 10000;
};

Contantes dos tipos long, float e string podem ser declarada.

Typedef

typedef short size;
size i;

typedef Account Accounts[100]; // acho que está invertido aqui
Accounts bankAccounts;

Módulos

Module Finance {
  interface Bank { ... };
  interface Account { ... };
};

Preprocessador

Mapeamento para linguagem

Mapeamento de identificadores

Mapeamento de interface

Interface Account { ... }

será mapeado para:

class Account : public virtual CORBA::Object { ... }

Mapeamento de escopo

Interface para classe

Interface Bank {
  struct Details { ... };
};

será mapeado para:

class Bank {
  public:
    struct Details { ... };
};

Module para namespace

Module M {
  Interface A { ... };
  Interface B { ... };
};

será mapeado para:

namespace M {
  class A { ... };
  class B { ... };
};

Acessíveis via M::A e M::B.

Mapeamento do módulo padrão do CORBA

Será mapeado para:

namespace CORBA { ... }

Utilize os membros como:

CORBA::ORB_init( ... );

Mapeamento de tipos

IDL C++ Tamanho e faixa
short CORBA::Short 16 bit: 215+2151
long CORBA::Long 32 bit: 231+2311
long long CORBA::LongLong 64 bit: 263+2631
unsigned short CORBA::UShort 16 bit: 02161
unsigned long CORBA::ULong 32 bit: 02321
unsigned long long CORBA::ULongLong 64 bit: 0264
float CORBA::Float 32 bit IEEE single precision floating point number
double CORBA::Double 64 bit IEEE double precision floating point number
long double CORBA::LongDouble
char CORBA::Char 8 bit
wchar CORBA::WChar – (Wide Char)
string CORBA::char *
wstring CORBA::WChar *
boolean CORBA::Boolean true/false
octet CORBA::Octet (unsigned char) 8 bit raw. Sem conversão
any CORBA::Any Arbitrary

Repositório de interfaces

Repositório de implementação

Dynamic Invocation Interface

Interoperabilidade

COSS - Common Object Service Specification

OA - Object Adapter

BOA - Basic Object Adapter

POA Portable Object Adapter

Identificador de Objeto

O Campo Object Id da IOR

Fig. 1 -

Instalação do omniORB no Debian

apt install libomniorb4-dev omniidl omniorb omniorb-doc omniorb-nameserver omniidl-python python-omniorb

Iniciar o servidor de nomes

OmniORB

Incluir no arquivo /etc/omniORB.cfg a seguinte linha:

InitRef = OmniNameService=corbaname::localhost

Para iniciar o servidor de nomes do OmniORB use:

omniNames -start

Exemplo de saída do comando acima:

omniNames: (0) 2019-12-30 15:36:29.516974: Data file: '/var/lib/omniorb/omninames-erebo.dat'.
omniNames: (0) 2019-12-30 15:36:29.517058: Starting omniNames for the first time.
omniNames: (0) 2019-12-30 15:36:29.517348: Wrote initial data file '/var/lib/omniorb/omninames-erebo.dat'.
omniNames: (0) 2019-12-30 15:36:29.517444: Read data file '/var/lib/omniorb/omninames-erebo.dat' successfully.
omniNames: (0) 2019-12-30 15:36:29.517529: Root context is IOR:010000002b00000049444c3a6f6d672e6f72672f436f734e616d696e672f4e616d696e67436f6e746578744578743a312e300000010000000000000070000000010102000d0000003139322e3136382e302e31330000f90a0b0000004e616d6553657276696365000300000000000000080000000100000000545441010000001c000000010000000100010001000000010001050901010001000000090101000354544108000000ad430a5e010075e0
omniNames: (0) 2019-12-30 15:36:29.517600: Checkpointing Phase 1: Prepare.
omniNames: (0) 2019-12-30 15:36:29.517703: Checkpointing Phase 2: Commit.
omniNames: (0) 2019-12-30 15:36:29.517802: Checkpointing completed.

A porta padrão é a 2809.

Os arquivos de controle serão criados por padrão no diretório /var/lib/omniorb/:

Para especificar um diretório diferente use a opção -logdir:

omniNames -logdir /tmp -start

MICO

Para iniciar o servidor de nomes do MICO use:

nsd -ORBIIOPAddr inet:localhost:2809

Veja exemplo em Connecting 3 ORBs.

Exemplo

Componentes do exemplo
Fig. 2 - Componentes do exemplo

Criando a IDL

Arquivo de interface Data.idl:

#ifndef __DATADIST_IDL__
#define __DATADIST_IDL__
module Data
{
  interface ServiceA {
    boolean CallServiceRoutineA (
        in    long num1,
        inout long num2,
        out   long retNum );
    boolean CallServiceRoutineB (
        inout long num1,
        inout long num2);
  };
};
#endif

Gerando backend a partir da IDL

Use a opção -b para escolher o backend a ser gerado.

omniidl -bcxx    Data.idl
omniidl -bpython Data.idl
omniidl -bdump   Data.idl

Gerando Python

omniidl -bpython Data.idl

Os arquivos abaixo são criados:

Gerando C++

Ao executar o seguinte comando:

omniidl -bcxx Data.idl

os arquivos abaixo são criados:

Contendo os seguintes items:

Data.hh
  // interface ServiceA
  class ServiceA {
  public:
    // Declarations for this interface type.
    typedef ServiceA_ptr _ptr_type;
    typedef ServiceA_var _var_type;
    static _ptr_type _duplicate(_ptr_type);
    static _ptr_type _narrow(::CORBA::Object_ptr);
    static _ptr_type _unchecked_narrow(::CORBA::Object_ptr);
    static _ptr_type _nil();
DataSK.cc
// This file is generated by omniidl (C++ backend)- omniORB_4_2. Do not edit.

#include "Data.hh"
#include <omniORB4/IOP_S.h>
#include <omniORB4/IOP_C.h>
#include <omniORB4/callDescriptor.h>
#include <omniORB4/callHandle.h>
#include <omniORB4/objTracker.h>

OMNI_USING_NAMESPACE(omni)
...

Criando o servidor

Server.cc

Código do servidor:

  1. Iniciar o ORB
  2. Obter a referência para o POA raiz para registra-se
  3. Vincular com o serviço de nomes
  4. Iniciar o servidor de objetos
// Server.cc

#include <assert.h>
#include <iostream>
#include <signal.h>
#include <stdlib.h>
#include <string>
#include <unistd.h>
#include "CServiceA_i.hh"
#include "Data.hh"

using namespace std;

int main( int argc, char **argv )
{
  try {
    // (1) Iniciar o ORB
    CORBA::ORB_var orb = CORBA::ORB_init( argc, argv );
    // (2) Obter a referência para o POA raiz para registra-se para ficar disponível para os clientes
    CORBA::Object_var       obj  = orb->resolve_initial_references( "RootPOA" );
    PortableServer::POA_var _poa = PortableServer::POA::_narrow( obj.in() );
    // As operações definidas na interface são invocadas pelo objeto de referência.
    // Um instância de CRequestSocketStream_i é iniciada
    PortableServer::Servant_var<CServiceA_i> myRequestServiceA = new CServiceA_i();
    // o servidor e objetos é ativado no RootPOA
    PortableServer::ObjectId_var myRequestServiceA_oid = _poa->activate_object( myRequestServiceA );
    // Obtêm o objeto de referência do servidor e registra no servidor de nome
    CORBA::Object_var SA_obj = myRequestServiceA->_this();
    // Obtêm uma referência para o objeto e imprime seu IOR como string
    CORBA::String_var sior( orb->object_to_string( SA_obj.in() ) );
    cerr << "'" << (char *)sior << "'" << endl;
    // (3) Vincular com o serviço de nomes
    // o serviço é definido pela diretiva InitRef e pelo identificador
    // "OmniNameService" no arquivo omniORB.cfg
    CORBA::Object_var obj1 = orb->resolve_initial_references( "OmniNameService" );
    assert( !CORBA::is_nil( obj1.in() ) );
    CosNaming::NamingContext_var nc = CosNaming::NamingContext::_narrow( obj1.in() );
    assert( !CORBA::is_nil( nc.in() ) );
    CosNaming::Name name;
    name.length( 1 );
    name[0].id = CORBA::string_dup( "DataServiceName1" );
    nc->rebind( name, SA_obj.in() );
    //========================================================================
    myRequestServiceA->_remove_ref();
    //
    PortableServer::POAManager_var pmgr = _poa->the_POAManager();
    pmgr->activate();
    // (4) Iniciar o servidor de objetos para aceitar requisições dos clientes
    orb->run();
    //
    // Se o ORB deixar o loop de tratamento de eventos
    // Atualmente está configurado para nunca dar timeout
    orb->destroy();
    //
    free( name[0].id ); // str_dup realiza um malloc internamente
  }

Tratar as exceções:

  catch( CORBA::TRANSIENT & ) {
    cerr << "Caught system exception TRANSIENT -- unable to contact the server." << endl;
  }
  catch( CORBA::OBJECT_NOT_EXIST & ) {
    cerr << "Caught system exception OBJECT_NOT_EXIST" << endl;
  }
  catch( CORBA::SystemException & ) {
    cerr << "Caught CORBA::SystemException." << endl;
  }
  catch( CORBA::Exception & ) {
    cerr << "Caught CORBA::Exception." << endl;
  }
  catch( omniORB::fatalException &fe ) {
    cerr << "Caught omniORB::fatalException:" << endl;
    cerr << "  file: " << fe.file() << endl;
    cerr << "  line: " << fe.line() << endl;
    cerr << "  mesg: " << fe.errmsg() << endl;
  }
  catch( ... ) {
    cerr << "Caught unknown exception." << endl;
  }

  return 0;
}

Implementação do Servant

São denotados pelo sufixo _i.

CServiceA_i.hh

// CServiceA_i.hh
#include "Data.hh"

class CServiceA_i : public POA_Data::ServiceA, public PortableServer::RefCountServantBase {
public:
  CServiceA_i();
  virtual ~CServiceA_i();
  virtual CORBA::Boolean CallServiceARoutineA(
    /*in*/    CORBA::Long num1,
    /*inout*/ CORBA::Long &num2,
    /*out*/   CORBA::Long &retNum );
  virtual CORBA::Boolean CallServiceARoutineB(
    /*inout*/ CORBA::Long &num1,
    /*inout*/ CORBA::Long &num2 );
};

CServiceA_i.cc

// CServiceA.cc

#include <fstream>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <sys/types.h>
#include <unistd.h>
#include <vector>

#include "CServiceA_i.hh"

using namespace Data;

#include <sys/wait.h>

CServiceA_i::CServiceA_i() { }

CServiceA_i::~CServiceA_i( void ) { }

CORBA::Boolean CallServiceARoutineA(
    /*in*/    CORBA::Long num1,
    /*inout*/ CORBA::Long &num2,
    /*out*/   CORBA::Long &retNum )
{
  num2   = num2 + num1;
  retNum = 10;
  return true;
}

CORBA::Boolean CallServiceARoutineB(
    /*inout*/ CORBA::Long &num1,
    /*inout*/ CORBA::Long &num2 )
{
  num1++;
  num2++;
  return true;
}

Cliente CORBA

Cliente CORBA
Fig. 3 - Cliente CORBA

Client.cc

// Client.cc
#include "CRequestServiceA.hh"

int main( int argc, char **argv )
{
  // O construtor estabelece o link como o servidor CORBA.
  CRequestServiceA requestServiceA;

  if( requestServiceA.RequestServiceARoutineA() ) {
    cout << "ServiceA RoutineA: True" << endl;
  }
  if( requestServiceA.RequestServiceARoutineB() ) {
    cout << "ServiceA RoutineB: True" << endl;
  }
  return 0;
}

CRrquestServiceA.hh

// CRrquestServiceA.hh
#include "Data.hh"
#include <cassert>
#include <fstream>
#include <iostream>

using namespace std;

class CRequestServiceA {
public:
  CRequestServiceA();
  ~CRequestServiceA();
  bool RequestServiceARoutineA();
  bool RequestServiceARoutineB();

  CosNaming::Name m_corbaCosName;

  // CORBA ORB
  CORBA::ORB_var m_orb;

  CORBA::Object_var m_obj;  // ORB Object
  CORBA::Object_var m_obj1; // Resolved id to object reference

  // Resolved and narrowed CORBA object for proxy calls
  Data::ServiceA_var m_Data;
};

class DS_ServerConnectionException {
public:
  DS_ServerConnectionException()
  {
    cerr << "CORBA COMM_FAILURE" << endl;
  };
};

class DS_SystemException {
public:
  DS_SystemException()
  {
    cerr << "CORBA Exception" << endl;
  };
};

class DS_FatalException {
public:
  DS_FatalException()
  {
    cerr << "CORBA Fatal Exception" << endl;
  };
};

class DS_Exception {
public:
  DS_Exception()
  {
    cerr << "Exception" << endl;
  };
};

CRequestServiceA.cc

// CRequestServiceA.cc

#include "CRequestServiceA.hh"
using namespace Data;

CRequestServiceA::CRequestServiceA()
{
  try {
    // Inicia o objeto ORB
    int            argc = 0; // Variáveis dummy.
    char **        argv = 0;
    CORBA::ORB_var orb  = CORBA::ORB_init( argc, argv );

    // Vincula o objeto ORB ao servidor de nome
    // Deve ser o mesmo nome definido em /etc/omniORB.cfg
    CORBA::Object_var obj = orb->resolve_initial_references( "OmniNameService" );
    assert( !CORBA::is_nil( obj.in() ) );

    CosNaming::NamingContext_var nc = CosNaming::NamingContext::_narrow( obj.in() );
    assert( !CORBA::is_nil( nc.in() ) );

    CosNaming::Name _corbaCosName;
    _corbaCosName.length( 1 );
    _corbaCosName[0].id = CORBA::string_dup( "DataServiceName1" );

    CORBA::Object_var obj1 = nc->resolve( _corbaCosName );
    assert( !CORBA::is_nil( obj1.in() ) );

    m_Data = ServiceA::_narrow( obj1.in() );
    if( CORBA::is_nil( m_Data.in() ) ) {
      cerr << "IOR is not an SA object reference." << endl;
    }
  }
  catch( CORBA::COMM_FAILURE &ex ) {
    cerr << "Caught system exception COMM_FAILURE -- unable to contact the "
         << "object." << endl;
    throw DS_ServerConnectionException();
    return;
  }
  catch( CORBA::SystemException & ) {
    cerr << "Caught a CORBA::SystemException." << endl;
    throw DS_SystemException();
    return;
  }
  catch( CORBA::Exception & ) {
    cerr << "Caught CORBA::Exception." << endl;
    throw DS_Exception();
    return;
  }
  catch( omniORB::fatalException &fe ) {
    cerr << "Caught omniORB::fatalException:" << endl;
    cerr << "  file: " << fe.file() << endl;
    cerr << "  line: " << fe.line() << endl;
    cerr << "  mesg: " << fe.errmsg() << endl;
    throw DS_FatalException();
    return;
  }
  catch( ... ) {
    cerr << "Caught unknown exception." << endl;
    throw DS_Exception();
    return;
  }
  return;
}

CRequestServiceA::~CRequestServiceA()
{
  // ...
}

bool CRequestServiceA::RequestServiceARoutineA()
{
  CORBA::Long num1 = 4;
  CORBA::Long num2 = 5;
  CORBA::Long retNum;

  cout << "Valores de entrada para a rotina A do serviço A: " << num1 << " " << num2 << " " << retNum << endl;

  // Esta chamada CORBA será executada remotamente
  if( m_Data->CallServiceRoutineA( num1, num2, retNum ) ) {
    cout << "Valores retornados pelo serviço A: " << num1 << " " << num2 << " " << retNum << endl;
    return true;
  }
  return false;
}

bool CRequestServiceA::RequestServiceARoutineB()
{
  CORBA::Long num1 = 0;
  CORBA::Long num2 = 50;

  cout << "Valores de entrada para a rotina B do serviço A: " << num1 << " " << num2 << endl;

  // Esta chamada CORBA será executada remotamente
  if( m_Data->CallServiceRoutineB( num1, num2 ) ) {
    cout << "Valores retornados pelo serviço B: " << num1 << " " << num2 << endl;
    return true;
  }
  return false;
}

Makefile

# Makefile
LDFLAGS+=-lomniORB4 -lomnithread -lomniDynamic4
CXXFLAGS+=-Wall

all: server client

server: DataSK.o CServiceA_i.o Server.o
	$(CXX) $(CXXFLAGS) -o $@ $^ $(LDFLAGS)

client: DataSK.o Client.o CRequestServiceA.o
	$(CXX) $(CXXFLAGS) -o $@ $^ $(LDFLAGS)

clean:
	rm -f *.o
	rm -f server
	rm -f client

This is the value of CORBA. This is why we use CORBA. Look at how simple the program looks. The constructor establishes the link with the CORBA server. The subsequent calls to Routine A and B are processed remotely on the CORBA server but called like any other local function call.

Todo

Referências