Latest Entries »

Arquivos dos Tutoriais

Olá pessoal, acabei de fazer o upload dos projetos dos tutoriais. Os arquivos estão hospedados no 4Shared.

Seguem os links ;)

Primeiro projeto
CRUD com janelas (Flash Builder)
CRUD com janelas (FlashDevelop)
Backend em Java (está no estado do último tutorial, o do FlashDevelop)

Abraços!

Olá pessoal, hoje vamos aprender como configurar um projeto no FlashDevelop para trabalhar com um backend em Java. Para acelerar o processo, iremos utilizar como base tanto o projeto do backend feito no NetBeans quanto o projeto do frontend feito no Flash Builder. Então vamos lá!

Vamos começar pelo FlashDevelop. Vá no menu Project e escolha New Project. Na janela New Project, escolha Flex 4 Project. Em Name, entre com “IntegracaoFlexJavaComJanelasFD” (sem as aspas). Em location, escolha o diretório onde você está salvando o projeto do NetBeans e os projetos que fizemos no Flash Builder. No meu caso o caminho é “C:\Users\David\Documents\Java\Flex”. O campo package pode ficar vazio. Deixe a opção “Create directory for project” e clique em OK. Veja a Figura abaixo.

Figura 1

Criando um projeto no FlashDevelop

Agora vamos copiar todos os arquivos de código fonte do nosso projeto do Flash Builder. Para isso, abra o Flash Builder e localize o projeto. Vá na pasta “src”, selecione os pacotes “entidades” e “gui”, clique com o botão direito e escolha “Copy”. Veja a Figura abaixo.

Figura 2

Copiando o código do projeto do Flash Builder

Com o código copiado, volte ao FlashDevelop, clique com o botão direito na pasta “src”, escolha Edit > Paste. Veja a Figura abaixo.

Figura 3

Colando o código do projeto do Flash Builder no projeto do FlashDevelop

Fazendo isso, todo o código fonte do projeto do Flash Builder será colado dentro do projeto do FlashDevelop. Agora precisamos configurar nossa aplicação padrão. No FlashDevelop, a aplicação padrão (default application) fica marcada com flexinha verde. No nosso projeto, a aplicação padrão atual é a Main.mxml. Vamos mudar então a aplicação padrão para a Principal.mxml. Para isso, clique com o botão direito no arquivo Principal.mxml e escolha “Always Compile”. Você vai perceber que a flexinha vai passar para o Principal.mxml. Agora apague o arquivo Main.mxml (botão direito no arquivo, Edit > Delete).

Até agora criamos um novo projeto do FlashDevelop, copiamos nossos arquivos fonte e configuramos nossa aplicação principal (default application), mas ainda precisamos realizar alguns passos:

  1. Apontar o compilador do Flex para o services-config.xml, fazendo com que os serviços configurados sejam “enxergados” pelo .swf compilado;
  2. Fazer algumas alterações no services-config.xml;
  3. Configurar o FlashDevelop para copiar os arquivos do build para a nossa pasta dos arquivos swf no NetBeans;
  4. Por fim, iremos configurar o nosso index.jsp para redirecionar para a página que contém o arquivo .swf.

Então vamos lá! No FlashDevelop, algumas configurações ficam mais “escondidas” do que no Flash Builder. A primeira é relacionada à compilação dos serviços. Na verdade, quando usamos o Flash Builder, as opções da localização do arquivo services-config.xml e outras relacionadas à utilização de serviços remotos via AMF, se tornam parâmetros do compilador (veja nas propriedades do projeto do Flash Builder). Então temos que configurar isso no nosso projeto no FlashDevelop. Para isso, clique com o botão direito no seu projeto (IntegracaoFlexJavaGUIComJanelasFD) e escolha Properties.

A janela de propriedades do projeto abrirá a última aba da janela é a “Compiler Options”. Clique nela. Veja a Figura abaixo.

Figura 4

Opções do compilador

Nós vamos mexer na primeira “propriedade”, a “Additional Compiler Options”. Para isso, selecione a propriedade e clique no botão “…” para abrir o editor. No diálogo que abriu, inseria o seguinte valor:

-services C:\Users\David\Documents\Java\Flex\IntegracaoFlexJava\web\WEB-INF\flex\services-config.xml
-context-root /IntegracaoFlexJava

Antes de dar OK, vamos à explicações. O parâmetro -services é usado para apontar para o arquivo services-config.xml que está contido no projeto do backend em Java. No meu caso, o projeto do backend está localizado em “C:\Users\David\Documents\Java\Flex\”, você vai precisar localizar o seu projeto no diretório que você escolheu anteriormente. Por fim, o segundo parâmetro é o -context-root, onde devemos preencher com a raiz do contexto do nosso backend, que no nosso caso é /IntegracaoFlexJava. Agora que você já entendeu para que serve cada parâmetro e já alterou o -services para apontar para o seu services-config.xml, dê OK. O resultado deve ser parecido com o mostrado na Figura abaixo.

Figura 5

Opções do compilador alteradas

Se estiver tudo certo, você pode então dar OK na janela de propriedades, alterando assim as opções do compilador do Flex. Até agora, configuramos para o compilador enxergar o services-config.xml, mas vamos precisar – infelizmente – fazer uma pequena alteração nesse arquivo de configuração… O Flash Builder consegue inserir parâmetros no arquivo em tempo de compilação, mas o FlashDevelop não. Então o que seria automático no Flash Builder vai ter que ser manual ao usar o FlashDevelop. Vamos por a mão na massa então.

Abra o projeto do backend no NetBeans e procure pelo arquivo services-config.xml (WEB-INF/flex/services-config.xml). Ao abrir  esse arquivo XML, procure pela tag “<channels>”. Nessa tag são configurados os canais do AMF, ou seja, onde o serviços de serialização/deserialização do AMF são encontrados. Por padrão, estão definidos três canais (my-amf, my-secure-amf e my-polling-amf). Estamos usando apenas o primeiro canal, o my-amf, então é nele que vamos mexer. Primeiro copie toda a definição do canal, cole logo abaixo e comente a segunda definição. Você vai ficar com algo assim:

services-config.xml (editando)

...
<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-amf" class="mx.messaging.channels.AMFChannel">
            <endpoint url="http://{server.name}:{server.port}/{context.root}/messagebroker/amf" class="flex.messaging.endpoints.AMFEndpoint"/>
        </channel-definition>
        -->

        <!-- outros canais aqui... -->

</channels>
...

O que vamos alterar é a URL do endpoint. Note que na URL temos três parâmetros: {server.name}, {server.port} e {context.root}. Quando usamos o Flash Builder, ele preenche os três parâmetros em tempo de compilação, mas o FlashDevelop só “consegue” preencher o parâmetro {context.root} (lembram do parâmetro -context-root que alteramos nas opções do compilador?). Os outros dois parâmetros vamos ter que alterar manualmente. Eu ainda não encontrei onde fazer isso automaticamente no FlashDevelop, então não sei se existe essa opção de fazer o preenchimento dos outros parâmetros automaticamente. Se alguém encontrar, é só avisar ;) .

Em {server.name} vamos ter que colocar o endereço do nosso servidor e em {server.port} a porta do servidor. Sendo assim, o segue o código alterado do services-config.xml.

services-config.xml (final)

...
<channels>

        <channel-definition id="my-amf" class="mx.messaging.channels.AMFChannel">
            <endpoint url="http://localhost:8084/{context.root}/messagebroker/amf" class="flex.messaging.endpoints.AMFEndpoint"/>
        </channel-definition>

        <!--
        <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>
        -->

        <!-- outros canais aqui... -->

</channels>
...

Muito bem, salve o arquivo. Com isso, a compilação dos serviços irá adicionar os caminhos apropriados para a utilização dos serviços. Vamos agora configurar o FlashDevelop para copiar os arquivos do build (resultado da compilação do projeto) para dentro da pasta swf do nosso projeto do backend. Primeiro, vá no NetBeans e apague TODO o conteúdo da pasta swf. Volte ao FlashDevelop e acesse novamente as propriedades do projeto (botão direito no projeto e escolha Properties).

Nós poderíamos alterar apenas o caminho para a criação do arquivo de saída, mas queremos que todo o resultado do build (swf + outros arquivos) sejam copiados para a pasta swf do projeto do backend. Então vamos lá. Na aba “Build” vamos adicionar um comando no “Post-Build Command Line” para copiar os arquivos de build para o nosso projeto do backend. No Windows, o comando para copiar todos os arquivos de um diretório de forma recursiva é o xcopy. Então vamos adicionar o comando para copiar todos os arquivos da pastas bin do projeto do FlashDevelop para a pasta “swf” do projeto do backend. Segue então o comando e a Figura onde é mostrado o comando inserido nas propriedades do projeto do FlashDevelop. Lembre-se de marcar a opção “Always execute”.

xcopy .\bin ..\IntegracaoFlexJava\web\swf /E /Y
Figura 6

Comando para executar após o build do projeto

Então usamos o comando xcopy, para copiar o conteúdo da pasta bin para a pasta swf do projeto do backend (note os caminhos relativos). As opções /E e /Y especificam que devem ser copiados todos os arquivos recursivamente (/E) e que os arquivos devem ser substituidos sem confirmação (/Y).

Dê OK na janela de propriedades, e faça o build do projeto usando o botão com o ícone de engrenagem, na barra de ferramentas do FlashDevelop. Verifique se os arquivos da pasta do projeto do FlashDevelop foram copiados para a pasta swf do projeto do backend. Se foram copiados, ótimo, está tudo OK. Se não foram, verifique os passos do tutorial novamente para ver onde errou.

Estamos quase lá, falta apenas mudar o nosso index.jsp para apontar para o arquivo html gerado pelo FlashDevelop. Abra então o index.jsp do projeto do backend e altere a tag meta para recionar para o index.html (gerado pelo FlashDevelop, na pasta bin). O meta vai ficar assim:

Meta alterado

<meta http-equiv="Refresh" content="0; url=swf/index.html>

Execute o projeto para testar. Você vai perceber que o swf não vai ocupar todo a página, mas essa correção fica como exercício. Nesse tutorial fizemos a migração toda na mão, mas poderíamos também usar o assistente de importação do FlashDevelop (Menu Project > Import Project…), mas isso também fica como exercício ;)

Então é isso, finalizamos mais um tutorial. Espero que tenham gostado!

Abraços!

Quando vamos desenvolver uma aplicação em Flex, temos como ferramenta padrão a IDE Flash Builder, que é uma ferramenta comercial da Adobe, entretanto, o Flex SDK, que constitui todo o ferramental para a compilação dos projetos do Flash Builder, é uma solução gratuita, sendo que qualquer um pode baixá-lo e usá-lo em qualquer outra IDE que desejar, ou mesmo desenvolver seus códigos em qualquer editor de texto.

Apesar do Flash Builder ser uma solução comercial, existe também uma versão gratuita do mesmo que pode ser obtida por estudantes, professores ou profissionais, desde que não o usem para fins comerciais. Esta versão pode ser obtida em http://www.adobe.com/devnet/flex/free/index.html.

Uma alternativa ao uso do Flash Builder é o FlashDevelop, uma IDE gratuita desenvolvida em .Net. Hoje iremos aprender como utilizar essa IDE para criar nossos projetos, além de aprender como baixar, instalar e configurar o Flex SDK.

Como já dito, o Flex SDK pode ser obtido gratuitamente no site da Adobe. A página de download é esta aqui. Neste link, são apresentados vários links, sendo que o principal para nós é este aqui que lista todos os downloads disponíveis do Flex SDK 4. Uma alternativa a estes links é este aqui, que vamos utilizar para fazer o download do Flex SDK. A vantagem desta página é que ela já vai nos redirecionar para a última versão do Flex SDK.

Para fazer o download, basta preencher o formulário e clicar em “Download now”. Logo que você clicar, o download vai iniciar. Para a versão que baixei (4.1) o tamanho do download foi de cerca de 171 MB. Sendo assim, baixe o arquivo. Assim que terminar, escolha onde vai salvar os SDKs, sugiro a raiz de qualquer partição. Crie então uma pasta chamada “Flex” e dentro dela uma pasta chamada “sdks”. No meu caso, o caminho para a pasta sdks ficou “D:\Flex\sdks\”. Dentro da pasta “sdks”, crie uma pasta com o número da versão do Flex. No meu caso, 4.1.0, e descompacte o conteúdo do arquivo baixado (no meu caso foi o flex_sdk_4.1.zip) na pasta criada. Deve ficar assim:

Figura 1

Flex SDK 4.1.0 descompactado

Note que você pode escolher o caminho que quiser para descompactar, estou apenas sugerindo um local. Note também que caso você já tenha instalado o Flash Builder 4, ele já vai vir com dois SDKs instalados, o 3.5 e o 4.0, que podem ser encontrados na pasta “sdks” que fica contida dentro da pasta onde o Flash Builder foi instalado.

Muito bem, até aqui tranquilo. Outro detalhe é que para você utilizar o compilador do Flex, você precisa instalar o Java SDK (JDK). Para isso, acesse esse link aqui, baixe o JDK e instale. Agora vamos fazer com que consigamos acessar pela linha de comando tanto o compilador do Java quanto do Flex. Para fazer isso, precisamos configurar uma variável de ambiente chamada “Path” no Windows. No Linux, a variável tem o mesmo nome. Para configurar essa variável, primeiro precisamos procurar onde fazer isso. O assistente para configurar variáveis de ambiente no Windows pode ser encontrado nas propriedades do sistema. Para chegar lá, você tem algumas alternativas. No Windows XP, procure pelo ícone “Meu Computador”, clique com o botão direito e vá em propriedades. No Windows Vista ou no 7, abra o menu iniciar, procure o item “Computador”, clique com o botão direito e vá em propriedades. Um atalho é win+pause. A janela sobre informações do computador vai abrir, nela procure por “Configurações Avançadas do Sistema” e clique nessa opção. A janela de propriedades do sistema vai ser aberta. Tanto no Windows XP quanto no Vista ou 7, procure pela aba “Avançado” e dentro dela, procure pelo botão “Variáveis de Ambiente…” e clique nele. Veja a Figura abaixo.

Figura 2

Acessando as variáveis de ambiente

Na Figura acima a janela de propriedades do sistema do Windows 7 é mostrada. Tanto no Windows XP quanto no Vista a janela é praticamente igual a esta. Ao clicar no botão “Variáveis de Ambiente…” uma nova janela se abrirá. É nela que as variáveis são criadas e configuradas. Existem dois escopos para essas variáveis. Um dos escopos é o escopo do usuário corrente (destacado em roxo) e o outro é o escopo das variáveis do sistema (em laranja). Eu gosto de alterar a variável do sistema e não a do usuário. Fazendo isso, a configuração é refletida para todo o sistema e não só para o usuário que a configurou. Veja a Figura abaixo.

Figura 3

Variáveis de ambiente

Note que na área destacada em laranja – que corresponde às variáveis do sistema – está destacada, em azul, a variável “Path”. Procure por essa variável na sua janela, selecione-a e clique no botão “Editar”. Na Figura seguinte é mostrada a janela que irá aparecer, ou seja, a janela para configurar o valor dessa variável.

Figura 4

Variável "Path"

O valor da variável “Path” é composto por uma série de “caminhos” (Path), ou seja, referências para diretórios onde estão contidos programas ou bibliotecas que devem ser enxergados de forma global pelo sistema operacional. Esses caminhos são separados pelo símbolo do ponto e vírgula “;”. Vamos então editar nossa variável para apontar para o diretório “bin” do JDK, onde estão as ferramentas do JDK como o compilador Java e o interpretador, e também vamos inserir o caminho do “bin” do Flex SDK, permitindo assim que possamos chamar o compilador do Flex pela linha de comando.

Primeiramente, posicione o cursor no campo “Valor da variável” e vá até o final do campo. Insira um ponto e vírgula e cole o caminho do “bin” do JDK. Após colar, insira outro ponto e vírgula e agora cole o caminho do “bin” do Flex SDK. No meu caso, a variável “Path” ficou com o seguinte valor:

<valor antigo>;C:\Program Files (x86)\Java\jdk1.6.0_20\bin;D:\Flex\sdks\4.1.0\bin

Sendo que <valor antigo> representa o que tinha anteriormente no campo “Valor da variável”. Não vou postar o conteúdo do meu, pois provavelmente sua máquina terá outro valor. Note que após o <valor antigo>, é necessário então colocar um ponto e vírgula. A seguir, em azul, está o caminho para o “bin” do JDK. Esse caminho provavelmente vai ser diferente para você também. Após isso, outro ponto e vírgula, e por fim, o caminho do “bin” do Flex SDK (destacado em roxo). Note que não há necessidade de colocar um ponto e vírgula no final. Feito isso, dê OK nesta janela, de OK na janela de “Variáveis de Ambiente” e dê OK na janela “Propriedades do Sistema”. Com isso finalizamos a configuração da variábel “Path”. Agora precisamos testar.

Abra um prompt de comando, ou digite “cmd” no executar do Windows (win+r) e dê OK. Vamos ver primeiramente se algum programa do “bin” do JDK está visível. Digite “javac -version” (sem aspas) no prompt e tecle <ENTER>.  Se tudo estiver correto a versão do javac será mostrada. Caso contrário, o Windows vai reclamar que não consegue reconhecer o comando dado. Da mesma forma que fizemos com o javac, para testar o “bin” do JDK, vamos testar agora o “bin” do Flex SDK. Para isso, ainda no prompt, digite “mxmlc -version” e dê <ENTER>. Novamente a versão do mxmlc deve ser apresentada. Veja a Figura abaixo. As saídas dos comandos javac e mxmlc estão destacadas respectivamente em azul e roxo. Caso tenha algum problema, verifique onde errou.

Figura 5

Saídas dos comandos javac e mxmlc

Muito bom! A partir de agora podemos compilar arquivos mxml e ActionScript a partir do prompt de comando ou a partir de arquivos em lote (batch scripts)! Mas não vamos fazer isso, não queremos sofrer muito para desenvolver nossas aplicações em Flex. Vamos automatizar um pouco isso, usando a IDE FlashDevelop que é gratuita.

Então vamos lá! Primeiramente, vamos fazer o download da ferramenta. O site oficial do FlashDevelop é http://www.flashdevelop.org/ e as instruções de download e das dependências necessárias para utilizá-lo pode ser encontrada nesse link: http://www.flashdevelop.org/community/viewtopic.php?f=11&t=6956. Note que nesse link está listada a última versão disponível quando esse post foi publicado. Sendo assim, recomendo que você entre no site da ferramenta e busque pelo link download. Note também que das dependências necessárias, já resolvemos a parte do JDK e do Flex SDK, sendo necessário que você instale o .Net framework caso ainda não o tenha instalado (Windows Vista e 7 já vem com ele instalado).

Depois que baixar o FlashDevelop, instale-o. Depois da instalação, abra a ferramenta. A cara dela é essa daqui (versão 3.2.2):

Figura 6

Interface principal do FlashDevelop 3.2.2

Vamos então criar um projeto para podermos configurar a ferramenta. Procure na barra de menu pelo item “Project”. Clique nele e escolha “New Project”. Em “Installed Templates” escolha “Flex 4 Project”. Em “Name” entre com “TesteFlashDevelop” (sem as aspas). Escolha onde quer salvar o projeto, dê uma nome para o pacote principal da aplicação (caso queira) e marque a opção “Create directory for project”. Dê OK e a ferramenta vai pedir o nome do autor. Preencha o campo e dê OK. Veja a Figura abaixo.

Figura 7

Criando um novo projeto no FlashDevelop

No canto superior direito da interface do FlashDevelop, a estrutura do projeto será apresentada. Procure na pasta “src” pelo Main.mxml e clique duas vezes. O arquivo será aberto no editor. Note que o código não tem nenhum componente visual, apenas a estrutura da aplicação. Vamos então tentar dar um build no projeto. Na barra de ferramentas da IDE, procure por um ícone com uma engrenagem e clique nele. A ferramenta vai avisar que é necessário configurar o compilador e vai perguntar se você quer fazer isso. Clique em OK e a seguinte janela será aberta:

Figura 8

Configurando o compilador do Flex no FlashDevelop

No campo “Flex SDK Location” precisamos apontar para o diretóri o raiz do Flex SDK. Note como ficou a configuração.

Figura 9

Apontando para o Flex SDK

Após fazer isso clique em close e tente dar o build novamente no projeto (botão da engrenagem). Com isso, o projeto deve ser compilado e você pode ver o resultado na pasta “bin” do projeto onde um arquivo “swf” com o nome do projeto vai ser gerado.

Muito legal não é? Temos agora uma IDE totalmente gratuita para utilizarmos. Uma desvantagem do FlashDevelop é que ele não tem um editor visual para a interface gráfica, mas é muito melhor usar ele do que ter que fazer tudo na mão, pois ele automatiza todo o processo de compilação para nós. No próximo tutorial, vou mostrar como portar o nosso projeto do CRUD em janelas para o FlashDevelop, configurando o compilador para ele compilar os serviços remotos do BlazeDS.

Então é isso, espero que tenham gostado ;)

Até a próxima!

Olá a todos. Estive um pouco sumido nos últimos dias. Criar esses tutoriais detalhados demanda um trabalho considerável sendo preciso ter coragem (e tempo) para fazer hehe. O Sérgio, leitor do Blog, me pediu algumas dicas em como estruturar uma aplicação feita no Flex em janelas. Pois bem, decidi então fazer um tutorial sobre isso. Para o tutorial, iremos utilizar como base o tutorial anterior e quem ainda não o leu, pode iniciar pela Parte 1.

Vamos começar então. Primeiro, vamos fazer uma cópia do nosso projeto do frontend. Para isso, no Flash Builder, procure pelo projeto “IntegracaoFlexJavaGUI”, clique com o botão direito do mouse na raiz do projeto e clique em “Copy”. Veja a Figura abaixo.

Figura 1

Copiando o projeto

Com o projeto copiado, agora basta colar. Para isso, clique em algum lugar fora do projeto original e escolha “Paste”. Veja a Figura abaixo.

Figura 2

Colando o projeto

Quando você clicar na opção “Paste”, a janela “Copy Project” vai aparecer. No campo “Project Name” vamos preencher com o nome “IntegracaoFlexJavaGUIComJanelas”. Desmarque a opção “Use default location” e no campo “Location” localize a pasta onde estamos salvando nossos projetos. No meu caso, estou salvando tudo na pasta “C:\Users\David\Documents\Java\Flex”. Tome o cuidado de indicar que você quer que uma pasta para o projeto seja criada, colocando no final do caminho o nome do projeto. No meu caso, o valor completo do campo “Location” ficou “C:\Users\David\Documents\Java\Flex\IntegracaoFlexJavaGUIComJanelas” (sem as aspas). Com isso pronto, clique no OK. Veja a Figura abaixo.

Figura 3

Criando o novo projeto com base no anterior

Legal, o Flash Builder vai criar então o novo projeto. Perceba que não vamos precisar configurar o caminho do backend, pois ele já estava configurado no projeto anterior, e como fizemos uma cópia, todas aquelas propriedades que alteramos quando criamos o projeto original foram copiadas.

Agora vamos reorganizar nosso novo projeto. Cuidado para não fazer as modificações no projeto antigo dentro do Flash Builder. Clique então com o botão direito na pasta “src”, escolha New > Package. Dê o nome de “gui” (sem as aspas) para o pacote e clique em Finish. Com isso, criamos um novo pacote para o nosso projeto, onde vamos colocar nossos arquivos mxml que vão representar nossa interface gráfica e seus componentes.

Perceba que nossos mxmls ficaram todos no “default package”. Primeiramente selecione o arquivo IntegracaoFlexJavaGUI.mxml e remova-o do projeto (selecione o arquivo, tecle <DELETE>). Sobraram dois arquivos, o CRUD.mxml e o DateRenderer.mxml. Selecione os dois arquivos e os arraste para o pacote “gui” que acabamos de criar. A janela “Move” vai aparecer. Deixe apenas a opção “Update references” marcada e clique em OK. Com isso os arquivos serão movidos para o novo pacote.

