abr

22

Java + FLEX Parte 1 -> Integrando Java e FLEX

By Felipe Saab

Hoje iremos abordar uma integração (que eu particularmente acho) fantástica: Java e FLEX (cuja versão mais atual se chama Flash Builder 4).

A primeira grande (super, ultra, …) vantagem que me vem na cabeça quando falo deste assunto é a facilidade para criar belas interfaces. Eu sou uma negação quando o assunto é criar interfaces, design me dá arrepios…

Outro ponto que é um grande vantagem é que toda a interface roda em cima do Flash, ou seja, basta ter o flash instalado no navegador e é certeza que o programa vai abrir exatamente do jeito que você o projetou! Não dá pau de navegador pra navegador!!!! Quem meche com CSS sabe do que eu estou falando, tua página está perfeita no Firefox e quando abre ela no IE dá até medo. Hahahaha quem nunca passou por isso que atire a primeira pedra.

Enfim, veremos como é possível ter uma interface fácil de desenvolver e ao mesmo tempo amigável para o usuário final com um backend poderosíssimo rodando o nosso bom e (não tão) velho Java e todos os frameworks que estamos acostumados a usar, como Spring e Hibernate.


Esta série será dividida (a princípio) em 2 partes:

  • Parte 1 – Comunicação básica entre Java e FLEX
  • Parte 2 – Integrando Spring e Hibernate na nossa aplicação

 

Antes de começarmos propriamente dito com o tutorial deixe me falar como funciona essa integração: como o FLEX roda na máquina virtual do Flash que fica no browser (diferente do AIR que é desktop) a nossa aplicação Java também vai rodar em um servidor de aplicativos web (neste caso o Tomcat) para eles poderem se comunicar.

Ambos (FLEX e Java) rodando na web fazem com que o canal de comunicação fique na nossa cara: a própria WEB! Sendo assim, toda vez que o FLEX precisa se comunicar com o Java (quando o usuário manda salvar um objeto por exemplo) o FLEX envia uma mensagem (utilizando o protocolo AMF) para o Java através da WEB.

No servidor Java vai haver um servlet responsável por receber essas mensagens, decodificá-las, transformar os dados em objetos Java e encaminhar para a classe e método que tais objetos devem ser encaminhados e retornar o resultado para o FLEX.

Dita a teoria vamos a prática! hehehe

Vamos precisar ter algumas coisas instaladas:

Eclipse IDE
Flash Builder 4 Eclipse Plugin (Trial por 60 dias)
BlazeDS 4 (Site da Adobe) ou local

Caso você já tenha o Eclipse instalado em sua máquina basta informar o caminho para ele durante a instalação que ele já instalará todos os plugins necessários. Caso não tenha o Eclipse basta baixar uma versão do Flash Builder que já vem com uma versão da IDE (porém não é a mais recente).

Depois que tudo estiver instalado vamos começar importando a estrutura base do projeto Java que vem junto com o arquivo do BlazeDS. No Eclipse selecione o menu Arquivo > Importar e selecione Arquivo WAR na categoria Web.

image

Na próxima tela informe o caminho para o arquivo blazeds.war (que veio no arquivo do BlazeDS que você baixou) e informe o nome que o novo projeto vai ter. Iremos fazer uma simples (e bonita! hehe) agenda de contatos onde guardaremos os telefones e emails dos nossos amigos, então irei chamar o projeto de AgendaJ.

*OBS: Para o desenvolvimento Java + FLEX são criados 2 projetos: uma projeto Java e um projeto FLEX. Eu tenho o costume (e diria que é uma boa prática) de dar o mesmo nome aos dois projetos adicionando como sufixo J ou F caso o projeto seja Java ou FLEX, respectivamente.

image

Ok. Agora podemos dar uma espiada para ver como funciona toda a configuração da nossa aplicação Java. Vamos começar olhando para o deployment descriptor WebContent/WEB-INF/web.xml*:

*Apaguei os comentários que vieram junto com o arquivo pois não vamos utilizá-los.

