Sie sind auf Seite 1von 111

Ateno: por essa edio ser muito antiga no h arquivo PDF para download.

Os artigos dessa edio esto disponveis somente atravs do formato HTML. Editorial Esta edio cobre uma gama variada de assuntos importantes para desenvolvedores Java em vrios nveis. O Struts 2, que est nas suas primeiras verses mas j chama muita ateno dos programadores, o destaque desta capa. No artigo, alm de serem descritos as principais mudanas do novo Struts (que no so poucas), criada uma aplicao completa que faz uso dos novos recursos, inclusive de validao de dados com AJAX. O Struts 2 ainda est em franca evoluo, mas aqui voc ter uma viso antecipada do que est pronto, e ver na prtica as reais vantagens da nova verso do framework que domina o cenrio da programao web, por sua qualidade, utilidade e versatilidade. Comeamos tambm uma srie sobre a tecnologia JDBC, explorando esse assunto essencial para praticamente qualquer desenvolvedor Java. A persistncia de dados o corao da maioria dos sistemas de informao, e o JDBC um padro to consolidado que hoje imitado mesmo pelos mais rduos concorrentes do Java. A primeira parte da srie aborda os conceitos bsicos da tecnologia, descreve o histrico de suas verses, e mostra como preparar e testar o ambiente-e, claro, como realizar as primeiras consultas e mudanas no banco de dados. O Swing considerado o toolkit grfico mais completo e bem-estruturado do mercado. Com ele possvel criar interfaces grficas que desafiam qualquer limite de forma funcionalidade. Talvez por essa versatilidade e poder, o Swing tem uma curva mais ngreme de aprendizado, assustando desenvolvedores iniciantes, especialmente ao trabalhar com componentes complexos como JList, JComboBOx e JTable. Nesta edio, voc com ela a conhecer os detalhes por trs desses elementos mais sofisticados do Swing, entendendo sua arquitetura e explorando na prtica a versatilidade que toolkit tem a oferecer. O agendamento de tarefas tratado em dois artigos que se complementam, tornando esta edio uma referncia completa sobre o assunto. Voc ver como usar da melhor forma os recursos includos no Java SE e no Java EE para agendar tarefas (peridicas ou no), e poder testar tudo com uma aplicao Swing reutilizvel, que usa vrias boas prticas do desenvolvimento. E para ir alm do oferecido nas plataformas Java obtendo o mximo de controle no agendamento de tarefas, voc ver como usar o framework open source Quartz, do OpenSymphony (o mesmo grupo criador do WebWork, que se juntou ao Struts para criar o Struts 2). Veja tambm nesta edio um tutorial crtico sobre o Java EE5. Ao mesmo tempo em que voc cria uma aplicao completa passo a passo, usando EJB 3.0, Java Pesistence API e JSF, e o NetBeans

5.5, conhece detalhes prticos sobre as novidades no Java EE 5, e como elas afetam a produtividade e os padres de desenvolvimento Java. E voc vai conferir uma ferramenta de qualidade que analisa seu cdigo procura de possveis problemas e apresenta mincias sobre as questes identificadas. O FindBugs uma mo na roda para quem est sempre procura de aumentar a qualidade e a performance de sua aplicaes. Boa Leitura!

Struts 2: primeiros passos Prepare-se para a Evoluo do Mais Popular Framework Web Explorando as principais novidades com um exemplo completo: interceptores, novas tags, componentes AJAX e muito mais produtividade. Os Struts 2 o resultado da juno do antigo Struts com outro excelente framework web chamado WebWork. Como ambos possuam arquitetura semelhante e trabalhavam como o paradigma de aes, as equipes de desenvolvimento decidiram unir o melhor dos dois mundos. Neste artigo abordamos a nova arquitetura do Struts, discutindo as mudanas mais significativas, e construmos uma aplicao completa de cadastro de usurios com validao cliente/servidor, utilizando as novas tags e componentes AJAX. Para a construo de exemplo, utilizaremos o IDE Eclipse com o plug-in WTP (Web Tools Project). possvel fazer o download do IDE j com o WTP integrado, em eclipse.org/webtools. Se voc j tem o Eclipse instalado, pode tambm baixar e instalar o WTP a partir do site de atualizaes do Callisto. Todo o cdigo aqui demonstrado est disponvel no site da Java Magazine. O Struts 2, no momento em que escrevo, ainda est no processo de construo do seu primeiro release, portanto a criao de um projeto compatvel pode ser um pouco tortuosa. Ento, para facilitar o acompanhamento e garantir que tudo funcione como descrito aqui, recomendo fazer o download do site da revista e importar o projeto completo para o Eclipse. Struts 2 na prtica Nada melhor para conhecer o novo Struts do que us-lo na pratica. Ento vamos diretamente nossa aplicao de exemplo. Para saber detalhes sobre os novos conceitos que surgiro no caminho, como Interceptors e Results, veja os quadros Mudanas e novidades no Struts 2 e Tipos de Interceptors. Utilizaremos como banco de dados o MySQL. A Listagem 1 mostra o script para criao das trs tabelas do exemplo. Note que um usurio possui nome, data de nascimento, um perfil e um departamento, e que as tabelas de departamento e perfil contm apenas um cdigo e uma descrio. A aplicao final em execuo dever se parecer com a Figura 1. Observe que a interface grfica consiste em um formulrio de incluso e uma lista de usurios cadastrados. O formulrio possuir validao no lado do cliente (atravs de chamadas AJAX) e tambm validao do servidor.

Figura 1. Layout da aplicao de exemplo Aps fazer o download do projeto no site Java Magazine, importe-o para workspace do Eclipse, acessando File/Menu>Web>Existing Project into Workspace. O projeto dever parecer com a Figura 2.