Feito isso, clique agora com o botão direito no pacote “gui” e escolha New > MXML Application. Preencha o campo “Name” com “Principal” (sem aspas) e deixe o campo “Layout” como “None”. Clique em Finish. O Flash Builder não vai criar o nosso arquivo no pacote “gui”, mesmo tendo selecionado esse pacote quando clicamos com o botão direito :( . O Principal.mxml vai ser criado no default package. Da mesma forma que acabamos de fazer com os arquivos CRUD.mxml e DateRenderer.mxml, selecione o arquivo Principal.mxml e o mova para o pacote “gui”.

Perceba que o ícone do CRUD.mxml tem uma bolinha azul. Isso quer dizer que esse arquivo é o ponto de entrada da nossa aplicação, ou seja, é a partir dele que a aplicação inicia. Vamos mudar isso. Queremos que o Principal.mxml seja o ponto de entrada. Para isso, clique com o botão direito no Principal.mxml e escolha a opção “Set as Default Application”. Você vai ver que a bolinha vai passar para o Principal.mxml. Iremos manter o CRUD.mxml, pois é nele que está implementada a nossa aplicação original e vamos reaproveitar aquele código quando estivermos montando nossas janelas.

Antes de qualquer outra coisa, lembra que o resultado da compilação do Flash Builder era armazenado na pasta “swf” do nosso projeto do NetBeans? Pois bem, vá no NetBeans, procure a pasta “swf” e apague todo o seu conteúdo. Lá dentro você vai ver que tem arquivos do nosso frontend antigo que não nos interessam mais. Faltou falar uma coisa. Iremos reaproveitar o projeto do NetBeans, então não iremos criar um projeto novo. Caso você queira fazer isso, o processo é parecido com o do Flash Builder. Só não fiz isso, pois senão o caminho do projeto do backend iria mudar e então teríamos que configurar mais coisas no nosso projeto no Flash Builder, mudando o caminho do backend. Sendo assim, sugiro que mantenha o projeto antigo do NetBeans.

Ainda no projeto do NetBeans, abra o index.jsp e agora no <meta … > mude a url que define o redirecionamento para o CRUD.html, colocando no lugar o Principal.html (que é o arquivo HTML que vai ser gerado para conter nosso projeto). O <meta … > do index.jsp vai ficar assim:

Alteração da tag <meta … >

<meta http-equiv="Refresh" content="0; url=swf/Principal.html">

Rode o projeto do NetBeans. Uma página em branco deve aparecer, afinal, ainda não colocamos nada no nosso Principal.mxml. Não precisaremos mais mexer no NetBeans, só iremos utilizá-lo agora para executar nosso projeto quando formos fazendo alterações na interface gráfica. Lembre-se, nossa infraestrutura de persistência e de serviços já está pronta!

Vamos então à montagem da nossa interface gráfica. Para isso, no Flash Builder, abra o Principal.mxml e vá na aba Design. Na aba dos componentes (canto inferior esquerdo) procure pelo grupo “Navigators”. Dento desse grupo existe o componente “MenuBar”. Clique e arraste um MenuBar para o Principal.mxml. A aparência da barra de menu, por enquanto, é igual a de um botão, mas sem nenhum texto.Veja a Figura abaixo.

Figura 4

Arrastando um componente MenuBar

Queremos então que esse menu ocupe toda a parte acima da nossa aplicação. Para isso, arraste o componente até o canto superior esquerdo do Principal.mxml. Veja a Figura abaixo.

Figura 5

Posicionando o componente MenuBar

Feito isso, selecione o componente (clique nele) caso ainda não esteja selecionado. No canto inferior direito do Flash Buillder, na aba “Properties” podemos alterar as propriedades do componente. Iremos utilizar a visualização padrão das propriedades (Standard View), que nos fornece um editor amigável para as propriedades mais comuns do componente selecionado no momento. Veja a Figura abaixo. O botão para acessar as propriedades padrão está destacado.

Figura 6

Propriedades padrão do componente selecionado

No painel de propriedades, com o MeuBar selecionado, procure pela seção “Size and Position”. X e Y devem estar com 0, pois colocamos nosso componente no canto superior (Y=0) esquerdo (X=0) lembram? Agora vamos usar duas constrains para esticar a barra de menu, fazendo ela ocupar toda a interface horizontalmente. Veja a Figura abaixo.

Figura 7

Usando as constrains left e right para esticar o componente MenuBar

Veja que quando você modificar essas propriedades, a barra de menu vai passar a ocupar toda a interface na horizontal. Note também que com isso, a propriedade X ficou sem um valor. Isso porque quando especificamos o left (checkbox à esquerda) como 0 e o right (checkbox à direita) como 0 também (campos abaixo) não precisamos mais dizer onde o componente está na posição X, pois ele vai preencher horizontalmente seu container, no casso, o Principal.mxml (que é do tipo s:Application).

Vamos agora inserir os itens no nossa barra de menu. Infelizmente não temos uma opção visual para isso, então temos que fazer isso diretamente no código. Para isso, vá na opção “Source” do Principal.mxml e localize o seu MenuBar (<mx:MenuBar>).

Altere o código para isso aqui:

Código do componente MenuBar

<mx:MenuBar y="0" left="0" right="0" labelField="@label">
	<fx:XMLList>
		<menuitem label="Cadastros">
			<menuitem id="itemCadClientes" label="Clientes"/>
			<menuitem type="separator"/>
			<menuitem id="itemCadCidades" label="Cidades"/>
			<menuitem id="itemCadEstados" label="Estados"/>
		</menuitem>
	</fx:XMLList>
</mx:MenuBar>

Parece complicado, mas não é. Vamos primeiro detalhar o conteúdo da tag <mx:Menu>. Perceba que dentro do nosso menu, declaramos uma estrutura de dados chamada XMLList. Basicamente essa estrutura utiliza código XML para estruturar dados. Note que dentro das tags <fx:XMLList> e </fx:XMLList> temos o código XML que será utilizado para estruturar o nosso menu. Temos então um item para o cadastro, representado pela tag <menuitem> mais externa. Note que essa tag tem uma propriedade chamada “label”. Essa propriedade será utilizada pelo renderizador da barra de menus para desenhar o nome de cada botão. Note na tag <mx:MenuBar> o uso da propriedade “labelField”, onde usamos o valor “@label”. Isso significa que queremos usar o atributo “label” das tags definidas no XML como o label (rótulo) dos nossos itens do menu. O sinal de arroba (@) indica que “label” é um atributo. Dentro então da tag <menuitem> mais externa, temos então 4 outros itens, um para cada cadastro e um para o separador do menu (type=”separator”). Note que podemos ir aninhando tags do tipo <menuitem> para irmos construindo nosso menu.

Para ver as mudanças, dê o build no projeto (botão direito na raiz do projeto > Build Project) e execute a aplicação pelo NetBeans. Legal, o menu funciona. Agora falta o principal, ou seja, adicionar interação nos itens do menu, pois queremos que algo aconteça quando um dos itens do cadastro sejam clicados. Para isso iremos registrar um ouvinte na barra de menu, e na implementação do método ouvinte, verificamos qual foi o item clicado com base no id do item. Segue então o código completo do Principal.mxml.

gui.Principal.mxml

<?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" minWidth="955" minHeight="600">

	<fx:Script>
		<![CDATA[
			import mx.controls.Alert;
			import mx.events.MenuEvent;

			private function menuItemClickHandler( event: MenuEvent ): void {

				// faz o switch baseado no id do item selecionado
				switch ( event.item.@id.toString() ) {

					case "itemCadClientes":
						Alert.show( "Botão Clientes clicado!", "Mensagem" );
						break;

					case "itemCadCidades":
						Alert.show( "Botão Cidades clicado!", "Mensagem" );
						break;

					case "itemCadEstados":
						Alert.show( "Botão Estados clicado!", "Mensagem" );
						break;

				}

			}

		]]>
	</fx:Script>

	<fx:Declarations>
		<!-- Place non-visual  elements (e.g., services, value objects) here -->
	</fx:Declarations>

	<mx:MenuBar y="0" left="0" right="0" labelField="@label" itemClick="menuItemClickHandler(event)">
		<fx:XMLList>
			<menuitem label="Cadastros">
				<menuitem id="itemCadClientes" label="Clientes"/>
				<menuitem type="separator"/>
				<menuitem id="itemCadCidades" label="Cidades"/>
				<menuitem id="itemCadEstados" label="Estados"/>
			</menuitem>
		</fx:XMLList>
	</mx:MenuBar>

</s:Application>

Teste o projeto. Verifique que agora, baseado no item clicado no menu, um Alert diferente é mostrado. Vamos agora implementar nossa primeira janela. Quando tivermos ela pronta, iremos chamá-la pelo botão correspondente. Iniciaremos pela janela de cadastro de Estados.

Para isso, vamos criar um componente mxml, mas antes um pouco de teoria. Quando criamos arquivos MXML, na verdade estamos estendendo classes do ActionScript. Quando criamos uma aplicação MXML (MXML Application), você pode notar que o arquivo fonte é escrito em XML e que a tag raiz (root) é do tipo <s:Application>. Durante a compilação, o que o Flex faz é converter esse arquivo MXML em uma classe e então o compila. Sendo assim, cada arquivo MXML vira uma classe e no final e estamos estendendo uma classe. Imagine então o nosso arquivo Principal.mxml. Na verdade, ele vira uma classe mais ou menos assim:

package gui {

	import spark.components.Application;

	public class Principal extends Application {

		// implementação...

	}

}

Para cada uma das nossas janelas, nós vamos estender a classe “TitleWindow” do pacote spark.components, mas nós vamos seguir a estratégia de criar arquivos MXML da mesma forma que estamos fazendo para as aplicações. Então vamos lá. Começaremos pela tela de cadastro de Estados. Clique com o botão direito no pacote “gui” e escolha New >MXML Component. Na tela New MXML Component, preencha o campo “Name” com “CadastroEstados” (sem as aspas), deixe o campo “Layout” como “None” e por fim, preencha o “Based on” com “spark.components.TitleWindow” (semas as aspas). Por fim, clique em Finish e arquivo do novo componente será aberto. Deixa e largura e a altura da forma que estão. Veja a Figura abaixo.

Figura 8

Criando um novo componente para o cadastro de Estados

Com o arquivo CadastroEstados.mxml aberto, vá na aba Design e vamos fazer as primeiras modificações na nossa tela de cadastro. Primeiro dê um título para a janela (pelas propriedades ou clicando duas vezes na barra de título). Sugiro “Cadastro de Estados” (sem as aspas). Com isso feito, aumente um pouco a tela para termos espaço para trabalhar. Basta redimensionar, como faria com uma imagem. No final, teremos algo assim:

Figura 9

Preparando a janela de cadastro

Agora vamos ao componentes da tela de cadastro. O bom é que já temos tudo pronto :) . Abra o arquivo CRUD.mxml, vá em Design, selecione todos os componentes do painel de cadastro de estados, clique bom o botão direito e escolha “Copy”. Veja a Figura.

Figura 10

Copiando componentes

Agora volte no CadastroEstados.mxml, e em Design, clique com o botão direito na tela e escolha “Paste”. Os componentes serão colados. Organize eles e redimensione a janela. O resultado você pode ver na Figura abaixo, e o código parcial do CadastroEstados.mxml pode ser visto logo em seguinda.

Figura 11

Design da tela de cadastro de Estados pronto

gui.CadastroEstados.mxml

<?xml version="1.0" encoding="utf-8"?>
<s:TitleWindow xmlns:fx="http://ns.adobe.com/mxml/2009"
			   xmlns:s="library://ns.adobe.com/flex/spark"
			   xmlns:mx="library://ns.adobe.com/flex/mx" width="296" height="278" title="Cadastro de Estados">

	<fx:Declarations>
		<!-- Place non-visual elements (e.g., services, value objects) here -->
	</fx:Declarations>

	<mx:DataGrid y="10" id="tabelaEstados" left="10" right="10" height="107" itemClick="tabelaEstadosClickHandler(event)">
		<mx:columns>
			<mx:DataGridColumn headerText="Nome" dataField="nome" width="150" resizable="true"/>
			<mx:DataGridColumn headerText="Sigla" dataField="sigla" width="80" resizable="true"/>
		</mx:columns>
	</mx:DataGrid>
	<mx:Form y="121" right="10" left="10" height="86">
		<mx:FormItem label="Nome:">
			<s:TextInput width="187" id="fieldNomeEstado"/>
		</mx:FormItem>
		<mx:FormItem label="Sigla:">
			<s:TextInput width="40" id="fieldSiglaEstado"/>
		</mx:FormItem>
	</mx:Form>
	<s:Button y="215" label="Novo" click="btnNovoEstadoClickHandler(event)" right="162"/>
	<s:Button y="215" label="Salvar" click="btnSalvarEstadoClickHandler(event)" right="84"/>
	<s:Button y="215" label="Excluir" click="btnExcluirEstadoClickHandler(event)" right="6"/>
</s:TitleWindow>

Além da parte gráfica, precisamos agora copiar o código dos manipuladores de eventos, dos serviços, etc. Como você já tem alguma experiência com o funcionamento, pois provavelmente já acompanhou o tutorial anterior, estou postando o código completo do CasdatroEstados.mxml com todo o código da interação implementado.

gui.CadastroEstados.mxml

<?xml version="1.0" encoding="utf-8"?>
<s:TitleWindow xmlns:fx="http://ns.adobe.com/mxml/2009"
			   xmlns:s="library://ns.adobe.com/flex/spark"
			   xmlns:mx="library://ns.adobe.com/flex/mx" width="296" height="278" title="Cadastro de Estados"
			   creationComplete="creationCompleteHandler(event)">

	<fx:Script>
		<![CDATA[
			import entidades.Estado;

			import mx.collections.ArrayCollection;
			import mx.controls.Alert;
			import mx.events.FlexEvent;
			import mx.events.ItemClickEvent;
			import mx.events.ListEvent;
			import mx.rpc.events.FaultEvent;
			import mx.rpc.events.ResultEvent;

			private var estadoSelec: Estado;

			private function faultHandler( event: FaultEvent ): void {
				Alert.show( event.fault.toString(), "ERRO" );
			}

			private function estadoSaveResultHandler( event: ResultEvent ): void {
				servEstado.listAll();
				resetFormEstados();
			}

			private function estadoUpdateResultHandler( event: ResultEvent ): void {
				servEstado.listAll();
				resetFormEstados();
			}

			private function estadoDeleteResultHandler( event: ResultEvent ): void {
				servEstado.listAll();
				resetFormEstados();
			}

			private function estadoListAllResultHandler( event: ResultEvent ): void {
				tabelaEstados.dataProvider = event.result;
			}

			private function btnNovoEstadoClickHandler( event: MouseEvent ): void {
				resetFormEstados();
			}

			private function btnSalvarEstadoClickHandler( event: MouseEvent ): void {

				if ( estadoSelec ) {

					estadoSelec.nome = fieldNomeEstado.text;
					estadoSelec.sigla = fieldSiglaEstado.text;

					servEstado.update(estadoSelec);

				} else {

					var o: Estado = new Estado();

					o.nome = fieldNomeEstado.text;
					o.sigla = fieldSiglaEstado.text;

					servEstado.save(o);

				}
			}

			private function btnExcluirEstadoClickHandler( event: MouseEvent ): void {

				if ( estadoSelec ) {
					servEstado.getOperation( "delete" ).send( estadoSelec );
				}

			}

			private function tabelaEstadosClickHandler( event: ListEvent ): void {

				estadoSelec = tabelaEstados.selectedItem as Estado;

				fieldNomeEstado.text = estadoSelec.nome;
				fieldSiglaEstado.text = estadoSelec.sigla;

			}

			private function resetFormEstados(): void {

				fieldNomeEstado.text = "";
				fieldSiglaEstado.text = "";

				estadoSelec = null;

			}

			private function creationCompleteHandler( event: FlexEvent ): void {
				servEstado.listAll();
			}

		]]>
	</fx:Script>

	<fx:Declarations>

		<s:RemoteObject
			id="servEstado"
			destination="estadoServices"
			showBusyCursor="true">

			<s:method
				name="save"
				fault="faultHandler(event)"
				result="estadoSaveResultHandler(event)"/>

			<s:method
				name="update"
				fault="faultHandler(event)"
				result="estadoUpdateResultHandler(event)"/>

			<s:method
				name="delete"
				fault="faultHandler(event)"
				result="estadoDeleteResultHandler(event)"/>

			<s:method
				name="listAll"
				fault="faultHandler(event)"
				result="estadoListAllResultHandler(event)"/>

		</s:RemoteObject>

	</fx:Declarations>

	<mx:DataGrid y="10" id="tabelaEstados" left="10" right="10" height="107" itemClick="tabelaEstadosClickHandler(event)">
		<mx:columns>
			<mx:DataGridColumn headerText="Nome" dataField="nome" width="150" resizable="true"/>
			<mx:DataGridColumn headerText="Sigla" dataField="sigla" width="80" resizable="true"/>
		</mx:columns>
	</mx:DataGrid>
	<mx:Form y="121" right="10" left="10" height="86">
		<mx:FormItem label="Nome:">
			<s:TextInput width="187" id="fieldNomeEstado"/>
		</mx:FormItem>
		<mx:FormItem label="Sigla:">
			<s:TextInput width="40" id="fieldSiglaEstado"/>
		</mx:FormItem>
	</mx:Form>
	<s:Button y="215" label="Novo" click="btnNovoEstadoClickHandler(event)" right="162"/>
	<s:Button y="215" label="Salvar" click="btnSalvarEstadoClickHandler(event)" right="84"/>
	<s:Button y="215" label="Excluir" click="btnExcluirEstadoClickHandler(event)" right="6"/>
</s:TitleWindow>

Legal,  já temos nossa tela de cadastro pronta (quase na verdade). Vamos então aprender como fazer para abrir essa janela de cadastro a partir do menu do Principal.mxml. Usaremos para isso a classe PopUpManager do pacote mx.managers. Essa classe possui dois métodos estáticos, o addPopUp e o removePopUp. Esses métodos são usados respectivamente para adicionar uma janela em um container e para remover uma janela de um conteiner. Vamos então modificar o switch do Principal.mxml onde tratamos os eventos dos itens dos botões. Segue então o código do Principal.mxml atualizado e comentado.

gui.Principal.mxml

<?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" minWidth="955" minHeight="600">

	<fx:Script>
		<![CDATA[
			import mx.controls.Alert;
			import mx.events.MenuEvent;
			import mx.managers.PopUpManager;

			private function menuItemClickHandler( event: MenuEvent ): void {

				// faz o switch baseado no id do item selecionado
				switch ( event.item.@id.toString() ) {

					case "itemCadClientes":
						Alert.show( "Botão Clientes clicado!", "Mensagem" );
						break;

					case "itemCadCidades":
						Alert.show( "Botão Cidades clicado!", "Mensagem" );
						break;

					case "itemCadEstados":

						// instanciamos um objeto do tipo CadastroEstados
						var cadastroEstados: CadastroEstados = new CadastroEstados();

						/*
						Usa-se a classe PopUpManager para abrir a janela.
						O primeiro parâmetro é o componente a ser aberto, o segundo é
						onde ele estará contido e o terceiro é um boolean que diz se
						a janela deve ser modal ou não.
						*/
						PopUpManager.addPopUp( cadastroEstados, this, true );

						// centralizamos a janela
						cadastroEstados.x = ( this.width / 2 ) - ( cadastroEstados.width / 2 );
						cadastroEstados.y = ( this.height / 2 ) - ( cadastroEstados.height / 2 );

						break;

				}

			}

		]]>
	</fx:Script>

	<fx:Declarations>
		<!-- Place non-visual  elements (e.g., services, value objects) here -->
	</fx:Declarations>

	<mx:MenuBar y="0" left="0" right="0" labelField="@label" itemClick="menuItemClickHandler(event)">
		<fx:XMLList>
			<menuitem label="Cadastros">
				<menuitem id="itemCadClientes" label="Clientes"/>
				<menuitem type="separator"/>
				<menuitem id="itemCadCidades" label="Cidades"/>
				<menuitem id="itemCadEstados" label="Estados"/>
			</menuitem>
		</fx:XMLList>
	</mx:MenuBar>

</s:Application>

Teste sua aplicação. Você vai perceber que agora, quando clicar no item Estados do menu Cadastros, a janela de cadastro de Estados vai abrir. Note que ela é modal (um overlay vai esmaecer a tela principal) e que você pode arrastá-la como uma janela. O único detalhe que ainda falta é permitir que ela seja fechada. Se você clicar no botão fechar da janela, não acontecerá nada. Para implementar isso, vamos definir um método para manipular o evento “close” da janela de cadastro de Estados. Esse evento é disparado quando clicamos no botão fechar. O código do método é super simples. Ele vai chamar o método removePopUp da classe PopUpManager, passando como referência o próprio componente. Segue então o código final da janela de cadastro de Estados.

gui.CadastroEstados.mxml (final)

<?xml version="1.0" encoding="utf-8"?>
<s:TitleWindow xmlns:fx="http://ns.adobe.com/mxml/2009"
			   xmlns:s="library://ns.adobe.com/flex/spark"
			   xmlns:mx="library://ns.adobe.com/flex/mx" width="296" height="278" title="Cadastro de Estados"
			   creationComplete="creationCompleteHandler(event)"
			   close="closeHandler(event)">

	<fx:Script>
		<![CDATA[
			import entidades.Estado;

			import mx.collections.ArrayCollection;
			import mx.controls.Alert;
			import mx.events.CloseEvent;
			import mx.events.FlexEvent;
			import mx.events.ItemClickEvent;
			import mx.events.ListEvent;
			import mx.managers.PopUpManager;
			import mx.rpc.events.FaultEvent;
			import mx.rpc.events.ResultEvent;

			private var estadoSelec: Estado;

			private function faultHandler( event: FaultEvent ): void {
				Alert.show( event.fault.toString(), "ERRO" );
			}

			private function estadoSaveResultHandler( event: ResultEvent ): void {
				servEstado.listAll();
				resetFormEstados();
			}

			private function estadoUpdateResultHandler( event: ResultEvent ): void {
				servEstado.listAll();
				resetFormEstados();
			}

			private function estadoDeleteResultHandler( event: ResultEvent ): void {
				servEstado.listAll();
				resetFormEstados();
			}

			private function estadoListAllResultHandler( event: ResultEvent ): void {
				tabelaEstados.dataProvider = event.result;
			}

			private function btnNovoEstadoClickHandler( event: MouseEvent ): void {
				resetFormEstados();
			}

			private function btnSalvarEstadoClickHandler( event: MouseEvent ): void {

				if ( estadoSelec ) {

					estadoSelec.nome = fieldNomeEstado.text;
					estadoSelec.sigla = fieldSiglaEstado.text;

					servEstado.update(estadoSelec);

				} else {

					var o: Estado = new Estado();

					o.nome = fieldNomeEstado.text;
					o.sigla = fieldSiglaEstado.text;

					servEstado.save(o);

				}
			}

			private function btnExcluirEstadoClickHandler( event: MouseEvent ): void {

				if ( estadoSelec ) {
					servEstado.getOperation( "delete" ).send( estadoSelec );
				}

			}

			private function tabelaEstadosClickHandler( event: ListEvent ): void {

				estadoSelec = tabelaEstados.selectedItem as Estado;

				fieldNomeEstado.text = estadoSelec.nome;
				fieldSiglaEstado.text = estadoSelec.sigla;

			}

			private function resetFormEstados(): void {

				fieldNomeEstado.text = "";
				fieldSiglaEstado.text = "";

				estadoSelec = null;

			}

			private function creationCompleteHandler( event: FlexEvent ): void {
				servEstado.listAll();
			}

			private function closeHandler( event: CloseEvent ): void {
				PopUpManager.removePopUp(this);
			}

		]]>
	</fx:Script>

	<fx:Declarations>

		<s:RemoteObject
			id="servEstado"
			destination="estadoServices"
			showBusyCursor="true">

			<s:method
				name="save"
				fault="faultHandler(event)"
				result="estadoSaveResultHandler(event)"/>

			<s:method
				name="update"
				fault="faultHandler(event)"
				result="estadoUpdateResultHandler(event)"/>

			<s:method
				name="delete"
				fault="faultHandler(event)"
				result="estadoDeleteResultHandler(event)"/>

			<s:method
				name="listAll"
				fault="faultHandler(event)"
				result="estadoListAllResultHandler(event)"/>

		</s:RemoteObject>

	</fx:Declarations>

	<mx:DataGrid y="10" id="tabelaEstados" left="10" right="10" height="107" itemClick="tabelaEstadosClickHandler(event)">
		<mx:columns>
			<mx:DataGridColumn headerText="Nome" dataField="nome" width="150" resizable="true"/>
			<mx:DataGridColumn headerText="Sigla" dataField="sigla" width="80" resizable="true"/>
		</mx:columns>
	</mx:DataGrid>
	<mx:Form y="121" right="10" left="10" height="86">
		<mx:FormItem label="Nome:">
			<s:TextInput width="187" id="fieldNomeEstado"/>
		</mx:FormItem>
		<mx:FormItem label="Sigla:">
			<s:TextInput width="40" id="fieldSiglaEstado"/>
		</mx:FormItem>
	</mx:Form>
	<s:Button y="215" label="Novo" click="btnNovoEstadoClickHandler(event)" right="162"/>
	<s:Button y="215" label="Salvar" click="btnSalvarEstadoClickHandler(event)" right="84"/>
	<s:Button y="215" label="Excluir" click="btnExcluirEstadoClickHandler(event)" right="6"/>
