You are on page 1of 14

Desvendando Web Services (Soap/XML) Resumo: Este artigo tem por objetivo dar uma viso geral e completa

sobre uma das melhores novidades do Delphi 6, o suporte a Web Services. Viso geral Uma nova gerao de tecnologia de desenvolvimento nos apresentada com o Web Services. Com ela podemos criar aplicaes modulares e independentes que so distribudas facilmente em qualquer estrutura de redes TCP/IP, pois esse foi um dos princpios fundamentais de sua implementao. Um grande ponto positivo desta tecnologia que a criao de servidores e clientes independem da linguagem de programao e do sistema operacional que so implementados. Atualmente o suporte da Borland se restringe ao Windows, mas j foi anunciado para um futuro prximo o suporte no Kylix, o que abriria um grande leque de possibilidades no que diz respeito implementao de aplicaes distribudas multiplataformas. Os servidores podem descrever seus prprios servios atravs da WSDL (Web Service Definition Language). Dessa forma, os clientes podem facilmente obter informaes sobre os servidores que usaro, tais como: estrutura, mtodos e parmetros exigidos. Isso se torna essencialmente til quando se est codificando servidores que sero usados por terceiros ou implementando clientes que usam servios de outras empresas. No caso particular do Delphi podemos usar um wizard que importa essas informaes e cria automaticamente as units com as definies dos servios ofertados pelo servidor. SOAP/XML A comunicao entre clientes e servidores feita atravs do SOAP (Simple Object Access Protocol). Esse protocolo definido em XML, sendo assim, as chamadas a procedures remotas (RPC) so codificadas em XML. Para transporte das mensagens usado o HTTP, que alm de tornar o SOAP um protocolo leve, elimina inmeros problemas de outras tecnologias com proxys, como CORBA, DCOM e etc... Como foi citado acima, no temos a preocupao de contornar o esquema de segurana para realizar a comunicao entre clientes e servidores. Como o SOAP usa o HTTP como camada de transporte ele opera na porta 80, que na extrema maioria dos casos est liberada pelo proxy/firewall. Alm do mais, conceitualmente no existe diferena entre uma requisio a um mtodo e a uma pgina HTML. Dentre as vantagens dessa tecnologia, ainda podemos ressaltar a no necessidade de instalao de software adicional para o suporte tecnologia como acontece com o CORBA e com o DCOM (no windows95). Mais informaes sobre SOAP podem ser encontradas no site da especificao oficial em http://www.w3.org/TR/SOAP/. Implementando servidores com suporte a Web Services

Daremos incio , agora, parte prtica deste artigo, ento para comear devemos criar um novo projeto de Web Services. No meu caso escolhi CGI stand-alone como tipo da aplicao e ela rodar diretamente no IIS5, mas estejam livres para escolherem o que mais lhe convierem. Feito isso seremos apresentados a seguinte tela:

Ao examinarmos o projeto gerado, percebemos facilmente que se trata de uma Web Server Application. No Web Module criado, encontramos os componentes fundamentais para implementao do servidor, so eles: THTTPSoapDispatcher: Este componente atua como despachante recebendo as mensagens entrantes e as encaminha para o objeto especificado em sua propriedade Dispatcher para que seja decodificada. Ele registra-se automaticamente junto ao Web Module como um auto-dispatching object, fazendo assim com que no seja necessrio a criao de Actions para direcionar as requisies para o THTTPSoapDispatcher, este passa ento a receber todas as solicitaes automaticamente. THTTPSoapPascalInvoker: Este componente recebe a mensagem vinda do THTTPSoapDispatcher e a interpreta cuidando que seja disparado o mtodo correspondente a solicitao. Ele ainda codifica o retorno do mtodo para o padro SOAP/XML. TWSDLHTMLPublish: Este componente responsvel por publicar todas informaes registradas pelo Web Service em WSDL que faz com que qualquer pessoa possa adquiri-las para implementar o cliente desse servio, mesmo que seja em outra ferramenta que no o Delphi. O prximo passo na implementao de um Web Services a definio das interfaces que sero publicadas para uso por seus clientes. Aquelas devem herdar de IInvokable. Interfaces Invokable Antes de passarmos a definio, vamos ver alguma coisa a respeito delas. Por que precisamos derivar nossas interfaces de IInvokable? A resposta simples, a arquitetura trazida pelo Delphi requer que as interfaces de definio sejam compiladas com informao de run-time, e isso que a que a IInvokable garante. A nica diferena entre IUnknow e IInvokable que esta compilada com a diretiva {$M}, fazendo gerar RTTI para ela e todas suas descendentes. Dessa forma, tecnicamente chegamos a concluso de que podemos derivar nossas interfaces diretamente de IUnknow, desde que incluamos a diretiva {$M} na unit de declarao da mesma. Embora isso seja possvel e funcione, no recomendvel tal