<web-app>
	<display-name>BlazeDS</display-name>
	<description>BlazeDS Application</description>
 
	<!-- Http Flex Session attribute and binding listener support -->
	<listener>
		<listener-class>flex.messaging.HttpFlexSession</listener-class>
	</listener>
 
	<!-- MessageBroker Servlet -->
	<servlet>
		<servlet-name>MessageBrokerServlet</servlet-name>
		<display-name>MessageBrokerServlet</display-name>
		<servlet-class>flex.messaging.MessageBrokerServlet</servlet-class>
		<init-param>
			<param-name>services.configuration.file</param-name>
			<param-value>/WEB-INF/flex/services-config.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>
 
	<servlet-mapping>
		<servlet-name>MessageBrokerServlet</servlet-name>
		<url-pattern>/messagebroker/*</url-pattern>
	</servlet-mapping>
 
	<welcome-file-list>
		<welcome-file>index.html</welcome-file>
		<welcome-file>index.htm</welcome-file>
	</welcome-file-list>
</web-app>

Foi definido um servlet chamado MessageBrokerServlet da classe flex.messaging.MessageBrokerServlet que recebe o parâmetro services.configuration.file indicando qual é o arquivo que contém as configurações dos serviços do FLEX. Conseguimos ver também que o MessageBrokerServlet recebe todas as requisições que chegam para a URL “…/messagebroker/*”, ou seja, sempre que o FLEX envia uma requisição para o Java já sabemos que é seguindo este padrão de URL (caso ainda não esteja claro já já vai ficar Alegre).

Olhando o arquivo de configuração dos serviços (WebContent/WEB-INF/flex/services-config.xml)* podemos observar que:

*Retirei alguns comentários e fiz alguns pequenos ajustes nesse arquivo.

  • ele inclui três outros arquivos de configuração através da tag <service-include> dentro da tag <service>;
  • diz qual classe vai ser responsável pela segurança (autenticação e autorização) em um servidor de aplicativos J2EE;
  • define quais canais estarão disponíveis para a comunicação através da tag <channels>. Cada canal definido dentro desta tag indica o mapeamento de um padrão de URL para uma classe em específico, por exemplo, a classe flex.messaging.endpoints.AMFEndpoint irá receber todas as requisições que forem para a URL “…/messagebroker/amf”;
  • configura o Logging das comunicações;
  • configura o redeploy automático de alguns arquivos que ele fica “observando” em intervalos de tempo definidos.
<services-config>
    <services>
        <service-include file-path="remoting-config.xml" />
        <service-include file-path="proxy-config.xml" />
        <service-include file-path="messaging-config.xml" />
    </services>
 
    <security>
        <login-command class="flex.messaging.security.TomcatLoginCommand" server="Tomcat"/>
    </security>
 
    <channels>
        <channel-definition id="my-amf" class="mx.messaging.channels.AMFChannel">
            <endpoint url="http://{server.name}:{server.port}/{context.root}/messagebroker/amf" class="flex.messaging.endpoints.AMFEndpoint"/>
        </channel-definition>
 
        <channel-definition id="my-secure-amf" class="mx.messaging.channels.SecureAMFChannel">
            <endpoint url="https://{server.name}:{server.port}/{context.root}/messagebroker/amfsecure" class="flex.messaging.endpoints.SecureAMFEndpoint"/>
            <properties>
                <add-no-cache-headers>false</add-no-cache-headers>
            </properties>
        </channel-definition>
 
        <channel-definition id="my-polling-amf" class="mx.messaging.channels.AMFChannel">
            <endpoint url="http://{server.name}:{server.port}/{context.root}/messagebroker/amfpolling" class="flex.messaging.endpoints.AMFEndpoint"/>
            <properties>
                <polling-enabled>true</polling-enabled>
                <polling-interval-seconds>4</polling-interval-seconds>
            </properties>
        </channel-definition>
    </channels>
 
    <logging>
        <target class="flex.messaging.log.ConsoleTarget" level="Error">
            <properties>
                <prefix>[BlazeDS] </prefix>
                <includeDate>true</includeDate>
                <includeTime>true</includeTime>
                <includeLevel>true</includeLevel>
                <includeCategory>true</includeCategory>
            </properties>
            <filters>
                <pattern>Endpoint.*</pattern>
                <pattern>Service.*</pattern>
                <pattern>Configuration</pattern>
            </filters>
        </target>
    </logging>
 
    <system>
        <redeploy>
            <enabled>true</enabled>
            <watch-interval>20</watch-interval>
            <watch-file>{context.root}/WEB-INF/flex/services-config.xml</watch-file>
            <watch-file>{context.root}/WEB-INF/flex/proxy-config.xml</watch-file>
            <watch-file>{context.root}/WEB-INF/flex/remoting-config.xml</watch-file>
            <watch-file>{context.root}/WEB-INF/flex/messaging-config.xml</watch-file>
            <touch-file>{context.root}/WEB-INF/web.xml</touch-file>
        </redeploy>
    </system>
</services-config>

OBS: perceba que todos os canais tem o padrão de URL parecido (“…/messagebroker/…”). Por isso que o MessageBrokerServlet recebe todas as requisições para a URL “…/messagebroker/*”, ele verifica para qual canal ela vai e encaminha para a classe correta. Smiley piscando

Os outros arquivos de configuração não contém nada que precise chamar a atenção agora. Então já dá pra dizer que sabemos como o Java recebe e trata as requisições do FLEX! Smiley de boca aberta

Vamos preparar o backend da nossa aplicação então: vamos criar a classe que vai representar um contato da nossa agenda e um serviço para executar as regras de negócio da nossa aplicação:

Contato.java

public class Contato implements Serializable {
 
	private static final long serialVersionUID = -1549466344815889020L;
 
	private String nome;
 
	private String telefone;
 
	private String email;
 
	public String getNome() {
		return nome;
	}
 
	public void setNome(String nome) {
		this.nome = nome;
	}
 
	public String getTelefone() {
		return telefone;
	}
 
	public void setTelefone(String telefone) {
		this.telefone = telefone;
	}
 
	public String getEmail() {
		return email;
	}
 
	public void setEmail(String email) {
		this.email = email;
	}
 
	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		sb.append("[");
		sb.append(this.getNome()+",");
		sb.append(this.getTelefone()+",");
		sb.append(this.getEmail());
		sb.append("]");
		return sb.toString();
	}
}

A classe que representa um contato possui apenas o nome, telefone e email do contato. Suficiente para nossa pequena agenda. Alegre

OBS: essa classe implementa a interface java.io.Serializable pois ela vai trafegar na Web (do Java para o FLEX e vice versa). É aconselhavel que todo objeto que for ser transmitido através de um canal (socket, web, etc) implemente esta interface e tenha o serialVersionUID (que o Eclipse gerou pra mim hehe) que indica um número único para identificar aquela classe.

Vamos ver agora o serviço que irá tratar a inserção de novos contatos na nossa agenda:

ContatoService.java

public class ContatoService {
 
	private static List<contato> agenda = new ArrayList<contato>();
 
	public void salvar(Contato contato) {
		ContatoService.agenda.add(contato);
		System.out.println("Contato adicionado: " + contato);
	}
 
	public List<contato> getAgenda() {
		return ContatoService.agenda;
	}
}

Como ainda não vamos utilizar o banco de dados e o Hibernate nesta parte do tutorial vamos simular um “banco de dados” (está entre aspas duplas tá.. hehehe) com uma lista estática.

O método salvar(Contato) simplesmente adiciona um novo contato na agenda e o método getAgenda() retorna a nossa agenda com todos os contatos.

Esses métodos vão ser utilizados para o FLEX pedir para o Java salvar um novo contato na agenda e para ele recuperar a agenda com os contatos atualizados.

Para finalizar o desenvolvimento do nosso backend precisamos apenas expor nossa classe de serviço para que as classes que recebem a requisição do FLEX (pacote flex.messaging.endpoint) consigam localizá-la em tempo de execução.

Para isso adicionaremos uma nova tag <destination> no arquivo WebContent/WEB-INF/flex/remoting-config.xml informando um ID para este novo destino e qual classe é responsável por tratar as requisições destinadas a ele. Desta maneira nós informaremos este mesmo ID no FLEX quando precisarmos chamar algum método desta classe.

<service id="remoting-service" class="flex.messaging.services.RemotingService">
 
	<adapters>
		<adapter-definition id="java-object" class="flex.messaging.services.remoting.adapters.JavaAdapter" default="true" />
	</adapters>
 
	<default-channels>
		<channel ref="my-amf" />
	</default-channels>
 
	<destination id="contatoService">
		<properties>
			<source>br.blog.javasimples.agenda.service.ContatoService</source>
		</properties>
	</destination>
 
</service>

Agora já podemos deixar o Java um pouco de lado e ir pra parte nova: o FLEX! Smiley de boca aberta

Vamos criar um novo projeto no Eclipse: Arquivo > Novo > Projeto. Na categoria Flash Builder vamos escolher um Flex Project. Na primeira tela configuramos o nome do projeto (vou colocar AgendaF como já comentei lá em cima), dizemos que ele vai rodar na Web e vai se comunicar com um servidor J2EE utilizando o BlazeDS.

OBS: tenha certeza de que o checkbox “Create combined Java/Flex project using WTP” está DESMARCADO.

image

Na próxima tela informamos algumas configurações do servidor J2EE (no meu caso o Tomcat):

  • Root folder –> O caminho pra raiz da aplicação Java no disco;
  • Root URL –> O caminho para a raiz da aplicação Java através do servidor;
  • Context Root –> O contexto da aplicação no servidor;
  • Output Folder (o Eclipse sugere) –> Caminho para a pasta onde o projeto FLEX será compilado e ficará “guardado”. Fica dentro da raiz da aplicação Java pra ser executada junto com o servidor.

 

image

Na próxima tela temos que adicionar uma pasta que será a raíz dos códigos fonte. Na aba Source Path apenas clique em Add Folder… e crie uma nova pasta com o nome src.

image

Após criado o projeto temos mais um ajuste a fazer: caso o Flash Builder 4 que você tenha baixado veio na língua inglesa e seu sistema operacional esteja em português o Eclipse vai criar uma configuração em português (porque ele “reflete” o locale do SO) e o Flash Builder não vai se dar muito bem com ele não… hehehe

Para corrigir esse parâmetro vamos nas propriedades do projeto (botão direito no projeto > Propriedades) e na categoria Flex Compiler existe o campo de texto Additional compiler arguments onde está escrito “-services …\services-config.xml –locale pt_BR”. Vamos apenas trocar o parâmetro “-locale pt_BR” por “-locale en_US”.

image

Legal, vamos começar a programar mesmo. Mude a perspectiva do Eclipe para Flash (caso ainda não tenha o feito). Vamos entrar em outro mundo agora, vamos deixar o Java um pouco de lado e programar em ActionScript 3 (aka AS3), linguagem utilizada na plataforma FLEX (e outras da Adobe).

OBS: eu não vou entrar em detalhes da linguagem propriamente dita, uma vez que o nome do blog não é AS3 Simples.. kkkkkkkk Mas não tem diferenças muito grandes não, uma pessoa que está acostumada com Java consegue pegar o AS3 rapidinho. Smiley de boca aberta

Vamos começar criando a classe que representa um contato da agenda. Vamos criar um pacote com o mesmo nome do pacote no Java (outra boa prática) e criar uma ActionScript Class com o mesmo nome (Contato.as):

image

package br.blog.javasimples.agenda.model
{
	[Bindable]
	[RemoteClass(alias="br.blog.javasimples.agenda.model.Contato")]
	public class Contato
	{
		public var nome:String;
		public var telefone:String;
		public var email:String;
 
		public function Contato()
		{
		}
	}
}

A classe que representa o contato do lado do FLEX deve possuir os atributos com os mesmos nomes do nome de atributo extraído dos getters da classe Java. Estranho né… heheheh Vamos ver com um pequeno exemplo:

Na classe Contato.java eu tenho os getters getNome(), getTelefone() e getEmail(). Quando o BlazeDS vai enviar os dados do Java para o FLEX ele vai fazer um algorítmo que siga uma lógica parecida com essa (não olhei no código fonte pra saber se é assim mesmo, porém o resultado final é algo assim):

String getterName = "getEmail";
String attributeName = getterName.replaceFirst("get",""); // attributeName == "Email"
attributeName = attributeName.substring(0,1).toUpperCase() + attributeName.substring(1); // // attributeName == "email"

Ou seja, com os getters getNome(), getTelefone() e getEmail() o BlazeDS vai enviar os valores para a classe Contato.as esperando que existam os atributos nome, telefone e email.

O BlazeDS segue esse mesmo padrão para enviar os dados do FLEX para o Java, ou seja, os setters devem seguir esse mesmo padrão de nomenclatura.

Continuando com a nossa classe, algo novo para quem nunca usou AS3 são as anotações: [Bindable] e [RemoteClass(alias)].

Se essas anotações fossem em Java seriam: @Bindable e @RemoteClass(alias=”…”). Só a primeira vista que confunde um pouco, mas é a mesma coisa.

Segue o significado de tais anotações:

  • [Bindable] – é algo como a implementação do design pattern Observer. Todo lugar que estiver referenciando alguma propriedade dessa classe será imediatamente avisado assim que alguma delas sofrer alguma mudança;
  • [RemoteClasse(alias)] – indica para o BlazeDS qual a classe correspondente a essa na aplicação Java.

 

Tão simples quanto fazer um POJO no Java não?! hehehe

Agora vem a parte que me fascina no FLEX: layout! Smiley de boca aberta

Vamos abrir o arquivo AgendaF.mxml que o Eclipse gerou para nós. Ao mudar para a aba design nós podemos criar a nossa tela com um simples clicar e arrastar de componentes. Arrastei alguns componentes e mudei os labels e em menos de 10 minutos eu fiz o design da nossa aplicação de exemplo:

image

O código do arquivo AgendaF.mxml ficou assim:

<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
               xmlns:s="library://ns.adobe.com/flex/spark"
               xmlns:mx="library://ns.adobe.com/flex/mx">
 
    <mx:Form width="500">
        <mx:FormItem label="Nome" width="100%" direction="horizontal">
            <s:TextInput id="txtNome" width="220"/>
            <mx:Spacer width="100%" height="10"/>
            <mx:FormItem label="Telefone" direction="horizontal">
                <s:TextInput width="100" id="txtTelefone"/>
            </mx:FormItem>
        </mx:FormItem>
        <mx:FormItem label="Email" width="100%" direction="horizontal">
            <s:TextInput width="220" id="txtEmail"/>
            <mx:Spacer width="100%" height="10"/>
            <s:Button label="Salvar"/>
        </mx:FormItem>
        <mx:HBox width="100%">
            <mx:DataGrid width="100%">
                <mx:columns>
                    <mx:DataGridColumn headerText="Nome"/>
                    <mx:DataGridColumn headerText="Telefone"/>
                    <mx:DataGridColumn headerText="Email"/>
                </mx:columns>
            </mx:DataGrid>
        </mx:HBox>
    </mx:Form>
 
</s:Application>

OBS: perceba que os TextInputs tem ids definidos. Isso para que possamos recuperar os seus valores via programação logo mais.

Para vermos como vai ficar esse layout no browser basta rodar o projeto AgendaJ primeiro e depois o AgendaF.

  • AgendaJ – botão direito no projeto > Run As > Run on Server
  • AgendaF – botão direito no projeto > Run As > Web Application

 

image

Vamos fazer isso funcionar agora!

Vamos começar com a funcionalidade de salvar um contato: toda vez que o botão Salvar for pressionado iremos criar um objeto Contato e enviar para o Java, mais especificamente para o método salvar(Contato) da classe ContatoService.

Para isso vamos precisar de uma área para programar em ActionScript dentro do arquivo AgendaF.mxml, que como o nome indica (e conseguimos ver através da aba Source) é um XML. Tal área fica disponível através da tag <fx:Script>.

Segue o código que salva um novo contato:

<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
               xmlns:s="library://ns.adobe.com/flex/spark"
               xmlns:mx="library://ns.adobe.com/flex/mx">
 
<fx:Script>
        <![CDATA[
            import br.blog.javasimples.agenda.model.Contato;
 
            import mx.controls.Alert;
            import mx.rpc.AsyncToken;
            import mx.rpc.Responder;
            import mx.rpc.events.FaultEvent;
            import mx.rpc.events.ResultEvent;
            import mx.rpc.remoting.RemoteObject;
 
            private var remoteObject:RemoteObject=new RemoteObject("contatoService");
 
            private function salvaContato():void
            {
                var contato:Contato=new Contato();
                contato.nome=txtNome.text;
                contato.telefone=txtTelefone.text;
                contato.email=txtEmail.text;
 
                // Insere esse contato na lista do Java
                var token:AsyncToken=remoteObject.salvar(contato);
                token.addResponder(new mx.rpc.Responder(salvarSucesso, erro));
            }
 
            private function salvarSucesso(result:ResultEvent):void
            {
                Alert.show(“Contato salvo com sucesso!);
            }
 
            private function erro(event:FaultEvent):void
            {
                Alert.show("Ocorreu o seguinte erro: " + event.fault.faultString);
            }
 
        ]]>
    </fx:Script>
 
   <!-- ..... -->
 
</s:Application>

Vamos começar de cima para baixo:

Primeiro temos a declaração e instanciação de um objeto da classe RemoteObject. Essa classe é a responsável por fazer a comunicação com outra classe do lado do Java. Para criarmos esse vínculo basta passarmos o ID que definimos para a classe Java (no arquivo WebContent/WEB-INF/flex/remoting-config.xml) no construtor do RemoteObject.

No nosso caso a classe ContatoService.java está vinculada dentro da tag <destination id=”contatoService”> no arquivo remoting-config.xml então utilizamos este mesmo ID no construtor do nosso obejto.

Vamos olhar agora para o método salvaContato(): é criado um novo objeto Contato e seus dados são preenchidos com os valores dos TextInputs. Logo depois utilizamos o RemoteObject para invocar o método salvar(Contato) da classe a que ele está vinculado (ContatoService.java).

Para invocar os métodos remotos nós temos que imaginar que o RemoteObject é a classe que queremos utilizar (neste caso ContatoService). Exemplo: na classe ContatoService nós temos o método salvar(Contato). Para executar esse método através do RemoteObject basta executar um método com o mesmo nome e com os mesmos parâmetros (super usual né Alegre).

Continuando, todas as chamadas a métodos remotos são assíncronas, ou seja, temos que esperar voltar uma resposta do Java para receber o retorno do método ou uma possível falha (caso seja jogada alguma Exception).

Para lidarmos com isso, todo método remoto executado através do RemoteObject retorna um AsyncToken. Através deste AsyncToken nós podemos ser notificados sempre que o método remoto retorna algo (o retorno do método ou uma exception).

Para isso nós adicionamos um Responder no AsyncToken. O construtor do Responder requer que passemos duas funções de parâmetro: a primeira será chamada quando o método remoto for concluído com sucesso e a segunda será chamada quando o método remoto jogar um erro.

A função que vai ser chamada quando o método remoto for concluído vai receber um ResultEvent como parâmetro (na grande maioria das vezes, porém não é regra) e caso o método remoto retornasse algum objeto nós conseguimos acessar esse retorno atavés do atributo result do objeto ResultEvent.

Já no caso da função que vai ser chamada quando o método remoto jogar um erro, ela recebe um FaultEvent (na maioria dos casos, porém também não é regra). E para acessar detalhes sobre o erro que foi jogado utilizamos o atributo fault do objeto FaultEvent.

Voltando para o nosso código, quando o método ContatoService.salvar(Contato) terminar de salvar o contato na lista nós exibiremos uma mensagem informando o usuário de que o novo contato foi salvo.

Ao executarmos o projeto e inserir um novo contato acontece o seguinte:

image

E se observarmos o console do Eclipse também vamos ver:

image

Porém a grid ainda continua vazia… Vamos configurá-la então:

Todos os registros que aparecem em uma DataGrid devem estar em um  array, uma lista ou algo parecido (por hora um Array está ótimo! hehehe). O Array que contém os dados que serão mostrados na DataGrid deve alimentar o atributo dataProvider dela. Vamos então criar um Array e alimentar a nossa grid:

<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
               xmlns:s="library://ns.adobe.com/flex/spark"
               xmlns:mx="library://ns.adobe.com/flex/mx">
 
    <fx:Script>
        <![CDATA[
            import br.blog.javasimples.agenda.model.Contato;
 
            import mx.collections.ArrayCollection;
            import mx.controls.Alert;
            import mx.rpc.AsyncToken;
            import mx.rpc.Responder;
            import mx.rpc.events.FaultEvent;
            import mx.rpc.events.ResultEvent;
            import mx.rpc.remoting.RemoteObject;
 
            private var remoteObject:RemoteObject=new RemoteObject("contatoService");
 
            [Bindable]
            private var agenda:ArrayCollection=new ArrayCollection();
 
            private function salvaContato():void
            {
                var contato:Contato=new Contato();
                contato.nome=txtNome.text;
                contato.telefone=txtTelefone.text;
                contato.email=txtEmail.text;
 
                // Insere esse contato na lista do Java
                var token:AsyncToken=remoteObject.salvar(contato);
                token.addResponder(new mx.rpc.Responder(salvarSucesso, erro));
            }
 
            private function salvarSucesso(result:ResultEvent):void
            {
                Alert.show("Contato salvo com sucesso!");
            }
 
            private function erro(event:FaultEvent):void
            {
                Alert.show("Ocorreu o seguinte erro: " + event.fault.faultString);
            }
 
        ]]>
    </fx:Script>
 
    <mx:Form width="500">
        <mx:FormItem label="Nome" width="100%" direction="horizontal">
            <s:TextInput id="txtNome" width="220"/>
            <mx:Spacer width="100%" height="10"/>
            <mx:FormItem label="Telefone" direction="horizontal">
                <s:TextInput width="100" id="txtTelefone"/>
            </mx:FormItem>
        </mx:FormItem>
        <mx:FormItem label="Email" width="100%" direction="horizontal">
            <s:TextInput width="220" id="txtEmail"/>
            <mx:Spacer width="100%" height="10"/>
            <s:Button label="Salvar" click="this.salvaContato()"/>
        </mx:FormItem>
        <mx:HBox width="100%">
            <mx:DataGrid width="100%" dataProvider="{this.agenda}">
                <mx:columns>
                    <mx:DataGridColumn headerText="Nome" dataField="nome"/>
                    <mx:DataGridColumn headerText="Telefone" dataField="telefone"/>
                    <mx:DataGridColumn headerText="Email" dataField="email"/>
                </mx:columns>
            </mx:DataGrid>
        </mx:HBox>
    </mx:Form>
 
</s:Application>

Foi criado um ArrayCollection chamado agenda e talvariável foi passada para o atributo dataProvider da DataGrid, sendo assim todos os elementos que estiverem dentro do ArrayCollection irão aparecer na grid também.

Indicamos também nas colunas qual atributo dos objetos que estão dentro do dataProvider deve ser exibido através do atributo dataField.

Tudo o que precisamos fazer agora é adicionar os nossos contatos nesse ArrayCollection agora. Vamos fazer o seguinte, toda vez que um novo contato for salvo nós iremos chamar o método getAgenda() da classe ContatoService e armazenar o retorno do método na variável agenda que acabamos de criar, assim manteremos a lista sempre atualizada e consequentemente a grid com todos os nossos contatos.

<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
               xmlns:s="library://ns.adobe.com/flex/spark"
               xmlns:mx="library://ns.adobe.com/flex/mx">
 
    <fx:Script>
        <![CDATA[
            import br.blog.javasimples.agenda.model.Contato;
 
            import mx.collections.ArrayCollection;
            import mx.controls.Alert;
            import mx.rpc.AsyncToken;
            import mx.rpc.Responder;
            import mx.rpc.events.FaultEvent;
            import mx.rpc.events.ResultEvent;
            import mx.rpc.remoting.RemoteObject;
 
            private var remoteObject:RemoteObject=new RemoteObject("contatoService");
 
            [Bindable]
            private var agenda:ArrayCollection=new ArrayCollection();
 
            private function salvaContato():void
            {
                var contato:Contato=new Contato();
                contato.nome=txtNome.text;
                contato.telefone=txtTelefone.text;
                contato.email=txtEmail.text;
 
                // Insere esse contato na lista do Java
                var token:AsyncToken=remoteObject.salvar(contato);
                token.addResponder(new mx.rpc.Responder(salvarSucesso, erro));
            }
 
            private function salvarSucesso(result:ResultEvent):void
            {
                Alert.show("Contato salvo com sucesso!");
 
                // Recupera a lista completa
                var token:AsyncToken=remoteObject.getAgenda();
                token.addResponder(new mx.rpc.Responder(getAgendaSucesso, erro));
            }
 
            private function getAgendaSucesso(event:ResultEvent):void
            {
                this.agenda=event.result as ArrayCollection;
            }
 
            private function erro(event:FaultEvent):void
            {
                Alert.show("Ocorreu o seguinte erro: " + event.fault.faultString);
            }
 
        ]]>
    </fx:Script>
 
    <mx:Form width="500">
        <mx:FormItem label="Nome" width="100%" direction="horizontal">
            <s:TextInput id="txtNome" width="220"/>
            <mx:Spacer width="100%" height="10"/>
            <mx:FormItem label="Telefone" direction="horizontal">
                <s:TextInput width="100" id="txtTelefone"/>
            </mx:FormItem>
        </mx:FormItem>
        <mx:FormItem label="Email" width="100%" direction="horizontal">
            <s:TextInput width="220" id="txtEmail"/>
            <mx:Spacer width="100%" height="10"/>
            <s:Button label="Salvar" click="this.salvaContato()"/>
        </mx:FormItem>
        <mx:HBox width="100%">
            <mx:DataGrid width="100%" dataProvider="{this.agenda}">
                <mx:columns>
                    <mx:DataGridColumn headerText="Nome" dataField="nome"/>
                    <mx:DataGridColumn headerText="Telefone" dataField="telefone"/>
                    <mx:DataGridColumn headerText="Email" dataField="email"/>
                </mx:columns>
            </mx:DataGrid>
        </mx:HBox>
    </mx:Form>
 
</s:Application>

Prontinho! Ao rodar a aplicação de novo e adicionar mais contatos conseguimos ver que agora tudo está funcionando perfeitamente! Smiley de boca aberta

image

Bom, confesso que este post ficou um pouco maior do que eu imaginei inicialmente… hehehe

Mas o importante é que deu pra passar a idéia sobre como funciona a integração do Java e do FLEX e como pessoas cujo dom na área de design não é muito elevado (como eu) podem fazer aplicações bonitas e amigáveis para o usuário final! Smiley de boca aberta

O código fonte desses projeto está disponível aqui.

Veja a parte 2 também..! ;)

Na parte 2 vamos integrar o Spring e o Hibernate nesse nosso pequeno projeto e ficará muito claro como podemos conseguir uma produtividade incrível aliada com um resultado fantástico para o usuário utilizando essas tecnologias e esses frameworks.

[]s e até a próxima,

Saab.

PS: Gostou? Não gostou? Ajudou? Não ajudou? Quebrou um galho? Perdeu 10 minutos da tua vida lendo? Comente! Divulgue! Fale mal! Compartilhe sua opinião com toda a comunidade! Smiley de boca aberta

30 Responses so far

Simplesmente fantástico..

bomm!

Parabéns mesmo! Post fantástico!

opaaa bacana, muito claro o tutorial… parabéns.
A parte II já está disponível??
abraços.!!

PERFEITO. esta só faltando a parte 2 …

Aproveitando, fica aqui minha sugestão… creio que seja a grande duvida na maior parte do pessoal que esta iniciando em flex + java.

Projeto possui um escopo de Agenda simples, sem relacionamento…
Já que irá usar os frameworks Hibernate e Spring, poderia bolar algo como (Agenda…Eventos)… simulando um relacionamento de N..N ou 1…N.

Sei que estenderia o tutorial, mas é o diferencial dos demais tutos que tem por ai….
Vlw abraços… até momento show.

Ótima sugestão Ederson! :D
Porém como você mesmo disse, se eu fizer isso na parte 2 ela vai ficar muito grande, ai fica complicado..
Vou deixar esse assunto para a parte 3 (que nem estava planejada.. hehehe).

[]s e muito obrigado pela dica, vai ser muito show!
Saab.

Bacana que curtiu Saab, aguardo atentamente as partes II e III… abraços e sucesso!

Cara, Meus Parabens, vc não imagina o quando me ajudou com o sue POST, estou inciando em FLEX com Java porém li muita coisa que cada vez mais deixava a minha cabeça mais bagunçada ainda… mas foi incrivel eu li seu POST e entendi detalhe por Detalhe…
Sou Imensamente agradecido…
Que DEUS continue a lhe abeçoar

[]s

Fico muito feliz em saber que te ajudei Haroldo! :D

[]s

Felipe, estou tendo um problema ao seguir a risca seu tutorial…

Na parte II voce informa que pode ser excluído a tag do remoting-config.xml e adicionar a anotação @RemotingDestination informando o mesmo id no caso ‘contatoService’, porém executei esta operação e no click do salvar o FaultEvent me informa o erro abaixo.

Ocorreu o seguinte erro: [MessagingError message='Destination 'contatoService' either does not exist or the destination has no channels defined (and the application does not define any default channels.)']

Obs: Por segurança segui a I parte do tutorial e o mesmo executou e funcionou com sucesso, indo para II parte me retornou o erro acima.

Pode me dar uma ajuda? abss

Cara que bacana sou de PP, e estou me formando esse semestre pela FIPP também.
Sucesso!

Fiz mais outro teste importei seu projeto, joguei somente as Libs no classpath e ajustei o flex compiler(-services) para o diretorio que utilito aqui no PC e deu o mesmo erro :/

A unica diferença fiz um ajuste no applicationContext.xml, coloquei driver e propriedades do PostgreSQL… e claro coloquei o driver do postgre 9 na classpath.

Abs. valeu até o momento.

Ederson,

Dei uma conferida aqui e vi que tinha uma falha no projeto do jeito que eu havia feito mesmo, uma pequena falha na configuração mas já consertei o post, por favor dê uma olhada na Parte 2 de novo, mais especificamente no fim da parte das configurações do Spring, antes de começar a mecher no Hibernate.

Desculpe pelo erro, enquanto eu estava escrevendo o post meu projeto estava funcionando pois algumas confiugrações antigas estavam em cache no eclipse.. Após um clean eu recebi o mesmo erro que você e pude corrigir.

Obrigado por avisar e boa sorte com os estudos.

[]s,
Saab.

Maravilha saab!!! vou testar e posto o retorno!!! abraços..

Testato e funcionando que é uma maravilha…
Aguardando a parte III em saab, simula ai um many-to-one ou many-to-many pra nois!! ;)
Valeus []´s

Parabéns pelo post! O conteúdo é excelente!
Abraços

Parabéns pelo tutorial, Saab. Eu sou desenvoledor Flash/Flex e estou começando a aprender Java, e artigos como esse, com mais prática do que teoria, caem como uma luva. Parabéns mesmo.
Eu só faria uma pequena observação: Na criação do objeto Button, , você usa chaves ({}). Isso é uma notificação de escuta de uma propriedade com Binding, o que você chamou de semelhante a Observer no artigo. Está correto sim no caso em que você as utiliza na propriedade dataProvider do DataGrid, mas não nesse.

Abraço e obrigado por compartilhar seu conhecimento.

Opa Bruno,
Valew pelo comentário rapaz, não sabia disso não… Valew mesmo =D
Já arrumei o post já. hehehe
Boa sorte nos estudos.
[]s,
Saab.

E ai Saab!! a parte III está conseguindo desenvolver?… aguardo atentamente, por esta parte.

Falando nisso, estou tendo bastante dificuldade com meu TCC da faculdade e estou usando seus tuto´s como guia. Porém ando tendo dificuldade para fazer Many to Many, Many-to One usando @annotations… por acaso voce nao tem algum exemplar simples nos casos citados a cima para me passar? caso sim, agraderia enormemente.

Obs: Para fazer os CRUD sem relacionamento esta de boa.

Abraços, e Sucesso!

Opa Ederson,
Então rapaz, estou muito sem tempo últimamente porém como já tem mais de 1 mês do último post eu vou começar a escrever a terceira parte hoje e pretendo dentro de no máximo 1 semana já terminá-la.
Visite o blog daqui 1 semana que a terceira parte provavelmente já vai estar aqui. =]
[]s

Opa saab, fico no aguardo… estou enrolado com com TCC e estou com o tempo apertadissimo…
Mto obrigado mesmo.

Olá Boa tarde!

Gostaria de saber se posso contar com a sua ajuda para resolver o meu problema. Bem no começo do artigo você chega a sugerir que:

” Para vermos como vai ficar esse layout no browser basta rodar o projeto AgendaJ primeiro e depois o AgendaF.

AgendaJ – botão direito no projeto > Run As > Run on Server
http://localhost:8400/AgendaJ/

AgendaF – botão direito no projeto > Run As > Web Application ”

Quando eu vou executar AgendaJ – run on server obtenho o seguinte erro:

Directory Listing For/

Filename Size Last Modifeid

Apache Tomcat / 6.0.14

Poderia me ajudar?

Atenciosamente
Daniel

Daniel, copia o erro certinho ai que a gente vê o que pode estar acontecendo.

[],
Saab

Olá Boa Tarde,
Quando eu vou rodar AgendaJ on Server da o seguinte erro:

HTTP Status 404 – /AgendaJ/

——————————————————————————–

type Status report

message /AgendaJ/

description The requested resource (/AgendaJ/) is not available.

——————————————————————————–

Apache Tomcat/6.0.32

Poderia Ajudar?

Bem, não sei se pode ser isto…

Esta mensagem de erro é exibida quando o seguinte endereço é chamado:
http://localhost:8080/AgendaJ

E a aplicação flex roda apartir deste:
http://localhost:8080/AgendaJ/AgendaF-debug/AgendaF.html

Terá isso alguma coisa com este erro?

Kléber,
Isso acontece porque o projeto AgendaJ é uma aplicação web que não contém nenhuma página padrão, ou seja, não tem nada para ser mostrado.
O projeto AgendaJ só vai receber as requisições do projeto AgendaF, processá-las e devolver a resposta.
Espero ter esclarecido.
[],
Saab.

Blz,

Porém fiz tudo como no tutorial, mas o botão salvar não faz nada =/
Mas já deu pra dar uma clareada.

Obrigado pela atenção e parabéns pelo post.

Hey There. I discovered your blog the use of msn. That is a very neatly written article. I will be sure to bookmark it and return to read more of your helpful information. Thank you for the post. I’ll certainly return.

Leave a comment