</s:TitleWindow>

Teste e perceba que agora a janela é fechada, e o foco retorna para a tela principal da aplicação. Muito bem. Agora como exercício, faça o mesmo processo para as outras duas telas. Copie os componentes, organize-os, copie o código dos manipuladores de eventos, dos serviços, remova algum código que esteja sobrando e altere o Principal.mxml para abrir as janelas restantes. Note que para as janelas de cadastro de Cidades e de Clientes você precisará de outros serviços além do da entidade tratada na janela. Por exemplo, no cadastro de Cidades, você precisará do serviço de estados também (o combo de estados, lembra?). Não se esqueça do evento close das janelas. Para finalizar, seguem os códigos finais das janelas de cadastro de Cidades e de Clientes o código final do Principal.mxml. Agora você pode apagar o CRUD.mxml, pois já separamos todo o seu código nas nossas telas.

gui.CadastroCidades.mxml (final)

<?xml version="1.0" encoding="utf-8"?>
<s:TitleWindow xmlns:fx="http://ns.adobe.com/mxml/2009"
			   xmlns:s="library://ns.adobe.com/flex/spark"
			   xmlns:mx="library://ns.adobe.com/flex/mx" width="304" height="272" title="Cadastro de Cidades"
			   creationComplete="creationCompleteHandler(event)"
			   close="closeHandler(event)">

	<fx:Script>
		<![CDATA[
			import entidades.Cidade;
			import entidades.Estado;

			import mx.collections.ArrayCollection;
			import mx.controls.Alert;
			import mx.events.CloseEvent;
			import mx.events.FlexEvent;
			import mx.events.ItemClickEvent;
			import mx.events.ListEvent;
			import mx.managers.PopUpManager;
			import mx.rpc.events.FaultEvent;
			import mx.rpc.events.ResultEvent;

			private var cidadeSelec: Cidade;

			private function faultHandler( event: FaultEvent ): void {
				Alert.show( event.fault.toString(), "ERRO" );
			}

			private function cidadeSaveResultHandler( event: ResultEvent ): void {
				servCidade.listAll();
				resetFormCidades();
			}

			private function cidadeUpdateResultHandler( event: ResultEvent ): void {
				servCidade.listAll();
				resetFormCidades();
			}

			private function cidadeDeleteResultHandler( event: ResultEvent ): void {
				servCidade.listAll();
				resetFormCidades();
			}

			private function cidadeListAllResultHandler( event: ResultEvent ): void {
				tabelaCidades.dataProvider = event.result;
			}

			private function estadoListAllResultHandler( event: ResultEvent ): void {
				comboEstadoCidade.dataProvider = event.result as ArrayCollection;
			}

			private function btnNovoCidadeClickHandler( event: MouseEvent ): void {
				resetFormCidades();
			}

			private function btnSalvarCidadeClickHandler( event: MouseEvent ): void {

				if ( cidadeSelec ) {

					cidadeSelec.nome = fieldNomeCidade.text;
					cidadeSelec.estado = comboEstadoCidade.selectedItem as Estado;

					servCidade.update(cidadeSelec);

				} else {

					var o: Cidade = new Cidade();

					o.nome = fieldNomeCidade.text;
					o.estado = comboEstadoCidade.selectedItem as Estado;

					servCidade.save(o);

				}

			}

			private function btnExcluirCidadeClickHandler( event: MouseEvent ): void {
				if ( cidadeSelec ) {
					servCidade.getOperation( "delete" ).send( cidadeSelec );
				}
			}

			private function tabelaCidadesClickHandler( event: ListEvent ): void {

				cidadeSelec = tabelaCidades.selectedItem as Cidade;

				fieldNomeCidade.text = cidadeSelec.nome;
				comboEstadoCidade.selectedItem = cidadeSelec.estado;

			}

			private function resetFormCidades(): void {

				fieldNomeCidade.text = "";
				comboEstadoCidade.selectedIndex = -1;

				cidadeSelec = null;

			}

			private function creationCompleteHandler( event: FlexEvent ): void {
				servCidade.listAll();
				servEstado.listAll();
			}

			private function closeHandler( event: CloseEvent ): void {
				PopUpManager.removePopUp(this);
			}

		]]>
	</fx:Script>

	<fx:Declarations>

		<s:RemoteObject
			id="servCidade"
			destination="cidadeServices"
			showBusyCursor="true">

			<s:method
				name="save"
				fault="faultHandler(event)"
				result="cidadeSaveResultHandler(event)"/>

			<s:method
				name="update"
				fault="faultHandler(event)"
				result="cidadeUpdateResultHandler(event)"/>

			<s:method
				name="delete"
				fault="faultHandler(event)"
				result="cidadeDeleteResultHandler(event)"/>

			<s:method
				name="listAll"
				fault="faultHandler(event)"
				result="cidadeListAllResultHandler(event)"/>

		</s:RemoteObject>

		<s:RemoteObject
			id="servEstado"
			destination="estadoServices"
			showBusyCursor="true">

			<s:method
				name="listAll"
				fault="faultHandler(event)"
				result="estadoListAllResultHandler(event)"/>

		</s:RemoteObject>

	</fx:Declarations>

	<mx:DataGrid y="10" id="tabelaCidades" height="106" itemClick="tabelaCidadesClickHandler(event)" left="10" right="10">
		<mx:columns>
			<mx:DataGridColumn headerText="Nome" dataField="nome" width="180"/>
			<mx:DataGridColumn headerText="Estado" dataField="estado.nome" width="150"/>
		</mx:columns>
	</mx:DataGrid>
	<mx:Form y="120" left="10" right="10" height="83">
		<mx:FormItem label="Nome:">
			<s:TextInput id="fieldNomeCidade" width="189"/>
		</mx:FormItem>
		<mx:FormItem label="Estado:">
			<s:ComboBox id="comboEstadoCidade"/>
		</mx:FormItem>
	</mx:Form>
	<s:Button y="211" label="Novo" click="btnNovoCidadeClickHandler(event)" right="166"/>
	<s:Button y="211" label="Salvar" click="btnSalvarCidadeClickHandler(event)" right="88"/>
	<s:Button y="211" label="Excluir" click="btnExcluirCidadeClickHandler(event)" right="10"/>
</s:TitleWindow>

gui.CadastroClientes.mxml (final)

<?xml version="1.0" encoding="utf-8"?>
<s:TitleWindow xmlns:fx="http://ns.adobe.com/mxml/2009"
			   xmlns:s="library://ns.adobe.com/flex/spark"
			   xmlns:mx="library://ns.adobe.com/flex/mx" width="484" height="562" title="Cadastro de Clientes"
			   creationComplete="creationCompleteHandler(event)"
			   close="closeHandler(event)">

	<fx:Script>
		<![CDATA[
			import entidades.Cidade;
			import entidades.Cliente;

			import mx.collections.ArrayCollection;
			import mx.controls.Alert;
			import mx.events.CloseEvent;
			import mx.events.FlexEvent;
			import mx.events.ItemClickEvent;
			import mx.events.ListEvent;
			import mx.managers.PopUpManager;
			import mx.rpc.events.FaultEvent;
			import mx.rpc.events.ResultEvent;

			private var clienteSelec: Cliente;

			private function faultHandler( event: FaultEvent ): void {
				Alert.show( event.fault.toString(), "ERRO" );
			}

			private function cidadeListAllResultHandler( event: ResultEvent ): void {
				comboCidadeCliente.dataProvider = event.result as ArrayCollection;
			}

			private function clienteSaveResultHandler( event: ResultEvent ): void {
				servCliente.listAll();
				resetFormClientes();
			}

			private function clienteUpdateResultHandler( event: ResultEvent ): void {
				servCliente.listAll();
				resetFormClientes();
			}

			private function clienteDeleteResultHandler( event: ResultEvent ): void {
				servCliente.listAll();
				resetFormClientes();
			}

			private function clienteListAllResultHandler( event: ResultEvent ): void {
				tabelaClientes.dataProvider = event.result;
			}

			private function btnNovoClienteClickHandler( event: MouseEvent ): void {
				resetFormClientes();
			}

			private function btnSalvarClienteClickHandler( event: MouseEvent ): void {

				if ( clienteSelec ) {

					clienteSelec.nome = fieldNomeCliente.text;
					clienteSelec.sobrenome = fieldSobrenomeCliente.text;
					clienteSelec.cpf = fieldCPFCliente.text;
					clienteSelec.dataNascimento = fieldDataNascCliente.selectedDate;
					clienteSelec.rua = fieldRuaCliente.text;
					clienteSelec.numero = fieldNumeroCliente.text;
					clienteSelec.cep = fieldCEPCliente.text;
					clienteSelec.cidade = comboCidadeCliente.selectedItem as Cidade;

					servCliente.update(clienteSelec);

				} else {

					var o: Cliente = new Cliente();

					o.nome = fieldNomeCliente.text;
					o.sobrenome = fieldSobrenomeCliente.text;
					o.cpf = fieldCPFCliente.text;
					o.dataNascimento = fieldDataNascCliente.selectedDate;
					o.rua = fieldRuaCliente.text;
					o.numero = fieldNumeroCliente.text;
					o.cep = fieldCEPCliente.text;
					o.cidade = comboCidadeCliente.selectedItem as Cidade;

					servCliente.save(o);

				}

			}

			private function btnExcluirClienteClickHandler( event: MouseEvent ): void {
				if ( clienteSelec ) {
					servCliente.getOperation( "delete" ).send( clienteSelec );
				}
			}

			private function tabelaClientesClickHandler( event: MouseEvent ): void {

				clienteSelec = tabelaClientes.selectedItem as Cliente;

				fieldIdCliente.text = String( clienteSelec.id );
				fieldNomeCliente.text = clienteSelec.nome;
				fieldSobrenomeCliente.text = clienteSelec.sobrenome;
				fieldCPFCliente.text = clienteSelec.cpf;
				fieldDataNascCliente.selectedDate = clienteSelec.dataNascimento;
				fieldRuaCliente.text = clienteSelec.rua;
				fieldNumeroCliente.text = clienteSelec.numero;
				fieldCEPCliente.text = clienteSelec.cep;
				comboCidadeCliente.selectedItem = clienteSelec.cidade;

			}

			private function resetFormClientes(): void {

				fieldIdCliente.text = "";
				fieldNomeCliente.text = "";
				fieldSobrenomeCliente.text = "";
				fieldCPFCliente.text = "";
				fieldDataNascCliente.selectedDate = null;
				fieldRuaCliente.text = "";
				fieldNumeroCliente.text = "";
				fieldCEPCliente.text = "";
				comboCidadeCliente.selectedIndex = -1;

				clienteSelec = null;

			}

			private function creationCompleteHandler( event: FlexEvent ): void {
				servCidade.listAll();
				servCliente.listAll();
			}

			private function closeHandler( event: CloseEvent ): void {
				PopUpManager.removePopUp(this);
			}

		]]>
	</fx:Script>

	<fx:Declarations>

		<s:RemoteObject
			id="servCidade"
			destination="cidadeServices"
			showBusyCursor="true">

			<s:method
				name="listAll"
				fault="faultHandler(event)"
				result="cidadeListAllResultHandler(event)"/>

		</s:RemoteObject>

		<s:RemoteObject
			id="servCliente"
			destination="clienteServices"
			showBusyCursor="true">

			<s:method
				name="save"
				fault="faultHandler(event)"
				result="clienteSaveResultHandler(event)"/>

			<s:method
				name="update"
				fault="faultHandler(event)"
				result="clienteUpdateResultHandler(event)"/>

			<s:method
				name="delete"
				fault="faultHandler(event)"
				result="clienteDeleteResultHandler(event)"/>

			<s:method
				name="listAll"
				fault="faultHandler(event)"
				result="clienteListAllResultHandler(event)"/>

		</s:RemoteObject>

	</fx:Declarations>

	<mx:DataGrid y="6" left="10" right="10" height="196" id="tabelaClientes" click="tabelaClientesClickHandler(event)">
		<mx:columns>
			<mx:DataGridColumn headerText="Nome" dataField="nome"/>
			<mx:DataGridColumn headerText="Sobrenome" dataField="sobrenome"/>
			<mx:DataGridColumn headerText="CPF" dataField="cpf" width="100"/>
			<mx:DataGridColumn dataField="dataNascimento" headerText="Dt. Nasc" width="80" itemRenderer="gui.DateRenderer"/>
			<mx:DataGridColumn dataField="cidade.nome" headerText="Cidade"/>
		</mx:columns>
	</mx:DataGrid>
	<mx:Form y="210" left="10" right="10" height="281">
		<mx:FormItem label="ID:">
			<s:TextInput width="40" enabled="false" id="fieldIdCliente"/>
		</mx:FormItem>
		<mx:FormItem label="Nome:">
			<s:TextInput id="fieldNomeCliente"/>
		</mx:FormItem>
		<mx:FormItem label="Sobrenome:">
			<s:TextInput id="fieldSobrenomeCliente"/>
		</mx:FormItem>
		<mx:FormItem label="CPF:">
			<s:TextInput width="95" id="fieldCPFCliente"/>
		</mx:FormItem>
		<mx:FormItem label="Data de Nascimento:">
			<mx:DateField id="fieldDataNascCliente" formatString="DD/MM/YYYY"/>
		</mx:FormItem>
		<mx:FormItem label="Rua:">
			<s:TextInput id="fieldRuaCliente" width="249"/>
		</mx:FormItem>
		<mx:FormItem label="Número:">
			<s:TextInput width="59" id="fieldNumeroCliente"/>
		</mx:FormItem>
		<mx:FormItem label="CEP:">
			<s:TextInput width="94" id="fieldCEPCliente"/>
		</mx:FormItem>
		<mx:FormItem label="Cidade:">
			<s:ComboBox width="200" id="comboCidadeCliente"/>
		</mx:FormItem>
	</mx:Form>
	<s:Button y="498" label="Novo" click="btnNovoClienteClickHandler(event)" right="166"/>
	<s:Button y="498" label="Salvar" click="btnSalvarClienteClickHandler(event)" right="88"/>
	<s:Button y="498" label="Excluir" right="10" click="btnExcluirClienteClickHandler(event)"/>
</s:TitleWindow>

gui.Principal.mxml (final)

<?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" minWidth="955" minHeight="600">

	<fx:Script>
		<![CDATA[
			import mx.controls.Alert;
			import mx.events.MenuEvent;
			import mx.managers.PopUpManager;

			private function menuItemClickHandler( event: MenuEvent ): void {

				// faz o switch baseado no id do item selecionado
				switch ( event.item.@id.toString() ) {

					case "itemCadClientes":
						var cadastroClientes: CadastroClientes = new CadastroClientes();
						PopUpManager.addPopUp( cadastroClientes, this, true );
						cadastroClientes.x = ( this.width / 2 ) - ( cadastroClientes.width / 2 );
						cadastroClientes.y = ( this.height / 2 ) - ( cadastroClientes.height / 2 );
						break;

					case "itemCadCidades":
						var cadastroCidades: CadastroCidades = new CadastroCidades();
						PopUpManager.addPopUp( cadastroCidades, this, true );
						cadastroCidades.x = ( this.width / 2 ) - ( cadastroCidades.width / 2 );
						cadastroCidades.y = ( this.height / 2 ) - ( cadastroCidades.height / 2 );
						break;

					case "itemCadEstados":

						// instanciamos um objeto do tipo CadastroEstados
						var cadastroEstados: CadastroEstados = new CadastroEstados();

						/*
						Usa-se a classe PopUpManager para abrir a janela.
						O primeiro parâmetro é o componente a ser aberto, o segundo é
						onde ele estará contido e o terceiro é um boolean que diz se
						a janela deve ser modal ou não.
						*/
						PopUpManager.addPopUp( cadastroEstados, this, true );

						// centralizamos a janela
						cadastroEstados.x = ( this.width / 2 ) - ( cadastroEstados.width / 2 );
						cadastroEstados.y = ( this.height / 2 ) - ( cadastroEstados.height / 2 );

						break;

				}

			}

		]]>
	</fx:Script>

	<fx:Declarations>
		<!-- Place non-visual  elements (e.g., services, value objects) here -->
	</fx:Declarations>

	<mx:MenuBar y="0" left="0" right="0" labelField="@label" itemClick="menuItemClickHandler(event)">
		<fx:XMLList>
			<menuitem label="Cadastros">
				<menuitem id="itemCadClientes" label="Clientes"/>
				<menuitem type="separator"/>
				<menuitem id="itemCadCidades" label="Cidades"/>
				<menuitem id="itemCadEstados" label="Estados"/>
			</menuitem>
		</fx:XMLList>
	</mx:MenuBar>

</s:Application>

Com isso terminamos nosso tutorial onde aprendemos a separar nossa aplicação em janelas, fazendo com que ela fique mais modularizada. Como exercício você ainda pode melhorar ainda mais cada cadastro, adicionando validadores aos formulários, criando um método para centralizar as janelas na janela principal, etc. Espero que tenham gostado!

Até a próxima ;)

Parte 1Parte 2Parte 3Parte 4.1Parte 4.2

Finalmente, chegamos na última parte do nosso tutorial. Desde a primeira parte fomos construindo incrementalmente a arquitetura do nosso projeto e fomos testando como comunicar a camada do cliente (feita no Flash Builder e compilada pelo Flex) com a camada do servidor, onde usamos Java e o BlazeDS. Até o momento, nosso projeto no NetBeans tem a seguinte estrutura:

Figura 1

Estrutura do projeto no NetBeans

Com essa estrutura, temos então toda a camada de persistência e a camada de serviços pronta para ser utilizada. Percebam que os serviços dos exemplos anteriores ainda continuam no projeto. Eles não serão retirados, pois vamos deixar todos os exemplos dentro de um único projeto. Vamos agora trabalhar na nossa interface gráfica e em como integrá-la ao nosso backend.

Quando enviamos valores (primitivos e/ou objetos) através do canal do AMF, implementado pelo BlazeDS, algumas conversões são feitas. O BlazeDS acaba funcionando como um tradutor e um interpretador. Ele serializa os objetos em Java para o formato do AMF e envia para o cliente, que por sua vez entende o AMF e deserializa os dados no formato de objetos ActionScript. O contrário também é verdade. O cliente serializa objetos de ActionScript para AMF e envia para a camada em Java, onde o BlazeDS deserializa os dados em AMF convertendo-os em objetos Java. Para os tipos padrão do Java (int, double, String, etc.) e do ActionScript (int, uint, Number, String, etc.) existe um padrão de conversão. A seguir, nas próximas duas tabelas estão listadas a maioria dessas conversões.

Deserialização de objetos ActionScript para objetos Java

ActionScript Java
Array (denso) java.util.List
Array (esparso) java.util.Map
Boolean, ou as Strings “true” ou “false” java.lang.Boolean
Date java.util.Date
int/uint java.lang.Integer
Number java.lang.Double
String java.lang.String
undefined null

Deserialização de objetos Java para objetos ActionScript

Java ActionScript
java.util.Collection e subclasses, Object[] (array nativo) Array (denso)
java.util.Map Array (esparso)
Boolean, boolean Boolean
java.util.Date,
java.util.Calendar,
java.sql.Timestamp,
java.sql.Time,
java.sql.Date
Date
java.lang.Double,
java.lang.Long,
java.lang.Float,
java.lang.Integer,
java.lang.Short,
java.lang.Byte,
java.math.BigDecimal,
java.math.BigInteger,
String,
tipos primitivos: double, long, float, int, short, byte.
int/uint (truncagem automática)
java.lang.Double,
java.lang.Long,
java.lang.Float,
java.lang.Integer,
java.lang.Short,
java.lang.Byte,
java.math.BigDecimal,
java.math.BigInteger,
String, 0 (zero) se for enviado null,
tipos primitivos: double, long, float, int, short, byte
Number
java.lang.String, char[], enum String
null undefined

A primeira tabela é relativamente simples, pois um tipo ActionScript vira um tipo em Java. A única observação a ser feita é entre a diferenciação de Arrays densos e esparsos. No ActionScript, a classe Array funciona tanto como um array simples como um mapa (dicionário ou array associativo, escolha o nome que gostar mais). Quando um array denso é serializado, ele vira uma lista. Um array denso é aquele onde os valores atribuidos a ele seguem um índice determinado (0, 1, 2, …, n). No array esparso a distribuição é baseada em uma “chave” que é utilizada como índice. Sendo assim, o posicionamento dos dados dentro da estrutura de dados é indeterminada. Note a semelhança com um mapa do Java.

Em relação à segunda tabela, perceba que um ou mais tipos do Java são convertidos em um ou mais tipos em ActionScript. O funcionamento é o seguinte. Imagine que você tem uma variável do tipo Number do lado do ActionScript e então é enviada uma String em Java, que vai representar um número. Quando chegar a String e ela for ser atribuida a um Number, a conversão vai ser automática. Outro exemplo. Imagine que foi enviado um double em Java e que a variável no ActionScript é do tipo uint. Novamente, a conversão vai ser automática, truncando a parte fracionária do double. Note que a conversão é sempre automática quando usamos o AMF.

Essas tabelas são um resumo das tabelas encontradas nos dois links a seguir, que fazem parte da documentação do BlazeDS.

Legal, o problema para tipos padrão está resolvido, mas ai você pergunta:

“Certo, e o que eu faço se eu mandar um Estado, uma Cidade, um Cliente ou mesmo qualquer outro  tipo de objeto criado por mim? Como será feita a conversão?”

Vamos às repostas: Por padrão o objeto será convertido em Object no ActionScript, mas isso nos atrapalha, pois fica difícil trabalhar com algo que você (desenvolvedor) não sabe o que é. Como o ActionScript é uma linguagem dinâmica, ele vai resolver as propriedades do objeto para nós automaticamente, mas como já disse, seria legal a gente saber com o que estamos trabalhando, facilitando a nossa vida, tornando o código mais claro. Para isso, precisamos então implementar as classes que enviamos do lado Java para suas correspondentes em ActionScript. Os tipos padrão continuam a mesma coisa (siga as tabelas acima). Vamos então ao primeiro exemplo.

No Flash Builder, no projeto da nossa interface, clique com o botão direito na pasta “src” e escolha New > Package. Dê o nome de “entidades” ao pacote e clique em OK. Lembre-se, para cada classe Java que tivermos (as entidades), teremos uma classe em ActionScript. Sendo assim, teremos três classes em ActionScript: Estado, Cidade e Cliente. Vamos criar então uma classe dentro do pacote criado. Começaremos pela classe Estado. Vamos lá então: botão direito no pacote “entidades”, New > ActionScript Class. Preencha o campo “Name” com “Estado” (sem aspas) e clique no OK. A classe será criada e será aberta no editor. Note que por padrão o Flash Builder vai usar as chaves de abertura de bloco abaixo da declaração dos métodos, etc. Eu não gosto desse padrão, prefiro seguir o padrão do Java, mas você fica livre para escolher o que achar melhor.

Note que a estrutura é parecida com a estrutura de uma classe em Java, menos pelo detalhe do pacote, que  também é um bloco em classes ActionScript. Vamos começar a preencher nossa classe. Vamos começar pelos atributos. Note que todos os atributos privados iniciam com um “_”. Isso não é obrigatório, mas já eu explico o motivo.

Classe em Java Classe em ActionScript
package entidades;

public class Estado {

    private Long id;
    private String nome;
    private String sigla;

    // getters e setters

}
package entidades {

	public class Estado {

		private var _id: Number;
		private var _nome: String;
		private var _sigla: String;

		// getters e setters

	}

}

