Você está na página 1de 76

      l

AJAX Avançado com GWT     a


      i
    c
    e
    p
    s
Criação de componentes e acesso a dados       E

A Revista da Comunidade Java Brasileira

Edição 39 - Ano V - R$ 9,90

 JGoodies Binding
Ligue componentes gráficos a objetos
de negócio mantendo a abstração OO

 JavaMail Aplicado Dicas na We


Webb
Criando uma mala direta em HTML Recapitulando técnicas essenciais
com anexos, fazendo personalização na programação com servlets e JSPs
através de expressões regulares

RIA com OpenLaszlo SWT com Visual XP


Torne suas aplicações gráficas mais
Crie Rich Internet Applications e traga integradas ao sistema operacional
interatividade de desktop às suas
aplicações web, com Flash, XML e Java
J ava

 JAVA EE 5
 JA Explorando a Plataforma
Conheça a fundo todas as novidades,
 Javaa Persistence
 Jav Persistence API
A nova API de persistência do EJB 3.0
de motivações a efeitos práticos – com que muda as bases do mapeamento
um tutorial no GlassFish objeto-relacional em Java

 jm39.indb 1 15/8/2006 18:11:24


 jm39.indb 2 15/8/2006 18:11:30
 jm39.indb 2 15/8/2006 18:11:30
Conteúdo
CAFEÍNA
News & Bits 06
LEONARDO GALVÃO
Novos produtos wireles s, TestNG
TestNG 5, repositório open source do Google,
Subversive 1.0

SWT com visual nativo do XP


FERNANDO LOZANO
Faça o Eclipse e outras aplicações SWT assumirem o visual do Windows XP

    s
    e RECAPITULANDO: DICAS NA WEB
      õ
    ç
FELIPE LEME
10
    e Obtendo as versões suportadas de JSP e servlets, usando forward e redirect,
      S manipulando JavaBeans em taglibs e alterando o nome de arquivos para download

JAVA EE 5
OSVALDO PINALI DOEDERLEIN
16
Explorando a nova versão do Java corporativo: anotações, injeção de dependên-
cias, novas JSRs e um exemplo prático usando o servidor open source GlassFish

PERSISTÊNCIA NO JAVA EE 5
AVA
    a ANDRÉ DANTAS ROCHA E SÉRGIO OLIVEIRA KUBOTA
28
    p
    a Aprenda a utilizar a nova Java Persistence API, de conceitos de mapeamento
      C objeto-relacional, a um exemplo completo usando Toplink
Toplink Essentials e MySQL

AJAX AVANÇADO COM GWT


ARI DIAS NETO
38
Explore a API do Google Web Toolkit e crie novos componentes,
desenvolvendo aplicações altamente interativas com AJAX e Java

UMA MALA DIRETA COM JAVAMAIL
YARA SENGER E ANA ABRANTES
62
Utilize a API JavaMail para enviar e-mails em HTML com anexos para múltiplos
destinatários, e personalize mensagens usando expressões regulares

RIA COM OPEN LASZLO
      b ANDRÉ LUÍS MONTEIRO
70
    e Turbine suas interfaces gráficas e maximize a interatividade com o usuário
      W utilizando uma plataforma open source baseada em Flash, XML e Java

    p
    o
     t
INTERFACES GRÁFICAS COM QUALIDADE – PARTE 2
      k
    s
HUGO VIDAL TEIXEIRA
Descubra como aplicar o pattern Presentation Model e a API Binding do
50
    e JGoodies para construir GUIs com produtividade, favorecendo os testes unitários
      D
 jm39.indb 3 15/8/2006 18:12:15
Direção
Ano V • Edição 39 • 2006 • ISSN 1676-8361

Diretor Editorial Leonardo Galvão


Diretor de Marketing Gladstone Matos
Diretor Comercial Casseano Filho
Edição
Esp
Publisher e Editor-Chefe
Leonardo Galvão (leonardo@javamagazine.com.br )
Editores-Adjuntos
Fernando Lozano (lozano@javamagazine.com.br )
Osvaldo Doederlein ( osvaldo@javamagazine.com.br )
Colaboraram nesta edição
Ana Abrantes, André Dantas Rocha, André Luís Monteiro,

C
Ari Dias Neto, Fernando Lozano, Hugo Vidal, Leonardo Galvão, om o Java Enterprise Edition 5.0, entramos em numa nova e importante
Osvaldo Doederlein, Sérgio Kubota, Yara Senger geração do desenvolvimento corporativo. O foco na facilidade de uso, a ab-
Arte sorção de técnicas utilizadas em produtos open source, uma participação sem
Diretor de Arte Tarcísio Bannwart ( phdesign@phdesign.com.br ) precedentes da comunidade e uma implementação de referência que já rivaliza com
Diagramação Jaime Peters Junior, Lais Pancote e Tersis Zonato os melhores servidores de aplicações... Talvez pela primeira vez na história do Java
Ilustrações Felipe Machado e Francisco Peixoto corporativo vemos lançada uma versão que é sucesso entre as mais diversas castas
Capa Felipe Machado de desenvolvedores Java. O Java EE 5.0 faz grandes correções de rumo, criando novas
Produção tecnologias que tornam obsoletas áreas problemáticas da especificação como os tão
Gerência de Marketing Kaline Dolabella polêmicos Entity Beans, incorpora mais do que nunca os web services, e já olha adiante
integrando-se com várias novidades que virão no Java SE 6.0 (Mustang). Quem diria
Distribuição
Fernando Chinaglia Distribuidora S.A.
que uma especificação do JCP, envolvendo gigantes do mercado com visões e perfis
Rua Teodoro da Silva, 907, Grajaú - RJ tão distintos como Sun, IBM, Oracle, BEA, Motorola, Cisco e SAP, chegaria a um con-
CEP 20563-900, (21) 3879-7766 - (21) 2577-6362 senso de tal solidez, criando uma especificação tão eficaz e alin hada às necessidades
do mercado? Mas foi o que aconteceu, em mais uma demonstração da efetividade do
Atendimento ao leitor  Java Community Process e da força da comunidade Java.
A DevMedia possui uma Central de Atendimento on-line, onde você pode Nesta edição, o Java EE 5 é tratado em dois artigos aprofundados. Em uma visão
tirar suas dúvidas sobre serviços, enviar críticas e sugestões e falar com um
de nossos atendentes.Através da nossa central t ambém é possível alterar geral da plataforma, você conhece todas as grandes novidades, com diversos exem-
dados cadastrais, consultar o status de assinaturas e conferir a data de envio plos funcionais, e ainda um tutorial mostrando como criar uma aplicação usando
de suas revistas.Acesse www.devmedia.com.br/central, ou se preferir uma seleção dos novos recursos da especificação, além do GlassFish, o servidor que
entre em contato conosco através do telefone 21 2283-9012. é a base da implementação de referência do novo Java EE. Em um segundo artigo, é
Edições anteriores vista em detalhes a API que tem chamado mais atenção no Java EE 5, a Java Persis-
tence API (JPA). Parte da especificação do EJB 3.0, mas planejada como especificação
Adquira as edições anteriores da revista Java Magazine ou de qualquer outra
publicação do Grupo DevMedia de forma prática e segura, em
independente na sua próxima versão, a JPA se inspira fortemente em ferramentas
www.devmedia.com.br/anteriores. de mapeamento objeto-relacional como Hibernate e iBatis, padronizando uma área
fundamental do desenvolvimento corporativo. Você vai conhecer os fundamentos da
Publicidade  JPA, e criar um exemplo completo usando vários tipos de associações e herança.
 publicidade@javamagazine.com.br , 21 2213-0940  Ainda nesta edição, confira um artigo sobre desenvolvimento AJAX que explora a
Anúncios – Anunciando nas publicações e nos si tes do Grupo DevMedia, API do Google Web Toolkit para criar componentes altamente interativos e integrá-
você divulga sua marca ou produto para mais de 100 mil desenvolvedores los em uma aplicação sofisticada com acesso a dados. Veja também como melhorar
de todo o Brasil, em mais de 200 cidades.Solicite nossos Media Kits, com
detalhes sobre preços e formatos de anúncios.
suas aplicações desktop usando o pattern Presentation Model e a API JGoodies Bin-
ding, que permite vincular os seus modelos OO à interface gráfica de forma eficaz
Reprints Editoriais – Se foi publicado na Java Magazine um artigo que
possa alavancar as suas vendas, multiplique essa oportunidade! Solicite a e organizada.
reimpressão da matéria junto com a capa da edição em que s aiu, e distribua Em outra matéria passo a passo, é mostrado como criar uma mala direta completa
esse reprint personalizado entre seus clientes. usando a API JavaMail e expressões regulares. E mais um artigo voltado à internet
Encarte de CDs – Faça como nossos maiores anunciantes.Encarte um CD mostra como usar o OpenLaszlo para criar Rich Internet Applications, baseadas em
com uma amostra de seus produtos na Java Magazine e atinja um público Flash, XML e Java. Finalmente, nesta edição retomamos a seção “Recapitulando”, dessa
segmentado e formador de opinião. vez trazendo dicas sobre o desenvolvimento com JSP e servlets; também incluímos
Realização
um artigo especial no Cafeína mostrando como fazer sua aplicação
Apoio
SWT se integrar perfeitamente ao visual do Windows XP.

Boa leitura!
Parceiros

Leonardo Galvão

 jm39.indb 4 15/8/2006 18:12:21


 aço do Leitor
Diálogos modais para a área de trabalho
E  stou desenvolvendo uma aplicação Swing
usando NetBeans 5 e JDK 1.5, a qual solicita
login e senha. Ela deveria bloquear todas as janelas
diálogos modais apenas com respeito à própria
aplicação que o criou. No Java 6 (Mustang),
portável. O site para desenvolvedores da Borland
tem um bom artigo explicando como fazer isso:
haverá um controle mais fino sobre diálogos community.borland.com/article/0,1410,20679,00.
da área de trabalho, só liberando-as após a autenti- modais, mas ainda assim o máximo será restringir html .
cação. Mas não consigo descobrir como configurar todas as aplicações Java na área de trabalho, não Por outro lado,
um diálogo modal para a área de trabalho como afetando as aplicações nativas. Veja mais sobre aplicações SWT
um todo, como seria possível usando Delphi ou as mudanças em diálogos modais no Java 6 em têm o recurso
Visual Basic.  java.s un.com /develo per/te chnical Artic les/J2 SE/  de “system mo-
Eloir Cortes Desktop/mustang/modality . dal” disponível:
É possível, pelo menos no Windows, configurar basta confi-
Infelizmente nem o Swing nem o AWT forne- diálogos Swing como “system modal” mas isto gurar o estilo
cem o recurso conhecido como “system modal” envolve o uso da tecnologia JNI (Java Native SWT.SYSTEM_MODAL
no Windows, que permite bloquear todas as Interface) para chamar funções da API nativa na criação de
aplicações na área de trabalho até que o diálogo do Windows (Win32 API). Lembrando que uma um Dialog ou
seja fechado. Até o Java SE 5.0, é possível criar aplicação usando este recurso, é claro, não seria de um Shell.

SOA importantes novidades do Java EE 5: a Java simples configuração do container web (no caso
Persistence API. do Tomcat,a ativação da válvula “SingleSignOn”) faz
Sou assinante da Java Magazine e gostaria
com que o login em uma aplicação seja automati-
 primeiramente de parabenizá-los pela excelente
camente repassado para todas as outras aplicações
qualidade do material apresentado. Também Ferramentas dentro do mesmo servidor de aplicações, ou do
gostaria de saber de vocês se há possibilidade de
Parabenizo a Java Magazine pelas excelentes mesmo cluster de servidores de aplicações.
 publicar na revista um artigo falando sobre SOA e
edições. Sou iniciante em Java e uma das grandes As dificuldades surgem quando o desenvol-
web services, visto que o assunto é cada vez mais
dificuldades para um iniciante é, além de aprender vedor, em vez de adaptar seus programas aos
comentado na comunidade Java.
a linguagem, saber utilizar as ferramentas exis- recursos de segurança previstos pelo Java EE,
Michel Pires da Silva
tentes. E a Java Magazine sabe nos mostrar como cria sua própria solução “personalizada” de
utilizar essas ferramentas de forma muito didática, autenticação. Mas não há motivos para se fugir
Web Services e SOA (Service-Oriented Archi-
o que facilita bastante a vida dos iniciantes. aos recursos padrões do Java EE, pois qualquer
tecture) foi um dos assuntos de capa da Java
Rubens Renato Lima servidor de aplicações com um mínimo de qua-
Magazine 34. O artigo de Osvaldo Doederlein
lidade é capaz de usar senhas armazenadas em
cobriu desde as tecnologias precursoras dos web
vários locais, como bancos de dados, diretórios
services como RPC, até o atual enfoque na criação Autenticação integrada no Java EE LDAP ou mesmo as senhas de rede em Windows,
de arquiteturas orientadas a serviços. Tenho várias aplicações Java EE e preciso que elas Linux e Netware.
trabalhem com um único login. Ou seja, quando
 Java EE 5 o usuário efetuar o login em uma das aplicações,

Gostaria de ver as novidades do Java EE 5 nas


será necessário que todas as outras trabalhem
com essa autenticação. Existe alguma forma de
Participe!
 próximas edições. Afinal de contas, a especificação compartilhar as informações de autenticação entre Envie sua dúvida,
 já foi aprovada , e a Java Magazine sempre nos as aplicações?  comentário, correção ou
apresenta as novidades em primeira mão. Everton Trindade sugestão, com nome completo, cidade e
Michel Zanini estado, para:
Os dois artigos “Segurança no J2EE”, publicados
Esta edição atende ao seu pedido! Além de nas Edições 22 e 23 respondem em detalhes à sua cartas@javamagazine.com.br
um artigo fornecendo uma visão geral da nova pergunta. De forma resumida, se sua aplicação utili-
plataforma destacando as prin cipais mudanças, Cartas publicadas podem ser editadas por
za os recursos de autenticação e controle de acesso
motivos de clareza ou extensão.
você verá uma matéria sobre uma das mais baseados em roles  definidos pelo Java EE, uma

Edição 39 •  Java Magazine 5

 jm39.indb 5 15/8/2006 18:12:31


Cafeín
News & Bits

Nokia (há um SDK da empresa para cada série de dispositivos), e

Nokia Carbide.j 1.5 realizar a instalação/deployment usando conexões RS232 (serial),


IrDA (infra-vermelho) e Bluetooth, assim como FTP. Há ainda um

O Carbide.j é um conjunto de ferramentas integradas voltado


à criação, depuração e testes de aplicações Java para dis-
positivos Nokia, que pode ser executado independentemente
designer de interfaces gráficas com suporte a dez tamanhos de
telas, entre outras funcionalidades.
A novidade mais importante da versão 1.5 é o suporte ao cha-
ou de forma integrada a um IDE (são suportados o Eclipse 3.0 e mado “on-device debugging”, que permite realizar a depuração
3.1, JBuilder Mobile Edition, NetBeans 4.x e 5.0, e IBM WebSphere de aplicações em execução no próprio dispositivo. Há também
Studio Device Developer 5.6 e 5.7). Com o Carbide.j, você pode suporte a novos dispositivos e várias melhorias, especialmente na
criar aplicações baseadas nos perfis MIDP e Personal Profile, assi- integração com o Eclipse e no designer de interfaces. forum.nokia.
nar pacotes de aplicações MIDP, configurar e gerenciar SDKs da com/main/resources/tools_and_sdks/carbide .

SNAP Mobile SDK 1.2


A plataforma Scalable Network Application Package (SNAP)
da Nokia suporta a criação de jogos móveis conectados e
comunidades de jogadores, através de uma API cliente e
uma infra-estrutura de serviços. Para comunidades, são
oferecidos recursos para conversas on-line e criação
de listas de amigos, indicadores de presença (on-
line, offline, jogando) e rankings, para estimular
a competição. Há funcionalidades para jogos
“ponto a ponto”, nos quais dois adversários
 jogam um contra o outro do começo ao fim,
e também a possibilidade de pesquisar e
escolher adversários e montar salas virtuais.
Outro ponto forte são os recursos para a
criação de sites com notícias e eventos,
quadros de mensagens moderados e
páginas de apresentação dos jogos. A
plataforma SNAP roda sobre o MIDP
2.0 e é baseada na plataforma Arena
criada para o N-Gage (os primeiros ce-
lulares da Nokia com suporte especial
a jogos), que já conta com mais de 500
mil usuários cadastrados. Um atestado
da importância do SNAP é a sua inclu-
são no novo Wireless Toolkit 2.5 da Sun.
snapmobile.nokia.com.

6  Java Magazine • Edição 39

 jm39.indb 6 15/8/2006 18:12:40


a LEONARDO GALVÃO

TestNG 5.0 nível de suporte oferecido no IDE para o CVS. construído sobre o Apache Lucene. Recursos
O framework de testes TestNG é o principal  polari on.org . enterprise não disponíveis na versão Commu-
concorrente do JUnit, e se posiciona como nity, como Single Sign-On e suporte a clusters
uma alternativa mais capaz e mais moderna LimpidLog estão acessíveis sob uma licença baseada na
(NG é uma abreviação de “New Generation”). A nova API de logging open source idéia de “contribuição ou pagamento”, pela
Primeiro framework de testes a suportar LimpidLog tem uma proposta radical, que qual empresas ou indivíduos podem escolher
anotações (mesmo antes do Java 5), o Tes- pode simplificar o log de informações sobre pagar royalties pelos recursos adicionais, ou
tNG inclui suporte a métodos dependentes classes durante a execução. Em vez de codificar trocá-los por contribuições para o projeto.
e parâmetros, distribuição de testes e testes chamadas à API de logging, você registra a  jahia.o rg.
orientados a dados. classe a ser monitorada e o LimpidLog acom-
O TestNG segue um modelo de execução que panha e loga informações importantes sobre Google lança repositório open source
dispensa o uso do tradicional TestSuite e traz o que está sendo feito na classe (isso vale O Google lançou um novo repositório de
o BeanShell embutido. Há também plug-ins para mensagens do tipo TRACE e ERROR; para projetos open source, baseado no sistema de
para várias ferramentas, entre elas o Eclipse e outros casos, ainda será necessário chamar controle de versões Subversion, e subordinado
o Apache Maven. A versão 5.0 tem melhorias uma API de logging comum). A API usa a classe ao portal “Google Code” (que hospeda pro-
importantes nos relatórios de testes gerados,  java. lang .ins trume nt.In stru menta tion  para instru-  jetos como a Go ogle Search API). O cadastro
com uma estrutura mais organizada e mudan- mentar classes e é capaz de logar eventos de de novos projetos é simples e há recursos
ças visuais. Outro destaque é o trabalho com chamada/entrada/saída de métodos, atribui- completos de busca, além de navegação por
stack traces, que agora podem ser mostrados ção de variáveis e levantamento de exceções. palavras-chave. Para registrar um projeto, é
acelet.com/limpidlog. necessário ter uma conta no Google, o que
interativamente nos relatórios HTML, sendo
possível também filtrá-los. testng.org abre a possibilidade de se usar diversos ou-
 Jahia Community Edition tros serviços da empresa juntamente com o
Subversive 1.0.0 Mais um produto importante liberado como repositório.
Um dos mais populares plug-ins Eclipse open source, o Jahia Community Edition torna Um detalhe interessante é que apenas algu-
para integração com o sistema de controle de disponível sob uma licença CDDL modificada, mas licenças open source são acei tas. Diz o FAQ
versões Subversion, o Subversive chegou à 80% do código do produto. São mais de 2.200 do Google, “A comunidade open source está
versão 1.0.0. O Subversion é considerado em classes e arquivos. cheia de licenças praticamente iguais. Gostarí-
muitos pontos melhor e mais moderno que o O Jahia é uma “plataforma web unificada”, amos que projetos fossem padronizados com
CVS e hoje é adotado como sistema de controle que integra tecnologias como ECM ( Enterprise as licenças mais populares e maduras”. O repo-
de versões oficial da Fundação Apache, entre Content Management) e portlets, bem como sitório inclui ainda recursos de issue tracking,
outras organizações. O objetivo do plug-in um gerenciador de documentos baseado em um módulo que foi criado inteiramente
Subversive é trazer para o Eclipse o mesmo no Apache Slide e um mecanismo de buscas pelo Google. code.google.com/hosting.

Edição 39 •  Java Magazine 7

 jm39.indb 7 15/8/2006 18:12:41


Cafeína • Especial

manifest  à instalação padrão do JRE ou JDK.

SWT com visual Assim sendo, o arquivo deve ser instalado


manualmente pelos próprios usuários.
Do lado da Microsoft, este arquivo tem
sua razão de ser, pois ele permite que apli-
cações criadas para versões anteriores do

nativo do XP Windows executem sem modificações no


XP. Ao mesmo tempo, ele torna fácil para os
desenvolvedores adequarem suas aplicações
ao novo visual, sem esforço adicional de
FERNANDO LOZANO
programação.
Em versões anteriores do Windows, a

M
Microsoft simplesmente forçava todas as
uito se fala que uma das grandes executável  javaw.exe utilizado para rodar as
aplicações a assumirem o novo visual (por
vantagens do SWT sobre o Swing aplicações SWT em seu computador.
exemplo, do Windows 3.1 para o Windows
é a aderência ao visual nativo da
95). Entretanto, isto criava problemas quando
plataforma, obtido graças ao uso dos com-  Os programas javaw.exe e java.exe são ambos
a aplicação utilizava componentes customi-
ponentes gráficos nativos. Entretanto, desen- alternativas para a execução de uma máquina
virtual Java (JVM). A única diferença entre eles é
zados que não se adequavam bem ao novo
volvedores de aplicações SWT e usuários do
que o cabeçalho do javaw.exe diz ao Windows que visual. Por isso, no XP, qualquer aplicação
Eclipse em geral devem ter percebido que,
ele é uma aplicação gráfica, e assim sua execução pode ser configurada para o visual “antigo”
no Windows XP, é assumido o visual nativo
não provoca a abertura de uma janela de console ou para o novo.
do Windows 98 e 2000, em vez do visual mais
moderno do Windows XP. ou prompt do MS-DOS.
Veja na Figura 1, por exemplo, o exemplo
EnderecoTerrestre.java publicado original- Uma vez criado (ou copiado) o arquivo, a
mente no artigo da Edição 31 sobre desen- linha de comando a seguir executa o exemplo
volvimento SWT. A Figura 2  ilustra como conforme visto na Figura 2 :
deveria ser a aparência da aplicação com o
 javaw EnderecoTerrestre
visual nativo do XP.
A boa notícia é que as duas figuras foram
A criação do arquivo irá afetar todas as apli -
geradas pela mesma aplicação. Não foi ne-
cações SWT executadas neste computador,
cessário modificar uma única linha do código
inclusive o próprio Eclipse. As Figuras 3 e 4
Java, nem recompilar a aplicação contra uma
mostram o próprio Eclipse antes e depois da
versão diferente do SWT.
criação do  javaw.exe.manifest .
A solução exige que o arquivo  javaw.exe.
Fica a pergunta: por que essa “mágica”
manifest , apresentado na Listagem 1, seja
não é parte da instalação padrão do Eclipse Figura 1. Exemplo EnderecoTerrestre.java  como exibido na
copiado para a mesma pasta que contém o instalação padrão do SWT no Windows XP, assumindo a
para Windows? Afinal o arquivo  javaw.exe.
manifest   seria simplesmente ignorado em aparência padrão do Windows 98 e 2000.
versões mais antigas do Windows, e assim
não causaria prejuízo algum.
A impossibilidade de o arquivo ser instalado
pelo próprio Eclipse decorre da licença do
Java. A redistribuição do Java da Sun deve ser
feita de modo inalterado, assim não é possível
para o Eclipse (ou para qualquer outro forne-
cedor que não tenha adquirido uma licença
especial junto à Sun) acrescentar o javaw.exe.

Figura 2. Exemplo EnderecoTerrestre.java  com a aparência


nativa do Windows XP.

8  Java Magazine • Edição 39

 jm39.indb 8 15/8/2006 18:12:48


Listagem 1. rqu vo javaw.exe.manifest que l
xml vers on= . encod ng
assem ly xmlns= urn:sc emas
assem lyIdent ty vers on=
  processorArchitecture=”X
  name= . avaw
  type=”win32”/>
<description>Standard Widg
<dependency>
  <dependentAssembly>
<assemblyIde ntity type
  name=”Microsoft.Wind
  vers on= . . .
  processorArchitectur
  publicKeyToken=”6595
  language=”*”/>
  </dependentAssembly>
  </dependency>
</assembly>

Figura 3. Eclipse com sua aparência padrão no Windows XP

Figura 4. Eclipse depois de criado o arquivo  javaw.exe.manifest 

Edição 39 •  Java Magazine 9

 jm39.indb 9 15/8/2006 18:12:56


Recapi
Para garantir a compatibilidade de suas

Dicas na
aplicações web, portanto, é sempre impor-
tante saber as versões das APIs suportadas
nos containers web utilizados. Os métodos
getMajorVersion() e getMinorVersion() da classe
 javax.servlet.ServletContext  permitem obter a

Web
versão da API de servlets. E sta classe fornece
ainda o método getServerInfo(), que descreve
o nome do servidor e sua versão.
Já para a versão da especificação JSP, é pre-
ciso obter um objeto javax.servlet.jsp.JspEngine e
chamar seu método getSpecificationVersion() . A
Listagem 1 contém uma página JSP que pode

N
este artigo são apresentadas dicas ser usada para obter essas informações.
úteis sobre JSP e servlets, soluções
para problemas recorrentes no de-
senvolvimento web, e esclarecimentos sobre Definindo o nome do
alguns procedimentos comuns. arquivo em downloads
Um uso comum para servlets (e mesmo
para páginas JSP) é a geração dinâmica de
Obtendo as versões de JSP arquivos, tais como planilhas ou documentos
e Servlets PDF. Nessas situações, ao enviar o arquivo
As tecnologias JSP e servlets gerado, é preciso que o servlet especifique
Nesta edição, evoluíram muito desde suas o tipo do conteúdo através do método
recapitulamos um dos primeiras versões. Por exemplo, response.setContentType(String tipo) . O tipo segue
a API de servlets 2.3 introduziu no cabeçalho da resposta HTTP retornada pelo
assuntos mais populares o conceito de filtros (interface servlet e, de acordo com ele, o navegador po-
 java x.sevle t.Fi lter ), e com o JSP derá decidir o que fazer com o arquivo.
na Java Magazine: o
2.0, veio o suporte a uso da Ex- Normalmente o arquivo é exibido através
desenvolvimento com JSP e pression Language dentro das de um plug-in, ou é oferecida a opção de
páginas. salvá-lo no disco, mas nos casos onde a in-
servlets. Foram selecionadas
quatro dicas, ainda
Listagem 1. versoes.jsp , obtendo a versão das APIs web suportadas
totalmente atuais, do artigo <%@ page import=”javax.servlet.ServletContext” %>
<%@ page import=”javax.servlet.jsp.JspFactory” %>
“Dicas na Web” publicado
<%
na Edição 12, que resolvem ServletContex t sc = pageContext .getServletCo ntext();
String servidor = sc.getServerInfo();
String versaoServlet = “” + sc.getMajorVersion() + “.”
dúvidas comuns e exploram + sc.getMinorVe rsion();
String versaoJsp = JspFactory.g etDefaultFact ory().
  getEngineInfo().getSpecificationVersion();
detalhes importantes da %>

programação web com Java. <b>Servidor:</b> <i><%= servidor %></i><br>


<b>Servlet:</b> <i><%= versaoServlet %></i><br>
<b>JSP:</b> <i><%= versaoJsp %></i><br>

10  Java Magazine • Edição 39

 jm39.indb 10 15/8/2006 18:13:03


  tulando FELIPE LEME

tenção é que o usuário faça o download, esse sejado (como “relatorio.pdf” ou “contador. para outra página. Existem duas formas de s e
procedimento traz alguns problemas: txt”) para o servlet ou página JSP responsável fazer isso – forward  (encadeamento) e redirect 
•Não é garantido que o usuário tenha a pela geração do arquivo, o que funcionará (redirecionamento). Embora o resultado final
opção de salvar o arquivo em vez de o nave- independente do navegador utilizado. No pareça muitas vezes ser o mesmo, o funciona-
gador abrir automaticamente um plug-in. entanto essa solução não tem a flexibilidade mento é bem diferente sob o ponto de vista
•Mesmo que o navegador web ofereça a de permitir a definição do nome no momento do navegador web.
opção de salvamento, o nome sugerido será de execução do ser vlet/JSP.
o do servlet ou o da página JSP (por exemplo, Forward 
“contador.jsp”), sendo que o ideal seria um Forward ou redirect? A primeira forma usa o método forward()
nome mais amigável, ou ao menos com a Uma necessidade muito comum na pro- da classe RequestDispatcher, como mostra o
extensão correta (como "contador-10.txt"). gramação web é passar o fluxo de execução código da Listagem 3.

Para resolver esses problemas, a solução Listagem 2. contador.jsp , exemplo simples para demonstrar mudança de nome do download
é definir a propriedade content-disposition do
<%
cabeçalho da resposta HTTP, com o valor int limite = 10; // valor padrão
attachment;filename=  nome. extensão, como no try {
limite = Integer.pars eInt(request .getParameter (“limite”));
trecho de código a seguir: }
catch(Exception exc) {
// ignora exceção (limite=10)
response.setHeader( }
“content-disposition”, String nomeArquivo = “contador-” + limite + “.txt”;
“attachment;filename =” + nomeArquivo   response.setContentType(“text/plain”);
);   response.setHeader(“content-disposition”,
“attachment;filename=” + nomeArquivo);
for (int i=1; i<=limite; i++) {
A Listagem 2 inclui uma página JSP que   out.println(i);
}
imprime um contador, de 1 até 10 (ou até %>
o valor definido no parâmetro limite). Note
que o navegador não exibe o conteúdo da Listagem 3.Forwards em JSPs e servlets
página; em vez disso abre a janela com a op -
ção de salvar o arquivo, com nome sugerido Em páginas JSP:
“contador- XX .txt” (sendo  XX o limite). RequestDispatcher dispatcher =
pageContext.getServletContext().getRequestDispatcher(“/novaPagina.jsp”);
A propriedade content-disposition é definida dispatcher.forward(request, response);
no protocolo HTTP (RFC 2183 1), e não na
API de servlets/JSP. Assim o funcionamento Em servlets:
dessa dica depende da compatibilidade do RequestDispatcher dispatcher =
navegador com os padrões W3C, e pode getServletContext().getRequestDispatcher(“/novaPagina.jsp”);
dispatcher.forward(request, response);
não funcionar em navegadores mais antigos
(nesses casos, o nome sugerido continuará
sendo o nome do JSP).
Outra opção é definir no descritor
web.xml   um mapeamento com o nome de-

1
  RFCs (Requests for Comments)   são documentos que
definem protocolos, modelos ou especificações para os
sistemas da internet. De forma ampla, representam para
a internet o que os JSRs (Java Specification Requests)  são
para Java. Figura 1. Uso de RequestDispatcher.forward() numa arquitetura simplificada

Edição 39 •  Java Magazine 11

 jm39.indb 11 15/8/2006 18:13:04


Recapitulando · Dicas na Web

O método forward() repassa a requisição chamada a out.print()  ou out.println(), como 1.  O corpo da tag <jsp:useBean> só é exe-
para outro recurso – como um ser vlet, página mostrado nos exemplos. cutado quando o JavaBean é instanciado.
JSP ou página HTML – dentro do mesmo con- Assim, se o escopo do bean for de sessão,
tainer web. Esse recurso pode fornecer uma Cuidados com <jsp:useBean> o código de inicialização só será executado
resposta final ao cliente, ou então repassar e <jsp:setProperty> uma vez. Códigos como o seguinte são causa
novamente a requisição (veja a  Figura 1). Uma prática muito comum no desenvol- constante de bugs:
Usar forward() é útil quando o primeiro servlet vimento de páginas JSP é o uso das tags
precisa fazer algum pré-processamento <jsp:useBean id=”beanEntrada” scope= ”session”
<jsp:useBean> e <jsp:setProperty> para, respec- class=”jm.dicas.EntradaDadosBean”
(por exemplo, validar se o usuário atual tem tivamente, instanciar JavaBeans e preencher scope=”session”>
permissões para acessar a URL especificada), suas propriedades com os valores fornecidos <jsp:setProperty name=”beanEntrada” property=”*”/>
antes de passar a requisição para o servlet ou </jsp:useBean>
no formulário da página. Embora a prática
JSP que realizará o processamento final. Em seja correta, é preciso tomar alguns cuidados O problema aqui é que os valores do formulário
particular, é dessa forma que o Struts e outras com o uso dessas duas tags: só são repassados para o bean na primeira vez
implementações da arquitetura MVC realizam
o trabalho do controller (veja a Figura 2).

Redirect 
A segunda forma de passar o fluxo de exe-
cução para outra página consiste em usar o
método sendRedirect() da classe HttpResponse ,
enviando com isso um comando HTTP SEND
REDIRECT para o navegador web. Nesse caso
a requisição volta para o navegador, que
automaticamente faz a requisição da página
especificada (que não precisa estar localiza-
da no mesmo servidor). A Figura 3 ilustra o
procedimento.
Figura 2. Uso de RequestDispatcher.forward() na arquitetura MVC
Comparando as técnicas
A Tabela 1 mostra as demais diferenças RequestDispatcher.forward() Response.sendRedirect()
entre cada opção, e a  Listagem 4 contém Fl ux o d e e xe cu çã o p er ma ne ce no s er vi do r Fl uxo v ol ta ao cl ie nt e
uma página JSP com exemplos das duas
Atributos definidos no request são acessíveis na O request da nova página não tem nenhum atribu-
técnicas. Note que, em ambos os casos, nova página to definido
se o buffer de resposta já tiver recebido Nova página deve obrigatoriamente estar no Não há restrições quanto à localização da nova
alguma saída, a tentativa de mudar o f luxo mesmo contexto web página
de execução lança uma IllegalStateException . A URL exibida no navegador continua sendo a da
A URL no navegador muda para a da nova página
Assim, é recomendável que chamadas a página que originou a chamada de forward()
forward() ou a sendRedirect() sejam feitas A URL da nova página deve começar com “/”
Endereço da nova página pode ser qualquer URL
no começo do código principal do servlet válida
( doPost() , doGet()  etc.), antes de qualquer Tabela 1. Diferenças entre redirecionamento e encadeamento

