quinta-feira, 24 de julho de 2008

Configurando projeto Flex num Tomcat para acessar EJBs do JBoss

Pretendo explicar nesse post como configurar a biblioteca do Ryannorris (http://www.ryannorris.com/) para acessar um servidor de aplicação em uma máquina virtual diferente daquela onde roda seu aplicativo Flex.

O cenário onde apliquei os passos descritos nesse post foi o seguinte:

Sistema Operacional: Mac Os X 10.4
Versão do Java: 1.5
Servidor de aplicação: Jboss 2.0GA (http://www.jboss.org/)
Servidor Web: Esse servidor roda o projeto em Flex + Lcds: Tomcat 6.0 (http://tomcat.apache.org/)

Considere que o projeto em Flex já está funcionando juntamente com o LCDs, e os objetos remotos já estão sendo consumidos pelas aplicações em MXML.

A partir do momento em que o meu projeto já estava configurado e integrado com o LCDs, o que precisei fazer foi o seguinte:

(Ah! Tambem estou supondo que já exista um Application Server com os EJBs configurados e rodando perfeitamente)


Passo 1 - Adicionei as bibliotecas de cliente do JBoss na pasta lib do WEB-INF do meu projeto flex. ( No Flex Builder 3 o path seria algo mais ou menos assim
<projetoflex_home>/WebContent/WEB-INF/lib )

Passo 2 - Adicionei a biblioteca do Ryannorris na mesma pasta lib (<projetoflex_home>/WebContent/WEB-INF/lib). No entanto, precisei modificar a biblioteca do Ryannorris para acessar meu sevidor. Veja mais a frente como fiz.

Passo 3 - Registrar o Factory no services-config.xml do Lcds. O diretorio desse arquivo é o seguinte: /WebContent/WEB-INF/flex/.
Para registrar o Factory precisamos colocar o seguinte trecho de codigo no final do documento:

<factories>
<factory id="ejbFactory" class="com.adobe.ac.ejb.EJB3Factory">
</factories>
Essa classe EJB3Factory faz parte do jar da biblioteca do Ryannorris(flex-ejb-factory.jar).

Passo 4 - Registrar seu EJB no remoting-config.xml ( o arquivo fica no mesmo diretório do services-config.xml)


<destination id="carrinhoCompraBean">
<properties>
<factory>ejbFactory</factory>
<source>nomeRemotoDoSeuEJB</source>
<scope>application</scope>
</properties>
</destination>

O "nomeRemotoDoSeuEJB" é o nome usado para dar Lookup no objeto. Esse nome varia de servidor para servidor. No meu caso, usando o JBoss é algo mais ou menos assim: nomeProjeto/nomeClasseImplementacao/remote.
Ex:
myFlexEjbSample/CarrinhoBean/remote
Pronto. Isso é tudo que precisei configurar do lado do servidor. Iniciei o tomcat, e vi que ele startou corretamente, inclusive carregando o Factory especificado.

Agora vamos ao lado da aplicação MXML.

Na aplicação MXML precisamos apenas mapear mais um objeto remoto, como qualquer outro que já estejamos usando.

O código que usei foi o seguinte:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
<mx:Script>
<![CDATA[
import mx.controls.Alert;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;

/* Listener para o sucesso do metodo */
private function meuMetodoRemotoHandler(result:ResultEvent):void{
Alert.show("Result: "+result," Executou Metodo remoto ");
}
/* Listener para o erro do metodo */
private function meuMetodoRemotoErrorHandler(fault:FaultEvent):void{
Alert.show("Fault: "+fault," Erro ao executar metodo remoto ");
}
]]>
</mx:Script>

<mx:RemoteObject id="meuEjb" destination="carrinhoCompraBean">
<mx:method name="meuMetodoRemoto" result="meuMetodoRemotoHandler(event)" fault="meuMetodoRemotoErrorHandler(event)"/>
</mx:RemoteObject>
<mx:Button click="meuEjb.meuMetodoremoto()" label="Executar" x="250" y="218"/>

</mx:Application>


Na teoria é somente isso que precisamos. Se o seu serivor de aplicação e o servidor onde roda o LCDS forem o mesmo, provavelmente isso funciona. Como expliquei no inicio do post, o ambiente em que desenvolvo é diferente. O servidor de aplicação é em uma maquina, o meu servidor onde roda o LCDs é em outra... simplesmente o Factory não vai encontrar o meu EJB.

Por padrão, quando vamos desenvolver um cliente para um EJB, em outra máquina virtual, e em uma máquina com outro IP, precisamos adicionar um arquivo jndi.properties na pasta "src" do nosso projeto com as informacões para que nosso "initial context" encontre nosso servidor de aplicação. Eu tentei fazer isso. Adicionei esse arquivo na pasta source (que quando exportado vai para a pasta "/WEB-INF/classes" do projeto. Não funcionou. A Factory ainda não encontrava o servidor de aplicação.
Testei colocar o arquivo jndi.properties em outros diretórios, mas também não tive sucesso. Parti para o plano B, alterar os fontes do nosso amigo Ryannorris. ( a biblioteca ja vem com os fonts configurados para trabalhar no eclipse!! Uma mão na roda!)

Joguei a pasta dos fontes em um projeto novo no eclipse e dei uma bisbilhotada! Poxa.. como é simples fazer essa tal de Factory! Achei fantástico o trabalho do Ryannorris!

A idéia da Factory é a seguinte: Extender uma interface do LCDS (flex.messaging.FlexFactory) e implementar os métodos para criar seus objetos. No nosso caso, fazer o lookup dos EJBs. Como não poderia deixar de ser, em algum lugar vamos ter que criar nosso InitialContext e fazer o "lookup" do ejb. Fui na veia procurando já essa instanciação do Initial context. Isso acontece na classe "com.adobe.ac.ejb.LocalJNDIResourceLocator" da biblioteca do Ryannorris. Bom, como não consegui externalizar os parametros, criei um HashMap com eles e passei no construtor do InitialContext. Sei que isso é "feio" de se fazer, porque torna a implementação totalmente acoplada, não fica nada genérico. Toda vez que eu mudar meu servidor de aplicação de IP terei que alterar essa biblioteca. Uma alternativa imbutir o codigo do Ryannorris no meu projeto, mas não pretendo fazer isso. Por enquanto deixarei o código fixo, simplemente para fazer funcionar o lookup. Futuramente me preocuparei em externalizar essas configurações.

Vamos ao código. O que adicionei foram as seguintes linhas no método "locate" da classe do Ryannorris:


Hashtable hashParametros = new Hashtable();
hashParametros.put("java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory");
hashParametros.put("java.naming.provider.url", "jnp://10.0.0.22:1099");
final Context ctx = new InitialContext(hashParametros);



O código completo do método ficou da seguinte maneira:


public Object locate(final String name ) throws ResourceException {
try {
Hashtable hashParametros = new Hashtable();
hashParametros.put("java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory");
hashParametros.put("java.naming.provider.url", "jnp://localhost:1099");
final Context ctx = new InitialContext(hashParametros);
final Object res = ctx.lookup( name );
return res;
} catch ( NamingException e ) {
// tratamento da exception
}
}


Percebam que a única coisa que mudei foram realmente os parametros. Esses são os parametros que ficariam dentro do jndi.properties

E.. Voila! Funcionou!

Executei a aplicação flex e meu método remoto funcionou perfeitamente, invocando oEJB no outro servidor corretamente!

Se alguém tem uma solução simples e mais inteligente que a minha (heheh coisa que não é dificil) ou sabe onde colocar o arquivo jndi.porpeties para trabalhar com a biblioteca do Ryannorris, por favor me diga!

Bom, é isso ai, acho que fazer funcionar o EJB não foi tão dificil. Agora quero ver é usar o EJB de uma forma eficiente no projeto! é meu próximo desafio!



1 comments:

Flávio Otoni disse...

Ola Giuliano,

Muito bom o post, tem como vc disponibilizar o codigo fonte tanto do projeto no tomcat como o do JBoss?

Preciso fazer a mesma situação.

E fiquei na duvida referente ao projeto que esta rodando no tomcat.

flaviootoni@gmail.com