Perceba então que o Long do Java virou Number no ActionScript e o tipo String no Java virou String no ActionScript. Fácil não é? É só seguir as tabelas do começo do post. Agora quanto ao “_”. No ActionScript, como em outras linguagens, existe um recurso que se chama propriedades. Você cria métodos especiais que são chamados para atualizar os membros privados da classe como se você estivesse acessando esse membro como um membro público. No Java esse recurso não existe, então seguimos o padrão JavaBeans (get e set para expor os atributos de uma classe, etc.). Então para cada par get e set do Java, teremos um par de propriedades na classe ActionScript. Veja então como ficaria o método setId( Long id ) e getId() da classe Java na classe ActionScript.

Gets e Sets (Padrão JavaBeans) Propriedades em ActionScript
public Long getId() {
    return id;
}

public void setId( Long id ) {
    this.id = id;
}
public function get id(): Number {
	return _id;
}

public function set id( valor: Number ): void {
	_id = valor;
}

Então, enrolei mas ainda não falei do “_”. Note que se caso a nossa variável privada “_id” não tivesse o “_”, o nome dela entraria em conflito com o nome da pripriedade “id” que foi criada para acessar a variável privada. Sendo assim, normalmente, em linguagens que suportam propriedades, usa-se o “_” como prefixo para as variáveis privadas. Entendido isso e as propriedades, vamos então ao código completo da nossa classe Estado em ActionScript:

entidades.Estado.as

package entidades {

	[Bindable]
	[RemoteClass(alias="entidades.Estado")]
	public class Estado {

		private var _id: Number;
		private var _nome: String;
		private var _sigla: String;

		public function get id(): Number {
			return _id;
		}

		public function set id( valor: Number ): void {
			_id = valor;
		}

		public function get nome(): String {
			return _nome;
		}

		public function set nome( valor: String ): void {
			_nome = valor;
		}

		public function get sigla(): String {
			return _sigla;
		}

		public function set sigla( valor: String ): void {
			_sigla = valor;
		}

		public function toString(): String {
			return _nome;
		}

	}

}

Note ainda que antes da declaração da classe Estado, temos ainda duas Metadata Tags (parecidas com as Annotations em Java). Segue a explicação de cada uma:

  • [Bindable]: Essa tag informa que os objetos do tipo Estado podem ser amarrados aos componentes gráficos e caso o estado do objeto seja alterado, ou seja, alguma de suas propriedades tenha o valor alterado, essas mudanças serão refletidas automaticamente no componente gráfico que o estiver usando.
  • [RemoteClass(alias="entidades.Estado")]: Essa tag é muito importante para nós. Ela diz que essa classe é uma classe remota, ou seja, que os objetos dela podem ser enviados via AMF. Note que a propriedade “alias” tem como valor o nome completo da classe em Java. Caso nossas entidades Java estivesse no pacote “model” por exemplo, o “alias” teria que ser “model.Estado”. Como estamos usando uma estrutura de pacotes igual nos dois projetos, o “alias” é igual ao do projeto em Flex, mas não se confunda. O “alias” é sempre igual ao nome completo da classe em Java.

Perceba também que implementamos o método toString() da classe Estado. O toString() no Flex tem o mesmo propósito que no Java, ou seja, fornecer uma representação em String do estado de um determinado objeto. No nosso caso, estamos apenas retornando o nome do Estado (e faremos isso para as outra duas classes). A vantagem disso é que os objetos serão mostrados nos componentes que os usarem (combos por exemplo) utilizando o retorno do toString().

Pronto, você já sabe todos os detalhes. Seria legal agora, como exercício, você criar as duas classes que estão faltando, ou seja, as classes Cidade e Cliente. Assim que criar, compare com as que estou listando abaixo. Como fiz nas entidades em Java, não vou colocar os gets e os sets para poupar espaço.

entidades.Cidade.as

package entidades {

	[Bindable]
	[RemoteClass(alias="entidades.Cidade")]
	public class Cidade {

		private var _id: Number;
		private var _nome: String;
		private var _estado: Estado;

		// getters e setters

		public function toString(): String {
			return _nome;
		}

	}

}

entidades.Cliente.as

package entidades {

	[Bindable]
	[RemoteClass(alias="entidades.Cliente")]
	public class Cliente {

		private var _id: Number;
		private var _nome: String;
		private var _sobrenome: String;
		private var _cpf: String;
		private var _dataNascimento: Date;
		private var _rua: String;
		private var _numero: String;
		private var _cep: String;
		private var _cidade: Cidade;

		// getters e setters

		public function toString(): String {
			return _nome;
		}

	}

}

Ótimo, temos todas as nossas classes mapeadas, agora vamos partir para a interface gráfica. Vamos criar outro arquivo mxml para criarmos nossa interface. Para isso, no “(default package)”, clique com o botão direito e escolhe New > MXML Application. Dê o nome de “CRUD” (sem aspas) e escolha “None” no layout. Clique em Finish. O novo arquivo será criado e aberto no editor.

Caso você execute diretamente o projeto dentro do Flash Builder, para ver como está ficando no navegador, você vai perceber que a interface anterior ainda vai ser exibida. Para mudar isso, precisamos fazer com que o CRUD.mxml seja a aplicação padrão. Para isso, clique com o botão direito no arquivo CRUD.mxml e escolha a opção “Set as Default Application”. Você vai perceber que vai aparecer uma bolinha azul no ícone do arquivo (que antes estava no IntergracaoFlexJavaGUI.mxml).

Ainda falta configurar o index.jsp lá no NetBeans para que seja redirecionado para o CRUD.html ao invés do IntegracaoFlexJavaGUI.html. Como exercício, faça a alteração então na tag <meta> lá do seu index.jsp e rode o projeto. Uma tela em branco deve ser carregada.

O próximo passo agora é criar os <s:RemoteObject> que vão fazer a ponte para a camada em Java e montar a interface gráfica. Depois iremos implementar os eventos para dar vida à interface. Segue então o CRUD.mxml com a interface pronta, os servicos mapeados, todos os manipuladores de eventos criados e registrados (aplicação, botões e tabelas) e o id de todos os componentes criados, além do prinscreen da interface.

CRUD.mxml

<?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" minWidth="955" minHeight="600"
			   creationComplete="creationCompleteHandler(event)">

	<fx:Script>
		<![CDATA[
			import entidades.Cidade;
			import entidades.Cliente;
			import entidades.Estado;

			import mx.collections.ArrayCollection;
			import mx.controls.Alert;
			import mx.events.FlexEvent;
			import mx.events.ItemClickEvent;
			import mx.events.ListEvent;
			import mx.rpc.events.FaultEvent;
			import mx.rpc.events.ResultEvent;

			// armazenam a referência para as instâncias selecionadas no momento.
			private var estadoSelec: Estado;
			private var cidadeSelec: Cidade;
			private var clienteSelec: Cliente;

			private function faultHandler( event: FaultEvent ): void {
				Alert.show( event.fault.toString(), "ERRO" );
			}

			// handlers para estado
			private function estadoSaveResultHandler( event: ResultEvent ): void {

			}

			private function estadoUpdateResultHandler( event: ResultEvent ): void {

			}

			private function estadoDeleteResultHandler( event: ResultEvent ): void {

			}

			private function estadoListAllResultHandler( event: ResultEvent ): void {
				tabelaEstados.dataProvider = event.result;
				comboEstadoCidade.dataProvider = event.result as ArrayCollection;
			}

			private function btnNovoEstadoClickHandler( event: MouseEvent ): void {

			}

			private function btnSalvarEstadoClickHandler( event: MouseEvent ): void {

			}

			private function btnExcluirEstadoClickHandler( event: MouseEvent ): void {

			}

			private function tabelaEstadosClickHandler( event: ListEvent ): void {

			}

			// handlers para cidade
			private function cidadeSaveResultHandler( event: ResultEvent ): void {

			}

			private function cidadeUpdateResultHandler( event: ResultEvent ): void {

			}

			private function cidadeDeleteResultHandler( event: ResultEvent ): void {

			}

			private function cidadeListAllResultHandler( event: ResultEvent ): void {
				tabelaCidades.dataProvider = event.result;
				comboCidadeCliente.dataProvider = event.result as ArrayCollection;
			}

			private function btnNovoCidadeClickHandler( event: MouseEvent ): void {

			}

			private function btnSalvarCidadeClickHandler( event: MouseEvent ): void {

			}

			private function btnExcluirCidadeClickHandler( event: MouseEvent ): void {

			}

			private function tabelaCidadesClickHandler( event: ListEvent ): void {

			}

			// handlers para cliente
			private function clienteSaveResultHandler( event: ResultEvent ): void {

			}

			private function clienteUpdateResultHandler( event: ResultEvent ): void {

			}

			private function clienteDeleteResultHandler( event: ResultEvent ): void {

			}

			private function clienteListAllResultHandler( event: ResultEvent ): void {
				tabelaClientes.dataProvider = event.result;
			}

			private function btnNovoClienteClickHandler( event: MouseEvent ): void {

			}

			private function btnSalvarClienteClickHandler( event: MouseEvent ): void {

			}

			private function btnExcluirClienteClickHandler( event: MouseEvent ): void {

			}

			private function tabelaClientesClickHandler( event: MouseEvent ): void {

			}

			// handlers da aplicação
			private function creationCompleteHandler( event: FlexEvent ): void {
				servEstado.listAll();
				servCidade.listAll();
				servCliente.listAll();
			}

		]]>
	</fx:Script>

	<fx:Declarations>

		<s:RemoteObject
			id="servEstado"
			destination="estadoServices"
			showBusyCursor="true">

			<s:method
				name="save"
				fault="faultHandler(event)"
				result="estadoSaveResultHandler(event)"/>

			<s:method
				name="update"
				fault="faultHandler(event)"
				result="estadoUpdateResultHandler(event)"/>

			<s:method
				name="delete"
				fault="faultHandler(event)"
				result="estadoDeleteResultHandler(event)"/>

			<s:method
				name="listAll"
				fault="faultHandler(event)"
				result="estadoListAllResultHandler(event)"/>

		</s:RemoteObject>

		<s:RemoteObject
			id="servCidade"
			destination="cidadeServices"
			showBusyCursor="true">

			<s:method
				name="save"
				fault="faultHandler(event)"
				result="cidadeSaveResultHandler(event)"/>

			<s:method
				name="update"
				fault="faultHandler(event)"
				result="cidadeUpdateResultHandler(event)"/>

			<s:method
				name="delete"
				fault="faultHandler(event)"
				result="cidadeDeleteResultHandler(event)"/>

			<s:method
				name="listAll"
				fault="faultHandler(event)"
				result="cidadeListAllResultHandler(event)"/>

		</s:RemoteObject>

		<s:RemoteObject
			id="servCliente"
			destination="clienteServices"
			showBusyCursor="true">

			<s:method
				name="save"
				fault="faultHandler(event)"
				result="clienteSaveResultHandler(event)"/>

			<s:method
				name="update"
				fault="faultHandler(event)"
				result="clienteUpdateResultHandler(event)"/>

			<s:method
				name="delete"
				fault="faultHandler(event)"
				result="clienteDeleteResultHandler(event)"/>

			<s:method
				name="listAll"
				fault="faultHandler(event)"
				result="clienteListAllResultHandler(event)"/>

		</s:RemoteObject>

	</fx:Declarations>

	<s:Panel x="10" y="10" width="567" height="565" title="Cadastro de Clientes">
		<mx:DataGrid y="6" left="10" right="10" height="196" id="tabelaClientes" click="tabelaClientesClickHandler(event)">
			<mx:columns>
				<mx:DataGridColumn headerText="Nome" dataField="nome"/>
				<mx:DataGridColumn headerText="Sobrenome" dataField="sobrenome"/>
				<mx:DataGridColumn headerText="CPF" dataField="cpf" width="100"/>
				<mx:DataGridColumn dataField="dataNascimento" headerText="Dt. Nasc" width="80"/>
				<mx:DataGridColumn dataField="cidade.nome" headerText="Cidade"/>
			</mx:columns>
		</mx:DataGrid>
		<mx:Form y="210" left="10" right="10" height="281">
			<mx:FormItem label="ID:">
				<s:TextInput width="40" enabled="false" id="fieldIdCliente"/>
			</mx:FormItem>
			<mx:FormItem label="Nome:">
				<s:TextInput id="fieldNomeCliente"/>
			</mx:FormItem>
			<mx:FormItem label="Sobrenome:">
				<s:TextInput id="fieldSobrenomeCliente"/>
			</mx:FormItem>
			<mx:FormItem label="CPF:">
				<s:TextInput width="95" id="fieldCPFCliente"/>
			</mx:FormItem>
			<mx:FormItem label="Data de Nascimento:">
				<mx:DateField id="fieldDataNascCliente"/>
			</mx:FormItem>
			<mx:FormItem label="Rua:">
				<s:TextInput id="fieldRuaCliente" width="249"/>
			</mx:FormItem>
			<mx:FormItem label="Número:">
				<s:TextInput width="59" id="fieldNumeroCliente"/>
			</mx:FormItem>
			<mx:FormItem label="CEP:">
				<s:TextInput width="94" id="fieldCEPCliente"/>
			</mx:FormItem>
			<mx:FormItem label="Cidade:">
				<s:ComboBox width="200" id="comboCidadeCliente"/>
			</mx:FormItem>
		</mx:Form>
		<s:Button x="329" y="499" label="Novo" click="btnNovoClienteClickHandler(event)"/>
		<s:Button x="407" y="499" label="Salvar" click="btnSalvarClienteClickHandler(event)"/>
		<s:Button y="499" label="Excluir" right="10" click="btnExcluirClienteClickHandler(event)"/>
	</s:Panel>
	<s:Panel x="585" y="10" width="360" height="277" title="Cadastro de Cidades">
		<mx:DataGrid x="10" y="10" id="tabelaCidades" width="338" height="106" itemClick="tabelaCidadesClickHandler(event)">
			<mx:columns>
				<mx:DataGridColumn headerText="Nome" dataField="nome" width="180"/>
				<mx:DataGridColumn headerText="Estado" dataField="estado.nome" width="150"/>
			</mx:columns>
		</mx:DataGrid>
		<mx:Form y="120" left="10" right="10" height="83">
			<mx:FormItem label="Nome:">
				<s:TextInput id="fieldNomeCidade" width="189"/>
			</mx:FormItem>
			<mx:FormItem label="Estado:">
				<s:ComboBox id="comboEstadoCidade"/>
			</mx:FormItem>
		</mx:Form>
		<s:Button x="126" y="211" label="Novo" click="btnNovoCidadeClickHandler(event)"/>
		<s:Button x="204" y="211" label="Salvar" click="btnSalvarCidadeClickHandler(event)"/>
		<s:Button x="278" y="211" label="Excluir" click="btnExcluirCidadeClickHandler(event)"/>
	</s:Panel>
	<s:Panel x="585" y="295" width="360" height="280" title="Cadastro de Estados">
		<mx:DataGrid y="10" id="tabelaEstados" left="10" right="10" height="107" itemClick="tabelaEstadosClickHandler(event)">
			<mx:columns>
				<mx:DataGridColumn headerText="Nome" dataField="nome" width="150" resizable="true"/>
				<mx:DataGridColumn headerText="Sigla" dataField="sigla" width="80" resizable="true"/>
			</mx:columns>
		</mx:DataGrid>
		<mx:Form y="121" right="11" left="9" height="86">
			<mx:FormItem label="Nome:">
				<s:TextInput width="187" id="fieldNomeEstado"/>
			</mx:FormItem>
			<mx:FormItem label="Sigla:">
				<s:TextInput width="40" id="fieldSiglaEstado"/>
			</mx:FormItem>
		</mx:Form>
		<s:Button x="121" y="215" label="Novo" click="btnNovoEstadoClickHandler(event)"/>
		<s:Button x="199" y="215" label="Salvar" click="btnSalvarEstadoClickHandler(event)"/>
		<s:Button x="277" y="215" label="Excluir" click="btnExcluirEstadoClickHandler(event)"/>
	</s:Panel>
</s:Application>
Figura 2

Estrutura da interface

Pronto, temos toda a estrutura da aplicação, agora só falta implementar os métodos que tratam os eventos. Primeiramente vou explicar o que já foi feito. Percebeam que no ínicio do CRUD.mxml, na tag <s:Application>, foi registrado um ouvinte para o evento creationComplete. Este evento é disparado quando toda a estrutura da aplicação está criada e pronta para funcionar. No corpo do método que trata o evento (linha 132) você pode perceber que são executados os métodos listAll dos três <s:RemoteObject>. Isso faz com que todas as instâncias de todas as entidades sejam carregadas. Lembre-se que o resultado da execução do serviço é tratado nos métodos registrados para o evento result.

Na linha 43, o método estadoListAllResultHandler obtém o resultado da execução (a lista de estados retornada pelo serviço) e a associa diretamente ao dataProvider da tabelaEstados. Isso faz com que a tabela mostre os Estados obtidos. Além de ligar os dados à tabela, o resultado também é associado ao combo de estados (dentro do formulário de cidades), sendo que para fazer isso existe a necessidade de fazer o cast do resultado para um ArrayCollection (o que não precisa ser feito para a tabela). Note que os outros métodos result (para Cidade e Cliente) executam tarefas parecidas.

Para que uma tabela (<d:DataGrid>) exiba corretamente os dados da lista de objetos que é atribuida ao seu dataProvider, você precisa configurar a propriedade dataField de cada coluna. Por exemplo, na tabela de clientes, a primeira coluna tem o dataField configurado como “name”, pois a lista de objetos que será usada como dataProvider, contém Clientes, que tem nome como propriedade. Ou seja, o dataField tem que ter o mesmo nome da propriedade dos objetos que são passados ao dataProvider. Veja o dataField da coluna na linha 239. O valor é “cidade.nome”, pois uma instância de Cliente tem uma propriedade que chama “cidade”, sendo que essa propriedade é uma instância de Cidade, que por sua vez, tem um “nome”.

Acredito que a maioria dos detalhes seja fácil de ser entendida se você der uma estudada no código. Agora vamos passar para a implementação de todos os métodos para o cadastro de Estados. Irei explicar um por um, com comentários dentro do código de cada método que vai ser apresentado. Lembre-se que caso você esteja acompanhando o tutorial e estiver implementando passo a passo, os métodos já estão declarados, bastando inserir o código dentro de cada um.

Métodos para o cadastro de Estados

// handlers para estado
private function estadoSaveResultHandler( event: ResultEvent ): void {

	// se entrou no método, quer dizer que o estado foi salvo, sendo assim,
	// atualiza a tabela chamando o listAll e reinicia o formulário.
	servEstado.listAll();
	resetFormEstados();

}

private function estadoUpdateResultHandler( event: ResultEvent ): void {

	// se entrou no método, quer dizer que o estado foi alterado, sendo assim,
	// atualiza a tabela chamando o listAll e reinicia o formulário.
	servEstado.listAll();
	resetFormEstados();

}

private function estadoDeleteResultHandler( event: ResultEvent ): void {

	// se entrou no método, quer dizer que o estado foi excluído, sendo assim,
	// atualiza a tabela chamando o listAll e reinicia o formulário.
	servEstado.listAll();
	resetFormEstados();

}

private function estadoListAllResultHandler( event: ResultEvent ): void {

	// configura o dataProvider da tabela com os dados que vem no result.
	// nesse caso, é uma lista de Estados.
	tabelaEstados.dataProvider = event.result;

	// o dataProvider de um combo espera por algo que implemente a interface IList
	// o result do serviço retorna uma lista, então podemos fazer o cast para ArrayCollection
	comboEstadoCidade.dataProvider = event.result as ArrayCollection;

}

private function btnNovoEstadoClickHandler( event: MouseEvent ): void {

	// reseta o formulário
	resetFormEstados();

}

private function btnSalvarEstadoClickHandler( event: MouseEvent ): void {

	// estadoSelec é diferente de null ou de undefined?
	if ( estadoSelec ) {

		// então existe um estado selecionado

		// altera os valores
		estadoSelec.nome = fieldNomeEstado.text;
		estadoSelec.sigla = fieldSiglaEstado.text;

		// manda atualizar
		servEstado.update(estadoSelec);

	} else {

		// não tem estado selecionado, cria um novo
		var o: Estado = new Estado();

		// insere os valores
		o.nome = fieldNomeEstado.text;
		o.sigla = fieldSiglaEstado.text;

		// manda salvar
		servEstado.save(o);

	}
}

private function btnExcluirEstadoClickHandler( event: MouseEvent ): void {

	// estadoSelec é diferente de null ou de undefined?
	if ( estadoSelec ) {

		// sim, então exclui o estado.
		// seria legal uma confirmação aqui não acha? fica de exercício para você hehehe

		// obs: note que não chamamos servEstado.delete diretamente, pois delete
		// é uma pavra-chave no ActionScript. Sendo assim, usamos a seguinte
		// instrução para invoca o método sem ter problemas:
		servEstado.getOperation( "delete" ).send( estadoSelec );

	}

}

private function tabelaEstadosClickHandler( event: ListEvent ): void {

	// estadoSelec recebe o item selecionado na lista
	estadoSelec = tabelaEstados.selectedItem as Estado;

	// preenche os campos
	fieldNomeEstado.text = estadoSelec.nome;
	fieldSiglaEstado.text = estadoSelec.sigla;

}

// função para limpar o campo e resetar a referência para o estado selecionado
// esse método é novo, não foi apresentado no código anterior.
private function resetFormEstados(): void {

	// limpa os campos
	fieldNomeEstado.text = "";
	fieldSiglaEstado.text = "";

	// não tem mais estados selecionados
	estadoSelec = null;

}

Teste o exemplo. Veja que agora todo o cadastro do Estados funciona, e caso um Estado seja alterado ou um novo Estado seja inserido, tanto a tabela de Estados quanto o combo de Estados do cadastro de cidades são atualizados. Note que enviamos os objetos inteiros como parâmetros nos métodos save, update e delete do serviço de estados. Percebeu como é fácil? A serialização/deserialização é transparatente para nós.

Agora, antes de finalizarmos, ainda precisamos resolver um último detalhe: Arrumar o formato da data de nascimento na tabela do cliente. Para isso, vamos criar um renderizador para a coluna da data. O primeiro passo é escolher a tabela de clientes. Clique na tabela, e na aba de propriedades, clique no botão “Configure Columns…”.

Figura 3

Acessando as propriedades das colunas

Ao clicar, a janela “Configura Columns” vai aparecer. Do lado esquerdo estão listadas as colunas da tabela. Selecione a “Dt. Nasc” e nas propriedades da coluna, clique no botão rosa (destacado em laranja na Figura abaixo) e escolha “Create Item Renderer…”.

Figura 4

Criando um renderizador para a coluna

Uma janela para criar o novo renderizador vai aparecer. Deixe o “Package” vazio e preencha o “Name” com “DateRenderer” (sem aspas). Clique em “Finish” para criar o renderizador e veja que agora o nome do renderizador aparece nas propriedades da coluna. Dê OK na janela de configuração das colunas. Perceba que um arquivo chamado DateRenderer.mxml foi criado. Vamos editá-lo. Basicamente iremos usar um <mx:DateFormatter> para formatar o label que vai conter os dados. Segue então o código do nosso renderizador, basta colar dentro do arquivo.

DateRenderer.mxml

<?xml version="1.0" encoding="utf-8"?>
<s:MXDataGridItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
						  xmlns:s="library://ns.adobe.com/flex/spark"
						  xmlns:mx="library://ns.adobe.com/flex/mx"
						  focusEnabled="true">
	<fx:Declarations>
		<mx:DateFormatter id="formatadorData" formatString="DD/MM/YYYY"/>
	</fx:Declarations>

	<s:Label id="lblData"
			 width="100%" height="100%" verticalAlign="middle" paddingLeft="10"
			 text="{formatadorData.format(dataGridListData.label)}"/>

</s:MXDataGridItemRenderer>

Salve o projeto e teste novamente. Você vai ver que agora a data de nascimento dos clientes vai ser formatada corretamente. Com isso terminamos as explicações referentes à integração do Flex com o Java, além de termos feito um exemplo com muitos recursos. Sugiro agora que como exercício você tente implementar os métodos que estão faltando. Caso tenha dúvidas, verifique o código completo (e final) abaixo.

CRUD.mxml (final)