Listagem 4. mudaFluxo.jsp, exemplos de encadeamento e redirecionamento String tipo = request.getParameter(“tipo”);


if (tipo != null) {
Teste de forward e sendRedirect<br><br> request.setAttribute(“atributo”, “teste”);
<a href=”mudaFluxo.jsp?tipo=forward&flush=false”> try {
forward válido</a><br> if (tipo.equals(“forward”)) {
<a href=”mudaFluxo.jsp?tipo=forward&flush=true”> RequestDispa tcher dispatcher =
forward inválido</a ><br> pageContext.getServletContext().
<a href=”mudaFluxo.jsp?tipo=sendRedirect&flush=false”> getRequestDispatcher(“/novaPagina.jsp”);
sendRedirect válido</a><br > dispatcher.forward(request, response);
<a href=”mudaFluxo.jsp?tipo=sendRedirect&flush=true”> }
sendRedirect inválido</a>< br> if (tipo.equals (“sendRedire ct”)) {
<br><br>   response.sendRedirect(“/jm12/novaPagina.jsp”);
}
<% }
if (“true”.equals(request.getParameter(“flush”))) { catch(Throwa ble t) {
out.println(“ <br>Chamando out.flush()< br>”); out.println(“Exceção: “ + t.getMessage() + “<br>”);
  out.flush(); }
} }
%>

12  Java Magazine • Edição 39

 jm39.indb 12 15/8/2006 18:13:06


Edição 39 •  Java Magazine 13

 jm39.indb 13 15/8/2006 18:13:12


Recapitulando · Dicas na Web

Listagem 5. usoCorreto.jsp, uso correto das tags <jsp:useBean> e <jsp:setProperty> class=”jm.dicas.EntradaDadosBean”


scope=”session”>
Exemplo do uso correto das tags:<br> <jsp:setProperty name=”beanEntrada” property=”*”/>
<jsp:useBean id=”beanEntrada” </jsp:useBean>
class=”jm.dicas.EntradaDadosBean” scope=”session”/>
<jsp:setProperty name=”beanEntrada” property=”*”/> <form>
Nome: <input type=”text” name=”nome”
<form> value=”<jsp:getProperty name=”beanEntrada”
Nome: <input type=”text” name=”nome”   property=”nome”/>”><br>
value=”<jsp:getProperty Email: <input type=”text” name=”email”
name=”beanEntrada” property=”nome”/>”><br> value=”<jsp:getProperty name=”beanEntrada”
Email: <input type=”text” name=”email ”   property=”email”/>”><br><br>
value=”<jsp:getProperty </form>
name=”beanEntrada” property=”email”/>”><br><br>
<input type=”submit” value=”Enviar”>
</form> Listagem 7. EntradaDadosBean.java, JavaBean utilizado nas Listagens 5 e 6

package jm.dicas;
Listagem 6. usoIncorreto.jsp, uso incorreto das tags <jsp:useBean> e <jsp:setProperty>
public class EntradaDadosBean {
Exemplo do uso incorreto das tags private String nome;
(valores dos textfields nunca mudam):<br> private String email;
<jsp:useBean id=”beanEntrada” //... métodos get/set
}

seria equivalente a2:

Object meuBean = (Object) pageContext.getAttribute(


“meuBean”, PageContext .SESSION );

if (meuBean == null) {
meuBean = new java.util.Date ();
  pageContext. setAtt ribute (“meuBean”, meuBean,
PageContext.SESSION_SCOPE);
}
Figura 3. Redirecionamento com Response.sendRedirect()
Ou seja, o código gerado tenta recuperar
que o formulário é submetido. Nas submissões 2. A especificação JSP diz que quando um um bean existente no escopo de sessão e
seguintes, o bean já está presente na sessão e parâmetro da requisição é nulo ou vazio, associá-lo a uma variável da classe Object
não precisa mais ser instanciado; conseqüente- a respectiva propriedade não é “setada” (definida no atributo type). Caso o bean não
mente a tag <jsp:setProperty> não é executada. através da tag <jsp:setProperty>. É muito exista no escopo, uma nova instância da clas-
A solução para o problema é retirar a tag comum, porém, o desenvolvedor criar có- se Date (definida no atributo class) é gerada e
<jsp:setProperty> do corpo de <jsp:useBean> : digo supondo que essa propriedade será atribuída à variável meuBean .
definida com o valor nulo (ou vazio), o que Quando apenas type é usado, o bean precisa
<jsp:useBean id=”beanEntrada”
class=”jm.dicas.EntradaDadosBean”
mais uma vez causa bugs quando o bean obrigatoriamente estar disponível no escopo,
scope=”session”/> está armazenado em escopo de sessão. A senão é lançada uma exceção. Por exemplo,
<jsp:setProperty name=”beanEntrada” property=”*”/> Figura 4 ilustra o comportamento da tag o seguinte trecho de código:
<jsp:setProperty>, passo a passo, sobre o
Ou então mudar o escopo do bean: código da Listagem 6. <jsp:useBean id=”meuBean” scope= ”session”
type=”java.lang.Object”/>
<jsp:useBean id=”beanEntrada” 3. O tipo do bean a ser usado por <jsp:useBean>
class=”jm.dicas.EntradaDadosBean” pode ser definido através dos atributos type e seria equivalente a:
scope=”request” >
class. Embora os significados desses atributos
<jsp:setProperty name=”beanEntrada” property=”*”/> Object meuBean = ( Object) pageContext.getAttribute(
</jsp:useBean> sejam semelhantes, eles representam concei- “meuBean”, PageContext .SESSION );
tos ligeiramente diferentes: class define a classe if (meuBean == null) {
Os exemplos nas Listagens 5 e 6 demons- throw new InstantiationException(
sendo instanciada, enquanto que type define
tram o uso correto e incorreto dessas tags (e “bean meuBean not found within scope”);
o tipo do bean, que, além da classe do bean, }
a Listagem 7 inclui o bean simples utilizado
pode ser uma das suas superclasses ou uma
por estas).
interface implementada por ele. Por exemplo,
2
 O código mostrado aqui é parecido com o gerado pelo
o código abaixo: compilador JSP, porém simplificado: além do tratamento
de exceções ter sido omitido, os objetos foram instancia-
<jsp:useBean id=”meuBean” scope= ”session” dos através do operador new, enquanto que o compilador
type=”java.lang.Object” class=”java.util.Date”/> JSP usa  java.beans.Beans.instantiate().

14  Java Magazine • Edição 39

 jm39.indb 14 15/8/2006 18:13:17


Usar a variação de <jsp:useBean> apenas
com type é muito útil em alguns casos; por
Assim que a página é acessada, o bean é instanciado, porém nenhu- exemplo, quando um objeto representan-
ma propriedade é setada (já que não foram passados parâmetros do um usuário é criado apenas na página
à requisição). de login e precisa ser acessado em outras
páginas. Mas é preciso se ter em mente que
o tag pode causar uma exceção (se a sessão
expirar, digamos), e dessa forma ter os devi-
Após serem preenchidos os valore s do nome e e-mail, o formulário é dos cuidados. Uma saída seria o uso de tags
submetido. No servidor, o bean já existe na sessão e não é instanciado JSTL para checar a existência do objeto na
novamente. Porém, como os valores dos parâmetros nome e email são sessão, como no código seguinte:
não-nulos, as respectivas propriedades do bean são setadas.
<c:if test=” ${empty sessionScope.beanEntrada}”>
<c:redirect url=”sessaoExpirada.jsp”/>
</c:if>

<jsp:useBean id=”beanEntrada” scope= ”session”


O formulário é submetido co m um novo nome, mas sem o endereço type=”jm.dicas.EntradaDadosBean”/>
de e-mail fornecido, então apenas a p roprieda de nome será setada
no bean que está na sessão.
Felipe Leme
(felipeal@gmail.com) é
Engenheiro de Computacão pela
UNICAMP, membro atuante das
comunidades Java brasileira e mundial,
Resultado final: o nome foi alterado, mas o e-mail não. tendo publicado artigos em revistas técnicas e sites
internacionais e palestrado nas principais conferências
sobre Java, além de participar como expert no JCP e
ser committer em projetos da Apache. Atualmente é
Diretor Técnico da Voxblue ( voxblue.com)
e instrutor da Globalcode.
Figura 4. Exemplo de uso incorreto do tag <jsp:setProperty>

Edição 39 •  Java Magazine 15

 jm39.indb 15 15/8/2006 18:13:19


Byte Code

 Java EE 5
Um Enterprise Edition muito mais fácil

16  Java Magazine • Edição 39

 jm39.indb 16 15/8/2006 18:13:45


Explorando a nova versão do
 Java corporativo: anotações,
injeção de dependências,
novas JSRs e um exemplo
prático no servidor open
source GlassFish

OSVALDO PINALI DOEDERLEIN

A o reler meu primeiro artigo na


 Java Mag az ine, “J2 EE Fu nda-
mental” (Edição 1, julho de 2002),
abordando o então recém-lançado J2EE 1.3,
pude verificar como o status da plataforma
Passados quatro anos, a plataforma,
agora rebatizada Java EE, dispensa apre-
sentações, tendo se constituído num com-
ponente obrigatório do conhecimento de
qualquer desenvolvedor Java atuando no
detalhe sobre APIs e funcionalidades es-
pecíficas, o que seria pouco viável devido
à extensão da plataforma. Esta necessidade
será melhor servida por artigos futuros es-
pecializados em temas como JPA (coberto
mudou. Eram apresentados fundamentos “lado do servidor” ( server-side). Mesmo nesta edição), EJB etc. Aqui nosso obj etivo
como containers, servidores de aplicações frameworks que correm por fora e ga- é fornecer uma visão conceitual. Mas agora
e EJB, até então pouco familiares para nharam prestígio e popularidade, como assumimos que o leitor já possui familiari-
muitos desenvolvedores. Servidores J2EE Spring e Hibernate, têm ocupado um lugar dade básica com a plataforma J2EE. Assim,
eram ferramentas caras e pesadas, e a mais complementar do que alternativo podemos enfocar as novidades, e analisar
portabilidade ainda deixava a desejar para em relação ao Java EE. E numa exibição e discutir a natureza e as motivações por
os padrões do Java. Alguns componentes, surpreendente de resposta às direções trás de cada mudança dessa atualização
como os Entity Beans, estavam francamen- escolhidas pela comunidade, o novo Java que pode mudar radicalmente sua rotina
te imaturos – isso quando não já tinham EE 5 adere a práticas popularizadas por de desenvolvimento de aplicações “En-
sido descartados por muitos. O artigo ter- estas outras soluções, como inversão de terprise Edition”. E para começar uma
minava com uma seção “Web services: o controle, uso mais intenso de metadados, exploração prática, o quadro  “GlassFish:
futuro”, já anunciando o J2EE 1.4 (lançado e persistência baseada em POJOs (Plain o Java EE open source” mostra como
em novembro de 2003), mas apontando os Old Java Objects). instalar a implementação de referência
problemas de compatibilidade de padrões Este artigo inicia com uma discussão (RI) desta plataforma, e desenvolver uma
ainda em definição. Hoje em dia, poderí- geral sobre esta grande atualização da aplicação Java EE 5 simples com o IDE
amos reclamar que já existem padrões de plataforma. Diferentemente de outros NetBeans 5.5.
Web Services até em demasia. artigos desta coluna, não entraremos em
 Java EE 5: Produtividade Corporativa
Se o release anterior, J2EE 1.4, era a versão
Complexidade escalável dos web services, o Java EE 5 é a versão da
produtividade. Não por coincidência, este

É comum que problemas complexos tenham


soluções complexas, e as opções que parecem
mais simples às vezes não escalam com as de-
avançados devem estar disponíveis para quem
precisar, mas sem impor esforço de desenvol-
vimento extra para aplicações com requisitos
é o mesmo foco do Java SE 5.0, especial-
mente com a nova sintaxe de anotações.
As anotações são o veículo principal
mandas das aplicações com mais “complexidade mais modestos. das melhorias de produtividade do
essencial”. A plataforma J2EE foi por muito tempo O Java EE 5 é bem mais escalável quanto à
 Java EE 5. Quer que uma classe seja um
vista como “canhão para matar mosca”: aplicações complexidade. Por um lado, quando você tiver
Stateless Session Bean ? Basta declarar
mais simples, sem necessidades avançadas de problemas muito complexos para resolver, as
integração, escalabilidade, segurança etc., eram qualidades “enterprise” continuam lá: todo o “@Session class MinhaClasse {...} ”. Precisa expor
mais bem servidas por concorrentes mais leves e poder dos EJBs, além de segurança declarativa, a funcionalidade de uma classe como um
produtivos. Mas não deveríamos ter que migrar transações distribuídas, conectores, web ser- web service? Use a tag @WebService. E para
para outra plataforma – perdendo investimentos vices, JNDI, clusters etc. Já aplicações simples conectar a um DataSource ? Nada de código
de código, treinamento e ferramentas – conforme serão codificadas de forma simples. No J2EE  JNDI – InitialContext, lookup() , narrow() , trata-
cada projeto for mais simples ou complexo. tradicional, artefatos burocráticos acabavam mento de exceções – basta usar a anotação
Tanto o velho J2EE quanto os competidores constituindo uma fração significativa do código apropriada, como:
mais simples (ex. Ruby on Rails) exibem a falta de aplicações de menor porte, ridicularizando
@Resource(name= ”jdbc/minhaDS”)
de uma qualidade que poderíamos chamar de o J2EE em cenários “alô mundo”. Com o Java
 DataSource minhaDS;
“escalabilidade de complexidade”: a mesma EE 5 isso deixa de acontecer. E mesmo para as
plataforma deveria comportar o desenvolvi- aplicações mais ambiciosas, a facilidade de pro-
mento de uma grande gama de aplicações, das gramação do Java EE 5 deverá ser sensivelmente POJOs e anotações versus descritores
mais triviais até as mais sofisticadas. E recursos maior em comparação com o J2EE 1.4. O paradigma de programação do Java EE
5 dá um guinada espetacular, abandonan-

Edição 39 •  Java Magazine 17

 jm39.indb 17 15/8/2006 18:13:53


 Java EE 5

do boa parte da burocracia do tradicional (mesma informação em anotações e no destas interfaces de framework, pois isto
 J2EE em favor de uma opção preferencial descritor), o container dará preferência ao “queima” a oportunidade única de herança
pela simplicidade. A “programação base- descritor. Preserva-se assim a flexibilidade de implementação2).
ada em POJOs1” é fortemente suportada. dos descritores – e só há o trabalho de Em oposição à rigidez da herança, as
Veja algumas novas características: manipulá-los quando isso for realmente anotações enriquecem uma classe com
•Temos POJOs no lugar de implementa- necessário. comportamentos de forma muito mais
ção de interfaces. Não precisam mais ser Mas note que estes casos são a minoria. granular. Há anotações por tipo, atributo,
escritos centenas de métodos completa- Na prática, as oportunidades de adap- método, e até por parâmetro ou variável
mente vazios, como ejbActivate(), só porque tar aplicações a mudanças no ambiente local. Assim, se o seu bean realmente pre-
uma interface de EJB os exige. externo mexendo apenas no descritor só cisa executar algum código especial após
•É usada a Injeção de Dependências em costumam ocorrer em casos triviais, como a ativação, basta implementar um método
vez de aborrecidos lookups na JNDI. mudanças nos nomes JNDI de DataSources anotado por @PostActivate.
•As anotações assumem o lugar de des- ou filas de JMS, causadas talvez por uma As anotações possuem uma vantagem
critores XML. migração de ambiente (ex.: homologação crucial: são estaticamente tipadas. Podem
•Há muitos comportamentos default que para produção). E mesmo nestes casos, a ser verificadas e validadas de forma
evitam a necessidade de especificar op- indireção proporcionada pelas resource re- offline, o que evita toda uma categoria
ções comuns, seja com descr itores ou com  ferences (J2EE 1.3+) já evita a necessidade de de bugs. O  javac só verificará a correção
anotações. Por exemplo, um @WebMethod alterações até nos nomes JNDI “internos”, sintática das anotações, por exemplo re-
(método de web service) suporta anotações usados nos descritores e no código. clamando se você usar uma propriedade
@WebParam  para os parâmetros, mas estas Observamos que existem algumas ca- inexistente numa anotação; não reportará
são opcionais. Na sua falta, o container racterísticas, consideradas “estruturais”, erros semânticos, ex.: uma propriedade
assumirá defaults bons para a maioria dos que se definidas por anotações não são @EJB.beanInterface  com um valor que não
usos (como holder=Mode.IN). redefiníveis por descritores. Por exemplo, seja uma interface local/remota de EJB.
novamente com EJBs, anotações como Mas as ferramentas de validação incluídas
Mas será que este paradigma de progra- @Stateless  e @Stateful denotam os diferentes em IDEs com suporte a Java EE e em ser-
mação é sempre melhor? Em relação às tipos de Session Beans. Mas se uma clas se vidores de aplicações poderão fazer estas
anotações, que são o instrumento funda- for anotada com @Stateless , não será possí- validações no momento da implantação
mental do novo modelo, já as questiona- vel, pelo descritor, transformá-la num EJB ou antes, evitando que o bug fique ocu lto
mos em artigos anteriores, em seções com Stateful. O motivo é simples: a diferença e “estoure” somente quando a aplicação já
títulos como “Cuidados com as anotações” entre um bean Stateless e um Statefu l não estiver em operação.
e “Uma crítica às anotações”. O motivo está apenas no descritor – beans Stateful,
de tanta precaução é que uma revisão tão presumivelmente, mantêm algum estado Injeção de Dependências
radical no processo de desenvolvimento interno, enquanto beans Stateless não o A persistência de POJOs, no estilo do
não deve ser abordada sem cuidado e fazem, portanto transformar um tipo no Hibernate, tem chamado atenção como
visão crítica. outro exige alterações de código de qual- a grande novidade que o Java EE 5 pega
Felizmente, com a forma que o Java EE quer maneira. emprestada de outros frameworks. Mas
5 tomou, esses temores não se concretiza- não menos importante é a Injeção de De-
ram. Em especial, os descritores ainda são  Anotações versus Orientação a Objetos pendências (ID). Para compreender a idéia
suportados (o que já é bom por motivo de As anotações constituem uma alter- fundamental da ID, podemos classificar as
compatibilidade) e eles têm prioridade so- nativa a técnicas mais tradicionais na
 bre informações declaradas por anotações. programação OO, como herança ou 1
Plain Old Java Objects, algo como “bons e velhos obje-
Isso evita que as anotações eliminem a design patterns. O J2EE tradicional, tos Java”. É uma definição por exclusão: objeto s que não
flexibilidade garantida pelos descritores. como sabemos, é fortemente baseado em são obrigados a herdar/implementar nenhuma interface
Por exemplo, numa classe persistente você herança. Um Entity Bean, por exemplo, é imposta por uma API. Também se usa POJI para interfaces
que não precisam herdar nenhuma outra interface. Um
pode usar uma anotação @Table(name=”TAB”) uma classe que implementa a interface framework que suporta POJOs, no lugar de herança, uti-
para determinar que os objetos da classe  javax.ejb.EntityBean, o que obriga a definir liza mecanismos mais fracamente acoplados, como ano-
serão persistidos na tabela “TAB”. Isso é sete métodos (como ejbActivate() , ejbLoad() tações ou arquivos de configuração, para interagir com
seus objetos.
muito conveniente, mas o que acontece se etc.), que na sua maioria são desneces- 2
A exceção fica por conta de casos simplistas como os
um dia o nome da tabela t iver que mudar? sários para a maior parte dos beans. Há listeners da AWT/Swing, ex.: MouseListener, acompanhados
No J2EE tradicional (EJB/CMP), bastaria certamente algo errado numa arquitetura por classes “adapter”, como MouseAdapter, que facilitam a
criação de inner classes   que implementam somente um
alterar o descritor. No Java EE 5 podería- que obriga as aplicações a definir até sete dos métodos exigidos por listeners que agrupam vários
mos alterar a anotação @Table,mas isto não métodos vazios para cada entidade de eventos. Como tratadores de eventos são simples e rara-
é obrigatório. Podemos também criar um negócio. (Lembrando que em Java, nem mente se beneficiariam de herança de implementação,
nestes casos não faz mal usar a herança única para deri-
descritor e informar nele o nome atuali- sempre é uma boa idéia usar classes-base var do adapter. O mesmo não pode ser dito de EJBs que
zado da tabela. No caso de redundância que forneçam implementações default implementam complexas regras de negócio.

18  Java Magazine • Edição 39

 jm39.indb 18 15/8/2006 18:13:54


aplicações em três graus de evolução: o da comunicação com EJBs. Se você já Por essas e outras, os únicos desenvolve-
1. Todas as dependências (de confi- programou com EJB, já deve ter visto ou es- dores que acham o EJB simples costumam
gurações e de código) estão amarradas crito muito código como o da Listagem 1. ser aqueles que já utilizaram gerações ante-
no código.  Neste caso, mudar qualquer Neste exemplo, na camada de apresentação riores de componentes distribuídos, como o
coisa exige alterar código, recompilar e de uma aplicação (no caso, uma GUI web COM/COM+ da Microsoft ou o CORBA da
reiniciar a aplicação. É o grau zero, hoje implementada com o Struts), precisamos OMG. Há justificativas para expor algumas
em dia só encontrado em aplicações de invocar um Session Bean que executa o das etapas que descrevemos, mas nem sem-
iniciantes ou em códigos descartáveis, login do usuário. Todo o código destacado pre. Por exemplo, nunca entendi a utilidade
como protótipos. em negrito não passa de “burocracia do da interface Home para Stateless Session
2. Dependências de configuração estão  J2EE”. Veja quanta coisa precisamos fazer Beans, que só podem ter um método create(),
fora do código. A aplicação é parametri- só para invocar um simples método login() sem parâmetros e cujas invocações retor-
zada por informações carregadas a partir no bean remoto: nam sempre a mesma referência.
de arquivos, bancos de dados, diretórios • Configurar a conexão com a JNDI (sim- Mais grave é nos obrigar a escrever
ou outros locais externos. A alteração plificado, neste código – pode ser preciso código que quase nunca é necessário.
destes parâmetros pode ser feita por um setar propriedades para o InitialContext). Por exemplo, quanta gente trata exceções
não-desenvolvedor, não exige mudanças • Fazer um lookup na JNDI. como NamingException, com um código de
no código e talvez nem indisponibilidade • Tratar possíveis erros deste lookup. recuperação de falha específico a cada
do sistema. Por outro lado, dependências • Como o que é retornado pelo lookup local de ocorrência? Quase ninguém; o que
de código continuam amarradas. Em é na verdade a interface Home, deve-se todo mundo faz é tratar de forma genérica
qualquer circunstância onde a classe A invocar um dos seus métodos create() para estas exceções, talvez encapsulando-as em
precisa utilizar a classe B, isto é feito com gerar a referência ao Session Bean que outra exceção que o framework utilizado
código, ex.: realmente desejamos. sabe tratar (como uma ServletException para
• Se estivermos usando uma interface aplicações web). Daí o framework se en-
class A { remota, ainda precisamos usar a operação carrega da exceção, cancelando a operação
void m() {
B b = new B(); narrow() ao invés de um simples typecast do em andamento, roteando a GUI para uma
// usa b...  Java. página de erro configurável, etc. Mas se
} • Finalmente, como estes lookups são é para deixar que o framework ou o con-
}.
relativamente custosos, aplicações preo- tainer trate exceções, não deveríamos ter
Alguns design patterns, como Abstract cupadas com o desempenho devem tentar que escrever centenas de blocos try/catch
Factory e Service Locator, ajudam a encap- reduzi-los, por exemplo com a estratégia exatamente iguais, nem saber que existe
sular e concentrar num só lugar a decisão de cache e inicialização sob demanda a NamingException! Aliás, nem deveríamos
de quais objetos instanciar ou utilizar, e ilustrada com o método lookupLoginServer() precisar lidar com a JNDI, ou as interfaces
permitem aos dependentes de um com- e o atributo loginServer. Home, exceto em casos excepcionais.
ponente depender apenas de interfaces
abstratas. Mas isso não é uma solução
completa, pois as dependências de código Listagem 1.Código de exemplo numa Web Application J2EE que acessa um servidor EJB.
continuam sendo resolvidas por código, public class LoginAction extends DispatchAction // usando Struts
ainda que de forma mais elegante e sem protected LoginServer loginServer ;
protected synchronized LoginServer lookupLoginServer ()
repetições. É neste ponto de evolução que throws ServletExcep tion
se encontra a maioria das aplicações atuais {
if (login == null) try
 bem escritas. {
InitialContex t ctx = new InitialContex t();
3. Uso de Injeção de Dependências . As Object obj = ctx.lookup(“ ejb/session/L oginServer”) ;
dependências de código não são respon- LoginServerHo me home = (LoginServerH ome)
PortableRemot eObject.narr ow(obj, LoginServer Home.class);
sabilidade do código da aplicação, e sim loginServer = home.create();
de um runtime de Injeção de Dependên- }
catch (final NamingExcept ion e) { throw new ServletExcept ion(e); }
cias, que “injeta” em cada objeto-cliente return login;
os recursos dos quais ele depende. As }
regras que determinam como esta injeção public ActionForward doLogin (final ActionMapping mapping, final ActionForm form,
é feita – qual classe instanciar e com quais final HttpServletRe quest request, final HttpServletR esponse response)
throws ServletExcep tion
parâmetros – ficam em metadados exter- {
nos, que são manipulados pelo runtime LoginServer loginServer = lookupLoginServer();
LoginForm f = (LoginForm)form;
de ID. boolean ok = loginServer. login(f.getUs er(), f.getPassword ());
// ... etc.
}
O exemplo mais comum de Injeção de }
Dependências, em aplicações Java EE, é

Edição 39 •  Java Magazine 19

 jm39.indb 19 15/8/2006 18:13:54


 Java EE 5

       l
GlassFish:
para o GlassFish, utilizando o plug-in disponível
em glassfishplugins.dev.java.net , mas o WTP (e

     a
versões anteriores do NetBeans) ainda não possui

       i
suporte específico para o Java EE 5.
O NetBeans pode ser baixado numa versão que

     r O Java EE 5
 já inclui o servido r Java EE, que desta forma é pré-
configurado na instalação, ou se pode instalar o
Glassfish à parte. Neste caso, será preciso ir em

     o
Tools > Server Manager > Add Server  e criar um novo

open source registro do tipo Sun Java System Application Ser-

      t
ver, entrando com seus parâmetros de instalação
(diretório, login de administrador etc.). (Este mes-

     uO
Java EE 5 é um padrão formal do JCP, e logo terá muitas implementações mo tipo de servidor funciona com o Java EE SDK
concorrentes. Mas vale a pena dar uma atenção privilegiada para uma 5.0 / SJSAS 9 e com o Glassfish; par a simplificar, nos
destas implementações, o projeto GlassFish. Trata-se do mais novo servidor referiremos ao servidor como apenas “GlassFish”.)

       T
Java EE 5 de fonte aberto. Aliás, o único por ora, já que o JBoss, apesar do pioneirismo Agora vá na janela Runtime do NetBeans e, sob
com o EJB 3.0, ainda não parece perto de finalizar o suporte para a Java EE 5 como Servers , encontre o servidor GlassFish e comande
um todo, e o Geronimo provavelmente demorará muito mais. Start in Debug Mode .
O GlassFish foi baseado em código doado pela Sun, sendo a base Em File | New Project , selecione Enterprise / Enter-
do Sun Java System Application Server 9 (também conhecido  prise App lication na primeira página, e preencha
como Java EE SDK 5), que é a implementação de referência (RI) a segunda conforme a Figura Q1 (modificando
do Java EE 5. Tanto a versão open source quanto a da Sun são é claro os drives e diretórios), selecionando o
certificadas. O GlassFish vem sendo desenvolvido como um GlassFish e criando um módulo EJB e um módulo
projeto do portal java.net, onde já angariou bastante suporte web. O NetBeans criará três projetos, para os mó-
da comunidade open source. Resta saber se isso se traduzirá em dulos EJB, WAR e EAR.
ganhos significativos de mercado para es te servidor, que até hoje, a No projeto EJB, acione New > Session Bean, e crie
despeito da liderança da Sun no desenvolvimento da tecnologia J2EE / um EJB com nome “Alo”, tipo Stateless e interface
Java EE e fortes resultados recentes de benchmarks SPEC jAppServer apenas Remote. O assistente irá criar os fontes da
2004, tem amargado pouca popularidade, apesar de gratuito para interface AloRemote  e da implementação AloBean.
uso comercial (um apelo que se mostrou insuficiente, já que o Após customizarmos estes fontes, eles deverão
JBoss é tanto gratuito quanto open source). ficar como nas Listagens Q1 e Q2 .
Examinando o projeto, você verá que temos
Testando o Java EE 5 com o Glassfish apenas estes dois arquivos .java , e além disso
Para exercitar um pouco os novos recursos do Java dois descritores – sun-ejb-jar.xml  e MANIFEST.MF .
EE 5 apresentados neste artigo, veremos uma apli- Mas ambos estão vazios; foram criados apenas
cação “alô mundo” de duas camadas, com uma por conveniência. Apague estes dois arquivos do
interface web JSF que invoca um Session Bean projeto, para comprovar o que dissemos sobre
– executando no GlassFish. serem opcionais.
Para este exercício, utilizare mos o IDE No projeto do EAR, acione Deploy Project . O
NetBeans 5.5. Usuários do Eclipse 3.2 / NetBeans irá executar a compilação dos módulos
WTP 1.5 também podem desenvolver dependentes, o empacotamento do EAR, e sua

Figura Q1. Criando o projeto de exemplo.

20  Java Magazine • Edição 39

 jm39.indb 20 15/8/2006 18:15:03


instalação no servidor GlassFish que já havíamos pela JSF, e outputText, que exibe dados (equivalente desta funcionalidade. Entre o que utilizamos,
deixado executando. à <c:out> da JSTL). O argumento da nossa outputText está a arquitetura de componentes distribuídos
Vamos agora à interface web com a JSF. Nas é uma expressão EL que obtém a propriedade alo do EJB: seria possível fazer o deploy dos módulos
propriedades do projeto WAR, na categoria do managed bean. EJB e web em servidores separados e a aplicação
Frameworks, comande Add  / Java Server Faces, acei- Para executar o exemplo, faça o deploy do funcionaria da mesma forma.
tando as opções default; isto configura o projeto projeto principal (o EAR), e acesse com o browser A Figura Q2 mostra o ambiente de desenvol-
para suportar a JSF. Além disso, como o projeto o endereço: http://localhost:8080/AloEE5-war/  vimento do NetBeans 5.5, onde podemos ver a
WAR precisará utilizar a interface AloRemote do faces/index.jsp edição de JSP/JSF, o runtime e os logs do servidor
projeto EJB, você deve configurar o classpath do Sendo que 8080 é a porta HTTP default do Glas- Glassfish, e a estrutura dos projetos AloEE . Como
projeto WAR: em Libraries / Add Project , selecione sFish, AloEE5-war  é o nome do projeto do módulo a especificação e as implementações de Java EE
o diretório do projeto EJB. Web, faces/  é o padrão de URL default para acio nar 5 ainda são muito recentes, o suporte de IDEs
Nossa GUI terá apenas uma página, que invoca o o controller da JSF (equivale ao “ *.do ” da Struts), ainda é preliminar (o NetBeans 5.5, no momento
Session Bean e exibe a mensagem retornada. Para e index.jsp é a nossa página. O resultado do teste em que escrevo, ainda está em beta). Mas até em
fazer esta invocação, vamos usar um managed não será surpreendente – a mensagem “Alô, Java função das simplifica ções do Java EE 5, a tendência
bean. Na JSF, “managed beans” são classes equi- EE 5!” exibida no seu browser. O surpreendente, é dependermos cada vez menos de ferramentas
valentes às Forms do Struts (mas com a vantagem em comparação com versões anteriores do J2EE, elaboradas.
de serem POJOs), normalmente contendo dados é a simplicidade do
(modelo) manipulados pelas telas (views). O nosso código, com menos de
managed bean só terá uma propriedade “virtual” 40 linhas (ou menos
cujo getter invoca o Session Bean. de 50 se contarmos
Crie o managed bean com New > File/Folder o faces-config.xml ) .
> Web > JSF Managed Bean. Modifique o nome Pode não parecer as-
da classe para “Alo”, e aceite os defaults para sim tão pouco para
as demais opções. Finalmente, edite o código um programa “alô
gerado para ficar como a Listagem Q3. Observe mundo”, mas note que
o uso de Injeção de Dependências para conectar o programa executa
com o Session Bean. (O assistente de criação num container Java
de managed beans é útil principalmente por EE 5, que disponibili-
registrar o bean na configuração da JSF, o faces- za às aplicações um
config.xml ). formidável volume
A última etapa é criar a página index.jsp , confor- de funcionalidades
me a Listagem Q4. Páginas JSF são basicamente – ainda que isso possa
JSPs que utilizam as taglibs da JSF; neste caso, uti- ser difícil de perceber
lizamos as tags ( ou componentes) view, que deve para o iniciante, pois
englobar todo o conteúdo da página gerenciado o exemplo usa pouco Figura Q2. O NetBeans 5.5, durante o desenvolvimento de uma aplicação Java EE 5.

Listagem Q1.Interface remota do stateless session bean “Alo”. import javax.ejb.EJB;

package aloee; public class Alo {


private @EJB AloRemote aloServer;
import javax.ejb.Remote; public String getAlo () {
return aloServer.alo ();
@Remote public interface AloRemote { }
String alo (); }
}

Listagem Q2.Implementação do stateless session bean “Alo”. Listagem Q4.Página de teste da JSF.

package aloee; <%@ taglib uri=”http://java.sun.com/jsf/html” prefix=”h” %>


<%@ taglib uri=”http://java.sun.com/jsf/core” prefix=”f” %>
import javax.ejb.Stateless;
<html>
@Stateless public class AloBean implements aloee.Alo {  <head>
public String alo () { <title>Alô, Mundo</title>
return “Alô, Java EE 5!”;  </head>
}  <body>
}   <f:view>
<h:outputTex t value=” #{Alo.alo} ”/>
  </f:view>
Listagem Q3.Managed Bean para a tela de entrada.  </body>
</html>
package aloee;

Edição 39 •  Java Magazine 21

 jm39.indb 21 15/8/2006 18:15:11


 Java EE 5

