SlideShare uma empresa Scribd logo
1 de 14
Baixar para ler offline
- 1 -
Integração de sistemas com XML-RPC
Miguel Henley Filho
Vivemos hoje em um mundo onde a necessidade de integração de aplicações
é constante. Sistemas escritos, há anos ou até mesmo décadas, em linguagens
diferentes, sendo suportados por sistemas operacionais também diferentes
fazem parte da realidade nas empresas. Muitas vezes a integração entre esses
sistemas torna-se indispensável.
O objetivo desse artigo é apresentar a tecnologia XML-RPC como solução de
integração entre ambientes diferentes. O plano é configurarmos um ambiente
onde teremos Java como plataforma de serviços e, como client, aplicativos em
Delphi Win32 e .NET para consumi-los. O ambiente foi assim escolhido por ter
uma boa aproximação da realidade. A linguagem Java foi escolhida como
servidora por possuir um rico ecossistema com milhares de frameworks para
todos os gostos e necessidades bem como possuir a característica de
independência de plataforma, o que nos permitirá rodar o servidor em qualquer
sistema operacional que possua uma máquina virtual Java instalada (Windows,
Linux, Solaris, Mac OS, entre muitos outros).
O que é RPC?
RPC é o acrônimo de Remote Procedure Call, isto é, chamada remota de
procedimentos tendo sido criado há muito tempo, mesmo antes do nascimento
da internet. Veja a expressão abaixo:
resultado := computar(x, y, z, w)
Não seria maravilhoso se a chamada a essa função fosse totalmente
transparente para o desenvolvedor, não importando se a computação fosse
realizada no próprio hardware, no servidor da sala ao lado ou até mesmo em
um servidor do outro lado do mundo? Esse é o objetivo do RPC. Além disso,
não importa para o client nem para o server quais plataformas estão sendo
utilizadas em cada uma das pontas – essa informação é totalmente irrelevante.
Um desenvolvedor baixo nível poderá argumentar que com as linguagens de
hoje em dia um servidor poderia ser construído do zero para oferecer os
serviços a que ele se propõe, com relativa facilidade. Isso é verdade, mas
algumas coisas muito feias teriam que ser programadas como: threads, pool de
threads, sockets, segurança, chamadas assíncronas, entre outros. O esforço
seria muito grande uma vez que já temos disponível, em especificação e
implementação, a tecnologia XML-RPC.
A parte XML que prefixa a tecnologia XML-RPC é devido a troca de
mensagens entre o client e o server (solicitação – resposta) ser feita em um
- 2 -
vocabulário XML. Trata-se de uma especificação pública que qualquer um pode
implementar. Obviamente não estamos pensando em implementar essa
especificação pois outros já o fizeram. Implementações disponíveis existem
para muitas plataformas / linguagens: .NET, Java, Delphi, C++, Perl, Python,
Groovy, PHP, etc...
Em resumo, XML-RPC é uma especificação e um conjunto de implementações
que permitem sistemas realizarem chamadas a procedimentos remotos com
independência total da tecnologia utilizada, utilizando o conhecido protocolo
http como camada de transporte.
Uma das vantagens da utilização do http como transporte é tornar a
comunicação, entre os processos, simples evitando também dores de cabeça
com configurações de políticas em firewalls – quem já trabalhou com objetos
distribuídos utilizando CORBA ou DCOM 1sabe do que eu estou falando!
A figura 1 mostra o funcionamento básico do XML-RPC.
Figura 1
1
Aqui faço a comparação de CORBA / DCOM com XML-RPC somente no aspecto de leveza e facilidade
de configuração, pois são tecnologias muito diferentes.
- 3 -
No nosso exemplo simples de uma chamada a função computar (x, y, z, w), o
mecanismo XML-RPC, do lado client, montaria o seguinte trecho XML enviando
em seguida para o servidor. No exemplo x = 10, y = 20, z = 30 e w = 40:
--- cabeçalho (preâmbulo) ---
POST /rpchandler…
Host: laplace.exemplo.com…
Content-Type: text/xml
Content-Length: …
---
<?xml version="1.0"?>
<methodCall>
<methodName>app.Computar</methodName>
<params>
<param>
<value><i4>10</i4></value>
<value><i4>20</i4></value>
<value><i4>30</i4></value>
<value><i4>40</i4></value>
</param>
</params>
</methodCall>
Listagem 1 – solicitação do client
No lado do servidor, após o parser do xml, a implementação faz o dispatcher
para o método computar, enviando os parâmetros x, y, z e w. Depois da
execução é montado uma reposta também xml e retornado ao client. Veja um
trecho de resposta possível – aqui supondo que computar realiza o somatório
dos parâmetros.
--- cabeçalho (omitido) ---
<?xml version="1.0"?>
<methodResponse>
<params>
<param>
<value><i4>100</i4></value>
</param>
</params>
</methodResponse>
</xml>
Listagem 2 – resposta do servidor
Cabe agora o client realizar o parser e obter a informação de resposta
desejada.
Tudo isso é feito em background e o desenvolvedor não precisa se preocupar
com detalhes de conexões, analisadores xml e dispatchers - salvo se quiser
implementar a especificação em sua linguagem favorita – uma boa atividade
para um final de semana chuvoso! Nesse caso é só baixar a especificação do
site http://www.xml-rpc.com
O padrão XML-RPC define alguns tipos de dados que podem ser trafegados
entre clientes e servidores. Na listagem 1 vemos tags <i4>. Esse elemento (i4)
representa os números inteiros na especificação – nossa hipotética função,
- 4 -
computar, precisa de 4 parâmetros inteiros!. A tabela 1 mostra os demais tipos.
Além desses tipos, estruturas e arrays também são suportados.
Tipo Tag XML Significado
Inteiro <i4> ou <int> Inteiro de 4 bytes com sinal = 32 bits
Double <double> Número de ponto flutuante
String <string> String
DateTime <datetime.iso8601> Data no padrão ISO 8601:
yyyymmddThh:mm:SS
Boolean <boolean> 1 = true, 0 = false
Byte[] <base64> Array de bytes
Tabela 1 – tipos suportados pelo XML-RPC
Devido ao XML-RPC utilizar o protocolo http como transporte, as comunicações
entre clientes e servidores são síncronas e sem estado (stateless). A
comunicação é dita síncrona quando há um “bloqueio” temporário do client
enquanto o server faz o processamento do que foi pedido – o client fica
bloqueado, esperando a resposta. Sem estado significa que nenhum estado é
guardado no servidor quando a resposta é retornada – pois a conexão é
sempre fechada quando a resposta é enviada ao cliente.
Mesmo assim é possível contornar com alguma facilidade esses “problemas”.
No caso de ser necessária uma chamada assíncrona, onde o client não precisa
ficar bloqueado, algumas implementações possuem formas de realizar isso – o
truque é enviar a solicitação ao servidor em um novo thread e programar um
método de callback para tratar o resultado. Para contornar a natureza stateless
a solução é um pouco mais braçal pois o server terá que ser programado para,
de alguma forma, identificar o client em chamadas subseqüentes e, além disso,
permitir armazenar as variáveis dos vários clients em zonas de memória
separadas – emulando sessões. Nesse caso uma estratégia de limpeza deve
existir e ser executada de tempos em tempos de acordo com um timeout
estabelecido.
Vamos ao lado server
Para tornar nosso cenário um pouco mais interessante vamos montar um
servidor com algumas funcionalidades não triviais - um exemplo do tipo “alô,
mundo” não seria nada atrativo. Então vamos oferecer os seguintes serviços.
a) Serviço para formatar um nome
Recebe como parâmetro um nome não formatado e o formata colocando
em letras maiúscula as inicias de cada nome e o resto em letras
minúsculas. Retira também o excesso de espaços em branco entre os
nomes. Por exemplo:
AlBeRtO SantoS DUMONT -> Alberto Santos Dumont
josé mauRo De vasconcelos -> José Mauro de Vasconcelos
b) Serviço para calcular o número PI (π) com qualquer quantidade de
casas decimais.
- 5 -
Recebe como parâmetro a quantidade de casas decimais para cálculo e
retorna um String contendo o resultado. Para os que não se lembram o
valor de PI pertence aos números irracionais e representa o quociente
entre o perímetro de uma circunferência e seu diâmetro. Não é possível
realizar esse cálculo com tipos primitivos de linguagem como double,
real ou extended - esses tipos tem precisão limitada.
c) Serviço para obter as fases da lua em um determinado mês.
Recebe como parâmetro o mês e o ano e retorna as fases da lua numa
escala inteira de 0 a 7. Sendo 4 a fase de lua cheia e 0 a fase de lua
nova – os outros números são os intermediários entre essas duas fases.
d) Serviço para fazer um post no twitter.
Recebe como parâmetro o login, senha e um status com no máximo 140
caracteres e realiza o post. Para isso, obviamente teremos que ter uma
conexão ativa com a Internet no lado server.
Essas implementações não poderiam ser programadas todas em Delphi e
colocadas dentro de um único executável? Sim, claro que poderiam. Só que
teríamos que colocar na balança se seria economicamente viável fazer o porte
– cada caso é um caso. Certa vez tive que realizar um trabalho de
programação baseada em regras utilizando lógica fuzzy. Programar essa lógica
do zero seria uma loucura e muito caro devido ao cronograma apertado e, não
existia (pelo menos na época) uma solução pronta para Delphi. A saída foi
utilizar uma biblioteca (das muitas existentes) em Java para lógica difusa. A
exceção do motor de inferência (que rodava no server), todo o sistema foi
desenvolvido em Delphi e a comunicação entre os processos feita em XML-
RPC.
Voltando ao nosso servidor, não vamos aqui dissecar a implementação de
cada um desses serviços. O Servidor é em Java e afinal de contas não
estamos na ActiveJava! Caso o micro que hospedará o servidor não tenha uma
máquina virtual Java (JVM) instalada é só baixar o JDK (Java Development Kit)
do JavaSE direto do site da Sun (http://www.javasoft.com) e fazer a instalação
que é muito simples (aquelas do tipo Next, Next, ..., Finish). Se o micro já
possuir uma JVM apenas assegure de que esteja instalado no mínimo o JDK
versão 5.
Depois disso faça o download do código fonte desse artigo e instale em uma
pasta qualquer, digamos: c:artigo_xmlrpc. Essa pasta possui tanto o lado
server como o lado client (cada um em sua respectiva subpasta). A
organização dos fontes do artigo está assim configurada:
client
dotNET
Win32
server
class
lib
src
build.xml
carga.bat
- 6 -
Dentro da pasta server, há um arquivo chamado carga.bat – edite esse arquivo
e onde se encontra a linha:
SET JAVA_HOME=......
Complete com o diretório onde foi instalado o JDK. Por exemplo, no meu micro:
SET JAVA_HOME=C:jdk1.6.0_11
Depois disso é só executar o script carga.bat – tenha preferência em executar
a partir da linha de comando pois caso venha a ocorrer algum erro na carga do
servidor, será mais fácil de visualizar. Veja a seqüência no console:
c:> cdartigo_xmlrpcserver [ Enter ]
c:artigo_xmlrpcserver> carga.bat [ Enter ]
.
.
Iniciando o servidor XML-RPC na porta 8081
Servidor pronto para integração ...
(Ctrl-C) para interromper
Pronto, nosso servidor já está pronto para receber as solicitações em XML-
RPC. Nota: Coloquei o servidor para ficar escutando uma porta diferente da
porta 80 (que é a padrão para http). Isso para não ocorrer conflito, pois nos
dias de hoje é raro um micro não ter um serviço http:80 iniciado. Se for
desejado modificar o numero da porta é só editar novamente o script carga.bat
e alterar o valor em SET SERVER_PORT. Essa variável está com 8081 por
default. Caso o leitor queira realizar a (re)compilação do servidor, é fornecido o
arquivo build.xml que deverá ser utilizado com a ferramenta ant.
Vamos ao lado Client
Aqui retornamos ao velho e bom Delphi. Primeiro vamos montar um client em
.NET utilizado o Delphi Prism e depois um client Win32 utilizando o Delphi 7.
Os dois clients irão consumir todos os serviços expostos no server.
Começamos primeiro com .NET
Client .NET
O client em .NET foi desenvolvido com Delphi Prism. A plataforma .NET não
possui suporte nativo a XML-RPC. Uma boa implementação é a XML-
RPC.NET e é preciso fazer o download em http://www.xml-rpc.net. A última
versão – até o momento que escrevo – é a 2.4.0. Baixe o arquivo xml-
rpc.net.2.4.0.zip e faça a descompactação em uma pasta qualquer. Localize na
descompactação a montagem CookComputing.XmlRpcV2.dll. Vamos precisar
somente dela. Os outros arquivos não são necessários, pois são constituídos
de: montagem para o Compact Framework, montagem para o NET Framework
versão 1, códigos fontes e testes – isso não nos interessa.
Devido a funcionalidade de reflexão presente na plataforma .NET, a
programação de um client é muito simples. Precisamos declarar uma interface
contendo os procedimentos remotos – listagem 3.
- 7 -
Type
[XmlRpcUrl('http://localhost:8081')]
IServicos = public interface
[XmlRpcMethod('app.formatarNome')]
method FormatarNome(s: String): String;
[XmlRpcMethod('app.obterPI')]
method ObterPI(casasDecimais: Integer): String;
[XmlRpcMethod('app.obterFasesLua')]
method ObterFasesLua(mes: integer; ano: integer): array of String;
[XmlRpcMethod('app.postarTwitter')]
method PostarTwitter(login: String; senha: String; post:String): String;
end;
Listagem 3 – declaração da interface no Delphi Prism
Repare que utilizamos atributos tanto no nível de interface quanto na
declaração dos métodos. Isso é necessário para que a reflexão seja feita e
uma classe proxy – que implementa essa interface – seja automaticamente
criada em tempo de execução.
O atributo XmlRpcUrl que aparece próximo a interface tem como parâmetro o
endereço (endpoint) do servidor. Já o atributo XmlRpcMethod que aparece em
cada método possui como parâmetro o nome do método correspondente
totalmente qualificado no server – o nome do método na interface pode ser
diferente do server, mas os tipos de parâmetros e de retorno devem ser iguais.
A qualificação tem o formato: namespace.procedimento. Daqui a pouco
falaremos mais sobre o objetivo do namespace.
O próximo passo é fazermos a chamada a esses procedimentos remotos. Para
que isso seja possível utilizamos a classe genérica XmlRpcProxyGen –
precisamos passar na construção desse objeto o nome da interface. Assim:
var servicos := XmlRpcProxyGen.Create<IServicos>;
Como podemos ver, a implementação da interface IServicos, que declaramos,
é feita de forma totalmente automática pelo XML-RPC.NET
E agora? Agora é só chamar os métodos:
var resultado :=
servicos.FormatarNome(‘douGlas aDamS’); // retorna Douglas Adams
var pi :=
servicos.ObterPI(35); // retorna 3.14159265358979323846264338327950288
.
.
var postar :=
servicos.PostarTwitter(‘mhenley@datamag.com.br’, ‘senha’, ‘Ola!!!’);
Obviamente a montagem CookComputing.XmlRpcV2.dll precisa ser
referenciada no Visual Studio (figura 2) – para isso basta clicar com o botão
- 8 -
direito no item References do projeto, no Solution Explorer, localizar e adicionar
a montagem à solução. As classes e interfaces estão contidas no namespace
‘CookComputing.XmlRpc’ dessa montagem. Para simplificar o client .NET foi
construído uma aplicação do tipo Console Application.
E isso é realmente tudo que precisamos fazer. A listagem 4 apresenta o código
fonte do client .NET e, na figura 3 temos a aplicação rodando.
namespace ClientPrism;
interface
uses
CookComputing.XmlRpc,
System.Text;
type
ConsoleApp = class
public
class method Main;
end;
implementation
class method ConsoleApp.Main;
begin
var servicos := XmlRpcProxyGen.Create<IServicos>;
// chama método remoto FormatarNome
Console.WriteLine();
Console.WriteLine('Método FormatarNome:');
var naoFormatado := 'mIgUeL hEnLEY';
var formatado := servicos.FormatarNome(naoFormatado);
Console.WriteLine('Nao formatado: ' + naoFormatado);
Console.WriteLine('Formatado : ' + formatado);
Console.WriteLine('------------------------------------------------');
Console.WriteLine();
// chama método para calcular o numero PI
Console.WriteLine('Método ObterPI: (50 casas decimais)');
var pi := servicos.ObterPI(50);
Console.WriteLine('Pi = ' + pi);
Console.WriteLine('------------------------------------------------');
Console.WriteLine();
// chama método para obter as fases da lua:
Console.WriteLine('Método ObterFasesLua: (maio/2009)');
Console.WriteLine('formato dia/mes/ano=[0-7]. 0=lua cheia, 7=lua nova');
Console.WriteLine();
var mes := 5;
var ano := 2009;
var fases := servicos.ObterFasesLua(mes, ano);
var strbuilder := new StringBuilder();
var dia := 1;
for each fase: String in fases do
begin
if dia <> 1 then
strbuilder.append('; ');
strbuilder.append(dia).
append('/').append(mes).append('/').append(ano).append('=').
append(fase);
inc(dia);
end;
- 9 -
Console.WriteLine(strbuilder);
Console.WriteLine('------------------------------------------------');
Console.WriteLine();
{
// chama método para fazer um post no Twitter:
Console.WriteLine('Fazendo um post no twitter');
var ok := servicos.PostarTwitter('login-twitter', 'senha-twitter', 'bla');
}
Console.WriteLine();
Console.ReadLine();
end;
end.
Listagem 4
Figura 2 – montagem referenciada no Visual Studio
Figura 3
- 10 -
Client Win32
Da mesma forma que acontece na plataforma .NET temos que baixar uma
implementação XML-RPC para Delphi Win32. Uma implementação é a
biblioteca Delphi XML-RPC que pode ser baixada do site sourceforge:
http://sourceforge.net/projects/delphixml-rpc/. Não é necessário baixar, pois o
download do artigo já contem essa biblioteca dentro da pasta
clientwin32dxmlrpc.
A última versão é a 2.0.0 e não há novos releases desde 2003. Essa versão
está homologada para Delphi 7 e faz uso dos componentes Indy para
comunicação TCP/IP. Como houve mudanças das API’s entre a versão 9 e 10
do Indy, versões posteriores do Delphi que utilizam o Indy 10 terão problema
para utilizar o Delphi XML-RPC. A solução é fazer as alterações “na unha” –
abrir o código fonte da biblioteca, sair à caça das chamadas ao Indy 9 e
adaptá-las ao Indy 10 – uma dica para quem quiser se aventurar nesse terreno
é utilizar alguns patterns2 como forma de isolamento entre o Delphi XML-RPC e
o Indy – isso facilitará futuros portes para novas versões do Indy que vierem a
aparecer.
A API do Delphi XML-RPC para o perfil client também é muito simples de
utilizar, porém, a configuração é um pouco mais trabalhosa em função da falta
do artifício de reflexão no Delphi Win32. Basicamente trabalhamos com uma
classe e duas interfaces: A classe TRpcCaller é a responsável por configurar o
endpoint (servidor + porta) e disparar a chamada ao servidor. O principal
método dessa classe é o Execute que recebe como parâmetro uma interface
do tipo IRpcFunction e retorna uma interface do tipo IRpcResult.
A interface IRpcFunction é implementada pela classe TRpcFunction e seus
principais métodos / propriedades são:
ObjectMethod – propriedade read/write que permite especificar o nome do
procedimento (totalmente qualificado) a ser executado no server.
AddItem – Métodos sobrecarregados que tem como objetivo “setar” os
parâmetros para o procedimento a ser executado no server. Exemplo: Se o
procedimento requer dois parâmetros: um string e um inteiro, as chamadas
devem ser assim realizadas (supondo obj um objeto do tipo IRpcFuntion):
obj.AddItem(‘parametro-string’); obj.AddItem(100); Também há sobrecarga
desse método para parâmetros do tipo array e estruturas.
Os principais métodos / propriedades da interface IRpcResult (resultado da
execução) são:
IsError – método que retorna um boolean. Retorna true se houve um erro no
server. (O procedimento pode – e deve - lançar exception caso alguma pré-
condição esteja fora do domínio de atuação).
2
Os patterns Factory e Adapter são bons candidatos para isso.
- 11 -
ErroMsg – propriedade read only que contem o erro lançado pelo servidor.
IsArray / IsBase64 / IsDate / IsFloat / IsInteger / IsString / IsStrut /
IsBoolean / IsBase64 – retorna true ou false se o resultado for um desses
tipos.
AsString / AsInteger / AsDateTime / AsBoolean / AsBase64 / AsArray /
AsStruct / AsFloat – para cada uma dessas propriedades retorna o seu
respectivo tipo (String, Integer, DateTime, ...). No caso do retorno ser um array,
a propriedade AsArray retorna a interface IRpcArray que possui formas de
obter os elementos através de índices. O retorno também pode ser uma
estrutura.
Conforme dito no inicio do artigo, a especificação XML-RPC também suporta
arrays e estruturas. Tipos array são muito úteis quando temos que passar para
o procedimento parâmetros multivalores – um procedimento remoto poderia
estar programado para calcular a média tendo como parâmetro de entrada um
array de double. Da mesma forma, o retorno do método pode ser multivalorado
– inclusive o servidor desse artigo oferece o método/procedimento
obterFasesLua que retorna um array de String contendo as fases da lua para
cada dia do mês. (nota: A especificação XML-RPC não impõe a
obrigatoriedade dos elementos de um array ser do mesmo tipo – podem ser de
tipos distintos – é possível misturar String com Array com Boolean com Array
de Array, etc...).
Quanto às estruturas, elas permitem um dicionário com armazenamento de
chave de busca ser trafegado entre o client e o server. Por exemplo, na
necessidade de trafegar uma estrutura como endereço (endereço, uf, cep, ...)
podemos utilizar a classe TRpcStruct - que implementa a interface
IRpcStruct. Essa interface possui métodos AddItem sobrecarregados – veja
na listagem 5 um trecho da interface extraído diretamente da unit
XmlRpcTypes.pas. Objetos do tipo IRpcStruct podem ser passados como
parâmetro para o método AddItem da interface IRpcFunction descrita
anteriormente.
type
…
IRpcStruct = interface(IInterface)
…
procedure AddItem(const Key: string; Value: Integer); overload;
procedure AddItem(const Key: string; const Value: string); overload;
procedure AddItem(const Key: string; Value: Double); overload;
procedure AddItem(const Key: string; Value: Boolean); overload;
procedure AddItem(const Key: string; Value: IRpcArray); overload;
procedure AddItem(const Key: string; Value: IRpcStruct); overload;
…
end;
Listagem 5 – parte da definição da interface IRpcStruct
A figura 4 mostra a interface visual do client Win32. Cada aba faz o acesso a
uma funcionalidade no servidor. A figura 5 mostra uma postagem no Twitter
realizada pelo client.
- 12 -
Figura 4 – interface client Win32
Figura 5 – postagem no Twitter
- 13 -
Voltando ao lado server
Vamos falar mais um pouco da implementação do servidor.
O pacote que permite o Java utilizar o XML-RPC (tanto como server ou client) é
o que está sob os auspícios da Apache Software Foundation e pode ser
encontrado em http://ws.apache.org/xmlrpc/ . Há outras implementações por aí,
mas essa é o padrão de fato3 para Java. Também não é necessário baixar
esse pacote, pois já o colocamos no arquivo de download do artigo.
Basicamente o que precisamos é utilizar somente a classe WebServer contida
no pacote org.apache.xmlrpc. Veja abaixo, o código relevante, que inicializa o
servidor:
1 public static void main(String[] args) {
2 System.out.println("Iniciando o servidor XML-RPC ...");
3 int port = getPort(args[0]);
4 WebServer ws = new WebServer(port);
5 ws.addHandler("app", new Servicos());
6 ws.start();
7 System.out.println("Servidor pronto para integração");
8 }
A linha 4 cria uma instância da classe WebServer passando como parâmetro o
numero da porta de escuta.
A linha 5 adiciona um handle ao servidor - o primeiro parâmetro define o nome
do namespace (definimos como app – mas poderia ter sido qualquer outro) e, o
segundo parâmetro é uma instancia da classe que queremos expor os métodos
ao mundo externo - classe Servicos - assim definida:
public class Servicos {
public String formatarNome(String uglyFormat) { … }
public String obterPI(int casasDecimais) { … }
public String[] obterFasesLua(int mes, int ano) { … }
public String postarTwitter(String login, String senha, String post) { … }
}
A implementação de cada método foi omitida - o código fonte completo está no
download do artigo e o leitor mais interessado poderá apreciar a forma que
essa classe foi implementada. Aqui fica a explicação prometida quanto a
qualificação do método/procedimento: namespace.procedimento. Podemos ter
vários procedimentos com mesmo nome, mas não no mesmo namespace.
Entre a linha 5 e a linha 6 poderíamos adicionar um outro handle da seguinte
forma: ws.addHandler(“app2”, new MaisServicos()). Fazendo isso todos os
métodos definidos na classe MaisServicos também seriam expostos e
poderiam ser invocados, pelo client, prefixando-os com “app2.”.
3
Padrões de jure (legais) são os desenvolvidos por organismos de padronização como ISO, ABNT, IEEE.
Padrões de fato são os estabelecidos pelo mercado, por sua grande utilização: PDF, MPEG, Windows,
etc...
- 14 -
A classe WebServer possui também funcionalidades de restrição de acesso por
IP. Podemos bloquear ou permitir o acesso por um determinado endereço, ou
faixa de endereços. Os métodos: acceptClient, denyClient e setParanoid dão
conta do recado e são muito úteis devido a natureza do XML-RPC – lembre-se
que o protocolo http é inseguro acarretando em uma troca de mensagens “clear
text” entre o client e o server. Se a segurança for um requisito para o seu
projeto o servidor deverá ser instalado em um web-server habilitado para SSL –
nesse caso o lembre-se que o cliente também deverá “falar” SSL.
Conclusão
No mundo real (e imperfeito) onde uma diversidade muito grande de
linguagens e sistemas operacionais se faz presente, é inevitável a necessidade
de integração entre sistemas rodando em plataformas distintas. XML-RPC se
destaca pela eficiência, portabilidade e, por uma curva de aprendizado muito
pequena, sendo uma solução leve, aberta e adequada para interoperabilidade.
Miguel Henley Filho (miguel.henley@gmail.com) é engenheiro e desenvolvedor de soluções
corporativas em Delphi e Java nas plataformas Windows e Linux. Possui interesse em
arquitetura de software, objetos distribuídos e algoritmos computacionais de alto desempenho.

Mais conteúdo relacionado

Semelhante a Integração de Sistemas com XML-RPC

Arquitetura de Software - Performance, Layers e Domain Layer
Arquitetura de Software - Performance, Layers e Domain LayerArquitetura de Software - Performance, Layers e Domain Layer
Arquitetura de Software - Performance, Layers e Domain LayerAndré Faria Gomes
 
JustJava 2005: Web Services em Java com o JWSDP 1.5
JustJava 2005: Web Services em Java com o JWSDP 1.5JustJava 2005: Web Services em Java com o JWSDP 1.5
JustJava 2005: Web Services em Java com o JWSDP 1.5Helder da Rocha
 
Especificação de plataforma DaaS para laboratórios de informática
Especificação de plataforma DaaS para laboratórios de informática Especificação de plataforma DaaS para laboratórios de informática
Especificação de plataforma DaaS para laboratórios de informática Demis Gomes
 
Modelos TCP/IP e OSI para CCNA
Modelos TCP/IP e OSI para CCNAModelos TCP/IP e OSI para CCNA
Modelos TCP/IP e OSI para CCNAwolkartt_18
 
Unidade1ainternet 110928173442-phpapp02
Unidade1ainternet 110928173442-phpapp02Unidade1ainternet 110928173442-phpapp02
Unidade1ainternet 110928173442-phpapp02DP7
 
funcionamento da internet
funcionamento da internetfuncionamento da internet
funcionamento da internetMarco Pinheiro
 
ALM e Operações - Workshop - Como Diagnosticar um Incidente
ALM e Operações - Workshop - Como Diagnosticar um IncidenteALM e Operações - Workshop - Como Diagnosticar um Incidente
ALM e Operações - Workshop - Como Diagnosticar um IncidenteAlan Carlos
 
Arquitetura Funcional em Microservices
Arquitetura Funcional em MicroservicesArquitetura Funcional em Microservices
Arquitetura Funcional em MicroservicesNubank
 
REST vs GraphQL - A batalha das APIs.pdf
REST vs GraphQL - A batalha das APIs.pdfREST vs GraphQL - A batalha das APIs.pdf
REST vs GraphQL - A batalha das APIs.pdfBrunoAlbuquerque864673
 
Replicacao Object Sistemas
Replicacao Object SistemasReplicacao Object Sistemas
Replicacao Object Sistemastaniamaciel
 
Multithreaded tecnologia
Multithreaded tecnologia Multithreaded tecnologia
Multithreaded tecnologia J Chaves Silva
 
Tdc 2013 eric lemes - integracoes entre sistemas-2
Tdc 2013   eric lemes - integracoes entre sistemas-2Tdc 2013   eric lemes - integracoes entre sistemas-2
Tdc 2013 eric lemes - integracoes entre sistemas-2Eric Lemes
 
Phpjedi 090307090434-phpapp01 2
Phpjedi 090307090434-phpapp01 2Phpjedi 090307090434-phpapp01 2
Phpjedi 090307090434-phpapp01 2PrinceGuru MS
 
REST vs GraphQL - A batalha das APIs.pdf
REST vs GraphQL - A batalha das APIs.pdfREST vs GraphQL - A batalha das APIs.pdf
REST vs GraphQL - A batalha das APIs.pdfBrunoAlbuquerque864673
 

Semelhante a Integração de Sistemas com XML-RPC (20)

Arquitetura de Software - Performance, Layers e Domain Layer
Arquitetura de Software - Performance, Layers e Domain LayerArquitetura de Software - Performance, Layers e Domain Layer
Arquitetura de Software - Performance, Layers e Domain Layer
 
Ntop
NtopNtop
Ntop
 
Protocolos logicos de_comunicacao
Protocolos logicos de_comunicacaoProtocolos logicos de_comunicacao
Protocolos logicos de_comunicacao
 
JustJava 2005: Web Services em Java com o JWSDP 1.5
JustJava 2005: Web Services em Java com o JWSDP 1.5JustJava 2005: Web Services em Java com o JWSDP 1.5
JustJava 2005: Web Services em Java com o JWSDP 1.5
 
Trabalho t.a 2015
Trabalho t.a   2015Trabalho t.a   2015
Trabalho t.a 2015
 
Especificação de plataforma DaaS para laboratórios de informática
Especificação de plataforma DaaS para laboratórios de informática Especificação de plataforma DaaS para laboratórios de informática
Especificação de plataforma DaaS para laboratórios de informática
 
World Wide Web
World Wide WebWorld Wide Web
World Wide Web
 
Modelos TCP/IP e OSI para CCNA
Modelos TCP/IP e OSI para CCNAModelos TCP/IP e OSI para CCNA
Modelos TCP/IP e OSI para CCNA
 
Unidade1ainternet 110928173442-phpapp02
Unidade1ainternet 110928173442-phpapp02Unidade1ainternet 110928173442-phpapp02
Unidade1ainternet 110928173442-phpapp02
 
funcionamento da internet
funcionamento da internetfuncionamento da internet
funcionamento da internet
 
Novidades do .Net 4.0
Novidades do .Net 4.0Novidades do .Net 4.0
Novidades do .Net 4.0
 
ALM e Operações - Workshop - Como Diagnosticar um Incidente
ALM e Operações - Workshop - Como Diagnosticar um IncidenteALM e Operações - Workshop - Como Diagnosticar um Incidente
ALM e Operações - Workshop - Como Diagnosticar um Incidente
 
Arquitetura Funcional em Microservices
Arquitetura Funcional em MicroservicesArquitetura Funcional em Microservices
Arquitetura Funcional em Microservices
 
REST vs GraphQL - A batalha das APIs.pdf
REST vs GraphQL - A batalha das APIs.pdfREST vs GraphQL - A batalha das APIs.pdf
REST vs GraphQL - A batalha das APIs.pdf
 
Replicacao Object Sistemas
Replicacao Object SistemasReplicacao Object Sistemas
Replicacao Object Sistemas
 
Multithreaded tecnologia
Multithreaded tecnologia Multithreaded tecnologia
Multithreaded tecnologia
 
Tdc 2013 eric lemes - integracoes entre sistemas-2
Tdc 2013   eric lemes - integracoes entre sistemas-2Tdc 2013   eric lemes - integracoes entre sistemas-2
Tdc 2013 eric lemes - integracoes entre sistemas-2
 
Phpjedi 090307090434-phpapp01 2
Phpjedi 090307090434-phpapp01 2Phpjedi 090307090434-phpapp01 2
Phpjedi 090307090434-phpapp01 2
 
REST vs GraphQL - A batalha das APIs.pdf
REST vs GraphQL - A batalha das APIs.pdfREST vs GraphQL - A batalha das APIs.pdf
REST vs GraphQL - A batalha das APIs.pdf
 
Sistemas Distribuídos - Clusters
Sistemas Distribuídos - ClustersSistemas Distribuídos - Clusters
Sistemas Distribuídos - Clusters
 

Integração de Sistemas com XML-RPC

  • 1. - 1 - Integração de sistemas com XML-RPC Miguel Henley Filho Vivemos hoje em um mundo onde a necessidade de integração de aplicações é constante. Sistemas escritos, há anos ou até mesmo décadas, em linguagens diferentes, sendo suportados por sistemas operacionais também diferentes fazem parte da realidade nas empresas. Muitas vezes a integração entre esses sistemas torna-se indispensável. O objetivo desse artigo é apresentar a tecnologia XML-RPC como solução de integração entre ambientes diferentes. O plano é configurarmos um ambiente onde teremos Java como plataforma de serviços e, como client, aplicativos em Delphi Win32 e .NET para consumi-los. O ambiente foi assim escolhido por ter uma boa aproximação da realidade. A linguagem Java foi escolhida como servidora por possuir um rico ecossistema com milhares de frameworks para todos os gostos e necessidades bem como possuir a característica de independência de plataforma, o que nos permitirá rodar o servidor em qualquer sistema operacional que possua uma máquina virtual Java instalada (Windows, Linux, Solaris, Mac OS, entre muitos outros). O que é RPC? RPC é o acrônimo de Remote Procedure Call, isto é, chamada remota de procedimentos tendo sido criado há muito tempo, mesmo antes do nascimento da internet. Veja a expressão abaixo: resultado := computar(x, y, z, w) Não seria maravilhoso se a chamada a essa função fosse totalmente transparente para o desenvolvedor, não importando se a computação fosse realizada no próprio hardware, no servidor da sala ao lado ou até mesmo em um servidor do outro lado do mundo? Esse é o objetivo do RPC. Além disso, não importa para o client nem para o server quais plataformas estão sendo utilizadas em cada uma das pontas – essa informação é totalmente irrelevante. Um desenvolvedor baixo nível poderá argumentar que com as linguagens de hoje em dia um servidor poderia ser construído do zero para oferecer os serviços a que ele se propõe, com relativa facilidade. Isso é verdade, mas algumas coisas muito feias teriam que ser programadas como: threads, pool de threads, sockets, segurança, chamadas assíncronas, entre outros. O esforço seria muito grande uma vez que já temos disponível, em especificação e implementação, a tecnologia XML-RPC. A parte XML que prefixa a tecnologia XML-RPC é devido a troca de mensagens entre o client e o server (solicitação – resposta) ser feita em um
  • 2. - 2 - vocabulário XML. Trata-se de uma especificação pública que qualquer um pode implementar. Obviamente não estamos pensando em implementar essa especificação pois outros já o fizeram. Implementações disponíveis existem para muitas plataformas / linguagens: .NET, Java, Delphi, C++, Perl, Python, Groovy, PHP, etc... Em resumo, XML-RPC é uma especificação e um conjunto de implementações que permitem sistemas realizarem chamadas a procedimentos remotos com independência total da tecnologia utilizada, utilizando o conhecido protocolo http como camada de transporte. Uma das vantagens da utilização do http como transporte é tornar a comunicação, entre os processos, simples evitando também dores de cabeça com configurações de políticas em firewalls – quem já trabalhou com objetos distribuídos utilizando CORBA ou DCOM 1sabe do que eu estou falando! A figura 1 mostra o funcionamento básico do XML-RPC. Figura 1 1 Aqui faço a comparação de CORBA / DCOM com XML-RPC somente no aspecto de leveza e facilidade de configuração, pois são tecnologias muito diferentes.
  • 3. - 3 - No nosso exemplo simples de uma chamada a função computar (x, y, z, w), o mecanismo XML-RPC, do lado client, montaria o seguinte trecho XML enviando em seguida para o servidor. No exemplo x = 10, y = 20, z = 30 e w = 40: --- cabeçalho (preâmbulo) --- POST /rpchandler… Host: laplace.exemplo.com… Content-Type: text/xml Content-Length: … --- <?xml version="1.0"?> <methodCall> <methodName>app.Computar</methodName> <params> <param> <value><i4>10</i4></value> <value><i4>20</i4></value> <value><i4>30</i4></value> <value><i4>40</i4></value> </param> </params> </methodCall> Listagem 1 – solicitação do client No lado do servidor, após o parser do xml, a implementação faz o dispatcher para o método computar, enviando os parâmetros x, y, z e w. Depois da execução é montado uma reposta também xml e retornado ao client. Veja um trecho de resposta possível – aqui supondo que computar realiza o somatório dos parâmetros. --- cabeçalho (omitido) --- <?xml version="1.0"?> <methodResponse> <params> <param> <value><i4>100</i4></value> </param> </params> </methodResponse> </xml> Listagem 2 – resposta do servidor Cabe agora o client realizar o parser e obter a informação de resposta desejada. Tudo isso é feito em background e o desenvolvedor não precisa se preocupar com detalhes de conexões, analisadores xml e dispatchers - salvo se quiser implementar a especificação em sua linguagem favorita – uma boa atividade para um final de semana chuvoso! Nesse caso é só baixar a especificação do site http://www.xml-rpc.com O padrão XML-RPC define alguns tipos de dados que podem ser trafegados entre clientes e servidores. Na listagem 1 vemos tags <i4>. Esse elemento (i4) representa os números inteiros na especificação – nossa hipotética função,
  • 4. - 4 - computar, precisa de 4 parâmetros inteiros!. A tabela 1 mostra os demais tipos. Além desses tipos, estruturas e arrays também são suportados. Tipo Tag XML Significado Inteiro <i4> ou <int> Inteiro de 4 bytes com sinal = 32 bits Double <double> Número de ponto flutuante String <string> String DateTime <datetime.iso8601> Data no padrão ISO 8601: yyyymmddThh:mm:SS Boolean <boolean> 1 = true, 0 = false Byte[] <base64> Array de bytes Tabela 1 – tipos suportados pelo XML-RPC Devido ao XML-RPC utilizar o protocolo http como transporte, as comunicações entre clientes e servidores são síncronas e sem estado (stateless). A comunicação é dita síncrona quando há um “bloqueio” temporário do client enquanto o server faz o processamento do que foi pedido – o client fica bloqueado, esperando a resposta. Sem estado significa que nenhum estado é guardado no servidor quando a resposta é retornada – pois a conexão é sempre fechada quando a resposta é enviada ao cliente. Mesmo assim é possível contornar com alguma facilidade esses “problemas”. No caso de ser necessária uma chamada assíncrona, onde o client não precisa ficar bloqueado, algumas implementações possuem formas de realizar isso – o truque é enviar a solicitação ao servidor em um novo thread e programar um método de callback para tratar o resultado. Para contornar a natureza stateless a solução é um pouco mais braçal pois o server terá que ser programado para, de alguma forma, identificar o client em chamadas subseqüentes e, além disso, permitir armazenar as variáveis dos vários clients em zonas de memória separadas – emulando sessões. Nesse caso uma estratégia de limpeza deve existir e ser executada de tempos em tempos de acordo com um timeout estabelecido. Vamos ao lado server Para tornar nosso cenário um pouco mais interessante vamos montar um servidor com algumas funcionalidades não triviais - um exemplo do tipo “alô, mundo” não seria nada atrativo. Então vamos oferecer os seguintes serviços. a) Serviço para formatar um nome Recebe como parâmetro um nome não formatado e o formata colocando em letras maiúscula as inicias de cada nome e o resto em letras minúsculas. Retira também o excesso de espaços em branco entre os nomes. Por exemplo: AlBeRtO SantoS DUMONT -> Alberto Santos Dumont josé mauRo De vasconcelos -> José Mauro de Vasconcelos b) Serviço para calcular o número PI (π) com qualquer quantidade de casas decimais.
  • 5. - 5 - Recebe como parâmetro a quantidade de casas decimais para cálculo e retorna um String contendo o resultado. Para os que não se lembram o valor de PI pertence aos números irracionais e representa o quociente entre o perímetro de uma circunferência e seu diâmetro. Não é possível realizar esse cálculo com tipos primitivos de linguagem como double, real ou extended - esses tipos tem precisão limitada. c) Serviço para obter as fases da lua em um determinado mês. Recebe como parâmetro o mês e o ano e retorna as fases da lua numa escala inteira de 0 a 7. Sendo 4 a fase de lua cheia e 0 a fase de lua nova – os outros números são os intermediários entre essas duas fases. d) Serviço para fazer um post no twitter. Recebe como parâmetro o login, senha e um status com no máximo 140 caracteres e realiza o post. Para isso, obviamente teremos que ter uma conexão ativa com a Internet no lado server. Essas implementações não poderiam ser programadas todas em Delphi e colocadas dentro de um único executável? Sim, claro que poderiam. Só que teríamos que colocar na balança se seria economicamente viável fazer o porte – cada caso é um caso. Certa vez tive que realizar um trabalho de programação baseada em regras utilizando lógica fuzzy. Programar essa lógica do zero seria uma loucura e muito caro devido ao cronograma apertado e, não existia (pelo menos na época) uma solução pronta para Delphi. A saída foi utilizar uma biblioteca (das muitas existentes) em Java para lógica difusa. A exceção do motor de inferência (que rodava no server), todo o sistema foi desenvolvido em Delphi e a comunicação entre os processos feita em XML- RPC. Voltando ao nosso servidor, não vamos aqui dissecar a implementação de cada um desses serviços. O Servidor é em Java e afinal de contas não estamos na ActiveJava! Caso o micro que hospedará o servidor não tenha uma máquina virtual Java (JVM) instalada é só baixar o JDK (Java Development Kit) do JavaSE direto do site da Sun (http://www.javasoft.com) e fazer a instalação que é muito simples (aquelas do tipo Next, Next, ..., Finish). Se o micro já possuir uma JVM apenas assegure de que esteja instalado no mínimo o JDK versão 5. Depois disso faça o download do código fonte desse artigo e instale em uma pasta qualquer, digamos: c:artigo_xmlrpc. Essa pasta possui tanto o lado server como o lado client (cada um em sua respectiva subpasta). A organização dos fontes do artigo está assim configurada: client dotNET Win32 server class lib src build.xml carga.bat
  • 6. - 6 - Dentro da pasta server, há um arquivo chamado carga.bat – edite esse arquivo e onde se encontra a linha: SET JAVA_HOME=...... Complete com o diretório onde foi instalado o JDK. Por exemplo, no meu micro: SET JAVA_HOME=C:jdk1.6.0_11 Depois disso é só executar o script carga.bat – tenha preferência em executar a partir da linha de comando pois caso venha a ocorrer algum erro na carga do servidor, será mais fácil de visualizar. Veja a seqüência no console: c:> cdartigo_xmlrpcserver [ Enter ] c:artigo_xmlrpcserver> carga.bat [ Enter ] . . Iniciando o servidor XML-RPC na porta 8081 Servidor pronto para integração ... (Ctrl-C) para interromper Pronto, nosso servidor já está pronto para receber as solicitações em XML- RPC. Nota: Coloquei o servidor para ficar escutando uma porta diferente da porta 80 (que é a padrão para http). Isso para não ocorrer conflito, pois nos dias de hoje é raro um micro não ter um serviço http:80 iniciado. Se for desejado modificar o numero da porta é só editar novamente o script carga.bat e alterar o valor em SET SERVER_PORT. Essa variável está com 8081 por default. Caso o leitor queira realizar a (re)compilação do servidor, é fornecido o arquivo build.xml que deverá ser utilizado com a ferramenta ant. Vamos ao lado Client Aqui retornamos ao velho e bom Delphi. Primeiro vamos montar um client em .NET utilizado o Delphi Prism e depois um client Win32 utilizando o Delphi 7. Os dois clients irão consumir todos os serviços expostos no server. Começamos primeiro com .NET Client .NET O client em .NET foi desenvolvido com Delphi Prism. A plataforma .NET não possui suporte nativo a XML-RPC. Uma boa implementação é a XML- RPC.NET e é preciso fazer o download em http://www.xml-rpc.net. A última versão – até o momento que escrevo – é a 2.4.0. Baixe o arquivo xml- rpc.net.2.4.0.zip e faça a descompactação em uma pasta qualquer. Localize na descompactação a montagem CookComputing.XmlRpcV2.dll. Vamos precisar somente dela. Os outros arquivos não são necessários, pois são constituídos de: montagem para o Compact Framework, montagem para o NET Framework versão 1, códigos fontes e testes – isso não nos interessa. Devido a funcionalidade de reflexão presente na plataforma .NET, a programação de um client é muito simples. Precisamos declarar uma interface contendo os procedimentos remotos – listagem 3.
  • 7. - 7 - Type [XmlRpcUrl('http://localhost:8081')] IServicos = public interface [XmlRpcMethod('app.formatarNome')] method FormatarNome(s: String): String; [XmlRpcMethod('app.obterPI')] method ObterPI(casasDecimais: Integer): String; [XmlRpcMethod('app.obterFasesLua')] method ObterFasesLua(mes: integer; ano: integer): array of String; [XmlRpcMethod('app.postarTwitter')] method PostarTwitter(login: String; senha: String; post:String): String; end; Listagem 3 – declaração da interface no Delphi Prism Repare que utilizamos atributos tanto no nível de interface quanto na declaração dos métodos. Isso é necessário para que a reflexão seja feita e uma classe proxy – que implementa essa interface – seja automaticamente criada em tempo de execução. O atributo XmlRpcUrl que aparece próximo a interface tem como parâmetro o endereço (endpoint) do servidor. Já o atributo XmlRpcMethod que aparece em cada método possui como parâmetro o nome do método correspondente totalmente qualificado no server – o nome do método na interface pode ser diferente do server, mas os tipos de parâmetros e de retorno devem ser iguais. A qualificação tem o formato: namespace.procedimento. Daqui a pouco falaremos mais sobre o objetivo do namespace. O próximo passo é fazermos a chamada a esses procedimentos remotos. Para que isso seja possível utilizamos a classe genérica XmlRpcProxyGen – precisamos passar na construção desse objeto o nome da interface. Assim: var servicos := XmlRpcProxyGen.Create<IServicos>; Como podemos ver, a implementação da interface IServicos, que declaramos, é feita de forma totalmente automática pelo XML-RPC.NET E agora? Agora é só chamar os métodos: var resultado := servicos.FormatarNome(‘douGlas aDamS’); // retorna Douglas Adams var pi := servicos.ObterPI(35); // retorna 3.14159265358979323846264338327950288 . . var postar := servicos.PostarTwitter(‘mhenley@datamag.com.br’, ‘senha’, ‘Ola!!!’); Obviamente a montagem CookComputing.XmlRpcV2.dll precisa ser referenciada no Visual Studio (figura 2) – para isso basta clicar com o botão
  • 8. - 8 - direito no item References do projeto, no Solution Explorer, localizar e adicionar a montagem à solução. As classes e interfaces estão contidas no namespace ‘CookComputing.XmlRpc’ dessa montagem. Para simplificar o client .NET foi construído uma aplicação do tipo Console Application. E isso é realmente tudo que precisamos fazer. A listagem 4 apresenta o código fonte do client .NET e, na figura 3 temos a aplicação rodando. namespace ClientPrism; interface uses CookComputing.XmlRpc, System.Text; type ConsoleApp = class public class method Main; end; implementation class method ConsoleApp.Main; begin var servicos := XmlRpcProxyGen.Create<IServicos>; // chama método remoto FormatarNome Console.WriteLine(); Console.WriteLine('Método FormatarNome:'); var naoFormatado := 'mIgUeL hEnLEY'; var formatado := servicos.FormatarNome(naoFormatado); Console.WriteLine('Nao formatado: ' + naoFormatado); Console.WriteLine('Formatado : ' + formatado); Console.WriteLine('------------------------------------------------'); Console.WriteLine(); // chama método para calcular o numero PI Console.WriteLine('Método ObterPI: (50 casas decimais)'); var pi := servicos.ObterPI(50); Console.WriteLine('Pi = ' + pi); Console.WriteLine('------------------------------------------------'); Console.WriteLine(); // chama método para obter as fases da lua: Console.WriteLine('Método ObterFasesLua: (maio/2009)'); Console.WriteLine('formato dia/mes/ano=[0-7]. 0=lua cheia, 7=lua nova'); Console.WriteLine(); var mes := 5; var ano := 2009; var fases := servicos.ObterFasesLua(mes, ano); var strbuilder := new StringBuilder(); var dia := 1; for each fase: String in fases do begin if dia <> 1 then strbuilder.append('; '); strbuilder.append(dia). append('/').append(mes).append('/').append(ano).append('='). append(fase); inc(dia); end;
  • 9. - 9 - Console.WriteLine(strbuilder); Console.WriteLine('------------------------------------------------'); Console.WriteLine(); { // chama método para fazer um post no Twitter: Console.WriteLine('Fazendo um post no twitter'); var ok := servicos.PostarTwitter('login-twitter', 'senha-twitter', 'bla'); } Console.WriteLine(); Console.ReadLine(); end; end. Listagem 4 Figura 2 – montagem referenciada no Visual Studio Figura 3
  • 10. - 10 - Client Win32 Da mesma forma que acontece na plataforma .NET temos que baixar uma implementação XML-RPC para Delphi Win32. Uma implementação é a biblioteca Delphi XML-RPC que pode ser baixada do site sourceforge: http://sourceforge.net/projects/delphixml-rpc/. Não é necessário baixar, pois o download do artigo já contem essa biblioteca dentro da pasta clientwin32dxmlrpc. A última versão é a 2.0.0 e não há novos releases desde 2003. Essa versão está homologada para Delphi 7 e faz uso dos componentes Indy para comunicação TCP/IP. Como houve mudanças das API’s entre a versão 9 e 10 do Indy, versões posteriores do Delphi que utilizam o Indy 10 terão problema para utilizar o Delphi XML-RPC. A solução é fazer as alterações “na unha” – abrir o código fonte da biblioteca, sair à caça das chamadas ao Indy 9 e adaptá-las ao Indy 10 – uma dica para quem quiser se aventurar nesse terreno é utilizar alguns patterns2 como forma de isolamento entre o Delphi XML-RPC e o Indy – isso facilitará futuros portes para novas versões do Indy que vierem a aparecer. A API do Delphi XML-RPC para o perfil client também é muito simples de utilizar, porém, a configuração é um pouco mais trabalhosa em função da falta do artifício de reflexão no Delphi Win32. Basicamente trabalhamos com uma classe e duas interfaces: A classe TRpcCaller é a responsável por configurar o endpoint (servidor + porta) e disparar a chamada ao servidor. O principal método dessa classe é o Execute que recebe como parâmetro uma interface do tipo IRpcFunction e retorna uma interface do tipo IRpcResult. A interface IRpcFunction é implementada pela classe TRpcFunction e seus principais métodos / propriedades são: ObjectMethod – propriedade read/write que permite especificar o nome do procedimento (totalmente qualificado) a ser executado no server. AddItem – Métodos sobrecarregados que tem como objetivo “setar” os parâmetros para o procedimento a ser executado no server. Exemplo: Se o procedimento requer dois parâmetros: um string e um inteiro, as chamadas devem ser assim realizadas (supondo obj um objeto do tipo IRpcFuntion): obj.AddItem(‘parametro-string’); obj.AddItem(100); Também há sobrecarga desse método para parâmetros do tipo array e estruturas. Os principais métodos / propriedades da interface IRpcResult (resultado da execução) são: IsError – método que retorna um boolean. Retorna true se houve um erro no server. (O procedimento pode – e deve - lançar exception caso alguma pré- condição esteja fora do domínio de atuação). 2 Os patterns Factory e Adapter são bons candidatos para isso.
  • 11. - 11 - ErroMsg – propriedade read only que contem o erro lançado pelo servidor. IsArray / IsBase64 / IsDate / IsFloat / IsInteger / IsString / IsStrut / IsBoolean / IsBase64 – retorna true ou false se o resultado for um desses tipos. AsString / AsInteger / AsDateTime / AsBoolean / AsBase64 / AsArray / AsStruct / AsFloat – para cada uma dessas propriedades retorna o seu respectivo tipo (String, Integer, DateTime, ...). No caso do retorno ser um array, a propriedade AsArray retorna a interface IRpcArray que possui formas de obter os elementos através de índices. O retorno também pode ser uma estrutura. Conforme dito no inicio do artigo, a especificação XML-RPC também suporta arrays e estruturas. Tipos array são muito úteis quando temos que passar para o procedimento parâmetros multivalores – um procedimento remoto poderia estar programado para calcular a média tendo como parâmetro de entrada um array de double. Da mesma forma, o retorno do método pode ser multivalorado – inclusive o servidor desse artigo oferece o método/procedimento obterFasesLua que retorna um array de String contendo as fases da lua para cada dia do mês. (nota: A especificação XML-RPC não impõe a obrigatoriedade dos elementos de um array ser do mesmo tipo – podem ser de tipos distintos – é possível misturar String com Array com Boolean com Array de Array, etc...). Quanto às estruturas, elas permitem um dicionário com armazenamento de chave de busca ser trafegado entre o client e o server. Por exemplo, na necessidade de trafegar uma estrutura como endereço (endereço, uf, cep, ...) podemos utilizar a classe TRpcStruct - que implementa a interface IRpcStruct. Essa interface possui métodos AddItem sobrecarregados – veja na listagem 5 um trecho da interface extraído diretamente da unit XmlRpcTypes.pas. Objetos do tipo IRpcStruct podem ser passados como parâmetro para o método AddItem da interface IRpcFunction descrita anteriormente. type … IRpcStruct = interface(IInterface) … procedure AddItem(const Key: string; Value: Integer); overload; procedure AddItem(const Key: string; const Value: string); overload; procedure AddItem(const Key: string; Value: Double); overload; procedure AddItem(const Key: string; Value: Boolean); overload; procedure AddItem(const Key: string; Value: IRpcArray); overload; procedure AddItem(const Key: string; Value: IRpcStruct); overload; … end; Listagem 5 – parte da definição da interface IRpcStruct A figura 4 mostra a interface visual do client Win32. Cada aba faz o acesso a uma funcionalidade no servidor. A figura 5 mostra uma postagem no Twitter realizada pelo client.
  • 12. - 12 - Figura 4 – interface client Win32 Figura 5 – postagem no Twitter
  • 13. - 13 - Voltando ao lado server Vamos falar mais um pouco da implementação do servidor. O pacote que permite o Java utilizar o XML-RPC (tanto como server ou client) é o que está sob os auspícios da Apache Software Foundation e pode ser encontrado em http://ws.apache.org/xmlrpc/ . Há outras implementações por aí, mas essa é o padrão de fato3 para Java. Também não é necessário baixar esse pacote, pois já o colocamos no arquivo de download do artigo. Basicamente o que precisamos é utilizar somente a classe WebServer contida no pacote org.apache.xmlrpc. Veja abaixo, o código relevante, que inicializa o servidor: 1 public static void main(String[] args) { 2 System.out.println("Iniciando o servidor XML-RPC ..."); 3 int port = getPort(args[0]); 4 WebServer ws = new WebServer(port); 5 ws.addHandler("app", new Servicos()); 6 ws.start(); 7 System.out.println("Servidor pronto para integração"); 8 } A linha 4 cria uma instância da classe WebServer passando como parâmetro o numero da porta de escuta. A linha 5 adiciona um handle ao servidor - o primeiro parâmetro define o nome do namespace (definimos como app – mas poderia ter sido qualquer outro) e, o segundo parâmetro é uma instancia da classe que queremos expor os métodos ao mundo externo - classe Servicos - assim definida: public class Servicos { public String formatarNome(String uglyFormat) { … } public String obterPI(int casasDecimais) { … } public String[] obterFasesLua(int mes, int ano) { … } public String postarTwitter(String login, String senha, String post) { … } } A implementação de cada método foi omitida - o código fonte completo está no download do artigo e o leitor mais interessado poderá apreciar a forma que essa classe foi implementada. Aqui fica a explicação prometida quanto a qualificação do método/procedimento: namespace.procedimento. Podemos ter vários procedimentos com mesmo nome, mas não no mesmo namespace. Entre a linha 5 e a linha 6 poderíamos adicionar um outro handle da seguinte forma: ws.addHandler(“app2”, new MaisServicos()). Fazendo isso todos os métodos definidos na classe MaisServicos também seriam expostos e poderiam ser invocados, pelo client, prefixando-os com “app2.”. 3 Padrões de jure (legais) são os desenvolvidos por organismos de padronização como ISO, ABNT, IEEE. Padrões de fato são os estabelecidos pelo mercado, por sua grande utilização: PDF, MPEG, Windows, etc...
  • 14. - 14 - A classe WebServer possui também funcionalidades de restrição de acesso por IP. Podemos bloquear ou permitir o acesso por um determinado endereço, ou faixa de endereços. Os métodos: acceptClient, denyClient e setParanoid dão conta do recado e são muito úteis devido a natureza do XML-RPC – lembre-se que o protocolo http é inseguro acarretando em uma troca de mensagens “clear text” entre o client e o server. Se a segurança for um requisito para o seu projeto o servidor deverá ser instalado em um web-server habilitado para SSL – nesse caso o lembre-se que o cliente também deverá “falar” SSL. Conclusão No mundo real (e imperfeito) onde uma diversidade muito grande de linguagens e sistemas operacionais se faz presente, é inevitável a necessidade de integração entre sistemas rodando em plataformas distintas. XML-RPC se destaca pela eficiência, portabilidade e, por uma curva de aprendizado muito pequena, sendo uma solução leve, aberta e adequada para interoperabilidade. Miguel Henley Filho (miguel.henley@gmail.com) é engenheiro e desenvolvedor de soluções corporativas em Delphi e Java nas plataformas Windows e Linux. Possui interesse em arquitetura de software, objetos distribuídos e algoritmos computacionais de alto desempenho.