<?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" minWidth="955" minHeight="600"
			   creationComplete="creationCompleteHandler(event)">

	<fx:Script>
		<![CDATA[
			import entidades.Cidade;
			import entidades.Cliente;
			import entidades.Estado;

			import mx.collections.ArrayCollection;
			import mx.controls.Alert;
			import mx.events.FlexEvent;
			import mx.events.ItemClickEvent;
			import mx.events.ListEvent;
			import mx.rpc.events.FaultEvent;
			import mx.rpc.events.ResultEvent;

			// armazenam a referência para as instâncias selecionadas no momento.
			private var estadoSelec: Estado;
			private var cidadeSelec: Cidade;
			private var clienteSelec: Cliente;

			private function faultHandler( event: FaultEvent ): void {
				Alert.show( event.fault.toString(), "ERRO" );
			}

			// handlers para estado
			private function estadoSaveResultHandler( event: ResultEvent ): void {

				// se entrou no método, quer dizer que o estado foi salvo, sendo assim,
				// atualiza a tabela chamando o listAll e reinicia o formulário.
				servEstado.listAll();
				resetFormEstados();

			}

			private function estadoUpdateResultHandler( event: ResultEvent ): void {

				// se entrou no método, quer dizer que o estado foi alterado, sendo assim,
				// atualiza a tabela chamando o listAll e reinicia o formulário.
				servEstado.listAll();
				resetFormEstados();

			}

			private function estadoDeleteResultHandler( event: ResultEvent ): void {

				// se entrou no método, quer dizer que o estado foi excluído, sendo assim,
				// atualiza a tabela chamando o listAll e reinicia o formulário.
				servEstado.listAll();
				resetFormEstados();

			}

			private function estadoListAllResultHandler( event: ResultEvent ): void {

				// configura o dataProvider da tabela com os dados que vem no result.
				// nesse caso, é uma lista de Estados.
				tabelaEstados.dataProvider = event.result;

				// o dataProvider de um combo espera por algo que implemente a interface IList
				// o result do serviço retorna uma lista, então podemos fazer o cast para ArrayCollection
				comboEstadoCidade.dataProvider = event.result as ArrayCollection;

			}

			private function btnNovoEstadoClickHandler( event: MouseEvent ): void {

				// reseta o formulário
				resetFormEstados();

			}

			private function btnSalvarEstadoClickHandler( event: MouseEvent ): void {

				// estadoSelec é diferente de null ou de undefined?
				if ( estadoSelec ) {

					// então existe um estado selecionado

					// altera os valores
					estadoSelec.nome = fieldNomeEstado.text;
					estadoSelec.sigla = fieldSiglaEstado.text;

					// manda atualizar
					servEstado.update(estadoSelec);

				} else {

					// não tem estado selecionado, cria um novo
					var o: Estado = new Estado();

					// insere os valores
					o.nome = fieldNomeEstado.text;
					o.sigla = fieldSiglaEstado.text;

					// manda salvar
					servEstado.save(o);

				}
			}

			private function btnExcluirEstadoClickHandler( event: MouseEvent ): void {

				// estadoSelec é diferente de null ou de undefined?
				if ( estadoSelec ) {

					// sim, então exclui o estado.
					// seria legal uma confirmação aqui não acha? fica de exercício hehehe

					// obs: note que não chamamos servEstado.delete diretamente, pois delete
					// é uma pavra-chave no ActionScript. Sendo assim, usamos a seguinte
					// instrução para invoca o método sem ter problemas:
					servEstado.getOperation( "delete" ).send( estadoSelec );

				}

			}

			private function tabelaEstadosClickHandler( event: ListEvent ): void {

				// estadoSelec recebe o item selecionado na lista
				estadoSelec = tabelaEstados.selectedItem as Estado;

				// preenche os campos
				fieldNomeEstado.text = estadoSelec.nome;
				fieldSiglaEstado.text = estadoSelec.sigla;

			}

			// função para limpar o campo e resetar a referência para o estado selecionado
			// esse método é novo, não foi apresentado no código anterior.
			private function resetFormEstados(): void {

				// limpa os campos
				fieldNomeEstado.text = "";
				fieldSiglaEstado.text = "";

				// não tem mais estados selecionados
				estadoSelec = null;

			}

			// handlers para cidade
			private function cidadeSaveResultHandler( event: ResultEvent ): void {
				servCidade.listAll();
				resetFormCidades();
			}

			private function cidadeUpdateResultHandler( event: ResultEvent ): void {
				servCidade.listAll();
				resetFormCidades();
			}

			private function cidadeDeleteResultHandler( event: ResultEvent ): void {
				servCidade.listAll();
				resetFormCidades();
			}

			private function cidadeListAllResultHandler( event: ResultEvent ): void {
				tabelaCidades.dataProvider = event.result;
				comboCidadeCliente.dataProvider = event.result as ArrayCollection;
			}

			private function btnNovoCidadeClickHandler( event: MouseEvent ): void {
				resetFormCidades();
			}

			private function btnSalvarCidadeClickHandler( event: MouseEvent ): void {

				if ( cidadeSelec ) {

					cidadeSelec.nome = fieldNomeCidade.text;
					cidadeSelec.estado = comboEstadoCidade.selectedItem as Estado;

					servCidade.update(cidadeSelec);

				} else {

					var o: Cidade = new Cidade();

					o.nome = fieldNomeCidade.text;
					o.estado = comboEstadoCidade.selectedItem as Estado;

					servCidade.save(o);

				}

			}

			private function btnExcluirCidadeClickHandler( event: MouseEvent ): void {
				if ( cidadeSelec ) {
					servCidade.getOperation( "delete" ).send( cidadeSelec );
				}
			}

			private function tabelaCidadesClickHandler( event: ListEvent ): void {

				cidadeSelec = tabelaCidades.selectedItem as Cidade;

				fieldNomeCidade.text = cidadeSelec.nome;
				comboEstadoCidade.selectedItem = cidadeSelec.estado;

			}

			private function resetFormCidades(): void {

				fieldNomeCidade.text = "";
				comboEstadoCidade.selectedIndex = -1;

				cidadeSelec = null;

			}

			// handlers para cliente
			private function clienteSaveResultHandler( event: ResultEvent ): void {
				servCliente.listAll();
				resetFormClientes();
			}

			private function clienteUpdateResultHandler( event: ResultEvent ): void {
				servCliente.listAll();
				resetFormClientes();
			}

			private function clienteDeleteResultHandler( event: ResultEvent ): void {
				servCliente.listAll();
				resetFormClientes();
			}

			private function clienteListAllResultHandler( event: ResultEvent ): void {
				tabelaClientes.dataProvider = event.result;
			}

			private function btnNovoClienteClickHandler( event: MouseEvent ): void {
				resetFormClientes();
			}

			private function btnSalvarClienteClickHandler( event: MouseEvent ): void {

				if ( clienteSelec ) {

					clienteSelec.nome = fieldNomeCliente.text;
					clienteSelec.sobrenome = fieldSobrenomeCliente.text;
					clienteSelec.cpf = fieldCPFCliente.text;
					clienteSelec.dataNascimento = fieldDataNascCliente.selectedDate;
					clienteSelec.rua = fieldRuaCliente.text;
					clienteSelec.numero = fieldNumeroCliente.text;
					clienteSelec.cep = fieldCEPCliente.text;
					clienteSelec.cidade = comboCidadeCliente.selectedItem as Cidade;

					servCliente.update(clienteSelec);

				} else {

					var o: Cliente = new Cliente();

					o.nome = fieldNomeCliente.text;
					o.sobrenome = fieldSobrenomeCliente.text;
					o.cpf = fieldCPFCliente.text;
					o.dataNascimento = fieldDataNascCliente.selectedDate;
					o.rua = fieldRuaCliente.text;
					o.numero = fieldNumeroCliente.text;
					o.cep = fieldCEPCliente.text;
					o.cidade = comboCidadeCliente.selectedItem as Cidade;

					servCliente.save(o);

				}

			}

			private function btnExcluirClienteClickHandler( event: MouseEvent ): void {
				if ( clienteSelec ) {
					servCliente.getOperation( "delete" ).send( clienteSelec );
				}
			}

			private function tabelaClientesClickHandler( event: MouseEvent ): void {

				clienteSelec = tabelaClientes.selectedItem as Cliente;

				fieldIdCliente.text = String( clienteSelec.id );
				fieldNomeCliente.text = clienteSelec.nome;
				fieldSobrenomeCliente.text = clienteSelec.sobrenome;
				fieldCPFCliente.text = clienteSelec.cpf;
				fieldDataNascCliente.selectedDate = clienteSelec.dataNascimento;
				fieldRuaCliente.text = clienteSelec.rua;
				fieldNumeroCliente.text = clienteSelec.numero;
				fieldCEPCliente.text = clienteSelec.cep;
				comboCidadeCliente.selectedItem = clienteSelec.cidade;

			}

			private function resetFormClientes(): void {

				fieldIdCliente.text = "";
				fieldNomeCliente.text = "";
				fieldSobrenomeCliente.text = "";
				fieldCPFCliente.text = "";
				fieldDataNascCliente.selectedDate = null;
				fieldRuaCliente.text = "";
				fieldNumeroCliente.text = "";
				fieldCEPCliente.text = "";
				comboCidadeCliente.selectedIndex = -1;

				clienteSelec = null;

			}

			// handlers da aplicação
			private function creationCompleteHandler( event: FlexEvent ): void {
				servEstado.listAll();
				servCidade.listAll();
				servCliente.listAll();
			}

		]]>
	</fx:Script>

	<fx:Declarations>

		<s:RemoteObject
			id="servEstado"
			destination="estadoServices"
			showBusyCursor="true">

			<s:method
				name="save"
				fault="faultHandler(event)"
				result="estadoSaveResultHandler(event)"/>

			<s:method
				name="update"
				fault="faultHandler(event)"
				result="estadoUpdateResultHandler(event)"/>

			<s:method
				name="delete"
				fault="faultHandler(event)"
				result="estadoDeleteResultHandler(event)"/>

			<s:method
				name="listAll"
				fault="faultHandler(event)"
				result="estadoListAllResultHandler(event)"/>

		</s:RemoteObject>

		<s:RemoteObject
			id="servCidade"
			destination="cidadeServices"
			showBusyCursor="true">

			<s:method
				name="save"
				fault="faultHandler(event)"
				result="cidadeSaveResultHandler(event)"/>

			<s:method
				name="update"
				fault="faultHandler(event)"
				result="cidadeUpdateResultHandler(event)"/>

			<s:method
				name="delete"
				fault="faultHandler(event)"
				result="cidadeDeleteResultHandler(event)"/>

			<s:method
				name="listAll"
				fault="faultHandler(event)"
				result="cidadeListAllResultHandler(event)"/>

		</s:RemoteObject>

		<s:RemoteObject
			id="servCliente"
			destination="clienteServices"
			showBusyCursor="true">

			<s:method
				name="save"
				fault="faultHandler(event)"
				result="clienteSaveResultHandler(event)"/>

			<s:method
				name="update"
				fault="faultHandler(event)"
				result="clienteUpdateResultHandler(event)"/>

			<s:method
				name="delete"
				fault="faultHandler(event)"
				result="clienteDeleteResultHandler(event)"/>

			<s:method
				name="listAll"
				fault="faultHandler(event)"
				result="clienteListAllResultHandler(event)"/>

		</s:RemoteObject>

	</fx:Declarations>

	<s:Panel x="10" y="10" width="567" height="565" title="Cadastro de Clientes">
		<mx:DataGrid y="6" left="10" right="10" height="196" id="tabelaClientes" click="tabelaClientesClickHandler(event)">
			<mx:columns>
				<mx:DataGridColumn headerText="Nome" dataField="nome"/>
				<mx:DataGridColumn headerText="Sobrenome" dataField="sobrenome"/>
				<mx:DataGridColumn headerText="CPF" dataField="cpf" width="100"/>
				<mx:DataGridColumn dataField="dataNascimento" headerText="Dt. Nasc" width="80" itemRenderer="DateRenderer"/>
				<mx:DataGridColumn dataField="cidade.nome" headerText="Cidade"/>
			</mx:columns>
		</mx:DataGrid>
		<mx:Form y="210" left="10" right="10" height="281">
			<mx:FormItem label="ID:">
				<s:TextInput width="40" enabled="false" id="fieldIdCliente"/>
			</mx:FormItem>
			<mx:FormItem label="Nome:">
				<s:TextInput id="fieldNomeCliente"/>
			</mx:FormItem>
			<mx:FormItem label="Sobrenome:">
				<s:TextInput id="fieldSobrenomeCliente"/>
			</mx:FormItem>
			<mx:FormItem label="CPF:">
				<s:TextInput width="95" id="fieldCPFCliente"/>
			</mx:FormItem>
			<mx:FormItem label="Data de Nascimento:">
				<mx:DateField id="fieldDataNascCliente" formatString="DD/MM/YYYY"/>
			</mx:FormItem>
			<mx:FormItem label="Rua:">
				<s:TextInput id="fieldRuaCliente" width="249"/>
			</mx:FormItem>
			<mx:FormItem label="Número:">
				<s:TextInput width="59" id="fieldNumeroCliente"/>
			</mx:FormItem>
			<mx:FormItem label="CEP:">
				<s:TextInput width="94" id="fieldCEPCliente"/>
			</mx:FormItem>
			<mx:FormItem label="Cidade:">
				<s:ComboBox width="200" id="comboCidadeCliente"/>
			</mx:FormItem>
		</mx:Form>
		<s:Button x="329" y="499" label="Novo" click="btnNovoClienteClickHandler(event)"/>
		<s:Button x="407" y="499" label="Salvar" click="btnSalvarClienteClickHandler(event)"/>
		<s:Button y="499" label="Excluir" right="10" click="btnExcluirClienteClickHandler(event)"/>
	</s:Panel>
	<s:Panel x="585" y="10" width="360" height="277" title="Cadastro de Cidades">
		<mx:DataGrid x="10" y="10" id="tabelaCidades" width="338" height="106" itemClick="tabelaCidadesClickHandler(event)">
			<mx:columns>
				<mx:DataGridColumn headerText="Nome" dataField="nome" width="180"/>
				<mx:DataGridColumn headerText="Estado" dataField="estado.nome" width="150"/>
			</mx:columns>
		</mx:DataGrid>
		<mx:Form y="120" left="10" right="10" height="83">
			<mx:FormItem label="Nome:">
				<s:TextInput id="fieldNomeCidade" width="189"/>
			</mx:FormItem>
			<mx:FormItem label="Estado:">
				<s:ComboBox id="comboEstadoCidade"/>
			</mx:FormItem>
		</mx:Form>
		<s:Button x="126" y="211" label="Novo" click="btnNovoCidadeClickHandler(event)"/>
		<s:Button x="204" y="211" label="Salvar" click="btnSalvarCidadeClickHandler(event)"/>
		<s:Button x="278" y="211" label="Excluir" click="btnExcluirCidadeClickHandler(event)"/>
	</s:Panel>
	<s:Panel x="585" y="295" width="360" height="280" title="Cadastro de Estados">
		<mx:DataGrid y="10" id="tabelaEstados" left="10" right="10" height="107" itemClick="tabelaEstadosClickHandler(event)">
			<mx:columns>
				<mx:DataGridColumn headerText="Nome" dataField="nome" width="150" resizable="true"/>
				<mx:DataGridColumn headerText="Sigla" dataField="sigla" width="80" resizable="true"/>
			</mx:columns>
		</mx:DataGrid>
		<mx:Form y="121" right="11" left="9" height="86">
			<mx:FormItem label="Nome:">
				<s:TextInput width="187" id="fieldNomeEstado"/>
			</mx:FormItem>
			<mx:FormItem label="Sigla:">
				<s:TextInput width="40" id="fieldSiglaEstado"/>
			</mx:FormItem>
		</mx:Form>
		<s:Button x="121" y="215" label="Novo" click="btnNovoEstadoClickHandler(event)"/>
		<s:Button x="199" y="215" label="Salvar" click="btnSalvarEstadoClickHandler(event)"/>
		<s:Button x="277" y="215" label="Excluir" click="btnExcluirEstadoClickHandler(event)"/>
	</s:Panel>
</s:Application>

Finalmente, terminamos nosso tutorial. Outra sugestão que dou é que você incremente ainda mais o exemplo, inserindo validadores nos formulários por exemplo e arrumando algum erro que ainda exista na lógica da aplicação. Espero que tenham gostado. Qualquer dúvida, postem um comentário.

Obrigado a todos que leram e tiveram paciência de chegar até aqui ;)

Parte 1Parte 2Parte 3Parte 4.1Parte 4.2

Parte 1Parte 2Parte 3Parte 4.1Parte 4.2

Olá, hoje vamos continuar nosso tutorial. Inicialmente eu tinha planejado quatro partes para este tutorial, mas hoje de manhã estive pensando melhor e decidi quebrar a quarta parte em duas partes. Então teremos a parte 4.1 (esta que você está lendo) onde iremos preparar o nosso backend para fazermos um CRUD de três entidades (Estado, Cidade e Cliente). Na parte final, a 4.2, iremos tratar o lado do cliente, ou seja, a parte em Flex onde iremos montar a interface gráfica do nosso CRUD. Sendo assim, vamos começar!

Para o nosso CRUD nós não iremos usar nenhum framework, pois como eu já disse, o foco do tutorial não é esse. Se você quiser posteriormente inserir o Hibernate por exemplo, tenho certeza que não vai ter dificuldades, porque o lado do Flex não vai precisar ser modificado e o lado implementado em Java também poderá ser modificado facilmente. Para o nosso “minisistema”, vamos considerar o seguinte “documento de requisitos”:

“Desenvolver uma aplicação Web utilizando Flex e Java que permita o cadastro e a manutenção de Estados (unidades da federação), Cidades e Clientes. Um Estado é composto por um identificador (utilizado internamente no sistema), um nome e uma sigla que deve ser única. O nome de um estado não deve ter mais que 50 caracteres, enquanto a sigla deve ter obrigatoriamente 2 caracteres. Uma Cidade é composta também por um identificador, um nome (máximo 50 caracteres) e um estado. Por fim, cada Cliente é composto por um identificador, um nome (máximo 50 caracteres), um sobrenome (máximo 50 caracteres), uma data de nascimento, um CPF que deve ser único (14 caracteres) e o endereço deste Cliente deve conter uma rua (máximo 60 caracteres), um número (máximo 8 cacacteres), um CEP (9 caracteres) e uma cidade. Todos os atributos de Estado, Cidade e Cliente são obrigatórios. Para cada uma das entidades, desenvolva um formulário de cadastro onde todos os registros cadastrados devem ser apresentados em uma tabela.”

Bem, não vou escrever um documento gigantesco, especificando todas as restrições, pois este é apenas um exemplo. Então vamos começar pelo nosso banco de dados. Vou utilizar o MySQL, mas você fica livre para usar o SGBD (Sistema Gerenciador de Banco de Dados) que quiser. Novamente, como o objetivo do tutorial é a integração Flex e Java, não vamos ficar entrando em detalhes relacionados à SQL. Sendo assim, segue a estrutura da base de dados do nosso sistema e o script em SQL (MySQL) para a criação da nossa base de dados, além de alguns registros já cadastrados.

Figura 1

Estrutura da base de dados do sistema de exemplo

SET FOREIGN_KEY_CHECKS=0;

CREATE DATABASE flexjava
    CHARACTER SET 'latin1'
    COLLATE 'latin1_swedish_ci';

USE flexjava;

