Esse artigo faz parte da revista Java Magazine edição 34. Clique aqui para ler todos os artigos desta edição

ht=34 alt=imagem_pdf.jpg src="/imagens/imagem_pdf.jpg" width=34 border=0>

 

Entendendo Web Services

Do RPC à Service-Oriented Architecture

Explorando o mundo dos sistemas distribuídos, messaging, web services e SOA: o novo paradigma de desenvolvimento de aplicações

Osvaldo Pinali Doederlein

Nesta coluna, já investigamos muitas APIs, tecnologias e padrões. Em muitos casos, uma abordagem técnica e prática é suficiente, pois os fundamentos conceituais do assunto estão no domínio do que se espera de todo desenvolvedor Java: orientação a objetos, a linguagem Java, a plataforma J2SE/JSE com suas APIs principais, e outros conhecimentos gerais de programadores de qualquer linguagem, como bancos de dados e HTML.

Quando começarmos a estudar web services e o lote associado de tecnologias "pós web services" como Service-Oriented Architecture (SOA), a coisa muda de figura. Temos que absorver um novo paradigma – conceitos e princípios não só novos, mas talvez anti-intuitivos, opostos a algumas coisas que aprendemos antes. Um bom programador Java poderia instalar um SDK de web services, como o Java Web Services Developer Pack (JWSDP) da Sun, e começar a devorar a documentação de APIs e tutoriais. Mas isso talvez resulte em aplicações que fazem direito a coisa errada: utilizando uma nova tecnologia, mas não projetando as aplicações da forma esperada pelo novo paradigma.

Neste artigo, vamos investigar o mundo dos web services, e além, mostrando não só um resumo geral destas tecnologias – de SOAP a BPEL – mas também os porquês de sua existência. Mostramos o motivo para serem como são, para usarmos SOAP ao invés de outras alternativas, mensagens assíncronas ao invés de invocações síncronas, documentos ao invés de métodos remotos. Investigaremos conceitos como baixo acoplamento e SOA, muito em voga ultimamente. Não entraremos em detalhes práticos, como a descrição de APIs do Java para web services, mas daremos um "ponto de vista do Java" para todas as discussões relevantes.

A estrada até os web services

A tecnologia de web services não surgiu do nada, pelo contrário, podemos rastrear suas origens a partir de diversas gerações de tecnologia anteriores.

No princípio dos tempos, nos anos 70, a programação de redes de computadores exigia o uso de APIs de camada de transporte, por exemplo os BSD Sockets para TCP/IP. Em Java, podemos fazer isso com classes do pacote java.net, como Socket e ServerSocket. Com essas APIs, podemos abrir conexões com processos que executam em outras máquinas, e enviar e receber blocos de bytes.

Este modelo de programação, ilustrado pela Figura 1, ainda é viável para aplicações muito simples, ou para casos que têm uma demanda extrema por eficiência, mas é um modelo muito rudimentar, difícil de trabalhar. Linguagens e técnicas de programação modernas, como o próprio Java, exigem APIs de rede de mais alto nível.

Inicialmente, muitas aplicações implementavam suas próprias bibliotecas de "utilitários de rede"; por exemplo, algoritmos para converter estruturas de dados complexas nos arrays de bytes transmissíveis pelos sockets[1], ou mecanismos para invocar procedimentos implementados por processos remotos. Com o tempo, estas soluções "ad-hoc" convergiram para APIs e serviços padronizados.

Sistemas de arquivos e BDs distribuídos

Os dois primeiros grandes avanços no sentido de permitir programação distribuída de mais alto nível foram os sistemas de arquivos distribuídos e os bancos de dados em rede[2].

Os sistemas de arquivos distribuídos, como o NFS da Sun ou o SMB da Microsoft, estendem o conceito de sistema de arquivos, permitindo acessar arquivos de outros computadores pela rede de forma transparente. Os sistemas gerenciadores de bancos de dados (SGBDs) também oferecem interfaces de acesso via rede, e permitem compartilhar dados de forma mais estruturada que os sistemas de arquivos, porém com maior custo em desempenho.

Os SGBDs e sistemas de arquivos distribuídos permitem às aplicações trocar informações de forma simples, utilizando APIs de alto nível (em comparação com as APIs de transporte de rede). Por exemplo, em Java usaríamos um java.io.FileInputStream ou um java.sql.ResultSet para ler dados remotos, o que é bem melhor que usar um java.net.Socket. Por outro lado, estas tecnologias não foram criadas para solucionar o problema de programação de rede. Não atacam questões complexas como a invocação de operações remotas, e impõem custos como exigir a persistência dos dados em disco, ou a execução de linguagens de consultas como SQL, ambos redundantes para aplicações que só precisam trocar dados pela rede.