Figura 2. Estrutura do projeto aps importao Listagem 1. Script de criao do banco de dados. CREATE TABLE departamento (

id bigint(20) NOT NULL auto_increment , nome varchar(30) NOT NULL , PRIMARY KEY (id) , UNIQUE KEY id_departamento (id) ); CREATE TABLE perfil ( id int(11) NOT NULL auto_increment , nome varchar(30) NOT NULL , PRIMARY KEY (id) ); CREATE TABLE usuario ( nome varchar(45) , id bigint(20) NOT NULL auto_increment, id_departamento bigint(20) NOT NULL, data_nascimento datetime , PRIMARY KEY (id) ); web.xml e entidades Vamos ento descrio do projeto mostrando como cri-lo passo a passo. Primeiro precisamos alterar o arquivo WEB-INF/web.xml (Listagem 2). No Struts 1.x era necessrio configurar um servlet para tratar todas as requisies (normalmente *.do). no Struts 2 configuramos um filtro: <filter> <filter-name>filtroStruts</filter-name> <filter-class> org.apache.struts2.dispatcher.ilterDispatcher </filter-class> </filter> <filter-mapping> <filter-name>filtroStruts</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> Observe que o padro de URL que adotamos aqui foi /*, o que significa que o filtro ir interceptar todas as requisies da aplicao. No web.xml voc v que tambm configuramos um servlet chamado dwr que ir interceptar as chamadas AJAX para validao (veja mais sobre isso no final do artigo). Tendo o arquivo web.xml ajustado, construmos as classes Usuario, Perfil e Departamento, mostradas nas Listagens 3,4 e 5. Listagem 2. Configurao do filtro-WEB-INF/web.xml <web-app> <filter> <filter-name>filtroStruts</filter-name> <filter-class> org.apache.struts2.dispatcher.FilterDispatcher </filter-class> </filter> <filter-mapping> <filter-name>filtroStruts</filter-name>

<url-pattern>/*</url-pattern> </filter-mapping> <servlet> <servlet-name>dwr</servlet-name> <servlet-class>uk.ltd.getahead.dwr.DWRServlet</servlet-class> <init-param> <param-name>debug</param-name> <param-value>true</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>dwr</servlet-name> <url-pattern>/dwr/*</url-pattern> </servlet-mapping> </web-app> Listagem 3. Entidade Usurio Usuario.java public class Usuario{ public Long id; public String nome; public Departamento departamento; public Perfil perfil; public Date dataNascimento; public Usuario() {} // getters/setters } Listagem 4. Entidade Perfil Perfil.java public class Perfil { public Integer id; public String nome; public Perfil(){} //...getters/setters } Listagem 5. Entidade Departamento Departamento.java public class Departamento { public Integer id; public String nome; public Departamento() { }

//...getters/setters } Action de persistncia A action que far o controle da persistncia do usurio ser UsuarioAction (Listagem 6). Os seguintes so os atributos da classe: Usurio usuario O objeto que ser instanciado pelo Struts e preenchido com os valores enviados pelo formulrio web. O Struts cuida da instanciao e do mapeamento entre os campos do formulrio e os atributos do objeto. Collection<Perfil>perfis A lista de perfis utilizada para popular um <select> no formulrio web. O Struts obtm essa lista atravs do mtodo getPerfis() da Action no momento de gerao do formulrio. Collection<Departamento>departamentos Lista tambm utilizada para popular um <select> no formulrio (o Struts obtm a lista usando getDepartamento()). A classe UsuarioAction estende ActionSupport e implementa Peparable. Fizemos isso para reutilizar funcionalidades oferecidas pelo Struts; no entanto a Action no precisa depender de outras classes ou interfaces. A classe ActionSupport fornece alguns mtodos e atributos importantes para facilitar o desenvolvimento, como: GetText(String chave) Retorna a string correspondente do ResourceBundle, e muito til para internacionalizao. GetActionMessages() Retorna as mensagens de erro referentes validao da Action. Por implementar Preparable, a nossa Action possui o mtodo prepare(), o qual no exemplo popula as listas de perfis e de departamentos. Este mtodo chamado antes de qualquer regra de controle que a Action possa ter. Veja sua implementao: public void prepare() { perfis = ServicoDeDados.get.Perfis(); departamentos = ServicoDeDados.getDepartamentos(); } A classe ServicoDeDados (no listada mas includo no download) responsvel em fazer os acessos ao banco de dados. Por simplicidade, sua implementao utiliza apenas JDBC diretamente. Para uma aplicao em produo, recomendo implementar o acesso a dados com o design pattern DAO (ou frameworks de persistncia como Hibernate). Vejamos mais dois mtodos de UsuarioAction. O mtodo salvar () ser chamado quando for postado o formulrio web com os dados do usurio: public String salvar () { ServicoDeDados.saveUsuario(usurio); return SUCCESS; } O mtodo faz a gravao dos dados do usurio no banco de dados atravs da classe ServicoDeDados. Depois, como nas Actions do antigo Struts, necessrio definir qual o retorno da aplicao. A mudana no Struts 2 que para isso agora informamos apenas uma string que ser o Result. No caso, retornamos a constante string SUCCESS, que herdada de ActionSupport:

O mtodo input() de UsuarioAction somente retorna o Result SUCCESS. Usamos este mtodo para fazer um redirecionamento para a pgina JSP contendo o formulrio. A configurao inicial do Struts 2, faz com que alguns mtodos no sejam submetidos s regras de validao. Um deles o input(). Veremos adiante mais detalhes sobre o novo sistema de validao do Struts 2.

Listagem 6. Action responsvel em executar a persistncia do usurio UsuarioAction.java public class UsuarioAction extends ActionSupport implements Preparable{ private Usuario usuario; private Collection<Perfil> perfis; public Collection<Departamento> departamentos; public String salvar() { ServicoDeDados.saveUsuario(usurio); return SUCESS; } public String input() { return SUCESS; } public void prepare() { perfis = ServicoDeDados.getPerfis(); departamentos = ServicoDeDados.getDepartamento(); } //... getters/setters } Configurao O arquivo de configurao do Struts (struts-config.xml) foi renomeado para struts.xml e agora fica junto das classes da aplicao em WEB-INF/classes. O struts.xml do nosso exemplo mostrado na Listagem 7. Vamos analisar cada parte desse arquivo, comeando pelo elemento <include>: <include file = struts-default.xml/> Aqui inclumos as configuraes padro do Struts com os tipos de Result disponveis e os Interceptors que acompanham o framework. Normalmente voc ira fazer essa incluso para reutilizar as configuraes bsicas do Struts. Depois encontramos: <package name = default extends = struts-default> Os packages (pacotes) so uma nova forma de organizar e agrupar as configuraes, definindo Actions, Results e Interceptors. Pacotes de configuraes podem tambm ser estendidos atravs do atributo extends. Note que nosso pacote declarado no trecho acima estende o pacote de configuraes padro, definido no arquivo struts-default.xml (este arquivo est dentro da biblioteca struts2-core.jar).

Prosseguimos no struts.xml, encontramos a definio das Actions. Um elemento <action> configura uma Action e tem os atributos name, class e method: <action name = inserirUsuario!* class = br.com.jm.action.UsuarioAction method = {1}> O atributo name define o mapeamento da Action. Observe que no exemplo ele possui o caractere * como sufixo. Como no antigo Struts, as Actions podem utilizar caracteres coringa (wildcards) nos seus mapeamentos. A diferena que agora se pode tambm usar coringas para definir qual mtodo ser chamado na Action. Por isso definimos o mtodo input() e salvar () na classe UsuarioAction. Por exemplo, se a chamada da Action for inserirUsuario! input. action o mtodo input() ser chamado. Caso seja inserirUsuario!salvar.action, ser a vez do mtodo salvar(). Esse recurso substitui as DispatchActions do Struts 1.x e traz maior flexibilidade ao desenvolvimento. E para o Struts saber qual mtodo chamar, configuramos o atributo method com o valor {1} que uma referencia ao caractere * do mapeamento da Action. Lembrando que o que estiver entre o caractere ! e a extenso .action definir o nome do mtodo a ser invocado. Em seguida so definidos os Results, que no exemplo so bem simples. Todos eles redirecionam o usurio a um arquivo JSP, formUsuario.jsp: <result name = success>/formUsuario.jsp</result> <result name = input>/formUsuario.jsp</result> O Result input utilizado para redirecionamento em caso de erro de validao. a mesma idia do atributo input das Actions na verso anterior do Struts. Continuando, note que a Action possui um Interceptor configurado: <interceptor-ref name = paramsPrepareParamsStack"/> O objetivo dos Interceptors adicionar cdigos que sero executados antes ou depois das chamadas s Actions. O elemento <interceptor-ref> mostrado anteriormente faz uma referencia a uma pilha (stack) de Interceptors. As pilhas de Interceptors so apenas uma forma de organiz-los em grupo para facilitar a reutilizao da mesma configurao. No exemplo essa configurao tem o objetivo de adicionar Action regras de validao e uma chamada ao mtodo prepare() , que carregara dados necessrios para o formulrio. A definio de Interceptors esta dentro do arquivo struts-default.xml, que importamos no comeo do struts.xml. O nome dos Interceptors responsveis pela chamada ao mtodo prepare() e ao cdigo de validao, so respectivamente prepare e validation . Como ambos esto dentro da pilha paramsPrepareParamsStack , no precisamos fazer uma referencia direta a cada um. Veja mais detalhes e exemplos de Interceptors no quadro tipos de Interceptors. Listagem 7. Configurao do Struts struts.xml <struts> <include file=struts-default.xml/> <package name=default extends=struts-default> <action name=inserirUsuario!* class=br.com.jm.action.UsuarioAction method={1}> <result name=success>/formUsuario.jsp</result>

<result name=input>/formUsuario.jsp</result> <interceptor-ref name=paramsPrepareParamsStack/> </action> <action name=listaUsuarios class=brl.com.jm.action.ListaUsuariosAction method=listaUsuarios> <result name=success>/listaUsuarios.jsp</result> </action> </package> </struts> Action de Listagem A prxima Action configurada utilizada para a listagem de usurios: <action name = listaUsuario class = br.com.jm.action.ListaUsuariosAction method = listaUsuario > <result name = sucess>/listaUsuarios.jsp</result> </action > Esta Action possui apenas um Result que redireciona para um arquivo listaUsuarios.jsp . Seu mtodo de execuo listaUsuarios() e sua classe ListaUsuariosAction (Listagem 8). A classe da Action bem simples, contendo um mtodo listaUsuarios () que popula o atributo usuarios com public string listaUsuarios () { usuarios = ServicoDeDados.getUsuarios() ; return SUCCESS; } O arquivo listaUsuarios.jsp possui uma tag de listagem de usurios, <s:iteratorvalue = "usuarios" >. Esta tag utilizara o mtodo getUsuarios() fornecido pela Action para obter a lista dos usurios. Listagem 8. Action responsvel em popular a lista de Usuarios ListaUsuariosActions.java public class ListaUsuariosActions extends ActionsSupport { public Collection<Usuario> usuarios; public String lista Usuarios() { usuarios=ServicoDeDados.getUsuarios(); return SUCCESS; } // ... getters/setters } Construindo a parte grfica Tendo apresentado o detalhes de configurao da aplicao, podemos passar s pginas JSP: index.jsp, formUsuario.jsp e listaUsuarios.jsp.

index.jsp A pgina index.jsp (listagem 9), embora seja bem pequena, pouco diferente do usual. Nela utilizamos as novas tags do Struts, que esto muito mais poderosas. Alm de as tags estarem agora definidas em um s arquivo.tld, elas trazem uma srie de novas funcionalidades e novos componentes, alguns j com suporte a AJAX. Veja mais no quadro Novos componentes grficos. A primeira tag que voc ir notar no arquivo index.jsp ser a <s:url>, que apenas reescreve uma URL considerando o contexto em que a aplicao est sendo executada: <s:url value = /css/styles.css/> A prxima <s:head>, que define parte da seo HEAD do HTML. Esta tag escreve cdigos JavaScript ou CSS necessrios para alguns componentes grficos do Struts. Em nosso exemplo, como utilizaremos AJAX para validao do formulrio web, definimos que a tag deve adicionar o cdigo JavaScript atravs do atributo theme=ajax. <s:head theme = ajax/> O resultado da traduo da tag <s:head> ser uma seqncia de importaes de scripts. Veja uma dessas importaes: <scriplanguage = JavaScript type = text/javascript src = /struts2/struts/dojo//dojo.js></script> Neste caso, o arquivo JavaScript importado o dojo.js, do toolkit DOJO (veja links). Os novos componentes grficos do Struts 2 usam esse toolkit tanto para chamadas AJAX como para tratamento de eventos JavaScript. Continuando no arquivo index.jsp, temos duas tags <s:div>. Esta nova tag s vezes chamada de Remote DIV, pois seu contedo pode ser definido com uma requisio remota atravs do seu atributo href. O contedo dessa tag tambm pode ser o resultado de algum outro evento na pgina, por exemplo, o retorno de uma postagem de formulrio. Veja a definio dos DIVs. <s:div id = formulario href = /inserirUsuario!input.action theme = ajax ></s:div> <s:div id = listaUsuario href = /listaUsuarios.action theme = ajax listenTopics = listagemUsuarios> </s:div> Os dois DIVs tm o atributo href configurado, portanto seu contedo vir das URLs fornecidas. Observe que as URLs so exatamente as definies das Actions no struts.xml: inserirUsuario e listaUsuarios. Um detalhe importante sobre o segundo DIV o atributo listenTopics. Este atributo transforma o DIV em um listener de tpicos. Os tpicos, neste contexto, so utilizados para manter um canal de comunicao entre os componentes grficos, e so criados e mantidos no cliente, ou seja, no navegador do usurio. Um listener de tpicos um componente que recebe avisos quando alguma mensagem chega ao tpico no qual ele se registrou. Listagem 9. JSP que define o layout do sistema ndex.jsp <%@ taglib prefix=s uri=/tags%> <html>

<head> <title>JM Struts 3</title> <link href=<s:url value=/css/styles.css/> rel=stylesheet type=text/css /> </head> <body> <s:div id=formulario href=/inserirUsuario!input.action theme=ajax></s:div> <s:div id=listaUsuario href=/listaUsuarios.action theme=ajax listen Topics=listagemUsuarios></s:div> </body> </html> listaUsuarios.jsp A exibio da lista de usurios feita pela pgina listaUsuarios.jsp, mostrada na Listagem 10. A tag que cria as linhas com os dados <s:iterator>. <s:iterator value = usuarios> ... </s:iterator> O atributo value = usuarios significa que ser chamado o mtodo getUsuarios() na Action que a precedeu, ou seja, a Action listaUsuarios. O retorno desse mtodo deve ser uma collection (objeto que implemente java.util.Collection) para que a tag <s:iterator> consiga criar um lao e exibir as linhas e tabela. A tag <s:property>, utilizada dentro do lao, exibe os atributos do usurio, definidos pela propriedade value. A tag <s:date> utilizada para exibir e formatar a data de nascimento: <s:property value = nome/> <s: date name = dataNascimento format = dd/MM/yyyy/> Antes de continuar, vamos revisar como est definido o fluxo da aplicao para exibir a lista de usurios: 1) O componente <s:div>, na pgina index.jsp, cria um Remote DIV e define seu contedo como sendo o retorno da requisio Action listaUsuarios. 2) Esta Action popula seu atributo usuarios e redireciona ao arquivo listaUsuarios.jsp. 3) Dentro do JSP utilizamos a tag <s:iterator> para acessar o atributo usuarios e exibir suas informaes como linhas de uma tabela. Listagem 10. Arquivo JSP que exibe a lista de usurios listaUsuarios.jsp <%@ taglib prefix= s uri= /tags %> <span class =titulo>Lista de usurios</span> <table> <tr> <td class=head>ID</td> <td class=head>Nome</td> <td class=head>Departamento</td> <td class=head>Perfil</td> <td class=head>Data Nascimento</td> </tr>

<s:iterator value=usuarios> <tr> <td><s:property value=id /></td> <td><s:property value=nome /></td> <td><s:property value=departamento.nome /></td> <td><s:property value=perfil.nome /></td> <td><s:date name=dataNascimento format=dd/MM/yyyy /></td> </tr> </s:iterator> </table> formUsuario.jsp e as novas tags A pgina formUsuario.jsp (Listagem 11) comea verificando se h erros de validao para os campos. Caso existam, estes so impressos. Antes de partir para o restante da pgina (o formulrio em si), vamos entender um pouco mais sobre as novas tags e o modelo de templates/temas do Struts 2. As novas tags do Struts podem seguir um template que traz, alm de definies grficas, cdigos que definem atributos com valores padro e importao de JavaScripts. Os templates do Struts 2 normalmente so escritos utilizando o framework FreeMarker. Alm dos templates, o Struts 2 traz os chamados temas (themes). Esses temas so basicamente um conjunto de templates nos quais as tags devero se basear, desde que configuradas para isso. Alguns temas j vm prontos para serem usados, como simple, xhtml, css_html e ajax. Cada um define estruturas e elementos de layout diferentes. Os temas so definidos atravs do atributo theme de cada tag. No exemplo, usamos o tema ajax, pois validaremos nossos campos atravs de chamadas AJAX: <s:form action = inserirUsuario!Salvar method = post theme = ajax validate = true id = formUsuario> Combinando o uso do tema ajax com validate = true, informamos que cada campo dever ser validado no evento onBlur. O evento onBlur disparado quando um componente do formulrio perde o foco (por exemplo, passando-se de um componente para outro). Outra caracterstica do tema ajax, que o formulrio ser dividido em um layout de duas colunas. A primeira coluna ter o nome/label do campo e a segunda o campo propriamente dito. Isso nos economiza algumas linhas de cdigo para definir o layout do formulrio, alm de deixar o cdigo JSP mais legvel. O contedo do formulrio utiliza os novos componentes e tags do Struts 2. Veja que todos os campos do formulrio tm as propriedades name e label em comum. A propriedade name define qual atributo ser procurado e configurado na Action do formulrio, e label define o texto exibido ao lado do campo na primeira coluna. Veja um exemplo: <s:extfield name = usuario.nome label = Nome size = 40/> A tag <s:datepicker> cria um campo texto e ao lado um link para um calendrio popup (veja a Figura 3). A propriedade format define qual o padro de exibio da data: <s:datepicker id = usurio.dataNascimento name = usurio.dataNascimento label = data de Nascimento format = #dd/#M/#yyyy theme = ajax/>

Os dois componentes <s:select> criam as opes de Departamento e Perfil: <s:select list = departamentos listKey = id listValue = nome name = usurio.departamento.id label = Departamento/> <s:select list = perfis listKey = id listValue = nome name = usurio.perfil.id label = Perfil/>

Figura 3. Componente de calendrio para campos de data O atributo list define qual mtodo dever ser chamado na Action precedente para obter a lista de opes. Para a lista de possveis perfis ser chamado o mtodo getPerfis(), j que a propriedade esta configurada com o valor perfis. Seguindo a mesma lgica, o mtodo chamado para departamentos ser getDepartamentos(). Os outros dois atributos, listKey e listValue, definem, respectivamente, o atributo value da tag HTML <option> e o texto apresentado para as opes. Veja o resultado: <select name = usurio.perfil.id id = formUsuario_usuario_perfil_id onblur = validate(this);> <option value = 1>Administrador</option> <option value = 2>Operador</option> </select> Finalizando a descrio do formUsuarios.jsp temos o boto de enviar, que possui trs atributos interessantes : resultDivld, notifyTopics e prelnvokeJS. <s:submit value = Cadastrar resultDivld = formUsuario notifyTopics = listagemUsuarios prelnvokeJS = confirm (Confirma a incluso de usurio?);/> No Struts 2, possvel informar qual DIV dever ser atualizado com a resposta de alguma requisio. Ou seja, a requisio efetuada mas a resposta exibida dentro do DIV especificado. No caso do formulrio, optamos por atualizar apenas o DIV do formulrio deixando o resto da pgina esttico. Essa configurao foi feita atravs do atributo resultDivlD. O atributo notifyTopics define quais tpicos recebero uma mensagem de aviso. O tpico informado o mesmo que o DIV da listagem de usurios, pois queremos que a listagem seja atualizada assim que o usurio acionar o boto Enviar. O ltimo atributo o prelnvokeJS, que invoca um cdigo JavaScript antes do envio dor formulrio. Utilizamos este recurso para pedir a confirmao do usurio sobre a incluso dos dados. Note que no programamos nenhuma linha de JavaScript para os eventos do formulrio, nem para a confirmao de envio como nem para mostrar o calendrio.

Listagem 11. Arquivo JSP que exibe o formulrio de cadastro de usuriosformUsuario.jsp <%@ taglib prefix= s uri= /tags %> <s:if teste=hasErrors()> <b>Erros:</b> <s:fieldderror /> </s:if> <s:form action=inserirUsuariosalvar method=post id=formUsuario validate-true theme=ajax> <span class=titulo>Cadastro de usurio:</span> <s:textfield name=usurio.nome label=Nome size=40 /> <s:datepicker id=usuario.dataNascimento name=usuario.dataNascimento label=Data de nascimento format=#dd/#M/#yyyy theme=ajax /> <s:select list=departamentos listKey=id listaValue=nome name=usuario.departamento.id label=Departamento /> <s:select list=perfis listKey=id listValue=nome name=usuario.perfil.id label=Perfil /> <s:submit value=Cadastrar resultDivId=formUsuario notifyTopics=listagemUsuarios preInvokeJS=confirm(Confirma a incluso de usurio?); /> </s:form> Regras de validao Vamos agora configurar as regras de validao. Para isso criamos o arquivo UsuarioActionvalidation.xml, mostrado na Listagem 12. A primeira regra de validao para o campo usurio.nome, que um campo de texto obrigatrio. Sua regra de validao requiredstring: <field name = usurio.nome> <field-validador type = requiredstring> <message > Campo nome obrigatrio </message> </field-validator> </field> A Segunda regra um pouco diferente pois a propriedade type est configurada como validatorPerfil: <field-validator type = validatorPerfil> Esse um validador personalizado, mostrado na Listagem 13. Para adicionar validadores personalizados como este preciso alterar arquivo WEB-INF/classes/validators.xml. Definimos o nosso com a seguinte linha: <validator name = validatorPerfil

class = br.com.jm.validador.ValidadorPerfil/>

Este validador verifica se um usurio configurado com o perfil de Administrador pertence ao departamento de Informtica. Lembre-se que no evento onBlur de cada campo, feita uma requisio AJAX. Por isso configuramos um Servlet chamado dwr para tratar essas requisies, como j mostramos na Listagem 2. A Figura 4 mostra o formulrio aps a postagem com campos invlidos. O Struts 2 utiliza o toolkit DWR para tratar as requisies AJAX de validao. Listagem 12. Arquivo que contm as regras de validao UsuarioActionvalidation.xml <!DOCTYPE validators PUBLIC -//OpenSymphony Group//XWork Validator 1.0.2//EN http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd> <validators> <field name=usuario.nome> <field-validator type=requiredstring> <message>Campo nome obrigatrio</message> </field-validator> </field> <field name=usuario.perfil.id> <field-validator type=validadorPerfil> <message>Administradores devem pertencer ao departamento de informatica.</message> </field-validator> </field> </validators> Listagem 13. Arquivo que contm as regras de validao UsuarioActionvalidation.xml public class ValidadorPerfil extends FieldValidatorSupport { public void validate(Object object) throws ValidationException { Usuario usuario = (Usuario) getFieldValue(usuario, object); Integer idPerfil usuario.getPerfil() !=null ? usuario.getPerfil() .getId() : null; Integer idDepartamento = usuario.getDepartamento() !=null ? usuario .getDepartamento().getId() : null; if (!isPerfilDepartamentoValid(idPerfil, idDepartamento)) { addFieldError(getFieldName(), object); } } private boolean isPerfilDepartamentoValid(Integer idPerfil, Integer idDepartamento) { if (idPerfil == null l l idDepartamento == null l l (idPerfil.equals(1) && idDepartamento.equals(1))) return false; return true; }

Figura 4. Erros de validao do formulrio Concluses Aqui exploramos apenas alguns recursos dos Struts 2. Suas novas funcionalidades e sua nova arquitetura sanam vrios problemas existentes no desenvolvimento web com verses anteriores do Struts, alm de trazer mais flexibilidade para a construo da camada de visualizao. As novas tags do Strutus 2, que seguem o modelo templates, tornam a construo de aplicaes web mais produtiva, oferecendo um layout padronizado e deixando o desenvolvedor mais livre das tags HTML. E os novos componentes grficos tornam os formulrios mais ricos e mais interativos, alm de diminuir a quantidade de cdigo JavaScript nas pginas JSP. A nova verso do Struts ainda levar algum tempo para amadurecimento e correo de bugs, mas j mostra que o projeto est mais vivo do que nunca. Vale a pena explorar Struts 2!

LINKS: struts.apache.org/2.x Para pgina inicial do Struts 2. getahead.ltd.uk/dwr Biblioteca para acessar componentes java no servidor via JavaScript dojotoolkit.org Toolkit para JavaScript. Mudanas e novidades no Struts 2 Actions As actions no Struts 2 agora so independentes de qualquer outra classe, como HttpServletRequest e HttpServletResponse, e podem ter apenas um mtodo execute(): public class MinhaAction{ public String execute (){ return sucesso; } } Essa independncia faz com que possamos criar testes unitrios para as Actions com mais simplicidade.

Arquivo de Configurao O arquivo de configurao do Struts mudou de nome e de localizao, de WEB-INF/strutsconfig.xml para WEB-INF/classes/struts.xml. A estrutura tambm mudou, como mostrado no exemplo do artigo. Os ActionsForwards foram substitudos pelos Results. Os Results seguem a mesma idia que seu predecessor porm do maior flexibilidade para definir o tipo de retorno que ser enviado ao usurio. Alguns exemplos de Results so JSP, JasperReports, XSL, texto simples, Action Redirection e frameworks de templates como Tiles, Velocity e FreeMaker. Veja o exemplo de como definir uma Action e um JSP simples como Result. <action name = salvarUsurio class = br.com.action.SalvaUsuarioAction method = salvar> <result name = success > / sucesso.jsp </result> </action> Aqui definimos um Result para o JasperReports: <result name = success type = jasper> <param name = location > /jasper/arquivo_compilado.jasper</param> <param name = dataSource>meuDataSource</param> <param name = format> PDF</param> </result > Nessa configurao, location define a localizao do arquivo compilado do Jasper Reports; dataSource define qual mtodo dever ser chamado na Action para obter a fonte de dados, ou seja, getMeuDataSource (); e format determina qual o formato de sada do relatrio, PDF neste exemplo. Interceptors Os Interceptors so outra grande novidade do Struts 2 e tm papel fundamental na nova arquitetura do framework. Sua principal inteno adicionar novas funcionalidades s Actions sem a necessidade de alter-las. Veja o quadro Tipos de Interceptors para conhecer vrios tipos de Interceptors e o exemplo do artigo para v-los em ao. Adeus aos FormBeans No Strusts 2 o FormBeans no so mais utilizados. Para cada request uma nova instncia da Action criada e preenchida com os valores postados. Os dados postados estaro disponveis dentro da sua Action como atributos da classe ou em um Map. Veja este exemplo: public class MinhaAction implements ParameterAware { public Map parameters; public String execute () {...} //getters e setters } A interface ParemeterAware implementada faz com que o Struts saiba que a classe tem o mtodo setParameters (), e assim a configure com os parmetros do request. Para adicionar esta funcionalidade Action necessrio configurar um Interceptor. A configurao ficaria assim: <action name = minhaAction class = br.com.jm.MinhaAction>

<result name = success>sucesso.jsp </result> <result name = error>erro.jsp</result> <interceptor ref name = servlet - config/> </action> Existem outras interfaces que a Action poder implementar para obter mais informaes sobre a aplicao, cada uma fornecendo um objeto diferente. So elas: ServletContextAware, ServletRequestAware, ServletResponseAware, RequestAware, SessionAware, AplicationAware e PrincipalAware Validao Para cada Action podemos criar um arquivo NomeDaAction-validation.xml que conter as validaes desejadas. Este arquivo deve estar localizado dentro do mesmo pacote que a Action. Veja um exemplo de validao para um campo obrigatrio: <validators> <field name = nome> <field-validator type = requiredstring> <message> Campo nome obrigatrio </message> </field-validator> </field> </validators> Para adicionar a funcionalidade de validao Action, voc apenas precisa definir o interceptor validation, como no seguinte exemplo: <action name = salvarUsuario class = br.com.jm.action.SalvarUsuarioAction method = salvar> <result name = success>/ sucesso.jsp</result> <interceptor-ref name = validation> </action > Neste exemplo, assim que a action salvarUsuario for requisitada, o Interceptor de validao procura o arquivo SalvaUsuarioAction-validation.xml e identifica os campos que devem ser validados. Caso todos estejam corretos, a Action executada. Este mesmo arquivo pode ser utilizado pelos componentes de validao AJAX, deixando suas regras de validao centralizadas. No exemplo do artigo e visto na prtica como fazer esta validao. Integrao com Spring, Hibernate e outros frameworks Para os desenvolvedores que projetam suas aplicaes utilizando os frameworks Spring Hibernates, boas notcias acompanham o Struts 2. A instanciao das Actions pode ser delegada ao Spring permitindo o uso de Injeo de Independncia. Isso faz com que as dependncias das Actions sejam resolvidas no momento de instanciao. A configurao do Spring feita no arquivo WEB-INF/application.xml. Veja o exemplo simples deste arquivo: <beans> <bean id = usuarioDAO class = br.com.jm.UsuarioDAO/> <bean id =employeeAction class = br.com.jm.MyAction> <constructor-arg ref = usuarioDAO/> </bean> </beans>

Mesmo para quem no conhece Spring, este XML no ser difcil de entender. No momento da instanciao da classe MyAction o Spring ir passar para o construtor (em <constructor-arg >) o bean usuarioDAO como parmetro. Assim, no momento em que a Action for executada, voc ter este objeto disponvel para uso. O Spring tambm fornece a facilidade de controle de transaes para acesso a dados, deixando a integrao com o Hibernate mais elegante e gerenciado, evitando usar o pattern Open Session in View (hibernate.org/43.html). O Struts 2 possui integrao com outros frameworks alm do Spring e Hibernate, incluindo JSF, JUnit, JasperReports, Pico Container, Quartz, SiteMesh e Tiles. Tipos de Interceptors Os Interceptors so objetos utilizados para execuo de cdigos antes ou depois de Actions. Com eles, possvel adicionar funcionalidades aplicao sem alterar o cdigo das Actions. Veja a seguir alguns exemplos de interceptors. Alias Interceptor Utilizado para Actions que possuem nomes de atributos diferentes dos nomes dos parmetros enviados no request. Nesse caso voc pode utilizar o interceptor para mapear tais parmetros para os atributos. Veja um exemplo onde o parmetro nomeUsuario ser mapeado para o atributo da Action nomeFuncionario: <action name = minhaAction class =br.com. jm.MinhaAction> <param name = aliases > #{ nomeUsuario }: nomeFuncionario}</param> <interceptor-ref name = alias/> <result name = success >posExecucaoAction.jsp</result> </action> Execute and Wait Interceptor Utilizado para rodar servios em segundo plano, enquanto mostrado ao usurio uma mensagem a negado. Exemplo de configurao: <action name = minhaAction class = br.com.jm.MinhaAction > <interceptor ref name = completStack /> <interceptor- ref name execAndWait /> <result name = wait > mensagemDeEspera.jsp </result> <result name = success > posExecucaoAction.jsp </result> </action> File upload Interceptor Criar dentro da Action uma referencia ao arquivo que existe no request. Isso facilitara o upload, visto que com esta referencia o desenvolvedor apenas precisa definir o local onde deseja gravar o arquivo. Veja um exemplo de configurao: <action name = minhaAction class = br.com.jm.MinhaAction> <interceptor-ref name fileUpload > <param name = allowedTypes > image/png,image/gif,image/jpeg

</param> </interceptor-ref> </action> Para obter a referencia e outras informaes sobre o arquivo, voc precisa criar os seguintes mtodos na sua Action: setUpload() , setUploadContentType() e setUploadFileName(). Exception Interceptor Permite que voc mapeie uma exceo Java para um Result. Exemplo: <action name = minhaAction class = br.com.jm.MinhaAction> <interceptor-ref name = exception/> <interceptor-ref name = basicStack/> <exception-mapping exception = java.lang.Exception result = erro_personalizado/> <result name = erro_personalizado/> paginaDeErro.jsp </result> <result name = sucesso>sucesso.jsp</result> </action > Token Interceptor Trata duplo-cliques em botes de envio, detectando o os e reenviando o usurio para outra pgina. Um duplo-clique pode causar erros graves, como por exemplo, dois cadastros no banco de dados com as mesmas informaes (ou dois dbitos em sua conta bancaria). A pgina para qual o usurio ser redirecionado definida pelo Result invalid.token. Configurao: <action name = someAction class = com.exemples.SomeAction> <interceptor-ref name = token /> <result name = invalid.token> erro.jsp </result> </action> <result name = successor> sucesso.jsp </result> </action> Criando novos Interceptors Voc pode tambm criar seu prprio interceptor implementado a interface com.opensymphony.xwork.interceptor.Interceptor e definindo o no arquivo struts.xml: <interceptor name = meuInterceptor class = br.com.jm.MeuInterceptor/>

Novos componentes grficos O Struts 2 traz uma coleo de novas tags para facilitar a construo da camada de visualizao de aplicativos web. Veja aqui alguns exemplos: DoubleSelect Permite que um <select> seja atualizado a partir de outro. Exemplo: <s:doubleselect label = Estados name = regiao

list = { Nordeste , Sudeste } value = Nordeste doubleValue = Sergipe doubleList = top = = Sudeste ? {Minas Gerais, Sao Paulo }: {Sergipe, Alagoas} doubleName = estado emptyOption = true/> Neste exemplo (Figura Q1) o primeiro <select> ter a regio e o segundo, os estados correspondentes. Neste exemplo simplificado, quando for selecionada a regio Nordeste, os estados de Sergipe e Alagoas sero carregados; ao selecionar Sudeste teremos os estados de Minas Gerais e So Paulo. Note que os valores neste exemplo foram populados diretamente no JSP, mas poderiam vir atravs de uma Action.

Figura Q1. Componente DoubleSelect com a regio Nordeste selecionado Option Transfer Select Componente muito til para exibir de maneira clara opes aos usurios. Permite a manipulao e a ordenao das opes (Figura Q2). Veja como utiliz-lo: <s:optiontransferselect label = Selecione suas cores favoritas name = esquerda doubleName = direita leftTitle = Cores rightTitle = Cores Escolhidas list = { Branco , Preto, Azul } doubleList = { } />

Figura Q2. Option Transfer Select com a cor Branca selecionada Novamente aqui as opes poderiam ser populares atravs da Action. rvore Componentes grfico para criao de rvores (Figura Q3). Veja como cri-lo : <s: tree label = Arvore Struts 2 id = arvore theme = ajax treeSelectedTopic = arvoreSelecionada > <s: treenode theme = ajax label = Raiz id = raiz > <s: treenode theme = ajax label = Folha 1 id = folha 1 /> <s: treenode theme = ajax label = Folha 2 id = folha 2 /> <s: treenode theme = ajax label = Folha 3 id = folha 3 /> </s: treenode> </s: tree >

Figura Q3. rvore de opes criada pela

nova tag do Struts 2. A rvore cria um tpico para avisar quando selecionada, atravs da propriedade treeSelectedTopic. Dessa forma outros componentes podem monitorar quando algum n da rvore for selecionado. Tabbled Panel Componente til para criao de formulrios passo a passo, mostrado em ao na Figura Q4. Veja como cri-lo: <s:tabbledPanel id = panel > <s:panel id = primeiro tabName = Primeira> Primeiro Panel selecionado </s: panel > <s:panel id = primeiro tabName = Segundo> Segundo Panel selecionado </s: panel > <s:panel id = primeiro tabName = Terceiro> Terceiro Panel selecionado </s: panel> <s:tabbedPanel> O contedo de cada Panel pode ser gerado a partir de uma chamada remota atravs das propriedades remote e href: <s:panel remote = true href = / MinhaAction.action id = ryh1 theme = ajax tabName = remote one />

Figura Q4. Exemplo de Tabbled Panel.

JDBC Ponta a Ponta Parte 1:De conceitos essenciais a configuraes e consultas Aprenda os fundamentos de uma APIs mais importante do Java A tecnologia de bancos de dados relacionais talvez a mais importante em todos os tempos para os Sistemas de Informao. Os bancos de dados mais populares do mercado, desde bancos leves como o HSQLDB, passando pelos livres convencionais como MySQL at os pesos-pesados como Oracle so, com poucas excees, banco de dados relacionais.Desde os primrdios do Java, a importncia dos bancos de dados foi reconhecida,e a verso 1.1 do JDK j trazia como componente padro a API JDBC. Por meio do JDBC,uma aplicao Java pode se conectar a qualquer banco relacional,submeter comandos SQL para execuo e recuperar os resultados gerados pela execuo desses comandos.alm disso,o JDBC permite acesso aos metadados do banco de dados (tambm conhecido como catalogo) permitindo a construo de ferramentas para administrao do prprio banco e apoiando o desenvolvimento de sistemas. Embora verses posteriores do Java tenham trazido alguns novos recursos ao JDBC (veja o quadroVerses da API JDBC),os recursos presentes j nas primeiras verses da API atendem plenamente s necessidades da maioria das aplicaes,mesmo quando h demandas fortes de performance e de suporte a recursos avanados como dados multimdia.O melhor de tudo que a compatibilidade retroativa foi preservado - ao contrrio de APIs como Swing e colees, que mudaram bastante do Java 1.1 para o Java 2.Ento aplicaes Java baseadas nas primeiras verses do JDBC no necessitam de modificaes para adaptao a verses mais recentes da JVM ou do JDK. Mesmo o desenvolvedor que utiliza mecanismo de persistncia objeto-relacional como Hibernate e EJB 3,ou que a prefere frameworks relacionais como iBatis ou Spring JDBC (veja links) necessita de um conhecimento abrangente da API JDBC e de conceitos de bancos de dados relacionais em geral.Afinal, todos esses frameworks e bibliotecas usam o JDBC como base para comunicao com banco de dados. Esta srie sobre JDBC atende a dois pblicos distintos.Para os iniciantes,que nunca tiveram contato com o JDBC,esta primeira parte apresenta os fundamentos da API.J para os desenvolvedores com alguma experincia prvia com JDBC,a segunda parte,na prxima edio,apresenta recursos que fazem a diferena entre uma aplicao de brinquedo e uma aplicao profissional como transaes,o uso de PreparedStatements,e dicas de segurana. Os exemplos sero todos executados sobre o HSQLDB, para que no seja necessrio instalar e administrar um banco mais sofisticado.Mas funcionam sem alteraes em qualquer outro banco de dados foram testados MySQL,PostgreSQL,FireBird e Oracle.Como no haveria espao suficiente para apresentar os procedimentos para inicializao de todas esses bancos,nesta parte focamos no HSSQLDB, e na segunda mostraremos como executar os exemplos (das duas partes) no MySQL e PostgreSQL,os dois bancos de dados livres mais populares. Drivers JDBC Para acessar um banco relacional, uma aplicao Java necessita,alm da prpria mquina virtual, de um driver JDBC.Este driver em geral fornecido junto com o banco de dados ou com um download separados pelo prprio fornecedor do banco,sem custo adicional. Se voc vem de outros ambientes, como VB e o Delphi, vai se surpreender ao descobrir que o JDBC no necessita de nenhuma configurao prvia, nem que seja instalado um cliente nativo do banco de dados para funcionar. Drivers JDBC so na grande maioria simples biblioteca Java arquivo JAR que podem ser copiados para qualquer sistema operacional. No h necessidade de editar arquivos de configurao nem de executar algum painel de controle administrativo.

Drivers JDBC escritos inteiramente em Java so conhecidos como drivers Tipo 4 (Type 4).

Existem no mercado alguns drivers que utilizam cdigos nativo (via JNI) para aproveitar cdigo dos clientes nativos proprietrios do banco de dados. Mas eles em geral tm performance inferior, e so mais pesados e menos estveis do que os escritos inteiramente em Java para o mesmo banco. Drivers que usam cdigos nativos tm o overhead de traduzir objetos Java para estruturas de dados e tipos nativos do sistema operacional. J drivers puro-Java pode usufruir dos recursos avanados de conectividade de rede, gerncia de memria e segurana embutidas no Java. A Figura 1 compara um driver JDBC escrito inteiramente em Java com drivers que usam cdigos nativo. A mesma figura compara drivers JDBC com mecanismo nativo de acesso a bancos de dados, tomando como referencia o popular ODBC do Windows. utilizado como exemplo o driver JDBC do Oracle.A figura ficaria praticamente igual se em vez de ODBC fosse utilizado o dbExpress do Delphi, o ADO.NET da Microsoft,ou tecnologia similares. Para compilar uma aplicao Java que acessa um banco de dados via JDBC,no necessrio ter nenhum drivers JDBC, no necessrio ter nenhum driver JDBC instalado nem configurao no seu IDE.Mas para executar a aplicao,as classes do driver devem estar no classpath.Isto significa que uma mesma aplicao pode ser executada utilizando qualquer banco de dados que deseje, contanto que a aplicao seja escrita usando apenas comando SQL padronizados,ou ento encapsulando com cuidado comandos que utilizam sintaxes proprietrias de cada banco.

Figura 1.Drivers JDBC Tipo 4 versus drivers ODBC do (os drivers JDBC OCI e Thin so ambos fornecidos pela Oracle,no mesmo pacote O banco de dados de exemplo Como indicado na introduo, os exemplos os exemplos deste artigo utilizam o HQSLDB,um banco de dados livre escrito inteiramente em Java,mas foram testados e funcionam com vrios bancos.No entraremos em muitos detalhes sobre HQSLDB,pois ele j foi descoberto extensamente em edies anteriores da Java Magazine.Apresentaremos aqui apenas o suficiente para criar o banco de dados de exemplo e executar as aplicaes criadas neste artigo. Baixe o arquivo hqsldb_1_8_0_7.zip de hqsldb.sf.net e descompacte em uma pasta qualquer,por exemplo c:\java ou /home/usurio no Linux.O resultado ser a criao de um diretrio chamado hqsldb,contendo a documentao,classes Java e fontes HQSLDB existe o arquivohsqldb.jar que contm tanto o driver JDBC quanto o prprio servidor do banco de dados.Este um caso raro:em geral o driver JDBC fornecido em um pacote JAR parte,mas o uso embarcado do HQSLDB justifica este empacotamento atpico.

O HQSLDB pode ser executado como um servidor de rede, aceitando conexes TCP/IP da mesma forma que o MySQL ou Oracle, ou ento no chamado modo standalone,onde o banco de dados fica embutido dentro da aplicao Java e acessa diretamente os arquivos de dados.Para a aplicao,os dois modos so indiferente.A aplicao apenas indica ou o caminho para um arquivo local,ou a URL para um servidor remoto. Vamos ento configurar o classpath do sistema operacional para incluir o driver do HSQLDB.Usurios Windows podem digitar o comando a seguir em um prompt de comandod do MSDOS: setCLASSPATH=%CLASSPATH%;c:\java\hsqldb\lib\hsqldb.jar Utilize este mesmo prompt de comando para executar os exemplos deste artigo. Caso a janela seja fechada, a configurao do classpath ser perdida. Usurios Linux podem usar o comando: exportCLASSPATH= $CLASSPATH:/home/lozano/hsqldb/lib/hsqldb.jar E, da mesma forma que no Windows, tambm devem ser executados os exemplos no mesmo shell onde foi configurado o classpath.Em ambos os casos,altere o diretrio de instalao do HSQLDB conforme sua preferncia. O arquivo hsqldb.jar contm ainda um pequeno aplicativo de administrao do banco de dados chamdo DataBase Manager.Todos os bancos de dados fornece uma interface similar para execuo de comandos SQL.Um detalhe que a fornecida pelo HSQLDB escrita em Java e pode ser usada com outros bancos. A Listagem 1 apresenta a seqncia de comando SQL que cria o banco de dados de exemplo e insere alguns dados de teste.Nosso banco de dados contm um resumo de informaes sobre vendas de produtos de uma rede de lojas varejistas. Para executar os comando da Listagem 1,inicie o DataBase Manager do HSQLDB utilizando o comando a seguir: java.org.hsqldb.util.DataBaseManagerSwing O DataBase Manager ir pedir informaes para a conexo ao banco de dados.Informe como tipo HSQLDB Standlone,URL de conexo jdbc:hsqldb:file:vendas:shutdown=true,usurio as e senha em branco.Como o banco de dados ainda no existe,ele ser automaticamente pelo HSQLDB no caminho especificado que por omisso diretrio corrente. A Figura 2 apresenta a seqncia de telas do DataBase Manager,desde conexo (criao) do banco de dados ate a execuo dos comandos SQL do Script que criam as tabelas e inserem dados de teste.

Figura 2.Usando o DataBase Manager do HSQLDB para criar e inicializar o banco de dados de exemplo.Observe que os arquivos vendas.properties,vendas log e vendas Ick formam o banco de dados em si,enquanto que vendas.sql contm script SQL da Listagem 1.

Muito cuidado na digitao do URL de conexo, pois qualquer erro far com que em vez do HSQLDB abrir um banco j existente,ele crie um novo banco.Por este motivo melhor especificar caminhos completos para os arquivos do banco de dados, em vez de usar caminhos relativos como foi feito por exemplo. Os comandos de criao das tabelas podem ser salvos em um arquivo texto, por exemplo, vendas.sql,e este arquivo pode ser aberto e executado pelo DataBase Manager.O script para o exemplo esta disponvel junto com fontes para download deste arquivo. Observe que, depois da execuo dos comandos SQL,a parte esquerda do DataBase Manager se altera,indicando a presena das tabelas recm-criadas.Feche o utilitrio e o abra novamente,para garantir que as tabelas e informaes no foram perdidas com a finalizao do banco de dados.Experimente ainda executar alguns comando SQL para consultar os dados de teste,por exemplo select*from vendas where regio=RJ.O resultado esperado apresentado na Figura 3.

Figura 3.Nova execuo do DataBase Manager,para verificar a correta inicializao do banco de dados.

Listagem 1. Comandos SQL para inicializao do banco de dados de exemplo. create table produto ( id integer not null primary key, nome varchar (30) not null, preco decimal (14,2) not null, categoria varchar (20) not null ); insert into produto values ( 1. iPod Nano, 299.00, Eletro Eletrnicos ) ; insert into produto values ( 2. Endredon dupla face , 34.00, Cama e Banho) ; insert into produto values ( 3. Tv de Plasma, 7599.00, Eletro Eletrnico) ; insert into produto values ( 4. Travesseiro anti-alrgico , 49.00, Cama e Banho) ; create table vendas ( regiao char(2) not null , produto integer not null , qtde integer not null , foreign key (produto) references produto (id) ); insert insert insert insert insert insert into into into into into into vendas vendas vendas vendas vendas vendas values values values values values values (RJ , 1, 322 ) ; (SP , 1, 518 ) ; (RJ , 2, 567 ) ; (SP, 2, 987 ) ; (RJ , 3, 45 ) ; (SP , 3, 78 ) ;

insert into vendas values (RJ , 4, 67 ) ; insert into vendas values (SP, 4, 75 ) ;

Um programa JDBC mnimo A Listagem 2 apresenta um programa JDBC mnimo.Ele conecta ao banco de dados,reduz os preos de todos os produtos da categoria Eletro Eletrnico em 12%, e encerra a conexo.A reduo nos preos inteiramente realizada por um comando SQL update, sem qualquer ajuda de cdigos Java. Para compilar e executar esse programa esse programa, use o comando javac e java no mesmo prompt de comandos onde foi antes configurado o classpath para incluir o driver JDBC do HSQLDB (ou de qualquer outro banco de dados desejado). $ javac ReduzPreco.java $ java ReduzPrecos 2 produtos tiveram seus preos reduzidos O sinal de $ representa o prompt de comando do sistema operacional e no deve ser digitado. Caso apenas o comando funcione mais no o segundo, ou voc no configurou classpath corretamente ou o banco de dados no foi inicializado de forma correta. Para que confirma que o programa reiniciou o preo dos produtos e eletroeletrnicos, e no os produtos de outras categorias, execute o Database Manager e consulte todos os registros na tabela de produtos, conforme exemplificado pela Figura 4.

Figura 4. Situao do banco de dados de exemplo aps a execuo do programa ReduzPreco.java Sempre encerre o Database Manager do HSQLDB antes de executar qualquer dos programas Java deste artigo, pois no modo stand-alone apenas uma aplicao pode acessar os arquivos do banco de dados.No modo servidor do HSQLDB, esta restrio eliminada,mas no queremos distrair o leitor com configuraes de rede e firewall. Vamos agora seguir programa mnimo, linha a linha, para entender o seu funcionamento:

1. A chamada a Class.forName() garente que o driver JDBC do banco de dados esteja carregado na memria do JVM.H varias outras formas de se fazer isso,mas esta a mais simples. 2. A chamada a DriverManager.getConnection() cria uma conexo (interface Connection) ao banco de dados indicado pela string passada como primeiro argumento.O login e senha para acesso ao banco de dados so passados no segundo e terceiro argumentos do mtodo. 3. O objeto Connection retornado pelo DriverManager utilizado para se criar um comando SQL (objeto do tipo Statement) pela chamada a createStatement(). 4. O objeto Statement ento utilizado para enviar ao banco de dados o comando SQL update que modifica os preos chamando o mtodo executeUpdate() 5. O comando SQL e a conexo ao banco de dados so encerrados pela chamada aos respectivos mtodos close(). Todo programa JDBC segue a mesma seqncia geral de passos: (1) Conexo (2) Execuo de comando SQL e (3) Encerramento. Um mesmo objeto de conexo (Connection) pode ser utilizado para executar vrios comandos. Na verdade, um mesmo objetos de comando (Statement) pode ser utilizado para executar vrios comandos SQL,sendo que a execuo de um novo comando descarta os resultados gerados pela execuo do comando anterior.

A forma de conexo ao banco de dados apresentada neste exemplo, utilizando Class.forName() e DriverManager.getConnection(), adequada para aplicao Java SE,isto , desktop ou em modo texto.Mas no ser a mais eficiente para aplicaes Java EE. Em servlets,pginas JSP EJBs devem utilizados objetos DataSource,configurados dentro do prprio servidor de aplicaes ou container web.Veja as referencias ao final do artigo para mais informaes. A classe DriverManager,alm das interfaces Connection e Statement, so partes da API JDBC. Elas e todas as outras classes e interfaces do JDBC que sero vistas nesse artigo esto dentro do pacote java.sql.

Listagem 2: ReduzPrecos.java,programa JDBC mnimo que modifica os preos de vrios produtos no banco de dados exemplo. import java.sql.*; public class ReduzPrecos { public static void main(String[] args) throws Exception { Class.forName(org.hsqldb.jdbcDriver) Connection conexao = DriverManager.getConnection( jdbc:hsqldb:file:vendas;shutdown=true, sa , ) ; Statement commando = conexao .createStatement () ; int registrosAfetados = comando.executeUpdate ( update produto set preco = preco (preco * 0.12) + where categoria = Eletro Eletrnicos ) ; System.out.println(registrosAfetados + produtos tiveram seus preos reduzidos , ) ; comando.close ( ) ;

conexo.close ( ) ; } } Configurando a aplicao para mudar de banco Nosso primeiro exemplo de cdigo Java no na verdade independente do banco de dados, porque ele fixa no cdigo a URL de conexo e o usurio e senha. Desse modo, adaptar a aplicao para outro banco de dados, ou simplesmente modificar o diretrio dos arquivos do banco de dados, exigiria a modificao do cdigo-fonte da aplicao e a sua recompilao. bem melhor deixar estes parmetros de conexo ao banco como informaes de configurao da aplicao. A forma padro de se fazer isto em Java por meio de arquivos e propriedades (arquivos de texto simples contendo linhas no formato nome=valor).Estes arquivos so carregados com recursos da JVM.A vantagem de se tratar arquivos de propriedades como recurso que a aplicao pode ser instalada em qualquer local do disco rgido, pois os recursos so localizados no classpth. A Listagem 3 apresenta um segundo evento de aplicao Java, que desse vez se limita a inserir o novo registro de venda no banco de dados de exemplo.O trabalho do banco realizado pelo comando SQL insert.Mas o que nos interessa agora a forma como utilizada a classe Properties para obter os parmetros para conexo ao banco de dados.(Note que esta classe no faz parte da API JDBC,mas sim um pacote java.util). Note que o arquivo banco.properties deve estar no mesmo diretrio que os arquivos.class do exemplo deste arquivo.Se voc estiver usando o JDK e a linha de comando para compilar e executar os exemplos,isto sera natural.J se voc estiver usando um IDE, a maioria deles ira copiar automaticamente qualquer arquivo no- Java nos diretrios de fonte para o diretrio de classe (ou binrios). Um objeto Properties pode ser inicializado a partir de um InputStream qualquer,isto , de um arquivo texto,conexo de rede, arquivo zip ou qualquer outro tipo de stream suportado pela API java.io.No exemplo, foi utilizado a prpria classe que representa o programa (InsereProduto.class)para criar um stream que acessa o contedo do arquivo banco.properties,localizado no classpath da JVM, pela chamada ao mtodo getResourceAsStream( ). Outra forma de ser obter uma referencia prpria classe para a carga de recursos usando this.get.Class(). Com o objeto de propriedades inicializado na varivel parametrosConexao, sucessivas chamadas a getPropertie () retornam os parmetros. Que so armazenadas em variveis e depois passadas para a chamada a getConnection).Dai em diante,o exemplo igual ao programa anterior,exceto por executar um comando SQL diferente.

Alguns desenvolvedores colocam tambm os comandos SQL nos arquivos de propriedades, para facilitar o porte ou otimizao da aplicao para diferentes banco de dado.

Novamente, para verificar os efeitos do programa de exemplo sobre banco de dados, ser necessrio iniciar o Database Manager do HSQLDB e executar um comando SQL select.Afinal ainda no vimos como executar consultas SQL via JDBC.Esse o assunto do prximo tpico. Listagem 3. InsereProduto.java:programa que utiliza JDBC para inserir novos dados de vendas de um produto(foram destacadas as linhas que utilizam um arquivo de propriedades para obter os parmetros de conexo).

import java.sql. *; import java.util. *; public class InsereProduto { public static void main(String[] args ) throws Exception { Properties parametrosConexao = new Properties ( ); parametrosConexao.laod(InsereProduto.class.getResourceAsStream( banco.properties)); String String String String driver = parametrosConexao.getProperty (driver); url = parametrosConexao.getProperty (url) login = parametrosConexao.getProperty (login) senha = parametrosConexao.getProperty (senha)

Class.forName(driver); Connection conexao = DriverManager.getConection( url, login, senha ) ; Statement commando = conexao.createStatement ( ); int resgistrosAfetados = comando.executeUpdate ( insert into produto (id, nome, preco, categoria) values ( + 5, DVD Playerportatil ,799.00, Eletro Eletrnicos )) ; System.out.println(registrosAfetados + produto inserido com sucesso, ) ; comando.close (); conexo.close(); } } Executando consultas SQL A interface Statement do JDBC permite executar um comando SQL qualquer, desde comandos de DDL,como create table,at os comandos de DML,como insert e select. Comandos SQL que retornam dados devem ser executados por meio do mtodo executeQuery(), e os que modificam dados (ou que no retornam dados )devem ser executados via executeUpdate(),como fizemos nos exemplos anteriores. O mtodo executeQuery( ), retorna um objeto de resultados (interface ResultSet),que permite ler, linha a linha , os resultados da consulta. Assim com o a conexo (connection) e o comando (Statement), o objeto de resultado deve ser fechado pelo mtodo close () quando no estiver mais em uso.

Objetos Connection,Statement e ResultSet (alm de sua interfaces especializadas,como PreparedStatement, que ser apresentada na segunda parte) devem ser fechados o mais cedo possvel, pois esses objetos representam recursos do sistema operacional e do banco de dados. um erro comum supor que o Java, por meio da coleta de lixo ou de mtodos destrutores,ir fazer automaticamente esta limpeza, o que no verdade. Alm disso , quanto mais tempo ele os recursos mais forem mantidos abertos na aplicao, maior ser o peso dela para a rede e para usurio.

A Listagem 4 apresenta um programa Java que lista o valor total de vendas de cada produto individualmente,seguido pelo total de todos os produtos somados. Este exemplo exige Java SE 5,0 ou mais recente, pois usa o novo mtodo de formatao(printf( )),para gerar uma sada textual alinhada em colunas. Foi usada tambm a interface java.text.numberFormat, para a formatao dos valores monetrio (esta interface existe em verses mais antigas do Java). Observe ainda que os valores monetrios foram armazenados em um BigDecimal (pacote java.math)em vez de em um double. Valores monetrios no devem ser armazenados nem manipulados como doubles seus tipos assemelhados (como float e Double). Para mais informaes, veja o quadro Tipos de dados Java X Tipos de dados SQL. O mtodo next( ) de ResultSet utilizado para acessar a prxima linha do resultado. Note que este mtodo deve ser chamado antes que se possa acessar a primeira linha. Desde forma uma consulta que retorna um resultado vazio pode ser tratada da mesma forma que uma que retorna dados. A interface ResultSet fornece ainda mtodos como getInteger( ), getString( ) ou getDate( ) para a recuperao dos valores armazenados nas colunas/campos de cada linha do resultado. O JDBC ir converter automaticamente os tipos de dados do SQL para o Java e vice-versa, incluindo converses como de tipos numricas para tipos de texto. A coluna desejada pode ser indicada tanto pela sua posio numrica no resultado, iniciando pela posio 1 (e no a zero) quanto pelo nome da coluna. Em alguns bancos de dados, por exemplo, no MySQL, ser prefervel usar a posio em vez do nome da coluna. Assim haver um pequeno ganho de performance.

Listagem 4.Totais Vendidos.java,exemplo que gera um relatrio de totais de vendas por produto.Foram destacadas as linhas que recuperam o resultado da consulta SQL. import import import import java.sql. *; java.util.*; java.math.*; java.text.*;

public class TotaisVendidos { public static void main(String[] args ) throws Exception { Properties parametrosConexao = new Properties ( ); parametrosConexao.load(TotaisVendidos.class.getResourceAsStream( banco.properties)); //... conexo igual InsereProduto.java Statement commando = conexao.createStatement ( ); ResultSet resultado = commando.executeQuery ( selecione nome, sum(preco * qtde) as total + from produto, vendas + where produto.id = vendas.produto + group by nome ) ; NumberFormat nf = new Decimal Format (##,##,#0.00) ; int registrosLidos = 0 ; BigDecimal totalGeral = new BigDecimal (0); while ( resultado.next( ) ) {

String nome = resultado.getString (1) ; BigDecimal total = resultados.getBigDecimal (2) ; // As duas linhas seguintes so equivalentes s duas anteriores // String nome = resultado.getString (nome) ; System.out.printf (%-30s:\t%15s\n, nome, nf.format (total)) ; totalGeral = totalGeral.add(total) ; registrosLidos++; } System.out.println (\nTotal geral: + registrosLidos + produtos, + R$ + nf.format(totalGeral) ) ; resultado.close ( ); comando.Close ( ); conexao.close ( ); } } Verses da API JDBC Aqui apresentado um resumo das novas funcionalidades introduzidas a cada verso da API JDBC JDBC 1.0 Verso original da API,embutida no Java 1.1,j trazia todas as classes apresentadas neste cargo. JDBC 2.0/Java 2.0 Interface DataSource,para conexes gerenciadas por container Java EE; Suporte a transaes distribudas; Interface RowSet,que permite fazer o cach do resultado de consultas no cliente

JDBC 2.1/Java 1.2 Atualizaes em batch, permitindo uma forma independente do banco de enviar vrios comandos SQL de uma s vez; Suporte a resultados rolveis (scrollable) e atualizveis, que permitem a navegao livre por um ResultSet; Atualizao direta do registro corrente em resultados (ResultSet),sem necessidade de enviar comandos SQL update; Mapeamento de tipos definidos pelo usurio. JDBC 3.0/Java 1.4 Save points, que permitem o rollback parcial de uma transao; Parmetros nomeados em procedimentos armazenados; Forma padronizada de ser recuperar o valor das colunas geradas automaticamente pelo banco, por exemplo colunas identity e autoincrement. JDBC 4.0 (proposed final draft) Suporte ao tipo rowid, que identifica fisicamente registro em alguns bancos, como o Oracle; Manipulao de dados XML no banco e suporte a extenses do SQL para dados XML; Maior controle sobre pools de conexes e comandos;

Novas excees, de modo que no mais necessrio lidar com o sqlCode nas situaes mais comuns; Novas interfaces Query e DataSet, que apoiadas por anotaes,permitem mapear diretamente operaes sobre o banco de para mtodo e atributos de classes Java. Tipos de dados Java X SQL Seja nos valores lidos de um resultado (ResultSet), ou nos parmetros fornecidos a um PrepareStatement, o programador Java deve se preocupar sempre em usar o tipo certo de dados corretos, ou mais prximo do tipo de dados SQL que foi definida para cada coluna no banco de dados. Na maioria dos casos, a correspondncia intuitiva,como number ou int32 do SQL para int(ou integer) do Java; ou char e varchar do SQL para String do Java.Mas h dois casos especiais aos quais o programador deve estar atento: Tipos numricos O tipo SQL decimal um numero fracionrio com representao exata. Isto bem diferente de um float ou double do Java, que so nmeros em pontoflutuante. Em particular a aritmtica com decimal no gera erros de arredondamento. O tipo Java correto java.math.BigDecimal. Fazer aritmtica com BigDecimal um tanto trabalhoso, pois exige chamar mtodos como add () e multipy em vez de usar os operadores +e*.Masse for feita a conversao de BigDecimal para double, de modo a simplificar o cdigo, sero introduzidos erros de arredondamento. Em um sistema financeiro, isto pode levar a situao onde o fluxo de caixa de caixa no zera, pois centavos so perdidos de modo aparentemente aleatrio e essas perdas podem se somar e gerar discrepncias significativas. Tipos de Data/Hora Os tipos data e time do SQL so diferentes dos tipos java.util.Date ou java.util.Calendar do Java.Ambos seriam equivalentes ao tipo timestamp do SQL,pois trs armazenam tanto uma data quanto uma hora. Parater um tipo que armazene apenas data ou apenas hora, use java.sqlDate e java.sql.Time, respectivamente. Concluso Nesta parte comeamos a apresentar os fundamentos da API JDBC, uma das mais importantes para aplicaes tanto Java SE como Java EE. Na prxima parte sero vistos tpicos importantes para a produo de aplicao em qualquer das edies do Java, tais como tratamento de erros,transaes e comandos preparados (PreparedStatements), alm de funcionalidades para obteno e utilizao de metade dos do banco. Para saber mais MySQL para programadores Java Fernando Lozano.Edio 40 Fundamentos da administrao. O novo HSQLDB Fernando Lozano. Edio 30 Apresenta em detalhes o HSQLDB, banco de dados livre 100% Java para uso embarcado. Persistncia Turbinada II Osvaldo Doederlein.Edio 26 Recursos avanados do JDBC Persistncia Turbinada I

Osvaldo Doederlein.Edio 25 Como usar o JDBC de modo eficiente para criar classes DAO. JDBC 3 Maiko Rocha. Edio 07 Apresenta os novos recursos do JDBC 3. LINKS: jcp.org/em/jsr/detail?od=221 Especificao JDBC 4.0,em estgios finais de aprovao. jcp.org/em/jsr/detail?id=54 Especificao JDBC 3.0, em vigor desde JDK 1.4 java.sun.com/javase/tecnologies/database.jsp Pagina da Sun sobre JDBC java.sun.com/docs/books/tutorial/jdbc/ Trilha sobre JDBC do Java Tutorial hsqldb.org Site Oficial do HSQLDB mysql.com Site oficial do MySQL e do seu driver JDBC postegresql.org Site oficial do PostgreSQL jdbc.postgresql.org Site oficial do driver JDBC do PostgreSQL

Java EE 5 na Prtica Criando uma aplicao passo a passo com EJB 3.0, JPA e NetBeans 5.5 Construindo uma aplicao de trs camadas com EJB 3, utilizando os recursos do mais novo NetBeans e conhecendo prticas e tcnicas importantes para aplicaes reais A tradicional plataforma J2EE sempre teve fama de ser poderosa, porm difcil, mas podemos argumentar que este julgamento discutvel. Difcil comparado com o que? Os crticos mais severos deveriam ter experimentado construir aplicaes com recursos como distribuio, concorrncia, persistncia, clustering, transaes e segurana, l por 1999, antes da introduo do J2EE 1.0 e EJB 1.0! Mas 1999 est longe. Uma tecnologia sempre comparada ao estado de arte do seu campo, e sua concorrncia. Hoje os Entity Beans so comparados ao Hibernate, os servlets/JSP ao Rails; a linguagem Java a Ruby ou Python, as plataformas Java SE e EE a .NET ou a LAMP, e assim por diante. Por isso o Java no pode parar. E interessante notar que o foco da competio nem sempre o mesmo. Em alguns momentos, o desempenho como nos anos 90, antes das JVMs modernas, quando o Java tinha grande

desvantagem contra linguagens como C/C++. Em outros momentos, a funcionalidade como h poucos anos na febre dos web services, quando todos os fornecedores de tecnologia se atropelavam para implementar suporte a SOAP e padres relacionados. Agora estamos num momento em que a prioridade geral a facilidade de desenvolvimento. Isso sem dvida resultado do amadurecimento e do conhecimento acmulo de funcionalidades das plataformas de desenvolvimento hoje m uso seja no Java, no .NET, ou mesmo em opes tradicionais (como C/C++ com os SDKs da Microsoft, ou em Unix com todas as suas bibliotecas). As conseqncias de toda a sofisticao hoje existente so assustadoras; todos os SDKs so enormes. Kits de documentao podem ocupar centenas de megabytes no seu disco, e alguns IDEs recentes tm gigabytes. Como mais complexidade em princpio implica maior esforo de aprendizado e custo de desenvolvimento mais alto, natural que a corrida hoje seja pela produtividade. A resposta do Java para este desafio vejo como o Java EE 5, que vimos no artigo Java EE 5 na Edio 39, destaca-se pelas inovaes direcionadas a uma maior produtividade. Guiaremos o leitor pelo desenvolvimento de uma aplicao de trs camadas, usando Session Beans do EJB 3.0, persistncia em banco de dados com JPA e uma interface web. No nos entendermos sobre APIs com JPA, recm coberta em Persistncia no Java EE 5 (Edio 39). Ao invs disso, vamos analisar o ciclo de desenvolvimento como um todo. E utilizaremos o IDE NetBeans 5.5, que no momento em que este artigo escrito, tem o suporte integrado mais completo plataforma Java EE 5. Instalando o NetBeans e o servidor de aplicaes Para acompanhar este tutorial, voc precisar, alm do NetBeans (5.5 ou superior), de um servidor de aplicaes suportado por este IDE. Recomendo o Sun Java System Application Server 9.0 (SJSAS tambm conhecido como Java EE 5 SDK) ou sua distribuio open source Glassfish (v1 Milestone 7) por ser o mais bem suportado pelo NetBeans. O GlassFIsh/SJSAS tambm o nico destes que atualmente implementa de forma completa e certificada o Java EE 5. Outros servidores j suportados pelo NetBeans so JBoss, WebLogic e Jonas (este exigindo o plugin JOnbAS). Em caso de dvida, o mais fcil baixar um dos downloads integrados: esto disponveis downloads do NetBeans + SJSAS, e NetBeans + JBoss e o WebLogic (bem como outros servidores) possuem suporte parcial ao Java EE 5, em pr-release. Mas quando voc estiver lendo, bem provvel que a lista de servidores certificados Java EE 5 tenha aumentado e que mais destes sejam suportados pelo NetBeans. Iniciando com a aplicao Vamos comear a aplicao no NetBeans. Selecione File/New Project>Enterprise/Enterprise Application. Este assistente cria um grupo de projetos de aplicao Java EE. Na aba Name and location, preencha o Project Name, ex.: JavaMagazine, e seu diretrio-raiz, e aceite do defaults para as demais opes, como na Figura 1. O resultado ser uma estrutura de trs projetos: JavaMagaziner-ejb, que ir gerar o EJB-JAR; JavaMagazine-war que gera o WAR; e JavaMagazine, que gera o EAR. Que j construiu aplicaes J2EE tradicionais ver que o padro de deployment de aplicaes, e conseqentemente o de organizao de projetos, no parece ter mudado com o Java EE 5. Na verdade, o padro de deployment teve simplificaes, como j vimos nesta coluna, com a remoo ou simplificao de descritores. Por exemplo, em JavaMagazine-ejb/Configuration Files, voc ver que o NetBeans ir criar somente um arquivo: o MANIFEST.MF, cuja incluso uma boa prtica para qualquer tipo de JAR. O Java EE 5 no exige mais este arquivo para incluir no classpath do mdulo os JARs empacotados no mesmo EAR; portanto raramente ser necessrio. O tradicional ejb-jar.xml (descritor de EJB padro) no ser mais criado. Mas dependendo das operaes que voc fizer no projeto, o NetBeans poder criar o sun-ejb-jar.xml (descritor especfico do SJSAS/GlassFish), ou talvez outro descritor proprietrio se voc tiver utilizado outro servodor. Pode parecer estranho no precisamos mais de um descritor padro e continarmos usando o proprietrio. Mais veja porque isso faz sentido. O ejb-jar.xml opcional porque as informaes que antes se encontravam nele agora podem ser determinadas por anotaes em cdigo. Porm, o

cdigo-fonte deve ser mantido portvel. Voc no iria querer utilizar anotaes proprietrias que continuaro existindo para opes de runtime (ex.: nmeros de porta, tamanhos de pools, timeouts etc.) que sempre sero especficas a cada servidor. Por isso, os descritores proprietrios podero continuar sendo necessrios, ainda que raramente.

Figura 1. Criando um projeto Java EE no NetBeans. Implementando as entidades persistentes Vamos agora camada de back-end da aplicao, gerada pelo nosso projeto JavaMagazine-ejb. Nossa aplicao tem apenas duas entidades, Edio e Artigo, persistidas pela Java Persistence API (JPA). E teremos um Session Bean chamado JavaMagazine com mtodos para consultar e modificar estas entidades. Pela descrio, vemos que uma aplicao bastante simples. Em casos como esse, a reclamao antiga da plataforma J2EE que, embora a burocracia de suas APIs e descritores pudesse se justificar para aplicaes ambiciosas, cheia de requisitos avanados como clustering, transaes distribudas ou segurana, essas exigncias eram um canho para matar mosca no caso de aplicaes mais simples sem estes requisitos. a escabilidade de complexidade: aplicaes simples devem resultar em codificao simples, ainda que a plataforma tambm suporte aplicaes muito mais complexas. Vejamos ento se o Java EE 5 cumpre a promessa de ser escapvel no quesito de complexidade. A entidade Edicao Sem nos aprofundar na JPA, vamos direto ao ponto com nossas duas entidades, aproveitando as facilidades do NetBeans. Com o projeto JavaMagazine-ejb selecionado, acione New>Entity Class, que traz o assistente ilustrado na Figura 2. Preencha o nome da classe com Edio, e o pacote com vo (de value objects). Aceite o default de Long para tipo da chave primria numrica artificial (gerada automaticamente).

Figura 2. Criando uma entidade persistente. Como esta a primeira entidade persistente do projeto, o assistente exibir um boto Create Persistence Unit. Voc deve executar essa tarefa antes de conseguir criar qualquer entidade. Acione o boto e ser exibido o dialogo da Figura 3. Uma Persistence Unit (unidade de persistncia, ou UP) um conjunto de configuraes de persistncia. Cada entidade associada a um UP. Uma aplicao Java EE pode possuir vrias UPs, se necessrio, para melhor organizar mapeamentos muito complexos (num EAR com vrios mdulos EJB, cada mdulo EJB ter sua prpria UP, que por sua vez, poder definir vrias entidades). Voc deve escolher um nome qualquer para a unidade de persistncia, e determinar qual runtime ser usado e qual o nome do datasource para acesso ao bando de dados. Se voc estiver usando o SJSAS ou o GlassFish, selecione TopLink(default). O TopLink um produto de mapeamento objeto-relacional da Oracle que possui uma verso open source, o TopLink Essencials. Na aplicao de exemplo usaremos o TopLink Essencials como provedor de JPA. Para o datasource, selecione a opo predefinida jdbc/sample, que aponta para um banco de dados Derby embutido neste servidor de aplicaes. (Usurios de outros servidores devero determinar qual implementao de persistncia deve ser usada, e podem ter que criar uma bas de dados vazia e o datasource.)

Figura 3. Criando uma Unidade de Persistncia Tambm possvel ignorar o provedor de JPA default do seu servidor e instalar seu provedor favorito, por exemplo o Hibernate Entity Manager em SJSAS/GlassFish. Esta outra vantagem do Java EE 5: a JPA suporta provedores plugveis, diferentemente do EJB/CMP, em que tipicamente cada servidor impunha sua implementao de CMP (persistncia gerenciada pelo container). Para fazer essa instalao, abra o arquivo JavaMagazine-ejb/Configuration Files/persistence.xml: este o descritor principal de persistncia, que pode conter detalhes de mapeamento, ou apontar para arquivos de mapeamento externos (inclusive proprietrios: quem tiver legado do Hibernate, pode aproveitar seus tradicionais .hbm!) No faremos isso neste artigo, pois definiremos o mapeamento somente com anotaes. Mas voc pode ver, no persistence.xml mnimo gerado pelo assistente do NetBeans, uma tag importante: <provider>oracle.toplink.essentials.ejb.cpm3.Entity ManagerFactoryProvider</provider> Esta tag <provider> determina qual o provedor de JPA a utilizar. Tambm pode haver tags de propriedades especificas a cada provedor. Para usar outro provedor, consulte sua documentao. Tipicamente, ser preciso instalar os JARs do provedor no diretrio lib do servidor de aplicaes, e preencher o persistence.xml com os valores corretos para a tag <provider> (ambas as tarefas podero ser feitas por plug-ins que suportem provedores especficos no IDE). Voltando ao tutorial, o cdigo gerado pelo assistente est na Listagem 1. Temos um POJO (objeto Java comum, que no implementam ou herda nenhuma API), no qual destacamos algumas caractersticas: Usamos algumas anotaes da JPA: @Entity identifica a entidade persistente, @Id seleciona o atributo usado como chave-primria, e @GereratedValue customiza o mesmo atributo para que o valor da chave-primria seja criado de forma automtica. A entidade deve possuir um construtor default, alm de getters e setters no estilo JavaBeans para seus atributos ou relacionamentos persistentes. comum utilizar objetos como Long, ao invs de tipos primitivos como long, para representar atributos persistentes, por isso nos permite suportar colunas nulaveis. Mas isso no uma exigncia da JPA. Poderamos ter declarado nossa chave-primria como long-id, o que mais eficiente (e como chaves-primrias nunca podem ser nulas, neste caso no haveria desvantagem). O Hibernate aceita construtores privados, e tambm getters e setters privados para propriedade que o programa no quer expor. Mas na JPA no tem esta capacidade; os construtores e mtodos devem ser public ou protected. Na listagem, destacamos em negrito as partes obrigatrias do cdigo: apenas uma dzia de linhas, sem contar os imports e os comentrios. Todo o resto hashCode(), equals(), toString(), anotaes @Override bnus do assistente do NetBeans. So boas prticas de programao, mas no so exigncias da JPA (e para simplificar, vamos omitir essas partes em listagens futuras). A JPA tambm no exige que as entidades sejam serializveis (mas chaves-primrias sim). Porm, na prtica smpre implementaremos Serializable, pois isso nos permite usar menos objetos com DTOs (data transfer objects), por exemplo em inovaes de EJBs, cujos argumentos e valores de retorno devem ser serializveis.

Os mtodos hashCode() e equals() de uma entidade no so utilizadas pelo JPA, pois esta API utiliza somente o atributo de chave-primria (marcado com @Id) para finalidades de identificao da entidade. Listagem 1. Cdigo inicial gerado para entidade Edicao. package vo; import Java.io.Serializable; import javax.persistence.*; @Entity public class Edicao implements Serializable { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; public Edicao() { } public Long getId() { return this.id; } public void setId(Long id) { this.id = id; } @Override public int hashCode() { int hash = 0; hash += (this.id != null ? this.id.hashCode() : 0); return hash; } @Override public Boolean equals(Object object) { if (!(object instanceof Edicao)) { return false; } Edicao other = (Edicao) object; if (this.id != other.id && ( this.id ==null l l !this.id.equals( other.id))) return false; return true; } @Override public String toString() { return vo.Edicao[id= + id + ]; } } A entidade Artigo Vamos agora implementar a entidade Artigo, que possui uma relao muitos-para-um com Edicao. Para complicar um pouco, decidi que o Artigo deve ter uma chave-primria natural, sendo composta por duas propriedades: o ttulo do artigo e o nome do autor. Assim diferentes autores podem, eventualmente, escrever artigos com nomes exatamente iguais, mas espera-se que pelo menos o mesmo autor no repita SUS prprios ttulos, ainda que em edies distintas.

Na JPA, chaves-primrias compostas so suportadas por entidades embutveis (embelddable). Neste exemplo, precisaremos criar uma classe, ArtigoPK, que encapsula os componentes da chave as propriedades titulo e autor (PK abrevia Primary Key). O NetBeans no possui um assistente especfico para isso, portanto, temos que implementar mo o cdigo da Listagem 2. Este cdigo tambm um POJO e segue muitas das regras anteriores (classe Serializable. Construtor default etc.). A novidade a anotao @Embeddable. Esta anotao denota objetos que no so persistentes de forma independente so apenas agrupamentos de propriedades de outro objeto persistente. Uma observao importante sobre classes de chave-primria: uma implementao correta e eficiente dos mtodos hashCode() e equals() obrigatria, pois a JPA ir depender muito destes mtodos, por exemplo para gerenciar caches de objetos em memria. Aqui no recomendo usar facilidades como as classes HashCodeBuilder e EqualsBuilder do projeto Jakarta Commons Lang, que automatizam estes mtodos, mas com um custo significativo em desempenho. Resolvido o problema da PK composta, podemos usar novamente o assistente de Entity Class. Preencha o nome da classe Artigo, e o pacote novamente com vo. Mas agora, no campo Primary key type, selecione o boto ... direita e ento escolha a ArtigoPK entre as classes listadas (tambm seria possvel digitar o nome completo). Novamente, o assistente ir criar boa parte do cdigo que precisamos. Mas ele comete um erro (talvez por ter sido usada uma verso beta do NetBeans 5.5): as tags @Id e @GeneratedValue no so vlidas para chaves compostas. Voc deve substituir ambas as anotaes por uma nica anotao @Embeddedld. O assistente tambm gera um atributo id e mtodos getId()/setId(), mas no recomendo este template de implementao. Para chaves embedded, melhor encapsular a classe de PK, e expor construtores e getters para seus componentes individuais, como se fossem atributos diretos da classe de entidade. Assim, se algum tiver um Artigo art, poder obter o nome do autor simplesmente com art.getAutor(), sem ter que fazer duas chamadas como em art.getId().getAutor(). A estrutura da chave-primria (PK) um detalhe de implementao da entidade. O encapsulamento deste tipo de detalhe uma regra elementar de design orientao a objetos. Ao lidar diretamente com JPA, difcil evitar a manipulao explcita de objetos de PK, por exemplo, ao invocar mtodos como EntityManager. Find(Class classe, Objects chave). Mas ao exportar esta funcionalidade para clientes do mdulo, por exemplo via mtodos de Session Beans, voc deve evitar expor as PKs. Assim, nunca crie um mtodo localizaArtigo(ArtigoPK pk); prefira localizaArtigo(String titulo, String autor). O problema de expor a PK que esse o tipo de detalhe de esquema de dados, que muitas vezes pode mudar. Digamos que um dia o DBA do projeto constante que no existe nenhuma repetio da coluna Artigo.titulo. O DBA ir querer alterar a tabela, usando apenas a coluna titulo como PK (o que mais eficiente!). Como fazer isso sem quebrar cdigo pr-existente? Mtodos que expem a PK quebrariam clientes, pois cdigos como newArtigoPK(titulo,autor) no funcionariam mais, j que ArtigoPK deixa de ter o autor ou melhor, deixa de existir totalmente, pois no precisamos de uma classe para encapsular uma PK no-composta! Por outro lado, um mtodo localizaArtigo(String titulo, String autor) continuaria funcionando com perfeio. Voltando ao cdigo da entidade Artigo: agora basta completar a classe, que tambm possui uma propriedade no-chave texto e um relacionamento com Edicao. Fazendo isto, seu IDE estar similar Figura 4. Observe que neste ponto o NetBeans apresenta uma marca de advertncia em amarelo, ao lado da declarao de Edicao edicao. A mensagem entity relation not defined reclama que este atributo parece ser um relacionamento com outra entidade, mas que falta uma anotao obrigatria para especific-lo.

Clicando na lmpada ao lado voc ter varias opes de correo do problema. Selecione Create bidirecional Many/ToOne relationship, o que apresenta o assistente da Figura 5. Como o relacionamento desejado bidirecional, a JPA exige que a entidade localizada no outro lado do relacionamento (Edicao) tenha um atributo que aponte o Artigo. Se este atributo j existisse, o assistente permitiria selecion-lo; como no existe, ele se oferece para cri-lo. Aceite o default de Create new Field com o nome artigos (gerado automaticamente , igual ao nome da entidade em minsculas e no plural). O resultado ser a Listagem 3, com anotao @ManyToOne. Nossa classe Edicao tambm estar completa agora,devendo estar parecida com a Listagem 4. Note que o assistente de criao de relacionamento gerou um atributo List<Artigo>artigos, com uma anotao @OneToMany(mappedBy=edicao). Mas temos que modificar esta anotao para incluir mais alguns atributos, ficando assim: @OneToMany(mappedBy=edicao, cascade=CascadeType.ALL,fetch=FetchType.EAGER) private List<Artigo>artigos; O atributo mappedBy=edicao opcional se na entidade Artigo s existir uma propriedade com o tipo Edicao (que o nosso caso). Mas como voc poderia ter no Artigo vrios relacionamentos independentes com objetos Edicao (ou acrescent-los futuramente), o assistente por segurana sempre gera o mappedBy. Se quiser, voc pode eliminar este parmetro. Explicaremos os atributos cascade e fetch mais adiante.

Figura 4. O NetBeans indicando a falta de anotaes de persistncia, e oferecendo-se para cri-las.

Figura 5. Assistente de criao de relacionamento Listagem 2. Classe de chave-primria composta para a entidade Artigo. package vo; import Java.io.Serializable; import Java.util.Date; import javax.persistence.*; @Embeddable public class ArtigoPK implements Serializable { private String titulo; private String autor; protected ArtigoPK () {} public ArtigoPK (String titulo, String autor) { this.titulo = titulo; this.autor = autor; } public String getAutor() { return autor; } public String getTitulo() ( return titulo; } public void setAutor(String autor) { this.autor = autor; } public void setTitulo(String titulo) { this.titulo = titulo; } public int hashCode() { return titulo.hashCode() ^ autor.hashCode(); } public boolean equals(Object obj) { if (obj instanceof ArtigoPK) { ArtigoPK a = (ArtigoPK)obj; return titulo.equals(a.titulo) && autor.equals(a.autor); }

else return false; } } Listagem 3.A entidade Artigo completa. package vo; import java.io.Serializable; import javax.persistence.*; @Entity public class Artigo implements Serializable { @Embeddedld private ArtigoPK id; private String texto; @ManyToOne private Edicao edicao; protected Artigo () { } public Artigo ( Edicao edio, String titulo. String autor, String texto) { this.id = new ArtigoPK(titulo, autor); this.edicao = edicao; this.texto = texto; } public ArtigoPK getId () { return this.id; } public void setId (ArtigoPK id) { this.id = id; } public String getTitulo () { return id.getTitulo(); } public String getAutor () { return id.getAutor(); } public String getTexto () { return texto; } public void setTexto (String texto) { this.texto = texto; } public Edicao getEdicao () { return edicao; } public void setEdicao (Edicao edicao) { this.edicao = edicao; } }

Listagem 4. A entidade Edicao completa. package vo; import java.io.Serializable; import java.util.List; import javax.persistence.*; @Entity public class Edicao implements Serializable { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @OneToMany(mappedBy = edicao, cascade=CascadeType.ALL, fetch=FetchType.EAGER) private List<Artigo> artigos; public Edicao () { } public Long getld () { return this.id; } public void setld (Long id) { this.id = id; } public List<Artigo> getArtigos () { return artigos; } public void setArtigos (List<Artigo> artigos) { this.artigos = artigos; } } Testando o deploy A parte de persistncia da aplicao est completa. Para testar, v na janela Runtime e inicie o servidor. Depois, no projeto JavaMagazine, acione o comando Deploy project. Aps algum tempo, voc dever ver os logs de sucesso da operao nas janelas de console do servidor e da tarefa JavaMagazine(run-deploy). Como, ao criar a Persistence Unit, utilizamos a opo Table Generation Strategy = Drop and Create, o deploy tambm ir criar as tabelas necessrias. Se as tabelas j existirem mas forem diferentes do desejado (devido a alteraes das entidades desde o ultimo deploy), elas sero destrudas e recriadas. A inteligncia e os limites das anotaes Como neste artigo tambm queremos das uma viso crtica da facilidade de programao do Java EE 5, no poderamos terminar esta seo deixando parecer que todo o trabalho de programao de persistncia ser to simples quanto em nosso exemplo (que tem uma complexidade necessariamente limitada). Ao ouvir falar de anotaes pela primeira vez, muitos desenvolvedores acham que s mudamos as coisas de lugar. Ou seja, metadados que antes ficavam em descritores (arquivos XML) agora ficam em anotaes. E essas anotaes tm que ser escritas do mesmo jeito, ainda que sua vinculao com as classes facilite a edio e previna erros de sincronia (mudar o cdigo e esquecer de mudar os metadados). Mas como podemos deduzir pelo exemplo, a vantagem das anotaes vai alm disso, porque no precisamos usar anotaes para todos os metadados que antes ficariam em descritores. Por

exemplo, voc no viu nenhuma anotao declarando que a propriedade Artigo.texto persistente. Nem viu uma anotao para declarar o nome da tabela usada por Artigo. Estas anotaes (e muitas outras que no mencionamos) existem. Por exemplo, poderamos ter escrito: @Entity @Table(name=T_Artigo,schema=JM) public class Artigo implements Serializable { @Column(name=TEXTO,nullable=false,length=512). private String texto; Estas anotaes so opcionais porque, na sua falta, a JPA (assim como o EJB 3.0 em geral, e vrias APIs incrementadas por anotaes) seleciona defaults a partir do prprio cdigo. Na ausncia de @Table, o nome da tabela ser igual ao nome da classe. Na ausncia de @Column, todo atributo ser considerado persistente e a coluna correspondente ter o nome do atributo. (Se voc quiser o POJO possua algum atributo no-persistente, deve anotar este atributo com @Transient, que tem um significado semelhante ao do modificador transient do Java SE para serializao). Em teoria, um sistema baseado em arquivos de configurao, como o EJB/CMP ou mesmo o Hibernate tradicional, poderia tambm deduzir defaults do cdigo. Mas a ligao forte entre cdigo e anotaes, e a existncia de APIs e ferramentas especiais no Java SE para manipular anotaes, tornam esta estratgia mais simples e eficiente. Outra moleza do nosso exemplo que no precisamos escrever um script SQL para criar as tabelas. Isso ser feito automaticamente pala JPA, que na falta de anotaes, utilizar defaults razoveis para detalhes como nulabilidade, tamanho de colunas, etc. Graas gerao automtica de tabelas e base de dados Derby embutida do SJSAS, voc pode desenvolver a aplicao inteira sem nem saber que existe um gerenciador de banco de dados por trs! Estas facilidades da JPA e das ferramentas de desenvolvimento permitem avanar rapidamente no desenvolvimento sem tais preocupaes. Do al mundo ao mundo real... Poderamos perguntar: at que ponto um exemplo como o da nossa aplicao JavaMagazine realista? Em aplicaes reais, comum a existncia de restries quanto ao esquema de dados. Por exemplo, voc pode ter que usar tabelas legadas pr-existentes, ou ento definir suas tabelas seguindo padres impostos pelo cliente, por exemplo com o prefixo T_ ilustrado anteriormente. Assim, os nomes de tabelas e colunas no serviro bem para nomear classes e atributos. E voc poder ter que usar anotaes @Table em todas as tabelas e anotaes @Column em todos os atributos. Outro motivo comum para no podermos nos beneficiar de defaults do EJB 3 o ajuste fino de opes com impacto em desempenho (como a opo fetch dos relacionamentos) ou com efeito sobre a integridade (como constraints). Tudo isso significa que, no pior caso, sua classe da entidade acabar possuindo muitas anotaes, com praticamente todas as informaes que antes estavam num descritor ou script SQL. Mas isso no significa que a facilidade proporcionada pelos comportamentos default do EJB 3 seja ilusria. Significa, apenas, que o esforo de codificao proporcional complexidade da aplicao. Se voc puder criar o esquema do zero e no tiver que seguir nenhum padro de estilo, poder usar os defaults de nome de tabelas e colunas. Se no tiver grandes necessidades de desempenho, poder usar defaults da JPA para fatores de performance (como leitura de dados otimizada por outer joins). E isso responde maior reclamao sobre o J2EE tradicional: que a tecnologia era complicada no s para fazer coisas complicadas, mas at mesmo para coisas simples.

Implementando o Session Bean Liquidadas as entidades persistentes, nosso prximo passo ser implementar um Stateless Session Bean com mtodos para localizar e manipular as entidades. Selecionando o projeto JavaMagazine-ejb, comece acionando New>Session Bean. No assistente de criao, preencha EJB name com JavaMagazine e Package com business, aceitando os defaults para as opes Stateless e interface Local, mas marcando tambm a opo de interface remota. O assistente criar um esqueleto de Session Bean, formado pela classe JavaMagazineLocal e JavaMagazineRemote. Voc dever complementar estes esqueletos para conter o cdigo das Listagens 5, 6 e 7. A Listagem 5 mostra a interface local do Session Bean, JavaMagazineLocal. Observe que temos uma anotao @Local, mas no precisamos mais herdar a interface EJBLocalObject. Em paralelo aos POJOs, chamamos de POJI (Plain Old Java Interface) a interfaces que no possuem dependncias de APIs, no precisando herdar nenhuma outra interface. A interface JavaMagazineRemote (no mostrada aqui) ser igual local: basta copiar as declaraes de mtodos, e substituir a anotao @Local por @Remote. Na Listagem 6, vemos a implementao do nosso Stateless Session Bean, qualificao como tal pela anotao @Stateless. Note que a classe JavaMagazineBean implementa as duas interfaces, JavaMagazineLocal e JavaMagazineRemote. Isto no gera nenhum conflito, pois os mtodos comuns a ambas so iguais: na interface remota, os mtodos no precisam mais lanar a RemoteException. Digo mtodos comuns ambas porque no precisamos expor todos os mtodos do bean em ambas as interfaces. Assim como nas verses anteriores do EJB, temos a liberdade de publicar alguns mtodos somente para acesso local e alguns mtodos somente para acesso remoto. Uma boa idia criar uma nova interface JavaMagazine com todos os mtodos em comum e herd-la em ambas as interfaces local e remoto. (Mas no possvel criar uma interface nica e anot-la com @Local e @Remote simultaneamente.) De qualquer forma, com a possibilidade de implementar todas as interfaces de acesso na classe do bean, ganhamos a tipagem forte que faltava no EJB. Quantas vezes voc j definiu um novo mtodo numa interface remota de EJB mas esqueceu de implement-lo na classe, ou talvez digitou sua assinatura diferente por engano (ex.: m(A a,B b) na interface, e m(B b, A a) no bean)? Embora alguns IDEs possuam validadores que capturam este erro antes de termos todo o trabalho de tentar um deploy, bem melhor se isto for feito pelo compilador, editor, e outras ferramentas bsicas. O EJB 3.0 acaba com este problema, e tambm ganhamos outras vantagens da tipagem forte, como a possibilidade de fazer refactoring (ex.: ao renomear um mtodo na interface, sua implementao na classe tambm ser renomeada). Agora vamos implementao do bean. Temos uma anotao @PersistenceContext que faz a injeo de dependncia do EntityManager (objeto da JPA responsvel por coordenar a persistncia). Quando qualquer mtodo de JavaMagazineBean for executado, o atributo EntityManager em unidade de persistncia do modulo JavaMagazine-ejb). O mtodo consultaEdicoes() executa uma query que retorna todos os objetos Edicao do banco de dados. Observe que estes objetos j viro preenchidos com as colees de artigos, devido ao uso da opo fetch = FetchType.EAGER na anotao deste relacionamento. Esta opo informa que desejamos que, sempre um objeto Edicao seja lido, todos os seus artigos sejam lidos no mesmo momento. Esta uma otimizao de desempenho importante (se os artigos forem acessados com freqncia), pois a JPA poder utilizar uma query nica com um outer join, ao invs de queries separadas para o objeto principal e para o relacionamento. Mas o fetch tambm tem uma utilidade importante quando quisermos usar os objetos com Vos. Enquanto os objetos lidos pela JPA esto associados ao EntityManager e transao na qual foram lidos, estes objetos so gerenciados pela JPA. Assim, com fetch = FetchType.LAZY (que o default), a query poderia retornar as instncias de Edicao com todos os atributos List<Artigo>artigos no inicializados (colees vazias). Somente quando voc utilizasse estas

colees pela primeira vez, por exemplo com edicao.getArtigos().iterator(), a JPA iria fazer uma nova query para preencher a coleo. O problema que, se voc transferir uma Edicao com valor de retorno de uma invocao ao Session Bean JavaMagazine, a aplicao que invocou o bean no recebera um objeto gerenciado pela JPA, e sim objetos comuns da, edicao.getArtigos() iria retornar uma coleo vazia. Uma soluo seria ter um novo mtodo JavaMagazine.consultaArtigos(Edicao edicao), mas essa soluo complicada e ineficiente. Este problema bem conhecido dos usurios do Hibernate, onde os objetos que deixam de ser associados a uma sesso persistente so chamados desvinculados (detached), e a soluo a mesma. Com a opo fetch=FetchType.EAGER, garantimos que os objetos associados a uma entidade principal sejam lidos, podendo ser retornados com segurana pela interface do Session Bean (ou por outro middleware que no possa transmitir o objeto original, precisando copi-lo, serializ-lo ou convert-lo para outro formato: RMI, CORBA, SOAP etc.). Voc no precisa mais fazer lookup de interfaces Home de CMPs, converter dados de CPMs para POJOs, e usar DAOs para controlar a baguna do velho EJB/CMP. Listagem 5.lnterface do Session Bean. package business; import import import import java.util.List; javax.ejb.Local; vo.Artigo; vo.Edicao;

@Local public interface JavaMagazineLocal { void criaEdicao (Edicao edicao); List<Edicao> consultaEdicoes (); } Listagem 6.lmplementao do SessionBean. package business; import import import import import import java.util.List; javax.annotation.Resource; javax.ejb.Stateless; javax.persistence.EntityManager; vo.Artigo; vo.Edicao;

@Stateless public class JavaMagazineBean implements business.JavaMagazineRemote, business.JavaMagazineLocal { @PersistenceContext private EntityManager em; public JavaMagazineBean () { } public void criaEdicao (Edicao edicao) { em.persist(edicao); } public List<Edicao> consultaEdicoes () { return em.createQuery(select e from Edicao e).getResultList();

} } Quem mexeu no meu ciclo de vida? Assim como no caso da JPA, tambm vale para os Sessions Beans a regra que sua codificao ser tanto mais simples ou complexa quanto for a necessidade de cada artefato de cdigo. Por exemplo, os EJBs tradicionais definiam vrios mtodos de ciclo de vida, como ejbCreate(), ejbRemove(), ejbActivate() e setSessionContext() para Session Beans. Todo Session Bean era obrigado a implementar este quadro mtodos. Para Entity Beans, eram sete. J no nosso bean de exemplo, no temos nenhum mtodo dessa natureza. Isso no quer dizer que no existam facilidades de ciclo de vida. Elas existem, mas so identificadas por anotaes. Por exemplo, se voc quiser executar algum cdigo aps a ativao de um bean, basta criar um mtodo como: @PostActivate public void postActivate(InvocationContext ctx) { //Aes a executar aps a ativao } A assinatura do mtodo deve ser public void nome (InvocationContext), onde o nome arbitrrio, e deve possuir a anotao @PostActivate. Diferentemente das entidades da JPA, no entanto, ao programar EJBs veremos que na enorme maioria dos casos, no precisamos implementar estes mtodos opcionais ou usar as anotaes correspondentes. Na minha primeira experincia com o velho J2EE, a grande maioria dos EJBs mesmo em aplicaes complexas usa implementaes vazias da maioria dos mtodos das interfaces SessionBean e EntityBean. Portanto, o modelo de programao do EJB 3.0 tende a produzir cdigo muito mais enxuto para EJBs, mesmo no caso de aplicaes mais ambiciosas. Implementando o cliente web Para testar nosso servidor, criaremos um mdulo web com o projeto JavaMagazine-war. Veremos esta parte mais rapidamente, pois no haver muitas novidades em relao ao que j falamos. Vamos comear configurando o projeto do NetBeans para utilizar a JSF. Na pgina de propriedades do projeto Javamagazine-war, acione Frameworks>Add, escolha JavaServer Faces e confirme. Isto preencher o projeto com os arquivos JAR e configuraes exigidas pela JSF. A aplicao web ter uma pgina JSP que permite criar edies e artigos e listar os j cadastrados no banco de dados. Esta pgina JSP, construda com as taglibs de componentes da JSF, mostrada na Listagem 7, e sua execuo aparece na Figura 6.

Figura 6. Executando a interface web. A pgina JSP utiliza um managed bean que pode ser criado pelo assistente New>JSF Managed Bean. Basta preencher o nome da classe com ClienteJM e do pacote com web, e aceitar os demais defaults. Um managed bean um objeto que interage com uma pgina da JSF para implementar a funcionalidade por trs da apresentao, desempenhando o papel de controller na arquitetura MVC. Para quem conhece o Apache Struts, algo equivalente s Actions com a vantagem que os managed beans so POJOs (s precisam ser registrados no faces-config.xml, o arquivo de configurao do JSF). Mtodos destes beans podem ser invocados para tratar eventos de componentes da JSF; por exemplo, no componente h:commandButton, a diretiva action=#{ClienteJM.criaDados} faz o mtodo criaDados() do bean ser executado quando este boto dor submetido. Na Listagem 8, podemos ver que nosso cdigo de controller mais simples do que o costumeiro em programao J2EE. No precisamos usar a JNDI para fazer o lookup do Session Bean: basta declarar um atributo JavaMagazineRemote (pois queremos acessar a interface remota, para permitir deploy da aplicao EJB e do cliente web em maquinas diferentes) e anot-lo com @EJB, que aciona a injeo de dependncia. E no precisamos tratar de excees RemoteException, embora tenhamos utilizado uma interface remota, pois esta exceo no mais obrigatria. Observe como implementamos o mtodo criaDados(): criamos um objeto Edicao contendo vrios Artigos, e invocamos o mtodo criaEdicao() do Session Bean JavaMagazine. Este mtodo trivial; s invoca o mtodo persist() do EntityManager, passando a Edicao. Seus Artigos sero persistidos por alcance, ou seja, objetos referenciados por um objeto persistido tambm devem ser persistidos. Mas para isso funcionar, temos que declarar o relacionamento Edicao.artigos com a opo cascade=CascadeType.ALL. A regra de cascade determina quais operaes de persistncia (como insero, deleo etc.) persistentes so propagadas por alcance; com CascadeType.ALL ativamos todas, que a opo mais comum para relacionamentos de agregao por valor. Para testar a aplicao finalizada, faa um novo deploy e navegue para a pgina: http://localhost/JavaMagazine-war/faces/index.jsp Clique duas vezes no boto. A cada vez uma edio ser criada, e a pgina exibir uma tabela atualizada com todas as edies e artigos cadastrados. Se voc tiver utilizado a base de banco de dados Derby do SJSAS, voc pode conferir as tabelas facilmente. Na janela,Runtime do NetBeans, no n jdbc:derby://localhost:1527/sample [app on APP], execute Connect, preenchendo o login e senha como app. Voc poder ver a estrutura das tabelas criadas pela JPA, e os registros correspondentes aos objetos que j tiver criado.

Listagem 7. Pgina JSP da GUI. <%@ taglib uri=http://java.sun.com/jsf/html prefix="h" %> <%@ taglib uri=http://java.sun.com/jsf/core prefix=f" %> <html> <head> <title>Java Magazine - Cadastro de Artigos</title> </head> <body> <f:view> <h:form id="criaDados"> <h:commandButton value="Cria dados de teste" action=#{ ClienteJM.criaDados}"/> </h:form> <h:dataTable value="#{ ClienteJM.edicoes}" var=edicao border="1"> <h:column> <f:facet name="header"> <h:outputText value="ID"/> </f:facet> <h:outputText value="#{edicao.id}"/> </h:column> <h:column> <f:facet name="header"> <h:outputText value="Artigos"/> </f:facet> <h:dataTable value="#{ edicao.artigos}" var="artigo" border=1> <h:column> <f:facet name="header"> <h:outputText value="Titulo"/> </f:facet> <h:outputTextvalue="#{artigo.titulo}/> </h:column> <h:column> <f:facet name="header"> <h:outputText value="Autor"/) </f:facet> <h:outputText value="#{artigo.autor}/> </h:column> <h:column> <f:facet name="header"> <h:outputText value="Texto"/> </f:facet> <h:outputText value="#{artigo.texto}/> </h:column> </h:dataTable> </h:column> </h:dataTable> </f:view> </body> </html>

Listagem 8. Managed bean da GUI. package web; import import import import import import business.JavaMagazineRemote; java.util.ArrayList; java.util.List; javax.ejb.EJB; vo.Artigo; vo.Edicao;

public class ClienteJM { @EJB private JavaMagazineRemote jm; private static int proxArtigo = 1; public ClienteJM () { } public void criaDados () { Edicao edicao = new Edicao(); List<Artigo> artigos = new ArrayList<Artigo>(); for (int j = 1; j <= 5; ++j) { artigos.add(new Artigo(edicao, "Titulo" + proxArtigo, "Autor" + proxArtigo. "Texto")); ++proxArtigo; } edicao.setArtigos(artigos); jm.criaEdicao(edicao); } public List<Edicao> getEdicoes () { return jm.consultaEdicoes(); } } Olha, sem JNDI! Talvez voc tenha estranhado que at aqui no mostramos nenhum nome JNDI, nem mesmo em lugares onde ocorre lookup (como a anotao @EJB). que tambm neste caso, o EJB 3.0 gera valores default para os beans e resource references. Se voc quiser, pode ignorar os defaults e forar nomes JNDI explcitos; por exemplo, @Stateless(name=ejb/session/JavaMagazine) e @EJB(name=ejb/session/JavaMagazine). Tambm neste caso, a simplificao feita pelo Java EE 5 tem grande aplicabilidade, pois raras aplicaes enterprise edition exigem um padro especifico e rigoroso de nomes JNDI. Depurao facilitada Durante a programao deste tutorial, houve oportunidades para avalidar a facilidade de uso do Java EE 5 no diagnstico de problemas. O SJSAS gera logs de erro extremamente volumosos (vrias pginas de dumps de muitas excees em cascata), o que dificulta um pouco localizar as mensagens importantes. Mas praticamente todos os erros so muito claros, notadamente os da JPA; por exemplo: Exception[TOPLINK-4002] (Oracle TopLink Essentials 2006.4 (Build 060412)): Oracle.toplink.essencials.exceptions.DatabaseException

Internal Exception: org.apache.derby.client.am.SqlException: The statement was aborted because it would have caused a duplicate key value in a unique or primary key constraint or unique index identifield by SQL060928093043370 defined on ARTIGO.Error Code: -1 Call:INSERT INTO ARTIGO (TEXTO, TITULO, AUTOR, EDICAO_ID) VALUES(?,?,?,?) bind =>[Texto, Titulo1, Autor1, 1] Query: InsertObjectQuery(vo.Artigo@936a66) Se tivssemos usado uma API de baixo nvel como a JDBC, s receberamos uma SQLException apontando a violao de um constraint de chave-primria. Teramos que verificar no nosso script de criao do banco a qual a tabela pertence o constraint, inserir logs no programa para capturar a query ou os valores que causam a violao. Com a JPA, todas essas informaes so apresentadas de forma automtica e bem estruturada. Portanto, o aumento de produtividade vale at quando programamos mal! Concluses O EJB 3.0 um dos carros-chefes da histria de melhoria de produtividade do Java EE 5. O resultado ainda no uma curva de aprendizado trivial, pois afinal de contas o EJB continua sendo uma sistema de componentes sofisticados com um nmero enorme de funcionalidades e conceitos. Mas o EJB 3.0 consegue avanos importantes em vrias reas: 1. Aprendizado incremental. Por exemplo, um iniciante poder escrever suas primeiras aplicaes EJB completas sem nem saber que existe alguma coisa chamada JNDI, pois a plataforma possui comportamentos default inteligentes. 2. Codificao proporcional complexidade dos problemas tambm devido ao uso de comportamentos default. 3. Eliminao das regras linha-dura como a exigncia da RemoteException. 4. Injeo de dependncias, que economiza muito cdigo repetitivo para obteno de recursos e conexo com servios. 5. Substituio de descritores por anotaes, o que facilita manter cdigo e configuraes em sincronia, e permite omitir muitas configuraes para as quais se pode extrair bons defaults do prprio cdigo. 6. Um sistema de persistncia (JPA) completamente novo e muito melhor que o anterior (Entity Beans), unificando as vantagens do Hibernate (persistncia de POJOs), do modelo de programao de anotaes (nada de arquivos de mapeamento complexos), e do Java EE (como controle automtico de transaes). 7. Ferramentas como o IDE NetBeans, com recursos com o assistentes para criar entidades e relacionamentos, e validao, auto-correo e auto-complemento de anotaes. Para quem evitava a J2EE devido sua complexidade, com o Java EE 5 chegada a hora de reavaliar essas escolhas. Embora as necessidades de muitas aplicaes mais simples possam ser satisfeitas pela plataforma Java SE + algumas bibliotecas (como Hibernate), middlewares no-EJB (como RMI ou COBRA), e/ou containers mais simples (como o Tomcat), a integrao destes componentes costuma dar algum trabalho e dor de cabea que no existem ao adotar um servidor Java EE completo. E todos os recursos avanados do Java EE, mesmo que no necessrios na primeira verso da sua aplicao, estaro disposio para uso imediato assim que a aplicao prosperar e crescer. Para saber mais Java EE 5. Edio 39 Viso geral do Java EE 5: conceitos, novidades, funcionalidades. Persistncia no Java EE. Edio 39

Mais detalhes sobre a Java Persistence API e um exemplo passo a passo. LINKS: jcp.org/em/jsr/detail?id=244 Especificao do Java EE 5. netbeans.org IDE NetBeans, cuja verso 5.5 suporta o Java EE 5. java.sun.com/javaee/downloads Java EE 5 SDK/ SJSAS, a Implementao de Referncia do Java EE 5. glassfish.dev.java.net Projeto GlassFish, distribuio open source do mesmo servidor.

Agendamento de tarefas em Java


Usando o melhor que o Java SE e EE oferecem Tire o mximo proveito dos recursos de agendamento disponveis da Java SE e Java EE, e veja como criar uma aplicao Swing de reservas usando vrios design patterns Neste artigo, vamos abordar os recursos de agendamento de tarefas disponveis em Java SE e Java EE. Apresentaremos as classes Timer e TimerTask, presentes na distribuio padro do Java SE desde a verso 1.3, e o servio de Timer (Timer Service) acrescentado ao EJB na verso 2.1. Para ilustrar a utilizao, criaremos um sistema completo de reservas com interface Swing. Agendamento de tarefas O agendamento de tarefas permite programar tarefas (Jobs) para execuo em momentos especficos, ou periodicamente. Este recuso suportado nos prprios sistemas operacionais, por exemplo atravs do agendador de tarefas do Windows ou utilitrio cron do Linux/Unix.Veja alguns cenrios onde o agendamento de tarefas til: Realizar um backup periodicamente Criar diariamente um relatrio consolidado de vendas para a gerncia Enviar um e-mail para os usurios do sistema cuja senha tenha expirado Verificar a cada cinco minutos se foram copiados novos arquivos para um determinado diretrio. O Java oferece elementos para o trabalho com agendamento de tarefas, tanto em sua plataforma Standard como na Enterprise, como veremos em detalhes a seguir. O estudo de caso Para ilustrar a utilizao do agendamento de tarefas com Java, criamos uma aplicao de exemplo que permite a criao de tarefas de verificao automtica de expirao de reservas. Poderamos utilizar este aplicativo em qualquer sistema que trabalhe com o conceito de reserva, como por

exemplo hotis, restaurantes, teatros ou empresas areas. A idia permitir que o prprio aplicativo verifique e atualize os status de reservas expiradas. Cada reserva representada por um objeto da classe br.com.globalcode.model.Reserva. Esta classe tem como atributos um cdigo, uma data de expirao, um status (PENDENTE,CONFIRMADA OU CANCELADA) e um id, que corresponde chave primria no banco de dados. Veja o cdigo completo na Listagem1. O acesso aos dados persistidos para as reservas realizado atravs da classe br.com.globalcode.dao.ReservasDAO. Alm dos mtodos diretamente relacionados manipulao de reservas, esta classe contm os mtodos para obteno e fechamento da conexo com o banco de dados. Usualmente, estes mtodos so implementados em uma classe separada, de maneira a serem compartilhados entre os diversos DAOs do aplicativo, mas no exemplo, por simplificao esto na prpria ReservasDAO. A figura 1 apresenta o diagrama de classe de ReservasDAO. O cdigo completo desta classe e de todo resto da aplicao est disponvel no site da Java Magazine. Alm de reserva tambm criamos a classe br.com.globalcode.model.Agendamento (Listagem 2), que contm apenas uma descrio e a data e o horrio do agendamento. No nosso exemplo o agendamento sempre dirio; por isso esta classe no contm o intervalo para repetio. Note tambm que, como a classe Agendamento no precisa ser persistida, no criamos um DAO para ela.

Figura 1. Diagrama de classe de ReservasDAO Listagem 1. Reserva. package br.com.globalcode.model; import java.text.SimpleDateFormat; import java.util.Date; public class Reserva { public static final SimpleDateFormat FORMATADOR_DATA_USUARIO = new SimpleDateFormat("dd/MM/yyyy hh:mm"); private private private private String codigo; Date dataExpiracao; int id; int status = PENDENTE;

public static final int PENDENTE= 1; public static final int CANCELADA= 2; public static final int CONFIRMADA= 3; public Reserva() { } public Reserva(String codigo. Date dataExpiracao){ this.codigo = codigo;

this.dataExpiracao = dataExpiracao; } public Reserva(int id, String codigo, Date dataExpiracao){ this(codigo,dataExpiracao); this.id = id; } public Reserva(int id, String codigo, Date dataExpiracao. int status){ this(codigo,dataExpiracao); this.id = id; this.status = status; }

public String toString(){ return "Codigo = " + codigo + " data expirao = " + FORMATADOR_DATA_ USUARIO.format(dataExpiracao) + " id= " + id + "status = " + status; } //getters e setters omitidos } Listagem 2.Agendamento. package br.com.globalcode.model; import java.io.Serializable; import java.text.SimpleDateFormat; import java.util.Date; public class Agendamento implements Serializable{ public static final SimpleDateFormat FORMATADOR_DATA_USUARIO = new SimpleDateFormat("dd/MM/yyyy hh:mm"); private String info; private Date data; public Agendamento() { } public Agendamento(String info, Date data){ this.info = info; this.data = data; } public String toString(){ return info + " " + FORMATADOR_DATA_USUARIO.format(data); } //mtodo equals, hashcode, getters e setters omitidos } Agendamento de tarefas com Java SE

Desde a verso 1.3, esto disponveis duas classes para o agendamento de tarefas no Java SE: java.util.Timer e java.util.TimerTask. java.util.TimerTask A classe abstrata TimerTask representa uma tarefa a ser agendada. uma implementao de java.lang.Runnable. Portanto para criar tarefas devemos criar subclasses de TimerTask e implementar o mtodo run(), da mesma forma que feito quando trabalhamos com operaes a serem executadas por threads. A classe permite tambm cancelar a tarefa (cancel()) e obter a data da sua ltima execuo (scheduledExecutionTime()). A classe br.com.globalcode.tasks.VerificadorReservasExpiradas ilustra um exemplo de tarefa baseada em TimerTask. Essa tarefa tem o objetivo de alterar o status de todas as reservas que expiraram at o momento. Ela utiliza a classe ReservasDAO que implementa o design pattern DAO contendo os mtodos para interao com o banco de dados. Veja mais no quadro Patterns Utilizados. O cdigo da classe est na Listagem 3. Listagem 3. VerificadorReservasExpiradas. package br.com.globalcode.tasks; import br.com.globalcode.dao.ReservasDAO; import java.sql.SQLException; import java.util.*; public class VerificadorReservasExpiradas extends TimerTask{ public void run() { ReservasDAO dao = new ReservasDAO(); try { dao.cancelarReservasExpiradas(new Date()); } catch (Exception ex) { String msg = " No foi possvel executar o cancelamento de reservas expiradas"; System.out.println("[VerificadorReservasExpiradas] ERRO : " + msg); ex.printStackTrace() ; } } } java.util.Timer A classe Timer responsvel por realizar o agendamento das tarefas (objetos TimerTask). Tarefas podem ser agendadas basicamente de duas maneiras: executar a tarefa uma nica vez em um instante determinado, ou executar a tarefa periodicamente. Podemos configurar o instante da primeira execuo usando um objeto java.util.Date ou definido um intervalo de tempo (em milissegundos) relativo ao momento atual: Schedule (TimerTask tarefa, Date incio) Schedule (TimerTask tarefa, long atrasoAtelnicio) Para os agendamentos peridicos h duas situaes possveis. O perodo pode ser considerado a partir da ltima execuo da tarefa (atraso fixo ou fixed-delay): Schedule (TimerTask tarefa, Date incio,long perodo) Schedule (TimerTask tarefa, long atrasoAtelnicio,long perodo) Ou o perodo pode ser contado a partir da data de incio (taxa fixa ou fixed-rate):

scheduleAtFixedRate (timerTask tarefa, Date inicio, long periodo) scheduleAtFixedRate (TimerTask tarefa, long atrasoAtelnicio, long periodo) Exemplificando os agendamentos peridicos, suponha que temos um agendamento com incio s 00:00:00 e periodicidade de 10 segundos, e que por alguma razo houve um atraso que s permitiu que a tarefa das 00:00:10 fosse iniciada s 00:00:12. Se for usado o agendamento com fixed-delay, as prximas execues seriam s 00:00:22, 00:00:32, 00:00:42, etc. O intervalo entre as execues permanece constante. Esse tipo de agendamento pode gerar distores ao longo do tempo, ou seja uma tarefa que deveria ser executada todos os dias s 2:00 pode acabar sendo executada s 2:10, digamos. J se for usado o agendamento do tipo fixed-rate, as prximas execues sero s 00:00:20, 00:00:30, 00:00:40 etc. sem atrasos. Note que, neste caso, entre a primeira e a segunda execues ocorreu um intervalo de 8 segundos-menor que o configurado. Ou seja, esse tipo de agendamento no gera distores de horrio ao longo do tempo, mas pode encavalar execues de tarefas se uma delas atrasar demais. A cada objeto Timer corresponde uma thread rodando em segundo plano, que ir chamar o mtodo run() de cada TimerTask medida que seus respectivos intervalos forem expirando. Esta thread por padro no do tipo daemon, ou seja, mantm a JVM no ar mesmo que no existam outras threads do aplicativo executando alm do Timer. Caso desejemos utilizar threads daemon, podemos instanciar o timer passando true no construtor. Alm disso, podemos especificar no construtor o nome da Thread que vai ser criada (Java SE 5 ou superior). E podemos cancelar um Timer (mtodo cancel()), eliminando todos os agendamentos de uma vez, ou limpar de sua lista de tarefas aquelas que foram canceladas (mtodo purge()). A classe br.com.globalcode.GerenciadorAgendamentosJavaSE ilustra em seu mtodo agendar Horario(), um exemplo de criao de um timer que gerencia os agendamentos para a tarefa VerificadorReservasExpiradas.GerenciadorAgendamentosJavaSE possui um Map esttico que contm todos os agendamentos realizados, bem como mtodos para recuperar todos os agendamentos para esta tarefa, agendar novos horrios e cancelar horrios previamente agendados. Veja o cdigo dessa classe na Listagem 4. Listagem 4. GerenciadorAgendamentosJavaSE. package br.com.g1oba1code; import import import import br.com.g1oba1code.dto.Agendamento; br.com.g1oba1code.tasks.VerificadorReservasExpiradas; br.com.g1oba1code.dto.*; java.util.*;

public class GerenciadorAgendamentosJavaSE { public static Map agendamentos = new HashMap(); public void agendarHorario(Date dataAgendamento, String descricao){ Agendamento agendamento = new Agendamento(descricao,dataAgendamento); System.out.println( "[GerenciadorAgendamentosJavaSE agendarHorario ] " + agendamento); Timer timer = new Timer(); // Agendamento dirio, ou seja periodo de repetio = 24 horas timer.schedule(new VerificadorReservasExpiradas(), dataAgendamento, 24*60*60*1000);

agendamentos.put(agendamento, timer); } public Collection getAgendamentos() { return agendamentos.keySet(); } public void cancelarAgendamento(Agendamento agendamento) { System.out.println( "[GerenciadorAgendamentosJavaSE cancelarAgendamento] " + agendamento); Timer timer = (Timer)agendamentos.remove(agendamento); if (timer != null){ timer.cancel (); } } } Agendamento de Tarefas com Java EE No Java Enterprise Edition, desde o J2EE 1.4, est disponvel o servio de timer para EJBs. O objetivo especificar um servio gerenciado pelo servidor de aplicaes, que permite o registro de EJBs para serem chamados como tarefas agendadas. Neste modelo de agendamento, temos trs elementos bsicos (EJB), o agendamento (uma instncia de javax.ejb.Timer) e o agendador (instncia de javax.ejb.TimerService). Veja a Figura 2.

Figura 2. Elementos do agendamento de tarefas em Java EE Desta forma, no precisamos criar uma classe especial para funcionar como tarefa agendada. Basta configurar um mtodo especial no EJB (chamado de mtodo de timeout ou callback) para que aceite chamadas do servio de timer do container. Para isto, temos duas abordagens, que variam de acordo com a verso de EJB utilizada: EJB 2.1 Implementamos a interface javax.ejb.TimedObject e redefinimos o mtodo ejbTimeout(javax.ejb.Timer). EJB 3.0 Em vez de implementar a interface javax.ejb.TimedObject (o que tambm suportado), podemos anotar o mtodo de timeout com @Timeout ou configurar um elemento <timeout-method> no descritor de deployment. O mtodo pode ter qualquer nome, mas deve receber como parmetro uma distncia de javax.ejb.Timer. Cada bean pode ter somente um mtodo de timeout. Javax.ejb.Timer A interface Timer representa um agendamento. Cada servidor de aplicaes possui a sua implementao especfica.Os principais mtodos de Timer so: voidcancel() Cancela os segmento. Date getNextTimeout() Retorna a data da prxima execuo da tarefa. long getTimeRemaining() Retorna o tempo em milissegundos que falta para a prxima execuo da tarefa.

TimerHandle getHandle() Retorna uma referncia serializvel do timer, que pode ser utilizada, por exemplo, para armazenamento em sesses web. Serializable getinfo() Quando um agendamento criado, podemos passar um objeto serializvel qualquer para ser utilizado ao processamento da tarefa. Este mtodo permite que o EJB recupere tal objeto. javax.ejb.TimerService A interface TimerService representa o agendador (cada servidor de aplicaes tem a sua implementao especfica). So oferecidos mtodos para criar e listar agendamentos criados e, de maneira anloga aos agendamentos do Java SE, possvel executar uma tarefa uma vez s em um determinado instante, ou repetidamente de acordo com um intervalo configurado. Os mtodos de agendamento de TimerService so: Timer createTimer ( Date inicio,Serializable info) Timer createTimer (long atrasoAtelnicio,Serializable info) Timer createTimer (Date inicio, long period, Serializable info) Timer createTimer (long atrasoAtelnicio,long periodo,Serializable info) Neste ponto, o leitor pode estar se indagando como feita a amarrao entre o Timer e o componente EJB. Em Java SE, passado um objeto TimerTask no mtodo de agendamento, mas com TimerService s passamos o perodo e um objeto serializvel (ou null, se no existir nenhum). O que ocorre que o prprio TimerService faz o registro do EJB que chamou o mtodo de criao do agendamento, de maneira implcita. Para obter uma referncia para um TimerService, o desenvolvedor do EJB tem trs opes: Utilizar o mtodo getTimerService() do seu contexto (EJBContext) Fazer um lookup JNDI Usar injeo de dependncia utilizando a anotao @Resource Aps obter a referncia para um TimerService, o EJB pode se registrar atravs de um dos mtodos createTimer(). Em Java SE tnhamos o agendamento peridico fixed-delay (criado atravs do mtodo Schedule()) e de taxa fixa (criado com scheduleAtFixedRate()). O agendamento peridico do TimerService tem funcionamento idntico ao tipo fixed-rate do Java SE, ou seja, o intervalo relativo data inicial do Timer. Alm disso, possvel utilizar o TimerService para obter uma lista dos Timers configurados para um determinado EJB, usando o mtodo getTimers(). As Listagens 5, 6, 7 e 8 contm as classes e interfaces necessrias para a criao de um componente EJB (verso 2.1) utilizando o servio de agendamento do Java EE. Este EJB implementa funcionalidades equivalentes classe VerificadorReservasExpiradas apresentada no exemplo para Java SE. As seguintes classes e interfaces foram criadas: VerificadorReservasBeanRemote (Listagem 5) Interface remota do EJB (permite que o bean seja acessado remotamente, ou seja, por componentes externos ao container EJB) VerificadorReservasBeanRemoteBusiness (Listagem 6) Interface que define os mtodos de negcio do EJB. Foram definidos mtodos para criao de agendamentos para verificao de reservas expiradas (mtodo agendarHorario()), recuperao dos agendamentos realizados (getAgendamentos()) e cancelamento de agendamentos (cancelarAgendamento()). VerificadorReservasBeanHome (Listagem 7) Interface home do EJB, que contm os mtodos para criao dos EJBs.

VerificadorReservasBean (Listagem 8) Implementao dos mtodos de criao do EJB, dos mtodos exigidos pela interface TimedObject, alm dos mtodos de negcio definidos na interface VerificadorReservasBeanRemoteBusiness. Listagem 5. VerificadorReservasBeanRemote. package br.com.globalcode.ejb; import javax.ejb.EJBObject; public interface VerificadorReservasRemote extends EJBObject, VerificadorReservasRemoteBusiness {} Listagem 6. VerificadorReservasBeanRemoteBusiness. package br.com.globalcode.ejb; import br.com.globalcode.model .Agendamento; import java.rmi.RemoteException; import java.util.*; public interface VerificadorReservasRemoteBusiness { void agendarHorario(Date dataInicio, String descricao) throws RemoteException; Collection getAgendamentos() throws RemoteException; void cancelarAgendamento(Agendamento agendamento) throws java.rmi.RemoteException; } Listagem 7. VerificadorReservasBeanHome. package br.com.globalcode.ejb; import java.rmi.RemoteException; import javax.ejb.CreateException; import javax.ejb.EJBHome; public interface VerificadorReservasRemoteHome extends javax.ejb.EJBHome { VerificadorReservasRemote create() throws javax.ejb.CreateException, RemoteException; } Listagem8. VerificadorReservasBean. package br.com.globalcode.ejb; import java.rmi.RemoteException; import java.sql.SQLException; import javax.ejb.*;

import java.util.*; import br.com.globalcode.dao.*; import br.com.globalcode.model.Agendamento; public class VerificadorReservasBean implements SessionBean, VerificadorReservasRemoteBusiness, TimedObject { private SessionContext context; public void setSessionContext( SessionContext aContext) { context = aContext; } public void ejbActivate() {} public void ejbPassivate() {} public void ejbRemove() {} public void ejbCreate() {} public void cancelarReservasExpjradas(Date dataAtual) { ReservasDAO dao = new ReservasDAO(); try { dao.cancelarReservasExpiradas(dataAtual) ; } catch (Exception ex) { System.out.println( "Erro ao cancelar reservas expiradas dataAtual = "+ dataAtual); ex. printStackTrace(); } } public void ejbTimeout (Timer timer) { cancelarReservasExpiradas(new Date()); } public void agendarHorario( Date dataAgendamento, String descricao) { TimerService timerService = context.getTimer Service(); timerService.createTimer( dataAgendamento,24*60*60*1000,descricao); System.out.println("Agendando servico de timer ( + descricao+ ")diario as "+ dataAgendamento); } public Collection getAgendamentos() { TimerService timerService = context.getTimerService(); Collection agendamentos = new ArrayList(): Iterator iteratorTimers = timerService.getTimers().iterator();

System.out.println(" Os seguintes agendamentos de servios foram localizados ("+timerService.getTimers().size()+")"); while (iteratorTimers.hasNext()){ Timer timer = (Timer)iteratorTimers.next(); agendamentos.add(new Agendamento( (String)timer.getInfo(), timer.getNextTimeout())); } return agendamentos; } public void cancelarAgendamento( Agendamento agendamento) { TimerService timerService = context.getTimerService(); Collection agendamentos = new ArrayList(); Iterator iteratorTimers = timerService.getTimers().iterator(); while (iteratorTimers.hasNext()){ javax.ejb.Timer timer = ( javax.ejb.Timer)iteratorTimers.next(); if (timer.getInfo() .equals( agendamento.getlnfo())){ timer.cancel (); } } } } Vantagens e limitaes de cada modelo Tendo discutido e demonstrado os recursos de agendamento fornecidos pelo Java SE e EE, vamos comentar algumas vantagens e limitaes de cada um deles. A Tabela 1 apresenta um resumo. Recurso Vantagens Simplicidade; No necessrio nada alm do JRE. Limitaes Perdem-se os agendamentos em caso de queda da JVM S permite dois tipos de agendamento: execuo futura uma nica vez ou com intervalo fixo. Exige um container EJB/servidor de aplicaes; S permite dois tipos de agendamento: execuo futura uma nica vez ou intervalo fixo. No permite agendamento via arquivo de configurao (agendamento declarativo).

Timer e TimerTask

TimerSerice e EJBs

Podemos reaproveitar um EJB j existente; Utilizamos os servios do container EJB de controle de transaes e pooling de objetos; H a recuperao automtica de agendamentos no caso da

queda do servidor. Tabela 1. Resumo das vantagens e limitaes do agendamento padro em Java SE e EE. No Java SE A forma mais simples de criar agendamentos utilizar as classes Timer e TimerTask do Java SE. No precisamos de nenhum recurso adicional de infra-estrutura, bastando haver uma instalao do JRE. Porm, para que o agendamento ocorra, precisamos manter a JVM em execuo. Em caso de queda da JVM ser necessrio refazer todos os agendamentos. Alm disso, h apenas dois tipos de agendamento suportados. Podemos executar a tarefa uma nica vez em um instante determinado, ou execut-la periodicamente com intervalo fixo. Caso seja necessrio um agendamento mais complexo, como por exemplo gerar um relatrio de segunda a sexta s 17:00, mas no aos sbados ou domingos, temos que fazer malabarismos no cdigo. Outra limitao que no existe um mecanismo padro para configurar o agendamento de maneira declarativa, ou seja, atravs da leitura de um arquivo. As alternativas envolvem colocar os perodos fixos no cdigo ou criar um mecanismo proprietrio de leitura. No Java EE Quando utilizamos o servio de Timer do Java EE, precisamos de um servidor de aplicaes J2EE/Java EE. Em compensao, usufrumos de todos os demais servios de infra-estrutura fornecidos pela plataforma Java EE, alm do agendamento em si. Assim temos, por exemplo: Gerenciamento automtico de transaes O mtodo de callback pode ser executado dentro de uma transao. Caso a transao seja cancelada (rollback), o container tenta novamente a execuo. Alm disso, tipicamente a criao de um Timer e seu cancelamento tambm ocorrem dentro de uma transao, permitindo que as operaes sejam desfeitas no caso de um rollback. Gerenciamento de recursos Um agendamento fica associado classe do EJB (com exceo de Entity Beans), e no a uma determinada instncia, permitindo que o container selecione qualquer objeto disponvel para o atendimento do Timer. Isso permite uma melhor utilizao dos objetos disponveis no servidor no h desperdcios com objetos alocados sem fazer nada, esperando que o Timer expire para executarem. No caso de um Entity Bean, o agendamento fica associado chave primria da entidade. Caso a entidade associada seja removida do banco de dados, o seu Timer automaticamente cancelado. Isto evita a alocao de Timers para entidades que no existem mais no banco de dados, desperdiando recursos. Outra vantagem da utilizao do TimerService sua maior robustez, que permite que os agendamentos sobrevivam a quedas do servidor. Quando o servidor reiniciado, os agendamentos so recuperados. Porm, assim como em Java SE, s h suporte para dois tipos de agendamento, e no existe um padro para configurao declarativa de agendamentos. Caso precisaremos destes recursos podemos utilizar frameworks de agendamento, como o excelente projeto open source Quartz (opensymphony.com/quartz), que apresentado em outro artigo nesta edio. Aplicao Swing para teste dos agendamentos Para demonstrar as vrias formas de agendamento, usando Java SE e EE, criamos uma aplicao em Swing utilizando o editor visual do NetBeans 5.5 (e o gerenciador de layoutn GroupLayout). Veja o resultado final na Figura 3.

Figura 3. Tela principal da aplicao Swing para agendamentos de servios Camada de controle Para deixar a aplicao Swing independentemente de uma implementao especfica de agendamento com Java EE ou Java SE, utilizamos os patterns Business Delegate, Service Locator e Factory (veja mais sobre estes e outros design patterns no quadro Patterns utilizados``). Foram implementadas as seguintes classes e interfaces: GerenciadorAgendamentosDelegate (Listagem 9) Interface que define os mtodos utilizados pelo cliente Swing. GerenciadorAgendamentosDelegateJavaEE (Listagem 10) Implementao de interface GerenciadorAgendamentosDelegate para agendamentos de tarefas utilizando Java EE. GerenciadorAgendamentosDelegateJavaSE (Listagem 11) Implementao da interface GerenciadorAgendamentosDelegate para agendamentos de tarefas utilizando Java SE. ServiceLocator Classe utilizada para obter uma referncia para o EJB que implementa o agendamento de tarefas com Java EE. Sua listagem encontra-se disponvel no download deste artigo. GerenciadorAgendamentosFactory (Listagem 12) Implementao do padro Factory utilizando para permitir que a aplicao Swing possa criar objetos dos tipos GerenciadorAgendamentosDelegateJavaEE ou GerenciadorAgendamentosJavaSE indiretamente. Para implementao do agendamento Java EE foi utilizado o servidor de aplicaes JBoss. Para o acesso do aplicativo Swing ao servidor foi criado o arquivo de configurao jndi.properties, que deve estar localizado no diretrio raiz das classes do aplicativo. A Listagem 13 mostra o contedo deste arquivo. Alm disso, necessrio disponibilizar no classpath do aplicativo, o conjunto de classes de acesso ao servidor. Estas classes esto localizadas no arquivo jbossall-client.jar, disponvel no diretrio de bibliotecas-cliente do JBoss (client). Listagem 9. GerenciadorAgendamentosDelegate. package br.com.globalcode.delegate; import br.com.globalcode.model.Agendamento; import java.util.*;

public interface GerenciadorAgendamentosDelegate { void agendarHorario(Date dataAgendamento, String descricao) throws DelegateExeeption; void cancelarAgendamento(Agendamento agendamento) throws DelegateExeeption; Collection getAgendamentos() throws DelegateExeeption; } Listagem 10. GerenciadorAgendamentosDelegateJavaEE. package br.com.globalcode.delegate; import import import import import import br.com.globalcode.ejb.VerificadorReservasRemote; br.com.globalcode.locator.ServiceLocator; br.com.globalcode.model.Agendamento; java.rmi.RemoteException; java.Util.*; java.util.Date;

public class GerenciadorAgendamentosDelegateJavaEE implements GerenciadorAgendamentosDelegate { public void agendarHorario( Date dataAgendamento, String descricao) throws DelegateException { try { VerificadorReservasRemote verificador = ServiceLocator.getInstance( ).lookupVerificadorReservasBean(); verificador.agendarHorario( dataAgendamento, descricao); } catch (RemoteException ex) { throw new DelegateException( "No foi possvel agendar o servio " + descricao + " para a data" + dataAgendamento, ex); } } public void cancelarAgendamento( Agendamento agendamento) throws DelegateException{ try{ VerificadorReservasRemote verificador = ServiceLocator.getInstance(). lookupVerificadorReservasBean(); verificador.cancelarAgendamento(agendamento); } catch (RemoteException ex) { throw new DelegateException( "No foi possvel cancelar o agendamento "

+ agendamento, ex); } } public Collection getAgendamentos() throws DelegateException { try { VerificadorReservasRemote verificador = ServiceLocator.getInstance( ).lookupVerificadorReservasBean(); return verificador.getAgendamentos(); } catch (RemoteException ex) { throw new DelegateException ( "No foi possvel obter todos os agendamentos", ex); } } } Listagem 11.GerenciadorAgendamentosDelegateJavaSE. package br.com.globalcode.delegate; import br.com.globalcode.GerenciadorAgendamentosJavaSE; import br.com.globalcode.model.Agendamento; import java.util.*; public class GerenciadorAgendamentosDelegateJavaSE implementsGereneiadorAgendamentosDelegate { public void agendarHorario( Date dataAgendamento, String descricao) { GerenciadorAgendamentosJavaSE gerenciador = new GerenciadorAgendamentosJavaSE(); gerenciador.agendarHorario( dataAgendamento, descricao); } public void cancelarAgendamento( Agendamento agendamento) { GerenciadorAgendamentosJavaSE gerenciador = new GerenciadorAgendamentosJavaSE(); gerenciador.cancelarAgendamento(agendamento); } public Collection getAgendamentos() { GerenciadorAgendamentosJavaSE gerenciador = new GerenciadorAgendamentosJavaSE(); return gerenciador.getAgendamentos(); }

} Listagem 12. GerenciadorAgendamentosFactory. package br.com.globalcode.factory; import br.com.globalcode.GerenciadorAgendamenkosJavaSE; import br.com.globalcode.delegate.*; public class GerenciadorAgendamentosFactory { public static int GERENCIADOR_AGENDAMENTOS_JAVASE = 1; public static int GERENCIADOR_AGENDAMENTOS_JAVAEE = 2; private static GerenciadorAgendamentosFactory instance = new GerenciadorAgendamentosFactory(); private GerenciadorAgendamentosFactory() {} public GerenciadorAgendamentosDelegate createGerenciadorAgendamentos (int tipo) { if (tipo == GERENCIADOR_AGENDAMENTOS JAVAEE) { return new GerenciadorAgendamentosDelegateJavaEE(); } else if (tipo == GERENCIADOR_AGENDAMENTOS JAVASE){ return new GerenciadorAgendamentosDelegateJavaSE(); } else return null; } public static GerenciadorAgendamentosFactory getInstance() { return instance; } } Listagem 13. jndi.properties. java.naming.provider.url=localhost java.naming.factory.initial = org.jnp.interfaces.NamingContextFactory java.naming.factory.url.pkgs= org.jboss.naming:org.jnp.interfaces Camada de Apresentao Para maior modularizao da aplicao, separamos o painel de agendamento e a janela principal. Para o painel de agendamentos criamos uma classe derivada de JPanel chamada PanelAgendamentoTarefas que includa pela janela principal, representada pela classe Gui. Esta ultima herda de JFrame. A classe PanelAgendamentoTarefas (Listagem 14) responsvel por listar os agendamentos ativos. Para isto utilizamos um componente JList. Quando um agendamento especfico selecionado, so exibidos seus atributos na janela.

Cada agendamento vinculado ao formulrio atravs da chamada ao mtodo bind() que recebe um objeto da classe Agendamento e preenche seus dados nos campos do formulrio. A leitura dos dados do formulrio e a criao de um novo objeto Agendamento so realizadas atravs do mtodo unbind(). Veja mais na Figura 4. Para iniciar o aplicativo devemos executar a classe Gui. No mtodo init() desta classe, indicamos qual gerenciador de agendamentos (implementao com Java SE ou Java EE) dever ser utilizado. O objeto GerenciadorAgendamentos criado atravs da fbrica GerenciadorAgendamentosFactory, e configurado no PanelAgendamentoTarefas atravs do mtodo setGerenciadorAgendamentos(). Os seguintes trechos de cdigo ilustram a utilizao das implementaes baseadas em Java SE e Java EE: Veja o cdigo da classe Gui na Listagem 15.

Figura 4. Sobre a implementao da tela principal Listagem 14. PanelAgendamentoTarefas. package br.com.globalcode.gui; import import import import import import br.com.globalcode.delegate.*; br.com.globalcode.model.Agendamento; java.io.Serializable; java.text.*; java.util.*; javax.swing.JList;

public class PanelAgendamentoTarefas extends javax.swing.JPanel implements Serializable { //...algumas declaraes de variveis e mtodos omit. private GerenciadorAgendamentosDelegate

gerenciadorAgendamentos; private Agendamento entity; public static final SimpleDateFormat FORMATADOR_DATA_USUARIO = new SimpleDateFormat("dd/MM/yyyy hh:mm"); public void configurarEstadoBotoes( boolean salvar, boolean cancelar, boolean novo) { buttomSalvar.setEnabled(salvar); buttomNovoAgendamento.setEnabled(novo); buttonCancelarAgendamento.setEnabled(cancelar); } public void bind(Agendamento agendamento){ this.entity = agendamento; tfAgendamentolnfo.setText(entity.getlnfo()); tfDataInicio.setText( entity.getDataFormatada().substring(0,10)); tfHorarioAgendamento.setText( entity.getDataFormatada().substring(11,16)); } public Agendamento unbind(){ Date dataAgendamento; String strDataAgendamento = tfDataInicio.getText() + " " + tfHorarioAgendamento.getText(); try { dataAgendamento = FORMATADOR_DATA_USUARIO.parse( strDataAgendamento); entity = new Agendamento( tfAgendamentoInfo.getText().dataAgendamento); tfAgendamentolnfo.setText(""); tfDataInicio.setText(""); tfHorarioAgendamento.setText(""); mensagemErro.setText(""); } catch (ParseException ex) { ex.privtStackTrace(); mensagemErro.setText("Data invlida"); } return entity; } public void atualizarListaAgendamentos(){ jListAgendamentos.setListData(( Vector)getAgendamentos()); } public void configurarNovoAgendamento( Agendamento agendamento){ try { gerenciadorAgendamentos.agendarHorario( agendamento.getData(), agendamento.getlnfo());

mensagemErro.setText(""); } catch (DelegateException ex) { ex.printStackTrace(); mensagemErro.setText("Erro; " + ex.getMessage()); } atualizarListaAgendamentos(); } public void cancelarAgendamento( Agendamento agendamento){ try { gerenciadorAgendamentos.cancelarAgendamento( agendamento); mensagemErro.setText(""); } catch (DelegateException ex) { ex.printStackTrace(); mensagemErro.setText("Erro: " + ex.getMessage()); } } //declaraes de varivel e cdigo gerado //automaticamente pelo NetBeans omitidos private void buttomHojeActionPerformed( java.awt.event.ActionEvent evt) { Date hoje = new Date(); SimpleDateFormat formatador = new SimpleDateFormat("dd/MM/yyyy"); this.tfDatalnicio.setText(formatador.format(hoje)); } private void buttonCancelarAgendamentoActionPerformed( java.awt.event.ActionEvent evt) { entity = unbind(); cancelarAgendamento(entity); atualizarListaAgendamentos(); mensagemErro.setText(""); configurarEstadoBotoes(false, false, true); } private void buttomSalvarActionPerformed( java.awt.event.ActionEvent evt) { System.out.println("[buttomSalvarActionPerformed]"); Agendamento agendamento = unbind(); configurarNovoAgendamento(agendamento); configurarEstadoBotoes(false, false, true); } private void buttomNovoAgendamentoActionPerformed( java.awt.event.ActionEvent evt) { System.out.println(

"[buttomNovoAgendamentoActionPerformed]"); this.entity = null; this.tfAgendamentolnfo.setText(""); tfDataInicio.setText(""); tfHorarioAgendamento.setText(""); configurarEstadoBotoes(true,false, false); } private void jListAgendamentosValueChanged{ javax.swing.event.ListSelectionEvent evt) { JList listaAgendamentos = (JList) evt.getSource(); Agendamento agendamento = ( Agendamento) listaAgendamentos.getSelectedValue(); System.out.println("[jListAgendamentosValueChanged] Agendamento Selecionado" + agendamento ); if (agendamento!=null){ bind(agendamento); } configurarEstadoBotoes(false, true, true); } } Listagem 15. Gui. package br.com.globalcode.gui; import br.com.globalcode.factory.GerenciadorAgendamentosFactory; public class Gui extends javax.swing.JFrame { public Gui () { initComponents(); panelAgendamentoTarefas.setGerenciadorAgendamentos( GerenciadorAgendamentosFactory.getlnstance() .createGerenciadorAgendamentos(GerenciadorAgendamentosFactory .GERENCIADOR_AGENDAMENTOS_JAVASE)); /* panelAgendamentoTarefas.setGerenciadorAgendamentos( GerenciadorAgendamentosFactory.getInstance() .createGerenciadorAgendamentos (GerenciadorAgendamentosFactory .GERENCIADOR_AGENDAMENTOS_JAVAEE)); */ } // ... Omitidos: declaraes de variveis e cdigo // gerado automaticamente pelo NetBeans } Concluses O recurso de agendamento de tarefas possui aplicaes, e Java oferece classes que permitem sua utilizao tanto nas plataformas SE (atravs de java.util.Timer e java.util.TimerTask), quanto EE (atravs de javax.ejb.TimerService). Em ambas as plataformas a criao das tarefas e de