CREATE TABLE estado (
  id int(11) NOT NULL AUTO_INCREMENT,
  nome varchar(50) NOT NULL,
  sigla varchar(2) NOT NULL,
  PRIMARY KEY (id),
  UNIQUE KEY sigla (sigla)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=latin1;

CREATE TABLE cidade (
  id int(11) NOT NULL AUTO_INCREMENT,
  nome varchar(50) NOT NULL,
  id_estado int(11) NOT NULL,
  PRIMARY KEY (id),
  KEY id_estado (id_estado),
  CONSTRAINT cidade_fk FOREIGN KEY (id_estado) REFERENCES estado (id) ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=latin1;

CREATE TABLE cliente (
  id int(11) NOT NULL AUTO_INCREMENT,
  nome varchar(50) NOT NULL,
  sobrenome varchar(50) NOT NULL,
  cpf varchar(14) NOT NULL,
  dataNascimento timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
  rua varchar(60) NOT NULL,
  numero varchar(8) NOT NULL,
  cep varchar(9) NOT NULL,
  id_cidade int(11) NOT NULL,
  PRIMARY KEY (id),
  UNIQUE KEY cpf (cpf),
  KEY id_cidade (id_cidade),
  CONSTRAINT cliente_fk FOREIGN KEY (id_cidade) REFERENCES cidade (id) ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1;

INSERT INTO `estado` (id, nome, sigla) VALUES
  (1,'São Paulo','SP'),
  (2,'Minas Gerais','MG'),
  (3,'Rio de Janeiro','RJ'),
  (4,'Paraná','PR');
COMMIT;

INSERT INTO `cidade` (id, nome, id_estado) VALUES
  (1,'Vargem Grande do Sul',1),
  (2,'São João da Boa Vista',1),
  (3,'São Paulo',1),
  (4,'Poços de Caldas',2),
  (5,'Belo Horizonte',2),
  (6,'Paraty',3),
  (7,'Rio de Janeiro',3),
  (8,'Curitiba',4),
  (9,'Maringá',4);
COMMIT;

INSERT INTO `cliente` (id, nome, sobrenome, cpf, dataNascimento, rua, numero, cep, id_cidade) VALUES
  (1,'David','Buzatto','123.456.789-12','1985-02-25 00:00:00','Rua Getúlio Vargas','123','13880-000',1),
  (2,'João','da Silva','456.789.123-25','1970-06-19 00:00:00','Rua dos Coqueiros','741','13880-000',1),
  (3,'Maria','do Bairro','789.123.456-45','1971-10-25 00:00:00','Rua dos Mexicanos','98','12345-456',8);
COMMIT;

Como você pode perceber pelo código SQL acima, a nossa base de dados terá o nome de “flexjava”. Sendo assim, carregue este script no seu SGBD para podermos dar continuidade.

Agora que já temos nossa base de dados, vamos ao NetBeans. Em “Source Code”, crie um novo pacote, com o nome de “entidades”. Vamos criar então três classes (Estado, Cliente e Cidade), uma para cada uma das tabelas. Essas classes serão nossas entidades. Elas devem mapear para o paradigma orientado a objetos suas contrapartes, ou seja, as tabelas do modelo relacional. Seguem então o código das três entidades, com os atributos e seus respectivos getters e os setters. Para poupar espaço, irei postar apenas os gets e os sets da entidade Estado.

entidades.Estado.java:

package entidades;

package entidades;

/**
 * Um Estado.
 *
 * @author David Buzatto
 */
public class Estado {

    private Long id;
    private String nome;
    private String sigla;

    public Long getId() {
        return id;
    }

    public void setId( Long id ) {
        this.id = id;
    }

    public String getNome() {
        return nome;
    }

    public void setNome( String nome ) {
        this.nome = nome;
    }

    public String getSigla() {
        return sigla;
    }

    public void setSigla( String sigla ) {
        this.sigla = sigla;
    }

}

entidades.Cidade.java:

package entidades;

/**
 * Uma Cidade.
 *
 * @author David Buzatto
 */
public class Cidade {

    private Long id;
    private String nome;
    private Estado estado;

    // getters e setters

}

entidades.Cliente.java:

package entidades;

import java.sql.Timestamp;

/**
 * Um Cliente.
 *
 * @author David Buzatto
 */
public class Cliente {

    private Long id;
    private String nome;
    private String sobrenome;
    private String cpf;
    private Timestamp dataNascimento;
    private String rua;
    private String numero;
    private String cep;
    private Cidade cidade;

    // getters e setters

}

Legal, temos nossas entidades, agora falta a nossa camada de persistência. Iremos utilizar o padrão DAO (Data Access Object), mas não vou ficar entrando em detalhes quanto a arquitetura, se o DAO está genérico o bastante, se vamos ter x camadas de abstração, etc. Se quiser informações quanto a esses assuntos arquiteturais, basta usar o Google ;)

Para começarmos, vamos criar dois novos pacotes no nosso projeto. Um com o nome de “jdbc” e outro com o nome de “dao”. Até o momento a estrutura do nosso projeto tem essa aparência:

Figura 2

Estrutura do projeto

Iremos iniciar pelo pacote jdbc, onde iremos criar uma fábrica de conexões, mas antes disso, precisamos fazer mais uma configuração no nosso projeto. Para que possamos utilizar o JDBC, precisamos colocar o driver do SGBD que estamos utilizando. No exemplo, como já dito, estou usando o MySQL. Lembra que na primeira parte deste tutorial, onde configuramos o NetBeans, nós criamos um projeto e informamos que queríamos que as nossas bibliotecas ficasses armazenadas dentro do projeto? Logo após isso, criamos a biblioteca para o BlazeDS e importamos os .jars. Por padrão, o NetBeans vem preconfigurado com diversas bibliotecas, mas como criamos um projeto com o nosso próprio escopo de bibliotecas, as bibliotecas padrão do NetBeans precisam ser importadas para que possamos utilizá-las. No nosso caso, iremos importar a biblioteca do driver do MySQL. Vamos lá então!

Procure na estrutura do projeto pela pasta de bibliotecas (Libraries). Clique com o botão direito e escolha “Add Library”. O diálogo “Add Library” vai aparecer.

Figura 3

Diálogo "Add Library"

Note que da primeira vez que utilizados esse diálogo, nós clicamos em “Create…”, pois nós criamos a nossa biblioteca, no caso, para o BlazeDS e suas dependências. Note que além do botão “Create…” existe o botão “Import”. É no botão “Import” que podemos importar as bibliotecas pré-definidas no NetBeans. Sendo assim, clique no botão “Import”. Assim que clicar, o diálogo “Import Library” vai aparecer. Procure pela biblioteca “MySQL JDBC Driver” e clique no botão “Import Library” logo abaixo.

Figura 4

Diálogo "Import Library"

Assim que clicar no botão, o diálogo “Add Library” vai reaparecer, só que agora vai conter a biblioteca do driver do MySQL que foi importada.

Figura 5

Biblioteca com o driver do MySQL importada

Selecione a bibiloteca e clique no botão “Add Library”. Perceba que agora, se você expandir a pasta “Libraries” do seu projeto, a biblioteca do driver do MySQL vai ter sido inserida.

Figura 6

Biblioteca do driver do MySQL inserida no projeto

Legal, com isso podemos agora usar o driver do MySQL para podermos conectar no SGBD através da nossa aplicação. Então voltemos ao código. Como eu disse, iremos criar uma fábrica de conexões, que será uma classe com um método estático que por sua vez será utilizado para criar conexões JDBC com o SGBD. Sendo assim, no pacote “jdbc” crie a classe “ConnectionFactory”. Acredito que você já tenha algum domínio sobre como utilizar o JDBC, mas mesmo assim inseri alguns comentários na implementação da classe, que pode ser vista abaixo:

jdbc.ConnectionFactory.java

package jdbc;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

/**
 * Uma fábrica de conexões.
 *
 * @author David Buzatto
 */
public class ConnectionFactory {

    /*
     * Este bloco estático será executado assim que esta classe for carregada,
     * sendo assim, será executado apenas uma vez.
     */
    static {
        try {
            /*
             * Carrega a classe com.mysql.jdbc.Driver, que é a implementação
             * do driver JDBC para o MySQL.
             */
            Class.forName( "com.mysql.jdbc.Driver" );

            // caso a classe não seja encontrada
        } catch ( ClassNotFoundException exc ) {

            /*
             * Como log usaremos o stacktrace das excessões, mas recomendo
             * que para um projeto real você utilize algum mecanismo de log
             * melhor, como o Log4J por exemplo.
             */
            exc.printStackTrace();

        }
    }

    /**
     * O método getConnection retorna uma conexão com o banco de dados baseado
     * nos parâmetros fornecidos.
     *
     * @param url O endereço da base de dados.
     * @param usuario O usuário que tem permissão na base de dados especificada.
     * @param senha A senha do usuário especificado
     * @return Uma conexão com o banco de dados especificado na url.
     * @throws SQLException Caso ocorra algum problema durante a conexão.
     */
    public static Connection getConnection(
            String url,
            String usuario,
            String senha ) throws SQLException {

        // retorna a conexão a partir do método getConnection de DriverManager
        return DriverManager.getConnection( url, usuario, senha );

    }

}

Os comentários no código da classe “ConnectionFactory” são suficientes para entendê-la. Vamos então aos nossos DAOs!

Para quem não conhece, resumidamente, o padrão de projeto DAO especifica um camada para trabalhar com a persistência dos dados de objetos na forma de registros em tabelas e a obtenção desses registros e a “conversão” deles novamente em objetos. Existem diversos frameworks que abstraem esse trabalho para o desenvolvedor, bastando criar as entidades e configurar o framework em questão para que ele consiga fazer essa conversão automática. Esses frameworks são chamados de frameworks ORM (Object-Relational Mapping), ou seja, servem como ponte entre o mundo dos objetos (uma linguagem OO) e o mundo relacional (um SBGD relacional). Um exemplo desse tipo de framework é o Hibernate, que provavelmente você há ouviu falar. Novamente vou bater na mesma tecla :D Neste tutorial só vamos trabalhar com a intergração Flex e Java, então vamos fazer quase tudo na mão. Cansou? Vamos ao código? Let’s go :D

Nosso primeiro passo vai ser criar a classe “Dao” no pacote “dao”. Essa classe vai ser o nosso DAO genérico, que vai ser usado como modelo para os DAOs mais específicos (um para cada entidade). No nosso Dao, nós especificamos então quatro métodos, um para cada ação que podemos fazer nas nossas tabelas, ou seja, o famoso CRUD: Create (salvar; criar), Read (ler), Update (atualizar) e Delete (excluir; remover; deletar). Segue então o código do nosso DAO genérico.

dao.Dao.java

package dao;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import jdbc.ConnectionFactory;

/**
 * DAO genérico.
 *
 * @author David Buzatto
 */
public abstract class Dao<T extends Object> {

    // cada Dao terá uma conexão.
    private Connection conexao;

    /**
     * Construtor do Dao.
     * É nesse construtor que a conexão é criada.
     *
     * @throws SQLException
     */
    public Dao() throws SQLException {

        /*
         * Usa-se o método getConnection da fábrica de conexões,
         * passando o endereço da base (string/url de conexão), o nome do
         * usuário (no caso, "root") e a senha ("root" também).
         */
        conexao = ConnectionFactory.getConnection(
                "jdbc:mysql://localhost/flexjava",
                "root",
                "root" );

    }

    /**
     * Método para obter a conexão criada.
     *
     * @return Retorna a conexão.
     */
    public Connection getConnection() {
        return conexao;
    }

    /**
     * Método para fechar a conexão aberta.
     *
     * @throws SQLException Caso ocorra algum erro durante o fechamento da
     * conexão.
     */
    public void closeConnection() throws SQLException {
        conexao.close();
    }

    /**
     * Método abstrato para salvar uma instância de uma entidade da base de
     * dados.
     * É o "C" do CRUD.
     *
     * @param obj Instância do objeto da entidade a ser salvo.
     * @throws SQLException Caso ocorra algum erro durante a gravação.
     */
    public abstract void save( T obj ) throws SQLException;

    /**
     * Método abstrato para atualizar uma instância de uma entidade da base de
     * dados.
     * É o "U" do CRUD.
     *
     * @param obj Instância do objeto da entidade a ser atualizado.
     * @throws SQLException Caso ocorra algum erro durante a atualização.
     */
    public abstract void update( T obj ) throws SQLException;

    /**
     * Método abstrato para excluir uma instância de uma entidade da base de
     * dados.
     * É o "D" do CRUD.
     *
     * @param obj Instância do objeto da entidade a ser salvo.
     * @throws SQLException Caso ocorra algum erro durante a exclusão.
     */
    public abstract void delete( T obj ) throws SQLException;

    /**
     * Método abstrato para obter todas as instâncias de uma entidade da base
     * de dados.
     * É o "R" do CRUD.
     *
     * @return Lista de todas as instâncias da entidade.
     * @throws SQLException Caso ocorra algum erro durante a consulta.
     */
    public abstract List<T> listAll() throws SQLException;

}

Perceba que o sintax highlighting do componente de código fonte do WordPress não funciona muito bem, mas assim que você copiar para o NetBeans, vc vai ver que está correto. Note também que comentei todos os métodos e indiquei qual operação do CRUD eles representam. Perceba que para a operação “R” (read) podemos ter vários métodos, mas o padrão será o que lista tudo. Outro detalhe é que nosso Dao, quando for ser implementado (é uma classe abstrata!) deve ser especificado em cima de qual classe (entidade) ele vai atuar. O “T extends Object” siginifica que todo lugar onde existe “T” vai ser trocado pela a entidade que vai ser utilizada. Bem, pode estar um pouco confuso caso você nunca tenha usado os genéricos do Java, então vamos para o nosso primeiro DAO concreto, que será o EstadoDao.

Quando vamos estender uma classe abstrata, precisamos implementar todos os seus métodos (caso a classe que esteja estendendo for uma classe concreta). Primeiramente, no pacote “dao” crie a classe “EstadoDao”. Especifique que EstadoDao estende (extends) Dao<Estado>. Isso mesmo, Dao<Estado> ou seja, estende a classe Dao e o tipo genérico é Estado. Veja o código:

dao.EstadoDao.java

package dao;

import entidades.Estado;

/**
 * DAO para a entidade Estado.
 *
 * @author David Buzatto
 */
public class EstadoDao extends Dao<Estado> {

}

Perceba que o NetBeans vai reclamar, grifanto EstadoDao em vermelho, pois EstadoDao é uma classe concreta que está estendendo uma classe abstrata. Note também que uma lâmpada aparecerá na linha do erro. Clique nela, a opção “Implement All Abstract Methods” vai aparecer.

Figura 7

Erro ao estender o Dao que é uma classe abstrata

Clique na opção que apareceu. O NetBeans vai fornecer a implementação de todos os métodos, lançando uma excessão no corpo de cada um.  Posterirmente (já já) iremos substituir o “throw new UnsupportedOperationException( “…” )” pela a implementação específica do método. Perceba ainda que o NetBeans continua reclamando que tem um problema, só que a gora toda a linha da declaração da classe está vermelha. Se você passar o mouse em cima da linha para ler o erro, vai ver a seguinte mensagem:

“unreported exception java.sql.SQLException in default constructor”

Isso acontece porque o construtor padrão do nosso Dao cria uma conexão com o banco e pode lançar uma SQLException (que é uma excessão checada). Sendo assim, precisamos implementar o construtor padrão do nosso EstadoDao para informar que ele lança uma SQLException também, pois o construtor padrão de EstadoDao vai chamar implicitamente o construtor padrão de Dao, que por sua vez lança uma SQLException. Por enquanto, nosso EstadoDao está assim:

package dao;

import entidades.Estado;
import java.sql.SQLException;
import java.util.List;

/**
 * DAO para a entidade Estado.
 *
 * @author David Buzatto
 */
public class EstadoDao extends Dao<Estado> {

    public EstadoDao() throws SQLException {
    }

    // métodos CRUD aqui...

}

Vamos a implementação dos métodos CRUD. Basicamente iremos criar uma query, pegar os dados do objeto passado, popular essa query e executá-la. Para poupar tempo e explicações já vou postar o código dos três Daos que iremos ter. Acredito que com o código seja possível você entender o que está acontecendo. Qualquer dúvida, mande um comentário ;)

dao.EstadoDao.java

package dao;

import entidades.Estado;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

/**
 * DAO para a entidade Estado.
 *
 * @author David Buzatto
 */
public class EstadoDao extends Dao<Estado> {

    public EstadoDao() throws SQLException {
    }

    @Override
    public void save( Estado obj ) throws SQLException {

        PreparedStatement stmt = getConnection().prepareStatement(
                "INSERT INTO "
                + "estado( nome, sigla ) "
                + "VALUES( ?, ? );" );

        stmt.setString( 1, obj.getNome() );
        stmt.setString( 2, obj.getSigla() );

        stmt.executeUpdate();
        stmt.close();

    }

    @Override
    public void update( Estado obj ) throws SQLException {

        PreparedStatement stmt = getConnection().prepareStatement(
                "UPDATE estado "
                + "SET"
                + "    nome = ?,"
                + "    sigla = ? "
                + "WHERE"
                + "    id = ?;" );

        stmt.setString( 1, obj.getNome() );
        stmt.setString( 2, obj.getSigla() );
        stmt.setLong( 3, obj.getId() );

        stmt.executeUpdate();
        stmt.close();

    }

    @Override
    public void delete( Estado obj ) throws SQLException {

        PreparedStatement stmt = getConnection().prepareStatement(
                "DELETE FROM estado "
                + "WHERE"
                + "    id = ?;" );

        stmt.setLong( 1, obj.getId() );

        stmt.executeUpdate();
        stmt.close();

    }

    @Override
    public List<Estado> listAll() throws SQLException {

        List<Estado> lista = new ArrayList<Estado>();

        PreparedStatement stmt = getConnection().prepareStatement(
                "SELECT * FROM estado;" );

        ResultSet rs = stmt.executeQuery();

        while ( rs.next() ) {

            Estado e = new Estado();

            e.setId( rs.getLong( "id" ) );
            e.setNome( rs.getString( "nome" ) );
            e.setSigla( rs.getString( "sigla" ) );

            lista.add( e );

        }

        rs.close();
        stmt.close();

        return lista;

    }

}

dao.CidadeDao.java

package dao;

import entidades.Cidade;
import entidades.Estado;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

/**
 * DAO para a entidade Cidade.
 *
 * @author David Buzatto
 */
public class CidadeDao extends Dao<Cidade> {

    public CidadeDao() throws SQLException {
    }

    @Override
    public void save( Cidade obj ) throws SQLException {

        PreparedStatement stmt = getConnection().prepareStatement(
                "INSERT INTO "
                + "cidade( nome, id_estado ) "
                + "VALUES( ?, ? );" );

        stmt.setString( 1, obj.getNome() );
        stmt.setLong( 2, obj.getEstado().getId() );

        stmt.executeUpdate();
        stmt.close();

    }

    @Override
    public void update( Cidade obj ) throws SQLException {

        PreparedStatement stmt = getConnection().prepareStatement(
                "UPDATE cidade "
                + "SET"
                + "    nome = ?,"
                + "    id_estado = ? "
                + "WHERE"
                + "    id = ?;" );

        stmt.setString( 1, obj.getNome() );
        stmt.setLong( 2, obj.getEstado().getId() );
        stmt.setLong( 3, obj.getId() );

        stmt.executeUpdate();
        stmt.close();

    }

    @Override
    public void delete( Cidade obj ) throws SQLException {

        PreparedStatement stmt = getConnection().prepareStatement(
                "DELETE FROM cidade "
                + "WHERE"
                + "    id = ?;" );

        stmt.setLong( 1, obj.getId() );

        stmt.executeUpdate();
        stmt.close();

    }

    @Override
    public List<Cidade> listAll() throws SQLException {

        List<Cidade> lista = new ArrayList<Cidade>();

        PreparedStatement stmt = getConnection().prepareStatement(
                "SELECT "
                + "    c.id idCidade, "
                + "    c.nome nomeCidade, "
                + "    e.id idEstado, "
                + "    e.nome nomeEstado, "
                + "    e.sigla siglaEstado "
                + "FROM "
                + "    cidade c, "
                + "    estado e "
                + "WHERE"
                + "    c.id_estado = e.id;" );

        ResultSet rs = stmt.executeQuery();

        while ( rs.next() ) {

            Cidade c = new Cidade();
            Estado e = new Estado();

            c.setId( rs.getLong( "idCidade" ) );
            c.setNome( rs.getString( "nomeCidade" ) );
            c.setEstado( e );

            e.setId( rs.getLong( "idEstado" ) );
            e.setNome( rs.getString( "nomeEstado" ) );
            e.setSigla( rs.getString( "siglaEstado" ) );

            lista.add( c );

        }

        rs.close();
        stmt.close();

        return lista;

    }

}

dao.ClienteDao.java

package dao;

import entidades.Cidade;
import entidades.Cliente;
import entidades.Estado;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

/**
 * DAO para a entidade Cliente.
 *
 * @author David Buzatto
 */
public class ClienteDao extends Dao<Cliente> {

    public ClienteDao() throws SQLException {
    }

    @Override
    public void save( Cliente obj ) throws SQLException {

        PreparedStatement stmt = getConnection().prepareStatement(
                "INSERT INTO "
                + "cliente( "
                + "    nome, "
                + "    sobrenome, "
                + "    cpf, "
                + "    dataNascimento, "
                + "    rua, "
                + "    numero, "
                + "    cep, "
                + "    id_cidade ) "
                + "VALUES( ?, ?, ?, ?, ?, ?, ?, ? );" );

        stmt.setString( 1, obj.getNome() );
        stmt.setString( 2, obj.getSobrenome() );
        stmt.setString( 3, obj.getCpf() );
        stmt.setTimestamp( 4, obj.getDataNascimento() );
        stmt.setString( 5, obj.getRua() );
        stmt.setString( 6, obj.getNumero() );
        stmt.setString( 7, obj.getCep() );
        stmt.setLong( 8, obj.getCidade().getId() );

        stmt.executeUpdate();
        stmt.close();

    }

    @Override
    public void update( Cliente obj ) throws SQLException {

        PreparedStatement stmt = getConnection().prepareStatement(
                "UPDATE cliente "
                + "SET"
                + "    nome = ?, "
                + "    sobrenome = ?,"
                + "    cpf = ?, "
                + "    dataNascimento = ?, "
                + "    rua = ?, "
                + "    numero = ?, "
                + "    cep = ?, "
                + "    id_cidade = ? "
                + "WHERE"
                + "    id = ?;" );

        stmt.setString( 1, obj.getNome() );
        stmt.setString( 2, obj.getSobrenome() );
        stmt.setString( 3, obj.getCpf() );
        stmt.setTimestamp( 4, obj.getDataNascimento() );
        stmt.setString( 5, obj.getRua() );
        stmt.setString( 6, obj.getNumero() );
        stmt.setString( 7, obj.getCep() );
        stmt.setLong( 8, obj.getCidade().getId() );
        stmt.setLong( 9, obj.getId() );

        stmt.executeUpdate();
        stmt.close();

    }

    @Override
    public void delete( Cliente obj ) throws SQLException {

        PreparedStatement stmt = getConnection().prepareStatement(
                "DELETE FROM cliente "
                + "WHERE"
                + "    id = ?;" );

        stmt.setLong( 1, obj.getId() );

        stmt.executeUpdate();
        stmt.close();

    }

    @Override
    public List<Cliente> listAll() throws SQLException {

        List<Cliente> lista = new ArrayList<Cliente>();

        PreparedStatement stmt = getConnection().prepareStatement(
                "SELECT "
                + "    c.id idCliente, "
                + "    c.nome nomeCliente, "
                + "    c.sobreNome sobrenomeCliente, "
                + "    c.cpf cpfCliente, "
                + "    c.dataNascimento dataNascimentoCliente, "
                + "    c.rua ruaCliente, "
                + "    c.numero numeroCliente, "
                + "    c.cep cepCliente, "
                + "    ci.id idCidade, "
                + "    ci.nome nomeCidade, "
                + "    e.id idEstado, "
                + "    e.nome nomeEstado, "
                + "    e.sigla siglaEstado "
                + "FROM "
                + "    cliente c, "
                + "    cidade ci, "
                + "    estado e "
                + "WHERE"
                + "    c.id_cidade = ci.id AND "
                + "    ci.id_estado = e.id;");

        ResultSet rs = stmt.executeQuery();

        while ( rs.next() ) {

            Cliente c = new Cliente();
            Cidade ci = new Cidade();
            Estado e = new Estado();

            c.setId( rs.getLong( "idCliente" ) );
            c.setNome( rs.getString( "nomeCliente" ) );
            c.setSobrenome( rs.getString( "sobrenomeCliente" ) );
            c.setCpf( rs.getString( "cpfCliente" ) );
            c.setDataNascimento( rs.getTimestamp( "dataNascimentoCliente" ) );
            c.setRua( rs.getString( "ruaCliente" ) );
            c.setNumero( rs.getString( "numeroCliente" ) );
            c.setCep( rs.getString( "cepCliente" ) );
            c.setCidade( ci );

            ci.setId( rs.getLong( "idCidade" ) );
            ci.setNome( rs.getString( "nomeCidade" ) );
            ci.setEstado( e );

            e.setId( rs.getLong( "idEstado" ) );
            e.setNome( rs.getString( "nomeEstado" ) );
            e.setSigla( rs.getString( "siglaEstado" ) );

            lista.add( c );

        }

        rs.close();
        stmt.close();

        return lista;

    }

}

Ufa! Quanto código hein? Quando fazemos tudo na mão temos muito trabalho, por isso um framework como o Hibernate pode nos ajudar muito! Enfim, com os DAOs prontos, precisamos agora criar os serviços e então terminaremos essa parte do tutorial! Vamos lá então.

Vamos usar uma abordagem parecida com a que usamos no nosso Dao genérico. Vamos criar um serviço genérico para trabalhar com entidades (EntityServices) e nele teremos novamente os quatro métodos CRUD. Dentro da implementação de cada um, criaremos o Dao correspondente e executaremos a operação correspondente ao serviço. Como há disse, a idéia é parecida com a do Dao, então, vou postar diretamente os códigos das quatro classes (EntityServices, EstadoServices, CidadeServices e ClienteServices).

servicos.EntityServices.java

package servicos;

import java.util.List;

/**
 * Classe abstrata que define a assinatura dos serviços para as entidades.
 *
 * @author David Buzatto
 */
public abstract class EntityServices<T extends Object> {

    /**
     * Método abstrato para salvar um objeto remoto.
     * Utiliza o método save do DAO correspondente.
     *
     * @param obj Objeto remoto a ser salvo.
     * @throws Exception Caso ocorra algum erro.
     */
    public abstract void save( T obj ) throws Exception;

    /**
     * Método abstrato para atualiar um objeto remoto.
     * Utiliza o método update do DAO correspondente.
     *
     * @param obj Objeto remoto a ser atualizado.
     * @throws Exception Caso ocorra algum erro.
     */
    public abstract void update( T obj ) throws Exception;

    /**
     * Método abstrato para excluir um objeto remoto.
     * Utiliza o método delete do DAO correspondente.
     *
     * @param obj Objeto remoto a ser excluído.
     * @throws Exception Caso ocorra algum erro.
     */
    public abstract void delete( T obj ) throws Exception;

    /**
     * Método abstrato para obter todas as intâncias de uma determinada
     * entidade e serializá-las em uma lista.
     * Utiliza o método listAll do DAO correspondente.
     *
     * @return Uma lista de objetos.
     * @throws Exception Caso ocorra algum erro.
     */
    public abstract List<T> listAll() throws Exception;

}

servicos.EstadoServices.java

package servicos;

import dao.Dao;
import dao.EstadoDao;
import entidades.Estado;
import java.util.List;

/**
 * Serviços para a entidade Estado.
 *
 * @author David Buzatto
 */
public class EstadoServices extends EntityServices<Estado> {

    @Override
    public void save( Estado obj ) throws Exception {
        Dao<Estado> dao = new EstadoDao();
        dao.save( obj );
        dao.closeConnection();
    }

    @Override
    public void update( Estado obj ) throws Exception {
        Dao<Estado> dao = new EstadoDao();
        dao.update( obj );
        dao.closeConnection();
    }

    @Override
    public void delete( Estado obj ) throws Exception {
        Dao<Estado> dao = new EstadoDao();
        dao.delete( obj );
        dao.closeConnection();
    }

    @Override
    public List<Estado> listAll() throws Exception {
        Dao<Estado> dao = new EstadoDao();
        List<Estado> lista = dao.listAll();
        dao.closeConnection();
        return lista;
    }

}

servicos.CidadeServices.java

package servicos;

import dao.Dao;
import dao.CidadeDao;
import entidades.Cidade;
import java.util.List;

/**
 * Serviços para a entidade Cidade.
 *
 * @author David Buzatto
 */
public class CidadeServices extends EntityServices<Cidade> {

    @Override
    public void save( Cidade obj ) throws Exception {
        Dao<Cidade> dao = new CidadeDao();
        dao.save( obj );
        dao.closeConnection();
    }

    @Override
    public void update( Cidade obj ) throws Exception {
        Dao<Cidade> dao = new CidadeDao();
        dao.update( obj );
        dao.closeConnection();
    }

    @Override
    public void delete( Cidade obj ) throws Exception {
        Dao<Cidade> dao = new CidadeDao();
        dao.delete( obj );
        dao.closeConnection();
    }

    @Override
    public List<Cidade> listAll() throws Exception {
        Dao<Cidade> dao = new CidadeDao();
        List<Cidade> lista = dao.listAll();
        dao.closeConnection();
        return lista;
    }

}

servicos.ClienteServices.java

package servicos;

import dao.Dao;
import dao.ClienteDao;
import entidades.Cliente;
import java.util.List;

/**
 * Serviços para a entidade Cliente.
 *
 * @author David Buzatto
 */
public class ClienteServices extends EntityServices<Cliente> {

    @Override
    public void save( Cliente obj ) throws Exception {
        Dao<Cliente> dao = new ClienteDao();
        dao.save( obj );
        dao.closeConnection();
    }

    @Override
    public void update( Cliente obj ) throws Exception {
        Dao<Cliente> dao = new ClienteDao();
        dao.update( obj );
        dao.closeConnection();
    }

    @Override
    public void delete( Cliente obj ) throws Exception {
        Dao<Cliente> dao = new ClienteDao();
        dao.delete( obj );
        dao.closeConnection();
    }

    @Override
    public List<Cliente> listAll() throws Exception {
        Dao<Cliente> dao = new ClienteDao();
        List<Cliente> lista = dao.listAll();
        dao.closeConnection();
        return lista;
    }

}

Poderíamos generalizar ainda mais nossa arquitetura, mas vamos ficar por aqui. Perceba que caso você deseje mudar o mecanismo de persistência, basta configurar o framework que vai utilizar e então mudar a implementação dos serviços. Agora, para terminar essa parte, vamos então configurar o nosso remoting-config.xml para apontar para as classes de serviços que acabamos de criar. Segue então o remoting-config.xml completo.

remoting-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<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="testeServicos">
        <properties>
            <source>servicos.ServicosTeste</source>
        </properties>
    </destination>

    <destination id="servicosCalculadora">
        <properties>
            <source>servicos.ServicosCalculadora</source>
        </properties>
    </destination>

    <destination id="estadoServices">
        <properties>
            <source>servicos.EstadoServices</source>
        </properties>
    </destination>

    <destination id="cidadeServices">
        <properties>
            <source>servicos.CidadeServices</source>
        </properties>
    </destination>

    <destination id="clienteServices">
        <properties>
            <source>servicos.ClienteServices</source>
        </properties>
    </destination>

</service>

Finalmente terminamos essa parte do tutorial. Ela nos deu bastante trabalho, principalmente por causa dos DAOs. Na próxima (e agora sim, a última!) parte do tutorial iremos integrar nossa interface criada no Flash Builder para fazermos tudo isso funcionar.

Então é isso, até amanhã!

Parte 1Parte 2Parte 3Parte 4.1Parte 4.2

Parte 1Parte 2Parte 3Parte 4.1Parte 4.2

Nesta parte do tutorial, vamos fazer um exemplo um pouquinho mais complicado que o anterior. Iremos criar quatro serviços, um para cada operação matemática. Cada um dos serviços vai receber dois números como parâmetro e vai retornar o valor da operação. Iremos também montar a interface gráfica para utilizar estes serviços e todo o mapeamento da parte em Flex para o backend em Java. Então vamos lá!

Antes de iniciarmos a implementação dos serviços, primeiro vamos configurar nossa aplicação para que quando executada, a página do swf seja aberta automaticamente. Não podemos configurar para abrir o HTML diretamente via web.xml, então teremos que usar o nosso index.jsp para fazer o redirecionamento. Para isso, basta adicionar um <meta http-equiv=”Refresh” …/> no nosso index.jsp. Segue então o código completo do index.jsp, onde já removi o “Hello World!” e o título da página. Esta página vai servir apenas como ponto de entrada da aplicação.

index.jsp

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
 "http://www.w3.org/TR/html4/loose.dtd">

<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <meta http-equiv="Refresh" content="0; url=swf/IntegracaoFlexJavaGUI.html">
    </head>
    <body>
    </body>
</html>

Com o index.jsp atualizado, teste novamente sua aplicação. Agora a página do swf vai ser carregada automaticamente. Vamos aos serviços.

No NetBeans, com o projeto aberto, vamos no pacote “servicos” que criamos na parte anterior e vamos criar uma nova classe, com o nome de “ServicosCalculadora”. Nesta classe iremos implementar quatro métodos: double somar(double n1, double n2), double subtrair(double n1, double n2), double multiplicar(double n1, double n2) e double dividir( double n1, double n2). A implementação de cada método é muito simples. Eles irão retornar o resultado da operação denotada pelo nome do método sob os operandos. Segue então a implementação da classe ServicosCalculadora:

servicos.ServicosCalculadora.java

package servicos;

/**
 * Serviços de cálculo.
 *
 * @author David Buzatto
 */
public class ServicosCalculadora {

    public double somar( double n1, double n2 ) {
        return n1 + n2;
    }

    public double subtrair( double n1, double n2 ) {
        return n1 - n2;
    }

    public double multiplicar( double n1, double n2 ) {
        return n1 * n2;
    }

    public double dividir( double n1, double n2 ) {
        return n1 / n2;
    }

}

Note que não estamos nos preocupando com divisão por zero. Legal! O segundo passo agora é editar o remoting-config.xml informar tanto ao BlazeDS quanto ao compilador do Flex que a classe ServicosCalculadora é uma classe que contém serviços. Abra então o remoting-config.xml e adicione mais um destination que agora aponta para a nova classe e de um id para ele. Segue o remoting-config.xml completo:

remoting-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<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="testeServicos">
        <properties>
            <source>servicos.ServicosTeste</source>
        </properties>
    </destination>

    <destination id="servicosCalculadora">
        <properties>
            <source>servicos.ServicosCalculadora</source>
        </properties>
    </destination>

</service>

Ótimo, a parte do lado Java está pronta. Simples não é? Vamos para o Flash Builder agora. Vamos reorganizar nossa interface, pois vamos utilizá-la a mesma tela para todos os exemplos. Na interface Design, vamos criar um painel (categoria Layout da paleta de componentes) e arrastar o botão do primeiro exemplo para dentro dele. Criaremos outro painel então para conter os componentes do exemplo que estamos implementando agora. Note que do lado direito da interface do Flash Builder existe a aba de propriedades do componente que está selecionado no momento. Lá você pode alterar as propriedades do componente que está editando. Veja como ficou:

Figura 1

Interface do exemplo de Calculadora

Temos então os campos para os números que serão calculados, alguns radios para as operações, um botão para chamar o serviço de cálculo e um campo para o resultado. Para mentermos um padrão, segue o código do mxml que temos até o momento. Já defini os ids dos componentes.

IntegracaoFlexJavaGUI.mxml

<?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" minWidth="955" minHeight="600">

	<fx:Script>
		<![CDATA[
			import mx.controls.Alert;
			import mx.rpc.events.FaultEvent;
			import mx.rpc.events.ResultEvent;

			private function botaoExecutarClick( event: Event ): void {
				servicos.imprimirMensagem();
			}

			private function imprimirMensagemFault( event: FaultEvent ): void {
				Alert.show( "Ocorreu um erro...", "ERRO" );
			}

			private function imprimirMensagemResult( event: ResultEvent ): void {
				Alert.show( "Serviço executado com sucesso!", "Mensagem" );
			}

		]]>
	</fx:Script>

	<fx:Declarations>

		<s:RemoteObject
			id="servicos"
			destination="testeServicos"
			showBusyCursor="true">

			<s:method
				name="imprimirMensagem"
				fault="imprimirMensagemFault(event)"
				result="imprimirMensagemResult(event)"/>

		</s:RemoteObject>
		<s:RadioButtonGroup id="grupoOperacoes"/>

	</fx:Declarations>

	<s:Panel x="11" y="10" width="250" height="200" title="Testes Serviços (ServicosTeste.java)">
		<s:Button x="15" y="13" label="Executar" click="botaoExecutarClick(event)"/>
	</s:Panel>
	<s:Panel x="269" y="10" width="250" height="241" title="Calculadora (ServicosCalculadora.java)">
		<s:Label x="12" y="20" text="Número 1:"/>
		<s:Label x="13" y="50" text="Número 2:"/>
		<s:TextInput x="79" y="14" width="64" id="campoN1"/>
		<s:TextInput x="80" y="44" width="63" id="campoN2"/>
		<s:Label x="13" y="102" text="Operação:"/>
		<s:RadioButton x="77" y="84" label="+" groupName="grupoOperacoes" selected="true" id="radioSomar"/>
		<s:RadioButton x="111" y="84" label="-" groupName="grupoOperacoes" id="radioSubtrair"/>
		<s:RadioButton x="77" y="110" label="*" groupName="grupoOperacoes" id="radioMultiplicar"/>
		<s:RadioButton x="111" y="110" label="/" groupName="grupoOperacoes" id="radioDividir"/>
		<s:Label x="10" y="184" text="Resultado:"/>
		<s:TextInput x="79" y="176" id="campoResultado" width="159"/>
		<s:Button x="10" y="141" label="Calcular!" width="228"/>
	</s:Panel>
</s:Application>

Da mesma forma que fizemos para o primeiro exemplo, precisamos agora definir o <s:RemoteObject> que vai apontar para a nossa classe e mapear todos os métodos. Como exercício, logo abaixo do <s:RemoteObject> do exemplo anterior, declare no novo <s:RemoteObject> e os quatro métodos. Lembre-se que o destination tem que ser o mesmo que declaramos no remoting-config.xml (servicosCalculadora). Para os eventos fault e result de cada um dos métodos, aponte para as funções calculadoraFault(event) e calculadoraResult(event) respectivamente. Vamos utilizar os mesmo tratadores de eventos para cada um dos métodos. Em cada um deles, coloque um Alert, indicando um erro ou que o cálculo foi executado. Terminou? Veja se ficou parecido com isso aqui:

IntegracaoFlexJavaGUI.mxml

<?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" minWidth="955" minHeight="600">

	<fx:Script>
		<![CDATA[
			import mx.controls.Alert;
			import mx.rpc.events.FaultEvent;
			import mx.rpc.events.ResultEvent;

			private function botaoExecutarClick( event: Event ): void {
				servicos.imprimirMensagem();
			}

			private function imprimirMensagemFault( event: FaultEvent ): void {
				Alert.show( "Ocorreu um erro...", "ERRO" );
			}

			private function imprimirMensagemResult( event: ResultEvent ): void {
				Alert.show( "Serviço executado com sucesso!", "Mensagem" );
			}

			private function calculadoraFault( event: FaultEvent ): void {
				Alert.show( "Ocorreu um erro...", "ERRO" );
			}

			private function calculadoraResult( event: ResultEvent ): void {
				Alert.show( "Serviço executado com sucesso!", "Mensagem" );
			}

		]]>
	</fx:Script>

	<fx:Declarations>

		<s:RemoteObject
			id="servicos"
			destination="testeServicos"
			showBusyCursor="true">

			<s:method
				name="imprimirMensagem"
				fault="imprimirMensagemFault(event)"
				result="imprimirMensagemResult(event)"/>

		</s:RemoteObject>

		<s:RemoteObject
			id="calculadora"
			destination="servicosCalculadora"
			showBusyCursor="true">

			<s:method
				name="somar"
				fault="calculadoraFault(event)"
				result="calculadoraResult(event)"/>

			<s:method
				name="subtrair"
				fault="calculadoraFault(event)"
				result="calculadoraResult(event)"/>

			<s:method
				name="multiplicar"
				fault="calculadoraFault(event)"
				result="calculadoraResult(event)"/>

			<s:method
				name="dividir"
				fault="calculadoraFault(event)"
				result="calculadoraResult(event)"/>

		</s:RemoteObject>

		<s:RadioButtonGroup id="grupoOperacoes"/>

	</fx:Declarations>

	<s:Panel x="11" y="10" width="250" height="200" title="Testes Serviços (ServicosTeste.java)">
		<s:Button x="15" y="13" label="Executar" click="botaoExecutarClick(event)"/>
	</s:Panel>
	<s:Panel x="269" y="10" width="250" height="241" title="Calculadora (ServicosCalculadora.java)">
		<s:Label x="12" y="20" text="Número 1:"/>
		<s:Label x="13" y="50" text="Número 2:"/>
		<s:TextInput x="79" y="14" width="64" id="campoN1"/>
		<s:TextInput x="80" y="44" width="63" id="campoN2"/>
		<s:Label x="13" y="102" text="Operação:"/>
		<s:RadioButton x="77" y="84" label="+" groupName="grupoOperacoes" selected="true" id="radioSomar"/>
		<s:RadioButton x="111" y="84" label="-" groupName="grupoOperacoes" id="radioSubtrair"/>
		<s:RadioButton x="77" y="110" label="*" groupName="grupoOperacoes" id="radioMultiplicar"/>
		<s:RadioButton x="111" y="110" label="/" groupName="grupoOperacoes" id="radioDividir"/>
		<s:Label x="10" y="184" text="Resultado:"/>
		<s:TextInput x="79" y="176" id="campoResultado" width="159"/>
		<s:Button x="10" y="141" label="Calcular!" width="228"/>
	</s:Panel>
</s:Application>

Certo, configuramos nosso objeto remoto, mapeamos os métodos, registramos os eventos de erro (FaultEvent) e de execução bem sucedida (ResultEvent). Falta agora fazer funcionar. Para isso iremos criar um método para tratar o evento click do botão, e neste método verificar qual a operação a ser executada e chamar o método correspondente do serviço. Para poupar espaço, vou colocar apenas o código do método que trata o evento click e como o código do botão “Calcular!” deve ficar.

private function botaoCalcularClick( event: Event ): void {

	/*
	 * Declaração de duas variáveis do tipo Number que recebem
	 * a conversão em número do texto dos seus respectivos campos.
	 * Note que não há nenhuma validação.
	 */
	var n1: Number = Number( campoN1.text );
	var n2: Number = Number( campoN2.text );

	/*
	 * grupoOperacoes é um RadioButtonGrupo que está relacionado
	 * aos quatro radios criados. O propriedade selectedValue retorna
	 * o valor da propriedade label do radio selecionado no momento.
	 * Perceba que em ActionScript, nós podemos fazer switch com Strings
	 * diferentemente do Java (até a versão 6.0) onde isso não é suportado
	 */
	switch ( grupoOperacoes.selectedValue.toString() ) {

		case "+":
			calculadora.somar( n1, n2 );
			break;

		case "-":
			calculadora.subtrair( n1, n2 );
			break;

		case "*":
			calculadora.multiplicar( n1, n2 );
			break;

		case "/":
			calculadora.dividir( n1, n2 );
			break;

	}

}

Código do botão “Calcular!”:

<s:Button x="10" y="141" label="Calcular!" width="228" click="botaoCalcularClick(event)"/>

Teste e veja se está ocorrendo algum erro. Se estive, corrija. Perceba que ainda não tratamos o resultado retornado. Isso será realizado no método que trata o ResultEvent, pois só quando ele é executado significa que o método remoto (nosso serviço) foi executado corretamente. Então vamos fazer a modificação necessária no método calculadoraResult para que o resultado do serviço seja colocado no campo de resultado. Segue então o método alterado:

private function calculadoraResult( event: ResultEvent ): void {

	/*
	 * a propriedade result do objeto event (do tipo ResultEvent)
	 * contém o valor de retorno do método remoto que foi executado
	 * essa propriedade é do tipo Object, então caso haja a necessidade
	 * de se converter o valor retornado, é necessário saber que tipo que está
	 * sendo retornado.
	 *
	 * Como os números no ActionScript também são objetos, aqui
	 * não é feita nenhuma conversão, só chamando o método toString()
	 * do valor, retornando assim a representação em String do número retornado.
	 */
	var valor: Object = event.result;
	campoResultado.text = valor.toString();

	Alert.show( "Serviço executado com sucesso!", "Mensagem" );

}

Teste para verificar se está funcionando. Caso após executar o projeto do NetBeans o aplicativo swf não tiver sido atualizado, segure a tecla Shift e clique no botão atualizar do seu navegador. Assim ele é forçado a recarregar do “zero” o swf. Não se esqueça de sempre salvar o projeto no Flash Builder para que o projeto seja recompilado. Segue então o código mxml final deste exemplo:

IntegracaoFlexJavaGUI.mxml

<?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" minWidth="955" minHeight="600">

	<fx:Script>
		<![CDATA[
			import mx.controls.Alert;
			import mx.rpc.events.FaultEvent;
			import mx.rpc.events.ResultEvent;

			private function botaoExecutarClick( event: Event ): void {
				servicos.imprimirMensagem();
			}

			private function imprimirMensagemFault( event: FaultEvent ): void {
				Alert.show( "Ocorreu um erro...", "ERRO" );
			}

			private function imprimirMensagemResult( event: ResultEvent ): void {
				Alert.show( "Serviço executado com sucesso!", "Mensagem" );
			}

			private function calculadoraFault( event: FaultEvent ): void {
				Alert.show( "Ocorreu um erro...", "ERRO" );
			}

			private function calculadoraResult( event: ResultEvent ): void {

				/*
				 * a propriedade result do objeto event (do tipo ResultEvent)
				 * contém o valor de retorno do método remoto que foi executado
				 * essa propriedade é do tipo Object, então caso haja a necessidade
				 * de se converter o valor retornado, é necessário saber que tipo que está
				 * sendo retornado.
				 *
				 * Como os números no ActionScript também são objetos, aqui
				 * não é feita nenhuma conversão, só chamando o método toString()
				 * do valor, retornando assim a representação em String do número retornado.
				 */
				var valor: Object = event.result;
				campoResultado.text = valor.toString();

				Alert.show( "Serviço executado com sucesso!", "Mensagem" );

			}

			private function botaoCalcularClick( event: Event ): void {

				/*
				 * Declaração de duas variáveis do tipo Number que recebem
				 * a conversão em número do texto dos seus respectivos campos.
				 * Note que não há nenhuma validação.
				 */
				var n1: Number = Number( campoN1.text );
				var n2: Number = Number( campoN2.text );

				/*
				 * grupoOperacoes é um RadioButtonGrupo que está relacionado
				 * aos quatro radios criados. O propriedade selectedValue retorna
				 * o valor da propriedade label do radio selecionado no momento.
				 * Perceba que em ActionScript, nós podemos fazer switch com Strings
				 * diferentemente do Java (até a versão 6.0) onde isso não é suportado
				 */
				switch ( grupoOperacoes.selectedValue.toString() ) {

					case "+":
						calculadora.somar( n1, n2 );
						break;

					case "-":
						calculadora.subtrair( n1, n2 );
						break;

					case "*":
						calculadora.multiplicar( n1, n2 );
						break;

					case "/":
						calculadora.dividir( n1, n2 );
						break;

				}

			}

		]]>
	</fx:Script>

	<fx:Declarations>

		<s:RemoteObject
			id="servicos"
			destination="testeServicos"
			showBusyCursor="true">

			<s:method
				name="imprimirMensagem"
				fault="imprimirMensagemFault(event)"
				result="imprimirMensagemResult(event)"/>

		</s:RemoteObject>

		<s:RemoteObject
			id="calculadora"
			destination="servicosCalculadora"
			showBusyCursor="true">

			<s:method
				name="somar"
				fault="calculadoraFault(event)"
				result="calculadoraResult(event)"/>

			<s:method
				name="subtrair"
				fault="calculadoraFault(event)"
				result="calculadoraResult(event)"/>

			<s:method
				name="multiplicar"
				fault="calculadoraFault(event)"
				result="calculadoraResult(event)"/>

			<s:method
				name="dividir"
				fault="calculadoraFault(event)"
				result="calculadoraResult(event)"/>

		</s:RemoteObject>

		<s:RadioButtonGroup id="grupoOperacoes"/>

	</fx:Declarations>

	<s:Panel x="11" y="10" width="250" height="200" title="Testes Serviços (ServicosTeste.java)">
		<s:Button x="15" y="13" label="Executar" click="botaoExecutarClick(event)"/>
	</s:Panel>
	<s:Panel x="269" y="10" width="250" height="241" title="Calculadora (ServicosCalculadora.java)">
		<s:Label x="12" y="20" text="Número 1:"/>
		<s:Label x="13" y="50" text="Número 2:"/>
		<s:TextInput x="79" y="14" width="64" id="campoN1"/>
		<s:TextInput x="80" y="44" width="63" id="campoN2"/>
		<s:Label x="13" y="102" text="Operação:"/>
		<s:RadioButton x="77" y="84" label="+" groupName="grupoOperacoes" selected="true" id="radioSomar"/>
		<s:RadioButton x="111" y="84" label="-" groupName="grupoOperacoes" id="radioSubtrair"/>
		<s:RadioButton x="77" y="110" label="*" groupName="grupoOperacoes" id="radioMultiplicar"/>
		<s:RadioButton x="111" y="110" label="/" groupName="grupoOperacoes" id="radioDividir"/>
		<s:Label x="10" y="184" text="Resultado:"/>
		<s:TextInput x="79" y="176" id="campoResultado" width="159"/>
		<s:Button x="10" y="141" label="Calcular!" width="228" click="botaoCalcularClick(event)"/>
	</s:Panel>
</s:Application>

Na próxima (e última) parte do nosso tutorial, iremos ver como trabalhar com objetos criados por nós mesmos, enviando e recebendo esses objetos entre as camadas feitas em ActionScript (swf) e Java (com suporte do BlazeDS). Utilizaremos banco de dados também, fazendo então um exemplo completo de um CRUD (Create Read Update Delete).

Espero que estejam gostando!

Parte 1Parte 2Parte 3Parte 4.1Parte 4.2

Parte 1Parte 2Parte 3Parte 4.1Parte 4.2

Olá. Nesta parte do tutorial vamos ver onde baixar o Flash Builder, como criar um novo projeto, como “amarrar” este projeto ao projeto criado na primeira parte e por fim como criar um serviço simples e consumi-lo. Então, vamos lá!

Primeiramente você precisará baixar o Flash Builder (antigo Flex Builder), que é uma IDE especializada na criação de RIAs (Rich Internet Application) em Flash (você já deve saber disso hehe). Internamente o Flash Builder utiliza o Flex SDK (que por sua vez é gratuíto) para compilar os arquivos fonte do projeto em um .swf que conterá a interface gráfica da aplicação. Neste tutorial estou abordando o uso da IDE para facilitar o aprendizado. Acessanto esse link, você pode baixar a versão trial do Flash Builder 4 (versão que estou usando). São disponibilizadas duas versões, uma que é um Plugin para o Eclipse, e outra que é Standalone (é um Eclipse com o plugin já instalado). Como estamos trabalhando com o NetBeans para construir nosso backend, então estarei utilizando a versão Standalone, mesmo porque não sou usuário do Eclipse e não sou muito fã também.

Já baixou? Já instalou? Ótimo! Mão na massa agora :D

Com o Flash Builder aberto, crie um novo projeto: File > New > Flex Project. Feito isso, a janela New Flex Project será exibida. Vamos preenchê-la da seguinte forma:

  • Project name: IntegracaoFlexJavaGUI – perceba que mantive o nome do projeto do NetBeans, colocando a sufixo GUI.
  • Project Location: desmarque o “Use default location”, pois iremos guardar o projeto no mesmo diretório onde estamos salvando o projeto do NetBeans. No meu caso, eu salvei o projeto do NetBeans em C:\Users\David\Documents\Java\Flex, sendo assim, o caminho para salvar o projeto do Flash Builder vai ficar C:\Users\David\Documents\Java\Flex\IntegracaoFlexJavaGUI
  • Application type: Web – estamos desenvolvendo para Web e não para o AIR. Só para contextualizar, o AIR é basicamente uma plataforma para execução de projetos do Flex em ambiente desktop, como uma aplicação convencional.
  • Flex SDK version: vamos usar o default, que é o Flex SDK 4.0 para o Flash Builder 4. Nada impede que outros SDKs sejam instalados, caso você queira usar algum mais antigo.
  • Server technology: Como estamos trabalhando com Java, vamos escolher J2EE.
    • Marque a opção “Use remote object access service” caso não esteja marcado.
    • Nós vamos utilizar o BlazeDS. Não expliquei no tutorial anterior o que é o BlazeDS, então lá vai. O BlazeDS implementa um protocolo chamado AMF (Action Message Format), que é utilizado na tarefa de serializar e deserializar objetos. Nossa aplicação em Flex vai enviar objetos ActionScript via AMF para a nossa aplicação Java. O BlazeDS vai deserializar esses objetos em objetos Java. O inverso também ocorrerá, ou seja, criaremos objetos Java, mandaremos via BlazeDS (serializando em AMF) para a aplicação em Flex, que por sua vez vai deserializá-los. Legal não? O LifeCycle Data Services é como o BlazeDS, só que mais robusto e é pago. Ah, além do Blaze e do LifeCycle que são implementações em Java, existem diversas outras implementações para outras linguagens.
Figura 1

Criando o projeto no Flash Builder

Com tudo preenchido, clique em Next. O próximo passo deve ser preenchido da seguinte forma:

  • Root folder: Este campo deve ser preenchido com o caminho do diretório que contém o diretório WEB-INF/flex. No meu caso, o caminho seria C:\Users\David\Documents\Java\Flex\IntegracaoFlexJava\web. Note que estamos apontando para o projeto do NetBeans e na pasta web estão contidas as pastas META-INF, WEB-INF e swf.
  • Root URL: Este campo deve ser preenchido com o endereço da raiz da aplicação em execução. Caso você esteja usando o Tomcat na porta padrão definida pelo NetBeans (8084), este campo deve ser preenchido com o seguinte valor: http://localhost:8084/IntegracaoFlexJava/. Perceba que normalmente o Tomcat ou outro servidor que manterá suas aplicações em produção rodam em outra porta. O padrão do Tomcat é a 8080. Então você tem duas opções. 1ª configurar o Tomcat do NetBeans para rodar na porta 8080, assim você não terá problemas quando for colocar a aplicação em produção. 2ª (não recomento) trocar a porta do Tomcat de produção para 8084. Existe a necessidade de colocar o endereço correto porque o Flex vai usar esse endereço para compilar o endereço dos serviços que iremos criar.
  • Context root: Esse campo define a raiz do contexto. Neste exemplo, a raiz é /IntegracaoFlexJava. Note que é o final do endereço do Root URL.
  • Output foler: Neste campo é definido onde o resultado da compilação do projeto no Flash Builder será colocado. Lembra da pasta swf que criamos? Queremos então que o Flash Builder jogue os .swfs gerados dentro daquela pasta. Então, no meu caso, o caminho ficou C:\Users\David\Documents\Java\Flex\IntegracaoFlexJava\web\swf
Figura 2

Segundo passo na criação do projeto

Por fim, clique em Finish. O novo projeto será criado. Por padrão, o Flash Builder vai criar um arquivo mxml que será o ponto de partida para a execução da aplicação (chamado de Default Application). No nosso caso, o arquivo gerado se chama IntegracaoFlexJavaGUI.mxml. Note que também por padrão é exibido o código do arquivo. Logo acima, podemos mudar para a visualização do Design do arquivo em questão.

Figura 3

Botão design

Clicando no botão Design, você poderá ver o editor gráfico da interface. À esquerda há uma paleta com os componentes gráficos. Arraste um botão para o Design para podermos ver o que está acontecendo quando executamos nosso projeto.

Figura 4

Um botão inserido na aplicação

Note que o objetivo deste tutorial não é ensinar a trabalhar com o Flex nem com o Flash Builder, mas sim, ajudar na integração com a camada escrita em Java. Sendo assim, não ficarei detalhando componentes o tempo todo.

Salve o arquivo que está aberto. Sempre que salvar, o Flash Builder vai compilar todo o projeto novamente, gerando os .swfs. Sendo assim, volte agora para o NetBeans e expanda a pasta swf que foi criada no tutorial anterior. Percebe que foram gerados diversos arquivos.

Figura 5

Arquivos gerados dentro do NetBeans

Esses arquivos sempre serão gerados automaticamente, então não há necessidade de se preocupar. Os arquivos principais no nosso caso são o IntegracaoFlexJavaGUI.html e o IntegracaoFlexJavaGUI.swf. O primeiro é a página HTML que vai ser usada para executar o segundo. Vamos testar então. No NetBeans, execute o projeto (F6 ou flecha verde). A página “Hello World!” ainda será exibida. Agora mude o endereço para http://localhost:8084/IntegracaoFlexJava/swf/IntegracaoFlexJavaGUI.html e acesse. Você vai ver que será exibida a aplicação.

Legal não é? A aplicação está rodando dentro do Tomcat. Temos dois ambientes separados, um para a infraestrutura em Java no NetBeans e um para a construção da interface gráfica no Flash Builder. Vamos agora começar a trabalhar com os serviços. Primeiro vamos criar um serviço que, quando acessado, simplesmente direciona para a saída padrão, via System.out.println(), uma mensagem. Depois iremos melhorar a idéia e testar outras possibilidades, e no final, na quarta parte do tutorial, faremos um exemplo envolvendo a transferência de objetos entre as duas camadas criadas.

Vamos ao serviço então?

No projeto do NetBeans, vá em Source Packages e crie um novo pacote chamado “servicos”. Neste pacote, crie uma classe chamada “ServicosTeste”. Nesta classe, crie um método chamado imprimirMensagem que em seu corpo simplesmente executa o método System.out.println( “mensagem aqui…” ). Veja a Figura abaixo.

Figura 6

Primeiro servico

servicos.ServicosTeste.java

package servicos;

/**
 *
 * @author David
 */
public class ServicosTeste {

    public void imprimirMensagem() {
        System.out.println( "Serviço rodando! Sim senhor!" );
    }

}

Cada método definido na classe criada será um serviço, mas para que esses métodos sejam reconhecidos como serviços, precisamos configurar o arquivo remoting-config.xml, dizendo ao Flash Builder que ao compilar o projeto, a classe ServicosTeste contém serviços. Abra então o arquivo remoting-config.xml que está dentro da pasta WEB-INF/flex do projeto do NetBeans. No arquivo, antes da última tag </service> insira o seguinte trecho de código:

<destination id="testeServicos">
    <properties>
        <source>servicos.ServicosTeste</source>
    </properties>
</destination>

Com isso dizemos ao compilador do Flex (que será chamado pelo Flash Builder) que a classe ServicosTeste – que está dentro do pacote servicos – é uma classe que contém serviços. Perceba que definimos um identificador para o destination. Esse id será importante quando formos chamar o serviço pela aplicação desenvolvida no Flash Builder.

Recaptulando: criamos uma classe no NetBeans que conterá nossos serviços. Criamos o método imprimirMensagem que é o nosso primeiro serviço. Editamos o arquivo remoting-config.xml para dizer que a classe que criamos contém serviços. Agora vamos no Flash Builder para vermos como fazemos para chamar o serviço criado.

Voltando então ao Flash Builder, volte para a edição do código fonte do arquivo mxml que foi gerado. Para isso, basta clicar no botão Source (do lado do botão Design). Apague o comentátio dentro da tag <fx:Declarations>. É entre as tags <fx:Declarations> e </fx:Declarations> que iremos declarar nossos serviços. Para declarar os serviços, utilizaremos a tag <s:RemoteObject>. Segue o código que deve ficar dentro de <fx:Declarations>.

<s:RemoteObject
    id="servicos"
    destination="testeServicos"
    showBusyCursor="true">

    <s:method
        name="imprimirMensagem"
        fault=""
        result=""/>

</s:RemoteObject>

Na tag <s:RemoteObject> definimos a propriedade id (que usaremos dentro do código para executar os serviços), a propriedade destination (lembra do destination do remoting-config.xml?) e se queremos que o cursor apareça como ocupado durante a execução de algum serviço. Dentro da tag <s:RemoteObject>, definimos então os métodos, que serão os nossos servicos. Para isso, usa-se a tag <s:method>. Na propriedade name colocamos o nome do método (tem que ser igual ao da classe em Java). As propriedades fault e result eu explico daqui há pouco.

Pois bem, temos o servico configurado tanto do lado do cliente (Flex) quanto do lado do servidor (BlazeDS), agora só falta executar. Lembra do botão que criamos na interface? Ele é representado pela tag <s:Button>. Edite seu botão disso:

<s:Button x="10" y="10" label="Button"/>

Para isso:

<s:Button x="10" y="10" label="Executar" click="botaoExecutarClick(event)"/>

O que fizemos? Primeiro trocamos o label do botão para “Executar” ao invés de “Button”. Segundo, registramos um método (ou função, você escolhe como chamar) para tratar o evento de clicar no botão. Precisamos implementar esse método. Para isso, antes da tag <fx:Declarations>, iremos colocar código fonte ActionScript. Para fazer isso dentro do código do mxml, usados a tag <fx:Script>. Veja como ficou:

<fx:Script>
    <![CDATA[
        private function botaoExecutarClick( event: Event ): void {
            servicos.imprimirMensagem();
        }
    ]]>
</fx:Script>

Note que dentro do método botaoExecutarClick, nós chamamos o <s:RemoteObject> que declaramos (o id era servicos, lembra?) e chamamo o método que foi definido (imprimirMensagem). Com isso feito, salve seu arquivo, vá no NetBeans e execute novamente o projeto. Após executar, troque o endereço novamente para http://localhost:8084/IntegracaoFlexJava/swf/IntegracaoFlexJavaGUI.html e acesse. Clique no botão executar.  Vá agora no NetBeans e veja a saída do Tomcat (logo abaixo na IDE, na aba Apache Tomcat 6.0.26). Se tudo deu certo, o println com a mensagem definida dentro de imprimirMensagem deve ter executado e a mensagem vai estar na saída do Tomcat.

Deu certo? Ótimo. Não deu? Verifique o que faltou. Até agora conseguimos chamar um serviço bem simples. Ainda não trocamos mensagens entre o servidor e o cliente, mas você verá que isso é muito fácil também. Por enquanto, vamos finalizar o que já fizemos. Você se lembra das propriedades fault e result do método (<s:method>) que foi definido no <s:RemoteObject>? Pois bem, essas duas propriedades definem respectivamente um método para tratar algum problema na execução do serviço e um método para tratar a execução bem sucedida do serviço.

Vamos então implementar esses métodos. Eles simplesmente mostrarão uma mensagem. A seguir, o arquivo IntegracaoFlexJavaGUI.mxml completo:

IntegracaoFlexJavaGUI.mxml

<?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" minWidth="955" minHeight="600">

    <fx:Script>
        <![CDATA[
            import mx.controls.Alert;
            import mx.rpc.events.FaultEvent;
            import mx.rpc.events.ResultEvent;

            private function botaoExecutarClick( event: Event ): void {
                servicos.imprimirMensagem();
            }

            private function imprimirMensagemFault( event: FaultEvent ): void {
                Alert.show( "Ocorreu um erro...", "ERRO" );
            }

            private function imprimirMensagemResult( event: ResultEvent ): void {
                Alert.show( "Serviço executado com sucesso!", "Mensagem" );
            }

        ]]>
    </fx:Script>

    <fx:Declarations>

        <s:RemoteObject
            id="servicos"
            destination="testeServicos"
            showBusyCursor="true">

            <s:method
                name="imprimirMensagem"
                fault="imprimirMensagemFault(event)"
                result="imprimirMensagemResult(event)"/>

        </s:RemoteObject>

    </fx:Declarations>

    <s:Button x="10" y="10" label="Executar" click="botaoExecutarClick(event)"/>

</s:Application>

Faça as modificações necessárias, salve o projeto do Flash Builder e execute o projeto do NetBeans de novo. Aponte para a URL  http://localhost:8084/IntegracaoFlexJava/swf/IntegracaoFlexJavaGUI.html e teste. Toda vez que clicar no botão, o serviço será executado e o método que trata a execução bem sucedida (imprimirMensagemResult) vai ser executado.

Bem, esta parte já está ficando muito grande. Na próxima parte do tutorial iremos fazer uma calculadora onde enviaremos os operandos e executaremos a operação desejada. Isso pode ser feito no cliente, mas vamos testar a chamada a serviços com passagem de parâmetros e recebimento de valores. Assim que fizermos esse exemplo, partiremos para a última parte do tutorial, onde iremos trabalhar com objetos mais complexos.

Espero que estejam gostando :)