É para resolver esses problemas que priedades, permitindo escolher a interface ponentizada (neste caso, a ID configura
surgiram design patterns como o Service local ou remota, ou o nome lógico ( java: referências para EJBs ou web services).
Locator, que já comentamos. Estes patterns comp/env ) ou o nome proprietário do con-  Já framework s esp ecial izados como o
reduzem o problema – podemos ter todos tainer EJB (mapped name). Spring incentivam e permitem o uso de
os lookups de EJBs numa única classe, e •Há anotações para interagir com o ID intenso, de alta granularidade. Neste
não em dezenas de classes Action – mas ciclo de vida do objeto sujeito à Injeção estilo, praticamente nenhum recurso é
não eliminam o problema totalmente. de Dependências. Por exemplo, poderí- inicializado em código, coisa que para
A solução definitiva é proposta pelo amos criar um método com a anotação alguns desenvolvedores parece ser uma
sistema de Injeção de Dependências, cuja @InjectionComplete , que seria então invocado maravilha e para outros um excesso ou
implementação no Java EE 5 nos permite uma única vez, após a inicialização de abuso da técnica. Assim, podemos arriscar
escrever código como o da Listagem 2, todos os recursos que usam Injeção de que o Java EE 5 satisfará às necessidades de
na qual foram destacadas as mudanças Dependências na sua classe. ID da maioria dos desenvolvedores, mas
em relação à versão anterior. Observe que os adeptos mais radicais da técnica terão
não precisamos mais nos preocupar com  A Injeção de Dependências no Java EE 5 que avaliar se preferem continuar usa ndo
 JNDI, lookup, exceções, interfaces Home, Quem já conhece a Injeção de Depen- algo como o Spring4, ou se preferem “mo-
conversões de interfaces remotas, ou mes- dências em frameworks como Spring, derar” para ter os benefícios de escrever
mo otimizações para reduzir os lookups. PicoContainer e outros, poderá se pergun- aplicações “puro-Java EE”.
Só precisamos declarar uma variável (local tar se o suporte a ID do Java EE 5 é melhor
ou atributo) que seja do tipo da interface ou pior. As anotações de ID utilizadas Os componentes do Java EE 5
de Session Bean desejada, e complementar pelo Java EE 5, especif icadas pela JSR-250 Nada menos que 21 JSRs separadas, que
esta declaração com a anotação @EJB. Esta (Anotações Comuns para a Plataforma podemos ver na Tabela 1, contribuem
anotação é simples: basta informar o nome  Java), são também incluídas no Java SE diretamente para o Java EE 5. Novas fun-
 JNDI do bean desejado, e o runtime de ID 6. Isso abre a possibilidade de melhorias cionalidades são poucas, mas importantes:
faz o resto. de APIs fundamentais e até otimizações a Java Persistence API (que sucede os EJB/
O código resultante é muito menor. Até da JVM, para tirar proveito da Injeção de CMP) é a única API totalmente nova. Além
mesmo o método doLogin() foi simplificado. Dependências, ou torná-la mais eficiente disso, temos a incorporação de APIs ante-
O funcionamento básico da ID é simples: e poderosa. riormente independentes, como a StAX e
quando o artefato que contém a dependên- Por outro lado, sistemas de ID mais ma- a JAXB, e atualizações de muitas outras. A
cia (no caso, a LoginAction3) for criado, o con- duros como o Spring são mais poderosos. mudança mais sensível ao programador,
tainer fará o lookup, a invocação do create() Por exemplo, o Spring pode “injetar” ob- que é a introdução de anotações para faci-
etc. Se o lookup falhar, ocorrerá um erro  jetos arbitrários com construção complexa litar o uso de muitas APIs, dá uma “cara”
impedindo a inicialização da Action, e este (algo que pode exigir arquivos XML bem de novidade, mas por si só não cria novas
erro será tratado pelo framework web. grandes...), enquanto o mecanismo do Java funcionalidades – só permite fazer com
Existem variantes neste comportamento, EE 5 é mais orientado a objetos publicados anotações as mesmas coisas que podíamos
que recuperam muito da flexibilidade que na JNDI. Entretanto, a nova especificação e continuamos podendo fazer invocando
você poderia imaginar ter perdido na tran- torna obsoletos os sistemas anteriores de as APIs tradicionais.
sição da Listagem 1 para a Listagem 2 : ID, pelo menos para as funcionalidades Versões do Java EE e SE sempre compar-
•Ao invés de anotar com @EJB o atributo em comum. tilham algumas novas APIs, e cada versão
loginServer, poderíamos utilizar esta anota- Uma maneira de ver essa diferença é que do Java EE depende da versão anterior
ção num setter ( setLoginServer(LoginServer)), a JSR-250 oferece um suporte a ID menos mais recente do Java SE, ambas tradicional-
que seria invocado pelo container. Isto nos radical, mais orientado ao desacoplamento mente compartilhando o mesmo número.
daria maior grau de controle sobre sua entre código de aplicação e recursos de O Java EE 5 depende do Java SE 5. Mas as
inicialização. deployment (como DataSources, filas etc.), versões subseqüentes do Java SE sempre
•A anotação @EJB  suporta outras pro- e entre módulos numa arquitetura com- incorporam algumas APIs introduzidas
inicialmente no Java EE. O Mustang ( Java
SE 6) é um exemplo disso, integrando as
Listagem 2.Injeção de Dependências no Java EE 5.

public class LoginAction extends DispatchAction // usando Struts 3


Este exemplo é fictício, pois a ID só pode ser usada em
 @EJB(beanName=”ejb/session/Login”) protected LoginServer loginServer; classes que são gerenciadas pelo container, o que inclui-
public ActionForward doLogin (final ActionMapping mapping, final ActionForm form,
final HttpServletR equest request, final HttpServletR esponse response)
ria uma Servlet, mas não uma Action da Struts (que não
{ faz parte do Java EE). Mas confiamos que versões próxi-
LoginForm f = (LoginForm)fo rm; mas de frameworks populares como a Struts suportarão
boolean ok = loginServer.login(f.getUser(), f.getPassword()); integração com o Java EE, dando suporte à ID nos seus
// ... etc. artefatos.
} 4
Note que estes frameworks podem oferecer outras
} funcionalidades além de ID – por exemplo, no Spring, te-
mos os módulos de AOP e Web Flow.

22  Java Magazine • Edição 39

 jm39.indb 22 15/8/2006 18:15:12


APIs de web services e XML agora incor-
 JSR Descrição Java SE?
poradas ao Java EE 5.
Um fenômeno importante que podemos JSR-244: Java EE 5 Especificação “guarda-chuva” da plataforma Java EE 5. -
observar no Java EE 5 é o “refactoring” JSR-250: Anotações Comuns
Anotações compartilhadas pelo Java SE 6 e Java EE 5. 6
de algumas especificações. Primeiro foi para a Plataforma Java
a EL (Expression Language). Embora o JSR-181: Anotações para Web
padrão JSP já possuísse uma linguagem de Especifica as anotações utilizadas pela JAX-WS 2.0. 6
Services
expressões, a JSF criou uma nova e mais Novo agrupamento de APIs para web services, sucedendo a
poderosa, parecida mas não totalmente JSR-224: JAX-WS 2.0 antiga JAX-RPC. Suporta padrões de web ser vices como SOAP 6
compatível com a da JSP. Isso confundiu 1.2 e WSDL 1.2, e binding pela JAXB 2.0.
os desenvolvedores e causou problemas
Mapeamento automático entre objetos Java e XML. Agora
para a mistura de ambas as tecnologias em
JSR-222: JAXB 2.0 dirigido por anotações, otimizado com o uso da StA X (JSR-173), 6
uma mesma aplicação. A JSF também tinha e com um runtime portável.
outras incompatibilidades com a especifi-
cação JSP, gerando páginas incorretas para JSR-173: StAX
Nova API para parsing de XML no estilo “streaming” (ou “pull”),
6
certas combinações de funcionalidades. mais eficiente que SAX (“push”) ou DOM.
Estes problemas agora foram resolvidos Arquitetura de componentes com funcionalidades “enterprise”
pelas últimas versões: temos uma nova EL (distribuição, segurança, transações etc.). A persistência agora
unificada que deverá servir igualmente às JSR-220: EJB 3.0 Não
é uma seção em separado, e introduz a Java Persistence API 1.0
necessidades da JSP e da JSF, e talvez de (versões futuras terão JSR própria).
outros frameworks. (Minha opinião é que Toolkit orientado a componentes para criação de GUIs para
esta funcionalidade é fundamental, e com web. Não possui muitas novas funcionalidades, mas traz um
as melhorias introduzidas pela nova EL, JSR-252: Java Server Faces 1.2 Não
alinhamento com a JSP, especialmente através da nova lingua-
deveríamos pensar em abandonar alter- gem de expressões (EL) unificada.
nativas como a OGNL, usada no Tapestry
Não possui muitas novas funcionalidades, exceto pela nova
e no WebWork.) JSR-245: JSP 2.1 Expression Language, que agora é especificada em separado Não
O segundo caso é a Java Persistence API (na mesma JSR, mas versões futuras terão JSR própria).
(JPA). Além do suporte a POJOs e anota-
ções, é igualmente importante o fato de a É o Maintenance Release 2 da mesma JSR da JSTL 1.0 e 1.1,
JSR-52: JSTL 1.2 Não
contendo só correções.
nova API de persistência do Java EE 5 ser
independente da tecnologia EJB. A JPA é JSR-154: Servlet 2.5
É o Maintenance Release 1 da mesma JSR da Servlet 2.4, con-
Não
definida num documento isolado e num tendo só correções.
pacote próprio ( javax.persistence), e pode ser Mesma versão do J2EE 1.4. O plano era incluir no Java EE 5 a
utilizada também na plataforma Java SE JSR-54: JDBC 3.0 JDBC 4.0 (JSR-221), mas devido a atrasos, a nova JDBC foi rea- 5
(o artigo “Persistência no Java EE” nesta gendada para o Java SE 6 e versões futuras do Java EE.
edição mostra um exemplo completo). É
Atualização da API para e-mail, com muitas novas funcionalida-
um grande contraste com a especificação JSR-919: JavaMail 1.4 Não
des e melhorias de desempenho.
EJB/CMP, que obriga a aplicação a su-
portar o peso de um container EJB e dos Pequena atualização da API que estende os JavaBeans com
JSR-925: JavaBeans Activation
Entity Beans. Framework 1.1
suporte a handlers ativados conforme tipos M IME. Usada tradi- 6
cionalmente pela API JavaMail, e agora também pela JAX-WS.
EJB 3.0 JSR-115: JACC 1.1 Maintenance Release 1 da API para provedores de autorização. Não
Se existe um patinho feio na história do
 J2EE, sem dúvida é a tecnologia EJB, em JSR-45: Debugging para Mesma versão do J2EE 1.4. Permite a depuração de códigos
Não
Outras Linguagens não-Java que gerem bytecode, por exemplo JSP e SQLJ.
especial os Entity Beans, acusados de baixa
eficiência, alta complexidade e dificuldade JSR-914: JMS 1.1 Mesma versão do J2EE 1.4. Não
de uso. Por outro lado, os Session Beans JSR-907: JTA 1.1 Maintenance Release 1 da API para transações. Não
foram um sucesso. Com eles, obteve-se um
excelente equilíbrio entre a sofisticação e Arquitetura de conectores para sistemas de informação (EIS)
JSR-112: JCA 1.5 Não
o desempenho do CORBA, a simplicidade não-Java. Mesma versão do J2EE 1.4.
do RMI, e as vantagens de baixo acopla- JSR-88: Java EE Application Maintenance Release 2 do padrão de APIs e descritores que
Não
mento do COM/COM+. Os EJBs também Deployment 1.2 permitem o deployment portável de aplicações Enterprise.
implementam facilidades de controle de-
JSR-77: Java EE Management 1.1 Maintenance Release 1 da API para gerenciamento de servidores. Não
clarativo de segurança e de transações, alta
integração entre todo o “stack” de servidor Tabela 1. JSRs componentes do Java EE 5.

Edição 39 •  Java Magazine 23

 jm39.indb 23 15/8/2006 18:15:12


 Java EE 5

de aplicações (ex.: uma mensagem SOAP Aproveite para observar, neste exemplo de um novo projeto que precisa de persistência
chega num Message-Driven Bean, onde é concreto, outras simplificações do EJB objeto-relacional, recomendo pensar bem antes
automaticamente mapeada, verificações 3.0 que já mencionamos: nada de inter- de decidir usar os já obsoletos Entity Beans, seja
de segurança são feitas, uma transação faces Home, nem exceções RemoteException com BMP ou com CMP.
XA é criada etc.). (mesmo ao usar a interface remota), nem
Este artigo não teria espaço para tratar herança de interfaces do framework, e
igualmente todas as funcionalidades do como veremos, um empacotamento mais Qual é a relevância da JPA para os adep-
 Java EE 5, mas basta dizer aqui que outros simples. tos de outras soluções (não-EJB) de persis-
recursos do EJB também são “envenena- tência? Nenhum sistema O/R será ideal
dos” por Injeção de Dependências e por  As especificações CMP e CMR são agora desa- para todas as aplicações e a concorrência
anotações como @SecurityRoles, @Interceptor ,  provadas (deprecated); não haverá mais evolução deve continuar existindo, mas creio que a
@MessageDriven etc. destas tecnologias, e implementações futuras (tal-  JPA tende a se impor como um forte pa-
vez a partir do Java EE 6) não terão que suportá-las drão, ao unificar o status de API oficial do
 A Java Persistence API (JPA)  para obter certificação. Note que o pacote javax.ejb  Java EE 5 com qualidades de facilidade de
Qual é a nova prática recomendada para define anotações para Session Beans ( @Stateful e uso e compatibilidade com o Java SE (antes
aplicações Java EE que acessam bancos de @Stateless ) e MDBs ( @MessageDriven ), mas nenhuma exclusividades do JDO, Hibernate e outras
dados? A mesma que muita gente, fugin- anotação para Entity Beans. A minha interpreta- soluções). Até mesmo a API do popular
do dos Entity Beans, já vinha praticando ção é que além dos CMPs, os próprios Entity Beans Hibernate deve perder terreno; o projeto já
há anos: usar só Session Beans (para a estão “marcados para morrer” num release futuro oferece uma implementação (hoje em beta)
comunicação remota, controle de transa- do Java EE. Alguns podem ficar confusos, pois a da Persistence API, o Hibernate Entity
ções, segurança etc.), criar uma camada Persistence API possui uma anotação @Entity que Manager (HEM). O HEM é uma “casca”
de POJOs para as entidades, e persisti-los denota entidades persistentes; mas esta anota- que implementa a nova API padronizada
com uma ferramenta como o Hiber nate. A ção não tem qualquer relação com Entity Beans. pela JSR-220, mas que possui no núcleo o
diferença é que agora você pode substituir Resumindo, se você está na fase de prospecção runtime tradicional do Hibernate 5. Pode
a API do Hibernate pela JPA, que funciona
da mesma maneira, mas é um padrão do
 Java EE. E pode esquecer dos DAOs, pois Listagem 3.Uma aplicação EJB 3.0 completa, com persistência e Session Bean.
a persistência direta de POJOs dispensa
// Entidade persistente simples
códigos de conversão de/para objetos
exigidos pela persistência, e o código de import
import
java.io.Serializable;
javax.persistence.Entity;
persistência que você terá que escrever é import javax.persistence.GeneratedValue;
tão pouco que não justifica encapsulá-los import javax.persistence.GenerationType;
import javax.persistence.Id;
numa camada de DAOs.
 @Entity public class Usuario implements Serializable {
A Listagem 3  apresenta um exemplo private Long id;
completo de EJB 3.0 e da Persistence API. private String nome;
private String senha;
O código é simples, e além disso familiar  @Id @GeneratedValue(strategy=GenerationType.AUTO)
para quem já usou o Hibernate. A classe public Long getId () { return id; }
  public void setId (Long id) { this.id = id; }
EntityManager faz as vezes da classe Session public String getNome () { return nome; }
do Hibernate, sendo até mais fácil de usar public void setNome (String nome) { this.nome = nome; }
public String getSenha() { return senha; }
– não precisamos implementar uma Fac- public void setSenha (String senha) { this.senha = senha; }
tory para ler a configuração, instanciar a }
Session, garantir que cada thread use sem- // Interface remota do Session Bean
pre a mesma Session etc. São as vantagens import javax.ejb.Remote;
da integração com o container Java EE, e
 @Remote public interface LoginServer {
é claro, da Injeção de Dependências. Note Usuario find (Long id);
que o método EntityManager.find()  é genérico, }
retornando o objeto já com o tipo correto, o // Session Bean que utiliza a entidade persistente
que dispensa o typecast que precisaríamos
import javax.ejb.Stateless;
com o Session.get() do Hibernate. import javax.persistence.EntityManager;
O código da Listagem 3 só não constitui import javax.persistence.PersistenceContext;
uma aplicação completa porque também  @Stateless public class LoginServerBean implements LoginServer {
precisamos de um descritor de “unidade  @PersistenceContext EntityManager em;
public Usuario find (Long id) {
de persistência” ( persistence.xml). Bastaria return em.find(Usuario.class, id);
então reunir estas classes e o descritor num }
}
arquivo .ear, e instalá-lo no container.

24  Java Magazine • Edição 39

 jm39.indb 24 15/8/2006 18:15:14


Edição 39 •  Java Magazine 25

 jm39.indb 25 15/8/2006 18:15:18


 Java EE 5

ser usado em qualquer servidor Java EE  JavaServer Faces runtime portável da JAXB 2.0; e é claro,
5 que suporte EJB 3.0 6 e até sem servidor A inclusão da JSF 1.2 é a única grande facilidade de programação com anotações
algum, em aplicações Java SE 5. novidade de funcionalidade do Java EE 5 7. e suporte a POJOs.
Este anúncio de “grande novidade” pode Quanto aos web services, seu principal
Empacotamento parecer uma surpresa para os já adeptos atrativo sobre soluções mais maduras
Uma das melhorias de facilidade de da JSF, comumente utilizada em servidores e eficientes – segundo as más línguas,
uso mais discretas do Java EE 5, mas nem  J2EE 1.4. Mas o fato é que até então, nenhu- qualquer outra coisa – sempre foi a inte-
por isso pouco importante, está nos seus ma versão da JSF fez parte, oficialmente, da roperabilidade. Um importante anúncio
padrões de pacotes para deployment, os plataforma J2EE (a JSF 1.0, JSR-127 (março no JavaOne 2006 foi o Projeto Tango 8, um
arquivos EAR, WAR e outros, que tradicio- de 2004) é posterior à J2EE 1.4 (novembro esforço conjunto da Sun e da Microsoft
nalmente exigiam uma estrutura bastante de 2003)). para alinhar as implementações de web
complexa. Mas isto talvez tenha sido bom, pois a services – de um lado, as APIs do Java Web
A estrutura básica continua existindo.  JSF é uma tecnologia complexa que teve Services Developer Pack (JWSDP) e do Java
Por exemplo, uma “enterprise application” um período relativamente longo de ama- EE 5; do outro, o Windows Communication
continua sendo empacotada num arquivo durecimento – de reclamações sobre o de- Framework (parte do .NET 3.0). O projeto
EAR, dentro do qual pode-se ter arquivos sempenho das primeiras implementações, Tango foi focado não só na interoperabili-
 JAR com os EJBs, um folder META-INF com a trapalhadas como problemas de compati- dade, mas também em eficiência, facilidade
descritores e assim por diante. As melhorias  bilidade com as especificações JSP e EL, que de uso, segurança, suporte a transações e
são duas. Primeiro, os descritores tendem a só recentemente foram resolvidos. Assim, outras características.
ser bem menores (e até opcionais), graças pode ser uma boa coisa que somente agora, Pode parecer estranho que uma tecnolo-
ao novo modelo de anotações + POJOs +  já na sua terceira revisão e com muitas des- gia criada sob medida para a interopera-
defaults. Segundo, não é mais preciso decla- sas “dores de crescimento” superadas, a JSF  bilidade, baixo acoplamento etc., precise
rar obviedades ou informar a mesma coisa tenha sido integrada ao complexo conjunto de esforços contínuos para funcionar
duas vezes – o Java EE aprendeu a seguir o de especificações e tecnologias que forma a corretamente entre as duas principais pla-
princípio DRY (don’t repeat yourself ). plataforma Java EE. taformas de mercado que a implementam.
Um bom exemplo disso é a configuração A JSF é um framework orientado a com- Mas o problema é que as especificações de
de JARs utilitários, que são colocados na ponentes para geração de GUIs web. Isto se web services continuam se multiplicando e
pasta  /lib   de um EAR, mas antigamente insere melhor na arquitetura geral do Java evoluindo a uma velocidade tão grande que
isso não era suficiente: era preciso declarar EE, que já possui modelos de componentes as implementações estão sempre correndo
todos os JARs no  META-INF/MANIFEST. para regras de negócio, serviços, integra- atrás, e tendo que corrigir dificuldades cau-
 MF, na diretiva Classpath. Agora nada disso ção com recursos e sistemas externos etc. sadas por bugs de implementação ou por