seus agendamentos bastante simples, permitindo que mesmo um usurio iniciante possa desenvolver aplicativos que aproveitem este recurso. LINKS: theserverside.com/tt/article.tss?!=MonsonHaefel-Column4 Pgina que apresenta um artigo de Richard Monson-Haefel sobre TimerService opensymphony.com/quartz Site do framework de agendamento Quartz Patterns Utilizados O estudo de caso criado neste artigo faz uso de cinco design patterns importantes no desenvolvimento corporativos com Java: DAO (Data Acess Object) Pattern para desacoplamento entre o cdigo de negcio e o cdigo de persistncia. Envolve a criao de uma classe separada, com todo o cdigo de acesso ao mecanismos de persistncia. Isto traz maior flexibilidade, permitindo a troca do mecanismo de persistncia (por exemplo, alterar o banco de dados usado, ou mesmo, de banco de dados para outros mecanismos, como arquivos XML), sem que seja necessria uma alterao em outras classes do aplicativo. No nosso estudo de caso, utilizamos a classe ReservasDAO para todas as operaes de acesso ao banco de dados relacionadas entidade Reserva. Factory Adaptao do pattern Abstract Factory, em que removemos a responsabilidade pela criao de objetos da classe que os utiliza. No nosso estudo de caso, criamos uma Factory para os objetos que realizam o agendamento, permitindo que a mesma interface grfica fosse utilizada para acesso aos componentes de Java SE e Java EE. A Factory responsvel por decidir qual objeto deve ser instanciado e retornado ao cliente. Business Delegate Pattern que minimiza o acoplamento entre a camada de apresentao e a camada de negcio. A idia bsica centralizar o acesso camada de negcio em um objeto. Desta maneira, todos os componentes que precisarem acessar mtodos de negcio o faro atravs deste objeto. Com isso, alteraes na camada de negcios implicam em alteraes somente no Business Delegate, ao invs de diversas classes espalhadas pela camada de apresentao. Alm disso, o Business Delegate permite que caractersticas especficas da camada de negcio sejam isoladas do aplicativo cliente. No nosso exemplo, isto se manifestou ao criarmos excees de aplicativo customizadas, no propagando para a interface grfica RemoteException, que um detalhe da tecnologia de componentes de negcio especifica utilizada (EJB). Singleton Pattern para controle do nmero de instncias, normalmente utilizado para garantir que somente uma instncia de uma classe criada. Aplicaes comuns desse pattern so evitar o uso de recurso excessivos criando mais objetos do que o necessrio, ou centralizar o acesso em um nico objeto (isso simplifica por exemplo a criao de cache de dados a serem compartilhadas pelo aplicativo). Service Locator Padro que isola o aplicativo dos detalhes de obteno de referncia para um determinado componente. So criados mtodos getXxx() para cada um dos componentes desejados e o Service Locator se encarrega da obteno da referncia, que normalmente ocorre utilizando APIs mais complexas, como JNDI.