Parte 1Parte 2Parte 3Parte 4.1Parte 4.2

Parte 1Parte 2Parte 3Parte 4.1Parte 4.2

Hoje em dia existem diversas bibliotecas e/ou frameworks que apoiam os desenvovedores na tarefa de construir interfaces gráficas para aplicações Web, sendo que a maioria delas são baseadas em JavaScript e/ou em Flash. Tenho certa familiaridade com algumas delas e por isso decidi montar esse tutorial para ajudar quem tiver interesse em aprender como fazer a integração entre uma interface criada em Flex com um backend escrito em Java. Vou apresentar da forma mais simples possível, sem utilizar frameworks MVC, tentando apenas puxar o fio da meada para você que quer começar a aprender a trabalhar com Flex.

Este tutorial será dividido em algumas partes e nessa primeira parte vou tratar da configuração do NetBeans. Então vamos lá! Mãos na massa! Para essa parte do tutorial vamos precisar de:

Alguns detalhes quanto à instalação de cada um dos itens citados acima:

  • NetBeans 6.9 – Recomendo que você faça o download da versão completa do NetBeans (a última coluna na página de download). No primeiro passo na instalação, o instalador lhe dará a opção de escolher quais componentes deseja instalar. Eu recomendo que você escolha a opção para instalar o Tomcat. Prefiro o Tomcat para desenvolvimento;
  • BlazeDS 4.x – O BlazeDS é que vai fazer a ponte entre o .swf gerado no Flash Builder e a nossa infraestrutura em Java. Quando for fazer o download, existem três pacotes: Turnkey, binary distribution e source. Baixe o segundo pacote, o binary distribution (cerca de 4.1MB). O pacote Turnkey vem com um servidor para testar o BlazeDS enquanto no source vem o código fonte, eles serão desnecessários.