é necessário – o container irá detectar todos Faltava suportar o mesmo paradigma no especificação insuficiente. Para mais sobre
os arquivos lib/*.jar e incluí-los no classpath lado do cliente, até então servido apenas o assunto, veja meu artigo “Web services e
do módulo EAR. por opções “força-bruta” (Servlets) ou de SOA”, na Edição 34.
Aplicações web (WAR) que usem somente templates (JSP – mesmo com extensões
 JSP e tecnologias relacionadas como JSTL, como EL, JSTL, Struts etc.). Por “orientado Conclusões
mas não servlets, não precisam de nenhum a componentes” queremos dizer que a JSF Neste artigo demos as primeiras pince-
descritor (WEB-INF/web.xml). O mesmo permite construir GUIs através da compo- ladas no Java EE 5, a plataforma que todos
vale para WARs expondo web services (não sição de objetos e (em se tratando de GUI) usaremos em algum ponto para aplicações
precisam mais de um webservices.xml , nem do tratamento de eventos gerados por estes
de arquivos da JAX-RPC). objetos. Um benefício desta arquitetura é a 5
A API proprietária do Hibernate possui algumas funcio-
Além disso, os EARs não precisam mais separação entre o comportamento da GUI nalidades que ainda não existem na JPA (como as queries
“Criteria”), mas isso só afeta usuários mais avançados. A
do application.xml. Os application clients e a sua apresentação. Outro é a facilidade maioria pode migrar facilmente para a JPA, ganhando
também dispensam o application-client.xml; para a construção de ferramentas pura- com isso as vantagens de padrões do Java EE, como im-
 basta ter no  META-INF/MANIFEST.MF o mente visuais. plementações concorrentes, maior suporte de IDEs etc.
6
É o caso do JBoss 4.0, cujos últimos releases (compatí-
 Main-Class apontando para a classe princi- veis com o J2EE 1.4) suportam também o EJB 3.0, embora
pal da aplicação. Finalmente, módulos EJB Web services e XML: JAXB 2.0, JAX-WS 2.0, StAX  o JBoss ainda não possua (no momento em que escrevo)
também não precisam obrigatoriamente do O Java EE 5 incorpora uma segunda gera- uma versão que suporte a plataforma Java EE como um
todo.
ejb-jar.xml, bastando que o arquivo JAR do ção de APIs para web services, integradas 7
A JPA é uma nova API , mas não uma nova funcionali-
módulo possua anotações como @Stateless. inicialmente ao J2EE 1.4. Não há novidades dade (a não ser qualitativamente), já que as versões an-
Tudo isso não quer dizer que os descrito- revolucionárias, mas temos muitas atuali- teriores do J2EE já suportavam persistência O/R com os
Entity Beans.
res tenham desaparecido completamente. zações no suporte às últimas especificações 8
O codinome “Tango” é específico ao lado da Sun do
Continuam sendo suportados, e às vezes do W3C, WS-I e OASIS. Melhorias de de- projeto, e achei ótimo. Afinal, o Tango é uma dança lenta,
podem ser necessários. A diferença é que sempenho críticas, como o suporte a StAX formal e sombria, e das mais difíceis de executar sem es-
corregar para o ridículo (pelo menos no estereótipo que
agora, só é preciso criar descritores quando e Fast Infoset (codificação binária de XML); aprendi no cinema: ver Quanto Mais Quente Melhor   com
são realmente necessários. aperfeiçoamentos de arquitetura, como o Jack Lemmon, e O Baile de Ettore Scola).

26  Java Magazine • Edição 39

 jm39.indb 26 15/8/2006 18:15:21


 Java server-side. Como vimos, a principal
promessa desta versão é a mesma do Java
SE 5: facilidade de desenvolvimento. E não
é sem tempo, pois a escalada dos requisitos
das aplicações que temos que construir, e
das tecnologias em voga (SOA, Web 2.0...)
 já torna nosso trabalho difícil mesmo
quando nossas ferramentas são simples
e racionais.
Com o objetivo de cumprir esta pro-
messa, o grupo de experts que defin iu
o Java EE 5 (e suas muitas APIs com-
ponentes) tomou decisões corajosas,
como a substituição total da malfa-
dada especificação de persistência
do EJB, e decisões humildes como a
introdução de técnicas populariza-
das por projetos open source, que
acabaram conquistando a comuni-
dade de desenvolvedores Java.
Procuramos exibir um pouco
destes avanços na facilidade de
desenvolvimento com o tutorial
no quadro “Glassfish: O Java EE
5 open source”. Algumas vanta-
gens só serão sentidas de fato em
aplicações bem mais complexas
do que os limites impostos por
um artigo (mesmo com um
tutorial mais extenso). Mas é
transparente o esforço que o
 JCP fez, nesta última revisão
do Java EE, para renovar o
velho J2EE, reduzindo muito
sua complexidade.

 jcp.org/en/jsr/detail?id=244
JSR-244, padrão do JCP que define a
plataforma JEE 5.
 java.sun.com/javaee
JEESDK 5 / Sun Java System Application Server 9.
 glassfish.dev.java.net 
Projeto GlassFish.
 javamagazine.com.br/downloads/jm39/ 
 jm-jee5.zip

Osvaldo Pinali Doederlein


(opinali@gmail.com) é Mestre em
Engenharia de Software Orienta-
do a Objetos, membro individual
do Java Community Process (tendo par-
ticipado do Expert Group da JSR-170: Tiger/J2SE 5.0), e
trabalha na Visionnaire Informática como arquiteto e
desenvolvedor.

Edição 39 •  Java Magazine 27

 jm39.indb 27 15/8/2006 18:15:26


Persistência no Java
Iniciando com a Java Persistence API

O principal foco da versão 5 do Java


EE é a facilidade de uso. O novo
 Java EE é bem mais simples que
sua versão anterior e suas novas APIs
aumentam a produtividade dos desenvol-
 Abordagens atuais de mapeamento
Desde as suas primeiras versões, a plataforma Java oferece
acesso a bancos de dados através da API JDBC, que tra-
 balha no mesmo nível do banco, sendo o acesso às in-
formações armazenadas feito através de comandos
vedores, exigindo menos esforço de codi- SQL. Em muitos aspectos, a JDBC é uma API de
ficação. Uma das principais novidades do  baixo nível, que muitas vezes exige do desen-
 Java EE 5 é a sua nova API de persistência. volvedor o conhecimento das “nuances” do
Há muito tempo esperada pela comunidade  banco de dados. Apesar de ser uma manei-
 Java, a Java Persistence API (JPA) padroniza ra eficiente de acessar dados em SGBDs
as operações de persistência sobre entida- relacionais, e a opção que normalmente
des Java, definindo uma especificação para oferece melhor performance, a JDBC
mapeamento objeto-relacional. Neste artigo oferece uma abstração OO bastante
apresentaremos a JPA através da criação de limitada (trabalha-se com tabelas,
um exemplo completo. registros e resultsets, ao invés de
objetos).
Persistência e mapeamento O/R Para usar os recursos de bancos
Como sabemos, a tecnologia de banco de dados relacionais em Java e
de dados relacionais existe há décadas, e ainda assim aproveitar os concei-
hoje os SGBDs são robustos e confiáveis. tos do paradigma OO, é necessá-
Os principais problemas relacionados ao rio fazer o que se conhece como
armazenamento e recuperação de dados já mapeamento objeto-relacional (ou
foram solucionados, e o investimento das simplesmente mapeamento O/R).
empresas nesses sistemas também é imen- No mapeamento O/R as classes
so, o que torna a sua utilização uma regra. e os atributos do sistema são
O uso dos bancos de dados relacionais, mapeados para tabelas e cam-
no entanto, traz alguns inconvenientes ao pos/colunas, e a persistência
programador de linguagens orientadas é feita de forma transparente
a objetos como Java. As tecnologias OO e pela aplicação. Assim, objetos
relacional são bastante diferentes, e seu uso em memória são armazenados
conjunto normalmente implica em enfati- no banco, e objetos do banco são
zar uma tecnologia em sacrifício da outra. trazidos para a memória sempre
Como os bancos de dados OO ainda estão que necessário. Com paradigmas
muito menos disseminados que os bancos tão diferentes, diversas questões
de dados relacionais, o desafio atual dos de- surgem durante o mapeamento:
senvolvedores é unir dois mundos comple- Como mapear herança? E agregação?
tamente distintos, utilizando a tecnologia Cada classe deve virar uma tabela?
relacional para armazenar objetos. Como aproveitar os recursos do banco
O armazenamento de objetos de uma sem perder a abstração de objetos?
aplicação é denominado  persistência de ob- Para suprir essas necessidades, surgiram
 jetos. Essa técnica permite que as instâncias diversos frameworks e tecnologias de persis-
existentes no sistema sejam armazenadas e tência, a exemplo do Hibernate e do iBatis. Essas
posteriormente recuperadas, conservando- ferramentas facilitam o trabalho do desenvolvedor
se o seu estado mesmo após a aplicação ter e aumentam sua produtividade, fornecendo poderosas
sido finalizada. APIs de manipulação de dados. Apesar de cada ferramenta

28  Java Magazine • Edição 39

 jm39.indb 28 15/8/2006 18:16:30


EE 5
Aprenda a utilizar a nova API
de persistência do Java através
de um exemplo completo,
com foco no uso em
aplicações Java SE

ANDRÉ DANTAS ROCHA E SÉRGIO OLIVEIRA K UBOTA

possuir uma forma distinta de efetuar o mapeamento O/R, os Um conjunto de ferramentas para

conceitos são semelhantes e relativamente simples, baseando- manipular entidades


se em POJOs (Plain Old Java Objects).
Em se tratando de um padrão do Java
O termo Plain Java Old Object (ou simplesmente POJO) Community Process, a JPA traz diversos
foi criado por Mar tin Fowler, Rebecca Parsons e Josh Ma-  benefícios. O uso de um padrão para a per-
cKenzie em 2000. A tradução é algo como “velho e bom sistência de objetos permite que diversos
objeto Java” e refere-se a objetos/classes Java simples, fabricantes trabalhem sobre os mesmos
não atrelados a ferramentas ou frameworks. conceitos e que o desenvolvedor escolha
a implementação de sua preferência. A
A Java Persistence API padronização também traz outra impor-
Até o J2EE 1.4 a plataforma Java tante vantagem: pode-se alternar entre
não possuía uma forma simples de implementações de fabricantes distintos,
mapear objetos no banco de dados. que estejam em conformidade com a
A única opção era a utilização de  JSR-220, sem nenhum esforço adicional.
Entity Beans, que exigem um Dessa forma, uma aplicação codificada de
container EJB e possuem uma acordo com o novo padrão irá funcionar
complexidade razoável 1. Apli- com qualquer implementação da JPA, não
cações cuja arquitetura não en- havendo necessidade de se conhecer (a
volvia EJBs precisavam utilizar princípio) qual tecnologia será utilizada
ferramentas não padronizadas para essa implementação.
como o Hibernate para fazer
a persistência, ou fazer a im- Conceitos básicos
plementação de persistência Na JPA os objetos persistentes são deno-
manualmente. minados entidades ( entities). Uma entidade
A Java Persistence API, de- é um objeto simples (POJO), que representa
finida na  JSR-220 (Enterprise um conjunto de dados persistido no banco.
 JavaBeans,Version 3.0), padroni- Como entidades são definidas por classes
za o mapeamento objeto-relacio-  Java comuns, sem relação com frameworks
nal na plataforma Java. Apesar ou bibliotecas, elas podem ser abstratas ou
de descrita na especificação do herdar de outras classes, sem restrições.
novo EJB, a JPA não depende de Um conceito importante é que as entida-
um container para funcionar, sen- des possuem um identificador (descrito
do possível usar e testar soluções pela chave primária) e estado, sendo seu
apenas com o Java SE 2. tempo de vida independente do tempo
A JPA é uma especificação baseada de vida da aplicação. Assim, aplicações
no conceito de POJOs, que incorpora distintas podem compartilhar a mesma
idéias de renomados frameworks de per- entidade, que é referenciada através de
sistência para padronizar o mapeamento seu identificador.
O/R em Java. A API oferece uma solução
completa para mapeamento e persistência de
objetos: 1
 No Java EE 5.0, os “Entity Beans” foram revistos e se tor-
naram bem mais simples de usar
• Um modo declarativo de descrever mapeamen- 2
 A independência do container trouxe uma grande me-
tos O/R lhoria na testabilidade das aplicações que mapeiam dados
• Uma linguagem de consulta do banco

Edição 39 •  Java Magazine 29

 jm39.indb 29 15/8/2006 18:17:25


Persistência no Java EE 5

Para que uma entidade se torne persisten- Grande parte da produtividade trazida uma alternativa ao SQL, que também é
te é necessário associá-la a um  persistence pela JPA deve-se à utilização de valores suportado. As consultas suportam poli-
context (contexto de persistência), que default de mapeamento, que facilitam morfismo, o que significa que quando uma
fornece a conexão entre as instâncias e  bast ante o trabalho do desenvolvedor. entidade é consultada, todas as entidades
o banco de dados. A manipulação das Assim, o que não é definido explicitamente descendentes que atendam ao critério da
entidades é feita, a partir desse contexto, assume a configuração padrão da API. Por consulta também são retornadas. A criação
por meio do entity manager (gerenciador exemplo, por padrão a JPA considera o de consultas é feita através do EntityManager,
de entidades), que é responsável por nome da entidade o mesmo nome da tabela que fornece métodos específicos para
executar as operações básicas sobre a no banco de dados e o nome da proprie- instanciar consultas estáticas e dinâmicas,
entidade (criação, atualização, exclusão, dade o mesmo nome da coluna. No código além de permitir a execução das operações
localização, consultas etc.). O  entity ma- anterior, a entidade Pessoa será salva na CRUD5.
nager na JPA é uma instância da interface tabela PESSOA e a propriedade cpf  na coluna As consultas estáticas possuem nomes e
 javax.persistence.EntityManager. CPF. Caso seja necessár io alterar a forma de são descritas pela anotação @NamedQuery.
A implementação da JPA é feita por um mapeamento, devem-se utilizar as anota- Elas são definidas nas entidades corres-
 persistence provider (provedor de persistên- ções @Table e @Column, por exemplo: pondentes e ficam “pré-compiladas”. Veja
cia). O provedor define “como as coisas um exemplo de consulta estática para
funcionam”, através da implementação de @Entity localizar uma pessoa pelo seu CPF:
@Table(name=”TB_PESSOA”)
todas as interfaces definidas pela especifi- public class Pessoa {
cação da JPA. Dessa forma, cada provedor @Id @NamedQuery(name = “consultarPorCPF”,
decide a maneira e o momento de carre-   @Column(name=”DS_CPF)” query = “SELECT p FROM Pessoa p WHERE p.cpf = :cpf”)
private String cpf;
gar, atualizar e armazenar as entidades, }
assim como sincronizar os dados com o O EntityManager utiliza o nome da consul-
 banco. As configurações utilizadas pelo Outro padrão utilizado pelo JPA é con- ta para instanciá-la, o que é feito através
provedor em uma determinada aplicação siderar todas as propriedades de uma en- do método createNamedQuery(). Depois que
(conexão com o banco de dados, entidades tidade como persistentes (o mapeamento a consulta é criada, basta setar os parâ-
que serão gerenciadas, tipo de transação segue as regras descritas anteriormente). metros e executá-la. A execução pode
etc.) são descritas em uma persistence unit, Caso seja desejável excluir alguma pro- ser feita pelos métodos getSingleResult()
que é configurada num arquivo especial priedade do mapeamento (ex.: no caso ou getResultList(), a depender do resultado
denominado persistence.xml. de ela poder ser criada a partir de outras esperado. Por exemplo, para localizar uma
propriedades), basta marcá-la com a ano- pessoa pelo CPF (supondo que o retorno
 Mapeamento tação @Transient : será único), basta executar a consulta con-
As classes e interfaces da JPA estão forme o exemplo abaixo:
localizadas no pacote  javax .persiste nce. A @Entity
public class Pessoa { Query consulta =
API faz uso intensivo de anotações 3; por @Id manager.createNamedQuery(“consultarPorCPF”);
isso não é necessário criar descritores private String cpf; consulta.s etParameter (“cpf”, “111.111.111-11”);
XML para cada uma das entidades da   @Transient Pessoa pessoa = consulta.getSingleResult();
private String nomeCompleto;
aplicação4. Uma entidade é uma classe }
 Java comum, rotulada através da anota- As consultas dinâmicas não possuem
ção @Entity. Não é preciso implementar Os dados de uma única entidade po- nome, e podem ser construídas em tem-
interfaces ou estender outras classes dem estar distribuídos em mais de uma po de execução. A criação desse tipo
para tornar uma classe “persistível”; a tabela, e diversos tipos de relacionamen- de consulta é feito através do método
única exigência é que a classe da entidade tos entre entidades são possíveis. Os createQuery() :
possua um construtor sem parâmetros, mais comuns são os de agregação (ano-
pois a instanciação da classe é feita por tações @OneToOne, @OneToMany, @ManyToOne, Query consulta = manager.createQuery(
“SELECT p FROM Pessoa p WHERE p.cpf = :cpf”);
reflexão. @ManyToMany, etc.) e herança (anotação
No código a seguir a classe Pessoa re- @Inheritance ), que serão vistos mais adiante.
presenta uma entidade. O atributo cpf  é o 3
 As anotações surgiram da versão 5.0 do Java e estão
presentes na maioria das APIs do novo JEE.
identificador da entidade (chave primária), Consultas 4
Mesmo não sendo obrigatório o uso de XML para
especificado através da anotação @Id : A JPA oferece suporte a consultas es- descrever o mapeamento, ainda é possível utilizar essa
táticas e dinâmicas. A API fornece uma opção. Um exemplo típico é o arquivo  persistence.xml ,
@Entity linguagem própria de consulta, que é que guarda as configurações da unidade de persistência
public class Pessoa { e é utilizado neste artigo.
uma extensão bastante poderosa da EJB
@Id 5
 As principais operações executadas sobre dados são
private String cpf; QL (a linguagem de consultas do EJB). conhecidas através da sigla CRUD ( Create, Read, Update,
} Essa linguagem pode ser usada como e Delete).

30  Java Magazine • Edição 39

 jm39.indb 30 15/8/2006 18:17:25


É interessante notar que a execução de tificador ou chave primária, e @Column pecificado pela anotação @GeneratedValue(
ambas as consultas é idêntica, uma vez especifica a coluna correspondente no strategy = GenerationType.IDENTITY) :
que as duas são objetos do t ipo Query.  banco (CD_CLIENTE ). No nosso caso, a chave
@Id
primária da entidade será gerada auto- @Column(name = “CD_CLIENTE”)
Um exemplo completo maticamente pelo banco através de um @GeneratedValue(strategy = GenerationType.IDENTITY)
Agora que os conceitos básicos da JPA campo de auto-incremento, o que é es- private Integer codigo;
foram apresentados, partiremos para
um exemplo completo, onde a API será
demonstrada na prática.
Enquanto a maioria dos artigos sobre
Configurando o ambiente
 JPA foca no uso de EJBs, aqui faremos
uma abordagem distinta. No exemplo
deste artigo implementaremos uma apli-
P ara executar o exemplo disponível no
site da Java Magazine, será necessário ter
instaladas as ferramentas Eclipse 3.2, MySQL e
pode variar.) Isso extrai , entre outros, o arquivo
toplink-essentials.jar , que deve ser adicionado
ao projeto no Eclipse.
cação desktop sem nenhum víncu lo com TopLink Essentials.
um container EJB. O objetivo é mostrar Executado a aplicação
que a JPA pode ser utilizada no Java SE Eclipse 3.2 Após esses passos, a aplicação estará pron-
facilmente. Usaremos na camada de per- O Eclipse 3.2 foi o IDE escolhido para imple- ta para ser iniciada. O projeto já contém a
sistência o TopLink Essentials e o MySQL. mentar o exemplo. O download pode ser feito configuração do  persis tence unit , localizada
O TopLink Essentials é a versão open a partir de eclipse.org/downloads . Para quem no arquivo META-INF/persistence.xml . Para
source do TopLink, uma ferramenta de ainda não usa o Eclipse, a instalação é simples, rodar a aplicação basta executar a classe
mapeamento objeto-relacional da Oracle. bastando extrair o conteúdo do arquivo com- br.com.jm.locadora.Principal . Durante a execução,
O TopLink Essentials implementa a JPA e pactado para o diretório de sua preferência. o console mostrará as seguintes mensagens:
é utilizado no projeto GlassFish, que é a Após a instalação do Eclipse, o projeto de exem-
Populando tabelas... OK
 base da implementação de referência do plo deve ser importado para o IDE. A estrutura
Listando todas as reservas...
do projeto é mostrada na Figura Q1.
 Java EE 5.0. Por simplicidade, e para focar  Sérgio - BMW
melhor na API, não implementamos uma  André - ECO SPORT
interface gráfica. O código completo da  MySQL Listando todas as locacoes...
aplicação está disponível no site da Java O MySQL é usado no exemplo devido à sua  André - ECO SPORT
Magazine. Veja mais sobre o ambiente facilidade de instalação e configuração. O Listando reservas entre 01/06/2006 e 01/08/2006...
utilizado e como executar a aplicação download do MySQL e do MySQL Connector/J  André - ECO SPORT
(driver JDBC) podem ser feitos em dev.mysql. Listando dados do veículo ABC1234...
de exemplo no quadro “Configurando o
com/downloads. Escolha a versão adequada  ECO SPORT - ABC1234 [Utilitario]
ambiente”.
A aplicação implementa um sistema ao seu sistema operacional e siga as instruções
(bastante simplificado) para gerencia- fornecidas no help. Após instalar, crie um data-
mento de uma locadora de veículos. Na base com nome “locadora” e execute o script
de criação das tabelas.
Figura 1 é exibido seu diagrama de clas-
O arquivo compactado do dri-
ses e na Figura 2 o modelo ER (Entidade-
ver Connector/J contém a biblioteca
Relacionamento) utilizado.
mysql-connector-java-3.1.12-bin.jar (a versão
pode variar), que deve ser adicionado ao pro-
 Mapeamento do cliente
 jeto do Eclipse.
Começaremos com o mapeamento da
classe Cliente, a mais simples da aplica-
ção. O código da classe é mostrado na Toplink Essentials
Listagem 1. Como vimos, a anotação O Toplink Essentials é a implementação da
@Entity torna a classe “persistível”, permi- JPA utilizada no projeto GlassFish (glassfish.dev.
tindo a sua manipulação pela JPA.  java.net ). O download pode ser feito a partir
Note que não foi necessário utilizar a da página do projeto: oracle.com/technology/ 
anotação @Table para especificar a tabela  products/ias /toplink/jpa /download.html . Após
utilizada para persistência, uma vez que baixar o arquivo execute o comando:
uma tabela de mesmo nome é usada para
persistir os clientes ( CLIENTE). A identida- java –jar glassfish-persistence-installer-v2-b11.jar
de de cada cliente é definida através da
propriedade codigo. A anotação  @Id indica (Lembrando mais uma vez que a versão Figura Q1. Estrutura do projeto.
que esse campo é utilizado como iden-

Edição 39 •  Java Magazine 31

 jm39.indb 31 15/8/2006 18:17:26


Persistência no Java EE 5

O mapeamento das propriedades nome que define se a propriedade em questão A propriedade reservas é um pouco mais
e endereco  é muito simples. Ambas as pro- pode ser nula: complexa, pois ela determina um rela-
priedades possuem a anotação @Column, cionamento um-para-muitos. No nosso
contendo dois atributos: name, que define a @Column(name = “DS_NOME”, nullable = false) modelo, um cliente pode estar relacionado
coluna correspondente no banco; e nullable, private String nome; a diversas reservas, sendo que cada reserva
relaciona-se a um único cliente.
A anotação @OneToMany  é usada para
indicar o relacionamento entre as enti-
Veiculo
{ From model } Reserva Cliente
dades (um cliente para muitas reservas).
 Attributes
{ From model } { From locadora } As reservas do cliente são armazenadas
private String placa veiculo  Attributes
cliente Attributes na propriedade reservas (uma lista de
private String modelo
private Integer ano
private String codigo
private Date inicio
private String codigo
private String nome
instâncias da entidade Reserva), que é po-
private String cor private Date fim private String endereco pulada automaticamente pelo TopLink.
private Double diaria Dois atributos da anotação são utilizados
reserva nesse mapeamento: cascade = CascadeType.ALL
indica que alterações na entidade Cliente
Esportivo Utilitario Locacao serão refletidas automaticamente nas
{ From model } { From model } { From model } entidades Reserva relacionadas. O atributo
 Attributes  Attributes  Attributes mappedBy = “cliente” indica que na classe
private Integer velocidade private Integer passageiros private Integer codigo
Reserva  existe uma propriedade denomi-
nada cliente, que mapeia o cliente do rela-
Figura 1. Diagrama de classes do sistema de locação de veículos. cionamento (veremos mais sobre a classe
Reserva adiante).

@OneToMany(cascade =
CascadeType.ALL, mappedBy = “cliente”)
private List<Reserva> reservas;

 Mapeamento do veículo
Na modelagem OO da aplicação decidi-
mos especializar a classe Veiculo, guardan-
do as particularidades de cada veículo em
classes específicas (veja a Figura 1). Veiculo
é uma classe abstrata, que possui duas
Figura 2. Modelo Entidade-Relacionamento para o sistema de locação de veículos. subclasses: Esportivo e Utilitario.
No modelo relacional a abordagem foi
distinta. Para evitar o custo de  joins, to-
Listagem 1.Cliente.java
dos os veículos foram mapeados em uma
package br.com.jm.locadora.model; única tabela, que contém propriedades
import java.util.List; tanto genéricas quanto específicas. Como
import javax.persistence.*; veremos a seguir, o nosso mapeamento
@Entity deve contemplar essa decisão arquitetural
public class Cliente { e permitir que os objetos sejam instan-
@Id ciados corretamente (de acordo com sua
@Column(nam e = “CD_CLIENTE”)
@GeneratedValue(strategy = GenerationType.IDENTITY)
classe), mesmo com todos residindo em
private Integer codigo; uma tabela genérica.
@Column(nam e = “DS_NOME”, nullable = false)
Vamos analisar o código da entidade
private String nome; Veiculo (Listagem 2). Como já conhecemos
@Column(nam e = “DS_ENDERECO” , nullable = false) as anotações @Entity, @Id e @Column, veremos
private String endereco; apenas as novas construções.
@OneToMany( cascade = CascadeType .ALL, mappedBy = “cliente”) O mapeamento da herança de entidades
private List<Reserva> reservas; é descrito através da anotação @Inheritance,
// ... gets e sets omitidos que define a estratégia usada no mape-
} amento. De forma semelhante a outros

32  Java Magazine • Edição 39

 jm39.indb 32 15/8/2006 18:17:28


frameworks O/R, a JPA oferece três tipos que o valor “E” for encontrado no campo pela anotação @DiscriminatorValue(“U”) da
de mapeamento de herança. Estes são CD_TIPO da tabela VEICULO, uma entidade etintidade  Utilitario.
descritos na enumeração InheritanceType : Esportivo  será criada. Quando o campo É importante notar que, como as entida-
SINGLE_TABLE, TABLE_PER_CLASS e  JOINED. CD_TIPO contiver o texto “U” uma entidade des Esportivo  e Utilitario  derivam de Veiculo, só
A estratégia SINGLE_TABLE  é a mais comum, Utilitario  será criada, como especificado é necessário mapear as novas propriedades
sendo a opção default da JPA. Nessa es-
tratégia todas as entidades da hierarquia
Listagem 2.Veiculo.java
são persistidas em uma única tabela, que
deve conter todos os campos necessários. package br.com.jm.locadora.model;
Na estratégia TABLE_PER_CLASS cada entidade import java.util.List;
é mapeada em uma tabela específica, en- import javax.persistence.*;
quanto na estratégia  JOINED uma associação @Entity
de tabelas é usada para persistência de @Table(name = “VEICULO”)
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
cada entidade. @DiscriminatorColumn(name = “CD_TIPO”, discriminatorType = DiscriminatorType.STRING)
A estratégia de mapeamento deve ser public abstract class Veiculo {
@Id
especificada na entidade raiz da hierar- @Column(name = “DS_PLACA”, nullable = false)
private String placa;
quia, através do atributo strategy. Seguindo
o nosso modelo, a hierarquia de veículos @Column(name = “DS_MODELO”, nullable = false)
private String modelo;
seguirá a estratégia SINGLE_TABLE:
@Column(name = “NR_ANO”, nullable = false)
private Integer ano;
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@Column(name = “DS_COR”, nullable = false)
private String cor;
O mesmo mapeamento pode ser de-
finido de forma simplificada, visto que @Column(name = “VL_DIARIA”, nullable = false)
private Double diaria;
SINGLE_TABLE é a estratégia de mapeamento
default da JPA: @OneToMany(c ascade = CascadeType. ALL, mappedBy = “veiculo”)
private List<Reserva> reservas;

@Inheritance @Column(name = “CD_TIPO”, nullable = false)


public abstract String getTipo();

// ... gets e sets omitidos


Como todas as entidades da hierarquia }
são armazenadas em uma única tabela, é
necessário determinar a correspondência
entre registros e entidades. A anotação Listagem 3.Esportivo.java
@DiscriminatorColumn  define a regra utilizada package br.com.jm.locadora.model;
pela JPA para “reconhecer” cada registro
import javax.persistence.*;
e instanciar a entidade correspondente.
O atributo name especifica a coluna que @Entity
@DiscriminatorValue(“E”)
armazena o descritor da entidade, enquan- public class Esportivo extends Veiculo {
to o atributo discriminatorType  define o tipo
@Column(name = “VL_VELOCIDADE”, nullable = false)
dessa coluna: private Integer velocidade;

// ... gets e sets omitidos


@DiscriminatorColumn(name = “CD_TIPO”, }
discrim inatorType = Discri minatorType.STRING )
Listagem 4.Utilitario.java
Durante a persistência, a JPA lê o valor
package br.com.jm.locadora.model;
armazenado na coluna CD_TIPO (uma string)
e escolhe a entidade com a qual irá traba- import javax.persistence.*;

lhar. Essa escolha é feita a partir de uma @Entity


outra anotação, especificada nas entidades @DiscriminatorValue(“U”)
public class Utilitario extends Veiculo {
descendentes: @DiscriminatorValue.
@Column(name = “NR_PASSAGEIROS”, nullable = false)
Nas Listagens 3 e 4 são exibidos os có- private Integer passageiros;
digos das entidades Esportivo e Utilitario. A
// ... gets e sets omitidos
anotação @DiscriminatorValue(“E”), contida na }
entidade Esportivo, especifica que sempre

Edição 39 •  Java Magazine 33

 jm39.indb 33 15/8/2006 18:17:29


Persistência no Java EE 5

especificadas nessas entidades ( velocidade  Mapeamento da reserva


e passageiros). Além disso, é possível espe-  Já apre se nt am os a entidade Reserva
cificar a obrigatoriedade dos campos das (Listagem 5), que descreve a reserva de um
entidades descendentes ( nullable = false) veículo, relacionando-se com as entidades
mesmo com todas compartilhando uma só Veiculo e Cliente. Reserva  representa o lado
tabela. A obrigatoriedade só será avaliada “filho” dos relacionamentos veiculo-reserva
para registros correspondentes à entidade e cliente-reserva, e armazena o cliente e o ve-
em questão. ículo nas propriedades cliente e veiculo. Para

Listagem 5.Reserva.java

package br.com.jm.locadora.model;

import java.util.Date;
import javax.persistence.*;

@Entity
@NamedQueries({
@NamedQuery( name = “Reserva.lis tarPorPeriodo ”,
query = “SELECT r FROM Reserva r WHERE “ +
“r.inicio >= :inicio AND r.fim <= :fim”),

@NamedQuery(name = “Reserva.ultima”,
query = “SELECT r FROM Reserva r WHERE r.codigo = “ +
“(SELECT MAX(r1.codigo ) FROM Reserva r1)”)

})
public class Reserva {
@Id
@Column(name = “CD_RESERVA”)
@GeneratedVa lue(strategy = GenerationTy pe.IDENTITY)
private Integer codigo;

  @ManyToOne
@JoinColumn( name = “CD_CLIENTE” , nullable = false)
private Cliente cliente;

  @ManyToOne
@JoinColumn( name = “DS_PLACA”, nullable = false)
private Veiculo veiculo;

@Column(name = “DT_INICIO”, nullable = false)


  @Temporal(TemporalType.DATE)
private Date inicio;

@Column(name = “DT_FIM”, nullable = false)


  @Temporal(TemporalType.DATE)
private Date fim;

// ... gets e sets omitidos


}

Listagem 6.Locacao.java

package br.com.jm.locadora.model;

import java.util.Date;
import javax.persistence.*;

@Entity
public class Locacao {
@Id
@Column(name = “CD_LOCACAO”)
@GeneratedVa lue(strategy = GenerationTy pe.IDENTITY)
private Integer codigo;

@OneToOne
@JoinColumn( name = “CD_RESERVA” )
private Reserva reserva;

// ... gets e sets omitidos


}

34  Java Magazine • Edição 39

 jm39.indb 34 15/8/2006 18:17:37


mapear o relacionamento entre as ent ida- @OneToOne gurações da  persistence unit do arquivo
des são usadas anotações @ManyToOne, que @JoinColumn(name= “CD_RESERVA”)  persistence.xml (Listagem 7). Esse arquivo
private Reserva reserva;
definem que “muitas reservas estão rela- possui um significado especial, arma-
cionadas a um cliente” e “muitas reservas O mapeamento está especificado em zenando as configurações de conexão e
estão relacionadas a um veículo”. A ano- apenas um lado da relação (na entidade a descrição das entidades. O arquivo de
tação @JoinColumn é usada para especificar Locacao) e, por isso, é dito unidirecional. Isso configuração deve residir numa pasta
a coluna que contém a chave estrangeira significa que a locação “conhece” a reserva, com nome  META-INF, na raiz do projeto
de cada relação. O relacionamento com a mas a reserva “não conhece” a locação. (veja o quadro “Configurando o ambiente”
entidade Cliente é exibido a seguir: para conhecer a estrutura do projeto de
Persistindo dados e executando consultas exemplo).
@ManyToOne
@JoinColumn(name = “CD_CLIENTE”, nullable = false) Como explicado anteriormente, a inter- A classe EntityManager  define os principais
private Cliente cliente; face EntityManager  é a responsável pelas métodos para incluir, localizar e salvar
operações de persistência. A estratégia de entidades. Os métodos de persistência
Vale lembrar que as propriedades cliente criação da instância desse gerenciador vai não declaram exceções checadas, portan-
e veiculo  são referenciadas nas entidades variar de acordo com o padrão de arquite- to não é obrigatório declarar exceções no
“pai” da relação ( Cliente e Veiculo ), atra- tura escolhido. Se a implementação de JPA código.
vés dos atributos mappedBy = “cliente” e for executada a partir de um servidor de Com o objeto EntityManager criado, basta
mappedBy = “veiculo”, como é possível ver nas aplicações Java EE, o EntityManager deve ser executar o método correspondente sobre
Listagens 1 e 2. declarado pela anotação @PersistenceContext a entidade. Por exemplo, para localizar
A entidade Reserva possui duas proprie- e será criado automaticamente pelo contai- um veículo através da sua placa (chave
dades de data/hora ou “temporais”: fim e ner (via injeção de dependência). Já no Java primária), basta chamar o método find()
inicio. Na JPA, todas as propriedades tempo- SE o EntityManager  deve ser instanciado pela passando como parâmetros a classe da
rais (do tipo  java.util.Date ou java.util.Calendar ) aplicação, o que é feito através da fábrica entidade e sua chave:
devem ser marcadas com a anotação abstrata EntityManagerFactory:
@Temporal, que especifica o tipo no banco Veiculo veiculo = manager.find(Veiculo.class, “ABC1234”);
de dados usado para persistência. EntityManagerFactory factory =
  Persistence.createEntityManagerFactory(“locadora”);
EntityManager manager = A inserção e atualização da entidade são
@Column(name = “DT_INICIO”, nullable = false)
@Temporal(TemporalType.DATE) factory.createEntityManager(); feitas através do método persist(). A primei-
private Date inicio; ra chamada ao método torna a entidade
A classe EntityManagerFactory  é criada persistente, associando-a ao contexto de
A entidade Reserva  também apresenta pela classe Persistence, que lê as confi- persistência:
uma particularidade em relação às ou-
tras entidades descritas anteriormente. Listagem 7.persistence.xml
Em Reserva  estão codificadas algumas
consultas estáticas, que podem ser exe- <?xml version=”1.0” encoding=”UTF-8”?>
<persistence xmlns=”http://java.sun.com/xml/ns/persistence”
cutadas sobre a entidade. A anotação   xmlns:xsi=”http ://www.w3.or g/2001/XMLSch ema-instance ” version=”1.0”
@NamedQueries  é usada para agrupar todas   xsi:schemaLocation=”http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd”>
as consultas estáticas da entidade, que são <persistence-unit name=”locadora”>
  <provider>
descritas individualmente em anotações   oracle.toplink.essentials.ejb.cmp3.EntityManagerFactoryProvider
@NamedQuery .   </provider>

  <class>br.com.jm.locadora.model.Cliente</class>
 Mapeamento da locação   <class>br.com.jm.locadora.model.Reserva</class>
  <class>br.com.jm.locadora.model.Locacao</class>
O mapeamento da entidade   Locacao   <class>br.com.jm.locadora.model.Veiculo</class>
(Listagem 6) é simples. Além das ano-   <class>br.com.jm.locadora.model.Utilitario</class>
  <class>br.com.jm.locadora.model.Esportivo</class>
tações @Id e @GeneratedValue   abordadas
anteriormente, essa entidade apresenta   <properties>
<property name=”topli nk.logging.le vel” value=”INFO ” />
um relacionamento um-para-um com <property name=”topli nk.jdbc.drive r” value=”com. mysql.jdbc.Dr iver” />
a entidade Reserva, pois uma locação só <property name=”topli nk.jdbc.url”
value=”jdbc:m ysql://127.0 .0.1:3306/loc adora” />
pode existir para uma reserva criada <property name=”topli nk.jdbc.user” value=”root ” />
<property name=”toplink.jdbc.password” value=”” />
anteriormente.   </properties>
O relacionamento é descrito pela anotação
  </persistence-unit>
@OneToOne, e possui uma sintaxe semelhante </persistence>
à de um mapeamento um-para-muitos:

Edição 39 •  Java Magazine 35

 jm39.indb 35 15/8/2006 18:17:37


Persistência no Java EE 5

Dali JPA Tools

O Dali JPA Tools é um plug-in da Fundação Eclipse que oferece


suporte ao JPA. Além de views que auxiliam no mapeamento
entre propriedades e colunas, o plug-in também fornece ferramen-
tas para a geração das entidades a partir do modelo relacional e
para geração do modelo relacional a partir das entidades. Aqui
demonstraremos como instalar o plug-in e gerar as entidades a
partir do banco de dados.
Supondo que o ambiente já está configurado (veja o quadro
“Configurando o ambiente”), o primeiro passo consiste em efetuar
o download do Dali de eclipse.org/dali . A instalação é simples,
bastando extrair o arquivo compactado para o diretório raiz do
Eclipse e reiniciar o IDE. Após a instalação, a perspectiva de per-
sistência estará disponível.
Em seguida é necessário criar um novo proj eto Java e adicionar

Figura Q3. Configurando a conexão.

Clique novamente em Finish para finalizar a configuração. Com


a conexão funcionado, a última etapa é gerar as entidades. Para
isso, clique com o botão direito sobre o projeto e selecione  Java
Perspective>Generate Entities. A tela de geração de entidades será
exibida ( Figura Q4 ) listando todas as entidades encontradas
no banco. Selecione as entidades que deseja gerar e clique em
Finish.
Todas as entidades e mapeamentos serão gerados automatica-
mente, assim como o arquivo  persistence.xml . Para mapeamentos
mais complexos é provável que algum ajuste manual ainda seja
necessário.

Figura Q2. Adicionado persistência ao projeto.

os JARs do Toplink Essentials e do driver do MySQL. Após a criação


do projeto, deve-se torná-lo “persistível”. Para isso clique com o
botão direito sobre o projeto e selecione  Java Perspective>Add
 Java Persistence. Uma caixa de diálogo semelhante à da Figura Q2
será exibida.
Preencha os dados conforme a figura e clique em  Add Connec-
tions para criar uma nova conexão ao MySQL. A tela de configu-
ração de conexão será exibida ( Figura Q3). Preencha os dados
conforme ilustrado e clique em Finish. Figura Q4. Gerando as entidades.

36  Java Magazine • Edição 39

 jm39.indb 36 15/8/2006 18:17:42


Veiculo veiculo = new Veiculo(“ABC1234”); e facilidades na execução de operações
manager.persist(veiculo); CRUD.. Mas a extensão da JP
CRUD JPAA e o número
de anotações e opções suportadas podem
A exclusão da entidade é feita através do tornar a API muitas vezes difícil de usar;
método remove(): não é fácil, por exemplo, conhecer todas
as anotações e atributos envolvidos em
manager.remove(veiculo); um mapeamento O/R. Além disso, a co- uma questão de tempo até que a JPA esteja
dificação manual de mapeamento (anotar disponível para qualquer aplicação. Agora
Consultas mais complexas são efetuadas entidades e propriedades) é um trabalho é aprofundar os conceitos e estar preparado
através da interface Query. Como visto an- geralmente maçante e propenso a erros. para o futuro quando ele chegar.
teriormente, o EntityManager é responsável Embora seja uma API nova, diversas
por instanciar as consultas, que podem ser ferramentas já oferecem
ofere cem apoio ao desenvol-  jcp.org/aboutJava/communityprocess/ 
 jcp.org/aboutJava/communityprocess/ 
tanto dinâmicas quanto estáticas. A seguin- vimento com JPA, fornecendo comandos final/jsr220
te consulta estática lista todas as reservas para mapeamento automático e recursos Especificação do EJB 3.0 e JPA
existentes em um determinado período: para edição de anotações. Essas ferra-
mentas aumentam a produtividade do  java.sun.com/developer/ 
@NamedQuery(name = “Reserva.listarPorP
“Reserva.listarPorPeriodo”
eriodo”,,
query = “SELECT r FROM Reserva r WHERE “ + desenvolvedor
desenvolved or e diminuem
dimi nuem a possibilidade technicalArticles/J2EE/jpa
“r.inicio >= :inicio AND r.fim <= :fim”) de erros. Introdução à JPA
O NetBeans 5.5, atualmente em beta,
Para exibir as reservas do período basta inclui vários wizards que auxiliam na eclipse.org/dali 
criar a consulta, preencher os parâmetros criação de entidades e na definição do Dali JPA Tools
e executar o método getResultList() . Não é ne- mapeamento e a geração do persisten ce.xml.
 persistence.xml
oracle.com/technology/products/ias/toplink 
cessário efetuar joins com as tabelas VEICULO  Já o Eclipse Dali, um projeto da Fundação
Página do Oracle Toplink
e CLIENTE, pois a JPA já faz as agregações
agregaçõe s auto- Eclipse, adiciona suporte a JP
J PA no Eclipse.
maticamente a partir do mapeamento: Veja um tutorial sobre o Dali no quadro  glassfish.dev.java.net/javaee5/persistence/ 
“Dali JPA Tools”.
Tools”. entity-persistence-support.html 
Query query = manager.createNamedQuery(
  “Reserva.listarPorPeriodo”); Sobre o suporte à persistência no projeto
query.setParameter(“inicio”
query.setP arameter(“inicio”,, formataData(“01/06/2006
formataData(“01/06/2006”));
”)); Conclusões GlassFish
query.setParameter(“fim”,
query.setP arameter(“fim”, formataData(“01/08/2006
formataData(“01/08/2006”));
”)); A Java Persistence API traz diversos bene-
List<Reserva> reservas = query.
query.getResultList();
getResultList(); fícios para o desenvolvedor Java e para os  javamagazine.com.br/downloads/jm39/ 
for (Reserva reserva : reservas) {
System.out.println(“
System.ou t.println(“ “ + reserva); fabricantes de produtos relacionados à per-  jm-jpa.zip
} sistência de objetos. A criação de um
u m padrão
 baseado
 basea do em framew
frameworks
orks líde
líderes
res de merca
mercado
do
A JPA também permite criar consultas incentiva o uso da API e facilita a migração André Dantas Rocha
(ad-rocha@uol.com.br ) é mestre
complexas, incluindo sub-consultas e fun- de softwares existentes. Desenvolvedores
em Engenharia de Software pela
ções agregadas. A consulta Reserva.ultima, com alguma vivência em ferramentas como USP e arquiteto da Fidelity SA.
por exemplo, seleciona a última reserva Hibernate e TopLink
TopLink não
n ão terão dificuldades
cadastrada (a que possui maior código): em utilizar a nova API.
Com um padrão de persistência, a escolha Sérgio Oliveira Kubota
@NamedQuery(name = “Reserva.ultima”,
“Reserva.ultima”,
query = “SELECT r FROM Reserva r WHERE r.codigo = “ + de uma determinada implementação passa ( sergio_kubota.@hotmail.co
 sergio_kubota.@hotmail.comm) é Pós-
“(SELECT MAX(r1.codigo) FROM Reserva r1)”) a ser guiada por critérios como preço, ro- Graduado em Objetos Distribuídos com
 bustez e tradição do fabricante,
fabricante, e não mais Java pela FIAP e atua como arquiteto
 Apoio de ferramentas por operações suportadas por um fabricante de sistemas corporativos baseados nas
É possível perceber que a JP
JPA
A específico
específ ico ou simplicidade de mapeamento tecnologias Java SE e EE, no segmento de inovações
oferece recursos avança- O/R provida por um framework. tecnológicas da Caixa Econômica Federal.
dos de consulta Os servidores de aplicações já iniciaram
a incorporação do novo padrão e é só

Edição 39 •  Java Magazine 37

 jm39.indb 37 15/8/2006 18:17:55


AJAX
AJA X Av
Avanç
ançad
ado
o
Criando componentes e serviços com

H á algum tempo houve uma mudança


significativa na forma como interagi-
mos com as aplicações web. Uma nova
tendência, denominada Web 2.0, tem mudado
nossas concepções sobre o paradigma request/wait/ 
response , usado na maioria dos sites t radicionais .
O conjunto de tecnologias AJAX ( Async  Asynchronous
hronous
 Java Scr ipt and XM L) tornou possível trazer às
Script
aplicações web recursos de aplicações desktop
com que sonhávamos desde nossos
primeiros websites
websites..
Uma grande divulgação
do AJAX aconteceu atra-
vés de aplicações criadas
pelo Google, como Google
Maps e GMail, e a em-
presa empresa vislum-
 brou a necessidade de se
construir um framework
para facilitar o desen-
volvimento de aplicações
com AJAX. Foi assim que
nasceu o Google
Goog le Web Toolkit
Toolkit
(GWT).
Neste artigo abordaremos a
construção
constr ução de uma aplicação AJAX
AJAX que
utiliza diversos componentes perso-
nalizados estendendo a API
do GWT. Veremos como
criar uma tabela pa-
ginada com campos
editáveis e uma cai-
xa de diálogo per-
sonalizada, além
de demonstrar
o gerenciamen-
to de eventos e
chamadas de
serviços para
acesso a da-
dos. Para cada
componente
abordaremos
particularidades
da API e dicas sobre

38  Java Magazine • Edição 39

 jm39.indb 38 15/8/2006 18:18:05


Explorando a API do GWT

com GWT para criar aplicações


AJAX sofisticadas e novos
componentes

o Goo
Google
gle Web Toolkit
ARI DIAS NETO

sua utilização. Para uma introdução ao • A tabela terá campos editáveis, sensí- • br.com.jm.gwt.server   – Onde estarão a
GWT,, sua API básica e a estrutura
GWT estrutu ra utiliza- veis a cliques do mouse e à tecla Enter. implementações dos serviços, como código
da para projetos, recomendo a leitura do • A persistência é feita de forma trans- de acesso a dados e classes de negócio.
artigo “Google Web Toolkit”
Toolkit” na Edição 38 parente: assim que o campo editável não • br.com.jm.gwt.public  – Contendo imagens,
da Java Magazine. possuir mais o foco, os dados serão atua- arquivos CSS, páginas HTML etc.
lizados na base de dados, sem necessidade
Preparação de intervenção do usuário. Note que alguns arquivos também foram
Para a construção do exemplo, vamos • A caixa de diálogo pode ser fechada criados:
utilizar o IDE
I DE Eclipse com dois plug-ins:
plug-ins: tanto com o mouse como com o teclado •

WTP (Web Tools Project) e Googlipse. No (Enter ou Esc). br.com.jm.gwt.public.JMListaContatos.html  –


site do WTP ( eclipse.org/webtools ) existe Arquivo HTML com tags indicando as
uma versão do Eclipse com este plug-in Iniciando o projeto posições onde serão incluídos os com-
incluso. Se você já tem o Eclipse instalado, Começamos criando um projeto GWT. ponentes AJAX, e onde é importada a
pode baixar e instalar
insta lar o WTP pelo próprio No Eclipse (com o WTP e o Googlipse  biblioteca JavaScript do GWT ( gwt.js). Esse
IDE, acessando o site de atual izações do instalados), acione File|New>Project> arquivo, que é chamado na documentação
Callisto (veja um artigo sobre o Callisto Web>Dynamic Web Project . Na primeira do GWT de Host Page
Page, possui também um
na edição anterior). tela (Figura 2) preencha apenas o nome elemento <meta> definindo qual módulo
Tendo instalado o WTP, faça o down- do projeto (“ComponentesGWT”) e clique será carregado.
load do JAR do Googlipse (em  googlipse. em Next.   Marque a op-
com) e coloque-o dentro da pasta eclipse/  ção Googlipse e finalize. O
 plugins. Depois, dentro do Eclipse, abra projeto criado já possuirá Listagem 1.Script para criação do banco de dados.Apenas uma tabela será
o menu Window|Preferences , escolha a as bibliotecas do GWT no necessária.
opção Googlipse  e defina a pasta onde classpath. CREATE TABLE artigogwt.contato (
está instalado o GWT (download em Para começar a cons- contato_id BIGINT NOT NULL AUTO_INCREME NT,
nome VARCHAR(45),
code.google.com/webtoolkit). Com isso sua truir nossos componen- telefone VARCHAR(45),
IDE fica preparada para o desenvolvimen- tes precisamos criar um celular VARCHAR(45),
email VARCHAR(45),
to dos exemplos mostrado neste artigo. GWT Module. Clique com endereco VARCHAR(150)
VARCHAR(150), ,
Como banco de dados usaremos o o botão direito sobre o PRIMARY KEY(contato_ id)
)
MySQL. A Listagem 1  mostra o script nome do projeto, navegue
para a criação da única tabela que preci- até New>Other>Googlipse
samos. Todo o código aqui demonstrado e escolha a opção GWT
está disponível no site da Java Magazine.  M o d u l e . Na próxima
Para facilitar os testes, o download inclui tela configure o campo
também uma classe
clas se que carrega o banco de Location: clique no botão
dados com nomes e endereços aleatórios.
a leatórios. Create, e em Name forneça
br.com.jm.gwt ; depois cli-
Construção de componentes que em Finish. Em Name
O exemplo que construiremos será uma digite “JMListaContatos”
lista de contatos (veja a Figura 1), formada para o nome do módulo.
por uma tabela com paginação e uma área Após finalizar, três paco-
de exibição de fotos. Além disso, criaremos tes serão criados:
uma caixa de diálogo que mostra mais • br.com.jm.gwt.client – Com
informações sobre umu m contato selecionado código Java que será trans-
na tabela. Veja algumas particularidades formado em JavaScript e
do exemplo: executará no browser. Figura 1. Lista de contatos com paginação, DialogBox, campos editáveis e fotografia.

Edição 39 •  Java Magazine 39

 jm39.indb 39 15/8/2006 18:18:12


AJAX Avançado com GWT

• br.com.jm.gwt.client.JMListaContatos. <table> chamado quando houver algum proble-


 java  – Classe responsável por inicializar   <tr> ma no carregamento da imagem, por
<td id=”contatos”></td>
os componentes e posicioná-los dentro <td id=”fotografia” valign=”top”></td> exemplo, quando ela não existir. É nele
do HTML. É chamada pelo GWT de   </tr> que carregamos uma imagem padrão.
Entry Point. </table>  Já o método onLoad()  é chamado quando
br.com.jm.gwt.JMListaContatos.gwt.xml
• a imagem é carregada corretamente; ele
– Documento que define as configurações Com a organização do projeto concluída está vazio porque não precisamos de ne-
do módulo GWT e seus serviços. (veja a Figura 3), podemos passar à cons- nhum tratamento caso a imagem tenha
trução dos nossos componentes. Todos sido carregada com sucesso. Note ainda
Deixe a classe  JMListaContatos  e o arqui- os componentes serão criados dentro do que no construtor é definida a própria
vo  JMLi staC ontatos.g wt.x ml inalterados pacote br.com.jm.gwt.client . instância da classe como listener, com
por enquanto. Mas modifique o arquivo addLoadListener(this) .
 JMListaCo ntatos. html   deixando-o igual Componente Fotografia O componente Fotografia será utilizado em
à Listagem 2 . Este arquivo inclui dois O primeiro componente que vamos criar dois lugares na nossa aplicação. Ao lado
componentes que são referenciados pelos mostra a fotografia do contato seleciona- da tabela de contatos e na caixa de diálogo
seus ids (contatos e fotografia ) dentro de uma do, e estende a classe Image do GWT. Uma mostrada ao se selecionar um contato.
tabela HTML: funcionalidade adicional da nossa classe
Fotografia  (mostrada na Lista- Componente MeuDialogBox
gem 3) é que se não houver A API do GWT inclui o componente
imagem cadastrada para o DialogBox, que permite mostrar janelas
contato, será mostrada uma popup com informações ou avisos. Em
imagem padrão. A classe nosso exemplo vamos criar uma classe que
também demonstra o trata- estende DialogBox , para aproveitar caracte-
mento de eventos na leitura rísticas desse componente, como a exibi-
de imagens. ção de uma área de título, a possibilidade
Observe que o constru- de arrastá-lo e sua característica modal
tor de Fotografia recebe um (enquanto a janela estiver ativa o resta nte
objeto da classe Contato da aplicação fica bloqueado).
(Listagem 4). Na instancia- A Figura 1 mostra como ficará o layout da
ção já é definido o tamanho, nossa caixa de diálogo. Utilizamos um pouco
o estilo CSS e qual a URL da de CSS para ajudar na formatação do título e
imagem. Outro fato impor-  bordas. Colocamos a definição dos estilos no
tante está na implementação arquivo estilosListaContatos.css (Listagem 5),
da interface LoadListener, atra- dentro da pasta br.com.jm.gwt.public. O CSS
Figura 2. Criando de um Dynamic Web Project 
vés dos métodos onError() e é importado pela página  JMListaContatos.
onLoad() . O método onError() é html.
A maioria dos componentes gráficos
(widgets) do GWT possui um estilo CSS
Listagem 2.HTML que define onde ficarão nossos componentes - JMListaContatos.html
pré-definido, ou seja, você precisa ape-
<html> nas defini-lo no seu arquivo CSS, que
<head>
<title>JavaM agazine - Lista de Contatos</ti tle>
o componente já o usará. O DialogBox
<meta name=’gwt:mo dule’ content=’br .com.jm.gwt.J MListaContat os’> utiliza dois estilos, um para a janela e
<link rel=”stylesh eet” href=”estilo sListaContato s.css” type=”text/ css”>
</head> outro para a área de título: gwt-DialogBox e
gwt-DialogBox .Caption.
<body>
<script language=”ja vascript” src=”gwt.js ”></script> A Listagem 6 mostra o código do com-
<h1>Lista de contatos</h1 > ponente MeuDialogBox . No construtor foram
  <table>
  <tr> definidos o título, o tamanho e a locali-
<!-- lista de contatos --> zação da caixa de diálogo. A fotografia,
<td id=”contatos”></td>
<!-- fotografia do contato --> o conteúdo e o botão foram posicionados
<td id=”fotografia” valign=”top”></td>
  </tr>
dentro de um componente DockPanel. Para
  </table> entender mais sobre painéis como este e
</body> outros componentes gráficos do GWT, veja
</html>
o quadro  “Panels e Widgets”.

40  Java Magazine • Edição 39

 jm39.indb 40 15/8/2006 18:18:13


Note também que na criação do botão Observe que um Composite  pode ter é gerenciar os cliques nos botões de pró-
de fechar já instanciamos um ClickListener, outros objetos Composite . Esse é o caso da ximo e anterior, e mostrar a mensagem
que no evento de clique ( onClick()) fechará a ListaContatos   e da TabelaEditavel . A seguir, “Carregando...” enquanto a tabela é popu-
caixa de diálogo através do método hide() , veremos detalhadamente como criar os lada. A barra será exibida acima da lista de
herdado da superclasse. E por último, o três componentes citados. contatos, como já mostrado na Figura 1 e
método onKeyDownPreview(), que trata dos seu layout é definido como a seguir:
eventos do teclado, define que ao pressio- Componente BarraNavegacao
nar Enter ou Esc a caixa de diálogo seja A barra de navegação, cujo código é mos- horizontalPanel.add(mensagemStatus);
horizontalPanel.setCellWidth(mensagemStatus, “100%”);
fechada, também usando hide() . trado na Listagem 7, terá três elementos: horizontalPanel.add(btnAnterior);
dois botões (próximo e anterior) e um horizontalPanel.add(btnProxima);
Trabalhando com Composites Label. Seu construtor configura o posi-
A classe Composite do GWT tem a função cionamento dos elementos dentro de um Para capturar os cliques de próximo
de organizar a construção de compo- HorizontalPanel, e recebe uma fonte de dados. ou anterior, o componente BarraNavegacao
nentes mais complexos. Ela funciona Não se preocupe com a fonte de dados por implementa ClickListener e se registra como
como um “wrapper” de componentes , enquanto; veremos mais sobre ela ao tratar listener no momento da instanciação
escondendo os métodos públicos do dos serviços do GWT. dos botões (passando this como segundo
componente “embrulhado”. Normal- A principal função da barra de navegação parâmetro):
mente configuramos o Composite  com
um  panel (ex. DockPanel ) contendo vários
componentes, pois dessa ma neira é pos-
sível tratá-los como um elemento único.
Criaremos três componentes baseados na
classe Composite :
•BarraNavegacao   – Composto de um
HorizontalPanel, que contém dois Button s e
um Label.
•TabelaEditavel – Composto de um DockPanel
com a barra de navegação no topo e um Grid
centralizado.
•ListaContatos  – Que instancia um compo-
nente TabelaEditavel com uma fonte de dados
e nomes das colunas.

Figura 3. Estrutura de pacotes e arquivos de um projeto GWT

Edição 39 •  Java Magazine 41

 jm39.indb 41 15/8/2006 18:18:15


AJAX Avançado com GWT

Listagem 3.Componente Fotografia – Fotografia.java {


  setText(titulo);
import com.google.gwt.user.client.ui.*;
//posicao do DialogBox
public class Fotografia extends Image implements LoadListener {   setPopupPosition(DESLOCAMENTO_ESQUERDA,
public Fotografia(Contato contato) { DESLOCAMENTO_SUPERIOR);

//configura o endereço da foto   setWidth(TAMANHO_JANELA);


setUrl(“foto s/” + Long.toString (contato.get ContatoId())
+ “.jpg”); //Criando o botão para fechar o DialogBox, e o seu listener
Button btnFechar =
//configurando o tamanho da imagem new Button(“Fechar”, new ClickListener() {
  setWidth(“110”); public void onClick(Widg et sender) {
  setHeight(“110”);   hide();
}
//configurando o estilo/css da imagem });
  setStyleName(“fotografia”);
//cria um DockPanel e define o layout do DialogBox
//configura a própria instância como o loadListener DockPanel panelBase = new DockPanel();
  addLoadListener(this); panelBase.a dd(imagem, DockPanel.WE ST);
} panelBase.add(btnFechar, DockPanel.SOUTH);
panelBase.a dd(new HTML(conteud o), DockPanel.CEN TER);
public void onError(Widge t sender) {
  setUrl(“fotos/foto_padrao.jpg”); //configura a distância entre os componentes deste panel
} panelBase.setSpacing(4);
public void onLoad(Widget sender) {}
} //configura o conteudo do DialogBox como sendo o DockPanel
  add(panelBase);
Listagem 4.Contato.java }

public boolean onKeyDownPrev iew(char key, int modifiers) {


public class Contato implements IsSerializable {
//caso o usuário tecle ESC ou ENTER o DialogBox se fechará
public long contatoId;
public String nome; switch (key) {
case KeyboardListe ner.KEY_ENTE R:
public String telefone;
case KeyboardListe ner.KEY_ESCA PE:
public String celular;
  hide();
public String email;
public String endereco;   break;
}
return true;
public Contato() {}
}
//getters e setters ... }
}
Listagem 7.BarraNavegacao.java
Listagem 5.Arquivo CSS com estilos utilizados pelos componentes GWT – estilosListaContatos.css
public class BarraNavegacao extends Composite
implements ClickListener
.gwt-TextBox {
{
border-width : 0px;
height: 15;
width: 140; public Button btnProxima = new Button(“&gt;”, this);
public Button btnAnterior = new Button(“&lt;”, this);
font-size: 12;
public Label mensagemStatu s = new Label();
}

.gwt-DialogBox { //... outros atributos


border: 2px solid #AAAAAA;
public BarraNavegaca o(FonteDeDad os fdContatos) {
background-c olor: white;
}
this.fdContatos = fdContatos;
.gwt-DialogBox .Caption {
  this.horizontalPanel.add(btnAnterior);
  backgr ound-image: url(‘imagens/ back_degrad. gif’);
background-repeat: repeat-x;   this.horizontalPanel.add(btnProxima);
padding: 4px;   this.btnAnterior.setEnabled(false);
  setWidget(horizontalPanel);
padding-bott om: 8px;
font-weight: bold;
cursor: default; //… definição do layout da barra de navegação
} }

public void onClick(Widget sender) {


.gwt-Button {
border: 1px solid #8E8E8E; if (sender == btnProxima) {
width: 70;   paginaAtual++;
}
}
else if (sender == btnAnterior) {
  paginaAtual--;
Listagem 6.Componente personalizado para o endereço do contato – MeuDialogBox.java }
setStatus(“ Carregando ...”);
public class MeuDialogBox extends DialogBox {   fdContatos.setPagina(paginaAtual);
}
private final static int DESLOCAMENTO _ESQUERDA = 80;
private final static int DESLOCAMENTO _SUPERIOR = public void setStatus(String status) {…}
(Window.getClientHeight() - 256) / 2;
private final static String TAMANHO_JAN ELA = “400px”; public void enable(boolean ultimaPagina) {…}
public MeuDialogBox(Image image, String titulo, }
String conteudo)

42  Java Magazine • Edição 39

 jm39.indb 42 15/8/2006 18:18:15


Listagem 8.TabelaEditavel.java

public class TabelaEditavel extends Composite


implements TableListener
{
public TabelaEdita vel(FonteDeDa dos fonteDeDados,
String[] nomesColunas,int nroRegistrosPagina)
{
//... config. dos atributos com os valores dos parâmetros

// configura esta instância como o Listener do Grid


// para receber informações sobre o clique, linha e coluna
  grid.addTableListener(this);

//adiciona a barra de navegação e o Grid ao DockPanel


panelBase.ad d(barraNavega cao, DockPanel.N ORTH);
panelBase.add(grid, DockPanel.CENTER);

//configura o Composite com o DockPanel


  setWidget(panelBase);

//... métodos para layout – tamanho, cabeçalho, css, etc


}

/* trata os eventos de cliques na tabela */


