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!



Flex3 + Lcds + EJB3

Recentemente terminei um curso de EJB3 (FJ-31) na Caelum. Curso muito bom, diga-se de passagem e tirei um peso de minhas costas: Aprender a usar efetivamente o tal do EJB.

A um tempo atraz eu tinha iniciado um blog somente para falar sobre tecnologia JEE e para me motivar a estudar o ejb (http://jeenoob.blogspot.com) mas acabei desistindo quando não consegui fazer o lookup e regigir corretamente os arquivos de deploy para o servidor Glassfish. No entanto agora, que aprendi como fazer o tal do EJB funcionar comecei a correr atraz de como usar ele nos meus projetos.

Hoje trabalho como arquiteto/gerente de produção em um projeto que usa as tecnologias Flex, Java e Microsoft SqlServer. Tirando o SQLServer a plataforma é bem produtiva e o Flex3 vem superando minhas expectativas no que diz respeito a produtividade.

Devido a alguns objetivos do lider do projeto, estou prevendo que vamos precisar se esforçar mais em relação a "robustes" da aplicação. Procurei então como mesclar o Flex, o Java e o EJB que poderia ser uma solução para um problema futuro.

Encontrei referências para o blog do Raynnorris ( http://www.ryannorris.com/ ) que desenvolveu uma Factory para o Lcds que consegue instanciar e fornecer EJBs de forma transparente para os aplicativos Flex. Achei a idéia fabulosa e fui testar.

O post do Raynnorris que explica como usar a biblioteca que ele criou é o seguinte: http://www.ryannorris.com/2007/05/18/using-flex-data-services-with-ejb3/

O post é bastante claro em relação a como configurar o LCDs para usar a Factory que ele criou, e explica muito bem como editar os XMLs do flex.

Simples, pratico e rápido.

No meu caso especifico tive que fazer algumas alterações nos fontes do Ray para que a Factory encontrasse meu servidor de aplicação.

Explico o que fiz para usar a biblioteca dele no post:
Configurando projeto Flex num Tomcat para acessar EJB3 no JBoss


Agora resta a dúvida... como será que o LCDs se comporta com uma grande quantidade de requisições??

O próximo passo vai ser fazer um "stress test" no meu aplicativo... simular as requisições e ver se o LCDs não vai gerar gargalo.

De nada adianta ter uma servidor robusto e uma arquitetura extremamente boa no servidor de aplicação se o servidor web não der conta do recado.

Um outro teste que estou agendando é fazer o LCDs rodar no próprio JBoss (hoje ele roda em um Tomcat 6.0).

Vamos ver no que dá!