Yara M. H. Senger:(yara@globalcode.com.br) formada em Cincias da Computao na USP em So Carlos, especialista em desenvolvimento web; possui as certificaes SCJA, SCIP e SCWCD. Atualmente Instrutora e Diretora Educacional da Globaldode, criadora e coordenadora de diversos cursos das carreiras Academia do Java e Academia do Web Developer.

Menos Bugs com FindBugs Encontrando Defeitos Automaticamente em Cdigo Java Conhea uma ferramenta capaz de analisar seus programas e listar possveis defeitos na programao. Buscamos sempre formas de melhorar a qualidade e a performance do nosso cdigo, e para isso utilizamos vrios tipos de testes e outras tcnicas automatizadas. Mas, ainda que sejam usadas tcnicas sofisticadas de testes, partes do cdigo podem no ser testadas ou se comportar de maneira indevida em condies atpicas. Isso se torna impossvel ( ou extremamente caro) retirar todos os defeitos de um software somente atravs da realizao de testes. A ferramenta que veremos neste artigo, o FindBugs, auxilia na deteco de defeitos/bugs em cdigo Java, atravs da anlise do bytecode. Podemos considerar o FindBugs uma ferramenta complementar aos testes (automatizados e manuais) para a garantia da qualidade em nossas aplicaes. Conhea a ferramenta Obtendo e executando O FindBugs pode ser obtido no site findbugs.sf.net. Aps o download, descompacte o arquivo findbugs-1.0.0.zip (ou verso mais recente) para uma pasta qualquer. Para executar a ferramenta, basta entrar na subpasta bin e executar o arquivo findbugs.bat no Windows ou findbugs no Linux/Unix. A tela principal muito simples, contendo inicialmente apenas uma barra de menus. Tipos de defeitos e seus significados O FindBugs classifica os defeitos encontrados por tipos, que so nomeados com abreviaes. Veja alguns exemplos de tipos de defeitos: BC: Defeitos relacionados com cast ou instanceof como um cast que nunca poder ocorrer e sempre ir lanar uma exceo, ou um instanceof que sempre retornar true ou false. DE: Tratamento incorreto de excees, como capturar e ignorar uma exceo sem executar ao menos um comando no bloco catch. Dm: Indica problemas de performance com cdigo desnecessrio ou incorreto. Por exemplo, executar o mtodo substring() informando o valor 0, o que retorna a mesma string; ou chamar um mtodo da classe Java.util.Calendar, informando como ms em valor fora do intervalo de 0 a 11.