Note que ainda não entraremos em detalhes relacionados ao Flash Builder (Antigo Flex Builder). Com tudo instalado e pronto, vamos começar. O primeiro passo é criar um projeto no NetBeans e prepará-lo para a integração. Primeiramente, vamos criar um novo projeto Web no NetBeans:

  • File
    • New Project…
      • Categories: Java Web
      • Projects: Web Application
Figura 1

Criando um novo projeto Web no NetBeans

  • Next
    • Project Name: IntegracaoFlexJava
    • Project Location: escolha onse será salvo o seu projeto
    • Marque a opção “Use Dedicated Folder for Storing Libraries”, com isso, os .jars que utilizaremos no nosso projeto ficarão contidos na estrutura do projeto, permitindo que o projeto seja compactado e enviado para qualquer pessoa. Por padrão, o NetBeans vai sugerir .\lib. Recomendo que deixe assim.
    • Marque a opção “Set as Main Project
Figura 2

Continuação da criação do projeto no NetBeans

  • Next
    • Server: Apache Tomcat 6.0.26
    • O restante das opções não precisam ser alteradas. Veja Figura abaixo
Figura 3

Continuação da criação do projeto no NetBeans

  • Finish

Pronto, o projeto vai ser criado e aberto no seu NetBeans. Por padrão, o index.jsp será aberto no editor. Note que estou utilizando o NetBeans em inglês, sendo assim, caso esteja utilizando em português, utilize as figuras como guia caso tenha dificuldades. Outro detalhe é que a versão usada no NetBeans é a 6.9, sendo assim, se você estiver usando alguma versão mais antiga, algumas coisas podem ser um pouco diferentes.

Com o projeto criado, vamos passar para a configuração do BlazeDS dentro do projeto do NetBeans. Lembra do pacote do BlazeDS que você baixou? O nome dele é parecido com blazeds-bin-4.x.x.xxxx.zip. Independente da versão baixada (no meu caso foi a 4.0.0.14931), descompacte o arquivo. Note que no pacote existem dois arquivos: blazeds.war e blazeds-bin-readme.html. Lembre sempre que estou tendo como base a versão que usei.

Um arquivo com extensão WAR (Web ARchive) é um arquivo .zip (claro, com extensão alterada) que contém uma aplicação Web em Java. Sabendo disso, precisamos “pegar” algumas coisas dessa aplicação e copiar para o nosso projeto. Sendo assim, renomeie o arquivo blazeds.war para blazeds.zip e decompacte-o.

Feito isso, vamos primeiro preparar a biblioteca do BlazeDS dentro do nosso projeto. No NetBeans, com a árvore do projeto aberta, procure pela pasta Libraries.

Figura 4

Pasta Libraries do projeto do NetBeans

Clique com o botão direito na pasta Libraries e escolha Add Library. Uma janela abrirá:

Figura 5

Bibliotecas do Projeto

Essa janela exibe todas as bibliotecas que estão configuradas dentro do projeto, sendo que elas não estão necessariamente inseridas na aplicação que será gerada. Vamos então criar a biblioteca para o BlazeDS. Clique no botão Create. No diálogo que aparecerá, coloque o nome da biblioteca (Library Name) como BlazeDS-4.0.0 (ou o nome que você desejar) e no tipo da biblioteca (Library Type) escolha Class Libraries e clique em OK. A janela Customize Library abrirá.

Figura 6

Janela Customize Library

Clique em Add JAR/Foder. Um outro diálodo abrirá. Lembra que você descompactou o blazeds.zip (que era blazeds.war)? Pois bem, procure onde você o descompactou e entre na pasta WEB-INF/lib. Note que aparecerão diversos .jars. Selecione todos e clique em Add JAR/Folder.

Figura 7

Escolhendo os .jars do blazeds.zip (blazeds.war)

O NetBeans vai perguntar se você quer mesmo criar a pasta para a biblioteca. Responda que sim. O diálogo Customize Library aparecerá novamente, agora com os .jars inseridos na biblioteca. Clique em OK.

Figura 8

.jars selecionados e inseridos na biblioteca

Ao clicar no OK, o diálogo Add Library aparcerá novamente, só que agora a nova biblioteca aparecerá na lista.

Figura 9

Janela Add Library com a nova biblioteca

Selecione-a e clique em Add Library. Perceba que fazendo isso, a biblioteca será inserida na estrutura do projeto. Se você expandir a pasta Libraries do projeto, verá que todos aqueles .jars que estão configurados na biblioteca que foi criada vão aparecer.

Figura 10

.jars adicionados na estrutura do projeto

Perceba que com isso inserimos a biblioteca BlazeDS no nosso projeto e no pacote que será gerado posteriormente para implantação da aplicação no servidor. Entretanto, ainda faltam alguns detalhes que precisamos nos preocupar. Quando utilizamos o BlazeDS, nós criamos serviços que serão expostos pela nossa aplicação. Esses serviços são implementados em classes, mas é necessário que o tanto o BlazeDS quanto o compilador do Flex saibam quais classes implementam esses serviços. Para isso, são utilizados diversos arquivos de configuração. Vamos copiá-los para nosso projeto agora. Procure a pasta WEB-INF do projeto, clique com o botão direito nela e escola New > Folder.

Figura 11

Criando uma nova pasta em WEB-INF

Um diálogo irá aparecer, perguntando o nome da pasta que será criada. Entre com o nome “flex” (sem as aspas) e clique em OK.

Figura 12

Pasta "flex" criada

Com isso feito, novamente vá aonde você descompactou o blazeds.zip (antigo blazeds.war), entre na pasta WEB-INF\flex, selecione todos os arquivos e dê um Ctrl+C ou botão direito, copiar.

Figura 13

Copiando os arquivos de configuração do Flex

Com os arquivos copiados, agora volte ao NetBeans, clique com o botão direito na pasta flex que você criou e escolha Paste (colar). Os arquivos serão copiados para a pasta.

Figura 14

Arquivos de configuração do Flex copiados para o projeto do NetBeans

Estamos quase cabando. Vamos agora modificar o web.xml do nosso projeto para configurar o gerenciador de mensagens do BlazeDS. Para isso, vá novamente no pacote que você descompactou e na pasta WEB-INF procure pelo arquivo web.xml. Abrá-o para edição. Selecione o conteúdo a partir do comentário “<!– Http Flex Session attribute and binding listener support –>” até a linha antes do do código <welcome-file-list>. Copie o texto selecionado (Ctrl+C), abra o web.xml do projeto dentro do NetBeans. O web.xml dentro do NetBeans possui uma interface gráfica para configuração. Não iremos utilizá-la agora, pois vamos editar o código XML diretamente. Para isso, clique no botão XML acima no editor.

Figura 15

Edição do web.xml

Cole o código que foi copiado do web.xml do pacote descompactado na linha entre </session-config>e <welcome-file-list>. Por fim, apague os trechos de código que estão comentados. No final, seu web.xml deve ter a seguinte aparência:

Figura 16

web.xml final

Falta pouco! Ainda no web.xml, remova a tag display-name dentro da tag servlet (<display-name>MessageBrokerServlet</display-name>). O código final do web.xml pode ser visto abaixo.

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
    <session-config>
        <session-timeout>
            30
        </session-timeout>
    </session-config>

    <!-- 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>
        <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.jsp</welcome-file>
    </welcome-file-list>
</web-app>

Da mesma forma que você criou a pasta “flex” dentro de WEB-INF, crie agora uma pasta chamada “swf” (sem aspas) na raiz do projeto (no mesmo nível de META-INF, WEB-INF e index.jsp).

Figura 17

Pasta "swf"

Posteriormente iremos configurar o Flash Builder para compilar nosso projeto em Flex e copiar os arquivos gerados para esta pasta.

O nosso último passo antes de testar o projeto é configurar o commons-logging que é é utilizado pelo BlazeDS. Para isso, primeiro vamos preparar onde iremos guardar o arquivo de configuração. Clique com o botão direito no mouse na raiz do projeto e escola Properties (último item da lista).

Figura 18

Acessando as propriedades do projeto

Com isso, a janela de propriedades do projeto abrirá. Por padrão, o item Sources estará selecionado, caso não esteja, selecione-o. Na opção sources, clique em Add Folder da área Source Package Folders.

Figura 19

Propriedades do projeto

A janela Add Source Folder irá aparecer. Nela, clique no botão destacado em vermelho na Figura abaixo e dê o nome de “logging” (sem aspas) ao diretório criado. Tecle enter para aceitar a mudança no nome do diretório, selecione-o e clique em Open.

Figura 20

Novo diretório de fontes

Feito isso, a janela de propriedades do projeto irá voltar a ser mostrada e note que o novo diretório será listado. Clique duas vezes no logging que aparece na coluna Label da tabela e dê o nome de “Configurações Logging” (sem aspas) e tecle Enter para aceitar a modificação.

Figura 21

Editando o label do diretório de logging

Por fim, clique no botão OK da janela de propriedades. Esse label que foi configurado será utilizado para mostrar essa nova pasta dentro da estrutura do projeto.

Figura 22

Configurações de logging dentro do projeto

Com isso feito, vamos agora copiar o arquivo de configuração de logging para dentro desta pasta. Vá novamente (última vez!) onde você descompactou o blazeds.zip, entre em WEB-INF/classes e copie o arquivo commons-logging.properties para a pasta que você criou dentro do NetBeans.

Figura 23

Arquivo de configurações de logging copiado

Nossa última tarefa (prometo!) é configurar para que nosso projeto não seja implantado no servidor cada vez que salvarmos algum arquivo. Isso previne que o Tomcat trave caso hajam muitos deploys seguidos. Para isso, acesse novamente as propriedades do projeto, ná na opção Run e desmarque a opção Deploy on Save e dê OK.

Figura 24

Desligando o deploy on save

Para finalizar esta parte do tutorial, pressione F6 ou então clique na flecha verde do NetBeans para compilar e executar o projeto (botão direito no projeto e Run também funciona). Se tudo estiver correto, o navegador abrirá e uma página com um “Hello World!” será exibida. Caso tenha ocorrido algum problema, revise o tutorial para verificar onde errou e tente novamente.

Na próxima parte do tutorial iremos baixar, configurar e utilizar o Flash Builder 4 para criarmos nossa primeira interface e fazer a comunicação com a camada em Java.

Grande abraço! Espero que tenha gostado ;)

Parte 1Parte 2Parte 3Parte 4.1Parte 4.2

Visualizando Grafos

Eu sou fascinado por infográficos e qualquer maneira diferente de visualizar dados. Ontem estive fazendo umas experiências e achei um programa chamado Skyrails. O Skyrails tem uma DSL (Domain Specific Language) para manipular grafos e suas respectivas visualizações. É muito interessante. Com base nisso, fiz um recorte da ConceptNetBr (uma rede semântica de conhecimento de senso comum), e utilizei esses dados para gerar uma visualização. Um dos resultados pode ser visto no vídeo abaixo. Outro programa legal para visualização de grafos é o Gephi. Vale a pena dar uma olhada.

Espero que gostem ;)