public void onCellClicked (SourcesTabl eEvents
sender, int row, int cell)
{
//retornar se clique for no cabeçalho
if (row == 0) return;

//se clicado na primeira coluna, mostra a caixa de diálogo


if (cell == 0) {
  mostraDialogBox(row);
  return;
}
//troca o conteúdo da célula por um campo editável
if (cell > 1) {
editaCelula( row, cell, widget);
}
//… definições de layout - destacar linha selecionada
} //ao perder o foco atualiza a base de dados com o novo valor
textBox.addF ocusListener( new FocusListener (){…});
private void mostraDialog Box(int row) { }
Contato contato = getContato(ro w);
Fotografia fotografia = new Fotografia(contato); private void atualizaRegi stro(
int row, int coluna, String conteudoCelul a)
//instancia o DialogBox que criamos {
MeuDialogBox dialogBox = new MeuDialogBox( //substitui o TextBox por um Label
fotografia, “Contato: “ Label label = new Label();
+ contato.getN ome(), “<b>Endereco : </b> <br />” TextBox textBox = (TextBox) grid.getWidg et(row, coluna);
+ contato.getEndereco());   label.setText(textBox.getText());
grid.setWidget(row, coluna, label);
  dialogBox.show();
} //busca o registro alterado
Contato contato = getContato(row);
private void editaCelula( final int row,
final int cell, Widget labelCelula) // como o GWT não possui suporte para reflection,
{ // usamos um switch
//troca o conteúdo da célula - de Label para TextBox switch (coluna) {
final TextBox textBox = new TextBox(); case 2:
textBox.setT ext(((Label) labelCelula) .getText());   contato.setTelefone(conteudoCelula);
grid.setWidget(row, cell, textBox);   break;
case 3:
//ao pressionar Enter atualiza o registro com a alteração   contato.setCelular(conteudoCelula);
textBox.addK eyboardListen er(new KeyboardList ener() {   break;
public void onKeyDown(Wid get sender, case 4:
char keyCode, int modifiers) {}   contato.setEmail(conteudoCelula);
public void onKeyPress(Widget sender,   break;
char keyCode, int modifiers) {} }

public void onKeyUp(Widge t sender, //atualiza base de dados


char keyCode, int modifiers)   fontDeDados.updateContato(contato);
{ }
switch (keyCode) {
case KeyboardListener.KEY_ENTER: private void mostraFoto(i nt row) {…}
atualizaRegistro(row, cell, textBox.getText()); public void atualizaDados (List contatos) {…}
  break; private void configuraGri dLayout() {…}
} private Contato getContato(in t row) {…}
} private void marcarLinha( int row) {…}
}); }

Edição 39 •  Java Magazine 43

 jm39.indb 43 15/8/2006 18:18:20


AJAX Avançado com GWT

public Button btnProxima = new Button(“&gt; ”, this); correto do objeto Contato e persiste a altera- de explicarmos seu código, veremos uma
public Button btnAnterior = new Button(“&l t;”, this); ção através da fonte de dados.  breve introdução sobre esses serviços.
Além disso, para saber qual registro foi O GWT já traz pronta uma infra-estru-
Assim que um dos botões é clicado, é selecionado, é mantido um mapeamento tura para trabalhar com RPCs ( Remote
chamado o método onClick(). Em seqü- interno entre as linhas da grade e os con- Procedure Calls ) facilitando a troca de
ência, o componente atualiza o atributo tatos exibidos. Assim, quando uma linha objetos Java entre o cliente (o browser)
paginaAtual, muda a mensagem do Label é selecionada, o objeto Contato correspon- e o servidor. Todas as chamadas são
para “Carregando...”, e chama o método dente também o é. assíncronas, logo a interface gráfica fica
setPagina() da fonte de dados.
Veja que a barra de navegação não possui Componente ListaContatos
referência ao componente TabelaEditavel , O componente ListaContatos , com código
interagindo apenas com a fonte de dados. mostrado na Listagem 9, reúne uma fonte
É a fonte de dados que é responsável por de dados (objeto FonteDeDados ) e um com-
atualizar os objetos conectados a ela. ponente TabelaEditavel. A fonte de dados
recebe o número de registros por página,
Componente TabelaEditavel e a tabela editável recebe os nomes das
O componente TabelaEditavel  é o mais colunas, o número de registros por página
complexo que criaremos neste artigo. e a própria fonte de dados.
Veja seu código na Listagem 8. A estru-
tura interna do componente é construída Serviços no GWT
dentro de um DockPanel, com um compo- Para fazer acesso aos dados dos contatos
nente BarraNavegacao  na posição NORTH, e um no banco, utilizaremos a classe FonteDeDados.
Grid (da API do GWT) no centro, onde serão Esta classe faz uso da infra-estrutura de
exibidos os dados dos contatos. serviços remotos do GWT, assim antes Figura 4. Configurando o serviço remoto
O construtor instancia a barra de navega-
ção já com uma fonte de dados, guarda o Listagem 9.ListaContatos.java: componente que reúne a tabela editável e a fonte de dados
número de registros por página, configura
public class ListaContatos extends Composite {
a própria instância como o listener do Grid
(para capturar os cliques sobre ele), e de- private TabelaEditavel tabelaEditavel;
private FonteDeDados fdContatos;
termina o layout dos componentes dentro
do DockPanel. public ListaContatos Widget(int nroRegistros Pagina) {
String[] nomesColuna s = new String[] { “”, “Nome”, “Telefone”, “Celular”,
O método onCellClicked() é chamado quan- “E-mail” };
do algum evento de clique ocorre na gra- this.fdConta tos = new FonteDeDados( nroRegistros Pagina);
this.tabelaE ditavel = new TabelaEditave l(fdContatos , nomesColunas,
de. Caso o clique tenha sido na primeira   nroRegistrosPagina);
coluna, é mostrada uma caixa de diálogo   setWidget(tabelaEditavel);
}
com o endereço do contato. Um clique nas }
outras colunas tornará a célula correspon-
dente editável. Listagem 10.ContatosService.java: interface de serviço
O método mostraDialogBox()   instancia a package br.com.jm.gwt.client.service;
caixa de dialogo já com uma fotografia
e o endereço do contato configurados. //… imports

A caixa de diálogo é exibida através do public interface ContatosService extends RemoteService {


//métodos de serviços
método show() . E editaCelula() substitui public List getContatos( int posicaoInicia l, int nroRegistros Pagina)
o Label  na célula clicada por um TextBox throws Exception;
public void updateContat o(Contato contato) throws Exception;
(da API do GWT) para edição do valor.
Este TextBox  terá dois listeners. Um para public static final String ENTRY_POINT = “/contatosService”;
eventos de foco, FocusListener, e outro para public static class Util {
eventos de teclado, KeyboardListener . Quan- public static ContatosServ iceAsync getInstance( ) {
ContatosServiceAsync instance = (ContatosServiceAsync) GWT
do o TextBox  perder o foco ou o usuário   .create(ContatosService.class);
pressionar Enter, será chamado o método ServiceDefTa rget target = (ServiceDefTa rget) instance;
  target.setServiceEntryPoint(ENTRY_POINT);
atualizaRegistro() para persistir no banco de return instance;
dados a alteração efetuada. }
}

Por fim, o método atualizaRegistro() substi- }


tui o TextBox por um Label, atualiza o campo

44  Java Magazine • Edição 39

 jm39.indb 44 15/8/2006 18:18:21


Panels e Widgets

N este artigo utilizamos vários painéis ( panels)


e componentes gráficos (widgets ) da API do
GWT. Aqui apresentamos uma breve explicação
seguir um pouco sobre os componentes utiliza-
dos neste artigo.

sobre cada um deles. TextoBox 


Campo simples para entrada de texto. Seu
estilo CSS padrão é: gwt-TextBox . Principais
Panels
métodos:
Os  panels são “recipientes” de componentes
• getText() : retorna o texto digitado no cam-
gráficos utilizados para facilitar o posiciona-
po;
mento e o agrupamento. Utilizamos três tipos
• getSelectedText(): retorna o texto selecionado
de painéis no exemplo: HorizonalPanel , DockPanel
dentro do campo;
e RootPanel .
• selectAll() : seleciona todo o texto dentro do
HorizontalPanel  campo;
Este tipo de painel organiza seus componentes • setText(String): configura o valor do campo; • setHTML(int linha, int coluna, String str) : adiciona
da esquerda para direita em uma mesma linha, • addChangeListener() , addClickListener() , um código HTML em uma determinada célula;
e o utilizamos para organizar os Button s e o Label addKeyboardListener() : adicionam listeners para • getWidget() , getText() , getHTML() : retornam o
de mensagem na barra de navegação. mudança do conteúdo, eventos de mouse e widget , a string ou código HTML de uma deter-
teclado, respectivamente. minada célula;
DockPanel  • addTableListener(): adiciona um listener para
O DockPanel  possiciona os seus elementos Button eventos de clique sobre a tabela.
seguindo a numeração da Figura Q1 (extraída Um botão HTML normal. Seu estilo CSS padrã o
da documentação do GWT). Após colocar um é: gwt-Button . Principais métodos: DialogBox 
widget na posição central nenhum outro wid- • click() : equivalente a clicar no botão; Caixa de diálogo que possui área de título e a
get deve ser adicionado ao DockPanel . Se não • addClickListener(), addKeyboardListener(): adi- possibilidade de arrastá-la. O DialogBox utiliza dois
for indicado o posicionamento ao adicionar os ciona listeners para eventos de clique e de estilos, um para a janela e outro para a área de
componentes, o último a ser adicionado ocupa- teclado. título: gwt-DialogBox e gwt-DialogBox .Caption .
rá a posição central. No exemplo, utilizamos o   No nosso exemplo, estendemos este com-
DockPanel para fazer o layout da caixa de diálogo Image ponente para customizá-lo conforme nossas
e da tabela editável. Componente para exibição de imagens a necessidades de layout, e para tratar eventos de
partir de uma dada URL. Seu estilo padrão é: teclado. Principais métodos:
RootPanel  gwt-Image{} . Principais métodos: • setText() : configura o título da caixa de di-
O RootPanel  é o último painel no qual um com- • setUrl() : configura a URL da imagem a ser álogo.
ponente deve ser adicionado. Sua localização é exibida; • setHTML(): configura o conteúdo da caixa de
definida dentro do HTML principal ( Host Page ) , • addClickListener() , addLoadListener() , diálogo com um código HTML.
normalmente utilizando-se um id. O RootPanel addMouseListener() : adiciona listeners para eventos • addWidget() : configura o conteúdo da caixa
é instanciado pelo GWT e obtido através de dois de clique, carregamento de imagem e mouse. com um widget.
métodos estáticos:
• RootPanel.get() : retorna o RootPanel   padrão Grid 
da aplicação. Tabela redimensionável que pode north (0)
• RootPanel.get(String id) : retorna o RootPanel  as- conter texto, html ou widgets. Não
sociado a um elemento/id definido no HTML. possui estilo padrão. Principais mé-
todos:
O RootPanel  não possui um layout definido. • resize(int, int) : redimensiona a west west center east
Sua função é apenas receber componentes e tabela; (1) (2) (5)  (3)
exibi-los. •

setWidget(int linha, int coluna, Widget w) :


Widgets adiciona um widget  em uma determi-
A API do GWT fornece componentes gráficos nada célula; south (4)
(widgets ) para vários fins, por exemplo campos • setText(int linha, int coluna, String str) :
de texto, checkboxes, tabelas, menus, janelas adiciona uma String em uma determina Figura Q1. Ordem de posicionamento de componentes em um
popup, árvores de navegação e grades. Veja a célula; DockPanel 

Edição 39 •  Java Magazine 45

 jm39.indb 45 15/8/2006 18:18:25


AJAX Avançado com GWT

Complete
a sua     1
   o
    2
   o
    3
   o

coleção!
    ã     ã     ã
   ç    ç    ç
    i     i     i
    d     d     d
    E     E     E

- Ferramentas livres - Multimídia no celular - Dados em J2ME


- Introdução ao J2EE 1.4 - Automação com Ant - JavaServer Faces
- Introdução a J2ME - Robocode - Jogos wireless
- J2EE Fundamental - Tag Libraries JSP - Certificação J2EE
- Dados com JDBC - Processando XML em Java - Montando um ambiente Java

    0     1     2     3


    9     1     1     1     1
   o    o    o    o    o
    ã     ã     ã     ã     ã
   ç    ç    ç    ç    ç
    i     i     i     i     i
    d     d     d     d     d
    E     E     E     E     E

- Java no Governo - Códigos no Eclipse - Tutorial de NetBeans - Eclipse para Web - Relatórios Corporativos
- Apache FOP - New I/O Fundamental - API New I/O (java.nio) - Fome Zero com Java - Gráficos com Java 2D
- JSTL – Guia Completo - Game API - Cesta de compras com Struts - Tags Customizadas em JSP 2.0 - Java.net na Prática
- Cocoon Inicial - Criando Plug-ins para Eclipse - Testes de carga com JMeter - Tiger: A Evolução do Java - Raio-X do Tiger
- Pacotes WAR e JAR - Preferences API - Concorrência e a JVM - Dicas para Web - Paginação na Web

    


    

    




  
    

  
  



  


   
 
    


 
    9
    1
   o
    ã
   ç
    0
    2
   o
    ã
    1
    2
   o 
    ã  
   ç  
    2
    2
   o
    ã
   ç
    3
    2
   o
    ã
   ç
    i    ç     i     
    i     i
    d     i  
    d  

     d     d
    E     d     E     E     E
    E

- JSTL aplicado no Tomcat 5 - Relatórios avançados - Criptografia aplicada - O Projeto Eclipse


- Threads no Java 5.0
- Modularizando páginas com Tiles - Mais design patterns - XML de alto desempenho - Segurança no JBoss
- Cadastros com Struts
- Componentes View do Struts - Gerenciamento com JMX - Segurança em apllicações web - JSF Avançado
- MVC na web
- O rugido do Java livre - Java Web Start - JSF Passo a Passo - Começando com Java
- Servlet API Avançada
- Padrões de projeto - Dúvidas de classpath - Datas e Horas em Java - Tira-dúvidas

    9     0     1     2     3


    2     3     3     3     3
   o    o    o    o    o
    ã     ã     ã     ã     ã
   ç    ç    ç    ç    ç
    i     i     i     i     i
    d     d     d     d     d
    E     E     E     E     E

- JavaMail - HSQLDB - SWT no Eclipse - NetBeans 5.0 - Hibernate em aplicações web


- Por Dentro do Apache Derby - Internacionalização de MIDlets - Eclipse Web Tools Project - Aprendendo Groovy - Java 6 (Mustang)
- Clusters com Tomcat e Apache - Performance na JVM - Validação avançada com Struts - Test-Driven Development - Programação em par
- Mais HttpClient - Caso de sucesso: Procon - Fronteiras do Java - Debate Internacional Sou+Java - Processos ágeis
- Examinando o Mustang - Benchmarks JME - CD do NetBeans - Java Business Integration (parte 1) - Java Business Integration (parte 2)

46  Java Magazine • Edição 39


www.javamagazine.com.br devmedia.com
 jm39.indb 46 15/8/2006 18:18:38
    4     5     6     7     8
   o    o    o    o    o
    ã     ã     ã     ã     ã
   ç    ç    ç    ç    ç
    i     i     i     i     i
    d     d     d     d     d
    E     E     E     E     E

- Eclipse inicial - JBoss Inicial - Introdução ao Tomcat - Bancos de dados livres - JavaOne 2003
- O mercado J2ME - Introdução ao JMX - Conectivade com MIDP - Testes unitários com JUnit - Conhecendo o CVS
- Segurança em aplicações web - Java no Lego Mindstorms - Struts, primeiros passos - JSTL- Guia Completo: tags Core - JSTL- Guia completo SQL e Format
- Interfaces ricas com Flash - Logging - Automação com XDoclet - Java na Droga Raia - Tomcat e o Servidor Apache
- Expressões regulares no J2SE 1.4 - Memória e desempenho - Jakarta Velocity - Validação na Web

    4     5     6     7     8


    1     1     1     1     1
   o    o    o    o    o
    ã     ã     ã     ã     ã
   ç    ç    ç    ç    ç
    i     i     i     i     i
    d     d     d     d     d
    E     E     E     E     E

- Genéricos no Tiger - SwingWT - JSP 2 e Servlets no Tomcat 5


- Formulários com Swing - Otimização de EJBs no JBoss
- JBuilder para web - Java 2D: Animação e impressão - Primeiros passos com wireless
- ANT - Automatizando Java - Processamento de imagens
- MIDP 2.0 - Anotações no Java 5 - Collections avançado
- JBoss e Entity Beans - Programação com regras
- JavaOne 2004 - Projeto Looking Glass - Conhecendo o JDeveloper 10g
- Extreme Programming - Jakarta Taglibs
- Segurança com JAAS - Java 2D: Animações e Impressão - Servlets: do básico ao avançado
- Metaprogramação e Reflection - Case J2ME

              Java 5 Impressão  Java Content Repository Caches e JDBC NetBeans  JavaOne
                     
• • • •
       •

      
  Imprimindo com Java
       
 ConheçaduasAPIsdeimpressãodo
 J2SEeobtenhacontrolefino
sobrelayouteformatação
 
  
  
   
Gerência deConteúdo
Edição24-AnoIII-R$9,90 DetalhessobreaNovíssimaAPI
 JCRea CriaçãodeRepositórios
  deConteúdonaWeb
  
 
Uma Aplicação Java Tira-Dúvidas Especial
 CacheseDAOscomJDBC, Usando
  Completa com NetBeans
      Partefinal–Acessoa Banco
 JVMsAlternativas,Relatóriose
DriblandoRestrições
      deDadoseGerênciade
    PreferênciasdeUsuários
daMáquina


 Java:

  Futuro
Presente

    4
    5     6     7
&     8
    2
    2     2     2  JavaOne 2005     2
   o Grandesnovidadesem
    ã    o    o    o produtoseAPIs,planose Migrando para o Java 5    o
   ç             ã     ã     ã comemoraçõesno evento Conheça na prática eem detalhes     ã
    i           ç    ç    ç mundialda tecnologia Java como,porquee quando fazero    ç
    i     i     i     i
    d           upgradepara o J2SE 5.0
    E     d     d     d     d
    E     E     E     E