EC: Problemas na chamada do mtodo equals(), que podem ser por exemplo a comparao de um array usando o mtodo equals() de um deles, o que no ir comparar o contedo dos arrays, mas sim verificar se ambos so a mesma referncia (o equivalente ao uso do operador ==). ES: Utilizao do operador == para comparar duas strings. INT: Indica problemas na manipulao de valores inteiros como, por exemplo, o cdigo a seguir, que vai sempre resultar em verdadeiro, pois a operao var % 1 sempre resulta em zero. Nesses casos, fica claro que foi cometido um erro de programao j que o cdigo seria intil: int var =... if(var % 1 ==0){ ... }

NP: Possvel ocorrncia de uma NullPointerException devido manipulao de uma referncia nula. Este tipo compreende mais de 15 defeitos diferentes, indicando desde possveis ocorrncias em determinados cenrios, at trechos de cdigo onde certamente ocorrer uma NullPointerException. OS: Defeitos desse tipo so identificados quando classes que representam streams de dados (java.io.InputStream e java.io.OutputStream) so instncias, mas o mtodo close() no invocado ou no h garantias de que sempre ser invocado. SA: Quando uma varivel atribuda a ela mesma. SQL: Erros na manipulao d APIs em java.sql como, por exemplo, obter um campo de um java.sql.ResultSet informando como ndice o valor 0. UwF: Um atributo nunca inicializado e pode no ser realmente necessrio. UrF: Um atributo nunca lido e talvez possa ser removido. UuF: Um atributo nunca recebe um valor ou lido, e pode ser removido. SBSC: Ocorrncia de concatenao de strings dentro e loops utilizando o operador +, o que indica um potencial problema de performance j que os objetos String so imutveis e concatenaes seguidas geram vrios objetos desnecessariamente. O correto neste caso seria utilizar a classe java.lang.StringBuffer ou, melhor ainda, Java.lang.StringBuilder a partir do Java 5.