conduta, porque podemos gerar problemas futuros com novas verses da arquitetura. Se numa verso posterior for definido mtodos essenciais na IInvokable teremos problemas de compatibilidade ou anomalias no funcionamento do cdigo. Agora que vimos o conceito vamos passar para a parte prtica. Vamos comear definindo uma interface para as operaes aritmticas bsicas: unit IMathIntf; interface type IMath = interface(IInvokable) ['{E4F918D6-429C-45D4-9D28-D3E64DDB65E3}'] function Soma(X, Y : Double): Double; stdcall; function Dife(X, Y : Double): Double; stdcall; function Mult(X, Y : Double): Double; stdcall; function Divi(X, Y : Double): Double; stdcall; end; implementation uses InvokeRegistry; initialization InvRegistry.RegisterInterface(TypeInfo(IMath)); end. Nessa unit encontramos uma declarao de interface normal, e esta ser usada pelos clientes para solicitar servios junto ao servidor. Uma importante observao a ser feita sobre a unit acima o cdigo localizado na seo initialization: InvRegistry.RegisterInterface(TypeInfo(IMath)); Essa mais uma caracterstica da programao de Web Services. Sempre que declararmos interfaces, classes de implementao ou classes de excees (vistas mais adiante) teremos que registr-las. Isso feito atravs de um objeto global disponibilizado pela unit InvokeRegistry, que deve ser declarada na seo uses da unit. Atravs desse objeto, chamamos mtodos especficos para cada tipo de declarao, no caso de interface usamos o RegisterInterface. Isso necessrio para o mecanismo de vinculao, feito pelo componente THTTPSOAPPascalInvoker, entre solicitaes SOAP e a chamada ao mtodo correto. Pronto, agora que j temos a definio do servio que desejamos prover vamos agora implement-la. Implementando Interfaces Invokable (TInvokableClass)

Quando vamos codificar as interfaces de nossos Web Services devemos criar classes descendentes da TInvokable. Mas por que isso? So basicamente trs os motivos que nos levam a seguir esta conveno: 1 - Embora na implementao atual no tenha nada que realmente empea que derivemos nossa classes de uma outra qualquer como TObject ou TInterfacedObject, assim como no caso da interface IUnknow/IInvokable, pode ser que em futuras distribuies sejam criados mtodos essenciais para o funcionamento da arquitetura na Tinvokable, o que levaria nosso cdigo a ter srios problemas de compatibilidade e funcionamento. 2 - Dispensa a necessidade de criar uma factory procedure, pois o invocation registry sabe como instanciar essas classes e suas descendentes, do contrrio teramos que registr-las juntamente com uma factory procedure para assegurar seu funcionamento. 3 - Dispensa a necessidade de implementar um esquema de liberao de memria. A classe TInvokable implementa uma contagem de referncias, e quando esta chega a zero ela se libera automaticamente. Mas se depois de tudo isso ainda quisermos usar outra ancestral para nossas classes de implementao ns podemos. Vejamos abaixo o cdigo da unit de implementao. unit IMathImpl; interface Uses InvokeRegistry, IMathIntf; type //TMath = class(TInterfacedObject, IMath) TMath = class(TInvokableclass, IMath) public function Soma(X: Double; Y: Double): Double; stdcall; function Dife(X: Double; Y: Double): Double; stdcall; function Mult(X: Double; Y: Double): Double; stdcall; function Divi(X: Double; Y: Double): Double; stdcall; end; implementation { TMath } function TMath.Soma(X, Y: Double): Double; begin Result := X + Y; end; function TMath.Dife(X, Y: Double): Double; begin

