10. Construção de aplicativos distribuídos CORBA
10.1. Introdução
No capítulo anterior, vimos como criar aplicativos distribuídos em Java com o pacote RMI. Abordamos aqui o mesmo problema, desta vez com a arquitetura CORBA. CORBA (Common Object Request Broker Architecture) é uma especificação definida pelo OMG (Object Management Group), que reúne diversas empresas do setor de TI. A CORBA define um “barramento de software” acessível a aplicativos escritos em diferentes linguagens:

Veremos que a construção de uma aplicação distribuída com CORBA é semelhante ao método empregado com Java RMI: os conceitos são parecidos. O CORBA apresenta a vantagem da interoperabilidade com aplicativos escritos em outras linguagens.
10.2. Processo de desenvolvimento de uma aplicação CORBA
10.2.1. Introdução
Para desenvolver uma aplicação cliente-servidor CORBA, seguiremos as seguintes etapas:
- escrita da interface do servidor com IDL (Interface Definition Language)
- geração das classes “esqueleto” e “stub” do servidor
- criação do servidor
- criação do cliente
- compilação de todas as classes
- inicialização de um diretório de serviços CORBA
- inicialização do servidor
- inicialização do cliente
Tomaremos como primeiro exemplo o servidor de eco já utilizado no contexto RMI. Assim, o leitor poderá observar as diferenças entre os dois métodos.
O aplicativo foi testado com o jdk1.2.
10.2.2. Criação da interface do servidor
Assim como no Java RMI, em relação ao cliente, o servidor é definido por sua interface. Se as classes que implementam o servidor não são necessárias para o cliente, as classes de sua interface, por outro lado, são. Enquanto o Java RMI utilizava uma interface Java que gerava as classes “esqueleto” e “stub” do servidor, a arquitetura Java CORBA requer que a interface seja descrita em uma linguagem diferente do Java. Essa interface dará origem a várias classes, algumas das quais serão utilizadas pelo cliente e outras pelo servidor.
A descrição da interface de eco será a seguinte:
A descrição da interface será armazenada em um arquivo echo.idl. Ela é escrita na linguagem IDL (Interface Definition Language) do OMG. Para ser utilizável, ela deve ser analisada por um programa que criará arquivos-fonte na linguagem utilizada para desenvolver a aplicação CORBA. Aqui, utilizaremos o programa idltojava.exe, que, a partir da interface anterior, criará os arquivos-fonte .java necessários para a aplicação. O programa idltojava.exe não vem junto com o JDK. Ele pode ser obtido no site da Sun http://java.sun.com.
Vamos analisar algumas linhas da interface idl anterior:
é equivalente ao pacote echo do Java. A compilação da interface resultará no pacote Java echo c.a.d, um diretório contendo classes Java.
é equivalente à interface iSrvEcho do Java. Gerará uma interface Java.
é equivalente à instrução Java String echo(String msg). Os tipos da linguagem IDL não correspondem exatamente aos da linguagem Java. As correspondências serão apresentadas um pouco mais adiante neste capítulo. Na linguagem IDL, os parâmetros de uma função podem ser de entrada (in), de saída (out) ou de entrada-saída (inout). Aqui, o método echo recebe um parâmetro de entrada msg, que é uma sequência de caracteres, e retorna uma sequência de caracteres como resultado.
A interface anterior é a do nosso servidor de eco. Vale lembrar que uma interface remota descreve os métodos do objeto servidor acessíveis aos clientes. Aqui, apenas o método echo estará disponível para os clientes.
10.2.3. Compilação da interface IDL do servidor
Uma vez definida a interface do servidor, geramos os arquivos Java correspondentes.
E:\data\java\corba\ECHO>dir *.idl
ECHO IDL 78 15/03/99 13:56 ECHO.IDL
E:\data\java\corba\ECHO>d:\javaidl\idltojava.exe -fno-cpp echo.idl
A opção -fno-cpp serve para indicar que não se deve utilizar um pré-processador (usado com mais frequência com C/C++). A compilação do arquivo echo.idl gera um subdiretório echo contendo os seguintes arquivos:
E:\data\java\corba\ECHO>dir echo
_ISRVE~1 JAV 1 095 17/03/99 17:19 _iSrvEchoStub.java
ISRVEC~1 JAV 311 17/03/99 17:19 iSrvEcho.java
ISRVEC~2 JAV 825 17/03/99 17:19 iSrvEchoHolder.java
ISRVEC~3 JAV 1 827 17/03/99 17:19 iSrvEchoHelper.java
_ISRVE~2 JAV 1 803 17/03/99 17:19 _iSrvEchoImplBase.java
O arquivo iSrvEcho.java é o arquivo Java que descreve a interface do servidor:
/* * Arquivo: ./ECHO/ISRVECHO.JAVA * De: ECHO.IDL * Data: Seg 15 de março 13:56:08 1999 * Por: D:\JAVAIDL\IDLTOJ~1.EXE Java IDL 1.2 18 de agosto de 1998 16:25:34 */
package echo;
public interface iSrvEcho
extends org.omg.CORBA.Object, org.omg.CORBA.portable.IDLEntity {
String echo(String msg)
;
}
Percebe-se que se trata praticamente de uma tradução literal da interface IDL. Se tivermos curiosidade em examinar o conteúdo dos outros arquivos .java, encontraremos elementos mais complexos. Veja a seguir o que diz a documentação sobre a função desses diferentes arquivos:
a interface do servidor
implementa a interface iSrvEcho anterior. Trata-se de uma classe abstrata, o “esqueleto” do servidor, que fornece ao servidor as funcionalidades CORBA necessárias para a aplicação distribuída.
Esta é a imagem (“stub”) do servidor que será utilizada pelo cliente. Ela fornece ao cliente as funcionalidades CORBA para acessar o servidor.
Fornece os métodos necessários para o gerenciamento de referências de objetos CORBA
Fornece os métodos necessários para o gerenciamento dos parâmetros de entrada e saída dos métodos da interface.
10.2.4. Compilação das classes geradas a partir da interface IDL
É recomendável compilar as classes anteriores. Veremos em outro exemplo que é possível detectar aqui erros decorrentes de um funcionamento incorreto do gerador idltojava. Neste caso, tudo correu bem e, após a compilação, temos no diretório do pacote echo os seguintes arquivos:
E:\data\java\corba\ECHO\echo>dir
_ISRVE~1 JAV 1 095 17/03/99 17:19 _iSrvEchoStub.java
ISRVEC~1 JAV 311 17/03/99 17:19 iSrvEcho.java
ISRVEC~2 JAV 825 17/03/99 17:19 iSrvEchoHolder.java
ISRVEC~3 JAV 1 827 17/03/99 17:19 iSrvEchoHelper.java
_ISRVE~2 JAV 1 803 17/03/99 17:19 _iSrvEchoImplBase.java
_ISRVE~1 CLA 2 275 18/03/99 11:25 _iSrvEchoImplBase.class
_ISRVE~2 CLA 1 383 18/03/99 11:25 _iSrvEchoStub.class
ISRVEC~1 CLA 251 18/03/99 11:25 iSrvEcho.class
ISRVEC~2 CLA 2 078 18/03/99 11:25 iSrvEchoHelper.class
ISRVEC~3 CLA 858 18/03/99 11:25 iSrvEchoHolder.class
10.2.5. Gravação no servidor
10.2.5.1. Implementação da interface iSrvEcho
Definimos acima a interface iSrvEcho. Agora, vamos escrever a classe que implementa essa interface. Ela será derivada da classe _iSrvEchoImplbase.java, que, conforme indicado acima, já implementa a interface iSrvEcho.
// pacotes importados
import echo.*;
// classe que implementa o eco remoto
public class srvEcho extends _iSrvEchoImplBase{
// método que realiza o eco
public String echo(String msg){
return "[" + msg + "]";
}// fim do eco
}// fim da classe
O código é autoexplicativo. Essa classe está registrada no arquivo srvEcho.java, no diretório pai do pacote da interface iSrvEcho.
É possível compilar para verificar:
E:\data\java\corba\ECHO>j:\jdk12\bin\javac srvEcho.java
E:\data\java\corba\ECHO>dir
ECHO IDL 78 15/03/99 13:56 ECHO.IDL
SRVECH~1 CLA 488 18/03/99 11:30 srvEcho.class
SRVECH~1 JAV 252 15/03/99 14:02 srvEcho.java
ECHO <REP> 17/03/99 17:19 echo
10.2.5.2. Registro da classe de criação do servidor
Assim como em uma aplicação cliente-servidor RMI, um servidor CORBA deve ser registrado em um diretório para que possa ser acessado pelos clientes. É esse procedimento de registro que, no nível de desenvolvimento, difere dependendo se se trata de um aplicativo CORBA ou RMI. A seguir, o código do servidor CORBA de eco registrado no arquivo serveurEcho.java:
// pacotes importados
import echo.*;
import org.omg.CosNaming.*;
import org.omg.CosNaming.NamingContextPackage.*;
import org.omg.CORBA.*;
//----------- classe serveurEcho
public class serveurEcho{
// ------- main: inicia o servidor de eco
// sintaxe pg machineAnnuaire portAnnuaire nomService
// máquina: máquina que suporta o diretório CORBA
// porta: porta do diretório CORBA
// nomService: nome do serviço a ser registrado
public static void main(String arg[]){
// os argumentos estão presentes?
if(arg.length!=3){
System.err.println("Syntaxe : pg machineAnnuaire portAnnuaire nomService");
System.exit(1);
}
// recuperamos os argumentos
String machine=arg[0];
String port=arg[1];
String nomService=arg[2];
try{
// precisamos de um objeto CORBA para trabalhar
String[] initORB={"-ORBInitialHost",machine,"-ORBInitialPort",port};
ORB orb=ORB.init(initORB,null);
// colocamos o serviço no diretório de serviços
// ele se chamará srvEcho
org.omg.CORBA.Object objRef=
orb.resolve_initial_references("NameService");
NamingContext ncRef=NamingContextHelper.narrow(objRef);
NameComponent nc= new NameComponent(nomService,"");
NameComponent path[]={nc};
// criamos o servidor e o associamos ao serviço srvEcho
srvEcho serveurEcho=new srvEcho();
ncRef.rebind(path,serveurEcho);
orb.connect(serveurEcho);
// acompanhamento
System.out.println("Serveur d'écho prêt");
// aguardando solicitações dos clientes
java.lang.Object sync=new java.lang.Object();
synchronized(sync){
sync.wait();
}
} catch(Exception e){
// ocorreu um erro
System.err.println("Erreur " + e);
e.printStackTrace(System.err);
}
}// manual
}// serveurEcho
A seguir, explicamos as linhas gerais da inicialização do servidor, sem entrar em detalhes que, à primeira vista, podem parecer complexos. É importante reter as linhas gerais do exemplo anterior, pois elas se repetem em todos os servidores CORBA.
10.2.5.2.1. Os parâmetros do servidor
Um servidor CORBA deve se registrar em um serviço de diretório que opera em uma máquina e em uma porta específicas. Nossa aplicação receberá esses dois dados como parâmetros. O serviço assim registrado deve ter um nome, que será o terceiro parâmetro.
10.2.5.2.2. Criar o objeto de acesso ao serviço de diretório CORBA
Para acessar o serviço de diretório e registrar nosso servidor de eco, precisamos de um objeto chamado ORB (Object Request Broker), obtido com o seguinte método de classe:
Args : tableau de paires de chaînes de caractères, chaque paire étant de la forme (paramètre,valeur)
Prop : propriétés de l’application
O exemplo utiliza a seguinte sequência para obter o objeto ORB:
String[] initORB={"-ORBInitialHost",machine,"-ORBInitialPort",port};
ORB orb=ORB.init(initORB,null);
Os pares (parâmetro, valor) utilizados são os seguintes:
("-ORBInitialHost",machine) : ce couple précise la machine ou opère l’annuaire des services CORBA, ici la machine passée en paramètre au serveur.
("-ORBInitialPort",port ) : ce couple précise le port ou opère l’annuaire des services CORBA, ici le port passé en paramètre au serveur.
O segundo parâmetro do método init é definido como null. Se o primeiro parâmetro também tivesse sido definido como null, o par (máquina, porta) utilizado teria sido o padrão (localhost,900).
10.2.5.2.3. Registrar o servidor no diretório de serviços CORBA
O registro do servidor no diretório é feito por meio das seguintes operações:
// o serviço está sendo adicionado ao diretório de serviços
// ele se chamará srvEcho
org.omg.CORBA.Object objRef=
orb.resolve_initial_references("NameService");
NamingContext ncRef=NamingContextHelper.narrow(objRef);
NameComponent nc= new NameComponent(nomService,"");
NameComponent path[]={nc};
// criamos o servidor e o associamos ao serviço srvEcho
srvEcho serveurEcho=new srvEcho();
ncRef.rebind(path,serveurEcho);
orb.connect(serveurEcho);
A primeira parte do código consiste em preparar o nome do serviço. Esse nome é representado no código pela variável path. O nome de um serviço é composto por vários elementos:
- um componente inicial objRef, um objeto genérico que deve ser convertido para o tipo NamingContext, neste caso ncRef.
- o nome do serviço, neste caso nomService, que foi passado como parâmetro ao servidor
Esses componentes do nome (NameComponent) são reunidos em uma matriz, neste caso path. É essa matriz que “nomeia” de forma precisa o serviço criado. Uma vez criado o nome, resta
- associá-lo a uma instância do servidor (a classe srvEcho criada anteriormente)
srvEcho serveurEcho=new srvEcho();
- registrá-la no diretório
10.2.5.3. Compilação da classe de inicialização do servidor
Compilamos a classe anterior:
E:\data\java\corba\ECHO>j:\jdk12\bin\javac serveurEcho.java
E:\data\java\corba\ECHO>dir
ECHO IDL 78 15/03/99 13:56 ECHO.IDL
SERVEU~1 CLA 1 793 18/03/99 13:18 serveurEcho.class
SERVEU~1 JAV 1 806 16/03/99 15:38 serveurEcho.java
SRVECH~1 CLA 488 18/03/99 11:30 srvEcho.class
SRVECH~1 JAV 252 15/03/99 14:02 srvEcho.java
ECHO <REP> 17/03/99 17:19 echo
10.2.6. Texto do cliente
10.2.6.1. O código
Estamos escrevendo um cliente para testar nosso serviço de eco. Passaremos ao cliente os mesmos três parâmetros que passamos ao servidor:
Máquina: máquina onde está localizado o diretório de serviços CORBA
Porta: porta na qual esse diretório opera
nomService: nome do serviço de eco
O cliente se conecta ao serviço de eco e, em seguida, solicita que o usuário digite mensagens no teclado. Essas mensagens são enviadas ao servidor de eco, que as reenvia. O acompanhamento desse diálogo é feito na tela.
O cliente CORBA do serviço de eco é muito semelhante ao cliente RMI já desenvolvido. Mais uma vez, o cliente precisa se conectar a um serviço de diretório para obter uma referência do objeto-servidor ao qual deseja se conectar. A diferença entre os dois clientes reside exatamente nisso e somente nisso. Aqui está o código do cliente de eco CORBA:
10.2.6.2. A conexão do cliente ao servidor
O cliente CORBA acima se conecta ao servidor por meio da instrução:
// estabelece-se a conexão com o servidor de eco
iSrvEcho serveurEcho=getServeurEcho(machine,port,nomService);
Após essa operação, o cliente obtém uma referência do servidor do écho. A partir daí, um cliente CORBA não difere de um cliente RMI. O método privado que garante a conexão com o servidor é o seguinte:
// pacotes importados
import java.io.*;
import echo.*;
import org.omg.CosNaming.*;
import org.omg.CORBA.*;
// ---------- classe cltEcho
public class cltEcho {
public static void main(String arg[]){
// sintaxe: cltEcho machineAnnuaire portAnnuaire nomeserviço
// máquina: máquina na qual o diretório de serviços está em operação CORBA
// porta: porta em que o diretório de serviços opera
// nomService: nome do serviço de eco
// verificação dos argumentos
if(arg.length!=3){
System.err.println("Syntaxe : pg machineAnnuaire portAnnuaire nomservice");
System.exit(1);
}
// recuperação dos parâmetros
String machine=arg[0];
String port=arg[1];
String nomService=arg[2];
// estabelece-se a conexão com o servidor de eco
iSrvEcho serveurEcho=getServeurEcho(machine,port,nomService);
// diálogo cliente-servidor
BufferedReader in=null;
String msg=null;
String reponse=null;
iSrvEcho serveur=null;
try{
// abertura do fluxo do teclado
in=new BufferedReader(new InputStreamReader(System.in));
// ciclo de leitura das mensagens a serem enviadas ao servidor de eco
System.out.print("Message : ");
msg=in.readLine().toLowerCase().trim();
while(! msg.equals("fin")){
// envio da mensagem ao servidor e recebimento da resposta
reponse=serveurEcho.echo(msg);
// acompanhamento
System.out.println("Réponse serveur : " + reponse);
// próxima mensagem
System.out.print("Message : ");
msg=in.readLine().toLowerCase().trim();
}// while
// concluído
System.exit(0);
// gestão de erros
} catch (Exception e){
System.err.println("Erreur : " + e);
System.exit(2);
}// try
}// main
// ---------------------- getServeurEcho
private static iSrvEcho getServeurEcho(String machine, String port,
String nomService){
// solicita uma referência do servidor de eco
// rastreamento
System.out.println("--> Connexion au serveur CORBA en cours...");
// a referência do servidor de eco
iSrvEcho serveurEcho=null;
try{
// é solicitado um objeto CORBA para trabalhar
String[] initORB={"-ORBInitialHost",machine,"-ORBInitialPort",port};
ORB orb=ORB.init(initORB,null);
// utiliza-se o serviço de diretório para localizar o servidor de eco
org.omg.CORBA.Object objRef=
orb.resolve_initial_references("NameService");
NamingContext ncRef=NamingContextHelper.narrow(objRef);
// o serviço procurado se chama srvEcho — ele é solicitado
NameComponent nc= new NameComponent(nomService,"");
NameComponent path[]={nc};
serveurEcho=iSrvEchoHelper.narrow(ncRef.resolve(path));
} catch (Exception e){
System.err.println("Erreur lors de la localisation du serveur d'écho ("
+ e + ")");
System.exit(10);
}// try-catch
// retornamos a referência ao servidor
return serveurEcho;
}// getServeurEcho
}// classe
Encontramos as mesmas sequências de código que no servidor:
- criamos um objeto ORB que nos permitirá acessar o diretório de serviços CORBA
String[] initORB={"-ORBInitialHost",machine,"-ORBInitialPort",port};
ORB orb=ORB.init(initORB,null);
- definimos os diferentes componentes do nome do serviço de eco
org.omg.CORBA.Object objRef=orb.resolve_initial_references("NameService");
NamingContext ncRef=NamingContextHelper.narrow(objRef);
NameComponent nc= new NameComponent(nomService,"");
NameComponent path[]={nc};
- solicitamos ao serviço de diretório uma referência do serviço de eco (é nesse ponto que nos diferenciamos do servidor)
10.2.6.3. Compilation
E:\data\java\corba\ECHO>j:\jdk12\bin\javac cltEcho.java
E:\data\java\corba\ECHO>dir
CLTECH~1 CLA 2 599 18/03/99 13:51 cltEcho.class
CLTECH~1 JAV 2 907 16/03/99 16:15 cltEcho.java
ECHO IDL 78 15/03/99 13:56 ECHO.IDL
SERVEU~1 CLA 1 793 18/03/99 13:18 serveurEcho.class
SERVEU~1 JAV 1 806 16/03/99 15:38 serveurEcho.java
SRVECH~1 CLA 488 18/03/99 11:30 srvEcho.class
SRVECH~1 JAV 252 15/03/99 14:02 srvEcho.java
ECHO <REP> 17/03/99 17:19 echo
10.2.7. Testes
10.2.7.1. Inicialização do serviço de diretório
Em um computador com Windows, iniciamos o serviço de diretório da seguinte maneira:
o que faz com que o serviço de diretório seja iniciado na porta 1000 do computador.
O serviço de diretório tnameserv exibe uma mensagem na tela semelhante à seguinte:
Initial Naming Context:
IOR:000000000000002849444c3a6f6d672e6f72672f436f734e616d696e672f4e616d696e67436f
6e746578743a312e3000000000010000000000000030000100000000000a69737469612d30303900
044700000018afabcafe000000027620dd9a000000080000000000000000
TransientNameServer: setting port for initial object references to: 1000
Não está muito legível, mas vamos nos concentrar na última linha: o serviço está ativo na porta 1000.
10.2.7.2. Lançamento do servidor de eco
O serviço de eco é iniciado com três parâmetros:
E:\data\java\corba\ECHO>start j:\jdk12\bin\java serveurEcho localhost 1000 srvEcho
O servidor exibe:
10.2.7.3. Inicialização do cliente no mesmo computador que o servidor
E:\data\java\corba\ECHO>j:\jdk12\bin\java cltEcho localhost 1000 srvEcho
--> Connexion au serveur CORBA en cours...
Message : msg1
Réponse serveur : [msg1]
Message : msg2
Réponse serveur : [msg2]
Message : fin
10.2.7.4. Inicialização do cliente em um computador Windows diferente do servidor
E:\data\java\corba\ECHO>j:\jdk12\bin\java cltEcho tahe.istia.univ-angers.fr 1000 srvEcho
--> Connexion au serveur CORBA en cours...
Message : abcd
Réponse serveur : [abcd]
Message : efgh
Réponse serveur : [efgh]
Message : fin
10.3. Exemplo 2: um servidor SQL
10.3.1. Introdução
Retomamos aqui a descrição do servidor SQL, já analisado no contexto do Java RMI, sempre com o objetivo de destacar os pontos em comum entre os dois métodos, bem como suas diferenças. Recordamos a função desse servidor SQL: ele está instalado em uma máquina Windows e permite que clientes remotos acessem os bancos de dados públicos ODBC desse computador Windows.