Estes so alguns dos tipos de defeitos mais comuns. Mais detalhes sobre os defeitos encontrados pelo FindBugs podem ser vistos em findbugs.sf.net/bugDescriptions.html. Realizando a anlise Para compreender melhor as informaes fornecidas pelo FindBugs, analisaremos um projeto open source de fcil acesso. Escolhemos o Jakarta Commons Lang, por este projeto no ser muito extenso e no ter dependncias com outros projetos. Preparao Para melhor demonstrao o FindBugs, vamos obter das distribuies binrias e de cdigo-fonte do Jakarta CommonsmLang (outra opo baixar apenas os fontes e fazer a compilao com ant dist). O FindBugs sempre efetua a anlise a partir de cdigo compilado, presente em arquivos JAR, WAR, EAR ou .class. Mesmo assim, ser muito til indicar a localizao do cdigo-fonte, para que o FindBugs seja capaz de exibir o trecho problemtico, facilitando a correo. Obtenha os pacotes source e binary de jakarta.apache.org/site/downloads/downloads_commonslang.cgi e descompacte ambos a mesma pasta. Ser gerada a subpasta commons-lang-2.1. Em seguida basta indicar ao FindBugs como localizar os fontes e o JAR para iniciar nossa anlise.

Nosso projeto Abra o FindBugs e selecione File/New Project. Neste momento, dever ser apresentada a tela para criao do novo projeto. Clique em Browse e selecione a pasta ou JAR desejado e depois clique em Add. No primeiro grupo de elementos (Archives/Directories) devero ser indicados os arquivos (JAR, WAR ou EAR) ou diretrio contendo os elementos compilados; e no segundo grupo (Source directories) devero ser especificados os diretrios ou um arquivo ZIP contendo os fontes. No nosso exemplo no ser necessrio indicar entradas para o classpath, j que no h dependncias. Ao fazer anlise de um projeto com dependncias (que geralmente sero arquivos JAR), voc pode selecion-los atravs do terceiro grupo de elementos, adicionando itens lista Classpath entries. Aps selecionar o JAR do projeto (commons-lang-2.1.jar) e o diretrio dos fontes, a tela dever ficar similar Figura 1. Com isso, nosso projeto est configurado e pronto para ser analisado, o que faremos clicando no boto Find Bugs.

