DClick

Simplificando Remoting com Flex


Twitter!

Apesar de existir um bom número de opções para facilitar o uso do remoting do Flex, desenvolvemos na DClick uma outra forma bastante simples que é adequada para projetos pequenos.


A API consiste em uma simples classe, Service, que deve ser extendida pelas classes de seu projeto que farão as chamadas remotas:

Actionscript:
  1. package br.com.dclick.services
  2. {
  3.     import br.com.dclick.mm.components.utils.GenericLoader;
  4.     import mx.controls.Alert;
  5.     import mx.rpc.AsyncToken;
  6.     import mx.rpc.events.AbstractEvent;
  7.     import mx.rpc.events.FaultEvent;
  8.     import mx.rpc.events.InvokeEvent;
  9.     import mx.rpc.events.ResultEvent;
  10.     import mx.rpc.remoting.RemoteObject;
  11.  
  12.     public class Service
  13.     {
  14.         private static const SUFIX_RESULT = "Result";
  15.         private static const SUFIX_FAULT  = "Fault";
  16.  
  17.         protected var service:RemoteObject;
  18.  
  19.         function Service( source:String = "", destination:String = "" )
  20.         {
  21.             service = new RemoteObject( source );
  22.             service.source = source;
  23.             service.addEventListener( InvokeEvent.INVOKE, invoke );
  24.             service.addEventListener( ResultEvent.RESULT, result );
  25.             service.addEventListener( FaultEvent.FAULT, fault );
  26.         }
  27.  
  28.         private function invoke( event:InvokeEvent ):void
  29.         {
  30.             before();
  31.         }
  32.  
  33.         private function result( event:ResultEvent ):void
  34.         {
  35.             resultOrFault( event, defaultResultHandler, SUFIX_RESULT );
  36.         }
  37.  
  38.         private function fault( event:FaultEvent ):void
  39.         {
  40.             resultOrFault( event, defaultFaultHandler, SUFIX_FAULT );
  41.         }
  42.  
  43.         private function resultOrFault( event:AbstractEvent, defaultHandler, sufix:String )
  44.         {
  45.             removeEventListeners();
  46.             try
  47.             {
  48.                 var method:String = getMethod( event.token ) + sufix; /// xxxResult ou xxxFault
  49.                 if( this.hasOwnProperty( method ) )
  50.                 {
  51.                     execute( method, event );
  52.                 }
  53.                 else
  54.                 {
  55.                     defaultHandler( event )
  56.                 }
  57.             }
  58.             catch( e:Error )
  59.             {
  60.                 throw e;
  61.             }
  62.             finally
  63.             {
  64.                 after();
  65.             }
  66.         }
  67.  
  68.         private function removeEventListeners():void
  69.         {
  70.             service.removeEventListener( InvokeEvent.INVOKE, invoke );
  71.             service.removeEventListener( ResultEvent.RESULT, result );
  72.             service.removeEventListener( FaultEvent.FAULT, fault );
  73.         }
  74.  
  75.         private function execute( method:String, event:AbstractEvent ):void
  76.         {
  77.             this[ method ]( event );
  78.         }
  79.  
  80.         private function getMethod( token:Object ):String
  81.         {
  82.             // nome do método executado no RemoteObject
  83.             return AsyncToken( token ).message[ "operation" ];
  84.         }
  85.  
  86.         // método executado antes de todas as chamadas. Pode sofrer override.
  87.         protected function before():void
  88.         {
  89.             GenericLoader.show();
  90.         }
  91.  
  92.         // método executado após todas as chamadas. Pode sofrer override.
  93.         protected function after():void
  94.         {
  95.             GenericLoader.hide();
  96.         }
  97.  
  98.         // método default executado após o sucesso de uma chamada caso não haja um result handler adequado
  99.         // na classe filha.
  100.         protected function defaultResultHandler( event:ResultEvent ):void
  101.         {}
  102.        
  103.         // método default executado após a falha de uma chamada caso não haja um result handler adequado
  104.         // na classe filha
  105.         protected function defaultFaultHandler( event:FaultEvent ):void
  106.         {
  107.             var method:String = getMethod( event.token );
  108.             trace( event.fault.getStackTrace() );
  109.             Alert.show( event.fault.getStackTrace(), "Error in method " + this + "." + method + "()" );
  110.         }
  111.     }
  112. }

Existem dois pontos chaves nesta classe:
1. Os event listeners adicionados no construtor. Eles permitem a execução de métodos genéricos antes e após as chamadas. No nosso caso mostramos um componente genérico de "loading", que reforça para o usuário que alguma tarefa está sendo realizada.
2. A lógica executada no método resultOrFault. Este método descobre qual o nome do método executado no servidor e chama o handler adequado na classe filha, se houver.
Por exemplo, se foi executado um método "listUsers" no servidor e a chamada teve sucesso, a classe Service procura um método chamado "listUsersResult" na classe filha e o chama. Caso houvesse erro na chamada, procuraria pelo método "listUsersFault".
As constantes "Result" e "Fault" são configuradas nas linhas 14 e 15.