O cliente CORBA poderia realizar três operações:
- conectar-se ao banco de dados de sua escolha
- enviar consultas SQL
- encerrar a conexão
O servidor executa as consultas SQL do cliente e envia os resultados a ele. Essa é sua função principal e é por isso que o chamamos de servidor SQL. Aplicamos as diferentes etapas vistas anteriormente com o servidor de eco.
10.3.2. Criação da interface IDL do servidor
Vale lembrar a interface RMI que utilizamos para o servidor:
import java.rmi.*;
// a interface remota
public interface interSQL extends Remote{
public String connect(String pilote, String url, String id, String mdp)
throws java.rmi.RemoteException;
public String[] executeSQL(String requete, String separateur)
throws java.rmi.RemoteException;
public String close()
throws java.rmi.RemoteException;
}
A função dos diferentes métodos era a seguinte:
Connect: o cliente se conecta a um banco de dados remoto, fornecendo o driver, a URL JDBC, bem como sua identidade (id) e senha (mdp) para acessar esse banco. O servidor retorna uma sequência de caracteres indicando o resultado da conexão:
executeSQL: o cliente solicita a execução de uma consulta SQL no banco de dados ao qual está conectado. Ele indica o caractere que deve separar os campos nos resultados que lhe são retornados. O servidor retorna uma matriz de cadeias de caracteres:
para uma consulta de atualização do banco de dados, sendo n o número de linhas atualizadas
se a consulta gerou um erro
se a consulta não gerou nenhum resultado
se a consulta gerou resultados. As linhas assim retornadas pelo servidor são as linhas de resultados da consulta.
Close: o cliente encerra sua conexão com o banco de dados remoto. O servidor retorna uma string indicando o resultado desse encerramento:
A interface IDL do servidor será a seguinte:
module srvSQL{
typedef sequence<string> resultats;
interface interSQL{
string connect(in string pilote, in string urlBase, in string id, in string mdp);
resultats executeSQL(in string requete, in string separateur);
string close();
};// interface
};// módulo
A única novidade em relação ao que vimos na interface IDL do servidor de eco é o uso da palavra-chave sequence. Essa palavra-chave permite definir um array unidimensional. A definição é feita em duas etapas:
- definição de um tipo para designar a matriz, neste caso “resultados”:
A palavra-chave typedef é bem conhecida pelos programadores de C/C++: ela permite definir um novo tipo. Aqui, o tipo resultats é definido como equivalente ao tipo sequence<string>, c.a.d, que é uma matriz dinâmica (sem dimensão definida) de cadeias de caracteres.
- Utilização do novo tipo onde for necessário
O método executeSQL, portanto, retorna um array de strings.
10.3.3. Compilação da interface IDL do servidor
A interface IDL anterior é inserida no arquivo srvSQL.idl. Compilamos esse arquivo:
E:\data\java\corba\sql>d:\javaidl\idltojava -fno-cpp srvSQL.idl
E:\data\java\corba\sql>dir
SRVSQL IDL 275 19/03/99 9:59 srvSQL.idl
SRVSQL <REP> 19/03/99 9:41 srvSQL
Percebe-se que a compilação gerou uma pasta com o nome do módulo da interface IDL (srvSQL). Vamos dar uma olhada no conteúdo dessa pasta:
E:\data\java\corba\sql>dir srvSQl
RESULT~1 JAV 833 19/03/99 10:00 resultatsHolder.java
RESULT~2 JAV 1 883 19/03/99 10:00 resultatsHelper.java
_INTER~1 JAV 2 474 19/03/99 10:00 _interSQLStub.java
INTERS~1 JAV 448 19/03/99 10:00 interSQL.java
INTERS~2 JAV 841 19/03/99 10:00 interSQLHolder.java
INTERS~3 JAV 1 855 19/03/99 10:00 interSQLHelper.java
_INTER~2 JAV 4 535 19/03/99 10:00 _interSQLImplBase.java
Vale lembrar que os arquivos Helper e Holder são classes relacionadas aos parâmetros de entrada e saída e aos resultados dos métodos da interface remota. O diretório srvSQL contém todos os arquivos .java relacionados à interface interSQL definida no arquivo .idl. Ele também contém arquivos relacionados ao tipo resultats criado na interface IDL.
O arquivo interSQL.java é o arquivo Java da interface do nosso servidor. É importante verificar se o que foi gerado automaticamente corresponde às nossas expectativas. O arquivo interSQL.java gerado é o seguinte:
/* * Arquivo: ./SRVSQL/INTERSQL.JAVA * De: SRVSQL.IDL * Data: Sexta-feira, 19 de março, 09:59:48 de 1999 * Por: D:\JAVAIDL\IDLTOJ~1.EXE Java IDL 1.2 18 de agosto de 1998 16:25:34 */
package srvSQL;
public interface interSQL
extends org.omg.CORBA.Object, org.omg.CORBA.portable.IDLEntity {
String connect(String pilote, String urlBase, String id, String mdp)
;
String[] executeSQL(String requete, String separateur)
;
String close()
;
}
Percebemos que temos a mesma interface utilizada para o cliente-servidor RMI. Portanto, podemos prosseguir. Vamos compilar todos esses arquivos .java:
E:\data\java\corba\sql\srvSQL>j:\jdk12\bin\javac *.java
Note: _interSQLImplBase.java uses or overrides a deprecated API. Recompile with
"-deprecation" for details.
1 warning
E:\data\java\corba\sql\srvSQL>dir *.class
_INTER~1 CLA 3 094 19/03/99 10:01 _interSQLImplBase.class
_INTER~2 CLA 1 953 19/03/99 10:01 _interSQLStub.class
INTERS~1 CLA 430 19/03/99 10:01 interSQL.class
INTERS~2 CLA 2 096 19/03/99 10:01 interSQLHelper.class
INTERS~3 CLA 870 19/03/99 10:01 interSQLHolder.class
RESULT~1 CLA 2 047 19/03/99 10:01 resultatsHelper.class
RESULT~2 CLA 881 19/03/99 10:01 resultatsHolder.class
10.3.4. Código do servidor SQL
Agora, vamos escrever o código do servidor SQL. Vale lembrar que essa classe deve derivar da classe abstrata _nomInterfaceImplBase, gerada pela compilação do arquivo IDL. Exceto por essa particularidade e se excluirmos as sequências de código relacionadas ao registro do serviço em um diretório, o código do servidor CORBA é idêntico ao do servidor RMI:
// pacotes importados
import java.sql.*;
import java.util.*;
import srvSQL.*;
// classe SQLServant
public class SQLServant extends _interSQLImplBase{
// dados globais da classe
private Connection DB;
// --------------- conexão
public String connect(String pilote, String url, String id,
String mdp){
// conexão à base de dados url por meio do driver
// identificação com ID e senha
String resultat=null; // resultado do método
try{
// carregamento do driver
Class.forName(pilote);
// solicitação de conexão
DB=DriverManager.getConnection(url,id,mdp);
// ok
resultat="200 Connexion réussie";
} catch (Exception e){
// erro
resultat="500 Echec de la connexion (" + e + ")";
}
// fim
return resultat;
}
// ------------- executeSQL
public String[] executeSQL(String requete, String separateur){
// executa uma consulta SQL no banco de dados DB
// e armazena os resultados em uma matriz de strings
// dados necessários para a execução da consulta
Statement S=null;
ResultSet RS=null;
String[] lignes=null;
Vector resultats=new Vector();
String ligne=null;
try{
// criação do contêiner da consulta
S=DB.createStatement();
// execução da consulta
if (! S.execute(requete)){
// consulta de atualização
// é retornado o número de linhas atualizadas
lignes=new String[1];
lignes[0]="100 "+S.getUpdateCount();
return lignes;
}
// era uma consulta
// recuperando os resultados
RS=S.getResultSet();
// número de campos do Resultset
int nbChamps=RS.getMetaData().getColumnCount();
// eles são processados
while(RS.next()){
// criação da linha de resultados
ligne="101 ";
for (int i=1;i<nbChamps;i++)
ligne+=RS.getString(i)+separateur;
ligne+=RS.getString(nbChamps);
// adição ao vetor de resultados
resultats.addElement(ligne);
}// while
// fim da análise dos resultados
// liberamos os recursos
RS.close();
S.close();
// retorno dos resultados
int nbLignes=resultats.size();
if (nbLignes==0){
lignes=new String[1];
lignes[0]="501 Pas de résultats";
} else {
lignes=new String[resultats.size()];
for(int i=0;i<lignes.length;i++)
lignes[i]=(String) resultats.elementAt(i);
}//if
return lignes;
} catch (Exception e){
// erro
lignes=new String[1];
lignes[0]="500 " + e;
return lignes;
}// try-catch
}// executeSQL
// --------------- fechar
public String close(){
// fecha a conexão com o banco de dados
String resultat=null;
try{
DB.close();
resultat="200 Base fermée";
} catch (Exception e){
resultat="500 Erreur à la fermeture de la base ("+e+")";
}
// retorno do resultado
return resultat;
}
}// classe SQLServant
Essa classe está localizada no arquivo SQLServant.java, que estamos compilando:
E:\data\java\corba\sql>dir
SRVSQL IDL 275 19/03/99 9:59 srvSQL.idl
SRVSQL <REP> 19/03/99 9:41 srvSQL
SQLSER~1 JAV 2 941 15/03/99 9:09 SQLServant.java
E:\data\java\corba\sql>j:\jdk12\bin\javac SQLServant.java
E:\data\java\corba\sql>dir *.class
SQLSER~1 CLA 2 568 19/03/99 10:19 SQLServant.class
10.3.5. Criação do programa de inicialização do servidor SQL
A classe anterior representa o servidor SQL após sua inicialização. Antes disso, ele deve ser registrado no diretório de serviços CORBA. Assim como no serviço de eco, faremos isso por meio de uma classe especial à qual passaremos, no momento da execução, três parâmetros:
Máquina: máquina onde se encontra o diretório de serviços CORBA
Porta: porta na qual esse diretório opera
nomService: nome do serviço SQL
O código dessa classe é praticamente idêntico ao da classe que fazia a mesma coisa para o serviço de eco. Destacamos em negrito a linha que difere entre as duas classes: ela não cria o mesmo objeto-servidor. Vemos, portanto, que ainda temos a mesma mecânica de inicialização do servidor. Se isolarmos essa mecânica em uma classe, como foi feito aqui, ela se torna praticamente transparente para o desenvolvedor.
// pacotes importados
import srvSQL.*;
import org.omg.CosNaming.*;
import org.omg.CosNaming.NamingContextPackage.*;
import org.omg.CORBA.*;
//----------- classe serveurSQL
public class serveurSQL{
// ------- main: inicia o servidor SQL
public static void main(String arg[]){
// serveurSQL serviço de porta da máquina
//: o número de argumentos está correto?
if(arg.length!=3){
System.err.println("Syntaxe : pg machineAnnuaireCorba portAnnuaireCorba nomService");
System.exit(1);
}
// recuperamos os argumentos
String machine=arg[0];
String port=arg[1];
String nomService=arg[2];
String[] initORB={"-ORBInitialHost",machine,"-ORBInitialPort",port};
try{
// precisamos de um objeto CORBA para trabalhar
ORB orb=ORB.init(initORB,null);
// colocamos o serviço no diretório de serviços
org.omg.CORBA.Object objRef=
orb.resolve_initial_references("NameService");
NamingContext ncRef=NamingContextHelper.narrow(objRef);
NameComponent nc= new NameComponent(nomService,"");
NameComponent path[]={nc};
// criamos o servidor e o associamos ao serviço srvSQL
SQLServant serveurSQL=new SQLServant();
ncRef.rebind(path,serveurSQL);
orb.connect(serveurSQL);
// acompanhamento
System.out.println("Serveur SQL prêt");
// aguardando solicitações dos clientes
java.lang.Object sync=new java.lang.Object();
synchronized(sync){
sync.wait();
}
} catch(Exception e){
// ocorreu um erro
System.err.println("Erreur " + e);
e.printStackTrace(System.err);
}
}// manual
}// srvSQL
Compilamos essa nova classe:
E:\data\java\corba\sql>j:\jdk12\bin\javac serveurSQL.java
E:\data\java\corba\sql>dir *.class
SQLSER~1 CLA 2 568 19/03/99 10:19 SQLServant.class
SERVEU~1 CLA 1 800 19/03/99 10:33 serveurSQL.class
10.3.6. Gravação do cliente
O cliente do servidor CORBA é chamado com os seguintes parâmetros:
máquina: máquina onde está localizado o diretório de serviços CORBA
porta: porta na qual esse diretório opera
nomService: nome do serviço SQL
driver: driver que o servidor SQL deve utilizar para gerenciar o banco de dados desejado
urlBase: URL JDBC do banco de dados a ser gerenciado
id: identidade do cliente ou null se não houver identidade
mdp: senha do cliente ou nulo se não houver senha
separador: caractere que o servidor SQL deve usar para separar os campos das linhas de resultados de uma consulta
Aqui está um exemplo de parâmetros possíveis:
onde:
máquina: máquina na qual está localizado o diretório de serviços CORBA
porta: porta na qual esse diretório opera
srvSQL: srvSQL, nome CORBA do servidor SQL
driver: sun.jdbc.odbc.JdbcOdbcDriver, o driver padrão para bancos de dados com interface ODBC
urlBase: jdbc:odbc:articles, para utilizar um banco de dados “articles” declarado na lista de bancos de dados públicos ODBC da máquina Windows
id: null, sem identificação
senha: null, sem senha
separador: , os campos dos resultados serão separados por uma vírgula
Uma vez iniciado com os parâmetros anteriores, o cliente segue as etapas a seguir:
- ele se conecta à máquina machine na porta port para solicitar o serviço CORBA srvSQL
- solicita a conexão com o banco de dados de artigos
- solicita que o usuário digite uma consulta SQL no teclado
- ela a envia ao servidor SQL
- exibe na tela os resultados retornados pelo servidor
- ele solicita novamente que o usuário digite uma consulta SQL no teclado. Ele será encerrado quando a consulta terminar.
Segue-se o código Java do cliente. Os comentários devem ser suficientes para sua compreensão. Observa-se que:
- o código é idêntico ao do cliente RMI já estudado. Ele difere neste último pelo processo de solicitação do serviço ao diretório, processo isolado no método getServeurSQL.
- O método getServeurSQL é idêntico ao escrito para o cliente de eco
Portanto, vemos que:
- um cliente CORBA difere de um cliente RMI apenas pela forma como se conecta ao servidor
- essa forma é idêntica para todos os clientes CORBA
import srvSQL.*;
import org.omg.CosNaming.*;
import org.omg.CORBA.*;
import java.io.*;
public class clientSQL {
// dados globais da classe
private static String syntaxe =
"syntaxe : cltSQL machine port service pilote urlBase id mdp separateur";
private static BufferedReader in=null;
private static interSQL serveurSQL=null;
public static void main(String arg[]){
// sintaxe: cltSQL máquina porta separador driver URL ID senha
// máquina porta: máquina e porta do diretório de serviços CORBA a ser contatado
// serviço: nome do serviço
// driver: driver a ser utilizado para o banco de dados a ser consultado
// urlBase: URL JDBC do banco de dados a ser utilizado
// id: identificação do usuário
// mdp: sua senha
// separador: string que separa os campos nos resultados de uma consulta
// verificação do número de argumentos
if(arg.length!=8)
erreur(syntaxe,1);
// inicialização dos parâmetros de conexão com o banco de dados
String machine=arg[0];
String port=arg[1];
String service=arg[2];
String pilote=arg[3];
String urlBase=arg[4];
String id, mdp, separateur;
if(arg[5].equals("null")) id=""; else id=arg[5];
if(arg[6].equals("null")) mdp=""; else mdp=arg[6];
if(arg[7].equals("null")) separateur=" "; else separateur=arg[7];
// parâmetros do serviço de diretório CORBA
String[] initORB={"-ORBInitialHost",arg[0],"-ORBInitialPort",arg[1]};
// cliente CORBA — solicitando uma referência do servidor SQL
interSQL serveurSQL=getServeurSQL(machine,port,service);
// diálogo cliente-servidor
String requete=null;
String reponse=null;
String[] lignes=null;
String codeErreur=null;
try{
// abertura do fluxo do teclado
in=new BufferedReader(new InputStreamReader(System.in));
// rastreamento
System.out.println("--> Connexion à la base de données en cours");
// solicitação de conexão inicial ao banco de dados
reponse=serveurSQL.connect(pilote,urlBase,id,mdp);
// acompanhamento
System.out.println("<-- "+reponse);
// análise da resposta
codeErreur=reponse.substring(0,3);
if(codeErreur.equals("500"))
erreur("Abandon sur erreur de connexion à la base",3);
// ciclo de leitura das consultas a serem enviadas ao servidor SQL
System.out.print("--> Requête : ");
requete=in.readLine().toLowerCase().trim();
while(! requete.equals("fin")){
// envio da solicitação ao servidor e recebimento da resposta
lignes=serveurSQL.executeSQL(requete,separateur);
// acompanhamento
afficheLignes(lignes);
// próxima solicitação
System.out.print("--> Requête : ");
requete=in.readLine().toLowerCase().trim();
}// while
// acompanhamento
System.out.println("--> Fermeture de la connexion à la base de données distante");
// encerramento da conexão
reponse=serveurSQL.close();
// próximo
System.out.println("<-- " + reponse);
// fim
System.exit(0);
// gestão de erros
} catch (Exception e){
erreur("Abandon sur erreur : " + e,2);
}// tentar
}// main
// ----------- AfficheLignes
private static void afficheLignes(String[] lignes){
for (int i=0;i<lignes.length;i++)
System.out.println("<-- " + lignes[i]);
}// afficheLignes
// ------------ erro
private static void erreur(String msg, int exitCode){
// exibição da mensagem de erro
System.err.println(msg);
// possível liberação de recursos
try{
in.close();
serveurSQL.close();
} catch(Exception e){}
// saindo
System.exit(exitCode);
}// erro
// ---------------------- getServeurSQL
private static interSQL getServeurSQL(String machine, String port, String service){
// solicita uma referência do servidor SQL
// máquina: máquina do diretório de serviços CORBA
// porta: porta do diretório de serviços CORBA
// serviço: nome do serviço CORBA a ser solicitado
// acompanhamento
System.out.println("--> Connexion au serveur CORBA en cours...");
// a referência do servidor SQL
interSQL serveurSQL=null;
// parâmetros do serviço de diretório CORBA
String[] initORB={"-ORBInitialHost",machine,"-ORBInitialPort",port};
try{
// solicita-se um objeto CORBA para trabalhar — para isso, utiliza-se a porta
// de escuta do diretório de serviços CORBA
ORB orb=ORB.init(initORB,null);
// utiliza-se o serviço de diretório para localizar o servidor SQL
org.omg.CORBA.Object objRef=
orb.resolve_initial_references("NameService");
NamingContext ncRef=NamingContextHelper.narrow(objRef);
// o serviço procurado se chama srvSQL — solicitamos esse serviço
NameComponent nc= new NameComponent(service,"");
NameComponent path[]={nc};
serveurSQL=interSQLHelper.narrow(ncRef.resolve(path));
} catch (Exception e){
System.err.println("Erreur lors de la localisation du serveur SQL ("
+ e + ")");
System.exit(10);
}// try-catch
// retornamos a referência ao servidor
return serveurSQL;
}// getServeurSQL
}// classe
Vamos compilar a classe do cliente:
E:\data\java\corba\sql>j:\jdk12\bin\javac clientSQL.java
E:\data\java\corba\sql>dir *.class
SQLSER~1 CLA 2 568 19/03/99 10:19 SQLServant.class
SERVEU~1 CLA 1 800 19/03/99 10:33 serveurSQL.class
CLIENT~1 CLA 3 774 19/03/99 10:45 clientSQL.class
Estamos prontos para os testes.
10.3.7. Testes
10.3.7.1. Pré-requis
Supõe-se que um banco de dados chamado “Artigos” esteja disponível publicamente na máquina Windows do servidor:

Essa base de dados tem a seguinte estrutura:
nome | tipo |
code | código do artigo com 4 caracteres |
nom | nome (sequência de caracteres) |
prix | seu preço (real) |
stock_actu | seu estoque atual (número inteiro) |
stock_mini | o estoque mínimo (número inteiro) abaixo do qual é necessário reabastecer o item |
10.3.7.2. Lançamento do serviço de diretório
E:\data\java\corba\sql>start j:\jdk12\bin\tnameserv -ORBInitialPort 1000
Le service d’annuaire est lancé sur le port 1000. Il affiche dans une fenêtre DOS quelque chose du genre :
Initial Naming Context:
IOR:000000000000002849444c3a6f6d672e6f72672f436f734e616d696e672f4e616d696e67436f
6e746578743a312e3000000000010000000000000030000100000000000a69737469612d30303900
052800000018afabcafe000000027693d3fd000000080000000000000000
TransientNameServer: setting port for initial object references to: 1000
10.3.7.3. Inicialização do servidor SQL
Iniciando o servidor SQL:
Ele exibe em uma janela DOS:
10.3.7.4. Inicialização de um cliente na mesma máquina que o servidor
Aqui estão os resultados obtidos com um cliente na mesma máquina que o servidor:
E:\data\java\corba\sql>j:\jdk12\bin\java clientSQL localhost 1000 srvSQL sun.jdbc.odbc.JdbcOdbcDriver jdbc:odbc:articles null null ,
--> Connexion au serveur CORBA en cours...
--> Connexion à la base de données en cours
<-- 200 Connexion réussie
--> Consulta: select nome, stock_actu, stock_mini from artigos
<-- 101 vélo,31,8
<-- 101 arc,9,8
<-- 101 canoé,7,7
<-- 101 fusil,9,8
<-- 101 skis nautiques,13,8
<-- 101 essai3,13,9
<-- 101 cachalot,6,6
<-- 101 léopard,7,7
<-- 101 panthère,7,7
--> Consulta: delete from artigos where stock_mini<7
<-- 100 1
--> Consulta: selecione nome, stock_actu, stock_mini da tabela artigos
<-- 101 vélo,31,8
<-- 101 arc,9,8
<-- 101 canoé,7,7
<-- 101 fusil,9,8
<-- 101 skis nautiques,13,8
<-- 101 essai3,13,9
<-- 101 léopard,7,7
<-- 101 panthère,7,7
--> Requête : fin
--> Fermeture de la connexion à la base de données distante
<-- 200 Base fermée
10.3.7.5. Execução de um cliente em uma máquina diferente daquela do servidor
Aqui estão os resultados obtidos com um cliente em uma máquina diferente daquela do servidor:
E:\data\java\corba\sql>j:\jdk12\bin\java clientSQL tahe.istia.univ-angers.fr 1000 srvSQL sun.jdbc.odbc.JdbcOdbcDriver jdbc:odbc:articles null null ,
--> Connexion au serveur CORBA en cours...
--> Connexion à la base de données en cours
<-- 200 Connexion réussie
--> Consulta: select * from artigos
<-- 101 a300,v_lo,1202,31,8
<-- 101 d600,arc,5000,9,8
<-- 101 d800,canoé,1502,7,7
<-- 101 x123,fusil,3000,9,8
<-- 101 s345,skis nautiques,1800,13,8
<-- 101 f450,essai3,3,13,9
<-- 101 z400,léopard,500000,7,7
<-- 101 g457,panthère,800000,7,7
--> Requête : fin
--> Fermeture de la connexion à la base de données distante
<-- 200 Base fermée
10.4. Correspondências entre IDL e JAVA
Apresentamos aqui as correspondências entre os tipos simples IDL e JAVA:
tipo IDL | tipo Java |
boolean | boolean |
char | char |
wchar | char |
octet | byte |
string | java.lang.String |
wstring | java.lang.String |
short | short |
unsigned short | short |
long | int |
long sem sinal | int |
long long | long |
longo sem sinal | long |
float | float |
double | double |
Vale lembrar que, para definir uma matriz de elementos do tipo T na interface IDL, utiliza-se a instrução:
e que, em seguida, utiliza-se nomType para referenciar o tipo da matriz. Assim, na interface do servidor SQL, utilizou-se a declaração:
para que resultats indique uma matriz String[] em Java.