Result := X - Y; end; function TMath.Mult(X, Y: Double): Double; begin Result := X * Y; end; function TMath.Divi(X, Y: Double): Double; begin Result := X / Y; end; {// Factory Procedure procedure CreateMath(out Obj : TObject); begin //Pode ser implementado aqui o conceito de singleton. Result := TMath.Create; end; } initialization InvRegistry.RegisterInvokableClass(TMath); //InvRegistry.RegisterInvokableClass(TMath, CreateMath); end. Repare nas linhas de cdigo comentadas, visto que elas representam o bsico necessrio para o funcionamento da arquitetura, se optarmos por no ter a Tinvokable como ancestral de nossas classes. Ressalto que essa conduta deve ser evitada. Novamente percebemos a necessidade de um cdigo de registro, dessa vez o da classe, que feito como descrito abaixo. InvRegistry.RegisterInvokableClass(TypeInfo(IMath)); Feito tudo isso at aqui, j podemos considerar que temos um servidor com suporte a Web Services completo e funcional. J at poderamos passar para a implementao de um cliente para us-lo, mas ao invs disso vamos continuar a increment-lo com mais alguns conceitos interessantes antes de passarmos ao desenvolvimento do cliente. At agora s usamos um tipo de dado em nossa interface, o double. Embora essa escolha tenha se dado devido ao servio escolhido para o exemplo, o trabalho com todos os outros tipos bsicos(primitivos) funcionam da mesma maneira. Mas e se ns precisarmos retornar para o cliente um tipo complexo como uma classe? Isso o que vamos ver a seguir. Tipos complexos em Interfaces Invokable (TRemotable)

Sempre que precisarmos retornar ou receber tipos complexos tais como records, sets ou classes em nossos Web Services, devemos mape-los para classes descendentes de TRemotable. Na compilao destas, so includas informaes de RTTI, usadas para converter os dados em SOAP stream. Ento para exemplificarmos o uso desse recurso vamos definir uma outra interface para o nosso Web Service, que usar e retornar um tipo complexo por ns definido. Vamos analisar o cdigo abaixo: unit IGeometryIntf; interface uses InvokeRegistry; type TLosango = class(TRemotable) private FX1, FX2, FY1, FY2 : Integer; published property X1 : Integer read FX1 write FX1; property X2 : Integer read FX2 write FX2; property Y1 : Integer read FY1 write FY1; property Y2 : Integer read FY2 write FY2; end; TPoint = class(TRemotable) private FX, FY : Integer; published property X : Integer read FX write FX; property Y : Integer read FY write FY; end; IGeometry = interface(IInvokable) ['{926590CF-4B48-4AB2-9079-24183DD8D34F}'] function DiagonalMaior(const Los : TLosango) : Integer; stdcall; function Losango(const Centro : TPoint; DiaU, DiaL : Integer) : TLosango; stdcall; end; implementation initialization InvRegistry.RegisterInterface(TypeInfo(IGeometry)); RemTypeRegistry.RegisterXSClass(TPoint); RemTypeRegistry.RegisterXSClass(TLosango); end.

Declaramos duas remotables classes TLosango e TPoint que como seus nomes

sugerem, representam respectivamente um losango e um ponto nos eixos de coordenadas. Tambm declaramos uma interface IGeometry contendo duas funes: - A primeira recebe um parmetro TLosango e calcula a diagonal maior de um losango, demonstrando a passagem de um tipo complexo como parmetro de uma funo. - A segunda recebe os valores do centro, diagonal maior e menor de um losango, com isso ela retorna um objeto TLosango contendo as coordenadas calculadas de acordo com os parmetros passados. Devemos observar na seo initialization dessa unit uma diferena entre o registro de uma interface e de uma remotable classe. . initialization InvRegistry.RegisterInterface(TypeInfo(IGeometry)); RemTypeRegistry.RegisterXSClass(TPoint); RemTypeRegistry.RegisterXSClass(TLosango); end. Como podemos observar at aqui, no caso das interfaces e invokables classes ns usamos os mtodos RegisterInterface e RegisterInvokableClass do objeto InvRegistry, e no caso das remotables classes e excees personalizadas usamos o mtodo RegisterXSClass do objeto RemTypeRegistry, todos eles definidos na unit InvokeRegistry. Ento vamos implementar agora a definio analisada acima: unit IGeometryImpl; interface uses InvokeRegistry, IgeometryIntf, Math; type TGeometry = class(TInvokableClass, IGeometry) public function DiagonalMaior(const Los: TLosango): Integer; stdcall; function Losango(const Centro: TPoint; DiaU: Integer; DiaL: Integer): TLosango; stdcall; end; implementation { TGeometry } function TGeometry.DiagonalMaior(const Los: TLosango): Integer; begin Result := Max(Los.X2 - Los.X1, Los.Y2 - Los.Y1); end;

function TGeometry.Losango(const Centro: TPoint; DiaU, DiaL: Integer): TLosango; begin Result := TLosango.Create; Result.X1 := Centro.X - (DiaU div 2); Result.X2 := Centro.X + (DiaU div 2); Result.Y1 := Centro.Y - (DiaL div 2); Result.Y2 := Centro.Y + (DiaL div 2); end; initialization InvRegistry.RegisterInvokableClass(TGeometry); end. Analisando essa ltima implementao, os mais atentos poderiam fazer a seguinte pergunta: E essa instncia de TLosango criada no mtodo Losango, no preciso desalocar a memria associada a ela? No, todas as descendentes de TRemotable so liberadas automaticamente logo depois que a codificao do pacote de retorno concluda, portanto no precisamos nos preocupar com isso. Bom, antes de passarmos a implementao do cliente vamos dar uma rpida olhada no manuseio de excees. Excees personalizadas em Web Services Todo programa bem implementado deve ter uma boa estrutura de excees para poder assegurar uma robustez desejvel. No caso dos Web Services isso no deve ser diferente. Quando uma exceo ocorre no escopo da chamada de um mtodo, o servidor automaticamente codifica as informaes sobre ela num SOAP fault packet e as envia como retorno do mtodo solicitado. Dessa forma, a aplicao cliente gera a exceo. Se no definirmos um tratamento de excees personalizadas, a aplicao cliente gera uma exceo comum(Exception) com a mensagem trazida no SOAP fault packet. Embora em alguns casos isso possa ser suficiente, ns temos como transmitir qualquer informao desejada de uma exceo. Para isso se tornar possvel, basta que a gente defina nossas classes de exceo descendentes de ERemotableException. Dessa forma podemos enviar ao cliente o valor de todas as propriedades published definidas na classe, assim ele pode levantar uma exceo equivalente ocorrida no servidor. E o melhor que se o cliente e o servidor compartilharem da mesma unit que define, implementa e registra suas classes de exceo, automaticamente o cliente levanta a exceo correta com todos seus valores de propriedades preenchidos quando receber um SOAP fault packet. Registrar as classes de exceo? Isso mesmo, como j podemos perceber tudo com que trabalhamos em Web Services deve ser registrado. O registro das classes de exceo se d de forma idntica a das descendentes de TRemotable que foi vista anteriormente.

Para exemplificar vamos criar agora uma exceo que ser levantada quando o mtodo IGeometry.Losango receber no parmetro Centro uma coordenada no compreendida no primeiro quadrante. Ento depois que definirmos a exceo vamos ter de alterar esse mtodo para implementar tal comportamento. Vejamos a seguir o cdigo de definio da exceo: unit EInvalidCentroU; interface uses InvokeRegistry; type EInvalidCentro = class(ERemotableException) private FX, FY : Integer; public constructor Create(const X, Y : Integer); published property X : Integer read FX write FX; property Y : Integer read FY write FY; end; implementation { EInvalidCentro } constructor EInvalidCentro.Create(const X, Y: Integer); begin inherited Create('O centro deve estar no primeiro quadrante.'); FX := X; FY := Y; end; initialization RemTypeRegistry.RegisterXSClass(EInvalidCentro); end. Ufa! O caminho foi longo, mas chegamos l. Com isso conclumos a codificao do nosso servidor, vamos passar agora para a implementao do cliente. Implementando clientes para Web Services Para iniciar vamos criar uma nova aplicao comum e deix-la com a interface como abaixo:

Agora que j construmos a interface, vamos ver como requisitar os servios remotos do Web Services. Como j destaquei anteriormente, um cliente independe da implementao do servidor, dessa forma, veremos como obter informaes sobre um servidor que j est rodando e disponvel. Todo servidor Web Services deve publicar informaes sobre si mesmo no padro WSDL, no Delphi isso feito automaticamente pela simples incluso e configurao do componente TWSDLHTMLPublish no projeto do servidor. Essas informaes esto acessveis para ns, normalmente, atravs de uma action com path info "/wsdl". Nesse caso estamos rodando o servidor localmente sobre o IIS5.0, ento podemos acessar essas informaes solicitando a seguinte URL : http://localhost/cgi-bin/WebServices.exe/wsdl Isso nos trar a seguinte tela:

Podemos encontrar ento todas as interfaces implementadas pelo servidor bem como um link para a descrio WSDL de cada interface. Para vermos essa descrio vamos clicar no link de IMath. Fazendo isso seremos apresentados a seguinte tela:

OBS: Este comportamento acima descrito pode variar de acordo com a implementao do servidor. Atravs dessas informaes obtidas j podemos ento analis-las para comear a codificao das units de interface com o servidor. Quer dizer que temos que ler estas informaes e codific-las manualmente? Depende, se voc estiver implementando o cliente em Delphi est livre dessa tarefa, podemos usar o Web Services Importer para fazer esse trabalho para ns:

Devemos repetir esse procedimento para cada interface que queremos importar, no caso da IMath devemos preencher o campo acima com a seguinte URL: http://localhost/cgi-bin/WebServices.exe/wsdl/IMath

Depois de clicarmos em Generate veremos que ele transformou aquela descrio WSDL em units do Delphi muito semelhantes (seno igual) quelas que foram definidas no servidor. Isso torna claro que se o servidor e o cliente forem ser implementados em Delphi podemos ignorar esse procedimento e simplesmente compartilhar as units de definio entre os dois projetos. Nesse ponto, j temos um projeto com a interface grfica e todas as units de importao prontas, passemos agora ento a requisio dos mtodos remotos. THTTPRIO O componente THTTPRIO o que usamos para obter uma referncia vlida de uma interface registrada. Quando fazemos um type cast desse objeto para uma interface especfica ele gera uma tabela de mtodos em memria que usada quando feita uma chamada a um mtodo especfico. Antes de comearmos a usar esse componente, devemos configur-lo, isso pode ser feito de duas maneiras diferentes: 1 - Se o servidor foi escrito em Delphi precisamos apenas configurar a propriedade URL, cujo valor gerado no registro da invokable interface. 2 - Independentemente da linguagem usada na implementao do servidor, podemos configurar as propriedades WSDLLocation, Service e Port. Quando configuramos WSDLLocation, deve ser usado o mesmo valor passado para o Web Services Importer (http://localhost/cgi-bin/WebServices.exe/wsdl/IMath), fica disponvel valores para as propriedades Service e Port que devem ser selecionados no object inspector.

Eu particularmente prefiro esse mtodo 2, por ser um mtodo mais genrico. Depois do componente configurado vamos passar a implementao do boto de somar para ilustrar o uso dele. Vejamos o cdigo do boto somar: procedure TfrmMain.SpeedButton1Click(Sender: TObject); Var

Im : IMath; A, B : Double; begin Im := HRMath as IMath; A := StrToFloat(EdtX.Text); B := StrToFloat(EdtY.Text); LblResult.Caption := FloatToStr(Im.Soma(A, B)); end; Podemos perceber que o uso dele bem simples. Basta declararmos uma varivel do tipo da interface desejada e atribuirmos a ela o type cast do componente. Depois de feito isso, podemos us-la normalmente como se fosse um objeto. Lembre-se tambm que no necessria a liberao de memria correspondente a referncia da interface, ela liberada automaticamente quando a varivel sai de escopo. Esse procedimento deve ser repetido para todos os mtodos que desejamos requisitar. Para uma melhor compreenso do assunto abordado, recomendo que seja analisado os fontes dos projetos criados ao longo deste artigo. Fontes disponveis em xxxxxxxxxx.

TSoapConnection O componente TSoapConnection pode ser usado por uma aplicao cliente que deseja conectar-se a uma mult-tiered database application, implementada como um Web Services. Ele nos d a possibilidade de estabelecer a conexo entre servidor e cliente e obter a IAppServer do servidor, implementado num RemoteDataModule, a partir da qual temos acesso aos providers existentes nele. Esse componente traz algumas vantagens em relao aos outros existentes que desempenham funes parecidas. Dentre essas vantagens, destacamos o uso do HTTP para transporte das informaes, bem como o suporte a SSL. Podemos ter segurana na transmisso de dados e ainda evitamos problemas com proxys. Consulte o help online para informaes sobre pr-requisitos para o uso deste componente. O componente TSoapConnection desempenha mais ou menos o mesmo papel dos componentes TDCOMConnection, TSocketConnection, TWebConnection, TCORBAConnection. Concluso Como podemos perceber a discusso sobre Web Services extensa e muito interessante. Uma das coisas que mais impulsiona a disseminao dessa tecnologia a simplicidade notada para implementar aplicaes distribudas quando comparada a outras tecnologias existentes no mercado. Termino este artigo desejando que as todas as expectativas daqueles que iniciaram a sua leitura tenham sido satisfeitas. Agradecerei a todos que venham a opin-lo ou coment-lo.