Figura 1. Projeto configurado e pronto para ser analisado no FindBugs. Analisando o relatrio Aps a anlise de um projeto, o resultado um relatrio contendo todos os defeitos encontrados. So geradas diversas visualizaes para agrupar os defeitos, como By Class e By Package. Para a nossa anlise, recomendo selecionar a viso By Bug Type, que nos fornece uma viso agregada por tipo de defeito, conforme a Figura 2. Analisando um defeito O primeiro defeito apresentado o tipo Dm, ou seja, um mtodo com possveis problemas de performance. Para obter mais informaes sobre o defeito, ele deve ser selecionado assim como a aba inferior Details, que detalha o que significa o defeito. A Figura 2 apresenta esse cenrio. O defeito apresentado se deve ao uso do construtor da classe java.lang.Boolean, onde o correto seria utilizar o mtodo valueOf() que retorna as constantes Bollean.TRUE ou Bollean.FALSE. Isso economiza memria e processamento, pois no necessrio instanciar um novo objeto. Note que o erro foi localizado na classe org.apache.commons.lang.ArrayUtils, no mtodo add(boolean[], int, boolean), o que informado pelo FindBugs e tambm pode ser observado na Figura 2.

Para facilitar a investigao do problema e identificar onde ele se localiza, possvel selecionar a aba inferior, Source Code, exibindo o trecho do cdigo-fonte que gerou o defeito selecionado (isso s funciona quando o FindBugs capaz de localizar o cdigo-fonte). A Figura 3 mostra o cdigo do defeito. Repare na instanciao de Boolean atravs do construtor, uma melhoria alterar new Boolean(element) para Boolean.valueOf(element), como explica a dia do FindBugs e como pregam as boas prticas de programao Java. O segundo agrupamento de defeitos do tipo ES e indica duas strings sendo comparadas atravs do operador ==, o que na maioria dos casos um erro, pois o uso desse operador compara apenas referncias (o correto seria utilizar o mtodo equals()). A Figura 4 ilustra esse agrupamento de defeitos expandido, exibindo o nico defeito do tipo localizado e o trecho do cdigo onde ele se encontra.

Figura 2. Descrio de um defeito localizado.

Figura 3. Trecho do cdigo onde o defeito foi encontrado.

Figura 4. Defeito do tipo ES e o trecho onde ocorreu. Configurando os detectores provvel que nem todos os detectores de defeitos do FindBugs sejam teis em seus projetos. Prevendo isso, o FindBugs permite configurar os detectores a serem executados. Para isso, podemos voltar tela de projetos, selecionando os menus View/View Project Details e ento selecionar Settings/Configure Detectors (veja a Figura 5). Isso permite selecionar os detectores a serem aplicados ou no. So tambm fornecidos detalhes sobre sua performance. Aps selecionar os detectores desejados, basta clicar novamente no boto Find Bugs para refazer a anlise do projeto.

Figura 5. Tela para configurao e detalhe dos detectores. Salvando os relatrios de defeitos possvel salvar os defeitos obtidos atravs dos menus File/Save Bugs. Isso permite analisar os defeitos futuramente, imprimir, enviar um relatrio para o responsvel pelas correes etc. Para abrir um arquivo salvo, basta acessar File/Load Bugs, escolher o arquivo gravado, clicar em Load Bugs e reiniciar a anlise. Concluses A homologao de uma aplicao atravs da execuao de testes essencial, porm numa temos a garantia que nossos testes cobriram 100% do cdigo-fonte, nem que testamos todas as condies possveis para um trecho de cdigo. Atravs do uso do FindBugs com ferramenta auxiliar para garantia de qualidade, podemos dormir um pouco mais tranqilos sabendo que nosso cdigo foi tambm analisado em busca de padres que indicam possveis problemas na programao. LINKS: findbugs.sf.net Projeto FindBugs findbugs.sf.net/bugDescriptions.html Descrio detalhada de todos os defeitos e seus tipos.

Tarefas na Hora com Quartz Use o framework Quartz para criar e agendar tarefas no Java Como utilizar passo a passo, a utilizar o framework open source Quartz. Dos conceitos fundamentais a um exemplo completo.

Ao desenvolver aplicaes corporativas, comum encontrar requisitos exigindo que determinados processos sejam executados em intervalos regulares (ou em horrios especficos), sem interveno humana. Esse planejamento, denominado agendamento de tarefas ou job scheduling, pode ser alcanado atravs de comandos do sistema operacional ou utilizando-se aplicativos especializados. O framework open source Quartz oferece um pacote completo de funcionalidades para esse fim, permitindo a criao e a manipulao de tarefas agendadas. Neste artigo explicaremos conceitos essenciais do agendamento de tarefas e detalhes sobre a utilizao do Quartz, atravs de um exemplo completo. Agendamento no Java SE A partir da verso 1.3 o Java passou a oferecer uma opo para agendamento de tarefas, atravs das classes java.util.Timer e java.util.TimerTask. Essa soluo bastante simples possuindo uma srie de limitaes, sendo adequada apenas em algumas situaes (veja detalhes no artigo Agendamento de tarefas em Java). Ao utilizar essa API certas restries se tornam evidentes: 6. O mecanismo de agendamento pouco flexvel, baseando-se apenas na data inicial e no intervalo de repetio das tarefas; 7. Cada objeto Timer cria uma nova thread em segundo plano, o que no desejvel em aplicaes Java EE; 8. No h a possibilidade de persistir tarefas, permitindo continuar a execuo da tarefa posteriormente (ex.: quando o sistema reiniciar). O Quartz disponibiliza funcionalidades para criao e execuo de tarefas em Java, e resolve as limitaes impostas pela API Timer/TimerTask. Alm disso, oferece bastante flexibilidade e mltiplas opes de uso. O framework Quartz O Quartz permite criar e executar tarefas de muitos tipos e traz implementaes para tarefas comuns (como envio de e-mails, execuo de EJBs, execuo de comandos nativos etc.). O framework baseia-se em trs conceitos fundamentais: job (tarefa), trigger (gatilho) e scheduler (agendador). Um job representa uma tarefa que se deseja executar, e normalmente descrito atravs da interface org.quartz.Job. Essa interface inclui apenas um mtodo encapsulando a lgica da tarefa: public void execute (JobExecutionContext context)

throws JobExecutionException;
Um trigger define os momentos em que a tarefa deve ser executada e dispara sempre que chega o instante de execuo. O Quartz disponibiliza diversos tipos de triggers, tratando desde as situaes mais simples, com a classe org.quartz.SimpleTrigger; at situaes complexas, incluindo intervalos de tempo e dias especficos, usando org.quartz.CronTrigger (veja mais no quadro Entendendo a classe CronTrigger). O scheduler possui um registro de todos os triggers e tarefas programadas e coordenada a sua execuo. No existe nenhum vnculo direto entre tarefas e triggers: ambos podem ser manipulados separadamente. A separao entre a tarefa e o seu agendamento um dos pontos fortes do framework Quartz. Isso permite que uma mesma tarefa seja executada em situaes completamente diferentes, e facilita o reaproveitamento da lgica das tarefas e dos triggers. Primeiros passos Para agendar uma tarefa usando o Quartz, o primeiro passo consiste (naturalmente) em criar a tarefa. O cdigo abaixo cria uma tarefa que imprime a hora atual: public class Tarefa implements Job {

public void execute (JobExecutionContext context)

throws JobExecutionException { System.out.println (Disparou: + new Date()); } } O segundo passo criar o agendador. Para isso utilizamos a fbrica org.quartz.impl.StdSchedulerFactory, que cria uma instncia do agendador a partir das propriedades descritas no arquivo quartz.properties (este arquivo ser detalhado posteriormente): Scheduler agendador = StdSchedulerFactory.getDefaultScheduler(); A seguir deve-se instanciar o detalhe da tarefa (um objeto org.quartz.JobDetail). O Quartz utiliza esse objetivo para armazenar os atributos da tarefa que ser executada (incluindo eventuais parmetros). Aqui utilizamos o JobDetail simplesmente para nomear a tarefa e atribu-la a um grupo. O uso de nomes e grupos permite identificar unicamente a tarefa (ou o trigger) no agendador e possibilita a criao de categorias de tarefas e triggers. Como no existe necessidade de categorias a tarefa, usamos um grupo default: JobDetail detalhe = new JobDetail(Tarefa exemplo, Scheduler.DEFAULT_GROUP,Tarefa.class); Uma vez criado o agendador, devemos criar um trigger. Neste exemplo e construmos um trigger com nome Trigger exemplo que dispara a cada 10 segundos e inicia imediatamente. Para simplificar a criao e a configurao do trigger usamos a classe org.quartz.helpers.TriggerUtils, que oferece diversos mtodos utilitrios com esse propsito: Trigger trigger = TriggerUtils.makeSecondlyTrigger(10); trigger.setName(Trigger exemplo); trigger.setStartTime(new Date()); Agora s agenda a tarefa e iniciar o agendador: agendador.scheduleJob(detalhe,trigger); agendador.start(); At aqui vimos como manipular o agendamento via programao, codificando a tarefa e o trigger. O Quartz tambm permite que as tarefas sejam configuradas de forma declarativa em um arquivo XML. Utilizando essa estratgia, possvel modificar o agendamento das tarefas sem precisar alterar o cdigo da aplicao. Na prxima seo veremos como configurar o framework tanto na forma programtica como na declarativa, e como utiliz-lo em container web. Apesar de o Quartz poder ser usado indistintamente em ambientes Java SE e EE, sem dvida o uso no ambiente corporativo mais comum e merece maior ateno. A configurao do Java SE no apresenta diferenas substanciais e poder ser feita pelo prprio leitor a partir dos exemplos apresentados aqui. Preparao inicial Antes de iniciar o uso do Quartz, necessrio configurar o ambiente de desenvolvimento. Para este artigo usamos o JBoss 4.0.4 e o Eclipse 3.2. Como a gerao dos pacotes e o deployment da aplicao so feitos atravs do Ant, qualquer IDE que oferea suporte ao Ant pode ser utilizada. O download do Quartz pode ser feito em opensymphony.com/quartz/download.action. Ao escrever este artigo, a verso mais recente do framework era 1.5.2, disponvel no arquivo quartz-1.5.2.zip.

Este ZIP inclui todas as bibliotecas necessrias, alm dos javadocs. Aps descompactar o arquivo, necessrio referenciar a biblioteca do Quartz (quartz-all-1.5.2.jar) no seu projeto. Um exemplo completo: Quartz no container web Em uma aplicao web, as configuraes do Quartz normalmente so carregadas atravs de um servlet, que configurado para iniciar junto com a aplicao. Apesar de essa ser a opo mais comum, tambm possvel usar o utilitrio em conjunto com outros frameworks, como o Struts (para mais detalhes veja o quadro Integrando Quartz e Struts). O Quartz oferece uma vasta gama de tarefas, aplicveis s mais diversas situaes. Para facilitar o entendimento do exemplo vamos separar as tarefas do Quartz em duas categorias: tarefas comuns e tarefas EJB. Em uma tarefa comum toda lgica que se deseja realizar codificado em uma classe que implementa a interface org.quartz.Job, conforme mostrado nos exemplos anteriores. J em uma tarefa EJB, a lgica reside em um EJB, que executado a partir de uma classe especial do Quartz: org.quartz.jobs.ee.ejb.EJBInvokerJob. Nas prximas sees, descreveremos mais trs possibilidades para configurao e utilizao do Quartz atravs se servlets, cobrindo todas as opes possveis. So elas: Configurao declarativa e tarefa comum; Configurao declarativa e tarefa EJB; Configurao programtica e tarefa EJB. O quadro Entendendo a aplicao de exemplo detalha a estrutura dessa aplicao (que demonstra as trs configuraes) e o procedimento de build adotado para gerao. Todos os arquivos da aplicao de exemplo esto disponveis para download no site da Java Magazine. Configurando uma tarefa simples ou q Veja na Listagem 1 a classe ConfiguradorServlet, que responsvel pela configurao do Quartz. Como sabemos, o mtodo init() de um servlet disparado automaticamente pelo container web sempre que o servlet colocado em servio. Usaremos este mtodo para configurar e iniciar o Quartz. A configurao do agendamento via servlet tambm pode ser feita atravs da classe org.quartz.ee.servlet.QuartzInitializerServlet. Essa classe cria a fbrica de agendadores (uma instncia de org.quartz.impl.StdSchedulerFactory) e a armazena no contexto do servlet, permitindo sua recuperao posterior. Como especificado no web.xml (Listagem 2), o servlet instanciado quando a aplicao carregada. O descritor web tambm informa o valor do parmetro tipoConfig, passado ao servlet. Analisando a implementao do mtodo int(), percebemos que ele apenas captura o tipo de configurao desejado (o parmetro tipoConfig definido na constante Configurador.TIPO_CONFIG) e delega a configurao classe Configurador (Listagem 3), que um singleton (classe com instncia nica): String tipo = getInitParameter(Configurador.TIPO_CONFIG); Configurador.getInstance().configurar(tipo); O mtodo configurar() da classe Configurador quem realmente realiza todo o trabalho pesado, iniciando o Quartz de acordo com o tipo de configurao escolhido pelo desenvolvedor. So suportados trs tipos de configurao: ejb-manual (configurao declarativa da tarefa que executa em EJB) e comum-arquivo (configurao declarativa de uma tarefa comum). O mtodo configurar() analisa o tipo de configurao informando no parmetro tipo e dispara o mtodo adequado. Configuraes declarativas so instanciadas atravs do mtodo configArquivo(), que efetua o parse do arquivo apropriado (tarefa-ejb.xml ou tarefa-comum.xml)

e inicia o agendador. A configurao manual feita atravs do mtodo configEJBManual(), que configura a tarefa, o trigger e o agendador manualmente. Voltando ao cdigo do web.xml (Listagem 2), possvel perceber que o parmetro tipoConfig foi definido como comum-arquivo. Isso implica que o arquivo tarefa-comum.xml ser utilizado para configurar o Quartz e inici-lo: <init-param> <param-name>tipoConfig</param-name> <param-value>comum-arquivo</param-value> </init-param> A Listagem 4 mostra um trecho do arquivo tarefa-comum.xml. Uma tarefa definida atravs do tag <job>, que possui dois tags filhos: <job-detail> e <trigger>. Em <job-detail> especificado o nome (EnviarLog), o grupo (DEFAULT), a descrio (Envio de log por email) e a classe que implementa a tarefa: <job-detail> <name>EnviarLog</name> <group>DEFAULT</group> <description>Envio de log por e-mail</description> <job-class>BR.com.jm.quartz.util.ExecutorAnt</job-class> <job-data-map> </job-data-map> </job-detail> Como definido no tag <job-class>, a classe BR.com.jm.quartz.util.ExecutorAnt ser instanciada e executada quando for necessrio iniciar a tarefa. Esta classe, mostrada na Listagem 5, permite a execuo de um script Ant diretamente de uma aplicao Java, aceitando a atribuio de valores a propriedades do script. A classe br.com.jm.quartz.util.ExecutorAnt uma classe utilitria, que pode ser utilizada em qualquer aplicao. No existe nenhum vinculo entre ela e o Quartz. O tag <job-data-map> possui um conjunto de tags <entry>. Estas tags informam os parmetros que sero passados classe ExecutorAnt. Os trs parmetros (arquivo, alvo e destinatario) so definidos atravs de tags <key> e <value>. Veja um exemplo: <job-data-map> <entry> <key>arquivo</key> <value>/WEB-INF/classes/envio-log.xml</value> </entry> <entry> <key>alvo</key> <value>enviar-log</value> </entry> <entry> <key>destinatario</key> <value>ad-rocha@uol.com.br</value> </entry> </job-data-map>

O parmetro arquivo informa o caminho do arquivo de script, enquanto o parmetro destinatrio utilizado para definir a propriedade correspondente do script. Voltando Listagem 5, vemos que o mtodo executarAlvo() executa o alvo (target) Ant definido na propriedade alvo. Assim, o alvo enviar-log do script /WEB-INF/classes/envio-log.xml ser executado com o parmetro destinatario preenchido com ad-rocha@uol.com.br. A Listagem 6 um exemplo de um script Ant (envio-log.xml), que compacta arquivos de log encontrados em um diretrio especfico e os envia por e-mail para o destinatrio indicado na propriedade destinatario. Para que a tarefa seja executada necessrio vincul-la a um trigger. Na configurao declarativa, o trigger descrito atravs do tag <trigger>. Para essa tarefa foi associado um trigger simples, por meio do tag <simple>. Esse tag especifica que o Quartz deve criar um objeto da classe org.quartz.SimpleTrigger. O trigger foi nomeado TriggerEnviarLog e executa a cada minuto (60 mil microssegundos): <trigger> <simple> <name>TriggerEnviarLog</name> <group>DEFAULT</group> <job-name>EnviarLog</job-name> <job-group>DEFAULT</job-group> <repeat-count>-1</repeat-count> <repeat-interval>60000</repeat-interval> </simple> </trigger> O valor -1 em <repeat-cont> indica que o trigger continuar sendo disparado at a aplicao ser finalizada. Listagem 1. Servlet para configurao do Quartz. package br.com.jm.quartz.servlet; import javax.servlet.*; import br.com.jm.quartz.util.Configurador; public class ConfiguradorServlet extends HttpServlet { public void init() throws ServletException { String tipo = getInitParameter(Configurador.TIPO_CONFIG); Configurador.getInstance().configurar(tipo); } } Listagem 2. web.xml com configurao declarativa usando um Servlet. <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"> <web-app> <servlet> <servlet-name>config</servlet-name> <servlet-class>br.com.jm.quartz.servlet. ConfiguradorServlet</servlet-class> <init-param>

<param-name>tipoConfig</param-name> <param-value>comum-arquivo</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> </web-app> Listagem 4. Configurao declarativa da tarefa que executa Scripts Ant. <?xml version="1.0" encoding="UTF-8"?> <quartz xmlns="http://www.opensymphony.com/quartz/JobSchedulingData" xmlns:xsi="http;//www.w3.org/2001/XMLSchemainstance" overwrite-existing-jobs="true"> <job> <job-detail> <name>EnviarLog</name> <group>DEFAULT</group> <description>Envio de log por e-mail</description> <job-class>br.com.jm.quartz.util. ExecutorAnt</job-class> <job-data-map> <entry> <key>arquivo</key> <value>/WEB-INF/classes/envio-log.xml</value> </entry> <entry> <key>alvo</key> <value>enviar-log</value> </entry> <entry> <key>destinatario</key> <value>ad-rocha@uol.com.br</value> </entry> </job-data-map> </job-detail> <trigger> <simple> <name>TriggerEnviarLog</name> <group>DEFAULT</group> <job-name>EnviarLog</job-name> <job-group>DEFAULT</job-group> <repeat-count>-1</repeat-count> <repeat-interval>60000</repeat-interval> </simple> </trigger> </job> </quartz> Listagem 5. Tarefa para execuo de scripts Ant. package br.com.jm.quartz.util; import java.io.*;

import java.util.*; import org.quartz.*; import org.apache.tools.ant.*; public class ExecutorAnt implements Job { private Project projeto = null; public void execute(JobExecutionContext context) throws JobExecutionException { JobDataMap mapa=context.getJobDetail().getJobDataMap(); String arquivo = mapa.getString("arquivo"); String alvo = mapa.getString("alvo"); configurarProjeto(arquivo, mapa); executarAlvo(alvo); } private void configurarProjeto(String arquivo, JobDataMap propriedades) { if (projeto == null) { projeto = new Project(); projeto.fireBuildStarted(); projeto.init(); String file = getClass().getResource(arquivo).getFile(); ProjectHelper projHelper = new ProjectHelperImpl (); projHelper.parse(projeto, new File(file)); PrintStream out = System.out; BuildLogger logger = new DefaultLogger(); logger.setMessageOutputLevel(Project.MSG_DEBUG); logger.setOutputPrintStream(out); logger.seVErrorPrintStream(out); projeto.addBuildListener(logger); configurarPropriedades(propriedades); } } private void configurarPropriedades(JobDataMap propriedades) { if (propriedades != null) { lterator it = propriedades.entrySet().iterator(); while (it.hasNext()) { Map.Entry entry = (Map.Entry) it.next(); Strtng chave = entry.getKey() + ""; String valor = entry.getValue() + ""; projeto.setProperty(chave, valor); } } } private void executarAlvo(String alvo) { if (alvo == null) { alvo = projeto.getDefaultTarget();

} projeto.executeTarget(alvo); } } Listagem 6. Script Ant para envio de logs por e-mail. <?xml version="1.0" encoding-"UTF-8"?> <project basedir="." default="enviar-log" name="Tarefa exemplo"> <property name="log.dir" location="/java/jboss-4.0.4.GA/server/default/log" /> <target name="compactar"> <echo>Compactando arquivos de log</echo> <zip destfile-"logs.zip"> <fileset dir="${log.dir}" includes="**/*.log" /> </zip> </target> <target name="enviar-log" depends-"compactar"> <echo>Enviando e-mail para ${destinatario}</echo> <mail mailhost="smtp.uol.com.br" user="ad-rocha" password-"xxxx" files="logs.zip" subject="Enyio de log"> <from address="ad-rocha@uol.com.br" /> <to address="${destinatario}" /> <message>Arquivo compactado</message> </mail> <delete file="logs.zip" /> <echo>OK</echo> </target> </project> Configurando uma tarefa EJB de forma declarativa O uso de um EJB a partir do Quartz no muito diferente do que vimos at aqui. No modelo declarativo, a nica diferena o arquivo XML utilizado para configurao da tarefa. A Listagem 7 exibe a configurao da tarefa que dispara um EJB para imprimir a hora atual no console (tarefaejb.xml). As classes e interfaces do EJB podem ser vistas nas Listagens 8, 9 e 10. O Quartz j incorpora uma classe para execuo de EJBs: org.quartz.jobs.ee.ejb.EJBInvokerJob, que deve ser indicada no tag <job-class>. Essa classe exige o preenchimento de alguns parmetros especiais, que so descritos em <java-datamap>: java.naming.factory.initial indica a fbrica que ser usada para montar o contexto; java.naming.provider.url indica o provedor de servio que ser utilizado; ejb usado para descrever a localizao JNDI da interface home do EJB e method define o mtodo do EJB que ser executado. Com base nesses parmetros o Quartz capaz de localizar o EJB e dispar-lo no momento adequado.

Listagem 7. Configurao declarativa da tarefa que executa um EJB. <?xml version="1.0" encoding="UTF-8"?> <quartz xmlns=" http://www.opensymphony.com/quartz/JobSchedulingData" xmlns:xsi="http://www.w3.org/2001/XMLSchemainstance" overwrite-existing-jobs="true"> <job> <job-detail> <name>MostrarDataAtual</name> <group>DEFAULT</group> <description>Invoca um EJB que mostra a data atual</description> <job-class>org.quartz.jobs.ee.ejb.EJBInvokerJob </job-class> <job-data-map> <entry> <key>java.naming.factory.initial</key> <value> org.jnp.interfaces.NamingContextFactory </value> </entry> <entry> <key>java.naming.provider.url</key> <value>jnp://localhost:1099</value> </entry> <entry> <key>ejb</key> <value>ejb/quartz</value> </entry> <entry> <key>method</key> <value>mostrarData</value> </entry> </job-data-map> </job-detail> <trigger> <cron> <name>TriggerDataAtual</name> <group>DEFAULT</group> <job-name>MostrarDataAtual</job-name> <job-group>DEFAULT</job-group> <cron-expression>0 0/1 * * * ? </cron-expression> </cron> </trigger> </job> </quartz> Listagem 8. EJB para imprimir a hora atual. package br.com.jm.quartz.ejb; import javax.ejb.*;

import java.util.Date; public class OuartzBC implements SessionBean { public void setSessionContext(SessionContext context) throws EJBException {} public void ejbCreate() { } public void ejbRemove() throws EJBException { } public void ejbActivate() throws EJBException { } public void ejbPassivate() throws EJBException { } public void mostrarData() {System.out.println("Data atual: "+ new Date());} } Listagem 9. lnterface remota para o EJB. package br.com.jm.quartz.ejb; import javax.ejb.EJBObject; import java.rmi.RemoteException; public interface QuartzInterface extends EJBObject { public void mostrarData() throws RemoteException; }