RPC

A primeira tentativa real de subir o nível da programação de rede em direção às linguagens de programação foi a chamada de procedimentos remotos (remote procedure call). A RPC permite invocar procedimentos através da rede, e introduz um conceito importante: as linguagens de definição de interface. Por exemplo, um programa "Alô Mundo" escrito com o Sun RPC[3] precisaria do seguinte arquivo:

 

program ALO { // Identificador da interface

  version V1 { // Identificador da versão

    string alo (string nome) = 1; // Definição do procedimento

  } = 1; // Número da versão

} = 0x12345678; // Identificador único da interface ALO

 

Este arquivo, na sintaxe XDR (eXternal Data Representation), declara um procedimento alo(). Um compilador de interface irá ler este script e gerar um código stub que intercepta as invocações no cliente e as converte em mensagens de rede; e um skeleton no servidor, que recebe estas mensagens e executa a invocação ao procedimento real (como ilustrado na Figura 2).

Stubs são rotinas que, incluídas no programa cliente, se fazem passar por um servidor, mas na verdade invocam a mesma operação no servidor através da rede. Skeletons são rotinas que, no programa servidor, recebem estas mensagens de rede e então invocam a implementação real da operação desejada no servidor, fazendo-se passar por um cliente local. Se a operação do servidor gerar valores de retorno ou exceções, o skeleton recebe estes valores e os devolve pela rede para o stub, que por sua vez devolve ao cliente.

Linguagens de definição de interfaces como XDR, além de alimentar esse gerador de código, têm a vantagem de ser neutras: independentes de arquitetura, de compilador ou de linguagem. Assim, um compilador de XDR para C (o rpcgen) irá converter o tipo string da XDR para o padrão do C, ou seja, um char* (ponteiro para uma seqüência de caracteres terminadas pelo byte 0). Compiladores para outras linguagens podem mapear string para tipos diferentes. Com isso, começamos a resolver um dos problemas fundamentais da programação em rede: a heterogeneidade. Sistemas distribuídos são como as caixas de chocolate de Forrest Gump: você nunca sabe o que haverá no outro lado da conexão de rede.

Observe, no entanto, que a RPC pertence à geração das linguagens estruturadas, como mostra o próprio nome da tecnologia (procedimentos remotos). No exemplo de XDR, também podemos ver outras operações de baixo nível, como a atribuição manual de identificadores numéricos para cada interface, versão e procedimento. O runtime do Sun RPC precisava destes números para gerar mensagens de rede eficientes, e identificar estas mensagens sem ambigüidade.

O RPC era uma tecnologia boa para os tempos do C, mas não para a geração do C++, Smalltalk ou Java. Esta tecnologia teve algumas novas encarnações e evoluções, como a RPC DCE (um padrão do Open Group) e a MSRPC (extensão da RPC DCE feita pela Microsoft e usada como base do COM). E o RMI do Java (java.rmi), apesar de dar suporte à programação OO[4], não se diferencia da RPC na sua arquitetura fundamental.

Observação: deste ponto em diante, utilizaremos "RPC" para nos referirmos a qualquer middleware que funcione de forma similar – transformando invocações de procedimento, função, método ou equivalente (conforme a linguagem) em invocações de rede. Usaremos "Sun RPC" para nos referirmos especificamente a esta tecnologia.

Objetos Distribuídos: CORBA

A adoção da Programação Orientada a Objetos (POO), entre meados dos anos 80 e o começo dos 90, exigiu uma tecnologia de programação distribuída adequada. Esta necessidade deu origem aos middlewares de Objetos Distribuídos (OD).

Lembrando: "middleware" é uma camada de software que fica no "meio" do caminho entre aplicações distribuídas, facilitando e habilitando a comunicação entre estas aplicações. Isto inclui desde serviços de rede comuns como DNS ou servidores proxy, até plataformas sofisticadas como ORBs ou servidores de messaging ou EAI.

O principal representante foi o CORBA (Common Object Request Broker Architecture), um padrão da OMG (Object Management Group), órgão que congrega centenas de empresas, e também é responsável por outros padrões de OO, como a UML. Um programa CORBA também precisa de uma definição de interface, agora na sintaxe OMG IDL (Interface Definition Language):

 

interface Banco : InstituicaoFinanceira {

  attribute short codigo;

  Conta abreConta (in Pessoa correntista) raises CreditoRuim;

}

...

Quer ler esse conteúdo completo? Tenha acesso completo