- Novo NetBeans - Aplicação Completa - Parte 1 - Aplicação Completa - Parte 2 - Aplicação Completa - Parte 3 - Hibernate Fundamental
- Máquinas virtuais alternativas - Desempenho com JDBC e DAOs - Mais Desempenho com JDBC - Migrando para o Java 5 - Apache Geronimo na Web
- Gráficos com JFreeChart - Portlets - Fundamentos - Portlets - Recursos Avançados - Impress ão com Java - Ajax: Interatividade Turbinada
- SuperWaba Inicial - JFreeChart Avançado - Jakarta Commons Inicial - Gerenciamento de Conteúdo - Tutorial de Genéricos
- Tag Files do JSP 2.0 - O Novo Extreme P rogramming - Números: Conceitos e Formatação - JavaOne 2005 - De Volta aos Patterns

    4
    3
    5
    3
    6
    3
    7
    3
Leia uma vez,
   o
    ã
   ç
    i
   o
    ã
   ç
    i
   o
    ã
   ç
    i
   o
    ã
   ç
    i
use em todos
    d
    E
    d
    E
    d
    E
    d
    E os lugares
- Para onde vai o Struts - Frameworks de Logging - Conhecendo o Ant - Criação de Plug-ins
- Teste com J2MEUnit - Otimização de Código - Acessando Código Nativo - Os 10 Mais do Eclipse 3.2
- Web Services - Ajuda com JavaHelp - Qualidade Aplicada - JavaOne 2006
- Scripting na JVM - Maven 2 Essencial - Migrando para o Maven 2 - Relatórios Passo a Passo
- Aspectos no Mundo Real - Java ME no Eclipse - Logging no Java SE - Testes com mock objects

Edição 39 •  Java Magazine 47


.br/anteriores 21 2283 9012
 jm39.indb 47 15/8/2006 18:18:46
AJAX Avançado com GWT

livre para eventuais mudanças enquanto Para criar um serviço remoto no GWT te- da API do GWT.
os dados não chegam. Isso aumenta a mos que definir uma interface de serviço, Outra interface também deve ser criada: a
performance da aplicação e faz com que na qual são escritos os métodos a serem interface assíncrona. Seus métodos serão os
se consiga paralelismo mesmo sem usar expostos. A implementação dos métodos mesmos da interface de serviço, porém terão
múltiplos threads – já que uma parte do fica a cargo de uma classe que será exe- um parâmetro a mais: um objeto de callback,
código é executada no servidor e a outra, cutada no servidor como um servlet. Esta que receberá o retorno do serviço assim que
que trata dos eventos da interface gráfica, classe além de implementar a interface de ele for processado. Outra diferença é que
executa no cliente. serviço estende a classe RemoteServiceServlet todos os seus métodos retornam void.

Listagem 11.ContatosServiceAsync.java: interface assíncrona Listagem 13.JMListaContatos.gwt.xml: arquivo de configuração do módulo GWT
<module>
package br.com.jm.gwt.client.service; <inherits name=’com.google.gwt.user.User’ />
//… imports <!-- Especifica o Entry Point do módulo -->
<entry-poin t class=’br.com .jm.gwt.clie nt.JMListaCon tatos’ />
public interface ContatosServiceAsync {
public void getContatos(int posicaoInicial, <!-- Serviço criado para acesso a dados -->
int nroRegistrosPagina, AsyncCallback callback); <servlet path=’/contat osService’
public void updateContato (Contato contato, class=’br.c om.jm.gwt.ser ver.Contatos ServiceImpl’ />
AsyncCallback callback); </module>
}
Listagem 14.FonteDeDados.java
Listagem 12.Implementa os serviços de acesso a dados – ContatosServiceImpl.java
public class FonteDeDados {
private int nroRegistrosPagina;
package  br.com.jm.gwt.server; private TabelaEditavel tabelaEditabel;
//… imports
public FonteDeDados( int nroRegistros ) {
public class ContatosServiceImpl extends RemoteServiceServlet this.nroReg istrosPagina = nroRegistros ;
implements   setPagina(0);
  ContatosService }
{ public void setPagina(in t pagina) {
ContatosServiceAsync service =
public ContatosSer viceImpl() {}
ContatosService.Util.getInstance();
service.getContatos(pagina * nroRegistrosPagina,
public List getContatos(int posicaoInicial, nroRegistrosPagina, new ContatosCallBack());
int nroRegistrosPagina) throws Exception }
{ public void setTabelaEdi tabel(Tabela Editavel tabelaEditab el) {
List temp = new ArrayList(); this.tabela Editabel = tabelaEditab el;
}
String sql =
“select contato_id, nome, telefone, celular, email,” public void updateContat o(Contato contato) {
+ “endereco from contato limit “ ContatosServiceAsync service =
+ posicaoInicial + “,” + nroRegistrosPagina); ContatosService.Util.getInstance();
Connection conn = getConnection (); service.updateContato(contato, new UpdateCallBack());
Statement stmt = conn.createStatement(); }
ResultSet rs = stmt.executeQuery(sql);
private class ContatosCallBack implements AsyncCallback {
public void onFailure(Th rowable caught) {
while (rs.next()) {
  Window.alert(caught.getMessage());
  temp.add(buildContato(rs)); }
} public void onSuccess(Object result) {
return temp; tabelaEdita bel.atualizaD ados((List) result);
} }
}
public void updateContato (Contato contato) throws Exception {
String sql = “update contato set nome = ?, private class UpdateCallBack implements AsyncCallback {
telefone = ?, celular = ?, public void onFailure(Th rowable caught) {
email = ? where contato_id = ?”; Window.aler t(“Erro ao atualizar o banco de dados”);
Connection conn = getConnection (); }
PreparedStat ement stmt = conn.prepare Statement(sql ); public void onSuccess(Object result) {}
stmt.setStri ng(1, contato.getNo me());
stmt.setStri ng(2, contato.getTe lefone()); }
stmt.setStri ng(3, contato.getCe lular()); }
stmt.setStri ng(4, contato.getEm ail());
stmt.setLong (5, contato.getCo ntatoId()); Listagem 15.Inici aliza os componentes do módulo – JMListaContatos.java
  stmt.execute();
}
public class JMListaContatos implements EntryPoint {
private Contato buildContato( ResultSet rs)
throws Exception {...} public void onModuleLoad() {
public Connection getConnection () //lista os contatos com 15 registros por página
throws ClassNotFoundException, SQLException {...} RootPanel.g et(“contatos” ).add(new ListaContatos (15));
} }
}

48  Java Magazine • Edição 39

 jm39.indb 48 15/8/2006 18:18:46


Criando nosso serviço criados ao arquivo de configuração do de configurações escolha a opção GWT
Para criar nosso serviço utilizaremos o nosso módulo, br.com.jm.JMListaContatos.  Application  e crie uma nova configuração.
plug-in Googlipse que facilita a criação  gwt.xml . Deixe esse arquivo como na Preencha a tela como a Figura 5 e clique
e sincroniza os métodos entre a interface Listagem 13. em Run. Sua aplicação será exibida e de-
de serviço e a interface assíncrona. Clique verá estar semelhante à Figura 1.
com o botão direito sobre o nome do pro- Montando o quebra-cabeça
 jeto, siga New>Other>Googlipse  e escolha Com os serviços e os componentes Conclusões
GWT Remote Service. No campo  Module, prontos, vamos agora uni-los. Chegou O GWT é uma excelente opção para
clique em Browse, navegue até o pacote a hora de falar da fonte de dados, cujo tornar o desenvolvimento web mais
br.com.jm.gwt   e escolha o XML do nosso código é mostrado na Listagem 14. A produtivo na criação de aplicações AJAX.
módulo; em Service Name, forneça Lista- classe FonteDeDados   é bem simples; ela Alguns sites já se especializara m em com-
ContatosService, digite contatosService em contém duas inner classes, ContatosCallBack ponentes para GWT, o que pode salvar
Service URI  (veja Figura 4), e finalize. e UpdateCallBack . Ambas possuem dois mé- muito tempo de desenvolvimento (veja
Serão geradas a interface de serviço todos: onFailure() , para tratamento de erros links). Há inclusive IDEs que já trazem
ContatosService, a interface assíncrona na chamada do serviço, e  onSuccess() , para suporte nativo ao GWT, como o IntelliJ e
ContatosServiceAsync e a classe de implemen- receber o retorno. o VistaFei (este último já possui suporte
tação ContatosServiceImpl . A classe ContatosCallBack  será usada para a “arrastar-e-soltar” para os widgets).
Definiremos na nossa interface de receber o resultado da pesquisa de conta-
serviço dois métodos: getContatos() , para tos e configurar a tabela com os dados re-
 buscar a lista de contatos; e updateContato() , tornados. A segunda não possui nenhuma  google.com/options
para atualizar o contato que foi editado. implementação específica pois o serviço Links para os produtos do Google, como Google
Veja as mudanças na Listagem 10. Ao se trata de um update, não havendo neste Maps.
salvar a interface alterada, você verá que o exemplo retorno do servidor. Em ambas www.googlipse.com
Googlipse atualiza a interface assíncrona tratamos os erros de maneira simples, Plugin do Eclipse para o GWT.
criando dois métodos semelhantes aos de monstrando um PopUp com uma mensa-
ContatosService (veja a Listagem 11). gem. Os métodos updateContato()  e setPagina() eclipse.org/webtools
Na classe ContatosServiceImpl  implemen- fazem as chamadas de serviços. Plugin do Eclipse para desenvolvimento Web.
tamos os métodos de serviço definidos Vamos voltar agora à classe  JMListaContatos ,  intellij.net 
em ContatosService , como mostrado na a classe que é o “ponto de entrada” (En- IDE para desenvolvimento Java com suporte
Listagem 12. Observe que fizemos acesso try Point) do módulo GWT. Altere esta nativo ao GWT.
direto ao banco de dados; em uma apli- classe e deixe-a como na Listagem 15.
cação real seria recomendado o uso do Em seu método onModuleLoad()  buscamos www.wirelexsoft.com/VistaFei.html 
pattern DAO (por exemplo). um RootPanel com id “contatos”. Lembre IDE com desenvolvimento “arrastar-e-soltar” para
Devemos também adicionar os serviços que definimos este mesmo id no arquivo o GWT.
 JMListaContatos.html eé  gwtpowered.org
 justamente lá que colo- Site com tutoriais, artigos e links para download
caremos nossa lista de de componentes GWT.
contatos. Então adicio-
namos um componente  java.sun.com/blueprints/corej2eepatterns/ 
ListaContatos ao RootPanel. Patterns/DataAccessObject.html 
Com isso, assim que Artigo sobre o design pattern Data Access Object
o arquivo HTML for (DAO)
exibido, o GWT iniciará
 javamagazine.com.br/downloads/jm39/
o módulo e carregará  jm-componentesgwt.zip
nossa lista de contatos.

Visualizando Ari Dias Neto


o exemplo (aridiasneto@gmail.com) é
Para visualizar a consultor Java/J2EE com expe-
aplicação de exemplo, riência de mais de sete anos em
abra o menu de confi- tecnogias para web. Atualmente atua
em projeto internacional para IBM. Certificados: SCJP,
gurações de execução
SCWCD, EA(I) e Borland CaliberRM Certified.
Figura 5. Criando uma configuração para executar o exemplo (Run|Run). Na árvore

Edição 39 •  Java Magazine 49

 jm39.indb 49 15/8/2006 18:18:49


Interfaces Gráficas co
Parte 2: Presentation Model e a API Binding

N a primeira parte desta


série apresentamos técni-
cas básicas para um bom
projeto visual de interfaces grá-
ficas (GUIs), discutindo questões
de organização e posicionamento
de elementos que compõem as te-
las. Criamos também um exemplo
demonstrando como a API Forms do
 JGoodies pode ajudar na construção
de GUIs que utilizam componentes
Swing.
Nesta segunda e última parte,
iremos nos aprofundar no código
que está por trás da interface e
que se comunica com o núcleo
do software. Nossa discussão
começa tratando de algumas
questões que envolvem pro-
 blem as e ne ce ss idades do
desenvolvimento de GUIs.
Apresentamos o pattern Pre-
sentation Model, como uma das
estratégias mais eficazes atual-
mente para o desenvolvimento de
aplicações desktop, e vemos seu fun-
cionamento detalhado e a con-
tribuição trazida pela API
Binding do JGoodies.
Ao longo de todo
o processo,

50  Java Magazine • Edição 39

 jm39.indb 50 15/8/2006 18:18:56


Descubra como aplicar o

m Qualidade padrão Presentation Model


e a API Binding do JGoodies
para construir GUIs de
alta produtividade que
do JGoodies favorecem os testes unitários

HUGO VIDAL TEIXEIRA

construímos um exemplo que apresenta usuário com a GUI, reproduzindo cliques  Model – não possui uma referência para a
diversas características desejadas em do mouse e eventos de teclado 1. classe View. Ocorre exatamente o contrário:
aplicações modernas. Mas será que a separação de interesses é a classe View que guarda uma referência
não poderia ajudar nesses testes, já que para a classe Presentation Model. Isso é extre-
Patterns e testes em GUIs a lógica está separada dos componentes mamente valioso na criação de testes.
A alta produtividade no desenvolvi- gráficos? Para responder essa pergunta,
mento de GUIs em aplicações desktop vamos examinar a Figura 1, que apresenta Estrutura e classes
pode ser alcançada com a ajuda de dois os três principais patterns para o desenvol- Quando aplicamos o pattern Presenta-
elementos importantes no nível do código: vimento de GUIs em aplicações desktop. tion  Model  no desenvolvimento de uma
a separação de interesses e a eficácia de Observe a estrutura do  Mod el-Vie w- GUI, precisamos criar basicamente três
testes unitários. Presenter (MVP) que é uma pequena varia- classes:
ção do MVC. O importante a ser observado • View – Classe simples que contém os
 Separação de interesses aqui é o fato de que tanto no MVC quanto componentes gráficos da GUI. Os dados
A separação de interesses, no nosso caso, no MVP, o controlador ( Controller/Presenter) apresentados na tela refletem o estado
significa separar o estado e a lógica da GUI, possui uma referência para a visão ( View). mantido pela classe Presentation Model.
dos componentes gráficos utilizados. Você Assim, para instanciar um controlador • Presentation Model  – Classe que con-
provavelmente já deve ter ouvido falar em dentro de um teste unitário, precisamos tém o estado e a lógica da GUI. Os dados
patterns que fazem essa separação, como da visão com seus componentes gráficos e variáveis são manipulados em memória,
o  Model-View-Controller (MVC), que vem também. Isso faz com que o desenvolvi- independentemente da existência de com-
sendo usado tanto no desenvolvimento de mento de testes unitários ainda continue ponentes e formas de apresentação.
aplicações desktop quanto web.  bastante difícil. • Service Layer   – Classe que oferece à
Embora a separação de interesses traga Existe uma solução para contornar esse classe Presentation Model uma comunica-
muitas vantagens ao desenvolvimento, problema: adicionar uma interface entre o ção com o mundo exterior, onde serviços
ela exige código específico para sincro- controlador e a visão. Mas não vale a pena externos podem existir (como EJBs, bancos
nizar as variáveis que guardam o estado explorar essa alternativa quando temos de dados etc.)
da GUI com os componentes gráficos. uma terceira opção como o Presentation
Normalmente as telas recebem objetos  Model, descrito a seguir. A separação em classes com diferen-
de negócio (JavaBeans, por exemplo) do tes responsabilidades traz vantagens
backend e precisam transferir seus dados O pattern Presentation Model
para os componentes gráficos, e vice-versa. O pattern Presentation Model, também 1
 Um dos fatores que torna isso muito difícil é a exis-
Para que essa conexão entre os dois lados ilustrado na Figura 1, é bastante diferente tência de somente uma thread para executar o código do
teste e o código que renderiza os componentes na tela. A
possa ser programada sem muito esforço, dos outros dois porque quem detém a lógica thread única é um requisito imutável de APIs como Swing
precisamos da ajuda de um framework e o estado da GUI – a classe Presentation e AWT.
genérico que interligue objetos de
negócio e componentes gráficos. Model-View-Controller Model-View-Presenter Presentation Model
Esse é papel da API Binding do
Model Model View
 JGoodies, que estudaremos nesse
artigo.

Eficácia de testes unitários View Controller View Presenter Presentation Model


Outro problema que atinge o
desenvolvimento de GUIs é a
 Referêcia
dificuldade de se construir testes  Notificação por Eventos Service Layer
unitários abrangentes e eficazes,
que simulem as interações do Figura 1. Estrutura dos principais patterns para GUIs desktop

Edição 39 •  Java Magazine 51

 jm39.indb 51 15/8/2006 18:19:00


Interfaces Gráficas com Qualidade – Parte 2

importantes para a implementação e a  JRadioButton, JPasswordField e JFormattedTextField. que guarda um valor booleano, mas não a
manutenção da GUI. Quando trabalha- A variável que pode ser conectada a eles é um que contém um String, por exemplo. Se
mos assim, as classes ficam mais “finas” uma implementação da interface ValueModel o ValueModel receber um valor incompatível
e consequentemente mais simples, o que (pacote com.jgoodies.binding.value ), que arma- com o valor esperado pelos componentes
facilita o entendimento do código e sua zena um valor e o mantém sincronizado conectados a ele, uma exceção será lançada
manutenção. com o que é apresentado pelo compo- durante a tentativa de conversão.
Na verdade, essas vantagens também nente. Ao mudar o valor do ValueModel, o A outra questão é a emissão de eventos
existem no MVC e no MVP. A grande componente refletirá essa mudança. Da PropertyChangeEvent, que acontece quan-
vantagem no Presentation Model é que não mesma forma, se o usuário editar esse do é alterado o valor guardado pelo
precisamos da classe View  para testar a valor pela GUI, o ValueModel   receberá o ValueModel . Isso permite que o valor seja
lógica da GUI. Isso permite a criação de valor fornecido. “observado” com PropertyChangeListeners.
testes unitários simples, eficientes e sem Existem duas formas de conec- Demonstraremos esse recurso na prática
a presença de componentes gráficos para tar um ValueModel  a um componente. mais adiante.
atrapalhar. Uma opção é utilizar o método está-
tico bind () da classe Bindings  (do pacote Conectando componentes a listas de objetos
A API JGoodies Binding com.jgoodies.binding.adapter ): Os componentes que podem ser conec-
A API Binding do JGoodies oferece um tados a listas de objetos são  JComboBox, JList
conjunto de classes que simplifica muito JTextField textField = new JTextField(); e  JTable. A lista de objetos, por sua vez, é
ValueModel valueModel = new ValueHolder();
a aplicação do pattern Presentation Model. Bindings.bind(textField, valueModel); representada por um objeto da interface
Essa API foi desenvolvida para trabalhar  javax.swing.ListModel   do Swing, que define
com o JFC/Swing e possui mecanismos Repare que criamos um ValueModel   ins- métodos para a manipulação da lista e emi-
para conectar componentes gráficos a tanciando a classe ValueHolder (do mesmo te eventos quando ela for modif icada.
variáveis que podem ser manipuladas pacote de ValueModel ), que é a implementa- A contribuição do JGoodies Binding foi
independentemente de suas formas de ção que precisamos para esse caso. criar implementações que combinassem
apresentação. A segunda maneira de fazer a conexão a interface ListModel  com a simplicidade
Para entender essa idéia, imagine, por é utilizando uma classe de conveniência da interface  java.uti l.List , resultando em
exemplo, uma variável booleana que está chamada BasicComponentFactory  (também duas classes: ArrayListModel   (que herda
conectada a um  JCheckBox  (Figura 2). Sem- de com.jgoodies.binding.adapter), que possui de ArrayList  e implementa ListModel ) e
pre que trocarmos o valor dessa variável, o métodos prontos para criar componentes. LinkedListModel (que herda de LinkedList e
 JCheckBox  irá refletir a mudança e vice-ver- Assim, podemos fazer: implementa ListModel). Dessa forma, po-
sa. Ao mesmo tempo, poderíamos conectar demos atuar sobre essas classes dentro
ValueModel valueModel = new ValueHolder();
outros componentes gráficos a essa mesma JTextField textField = da classe Presentation Model, enquanto os
variável, por exemplo um  JToggleButton . BasicComponentFactory.createTextField(valueModel); componentes gráficos refletem as mudan-
Assim, podemos ter quantos componentes ças na classe View.
quisermos ligados a uma mesma variável, Essa segunda alternativa será utilizada Entretanto, ainda existe um problema.
sendo que a mudança feita em um dos pelo nosso exemplo por ser mais simples. Sabemos que os componentes desta
componentes é refletida nos outros. Duas questões importantes ainda pre- categoria guardam, além da lista de
Uma variável pode conter tanto um valor cisam ser discutidas. Primeiro, o valor objetos, um item selecionado 2. Portanto,
simples quanto uma lista de objetos. Va- mantido pelo ValueModel pode ser lido e o ListModel   sozinho não resolve nosso
mos analisar cada caso separadamente. alterado através dos métodos getValue () e problema. Precisamos da ajuda da classe
setValue (). É importante lembrar que para SelectionInList (em com.jgoodies.binding.list ), que
Conectando componentes a valores simples conectar um componente a um ValueModel, o guarda um ListModel  e também contém um
Entre os componentes que podem ser componente precisa conhecer o valor guar- ValueModel  que aponta para o item selecio-
conectados a um valor simples, os princi- dado por ele. Portanto, um  JCheckBox  pode nado. Veja um exemplo:
pais são  JLabel,  JTextField,  JTextArea,  JCheckBox, se conectar sem problemas a um ValueModel
ListModel listModel = new ArrayListModel();
ValueModel itemSelecionadoHolder = new ValueHolder();
View Presentation Model SelectionInList selectionInList =
new Selecti onInList(listModel, itemSelecionadoHolder);
JToggleButton
true
JCheckBox
Variável Com isso, podemos conectar o objeto
JCheckBox JToggleButton Booleana
false
2
Os casos em que há mais de um item selecionado são
normalmente raros, e por isso não serão tratados neste
Figura 2. Variável booleana conectada a um  JCheckBox e a um  JToggleButton. artigo.

52  Java Magazine • Edição 39

 jm39.indb 52 15/8/2006 18:19:00


SelectionInList aos componentes da View da  A classe Usuario Ainda no construtor, repare a cha-
seguinte forma: Começaremos construindo o exem- mada de dois métodos: iniciaModelos() e
plo pela classe Usuario, mostrada na iniciaLogicaApresentacao(). É nesses métodos
 JComboBox comboBox =
new JComboBox(new ComboBoxAdapter(selectionInList)); Listagem 1. Para essa classe JavaBean, que o estado e a lógica da GUI começam a
JList list = BasicComponentFactory.createList(selectionInList); o ponto mais importante a destacar é surgir. Para entender o código, acompanhe
a emissão de eventos, que é realizada a Figura 4 e veja como a API Binding está
Repare que precisamos da classe quando os dados do usuário são alterados. sendo usada. A Tabela 1 apresenta um
ComboBoxAdapter  (pacote com.jgoodies.binding. Precisamos implementar isso porque as resumo dos objetos citados a seguir.
adapter ) para conectar o  JComb oBo x ao classes da API Binding exigem que os O método iniciaModelos() começa criando
SelectionInList . O caso do  JList é mais simples, objetos manipulados sejam observáveis3. o objeto usuarioSelectionInList, a partir de um
pois podemos usar a fábrica de compo- Assim sendo, o primeiro passo é fazer a ListModel (usuarioListModel) e um ValueModel
nentes da API Binding. Para a classe  JTable, classe Usuario herdar da classe Model (pa- (usuarioSelecionadoHolder ):
precisaríamos implementar um TableModel cote com.jgoodies.binding.beans ); em seguida usuarioListModel = new ArrayListModel();
que defina as colunas e exiba os objetos do alteramos todos os métodos setXxx() para usuarioSelecionadoHolder = new ValueHolder(null, true);
ListModel . Esse caso não será tratado neste que chamem firePropertyChange() e, dessa usuarioSelectionInList = new SelectionInList((ListModel)
usuarioListModel, usuarioSelecionadoHolder);
artigo, mas há exemplos dele dentro do ZIP forma, avisem que a propriedade em
de distribuição da API Binding. questão mudou seu valor. Embora ainda não tenhamos explicado a

A aplicação de exemplo  A classe Presentation Model 


Apresentaremos a partir de agora um A segunda classe que vamos desenvolver Basicamente, um objeto é observável   quando mudan-
3

ças nele emitem eventos. O objeto que escuta esses even-


exemplo de uso do pattern Presentation chama-se CadastroUsuarioPresentationModel tos chama-se observador . Esses são conceitos trazidos pelo
 Model  com a API JGoodies Binding. O (Listagem 2). Conforme vimos na Figura 1, pattern Observer  do livro “Design Patterns” [Gamma et al.].
código completo está disponível para essa classe deve possuir
download no site da Java Magazine (para uma referência para a clas-
rodar o exemplo, basta executar a classe se Service Layer, que é o
Programa ). único meio de comunica-
Além da API Binding você vai precisar ção da GUI com o resto do
das APIs Forms e Looks do JGoodies. Para software. Assim, o cons-
obtê-las, no site do projeto ( jgoodies.com) vá trutor da classe recebe e
até a seção Download e clique em Libraries. guarda um objeto do tipo
Lá você terá acesso aos arquivos zipados CadastroUsuarioServiceLayer,
de todas as APIs, contendo exemplos, do- que é explicado mais
cumentação, código-fonte e os JARs que adiante. Figura 3. Tela de cadastro de usuários.
devem ser incluídos no classpath da sua
aplicação.
Listagem 1.Classe de modelo Usuario.
 Apresentando a aplicação import com.jgoodies.binding.beans.Model;
O exemplo que vamos construir é um
public class Usuario extends Model {
cadastro de usuários conforme apresen- public static final String PROPERTY_NOME = “nome”;
tado na Figura 3. É mostrada uma lista public static final String
public static final String
PROPERTY_CARG O = “cargo”;
PROPERTY_ADMI NISTRADOR = “administrado r”;
contendo todos os usuários cadastrados; public static final String PROPERTY_CONTROLE_PERMISSAO = “controlePermissao”;
as informações do usuário selecionado private String nome;
são exibidas à direita. Cada usuário possui private String cargo;
private boolean isAdministr ador;
quatro atributos: o nome, o cargo, e dois private boolean isControleP ermissao;
indicadores definindo se é administrador e
public String getNome() {
se tem acesso ao controle de permissões da return nome;
aplicação. O acesso ao controle de permis- }
sões só é possível para administradores. public void setNome(Str ing nome) {
Um título no lado direito apresenta qual String valorAntigo = this.nome;
this.nome = nome;
usuário estamos editando. Se nenhum // Todos os metodos set lançam eventos...
estiver selecionado, é apresentado o texto this.fireProp ertyChange(P ROPERTY_NOME, valorAntigo , this.nome);
}
“Nenhum usuário selecionado” e todos
os componentes de edição ficam desabi- }
/*...Os outros metodos get/set são similares */

litados.

Edição 39 •  Java Magazine 53

 jm39.indb 53 15/8/2006 18:19:01


Interfaces Gráficas com Qualidade – Parte 2

Listagem 2.Classe CadastroUsuarioPresentationModel, onde fica o estado e a lógica da GUI.