Listagem10. lnterface home para o EJB. package br.com.jm.quartz.ejb; import javax.ejb.*; import java.rmi.RemoteException; public interface QuartzHome extends EJBHome ( public OuartzInterface create() throws CreateException, RemoteException; } Configurando uma tarefa EJB de forma programtica Tambm possvel configurar a execuo da tarefa que dispara o EJB via programao, como mostra o mtodo configEJBManual() da classe Configurador (Listagem 3). Neste caso todas as propriedades da tarefa e dados do trigger so preenchidos mo, sem necessidade de arquivos XML. Na configurao manual os parmetros do EJB so representados atravs de quatro constantes da classe EJBInvokerJob: INITIAL_CONTEXT_FACTORY, PROVIDER_URL, EJB_JNDI_NAME_KEY e EJB_METHOD_KEY. O comportamento de duas alternativas apresentadas (declarativa e programtica) idntico. Listagem 3. Classe utilitria para configurao do Quartz. package br.com.jm.quartz.util; import org.quartz.*; public class Configurador { private static Configurador configurador = null; public static final String TIPO_CONFIG = "tipoConfig";

public static Configurador getInstance() { if (configurador == null) { configurador = new Configurador(); } return configurador; } private Configurador() { } public void configurar(String tipo) { if ("ejb-manual".equals(tipo)) { configEJBManual(); } else if ("ejb-arquivo".equals(tipo)) { configEJBArquivo(); } else if ("comum-arquivo".equals(tipo)) { configComumArquivo(); } else { throw new IllegalArgumentException( "Tipo de tarefa nao suportada: " + tipo); } } private void configEJBArquivo() { configArquivo("/tarefa-ejb.xml"); } private void configComumArquivo() { configArquivo("/tarefa-comum.xml"); } private void configArquivo(String arquivo) { try { String file = getClass( ).getResource(arquivo).getFile(); Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); JobSchedulingDataProcessor xmlProcessor = new JobSchedulingDataProcessor(); xmlProcessor.processFileAndScheduleJobs( file, scheduler, false); scheduler.start(); } catch (Exception e) { e.printStackTrace(); } } private void configEJBManual() { try { JobDetail detail = new JobDetail(

"MostrarDataAtual", Scheduler.DEFAULT_GROUP, EJBlnvokerJob.class); detail.getJobDataMap( ).put( EJBInvokerJob.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory"); detail.getJobDataMap( ).put( EJBInvokerJob.PROVIDER_URL, "jnp://localhost: 1099"); detail.getJobDataMap( ).put( EJBInvokerJob.EJB_JNDI_NAME_KEY, "ejb/quartz"); detail.getJobDataMap( ).put( EJBInvokerJob.EJB_METHOD_KEY,"mostrarData"); SimpleTrigger trigger = new SimpleTrigger( "TriggerDataAtual", Scheduler.DEFAULT_GROUP); trigger.setRepeatCount( SimpleTrigger.REPEAT_INDEFINITELY); trigger.setRepeatInterval(60000L); Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); scheduler.scheduleJob(detail, trigger); scheduler.start(); } catch (SchedulerException e) {e.printStackTrace();} } } Configurando o Quartz Alm dos parmetros relativos ao agendador, s tarefas e ao triggers, o Quartz contempla propriedades que podem ser alteradas para adaptar o framework a situaes especficas. Essas propriedades so definidas no arquivo quartz.properties (dentro do JAR do framework), que utilizado pelo Quartz durante a criao do agendador. A distribuio j incorpora um arquivo com as opes padro (o que ser suficiente para a maioria das aplicaes), mas o desenvolvedor pode criar seu prprio arquivo e alterar as configuraes de acordo com a sua necessidade. Neste caso, para que as alteraes possam ser aplicadas necessrio colocar o novo arquivo no classpath da aplicao. O arquivo contido na distribuio do Quartz bastante simples, e comtempla configuraes do agendador, threads, RMI e tarefas. O arquivo completo mostrado na Listagem 11. Abordaremos apenas duas das principais: nmero de threads e persistncia de tarefas. Mais detalhes sobre as demais propriedades podem ser encontrados na documentao do Quartz. Listagem 11. Arquivo quartz.properties padro. org.quartz.scheduler.instanceName = DefaultQuartzScheduler org.quartz.scheduler.rmi.export = fatse org.quartz.scheduler.rmi.proxy = false org.quartz.scheduler.wrapJobExecutionInUserTransaction = false org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool org.quartz.threadPool.threadCount = 10 org.quartz.threadPool.threadPriority = 5 org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread=true

org.quartz.jobStore.misfireThreshold = 60000 org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore Nmero de threads O framework utiliza um pool de threads para executar as tarefas agendadas. O tamanho do pool determina a quantidade de tarefas que podem ser executadas simultaneamente, e constitui um parmetro da configurao importante. Caso no exista alguma thread disponvel no pool no momento agendado para execuo, a tarefa ter que aguardar. O padro definido no Quartz de dez threads, mas esse nmero pode ser alterado atravs da chave org.quartz.threadPool.threadCount. Persistncia O recurso de persistncia de tarefas do Quartz permite que o estado de uma tarefa seja salvo e posteriormente recuperado; assim as tarefas no sero canceladas se houver um reincio ou uma falha da aplicao. Por default, o estado das tarefas mantido em memria atravs da classe org.quartz.simpl.RAMJobStore, sendo perdido quando a aplicao reiniciada. possvel alterar a classe responsvel pela persistncia e assim armazenar os dados em um banco de dados. Essa configurao deve utilizar algumas das classes de persistncia baseada em JDBC oferecidas pelo Quartz: org.quartz.simpl.JDBCJobStoreTX ou org.quartz.simpl.JDBCJobStoreCMP. Outros recursos O Quartz oferece ainda mais dois recursos que merecem ateno: listeners e plug-ins. Listeners O Quartz permite o registro de listeners para tarefas, triggers e agendadores. Objetos listeners so disparados sempre que um evento importante ocorre como, por exemplo, quando uma trigger dispara (ou no dispara quando deveria). Atravs de um listener possvel forar a re-execuao de uma tarefa ou efetuar alguma manobra de contingncia, tratando o erro de forma adequada. Os listeners esto descritos nas interfaces org.quartz.JobListener, org.quartz.TriggerListener e org.quartz.SchedulerListener. Plug-ins O framework pode ainda ser estendido pela criao de plug-ins que tornam possvel codificar funcionalidades no existentes no Quartz. Para desenvolver um novo plug-in, basta criar uma classe que implemente a interface org.quartz.spi.SchedulerPlugin e codificar os trs mtodos necessrios. O plug-in tambm deve ser configurado no quartz.properties. Concluses O Quartz um utilitrio bastante poderoso e simples de usar. Sua arquitetura permite a utilizao tanto em aplicaes SE quanto EE, sem diferenas substanciais de configurao. O Quartz j incorpora classes que executam tarefas corriqueiras e possui um mecanismo de agendamento, baseado em triggers que bastante robusto. Da prxima vez que voc necessitar agendar tarefas em sua aplicao considere usar o Quartz; ele oferece vantagens que certamente pouparo muitas horas de desenvolvimento. LINKS opensymphony.com/quartz Pgina do Quartz opensymphony.com/quartz/api/org/quartz/CronTrigger.html Documentao da classe CronTrigger

opensymphony.com/quartz/api/org/quartz/CronExpression.html Documentaao da classe CronExpression nighttale.net/OpenSource/QuartzXML-RPCplugin.html Plug-in do Quartz para executar web services Entendendo a classe CronTrigger A classe org.quartz.CronTrigger utilizada para disparar tarefas em intervalos definidos atravs de expresses cron*, que so processadas e avaliadas pela classe org.quartz.CronExpression. Uma expresso cron algo como 0 0 12**?, que neste caso significa dispare todos os dias, s 12:00h PM. Veja na Tabela Q1 alguns exemplos de expresses cron (retirados da documentao do Quartz). Uma expresso definida atravs de sete campos que representam intervalos de tempo, como mostrado na Tabela Q2. Em cada campo da expresso cron pode ser utilizada uma gama de valores, ou caracteres coringa, que possuem significado especial, como mostrado (resumidamente) na Tabela Q3.

Tabela Q1. Exemplos de expresses cron

Tabela Q2. Campos de expresses cron

Tabela Q3. Caracteres coringas do cron Entendendo a aplicao de exemplo

A Figura Q1 exibe a estrutura do projeto de exemplo no Eclipse. Alm da biblioteca do Quartz (quartz-all-1.5.2.jar) o exemplo utiliza alguma bibliotecas (ant.jar,ant.auncher.jar e antjavamail.jar) para permitir a execuo dinmica do Ant. Esses JARs esto disponveis na pasta plugins do Eclipse. Alm do Ant, usado o Struts e, por isso, a biblioteca struts.jar deve estar disponvel par ao projeto. Web.xml e o build Os arquivos web-ejbcarquivd.xml, web-ejb-manua.xml, web-servlet-arquivo.xml e web-strutsarquivo.xml so configuraes do descritor web (web.xml) para cada uma das possveis configuraes descritas no exemplo. Durante o build esses arquivos so renomeados para "web.xml" pelo alvo que cria cada aplicao de exemplo: . exemplo_servletarquivo: cria aplicao Web utilizando a configurao declarativa do Quartz; . exemplo_ejb_arquivo: cria a aplicao Web+EJB utilizando a configurao declarativa do Quartz; . exemplo_ejb_manual: cria a aplicao Web+EJB utizando a configurao programtica do Quarrz; . exemplo_struts_arquivo: cria aplicao Web+Struts utilizando a configurao declarativa do Quartz.

Figura Q1. Estrutura do projeto. Integrando Quartz e Struts A integrao entre o Quartz e o Struts bastante simples. Supondo que o Struts j esteja configurado na aplicao, dois passos so necessrios para habilitar o Quartz: criar o plug-in de inicializaao e configurar o struts-config.xml.

A Listagem Q1 mostra a classe ConfiguradorStruts, que inicia o Quartz. A classe um plug-in do Struts (implementao da interface org.apache.struts.action.Plugin). Esse plug-in implementa o mtodo init(), responsvel pela configurao do Quartz. A Listagem Q2 exibe a configurao do struts-config.xml para comtemplar o novo plug-in. A sintaxe simples, bastando adicionar o tag <plug-in> e referenciar a classe que o implementa. As prioridades startOnLoad e startupDelay tambm devem ser setadas. Plug-ins do Struts funcionam de forma semelhante a servlets, mas os parmetros passados ao plug-in so codificados como atributos da classe. Note que a prioridade tipoConfig, passada como parmetro, mapeada em um atributo de mesmo nome na classe ConfiguradorStruts. Listagem Q1. Plug-in para configurao do Quartz. package br.com.jm.quartz.struts; import javax.servlet:ServletException; import org.apache.struts.action.*; import br.com.jm.quartz.util.Configurador; public class ConfiguradorStruts implements PlugIn { private String tipoConfig; // ... setters e getters omitidos public void init(ActionServlet servlet. ModuleConfig config) throws ServletException { Configurador.getInstance( ).configurar(getTipoConfig()); } public void destroy() {} } Listagem Q2. Configurao do struts-config.xml para suportar o plug-in de configurao do Quartz. <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts-config PUBLIC"-//Apache Software Founda tion//DTD Struts Configuration 1.1//EN" ''http://jakarta. apache.org/struts/dtds/struts-config_1_1.dtd"> <struts-config> <plug-in className= "br.com.jm.quartz.struts.ConfiguradorStruts"> <set-property property= "startOnLoad" value="true" /> <set-property property= "startupOelay" value="3000" /> <set-property property= "tipoConfig" value="comum-arquivo" /> </plug-in> </struts-config>

Componentes Avanados do Swing Parte 1: Explorando e Customizando o JCom boBox/JList Aprenda a customizar os componentes de exibio e seleo de conjuntos de valores do Swing para incrementar o visual das suas aplicaes

Nesta srie em duas partes ,mostramos como incrementar a aparncia de componentes visual do Swing para incluir imagens e outros recursos. Vemos conceitos fundamentais referentes arquitetura de componentes visuais, e exemplos de como customizar dois dos componentes mais sofisticados do Swuing:JComboBox e JTable. Nesta parte, enfocamos o JComboBox.

Os exemplos Os exemplos deste artigo so partes de uma aplicao hipottica de monitorao de redes. A aplicao deve indicar o sistema operacional (SO) de cada servidor e estao de trabalho, exibindo logotipos referentes ao SO em vez de apenas o seu nome, alm de outras customizaes visuais utilizadas em aplicaes profissionais. Vamos partir de um conjunto de classes de domnio que representam as informaes e conceitos utilizados. A Listagem 1 apresenta estas classes, Host e SistemaOperacional, ambas no pacote de dados. Para simplificar e focar melhor na parte visual, a classe SistemaOperacional ser apenas uma enumerao (Java 5) que fornece os valores prfixados Linux, Windows e MACOSX, e o conjunto de Hosts ser fornecido em uma coleo Java, em vez de lido de um arquivo ou banco de dados. Vamos construir duas telas simples para a visualizao destas informaes, cada qual em seu prprio exemplo, que pode ser compilado e executado em separado. Veremos adiante e na segunda parte que ambas as telas iro exigir a customizao de componentes Swing, mais especificamente JComboBox e JTable, e estas customizaes sero tornadas reusveis pela criao de novas classes Swing. Uma tela exibe uma tabela com todos os Hosts e suas propriedades, mostrando o sistema operacional como cone. A outra exibe apenas um combobox para escolha do sistema operacional, mostrando tanto o cone quanto o nome do sistema. Iniciaremos pela tela mais simples, a segunda.

Listagem 1 Objetos de domnio da aplicao de exemplo.

SistemaOperacional.java

package dados:

public enum SistemaOperacional { LINUX, WINDOWS, MACOSX }

HOST.java

package dados:

public class Host { private String nome ; private String ip ; private SistemaOperacional so ; private in memoriaRAM .

// construtores, metodo get/set omitidos }

A tela do Sistema Operacionais/ JCombobox

Para exibir o combobox de sistema operacionais, primeiro criamos uma classe de modelo que estende DefaultComboBoxModel e inicializada com todos os valores possveis de enumerao SistemaOperacional. A Listagem 2 apresenta esta classe, assim como a janela que faz uso dela (JanelaPrincipal). Todas as classes desta tela esto no pacote comboBox. Entre elas, a classe Principal apenas instancia uma JanelaPrincipal e a torna visvel.

A classe DefaultComboBoxModel encapsula um java.util.Vector. Dessa forma, qualquer tipo de objeto pode ser adicionado como elemento de JcomboBox (o mesmo vale para um JList). Cada item individual formatado pelo chamado ao seu mtodo toString()

O resultado apresentado na Figura 1. No ainda muito elegante, mas demonstra o que acontece se no for feita nenhuma customizao visual do componente. Para fazer esta customizao, necessrio conhecer mais sobre a arquitetura do JComboBox,o que faremos a seguir:

Figura 1.Primeira verso do exemplo de Sistema Operacionais / JComboBox.

Listagem 2. classes para exemplo de JComboBox.

SistemaOperacionalComboBoxModel.java

package combobox;

import dados.SistemaOperacional ;

public class SistemaOperacional ComboBoxModel

extends DefaultComboBoxModel { public SistemOperacional ComboBoxModel ( ) { addElement(SistemaOperacional.LINUX); addElement(SistemaOperacional.WINDOWS); addElement(SistemaOperacional.MACOSX); } }

JanelaPrincipal.java

package combobox;

import java.awt.Border Layout; import javax.swing.*;

public class JanelaPrincipal extends javax.swing.JFrame {

private JComboBox comboSO;

private JLabel labelSO; public JanelaPrincipal ( ) { labelSO = new JLabel ( );

comboSO = JComboBox ( ) ;

setDefaultOperation( WindowConstants.DISPOSE_ON_CLOSE) ; setTitle (Java Magazine Demo de JComboBox); labelSo.stText(Sistema Operacional:) ; getContentPane( ).add(labelSO, BorderLayout.WEST);

comboSO.setModel ( new SistemaOperacionalComboBoxModel()); getContentPane( ).add(comboSO, BorderLayout.CENTER); pack(); } }

Principal.java package combobox ;

public class Principal public static void main(String[] s ) { (new JanelaPrincipal ( ) ) .setVisilible (true) } }

Arquitetura do JComboBox A arquitetura do componente JComboBox apresentada na Figura 2. Observe que o JComboBox reusa muito da arquitetura e da funcionalidade do componente JList. Por exemplo, o modelo de um JComboBox (no diagrama, DefaultComboBoxModel) deve implementar a interface ComboBox. Esta interface, por sua vez estende a interface ListBoxModel. A diferena entre os componentes JList e JComboBox que no JComboBox o prprio ComboBox gerencia o item que este selecionado. J no componente JList possvel que no haja nenhum item selecionado, ou que haja uma seleo mltipla;por isso a seleo gerenciada por uma classe de modelo separada(ListSelectionModel) O JComboBox(assim como vrios outros componentes do Swing)utiliza um tipo de classe de apoio conhecida como cell renderer. Sua funo desenhar na tela um objeto de dados. Modificar a aparncia do nosso combobox para exibir os cones dos sistemas operacionais exige a criao de uma nova classe que implementa a interface ListCellRenderer. O observe que a implementao padro desta interface, o DefaultListCellRenderer, uma especializao de um JLabel.Nada obriga a cellrenderer a herdar de um Jlabel, mas como um Jlabel capaz de exibir textos, quebrar linhas, mudar cores e fontes e tambm exibir imagens, ele ser adequado para a maioria das situaes. Entretanto um,DefaultListCellRender (assim como outros cell renderes do Swing)no um JLabel comum. Uma nica instncia dele usada como um molde para desenhar todos os itens do JComboBox,d e modo a evitar que o componente se torna pesado pelo excesso de objetos agregados. Um JLabel comum no permite este uso, de modo que o DefaultListCellRenderer obrigado a redefinir mtodos herdados de java.awt.Component e javax.swing.JComponent, para garantir sua performance e evitar sujeiras ou tremidas na tela. Por isso mais facilcriar novos XxxListCellRenderers estendo o prprio DefaultListCellRenderer,do que estender diretamente um JLabel ou mesmo um JComponent. Nos pouco casos em que JLabel no fornea funcionalidade suficiente (por exemplo, a exibio de texto formado com diferentes estilos de texto), deve ser criado do zero uma classe que implementa a interface ListCellRendere. Tal classe pode ser baseada em outro componente do Swing ou realizar todo o desenho no brao usando os recursos API Java 2D.

Figura 2. Arquitetura simplificada componentes JList e JComboBox.Entre as classes omitidas,esto as classes de viso de ambos os componentes.Em azul temos as classes e interfaces de componente JList e em amarelo as classes e interfaces do componente

JComboBox.As classes dos componentes em si foram destacadas por uma tonalidade mais escuras.

Customizando o desenho dos itens A listagem 3 apresenta a classe SistemaOperacionalListCellRenderer e uma classe auxiliar chamada SistemaOperacionalUIResource. O motivo para criarmos esta classe adicional que os cones e os textos escritos para as instncia de SistemaOperacioal provavelmente sero necessrios para outros componentes. Isso tambm a classe SistemaOperacionalUIResource foi colocado em um pacote separado, chamado recursos. A implementao do cell renderer muito simples ela apenas configura o JLabel com o cone e o texto corretos. Os cones so carregados como recursos portando devem estar em um diretrio ou JAR disponvel no classpath da aplicao . A classe SistemaOperacionalUIResource, alem de encapsular os nomes e localizao dos cones, garante que no haja varias copias dele em memria. O coletor de lixo da JVM agradece, assim como o gerenciador de recursos do sistema operacional.

Embora o caminho para os cones parece ser um caminho absoluto inicia com /, ele na verdade um caminho relativo ao classpath da JVM, pois esse o comportamento do mtodo getResource()

Seria possvel vincular o cell renderer recm-criado ao JComboBox, como parte do cdigo da JanelaPrincipal, mas em uma aplicao real isto poderia exigir a customizao manual de varias telas. Melhor ento criar um novo componente embutindo estas customizaes. Esse novo componente estende JComboBox e se chama SistemaOperacionalJComboBox ( tambm na Listagem 3). H um pulo do gato neste novo componente: o mtodo setPRtotypeDisplayValue()4 recebe o maior valor que pode ser exibido, e calcula o tamanho que o JComboBox necessita assumir este valor completamente. O calcuo realizado utilizando-se o cell render do componente, garantindo o resultado correto mesmo com nossas customizaes.

Executando o exemplo O resultado final do exemplo de JComboBox pode ser visto na Figura 3. O leitor curioso pode experimentar o exemplo utilizando diferentes look-and-feels (LAFs), por exemplo o LAF nativo do GTK para i Linux, ou mesmo o Napkin LAF (veja links). Ambos so apresentados na Figura 4, e foram gerados pela seguintes linhas de comando:

java-Dswing.defaultlaf=com.sun.java.swing.plaf.gtk.GTKLookAndFeel -cp JTable/dist/JTable.jar jcombobox.Main java-Dswing.defaultlaf=net.source.napkinlaf.NapkinLookAndFeel -cp JTable/dist/JTable.jar:/d/java/napkinlaf.jar jcombobox.Main

Figura 3.Tela de teste do componente customizado,agora com o novo cell

Figura 4.Tela de teste com os LaFs GTK do Java 1.5 e Napkin.

No segundo caso, modifique os caminhos indicados na opo cp de acordo com o local onde voc instalou o Napkin LAF. O pacote Table.jar a distribuio dos exemplos deste arquivo (incluindo os exemplos as segunda parte), gerado pelo buildfile do Ant incluso no projeto. O segredo passar para JVM a opo Dswing.defaultlaf especificando o nome e a classe que implementa o LAF desejado. Finalizando, o leitor adepto da modelagem UML pode encontrar na Figura 5 um diagrama de classes exibindo a arquitetura do componente recm criado, o SistemaOperacionalJComboBox.

Figura 5 .Arquitetura do componente SistemaOperacionalComoBox (em amarelo) e seu super-componente JComboBox (em azul);setas tracejadas indicam dependncias.

Listagem 3. Cell renderer para sistemas operacionais.

SistemaOperacionalListCellRenderer.java

package combobox

import dados.SistemaOperacinal;

import Java.awt.*; import javax.swing.*; import recursos.SistemaOperacinalUIResources;

public class SistemaOperacionalListCellRenderer extends DefaultListCellRenderer { private static final SistemaOperacionalUIResources recursos = new SistemaOperacionalUIResources ();

public Component getListCellRendererComponent ( JList list, Object value, int index, boolean isSelected, Boolean cellHasFocus) { JLabel label = (JLabel) super.getListCellRendererComponent (list, value, index, isSelected, cellHasFocus);

label.setIcon(recursos.getIcone(( SistemaOperacional) value)); label.setText(recursos.getNome(( SistemaOperacional) value));

return label; } }

SistemaOperacionalUIResources.java package recursos; import dados.SistemaOperacional; import Java.util.*; import javax.swing.*;

// cones obtidos em http://www.entity.cc/ICONS/ // requests1.php e convertidos manualmente // do formato .ico para .png public class SistemaOperacionalUIResources {

protected static final Map<SistemaOperacional , ImageIcon> cones =

new HashMap<SistemaOperacional, ImageIcon>();

private static final Map<SistemaOperacional, string> nomes = new HashMap<SistemaOperacional, String>();

public SistemaOperacionalUIResources() { if 9icones.size() == 0) { icones.put(SistemaOperacional.LINUX, new ImageIcon(getClass().getResource( /recursos/icons/linux.png)));

icones.put(SistemaOperacional.WINDOWS. new ImageIcon(getClass().getResource( /recursos/icons/windows.png)));

icones.put(SistemaOperacional.MACOSX, new ImageIcon(getClass().getResource( /recursos/icons/macosx.png))); } if (nomes.size() == O) { nomes.put (SistemaOperacional. LINUX, Linux); nomes.put (SistemaOperacional.WINDOWS, Windows);

nomes.put (SistemaOperacional.MACOSX, Macosx): } } public ImageIcon getIcon(SistemaOperacional value) { return cones.get(value); } public int getIconHeight() { return cones.get(SistemaOperacional.LINUX). getIconHeight(); } public int getIconWidth() { return cones.get(SistemaOperacional.LINUX). getIconWidth(); } public String getNome(SistemaOperacional value) { return nomes.get(value); } }

SistemaOperacionalJComboBox.java package combobox;

import dados.SistemaOperacional;

import javax.swing.*;

public class SistemaOperacionalJComboBox extends JComboBox { public SistemaOperacionalJComboBox() { setModel (new SistemaOperacionalComboBoxModel());

SistemaOperacionalListCellRenderer cellRenderer = new SistemaOperacionalListCellRenderer();

setRenderer(cellRenderer); setPrototypeDisplayValue( SistemaOperacional.WINDOWS); } }

Concluses O uso dos componentes visuais de IDEs RAD (Rapid Apllication Development) como Delphi e VB. No Swing, espera-se que o desenvolvedor escreva cdigos e crie novas classes para customizar o comportamento dos componentes, em vez de se limitar edio de propriedade. A adaptao a esta nova filosofia de trabalho no imediata, mas os resultados compensam. No final da contas, uma aplicao Java com interface grfica ira conter uma grande quantidade de componentes customizados, e o benefcio sera sentido pela reutilizao de cdigo. As prximas telas que necessitarem de componentes semelhantes podero ser construda pelas simple referencias aos componentes criados anteriormente,em vez de se

repetir o processo manual de configurao das propriedades. Tambm ajuda o fato de que criar novos componentes em Java muito mais simples do que em outros ambientes de programao visual. Na segunda e ultima parte ser explorado o componente JTable. Tomando como base os conceitos vistos nessa parte, criaremos uma nova tela usando um JTable com varias customizaes, alm de examinar em detalhes a arquitetura desse componente.

Para saber mais

Interfaces Grficas com Qualidade, Parte 1 Hugo Teixeira. Edio 38 Apresenta boas prticas para a criao de interfaces grficas em Java, com exemplos prticos usando o JGoodies Forms.

Aplicao Completa com NetBeans Fernando Lozano. Edio 25, 26 e 27 Descreve passo a passo a construo de uma aplicao de Lista de Tarefas usando o recursosdo NetBeans e o banco de dados HSQLDB.

Datas com Swing Fernando Lozano. Edio 28 Descreve o uso dos componentes do NachoCalendar para Swing, inclusive como adicionlo ao pallete do Eclipse VE e do NetBeans.

Formulrios com Swing Brno Souza. Edio 14 Explica os conceitos relacionados com os Layout Managers do AWT/Swing.

LINKS: java.sun.com/docs/books/tutorial/uiswing/components Trilha do Java Tutorial sobre componentes Swing. muntjak.de/hans/java/tinylaf Pgina oficial do TinyLAF.

jgoodies.dev.java.net Pgina oficial do JGoodies.

napkinlaf.sf.net Pgina oficial do Napkin LAF.

Das könnte Ihnen auch gefallen