1/36 De JEE 5 para JEE 6 Como modificar uma aplicação para usar os novos recursos Descubra como converter uma aplicação que usa JSF, Servlets, EJBs e JPA para a nova versão e como fica a utilização de cada um desses recursos MARCELO ELIAS DEL VALLE Esse artigo ensina como migrar, passo a passo, uma aplicação Java EE 5 para Java EE 6. A aplicação utilizou JSF para criar a interface gráfica, EJBs para a conversão e JPA para ler dados de um banco de dados. Primeiramente é apresentada a parte funcional da aplicação. Em seguida, as diferenças entre as duas versões da aplicação (Java EE 5 e Java EE 6) são descritas sob várias perspectivas, por meio de diagramas de classes, de componentes e através da listagem de arquivos de cada versão. Depois disso é explicado como migrar cada camada. São mostradas as diferenças nos descritores de arquivo, na implementação dos EJBs e na chamada aos mesmos. Também é mostrado como fica o layout dos arquivos JSF com o uso de JSPs e com o uso de facelets, como fica o acesso a dados e o que muda na implementação de um Servlet. Durante todo esse processo são citadas muitas das novas características da especificação Java EE 6, como templates JSF, Criteria API, novidades no CDI, EJBs sem interface, EJBs instalados dentro de um WAR ao invés de em um EAR, servlets assíncronos e web-fragments. A especificação Java EE versão 6 trouxe muitas novidades com relação às versões anteriores e pode ser considerada uma quebra de paradigma. Finalmente, é possível desenvolver com Java EE sem uma necessidade tão grande de recorrer a frameworks de terceiros. Ficou muito mais simples desenvolver e chamar EJBs, nunca foi tão fácil organizar a parte visual da aplicação graças ao uso de templates, facelets agora é a tecnologia visual padrão, substituindo o JSP, o acesso a dados ficou menos acoplado ainda com o banco de dados graças à Criteria API e o uso de servlets assíncronas permite realizar Server Push usando apenas as ferramentas oferecidas por padrão no Java EE 6. Esse artigo fala sobre tudo isso. Funcionalmente, nada muda na aplicação da versão Java EE 5 para a versão 6, as únicas diferenças ficam na implementação. Ferramentas utilizadas O exemplo citado no artigo roda no GlassFish, mas com pode rodar em qualquer Application Server com poucas adaptações. Recomenda-se usar o Glassfish v2 para rodar a aplicação Java EE 5 e o Glassfish v3 para rodar a aplicação Java EE 6. O GlassFish é a implementação de referência da Sun (agora Oracle) para o Java EE e portanto é recomendado para o aprendizado, mas vários outros containers implementam a especificação Java EE da forma como mostrado nesse artigo, como é o caso do JBoss, da Red Hat, e do WebSphere, da IBM. As duas versões da aplicação (tanto na versão Java EE 5 quanto na versão Java EE 6), assim como a versão desse artigo em PDF, estão disponíveis para download NESSE LINK e a mesma utiliza ANT para o build e contém um arquivo de projeto para o IDE Eclipse. Contudo, é possível rodar a mesma aplicação em qualquer IDE sem necessidade de modificações no código. A aplicação faz uso de um banco de dados, acessado em tempo de execução. Nesse banco precisa existir a tabela “Taxa”, cujos campos são descritos mais abaixo. Os exemplos desse artigo foram testados no MySQL. Tudo o que você precisa fazer é criar o banco de dados, criar a tabela Taxa no mesmo e configurar um datasource no seu container. Um datasource é um recurso do container que permite que as configurações de banco específicas para o ambiente fiquem configuradas no mesmo e não na aplicação. Para obter uma conexão JDBC 2/36 com o banco basta dizer ao container qual é o JNDI do datasource configurado e o ele se preocupa em como criar as conexões e fechá-las, conforme necessário. O conceito de datasource já é bem conhecido pela maioria dos programadores e, como não faz parte do foco do artigo, não será explicado em detalhes como configurá-lo. No Glassfish, para criar um data source é necessário configurar um connection pool antes, no qual são especificados os dados de conexão com o banco de dados. Depois de criado o Connection Pool especificando tipo de banco e dados de conexão, deve ser cadastrado o DataSource associando o Pool com o JNDI utilizado pela aplicação: “jdbc/ConversorDS”. Outro recurso utilizado foi o Realm, para autenticação. Para rodar as aplicações, é necessário cadastrar um Realm no Container com informações de usuários, grupos e senhas usados para autenticação. Será mostrado no artigo, sucintamente, como fazer isso em ambas as versões do Glassfish, mas é um recurso largamente utilizado e programadores não deveriam sentir dificuldade de realizar essa tarefa. Para rodar a aplicação, adicione no Glassfish um usuário “admin” com senha “admin” e na opção “Group List” insira o texto “administrator”, para que o usuário seja adicionado a esse grupo. Adicione o usuário no “file” realm (Configuration> Security> Realms> file). Descrevendo a parte funcional da aplicação A aplicação descrita nesse artigo é bem simples do ponto de vista funcional: uma aplicação que converte um valor em reais para um valor em dólares através de uma interface gráfica e de dólares para reais através de um servlet. A taxa de conversão de cada moeda é obtida a partir do banco de dados e há também interface de usuário para consultar as taxas no banco, alterar, apagar e adicionar novas taxas. Todas as telas necessitam de login para serem acessadas, então há também uma tela de login na aplicação. Na figura 1, é mostrada a tela de login e na figura 2, a tela de conversão de unidades quando a mesma é iniciada. Na figura 3, vemos a mesma tela, depois de o usuário digitar um valor em reais e o mesmo ser exibido em dólares. 3/36 Figura 1. Tela de Login Figura 2. Tela de conversão, ao iniciar Figura 3. Tela de conversão, após clicar no botão converter 4/36 Ao tentar entrar na aplicação (http://localhost:8080/jee5app ou http://localhost:8080/jee6app, assumindo que você está usando o glassfish com a porta padrão configurada: 8080), é exibida a tela de login. Uma vez logado no sistema, o usuário é redirecionado para a tela exibida na figura 2. A página JSF exibida na figura 2 tem o seguinte comportamento: quando o usuário clica no botão submit, o JSF chama um ManagedBean, que aciona um EJB do tipo SessionBean. O EJB usa um EntityBean mapeado via JPA para acessar os dados da tabela. O mesmo acontece para as telas exibidas nas figuras 4, 5 e 6. Todas elas chamam um ManagedBean, que por sua vez chama um EJB session que usa JPA para acessar o banco. A tela mostrada na figura 4 exibe a lista de moedas cadastradas e permite, para cada moeda, editá-la ou apagá-la. Ao clicar no X, a moeda é simplesmente apagada e a lista é recarregada. Ao clicar no E para editá-la, é exibida a figura 6, permitindo a alteração da moeda. Clicando no menu “Adicionar Nova Moeda” o usuário é levado para a figura 5, que permite que o mesmo efetue a adição. Figura 4. Lista de moedas cadastradas 5/36 Figura 5. Adicionar nova moeda Figura 6. Alterar moeda A figura 7 mostra o retorno de um servlet, que quando chamado aciona um EJB para converter um valor em dólares para Real e retorna um XML com o valor original e o valor em reais. 6/36 Figura 7. XML retornado pela chamada ao servlet A aplicação lê as taxas de conversão de uma tabela em um banco de dados. A figura 8 mostra dados de exemplo para a tabela “TAXA”, que deve estar disponível no banco de dados. Essa tabela o valor, em reais, da unidade de cada moeda. Na figura, vemos que um dólar, por exemplo, custa R$ 1,70 e um Iene (moeda do Japão, MOEDA = JPY) custa R$ 0,0202631. Figura 8. Taxas de conversão de outras moedas para Real Visão de classes Antes de entrarmos nos detalhes da migração da aplicação, cabe fornecer uma visão geral das classes em cada aplicação. As figuras 9 e 10 mostram como ficam as classes da aplicação com JAVA EE 5 e com JAVA EE 6, respectivamente. Perceba que na versão nova não são usadas interfaces EJB, sendo o bean injetado diretamente nas classes ConversorMoedaCliente e MoedaCliente, que são managed beans usados pela página JSF que, assim como o servlet, acessam um EJB para converter uma quantia em reais para uma quantia em dólares. Esses ManagedBeans existem nas duas versões da aplicação, o que muda é sua implementação e a forma de injetar o EJB, como veremos nas próximas seções. Managed Beans Managed Beans, ou beans gerenciados pelo container, é um conceito da especificação JAVA EE usado, sobretudo, em aplicações JSF. O conceito de managed bean é bem simples, são classes Java acessíveis por facelets que normalmente fazem a ponte entre a parte visual e a parte de negócio. O facelet, que no JAVA EE 6 substitui o JSP como tecnologia padrão para a camada visual, por boa prática deve conter somente a parte visual da aplicação, delegando para os managed beans a parte de lógica de apresentação. Os managed beans, por sua vez, devem conter somente a lógica de apresentação, delegando a parte de negócio para classes específicas de negócio ou, como é o caso dessa aplicação, para EJBs. Os facelets acessam os managed beans através de expressões EL (Expression Language), que nada mais são além de expressões misturadas no HTML delimitadas por #{...}. A listagem 14, por exemplo, exibe um facelet que acessa um managed bean através da expressão “#{conversorMoedaCliente.valorConvertido}”. 7/36 O conceito de managed beans e EL deve ser bem conhecido também pela maioria nos leitores, então nesse artigo é feito apenas um comparativo entre as diferenças entre a versão JAVA EE 5 e JAVA EE 6. class App Jee 5 Serv let w eb JEE5Serv let home.j sp ej b «ManagedBean» Conv ersorMoedaCliente lista.j sp - «interface» ConversorMoeda «EJB» Conv ersorMoedaBean moedaDest: String moedaOri: String valorConvertido: Double valorOriginal: Double + converter(Double, String, String) : Double «EJB» MoedaBean editar.j sp «interface» Moeda «ManagedBean» MoedaCliente adicionar.j sp - id: Integer moeda: String taxa: Double + + + + adicionar(Taxa) : void alterar(Taxa) : void apagar(Integer) : void encontrar(Integer) : void modelo «EJB Entity» Taxa login.j sp Figura 9. Classes na aplicação JAVA EE 5 - id: Integer moeda: String taxa: Double 8/36 class App Jee 6 Serv let w eb JEE6Serv let home.xhtml «ManagedBean» Conv ersorMoedaCliente lista.xhtml - moedaDest: String moedaOri: String valorConvertido: Double valorOriginal: Double ej b «EJB» Conv ersorMoedaBean + editar.xhtml converter(Double, String, String) : Double «ManagedBean» MoedaCliente - id: Integer moeda: String taxa: Double adicionar.xhtml «EJB» MoedaBean + + + + adicionar(Taxa) : void alterar(Taxa) : void apagar(Integer) : void encontrar(Integer) : void modelo «EJB Entity» Taxa login.xhtml - id: Integer moeda: String taxa: Double Figura 10. Classes na aplicação JAVA EE 6 Visão de pacotes Uma das diferenças entre as especificações JAVA EE 5 e JAVA EE 6 é o fato de que agora é possível colocar EJBs dentro do WAR diretamente, sem a necessidade de um EAR. A Figura 11 mostra como seria a visão de pacotes nas duas versões da aplicação e ilustra essa diferença do JAVA EE . 9/36 Figura 11. Diferença entre JAVA EE 5 e JAVA EE 6 com relação a pacotes Numa aplicação tradicional JAVA EE 5, contendo tanto classes web como EJB, é necessário criar no mínimo 2 pacotes para fazer deploy da aplicação: um arquivo WAR (Web ARchive) contendo as classes web, e um jar contendo os EJBs. Além desses dois, é comum empacotarmos ambos os pacotes dentro de um EAR (Enterprise Application Archive), para não precisarmos fazer o deploy do WAR e do EJB-JAR separadamente, garantindo que a versão do WAR é sempre compatível com a versão do EJB-JAR. Embora esse esquema de empacotamento possa ser interessante para aplicações realmente grandes, onde queremos fazer deploy de apenas parte da aplicação em um servidor e parte da aplicação em outro, para a grande maioria das aplicações é um grande transtorno, pelo fato de ser difícil empacotar a aplicação dessa forma. A dificuldade se deve principalmente ao fato de existirem classes na aplicação que devem ficar acessíveis tanto pela camada web quanto pela camada EJB, como é o caso das interfaces dos EJBs, das classes de domínio ou dos TOs (Transfer Objects). Como existe uma parte comum tanto para a camada web quanto para a camada de negócio, tornase complicado fazer o deploy de uma aplicação completa com JAVA EE 5. É necessário colocar as bibliotecas dentro do EAR e referenciar tanto no EJB-JAR quanto no WAR ou, então, colocar as mesmas classes compiladas tanto no EJB-JAR quando no WAR, como foi feito no exemplo desse artigo. Com JAVA EE 6 esse problema não existe, pois todas as classes de todas as camadas ficam no WEB-INF/classes, tornando tudo acessível para todos. Na figura 7 também não aparece a parte com as interfaces EJBs, pelo fato delas não existirem mais no JAVA EE 6, como veremos mais adiante. Visão de arquivos A listagem 1 mostra o local de cada arquivo da aplicação JAVA EE 5 dentro dos pacotes gerados, ou seja, qual o caminho de cada arquivo dentro EAR, do WAR e do EJB-JAR. A listagem 2 mostra 10/36 a mesma visão para a aplicação JAVA EE 6, mostrando somente os caminhos dentro do WAR, já que a aplicação migrada não tem EAR nem EJB-JAR. Cuidado para não confundir a visão de componentes com a visão dos arquivos dentro do IDE. Independente de como os arquivos ficam no seu ambiente de desenvolvimento, o que importa para o container JAVA EE quando você faz deploy é como os arquivos estão dispostos dentro dos pacotes, que é o que é mostrado nas listagens 1 e 2. Listagem 1. Arquivos em cada componente – JAVA EE 5 EAR (Java EE 5app.ear): /Java EE 5app-ejb.jar /Java EE 5app.war /META-INF/application.xml /META-INF/MANIFEST.MF EJB-JAR (Java EE 5app-ejb.jar): /br/com/human/ejb/ConversorMoeda.class /br/com/human/ejb/ConversorMoedaBean.class /br/com/human/ejb/Moeda.class /br/com/human/ejb/MoedaBean.class /br/com/human/modelo/Taxa.class /META-INF/MANIFEST.MF /META-INF/persistence.xml WAR (Java EE 5app.war): /default.css /style.css /adicionar.jsp /editar.jsp /error.jsp /footer.jsp /header.jsp /home.jsp /lista.jsp /login.jsp /menu.jsp /META-INF/MANIFEST.MF /WEB-INF/classes/br/com/human/ejb/ConversorMoeda.class /WEB-INF/classes/br/com/human/ejb/Moeda.class /WEB-INF/classes/br/com/human/modelo/Taxa.class /WEB-INF/classes/br/com/human/servlet/JAVA EE 5Servlet.class /WEB-INF/classes/br/com/human/web/ApplicationMessages.properties /WEB-INF/classes/br/com/human/web/ConversorMoedaCliente.class /WEB-INF/classes/br/com/human/web/MoedaCliente.class /WEB-INF/faces-config.xml /WEB-INF/lib /WEB-INF/web.xml /WEB-INF/sun-web.xml Listagem 2. Arquivos em cada componente – JAVA EE 6 WAR (Java EE 5app.war): /default.css /style.css /adicionar.xhtml /editar.xhtml /error.xhtml /home.xhtml /lista.xhtml /login.xhtml /menu.xhtml /META-INF/MANIFEST.MF /WEB-INF/classes/br/com/human/ejb/ConversorMoedaBean.class /WEB-INF/classes/br/com/human/ejb/MoedaBean.class /WEB-INF/classes/br/com/human/modelo/Taxa.class /WEB-INF/classes/br/com/human/servlet/JAVA EE 6Servlet.class /WEB-INF/classes/br/com/human/web/ApplicationMessages.properties /WEB-INF/classes/br/com/human/web/ConversorMoedaCliente.class /WEB-INF/classes/br/com/human/web/MoedaCliente.class /WEB-INF/classes/META-INF/persistence.xml /WEB-INF/faces-config.xml /WEB-INF/lib /WEB-INF/web.xml 11/36 Na listagem 1, percebemos que as interfaces Moeda, ConversorMoeda e a classe Taxa ficam acessíveis tanto do EJB-JAR quanto do arquivo WAR. Note como fica mais simples na listagem 2, com JAVA EE 6. O arquivo persistence.xml, exibido na listagem 4, faz parte da especificação JPA e é usado para mapear as classes de entidade e para definir o data source usado para acessar o banco que contem a tabela correspondente à entidade. Em nosso exemplo, mapeamos a classe Taxa, nosso Entity Bean. Na versão nova (listagem 2) esse arquivo não fica mais em META-INF/persistence.xml, mas sim em /WEB-INF/classes/META-INF/, que é o local padrão para se colocar esse arquivo agora que é possível colocar EntityBeans dentro do WAR. Perceba que a classe Taxa também foi movida para dentro do WAR. O arquivo web.xml foi utilizado para configurar de forma fácil a servlet do JSF, mas não fosse por isso poderia ser omitido do WAR (ao menos segundo a especificação, embora alguns containers ainda exijam), pois a maioria das configurações agora podem ser feitas por anotações, como veremos. Migração da aplicação Teoricamente, existe backward compatibility da versão 6 para a versão 5, ou seja, tudo o que é possível fazer com JAVA EE 5 fica igual no JAVA EE 6. E então, se você tem uma aplicação desenvolvida em JAVA EE 5 poderia facilmente fazer deploy do EAR já desenvolvido num container novo, como o glassfish 3, sem precisar migrar nada. Por que migrar então? Um dos motivos é que a aplicação ficará muito mais simples de ser mantida, pois a maioria dos recursos adicionais do JAVA EE 6 facilita muito o dia a dia na hora de manter o código e diminui consideravelmente o número de arquivos necessários. Além disso, converter o código de uma aplicação é um excelente modo de se atualizar para a versão nova, já que vamos focar exatamente nas diferenças de uma versão para a outra do JAVA EE . Na prática, para projetos reais, pode ou não valer a pena migrar suas aplicações para a versão 6 da especificação JAVA EE . Espera-se que esse artigo contribua para dar ao leitor uma visão melhor das vantagens da nova versão, para que ele mesmo possa decidir se é interessante ou não migrar seus projetos. Se estiver criando aplicações a partir do zero, contudo, com certeza será uma boa idéia já usar a versão nova, a não ser que existam limitações externas, como impossibilidade de rodar um container que a suporte, como o glassfish 3. Nas seções a seguir, vamos migrar cada feature da aplicação JAVA EE 5 para utilizar os novos recursos da versão 6. Convertendo os EJBs O Entity Bean é sem dúvida a parte que menos muda, se você já estiver usando EJB 3. A classe taxa mostrada na listagem 3 é exatamente a mesma para as duas versões da aplicação. Basicamente é utilizada a anotação javax.persistence.Entity para definir o bean como uma entidade e javax.persistence.Id para denotar o campo correspondente ao ID. Nessa aplicação de exemplo, a camada web chama um EJB session Bean que consulta os dados utilizando esse EntityBean Taxa e retorna instâncias dessa mesma classe para a web. Embora isso funcione e esteja correto para esse caso simples, aplicações maiores deveriam transferir dados para a web através de TOs, usando os EntityBeans somente para realizar consultas no banco. Além disso, ao invés de realizar consultas diretamente do EJB Session Bean, seria interessante usar um pattern de acesso a dados, como DAO ou Active Record. Essas decisões, contudo, fazem parte da definição da arquitetura específica de cada aplicação, não havendo uma “melhor forma” de fazer universal. Por esse motivo, no artigo a aplicação de exemplo limitou-se a fazer apenas o necessário. Listagem 3. EntityBean – Taxa.java package br.com.human.modelo; 12/36 import javax.persistence.Entity; import javax.persistence.Id; @Entity public class Taxa { @Id private Integer id; private String moeda; private Double taxa; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getMoeda() { return moeda; } public void setMoeda(String moeda) { this.moeda = moeda; } public Double getTaxa() { return taxa; } public void setTaxa(Double taxa) { this.taxa = taxa; } } A configuração da persistence unit também é bem fácil e não teve mudanças. O XML de configuração é mostrado na listagem 4. Note que esse arquivo deve ficar em um local diferente dentro do pacote, conforme mostrado na seção “Visão de Pacotes”. Listagem 4. Unidade de persistência – persistence.xml <?xml version='1.0' encoding='UTF-8'?> <persistence> <persistence-unit name="ConversorDS"> <description>Unidade de persistência para acessar a tabela de moedas </description> <jta-data-source>jdbc/ConversorDS</jta-data-source> <class>br.com.human.modelo.Taxa</class> </persistence-unit> </persistence> Com relação ao EJB Session Bean, houve uma grande mudança na versão 6 do JAVA EE , que é a inexistência de interfaces. As listagens 5 e 6 mostram a versão JAVA EE 5 e a listagem 7 mostra a versão JAVA EE 6. A listagem 5 mostra a interface do Session Bean, que era incluída pelo ManagedBean ConversorMoedaCliente, exibido na listagem 18. Essa interface só existe na versão JAVA EE 5. 13/36 Na listagem 6, o EJB ConversorMoedaBean herda de ConversorMoeda, o que não acontece mais na versão nova, mostrada na listagem 7. Perceba também que na listagem 19, que mostra o ConversorMoedaCliente na versão JAVA EE 6, o EJB é injetado diretamente, não mais sua interface. Essa acabou sendo uma das grandes vantagens da nova versão do JAVA EE , já que o motivo de existir uma interface seria tornar possível ter várias implementações de um mesmo EJB em seu código, com o mesmo contrato. Na versão antiga isso nunca era utilizado e pelo fato do Session Bean ter virado um simples POJO, fica bem mais fácil usar os mecanismos de orientação a objeto da própria linguagem Java, ao mesmo tempo em que não deixamos de ter todas as vantagens do uso de EJBs, como capacidade de rodar em cluster e ser gerenciado pelo container. Comparando as implementações do EJB (listagens 6 e 7), podemos perceber uma diferença na forma de busca os dados, já que na versão JAVA EE 5 (listagem 6) é utilizado Query Language e na versão JAVA EE 6 (listagem 7) é utilizada a API de Criteria. Listagem 5. Interface EJB ConversorMoedaBean - versão JAVA EE 5 package br.com.human.ejb; import java.util.List; import javax.ejb.Remote; import br.com.human.modelo.Taxa; @Remote public interface ConversorMoeda { public Double converter(String moedaOri, String moedaDest, Double valorOri); public List<Taxa> getListaTaxas(); } Listagem 6. Implementação EJB ConversorMoedaBean - versão JAVA EE 5 package br.com.human.ejb; import java.util.List; import javax.ejb.Stateless; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import br.com.human.modelo.Taxa; @Stateless public class ConversorMoedaBean implements ConversorMoeda { @PersistenceContext EntityManager em; public Double converter(String moedaOri, String moedaDest, Double valorOri) { Double taxaMoedaOri, taxaMoedaDest; taxaMoedaOri = getTaxaMoeda(moedaOri); taxaMoedaDest = getTaxaMoeda(moedaDest); Double result = valorOri * (taxaMoedaOri / taxaMoedaDest); return result; } 14/36 @SuppressWarnings("unchecked") public Double getTaxaMoeda(String moeda) { Double taxaMoeda = 1.0; List<Taxa> resultado = em .createQuery("SELECT t FROM Taxa t WHERE t.moeda = ?1") .setParameter(1, moeda).getResultList(); Taxa m = resultado.get(0); taxaMoeda = m.getTaxa(); return taxaMoeda; } } @SuppressWarnings("unchecked") public List<Taxa> getListaTaxas() { return em.createQuery("SELECT t FROM Taxa t").getResultList(); } Listagem 7. Implementação EJB ConversorMoedaBean - versão JAVA EE 6 package br.com.human.ejb; import java.util.List; import import import import import import import import javax.ejb.Stateless; javax.persistence.EntityManager; javax.persistence.PersistenceContext; javax.persistence.TypedQuery; javax.persistence.criteria.CriteriaBuilder; javax.persistence.criteria.CriteriaQuery; javax.persistence.criteria.Predicate; javax.persistence.criteria.Root; import br.com.human.modelo.Taxa; @Stateless public class ConversorMoedaBean { @PersistenceContext EntityManager em; public Double converter(String moedaOri, String moedaDest, Double valorOri) { Double taxaMoedaOri, taxaMoedaDest; taxaMoedaOri = getTaxaMoeda(moedaOri); taxaMoedaDest = getTaxaMoeda(moedaDest); Double result = valorOri * (taxaMoedaOri / taxaMoedaDest); return result; } public Double getTaxaMoeda(String moeda) { Double taxaMoeda = 1.0; CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder(); CriteriaQuery<Taxa> criteriaQuery = criteriaBuilder .createQuery(Taxa.class); Root<Taxa> taxa = criteriaQuery.from(Taxa.class); Predicate condition = criteriaBuilder.equal(taxa.get("moeda"), moeda); criteriaQuery.where(condition); TypedQuery<Taxa> typedQuery = em.createQuery(criteriaQuery); List<Taxa> resultado = typedQuery.getResultList(); Taxa m = resultado.get(0); taxaMoeda = m.getTaxa(); 15/36 } return taxaMoeda; public List<Taxa> getListaTaxas() { CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder(); CriteriaQuery<Taxa> criteriaQuery = criteriaBuilder .createQuery(Taxa.class); TypedQuery<Taxa> typedQuery = em.createQuery(criteriaQuery); List<Taxa> resultado = typedQuery.getResultList(); } return resultado; } Criteria API Quem está acostumado com o Hibernate deve conhecer de longa sua API de Criteria Queries. Esse foi um feature tão largamente usado por desenvolvedores que acabou entrando para a própria especificação JAVA EE nessa versão 6, embora existam rumores de que será melhorada ainda mais nas novas versões e que não tenha ficado tão boa quanto a original, do Hibernate. A vantagem é poder usá-la com qualquer implementação JPA e não só com o Hibernate, o que pode ser crítico dependendo do container sendo utilizado. Basicamente, a idéia de uma API desse tipo é poder realizar consultas no banco sem qualquer uso de uma linguagem de consulta, deixando as regras de negócio todas no código e eliminando completamente a dependência do banco de dados sendo utilizado. Uma grande vantagem que esse approach representa é o fato de o compilador lançar erro caso algo esteja errado na query, coisa que não aconteceria caso fosse usada uma string com QL. Utilizando a factory CriteriaBuilder, é possível criar uma query para uma classe considerada a raiz da busca. Com o objeto de definição de consulta criado, é possível compor uma série de critérios a partir dessa raiz da busca, tornando-a cada vez mais específica para aquilo que se deseja encontrar. Por exemplo, poderíamos adicionar para a raiz da busca definições de predicados (que corresponderiam à cláusula WHERE de um SQL), ordenações, agrupamentos, subqueries, joins, etc. Convertendo os descritores de deploy Um dos principais motivos de se querer converter uma aplicação JAVA EE 5 para a versão 6 é que na versão nova quase não é necessário escrever XML. Muito do que se configurava através de arquivos de configuração XML agora é configurado via anotações. Por exemplo, não é mais necessário declarar os servlets no web.xml. A listagem 8 mostra o web.xml versão JAVA EE 5 e a listagem 9 mostra a versão 6. Listagem 8. web.xml da aplicação JAVA EE 5 <?xml version='1.0' encoding='UTF-8'?> <!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> <!-- Faces Servlet --> <servlet> <servlet-name>FacesServlet</servlet-name> <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> 16/36 <load-on-startup>1</load-on-startup> </servlet> <!-- Java EE 5 Servlet --> <servlet> <servlet-name>Java EE 5Servlet</servlet-name> <servlet-class>br.com.human.servlet.JAVA EE 5Servlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <!-- Faces Servlet Mapping --> <servlet-mapping> <servlet-name>FacesServlet</servlet-name> <url-pattern>/Java EE 5/*</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>Java EE 5Servlet</servlet-name> <url-pattern>/Java EE 5servlet</url-pattern> </servlet-mapping> <welcome-file-list> <welcome-file>Java EE 5/home.jsp</welcome-file> </welcome-file-list> <security-constraint> <web-resource-collection> <web-resource-name>Administrativo</web-resource-name> <!-- The URLs to protect --> <url-pattern>*.jsp</url-pattern> </web-resource-collection> <auth-constraint> <!-- The authorized users --> <role-name>administrador</role-name> </auth-constraint> </security-constraint> <login-config> <auth-method>FORM</auth-method> <realm-name>file</realm-name> <form-login-config> <form-login-page>/Java EE 5/login.jsp</form-login-page> <form-error-page>/Java EE 5/error.jsp</form-error-page> </form-login-config> </login-config> <security-role> <role-name>administrador</role-name> </security-role> </web-app> Listagem 9. JAVA EE 6 web.xml <?xml version="1.0" encoding="UTF-8"?> <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> <servlet> <servlet-name>Faces Servlet</servlet-name> <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> <load-on-startup>1</load-on-startup> 17/36 </servlet> <servlet-mapping> <servlet-name>Faces Servlet</servlet-name> <url-pattern>/Java EE 6/*</url-pattern> </servlet-mapping> <welcome-file-list> <welcome-file>Java EE 6/home.xhtml</welcome-file> </welcome-file-list> <security-constraint> <web-resource-collection> <web-resource-name>Administrativo</web-resource-name> <!-- The URLs to protect --> <url-pattern>*.xhtml</url-pattern> </web-resource-collection> <auth-constraint> <!-- The authorized users --> <role-name>administrador</role-name> </auth-constraint> </security-constraint> <login-config> <auth-method>FORM</auth-method> <realm-name>file</realm-name> <form-login-config> <form-login-page>/Java EE 6/login.xhtml</form-login-page> <form-error-page>/Java EE 6/error.xhtml</form-error-page> </form-login-config> </login-config> <security-role> <role-name>administrador</role-name> </security-role> </web-app> Como é possível notar, o servlet do faces continua sendo declarado. Em alguns containers existe como não realizar essa configuração, pois ela é feita no próprio framework, ou seja, não é necessário declarar o servlet do faces no web.xml pois o próprio jar do framework já contem um arquivo web-fragment.xml (veja quadro) que especifica o mapeamento de URL da servlet. Na implementação de referência do JSF ela ainda ficou como necessária e, se não fosse por isso, não precisaríamos de um web.xml na aplicação. Se você experimentar rodar a aplicação versão JAVA EE 6 tirando o web.xml, perceberá que o servlet continuará rodando normalmente, só as páginas JSF que não. Web Fragments Embora não tenha sido usado nessa aplicação de exemplo, uma das principais novidades do JAVA EE 6 é a existência de web fragments. A listagem 10 exemplifica como seria um arquivo webfragment.xml de um framework, embora não seja usado na aplicação. Esse arquivo ficaria dentro da pasta META-INF de um jar incluído em WEB-INF/lib. Por exemplo, se você inclui na sua aplicação o framework.jar como uma lib, esse arquivo deveria ter uma pasta META-INF e dentro dela um arquivo web-fragment.xml como o da listagem 10. Dessa forma, o container ao carregar sua aplicação já entenderia que você incluiu um módulo web, que tem servlets a serem carregadas. 18/36 A idéia de web-fragments é tornar a configuração web modularizável, de forma que seja possível construir vários módulos web independentes, cada um com sua configuração, e integrá-los de forma fácil quando desejado. Não é mais preciso desenvolver os servlets em vários JARs separados e colocar a configuração na aplicação que os utiliza, os próprios módulos podem vir com sua configuração definida. Esse arquivo poderia definir outras configurações hoje existentes no web.xml, mas no exemplo é mostrado o mapeamento de um servlet. A tag servlet é semelhante à usada no web.xml, mas no lugar da servlet-mapping é definida uma classe listener que faz o mesmo papel, programaticamente. É possível usar tanto arquivos web.xml, quanto anotações e web-fragments, mas existe uma ordem de processamento, sendo que a configuração do web.xml se sobrepõe às outras, ou seja, se existe configuração de uma servlet no web.xml, a anotação dessa servlet será ignorada. Se essa servlet foi incluída a partir de um JAR externo com um web-fragment.xml, a configuração do jar também será ignorada se a servlet for mapeada pelo web.xml da aplicação. Isso permite sobrescrever as configurações do módulo incluído. Listagem 10. web-fragment.xml – novo feature do JAVA EE 6 permite modularizar o web.xml <web-fragment> <servlet> <servlet-name>myFrameworkServlet</servlet-name> <servlet-class>myFramework.myFrameworkServlet</servlet-class> </servlet> <listener> <listener-class>myFramework.myFrameworkListener</listener-class> </listener> </web-fragment> Observe novamente as listagens 8 e 9. Note que existe uma diferença crucial entre os arquivos de ambas as listagens que é a designação da versão, no início do arquivo. O arquivo da listagem 8 define a versão 2.3 do servlet, enquanto que o arquivo da listagem 9 define a versão 3 (atente para o elemento web-app, atributo version). Se essa versão não estivesse configurada corretamente, o container não leria os EJBs, que não eram permitidos dentro do war no JAVA EE 5, e ignoraria completamente possíveis arquivos webfragment e anotações de mapeamento dentro de servlets. Além disso, na listagem 9 não é mapeada a servlet da aplicação, pois ela é mapeada através de anotações, conforme mostrado na próxima seção. Convertendo a servlet As listagens 11 e 12 mostram, respectivamente, as versões da servlet para as plataformas JAVA EE 5 e 6. A implementação das servlets exibidas nas duas listagens não muda praticamente nada: ambas as classes servlet recebem um parâmetro dólares vindo da URL (método GET), e retornam um XML com o valor original em dólares e o valor convertido para reais. O XML é retornado através de prints para o objeto response. Verifique, além das duas listagens, o mapeamento da servlet da listagem 11, exibido na listagem 8. A servlet está mapeada para atender a URLs do tipo: http://localhost:8080/Java EE 5app/Java EE 5servlet?dolares=23.0, onde Java EE 5app é o contexto da aplicação (o nome do EAR gerado, se você fizer deploy jogando o arquivo na pasta autodeploy do glassfish) e Java EE 5servlet é o mapeamento conforme configurado no web.xml. Uma das coisas que muda de uma versão para outra é o mapeamento. Na classe exibida na listagem 11, desenvolvida para a versão JAVA EE 5, não há nenhuma configuração de mapeamento, pois a mesma é realizada no web.xml. Na classe exibida na listagem 12, desenvolvida 19/36 para a versão JAVA EE 6, o mapeamento é feito através de uma anotação: @WebServlet("/Java EE 6servlet") Verifique agora a listagem 12. A servlet está mapeada para atender a URLs do tipo: http://localhost:8080/Java EE 6app/Java EE 6servlet?dolares=23.0, onde Java EE 6app é o contexto da aplicação (o nome do WAR gerado, se você fizer deploy jogando o arquivo na pasta autodeploy do glassfish) e Java EE 6servlet é o mapeamento conforme configurado na anotação da classe. Outra mudança é a forma de acessar o EJB. Na listagem 11 é necessário criar uma intância do InitialContext e então obter a referência para o EJB via JNDI, ou seja, é necessário se preocupar no código da aplicação em quando instanciar a referência para o EJB. Na listagem 12, versão JAVA EE 6, o EJB é injetado no atributo conversor com a tag @EJB. O próprio container se preocupa em obter a referência para o EJB através dessa injeção de dependência, tornando a instanciação completamente transparente para quem está desenvolvendo a aplicação. Dentro do método service, o atributo conversor é simplesmente usado, sem necessidade da aplicação instanciá-lo. Listagem 11. JAVA EE 5Servlet package br.com.human.servlet; import java.io.IOException; import java.io.PrintWriter; import import import import import javax.naming.InitialContext; javax.servlet.ServletException; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse; import br.com.human.ejb.ConversorMoeda; public class JAVA EE 5Servlet extends HttpServlet { private static final long serialVersionUID = 5231367249531988910L; private ConversorMoeda conversor; public void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { PrintWriter out = res.getWriter(); Double dolares = 100.00; dolares = Double.parseDouble(req.getParameter("dolares").toString()); double reais=0.0; try { InitialContext ic = new InitialContext(); conversor = (ConversorMoeda) ic.lookup(ConversorMoeda.class .getName()); reais = conversor.converter("USD", "BRL", dolares); conversor = null; } catch (Exception ex) { ex.printStackTrace(); } out.println("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>"); out.println("<Valor>"); out.print("\t<Dolar>"); out.print(dolares); out.println("</Dolar>"); out.print("\t<Real>"); out.print(reais); 20/36 out.println("</Real>"); out.println("</Valor>"); } } Listagem 12. JAVA EE 6Servlet package br.com.human.servlet; import java.io.IOException; import java.io.PrintWriter; import import import import import import javax.ejb.EJB; javax.servlet.ServletException; javax.servlet.annotation.WebServlet; javax.servlet.http.HttpServlet; javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse; import br.com.human.ejb.ConversorMoedaBean; @WebServlet("/Java EE 6servlet") public class JAVA EE 6Servlet extends HttpServlet { private static final long serialVersionUID = 5231367249531988910L; @EJB private ConversorMoedaBean conversor; public void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { PrintWriter out = res.getWriter(); Double dolares = 100.00; dolares = Double.parseDouble(req.getParameter("dolares").toString()); double reais = conversor.converter("USD", "BRL", dolares); out.println("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>"); out.println("<Valor>"); out.print("\t<Dolar>"); out.print(dolares); out.println("</Dolar>"); out.print("\t<Real>"); out.print(reais); out.println("</Real>"); out.println("</Valor>"); } } A annotation WebServlet exibida na listagem 12 aceitaria muito mais opções de mapeamento. Um outro exemplo de possibilidade é mostrado na listagem 13. Listagem 13. Exemplo fictício do uso da annotation WebServlet @WebServlet(name="Java EE 6servlet", asyncSupported=true, urlPatterns={"/jee6servlet", "/getVal"}) O exemplo define mais de uma URL pattern, ou seja, poderíamos chamar essa servlet usando tanto um URL como http://localhost:8080/jee6app/jee6servlet?dolares=23.0 quanto http://localhost:8080/jee6app/getVal?dolares=23.0. 21/36 A anotação tamém expõe a servlet como assíncrona. Embora não usado em nossa aplicação de exemplo, servlet assíncrona é outro feature impactante da especificação servlet 3.0, trazendo funcionalidades semelhantes às oferecidas por frameworks como cometd, ou seja, server-push para o client. Servlets Assíncronas Um dos grandes problemas enfrentados na vida real por quem desenvolve aplicações web é que algumas vezes o processamento realizado no lado servidor é muito demorado, tornando inviável deixar a aplicação no browser esperando a resposta. Por esse motivo, a solução é usar uma chamada para uma servlet somente para iniciar o processamento, de forma que a conexão do browser com o servidor web é fechada assim que o processamento é iniciado e a servlet retorna. Além de não deixar o cliente (no caso o browser) esperando uma resposta, fechar a conexão libera aquela instância da servlet para processar outros requests, no lado do servidor. Contudo, quando o processamento termina, a conexão com o browser já foi fechada e não há como devolver um status de resultado do processamento para o cliente. Uma das soluções mais usadas nesse caso é o pooling, ou seja, o browser faria uma nova chamada a cada X unidades de tempo (ex.: a cada 30 segundos) perguntando ao servidor se aquele processamento já terminou. Pooling também não é a solução ideal pois é um desperdício de recursos, já que consultar o status do processamento no servidor de tempos em tempos ocupa mais processamento e possivelmente acesso a banco do que simplesmente esperar o processamento terminar do lado do servidor para dar a resposta. O framework cometd foi uma das primeiras implementações de Server push, uma das melhores soluções para esse tipo de problema, que agora está também na especificação JAVA EE 6. No servlet 3.0, que faz parte da especificação JAVA EE 6, você pode especificar a servlet como assíncrona. Se a servlet é assíncrona, ao invés de realizar todo o processamento no método service, o desenvolvedor chama um método startAsync(), do objeto request, que retorna um objeto AsyncContext, responsável por encapsular o request e o response da chamada. O método service então termina de ser executado antes do processamento terminar e a servlet é devolvida para o container JAVA EE , mas a conexão com o cliente não é fechada. Através de um método complete do AsyncContext e de uma interface AsyncListener, a aplicação tem a possibilidade de escrever a resposta para o cliente posteriormente, tornando possível o servidor dar uma resposta para o cliente. Os detalhes de como usar servlets assíncronas estão fora do escopo desse artigo, mas é uma das novidades mais impactantes da nova versão e com certeza podem substituir implementações extremamente complexas de aplicações JAVA EE 5 já desenvolvidas. Convertendo o JSF As listagens 14 e 15 mostram respectivamente os arquivos header.jsp e footer.jsp, ambos usados na aplicação que usa JAVA EE 5. Esses arquivos são incluídos por todos os outros JSPs da aplicação, como o home.jsp (listagem 16) e o adicionar.jsp (listagem 17). Listagem 14. header.jsp – cabeçalho comum - camada visual da aplicação na versão JAVA EE 5 <% String path = request.getContextPath(); String basePath = request.getScheme() + "://" 22/36 + request.getServerName() + ":" + request.getServerPort() + path + "/Java EE 5/"; %> <HTML xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> <head> <title>Conversor de moeda JAVA EE </title> <link rel="stylesheet" type="text/css" href="style.css" media="screen" /> <link rel="stylesheet" type="text/css" href="default.css" media="screen" /> <base href="<%=basePath%>"> </head> <body> <div id="wrap"> <div id="top"></div> <div id="content"> <div class="header"> <h1><a href="#">Conversor de moedas JAVA EE </a></h1> <h2>Human Software</h2> </div> <div class="breadcrumbs"><a href="home.jsp"> Home </a> &middot; <%=nomePagina%> </div> Listagem 15. footer.jsp – rodapé comum - camada visual da aplicação na versão JAVA EE 5 <div id="clear"></div> </div> <div id="bottom"></div> </div> </body> </html> Listagem 16. home.jsp – página inicial - camada visual da aplicação na versão JAVA EE 5 <% %> String nomePagina = "Conversor de Moedas"; <%@ include file="header.jsp"%> <%@ taglib <%@ taglib <f:view> <div <div uri="http://java.sun.com/jsf/html" prefix="h"%> uri="http://java.sun.com/jsf/core" prefix="f"%> class="middle"> id="contents"><h:form id="conversorForm"> <h3>Entre com o valor a ser convertido e escolha as moedas de origem e destino</h3> <BR> <div class="valor">Valor original: <h:inputText id="valorOriginal" label="Valor original" 23/36 value="#{conversorMoedaCliente.valorOriginal}" converterMessage="#{ErrMsg.valorOriginalConvert}" required="true"> <f:validateDoubleRange minimum="0.0" /> <f:convertNumber maxFractionDigits="2" /> </h:inputText> <h:selectOneListbox size="1" id="moedaOri" value="#{conversorMoedaCliente.moedaOri}"> <f:selectItems value="#{conversorMoedaCliente.listaMoedas}" /> </h:selectOneListbox></div> <div class="valor">Valor destino: <div class="input"><h:outputText lang="pt_BR" value="#{conversorMoedaCliente.valorConvertido}"> <f:convertNumber groupingUsed="true" type="currency" currencySymbol="" /> </h:outputText></div> <h:selectOneListbox size="1" id="moedaDest" value="#{conversorMoedaCliente.moedaDest}"> <f:selectItems value="#{conversorMoedaCliente.listaMoedas}" /> </h:selectOneListbox></div> <h:commandButton id="submit" styleClass="submit" value="Converter" /> <P><h:message showSummary="true" showDetail="false" style="color: red; font-family: 'New Century Schoolbook', serif; font-style: oblique; text-decoration: overline" id="errors1" for="valorOriginal" /></P> </h:form></div> </div> <%@ include file="menu.jsp"%> </f:view> <%@ include file="footer.jsp"%> Listagem 17. adicionar.jsp – página adicionar - camada visual da aplicação na versão JAVA EE 5 <% %> String nomePagina = "Adicionar Moeda"; <%@ include file="header.jsp"%> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%> <f:view> <div class="middle"> <div id="contents"> <h:form id="conversorForm"> <h3>Adicionar moeda</h3> <BR> <div class="valor">Moeda: <h:inputText id="moeda" label="Moeda" value="#{moedaCliente.moeda}" required="true"> <f:validateLength minimum="3" maximum="3"/> </h:inputText> </div> <div class="valor">Taxa: <h:inputText id="taxa" label="Taxa" value="#{moedaCliente.taxa}" 24/36 required="true"> converterMessage="#{ErrMsg.valorOriginalConvert}" <f:validateDoubleRange minimum="0.0" /> <f:convertNumber maxFractionDigits="2" /> </h:inputText> </div> <h:commandButton id="submit" styleClass="submit" value="Adicionar" action="#{moedaCliente.adicionar}" > <f:param name="id" value="-1" /> </h:commandButton> <P> <h:messages showDetail="false" showSummary="true" style="color: red; font-family: 'New Century Schoolbook', serif; font-style: oblique; text-decoration: overline; list-style-type: none;"/> </P> </h:form></div> </div> <%@ include file="menu.jsp"%> </f:view> <%@ include file="footer.jsp"%> Listagem 18. menu.jsp – menu - camada visual da aplicação na versão JAVA EE 5 <div class="right"> <h2>Menu</h2> <ul> <li><h:outputLink value="home.jsp"> <h:outputText value="Converter Moedas" /> </h:outputLink></li> <li><h:outputLink value="lista.jsp"> <h:outputText value="Lista de Moedas Cadastradas" /> </h:outputLink></li> <li><h:outputLink value="adicionar.jsp"> <h:outputText value="Adicionar Nova Moeda" /> </h:outputLink></li> </ul> </div> Perceba como funciona a lógica de reuso da parte comum com JSPs, que são a tecnologia padrão para JAVA EE 5. Todo arquivo tem que incluir explicitamente a parte comum e fica difícil reutilizar as partes comuns. Isso fica mais claro quando observamos como foi alterado o nome da página nesse exemplo. Verifique que a variável nomePagina é exibida por um scriptlet dentro do header.jsp, mas o valor dessa variável é setado em cada JSP que inclui o mesmo. Outro ponto importante de ser observado é o número de scriptlets usados. Se a idéia do JSP é poder dividir o trabalho visual do trabalho de programação, é desejável ter código misturado com o HTML? Perceba que setar a variável basePath não é uma tarefa tão complicada para se colocar no JSP, uma vez que é utilizada somente por uma necessidade do próprio HTML, que é setar a pasta base dos arquivos. Contudo, mesmo para uma tarefa tão simples, o profissional que estiver criando esse arquivo precisará conhecer Java para criar a expressão. Na versão JAVA EE 6, ao invés de JSPs são usados facelets, que se tornaram a tecnologia padrão para a camada visual. O mesmo trabalho realizado com JSPs e JAVA EE 5 nas listagens 14, 15, 16, 17 e 18 é realizado com JAVA EE 6 e facelets nas listagens 19, 20, 21 e 22. 25/36 Listagem 19. template.xhtml – template comum - camada visual da aplicação na versão JAVA EE 6 <?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:ui="http://java.sun.com/jsf/facelets"> <head> <title>Conversor de moeda JAVA EE </title> <link rel="stylesheet" type="text/css" href="style.css" media="screen" /> <link rel="stylesheet" type="text/css" href="default.css" media="screen" /> <base href="#{request.getScheme()}://#{request.getServerName()}:#{request.getServerPor t()}#{request.getContextPath()}/Java EE 6/" /> </head> <body> <div id="wrap"> <div id="top"></div> <div id="content"> <div class="header"> <h1><a href="#">Conversor de moedas JAVA EE </a></h1> <h2>Human Software</h2> </div> <div class="breadcrumbs"><h:outputLink value="home.xhtml"> <h:outputText value="Home" /> </h:outputLink> &middot; #{nomePagina}</div> <f:view> <div class="middle"> <div id="contents"><ui:insert name="body" /></div> </div> <ui:insert name="menu" /> </f:view> <div id="clear"></div> </div> <div id="bottom"></div> </div> </body> </html> Listagem 20. menu.xhtml – menu - camada visual da aplicação na versão JAVA EE <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:ui="http://java.sun.com/jsf/facelets"> 6 26/36 <ui:composition> <div class="right"> <h2>Menu</h2> <ul> <li><h:outputLink value="home.xhtml"> <h:outputText value="Converter Moedas" /> </h:outputLink></li> <li><h:outputLink value="lista.xhtml"> <h:outputText value="Lista de Moedas Cadastradas" /> </h:outputLink></li> <li><h:outputLink value="adicionar.xhtml"> <h:outputText value="Adicionar Nova Moeda" /> </h:outputLink></li> </ul> </div> </ui:composition> </html> Listagem 21. home.xhtml – página inicial - camada visual da aplicação na versão JAVA EE 6 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:ui="http://java.sun.com/jsf/facelets"> <ui:composition template="template.xhtml"> <ui:param name="nomePagina" value="Conversor de Moedas" /> <ui:define name="menu"> <ui:include src="menu.xhtml" /> </ui:define> <ui:define name="body"> <h:form id="conversorForm"> <h3>Entre com o valor a ser convertido e escolha as moedas de origem e destino</h3> <BR /> <div class="valor">Valor original: <h:inputText id="valorOriginal" label="Valor original" value="#{conversorMoedaCliente.valorOriginal}" converterMessage="#{ErrMsg.valorOriginalConvert}" required="true"> <f:validateDoubleRange minimum="0.0" /> <f:convertNumber maxFractionDigits="2" /> </h:inputText> <h:selectOneListbox size="1" id="moedaOri" value="#{conversorMoedaCliente.moedaOri}"> <f:selectItems value="#{conversorMoedaCliente.listaMoedas}" /> </h:selectOneListbox></div> <div class="valor">Valor destino: <div class="input"><h:outputText lang="pt_BR" value="#{conversorMoedaCliente.valorConvertido}"> <f:convertNumber groupingUsed="true" type="currency" currencySymbol="" /> </h:outputText></div> <h:selectOneListbox size="1" id="moedaDest" value="#{conversorMoedaCliente.moedaDest}"> 27/36 <f:selectItems value="#{conversorMoedaCliente.listaMoedas}" /> </h:selectOneListbox></div> <h:commandButton id="submit" styleClass="submit" value="Converter" /> <P><h:message showSummary="true" showDetail="false" style="color: red; font-family: 'New Century Schoolbook', serif; font-style: oblique; text-decoration: overline" id="errors1" for="valorOriginal" /></P> </h:form> </ui:define> </ui:composition> </html> Listagem 22. adicionar.xhtml – página adicionar - camada visual da aplicação na versão JAVA EE 6 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:ui="http://java.sun.com/jsf/facelets"> <ui:composition template="template.xhtml"> <ui:param name="nomePagina" value="Adicionar Moeda" /> <ui:define name="menu"> <ui:include src="menu.xhtml" /> </ui:define> <ui:define name="body"> <h:form id="conversorForm"> <h3>Adicionar moeda</h3> <BR/> <div class="valor">Moeda: <h:inputText id="moeda" label="Moeda" value="#{moedaCliente.moeda}" required="true"> <f:validateLength minimum="3" maximum="3" /> </h:inputText></div> <div class="valor">Taxa: <h:inputText id="taxa" label="Taxa" value="#{moedaCliente.taxa}" converterMessage="#{ErrMsg.valorOriginalConvert}" required="true"> <f:validateDoubleRange minimum="0.0" /> <f:convertNumber maxFractionDigits="2" /> </h:inputText></div> <h:commandButton id="submit" styleClass="submit" value="Adicionar" action="#{moedaCliente.adicionar}"> <f:param name="id" value="-1" /> </h:commandButton> <P><h:messages showDetail="false" showSummary="true" style="color: red; font-family: 'New Century Schoolbook', serif; font-style: oblique; text-decoration: overline; list-style-type: none;" /> </P> 28/36 </h:form> </ui:define> </ui:composition> </html> Existem várias diferenças entre JAVA EE 5 e JAVA EE 6 no tocante ao uso de facelets. Os arquivos xhtml mostrados nas listagens 19, 20, 21 e 22 necessariamente tem de ser XMLs bem formatados, ou seja, tem que seguir as regras de formatação definidas pelo w3c (World Wide Web Consortion – entidade responsável por padrozinar as especificações para a web). Exemplos de regras: toda tag aberta ter de ser fechada, todo elemento (tag) ter um namespace, definido de acordo com os padrões XML do w3c, etc. O objetivo dessas regras é deixar a especificação do documento o mais formal e estrita o possível, não deixando margens para ambigüidades, como acontece no HTML, onde cada browser interpreta o documento de uma forma diferente. Na versão home.jsp exibida na listagem 16, as taglibs são definidas pelo elemento taglib. No home.xhtml, versão JAVA EE 6, da exibida na listagem 21, os elementos são definidos no padrão XML, através do atributo xmlns do elemento html. Os facelets também estão de acordo com o padrão de páginas XHTML “Transitional Document Type Definition” (veja a referência para o DTD na listagem), outro padrão do w3c. Apesar de essas diferenças serem significativas, a maior diferença entre o uso de JSPs ou de facelets, nesse exemplo é o uso de templates. Perceba que o conteúdo dos arquivos header.jsp (listagem 14), footer.jsp (listagem 15) e menu.jsp (listagem 18) foi praticamente todo movido para os arquivos template.xhtml (listagem 19) e menu.xhtml (listagem 20). Pegando como exemplo os arquivos home.jsp (listagem 16) e adicionar.jsp (listagem 17), vemos que o arquivo menu.jsp é incluído em cada página, pois fica difícil obter reuso apenas com os includes do JAVA EE 5. Comparando com o home.xhtml (listagem 21) e o adicionar.xhtml (listagem 22), vemos que com templates a divisão de conteúdo em arquivos é muito mais lógica, pois condiz mais com o funcional. Observe agora como o basePath e o nome da página são setados no template.xhtml da listagem 21. Compare com a versão JAVA EE 5, o header.jsp da listagem 14. Os scriptlets foram substituídos por expressões EL, eliminando a necessidade de código no HTML. O título de cada página também ficou setado de forma muito mais elegante, já que a variável EL nomePagina é setada de forma explícita como parâmetro (ui:param) para o template, em cada página. O uso de templates é um grande benefício dos facelets, padrão no JAVA EE 6, que com certeza tornarão a parte visual de suas aplicações muito mais organizadas. Veja o quadro “Usando templates com facelets” para mais informações de como funcionam. Usando templates com facelets Usar templates é uma forma excelente de obter reutilização de camada visual, além de deixar o código muito mais intuitivo e organizado. Um template define um conteúdo HTML com várias funcionalidades comuns, com inserções em algumas partes de conteúdo HTML específico, que varia conforme a página sendo renderizada. Fazendo uma analogia com design patterns, isso se parece muito com o pattern template method, onde uma classe base define funcionalidade comum e chama métodos virtuais, a serem implementados diferentemente em cada classe específica. Quando definimos um template, usamos a tag ui:insert (ui refere-se ao namespace http://java.sun.com/jsf/facelets) para definirmos as regiões onde deve ser inserido conteúdo personalizado. Por exemplo, <ui:insert name="menu" /> define um ponto de inserção de conteúdo HTML referenciado pelo nome menu. 29/36 Para uma página facelet utilizar o template, é necessário definir qual template está sendo utlizado pela tag ui:composition. Por exemplo, <ui:composition template="template.xhtml"> define que a página utiliza o template “template.xhtml”. Uma vez definido o template, podem ser passados parâmetros para o mesmo, específicos da página em questão, com o uso da tag ui:param e cada região do template onde foi definido conteúdo personalizável (com ui:insert) deve ter o HTML definido pela tag ui:define. Dentro da tag ui:define, que define o conteúdo HTML a ser utilizado na região específica do template, é possível definir diretamente o HTML ou utilizar a tag ui:include, para incluir o conteúdo a partir de um arquivo externo. Conforme já explicado na seção “Visão de classes”, a especificação JSF assume o uso de managed beans, ou beans gerenciados pelo container, para integrar a camada de apresentação com a camada de negócio. Os managed beans devem conter a lógica de apresentação da aplicação e são acessados via expressões EL (Expression Language) pelo JSF. Perceba que tanto no arquivo home.jsp mostrado na listagem 16 quanto no arquivo home.xhtml mostrado na listagem 21, um managedBean referenciado com o nome conversorMoedaCliente é acessado por uma expressão EL: “#{conversorMoedaCliente.valorOriginal}”. Na versão JAVA EE 5, o managed bean é configurado pelo arquivo faces-config.xml exibido na listagem 23. Note no início do arquivo a especificação de versão: xsi:schemaLocation=”http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/webfacesconfig_1_2.xsd” version="1.2" Nesse arquivo de configuração é configurado o nome e caminho do arquivo .properties que guardará as mensagens de erro da aplicação, o locale padrão e também o mapeamento da classe br.com.human.web.ConversorMoedaCliente para o nome conversorMoedaCliente. Através desse mapeamento, o container JAVA EE 5 sabe que, quando encontrar no arquivo JSF a expressão conversorMoedaCliente, deve acessar a classe ConversorMoedaCliente. Para poder acessar o atributo valorOriginal, o mesmo deve estar configurado também no faces-config.xml. A listagem 25 mostra a classe ConversorMoedaCliente versão JAVA EE 5. Já na listagem 24 temos o faces-config.xml da aplicação migrada. Observe que a especificação de versão muda: xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd" version="2.0". Se a versão estiver especificada estiver incorreta, o container irá ignorar as anotações do managed bean e as novidades da versão nova. Nesse arquivo continuam as configurações de resource bundle e locale, mas as configurações de mapeamento passam a ser feitas por anotações, conforme mostrado na listagem 26, que mostra a classe ConversorMoedaCliente de acordo com a especificação JAVA EE 6. O mapeamento da classe com o nome “conversorMoedaCliente” é feito pela anotação @ManagedBean. O nome conversorMoedaCliente é o nome default para o qual a classe é mapeada, ou seja, o mesmo nome da classe com a primeira letra em minúsculo. Assim como acontece com as servlets, na versão JAVA EE 6 deixa de ser necessário instanciar o EJB manualmente, como mostrado na listagem 25, que cria o InitialContext para chamar o EJB, bastando injetar o mesmo com o uso da anotação @EJB (listagem 26). Listagem 23. faces-config.xml da aplicação JAVA EE 5 <?xml version='1.0' encoding='UTF-8'?> <faces-config xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd" version="1.2"> <application> 30/36 <resource-bundle> <base-name>br.com.human.web.ApplicationMessages</base-name> <var>ErrMsg</var> </resource-bundle> <locale-config> <default-locale>pt</default-locale> <supported-locale>en</supported-locale> </locale-config> </application> <managed-bean> <managed-bean-name>conversorMoedaCliente</managed-bean-name> <managed-bean-class>br.com.human.web.ConversorMoedaCliente</managedbean-class> <managed-bean-scope>session</managed-bean-scope> <managed-property> <property-name>valorOriginal</property-name> <property-class>java.lang.Double</property-class> <value>0.0</value> </managed-property> <managed-property> <property-name>valorConvertido</property-name> <property-class>java.lang.Double</property-class> <value>0.0</value> </managed-property> <managed-property> <property-name>moedaOri</property-name> <property-class>java.lang.String</property-class> <value>USD</value> </managed-property> <managed-property> <property-name>moedaDest</property-name> <property-class>java.lang.String</property-class> <value>BRL</value> </managed-property> </managed-bean> <managed-bean> <managed-bean-name>moedaCliente</managed-bean-name> <managed-bean-class>br.com.human.web.MoedaCliente</managed-beanclass> <managed-bean-scope>request</managed-bean-scope> <managed-property> <property-name>id</property-name> <property-class>java.lang.Integer</property-class> <value>#{param.id}</value> </managed-property> <managed-property> <property-name>moeda</property-name> <property-class>java.lang.String</property-class> <null-value /> </managed-property> <managed-property> <property-name>taxa</property-name> <property-class>java.lang.Double</property-class> <null-value /> </managed-property> </managed-bean> <navigation-rule> <from-view-id>*</from-view-id> <navigation-case> <from-outcome>lista</from-outcome> <to-view-id>/Java EE 5/lista.jsp</to-view-id> 31/36 </navigation-case> </navigation-rule> <navigation-rule> <from-view-id>*</from-view-id> <navigation-case> <from-outcome>editar</from-outcome> <to-view-id>/Java EE 5/editar.jsp</to-view-id> </navigation-case> </navigation-rule> </faces-config> Listagem 24. faces-config.xml da aplicação JAVA EE 6 <?xml version='1.0' encoding='UTF-8'?> <faces-config xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd" version="2.0"> <application> <resource-bundle> <base-name>br.com.human.web.ApplicationMessages</base-name> <var>ErrMsg</var> </resource-bundle> <locale-config> <default-locale>pt</default-locale> <supported-locale>en</supported-locale> </locale-config> </application> </faces-config> CDI Perceba que não é necessário configurar o managed bean por XML na versão 6. Existem inúmeras novidades na versão 6 da API com relação aos managed beans, como o CDI – Context Dependency Injection. O conceito de CDI já foi apresentado em detalhes nas edições 84 e 85 da revista e foge do escopo desse artigo, portanto não será apresentado em detalhes aqui, mas para ter uma idéia da flexibilidade trazida, a injeção por contexto permitiria, por exemplo, ter mais de uma implementação de um bean e injetar uma implementação diferente dependendo do contexto. Por exemplo, para nosso caso poderíamos ter duas implementações para o bean conversorMoedaCliente, uma que convertesse de real para dólar e outra que convertesse de dólar para real. Poderiamos então definir dois contextos diferentes, como por exemplo “nativo” ou “estrangeiro”, usando os chamados qualificadores, que fazem parte da especificação CDI. Para usar a implementação que converte de dólar para real, usaríamos na classe cliente uma injeção do tipo @Inject @Nativo ConversorMoedaCliente conversorMoedaCliente. Para usar a outra implementação, usaríamos @Inject @Estrangeiro ConversorMoedaCliente conversorMoedaCliente. Listagem 25. ConversorMoedaCliente – managed bean da aplicação versão JAVA EE package br.com.human.web; import java.util.ArrayList; import java.util.List; 5 32/36 import javax.faces.model.SelectItem; import javax.naming.InitialContext; import br.com.human.ejb.ConversorMoeda; import br.com.human.modelo.Taxa; public class ConversorMoedaCliente { private Double valorConvertido; private Double valorOriginal; private String moedaOri; private String moedaDest; private ConversorMoeda conversor; public ConversorMoedaCliente() { } public Double getValorConvertido() { return valorConvertido; } public void setValorConvertido(Double valorConvertido) { this.valorConvertido = valorConvertido; if (valorConvertido != null) { try { InitialContext ic = new InitialContext(); conversor = (ConversorMoeda) ic.lookup(ConversorMoeda.class .getName()); this.valorOriginal = conversor.converter(moedaDest, moedaOri, valorConvertido); conversor = null; } catch (Exception ex) { ex.printStackTrace(); } } } public Double getValorOriginal() { return valorOriginal; } public void setValorOriginal(Double valorOriginal) { this.valorOriginal = valorOriginal; if (valorOriginal != null) { try { InitialContext ic = new InitialContext(); conversor = (ConversorMoeda) ic.lookup(ConversorMoeda.class .getName()); this.valorConvertido = conversor.converter(moedaOri, moedaDest, valorOriginal); conversor = null; } catch (Exception ex) { ex.printStackTrace(); } } } public List<SelectItem> getListaMoedas() { 33/36 } List<SelectItem> result = null; try { InitialContext ic = new InitialContext(); conversor = (ConversorMoeda) ic.lookup(ConversorMoeda.class .getName()); result = new ArrayList<SelectItem>(); for (Taxa t : conversor.getListaTaxas()) { SelectItem i = new SelectItem(t.getMoeda()); result.add(i); } conversor = null; } catch (Exception ex) { ex.printStackTrace(); } return result; public Taxa[] listaTaxas = new Taxa[1]; public List<Taxa> getListaTaxas() { List<Taxa> result = null; try { InitialContext ic = new InitialContext(); conversor = (ConversorMoeda) ic.lookup(ConversorMoeda.class .getName()); result = conversor.getListaTaxas(); conversor = null; // return (Taxa[]) result.toArray(); return result; } catch (Exception ex) { ex.printStackTrace(); } return null; } public String getMoedaOri() { return moedaOri; } public void setMoedaOri(String moedaOri) { this.moedaOri = moedaOri; // Faz refresh no valor setValorOriginal(getValorOriginal()); } public String getMoedaDest() { return moedaDest; } public void setMoedaDest(String moedaDest) { this.moedaDest = moedaDest; // Faz refresh no valor setValorOriginal(getValorOriginal()); } } Listagem 26. ConversorMoedaCliente – managed bean migrado package br.com.human.web; 34/36 import java.util.ArrayList; import java.util.List; import import import import import javax.ejb.EJB; javax.faces.bean.ManagedBean; javax.faces.bean.ManagedProperty; javax.faces.bean.SessionScoped; javax.faces.model.SelectItem; import br.com.human.ejb.ConversorMoedaBean; import br.com.human.modelo.Taxa; @ManagedBean @SessionScoped public class ConversorMoedaCliente { @ManagedProperty(value = "USD") private String moedaOri; @ManagedProperty(value = "BRL") private String moedaDest; @ManagedProperty(value = "0.0") private Double valorConvertido; @ManagedProperty(value = "0.0") private Double valorOriginal; @EJB private ConversorMoedaBean conversor; public ConversorMoedaCliente() { } public Double getValorConvertido() { return valorConvertido; } public void setValorConvertido(Double valorConvertido) { this.valorConvertido = valorConvertido; if (valorConvertido != null) { try { this.valorOriginal = conversor.converter(moedaDest, moedaOri, valorConvertido); } catch (Exception ex) { ex.printStackTrace(); } } } public Double getValorOriginal() { return valorOriginal; } public void setValorOriginal(Double valorOriginal) { this.valorOriginal = valorOriginal; if (valorOriginal != null) { try { this.valorConvertido = conversor.converter(moedaOri, moedaDest, valorOriginal); } catch (Exception ex) { ex.printStackTrace(); } } 35/36 } public List<SelectItem> getListaMoedas() { List<SelectItem> result = null; try { result = new ArrayList<SelectItem>(); for (Taxa t : conversor.getListaTaxas()) { SelectItem i = new SelectItem(t.getMoeda()); result.add(i); } } catch (Exception ex) { ex.printStackTrace(); } return result; } public Taxa[] listaTaxas = new Taxa[1]; public List<Taxa> getListaTaxas() { List<Taxa> result = null; try { result = conversor.getListaTaxas(); // return (Taxa[]) result.toArray(); return result; } catch (Exception ex) { ex.printStackTrace(); } return null; } public String getMoedaOri() { return moedaOri; } public void setMoedaOri(String moedaOri) { this.moedaOri = moedaOri; // Faz refresh no valor setValorOriginal(getValorOriginal()); } public String getMoedaDest() { return moedaDest; } public void setMoedaDest(String moedaDest) { this.moedaDest = moedaDest; // Faz refresh no valor setValorOriginal(getValorOriginal()); } } Conclusões O que foi mostrado nesse artigo foi apenas uma pequena ponta de um enorme iceberg de mudanças trazidas pelo JAVA EE 6. Contudo, espera-se que depois de ler esse artigo inteiro o leitor tenha uma visão muito mais elaborada do que ficou diferente do JAVA EE 5 para o JAVA EE 6 e de como pensar no estilo da nova versão. 36/36 Depois que ter conhecido na prática como fazer no estilo do JAVA EE 6 aquilo com o que se está acostumado a fazer no estilo JAVA EE 5, fica mais fácil ter uma visão das diferenças entre cada especificação e de quais partes da nova especificação são melhores para cada projeto. Links http://download.oracle.com/javaee/5/tutorial/doc/ JAVA EE tutorial da Sun/Oracle – versão 5 http://download.oracle.com/javaee/6/tutorial/doc/ JAVA EE tutorial da Sun/Oracle – versão 6 Marcelo Elias Del Valle ([email protected] / http://www.mvalle.com) é arquiteto de TI e trabalha com Java desde 1998 nas áreas bancária, Telecom e de pesquisa. É nerd por opção e adora desenvolvimento de jogos.