import com.jgoodies.binding.beans.*; Usuario novoUsuario = new Usuario(“Novo Usuário”, null);
import com.jgoodies.binding.list.*;   usuarioListModel.add(novoUsuario);
import com.jgoodies.binding.value.*; }
import javax.swing.*;
import java.awt.event.ActionEvent; private void removeUsuario() {
import java.beans.*; int indice = usuarioSelectionInList.getSelectionIndex();
import java.util.*; if (indice != -1) {
  usuarioListModel.remove(indice);
public class CadastroUsuarioPresentationModel extends Model { }
public static final String TITULO_SEM_SELECAO = }
“Nenhum usuário selecionado.”;
public static final String TITULO_COM_SELECAO = “Detalhes de “; private void salvaUsuarios() {
List lista = Collections.unmodifiableList(usuarioListModel);
private CadastroUsuarioServiceLayer serviceLayer;   serviceLayer.salvaUsuarios(lista);
private ArrayListModel usuarioListModel; }
private BeanAdapter usuarioSelecionadoBeanAdapter;
private SelectionInList usuarioSelectionInList; private void habilitaCampos() {
private SelectionInList cargoSelectionInList; boolean enabled =
private ValueHolder usuarioSelecionadoHolder; usuarioSelecionadoHolder.getValue() != null;
private ValueHolder tituloHolder;   usuarioDisponivelHolder.setValue(enabled);
private ValueHolder usuarioDisponivelHolder;   habilitaCampoControlePermissao();
private ValueHolder campoPermissaoDisponivelHolder; }
private Action adicionarAction;
private Action removerAction; private void habilitaCampoControlePermissao() {
private Action salvarAction; Usuario selecionado = (
Usuario) usuarioSelecionadoHolder.getValue();
public CadastroUsuarioPresentationModel( boolean enabled = selecionado !=
CadastroUsuarioServiceLayer serviceLayer) { null && selecionado.isAdministrador();
this.serviceLayer = serviceLayer;   campoPermissaoDisponivelHolder.setValue(enabled);
  iniciaModelos(); }
  iniciaLogicaApresentacao();
}
/* Inner classes de ações */
private void iniciaModelos() { private class AdicionarAction extends AbstractAction {
usuarioListModel = new ArrayListModel(); public void actionPerformed(ActionEvent e) {
usuarioSelecionadoHolder = new ValueHolder(null, true);   adicionaUsuario();
usuarioSelectionInList = new SelectionInList( }
(ListModel) usuarioListModel, usuarioSelecionadoHolder); }
usuarioSelecionadoBeanAdapter = new BeanAdapter(
usuarioSelecionadoHolder, true); private class RemoverAction extends AbstractAction {
public void actionPerformed(ActionEvent e) {
ArrayListModel cargoListModel = new ArrayListModel(   removeUsuario();
  Arrays.asList(Cargos.TODOS)); }
cargoSelectionInList = new SelectionInList( }
(ListModel) cargoListModel, getCargoHolder());
private class SalvarAction extends AbstractAction {
tituloHolder = new ValueHolder(); public void actionPerformed(ActionEvent e) {
usuarioDisponivelHolder = new ValueHolder();   salvaUsuarios();
campoPermissaoDisponivelHolder = new ValueHolder(); }
}
adicionarAction = new AdicionarAction();
removerAction = new RemoverAction();
salvarAction = new SalvarAction(); /* Inner classes de observadores */
}
private class ObservadorNomeUsuario implements
private void iniciaLogicaApresentacao() { PropertyChangeListener {
ObservadorSelecaoUsuario observadorSelecao = public void propertyChange(PropertyChangeEvent evt) {
new ObservadorSelecaoUsuario(); if (getUsuarioSelecionadoHolder().getValue() == null) {
  usuarioSelecionadoHolder.addValueChangeListener(   getTituloHolder().setValue(TITULO_SEM_SELECAO);
  observadorSelecao); } else {
  observadorSelecao.propertyChange(null); getTituloHolder().setValue(TITULO_COM_SELECAO +
  getNomeHolder().getValue());
ObservadorNomeUsuario observadorNome = }
new ObservadorNomeUsuario(); // Emite um evento de mudança para que a GUI reflita o novo
  this.getNomeHolder().addValueChangeListener(observadorNome); // nome.
  observadorNome.propertyChange(null);   usuarioListModel.fireContentsChanged(
  usuarioSelectionInList.getSelectionIndex());
ObservadorCampoAdmin observadorAdmin = }
new ObservadorCampoAdmin(); }
  this.getAdminHolder().addValueChangeListener(
  observadorAdmin); private class ObservadorSelecaoUsuario implements
  observadorAdmin.propertyChange(null); PropertyChangeListener {
public void propertyChange(PropertyChangeEvent evt) {
PropertyConnector connector = new PropertyConnector(   habilitaCampos();
usuarioDisponivelHolder, “value”, removerAction, “ }
  enabled”); }
  connector.updateProperty2();
private class ObservadorCampoAdmin implements
List usuarios = serviceLayer.buscaUsuarios(); PropertyChangeListener {
  usuarioListModel.addAll(usuarios); public void propertyChange(PropertyChangeEvent evt) {
}   habilitaCampoControlePermissao();
}
/* Metodos get omitidos */ }
}
private void adicionaUsuario() {

54  Java Magazine • Edição 39

 jm39.indb 54 15/8/2006 18:19:04


Eventos Java
Agende-se!

O Maior Evento Java do Brasil De Competições a Workshops


30/Nov a 2/Dez Outubro
São Paulo - SP Várias Cidades

E para o primeiro semestre de 2007

A Conferência Interna- A Tecnologia  Java e o Governo


cional do SouJava  Java, Livre Brasileiro
Edição 39 •  Java Magazine 55
www.soujava.org.br
 jm39.indb 55 15/8/2006 18:19:11
Interfaces Gráficas com Qualidade – Parte 2

classe View do exemplo, podemos adiantar Para que você entenda o significado desse gerenciando as mudanças de valores quan-
que o objeto usuarioSelectionInList conecta-se objeto, considere a nossa situação: temos do a seleção na lista de usuários mudar,
ao JList que apresenta os usuários na tela uma lista de objetos JavaBeans (usuários) podemos usar a classe BeanAdapter.
(usuariosList). e precisamos amarrar as propriedades do A classe BeanAdapter   funciona como
O próximo objeto a ser criado chama-se  JavaBean que está selecionado aos com- uma “caixa”, na qual podemos colocar
usuarioSelecionadoBeanAdapter : ponentes da tela: nome, cargo, adminis- um objeto JavaBean. Essa classe fornece
trador e controle de permissões. Para não um ValueModel  para cada propriedade do
usuarioSelecionadoBeanAdapter = termos que declarar um ValueModel para  JavaBean – justamente o que precisamos.
new BeanAdapter(usuarioSelecionadoHolder, true); cada propriedade desse JavaBean e ficar Assim, quando mudamos o usuário sele-
cionado na lista, os ValueModel s fornecidos
CadastroUsuarioView CadastroUsuarioPresentationModel pelo BeanAdapter  irão refletir os valores do
usuarioSelectionInList usuarioListModel novo usuário.
usuarioSelecionadoHolder
Repare que passamos o objeto
usuarioSelecionadoHolder   como parâmetro
usuarioSelecionadoBeanAdapter
na criação do BeanAdapter . Isso faz com
nomeHolder
cargoSelectionInList que o BeanAdapter  sempre exiba os dados
cargoListModel cargoHolder do usuário selecionado. Observe ainda
administradorHolder
o valor true como segundo parâmetro do
construtor, o que indica que as mudanças
controlePermissaoHolder
no JavaBean precisam ser observadas. No
Figura 4. Conexão entre os modelos da classe Presentation Model  com os componentes da classe View . nosso caso, isso é importante porque temos
que atualizar certas partes da GUI quando
Modelo Classe Descrição
os valores do usuário mudarem (ex.: para
habilitar/desabilitar o  JCheckBox  “Controle
usuarioListModel ArrayListModel Lista observável que guarda todos os usuários. de Permissões” quando a propriedade
 Administrador for alterada).
Mantém uma referência para o usuário sele- Por fim, para adquirir os ValueModels
usuarioSelecionadoHolder ValueModel
cionado na lista. fornecidos pelo BeanAdapter, utilizamos
o método getValueModel() , passando o
Une o usuarioListModel e o nome da propriedade em que estamos
usuarioSelectionInList SelectionInList
usuarioSelecionadoHolder em um mesmo objeto.
interessados. Assim, para conectar o
Fornece um ValueModel para cada propriedade
usuarioSelecionadoBeanAdapter BeanAdapter
do usuário selecionado.

ValueModels (fornecidos pelo
nomeHolder, usuarioSelecionadoBeanAdapter) que
cargoHolder, contém, para o usuário selecionado, o nome,
ValueModel
administradorHolder, o cargo, um booleano indicando se é um
controlePermissaoHolder administrador, e um booleano indicando se
pode controlar as permissões do sistema.

Lista observável com os possíveis cargos de


cargoListModel ArrayListModel
um usuário.

Une o cargoHolder e o cargoListModel em um


cargoSelectionInList SelectionInList
mesmo objeto.

Contém um booleano que indica se existe um


usuarioDisponivelHolder ValueModel
usuário disponível para edição.
Contém um booleano que indica se o campo
campoPermissaoDisponivelHolder ValueModel “Controle de Permissões” deve estar habilita-
do ou não.
Contém o texto que é apresentado no topo
tituloHolder ValueModel
da tela.

Tabela 1. Resumo dos objetos mantidos pela classe CadastroUsuarioPresentationModel .

56  Java Magazine • Edição 39

 jm39.indb 56 15/8/2006 18:19:14


 JTextField nomeTextField à propriedade Nome do ponente que contém uma lista de objetos outros ValueHolder s: usuarioDisponivelHolder e
usuário selecionado na lista, a classe View e uma seleção (da mesma forma que o campoPermissaoDisponivelHolder . O primeiro
utiliza o método getNomeHolder()  da classe  JList de usuários). Portanto precisamos guarda um booleano que indica se existe
CadastroUsuarioPresentationModel : criar o objeto cargoSelectionInList a partir do um usuário disponível para edição, e o se-
cargoListModel   e do ValueModel que aponta gundo contém outro booleano que indica
// Definido na classe CadastroUsuarioPresentationModel
public ValueModel getNomeHolder() { para o cargo selecionado (o cargoListModel é se o  JCheckBox “Controle de Permissões”
  return usuarioSelecionadoBeanAdapter.getValueModel( um ListModel preenchido com uma lista for- está habilitado. Esses valores são mani-
  Usuario.PROPERTY_NOME); necida pela classe  Cargos4 ): pulados pelos observadores descritos
}
mais adiante.
// Na classe View… // Na classe CadastroUsuarioPresentationModel... Ainda nesse método temos a criação de
JComponent nomeTextField = BasicComponentFactory. ArrayListModel cargoListModel = new ArrayListModel(
  Arrays.asList(Cargos.TODOS)); três ações (objetos que implementam a
createTextField(
pm.getNomeHolder(), false); cargoSelectionInList = new SelectionInList( interface  javax.swing.Action ): adicionarAction,
(ListModel) cargoListModel, getCargoHolder()); removerAction e salvarAction. Essas ações
O parâmetro false passado para o método // Na classe View... são usadas pela classe View para criar
createTextField()  significa que o valor editado JComboBox cargoComboBox = new JComboBox( os botões da tela (com exceção do botão
pelo usuário será transferido ao ValueModel new ComboBoxAdapter(pm.getCargoSelectionInList())); “Fechar” que é implementado na própria
a cada tecla pressionada. Se o parâmetro classe View  por não possuir lógica de
fosse true, o valor só seria transferido Continuando no método iniciaModelos() , negócio). A implementação dessas ações
quando o componente  JTextField perdesse vemos a criação do objeto tituloHolder, é bem simples, por exemplo:
o foco. que guarda o texto apresentado no topo
O caso do  JComboBox   de cargos segue da tela, informando o usuário sendo 4
A classe Cargos, não listada, é bem simples e define ape-
um estilo diferente, porque é um com- editado. Depois temos a criação de dois nas um array de strings.

Edição 39 •  Java Magazine 57

 jm39.indb 57 15/8/2006 18:19:17


Interfaces Gráficas com Qualidade – Parte 2

Classe Interface Implementada Descrição

Observa o usuarioSelecionadoHolder: qualquer mudança de seleção na lista de usuários resulta


Obser va dorSelecaoUsua rio Prope rt yChangeLis tener
numa chamada ao método habilitaCampos() .

ObservadorNomeUsuario PropertyChangeListener Observa as mudanças no nome do usuário selecionado e preenche o valor dotituloHolder.

Observa a propriedade Administrador  do usuário selecionado para inferir se o campo “Con-


ObservadorCampoAdmin PropertyChangeListener
trole de Permissões” deve estar habilitado (chama o métodohabilitaCampoControlePermissao() )

Tabela 2. Observadores definidos na classe CadastroUsuarioPresentationModel.

usuarioDisponivelHolder nos indica se há um


Listagem 3.CadastroUsuarioServiceLayer: classe  Service Layer  com dados fixos de teste.
usuário disponível para edição. Só se
import java.util.*; houver, habilitamos a remoção.
public class CadastroUsuarioServiceLayer {
 A classe Service Layer 
public List buscaUsuarios () {
// Retorna dados fixos para nosso exemplo... A classe CadastroUsuarioServiceLayer,
// O primeiro usuário é um administrador (true no terceiro parâmetro) mostrada na Listagem 3, é a fronteira
ArrayList usuarios = new ArrayList();
usuarios.add (new Usuario(“Deo doro da Fonseca”, Cargos.GERE NTE_PROJETO, true)); através da qual passam os dados do exem-
usuarios.add (new Usuario(“Flo riano Peixoto”, Cargos.DESE NVOLVEDOR));
usuarios.add (new Usuario(“Pru dente de Moraes”, Cargos.LIDE R_EQUIPE));
plo. Temos dois métodos: buscaUsuarios() e
usuarios.add (new Usuario(“Cam pos Salles”, Cargos.SUPOR TE)); salvaUsuarios(). Para o nosso caso, criamos
return usuarios; o método buscaUsuarios() , que retorna um
}
conjunto fixo de objetos Usuario  para que
public void salvaUsuarios (List listaUsuarios ) {
// Aqui você coloca seu código para salvar na base de dados.
a GUI possa ser testada sem depender de
} qualquer outra coisa.
}
Em sistemas mais robustos, recomendo
a adoção de duas práticas importantes. A
private class AdicionarAction extends AbstractAction { Para fazer o ObservadorSelecaoUsuario  obser- primeira é criar uma interface para a Ser-
public void actionPerformed(ActionEvent e) { var o objeto usuarioSelecionadoHolder, deve- vice Layer que resume todos os métodos de
  adicionaUsuario(); mos adicioná-lo como sendo seu listener: manipulação de dados. A idéia é que haja
}
} duas implementações para ela: uma de
ObservadorSelecaoUsuario observadorSelecao = teste e uma real. A implementação de teste
new ObservadorSelecaoUsuario();
E para criar um botão a partir de uma usuarioSelecionadoHolder. é igual à nossa, onde dados fixos ajudam no
ação, basta passar uma instância da ação   addValueChangeListener(observadorSelecao); desenvolvimento e nos testes. A implemen-
ao construtor do  JButton : Assim, quando o valor guardado pelo tação real é a que acessa verdadeiramente o
usuarioSelecionadoHolder   mudar, o método backend do sistema (bancos de dados, EJBs,
JButton botaoAdicionar = new JButton(pm.getAdicionarAction());
habilitaCampos() será invocado.
Agora que os modelos estão criados e co- Logo após criar e configurar os ob-
nectados, o método iniciaLogicaApresentacao() servadores, utilizamos a classe
(ainda na Listagem 2) adiciona mais vida PropertyConnector para habilitar/desabilitar
à GUI. Começamos com a criação de obje- a ação removerAction. Isso é feito conec-
tos “observadores” – que implementam a tando-se a propriedade value  do objeto
interface PropertyChangeListener. A Tabela 2 usuarioDisponivelHolder  com a propriedade
explica os três observadores presentes no enabled  da ação:
final da classe Presentation Model. A imple-
mentação deles é bastante simples e segue PropertyConnector connector = new PropertyConnector(
este estilo: usuarioDis ponivelHold er, “value”, removerAction, “enabled”) ;

private class ObservadorSelecaoUsuario Assim, sempre que o valor boolea-


implements PropertyChangeListener
{ no em usuarioDisponivelHolder   mudar, o
public void propertyChange(PropertyChangeEvent evt) { PropertyConnector  vai transmitir esse valor
  habilitaCampos(); ao enabled  da ação (via setEnabled()). Fa-
}
} zemos isso porque o valor mantido pelo

58  Java Magazine • Edição 39

 jm39.indb 58 15/8/2006 18:19:19


etc). Recomendo manter ambas as imple- a classe CadastroUsuarioPresentationModel , que fizemos com a removerAction  dentro do
mentações juntas no código e utilizar uma que é esperada em seu construtor. A método iniciaLogicaApresentacao()). No caso
ou outra conforme o objetivo. A segunda montagem da tela é feita dentro do do controlePermissaoCheckBox, amarramos o
prática recomendada é criar, dentro da método createViewComponent() , onde os enabled  ao valor do controlePermissaoHolder.
classe Service Layer, uma outra thread para componente gráficos são instanciados e Dessa forma, a lógica que controla a habili-
comunicação com o restante do sistema. conectados aos modelos mantidos pela tação desses componentes fica inteiramen-
Normalmente, chamadas ao backend demo- classe CadastroUsuarioPresentationModel, con- te dentro da classe Presentation Model.
ram alguns segundos; assim disparar uma forme foi visto na Figura 4. Como a classe View   é a responsável
nova thread para processar as chamadas Repare que não há necessidade de manter pela apresentação visual da GUI, é nela
evita o travamento da GUI e possíveis irri- referências para os componentes criados, que colocamos os textos e ícones da tela:
tações por parte dos usuários.  já que os valores mostrados neles vêm dos veja os métodos criaBarraFerramentas() e
modelos com os quais estão conectados. montaBarraBotoes() . Poderíamos ter colocado
 A classe View  Além disso, o método conectaHabilitacao()  es- essas informações dentro das ações, mas
A classe CadastroUsuarioView (Listagem 4) tabelece uma ligação entre o valor mantido estaríamos introduzindo in formações
contém os componentes gráficos da nos- pelo usuarioDisponivelHolder e a propriedade visuais em classes que não deveriam ter
sa GUI. Ela possui uma referência para enabled dos componentes (da mesma forma esse conhecimento.

Listagem 4.CadastroUsuarioView: classe View  com os componentes gráficos.

import com.jgoodies.binding.adapter.*; private void conectaHabilitacao(JComponent component) {


import com.jgoodies.binding.beans.PropertyConnector; PropertyConnector connector = new PropertyConnector(
import com.jgoodies.forms.builder.DefaultFormBuilder;   pm.getUsuarioDisponivelHolder(
import com.jgoodies.forms.factories.ButtonBarFactory; ), “value”, component, “enabled”);
import com.jgoodies.forms.layout.*;   connector.updateProperty2();
import javax.swing.*; }
import java.awt.*;
import java.awt.event.ActionEvent; private Component criaBarraFerramentas() {
import java.io.File; JButton botaoAdicionar = new JButton(pm.getAdicionarAction());
import java.net.URL;   botaoAdicionar.setIcon(getIcon(“add.png”));

public class CadastroUsuarioView { JButton botaoRemover = new JButton(pm.getRemoverAction());


private CadastroUsuarioPresentationModel pm;   botaoRemover.setIcon(getIcon(“remove.png”));
private JFrame frame;
/* Aqui a toolbar é montada e retornada */
public CadastroUsuarioView(CadastroUsuarioPresentationModel pm) { }
this.pm = pm;
} private Component montaBarraBotoes() {
JButton botaoSalvar = new JButton(pm.getSalvarAction());
protected Component createViewComponent() {   botaoSalvar.setText(“Salvar”);
JList usuariosList = BasicComponentFactory.createList(
  pm.getUsuarioSelectionInList()); JButton botaoCancelar = new JButton(new FecharAction());
  botaoCancelar.setText(“Fechar”);
JLabel tituloLabel = BasicComponentFactory.createLabel(
  pm.getTituloHolder()); return ButtonBarFactory.buildCenteredBar(
  tituloLabel.setFont(tituloLabel.getFont().deriveFont( botaoSalvar, botaoCancelar);
Font.BOLD, 14f)); }

JComponent nomeTextField = BasicComponentFactory public JFrame criaFrame() {


.createTextField(pm.getNomeHolder(), false); if (frame == null) {
  conectaHabilitacao(nomeTextField); frame = new JFrame(“Exemplo do Padrão Presentation Model”);
  frame.getContentPane().add(createViewComponent());
JComboBox cargoComboBox = new JComboBox(   frame.pack();
new ComboBoxAdapter(pm.getCargoSelectionInList()));   frame.setLocationRelativeTo(null);
  conectaHabilitacao(cargoComboBox);   frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
JCheckBox administradorCheckBox = return frame;
BasicComponentFactory.createCheckBox( }
pm.getAdminHolder(), “Administrador”);
  conectaHabilitacao(administradorCheckBox); private Icon getIcon(String fileName) {
String path = “images” + File.separator + fileName;
JCheckBox controlePermissaoCheckBox = URL url = CadastroUsuarioView.class.getResource(path);
BasicComponentFactory.createCheckBox( return new ImageIcon(url);
pm.getControlePermissaoHolder(), “Controle de Permissões”); }
PropertyConnector connector = new PropertyConnector(
pm.getCampoPermissaoDisponivelHolder(), “value”, private class FecharAction extends AbstractAction {
controlePermissaoCheckBox, “enabled”); public void actionPerformed(ActionEvent e) {
  connector.updateProperty2();   frame.hide();
  System.exit(0);
/* Aqui são posicionados os componentes no painel... */ }
} }
}

Edição 39 •  Java Magazine 59

 jm39.indb 59 15/8/2006 18:19:20


Interfaces Gráficas com Qualidade – Parte 2

Desenvolvendo testes método testAdicionarUsuario() , que mede campoPermissaoDisponivelHolder contêm o valor


unitários sobre a GUI o tamanho da lista de usuários antes e false. Ainda nesse caso, a ação removerAction
Como vimos no início do artigo, uma depois de executar a ação adicionarAction deve estar desabilitada. Depois disso,
das maiores vantagens do pattern da classe CadastroUsuarioPresentationModel.  O selecionamos o primeiro usuário da lista
Presentation Model é a facilidade de cria- tamanho final da lista deve ser o tamanho e checamos se tudo está habilitado (con-
ção de testes unitários sobre a GUI. Para inicial mais uma unidade. Da mesma siderando que o primeiro usuário é um
ilustrar isso, criamos diversos testes na forma, temos o teste testRemoverUsuario() , administrador).
classe CadastroUsuarioPresentationModelTest que começa selecionando o primeiro usu- Outros três testes foram omitidos da lis-
(Listagem 5), utilizando o JUnit. ário (índice zero) e depois executa a ação tagem, mas seus códigos estão bem docu-
Essa classe cria no méto- removerAction . mentados na versão completa disponível
do setUp()   uma instância da classe O objetivo do terceiro teste – no site da Java Magazine.
CadastroUsuarioPresentationModel e outra de testHabilitacaoControles() – é verificar se a
CadastroUsuarioServiceLayer , que contém nos- habilitação dos controles está funcio- Conclusões
sos dados de teste. Conforme dito antes, nando corretamente. Para isso, lim- Apresentamos nesse artigo a combina-
não precisamos da classe View para testar pamos a seleção da lista de usuários ção do padrão Presentation Model com a
a lógica da GUI. com o método clearSelection()   e depois API Binding do JGoodies, que nos permite
O nosso primeiro teste é feito pelo verificamos se o usuarioDisponivelHolder e o construir GUIs pela simples conexão de
modelos e propriedades. Dentre as vanta-
Listagem 5.CadastroUsuarioPresentationModelTest.java: Testes sobre a lógica da GUI.
gens dessa abordagem, temos um código
mais limpo, separado em responsabilida-
import junit.framework.TestCase; des, e a simplificação da c riação de testes
public class CadastroUsuarioPresentationModelTest extends TestCase { unitários eficazes sobre a GUI.
private CadastroUsuar ioServiceLay er serviceLayer ;
Assim terminamos a série sobre quali-
private CadastroUsuar ioPresentati onModel pm; dade de interfaces gráficas que abordou
protected void setUp() throws Exception { os dois lados do desenvolvimento de
serviceLayer = new CadastroUsua rioServiceLay er(); GUIs: a aparência das aplicações, na
pm = new CadastroUsuarioPresentationModel(serviceLayer);
} Parte 1, e o código escondido por trás das
telas nesta segunda parte.
public void testAdicionaU suario() {
int tamanhoInici al = pm.getUsuari oListModel(). getSize();
  pm.getAdicionarAction().actionPerformed(null);
int tamanhoFinal = pm.getUsuari oListModel(). getSize();
assertEquals(“Deveria ter mais um usuário na lista”,  martinfowler.com/eaaDev/ 
tamanhoInicial + 1, tamanhoFinal);
} OrganizingPresentations.html 
Página escrita pelo autor Martin Fowler
public void testRemoveUsu ario() {
  pm.getUsuarioSelectionInList().setSelectionIndex(0); que discute os diferentes padrões para o
int tamanhoInici al = pm.getUsuari oListModel(). getSize(); desenvolvimento de GUIs em aplicações desktop.
  pm.getRemoverAction().actionPerformed(null);
int tamanhoFinal = pm.getUsuari oListModel(). getSize();
assertEquals (“Deveria ter menos um usuário na lista”,  jgoodies.com
tamanhoInicial - 1, tamanhoFinal);
}
Site do JGoodies com tutoriais, artigos, aplicações
e APIs para download.
public void testHabilitac aoControles( ) {
  pm.getUsuarioSelectionInList().clearSelection();  javamagazine.com.br/downloads/jm39/
assertEquals (“Os controles devem estar desabilitad os”,
pm.getUsuarioDisponivelHolder().getValue(), Boolean.FALSE);  jm-gui2.zip
assertEquals (“O campo ‘controle de permissões’ deve estar desabilitado” ,
pm.getCampoPermissaoDisponivelHolder().getValue(), Boolean.FALSE);
assertFalse( “A ação de remover deve estar desabilitada ”,
pm.getRemoverAction().isEnabled());

  pm.getUsuarioSelectionInList().setSelectionIndex(0); Hugo Vidal Teixeira


assertEquals (“Os controles devem estar habilitados ”, (hugo.tech@gmail.com ) é Bacharel
pm.getUsuarioDisponivelHolder().getValue(), Boolean.TRUE);
assertEquals (“O campo ‘controle de permissões’ deve estar habilitado”,
em Informática pela UFRJ,mestre
pm.getCampoPermissaoDisponivelHolder().getValue(), Boolean.TRUE); em Engenharia de Software pela
assertTrue(“A ação de remover deve estar habilitada”, COPPE/UFRJ e pesquisador da área de
pm.getRemoverAction().isEnabled());
} componentes e GUI Design.Atua como
consultor integrado à Sakonnet Technology,onde trabalha
public void testHabilitac aoCampoPermi ssao() {...}
public void testLabelTitu lo() {...} diretamente com Karsten Lentzsch (criador do JGoodies) e
public void testConexaoNo meTitulo() {...} empresas especializadas em GUI design,como Ergosign
} (.de),Centigrade(.de)e OculusInfo(.com).

60  Java Magazine • Edição 39

 jm39.indb 60 15/8/2006 18:19:23


Edição 39 •  Java Magazine 61

 jm39.indb 61 15/8/2006 18:19:26


Uma Mala Direta com
Mensagens Personalizadas e Expressõ

N este artigo, vamos demonstrar a


API JavaMail criando uma apli-
cação completa. Iniciamos com
conceitos básicos sobre a API, depois mos-
tramos passo a passo o desenvolvimento
A API JavaMail
A API JavaMail é utilizada para leitura e
envio de e-mails, e oferece diversos recur-
sos essenciais para aplicações corporativas,
entre os quais destacamos:
de uma aplicação de mala direta funcional, • Suporte a vários protocolos de envio e
na qual lemos informações sobre clientes leitura de e-mail (ex.: POP3, SMTP, IMAP)
de uma tabela em um banco de dados • Suporte a mensagens em HTML
MySQL e enviamos e-mails personaliza- • Suporte a envio de mensagens com
dos para cada cliente. Também apresenta- anexos
mos o conceito de expressões regulares e
as funcionalidades relacionadas oferecidas A API JavaMail não é incluída com o
pela plataforma Java, usando-as para per-  JDK, portanto é necessár io fazer o down-
sonalizar as mensagens enviadas. load e disponibilizá-la para a aplicação.
Além disso, será necessária a API Java-
Beans Activation Framework (JAF). Os
downloads podem ser feitos em  java.sun.
com/products/javamail/downloads
e  java.sun.com/ products/javabe-
ans/jaf/downloads. Será preciso
adicionar dois JARs ao class-
path da aplicação: mail.jar e
activation.jar.
Se você está desenvolven-
do aplicações Java EE, não
há nenhuma preocupação
adicional, pois as APIs JavaMail
e JAF fazem par te do Java EE.

Visão geral das classes da API


Apresentamos a seguir uma breve
descrição de algumas clas-
ses fundamentais do
 JavaMail.

 javax.mail.Session
A classe Session re-
presenta uma sessão
de e-mail. É através
dela que obtemos os
objetos responsáveis
pelo envio e leitura de e-mails,
como por exemplo Transport , Store e
Folder. Para obter um Session precisa-
mos de informações sobre o servidor de

62  Java Magazine • Edição 39

 jm39.indb 62 15/8/2006 18:19:52


Aprenda como utilizar a API
 JavaMail para enviar e-mails

  JavaMail com HTML, enviar anexos e


ainda personalizar o e-mail com
os dados dos clientes utilizando
expressões regulares
es Regulares
YARA SENGER E ANA ABRANTES

e-mails, que podem ser passadas através dereço de e-mail, incluindo opcionalmente Nessa segunda etapa, é feita a substituição
de um objeto  java.util.Properties . um nome. do nome do cliente, e caso o telefone esteja
em um formato incorreto, será adicionada
 javax.mail.Authenticator e
 javax.mail.Transport  uma mensagem no corpo do e-mail solici-
A classe abstrata Transport   encapsula  javax.mail.PasswordAuthentication tando a atualização cadastral.
o protocolo de envio de e-mails (geral- Um objeto da classe Authenticator   sabe As próximas seções descrevem o proces-
mente SMTP) e contém métodos para como obter os dados para autentica- so passo a passo e apresentam detalhes de
envio de mensagens. Um objeto do ção do usuário, retornando um objeto cada classe.
tipo Transport   é obtido através do objeto PasswordAuthentication . Não há restrição para
Session   informando-se o protocolo uti- as formas de se obter esses dados, que Classe EnviadorDeEmail: enviando
lizado. A API JavaMail fornece apenas podem vir de um arquivo, de entradas do mensagens em HTML com um anexo
uma subclasse concreta de Transport : usuário em um prompt, de uma conexão A classe EnviadorDeEmail tem apenas um
 javax.mail.internet.SMTPTransport . a uma fonte de dados externa, ou mesmo método público: enviarEmail() . Neste método
informando-se o usuário e a senha no é obtido um Session e criado um Message  já
 javax.mail.Store e javax.mail.Folder  construtor do objeto, como implementa- com o destinatário e o assunto. Em segui-
Apesar de não utilizarmos estas classes mos no exemplo deste artigo. da, o método adiciona o conteúdo e anexa
no nosso exemplo pois são usadas apenas um arquivo à mensagem. São usados vá-
para leitura/transferência de mensagens, O projeto de Mala Direta rios métodos auxiliares para modularizar
vale a pena citá-las. A classe abst rata Store A aplicação de exemplo é formada por o código.
é utilizada para a leitura de mensagens de quatro classes. A classe EnviadorDeEmail
um servidor de e-mail. Ao obter um objeto (Listagem 1) é responsável por criar a Obtendo a sessão
Store através da sessão indicamos qual o mensagem, anexar um arquivo e fazer O método obterSessao() é o primeiro a ser
protocolo utilizado, por exemplo POP3 o envio (e numa segunda etapa, por chamado em enviarEmail(). Ele configura
ou IMAP (há duas subclasses concretas personalizar as mensagens). A classe um objeto da classe Properties indicando,
de Store na API: POP3Store e IMAPStore). Então MalaDireta (Listagem 2) é a classe execu- através de propriedades pré-estabeleci-
nos conectamos ao servidor informando tável da aplicação; ela cuida da leitura das pela API, o nome do servidor SMTP
os dados para autenticação e recebimento dos clientes do banco de dados e chama (propriedade mail.smtp.host ), e se o servidor
das pastas ( javax.mail.Folder) que contêm os os métodos da classe EnviadorDeEmail  para exige ou não autenticação (propriedade
e-mails do usuário autenticado. cada cliente recuperado. Cada destina- mail.smtp.auth). No nosso caso consideramos
A classe abstrata Folder  representa uma tário da mala direta é representado por que a autenticação é necessária.
pasta de e-mails do usuário autenticado. um objeto Cliente (Listagem 3), com nome,
Properties props = new Properties();
Objetos Folder (há as subclasses POP3Folder endereço de e-mail, telefone e id. A classe props.put(“mail.smtp.host”, SERVIDOR_SMTP);
e IMAPFolder na API) são obtidos através de Autenticador (Listagem 4) faz a autenticação props.put(“mail.smtp.auth”, “true”);
um objeto Store fornecendo seu nome. no servidor de e-mails, necessária para o
envio. Na Listagem 5 é mostrado o coman- Depois, obterSessao() obtém o objeto Session
 javax.mail.internet.MimeMessage do SQL para criação da tabela de clientes usando Session.getDefaultInstance() . Como
A classe MimeMessage  representa uma no banco de dados (chamamos o banco de indicamos que o servidor exige auten-
mensagem a ser lida ou enviada por e-mail, “maladireta”). ticação, é necessário passar um objeto
e estende a classe abstrata  javax.mail.Message. Inicialmente iremos mostrar como en- que implementa a interface Authenticator.
Para criarmos uma MimeMessage  é necessá- viar e-mails em formato HTML com um Passamos um objeto da nossa classe
rio termos um objeto Session. A mensagem anexo. Depois veremos a personalização Autenticador (Listagem 4), que é instancia-
fica associada à sessão de e-mail. do conteúdo do e-mail. A Listagem 7 e a da informando o usuário e a senha para
Listagem 8 exibem as alterações nas clas- autenticação no servidor SMTP.
 javax.mail.internet.InternetAddress ses EnviadorDeEmail e MalaDireta  necessárias
sessao = Session.getDefaultInstance(
A classe InternetAddress  representa um en- para a personalização das mensagens. props, new Autenticador(EMAIL, SENHA));

Edição 39 •  Java Magazine 63

 jm39.indb 63 15/8/2006 18:19:53


Uma Mala Direta com JavaMail

Criando o objeto Message representados por atributos da inner class enderecoRemetente = new InternetAddress(
EMAIL_REMETENTE, NOME_REMETENTE);
Em seguida, o método enviarEmail() cria RecipientType  da classe Message . message.setFrom(enderecoRemetente);
uma mensagem chamando criarMensagem(), No trecho a seguir de criarMensagem() é
que retorna um MimeMessage . Note que é criado um InternetAddress  com o e-mail e o O assunto e a data de envio são confi-
passado como parâmetro o objeto Session; nome do destinatário, e depois este ende- gurados através dos métodos setSubject() e
dessa forma a mensagem fica vinculada reço é associado à mensagem, informan- setSentDate(), que são bastante simples.
a esta sessão. do que será utilizado como destinatário
message.setSubject(assunto);
principal (TO). message.setSentDate(new Date()) ;
MimeMessage mensagem =
criarMensagem(sessao, assunto, destinatario);
enderecoDestinatario = new InternetAddress( E por fim o método criarMensagem()  retorna
É possível enviar mensagens para um destinatario.getEmail(), destinatario.getNome()); a mensagem criada.
message.addRecipient(Message.RecipientType.TO,
ou mais destinatários, que podem ser   enderecoDestinatario);
configurados com CC  (Carbon Copy), BCC Definindo o conteúdo do e-mail 
(Blind Carbon Copy) e TO  (Destinatário O remetente também é representado por O próximo método a ser executado é
principal). Além disso, cada destinatário uma instância da classe InternetAddress , que anexarConteudoEAnexo() . O conteúdo do e-
pode ter um nome e um e-mail (um objeto é associada à mensagem através do método mail é representado por um objeto Multipart,
InternetAddress ). Os tipos do destinatário são setFrom(). sendo que nossa mensagem terá duas

Listagem 1.EnviadorDeEmail.java sem personalização

package maladireta; throw new AddressException(ex.getMessage());


}
import java.io.*;   mensagem.addRecipient(Message.RecipientType.TO,
import java.util.*;   enderecoDestinatario);
import java.util.regex.*;   mensagem.setFrom(enderecoRemetente);
import javax.activation.*;   mensagem.setSubject(assunto);
import javax.mail.*; mensagem.setSentDate(new Date());
import javax.mail.internet.*; return mensagem;
}
public class EnviadorDeEmail {
private static String EMAIL_REMETENTE = “yara@globalcode.com.br”; private void adicionarConteudoEAnexo(Message message,
private static String NOME_REMETENTE = “Yara”; String conteudo, Cliente destinatario, String arquivo)
private static String SERVIDOR_SMTP = “smtp.globalcode.com.br”; throws MessagingException, AddressException
private static String SENHA = “senha”; {
// Cria um Multipart que será composto pelo conteudo e pelo anexo
public void enviarEmail(String assunto, String conteudo, Multipart multipart = new MimeMultipart();
Cliente destinatario, String arquivo) throws AddressException,
  MessagingException
// Cria a parte do corpo da mensagem (texto do conteúdo do e-mail)
{
BodyPart messageBodyPartConteudo = new MimeBodyPart();
Session sessao = obterSessao();
messageBodyPartConteudo.setContent(conteudo, “text/html”);
Message mensagem = criarMensagem(sessao, assunto, destinatario);
  adicionarConteudoEAnexo(
// Adiciona o corpo da mensagem ao Multipart
mensagem, conteudo, destinatario, arquivo);
  multipart.addBodyPart(messageBodyPartConteudo);
  mensagem.saveChanges();
Transport transport = null; // Cria uma segunda parte do corpo (anexos do contéudo do e-mail)
try { BodyPart messageBodyPartAnexo = new MimeBodyPart();
transport = sessao.getTransport(“smtp”); // Cria o anexo
  transport.send(mensagem); DataSource source = new FileDataSource(arquivo);
} // Define o data handler para o anexo
finally { messageBodyPartAnexo.setDataHandler(new DataHandler(source));
  transport.close(); // Define o nome do arquivo
} String nomeArquivo = new File(arquivo).getName();
}   messageBodyPartAnexo.setFileName(nomeArquivo);
// Adiciona o anexo ao Multipart
private Message criarMensagem(Session session, String assunto,   multipart.addBodyPart(messageBodyPartAnexo);
Cliente destinatario) throws MessagingException, // Adiciona o Multipart a mensagem
  AddressException   message.setContent(multipart);
{ }

MimeMessage mensagem = new MimeMessage(session); private Session obterSessao() {


Address enderecoRemetente = null; Session sessao = null;
Address enderecoDestinatario = null; Properties props = new Properties();
try { props.put(“mail.smtp.host”, SERVIDOR_SMTP);
enderecoDestinatario = new InternetAddress( props.put(“mail.smtp.auth”, “true”);
destinatario.getEmail(), destinatario.getNome()); sessao = Session.getDefaultInstance(props, new Autenticador(
enderecoRemetente = new InternetAddress(EMAIL_REMETENTE, EMAIL_REMETENTE, SENHA));
  NOME_REMETENTE); return sessao;
} }
catch (UnsupportedEncodingException ex) { }

64  Java Magazine • Edição 39

 jm39.indb 64 15/8/2006 18:19:54


partes, uma com o corpo do e-mail e outra  Anexando um arquivo FileDataSource  instanciado, e associá-lo
com o arquivo a ser anexado. As partes são O anexo do e-mail também é repre- ao objeto da classe MimeBodyPart   que
representadas por objetos MimeBodyPart . sentado por uma instância da classe representa o anexo através do método
 O corpo do e-mail deve estar em HTML, MimeBodyPart, criada com seu construtor setDataHandler() :
por isto a instância de MimeBodyPart é default:
messageBodyPartAnexo.setDataHandler(
configurada informando o tipo como new DataHandler(source));
“text/html”: BodyPart messageBodyPartAnexo = new MimeBodyPart();
3.   E
indicar qual o nome do arquivo
Multipart multipart = new MimeMultipart(); Para anexar o arquivo ao e-mail é neces- anexo para o objeto MimeBodyPart , através
BodyPart messageBodyPartConteudo = new MimeBodyPart();
messageBodyPartConteudo.setContent(conteudo, “text/html”); sário realizar os seguintes passos: do método setFileName(String nome):
1. Criar um FileDataSource  a partir do ca-
Depois associamos esta parte ao objeto minho completo do arquivo que queremos String nomeArquivo = new File( arquivo).getName();
messageBodyPartAnexo.setFileName(nomeArquivo);
Multipart que representa todo o conteúdo anexar:
da mensagem. Depois, mais uma vez, associamos esta
DataSource source = new FileDataSource(arquivo);
parte da mensagem ao objeto Multipart, que
multipart.addBodyPart(messageBodyPartConteudo); 2 .   Criar um DataHandler   a partir do representa todo o conteúdo da mensagem:

Listagem 2. MalaDireta.java sem personalização

package maladireta; }
catch (MessagingExcepti on e) {
import java.io.*; System.out.prin tln(“Erro ao enviar email para “
import java.sql.*; + cliente.getNome ());
import java.util.*;   e.printStackTrace();
import javax.mail.MessagingException; }
}
public class MalaDireta { }
private final static String STRING_CON = “ catch (Exception e) {
jdbc:mysql://localhost/maladireta”; System.out.printl n(“Erro ao ler clientes do banco de dados.\n”);
private final static String USUARIO_BD = “root”; throw e;
private final static String SENHA_BD = “root”; }
private final static String ANEXO = “C:\\temp\\teste.txt”; }