O uso desta API é bastante simples. Vamos supor uma classe UserService:

Actionscript:
  1. package br.com.dclick.services
  2. {
  3.     // imports omitidos
  4.     public class UserService extends br.com.dclick.services.Service
  5.     {
  6.         function UserService()
  7.         {
  8.             // id do servico configurado
  9.             super( "clientService" );
  10.         }
  11.  
  12.         function listUsers():void
  13.         {
  14.             service.listUsers();
  15.         }
  16.         function listUsersResult( event:ResultEvent ):void
  17.         {
  18.             var users:ArrayCollection = event.result as ArrayCollection;
  19.             UserModel.getInstance().users = users;
  20.         }
  21.  
  22.         function login( user:UserVO ):void
  23.         {
  24.             service.login( user );
  25.         }
  26.         function loginResult( event:ResultEvent ):void
  27.         {
  28.             UserModel.getInstance().user = event.result as user;
  29.         }
  30.         function loginFault( event:FaultEvent ):void
  31.         {
  32.             Alert.show( "Erro", "Login inválido" );
  33.         }
  34.     }
  35. }

E para usar nosso serviço, basta:

Actionscript:
  1. new UserService().listUsers();
  2. new UserService().login( userVO );

É possível reutilizar uma instância do mesmo serviço, desde que os métodos chamados não utilizem variáveis de instância, o que não é recomendado de qualquer forma:

Actionscript:
  1. var userService:UserService = new UserService();
  2. userService.listUsers();
  3. userService.login( userVO );

O código da classe Service está liberado sob a licensa LGPL.

Compartilhe:

  • RSS
  • Twitter
  • del.icio.us
  • Facebook
  • MySpace
  • LinkedIn
  • Google Bookmarks
Por Filipe Sabella em 21/January/2008 | Comentar | Trackback


No Translations

11 comentários para “Simplificando Remoting com Flex”


Hun…

Gostei da abstração, eu havia implementado algo assim, só que não deixei tão genérica pois sou da abordagem de que quanto mais genérico mais acoplado fica. Porém, esta Service ficou perfeita! muito boa mesmo.

Parabéns a DClick e a você Filipe Sabella.


Poxa! mandaram muito bem!! ficou excelente…

Ficou bastante parecido com o generic DAO que usamos no NEO Framework, só que em as3! Parabéns mesmo!!!

Flw!


Fala Fifi!

Mando muito bem ….

Abração pra todos aí na Dclick….


OLá!

Teria algum exemplo completo de população de um GRID utilizando PHP ?


Hola,

Creo que es bueno tu ejemplo, pero tengo algunas dudas en siertas lineas de código:

#
function UserService()
{
// id do servico configurado
super( “clientService” );
}

aqui llamas al construtor de la clase padre, pero sorpresa el constructo padre se define con 2 String y nunca conficguras el service.destination:

function Service( source:String = “”, destination:String = “” )
{
service = new RemoteObject( source );
service.source = source;
service.addEventListener( InvokeEvent.INVOKE, invoke );
service.addEventListener( ResultEvent.RESULT, result );
service.addEventListener( FaultEvent.FAULT, fault );
}

Soy nuevo en flex, por lo cual te pido un favor, ayudanos a poder usar tu código que funcióne correctamente.

Gracias


O que seria exatamente o
UserModel.getInstance().users = users; ?


@Carlos, dependendo da versão do AMF que está usando, destination e source podem ser diferentes – neste caso você usaria o segundo parâmetro do construtor.

@Rafael, esta linha serve apenas como exemplo – o código da casse UserModel foi omitido. Em sua implementação, você enviaria a lista com os resultados para qualquer lugar que precisasse.


Gostei muito da implementação de vcs, tentei adaptá-la um pouco para ser utilizada da seguinte maneira:

private var retorno:Pessoa;

function inserir(pessoa:Pessoa):Pessoa {
service.inserir(pessoa);
return retorno;
}

function inserirResult( event:ResultEvent ):void {
retorno = event.result as Pessoa;
}

Isto para atribuir a chamada deste serviço diretamente à um objeto:

var pessoa:Pessoa = inserir(pessoa);

Isto para “burlar” a chamada assíncrona ao serviço remoto. Porém esta estratégia não funcionou, creio q a execução do método result ocorra em paralelo e quando o método inserir é encerrado o método result ainda está rodando.. Existe alguma maneira de fazer isto de uma forma melhor??

Obrigado


Boa noite!

Muito útil esta tua abstração. Entretanto, não consegui entender o porque não é recomendável instanciar uma classe (em uma variável) e então chamar seus métodos. Não é recomendável para este caso (porque não iria funcionar), ou fere alguma boa prática, no caso, em flex?

Obrigado e um abraço!


como faço para obter a classe GenericLoader ?

obrigado !


Gostei! mais simples do que a proposta de muitos frameworks por aí!

Adicionar comentário

(requerido)
(requerido, não será publicado)