public static void main(String[] args) throws Exception { public static List obterClientes() throws SQLException,
String assunto = “Teste de envio de e-mail do projeto MalaDireta”;   ClassNotFoundException
{
StringBuilder conteudo = new StringBuilder(); Connection con = obterConexao();
  conteudo.append(“ Statement st = null;
<!DOCTYPE html PUBLIC ‘-//W3C//DTD HTML 4.01 Transitional//EN’>”); ResultSet rs = null;
  conteudo.append( List listaClientes = new ArrayList();
“<html><head><title>Teste de envio de e-mail</title></head>”); try {
conteudo.append(“<body> Caro(a) cliente,<br> <br>”); st = con.createStat ement();
  conteudo.append( rs = st.executeQuer y(“select * from clientes”);
“Voc&ecirc; est&aacute; cadastrado(a) no nosso sistema e esta“); while (rs.next()) {
conteudo.append(“&eacute; uma mensagem que testa o envio de” Cliente cliente = new Cliente(rs.getL ong(“id”), rs
+ “ e-mail para a sua conta.<br>”); .getString(“nome ”), rs.getString(“e mail”), rs
conteudo.append(“<br> Se voc&ecirc; n&atilde;o quer mais”   .getString(“telefone”));
+ “ receber nossos e-mails, por “);   listaClientes.add(cliente);
  conteudo.append( }
“favor escreva para <a href=’mailto:contato@globalcode.com.br’>”); return listaClientes;
  conteudo.append( }
“contato@globalcode.com.br</a> solicitando descadastramento.<br>”); finally {
conteudo.append(“Pedimos a gentileza de responder este ”
if (rs != null)
+“e-mail confirmando o recebimento.<br>”);
  rs.close();
  conteudo.append(
if (st != null)
“<br>Atenciosam ente,<br><br>Equ ipe Globalcode<br>< br>”);
  st.close();
conteudo.append(“<a href=’http://www.globalcode.com.br’>”
if (con != null)
+ “www.globalcode.com.br</a><br>”);
  con.close();
conteudo.append(“Telefone: (11) 3171-1987<br> </body></html>”);
}
}
EnviadorDeEmail enviadorDeEmail = new EnviadorDeEmail();
try {
public static Connection obterConexao() throws SQLException,
List clientes = obterClientes();
  ClassNotFoundException
// envia o e-mail para todos os clientes
{
for (Iterator it = clientes.iterator(); it.hasNext();) {
Cliente cliente = (Cliente) it.next();   Class.forName(“com.mysql.jdbc.Driver”);
try { Connection con = DriverManager.g etConnection(STRI NG_CON,
enviadorDeEmail.enviarEmail(assunto, conteudo.toString(), USUARIO_BD, SENHA_BD);
cliente, ANEXO); return con;
System.out.println(“e-mail enviado com sucesso para “ }
+ cliente.getNome()); }

Edição 39 •  Java Magazine 65

 jm39.indb 65 15/8/2006 18:19:55


Uma Mala Direta com JavaMail

multipart.addBodyPart(messageBodyPartAnexo); inteiramente na classe MalaDireta ; em um (o código HTML do corpo da mensagem).


projeto real haveria uma separação de Obtemos a lista de clientes do banco de
Finalmente, colocamos as duas partes na responsabilidades muito maior. O acesso dados através da chamada ao método
mensagem: é feito através dos métodos obterConexao() e obterClientes() e enviamos um e-mail para
obterClientes(), que retornam respectivamen- cada cliente recuperado utilizando o méto-
mensagem.setContent(multipart); te uma conexão ao banco de dados e uma do enviarEmail() da classe EnviadorDeEmail  dis-
lista de objetos Cliente criados a partir dos cutida anteriormente. Veja o código com-
Classe MalaDireta: dados na tabela de clientes. pleto da classe MalaDireta na Listagem 2. A
controlando a aplicação No método main() criamos um String com Figura 2 mostra o e-mail enviado.
Por simplicidade, no nosso exemplo o o assunto da mensagem e um StringBuilder Até aqui mostramos como enviar e-mails
acesso ao banco de dados foi realizado para armazenar o conteúdo a ser enviado para múltiplos destinatários, mas sem per-
sonalizar as mensagens para cada cliente.
No nosso exemplo, essa personalização
Listagem 3. Cliente.java será feita usando expressões regulares.
package maladireta;
Por isso, antes de continuar com o código,
vamos apresentar conceitos gerais sobre
import java.io.Serializable;
este tema e suas diversas aplicações.
public class Cliente implements Serializable {
private long id;
private String nome = “”; Conceitos gerais de
private String email = “”;
private String telefone = “”;
expressões regulares
Expressões regulares são muito utili-
public Cliente() {} zadas para localização de informações
public Cliente(String nome, String email, String telefone) { dentro de textos e para validação de dados
this.nome = nome;
this.email = email;
textuais que devem seguir formatos espe-
this.telefone = telefone; cíficos, como URLs, datas, endereços de
}
e-mail, CEPs, números de telefone etc.
public Cliente(long id, String nome, String email, String telefone) { Na API do Java SE, o pacote  javax.util.regex
this(nome, email, telefone);
this.id = id;
contém duas classes principais voltadas
} ao trabalho com expressões regulares.
//… get/set Pattern representa uma expressão regular
} compilada, e Matcher é capaz de verificar
se uma dada str ing está de acordo com a
Listagem 4. Autenticador.java expressão regular em um objeto Pattern.
package maladireta;
A documentação JavaDoc da classe
Pattern inclui um resumo dos principais
import javax.mail.PasswordAuthentication;
símbolos utilizados para expressões
public class Autenticador extends javax.mail.Authenticator { regulares. Veja alguns exemplos de ex-
private String user;
private String senha;
pressões usando esses símbolos e o que
elas representam:
public Autenticador() {}
• \d  – um digito [0-9]
public Autenticador(String user, String senha) { • [a-zA-Z] – qualquer letra
this.user = user;
this.senha = senha; • [a-z] – qualquer letra minúscula
} • ?  – o símbolo antes do ?  aparece zero
public PasswordAuthentication getPasswordAuthentication() { ou uma vez
return new PasswordAuthentication(user, senha);
}
• *  – o símbolo antes do * aparece zero
} ou mais vezes
• + – o símbolo antes do + aparece uma
Listagem 5. Script para criação da tabela no banco de dados ou mais vezes
• {n}  – o símbolo antes do {n}  aparece exa-
CREATE TABLE `clientes` (
`id` int(11) NOT NULL auto_increment, tamente n vezes
`nome` char(40) NOT NULL default ‘’, • {n,m} – o símbolo antes do {n,m} aparece
`email` char(40) NOT NULL default ‘’,
`telefone` char(20) NOT NULL default ‘’, no mínimo n vezes e não mais do que m
PRIMARY KEY (`id`) vezes
) ENGINE=MyISAM DEFAULT CHARSET=latin1 ROW_FORMAT=FIXED;
• {n,}  – o símbolo que está antes do {n,}

66  Java Magazine • Edição 39

 jm39.indb 66 15/8/2006 18:19:55


aparece no mínimo n vezes
• X|Y  – ou  X ou  Y  \\s[A-Z][a-z]+
• \s  – espaço em branco [A-Z] [a-z] + Um espaço, seguido de
O texto deve começar com Seguido de uma ou uma letra maiúscula e uma |
uma letra maiuscula mais letras minusculas ou mais letras minúsculas ou
Na  Figura 1  é analisado um exemplo
complexo de expressão regular. A figura
inclui também uma tabela com nomes
que estão ou não de acordo com essa
expressão. [A-Z][a-z]+((\\s[A-Z][a-z]+)|\\s[a-z]{2}|\\s[A-Z].)+
O trecho de código na Listagem 6 ilustra
a utilização das classes Pattern e Matcher + Uma ou mais vezes
para verificar vários nomes com a expres-
são regular detalhada na Figura 1.
\\s[a-z]{2} \\s[A-Z] .
As classes Pattern e Matcher atendem das Um espaço, seguido de duas letras minúsculas Um espaço, seguido de uma letra
mais simples às mais complexas situações maiúscula, seguido de um ponto
envolvendo expressões regulares. No en-
tanto, outras classes do Java SE também
suportam essas expressões, como a própria No me s válidos de aco rdo com a exp ress ão N ome s invá lidos de acord o co m a expre ssã o
classe String, que oferece funcionalidades
Maria Aparecida
suficientes em muitos casos. Veja alguns Maria A. Soares
maria aparecida
MARIA APARECIDA
Maria Aparecida Soares
métodos da classe String   que suportam Maria da Silva
Maria A Soares
expressões regulares:
•   Strin g replaceAll (String regex, Strin g Figura 1. Um exemplo de expressão regular
replacement)  – Substitui todos os “pedaços”
da string que obedecem ao padrão estabe-
lecido pela expressão regular em regex.
• String replaceFirst (String regex, String
replacement)  – Substitui a primeira ocorrên-
cia dentro da string que obedeça o padrão
estabelecido pela expressão regular.

Vamos apresentar agora a aplicação de


expressões regulares no nosso exemplo,
realizando a substituição do nome do
cliente no e-mail e também a verificação
do telefone. Se o telefone for considerado
inválido pela expressão regular constru-
ída, iremos inserir um texto no e-mail
solicitando que o cliente atualize seus
dados cadastrais.

Aplicando as expressões regulares


Para permitir a personalização das men-
sagens vamos fazer algumas alterações im- Figura 2. E-mail enviado em HTML com anexo teste.txt

Edição 39 •  Java Magazine 67

 jm39.indb 67 15/8/2006 18:19:57


Uma Mala Direta com JavaMail

portantes na classe MalaDireta, mostradas na com oito dígitos, sendo que o DDD pode MalaDireta, que realiza a substituição do
Listagem 7. Observe que no texto do e-mail estar colado ao número, ou separado por nome e a verificação do telefone.
incluímos no conteúdo HTML do e-mail a um espaço ou hífen. De acordo com essa
ser enviado “pedaços” de texto para serem expressão, os seguintes números seriam 1.   Utilizamos o método replaceAll() da
substituídos: %cliente.nome% e %cliente. considerados válidos: classe String. Este método recebe uma string
telefone% . Vamos utilizar expressões re- • 11-31711987 contendo uma expressão regular e a string
gulares através do método replaceAll() da • 11 31711987 que deve substituir o texto localizado. Neste
classe String para substituir esses pedaços • 1131711987 caso a expressão contém apenas caracteres,
pelo nome e o telefone do cliente. ou seja, não utilizamos símbolos espe-
Observe que este é o caso mais simples  Já os números a seguir não estariam de ciais como  /d, ou [A-Z] , apenas uma string
de uso de expressões regulares, em que acordo com a expressão: %cliente.nome% (note que a porcentagem
o padrão fornecido é exatamente igual • 31711987 não é um símbolo especial de expressão
ao texto que deve ser localizado. Para • 222323 regular).
demonstrar mais recursos de expressões • 7632323r34
conteudo = conteudo.replaceAll(
regulares, vamos verificar se um tele-   “%cliente.nome%”,destinatario.getNome());
fone de cada cliente segue um padrão Agora vamos analisar passo a passo o
estabelecido, de acordo com a seguinte novo método personalizarConteudo()  da classe 2.  Compilamos a expressão regular que
expressão:

\\d{2}(-|\\s)??\\d{8}
Listagem 6. Trecho demonstrando o uso da expressão regular ilustrada na Figura 1.

Essa expressão ve- Pattern p = Pattern.compile(


“[A-Z][a-z]+((\\s[A- Z][azz]+)|\\s[a-z]{2}|\\s[A-Z].)+”);
rifica se o telefone String nomeCorreto = “Maria Aparecida da Silva”;
contém um DDD String nomeCorreto1 = “Maria Aparecida Silva”;
String nomeCorreto2 = “Maria A. Soares”;
com dois dígitos String nomeIncorreto = “maria aparecida”;
e um número String nomeIncorreto2 = “MARIA APARECIDA”;
String nomeIncorreto3 = “Maria”;

String[] nomes = {nomeCorreto, nomeCorreto1, nomeCorreto2,


nomeIncorreto, nomeIncorreto2, nomeIncorreto3};

for (String nome: nomes){


Matcher m = p.matcher(nome);
if (m.matches()){
System.out.println(nome+”: está no padrão”);
}
  else{
System.out.println(nome+”: não está no padrão”);
}

Listagem 7. Alterações na classe MalaDireta para personalização das mensagens

public class MalaDireta {


  //...

public static void main(String[] args) throws Exception {


String assunto = “Teste de envio de e-mail do projeto MalaDireta”;
StringBuilder conteudo = new StringBuilder();

conteudo.append(“<!DOCTYPE html PUBLIC ‘-//W3C//DTD HTML 4.01 Transitional//EN’>”);


conteudo.append(“<html><head> <title>Teste de envio de e-mail</title></head>”);
conteudo.append(“<body> Caro(a)   %cliente.nome% ,<br> <br>”);
conteudo.append(“Voc&ecirc; est&aacute; cadastrado(a) no nosso sistema e esta “);
conteudo.append(“&eacute; uma mensagem que testa o envio de e-mail para a sua conta.<br>”);
conteudo.append(“<br> Se voc&ecirc; n&atilde;o quer mais receber nossos e-mails, por “);
conteudo.append(“favor escreva para <a href=’mailto:contato@globalcode.com.br’>”);
conteudo.append(“contato@globalcode.com.br</a> solicitando descadastramento.<br>”);
  conteudo.append(“<br> %corrigir_telefone% <br>”);
conteudo.append(“Pedimos a gentileza de responder este e-mail confirmando o”
+“recebimento.<br>”);
  //...
}

68  Java Magazine • Edição 39

 jm39.indb 68 15/8/2006 18:20:16


irá validar o telefone com um método String telefone = destinatario.getTelefone(); if (m.matches()){
estático da classe Pattern. Matcher m = p.matcher(telefone); System.out.println(“O “+telefone+” está no padrão”);
conteudo = conteudo.replaceAll(“%corrigir_telefone%”,””);
Pattern p = Pattern.compile(“\\d{2}(-|\\s)??\\d{8}”); } else {
4. Se o telefone estiver no padrão, a cadeia System.out.println(“O “+telefone+” não está no padrão”);
3. Depois Verificamos se o telefone do de caracteres “%corrigir_telefone%” será conteudo = conteudo.replaceAll(“%corrigir_
destinatário está de acordo com a expres- removida do e-mail, através da substituição telefone%”,”<br>Seu telefone “+ telefone+
são regular compilada. Esta operação por uma string vazia. Se o telefone não es- “ parece estar incorreto /*... */ “);
}
retorna um objeto da classe Matcher, sobre tiver no padrão esperado, será substituído
o qual podemos chamar o método matches() por um texto que solicita que o cliente entre
que retorna verdadeiro ou falso. em contato para atualização do cadastro. O método personalizarConteudo()  deve ser
chamado na classe EnviadorDeEmail. Veja na
Listagem 8 o trecho alterado nesta classe.
A Figura 3 mostra um exemplo de e-mail
enviado com o conteúdo personalizado.

Conclusões
Neste artigo, vimos como usar recursos
essenciais da API JavaMail aplicando-os
em uma situação real. Usamos também
expressões regulares, um recurso podero-
so que é suportado por classes específicas
da API do Java SE, bem como a classe
String.

 java.sun.com/products/javamail/ 
Página oficial do JavaMail.
 java.sun.com/docs/books/tutorial/extra/ 
 regex/intro.html 
Figura 3. E-mail com conteúdo personalizado
Tutorial sobre Expressões Regulares
Listagem 8. Alterações na classe EnviadorDeEmail para personalização
public class EnviadorDeEmail {  javamagazine.com.br/downloads/jm39/
  //...  jm-maladireta.zip
private void adicionarConteudoEAnexo(Message message,
String conteudo, Cliente destinatario, String arquivo)
throws MessagingException, AddressException
{  Yara M. H. Senger
conteudo = personalizarConteudo(conteudo, destinatario); ( yara@globalcode.com.br ) é
//... Igual à Listagem 1 formada em Ciências da Com-
} putação na USP em São Carlos,
private String personalizarConteudo(String conteudo, Cliente destinatario){ especialista em desenvolvimento web;
// personalização do conteúdo possui as certificações SCJA, SCJP e
conteudo = conteudo.replaceAll(“%cliente.nome%”,destinatario.getNome());
SCWCD. Atualmente é Instrutora e Diretora Educacional
Pattern p = Pattern.compile(“\\d{2}(-|\\s)??\\d{8}”);
String telefone = destinatario.getTelefone(); da Globalcode, criadora e coordenadora de diversos
Matcher m = p.matcher(telefone); cursos das carreiras Academia do Java e Academia do
if (m.matches()){
System.out.println(“O “+telefone+” está no padrão”); Web Developer.
conteudo = conteudo.replaceAll(“%corrigir_telefone%”,””);
}
else {
System.out.println(“O “+telefone+” não está no padrão”); Ana Abrantes
conteudo = conteudo.replaceAll(“%corrigir_telefone%”,
“<br>Seu telefone “+ telefone + “ parece estar incorreto,”
(ana.abrantes@globalcode.com.br ) é
+ “solicitamos a gentileza de entrar em contato” desenvolvedora Java na Globalcode,
+ “para atualização do cadastro<br>”);
} co-autora do curso de JasperReports/
return conteudo; iReport e possui algumas certificações
}
//... em Java (SCJA, SCJP e SCWCD). É formada pela FATEC-
} SP e atua na área de informática há mais de 15 anos.

Edição 39 •  Java Magazine 69

 jm39.indb 69 15/8/2006 18:20:18


RIA com Open
Construindo Rich Internet Applications

C om o crescimento da internet, a
distribuição e a manutenção de
aplicações tornaram-se muito
mais simples e rápidas. Utilizando um
 brows er, o usuário passou a aces sar o
premissa de rodar na máquina do usu-
ário todo o processamento de interface
gráfica, deixando para o lado do servidor
o processamento da lógica de negócio.
O lado cliente das aplicações RIA pode
aplicação de exemplo funcional.

Funcionamento do OpenLaszlo
Aplicações OpenLaszlo são definidas em
documentos na linguagem LZX, baseada
último release sem ter que se preocupar ser implementado de diversas maneiras, em XML. A engine de renderização do
com versionamento ou procedimentos dentre elas applets Java, páginas DHTML, OpenLaszlo (que chamaremos de “Ser-
de instalação. Mas um efeito colateral do aplicativos Macromedia Flash, JavaScript vidor OpenLaszlo”) é uma aplicação Java
paradigma web não tardou a aparecer. e outros. para web, portanto deve ser instalada em
Interfaces mais pobres e menos intuitivas O OpenLaszlo é uma plataforma madura um container web como o Tomcat, ou num
substituíram os sistemas “fat-client” antes de desenvolvimento RIA baseada em Ja- servidor de aplicações Java EE.
largamente utilizados, forçando o usuário vaScript e XML com suporte à depuração Em tempo de execução, o servidor Open-
a se acostumar com o “clicar e esperar” das de aplicações, que renderiza aplicações Laszlo “compila” os arquivos-fonte LZX
interfaces web. em Macromedia Flash. para um aplicativo Flash ( .SWF) que é
O conceito de Rich Internet Application Neste artigo va- enviado ao cliente. Na máquina cliente,
(RIA) é uma das respostas a este problema. mos apresentar os o browser roda o aplicativo (às vezes
Permitindo que o usuário interaja com a principais concei- chamado de “Cliente OpenLaszlo”)
aplicação como se esta fosse um sistema tos dessa platafor- normalmente, utilizando o plug-in Flash.
desktop tradicional, o RIA baseia-se na ma e construir uma A Figura 1 ilustra a organização dos com-
ponentes-chave do OpenLaszlo.

Obtendo e configurando o
OpenLaszlo Server
O download do OpenLaszlo pode ser fei-
to em openlaszlo.org/download, selecionan-
do-se a plataforma desejada (Windows, Li-
nux ou Mac OS). Para outras plataformas,
ou para fazer a instalação manualmente,
obtenha o pacote identificado como “Dev
Kit”. O download já inclui o Tomcat, com o
servlet do Servidor OpenLaszlo configura-
do, bem como documentação e exemplos.
Para verificar a instalação, inicie o
servidor com um duplo-clique no
arquivo server\lps-3.3.3\lps\utils\
startTomcat, dentro do diretório raiz
do OpenLaszlo (ajustando sempre o

70  Java Magazine • Edição 39

 jm39.indb 70 15/8/2006 18:20:28


  Laszlo Turbine suas interfaces gráficas
e maximize a interatividade
com o usuário utilizando
uma plataforma open source
baseada em XML e Java
com Flash e Java
ANDRÉ LUÍS MONTEIRO

número da versão para o seu caso). Após Salve-o com extensão . lzx (ex.: OlaMundo. Como se trata de uma linguagem orien-
iniciá-lo, aponte o browser para: http://  lzx) no diretório server\lps-3.3.3\my-apps, tada a objetos, a LZX nos permite definir
localhost:8080/lps-3.3.3/examples/hello.lzx. e teste a aplicação apontando o browser classes e métodos e usar herança e poli-
Se tudo estiver OK, deverá aparecer a para http://localhost:8080/lps-3.3.3/my- morfismo. Tomemos como exemplo a classe
mensagem “Hello Laszlo!”. (Para parar o apps/OlaMundo.lzx. meuBotao. Ela herda da classe button e define
servidor, dê um duplo-clique no arquivo Toda aplicação OpenLaszlo começa com um método que será disparado pelo evento
server\lps-3.3.3\lps\utils\stopTomcat. ) a tag <canvas>, que renderiza o painel onclick. O corpo do método é todo escrito
principal. Já a tag <text>  renderiza um em JavaScript e faz referência a outros
Primeiro aplicativo OpenLaszlo texto qualquer. objetos do documento, através dos seus
Criar uma aplicação “Olá Mundo” no respectivos ids. Um id permite que um ob-
OpenLaszlo é muito simples. Comece Aplicação de exemplo  jeto seja referenciado de qualquer parte do
criando um arquivo texto com o conteúdo Como exemplo, iremos desenvolver uma documento LZX. Por exemplo, na chamada
a seguir: aplicação simples de calculadora, cujos visor.setAttribute(‘text’, ‘ ‘) visor é o id.
cálculos são realizados no lado do servidor Ao desenvolver aplicações OpenLaszlo,
<canvas width=”200” >
<text>Ola Mundo!</text> por uma página JSP. Os cálculos a serem geralmente criamos primeiro toda a es-
</canvas> processados são submetidos ao Tomcat trutura estática da aplicação, definindo os
usando parâmetros HTTP, e o resultado é componentes visuais e o layout, e depois
um documento XML como a seguir: adicionamos o comportamento e o proces-
samento de eventos. Para a calculadora,
<resultado>25</resultado>
definimos uma janela, instanciando uma
O objetivo é demonstrar como uma apli- classe window. Dentro dela adicionamos
cação cliente criada com o OpenLaszlo uma caixa de texto edittext (o visor), e os
(a interface gráfica da calculadora) pode  botões do tipo meuBotao. Em arquivos LZX,
submeter dados para processamento no o layout é definido pela classe lzLayout e
servidor e obter o resultado mostrando-o suas classes filhas. No exemplo utilizamos
para o usuário. Com as técnicas mostra- a classe simplelayout  que estende lzLayout  e
das, você poderá criar aplicações RIA em
Flash que acessam recursos corporativos
rodando, por exemplo, em um Browser Cliente
servidor Java EE.
Plug-in Flash

Componentes gráficos e layout Cliente OpenLaszlo


O primeiro passo é criar um arqui-
vo LZX de acordo com a Listagem 1.
Analisando o código, você verá que os
elementos XML representam compo-
nentes visuais, e definições de classes HTTP
e de funções JavaScript (estas últimas
em tags <script>). Outro item que chama
atenção é o fato de as classes usadas como Container Web / Servidor Java EE
elementos XML começarem com letra
minúscula, por exemplo button, o que Servidor OpenLaszlo
pode soar um tanto estranho para
quem está acostumado às conven-
ções do Java. Figura 1. Arquitetura da plataforma OpenLaszlo

Edição 39 •  Java Magazine 71

 jm39.indb 71 15/8/2006 18:20:40


RIA com Open Laszlo

organiza os componentes na tela, verti- Definindo o comportamento


cal e horizontalmente.  Com o layout definido e a parte estática
Além desses componentes gráficos, cria- construída, partimos para o comporta-
mos também as views que são “recipientes” mento da aplicação e a interação com
para outros componentes (similares aos o usuário. A começar com os botões, o
containers do Swing/AWT) e ajudam na leitor notará que todos são instâncias
organização visual. A Figura 2  ilustra da classe meuBotao  criada no início do
como foi feito o layout da calculadora arquivo LZX, e que contêm um método
– os retângulos em laranja são views. A para o evento onclick definido no elemento
Figura 3  mostra a calculadora em sua <method event=”onclick”>. Ou seja, toda ins-
versão final. tância desta classe invocará este método

Listagem 1. calculadora.lzx 

<canvas width=”400” debug=”false”> if (valorVisor == ‘0’) {


 <simplelayout axis=”y” spacing=”5”/> visor.setAttribute(‘text’, ‘ ‘);
 <script> }
<![CDATA[ visor.setAttribute(‘text’, valorVisor + textoBotao);
var param1 = 0; }
var param2 = 0;   </method>
var operacao = “”;  </class>

function chamaServidor() {  <text id=”visorResultado” bgcolor=”#ffcccc” width=”0”>


anm.doStart(); <animatorgroup id=”anm2” start=”false” process=”simultaneous”>
var p = new LzParam(); <animator attribute=”wi dth” to=”150”
p.addValue(“ par1”, param1, true); duration=”2500” motion=”linear”/>
p.addValue(“ par2”, param2, true); <animator attribute=”op acity” from=”0” to=”100”
p.addValue(“ op”, operacao, true); duration=”2000”/>
  myData.setQueryString(p);   </animatorgroup>
myData.doRequest();  </text>
}
 <window id=”janela”>
function mostraResultado() { <animatorgr oup id=”anm” start=”false ” process=”simu ltaneous”>
var resultado = myData.getPointer().xpathQuery( <animator attribute=”x”
  “RESULTADO/text()”); to=”100” duration=”1000” motion=”linear”/>
  Debug.write(resultado); <animator attribute=”y” to=”100” duration=”100 0”/>
  visorResultado.setAttribute(   </animatorgroup>
‘text’, param1 + operacao + param2 + “=” + resultado); <view bgcolor=”#666699” id=”minhaView”>
anm2.doStart(); <simplelayou t axis=”y” spacing=”5”/ >
} <edittext id=”visor” text=” “ enabled=”fal se”/>
<view bgcolor=”#666 699”>
function animaJanela() { <simplelayout axis=”x” spacing=”5”/>
tsp = janela.getAttribute(‘opacity’); <meuBotao text=”1”/>
<meuBotao text=”2”/>
if (tsp < 1) <meuBotao text=”3”/>
janela.animate(‘opacity’,+1,3500,true); <meuBotao text=”X”/>
  else   </view>
  janela.animate(‘opacity’,-1,3500,true); <view bgcolor=”#666 699”>
} <simplelayout axis=”x” spacing=”5”/>
]]> </script> <meuBotao text=”4”/>
<meuBotao text=”5”/>
 <dataset name=”myData” id=”myData” <meuBotao text=”6”/>
src=”http://localhost:8080/calculadora.jsp” <meuBotao text=”+”/>
  ondata=”mostraResultado()”/>   </view>
 <class name=”meuBotao” extends=”button” width=”30”> <view bgcolor=”#666 699”>
<method event=”oncli ck”> <simplelayout axis=”x” spacing=”5”/>
var textoBotao = this.getAttri bute(‘text’) ; <meuBotao text=”7”/>
var valorVisor = visor.getAttribute(‘text’); <meuBotao text=”8”/>
<meuBotao text=”9”/>
if (textoBotao == ‘+’ || textoBotao == ‘-’ ||
<meuBotao text=”-”/>
textoBotao == ‘/’ || textoBotao == ‘X’)
  </view>
{
<view bgcolor=”#666 699”>
operacao = textoBotao;
<simplelayout axis=”x” spacing=”5”/>
param1 = valorVisor.substring(1);
<meuBotao text=”C”/>
visor.setAtt ribute(‘text’ , ‘ ‘);
<meuBotao text=”0”/>
} else if (textoBotao == ‘=’) { <meuBotao text=”=”/>
param2 = valorVisor.substring(1); <meuBotao text=”/”/>
visor.setAtt ribute(‘text’ , ‘ ‘);   </view>
chamaServidor();   </view>
}  </window>
else if (textoBotao == ‘C’) {
visor.setAtt ribute(‘text’ , ‘ ‘);  <button text=”Clique aqui” onclick=”animaJanela()”/>
} else { </canvas>

72  Java Magazine • Edição 39

 jm39.indb 72 15/8/2006 18:20:48


Edição 39 •  Java Magazine 73

 jm39.indb 73 15/8/2006 18:20:51


RIA com Open Laszlo

ao receber o evento onclick. O método Para rodar a aplicação, basta copiar o


seta as variáveis param1, operacao e param2 arquivo calculadora.lzx   para o diretório
de acordo com a operação indicada pelo server\lps-3.3.3\my-apps de sua instalação
usuário. do OpenLaszlo. É necessário também
  Para submeter os dados para o JSP, instalar o JSP no Tomcat. Para simplificar,
utilizamos a classe dataset , que permite vamos colocá-lo no diretório server\tom-
enviar parâmetros HTTP e recuperar cat-5.0.24\webapps\ROOT (a versão do
a resposta formatada em XML. Note Tomcat pode variar). Além disso, como o
que na definição do dataset myData, o  JSP usa a JSTL precisamos do  jstl.jar e do
evento ondata chama a função JavaScript standard.jar  (disponíveis em  jakarta.apa-
mostraResultado() . Dessa forma, assim que o che.org/site/downloads/downloads_taglibs.
servidor retornar a reposta, a função será html, na seção “Standard 1.1 Taglib”).
invocada. O código do JSP é bem si mples, Copie estes dois JARs para o diretório
conforme ilustra a Listagem 2. Server\tomcat-5.0.24\webapps\ROOT\
Após os dados serem submetidos e a WEB-INF\lib.
resposta do cálculo ser rec uperada, uma Pronto. Basta apontar o browser para
animação faz com que a calculadora se http://localhost:8080/lps-3.3.3/my-apps/ 
mova para a direita, para que o resultado calculadora.lzx para ver o exemplo fun-
Figura 2. Layout da calculadora. seja mostrado no plano de fundo. Isso é cionando.
possível graças ao método animate() da
classe lzNode (que é a superclasse de todos Conclusões
os componentes gráficos). Este método Neste artigo apresentamos o básico do
permite que alterações nas propriedades OpenLaszlo, da instalação à criação de
de um objeto sejam feitas num espaço de uma aplicação funcional que faz uso de
tempo determinado. A nossa aplicação código Java no servidor. O OpenLaszlo é
contém algumas animações, como a de- uma plataforma que vem ganha ndo apoio
finida no elemento <animatorgroup> . Este crescente da comunidade de desenvolve-
elemento agrupa um conjunto de anima- dores e integra-se especialmente bem com
ções relacionadas, que são executadas a tecnologia Java. A plataforma está em
simultaneamente ou em seqüência. franca evolução, como atesta o novo pro-
A função animaJanela() também utiliza  jeto “Legals” com release final previsto
recursos de animação. No código dessa para o final deste ano, e que suportará a
função, observe que o método animate() geração para Flash 9 e DHTML.
recebe quatro parâmetros: o atributo que
será animado, o valor que o atributo de-
verá receber; o tempo total da animação, OpenLaszlo.org
para que o atributo assuma o novo valor; Site oficial do OpenLaszlo que contém
e um booleano, indicando se a alteração tutoriais e um fórum com participação da
Figura 3. Versão final da calculadora. do atributo deve ser relativa ou não. comunidade.
 laszlosystems.com
Listagem 2. calculadora.jsp Empresa que criou o Laszlo original. Disponibiliza
<%@ page contentType=”text/xml” %>
aplicações comerciais bastante profissionais.
<%@ taglib prefix=”c” uri=”http://java.sun.com/jstl/core” %>
 laszlomail.com
<c:set var=”par1” value=”${param[‘par1’]}”/> Exemplo de uma aplicação feita em Laszlo.
<c:set var=”par2” value=”${param[‘par2’]}”/>
<c:set var=”op” value=”${param[‘op’]}”/>
<c:if test=”${op==’+’}”>  javamagazine.com.br/downloads/jm39/
<c:out value=”<RESU LTADO>${(par1 + par2)}</RESU LTADO>” escapeXml=”f alse”/>  jm-openlaszlo.zip
</c:if>
<c:if test=”${op==’-’}”>
<c:out value=”<RESU LTADO>${(par1 - par2)}</RESU LTADO>” escapeXml=”f alse”/>
</c:if>
<c:if test=”${op==’/’}”> André Luís Monteiro
<c:out value=”<RESU LTADO>${(par1 / par2)}</RESU LTADO>” escapeXml=”f alse”/> (andre.monteiro@fiveware.com.
</c:if>
<c:if test=”${op==’X’}”> br ) é consultor sênior da Fiveware
<c:out value=”<RESU LTADO>${(par1 * par2)}</RESU LTADO>” escapeXml=”f alse”/> Solutions e tem as certificações
</c:if> SCJP, SCWCD e SCEA.

74  Java Magazine • Edição 39

 jm39.indb 74 15/8/2006 18:20:52


 jm39.indb 75 15/8/2006 18:20:56

Você também pode gostar