Uma abordagem comparativa de aplicaes Web em Java orientada ao reuso Flvio Gomes da Silva Lisboa
com a colaborao de Luciana Campos Mota
Lisboa, Flvio Gomes da Silva Introduo ao Demoiselle Framework: Uma abordagem comparativa de Utilizao do padro MVC para o desenvolvimento de aplicaes Web em Java orientada ao reuso. Braslia. SERPRO, 2010. III Congresso Internacional Software Livre e Governo Eletrnico. 1.Framework 2.Java 3.Reuso. 4.Web
Dedicatria
Este livro dedicado a Cleonisse Cintra da Silva, para que seu nome nunca seja esquecido. Viveu pelos outros, cuidou de todos e nunca recebeu recompensa. Onde estiver agora, que tenha seu descanso merecido.
Sumrio
Dedicatria..............................................................................................................................3 Agradecimentos.......................................................................................................................8 Sobre o autor...........................................................................................................................9 Prefcio.................................................................................................................................10 Introduo.............................................................................................................................12 1.1 A Arte da Guerra no Desenvolvimento de Software..................................................13 1.2 Frameworks................................................................................................................15 1.2 O Desafio dos Frameworks........................................................................................17 A loja virtual de livros...........................................................................................................19 2.1 Levantamento de requisitos........................................................................................19 2.2 O Sonho de Nabucodonosor.......................................................................................24 Modelagem............................................................................................................................26 3.1 Descrevendo os Casos de Uso....................................................................................26 3.1.1 Caso de Uso Adicionar Item ao Carrinho...........................................................27 3.1.2 Caso de Uso Editar Item do Carrinho.................................................................28 3.1.3 Caso de Uso Excluir Item do Carrinho...............................................................28 3.1.4 Caso de Uso Buscar Livro..................................................................................29 3.1.5 Caso de Uso Fechar Pedido................................................................................29 3.1.6 Caso de Uso Cadastrar Cliente...........................................................................30 3.1.7 Caso de Uso Manter Cadastro............................................................................31 3.1.8 Caso de Uso Autenticar Usurio.........................................................................31 3.1.9 Caso de Uso Verificar Permisso de Funcionrio..............................................32 3.1.10 Caso de Uso Gerenciar Carrinho......................................................................33 3.1.11 Caso de Uso Gerar Relatrios...........................................................................33 3.1.12 Diagramas de Casos de Uso..............................................................................34 3.3 Diagrama Entidade Relacionamento..........................................................................35 3.4 Diagrama de Classes...................................................................................................36 3.5 A Implementao........................................................................................................36 Livraria com JSP e JDBC.....................................................................................................38 4.1 Servlets.......................................................................................................................38 4.2 JSP..............................................................................................................................43 4.3 Ambiente de desenvolvimento...................................................................................46 4.4 Configurando o servidor no Eclipse...........................................................................46 4.5 Criando um projeto Web............................................................................................50 4.6 Estrutura do projeto Web............................................................................................52 4.7 MVC...........................................................................................................................53 4.8 Camada de Persistncia..............................................................................................55 4.8.1 Criando os Modelos............................................................................................55 4.8.2. Criando os DAOs...............................................................................................72 4.8.3 Testando a Camada de Persistncia..................................................................108 4.8.4 Classes e Arquivos Auxiliares da Camada de Persistncia..............................111 4.9.Camada de Controle.................................................................................................112 4
4.9.1 Servlet de Autenticao....................................................................................115 4.9.2 Classe Abstrata para Servlets Autenticados......................................................118 4.9.3 Servlet de Gravao..........................................................................................119 4.9.3.1 Classe de Controle de Acesso...................................................................132 4.9.3.2 Constantes com os Papis.........................................................................134 4.9.3.3 Arquivo com os Papis dos Usurios........................................................134 4.9.3.4 Constantes com os Nomes dos Cadastros.................................................135 4.9.4 Servlet de Remoo..........................................................................................135 4.10 Camada de Viso....................................................................................................143 4.10.1 Pginas JSP.....................................................................................................143 4.10.2 Classes Auxiliares da Camada de Viso.........................................................157 4.11 Concluses..............................................................................................................183 Livraria com JSF e Hibernate.............................................................................................186 5.1 Hibernate..................................................................................................................186 5.2 JSF ...........................................................................................................................186 5.3 Configurao do Web Page Editor...........................................................................187 5.4 Criando um Projeto JavaServer Faces......................................................................188 5.5 Instalando e Usando JBoss Tools para Configurar o Hibernate...............................192 5.6 Camada de Persistncia............................................................................................198 5.6.1 Criando Modelos com Anotaes.....................................................................198 5.6.2 Criando DAOs sem JDBC................................................................................218 5.6.3 Testando a Camada de Persistncia..................................................................235 5.6.4 Classes e Arquivos Auxiliares da Camada de Persistncia..............................236 5.7 Camada de Controle.................................................................................................237 5.7.1 Generalizao....................................................................................................237 5.7.2 ManagedBeans..................................................................................................244 5.7.3 Registro no faces-config.xml............................................................................262 5.7.4 Classes Auxiliares da Camada de Controle......................................................265 5.8 Camada de Viso......................................................................................................268 5.8.1 Criando as Pginas JSF.....................................................................................268 5.8.2 Pginas de Cadastro..........................................................................................273 5.8.3 Pginas de Edio.............................................................................................279 5.8.4 Pginas de Listagem.........................................................................................291 5.9 Executando a Aplicao...........................................................................................305 5.10 Concluses..............................................................................................................306 Livraria com Demoiselle Framework.................................................................................308 6.1 Demoiselle Framework............................................................................................308 6.2 Demoiselle Infra.......................................................................................................309 6.3 Criao do Projeto Demoiselle Framework.............................................................309 6.4 Camada de Persistncia............................................................................................312 6.4.1 Criando os Modelos .........................................................................................312 6.4.2 Criando os DAOs..............................................................................................315 6.4.3 Testando a Camada de Persistncia..................................................................316 6.4.4 Classes e Arquivos Auxilares da Camada de Persistncia...............................317 6.5 Camada de Negcio..................................................................................................321 5
6.5.1 Controle de Acesso...........................................................................................322 6.6 Camada de Controle e Viso....................................................................................323 6.6.1 Autenticao.....................................................................................................326 6.7 Concluso.................................................................................................................326 Demoiselle: Framework de Arquitetura..............................................................................328 7.1 Fundamentos.............................................................................................................328 7.1.1 Diversidade.......................................................................................................328 7.1.2 Elementos de deciso........................................................................................329 7.1.3 Estratgia para a rea de tecnologia..................................................................329 7.1.4 Diretivas da arquitetura tecnolgica.................................................................331 7.1.4.1 Computao distribuda............................................................................331 7.1.4.2 Aplicaes baseadas em componentes......................................................332 7.1.4.3 Processos orientados a eventos.................................................................333 7.1.4.4 Acoplamento fraco de funes de negcio...............................................333 7.1.4.5 Infraestrutura para suporte a decises.......................................................333 7.1.4.6 Automao de processos...........................................................................334 7.1.4.7 Acesso por Internet...................................................................................334 7.1.4.8 Software Livre...........................................................................................335 7.1.5 Demoiselle com Desenvolvimento Colaborativo............................................335 7.1.6 Orientao a Especificaes.............................................................................338 7.1.6.1 JAAS.........................................................................................................339 7.1.6.2 JCA e JCE.................................................................................................340 7.1.6.3 Servlet.......................................................................................................341 7.1.6.4 JSF.............................................................................................................342 7.2 Estrutura do Projeto Demoiselle...............................................................................343 7.3 Arquitetura................................................................................................................346 7.3.1 Objetivos e Restries Arquiteturais ...............................................................348 7.3.1.1 Extensibilidade..........................................................................................348 7.3.1.2 Reusabilidade............................................................................................349 7.3.1.3 Manutenibilidade......................................................................................350 7.3.1.4 Desempenho..............................................................................................350 7.3.1.5 Estabilidade e Confiabilidade...................................................................350 7.3.2 Perspectiva Estrutural da Soluo.....................................................................351 7.3.2.1 Mdulo Core.............................................................................................351 7.3.2.1.1 Integrao entre Camadas.................................................................351 7.3.2.1.2 Contexto de Mensagens.....................................................................354 7.3.2.1.3 Exceo Padronizada.........................................................................355 7.3.2.1.4 Contexto de Segurana......................................................................356 7.3.2.1.5 Entidades...........................................................................................357 7.3.2.1.6 Contexto de Transao......................................................................357 7.3.2.1.7 Acionadores.......................................................................................358 7.3.2.1.8 Localizador de Contextos..................................................................359 7.3.2.2 Mdulo Util...............................................................................................360 7.3.2.2.1 Carregamento de Configurao.........................................................360 7.3.2.2.2 Paginao de Dados...........................................................................362 6
7.3.2.3 Mdulo Web.............................................................................................363 7.3.2.3.1 Contexto de Segurana......................................................................364 7.3.2.3.2 Contexto de Mensagens.....................................................................364 7.3.2.3.3 Integrao entre Camadas.................................................................365 7.3.2.3.4 Contexto de Transao......................................................................367 7.3.2.3.5 Inicializao do Ambiente.................................................................368 7.3.2.3.6 Redirecionamento Baseado em URL................................................369 7.3.2.3.7 Injeo de Dependncias com Aspectos...........................................372 7.3.2.4 Mdulo Persistence...................................................................................375 7.3.2.5 Mdulo View............................................................................................384 7.3.2.3 Modelo de Arquitetura em Camadas do Demoiselle................................386 7.4 Concluso ................................................................................................................389 Demoiselle Wizard..............................................................................................................391 Script para criao do banco de dados................................................................................392 Script para apagar as tabelas...............................................................................................399 Driver JDBC para PostgreSQL...........................................................................................400 Referncias Bibliogrficas..................................................................................................401
Agradecimentos
O primeiro agradecimento para Deus, que criou o Universo. Sem o Universo seria complicado escrever o livro. O segundo agradecimento para Jos Maria Leocdio, coordenador da CETEC, que colocou este livro como meta para 2010. O terceiro agradecimento para Antonio Carlos Tiboni, que acreditou neste livro. Os demais agradecimentos so para os funcionrios dos Centros de Documentao e Informao do Serpro, que forneceram a bibliografia necessria para a redao do livro e renovaram os livros com prestatividade at que o trabalho estivesse concludo:
Jussara Justino Jos Fonseca Lisania Medianeira Mathias Costas Mrcia Zago Marina Costacurta Falavinha Marilza Fernandes Trindade Salvador Fernandes de Azevedo
Sobre o autor
Flvio Gomes da Silva Lisboa bacharel em Cincia da Computao com ps-graduao em Aplicaes Corporativas usando Orientao a Objetos e Tecnologia Java pela Universidade Tecnolgica Federal do Paran. Atuou como programador em empresas privadas de automao comercial e servios de informtica, e foi funcionrio de carreira do Banco do Brasil, onde chegou a analista na diretoria internacional. Atualmente analista de desenvolvimento de sistemas da Coordenao Estratgica de Tecnologia do Servio Federal de Processamento de Dados (Serpro). membro do time oficial de traduo do Zend Framework. Tem experincia na rea de Cincia da Computao, com nfase em Software Livre, atuando principalmente nos seguintes temas: Java, PHP, padres, frameworks, MVC e objetos.
Prefcio
Este livro traz uma introduo ao Demoiselle Framework. Ele no pressupe um domnio de JEE, e tenta explicar de maneira prtica qual a necessidade do uso de frameworks para desenvolvimento. O principal objetivo deste livro ensinar a criar aplicaes com uma arquitetura que permita o reuso de componentes e facilite sua manuteno. Iremos progredir de uma programao com preocupao mais imediatista at uma que seja orientada a resolver problemas mais genricos. Para tanto, utilizaremos uma abstrao de um problema real. A soluo do problema ter trs implementaes. A primeira ser feita com Java puro, sem o auxlio de frameworks. A segunda utilizar frameworks JSF e Hibernate para a persistncia de dados. A terceira utilizar o framework Demoiselle. A inteno aprender como desacoplar produtos especficos da aplicao por meio de uma camada de alto nvel baseada em especificaes. Essa ltima frase complexa significa o seguinte: como conseguir reaproveitar cdigo entre aplicaes e gastar menos, ao evitar modificaes no cdigo fonte. A metodologia deste deste livro consiste em: 1. Desenvolver uma aplicao (ou mais precisamente uma parte dela) de duas formas diferentes, e compar-las de modo a perceber quais as vantagens e desvantagens que cada uma apresenta. 2. Mostrar o caminho para criar a mesma aplicao usando Demoiselle Framework. Ou seja, este livro o pr-Demoiselle Framework. No objetivo aqui oferecer uma referncia completa sobre o Demoiselle Framework, mas sim dar ao programador a base para que ele comece a utilizar a plataforma. O maior propsito deste livro incluir o leitor no time dos capacitados a iniciar a utilizao do Demoiselle Framework. Antes de partir para a apresentao do problema, iremos fazer uma pequena introduo ao assunto que justifica esse livro, a manuteno de software. Os demais conceitos necessrios sero passados no momento em que eles forem utilizados na codificao. Este livro se baseia na verso 5 de Java, o que no impede que o cdigo aqui exposto funcione para verses superiores, apenas o requisito mnimo. Este livro tambm pressupe conhecimentos bsicos de linguagem Java, orientao a objetos e HTML. Ele
10
contm o necessrio para programadores que no conhecem Servlets, JSP, JSF, Hibernate e muito menos Demoiselle Framework.
11
Introduo
No vale a pena criar um projeto rgido e difcil de manter em um esforo de economizar tempo e dinheiro no incio, se voc continuamente for forado a reservar recursos de desenvolvimento para manter o projeto no futuro. Duane Fields e Mark Kolb.
Neste captulo, introduziremos o problema da manuteno dentro do contexto do desenvolvimento de software. Em seguida, falaremos como o uso de frameworks pode resolver esse problema e, finalmente, como frameworks podem se tornar um problema. Nos primrdios da computao eletrnica, a palavra software referia-se somente a uma sequncia de instrues a serem seguidas e/ou executadas por uma mquina denominada hardware (o computador propriamente dito), na manipulao, redirecionamento ou modificao de um dado/informao ou acontecimento. Mas esse conceito sofreu uma modificao, pois a crescente indstria de software verificou que seu produto final no estava acompanhando a evoluo do hardware. Hoje, o termo software abrange um conjunto de produtos desenvolvidos durante um processo contnuo de construo, que inclui no somente o programa de computador, mas tambm manuais, especificaes, planos de teste e quaisquer outras formas de documentao referentes ao projeto de software. Segundo McConnel(2005), o desenvolvimento de software de computador pode ser considerado um processo complexo; nos ltimos 25 anos, pesquisadores da rea identificaram diversas atividades distintas relacionadas a essa prtica. Entre elas, esto:
Definio do problema; Desenvolvimento dos requisitos; Planejamento da construo; Arquitetura do software ou projeto de alto nvel; Projeto detalhado; Codificao e depurao; Testes unitrios; Testes de integrao; Integrao;
12
Alm disso, Grohs(2007, p.6) coloca mais quatro questes especficas, dentro do contexto de sistemas multiplataformas e baseados em software livre, que hoje possuem uma grande base instalada: 1. Como aumentar a complexidade das solues, sem deix-las difceis de serem mantidas no futuro (por demandas corretivas ou evolutivas)? 2. Como desenvolver sistemas multiplataformas baseados em software livre? 3. Como controlar os dados (sem abrir mo de eficincia) que vem aumentando em termos de complexidade de estrutura e em termos de volume? 4. Como atender necessidade de aumento de complexidade nos dados sem tornar a interface de entrada dos mesmos mais complexa? As respostas a essas questes precisam ser dadas em um tempo realizvel para a empresa e aceitvel pelo cliente. necessrio dar conta de todas as atividades pertinentes ao processo de desenvolvimento de software no menor tempo possvel, dado que a demanda por implementaes e mudanas s tende a crescer (a ltima mais que a primeira). Alguns defendem que a criao de software uma arte, como Donald Knuth, na obra The Art of Computer Programming. claro que a acepo de software dele limitada, refere-se mais a um contexto matemtico dominante nos primrdios da programao de computadores. O conceito de software, aps Pressman (2006), de um produto de engenharia. Se existe algo de arte no software, podemos dizer que uma Arte da Guerra.
13
Projetos tm de ser os mais curtos possveis, porque s assim sero realizveis. Da mesma forma que na guerra, quanto mais tempo os desenvolvedores demorarem para terminar o projeto, menos eles acreditaro que conseguiro faz-lo. O oramento tambm limitado, pelo cliente, e pelo capital alocado antes que o mesmo efetue o pagamento pelo servio. O tempo no reutilizvel. Depois que ele passou, o que foi consumido em recursos j se perdeu. No como no seriado Brimstone, onde o detetive Ezekiel Stone acorda toda manh com os mesmos 36 dlares e 27 centavos que tinha quando morreu. O conhecedor dos terrveis e devastadores efeitos e dos perigos de empreender uma guerra, est profundamente consciente de como aproveit-la da melhor maneira e lev-la com rapidez a seu trmino. Da mesma forma, o desenvolvedor de software deve utilizar os meios necessrios para obter um rpido desenvolvimento. Porque assim como pode haver uma nova guerra, pode haver um novo projeto. E da mesma forma que um exrcito pode se ver confrontado por dois oponentes, uma equipe de desenvolvimento pode se ver dividida em dois projetos. Ou mais. Quando a Primeira Guerra Mundial comeou, a Alemanha tinha um grave problema. O falecido chanceler Bismarck havia tentado por todos os meios tecer uma rede de alianas que favorecesse o Imprio Alemo no caso de um conflito armado no continente, mas teve que assistir decepcionado aliana entre a repblica da Frana e o Imprio Russo. Isso deixava o exrcito alemo com a possibilidade de uma guerra em duas frentes. E o que os militares germnicos mais temiam terminou por ocorrer. Quando o Imprio Austro-Hngaro declarou guerra Srvia, culpando-a pelo assassinato do arquiduque Francisco Ferdinando, a Alemanha fez o mesmo, por ser aliada dos austracos. Mas a Rssia era aliada da Srvia, e assim declarou guerra ustria e Alemanha. E como a Frana era aliada da Rssia, declarou guerra Alemanha. Concluso: os alemes teriam que combater dois exrcitos ao mesmo tempo. O exrcito alemo tentou repetir o sucesso do general Helmuth Von Moltke, que em apenas quatro meses invadiu e derrotou a Frana em 1870. Os generais germnicos pensavam em derrotar rapidamente os franceses para poder voltar e se concentrar nos russos. Mas infelizmente, os britnicos vieram em auxlio dos franceses e atrasaram o avano alemo. Nesse nterim, o lado leste do Imprio Alemo foi atacado pelos russos. Isso obrigou os germnicos a deslocarem tropas que estavam planejadas para lutar na frente ocidental. A Alemanha acabou tendo que dividir constantemente seus recursos em duas frentes. Apesar da Rssia se render em 1917, no houve tempo para os alemes se reorganizarem, e a entrada dos norte-americanos no conflito nesse mesmo ano terminou por esgotar os recursos do Imprio Alemo. Longe de ser apenas um evento blico, a guerra em duas frentes uma realidade no desenvolvimento de software. E, assim como os alemes, os gerentes e lderes de
14
projeto tm de tentar evitar a sustentao simultnea de dois projetos por um tempo longo. Claro que aqui estamos tratando de projetos grandes, com escopos bem diferentes, os quais so anlogos a dois adversrios que atacam por lados diferentes. H a situao em que os projetos possuem uma certa convergncia, similaridade, o que pode ser metaforizado na situao blica de atacar dois exrcitos em uma s frente.
1.2 Frameworks
De acordo com Martin(1995), para conseguir um rpido desenvolvimento, novos sistemas devem ser construdos a partir de classes que j existam e que possam ser adaptadas s circunstncias. Um desenvolvimento baseado em repositrios necessrio com ferramentas automatizadas. Estas ferramentas devem tornar rpido e fcil o trabalho de se personalizar uma interface grfica padro. Sistemas devem ser construdos de forma que possam ser mudados rpida e facilmente, sem os problemas de manuteno do passado.
Em consonncia com essa afirmao, o principal propsito de um framework ajudar no processo de desenvolvimento de aplicaes. Ele permite que as aplicaes sejam desenvolvidas mais rapidamente e mais facilmente, e deve resultar em uma aplicao de qualidade superior. A figura 1 mostra uma hierarquia tpica de uma aplicao Java web baseada em frameworks. Segundo Fayad (1997), "framework um conjunto de classes que colaboram para realizar uma responsabilidade para um domnio de um subsistema da aplicao." A necessidade de construir software de forma cada vez mais gil e a exigncia da criao de produtos com mais qualidade fazem com que o processo de desenvolvimento de software seja apoiado pelo reuso de estruturas pr-existentes, por exemplo, frameworks. Vrios frameworks podem ser usados na construo de um nico aplicativo de software. Para facilitar a escolha e o uso de vrios frameworks durante o desenvolvimento de software, e garantir a integrao, evoluo e manuteno dos mesmos, Macias (2008) prope uma estrutura chamada Framework Integrador.
15
De acordo com Macias, um framework define parmetros de projeto; a forma como realizada a diviso em classes e objetos e suas responsabilidades; a maneira como colaboram entre si, e o fluxo de controle, ditando, assim, a arquitetura de software das aplicaes que os utilizam. Alm disso, fornece padres de projetos - estruturas essenciais para o desenvolvimento de projetos de aplicaes de software - uma espinha dorsal e o continente para os componentes criados nas aplicaes e que iro funcionar dentro delas. Como os frameworks absorvem as decises de projeto que so comuns ao seu domnio de aplicao, eles priorizam a reutilizao de projetos ao invs da reutilizao de cdigo, apesar de inclurem classes concretas que podem ser imediatamente utilizadas. O uso de frameworks e a reutilizao em nvel de projeto implicam em uma inverso de controle entre um cdigo especfico de aplicao e o software na qual ela se baseia. No caso de aplicaes que no usam frameworks e sim bibliotecas ou DLLs (Dynamiclink library), o cdigo principal construdo na aplicao que realiza chamadas ao trecho que se deseja utilizar. J no caso de aplicaes que utilizam frameworks, o trecho principal reutilizvel est nele, que chama o cdigo escrito pelo desenvolvedor, como mostrado na figura 2.1.
16
17
18
19
A concepo a fase na qual identificamos as necessidades do cliente e analisamos se o projeto vivel. Esta a hora certa para desistir de um projeto de software. Qualquer deciso posterior custar caro. A primeira coisa a ser feita identificar as pessoas envolvidas no projeto. Vamos supor que uma empresa de desenvolvimento chamada Laele contatada por uma livraria chamada Alexandria. Essa livraria nova, e tem dificuldade de concorrer com gigantes do mercado, por isso quer comercializar seus produtos pela Internet. Se o retorno for maior do que as vendas presenciais, o dono, seu Omar, ir fechar a loja fsica e trabalhar apenas virtualmente. O analista de negcios da Laele, Tio, faz uma entrevista com o dono da Alexandria e alguns de seus funcionrios. A partir da ele obtm uma lista simplificada de requisitos: 1. O sistema da livraria compreende duas interfaces: uma para o cliente e outra para o funcionrio da loja; 2. A interface do cliente a padro, e deve ser apresentada se nada em contrrio for especificado; 3. A interface do cliente dever apresentar na pgina inicial uma relao aleatria de livros; 4. Essa relao dever apresentar a imagem da capa do livro, o ttulo, o(s) autor(es), o preo e um boto para colocar o livro em um carrinho de compras. 5. O carrinho de compras criado quando o cliente seleciona o primeiro livro. Ele no precisa estar autenticado para fazer isso. 6. O cliente pode alterar a quantidade de exemplares de um livro selecionado. 7. O cliente pode excluir qualquer livro do carrinho, a qualquer momento, antes de finalizar a compra. 8. A pgina inicial tambm dever apresentar uma caixa de busca, que permita a recuperao de livros pelo ISBN, ttulo, autor e editora. 9. O resultado das buscas, se exceder um determinado limite de livros, dever ser distribudo em vrias pginas. O limite padro de livros a serem visualizados quatro. 10. A interface dever apresentar um boto para fechar o pedido de venda em todas as pginas, assim como um link para ver o contedo do carrinho de compras. O boto de fechamento do pedido dever levar o cliente para uma pgina onde ele
20
escolher a forma de pagamento, dentre quatro: depsito em conta, dbito automtico, carto de crdito e boleto bancrio. 11. Aps confirmar a forma de pagamento, o cliente dever obrigatoriamente se autenticar, se j no estiver autenticado. 12. A autenticao feita com o apelido ou CPF e senha do cliente. 13. O cliente pode se autenticar a qualquer momento. Para isso, o sistema dever apresentar um link chamado Conectar. Se o cliente estiver autenticado, dever ser apresentado em seu lugar um link chamado Desconectar. 14. Se o cliente no tiver cadastro, ele pode se cadastrar a qualquer momento. Para isso, deve haver um link chamado Cadastrar. Ele s pode estar disponvel se o cliente estiver desconectado. 15. O link Cadastrar apresentar um formulrio onde o cliente registrar seu CPF, um nome, um apelido, seu e-mail e uma senha de 8 nmeros ou letras. 16. Com a confirmao da compra pelo cliente, deve ser gerado um pedido de venda. 17. Com a gerao do pedido de venda, o carrinho de compras deve ser esvaziado. 18. A interface do funcionrio apresenta opes para incluir, alterar e excluir registros das tabelas do sistema. 19. O sistema possui controle de acesso, pois os funcionrios tm acesso privilgios limitados. e
20. H um funcionrio administrador, que tem acesso a tudo. O administrador quem define os acessos e privilgios. 21. A interface do funcionrio tambm gera um relatrio de pedidos de um determinado perodo (dia, ms, ano). Todos esses requisitos so funcionais, ou, de acordo com Sommerville (2007, p. 80), so requisitos que descrevem o que o sistema deve fazer. Para no tornar o exemplo complexo demais, descartaremos requisitos no funcionais.
21
Mas oportuno salientar a importncia de um bom levantamento de requisitos em um projeto de software. Segundo Blaschek (apud Fraga Filho e Reis, 2005, p. 2), os principais problemas do desenvolvimento de software esto associados aos requisitos. Isso ocorre porque falhas nos requisitos tem um impacto crescente nas demais fases de um processo de desenvolvimento. Um requisito incorreto produz uma anlise incorreta, que produz um projeto incorreto, que por sua vez produz uma implementao incorreta. E os testes sero realizados sobre falsas premissas, o que os tornar invlidos. Finalmente, o pior caso chegar-se concluso do projeto com um produto que no atende os requisitos. Segundo Pinho (2010), para minimizar os problemas relacionados ao levantamento de requisitos, vrias tcnicas foram desenvolvidas. A entrevista, usada no exemplo, uma tcnica tradicional, cuja eficincia depende da experincia do entrevistador e tem mais utilidade quando as partes interessadas tem muitos conhecimentos subjetivos (conceitos estritamente ligados ao seu negcio) e esto dispostos a serem entrevistados. A academia e o mercado de TI tm reconhecido a importncia da Engenharia de Requisitos, tanto que j existe grande literatura sobre o assunto, eventos especficos e produtos de software para auxiliar essa disciplina. O engenheiro de requisitos um profissional que precisa ter um conhecimento tcnico abrangente e ao mesmo tempo uma capacidade de relacionamento pessoal que ajude a extrair as informaes necessrias e relevantes para a elaborao de um projeto de software. A escolha das tcnicas mais adequadas de levantamento de requisitos para um projeto de suma importncia. Mas to importante conseguir estabelecer um bom entendimento com o cliente, para que ele tambm fique ciente do quo importante que tudo que ele deseja esteja claro para o fornecedor de software. Uma analogia exagerada de como pode se sentir um engenheiro de requisitos diante das incertezas do cliente pode ser encontrada na histria de Nabucodonosor, o fundador do segundo imprio babilnico, relatada na Bblia (Daniel 2:1-5). Ele teve um sonho estranho uma noite e acordou perturbado. Mandou chamar os sbios e magos e pediu que eles interpretassem o sonho. Eles perguntaram qual era o sonho, para poderem interpret-lo. O rei disse que no se lembrava, mas se eles no interpretassem o sonho, iria mandar mat-los. algo similar a mandar criar um sistema, mas no saber ao certo o que ele tem de fazer, e ainda ameaar o desenvolvedor com um processo judicial.
22
CAPTULO 3 Modelagem
(colaborao de Luciana Campos Mota)
Este captulo tratar da formalizao de requisitos e da modelagem do sistema de livraria virtual. A partir da lista de requisitos do captulo anterior, sero criados os casos uso e os diagramas de classe.
Uma hiptese inicial; Uma descrio passo a passo do fluxo normal de eventos; Uma descrio passo a passo do tratamento de erros; Uma descrio passo a passo de atividades alternativas; Qual o estado do sistema aps o trmino.
Esse contedo, com algumas variaes, pode ser encontrado em vrios autores de Engenharia de Software. Os casos de uso podem ser representados graficamente por diagramas de casos de uso. As interaes descritas nos casos de uso podem ser representadas por diagramas de sequncia.
23
Vamos criar os casos de uso de nosso sistema, a partir da lista inicial de requisitos. O objetivo
Hiptese inicial: O cliente est visualizando a pgina inicial do sistema, com a lista de livros. A lista pode ser aleatria ou resultado de uma busca; Fluxo normal de eventos: 1. O cliente clica no boto Comprar localizado abaixo da imagem do livro. 2. O livro adicionado ao carrinho de compras. 3. Ele direcionado para uma pgina que exibe o contedo do carrinho de compras, com a soma dos valores dos itens, um boto Fechar Pedido e um boto Continuar Comprando. 4. Ele clica no boto Continuar Comprando e redirecionado para a pgina inicial do sistema.
Tratamento de erros:
No caso de alguma pgina no ser encontrada, o cliente deve ser direcionado para uma pgina de erro padro, e a exceo deve ser gravada em log.
Atividades alternativas: 1. Quando estiver na pgina de contedo do carrinho, o cliente pode editar qualquer um dos itens, alterando sua quantidade - caso de uso 3.1.2 - ou removendo-o - caso de uso 3.1.3. 2. A pgina do carrinho tambm deve exibir a caixa de busca, permitindo que o usurio procure por outros livros sem ter de voltar para a pgina inicial.
Hiptese inicial: O cliente j est autenticado no sistema e clica sobre o carrinho de compras para modificar um item. Fluxo normal de eventos: 1. O cliente seleciona um item. 2. O cliente altera sua quantidade. 3. Ele clica no boto Continuar Comprando e redirecionado para a pgina inicial do sistema.
24
Tratamento de erros:
No caso de alguma pgina no ser encontrada, o cliente deve ser direcionado para uma pgina de erro padro, e a exceo deve ser gravada em log.
Atividades alternativas: 1. O cliente pode remover um item do carrinho caso de uso 3.1.3. 2. A pgina do carrinho tambm deve exibir a caixa de busca, permitindo que o usurio procure por outros livros sem ter de voltar para a pgina inicial.
Hiptese inicial: O cliente j est autenticado no sistema e clica sobre o carrinho de compras para excluir um item. Fluxo normal de eventos: 1. O cliente clica sobre o carrinho de compras. 2. O cliente exclui um item do carrinho.
3. Ele clica no boto Continuar Comprando e redirecionado para a pgina inicial do sistema.
Tratamento de erros:
No caso de alguma pgina no ser encontrada, o cliente deve ser direcionado para uma pgina de erro padro, e a exceo deve ser gravada em log.
2. A pgina do carrinho tambm deve exibir a caixa de busca, permitindo que o usurio procure por outros livros sem ter de voltar para a pgina inicial.
Hiptese inicial: O cliente est na pgina inicial, preenche a caixa de texto de busca, seleciona um critrio de busca e clica sobre o boto Buscar. Fluxo normal de eventos: 1. O cliente preenche a caixa de busca com as informaes do livro.
25
2. O cliente seleciona o critrio de busca desejado (nome do livro, nome do autor ou ISBN) 3. O cliente clica no boto Buscar Livro. 4. O sistema apresenta uma pgina com os livros que atendem ao critrio selecionado.
Tratamento de erros:
No caso de alguma pgina no ser encontrada, ou houver algum problema de conexo com o banco de dados, o cliente deve ser direcionado para uma pgina de erro padro, e a exceo deve ser gravada em log.
Hiptese inicial: O cliente est na pgina do carrinho e clica sobre o boto Fechar Pedido. Fluxo normal de eventos: 1. O carrinho de compras travado. Se o cliente abrir o site do sistema por meio de uma outra aba ou um outro navegador, no conseguir adicionar mais itens. 2. O cliente direcionado para uma pgina onde escolhe uma forma de pagamento dentre quatro: depsito em conta, dbito automtico, carto de crdito e boleto bancrio. 3. Aps escolher a forma de pagamento, o cliente confirma o pedido. 4. O sistema grava o pedido de venda e informa o nmero do pedido. O cliente pode voltar a tela inicial do sistema.
Tratamento de erros:
No caso de alguma pgina no ser encontrada, ou houver algum problema de conexo com o banco de dados, o cliente deve ser direcionado para uma pgina de erro padro, e a exceo deve ser gravada em log.
Atividades alternativas: No h.
26
Hiptese inicial: O cliente clicou no boto Cadastrar na pgina inicial. Fluxo normal de eventos: 1. O sistema exibe um formulrio pedindo nome, apelido, e-mail e senha. 2. O cliente preenche os dados e confirma a gravao. 3. O sistema grava os dados no banco. 4. O cliente redirecionado para a pgina onde se encontrava anteriormente.
Tratamento de erros:
No caso de alguma pgina no ser encontrada, ou houver algum problema de conexo com o banco de dados, o cliente deve ser direcionado para uma pgina de erro padro, e a exceo deve ser gravada em log.
Atividades alternativas:
A partir de 2, se os dados no forem validados, o formulrio pode ser exibido novamente com mensagem indicando quais so os problemas.
Hiptese inicial: Um funcionrio se autenticou no mdulo administrativo do sistema e deseja realizar uma operao em um cadastro. Fluxo normal de eventos: 1. O funcionrio seleciona um cadastro. 2. O funcionrio edita o registro. 3. O funcionrio grava as alteraes. 5. O funcionrio volta para o menu principal do sistema.
Tratamento de erros:
No caso de alguma pgina no ser encontrada, ou houver algum problema de conexo com o banco de dados, o funcionrio deve ser direcionado para uma pgina de erro padro, e a exceo deve ser gravada em log.
Atividades alternativas:
27
A partir de 1, o funcionrio pode procurar um registro. A partir de 2, o funcionrio pode incluir um registro. A partir de 2, o funcionrio pode remover um registro. A partir de 2, o funcionrio pode pesquisar um registro. A realizao de qualquer operao depende de permisso.
Hiptese inicial: O usurio est na pgina de autenticao. Fluxo normal de eventos: 1. O usurio preenche os dados de login (apelido e senha). 2. O usurio clica sobre o boto Autenticar. 3. O sistema valida os dados. 4. O usurio direcionado para a tela correspondente dependendo de seu perfil (cliente, funcionrio).
Tratamento de erros:
No caso de alguma pgina no ser encontrada, ou houver algum problema de conexo com o banco de dados, o funcionrio deve ser direcionado para uma pgina de erro padro, e a exceo deve ser gravada em log.
Atividades alternativas:
A partir de 3, se os dados no forem vlidos, o funcionrio ser redirecionado para o formulrio de autenticao.
Hiptese inicial: O funcionrio solicita acesso a um recurso do mdulo administrativo. Fluxo normal de eventos: 1. O sistema verifica as permisses do funcionrio. 2. O sistema direciona o funcionrio para a pgina responsvel pela interao com o recurso solicitado e registra o acesso no log.
28
Tratamento de erros:
No caso de alguma pgina no ser encontrada, ou houver algum problema de conexo com o banco de dados, o funcionrio deve ser direcionado para uma pgina de erro padro, e a exceo deve ser gravada em log.
Atividades alternativas:
A partir de 1, se o funcionrio no tiver permisso para manipular o recurso, ele ser redirecionado para a pgina do menu principal, com mensagem avisando que ele no tem permisso. A tentativa acessar um recurso no permitido ser gravada em log.
Hiptese inicial: O usurio do tipo cliente j est autenticado no sistema e clica sobre o carrinho de compras. Fluxo normal de eventos: 1. O cliente verifica o contedo do carrinho. 2. Ele clica no boto Continuar Comprando e redirecionado para a pgina inicial do sistema.
Tratamento de erros:
No caso de alguma pgina no ser encontrada, o cliente deve ser direcionado para uma pgina de erro padro, e a exceo deve ser gravada em log.
Atividades alternativas: 1. O cliente pode executar o caso de uso 3.1.1 (Adicionar item ao carrinho) 2. O cliente pode executar o caso de uso 3.1.2 (Editar item do carrinho) 3. O cliente pode executar o caso de uso 3.1.3 (Excluir item do carrinho) 4. A pgina do carrinho tambm deve exibir a caixa de busca, permitindo que o usurio procure por outros livros sem ter de voltar para a pgina inicial.
Hiptese inicial: O usurio (do tipo funcionrio) deseja gerar um relatrio de pedidos de determinado dia/ms/ano. Fluxo normal de eventos:
29
1. O usurio(funcionrio) se autentica no sistema. 2. No mdulo administrativo, ele clica no boto Relatrios. 3. Na pgina de relatrios, ser solicitado para que ele indique o perodo desejado, data incio e data fim. 4. Aps informar o perodo, ele clica em Gerar Relatrio. 5. O relatrio ser gerado e impresso na tela.
Tratamento de erros:
No caso de alguma pgina no ser encontrada, o cliente deve ser direcionado para uma pgina de erro padro, e a exceo deve ser gravada em log.
30
31
32
3.5 A Implementao
A modelagem feita neste captulo teve por objetivo principal oferecer uma viso completa do sistema, mas no realizaremos sua implementao completa. Um motivo que iremos fazer trs implementaes diferentes, e desenvolver uma aplicao completa trs vezes tornaria este livro muito extenso. Outro motivo que o propsito deste livro to somente ensinar prticas de reuso que facilitem a manuteno de aplicaes Java Web, e no apresentar uma aplicao para ser vendida. A aplicao aqui o meio de ensino, e no o fim. Iremos nos restringir a alguns casos de uso que sero suficientes para realizar a comparao entre trs alternativas de desenvolvimento Java: sem frameworks, com JSF
33
e Hibernate e com Demoiselle. Desenvolver todos os casos de uso no acrescentaria em nada ao objetivo aqui almejado, apenas traria repeties desnecessrias. Por isso, nos captulos a seguir, sero implementados apenas os casos de uso Manter Cadastro, Autenticar Usurio e Verificar Permisso de Funcionrio, que compem grande parte da metade administrativa da aplicao.
34
4.1 Servlets
Servlets so aplicaes Java executadas em servidores web. Eles so consequncia de uma demanda por tecnologias de contedo dinmico. Nesta seo iremos apresentar o contexto da criao dos servlets. Um servidor web um dos terminais da arquitetura cliente-servidor proposta pelo protocolo HTTP. O protocolo HTTP roda no topo do TCP/IP e define uma comunicao entre um cliente e um servidor. Essa comunicao , a princpio, assncrona. O cliente faz uma requisio ao servidor e este envia uma resposta. O cliente no precisa esperar pela resposta para enviar outra requisio. Por outro lado, o servidor no envia resposta alguma se no receber uma requisio. O objeto requisitado sempre um arquivo de texto. O protocolo TCP responsvel por garantir que o arquivo enviado de um n da rede para outro chegue ntegro ao seu destino, ainda que o arquivo esteja dividido em blocos no momento do envio (Basham, 2005, p. 6). J o protocolo IP se encarrega de sustentar a transferncia e roteamento dos blocos, ou pacotes, de um hospedeiro para outro at que eles cheguem ao seu destino. A estrutura bsica do protocolo HTTP atende bem ao servio de pginas estticas. Um computador que faz o papel de cliente envia uma requisio de um arquivo (geralmente uma pgina HTML) e o servidor web a devolve. Um software chamado navegador interpreta o contedo do arquivo e exibe algo interessante para o usurio final.
35
Esse servio de pginas estticas foi o princpio da utilizao da web, mas somente um conjunto de pginas HTML no permite criar uma aplicao de software, em que haja interao com o usurio final. Uma aplicao no deve somente prover texto prdefinido, mas processar dados fornecidos pelo usurio e reagir a eventos. Com o crescimento do uso comercial da Internet, surgiu a necessidade do servidor web fazer algum processamento adicional, de modo a prover contedo dinmico. Em vez de uma resposta fixa, um sistema web dinmico oferece respostas personalizadas, com base em parmetros e eventos disparados pela pgina. Alm disso, para que um conjunto de pginas web possa funcionar como uma aplicao, preciso de mecanismos que mantenham estados entre requisies. O protocolo HTTP no mantm estado, assim necessrio uma aplicao no servidor que consiga fazer isso. Segundo Fields e Kolb (2000, p. 3), o primeiro padro para contedo de web dinmico se baseava na Common Gateway Interface, ou CGI. O CGI especifica como servidores web passam informaes de requisies para programas externos. Esses programas externos, rodados pelo servidor, geram respostas em tempo de execuo. Para o cliente, no muda nada, ele continua recebendo uma pgina web. O que muda que no servidor essa pgina no existia originalmente (e no existir mais depois do envio da resposta). No CGI, ao receber uma requisio, o servidor web verifica se ela se refere a um arquivo esttico (pgina HTML) ou a um arquivo associado a uma aplicao. Essa associao deve estar configurada no servidor. Ao identificar que o arquivo solicitado deve ser processado por uma aplicao externa, o servidor gera um processo para executar a aplicao, e recebe a sada desta. Ao receb-la, ele simplesmente a envia para o cliente, como faria com o contedo de qualquer arquivo ordinrio gravado em disco. O problema do CGI que ele cria um novo processo para cada nova requisio, pois os programas de CGI rodam fora do software servidor da web. Isso torna sua aplicabilidade limitada para uso em grande escala de aplicaes baseadas na web. Em 1996, a Sun Introduziu os servlets. Servlets so aplicaes Java, como dissemos no incio da seo. A diferena entre eles e os programas tradicionais de CGI (incluindo programas Java padro), que todos os servlets associados com um servidor da Web rodam dentro de um nico processo. A JVM cria uma thread para cada requisio. Como as threads em Java tm muito menos overhead que os processos completos, e so executados dentro da memria do processador (j alocada pela JVM), a execuo de um servlet muito mais eficiente do que o processamento de um programa CGI.
36
Apresentaremos um pequeno exemplo de servlet que servir para explicar porque no o utilizamos diretamente, ou melhor, porque no utilizamos somente servlets para criar aplicaes web em Java. Para um servlet funcionar, ele precisa de um Container Servlet. Um Container Servlet um componente de software que pode ser executado como um processo especial do servidor web, dedicado a tratar da execuo de servlets, ou pode ser invocado diretamente pelo cliente. No primeiro caso, um servidor web pode ter um Container Servlet embutido ou pode direcionar as requisies para um Container externo. Para o nosso primeiro exemplo, no utilizaremos um servidor web, mas somente um Container Servlet. Nosso Container ser o Apache Tomcat, verso 6.x. Ele pode ser obtido em http://tomcat.apache.org. Baixe a distribuio binria e descompacte em um diretrio de sua preferncia. Para executar o Tomcat, entre no subdiretrio bin de sua instalao e execute o arquivo startup.bat (windows) ou startup.sh (linux). O Tomcat executado na porta 8080 de TCP, para evitar conflitos com servidores web padro que usam a porta 80. Para parar o Tomcat, basta executar o arquivo shutdown.sh (linux). shutdown.bat (windows) ou
Vamos criar um projeto de servlet. Escolha um local (qualquer um menos o diretrio do Tomcat) e crie uma pasta chamada ExemploServlet. Dentro dessa pasta crie trs subpastas: src, classes e etc. A pasta src conter os arquivos de cdigo-fonte (.java); A pasta classes conter os arquivos de bytecode (.class); A pasta etc conter a configurao dos servlets (arquivo web.xml). A estrutura de pastas ficar assim:
ExemploServlet classes etc src
37
public class AloMundoServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { PrintWriter out = response.getWriter(); out.println("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Strict//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">"); out.println("<html>"); out.println("<head>"); out.println("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">"); out.println("<title>Al Mundo</title>"); out.println("</head>"); out.println("<body>"); out.println("Al Mundo"); out.println("</body>"); out.println("</html>"); } }
O mtodo doGet executado em resposta a uma requisio HTTP GET. Nesse mtodo, criamos um objeto que encapsula a sada para o cliente. No diretrio etc, iremos criar o arquivo web.xml. Esse arquivo um deployment descriptor (DD), e cada aplicao web em Java precisa ter um. Um DD pode declarar vrios servlets (que compem a aplicao). O cdigo ficar assim:
<web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4"> <servlet> <servlet-name>AloMundo</servlet-name> <servlet-class>AloMundoServlet</servlet-class> </servlet>
38
A tag servlet-name um apelido que serve para fazer a ligao entre a url invocada e o servlet a ser executado. Essa estrutura permite que vrias requisies sejam direcionadas para um mesmo servlet. Compile o servlet com o seguinte comando:
javac -cp [diretrio do tomcat]/lib/servlet-api.jar -d classes
src/AloMundoServlet.java
No diretrio do Tomcat, h uma subpasta chamada webapps. Dentro dela, crie um diretrio com o nome da aplicao web. No nosso caso, ser exemploservlet. Dentro desse diretrio, crie o subdiretrio WEB-INF e copie o arquivo web.xml para dentro dele. Tambm crie dentro de WEB-INF o subdiretrio classes e copie para dentro dele o AloMundoServlet.class. Voc vai ter uma estrutura assim:
webapps exemploservlet WEB-INF web.xml classes AloMundoServlet.class
Inicie o Tomcat e digite no seu navegador a seguinte URL: http://localhost:8080/exemploservlet/alomundo Em comparao com uma linguagem de script, como ASP ou PHP, tivemos muito trabalho s para escrever Al Mundo na tela do navegador. Sem contar que voc pode ter visto um smbolo estranho no lugar da letra acentuada dependendo da codificao de seu navegador. O uso exclusivo de servlets para criar aplicaes web cria dificuldades de lidar com a camada de apresentao. Todo o texto HTML a ser enviado para o navegador teria de ser colocado como argumento do mtodo println. A formatao do HTML dentro de uma
39
String literal o torna ilegvel para o webdesigner. Sem contar que exige que ele conhea Java. claro que aqui fizemos tudo manualmente. O uso de um ambiente integrado de desenvolvimento facilitaria a tarefa, mas no resolveria o problema da mistura de cdigo HTML com Java.
4.2 JSP
De acordo com Fields e Kolb (2000, p. 9), a desvantagem potencial de usar servlets que todo o contedo de documentos, tanto esttico quanto dinmico, reside no cdigo fonte do programa. Como resultado, qualquer modificao em tal documento necessita de interveno de um programador. Para resolver este, problema, segundo Gonalves (2007, p. 115), a Sun Microsystems criou uma tecnologia baseada em servlets chamada JavaServer Pages (JSP), que permite separar o cdigo HTML de Java. Essa separao evita que o programador tenha de inserir muito cdigo para produzir apresentao e no obriga o webdesigner a conhecer (pelo menos no profundamente) a linguagem Java. Faremos uma rpida introduo ao JSP reescrevendo o exemplo da seo anterior. No nos deteremos aqui com muitas explicaes porque o intuito deste livro, citando Silveira et al (2005, p. 1) ensinar de maneira elegante, mostrando apenas o que necessrio no momento correto e poupando o leitor de assuntos que no costumam ser de seu interesse em determinadas fases do aprendizado. Vamos criar um projeto de servlet. Escolha um local (qualquer um menos o diretrio do Tomcat) e crie uma pasta chamada ExemploServlet. Dentro dessa pasta crie trs subpastas: src, classes e etc. A pasta src conter os arquivos de cdigo-fonte (.java); A pasta classes conter os arquivos de bytecode (.class); A pasta etc conter a configurao dos servlets (arquivo web.xml). A estrutura de pastas ficar assim:
ExemploJsp classes etc src
S que na raiz do projeto, criaremos um arquivo chamado index.jsp. Nesse arquivo teremos o seguinte cdigo:
<%@page language="java" contentType="text/html" pageEncoding="UTF-8"
40
import="java.util.*" import="java.text.SimpleDateFormat" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html> <head> <title>Al Mundo</title> </head> Hoje dia <%= (new SimpleDateFormat("dd/MM/yy")).format(new Date())%> <body> <form action="/exemplojsp/alomundo" method="get"> <input type="submit" value="Diga alguma coisa"/> </form> </body> </html>
Arquivos com extenso .jsp so interpretados pelo Container Java. No arquivo criado anteriormente, temos uma parte dinmica na pgina, a data. Ela gerada por duas classes Java, importadas no incio do arquivo. Alm disso, temos uma chamada a um Servlet por meio de uma requisio HTTP GET feita pelo formulrio HTML. Dentro da pasta src, crie a classe ControllerAloMundoServlet, com este cdigo:
import javax.servlet.*; import javax.servlet.http.*; import java.io.*;
public class ControllerAloMundoServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { PrintWriter out = response.getWriter(); out.println("Al Mundo"); }
41
Como pode perceber, esse Servlet idntico ao anterior, exceto pelo fato de que no envia as tags HTML que formam a pgina. Como no exemplo anterior, iremos criar o arquivo web.xml no diretrio etc. O cdigo este:
<web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4"> <servlet> <servlet-name>ControllerAloMundo</servlet-name> <servlet-class>ControllerAloMundoServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>ControllerAloMundo</servlet-name> <url-pattern>/alomundo</url-pattern> </servlet-mapping> </web-app>
src/ControllerAloMundoServlet.java
Crie um diretrio dentro da subpasta webapps do Tomcat com o nome de exemplojsp. Copie para dentro dele o arquivo index.jsp. Dentro de exemplojsp, crie o subdiretrio WEB-INF e copie o arquivo web.xml para dentro dele. Tambm crie dentro de WEB-INF o subdiretrio classes e copie para dentro dele o ControllerAloMundoServlet.class. Voc vai ter uma estrutura assim:
webapps exemplojsp index.jsp WEB-INF web.xml
42
classes ControllerAloMundoServlet.class
Inicie o Tomcat e digite no seu navegador a seguinte URL: http://localhost:8080/exemplojsp Apesar de simples, este exemplo mostra como JSP permite manter as vantagens obtidas pelos servlets em relao a programas CGI, ao mesmo tempo em que promove uma clara diviso do trabalho entre webdesigner e programador. Aps essa introduo a servlets e JSP, vamos dar incio a implementao do nosso projeto.
43
44
45
46
47
49
procurar um arquivo qualquer do projeto, inclusive oculto pelo sistema operacional, voc pode usar a combinao de teclas CTRL+SHIFT+R. Na viso Package Explorer, a pasta src equivale a Java Resources:src de Project Explorer.
4.7 MVC
O primeiro caso de uso que implementaremos ser o Manter Cadastro. O motivo muito simples, os demais dependem dele. No adianta autenticar um usurio se ele no tem nada para fazer. Tambm no possvel verificar permisso se no h recurso definido. A aplicao precisa de um ponto de partida. Por isso, vamos criar a pgina inicial do sistema. Ela ser gerada pelo arquivo index.jsp que ser criando dentro da pasta WebContent. Selecione essa pasta e acesse o menu File->New->Other. Procure o item Web e o abra. Dentro dele est o wizard para JSP, conforme a figura 14.
50
Ainda no nos preocupamos em definir uma arquitetura para a aplicao, mas teramos de ter feito isso antes de partir para a implementao. Antes de fazer algo mais substancial, vamos pensar na arquitetura. Neste momento, vamos introduzir um padro de projeto para dividir as responsabilidades de nossa aplicao em trs camadas: Modelo, Controlador e Viso. Esse o padro de projeto MVC, descrito por Fowler (2006, p. 315-317).
51
O Controlador ir se encarregar de capturar as requisies, invocar os Modelos e enviar a sada para a Viso. A Viso ir exibir os dados para o usurio. a camada onde esto os arquivos JSP.
Este padro de projeto um padro de alto nvel, ou seja, ele trata a aplicao como um todo. Com relao camada de Modelo, faz-se necessria ainda uma separao entre o armazenamento dos dados em memria para manipulao e as operaes de persistncia no banco de dados. Por isso, iremos utilizar, em nossa aplicao, uma variao do MVC. Em vez da camada de Modelo, utilizaremos uma camada de Persistncia. Essa, por sua vez, ser subdividida em duas camadas: Modelo e DAO.
IModel
52
O objetivo dos mtodos de IModel obrigar as classes que a implementarem a retornar o nome do campo na tabela que a chave primria (getPrimaryKey) e o valor para o registro corrente (getId). Temos sempre que tentar identificar caractersticas e comportamentos similares, de modo a criar cdigo centralizado e fcil de manter. Por isso, vamos criar algumas classes abstratas no pacote models.generic, baseando-nos em algumas observaes do diagrama de classes do captulo 3. As classes que implementarem IModel devero ter os atributos id e primaryKey, e pares de mtodos para esses atributos (um para ler e outro para gravar). Para que repetir isso em cada classe? Vamos criar uma classe abstrata que j traga os atributos e os mtodos. Ns a chamaremos de AbstractModel. Ela implementa IModel, de forma que as subclasses no tenham de fazer isso.
AbstractModel
public abstract class AbstractModel implements IModel{ protected Integer id; protected String primaryKey;
53
Algumas classes (como Autor e Editora) tem mais em comum do que apenas o id. Elas tambm compartilham um atributo nome. Para evitar replicao de cdigo, podemos criar uma classe abstrata que estenda AbstractModel. Ela se chamar AbstractOnlyName.
AbstractOnlyName
public abstract class AbstractOnlyName extends AbstractModel{ protected String nome; public String getNome() { return nome; } public void setNome(String nome) { this.nome = nome; }
As classes Cliente e Funcionario tem muitos atributos em comum. Essas com certeza merecem uma superclasse abstrata. Ns a chamaremos de AbstractUsuario.
AbstractUsuario
public abstract class AbstractUsuario extends AbstractModel{ protected String nome; protected String apelido; protected String senha; protected String email; protected Set<Telefone> telefones = new HashSet<Telefone>(); public String getNome() { return nome; } public void setNome(String nome) {
54
this.nome = nome; } public String getApelido() { return apelido; } public void setApelido(String apelido) { this.apelido = apelido; } public String getSenha() { return senha; } public void setSenha(String senha) { this.senha = senha; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public Set<Telefone> getTelefones() { return telefones; } public void setTelefones(Set<Telefone> telefones) { this.telefones = telefones; }
Mais adiante, veremos duas classes que representam os relacionamentos entre as classes Cliente, Funcionario e Telefone. Para evitar ao mximo a replicao de cdigo, criaremos uma classe abstrata para manipular um atributo do tipo Telefone: AbstractTelefone.
55
AbstractTelefone
public abstract class AbstractTelefone implements IModel{ protected Telefone telefone; public Telefone getTelefone() { return telefone; } public void setTelefone(Telefone telefone) { this.telefone = telefone; }
Com essas pequenas generalizaes, podemos enfim criar os modelos, na raiz do pacote models.
Autor
public class Autor extends AbstractOnlyName implements Comparator<Autor> { private String sobrenome;
56
No construtor do modelo Autor, assim como nos demais que veremos adiante, temos a inicializao do atributo primaryKey, que o nome da chave primria na tabela mapeada. Essa classe Autor em especial implementa a interface Comparator, para que posteriormente possamos fazer a ordenao de uma coleo de Autores pelo sobrenome.
Cliente
public class Cliente extends AbstractUsuario {
57
this.cpf = cpf; }
Funcionario
public class Funcionario extends AbstractUsuario { private String matricula;
Editora
public class Editora extends AbstractOnlyName {
58
Local
public class Local extends AbstractOnlyName {
Livro
public class Livro extends AbstractModel { private String isbn; private String titulo; private Local local; private Editora editora; private Integer ano; private Float preco; private Set<Autor> autores = new HashSet<Autor>();
59
60
Pedido
public class Pedido implements IModel { private Integer numeroPedido; private GregorianCalendar dataPedido; private Cliente cliente; private Set<ItemPedido> items = new HashSet<ItemPedido>(); protected String primaryKey;
61
62
ItemPedido
public class ItemPedido extends AbstractModel { private Pedido pedido; private Livro livro; private Float preco; private Integer quantidade;
63
Telefone
public class Telefone extends AbstractModel { private String numero; private String tipo;
64
Existem alguns relacionamentos muitos-para-muitos entre algumas tabelas. Eles esto refletidos nos atributos que so colees de objetos. Para facilitar o trabalho da prxima categoria de classes que iremos construir, vamos criar algumas classes que representam os relacionamentos.
AutorLivro
public class AutorLivro implements IModel, Comparator<AutorLivro> { private Autor autor; private Livro livro;
65
public boolean equals(AutorLivro autorLivro) { if (getAutor().getId() == autorLivro.getAutor().getId() && getLivro().getId() == autorLivro.getLivro().getId()) { return true; }
66
return false; }
A classe AutorLivro implementa a interface Comparator, para que posteriormente possamos fazer a ordenao de uma coleo de Autores pelo sobrenome.
TelefoneCliente
public class TelefoneCliente extends AbstractTelefone {
67
TelefoneFuncionario
public class TelefoneFuncionario extends AbstractTelefone { private Funcionario funcionario;
68
DAOEngine
public class DAOEngine { private static String url; private static String username; private static String password; private static String driver;
69
private static Connection connection = null; private static final byte NAME = 0; private static final byte VALUE = 1; private static Statement statement = null;
private static final String ERROR_NO_CONNECTION = "No conseguiu estabelecer conexo com o banco de dados";
private DAOEngine() {
O mtodo openConnnection abre uma conexo de banco de dados baseada em configuraes de um arquivo de propriedades chamado connection.properties. Esse arquivo fica na raiz do projeto.
private static boolean openConnnection() { InputStream in = new DAOEngine().getClass().getClassLoader() .getResourceAsStream("connection.properties"); Properties properties = new Properties();
70
try { Class.forName(driver);
Message.setLastException(e.getMessage());
e.printStackTrace(); } catch (SQLException e) {
Message.setLastException(e.getMessage());
e.printStackTrace(); } return false; }
O mtodo insert faz o encapsulamento da instruo SQL INSERT. Ele ser usado pelas classes DAO para incluir registros em uma tabela.
public static boolean insert(String table, String[][] fields) { String names = ""; String values = ""; String sql = "INSERT INTO " + table + "(";
71
return executeSQL(sql); }
A execuo propriamente dita do comando SQL feita pelo mtodo executeSQL. Isso porque esse trecho de cdigo comum para os mtodos de incluso, alterao e remoo de registros.
private static boolean executeSQL(String sql) { if (!connectionEstablished()) return false;
try {
statement = connection.createStatement();
boolean result = statement.execute(sql);
} catch (SQLException e) {
Message.setLastException(e.getMessage());
72
connection = null;
}
return false;
A atualizao de registros feita pelo mtodo update, que encapsula a instruo SQL UPDATE.
public static boolean update(String table, String[][] fields, String where) { String settings = ""; String sql = "UPDATE " + table + " SET ";
for (int i = 0; i < fields.length; i++) { settings = settings + fields[i][NAME] + " = " + fields[i][VALUE] + ","; }
return executeSQL(sql); }
public static boolean delete(String table, String where) { String sql = "DELETE FROM " + table + "";
73
return executeSQL(sql); }
O mtodo fetchAll obtm um conjunto de registros de uma tabela baseado em uma restrio (where). Esse conjunto de registros pode estar ordenados (order), e serem um subconjunto dos que atendem a restrio (limit e offset).
public static ResultSet fetchAll(String table, String where, String order, Integer limit, Integer offset) { String sql;
connection = null;
}
sql = "SELECT * FROM " + table; sql = sql + (where == null ? "" : " WHERE " + where); sql = sql + (order == null ? "" : " ORDER BY " + order); sql = sql + (limit == null ? "" : " LIMIT " + limit); sql = sql + (offset == null ? "" : " OFFSET " + offset);
try {
statement = connection.createStatement();
ResultSet rs = statement.executeQuery(sql); return rs; } catch (SQLException e) {
74
Message.setLastException(e.getMessage());
} return null; }
O mtodo connectionEstablished verifica se a conexo com o banco de dados est aberta. Caso no esteja, tenta se conectar. Em caso de falha, armazena uma mensagem padro em uma classe preparada especificamente para isso.
private static boolean connectionEstablished() { if (!isConnected()) if (!openConnnection()) {
Message.setLastException(ERROR_NO_CONNECTION);
return false; } return true; }
O mtodo lastValue obtm o ltimo valor gravado em um campo do tipo inteiro. A sua finalidade recuperar o contedo de campos gerados automaticamente pela banco de dados.
public static Integer lastValue(String table, String field) { String sql;
sql = "SELECT * FROM " + table + " ORDER BY " + field + " DESC";
try {
statement = connection.createStatement();
ResultSet rs = statement.executeQuery(sql);
if (rs.next()) {
75
Message.setLastException(e.getMessage());
} return null; }
Esta classe isola o gerenciamento de SQL. Observe que ela faz uso de um arquivo de propriedades para abrir a conexo com o banco. Isso importante, porque se for necessrio alterar os dados de conexo, no preciso modificar a classe, mas to somente o arquivo. Modificar a classe significa ter de recompil-la e redistribu-la. Editar um arquivo de texto algo mais simples. A interface em Java padroniza comportamentos entre classes. Todas as nossas classes DAOs iro executar as mesmas operaes, sobre tabelas diferentes. Assim, vamos garantir que as assinaturas dos mtodos sejam as mesmas.
IDAO
public interface IDAO { public boolean insert(IModel model); public boolean update(IModel model); public boolean delete(IModel model); public IModel fetchOne(IModel model); public List<? extends IModel> fetchAll(String where, String order, Integer limit, Integer offset);
A interface IDAO define assinaturas para os mtodos de incluso (insert), atualizao (update) e remoo (delete) de registros. O mtodo fetchOne serve para retornar um objeto nico, correspondente a um registro da tabela. Ele recebe e retorna uma implementao de IModel. O mtodo fetchAll retorna uma coleo de objetos a partir de um critrio de consulta, que a expresso utilizada pela clusula WHERE da expresso SQL INSERT. Observe que usamos um tipo List, da API Collections de Java. O curinga ? usado para permitir
76
que as classes que implementam esse mtodo definam para a List um tipo de classe, em vez de usar a interface. Isso evita a necessidade de casting. Isso ficar mais claro quando implementarmos as classes DAO utilizando essa interface. A ltima generalizao ser criar uma classe abstrata que implemente os mtodos de incluso, atualizao e remoo definidos por IDAO. Contudo, essa classe no ir implementar diretamente a interface, pois os mtodos de consulta ficaro a cargo de cada DAO. A superclasse dos DAOs de nossa aplicao se chamar AbstractDAO.
AbstractDAO
public abstract class AbstractDAO { protected String table;
77
O mtodo getDefaultWhere retorna uma condio padro, usada para busca de objetos nicos. O mtodo getFields retorna um array com os campos editveis da tabela e os valores a serem gravados. Com essas generalizaes, nossas classes DAO, a princpio, s precisaro implementar os mtodos de busca, e os que fornecem os nomes de campos da tabela e critrios de consulta.
AutorDAO
public class AutorDAO extends AbstractDAO implements IDAO {
public List<Autor> fetchAll(String where, String order, Integer limit, Integer offset) { List<Autor> list = new ArrayList<Autor>(); ResultSet rs = DAOEngine.fetchAll(this.table, where, order, limit, offset);
try {
78
while (rs.next()) { Autor autor = new Autor(); autor.setId(rs.getInt("id_autor")); autor.setNome(rs.getString("nome")); autor.setSobrenome(rs.getString("sobrenome")); list.add(autor); } return list; } catch (SQLException e) { e.printStackTrace(); }
return null; }
List<Autor> list = this.fetchAll(where, null, null, null); if (list != null && list.size() > 0) return list.get(0); else return null; }
79
fields[0][0] = "nome"; fields[0][1] = "'" + autor.getNome() + "'"; fields[1][0] = "sobrenome"; fields[1][1] = "'" + autor.getSobrenome() + "'";
return fields; }
AutorLivroDAO
public class AutorLivroDAO extends AbstractDAO implements IDAO {
@Override public String getDefaultWhere(IModel model) { return "id_autor = " + ((AutorLivro) model).getAutor().getId() + " and id_livro = " + ((AutorLivro) model).getLivro().getId(); }
fields[0][0] = "id_autor";
80
fields[0][1] = autorLivro.getAutor().getId().toString(); fields[1][0] = "id_livro"; fields[1][1] = autorLivro.getLivro().getId().toString(); fields[2][0] = "ordem"; fields[2][1] = autorLivro.getOrdem().toString();
return fields; }
public List<AutorLivro> fetchAll(String where, String order, Integer limit, Integer offset) { List<AutorLivro> list = new ArrayList<AutorLivro>(); ResultSet rs = DAOEngine.fetchAll(this.table, where, order, limit, offset);
Autor autor = new Autor(); autor.setId(rs.getInt("id_autor")); autor = (Autor) new AutorDAO().fetchOne(autor); autorLivro.setAutor(autor);
Livro livro = new Livro(); livro.setId(rs.getInt("id_livro")); livro = (Livro) new LivroDAO().fetchOne(livro); autorLivro.setLivro(livro);
autorLivro.setOrdem(rs.getInt("ordem")); list.add(autorLivro);
81
return null; }
public Set<Autor> fetchAll(String where) { Set<Autor> set = new HashSet<Autor>(); ResultSet rs = DAOEngine.fetchAll(this.table, where, null, null, null);
try { AutorDAO dao = new AutorDAO(); while (rs.next()) { Autor autor = new Autor(); autor.setId(rs.getInt("id_autor")); autor = (Autor) dao.fetchOne(autor); set.add(autor); } return set; } catch (SQLException e) { e.printStackTrace(); }
return null; }
82
List<AutorLivro> list = this.fetchAll(where, null, null, null); if (list != null && list.size() > 0) return list.get(0); else return null; }
ClienteDAO
public class ClienteDAO extends AbstractDAO implements IDAO{ public ClienteDAO() { this.table = "clientes"; }
public List<Cliente> fetchAll(String where) { List<Cliente> list = new ArrayList<Cliente>(); ResultSet rs = DAOEngine.fetchAll(this.table,where); try { while (rs.next()) { Cliente cliente = new Cliente(); cliente.setId(rs.getInt("id_cliente")); cliente.setCpf(rs.getString("cpf")); cliente.setNome(rs.getString("nome")); cliente.setApelido(rs.getString("apelido")); cliente.setSenha(rs.getString("senha")); cliente.setEmail(rs.getString("email")); Set<Telefone> telefones = new HashSet<Telefone>();
83
List<TelefoneCliente> telefonesCliente = new TelefoneClienteDAO().fetchAll("id_cliente = "+cliente.getId()); for (Iterator<TelefoneCliente> iterator = telefonesCliente.iterator(); iterator .hasNext();) { TelefoneCliente telefoneFuncionario = (TelefoneCliente) iterator .next(); telefones.add(telefoneFuncionario.getTelefone()); } list.add(cliente); } return list; } catch (SQLException e) { e.printStackTrace(); } return null; }
public IModel fetchOne(IModel model) { String where = this.getDefaultWhere(model); List<Cliente> list = this.fetchAll(where); if (list != null && list.size()>0) return list.get(0); else return null; } @Override public String[][] getFields(IModel model) { Cliente cliente = (Cliente) model; String[][] fields = new String[5][2]; fields[0][0] = "cpf"; fields[0][1] = "'" + cliente.getCpf() + "'";
84
fields[1][0] = "nome"; fields[1][1] = "'" + cliente.getNome() + "'"; fields[2][0] = "apelido"; fields[2][1] = "'" + cliente.getApelido() + "'"; fields[3][0] = "senha"; fields[3][1] = "'" + cliente.getSenha() + "'"; fields[4][0] = "email"; fields[4][1] = "'" + cliente.getEmail() + "'"; return fields; } @Override public String getDefaultWhere(IModel model) { return "id_cliente = "+((Cliente)model).getId(); } }
EditoraDAO
public class EditoraDAO extends AbstractDAO implements IDAO {
public List<Editora> fetchAll(String where, String order, Integer limit, Integer offset) { List<Editora> list = new ArrayList<Editora>(); ResultSet rs = DAOEngine.fetchAll(this.table, where, order, limit, offset);
try {
85
while (rs.next()) { Editora Editora = new Editora(); Editora.setId(rs.getInt("id_editora")); Editora.setNome(rs.getString("nome")); list.add(Editora); } return list; } catch (SQLException e) { e.printStackTrace(); }
return null; }
List<Editora> list = this.fetchAll(where, null, null, null); if (list != null && list.size() > 0) return list.get(0); else return null; }
86
return fields; }
FuncionarioDAO
public class FuncionarioDAO extends AbstractDAO implements IDAO {
public List<Funcionario> fetchAll(String where, String order, Integer limit, Integer offset) { List<Funcionario> list = new ArrayList<Funcionario>(); ResultSet rs = DAOEngine.fetchAll(this.table, where, order, limit, offset);
try { while (rs.next()) { Funcionario funcionario = new Funcionario(); funcionario.setId(rs.getInt("id_usuario")); funcionario.setMatricula(rs.getString("matricula")); funcionario.setNome(rs.getString("nome")); funcionario.setApelido(rs.getString("apelido")); funcionario.setSenha(rs.getString("senha")); funcionario.setEmail(rs.getString("email"));
87
return null; }
List<Funcionario> list = this.fetchAll(where, null, null, null); if (list != null && list.size() > 0) return list.get(0); else return null; }
88
fields[1][1] = "'" + Funcionario.getNome() + "'"; fields[2][0] = "apelido"; fields[2][1] = "'" + Funcionario.getApelido() + "'"; fields[3][0] = "senha"; fields[3][1] = "'" + Funcionario.getSenha() + "'"; fields[4][0] = "email"; fields[4][1] = "'" + Funcionario.getEmail() + "'";
return fields; }
ItemPedidoDAO
public class ItemPedidoDAO extends AbstractDAO implements IDAO {
fields[0][0] = "id_pedido"; fields[0][1] = itemPedido.getPedido().getNumeroPedido().toString(); fields[1][0] = "id_livro"; fields[1][1] = itemPedido.getLivro().getId().toString(); fields[2][0] = "preco"; fields[2][1] = itemPedido.getPreco().toString(); fields[3][0] = "quantidade"; fields[3][1] = itemPedido.getQuantidade().toString();
89
return fields; }
public List<ItemPedido> fetchAll(String where, String order, Integer limit, Integer offset) { List<ItemPedido> list = new ArrayList<ItemPedido>(); ResultSet rs = DAOEngine.fetchAll(this.table, where, order, limit, offset);
Pedido pedido = new Pedido(); pedido.setNumeroPedido(rs.getInt("id_pedido")); pedido = (Pedido) new PedidoDAO().fetchOne(pedido); itemPedido.setPedido(pedido);
Livro livro = new Livro(); livro.setId(rs.getInt("id_livro")); livro = (Livro) new LivroDAO().fetchOne(livro); itemPedido.setLivro(livro);
itemPedido.setPreco(rs.getFloat("preco")); itemPedido.setQuantidade(rs.getInt("quantidade"));
90
return null; }
List<ItemPedido> list = this.fetchAll(where, null, null, null); if (list != null && list.size() > 0) return list.get(0); else return null; }
LivroDAO
public class LivroDAO extends AbstractDAO implements IDAO {
public List<Livro> fetchAll(String where, String order, Integer limit, Integer offset) { List<Livro> list = new ArrayList<Livro>();
91
try { while (rs.next()) { Livro livro = new Livro(); livro.setId(rs.getInt("id_livro")); livro.setIsbn(rs.getString("isbn")); livro.setTitulo(rs.getString("titulo"));
Local local = new Local(); local.setId(rs.getInt("id_local")); local = (Local) (new LocalDAO()).fetchOne(local); livro.setLocal(local);
Editora editora = new Editora(); editora.setId(rs.getInt("id_editora")); editora = (Editora) (new EditoraDAO()).fetchOne(editora); livro.setEditora(editora);
livro.setAno(rs.getInt("ano"));
livro.setPreco(formataPreco(preco));
list.add(livro); }
92
return null; }
preco = preco.substring(i);
return Float.parseFloat(preco); }
List<Livro> list = this.fetchAll(where, null, null, null); if (list != null && list.size() > 0) return list.get(0);
93
fields[0][0] = "isbn"; fields[0][1] = "'" + livro.getIsbn() + "'"; fields[1][0] = "titulo"; fields[1][1] = "'" + livro.getTitulo() + "'"; fields[2][0] = "id_editora"; fields[2][1] = livro.getEditora().getId().toString(); fields[3][0] = "id_local"; fields[3][1] = livro.getLocal().getId().toString(); fields[4][0] = "ano"; fields[4][1] = livro.getAno().toString(); fields[5][0] = "preco"; fields[5][1] = "'" + livro.getPreco().toString() + "'";
return fields; } }
LocalDAO
public class LocalDAO extends AbstractDAO implements IDAO {
public LocalDAO() {
94
this.table = "locais"; }
public List<Local> fetchAll(String where, String order, Integer limit, Integer offset) { List<Local> list = new ArrayList<Local>(); ResultSet rs = DAOEngine.fetchAll(this.table, where, order, limit, offset);
try { while (rs.next()) { Local local = new Local(); local.setId(rs.getInt("id_local")); local.setNome(rs.getString("nome")); list.add(local); } return list; } catch (SQLException e) { e.printStackTrace(); }
return null; }
List<Local> list = this.fetchAll(where, null, null, null); if (list != null && list.size() > 0)
95
return fields; } }
PedidoDAO
public class PedidoDAO extends AbstractDAO implements IDAO {
96
return fields; }
public List<Pedido> fetchAll(String where, String order, Integer limit, Integer offset) { List<Pedido> list = new ArrayList<Pedido>(); ResultSet rs = DAOEngine.fetchAll(this.table, where, order, limit, offset);
try { while (rs.next()) { Pedido pedido = new Pedido(); pedido.setNumeroPedido(rs.getInt("numero_pedido")); Date date = rs.getDate("data_pedido"); GregorianCalendar calendar = new GregorianCalendar(); calendar.setTime(date); pedido.setDataPedido(calendar); list.add(pedido); } return list; } catch (SQLException e) { e.printStackTrace(); }
return null; }
97
List<Pedido> list = this.fetchAll(where, null, null, null); if (list != null && list.size() > 0) return list.get(0); else return null; }
TelefoneClienteDAO
public class TelefoneClienteDAO extends AbstractDAO implements IDAO {
@Override public String getDefaultWhere(IModel model) { return "id_cliente = " + ((TelefoneCliente) model).getCliente().getId() + " and id_telefone = " + ((TelefoneCliente) model).getTelefone().getId(); }
98
return fields; }
public List<TelefoneCliente> fetchAll(String where, String order, Integer limit, Integer offset) { List<TelefoneCliente> list = new ArrayList<TelefoneCliente>(); ResultSet rs = DAOEngine.fetchAll(this.table, where, order, limit, offset);
Cliente cliente = new Cliente(); cliente.setId(rs.getInt("id_cliente")); cliente = (Cliente) new ClienteDAO().fetchOne(cliente); telefoneCliente.setCliente(cliente);
99
telefoneCliente.setTelefone(telefone);
return null; }
List<TelefoneCliente> list = this.fetchAll(where, null, null, null); if (list != null && list.size() > 0) return list.get(0); else return null; }
TelefoneDAO
public class TelefoneDAO extends AbstractDAO implements IDAO {
100
@Override public String getDefaultWhere(IModel model) { return "id_telefone = " + ((Telefone) model).getId(); }
fields[0][0] = "numero"; fields[0][1] = "'" + telefone.getNumero() + "'"; fields[1][0] = "tipo"; fields[1][1] = "'" + telefone.getTipo().toString() + "'";
return fields; }
public List<Telefone> fetchAll(String where, String order, Integer limit, Integer offset) { List<Telefone> list = new ArrayList<Telefone>(); ResultSet rs = DAOEngine.fetchAll(this.table, where, order, limit, offset);
101
return null; }
List<Telefone> list = this.fetchAll(where, null, null, null); if (list != null && list.size() > 0) return list.get(0); else return null; }
TelefoneFuncionarioDAO
public class TelefoneFuncionarioDAO extends AbstractDAO implements IDAO {
102
@Override public String getDefaultWhere(IModel model) { return "id_funcionario = " + ((TelefoneFuncionario) model).getFuncionario().getId() + " and id_telefone = " + ((TelefoneFuncionario) model).getTelefone().getId(); }
return fields; }
public List<TelefoneFuncionario> fetchAll(String where, String order, Integer limit, Integer offset) { List<TelefoneFuncionario> list = new ArrayList<TelefoneFuncionario>(); ResultSet rs = DAOEngine.fetchAll(this.table, where, order, limit, offset);
103
Funcionario funcionario = new Funcionario(); funcionario.setId(rs.getInt("id_funcionario")); funcionario = (Funcionario) new FuncionarioDAO() .fetchOne(funcionario); telefoneFuncionario.setFuncionario(funcionario);
Telefone telefone = new Telefone(); telefone.setId(rs.getInt("id_telefone")); telefone = (Telefone) new TelefoneDAO().fetchOne(telefone); telefoneFuncionario.setTelefone(telefone);
List<TelefoneFuncionario> list = this.fetchAll(where, null, null, null); if (list != null && list.size() > 0) return list.get(0); else return null; }
104
105
Um teste de integrao um teste que verifica o funcionamento de duas partes independentes de um sistema de software. No caso, o banco de dados e a camada de persistncia poderiam ser testados de forma isolada cada um, mas aqui vamos testar ambos simultaneamente. Como exemplo, iremos criar um teste de integrao para a persistncia do modelo Autor. Temos de criar mtodos de teste que consigam verificar todas as funcionalidades presentes nas classes envolvidas. Seguindo o padro do JUnit, a classe teste se chamar TestAutorDAO.
TestAutorDAO
public class TestAutorDAO extends TestCase { private static Autor autor; private static AutorDAO autorDAO = new AutorDAO(); public void testInsert() { TestAutorDAO.autor = new Autor(); TestAutorDAO.autor.setNome("Maurcio"); TestAutorDAO.autor.setSobrenome("de Sousa");
assertNotNull(autor);
if (autor!=null) { TestAutorDAO.autor = autor; } System.out.println(autor); } public void testUpdate() { TestAutorDAO.autor.setNome("Carlos"); TestAutorDAO.autor.setSobrenome("Gomes");
106
assertEquals(TestAutorDAO.autor.getId(), autor.getId());
System.out.println(autor); } public void testDelete() { TestAutorDAO.autorDAO.delete(TestAutorDAO.autor); Autor autor = (Autor) new AutorDAO().fetchOne(TestAutorDAO.autor);
assertNull(autor);
} }
Observe que com trs mtodos de teste conseguimos cobrir todas as funcionalidades presentes no modelo e no DAO.
connection.properties
# configurao do banco de dados driver = org.postgresql.Driver username = postgres password = postgres url = jdbc:postgresql://localhost:5432/livraria
A classe DAOEngine tambm utiliza uma classe para guardar mensagens de exceo. a classe Message, que fica no pacote messages. Ela na verdade servir a todas as camadas da aplicao. Seu cdigo segue adiante.
Message
public class Message { private static String lastException = ""; private static String lastInfo = "";
107
private Message(){};
lastException = null;
return message; }
lastInfo = "";
return message; }
108
4.9.Camada de Controle
O controle da aplicao ser feito por servlets. Nossa aplicao ter quatro servlets de controle. Um, chamado GravarServlet, ser responsvel por operaes de gravao (incluso e alterao de registros). Outro, chamado RemoverServlet, ser responsvel pela excluso de registros. O terceiro, chamado AutenticarServlet, ser responsvel pela autenticao do usurio e pela sua sada do sistema. Os dois primeiros servlets iro herdar de AuthServlet, que ir encapsular a verificao da autenticao do usurio. Para criar um servlet no Eclipse, basta acessar o menu File->New->Other. Abra o item Web e selecione Servlet, conforme mostra a figura 16. O Eclipse, alm de criar o esqueleto da classe Servlet, tambm cria as entradas no arquivo web.xml. Mas a URL padro o prprio nome do servlet. Iremos alterar para um padro de nome com letras minsculas. No arquivo web.xml, dentro de WebContent/WEB-INF, edite a configurao dos servlets para a seguinte:
<servlet> <description></description> <display-name>GravarServlet</display-name> <servlet-name>GravarServlet</servlet-name> <servlet-class>controllers.GravarServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>GravarServlet</servlet-name> <url-pattern>/gravar</url-pattern> </servlet-mapping> <servlet> <description></description> <display-name>RemoverServlet</display-name> <servlet-name>RemoverServlet</servlet-name> <servlet-class>controllers.RemoverServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>RemoverServlet</servlet-name> <url-pattern>/remover</url-pattern> </servlet-mapping>
109
<servlet> <description></description> <display-name>AutenticarServlet</display-name> <servlet-name>AutenticarServlet</servlet-name> <servlet-class>controllers.AutenticarServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>AutenticarServlet</servlet-name> <url-pattern>/autenticar</url-pattern> </servlet-mapping> <servlet> <description></description> <display-name>AuthServlet</display-name> <servlet-name>AuthServlet</servlet-name> <servlet-class>controllers.AuthServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>AuthServlet</servlet-name> <url-pattern>/AuthServlet</url-pattern> </servlet-mapping>
110
111
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); }
private void logout(HttpServletRequest request, HttpServletResponse response) { getServletContext().removeAttribute("funcionario"); try { response.sendRedirect("login.jsp"); } catch (IOException e) { tratarErro(response, e); }
112
private void login(HttpServletRequest request, HttpServletResponse response) { String apelido = request.getParameter("apelido"); String senha = request.getParameter("senha");
try {
List<Funcionario> funcionarios = dao.fetchAll("apelido = '" + apelido + "' and senha ='" + senha + "'", null, null, null);
} catch (Exception e) {
Message.setLastException(e.getMessage());
tratarErro(response, e); }
113
private void tratarErro(HttpServletResponse response, Exception e) { try { response.sendRedirect("erro.jsp?mensagem=" + e.getMessage()); } catch (IOException e1) { e1.printStackTrace(); } }
Esse servlet faz uso de uma classe de constantes chamada Operacoes, que fica no pacote constants:
public class Operacoes { public final static String LOGIN = "login"; public final static String LOGOUT = "logout";
@Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
if (getServletContext().getAttribute("funcionario") == null) {
114
115
AuthorizationController ac = AuthorizationController.getInstance(funcionario.getApelido());
if (!ac.hasRole(new String[]{Papeis.GRAVADOR,Papeis.ADMINISTRADOR})) {
116
if (cadastro.equals(Cadastros.AUTOR)) { gravarAutor(request, response);return; } if (cadastro.equals(Cadastros.CLIENTE)) { gravarCliente(request, response);return; } if (cadastro.equals(Cadastros.EDITORA)) { gravarEditora(request, response);return; } if (cadastro.equals(Cadastros.FUNCIONARIO)) { gravarFuncionario(request, response);return; } if (cadastro.equals(Cadastros.LIVRO)) { gravarLivro(request, response);return; } if (cadastro.equals(Cadastros.LOCAL)) { gravarLocal(request, response);return; } if (cadastro.equals(Cadastros.PEDIDO)) { gravarPedido(request, response);return; } if (cadastro.equals(Cadastros.TELEFONE)) { gravarTelefone(request, response);return; } if (cadastro.equals(Cadastros.AUTOR_LIVRO)) { gravarAutorLivro(request, response);return; } if (cadastro.equals(Cadastros.TELEFONE_CLIENTE)) { gravarTelefoneCliente(request, response);return; }
117
private void gravarTelefoneFuncionario(HttpServletRequest request, HttpServletResponse response) { Funcionario funcionario = new Funcionario(); funcionario.setId(Integer.parseInt(request.getParameter("id_funcionario")));
telefone.setNumero(request.getParameter("numero")); telefone.setTipo(request.getParameter("tipo"));
telefone.setId(DAOEngine.lastValue(telefoneDAO.getTable(), "id_telefone"));
118
tratarErro(response, e); } }
telefone.setNumero(request.getParameter("numero")); telefone.setTipo(request.getParameter("tipo"));
telefone.setId(DAOEngine.lastValue(telefoneDAO.getTable(), "id_telefone"));
119
} }
if (autorLivroExistente == null) { List<AutorLivro> autoresLivro = new ArrayList<AutorLivro>(); autoresLivro = dao.fetchAll("id_livro = " + autorLivro.getLivro().getId(), "ordem", null, null);
autorLivro.setOrdem(ordem);
dao.insert(autorLivro); }
120
private void gravarPedido(HttpServletRequest request, HttpServletResponse response) { Pedido pedido = new Pedido();
GregorianCalendar calendar = new GregorianCalendar(); calendar.setTimeInMillis(System.currentTimeMillis()); pedido.setDataPedido(calendar); pedido.setCliente(new Cliente()); pedido.getCliente().setId(Integer.parseInt(request.getSession().getAttribute( "id_cliente").toString())); PedidoDAO dao = new PedidoDAO(); dao.insert(pedido); pedido.setNumeroPedido(dao.ultimoNumeroPedido()); pedido = (Pedido) dao.fetchOne(pedido); request.getSession().setAttribute("ultimo_pedido", pedido.getNumeroPedido());
121
private void gravarTelefone(HttpServletRequest request, HttpServletResponse response) { Telefone telefone = new Telefone();
telefone.setNumero(request.getParameter("numero")); telefone.setTipo(request.getParameter("tipo"));
private void gravarCliente(HttpServletRequest request, HttpServletResponse response) { Cliente cliente = new Cliente();
122
private void gravarLocal(HttpServletRequest request, HttpServletResponse response) { Local local = new Local();
local.setNome(request.getParameter("nome"));
123
private void gravarLivro(HttpServletRequest request, HttpServletResponse response) { Livro livro = new Livro();
livro.setAno(Integer.parseInt((request.getParameter("ano"))));
Editora editora = new Editora(); editora.setId(Integer.parseInt(request.getParameter("id_editora"))); editora = (Editora) new EditoraDAO().fetchOne(editora); livro.setEditora(editora);
livro.setIsbn(request.getParameter("isbn"));
124
livro.setLocal(local);
LivroDAO dao = new LivroDAO(); if (livro.getId() == null) { dao.insert(livro); livro.setId(DAOEngine.lastValue(dao.getTable(), "id_livro")); } else { dao.update(livro); }
private void gravarFuncionario(HttpServletRequest request, HttpServletResponse response) { Funcionario funcionario = new Funcionario();
125
funcionario.setSenha(request.getParameter("senha"));
private void gravarAutor(HttpServletRequest request, HttpServletResponse response) { Autor autor = new Autor();
autor.setNome(request.getParameter("nome")); autor.setSobrenome(request.getParameter("sobrenome"));
126
editora.setNome(request.getParameter("nome"));
127
private IModel trataId(HttpServletRequest request, AbstractModel model) { if (request.getParameter("id") != null && !request.getParameter("id").equals("null")) { model.setId(Integer.parseInt(request.getParameter("id"))); } return model; }
private void tratarErro(HttpServletResponse response, Exception e) { try { response.sendRedirect("erro.jsp?mensagem=" + e.getMessage()); } catch (IOException e1) { e1.printStackTrace(); } }
128
instance.updateRoles(user);
return instance; }
private void updateRoles(String user) { InputStream in = this.getClass().getClassLoader().getResourceAsStream("authorization.properties"); Properties properties = new Properties();
try {
roles.clear();
properties.load(in); in.close(); String[] papeisDoUsuario = properties.getProperty(user).split(",", -1); for (int i=0;i<papeisDoUsuario.length;i++) {
roles.add(papeisDoUsuario[i]);
} } catch (Exception e) {
Message.setLastException(e.getMessage());
} }
129
public boolean hasRole(String[] userRoles) { for (int i=0;i<userRoles.length;i++) { if (roles.contains(userRoles[i])) return true; }
return false; }
authorization.properties
# papeis dos usuarios
130
patricia = administrador
131
if (cadastro.equals(Cadastros.AUTOR)) { removerAutor(response, id); return; } if (cadastro.equals(Cadastros.CLIENTE)) { removerCliente(response, id); return; } if (cadastro.equals(Cadastros.EDITORA)) { removerEditora(response, id); return; }
132
if (cadastro.equals(Cadastros.FUNCIONARIO)) { removerFuncionario(response, id); return; } if (cadastro.equals(Cadastros.LIVRO)) { removerLivro(response, id); return; } if (cadastro.equals(Cadastros.LOCAL)) { removerLocal(response, id); return; } if (cadastro.equals(Cadastros.TELEFONE)) { removerTelefone(response, id); return; } if (cadastro.equals(Cadastros.AUTOR_LIVRO)) { removerAutorLivro(response, Integer.parseInt(request .getParameter("id_autor")), Integer.parseInt(request .getParameter("id_livro"))); return; } if (cadastro.equals(Cadastros.TELEFONE_CLIENTE)) { removerTelefoneCliente(response, Integer.parseInt(request .getParameter("id_cliente")), Integer.parseInt(request .getParameter("id_telefone"))); return; } if (cadastro.equals(Cadastros.TELEFONE_FUNCIONARIO)) { removerTelefoneFucionario(response, Integer.parseInt(request .getParameter("id_funcionario")), Integer.parseInt(request
133
.getParameter("id_telefone"))); return; }
private void removerTelefoneFucionario(HttpServletResponse response, Integer idFuncionario, int idTelefone) { TelefoneFuncionario telefoneFuncionario = new TelefoneFuncionario(); telefoneFuncionario.setFuncionario(new Funcionario()); telefoneFuncionario.setTelefone(new Telefone());
telefoneFuncionario.getFuncionario().setId(idFuncionario); telefoneFuncionario.getTelefone().setId(idTelefone);
new TelefoneFuncionarioDAO().delete(telefoneFuncionario);
private void removerTelefoneCliente(HttpServletResponse response, Integer idCliente, Integer idTelefone) { TelefoneCliente telefoneCliente = new TelefoneCliente(); telefoneCliente.setCliente(new Cliente()); telefoneCliente.setTelefone(new Telefone());
134
telefoneCliente.getCliente().setId(idCliente); telefoneCliente.getTelefone().setId(idTelefone);
new TelefoneClienteDAO().delete(telefoneCliente);
private void removerAutorLivro(HttpServletResponse response, Integer idAutor, Integer idLivro) { AutorLivro autorLivro = new AutorLivro(); autorLivro.setAutor(new Autor()); autorLivro.setLivro(new Livro());
autorLivro.getAutor().setId(idAutor); autorLivro.getLivro().setId(idLivro);
new AutorLivroDAO().delete(autorLivro); try { response.sendRedirect("edicao-livro.jsp?id=" + idLivro); } catch (IOException e) { tratarErro(response, e); e.printStackTrace(); } }
135
private void removerTelefone(HttpServletResponse response, Integer id) { Telefone telefone = new Telefone(); telefone.setId(id); new TelefoneDAO().delete(telefone); try { response.sendRedirect("cadastro.jsp?cadastro=telefone"); } catch (IOException e) { tratarErro(response, e); } }
private void removerLocal(HttpServletResponse response, Integer id) { Local local = new Local(); local.setId(id); new LocalDAO().delete(local); try { response.sendRedirect("cadastro.jsp?cadastro=local"); } catch (IOException e) { tratarErro(response, e); } }
private void removerLivro(HttpServletResponse response, Integer id) { Livro livro = new Livro(); livro.setId(id); new LivroDAO().delete(livro); try { response.sendRedirect("cadastro.jsp?cadastro=livro"); } catch (IOException e) { tratarErro(response, e);
136
} }
private void removerFuncionario(HttpServletResponse response, Integer id) { Funcionario funcionario = new Funcionario(); funcionario.setId(id); new FuncionarioDAO().delete(funcionario); try { response.sendRedirect("cadastro.jsp?cadastro=funcionario"); } catch (IOException e) { tratarErro(response, e); } }
private void removerEditora(HttpServletResponse response, Integer id) { Editora editora = new Editora(); editora.setId(id); new EditoraDAO().delete(editora); try { response.sendRedirect("cadastro.jsp?cadastro=editora"); } catch (IOException e) { tratarErro(response, e); } }
private void removerCliente(HttpServletResponse response, Integer id) { Cliente cliente = new Cliente(); cliente.setId(id); new ClienteDAO().delete(cliente); try { response.sendRedirect("cadastro.jsp?cadastro=cliente");
137
private void removerAutor(HttpServletResponse response, Integer id) { Autor autor = new Autor(); autor.setId(id); new AutorDAO().delete(autor); try { response.sendRedirect("cadastro.jsp?cadastro=autor"); } catch (IOException e) { tratarErro(response, e); } }
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); }
private void tratarErro(HttpServletResponse response, Exception e) { try { response.sendRedirect("erro.jsp?mensagem=" + e.getMessage()); } catch (IOException e1) { e1.printStackTrace(); } }
138
139
login.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <jsp:useBean id="view" class="view.ViewController"></jsp:useBean> <%if (getServletContext().getAttribute("funcionario") != null) response.sendRedirect("admin.jsp");%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Autenticao</title> </head> <body> <form action="/livrariajsp/autenticar?operacao=login" method="post"> <h1><%=view.getMessage(null)%></h1> <table border="0"> <tr> <td>Apelido:</td> <td><input type="text" name="apelido"></td> </tr> <tr> <td>Senha:</td> <td><input type="password" name="senha"></td> </tr> </table>
140
</html>
Todas as pginas de listagem dos cadastros so iguais. Por isso no precisamos criar um arquivo para cada uma. O arquivo cadastro.jsp faz a exibio de qualquer cadastro por meio de parmetro passado por HTTP GET.
cadastro.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ page import="constants.Papeis" %> <%@ page import="models.Funcionario" %> <jsp:useBean id="view" class="view.ViewController"></jsp:useBean> <%if (getServletContext().getAttribute("funcionario") == null) {response.sendRedirect("login.jsp");} else{ if (!view.hasRole((Funcionario)getServletContext().getAttribute("funcionario"),Papeis.LEITOR)) {response.sendRedirect("admin.jsp");} else{ %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Cadastro</title> </head> <body> <a href="/livrariajsp/edicao-<%=request.getParameter("cadastro")%>.jsp">Incluir</a> <form action="/livrariajsp/cadastro.jsp?cadastro=<%=request.getParameter("cadastro")%>" method="post"> Busca: <input type="text" name="valorprocurado"> <select name="campoprocurado"> <%=view.getOpcoesDeBusca(request.getParameter("cadastro"))%>
141
</select> <input type="submit" value="OK"> </form> <%Integer pagina = request.getParameter("pagina") == null ? null : Integer.parseInt(request.getParameter("pagina"));%> < %=view.getLista(request.getParameter("cadastro"),request.getParameter("campoprocurado"),request.getParameter("valor procurado"),request.getParameter("ordem"),pagina)%> <p align="center"><a href="/livrariajsp/admin.jsp">Mdulo Administrativo</a></p> </body> </html> <%}}%>
As pginas de edio j contm peculiaridades que ficam difceis de se generalizar apenas com os recursos de JSP.
edicao-autor.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ page import="constants.Papeis" %> <%@ page import="models.Funcionario" %> <jsp:useBean id="view" class="view.ViewController"></jsp:useBean> <%if (getServletContext().getAttribute("funcionario") == null) {response.sendRedirect("login.jsp");} else{ if (!view.hasRole((Funcionario)getServletContext().getAttribute("funcionario"),Papeis.LEITOR)) {response.sendRedirect("admin.jsp");} else{ %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <%@ page import="models.Autor"%> <%@ page import="dao.AutorDAO"%> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
142
<jsp:useBean id="model" class="models.Autor"></jsp:useBean> <jsp:setProperty property="id" name="model" value="<%=request.getParameter("id") == null ? null : Integer.parseInt(request.getParameter("id"))%>"/> <title><%= model.getId() == null ? "Incluso" : "Alterao"%></title> </head> <body> <h1>Cadastro de Autores</h1> <form action="/livrariajsp/gravar?cadastro=autor" method="post"> <%model = (Autor) view.getModel(model);%> Nome: <input type="text" name="nome" value="<%=model.getNome()%>"><br/> Sobrenome: <input type="text" name="sobrenome" value="<%=model.getSobrenome()%>"><br/> <input type="hidden" name="id" value="<%=request.getParameter("id")%>"> <input type="hidden" name="cadastro" value="autor"> <input type="submit" value="Gravar"> <input type="submit" name="retornar" value="Retornar"> </form> </body> </html> <%}}%>
edicao-cliente.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ page import="constants.Papeis" %> <%@ page import="models.Funcionario" %> <jsp:useBean id="view" class="view.ViewController"></jsp:useBean> <%if (getServletContext().getAttribute("funcionario") == null) {response.sendRedirect("login.jsp");} else{ if (!view.hasRole((Funcionario)getServletContext().getAttribute("funcionario"),Papeis.LEITOR)) {response.sendRedirect("admin.jsp");}
143
else{ %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <%@ page import="models.Cliente"%> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <jsp:useBean id="model" class="models.Cliente"></jsp:useBean> <jsp:setProperty property="id" name="model" value="<%=request.getParameter("id") == null ? null : Integer.parseInt(request.getParameter("id"))%>"/> <title><%= model.getId() == null ? "Incluso" : "Alterao"%></title> </head> <body> <h1>Cadastro de Clientes</h1> <table border="0"> <tr> <td> <h2>Dados Bsicos</h2> <form action="/livrariajsp/gravar?cadastro=cliente" method="post"> <%model = (Cliente) view.getModel(model);%> CPF: <input type="text" name="cpf" value="<%=model.getCpf()%>"><br/> Nome: <input type="text" name="nome" value="<%=model.getNome()%>"><br/> Apelido: <input type="text" name="apelido" value="<%=model.getApelido()%>"><br/> Senha: <input type="password" name="senha" value="<%=model.getSenha()%>"><br/> e-mail: <input type="text" name="email" value="<%=model.getEmail()%>"><br/> <input type="hidden" name="id" value="<%=request.getParameter("id")%>">
144
<input type="hidden" name="cadastro" value="cliente"> <input type="submit" value="Gravar"> <input type="submit" name="retornar" value="Retornar"> </form> </td> <td> <%if (request.getParameter("id") != null){%> <h2>Telefones</h2> <form action="/livrariajsp/gravar?cadastro=telefone_cliente" method="post"> Telefone: <input type="text" name="numero"><br/> Tipo: <select name="tipo"> <%=view.getTiposTelefone()%> </select> <input type="submit" id="adicionar" value="Adicionar telefone"> <input type="hidden" name="id_cliente" value="<%=request.getParameter("id")%>"> </form> <form action="/livrariajsp/remover?cadastro=telefone_cliente" method="post"> <select name="id_telefone"> <%=view.getTelefonesCliente(request.getParameter("id"))%> </select> <input type="submit" id="remover" value="Remover telefone"> <input type="hidden" name="id_cliente" value="<%=request.getParameter("id")%>"> </form> <%}%> </td> </tr> </table> </body> </html>
145
<%}}%>
edicao-editora.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ page import="constants.Papeis" %> <%@ page import="models.Funcionario" %> <jsp:useBean id="view" class="view.ViewController"></jsp:useBean> <%if (getServletContext().getAttribute("funcionario") == null) {response.sendRedirect("login.jsp");} else{ if (!view.hasRole((Funcionario)getServletContext().getAttribute("funcionario"),Papeis.LEITOR)) {response.sendRedirect("admin.jsp");} else{ %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <%@ page import="models.Editora"%> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <jsp:useBean id="model" class="models.Editora"></jsp:useBean> <jsp:setProperty property="id" name="model" value="<%=request.getParameter("id") == null ? null : Integer.parseInt(request.getParameter("id"))%>"/> <title><%= model.getId() == null ? "Incluso" : "Alterao"%></title> </head> <body> <h1>Cadastro de Editoras</h1> <form action="/livrariajsp/gravar?cadastro=editora" method="post"> <%model = (Editora) view.getModel(model);%> Nome: <input type="text" name="nome" value="<%=model.getNome()%>"><br/> <input type="hidden" name="id" value="<%=request.getParameter("id")%>"> <input type="hidden" name="cadastro" value="editora">
146
<input type="submit" value="Gravar"> <input type="submit" name="retornar" value="Retornar"> </form> </body> </html>
<%}}%>
edicao-funcionario.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ page import="constants.Papeis" %> <%@ page import="models.Funcionario" %> <jsp:useBean id="view" class="view.ViewController"></jsp:useBean> <%if (getServletContext().getAttribute("funcionario") == null) {response.sendRedirect("login.jsp");} else{ if (!view.hasRole((Funcionario)getServletContext().getAttribute("funcionario"),Papeis.LEITOR)) {response.sendRedirect("admin.jsp");} else{ %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <%@ page import="models.Funcionario"%> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <jsp:useBean id="model" class="models.Funcionario"></jsp:useBean> <jsp:setProperty property="id" name="model" value="<%=request.getParameter("id") == null ? null : Integer.parseInt(request.getParameter("id"))%>"/> <title><%= model.getId() == null ? "Incluso" : "Alterao"%></title> </head> <body> <h1>Cadastro de Funcionrios</h1> <table border="0">
147
<tr> <td> <form action="/livrariajsp/gravar?cadastro=funcionario" method="post"> <%model = (Funcionario) view.getModel(model);%> Matrcula: <input type="text" name="matricula" value="<%=model.getMatricula()%>"><br/> Nome: <input type="text" name="nome" value="<%=model.getNome()%>"><br/> Apelido: <input type="text" name="apelido" value="<%=model.getApelido()%>"><br/> Senha: <input type="password" name="senha" value="<%=model.getSenha()%>"><br/> e-mail: <input type="text" name="email" value="<%=model.getEmail()%>"><br/> <input type="hidden" name="id" value="<%=request.getParameter("id")%>"> <input type="hidden" name="cadastro" value="funcionario"> <input type="submit" value="Gravar"> <input type="submit" name="retornar" value="Retornar"> </form> </td> <td> <%if (request.getParameter("id") != null){%> <h2>Telefones</h2> <form action="/livrariajsp/gravar?cadastro=telefone_funcionario" method="post"> Telefone: <input type="text" name="numero"><br/> Tipo: <select name="tipo"> <%=view.getTiposTelefone()%> </select> <input type="submit" id="adicionar" value="Adicionar telefone">
148
<input type="hidden" name="id_funcionario" value="<%=request.getParameter("id")%>"> </form> <form action="/livrariajsp/remover?cadastro=telefone_funcionario" method="post"> <select name="id_telefone"> <%=view.getTelefonesFuncionario(request.getParameter("id"))%> </select> <input type="submit" id="remover" value="Remover telefone"> <input type="hidden" name="id_funcionario" value="<%=request.getParameter("id")%>"> </form> <%}%> </td> </tr> </table> </body> </html> <%}}%>
edicao-livro.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ page import="constants.Papeis" %> <%@ page import="models.Funcionario" %> <jsp:useBean id="view" class="view.ViewController"></jsp:useBean> <%if (getServletContext().getAttribute("funcionario") == null) {response.sendRedirect("login.jsp");} else{ if (!view.hasRole((Funcionario)getServletContext().getAttribute("funcionario"),Papeis.LEITOR)) {response.sendRedirect("admin.jsp");} else{ %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <%@ page import="models.Livro"%> <html>
149
<head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <jsp:useBean id="model" class="models.Livro"></jsp:useBean> <jsp:setProperty property="id" name="model" value="<%=request.getParameter("id") == null ? null : Integer.parseInt(request.getParameter("id"))%>"/> <title><%= model.getId() == null ? "Incluso" : "Alterao"%></title> </head> <body> <h1>Cadastro de Livros</h1> <table border="0"> <tr> <td> <h2>Dados Bsicos</h2> <form action="/livrariajsp/gravar?cadastro=livro" method="post"> <%model = (Livro) view.getModel(model);%> ISBN: <input type="text" name="isbn" value="<%=model.getIsbn()%>"><br/> Ttulo: <input type="text" name="titulo" value="<%=model.getTitulo()%>"><br/> Local: <select name="id_local"> <%=view.getOpcoesRelacionadas("local",model.getLocal().getId())%> </select><br/> Editora: <select name="id_editora"> <%=view.getOpcoesRelacionadas("editora",model.getEditora().getId())%> </select><br/> Ano: <input type="text" name="ano" value="<%=model.getAno()%>"><br/> Preo: <input type="text" name="preco" value="<%=String.valueOf(model.getPreco()).replace('.',',')%>"><br/>
150
<input type="hidden" name="id" value="<%=request.getParameter("id")%>"> <input type="hidden" name="cadastro" value="Livro"> <input type="submit" value="Gravar"> <input type="submit" name="retornar" value="Retornar"> </form> </td> <td> <%if (request.getParameter("id") != null){%> <h2>Autores</h2> <form action="/livrariajsp/gravar?cadastro=autor_livro" method="post"> <select name="id_autor"> <%=view.getOpcoesAutores()%> </select> <input type="submit" id="adicionar" value="Adicionar autor"> <input type="hidden" name="id_livro" value="<%=request.getParameter("id")%>"> </form> <form action="/livrariajsp/remover?cadastro=autor_livro" method="post"> <select name="id_autor"> <%=view.getOpcoesAutores(request.getParameter("id"))%> </select> <input type="submit" id="remover" value="Remover autor"> <input type="hidden" name="id_livro" value="<%=request.getParameter("id")%>"> </form> <%}%> </td> </tr> </table> </body> </html> <%}}%>
151
edicao-local.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ page import="constants.Papeis" %> <%@ page import="models.Funcionario" %> <jsp:useBean id="view" class="view.ViewController"></jsp:useBean> <%if (getServletContext().getAttribute("funcionario") == null) {response.sendRedirect("login.jsp");} else{ if (!view.hasRole((Funcionario)getServletContext().getAttribute("funcionario"),Papeis.LEITOR)) {response.sendRedirect("admin.jsp");} else{ %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <%@ page import="models.Local"%> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <jsp:useBean id="model" class="models.Local"></jsp:useBean> <jsp:setProperty property="id" name="model" value="<%=request.getParameter("id") == null ? null : Integer.parseInt(request.getParameter("id"))%>"/> <title><%= model.getId() == null ? "Incluso" : "Alterao"%></title> </head> <body> <h1>Cadastro de Locais</h1> <form action="/livrariajsp/gravar?cadastro=local" method="post"> <%model = (Local) view.getModel(model);%> Nome: <input type="text" name="nome" value="<%=model.getNome()%>"><br/> <input type="hidden" name="id" value="<%=request.getParameter("id")%>"> <input type="hidden" name="cadastro" value="local"> <input type="submit" value="Gravar">
152
ViewController
enum TipoMensagem {EXCEPTION, INFO};
public class ViewController { private ModelFactory modelFactory = new ModelFactory(); private ListaFactory listaFactory = new ListaFactory(); private OpcoesFactory opcoesFactory = new OpcoesFactory();
public IModel getModel(IModel model) { if (model instanceof Autor) { return modelFactory.getAutor((Autor)model); } if (model instanceof Cliente) { return modelFactory.getCliente((Cliente)model); } if (model instanceof Editora) { return modelFactory.getEditora((Editora)model);
153
} if (model instanceof Funcionario) { return modelFactory.getFuncionario((Funcionario)model); } if (model instanceof Livro) { return modelFactory.getLivro((Livro)model); } if (model instanceof Local) { return modelFactory.getLocal((Local)model); } if (model instanceof Telefone) { return modelFactory.getTelefone((Telefone)model); } return null; }
public String getLista(String cadastro, String campoProcurado, String valorProcurado, String ordem, Integer pagina) { return listaFactory.getLista(cadastro,campoProcurado,valorProcurado,ordem,pagina); }
154
public String getOpcoesAutores(String StringId) { if (StringId == null) return opcoesFactory.getOpcoesAutores(-1); else return opcoesFactory.getOpcoesAutores(Integer.parseInt(StringId)); }
155
{ Integer id = Integer.parseInt(paramId);
return opcoesFactory.getTelefonesCliente(id); }
return opcoesFactory.getTelefonesFuncionario(id); }
public String getMessage(TipoMensagem tipo) { if (tipo == null) { return Message.isException() ? Message.getLastException() : (Message.isInfo() ? Message.getLastInfo() : ""); } if (tipo == TipoMensagem.EXCEPTION) { return Message.getLastException(); } if (tipo == TipoMensagem.INFO) {
156
AuthorizationController ac = AuthorizationController.getInstance(funcionario.getApelido());
if (!ac.hasRole(new String[]{papel})) {
return true; } }
ViewController faz uso de trs classes especializadas para prover contedo dinmico para as pginas: ListaFactory, ModelFactory e OpcoesFactory. ListaFactory tem por objetivo prover mtodos para a criao das listagens dos cadastros e dos campos do formulrio de pesquisa.
ListaFactory
public class ListaFactory extends ModelFactory { private Integer registrosPorPagina = 20;
157
public String getLista(String cadastro, String where, String order, Integer pagina) { String tabela = "<table border=\"1\">";
if (cadastro.equals(Cadastros.AUTOR)) { tabela = tabela + getListaAutor(where, order, numeroDeDegistros, pagina); } if (cadastro.equals(Cadastros.CLIENTE)) { tabela = tabela + getListaCliente(where, order, numeroDeDegistros, pagina); } if (cadastro.equals(Cadastros.EDITORA)) { tabela = tabela + getListaEditora(where, order, numeroDeDegistros, pagina); } if (cadastro.equals(Cadastros.FUNCIONARIO)) { tabela = tabela + getListaFuncionario(where, order, numeroDeDegistros, pagina); } if (cadastro.equals(Cadastros.LIVRO)) { tabela = tabela + getListaLivro(where, order, numeroDeDegistros, pagina); } if (cadastro.equals(Cadastros.LOCAL)) { tabela = tabela
158
+ getListaLocal(where, order, numeroDeDegistros, pagina); } if (cadastro.equals(Cadastros.TELEFONE)) { tabela = tabela + getListaTelefone(where, order, numeroDeDegistros, pagina); }
return tabela; }
private String getListaTelefone(String where, String order, Integer numeroDeDegistros, Integer pagina) { String tabela = "";
tabela = tabela + "<thead>"; tabela = tabela + "<th>Id</th>"; tabela = tabela + "<th><a href=\"/livrariajsp/cadastro.jsp?cadastro=" + cadastro + "&ordem=numero\">Nmero</a></th>"; tabela = tabela + "<th><a href=\"/livrariajsp/cadastro.jsp?cadastro=" + cadastro + "&ordem=tipo\">Tipo</a></th>"; tabela = tabela + "<th>Excluir</th>"; tabela = tabela + "</thead>";
int i = 0;
159
tabela = tabela + "<tr>"; tabela = tabela + "<td><a href=\"/livrariajsp/edicao-" + cadastro + ".jsp?id=" + telefone.getId() + "\">" + telefone.getId() + "</a></td>"; tabela = tabela + "<td>" + telefone.getNumero() + "</td>"; tabela = tabela + "<td>" + telefone.getTipo() + "</td>"; tabela = tabela + "<td><a href=\"/livrariajsp/remover?cadastro=" + cadastro + "&id=" + telefone.getId() + "\">X</a></td>"; tabela = tabela + "</tr>"; i++; } return tabela;
private String getListaLocal(String where, String order, Integer numeroDeDegistros, Integer pagina) { String tabela = "";
tabela = tabela + "<thead>"; tabela = tabela + "<th>Id</th>"; tabela = tabela + "<th><a href=\"/livrariajsp/cadastro.jsp?cadastro=" + cadastro + "&ordem=nome\">Nome</a></th>";
160
int i = 0; for (Iterator<Local> iterator = lista.iterator(); iterator.hasNext();) { Local local = (Local) iterator.next();
tabela = tabela + "<tr>"; tabela = tabela + "<td><a href=\"/livrariajsp/edicao-" + cadastro + ".jsp?id=" + local.getId() + "\">" + local.getId() + "</a></td>"; tabela = tabela + "<td>" + local.getNome() + "</td>"; tabela = tabela + "<td><a href=\"/livrariajsp/remover?cadastro=" + cadastro + "&id=" + local.getId() + "\">X</a></td>"; tabela = tabela + "</tr>"; i++; } return tabela; }
private String getListaLivro(String where, String order, Integer numeroDeDegistros, Integer pagina) { String tabela = "";
161
tabela = tabela + "<th><a href=\"/livrariajsp/cadastro.jsp?cadastro=" + cadastro + "&ordem=isbn\">ISBN</a></th>"; tabela = tabela + "<th><a href=\"/livrariajsp/cadastro.jsp?cadastro=" + cadastro + "&ordem=titulo\">Ttulo</a></th>"; tabela = tabela + "<th>Local</th>"; tabela = tabela + "<th>Editora</th>"; tabela = tabela + "<th><a href=\"/livrariajsp/cadastro.jsp?cadastro=" + cadastro + "&ordem=ano\">Ano</a></th>"; tabela = tabela + "<th><a href=\"/livrariajsp/cadastro.jsp?cadastro=" + cadastro + "&ordem=preco\">Preo</a></th>"; tabela = tabela + "<th>Excluir</th>"; tabela = tabela + "</thead>";
int i = 0; for (Iterator<Livro> iterator = lista.iterator(); iterator.hasNext();) { Livro livro = (Livro) iterator.next();
tabela = tabela + "<tr>"; tabela = tabela + "<td><a href=\"/livrariajsp/edicao-" + cadastro + ".jsp?id=" + livro.getId() + "\">" + livro.getId() + "</a></td>"; tabela = tabela + "<td>" + livro.getIsbn() + "</a></td>"; tabela = tabela + "<td>" + livro.getTitulo() + "</a></td>"; tabela = tabela + "<td>" + livro.getLocal().getNome() + "</td>"; tabela = tabela + "<td>" + livro.getEditora().getNome() + "</td>"; tabela = tabela + "<td>" + livro.getAno() + "</td>"; tabela = tabela + "<td>" + livro.getPreco().toString().replace('.', ',') + "</td>"; tabela = tabela + "<td><a href=\"/livrariajsp/remover?cadastro=" + cadastro + "&id=" + livro.getId() + "\">X</a></td>"; tabela = tabela + "</tr>";
162
private String getListaFuncionario(String where, String order, Integer numeroDeDegistros, Integer pagina) { String tabela = "";
tabela = tabela + "<thead>"; tabela = tabela + "<th>Id</th>"; tabela = tabela + "<th><a href=\"/livrariajsp/cadastro.jsp?cadastro=" + cadastro + "&ordem=matricula\">Matrcula</a></th>"; tabela = tabela + "<th><a href=\"/livrariajsp/cadastro.jsp?cadastro=" + cadastro + "&ordem=nome\">Nome</a></th>"; tabela = tabela + "<th><a href=\"/livrariajsp/cadastro.jsp?cadastro=" + cadastro + "&ordem=apelido\">Apelido</a></th>"; tabela = tabela + "<th><a href=\"/livrariajsp/cadastro.jsp?cadastro=" + cadastro + "&ordem=email\">e-mail</a></th>"; tabela = tabela + "<th>Excluir</th>"; tabela = tabela + "</thead>";
int i = 0; for (Iterator<Funcionario> iterator = lista.iterator(); iterator .hasNext();) { Funcionario funcionario = (Funcionario) iterator.next();
163
tabela = tabela + "<tr>"; tabela = tabela + "<td><a href=\"/livrariajsp/edicao-" + cadastro + ".jsp?id=" + funcionario.getId() + "\">" + funcionario.getId() + "</a></td>"; tabela = tabela + "<td>" + funcionario.getMatricula() + "</a></td>"; tabela = tabela + "<td>" + funcionario.getNome() + "</td>"; tabela = tabela + "<td>" + funcionario.getApelido() + "</td>"; tabela = tabela + "<td>" + funcionario.getEmail() + "</td>"; tabela = tabela + "<td><a href=\"/livrariajsp/remover?cadastro=" + cadastro + "&id=" + funcionario.getId() + "\">X</a></td>"; tabela = tabela + "</tr>"; i++; } return tabela; }
private String getListaCliente(String where, String order, Integer numeroDeDegistros, Integer pagina) { String tabela = "";
tabela = tabela + "<thead>"; tabela = tabela + "<th>Id</th>"; tabela = tabela + "<th><a href=\"/livrariajsp/cadastro.jsp?cadastro=" + cadastro + "&ordem=cpf\">CPF</a></th>"; tabela = tabela + "<th><a href=\"/livrariajsp/cadastro.jsp?cadastro="
164
+ cadastro + "&ordem=nome\">Nome</a></th>"; tabela = tabela + "<th><a href=\"/livrariajsp/cadastro.jsp?cadastro=" + cadastro + "&ordem=apelido\">Apelido</a></th>"; tabela = tabela + "<th><a href=\"/livrariajsp/cadastro.jsp?cadastro=" + cadastro + "&ordem=email\">e-mail</a></th>"; tabela = tabela + "<th>Excluir</th>"; tabela = tabela + "</thead>";
int i = 0; for (Iterator<Cliente> iterator = lista.iterator(); iterator.hasNext();) { Cliente cliente = (Cliente) iterator.next();
tabela = tabela + "<tr>"; tabela = tabela + "<td><a href=\"/livrariajsp/edicao-" + cadastro + ".jsp?id=" + cliente.getId() + "\">" + cliente.getId() + "</td>"; tabela = tabela + "<td>" + cliente.getCpf() + "</td>"; tabela = tabela + "<td>" + cliente.getNome() + "</td>"; tabela = tabela + "<td>" + cliente.getApelido() + "</td>"; tabela = tabela + "<td>" + cliente.getEmail() + "</td>"; tabela = tabela + "<td><a href=\"/livrariajsp/remover?cadastro=" + cadastro + "&id=" + cliente.getId() + "\">X</a></td>"; tabela = tabela + "</tr>"; i++; } return tabela; }
public String getLista(String cadastro) { return getLista(cadastro, null, null, null, null); }
165
private String getListaEditora(String where, String order, Integer limit, Integer offset) { String tabela = "";
tabela = tabela + "<thead>"; tabela = tabela + "<th>Id</th>"; tabela = tabela + "<th><a href=\"/livrariajsp/cadastro.jsp?cadastro=" + cadastro + "&ordem=nome\">Nome</a></th>"; tabela = tabela + "<th>Excluir</th>"; tabela = tabela + "</thead>";
int i = 0; for (Iterator<Editora> iterator = lista.iterator(); iterator.hasNext();) { Editora editora = (Editora) iterator.next();
tabela = tabela + "<tr>"; tabela = tabela + "<td><a href=\"/livrariajsp/edicao-" + cadastro + ".jsp?id=" + editora.getId() + "\">" + editora.getId() + "</a></td>"; tabela = tabela + "<td>" + editora.getNome() + "</a></td>"; tabela = tabela + "<td><a href=\"/livrariajsp/remover?cadastro=" + cadastro + "&id=" + editora.getId() + "\">X</a></td>"; tabela = tabela + "</tr>"; i++; }
166
return tabela; }
private String getListaAutor(String where, String order, Integer limit, Integer offset) { String tabela = "";
tabela = tabela + "<thead>"; tabela = tabela + "<th>Id</th>"; tabela = tabela + "<th><a href=\"/livrariajsp/cadastro.jsp?cadastro=" + cadastro + "&ordem=nome\">Nome</a></th>"; tabela = tabela + "<th><a href=\"/livrariajsp/cadastro.jsp?cadastro=" + cadastro + "&ordem=sobrenome\">Sobrenome</a></th>"; tabela = tabela + "<th>Excluir</th>"; tabela = tabela + "</thead>";
int i = 0; for (Iterator<Autor> iterator = lista.iterator(); iterator.hasNext();) { Autor autor = (Autor) iterator.next();
tabela = tabela + "<tr>"; tabela = tabela + "<td><a href=\"/livrariajsp/edicao-" + cadastro + ".jsp?id=" + autor.getId() + "\">" + autor.getId() + "</a></td>"; tabela = tabela + "<td>" + autor.getNome() + "</td>"; tabela = tabela + "<td>" + autor.getSobrenome() + "</td>";
167
tabela = tabela + "<td><a href=\"/livrariajsp/remover?cadastro=" + cadastro + "&id=" + autor.getId() + "\">X</a></td>"; tabela = tabela + "</tr>"; i++; } return tabela; }
public String getLista(String cadastro, String campoProcurado, String valorProcurado, String order, Integer pagina) { String where = null;
if (cadastro.equals(Cadastros.AUTOR)) { where = campoProcurado + " LIKE '%" + valorProcurado + "%'"; } if (cadastro.equals(Cadastros.CLIENTE)) { where = campoProcurado + " LIKE '%" + valorProcurado + "%'"; } if (cadastro.equals(Cadastros.EDITORA)) { where = campoProcurado + " LIKE '%" + valorProcurado + "%'"; } if (cadastro.equals(Cadastros.FUNCIONARIO)) { where = campoProcurado + " LIKE '%" + valorProcurado + "%'"; } if (cadastro.equals(Cadastros.LIVRO)) { where = campoProcurado + " LIKE '%" + valorProcurado + "%'"; } if (cadastro.equals(Cadastros.LOCAL)) {
168
where = campoProcurado + " LIKE '%" + valorProcurado + "%'"; } if (cadastro.equals(Cadastros.TELEFONE)) { where = campoProcurado + " LIKE '%" + valorProcurado + "%'"; }
A finalidade da classe ModelFactory prover mtodos que retornem modelos para as pginas de edio. Esses modelos podem ser vazios se for uma incluso. ModelFactory
public class ModelFactory { public Autor getAutor(Autor model) { AutorDAO dao = new AutorDAO();
169
if (model == null) { model = new Cliente(); model.setApelido(""); model.setCpf(""); model.setEmail(""); model.setNome(""); model.setSenha("");
170
} return model; }
if (model == null) { model = new Funcionario(); model.setApelido(""); model.setEmail(""); model.setMatricula(""); model.setNome(""); model.setSenha(""); } return model; }
171
if (model == null) { model = new Livro(); GregorianCalendar calendar = new GregorianCalendar(); calendar.setTimeInMillis(System.currentTimeMillis()); model.setAno(calendar.get(Calendar.YEAR)); model.setIsbn(""); model.setPreco(0.0f); model.setTitulo(""); model.setEditora(new Editora()); model.setLocal(new Local()); } return model; }
A classe OpcoesFactory serve para prover mtodos que preencham caixas de seleo de formulrios.
OpcoesFactory
public class OpcoesFactory {
172
if (cadastro.equals(Cadastros.AUTOR)) { opcoes = opcoes + "<option value=\"nome\">Nome</option>"; opcoes = opcoes + "<option value=\"sobrenome\">Sobrenome</option>"; } if (cadastro.equals(Cadastros.CLIENTE)) { opcoes = opcoes + "<option value=\"cpf\">CPF</option>"; opcoes = opcoes + "<option value=\"nome\">Nome</option>"; opcoes = opcoes + "<option value=\"apelido\">Apelido</option>"; opcoes = opcoes + "<option value=\"email\">e-mail</option>"; } if (cadastro.equals(Cadastros.EDITORA)) { opcoes = opcoes + "<option value=\"nome\">Nome</option>"; } if (cadastro.equals(Cadastros.FUNCIONARIO)) { opcoes = opcoes + "<option value=\"matricula\">Matrcula</option>"; opcoes = opcoes + "<option value=\"nome\">Nome</option>"; opcoes = opcoes + "<option value=\"apelido\">Apelido</option>"; opcoes = opcoes + "<option value=\"email\">e-mail</option>"; } if (cadastro.equals(Cadastros.LIVRO)) { opcoes = opcoes + "<option value=\"isbn\">ISBN</option>"; opcoes = opcoes + "<option value=\"titulo\">Ttulo</option>"; opcoes = opcoes + "<option value=\"ano\">Ano</option>"; } if (cadastro.equals(Cadastros.LOCAL)) { opcoes = opcoes + "<option value=\"nome\">Local</option>"; } if (cadastro.equals(Cadastros.TELEFONE)) { opcoes = opcoes + "<option value=\"numero\">Nmero</option>"; opcoes = opcoes + "<option value=\"tipo\">Tipo</option>"; }
173
return opcoes; }
for (Iterator<Local> iterator = locais.iterator(); iterator .hasNext();) { Local local = (Local) iterator.next(); opcoes = opcoes + "<option value=\"" + local.getId() + "\" " + (selecionado == null ? "" : (local.getId() == selecionado ? "selected" : "")) + ">" + local.getNome() + "</option>"; } } if (cadastro.equals(Cadastros.EDITORA)) { List<Editora> editoras = new EditoraDAO().fetchAll(null, "nome", null, null);
for (Iterator<Editora> iterator = editoras.iterator(); iterator .hasNext();) { Editora editora = (Editora) iterator.next();
174
opcoes = opcoes + "<option value=\"" + editora.getId() + "\" " + (selecionado == null ? "" : (editora.getId() == selecionado ? "selected" : "")) + ">" + editora.getNome() + "</option>"; } }
return opcoes; }
for (Iterator<Autor> iterator = autores.iterator(); iterator .hasNext();) { Autor autor = (Autor) iterator.next();
175
for (Iterator<AutorLivro> iterator = autoresLivro.iterator(); iterator .hasNext();) { AutorLivro autorLivro = (AutorLivro) iterator.next();
opcoes = opcoes + "<option value=\"" + autorLivro.getAutor().getId() + "\">" + autorLivro.getAutor().getSobrenome() + "," + autorLivro.getAutor().getNome() + "</option>"; } } return opcoes; }
for (int i = 0; i < tipos.length; i++) { Field field = tipos[i]; String tipo = ""; try { tipo = (String) field.get(null); } catch (IllegalArgumentException e) {
176
} catch (IllegalAccessException e) { } opcoes = opcoes + "<option value=\"" + tipo + "\">" + tipo + "</option>"; }
return opcoes; }
public String getTelefonesCliente(Integer id) { String opcoes = ""; TelefoneClienteDAO dao = new TelefoneClienteDAO();
for (Iterator<TelefoneCliente> iterator = telefones.iterator(); iterator .hasNext();) { TelefoneCliente telefoneCliente = (TelefoneCliente) iterator.next(); opcoes = opcoes + "<option value=\"" + telefoneCliente.getTelefone().getId() + "\">" + telefoneCliente.getTelefone().getNumero() + " - " + telefoneCliente.getTelefone().getTipo() + "</option>";
} return opcoes; }
public String getTelefonesFuncionario(Integer id) { String opcoes = ""; TelefoneFuncionarioDAO dao = new TelefoneFuncionarioDAO();
177
for (Iterator<TelefoneFuncionario> iterator = telefones.iterator(); iterator .hasNext();) { TelefoneFuncionario telefoneFuncionario = (TelefoneFuncionario) iterator .next(); telefone.setId(telefoneFuncionario.getTelefone().getId()); telefone = (Telefone) telefoneDao.fetchOne(telefone); opcoes = opcoes + "<option value=\"" + telefone.getId() + "\">" + telefone.getNumero() + " - " + telefone.getTipo() + "</option>";
} return opcoes; }
for (Iterator<Funcionario> iterator = funcionarios.iterator(); iterator .hasNext();) { Funcionario funcionario = (Funcionario) iterator.next(); opcoes = opcoes + "<option value=\"" + funcionario.getId() + "\">"
178
return opcoes; }
4.11 Concluses
Terminamos assim a implementao das trs camadas propostas na seo 4.7: Persistncia, Controle e Viso. A estrutura final de pacotes da aplicao pode ser vista na figura 17.
Essa implementao torna funcionais os casos de uso Manter Cadastro, Autenticar Usuario e Verificar Permisso de Funcionrio. A arquitetura adotada procurou reutilizar o mximo de estruturas, para facilitar a manuteno futura e permitir que o sistema cresa. Contudo, percebe-se que a maior parte do cdigo que implementamos no o que realmente interessa para o cliente, mas sim o necessrio para a aplicao funcionar. E apesar de tentarmos generalizar diversas pores de cdigo, ainda h muita repetio. O prximo captulo mostra como Hibernate e JSF j trazem prontas funcionalidades que tivemos de implementar neste captulo. Uma nica observao. A figura 17 mostra um pacote util, que no foi tratado aqui. Sua finalidade armazenar classes utilitrias que sejam usadas por todas as camadas. Como no implementamos todos os casos de uso, ele terminou por ficar vazio.
180
5.1 Hibernate
Segundo Bauer e King (2007, p. 31), Hibernate uma ferramenta de mapeamento objeto relacional [ORM] completa que fornece todos os benefcios de um ORM. Esses benefcios so: produtividade, manutenibilidade, performance e independncia de fornecedor. Ainda segundo os autores, o Hibernate Core o servio bsico para a persistncia. Ele faz mapeamento utilizando arquivos XML e possui uma linguagem de consulta chamada HQL, assim como interfaces de consulta programtica para consultas do tipo Criteria e Example. A partir de JDK 5.0, Hibernate criou uma nova maneira de definir o mapeamento objeto-relacional usando anotaes. O pacote Hibernate Annotations, usado em cima do Core, reduz significativamente a quantidade de linhas de cdigo necessrias para fazer ORM. Por isso, utilizaremos Hibernate Annotations em nossa implementao. Os conceitos necessrios para utilizao do Hibernate sero vistos conforme criarmos a camada de Persistncia. A verso do Hibernate a ser utilizada aqui a 3.3.1.
5.2 JSF
Segundo Kurniawan (2004, p. XIII), JavaServer Faces (JSF) uma tecnologia nova para construo de aplicativos Web em Java, cuja especificao foi desenvolvida pelo grupo de peritos JSR-127 sob a superviso do Java Community Process.
181
JSF baseada nas tecnologias Servlet e JavaServer Pages. Um framework que implementa JSF realiza muito do trabalho que o programador teria de fazer manualmente se utilizasse somente servlets e JSP. De acordo com Geary e Horstmann (2007, p. 3), JSF define um conjunto de componentes pr-fabricados de interface de usurio, um modelo de programao orientado a eventos e um modelo de componentes que permite a adio de novos componentes por desenvolvedores independentes. O funcionamento de JSF se baseia no redirecionamento das requisies para um servlet chamado FacesServlet. Esse redirecionamento feito no arquivo web.xml. FacesServlet cria um objeto chamado FacesContext, que por sua vez contm os objetos ServletContext, ServletRequest e ServletResponse, que lhe so passados pelo continer Web. Em seguida, FacesServlet cria o objeto Lyfecycle, que processa FacesContext em seis fases. So elas:
Reconstituio da rvore de componentes; Aplicao dos valores da requisio; Processamento de validaes; Atualizao de valores de modelos; Invocao do aplicativo; Renderizao da resposta.
JSF permite que o programador crie ouvidores (listeners) que so verificados entre quaisquer duas fases do ciclo de vida de processamento da requisio. Esses ouvidores podem inclusive alterar o curso do fluxo de processamento. A utilizao de JSF ser vista durante a implementao do projeto deste captulo. A verso da especificao a ser utilizada aqui a 1.2.
182
183
184
A implementao de referncia no o melhor produto de software que pde ser produzido a partir da especificao, mas sim aquele que est totalmente de acordo com ela e que funciona. A especificao cria um padro e estabelece um mnimo de funcionalidades. Mas isso no impede que cada fornecedor crie uma implementao que possua mais recursos do que os exigidos por uma JSR. Dessa forma, os consumidores ficam protegidos, pois a JSR assegura que todas as implementaes iro operar da mesma maneira. A escolha por uma ou outra implementao no significa aprisionamento de software, desde que, claro, a aplicao crie dependncias apenas para os recursos previstos pela especificao. Os fornecedores de software, por sua vez, podem concorrer entre si oferecendo funcionalidades que extrapolem as definidas na JSR, e criando ferramentas auxiliares. Aps essa defesa do modelo tecnolgico do JCP, vamos incluir as bibliotecas JSF em nossa aplicao.
185
Selecione User Library na caixa de seleo Type e clique no boto Download libraries... O Eclipse ir fazer uma busca e mostrar uma tela (figura 20), onde voc pode escolher uma biblioteca JSF de sua preferncia. Ns selecionaremos o Mojarra para o nosso projeto. Aps aceitar a licena, basta aguardar o download.
186
http://www.jboss.org/tools/download. A verso utilizada aqui a 3.1, para o Eclipse 3.5.2. A instalao de plug-ins no Eclipse torna-se bem simples quando se usa update sites. Por meio do menu Help->Install New Software, possvel indicar uma URL a partir da qual o Eclipse ir baixar e instalar um plug-in. Os update sites do JBoss Tools esto disponveis na pgina de downloads, j citada anteriormente. Aps instalar JBoss Tools, vamos selecionar o projeto livrariajsf e acessar o menu New->Other. Localize o item Hibernate. Abra-o e selecione Hibernate Console Configuration, conforme mostra a figura 22.
Esse item ir abrir uma tela que nos ajudar a criar os arquivos de configurao do Hibernate. Primeiro temos de selecionar o tipo de configurao. Iremos usar anotaes nas classes para fazer o mapeamento objeto-relacional, por isso selecionaremos no campo Type a opo Annotations (jdk 1.5+). Precisamos indicar uma conexo com banco de dados. Isso feito no frame Database Connection. Se j no houver uma conexo criada, use o boto New para criar uma. possvel testar facilmente a nova conexo por meio de um boto da tela de dados de conexo. No frame Property file, criaremos o arquivo de propriedades do Hibernate. Basta clicar no boto Setup desse frame e escolher a opo Create new. Tome cuidado para criar o arquivo dentro do diretrio src do projeto, que est no classpath. No frame Configuration File, criaremos o arquivo de configurao do Hibernate. Clique no boto Setup e ele abrir um formulrio no qual colocaremos os dados de configurao, conforme mostra a figura 23. O campo Session factory name no obrigatrio. Vamos preeench-lo apenas para mostrar no arquivo de configurao o que gerado. O imprescindvel no formulrio o preenchimento dos campos Database dialect, Username e Password. No campo Database dialect selecionamos qual dialeto SQL ser utilizado. Neste caso, ser PostgreSQL. A escolha do dialeto provoca o preenchimento dos campos Driver class e Connection URL. Este ltimo traz o modelo da URL de conexo, preciso complet-lo com o nome do banco de dados. Aps preencher o formulrio do arquivo de configurao, devemos ter uma tela igual da figura 24. Observe que os dois arquivos de configurao esto no diretrio src do projeto, para serem localizados Basta clicar em Finish e os arquivos hibernate.cfg.xml e hibernate.properties sero criados na raiz do projeto. Isso configura o Hibernate, mas no o instala. O modo ideal de fazer a instalao do Hibernate usado Maven, mas este assunto ficar para o prximo captulo. Por ora, vamos baixar manualmente o Hibernate e configurar o Build Path do projeto para localizar as bibliotecas. necessrio baixar dois arquivos a partir de http://sourceforge.net/projects/hibernate/files, um contendo o ncleo do Hibernate e outro que contm o suporte a anotaes. O primeiro est na pasta hibernate3, e o segundo na pasta hibernate-annotations. Iremos baixar a verso 3.3.1GA de ambos. Faa o download dos arquivos e os descompacte em um diretrio de sua preferncia. Depois, selecione o projeto livrariajsf, entre no menu popup Build Path->Configure Build Path. Na aba Libraries, clique em Add External jars e importe todos os arquivos .jar do Hibernate.
188
Tome cuidado, porque h arquivos .jar na raiz das instalaes e dentro do diretrio lib de cada uma. No caso do Hibernate Core, inclua os .jar dos subdiretrios required e bytecode. Do subdiretrio optional, inclua o jar de ehcache.
189
190
191
Um grande problema da linguagem Java que ela sozinha no faz nada. Para fazer aplicaes teis, voc precisa de APIs que usam outras APIs, que por sua vez usam outras APIs. Esse controle de quem depende de quem algo muito complexo, que se for feito sem uma ferramenta adequada pode causar muita dor de cabea. Ns faremos a incluso manual das dependncias neste projeto para deixar clara a dificuldade. O meio mais elegante para fazer gesto de dependncias ser mostrado no prximo captulo. O Hibernate tem algumas dependncias de APIs de terceiros. So elas: SLF4, Log4J e Commons Logging. Elas podem ser obtidas, respectivamente nos seguintes endereos:
As verses necessrias para o Hibernate aqui utilizado so: SLF4 1.5.2, Log4J 1.2.14 e Commons Logging 1.1.1. Aps descompactar essas bibliotecas em um diretrio de sua preferncia, selecione o projeto livrariajsf e entre no menu popup Build Path->Configure Build Path. Na aba Libraries, clique em Add External jars e importe todos os arquivos .jar das trs APIs citadas. Para ter certeza de que no falta nada, listamos a seguir os arquivos .jar necessrios para o funcionamento do Hibernate, com o caminho diretrio completo da instalao:
hibernate-distribution-3.3.1.GA/hibernate3.jar hibernate-distribution-3.3.1.GA/lib/required/antlr-2.7.6.jar hibernate-distribution-3.3.1.GA/lib/required/commons-collections-3.1.jar hibernate-distribution-3.3.1.GA/lib/required/dom4j-1.6.1.jar hibernate-distribution-3.3.1.GA/lib/required/javassist-3.4.GA.jar hibernate-distribution-3.3.1.GA/lib/required/jta-1.1.jar hibernate-distribution-3.3.1.GA/lib/required/slf4j-api-1.5.2.jar hibernate-distribution-3.3.1.GA/hibernate-testing.jar hibernate-annotations-3.3.1.GA/hibernate-annotations.jar hibernate-annotations-3.3.1.GA/lib/ejb3-persistence.jar hibernate-annotations-3.3.1.GA/lib/hibernate-commons-annotations.jar
192
193
campo na tabela mapeada pela classe concreta que estende AbstractOnlyName. Com o parmetro length definimos o comprimento do campo.
AbstractOnlyName
@MappedSuperclass public abstract class AbstractOnlyName implements IModel { @Column(name = "nome", length = 30) protected String nome; public String getNome() { return nome; } public void setNome(String nome) { this.nome = nome; } }
A classe AbstractUsuario agora estende AbstractOnlyName e tambm utiliza as anotaes @MappedSuperclass e @Column.
AbstractUsuario
@MappedSuperclass public abstract class AbstractUsuario extends AbstractOnlyName { @Column(name = "apelido", length = 10) protected String apelido;
194
J tivemos uma reduo de duas classes, em relao aplicao anterior. E ao contrrio dela, construda com JDBC, desta vez no precisaremos de uma classe abstrata para definir o atributo telefone. O motivo que no haver necessidade de classes auxiliares para definir os relacionamentos das tabelas, porque o Hibernate faz isso com o uso de anotaes, que so metadados que afetam o modo pelo qual os programas Java so tratados por ferramentas e bibliotecas.
195
Iremos daqui para frente mostrar como ficam os modelos usando anotaes. Aps a apresentao do cdigo-fonte, sero discutidas as anotaes utilizadas na respectiva classe. Todas as anotaes utilizadas doravante so do pacote javax.persistence. Comecemos pelo modelo Autor. A nica modificao, alm das anotaes, no construtor. Todos os atributos, exceto o que mapeia a chave primria, devem ser inicializados, para que objeto possa ser manipulado pelo JSF.
Autor
@Entity @Table(name = "autores") @SequenceGenerator(name = "AutorSequence", sequenceName = "autores_id_autor_seq") public class Autor extends AbstractOnlyName implements Comparator<Autor> {
@Id @GeneratedValue(generator = "AutorSequence", strategy = GenerationType.SEQUENCE) @Column(name = "id_autor", columnDefinition = "serial") private Integer id;
196
this.sobrenome = sobrenome; } public int compare(Autor autor1, Autor autor2) { return (autor2.getSobrenome().compareTo(autor1.getSobrenome())); } public void setNome(String nome) { this.nome = nome; } public String getNome() { return nome; } public void setId(Integer id) { this.id = id; }
A anotao @Entity identifica essa classe como uma entidade, ou seja, os seus dados so provenientes de uma entidade do banco de dados, de uma tabela. A anotao @Table, por meio do parmetro name, identifica o nome da tabela no banco de dados. A anotao @SequenceGenerator define uma sequncia, um elemento do banco de dados responsvel pela gerao de sries numricas baseadas em uma regra. O parmetro name cria um nome para ser utilizado pela classe. O parmetro @sequenceName leva o nome da sequncia no banco de dados. A anotao @Id identifica qual atributo est mapeando a chave primria da tabela. Conforme j vimos, @Column identifica os atributos que mapeiam campos de uma tabela. A anotao @GeneratedValue indica ao Hibernate que o atributo em questo ter seus valores gerados automaticamente. A fonte dos valores definida pelo parmetro generator, que neste caso AutorSequence, especificado por @SequenceGenerator. O parmetro strategy indica como o valor ser criado pelo gerador.
197
A anotao @columnDefinition serve para forarmos o Hibernate a criar um campo exatamente da maneira como queremos. No caso, no queremos que o Hibernate crie um campo do tipo inteiro (o que ele vai fazer baseado no tipo de dados Java), mas sim um campo do tipo serial do Postgresql. O campo serial tambm inteiro, mas tem uma sequncia associada a ele. Vamos agora para o prximo modelo, Cliente. Cliente
@Entity @Table(name = "clientes") @SequenceGenerator(name = "ClienteSequence", sequenceName = "clientes_id_cliente_seq") public class Cliente extends AbstractUsuario { @Id @GeneratedValue(generator = "ClienteSequence", strategy = GenerationType.SEQUENCE) @Column(name = "id_cliente", columnDefinition = "serial") private Integer id;
@ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY) @JoinTable(name = "telefones_cliente", joinColumns = @JoinColumn(name = "id_cliente", columnDefinition = "integer NOT NULL"), inverseJoinColumns = @JoinColumn(name = "id_telefone", columnDefinition = "integer NOT NULL")) protected Set<Telefone> telefones = new HashSet<Telefone>();
public Cliente() { this.apelido = ""; this.cpf = ""; this.email = ""; this.nome = ""; this.senha = ""; this.telefones = new HashSet<Telefone>(); }
198
public String getCpf() { return cpf; } public void setCpf(String cpf) { this.cpf = cpf; } public void setId(Integer id) { this.id = id; } public Integer getId() { return id; } public Set<Telefone> getTelefones() { return telefones; } public void setTelefones(Set<Telefone> telefones) { this.telefones = telefones; } }
A anotao @ManyToMany indica que o atributo representa um relacionamento de muitos-para-muitos. O argumento cascade configura as operaes em cascata, ou seja, quais operaes sobre a tabela principal tero repercusso na tabela relacionada. A constante CascadeType.ALL informa que todas as operaes na tabela principal, incluindo atualizao e remoo, sero propagadas para as tabelas dependentes. Neste caso, se um cliente for removido, todos os seus telefones tambm sero removidos. O argumento fetch informa ao Hibernate como ser a recuperao dos dados das tabelas dependentes. Voc pode carregar todos os dados relacionados de uma vez, ou busc-los sob demanda. A constante FetchType.LAZY informa que a busca dos registros dependentes s ser feita quando o atributo for lido. Ou seja, para este caso, o atributo telefones no ser imediatamente populado quando um objeto Cliente for recuperado, mas apenas quando for invocado seu mtodo de leitura.
199
A anotao @JoinTable complementa @ManyToMany, trazendo as sobre a tabela que realiza o relacionamento. O argumento name define o nome da tabela de relacionamento. O argumento joinColumns define quais sero os campos na tabela de relacionamento que sero a chave primria da tabela principal (a tabela da classe que est definindo o relacionamento). A anotao @JoinColumn, similar a a @Column, permite informar todos os detalhes sobre um campo, incluindo nome e tipo de dados. O argumento name dessa anotao indica o nome da chave estrangeira. O argumento inverseJoinColumns define quais sero os campos na tabela de relacionamento que sero a chave primria da tabela dependente. As anotaes da classe Funcionario so exatamente as mesmas de cliente.
Funcionario
@Entity @Table(name = "funcionarios") @SequenceGenerator(name = "FuncionarioSequence", sequenceName = "funcionarios_id_usuario_seq") public class Funcionario extends AbstractUsuario { @Id @GeneratedValue(generator = "FuncionarioSequence", strategy = GenerationType.SEQUENCE) @Column(name = "id_usuario", columnDefinition = "serial") private Integer id;
@ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY) @JoinTable(name = "telefones_funcionario", joinColumns = @JoinColumn(name = "id_cliente", columnDefinition = "integer NOT NULL"), inverseJoinColumns = @JoinColumn(name = "id_funcionario", columnDefinition = "integer NOT NULL")) protected Set<Telefone> telefones = new HashSet<Telefone>();
public Funcionario() {
200
this.apelido = ""; this.email = ""; this.matricula = ""; this.nome = ""; this.senha = ""; this.telefones = new HashSet<Telefone>(); }
201
Editora
@Entity @Table(name = "editoras") @SequenceGenerator(name = "EditoraSequence", sequenceName = "editoras_id_editora_seq") public class Editora extends AbstractOnlyName { @Id @GeneratedValue(generator = "EditoraSequence", strategy = GenerationType.SEQUENCE) @Column(name = "id_editora", columnDefinition = "serial") private Integer id;
Local
@Entity @Table(name = "locais") @SequenceGenerator(name = "LocalSequence", sequenceName = "locais_id_local_seq") public class Local extends AbstractOnlyName { @Id @GeneratedValue(generator = "LocalSequence", strategy = GenerationType.SEQUENCE)
202
Livro
@Entity @Table(name = "livros") @SequenceGenerator(name = "LivroSequence", sequenceName = "livros_id_livro_seq") public class Livro implements IModel {
@Id @GeneratedValue(generator = "LivroSequence", strategy = GenerationType.SEQUENCE) @Column(name = "id_livro", columnDefinition = "serial") protected Integer id;
203
@OneToOne @JoinColumn(name = "id_local", referencedColumnName = "id_local", columnDefinition = "integer NOT NULL") private Local local;
@OneToOne @JoinColumn(name = "id_editora", referencedColumnName = "id_editora", columnDefinition = "integer NOT NULL") private Editora editora;
@ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY) @JoinTable(name = "autores_livro", joinColumns = @JoinColumn(name = "id_autor", columnDefinition = "integer NOT NULL"), inverseJoinColumns = @JoinColumn(name = "id_livro", columnDefinition = "integer NOT NULL")) private Set<Autor> autores = new HashSet<Autor>();
public Livro() { this.ano = 0; this.autores = new HashSet<Autor>(); this.editora = new Editora(); this.isbn = ""; this.local = new Local(); this.preco = 0.0f; this.titulo = ""; }
204
205
206
this.id = id; } }
A anotao @OneToOne define um relacionamento de um-para-um. As informaes sobre o relacionamento so dadas pela anotao @JoinColumn, j vista anteriormente. Aqui necessrio configurar o argumento referencedColumnName, que o nome da chave primria na tabela dependente.
Pedido
@Entity @Table(name = "pedidos") public class Pedido implements IModel { @Id @Column(name = "numero_pedido") private Integer numeroPedido;
@OneToOne @JoinColumn(name = "id_cliente", referencedColumnName = "id_cliente", columnDefinition = "integer NOT NULL") private Cliente cliente;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, targetEntity = ItemPedido.class) private Set<ItemPedido> items = new HashSet<ItemPedido>();
public Pedido() { this.cliente = new Cliente(); this.dataPedido = Calendar.getInstance(); this.items = new HashSet<ItemPedido>();
207
this.numeroPedido = 0; }
208
A anotao @Temporal especfica para tratar campos de data e hora. A constante TemporalType.DATE indica que o campo ter o tipo de dados de data padro do banco de dados utilizado. A anotao @OneToMany indica um relacionamento de um-para-muitos. O argumento targetEntity configura a classe que mapeia a tabela dependente.
ItemPedido
@Entity @Table(name = "itens_pedido") @SequenceGenerator(name = "ItemPedidoSequence", sequenceName = "itens_pedido_id_item_seq", allocationSize = 1) public class ItemPedido implements IModel { @Id @GeneratedValue(generator = "ItemPedidoSequence", strategy = GenerationType.SEQUENCE) @Column(name = "id_item", columnDefinition = "serial") private Integer id;
@OneToOne @JoinColumn(name = "id_pedido", referencedColumnName = "numero_pedido", columnDefinition = "integer NOT NULL") private Pedido pedido;
209
@OneToOne @JoinColumn(name = "id_livro", referencedColumnName = "id_livro", columnDefinition = "integer NOT NULL") private Livro livro;
public ItemPedido() { this.livro = new Livro(); this.pedido = new Pedido(); this.preco = 0.0f; this.quantidade = 0; }
210
this.preco = preco; }
Telefone
@Entity @Table(name = "telefones")
211
@Id @GeneratedValue(generator = "TelefoneSequence", strategy = GenerationType.SEQUENCE) @Column(name = "id_telefone", columnDefinition = "serial") private Integer id;
212
Como os relacionamentos foram definidos por meio de anotaes, os trs modelos auxiliares criados na aplicao anterior so desnecessrios. E assim, eliminamos mais trs classes.
213
throw new ExceptionInInitializerError(ex); } } public static final ThreadLocal<Session> session = new ThreadLocal<Session>();
public static Session currentSession() { Session s; try { s = (Session) session.get(); if (s == null) { s = sessionFactory.openSession(); // Armazena na varivel ThreadLocal
session.set(s);
} } catch (HibernateException e) { SessionFactory exceptionFactory = new AnnotationConfiguration() .configure().buildSessionFactory(); s = exceptionFactory.openSession();
session.set(s);
}
// Abre uma nova Session, se esta thread ainda no tem nenhuma return s; }
public static void closeSession() throws HibernateException { Session s = (Session) session.get(); if (s != null) s.close();
session.set(null);
}
214
O mtodo ultimoNumeroDoPedido de DAOEngine no ser mais necessrio, porque o Hibernate atualiza automaticamente o atributo id do objeto Pedido com o valor criado no registro aps a incluso. A interface IDAO ser mantida, sem modificaes: IDAO
public interface IDAO { public boolean insert(IModel model); public boolean update(IModel model); public boolean delete(IModel model); public IModel fetchOne(IModel model); public List<? extends IModel> fetchAll(String where, String order, Integer limit, Integer offset); }
Desta vez, a classe AbstractDAO implementa a interface IDAO, o que faz com que as classes DAO precisem apenas herdar da primeira. No mais necessrio um atributo para guardar o nome da tabela (nem um mtodo para recuper-lo), pois essa informao est no modelo, por meio de anotao. Agora possvel definir um mtodo genrico de busca, utilizando a linguagem de consulta orientada a objetos do Hibernate, o HQL. Tambm no mais necessrio um mtodo para recuperar os nomes dos campos (getFields), pois o Hibernate cuida disso.
AbstractDAO
public abstract class AbstractDAO implements IDAO {
/** * Mtodo padro para incluir registros * * @param model */ public boolean insert(IModel model) { return save(model); }
215
/** * Mtodo padro para atualizar registros * * @param model */ public boolean update(IModel model) { return save(model); }
private boolean save(IModel model) { Session session = HibernateUtil.currentSession(); Transaction tx = session.beginTransaction(); try { session.saveOrUpdate(model); session.flush(); tx.commit(); return true; } catch (Exception e) { Message.setLastException(e.getMessage()); tx.rollback(); return false; }
/** * Mtodo padro para remover registros * * @param model */ public boolean delete(IModel model) {
216
Session session = HibernateUtil.currentSession(); Transaction tx = session.beginTransaction(); try { session.delete(model); session.flush(); session.evict(model); tx.commit(); return true; } catch (Exception e) { Message.setLastException(e.getMessage()); tx.rollback(); return false; } }
/** * Mtodo para obter a condio padro para consultas * * @param model * @return */ public String getDefaultWhere(IModel model) { return "id=" + model.getId(); }
public List<?> fetchAll(String entity, String where, String order, Integer limit, Integer offset) { if (where == null) { where = ""; } else { where = " where " + where;
217
if (order == null) { order = ""; } else { order = " order by " + order; }
return hqlQuery.list(); } }
As classes DAO agora ficam muito mais simples, porque precisam implementar apenas dois mtodos, sendo que o mtodo fetchOne uma extrao de dados de fetchAll, e este ltimo apenas invoca o mtodo de consulta da superclasse com o argumento do nome do modelo.
AutorDAO
public class AutorDAO extends AbstractDAO { @SuppressWarnings("unchecked")
218
public List<Autor> fetchAll(String where, String order, Integer limit, Integer offset) {
try { return (List<Autor>) super.fetchAll("Autor",where, order, limit, offset); } catch (Exception e) { e.printStackTrace(); }
return null; }
List<Autor> list = this.fetchAll(where, null, null, null); if (list != null && list.size() > 0) return list.get(0); else return null; } }
219
ClienteDAO
public class ClienteDAO extends AbstractDAO { @SuppressWarnings("unchecked") public List<Cliente> fetchAll(String where, String order, Integer limit, Integer offset) { try { return (List<Cliente>) super.fetchAll("Cliente", where, order, limit, offset); } catch (Exception e) { e.printStackTrace(); }
return null; }
List<Cliente> list = this.fetchAll(where, null, null, null); if (list != null && list.size() > 0) return list.get(0); else return null; } }
220
EditoraDAO
public class EditoraDAO extends AbstractDAO { public EditoraDAO() { }
@SuppressWarnings("unchecked") public List<Editora> fetchAll(String where, String order, Integer limit, Integer offset) { try { return (List<Editora>) super.fetchAll("Editora", where, order, limit, offset); } catch (Exception e) { e.printStackTrace(); }
return null; }
List<Editora> list = this.fetchAll(where, null, null, null); if (list != null && list.size() > 0) return list.get(0); else return null; } }
221
FuncionarioDAO
public class FuncionarioDAO extends AbstractDAO { @SuppressWarnings("unchecked") public List<Funcionario> fetchAll(String where, String order, Integer limit, Integer offset) { try { return (List<Funcionario>) super.fetchAll("Funcionario", where, order, limit, offset); } catch (Exception e) { e.printStackTrace(); }
return null; }
List<Funcionario> list = this.fetchAll(where, null, null, null); if (list != null && list.size() > 0) return list.get(0); else return null; } }
222
223
ItemPedidoDAO
public class ItemPedidoDAO extends AbstractDAO { @SuppressWarnings("unchecked") public List<ItemPedido> fetchAll(String where, String order, Integer limit, Integer offset) { try { return (List<ItemPedido>) super.fetchAll("ItemPedido", where, order, limit, offset); } catch (Exception e) { e.printStackTrace(); }
return null; }
List<ItemPedido> list = this.fetchAll(where, null, null, null); if (list != null && list.size() > 0) return list.get(0); else return null; }
224
LivroDAO
public class LivroDAO extends AbstractDAO { @SuppressWarnings("unchecked") public List<Livro> fetchAll(String where, String order, Integer limit, Integer offset) { try { return (List<Livro>) super.fetchAll("Livro", where, order, limit, offset); } catch (Exception e) { e.printStackTrace(); }
return null; }
List<Livro> list = this.fetchAll(where, null, null, null); if (list != null && list.size() > 0) return list.get(0); else return null; } }
225
LocalDAO
public class LocalDAO extends AbstractDAO { @SuppressWarnings("unchecked") public List<Local> fetchAll(String where, String order, Integer limit, Integer offset) { try { return (List<Local>) super.fetchAll("Local", where, order, limit, offset); } catch (Exception e) { e.printStackTrace(); }
return null; }
List<Local> list = this.fetchAll(where, null, null, null); if (list != null && list.size() > 0) return list.get(0); else return null; } }
226
PedidoDAO
public class PedidoDAO extends AbstractDAO { public String getDefaultWhere(IModel model) { return "numeroPedido=" + model.getId(); } @SuppressWarnings("unchecked") public List<Pedido> fetchAll(String where, String order, Integer limit, Integer offset) { try { return (List<Pedido>) super.fetchAll("Pedido", where, order, limit, offset); } catch (Exception e) { e.printStackTrace(); }
return null; }
List<Pedido> list = this.fetchAll(where, null, null, null); if (list != null && list.size() > 0) return list.get(0); else return null; } }
227
TelefoneDAO
public class TelefoneDAO extends AbstractDAO { @SuppressWarnings("unchecked") public List<Telefone> fetchAll(String where, String order, Integer limit, Integer offset) { try { return (List<Telefone>) super.fetchAll("Telefone", where, order, limit, offset); } catch (Exception e) { e.printStackTrace(); }
return null; }
List<Telefone> list = this.fetchAll(where, null, null, null); if (list != null && list.size() > 0) return list.get(0); else return null; } }
No precisamos mais de DAOs para tratar os relacionamentos, pois estes so tratados dentro dos DAOs dos modelos principais, que os definem com anotaes. Assim, eliminamos mais trs classes.
228
TestAutorDAO
public class TestAutorDAO extends TestCase { private static Autor autor; private static Autor autorRecuperado; private static AutorDAO autorDAO = new AutorDAO();
TestAutorDAO.autorDAO.insert(autor);
assertNotNull(autorRecuperado.getId());
System.out.println(autor);
229
autor.setNome("Carlos"); autor.setSobrenome("Gomes");
autorDAO.update(autor);
assertEquals(autor.getId(), autorRecuperado.getId());
System.out.println(autor); }
autorDAO.delete(autor);
assertNull(autorRecuperado);
}
230
IManagedBean
public interface IManagedBean { public String gravar(); public String remover(); public ArrayList<? extends IModel> getLista(); }
Como j sabemos que teremos de verificar a autenticao do usurio, e isso feito na camada de controle, vamos criar uma classe abstrata para os ManagedBeans que faa essa verificao.
AuthController
public abstract class AuthController {
231
O construtor invoca o mtodo isAuthenticated se receber um valor verdadeiro, indicado que deve ser verificada a autenticao. Caso o usurio no esteja autenticado, lanada uma exceo. O mtodo isAuthenticated recupera os dados do usurio da sesso, por meio da classe de contexto do JSF (FacesContext). Agora podemos criar uma classe abstrata para os ManagedBeans que implemente a interface ImanagedBean e herde AuthController.
AbstractManagedBean
public abstract class AbstractManagedBean extends AuthController implements IManagedBean { protected IModel bean = null; protected IDAO dao = null; protected String order = null; protected String where = null; protected String searchKey = null;
232
public String gravar() { if (this.bean.getId() == null) { this.dao.insert(this.bean); } else { this.dao.update(this.bean); } try { this.bean = bean.getClass().newInstance(); } catch (InstantiationException e) { Message.setLastException(e.getMessage()); this.bean = null; } catch (IllegalAccessException e) { Message.setLastException(e.getMessage()); this.bean = null; } return new CadastroMB().getCadastro(); }
public String remover() { try { this.dao.delete(this.bean); this.bean = bean.getClass().newInstance(); } catch (InstantiationException e) { Message.setLastException(e.getMessage()); } catch (IllegalAccessException e) { Message.setLastException(e.getMessage()); } catch (Exception e) {
233
234
protected void prepareWhere() { this.where = this.where == null ? null : this.where+" like '%"+this.searchKey+"%'"; }
Perceba, comparativamente, que essa classe AbstractManagedBean muito mais simples do que os servlets GravarSelect e RemoverServlet. S que os ManagedBeans no herdaro diretamente de AbstractManagedBean. Devemos lembrar tambm que precisamos fazer o controle de acesso aos recursos da
235
aplicao. Por isso, iremos definir uma classe que contenha a verificao das permisses. A classe AcessControllerMB estende AbstractManagedBean e aplica o controle de acesso aos mtodos de gravao e remoo. Ela reutiliza a classe AuthorizationController, criada na aplicao anterior. Mas aqui ns movemos essa classe para o pacote controllers.generic, por entender que ela faz parta da infraestrutura genrica dos ManagedBeans.
AccessControllerMB
public abstract class AccessControllerMB extends AbstractManagedBean { protected AuthorizationController ac;
this.ac = AuthorizationController.getInstance(funcionario.getApelido());
public String gravar() { if (!ac.hasRole(new String[] {Papeis.GRAVADOR,Papeis.ADMINISTRADOR})) { throw new SecurityException("O usurio no tem acesso a este recurso [gravar]"); } return super.gravar(); }
236
if (!ac.hasRole(new String[] {Papeis.REMOVEDOR,Papeis.ADMINISTRADOR})) { throw new SecurityException("O usurio no tem acesso a este recurso [remover]"); } return super.remover(); } }
Especificamente para as classes que possuem o atributo telefone, ser necessrio um mtodo que faa a recuperao dos tipos de telefone. Para isso, criamos uma classe abstrata que retorne uma lista das constantes da classe Telefone.
AbstractUsuarioMB
public abstract class AbstractUsuarioMB extends AccessControllerMB{ public AbstractUsuarioMB(boolean checkUser) { super(checkUser); }
public List<SelectItem> getTiposTelefone() { List<SelectItem> items = new ArrayList<SelectItem>(); Field[] tipos = Telefones.class.getFields();
for (int i = 0; i < tipos.length; i++) { Field field = tipos[i]; String tipo = ""; try { tipo = (String) field.get(null); } catch (IllegalArgumentException e) { } catch (IllegalAccessException e) { } items.add(new SelectItem(tipo, tipo)); }
237
return items; }
5.7.2 ManagedBeans
AutorMB
public class AutorMB extends AccessControllerMB{
@SuppressWarnings("unchecked") public ArrayList<Autor> getLista() { if (!ac.hasRole(new String[] {Papeis.LEITOR,Papeis.ADMINISTRADOR})) { throw new SecurityException("O usurio no tem acesso a este recurso [listar]"); }
238
this.prepareWhere(); ArrayList<Autor> lista = (ArrayList<Autor>) this.dao.fetchAll(this.where,this.order, null, null); this.cleanWhereAndOrder(); return lista; } }
ClienteMB
public class ClienteMB extends AbstractUsuarioMB { private TelefoneDAO telefoneDAO = null; private Telefone telefone = null;
public ClienteMB() { super(true); this.bean = new Cliente(); this.dao = new ClienteDAO(); this.telefoneDAO = new TelefoneDAO(); this.setTelefone(new Telefone()); }
239
for (Iterator<Telefone> iterator = ((Cliente) this.bean).getTelefones() .iterator(); iterator.hasNext();) { Telefone telefone = (Telefone) iterator.next(); telefone = (Telefone) telefoneDAO.fetchOne(telefone); telefones.add(telefone); } ((Cliente) this.bean).setTelefones(telefones); this.dao.update(bean);
240
for (Iterator iterator = ((Cliente) this.bean).getTelefones().iterator(); iterator .hasNext();) { Telefone telefone = (Telefone) iterator.next(); items.add(new SelectItem(telefone.getId(), telefone.getNumero()+" - "+telefone.getTipo())); }
return items; }
@SuppressWarnings("unchecked") public ArrayList<Cliente> getLista() { if (!ac.hasRole(new String[] {Papeis.LEITOR,Papeis.ADMINISTRADOR})) { throw new SecurityException("O usurio no tem acesso a este recurso [listar]"); } this.prepareWhere(); ArrayList<Cliente> lista = (ArrayList<Cliente>) this.dao.fetchAll(this.where, this.order, null, null); this.cleanWhereAndOrder(); return lista; } }
241
EditoraMB
public class EditoraMB extends AccessControllerMB{
@SuppressWarnings("unchecked") public ArrayList<Editora> getLista() { if (!ac.hasRole(new String[] {Papeis.LEITOR,Papeis.ADMINISTRADOR})) { throw new SecurityException("O usurio no tem acesso a este recurso [listar]"); } this.prepareWhere(); ArrayList<Editora> lista = (ArrayList<Editora>) this.dao.fetchAll(this.where, this.order, null, null); this.cleanWhereAndOrder(); return lista; }
242
FuncionarioMB
public class FuncionarioMB extends AbstractUsuarioMB{ private Telefone telefone = null;
public FuncionarioMB() { super(true); this.bean = new Funcionario(); this.dao = new FuncionarioDAO(); this.telefone = new Telefone(); }
243
{ return AliasNavigationRules.EDITAR_CLIENTE; }
@SuppressWarnings("unchecked") public ArrayList<Funcionario> getLista() { if (!ac.hasRole(new String[] {Papeis.LEITOR,Papeis.ADMINISTRADOR})) { throw new SecurityException("O usurio no tem acesso a este recurso [listar]"); } this.prepareWhere(); ArrayList<Funcionario> lista = (ArrayList<Funcionario>) this.dao.fetchAll(this.where, this.order, null, null); this.cleanWhereAndOrder(); return lista; }
for (Iterator iterator = ((Funcionario) this.bean).getTelefones().iterator(); iterator .hasNext();) { Telefone telefone = (Telefone) iterator.next(); items.add(new SelectItem(telefone.getId(), telefone.getNumero()+" - "+telefone.getTipo())); }
244
return items; } }
LivroMB
public class LivroMB extends AccessControllerMB { private Autor autor = null; private AutorDAO autorDAO = null; private EditoraDAO editoraDAO = null; private LocalDAO localDAO = null;
public LivroMB() { super(true); this.bean = new Livro(); ((Livro) this.bean).setEditora(new Editora()); ((Livro) this.bean).setLocal(new Local()); this.dao = new LivroDAO(); this.autor = new Autor(); this.autorDAO = new AutorDAO(); this.editoraDAO = new EditoraDAO(); this.localDAO = new LocalDAO(); }
245
@SuppressWarnings("unchecked") public ArrayList<Livro> getLista() { if (!ac.hasRole(new String[] {Papeis.LEITOR,Papeis.ADMINISTRADOR})) { throw new SecurityException("O usurio no tem acesso a este recurso [listar]"); } this.prepareWhere(); ArrayList<Livro> lista = (ArrayList<Livro>) this.dao.fetchAll(this.where, this.order, null, null); this.cleanWhereAndOrder(); return lista; }
for (Iterator<Autor> iterator = ((Livro) this.bean).getAutores() .iterator(); iterator.hasNext();) { Autor autor = (Autor) iterator.next(); autor = (Autor) autorDAO.fetchOne(autor); autores.add(autor); } ((Livro) this.bean).setAutores(autores); this.dao.update(bean);
246
for (Iterator iterator = ((Livro) this.bean).getAutores().iterator(); iterator .hasNext();) { Autor autor = (Autor) iterator.next(); items.add(new SelectItem(autor.getId(), autor.getSobrenome() + ", " + autor.getNome())); }
return items; }
247
for (Iterator iterator = lista .iterator(); iterator.hasNext();) { Autor autor = (Autor) iterator.next(); items.add(new SelectItem(autor.getId(), autor.getSobrenome() + ", " + autor.getNome())); }
return items; }
for (Iterator iterator = this.editoraDAO.fetchAll(null, null, null, null).iterator(); iterator.hasNext();) { Editora editora = (Editora) iterator.next(); items.add(new SelectItem(editora.getId(), editora.getNome())); }
return items; }
248
return items; }
public String gravar() { String uri = super.gravar(); ((Livro) this.bean).setEditora(new Editora()); ((Livro) this.bean).setLocal(new Local()); return uri; }
249
public String remover() { String uri = super.remover(); ((Livro) this.bean).setEditora(new Editora()); ((Livro) this.bean).setLocal(new Local()); return uri; }
LocalMB
public class LocalMB extends AccessControllerMB{
250
{ if (!ac.hasRole(new String[] {Papeis.LEITOR,Papeis.ADMINISTRADOR})) { throw new SecurityException("O usurio no tem acesso a este recurso [listar]"); } this.prepareWhere(); ArrayList<Local> lista = (ArrayList<Local>) this.dao.fetchAll(this.where, this.order, null, null); this.cleanWhereAndOrder(); return lista; } }
Um ManagedBean especfico ir lidar com o controle do menu principal do mdulo administrativo. Ele herdar diretamente de AuthController, porque para ter acesso a ele, basta estar autenticado. CadastroMB
public class CadastroMB extends AuthController { private static String cadastro = null;
251
252
return AliasNavigationRules.EDITAR_AUTOR; } if (CadastroMB.cadastro.equals(Cadastros.CLIENTE)) { return AliasNavigationRules.EDITAR_CLIENTE; } if (CadastroMB.cadastro.equals(Cadastros.EDITORA)) { return AliasNavigationRules.EDITAR_EDITORA; } if (CadastroMB.cadastro.equals(Cadastros.FUNCIONARIO)) { return AliasNavigationRules.EDITAR_FUNCIONARIO; } if (CadastroMB.cadastro.equals(Cadastros.LIVRO)) { return AliasNavigationRules.EDITAR_LIVRO; } if (CadastroMB.cadastro.equals(Cadastros.LOCAL)) { return AliasNavigationRules.EDITAR_LOCAL; }
return AliasNavigationRules.ADMINISTRACAO; }
public String listar() { if (CadastroMB.cadastro.equals(Cadastros.AUTOR)) { return Cadastros.LISTA_AUTOR; } if (CadastroMB.cadastro.equals(Cadastros.CLIENTE)) { return Cadastros.LISTA_CLIENTE; } if (CadastroMB.cadastro.equals(Cadastros.EDITORA)) { return Cadastros.LISTA_EDITORA; }
253
if (CadastroMB.cadastro.equals(Cadastros.FUNCIONARIO)) { return Cadastros.LISTA_FUNCIONARIO; } if (CadastroMB.cadastro.equals(Cadastros.LIVRO)) { return Cadastros.LISTA_LIVRO; } if (CadastroMB.cadastro.equals(Cadastros.LOCAL)) { return Cadastros.LISTA_LOCAL; } return ""; }
LoginMB
public class LoginMB extends AuthController{ private Funcionario funcionario; private FuncionarioDAO funcionarioDAO;
254
public String login() { ArrayList<Funcionario> lista = (ArrayList<Funcionario>) this.funcionarioDAO .fetchAll("apelido='" + this.funcionario.getApelido() + "' and senha='" + this.funcionario.getSenha() + "'", null, null, null);
return AliasNavigationRules.ADMINISTRACAO; }
public boolean autenticado() { HttpSession session = getSession(); Funcionario funcionario = (Funcionario) session
255
.getAttribute("funcionario");
public String logout() { HttpSession session = getSession(); session.setAttribute("funcionario", null); session.removeAttribute("funcionario"); this.funcionario = new Funcionario(); return AliasNavigationRules.LOGOUT; } }
ficar
encarregado
de recuperar
as
ExceptionMB
public class ExceptionMB { public String getException() { return Message.getLastException(); } }
256
257
<managed-bean-name>cadastroMB</managed-bean-name> <managed-bean-class>controllers.CadastroMB</managed-bean-class> <managed-bean-scope>session</managed-bean-scope> </managed-bean> <managed-bean> <managed-bean-name>clienteMB</managed-bean-name> <managed-bean-class>controllers.ClienteMB</managed-bean-class> <managed-bean-scope>session</managed-bean-scope> </managed-bean> <managed-bean> <managed-bean-name>editoraMB</managed-bean-name> <managed-bean-class>controllers.EditoraMB</managed-bean-class> <managed-bean-scope>session</managed-bean-scope> </managed-bean> <managed-bean> <managed-bean-name>exceptionMB</managed-bean-name> <managed-bean-class>controllers.ExceptionMB</managed-bean-class> <managed-bean-scope>session</managed-bean-scope> </managed-bean> <managed-bean> <managed-bean-name>funcionarioMB</managed-bean-name> <managed-bean-class>controllers.FuncionarioMB</managed-bean-class> <managed-bean-scope>session</managed-bean-scope> </managed-bean> <managed-bean> <managed-bean-name>livroMB</managed-bean-name> <managed-bean-class>controllers.LivroMB</managed-bean-class> <managed-bean-scope>session</managed-bean-scope> </managed-bean> <managed-bean> <managed-bean-name>localMB</managed-bean-name>
258
Cadastros
public class Cadastros { public static final String AUTOR = "autor"; public static final String EDITORA = "editora"; public static final String CLIENTE = "cliente"; public static final String FUNCIONARIO = "funcionario"; public static final String LIVRO = "livro"; public static final String LOCAL = "local"; public static final String TELEFONE = "telefone"; public static final String PEDIDO = "pedido"; public static final String AUTOR_LIVRO = "autor_livro"; public static final String TELEFONE_CLIENTE = "telefone_cliente"; public static final String TELEFONE_FUNCIONARIO = "telefone_funcionario";
public static final String LISTA_AUTOR = "lista-autor.jsp"; public static final String LISTA_EDITORA = "lista-editora.jsp"; public static final String LISTA_CLIENTE = "lista-cliente.jsp"; public static final String LISTA_FUNCIONARIO = "lista-funcionario.jsp";
259
public static final String LISTA_LIVRO = "lista-livro.jsp"; public static final String LISTA_LOCAL = "lista-local.jsp"; }
No pacote navigation criamos uma classe chamada AliasNavigationRules, que contm os apelidos para pginas JSF definidos no arquivo faces-config.xml.
AliasNavigationRules
public class AliasNavigationRules { public static final String ADMINISTRACAO = "admin"; public static final String EDITAR_AUTOR = "editar-autor"; public static final String EDITAR_CLIENTE = "editar-cliente"; public static final String EDITAR_EDITORA = "editar-editora"; public static final String EDITAR_FUNCIONARIO = "editar-funcionario"; public static final String EDITAR_LIVRO = "editar-livro"; public static final String EDITAR_LOCAL = "editar-local"; public static final String LOGIN = "login"; public static final String LOGOUT = "logout"; }
Uma regra de navegao a definio dos possveis destinos a partir de uma nica origem. Ou seja, especificamos, a partir de uma pgina, para onde o usurio pode ir. Um caso de navegao, por sua vez, define um dos destinos de uma regra de navegao. No nosso caso, teremos apenas uma regra de navegao. As regras e casos de navegao no faces-config.xml podem ser definidos pelo editor grfico, conforme figura 26. O trecho de cdigo corresponde no arquivo este:
<navigation-rule> <navigation-case> <from-outcome>admin</from-outcome> <to-view-id>/admin.jsf</to-view-id> </navigation-case> <navigation-case> <from-outcome>autor</from-outcome> <to-view-id>/cadastro-autor.jsf</to-view-id>
260
</navigation-case> <navigation-case> <from-outcome>cliente</from-outcome> <to-view-id>/cadastro-cliente.jsf</to-view-id> </navigation-case> <navigation-case> <from-outcome>editar-autor</from-outcome> <to-view-id>/edicao-autor.jsf</to-view-id> </navigation-case> <navigation-case> <from-outcome>editar-cliente</from-outcome> <to-view-id>/edicao-cliente.jsf</to-view-id> </navigation-case> <navigation-case> <from-outcome>editar-editora</from-outcome> <to-view-id>/edicao-editora.jsf</to-view-id> </navigation-case> <navigation-case> <from-outcome>editar-funcionario</from-outcome> <to-view-id>/edicao-funcionario.jsf</to-view-id> </navigation-case> <navigation-case> <from-outcome>editar-livro</from-outcome> <to-view-id>/edicao-livro.jsf</to-view-id> </navigation-case> <navigation-case> <from-outcome>editar-local</from-outcome> <to-view-id>/edicao-local.jsf</to-view-id> </navigation-case> <navigation-case> <from-outcome>editora</from-outcome>
261
<to-view-id>/cadastro-editora.jsf</to-view-id> </navigation-case> <navigation-case> <from-outcome>funcionario</from-outcome> <to-view-id>/cadastro-funcionario.jsf</to-view-id> </navigation-case> <navigation-case> <from-outcome>livro</from-outcome> <to-view-id>/cadastro-livro.jsf</to-view-id> </navigation-case> <navigation-case> <from-outcome>local</from-outcome> <to-view-id>/cadastro-local.jsf</to-view-id> </navigation-case> <navigation-case> <from-outcome>login</from-outcome> <to-view-id>/login.jsf</to-view-id> </navigation-case> <navigation-case> <from-outcome>logout</from-outcome> <to-view-id>/index.jsp</to-view-id> </navigation-case> </navigation-rule>
262
https://jstl.dev.java.net/download.html Aqui foi utilizada a verso 1.2. Aps baixar o arquivo jar da biblioteca em um diretrio de sua preferncia, selecione o projeto livrariajsf e entre no menu popup Build Path>Configure Build Path. Na aba Libraries, clique em Add External jars e importe os arquivos jstl-api-1.2.jar e jslt-impl-1.2.jar.
263
</html>
A pgina de login j contm cdigo JSF. Para se referir a uma classe ManagedBean, usamos o identificador do arquivo faces-config.xml. Por exemplo, neste caso, o nome loginMB representa a classe LoginMB. Ao utilizar esse identificador, o JSF se encarrega de criar a instncia dessa classe e capturar a referncia para ter acesso aos mtodos e atributos pblicos. login.jsp
<?xml version="1.0" encoding="UTF-8" ?> <jsp:root version="2.0" xmlns:jsp="http://java.sun.com/JSP/Page" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html"> <jsp:directive.page contentType="text/html" /> <jsp:output omit-xml-declaration="no" doctype-root-element="html" doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" /> <f:view> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Autenticao</title> </head> <body> <h1>Autenticao</h1> <h:form> <h:outputText value="#{exceptionMB.exception}"/> </h:form> <h:form>
264
<table> <tr> <td>Apelido:</td> <td><h:inputText value="#{loginMB.funcionario.apelido}"/></td> </tr> <tr> <td>Senha:</td> <td><h:inputSecret value="#{loginMB.funcionario.senha}"/></td> </tr> <tr> <td><h:commandButton value="Entrar" action="#{loginMB.login}"/></td> </tr> </table> </h:form> </body> </html> </f:view> </jsp:root>
Utilizaremos duas bibliotecas de tags JSF, a core (referenciada pela letra f) e a html (referenciada pela letra h). A tag f:view define a viso propriamente dita, a pgina HTML que ser enviada para o navegador. A tag h:form define um formulrio controlado pelo JSF. A tag h:outputText representa um componente grfico, no caso um texto no editvel pelo usurio. A tag h:inputText, por outro lado, uma caixa de texto para edio. Com o atributo value, configuramos qual atributo do ManagedBean est relacionado com o componente grfico. O JSF recuperar os valores dos atributos da instncia para os componentes grficos. Qualquer alterao dos dados de um componente de entrada implicar na modificao automtica dos respectivos atributos, assim que o formulrio for submetido. A tag h:inputSecret gera um campo especfico para senhas, onde os caracteres digitados no so exibidos.
265
A tag h:commandButton cria um boto de submisso. Este, uma vez acionado, faz a associao dos dados dos componentes grficos com os respectivos atributos dos ManagedBeans e executa o mtodo especificado no argumento action. Aps o login, o usurio tem acesso ao menu de administrao.
admin.jsp
<?xml version="1.0" encoding="UTF-8" ?> <jsp:root version="2.0" xmlns:jsp="http://java.sun.com/JSP/Page" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html"> <jsp:directive.page contentType="text/html" /> <jsp:output omit-xml-declaration="no" doctype-root-element="html" doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" /> <f:view> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Mdulo Administrativo</title> </head> <body> <h1>Manuteno de Cadastros</h1> <h:form> <table> <tr><td><h:commandButton action="#{cadastroMB.autores}" value="Autores" /></td></tr> <tr><td><h:commandButton action="#{cadastroMB.clientes}" value="Clientes" /></td></tr> <tr><td><h:commandButton action="#{cadastroMB.editoras}" value="Editoras" /></td></tr> <tr><td><h:commandButton action="#{cadastroMB.funcionarios}" value="Funcionrios" /></td></tr> <tr><td><h:commandButton action="#{cadastroMB.livros}" value="Livros" /></td></tr> <tr><td><h:commandButton action="#{cadastroMB.locais}" value="Locais" /></td></tr> <tr><td><h:commandButton action="#{loginMB.logout}" value="Sair" /></td></tr> </table>
266
cadastro-autor.jsp
<?xml version="1.0" encoding="UTF-8" ?> <jsp:root version="2.0" xmlns:jsp="http://java.sun.com/JSP/Page" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:c="http://java.sun.com/jstl/core"> <jsp:directive.page contentType="text/html" /> <jsp:output omit-xml-declaration="no" doctype-root-element="html" doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" /> <f:view> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Cadastro de Autores</title> </head> <body> <h1>Cadastro de Autores</h1> <h:form> <h:commandButton action="#{cadastroMB.editar}" value="Incluir" /> </h:form>
267
<f:subview id="listing"> <c:import url="lista-autor.jsp" /> </f:subview> <h:form> <h:commandLink action="#{cadastroMB.admin}">Mdulo Administrativo</h:commandLink> </h:form> </body> </html> </f:view> </jsp:root>
cadastro-cliente.jsp
<?xml version="1.0" encoding="UTF-8" ?> <jsp:root version="2.0" xmlns:jsp="http://java.sun.com/JSP/Page" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:c="http://java.sun.com/jstl/core"> <jsp:directive.page contentType="text/html" /> <jsp:output omit-xml-declaration="no" doctype-root-element="html" doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" /> <f:view> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Cadastro de Clientes</title> </head> <body> <h1>Cadastro de Clientes</h1> <h:form> <h:commandButton action="#{cadastroMB.editar}" value="Incluir" /> </h:form>
268
<f:subview id="listing"> <c:import url="lista-cliente.jsp" /> </f:subview> <h:form> <h:commandLink action="#{cadastroMB.admin}">Mdulo Administrativo</h:commandLink> </h:form> </body> </html> </f:view> </jsp:root>
cadastro-editora.jsp
<?xml version="1.0" encoding="UTF-8" ?> <jsp:root version="2.0" xmlns:jsp="http://java.sun.com/JSP/Page" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:c="http://java.sun.com/jstl/core"> <jsp:directive.page contentType="text/html" /> <jsp:output omit-xml-declaration="no" doctype-root-element="html" doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" /> <f:view> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Cadastro</title> </head> <body> <h1>Cadastro de Editoras</h1> <h:form> <h:commandButton action="#{cadastroMB.editar}" value="Incluir" /> </h:form>
269
<f:subview id="listing"> <c:import url="lista-editora.jsp" /> </f:subview> <h:form> <h:commandLink action="#{cadastroMB.admin}">Mdulo Administrativo</h:commandLink> </h:form> </body> </html> </f:view> </jsp:root>
cadastro-funcionario.jsp
<?xml version="1.0" encoding="UTF-8" ?> <jsp:root version="2.0" xmlns:jsp="http://java.sun.com/JSP/Page" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:c="http://java.sun.com/jstl/core"> <jsp:directive.page contentType="text/html" /> <jsp:output omit-xml-declaration="no" doctype-root-element="html" doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" /> <f:view> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Cadastro de Funcionrios</title> </head> <body> <h1>Cadastro de Funcionrios</h1> <h:form> <h:commandButton action="#{cadastroMB.editar}" value="Incluir" /> </h:form>
270
<f:subview id="listing"> <c:import url="lista-funcionario.jsp" /> </f:subview> <h:form> <h:commandLink action="#{cadastroMB.admin}">Mdulo Administrativo</h:commandLink> </h:form> </body> </html> </f:view> </jsp:root>
cadastro-livro.jsp
<?xml version="1.0" encoding="UTF-8" ?> <jsp:root version="2.0" xmlns:jsp="http://java.sun.com/JSP/Page" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:c="http://java.sun.com/jstl/core"> <jsp:directive.page contentType="text/html" /> <jsp:output omit-xml-declaration="no" doctype-root-element="html" doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" /> <f:view> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Cadastro de Livros</title> </head> <body> <h1>Cadastro de Livros</h1> <h:form> <h:commandButton action="#{cadastroMB.editar}" value="Incluir" /> </h:form>
271
<f:subview id="listing"> <c:import url="lista-livro.jsp" /> </f:subview> <h:form> <h:commandLink action="#{cadastroMB.admin}">Mdulo Administrativo</h:commandLink> </h:form> </body> </html> </f:view> </jsp:root>
cadastro-local.jsp
<?xml version="1.0" encoding="UTF-8" ?> <jsp:root version="2.0" xmlns:jsp="http://java.sun.com/JSP/Page" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:c="http://java.sun.com/jstl/core"> <jsp:directive.page contentType="text/html" /> <jsp:output omit-xml-declaration="no" doctype-root-element="html" doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" /> <f:view> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Cadastro de Locais</title> </head> <body> <h1>Cadastro de Locais</h1> <h:form> <h:commandButton action="#{cadastroMB.editar}" value="Incluir" /> </h:form>
272
<f:subview id="listing"> <c:import url="lista-local.jsp" /> </f:subview> <h:form> <h:commandLink action="#{cadastroMB.admin}">Mdulo Administrativo</h:commandLink> </h:form> </body> </html> </f:view> </jsp:root>
As tags f:subview e c:import definem que uma rea ser preenchida com o contedo de outra pgina JSF, importada para a seo indicada. Isso permite dividir as responsabilidades entre pginas. No caso, a rea de listagem criada por uma pgina especfica para isso, e a leitura e compreenso do arquivo fica mais fcil. A tag h:commandLink cria um hiperlink, associado com um mtodo de um ManagedBean.
edicao-autor.jsp
<?xml version="1.0" encoding="UTF-8" ?> <jsp:root version="2.0" xmlns:jsp="http://java.sun.com/JSP/Page" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html"> <jsp:directive.page contentType="text/html" /> <jsp:output omit-xml-declaration="no" doctype-root-element="html" doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" /> <f:view> <html xmlns="http://www.w3.org/1999/xhtml"> <head>
273
<title>Cadastro de Autores</title> </head> <body> <h1>Cadastro de Autores</h1> <h:form> <table> <tr> <td>Nome:</td> <td><h:inputText value="#{autorMB.bean.nome}"/></td> </tr> <tr> <td>Sobrenome:</td> <td><h:inputText value="#{autorMB.bean.sobrenome}"/></td> </tr> <tr> <td><h:commandButton value="Gravar" action="#{autorMB.gravar}"/></td> <td><h:commandButton value="Retornar" action="#{cadastroMB.cadastroAtual}"/></td> </tr> </table> </h:form> </body> </html> </f:view> </jsp:root>
edicao-cliente.jsp
<?xml version="1.0" encoding="UTF-8" ?> <jsp:root version="2.0" xmlns:jsp="http://java.sun.com/JSP/Page" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html"> <jsp:directive.page contentType="text/html" />
274
<jsp:output omit-xml-declaration="no" doctype-root-element="html" doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" /> <f:view> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Cadastro de Clientes</title> </head> <body> <h1>Cadastro de Clientes</h1> <table border="0"> <tr> <td> <h2>Dados Bsicos</h2> <h:form> <table> <tr> <td>CPF:</td> <td><h:inputText value="#{clienteMB.bean.cpf}" /></td> </tr> <tr> <td>Nome:</td> <td><h:inputText value="#{clienteMB.bean.nome}" /></td> </tr> <tr> <td>Apelido:</td> <td><h:inputText value="#{clienteMB.bean.apelido}" /></td> </tr> <tr> <td>Senha:</td> <td><h:inputSecret value="#{clienteMB.bean.senha}" /></td>
275
</tr> <tr> <td>e-mail:</td> <td><h:inputText value="#{clienteMB.bean.email}" /></td> </tr> <tr> <td><h:commandButton value="Gravar" action="#{clienteMB.gravar}" /></td> <td><h:commandButton value="Retornar" action="#{cadastroMB.cadastroAtual}" /></td> </tr> </table>
</h:form></td> <td> <h2>Telefones</h2> <p> <h:form rendered="#{clienteMB.bean.id != null}"> Telefone: <h:inputText value="#{clienteMB.telefone.numero}" /> <br /> Tipo: <h:selectOneMenu value="#{clienteMB.telefone.tipo}"> <f:selectItems value="#{clienteMB.tiposTelefone}"/> </h:selectOneMenu> <br /> <h:commandButton value="Adicionar telefone" action="#{clienteMB.adicionarTelefone}" /> </h:form> </p> <p>
276
<h:form rendered="#{clienteMB.bean.id != null}"> <h:selectOneMenu value="#{clienteMB.telefone.id}"> <f:selectItems value="#{clienteMB.telefonesCliente}"/> </h:selectOneMenu> <br /> <h:commandButton value="Remover telefone" action="#{clienteMB.removerTelefone}" /> </h:form> </p> </td> </tr> </table> </body> </html> </f:view> </jsp:root>
A tag h:selectOneMenu cria uma caixa de listagem. O item escolhido configura o atributo apontado pelo argumento value. A tag f:selectItems cria as opes da caixa de listagem a partir de uma coleo de objetos SelectItem.
edicao-editora.jsp
<?xml version="1.0" encoding="UTF-8" ?> <jsp:root version="2.0" xmlns:jsp="http://java.sun.com/JSP/Page" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html"> <jsp:directive.page contentType="text/html" /> <jsp:output omit-xml-declaration="no" doctype-root-element="html" doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" /> <f:view>
277
<html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Cadastro de Editoras</title> </head> <body> <h1>Cadastro de Editoras</h1> <h:form> <table> <tr> <td>Nome:</td> <td><h:inputText value="#{editoraMB.bean.nome}"/></td> </tr> <tr> <td><h:commandButton value="Gravar" action="#{editoraMB.gravar}"/></td> <td><h:commandButton value="Retornar" action="#{cadastroMB.cadastroAtual}"/></td> </tr> </table> </h:form> </body> </html> </f:view> </jsp:root>
edicao-funcionarios.jsp
<?xml version="1.0" encoding="UTF-8" ?> <jsp:root version="2.0" xmlns:jsp="http://java.sun.com/JSP/Page" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html"> <jsp:directive.page contentType="text/html" /> <jsp:output omit-xml-declaration="no" doctype-root-element="html" doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN"
278
doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" /> <f:view> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Cadastro de Funcionrios</title> </head> <body> <h1>Cadastro de Funcionrios</h1> <table border="0"> <tr> <td> <h2>Dados Bsicos</h2> <h:form> <table> <tr> <td>Matrcula:</td> <td><h:inputText value="#{funcionarioMB.funcionario.matricula}" /></td> </tr> <tr> <td>Nome:</td> <td><h:inputText value="#{funcionarioMB.funcionario.nome}" /></td> </tr> <tr> <td>Apelido:</td> <td><h:inputText value="#{funcionarioMB.funcionario.apelido}" /></td> </tr> <tr> <td>Senha:</td> <td><h:inputSecret value="#{funcionarioMB.funcionario.senha}" /></td> </tr> <tr>
279
<td>e-mail:</td> <td><h:inputText value="#{funcionarioMB.funcionario.email}" /></td> </tr> <tr> <td><h:commandButton value="Gravar" action="#{funcionarioMB.gravar}" /></td> <td><h:commandButton value="Retornar" action="#{cadastroMB.cadastroAtual}" /></td> </tr> </table>
</h:form></td> <td> <h2>Telefones</h2> <p> <h:form rendered="#{funcionarioMB.bean.id != null}"> Telefone: <h:inputText value="#{funcionarioMB.telefone.numero}" /> <br /> Tipo: <h:selectOneMenu value="#{funcionarioMB.telefone.tipo}"> <f:selectItems value="#{funcionarioMB.tiposTelefone}"/> </h:selectOneMenu> <br /> <h:commandButton value="Adicionar telefone" action="#{funcionarioMB.adicionarTelefone}" /> </h:form> </p> <p> <h:form rendered="#{funcionarioMB.bean.id != null}"> <h:selectOneMenu value="#{funcionarioMB.telefone.id}">
280
<f:selectItems value="#{funcionarioMB.telefonesFuncionario}"/> </h:selectOneMenu> <br /> <h:commandButton value="Remover telefone" action="#{funcionarioMB.removerTelefone}" /> </h:form> </p> </td> </tr> </table> </body> </html> </f:view> </jsp:root>
edicao-livro.jsp
<?xml version="1.0" encoding="UTF-8" ?> <jsp:root version="2.0" xmlns:jsp="http://java.sun.com/JSP/Page" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html"> <jsp:directive.page contentType="text/html" /> <jsp:output omit-xml-declaration="no" doctype-root-element="html" doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" /> <f:view> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Cadastro de Livros</title> </head> <body> <h1>Cadastro de Livros</h1>
281
<table border="0"> <tr> <td> <h2>Dados Bsicos</h2> <h:form> <table> <tr> <td>ISBN:</td> <td><h:inputText value="#{livroMB.bean.isbn}" /></td> </tr> <tr> <td>Ttulo:</td> <td><h:inputText value="#{livroMB.bean.titulo}" /></td> </tr> <tr> <td>Local:</td> <td> <h:selectOneMenu value="#{livroMB.bean.local.id}"> <f:selectItems value="#{livroMB.locais}"/> </h:selectOneMenu> </td> </tr> <tr> <td>Editora:</td> <td> <h:selectOneMenu value="#{livroMB.bean.editora.id}"> <f:selectItems value="#{livroMB.editoras}"/> </h:selectOneMenu> </td> </tr> <tr>
282
<td>Ano:</td> <td><h:inputText value="#{livroMB.bean.ano}"/></td> </tr> <tr> <td>Preo:</td> <td><h:inputText value="#{livroMB.bean.preco}"/></td> </tr> <tr> <td><h:commandButton value="Gravar" action="#{livroMB.gravar}" /></td> <td><h:commandButton value="Retornar" action="#{cadastroMB.cadastroAtual}" /></td> </tr> </table> </h:form></td> <td> <h2>Autores</h2> <h:form rendered="#{livroMB.bean.id != null}"> Autor: <h:selectOneMenu value="#{livroMB.autor.id}"> <f:selectItems value="#{livroMB.autores}"/> </h:selectOneMenu> <br /> <h:commandButton value="Adicionar autor" action="#{livroMB.adicionarAutor}" /> </h:form> <h:form> <h:selectOneMenu value="#{livroMB.autor.id}"> <f:selectItems value="#{livroMB.autoresLivro}"/> </h:selectOneMenu> <br />
283
edicao-local.jsp
<?xml version="1.0" encoding="UTF-8" ?> <jsp:root version="2.0" xmlns:jsp="http://java.sun.com/JSP/Page" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html"> <jsp:directive.page contentType="text/html" /> <jsp:output omit-xml-declaration="no" doctype-root-element="html" doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" /> <f:view> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Cadastro de Locais</title> </head> <body> <h1>Cadastro de Locais</h1> <h:form> <table> <tr> <td>Nome:</td>
284
<td><h:inputText value="#{localMB.bean.nome}"/></td> </tr> <tr> <td><h:commandButton value="Gravar" action="#{localMB.gravar}"/></td> <td><h:commandButton value="Retornar" action="#{cadastroMB.cadastroAtual}"/></td> </tr> </table> </h:form> </body> </html> </f:view> </jsp:root>
lista-autor.jsp
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%> <h:form> Busca <h:selectOneMenu value="#{autorMB.where}"> <f:selectItem itemValue="nome" itemLabel="Nome" /> <f:selectItem itemValue="sobrenome" itemLabel="Sobrenome" /> </h:selectOneMenu> <h:inputText value="#{autorMB.searchKey}" /> <h:commandButton action="#{cadastroMB.autores}" value="OK"> </h:commandButton> <h:outputText value="#{exceptionMB.exception}" /> </h:form> <h:dataTable value="#{autorMB.lista}" var="row" border="1">
285
<h:column> <f:facet name="header"> <h:outputText value="Id" /> </f:facet> <h:form> <h:commandLink action="#{cadastroMB.editar}" value="#{row.id}"> <f:setPropertyActionListener value="#{row}" target="#{autorMB.bean}" /> </h:commandLink> </h:form> </h:column> <h:column> <f:facet name="header"> <h:form> <h:commandLink action="#{cadastroMB.autores}" value="Sobrenome"> <f:setPropertyActionListener value="sobrenome" target="#{autorMB.order}" /> </h:commandLink> </h:form> </f:facet> <h:outputText value="#{row.sobrenome}" /> </h:column> <h:column> <f:facet name="header"> <h:form> <h:commandLink action="#{cadastroMB.autores}" value="Nome"> <f:setPropertyActionListener value="nome" target="#{autorMB.order}" /> </h:commandLink> </h:form> </f:facet> <h:outputText value="#{row.nome}" /> </h:column> <h:column>
286
<f:facet name="header"> <h:outputText value="Remover" /> </f:facet> <h:form> <h:commandButton action="#{autorMB.remover}" value="X"> <f:setPropertyActionListener value="#{row}" target="#{autorMB.bean}" /> </h:commandButton> </h:form> </h:column> </h:dataTable>
O componente h:dataTable constri uma tabela HTML populada com o contedo de uma coleo de dados itervel. O argumento value aponta para o mtodo que retorna a coleo, enquanto row armazena, a cada iterao, o objeto atual da coleo. A tag h:column define uma coluna para a tabela. A tag f:facet cria um cabealho para a coluna. A tag f:setPropertyActionListener atualiza um atributo, definido pelo argumento target, com o valor especificado pelo argumento value. A atualizao feita quando ocorre o componente que contm a tag sofre uma ao.
lista-cliente.jsp
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%> <h:form> Busca <h:selectOneMenu value="#{clienteMB.where}"> <f:selectItem itemValue="cpf" itemLabel="CPF" /> <f:selectItem itemValue="nome" itemLabel="Nome" /> <f:selectItem itemValue="apelido" itemLabel="Apelido" /> <f:selectItem itemValue="email" itemLabel="e-mail" /> </h:selectOneMenu> <h:inputText value="#{clienteMB.searchKey}" /> <h:commandButton action="#{cadastroMB.clientes}" value="OK">
287
</h:commandButton> <h:outputText value="#{exceptionMB.exception}"/> </h:form> <h:dataTable value="#{clienteMB.lista}" var="row" border="1"> <h:column> <f:facet name="header"> <h:outputText value="Id" /> </f:facet> <h:form> <h:commandLink action="#{cadastroMB.editar}" value="#{row.id}"> <f:setPropertyActionListener value="#{row}" target="#{clienteMB.bean}"/> </h:commandLink> </h:form> </h:column> <h:column> <f:facet name="header"> <h:form> <h:commandLink action="#{cadastroMB.clientes}" value="CPF"> <f:setPropertyActionListener value="cpf" target="#{clienteMB.order}" /> </h:commandLink> </h:form> </f:facet> <h:outputText value="#{row.cpf}" /> </h:column> <h:column> <f:facet name="header"> <h:form> <h:commandLink action="#{cadastroMB.clientes}" value="Nome"> <f:setPropertyActionListener value="nome" target="#{clienteMB.order}" /> </h:commandLink> </h:form>
288
</f:facet> <h:outputText value="#{row.nome}" /> </h:column> <h:column> <f:facet name="header"> <h:form> <h:commandLink action="#{cadastroMB.clientes}" value="Apelido"> <f:setPropertyActionListener value="apelido" target="#{clienteMB.order}" /> </h:commandLink> </h:form> </f:facet> <h:outputText value="#{row.apelido}" /> </h:column> <h:column> <f:facet name="header"> <h:form> <h:commandLink action="#{cadastroMB.clientes}" value="e-mail"> <f:setPropertyActionListener value="email" target="#{clienteMB.order}" /> </h:commandLink> </h:form> </f:facet> <h:outputText value="#{row.email}" /> </h:column> <h:column> <f:facet name="header"> <h:outputText value="Remover" /> </f:facet> <h:form> <h:commandButton action="#{clienteMB.remover}" value="X"> <f:setPropertyActionListener value="#{row}" target="#{clienteMB.bean}"/> </h:commandButton>
289
lista-editora.jsp
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%> <h:form> Busca <h:selectOneMenu value="#{editoraMB.where}"> <f:selectItem itemValue="nome" itemLabel="Nome" /> </h:selectOneMenu> <h:inputText value="#{editoraMB.searchKey}" /> <h:commandButton action="#{cadastroMB.editoras}" value="OK"> </h:commandButton> <h:outputText value="#{exceptionMB.exception}"/> </h:form> <h:dataTable value="#{editoraMB.lista}" var="row" border="1"> <h:column> <f:facet name="header"> <h:outputText value="Id" /> </f:facet> <h:form> <h:commandLink action="#{cadastroMB.editar}" value="#{row.id}"> <f:setPropertyActionListener value="#{row}" target="#{editoraMB.bean}"/> </h:commandLink> </h:form> </h:column> <h:column> <f:facet name="header">
290
<h:form> <h:commandLink action="#{cadastroMB.editoras}" value="Nome"> <f:setPropertyActionListener value="nome" target="#{editoraMB.order}" /> </h:commandLink> </h:form> </f:facet> <h:outputText value="#{row.nome}" /> </h:column> <h:column> <f:facet name="header"> <h:outputText value="Remover" /> </f:facet> <h:form> <h:commandButton action="#{editoraMB.remover}" value="X"> <f:setPropertyActionListener value="#{row}" target="#{editoraMB.bean}"/> </h:commandButton> </h:form> </h:column> </h:dataTable>
lista-funcionario.jsp
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%> <h:form> Busca <h:selectOneMenu value="#{funcionarioMB.where}"> <f:selectItem itemValue="matricula" itemLabel="Matrcula" /> <f:selectItem itemValue="nome" itemLabel="Nome" /> <f:selectItem itemValue="apelido" itemLabel="Apelido" /> <f:selectItem itemValue="email" itemLabel="e-mail" /> </h:selectOneMenu>
291
<h:inputText value="#{funcionarioMB.searchKey}" /> <h:commandButton action="#{cadastroMB.funcionarios}" value="OK"> </h:commandButton> <h:outputText value="#{exceptionMB.exception}"/> </h:form> <h:dataTable value="#{funcionarioMB.lista}" var="row" border="1"> <h:form><h:outputText value="#{cadastroMB.exception}"/></h:form> <h:column> <f:facet name="header"> <h:outputText value="Id" /> </f:facet> <h:form> <h:commandLink action="#{cadastroMB.editar}" value="#{row.id}"> <f:setPropertyActionListener value="#{row}" target="#{funcionarioMB.bean}"/> </h:commandLink> </h:form> </h:column> <h:column> <f:facet name="header"> <h:form> <h:commandLink action="#{cadastroMB.funcionarios}" value="Matrcula"> <f:setPropertyActionListener value="matricula" target="#{funcionarioMB.order}" /> </h:commandLink> </h:form> </f:facet> <h:outputText value="#{row.matricula}" /> </h:column> <h:column> <f:facet name="header"> <h:form> <h:commandLink action="#{cadastroMB.funcionarios}" value="Nome">
292
<f:setPropertyActionListener value="nome" target="#{funcionarioMB.order}" /> </h:commandLink> </h:form> </f:facet> <h:outputText value="#{row.nome}" /> </h:column> <h:column> <f:facet name="header"> <h:form> <h:commandLink action="#{cadastroMB.funcionarios}" value="Apelido"> <f:setPropertyActionListener value="apelido" target="#{funcionarioMB.order}" /> </h:commandLink> </h:form>
</f:facet> <h:outputText value="#{row.apelido}" /> </h:column> <h:column> <f:facet name="header"> <h:form> <h:commandLink action="#{cadastroMB.funcionarios}" value="e-mail"> <f:setPropertyActionListener value="email" target="#{funcionarioMB.order}" /> </h:commandLink> </h:form> </f:facet> <h:outputText value="#{row.email}" /> </h:column> <h:column> <f:facet name="header"> <h:outputText value="Remover" /> </f:facet>
293
<h:form> <h:commandButton action="#{funcionarioMB.remover}" value="X"> <f:setPropertyActionListener value="#{row}" target="#{funcionarioMB.bean}"/> </h:commandButton> </h:form> </h:column> </h:dataTable>
lista-livro.jsp
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%> <h:form> Busca <h:selectOneMenu value="#{livroMB.where}"> <f:selectItem itemValue="isbn" itemLabel="ISBN" /> <f:selectItem itemValue="titulo" itemLabel="Ttulo" /> <f:selectItem itemValue="editora.nome" itemLabel="Editora" /> <f:selectItem itemValue="local.nome" itemLabel="Local" /> </h:selectOneMenu> <h:inputText value="#{livroMB.searchKey}" /> <h:commandButton action="#{cadastroMB.livros}" value="OK"> </h:commandButton> <h:outputText value="#{exceptionMB.exception}" /> </h:form> <h:dataTable value="#{livroMB.lista}" var="row" border="1"> <h:column> <f:facet name="header"> <h:outputText value="Id" /> </f:facet> <h:form> <h:commandLink action="#{cadastroMB.editar}" value="#{row.id}">
294
<f:setPropertyActionListener value="#{row}" target="#{livroMB.bean}" /> </h:commandLink> </h:form> </h:column> <h:column> <f:facet name="header"> <h:form> <h:commandLink action="#{cadastroMB.livros}" value="ISBN"> <f:setPropertyActionListener value="isbn" target="#{livroMB.order}" /> </h:commandLink> </h:form> </f:facet> <h:outputText value="#{row.isbn}" /> </h:column> <h:column> <f:facet name="header"> <h:form> <h:commandLink action="#{cadastroMB.livros}" value="Ttulo"> <f:setPropertyActionListener value="titulo" target="#{livroMB.order}" /> </h:commandLink> </h:form> </f:facet> <h:outputText value="#{row.titulo}" /> </h:column> <h:column> <f:facet name="header"> <h:form> <h:commandLink action="#{cadastroMB.livros}" value="Local"> <f:setPropertyActionListener value="local.nome" target="#{livroMB.order}" />
295
</h:commandLink> </h:form> </f:facet> <h:outputText value="#{row.local.nome}" /> </h:column> <h:column> <f:facet name="header"> <h:form> <h:commandLink action="#{cadastroMB.livros}" value="Editora"> <f:setPropertyActionListener value="editora.nome" target="#{livroMB.order}" /> </h:commandLink> </h:form>
</f:facet> <h:outputText value="#{row.editora.nome}" /> </h:column> <h:column> <f:facet name="header"> <h:form> <h:commandLink action="#{cadastroMB.livros}" value="Ano"> <f:setPropertyActionListener value="ano" target="#{livroMB.order}" /> </h:commandLink> </h:form> </f:facet> <h:outputText value="#{row.ano}" /> </h:column> <h:column> <f:facet name="header"> <h:outputText value="Preo" /> </f:facet>
296
<h:outputText value="#{row.preco}" /> </h:column> <h:column> <f:facet name="header"> <h:outputText value="Remover" /> </f:facet> <h:form> <h:commandButton action="#{livroMB.remover}" value="X"> <f:setPropertyActionListener value="#{row}" target="#{livroMB.bean}" /> </h:commandButton> </h:form> </h:column> </h:dataTable>
lista-local.jsp
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%> <h:form> Busca <h:selectOneMenu value="#{localMB.where}"> <f:selectItem itemValue="nome" itemLabel="Nome" /> </h:selectOneMenu> <h:inputText value="#{localMB.searchKey}" /> <h:commandButton action="#{cadastroMB.locais}" value="OK"> </h:commandButton> <h:outputText value="#{exceptionMB.exception}" /> </h:form> <h:dataTable value="#{localMB.lista}" var="row" border="1"> <h:column> <f:facet name="header"> <h:outputText value="Id" />
297
</f:facet> <h:form> <h:commandLink action="#{cadastroMB.editar}" value="#{row.id}"> <f:setPropertyActionListener value="#{row}" target="#{localMB.bean}" /> </h:commandLink> </h:form> </h:column> <h:column> <f:facet name="header"> <h:form> <h:commandLink action="#{cadastroMB.locais}" value="Nome"> <f:setPropertyActionListener value="nome" target="#{localMB.order}" /> </h:commandLink> </h:form> </f:facet> <h:outputText value="#{row.nome}" /> </h:column> <h:column> <f:facet name="header"> <h:outputText value="Remover" /> </f:facet> <h:form> <h:commandButton action="#{localMB.remover}" value="X"> <f:setPropertyActionListener value="#{row}" target="#{localMB.bean}" /> </h:commandButton> </h:form> </h:column> </h:dataTable>
298
299
5.10 Concluses
Terminamos assim a implementao da aplicao em trs camadas usando Hibernate e JSF. A estrutura final de pacotes da aplicao pode ser vista na figura 27.
300
O Hibernate prov um controle de transao, o que permite que recuperemos o estado do banco em caso de falha. O uso das anotaes do Hibernate para o mapeamento objeto-relacional permitiu a eliminao de cinco classes da camada de modelo. A princpio, pode parecer que a camada de controle ficou mais complexa, porque livrariajsp tinha cinco classes nessa camada, e livrariajsf tem catorze. No entanto, devese considerar que somente duas classes de livrariajsp, GravarServlet e RemoverServlet tinham muitos mtodos, alm de misturar vrias regras de negcio. O uso de ManagedBeans permitiu o estabelecimento de mais pontos de controle por meio de classes mais simples. O que ocorreu na verdade foi que o domnio de cada problema (no caso, um determinado cadastro) foi estabelecido de forma bem definida. As abstraes da camada de controle permitem que o programador tenha vrias vises de alto nvel sobre a camada de controle, focando as funcionalidades gerais, comportamentos genricos entre alguns ManagedBeans, controle de acesso e autenticao. O uso de JSF tornou desnecessrio o pacote view, criado em livrariajsp, porque o ManagedBean prov todo o controle necessrio sobre os dados da camada de viso. Assim, a camada de viso em livrariajsf constituda to somente de pginas Web. As abstraes proporcionadas pelo Hibernate e pela implementao JSF permitiram, para esta aplicao livrariajsf, que o programador ficasse mais focado na implementao de regras de negcio, e menos com questes de infraestrutura. Embora alguns elementos tenham se fragmentado (classes de controle e pginas JSF), a possibilidade de reuso aumentou. No prximo captulo, veremos como fica ainda mais fcil lidar com essas questes de infraestrutura de aplicao com o uso de um framework integrador.
301
As camadas so independentes, porque referncias a objetos so obtidas indiretamente por meio de programao orientada a aspectos (AOP). A aplicao independente da interface, pois utiliza JSF. A aplicao independente de banco de dados, pois utiliza JPA. Os servios consumidos por todas as camadas esto disponveis por meio de contextos.
302
O segundo passo atualizar os ndices do APT (Advanced Package Tool) por meio do seguinte comando do terminal:
apt-get update
Isso criar no menu principal da interface grfica um subitem dentro de 'Programao', chamado Demoiselle Eclipse 3.5.
303
O primeiro passo criar um projeto Maven. Selecione o projeto Maven no menu File>New->Project, conforme a figura 28.
304
Ao selecionar o catlogo, so listados todos os arqutipos registrados nele. Escolha o demoiselle-archetype-webapp-sample 1.1.0, conforme a figura 29.
305
evitando quebras na distribuio da aplicao por falta de algum arquivo. E tambm permite um controle eficaz dos nmeros de verso de cada API ou biblioteca utilizadas.
IModel
public interface IModel extends IPojo { public Integer getId();
306
no
pacote
Tambm precisamos incluir a dependncia ao banco PostgreSQL no arquivo pom.xml. Dentro da tag dependencies, inclua o seguinte trecho de cdigo:
<dependency> <groupId>postgresql</groupId> <artifactId>postgresql</artifactId> <version>8.4-701.jdbc3</version> </dependency>
IAutorDAO
public interface IAutorDAO extends IDAO<Autor> { public PagedResult<Autor> listar(Page pagina); public List<Autor> listar(); public PagedResult<Autor> filtrar(Autor Autor, Page pagina); public Autor buscar(Autor Autor); }
308
AutorDAO
public class AutorDAO extends JPAExtensionDAO<Autor> implements IAutorDAO {
public PagedResult<Autor> listar(Page pagina) { return findByJPQL("select a FROM Autor a ORDER BY a.nome ASC", pagina); }
309
Isso indica que o atributo autorDAO sofrer uma injeo de dependncia, ou seja, a instncia ser atribuda em tempo de compilao. Para que isso ocorra, preciso que a classe de teste implemente uma interface do framework chamada IFacade. Outra mudana a inicializao (e encerramento) do contexto de transao. Como o teste executado fora do framework, precisamos fazer isso explicitamente. Inclumos o cdigo do contexto de transao nos mtodos setUp e tearDown da classe de teste, que, respectivamente, so executados antes e depois de cada mtodo de teste. Eis o cdigo:
public void setUp() { WebTransactionContext.getInstance().init(); } public void tearDown() { WebTransactionContext.getInstance().end(); }
As duas ltimas alteraes so nos nomes dos mtodos utilizados. Em vez de fetchOne, usaremos buscar, pois este ltimo o que foi definido pela interface IAlunoDAO. E em vez do mtodo delete para remover, usaremos remove.
persistence.xml
<?xml version="1.0" encoding="UTF-8"?> <persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence persistence_1_0.xsd" version="1.0">
310
<provider>oracle.toplink.essentials.PersistenceProvider</provider>
<!-- EclipseLink JPA Settings --> <!-<properties> <property name="eclipselink.target-database" value="PostgreSQL" /> <property name="eclipselink.logging.level" value="FINE" /> <property name="eclipselink.ddl-generation" value="create-tables" /> <property name="eclipselink.ddl-generation.output-mode" value="database" /> <property name="eclipselink.session.customizer" value="samples.web.livrariademoiselle.persistence.config.EclipseLinkSessionCustomizer" /> <property name="eclipselink.jdbc.driver"
311
value="org.postgresql.Driver" /> <property name="eclipselink.jdbc.url" value="jdbc:postgresql://localhost/postgres" /> <property name="eclipselink.jdbc.user" value="postgres" /> <property name="eclipselink.jdbc.password" value="postgres" /> </properties> -->
<!-- TopLink JPA Settings --> <properties> <property name="toplink.target-database" value="PostgreSQL" /> <property name="toplink.logging.level" value="FINE" /> <property name="toplink.ddl-generation" value="create-tables" /> <property name="toplink.ddl-generation.output-mode" value="database" /> <property name="toplink.session.customizer" value="samples.web.livrariademoiselle.persistence.config.TopLinkSessionCustomizer" /> <property name="toplink.jdbc.driver" value="org.postgresql.Driver" /> <property name="toplink.jdbc.url" value="jdbc:postgresql://localhost/postgres" /> <property name="toplink.jdbc.user" value="postgres" /> <property name="toplink.jdbc.password" value="postgres" /> </properties> </persistence-unit> </persistence>
O nome da unidade de persistncia deve ser definido em uma propriedade do arquivo demoiselle.properties, assim:
framework.demoiselle.persistence.default_persistence_unit=LivrariaPU
preciso incluir no arquivo pom.xml do projeto as dependncias da implementao JPA. Para o caso da TopLink, o seguinte trecho de cdigo deve ser includo dentro da tag dependencies:
<dependency> <groupId>toplink.essentials</groupId> <artifactId>toplink-essentials</artifactId>
312
<version>2.1-60f</version> </dependency> <dependency> <groupId>toplink.essentials</groupId> <artifactId>toplink-essentials-agent</artifactId> <version>2.1-60f</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-entitymanager</artifactId> <version>3.2.1.ga</version> <scope>provided</scope> </dependency>
Alm disso, especificamente para o Tomcat, necessrio definir uma classe que customize a sesso para o provedor de persistncia. Essa classe est referenciada no arquivo persistence.xml, a TopLinkSessionCustomizer, cujo cdigo o seguinte:
public class TopLinkSessionCustomizer implements SessionCustomizer {
public void customize(Session session) throws Exception { JNDIConnector connector = (JNDIConnector) session.getLogin().getConnector(); connector.setLookupType(JNDIConnector.STRING_LOOKUP);
prepareForPostgreSQL(); }
313
IAutorDAO
public interface IAutorBC extends IBusinessController { public void incluir(Autor autor); public void alterar(Autor autor); public void excluir(Autor autor); public Autor buscar(Autor aluno); public PagedResult<Autor> listar(Page pagina); public PagedResult<Autor> filtrar(Autor aluno, Page pagina); }
AutorDAO
public class AutorBC implements IAutorBC { @Injection private IAutorDAO autorDAO;
314
315
AutorMB
public class AutorMB extends AbstractManagedBean { @Injection private IAutorBC autorBC; private Autor bean; private PagedResultDataModel<Autor> listAutor;
316
public String gravar() { try { if (this.bean.getId()==null) autorBC.incluir(this.bean); else autorBC.alterar(this.bean); for (IMessage imsg : WebMessageContext.getInstance().getMessages()) {
addMessage(imsg);
} } catch (ApplicationRuntimeException e) {
317
addMessage(e.getObjectMessage(), e);
} WebMessageContext.getInstance().clear(); return AliasNavigationRule.ALIAS_AUTOR_EDITAR; }
addMessage(imsg);
} } catch (ApplicationRuntimeException e) {
addMessage(e.getObjectMessage(), e);
} WebMessageContext.getInstance().clear(); return AliasNavigationRule.ALIAS_AUTOR_LISTAR; }
O mtodo addMessage no da classe AbstractManagedBean. Ele importado estaticamente do seguinte pacote: br.gov.framework.demoiselle.view.faces.util.ManagedBeanUtil
6.6.1 Autenticao
Com Demoiselle Framework, no precisamos incluir a lgica de autenticao em nossas classes de controle. Demoiselle utiliza JAAS para autenticao, assim precisamos apenas configurar os usurios e papis no arquivo de configurao do servidor de aplicao. No caso do Apache Tomcat, por exemplo, bastaria incluir as seguintes linhas (para o caso da aplicao livraria) no arquivo tomcat-users.xml:
318
<tomcat-users> <role rolename="leitor"/> <role rolename="gravador"/> <role rolename="removedor"/> <role rolename="administrador"/> <user username="leitor" password="leitor" roles="leitor"/> <user username="gravador" password="gravador" roles="leitor,gravador"/> <user username="admin" password="admin" roles="administrador"/> </tomcat-users>
6.7 Concluso
Com o uso de Demoiselle Framework, conseguimos obter diversas vantagens em nossa aplicao, com relao ao reuso de componentes e independncia de fornecedores. Com o uso de Hibernate na aplicao anterior, j tnhamos conseguido independncia do banco de dados. Agora, com o uso de JPA, ficamos independentes do provedor do mecanismo de persistncia e mapeamento objeto-relacional. Demoiselle Framework introduz a camada de Negcio, removendo tanto do Modelo quanto do Controle a responsabilidade por tratar as regras de negcio da aplicao. Essa na verdade a camada onde deve ser gasto o maior esforo, sendo que as demais devem reaproveitar tudo o que for possvel. Como se pode notar, JSF j havia trazido melhorias com relao ao uso de JSP e Servlets. O Demoiselle Framework reaproveita todas a capacidade das implementaes JSF e ainda adiciona a injeo de dependncia, que torna a camada de Controle e Viso desacoplada das demais, assim como ocorre com a camada de Negcio. As questes relativas segurana da aplicao, como autenticao e autorizao, so facilitadas pelo Demoiselle Framework graas ao uso da especificao JAAS. Por meio do uso de padres, Demoiselle Framework realmente facilita o desenvolvimento de aplicaes Java EE e estimula a reutilizao de cdigo e componentes. O prximo e ltimo captulo trar uma viso geral e profunda sobre as motivaes que levaram criao do Demoiselle Framework, e tambm discorrer de forma mais profunda sobre sua arquitetura. De qualquer modo, para maiores informaes sobre o Demoiselle Framework, o ponto de partida o portal www.frameworkdemoiselle.gov.br.
319
320
Recursos: a nica certeza em relao aos recursos que eles sero limitados. Fora isso, dependen do da combinao dos elementos anteriores, pode-se ter mais ou menos disponibilidade de recursos.
321
O governo trabalha com muitas restries de recursos, e no possvel depender de terceiros para manter sistemas de informao ou estender suas funcionalidades. Pois isso a necessidade do governo de ter a capacidade de realizar essas tarefas por conta prpria, por meio dos rgos e empresas desenvolvedores de sistemas. A criao, manuteno e integrao de sistemas um processo contnuo, o que certamente gera experincia e conhecimento. Por outro lado, os aspectos ligados a infraestrutura das aplicaes no interessam ao cliente, mas so alvo de esforo por parte do desenvolvedor. Finalmente, segundo Pacitti (2006, p. 45), o software (...) torna-se cada vez mais complicado, caro, menos confivel, difcil de configur-lo e mant-lo. Em funo disso, a definio de uma infraestrutura mnima para a criao de aplicaes permite que o desenvolvedor ignore os detalhes de baixo nvel, mantendo o foco na implementao das regras de negcio. Conforme os requisitos definidos para o governo eletrnico (www.governoeletronico.gov.br), essa infraestrutura de aplicaes deve ser distribuda, escalvel e habilitada para a Web (mas no restrita mesma), que proporcione alta disponibilidade, seja preparada para contingncia e tenha um baixo custo de propriedade. Por ltimo, mas no menos importante, a estratgia para a rea de tecnologia contempla o alinhamento com o movimento de software livre. Segundo Pacitti (2006, p. 40), o software livre vem ao encontro da necessidade, cada vez maior, de as decises dos governos e da administrao pblica dependerem dos sistemas de software. Ele afirma que a opo de alguns pases desenvolvidos pelo software livre se d pelos aspectos tcnicos, econmicos e de segurana nacional do mesmo. De acordo com Pacitti (2006, p. 41), o software livre proporciona aos governos:
Diminuio da dependncia econmica e tecnolgica causada pelo uso de softwares produzidos fora do pas; Economia trazida pelo fato de o software ser produzido por poucos rgos pblicos especializados de excelncia e da poder ser o mesmo software utilizado e aproveitado pelos demais rgos no especializados do mesmo governo.
322
e de como eles interagem. Tambm diz respeito a decises, pois os desenvolvedores gostariam de tomar as decises certas desde o incio, j que elas so vistas como difceis de alterar. A subjetividade pode levar a criao de uma mirade de arquiteturas diferentes, para projetos diferentes de equipes diferentes, que, no entanto, esto em uma mesma estrutura governamental. O reuso e consequentemente a manuteno so prejudicados. Uma arquitetura de referncia d aos desenvolvedores um ponto de partida e induz a uma compreenso uniformizada dos sistemas de informao. A arquitetura de referncia definida para a criao da infraestrutura de aplicaes do governo estabeleceu oito diretivas: 1. Computao distribuda 2. Aplicaes baseadas em componentes 3. Processos orientados a eventos 4. Acoplamento fraco de funes de negcio 5. Infraestrutura para suporte a decises 6. Automao de processos 7. Acesso por Internet 8. Software livre
323
primrias, sendo que cada uma acessada por um processador diferente, o qual executa a parte do programa que se encontra na memria primria a ele associada. A distribuio do controle est relacionada diretamente com a distribuio do prprio sistema operacional. O que distingue um sistema distribudo de um sistema de arquitetura clssica a distribuio do controle. O controle centralizado quando a execuo de um programa, em qualquer instante, est sob os cuidados de um nico elemento processador. J, quando o controle distribudo, a execuo de um programa est sob os cuidados de mais um elemento processador. A diretiva de computao distribuda da arquitetura de referncia visa estabelecer uma infraestrutura de software escalvel, preparada para o aumento de usurios, para o crescimento dos dados persistentes e para a expanso do escopo de funcionalidades. Ela advm tambm da distribuio geogrfica do governo brasileiro, em um pas de grande extenso territorial. Alm disso, visa criar aplicaes com suporte para diversas tecnologias, baseadas em padres de interoperabilidade e alinhadas com a e-Ping (http://www.governoeletronico.gov.br/acoes-e-projetos/e-ping-padroes-deinteroperabilidade).
324
condicionais, ou alternativas, ou rodar em paralelo, ou precisam ser repetidas; raramento uma simples cadeia (Allen, 2007) A implementao dos processos orientados a evento promove a diminuio do emprego de transferncia de arquivos e filas de entrada, de processos dinmicos e o desenho (projeto) simples de aplicaes.
325
como Web services podem interagir em um nvel de mensagem, incluindo a lgica de negcios e a ordem de execuo das interaes. Essas interaes podem abranger aplicaes e/ou organizaes, e resultar em um processo transacional de vida longa. Com a orquestrao, o processo sempre controlado da perspectiva de uma das partes do negcio. Ainda segundo o mesmo autor, a coreografia mais colaborativa em sua natureza, onde cada parte envolvida no processo descreve a parte com quem atua na interao. A coreografia segue uma sequencia de mensagens que pode envolver mltiplas partes e mltiplas fontes. Ela associada com as trocas de mensagem pblicas que ocorrem entre mltiplos Web services. Peltz afirma que a orquestrao difere da coreografia quando descreve um fluxo de processo entre servios, controlado por uma nica parte. Na coreografia nenhuma parte verdadeiramente dona da conversa.
326
Compartilhamento de solues; Desenvolvimento em rede; Reduo de custos; Simplificao de prticas de segurana; Gerenciamento compartilhado de conhecimento; Estmulo inovao; Sustentabilidade a longo prazo.
327
Independentemente do tipo de produto de software, Somerville (2007, p. 275) afirma que no final do primeiro milnio da Era Crist e nos anos iniciais do segundo, houve um movimento da indstria de software no sentido de praticar o desenvolvimento baseado em reuso. O autor justifica esse movimento como uma resposta s demandas por menores curtos de produo e manuteno de software, entregas mais rpidas de sistemas e aumento da qualidade do software. Segundo ele, cada vez mais empresas veem seu software como um ativo valioso e esto promovendo o reuso para aumentar seu retorno sobre investimentos em software. Para, Somerville (2007, p. 276), o reuso traz como benefcios o aumento de confiana no software, a reduo do risco de processo, o uso eficiente de especialistas, a conformidade com padres e a acelerao do desenvolvimento. Mas o mesmo autor alerta (Somm07, p. 277) para os problemas que podem advir com o reuso, se no houver planejamento. Vamos destacar alguns deles. Um dos problemas o aumento do custo de manuteno, provocado pela indisponibilidade do cdigo-fonte de um sistema ou componente de software reusvel. Uma soluo para isso a adoo preferencial de softwares livres, que trazem como pressuposto o cdigo-fonte aberto. Outro problema a criao e manuteno de uma biblioteca de componentes reusveis. E aqui tocamos num ponto crucial. Alm das questes que envolvem as tcnicas de classificao, catalogao e recuperao de componentes, e da compreenso e adaptao dos mesmos, est a replicao de componentes. Se uma equipe de uma empresa cria uma biblioteca de componentes reusveis, ela deve ficar disponvel para que todos os desenvolvedores da empresa possam utiliz-la. Alm disso, preciso permitir que os usurios dos componentes possam contribuir com sua evoluo. Se os usurios no puderem ver mudanas de que necessitam implementadas nos componentes existentes, eles criaro novos componentes. A dimenso do problema pode se tornar maior se o cdigo-fonte no estiver disponvel, o que implicar na reescrita de funcionalidades j implementadas. Se o desdobramento de um projeto de componente reusvel em vrios pode ocorrer dentro de uma organizao, que dir entre organizaes distintas. No intuito de assegurar a propriedade do software, algumas companhias competem entre si, criando componentes similares. Outras cooperam no desenvolvimento de componentes comuns, agregando experincias e beneficiando-se da expertise de cada uma. Para que a segunda estratgia tenha xito, um fator importante a motivao dos contribuidores do software. Na viso tradicional da Engenharia de Software, os desenvolvedores criam cdigo por imposio, em seu horrio de trabalho. O modelo de desenvolvimento colaborativo prope uma nova abordagem, onde o desenvolvedor contribui espontaneamente com cdigo, em seu tempo livre. E, uma vez que o software seja livre, esse desenvolvedor no precisa pertencer ao quadro da empresa.
328
Isso abre possibilidades para que uma companhia tenha a sua disposio um nmero maior de desenvolvedores do que poderia manter em uma estrutura formal. E aqui chegamos ao ponto que abrimos no incio desta seo: o significado de desenvolvimento colaborativo para as comunidades de software livre. Os desenvolvedores que contribuem com o software em seu tempo livre e que no fazem parte da empresa no so obrigados a contribuir. Segundo Taurion (2004), eles o fazem por status, satisfao pessoal e benefcios indiretos, como empregabilidade em outras empresas. Segundo pesquisa realizada por Lakhani (2005, p. 27), 44,9% dos contribuidores de comunidades de software livre escrevem cdigo para os projetos porque acham isso intelectualmente estimulante. Mas Fogel (2007, p. 126) afirma que essas razes no constituem toda a verdade. Segundo ele, as pessoas gastam tempo em software livre por razes que excedem um desejo abstrato de produzir bom cdigo. Ele diz que alm dessa motivao, e do desafio com valor educacional de trabalhar em problemas difceis, est o desejo intrnseco dos seres humanos em trabalhar com outros seres humanos, e dar e obter respeito por meio de atividades cooperativas. Dessa forma, para manter o software em comunidade, preciso estabelecer aes que incentivem a colaborao e o compartilhamento do conhecimento. O Demoiselle Framework e seus subprojetos so mantidos em um ambiente de desenvolvimento colaborativo, no Sourceforge, com ferramentas que permitem o controle de verses, o registro e acompanhamento de bugs e novas funcionalidades, a comunicao por meio de listas de discusso, fruns e wikipages e a criao de desdobramentos (ramos) do projeto, como estmulo a inovao.
329
implementaes especficas de software. As especificaes utilizadas como fundamento para o Demoiselle so as JSRs. Segundo a Apache Foundation (2009), uma JSR (Java Specification Request) um veculo formal pelo qual as tecnologias Java so criadas ou atualizadas. Uma JSR proposta por qualquer membro do JCP (Java Community Process), que ento fica conhecido como lder da especificao. O lder da especificao organiza um grupo de especialistas e esse grupo trabalha para criar a especificao. O grupo de especialistas deve criar tambm uma implementao de referncia assim como um kit de compatibilidade de tecnologia. O JCP a organizao que governa as tecnologias Java. Inicialmente criada pela Sun Microsystems, ela composta de um comit executivo formado por 32 representantes de empresas, indivduos e acadmicos que representam milhares de membros. Por meio do JCP, que as JSRs so criadas. Uma especificao tecnolgica permite independncia de fornecedor, pois sendo aberta, qualquer um pode fazer sua implementao, que dever operar seguindo os mesmos padres. Alm disso, a manuteno de uma especificao por um organismo independente (JCP uma organizao no governamental), garante estabilidade aos padres estabelecidos. A especificao diz o que a tecnologia deve fazer, e no o como. Assim, ela consegue manter um padro ao mesmo tempo que permite a evoluo tecnolgica. Alm das especificaes ditadas pelas JSRs, o Demoiselle se orienta para o uso de APIs que fazem parte da distribuio padro da tecnologia Java, de modo a evitar a dependncia direta de componentes de terceiros. As especificaes e APIs que fundamentam o Demoiselle so JAAS, JCA e JCE, JSF e Servlet.
7.1.6.1 JAAS
O texto a seguir uma traduo livre do guia de referncia do JAAS, disponvel na URL: http://java.sun.com/javase/6/docs/technotes/guides/security/jaas/JAASRefGuide.html. O servio de autenticao e autorizao Java (JAAS) foi introduzido como um pacote opctional (extenso) para o Java 2 SDK, Standard Edition (J2SDK), v 1.3. JAAS foi integrado ao J2SDK 1.4. JAAS pode ser usado para dois propsitos: Para autenticao de usurios, para determinar de modo seguro e confivel quem est atualmente executando o cdugo Java, independente do cdigo estar rodando em uma aplicao, um applet, um bean, ou um servlet; Para autorizao de usurios para garantir que eles tem os direitos de controle de acesso (permisses) requeridas para fazer as aes realizadas.
330
JAAS implementa uma verso Java do framework padro Pluggable Authentication Module (PAM). (...). Tradicionalmente, Java prov controle de acesso baseado em cdigo-fonte (controle de acesso baseado em de onde o cdigo veio e quem assinou o cdigo). Faltava, entretanto, a habilidade adicional de impor controles de acesso baseados em quem executa o cdigo. JAAS provides prov um framework que aumenta a arquitetura de segurana de Java com tal suporte. A autenticao JAAS executada de um modo plugvel. Isso permite que aplicaes permaneam independentes de tecnologias de autenticao subjacentes. Tecnologias de autenticao novas ou atualizadas podem ser plugadas na aplicao sem obrigar modificaes na prpria aplicao. Aplicao habilitam o processo de autenticao pela instanciao de um objeto LoginContext, que por sua vez referencia um Configuration para determinar a(s) tecnologia(s) de autenticao, ou os LoginModule(s), a serem usados para executar a autenticao. LoginModules tpicos devem estar prontos para ler verificar um nome de usurio e senha. Outros podem ler e verificar uma voz ou uma impresso digital, por exemplo. Uma vez que o usurio ou servio executando o cdigo tenha sido autenticado, o componente de autorizao JAAS trabalha em conjunto com o modelo de controle de acesso do Java SE para proteger o acesso a recursos sensveis. Ao contrrio da J2SDK 1.3 e anteriores, onde as decises de controle de acesso so baseadas somente na localidade do cdigo e nos assinantes do cdigo (um objeto CodeSource), na J2SDK 1.4 as decises de controle de acesso so baseadas tanto na execuo de cdigo CoudeSource quanto no usurio ou servio rodando o cdigo, que representado por um objeto Subject. Subject atualizado por um LoginModule com objetos Principals relevantes e credenciais se a autenticao foi bem sucedida. Um objeto CodeSource estende o conceito de um codebase para encapsular no somente a URL, mas tambm os certificados que foram usados para verificar a assinatura de cdigo originria daquela localizao.
331
criptogrficas para a plataforma Java. Na JDK 1.1, a JCA inclua APIs para assinaturas digitais e sumrios de mensagens. Nos lanamentos sebsequentes, a Java 2 SDK estendeu de forma significativa a Java Cryptography Architecture, como descrito neste documento. Ela tambm atalizou a infraestrutura de gerenciamento de certificado para suportar certificados X.509 v3, e introduziu uma nova Java Security Architecture para controle acesso refinado, altamente configurvel e controle de acesso extensvel. A Java Cryptography Architecture engloba as partes da API Java 2 SDK Security relacionadas a criptografia, assim como um conjunto de convenes e especificaes fornecidas neste documento. Isso inclui um provedor de arquitetura que permite implementaes de criptografia mltiplas e interoperveis. A Java Cryptography Extension (JCE) estende a API JCA para incluir APIs para criptografia, troca de chave, e Message Authentication Code (MAC). Juntas, a JCE e os aspectos de criptografia da SDK fornecem uma API de cripografia completa e independente de plataforma. JCE era originalmente um pacote opcional (extenso) para a Java 2 SDK, Standard Edition, verses 1.2.x e 1.3.x. JCE agora est integrado ao Java 2 SDK, v 1.4.
7.1.6.3 Servlet
O texto a seguir uma traduo livre de excertos da especificao Servlet 2.5, disponvel na URL: http://jcp.org/aboutJava/communityprocess/mrel/jsr154/index.html Essa especificao define os conceitos, arquitetura e padres para servlets e servlet containers. Segundo a especificao JSR 154, um servlet um componente Web baseado em tecnologia Java, gerenciado por um recipiente, que gera contedo dinmico. Como outros componentes baseados em tecnologia Java, servlets so classes Java independentes de plataforma que so compilados para um cdigo de bytes de plataforma neutra que pode ser carregado dinamicamente e executado por um servidor Web habilitado com a tecnologia Java. Containers , s vezes chamado de mecanismos de servlet, so extenses do servidor Web que oferecem funcionalidade de servlet. Servlets interagem com os clientes Web atravs de um paradigma de solicitao/resposta implementado pelo servlet container. A mesma especificao detalha o conceito de servlet container. O servlet container uma parte de um servidor Web ou servidor de aplicao que fornece o servios de rede atravs da qual os pedidos e as respostas so enviadas, decodifica pedidos baseados em MIME, e formata respostas baseadas em MIME. Um servlet container tambm contm e gera servlets atravs do seu ciclo de vida.
332
Um servlet container pode ser construdo em um servidor Web, ou instalado como um componente suplementar do servidor Web atravs dessa API de extenso nativa do servidor API. Servlet con tainers tambm podem ser construdos ou, eventualmente, instalados em servidores de aplicao habilitados para Web. Todos os servlet containers devem suportar HTTP como um protocolo para solicitaes e respostas, mas protocolos adicionais de solicitao/resposta, tais como HTTPS (HTTP sobre SSL) podem ser suportados. As verses exigidas das especificaes HTTP que um container deve implementar so HTTP/1.0 e HTTP/1.1. Porque o container pode ter um mecanismo de cache descrito na RFC2616 (HTTP/1.1), ele pode modificar as solicitaes dos clientes antes de entreg-las ao servlet, pode modificar as respostas produzidas pelos servlets antes de envi-las aos clientes , ou pode responder s solicitaes, sem entreg-las para o servlet de acordo com a RFC2616. Um servlet container pode impor restries de segurana no ambiente em que um servlet executado. Em um ambiente Java 2 Platform, Standard Edition (J2SE, v.1.3 ou acima) ou Java 2 Platform, Enterprise Edition (J2EE, v.1.3 ou superior), estas restries devem ser colocadas usando a arquitetura de permisso definida pela plataforma Java 2. Por exemplo, servidores de aplicao high-end podem limitar a criao de um objeto Thread para garantir que outros componentes do container no sero afetados de forma negativa. J2SE 1.3 a verso mnima da plataforma Java com as quais servlet containers devem ser construdos.
7.1.6.4 JSF
O texto a seguir uma traduo livre da pgina da JSF, disponvel na URL http://java.sun.com/javaee/javaserverfaces. Desenvolvida atravs do Java Community Process sob JSR - 314, tecnologia JavaServer Faces estabelece o padro para a construo de interfaces de usurio do lado servidor. Com as contribuies do grupo de peritos, as APIs JavaServer Faces esto sendo projetadas de modo que possam ser aproveitadas por ferramentas que iro tornar o desenvolvimento de aplicativos web ainda mais fcil. Vrios fornecedores de ferramentas foram respeitados membros do grupo de especialistas da especificao JSR-314, que desenvolveu o JavaServer Faces 1.0. Esses fornecedores esto comprometidos em apoiar a tecnologia JavaServer Faces em suas ferramentas, promovendo a adoo do padro de tecnologia JavaServer Faces. A tecnologia JavaServer Faces inclui:
Um conjunto de APIs para representar os componentes UI e gerenciar o seu estado, a manipulao de eventos e validao de entrada, definio de navegao da pgina, e apoiar a internacionalizao e acessibilidade.
333
Uma biblioteca de tags JavaServer Pages (JSP) personalizada para expressar uma interface JavaServer Faces dentro de uma pgina JSP.
Projetada para ser flexvel, a tecnologia JavaServer Faces aproveita a interface padro web e os conceitos de camada existentes sem limitar os desenvolvedores a uma marca, linguagem, protocolo ou dispositivo cliente particular. As classes de componentes UI includas com a tecnologia JavaServer Faces encapsulam a funcionalidade do componente, e no a apresentao especfica de um cliente, permitindo assim que os componentes JavaServer Faces UI a ser prestado a vrios dispositivos do cliente. Ao combinar a funcionalidade do componente de UI com geradores customizados, que geram atributos para um componente especfico do usurio, os desenvolvedores podem construir tags personalizadas para um dispositivo cliente em particular. Como uma convenincia, a tecnologia JavaServer Faces fornece um gerador personalizado e uma biblioteca de tags JSP personalizadas para processamento para um cliente de HTML, permitindo que os desenvolvedores de aplicativos para Java Platform, Enterprise Edition (Java EE) usem a tecnologia JavaServer Faces em suas aplicaes. Como a facilidade de uso o principal objetivo, a arquitetura do JavaServer Faces define claramente uma separao entre a lgica da aplicao e a apresentao enquanto torna fcil conectar a camada de apresentao com o cdigo do aplicativo. Este projeto permite que cada membro de uma equipe de desenvolvimento de aplicativos web focalize a sua parte do processo de desenvolvimento, e tambm oferece um modelo de programao simples para juntar os partes. Por exemplo, os desenvolvedores de pgina web sem conhecimentos de programao podem usar os componentes de tags UI de JavaServer Faces para ligar o cdigo do aplicativo com uma pgina web sem escrever scripts.
334
O framework, a infraestrutura bsica para criao de aplicaes, o projeto principal. Sua arquitetura ser detalhada no prximo captulo. Em linhas gerais, o framework oferece uma interface que integra e abstrai o uso de vrias tecnologias baseando-se em especificaes. A evoluo das especificaes Java deve manter o framework com um mnimo de implementao, ou at mesmo reduzi-la. As implementaes dos outros subprojetos, por outro lado, tendem a crescer. Para estabelecer padronizao, sem criar impedimentos para a evoluo tecnolgica, o framework concentra-se em definir uma arquitetura de referncia. Ela foi construda a partir do conhecimento e da experincia de um grupo de arquitetos, tendo como base as especificaes de interoperabilidade da e-PING e um direcionamento estratgico do uso de tecnologias. Segundo Hofmeister (2000, p. 8), uma arquitetura de referncia define tipos de elementos de uma arquitetura e como eles interagem. Ela tambm define um mapeamento de funcionalidades para elementos arquiteturais. A arquitetura de referncia uma especificao, portanto. Assim, necessrio um produto de software, que implemente os padres definidos por ela. O framework esse produto. Mas alm de implementar a arquitetura de referncia, o framework tambm serve para evoluir a mesma. A utilizao do framework, ao longo do tempo, gera questionamentos, que so usados para avaliar e manter a arquitetura.
335
336
Demoiselle Infra tem como objetivo ajudar desenvolvedores a instalar e configurar o ambiente de desenvolvimento e tcnicos de suporte a criarem e manterem um ambiente de produo.
7.3 Arquitetura
Demoiselle Framework uma soluo para desenvolvimento de aplicaes em Java construda pelo Serpro, para padronizar a construo de suas aplicaes e promover a gerao de componentes de software reutilizveis. Ele consiste em duas partes, um framework arquitetural e as dependncias estruturais desse framework. O foco, neste caso o framework arquitetural, mas apresentaremos uma viso geral dessas duas partes, para justificar a escolha da plataforma utilizada. Um framework pode ser compreendido como um conjunto de classes cooperativas que implementam os mecanismos que so essenciais para um domnio de problemas especficos (Horstmann, 2007, p. 312) e que constroem um projeto reutilizvel para uma determinada categoria de software (Gamma et alli, 2000, p. 41). Gamma et alli (2000, p. 332) ainda afirma que um framework fornece uma melhor orientao arquitetnica do software, atravs do particionamento do projeto em classes abstratas e da definio de suas responsabilidades e colaboraes. Segundo Gamma, o desenvolvedor pode customizar o framework para uma aplicao particular, atravs da especializao e da composio de instncias de suas classes. Demoiselle Framework, alm das definies acima, implementa o conceito de framework integrador (Macias, 2008). Isso ficar mais evidente adiante, quando for tratada a camada dos frameworks especialistas. A estrutura geral do Demoiselle baseada na diviso em camadas, pois, segundo Fowler (2006, p. 37-38), isso permite a compreenso de uma parte do sistema como um todo coerente sem a necessidade de saber muito sobre as demais partes. Alm disso, a utilizao de camadas permite a fcil substituio de implementaes, a minimizao de dependncias entre partes das aplicaes, e a definio de padres que podem gerar cdigo reutilizvel. A camada mais baixa do Demoiselle o sistema operacional. Essa camada no passvel da imposio de padro, porque a escolha do sistema operacional depende de vrios fatores, como os tipos de aplicao que sero executadas, a necessidade de customizao (adaptao do sistema) e a disponibilidade de recursos financeiros (para pagamento de licenas e contrato de suporte) ou humanos (para manuteno do sistema por conta prpria). A imposio de um sistema operacional para um framework voltado ao governo invivel, pois aprisiona o Estado a uma tecnologia e tende a onerar o cidado. Dado isso, as aplicaes devem ser capazes de rodar em qualquer sistema operacional (ou
337
pelo menos na maioria disponvel/usada no mercado). A tecnologia Java permite a portabilidade pela implementao do conceito de mquina virtual, que constitui a segunda camada mais baixa do Demoiselle. A prxima camada mais acima denominada plataforma. Como a implementao atual do Demoiselle est direcionada a construo de aplicaes Web, a camada de plataforma se constitui de servidores de aplicao Web que sigam a especificao Servlets 2.5 (JCP, 2000). A criao de aplicaes que no sejam Web (Desktop, embarcadas em dispositivos mveis) implicar em mudanas nessa camada. Em seus projetos, o Serpro fez uso de JBoss e Tomcat. A prxima camada mais superior, dos frameworks de fundamento, constitui-se da reunio de especificaes da tecnologia Java que norteiam as implementaes de software utilizadas pelo framework. A ideia que as aplicaes no fiquem presas a produtos de software especficos. A adoo dos padres definidos pelas especificaes garante uma certa liberdade na troca de tecnologias especialistas. As tecnologias especialistas, ou melhor, os frameworks especialistas, fazem parte da camada seguinte. Este o ponto de mutao do Demoiselle Framework. Os frameworks especialistas so integrados pela ltima camada no topo, o framework arquitetural, que constitui efetivamente o Demoiselle. O gerenciamento de mudanas deve se concentrar nessas duas camadas superiores, sendo que o framework arquitetural deve permanecer estvel. A ideia proteger as aplicaes do impacto das mudanas, conservando a mesma interface para as classes e mtodos utilizados, mas alterando os componentes que implementam as funcionalidades conforme for necessrio. O intento tirar do desenvolvedor a preocupao com as implementaes de baixo nvel, que constituem a infraestrutura de software. Todas as tecnologias integradas pelo Demoiselle podem ser perfeitamente usadas sem o framework. Porm, nesse caso, o desenvolvedor divide a sua preocupao com diversas estruturas, precisa gerenciar isso por conta prpria e torna as aplicaes dependentes de determinadas tecnologias, fortemente sujeitas mudana. Ao programar para o Demoiselle Framework, o desenvolvedor consegue criar uma estrutura de software reutilizvel, de duas formas. Primeiro, se algum framework especialista tiver de ser substitudo, a aplicao ir ignorar essa mudana, pois sua interface com o framework permanecer inalterada. A mudana ocorrer na implementao do framework que faz a integrao, sendo que o resultado final permanecer o mesmo.
338
objetivos, a saber, extensibilidade, reusabilidade, manutenibilidade, desempenho, estabilidade e confiabilidade. A arquitetura atual foi desenhada para atender somente aplicaes Web no distribudas.
7.3.1.1 Extensibilidade
Seria absurdo presumir a possibilidade da criao de uma infraestrutura de software que pudesse atender s demandas de todas as aplicaes. Essa uma dificuldade do estabelecimento e aceitao de padres. Tentativas de cercar todos os problemas tendem ao fracasso, pois no possvel prever as funcionalidades necessrias para atender aplicaes futuras. De modo a convergir a padronizao das aplicaes, pr-requisito para o objetivo tratado a seguir, e a capacidade dos sistemas de crescerem, a arquitetura do framework define a existncia de pontos de extenso seja por meio de interfaces, abstraes ou pela utilizao de padres de projetos. As principais interfaces da arquitetura so: IDAO: Interface para as classes que implementam o padro de projetos DAO (Alur et alli, 2002, p. 346), no qual um objeto responsvel por extrair e encapsular todos os acessos origem de dados e gerenciar a conexo com a origem de dados para obter e armazenar dados. Classes que implementam IDAO no acessam outras camadas da aplicao, apenas a de persistncia; IFacade: Interface para classes que implementam o padro Faade (Gamma et alli, p. 179), cujo objetivo unificar subsistemas por meio de uma interface de nvel mais alto e tornar mais fcil o uso dos mesmos; IBusinessController: Interface para classes que definem objetos da camada de negcios (Alur et alli, 2002, p. 50) que acessam a camada de persistncia atravs de classes que implementam a interface IDAO. Objetos que implementam IBusinessController podem acessar funcionalidades de outros sistemas ou mdulos atravs de implementaes de IFacade; IViewController: Interface para classes que fundem os conceitos de viso e controle do MVC (2006, p. 315). Objetos dessas classes tero acesso somente a instncias de classes que implementam IFacade e IBusinessController; Uma caracterstica inerente dos frameworks, citada por Fowler (Fowl05) que os mtodos definidos por um usurio para adaptar o framework s suas necessidades so frequentemente chamados de dentro do prprio framework, em vez do cdigo de aplicao do usurio. O framework frequentemente faz o papel do programa principal na coordenao e sequenciamento de atividades da aplicao. Essa inverso de
339
controle d aos frameworks o poder de servir como esqueletos extensveis. Os mtodos fornecidos pela adaptao do usurio para os algoritmos genricos definidos no framework para uma aplicao particular.
7.3.1.2 Reusabilidade
Segundo Paula Filho (2009, p. 256), quando uma organizao comea a usar processos definidos de desenvolvimento de software, os maiores ganhos iniciais resultam da reduo dos defeitos introduzidos em cada iterao. Isso ocorre por causa da reduo do desperdcio de tempo e dinheiro, principalmente aquele que causada por defeitos de requisitos, anlise e desenho. A partir da, ganhos significativos de produtividade s so conseguidos por meio da reutilizao. Em face disso, a arquitetura de uma infraestrutura de software deve favorecer o reuso, a partir da especificao de artefatos comuns em diversos projetos. O Demoiselle Framework prope dois principais artefatos de reuso, uma arquitetura de referncia e componentes fracamente acoplados. A arquitetura de referncia uma estrutura padro de aplicao em camadas. A partir do padro de projeto MVC (Fowler, 2006, p. 315) e do modelo J2EE (Alur et alli, 2002, p. 114), o Demoiselle prope trs camadas: Viso e Controle, Negcios e Persistncia. Viso e Controle a fuso das camadas homnimas do padro MVC, em um entendimento de que so indissociveis, exceto em aplicaes servidoras de servios. Negcios centraliza a maior parte do processamento de negcios para a aplicao. Persistncia corresponde a camada de Modelo do MVC e de Integrao do J2EE. Camadas podem ser reaproveitadas entre aplicaes, geralmente com adaptaes. O Demoiselle Framework prope a reduo ao mnimo de adaptaes com o uso de injeo de dependncias (Fowler, 2004). Esse um padro cuja ideia bsica consiste em um objeto separado, um montador, que realiza a instanciao da implementao apropriada de uma interface, de modo que uma camada no dependa de classes especficas. A injeo de dependncias no Demoiselle feito com o uso de orientao a aspectos, implementada por AspectJ. A extensibilidade por meio de componentes fracamente acoplados serve tambm a reutilizao. A proposta do Demoiselle que as aplicaes sejam criadas com blocos de cdigo pr-fabricados, que encerrem funcionalidades completas. O desenvolvimento orientado a componentes favorece o framework quando preserva sua extensibilidade, ao mesmo tempo em que acelera o desenvolvimento e aumenta a qualidade do cdigo, ao induzir a construo de aplicaes pela composio de funcionalidades. A criao de componentes para o Demoiselle segue a orientao de McConnell (2005, p. 78), na qual a responsabilidade de cada bloco de construo deve ser bemdefinida. Um bloco de construo deve ter uma rea de responsabilidade e deve saber o
340
mnimo possvel sobre as reas de responsabilidade de outros blocos de construo (fraco acoplamento).
7.3.1.3 Manutenibilidade
A arquitetura divide responsabilidades entre mdulos lgicos, que sero vistos adiante, com o intuito de causar o menor impacto no todo em caso de manuteno das aplicaes. Isso alcanado com duas abordagens: diminuio do acoplamento e foco da manuteno em pontos especficos.
7.3.1.4 Desempenho
O projeto do Demoiselle identificou, com base na experincia das equipes de desenvolvimento do Serpro, pontos crticos de performance, tais como integrao entre camadas e controle de transaes. De modo a diminuir os riscos de perda de desempenho, na manuteno e evoluo de aplicaes, o framework implementa os pontos crticos como funcionalidades, que fazem parte dos pontos especficos de manuteno citados anteriormente.
341
O pacote br.gov.framework.demoiselle.core.layer define as abstraes para os objetos de cada camada. Ele contm as interfaces vistas nos objetivos e restries arquiteturais: IViewController, IBusinessController, IDAO e IFacade. exceo de IDAO, as demais interfaces no contm declarao de mtodos, o que pode parecer estranho, j que interfaces servem para definir comportamentos padronizados por meio de cdigo genrico reusvel. O que ocorre as interfaces do mdulo Core possuem outro propsito: servir para identificar os pontos de injeo de dependncias. Geralmente as aplicaes faro uso das trs primeiras interfaces. IFacade serve para definir uma fachada quando a aplicao trata de integrao entre mdulos ou integrao de subsistemas. O Core no possui nenhuma funcionalidade imediatamente utilizvel. Ele apenas define os padres do framework, de modo que outros mdulos tem de estend-lo para tornar o framework funcional.
342
343
O mdulo Core especifica quem trata a injeo de dependncia, mas a forma como a injeo ser realizada deve ser definida pelos mdulos que implementam as abstraes do Core. O mdulo Web que implementa a injeo de dependncia por padro. Isso no impede que ilustremos como se d o fraco acoplamento proporcionado pelo uso desse padro. O cdigo a seguir uma classe que implementa a interface IViewController, que no contm nenhum comportamento definido. Isso to somente funciona como um marcador, ou na linguagem da AOP (Aspect Oriented Programming), um join point. Isso serve para avisar ao compilador da linguagem orientada a aspectos, que executado antes do compilador Java ordinrio, que um aspecto pode ser aplicado neste ponto, ou mais especificamente, nessa classe. Observe que a classe MeuMB define um atributo cujo tipo no uma classe, e sim uma interface. IMeuBC uma extenso de IBusinessController e tambm constitui um join point. Antes do atributo, porm, h uma anotao @Injection. O que vai ocorrer quando da compilao dessa classe, que o compilador de aspectos identificar pela anotao que o atributo precisa receber uma instncia. A ocorre a injeo de cdigo. Na linha imediatamente a seguir ao da declarao includo um pedao de cdigo (advice) que faz a atribuio de uma classe que implementa IMeuBC para meuBC.
A classe que ser instanciada definida pelo mdulo que implementa o Core. Podese induzir que a classe tenha que seguir um padro de nome para ser localizada. E isso pode levar a crer que difcil flexibilizar a aplicao para que determinados atributos instanciem classes fora do padro. Mas facil criar excees regra, pela passagem do parmetro name para a anotao @Injection, como se v na classe MeuMB modifica a seguir.
public class MeuMB implements IViewController{ @Injection (name=br.gov.escola.business.implementation.AlunoBC) private IMeuBC meuBC; }
344
345
346
A utilizao do mtodo getInstance() assinala o uso do padro de projeto Singleton (Gamma et alli, 2000, p. 130), o que coerente, visto que as informaes relativas a segurana devem ficar centralizadas. A classe que implementa esse Singleton ser tratada adiante.
7.3.2.1.5 Entidades
O pacote br.gov.framework.demoiselle.core.bean prope uma abstrao para as entidades da aplicao, a interface IPojo, que fora a utilizao do padro Value Object (Alur et alli, 2002, p. 232).
347
7.3.2.1.7 Acionadores
O pacote br.gov.framework.demoiselle.core.action define um mecanismo padronizado de aes a serem executadas pela aplicao. Essas aes so definidas como funes estruturais da aplicao, como carregamento de configurao e inicializao de ambiente. As interfaces desse pacote so IActionManager, ILoaderAction e IAction. A primeira define um padro para classes que gerenciam aes, com mtodos para configurar o
348
carregador da ao (que deve implementer ILoaderAction). O carregador da ao contm uma coleo de tipos IAction, que per sua vez representam aes.
Uma classe que implemente ILoaderAction, por exemplo MeuCarregadorAction, recupera as aes com seu mtodo getActions(), que retorna uma coleo do tipo IAction. Uma implementao de IActionManager, por sua vez, dispara todas as aes pela chamada a seus mtodos execute(), algo extremamente simples para a estrutura for trazida pelo Java 5, que itera sobre colees.
349
350
351
Esses atributos poderiam, por exemplo, pertencer a uma classe chamada MeuConfig. O carregamento e uso da configurao se daria da forma a seguir:
public void meuMetodo() { MeuConfig meuConfig = new MeuConfig(); ConfigurationLoader.load(meuConfig); System.out.print( meuConfig.getMinhaPropriedade()); }
352
353
354
O mtodo getMessageContext() devolve uma instncia de WebContextMessage, mas isso no importa para a aplicao, desde que a classe implemente IMessageContext. Isso permite que esse cdigo fique protegido contra mudanas caso o contexto de mensagens seja modificado. A recuperao de mensagens pode ser feita pela chamada ao mtodo getMessages() do contexto de mensagens, que retorna uma coleo de instncias de IMessage.
355
356
deve ser registrada no arquivo de configurao do framework e carregada atravs da classe ProxyConfig. A classe WebProxy a implementao bsica da interface IProxy. Uma classe de configurao chamada ProxyConfig diz qual classe implementa a interface IProxy. A injeo de dependncias faz uso do controlador de chamadas de mtodos WebInvocationHandler na hora de envolver o objeto criado em um proxy.
357
IJNDITransactionManagerLookup define as informaes para o mecanismo JNDI localizar uma UserTransaction do continer com suporte JTA. O mdulo Web prov uma implementao para essa interface, voltada para o JBoss: JbossTransactionManagerLookup. A implementao padro do contexto transacional a classe WebTransactionContext. A classe WebTransactionAction define a ao de inicializao em aplicaes Web onde o contexto transacional configurado.
WebTransactionActionConfig define as configuraes padro do contexto transacional definido pela aplicao em arquivo externo.
A classe WebTransactionServletRequestListener o controlador do contexto transacional. Ela responsvel por iniciar e finalizar, normalmente ou com erro, o contexto transacional. acionado em todas as requisies Web.
358
Figura 52: Contexto de transao implementado pelo mdulo Web O mtodo execute() dessa classe ser invocado automaticamente na inicializao, desde que a classe seja referenciada no arquivo demoiselle.properties, conforme exemplo a seguir:
framework.demoiselle.web.initialization.action=MinhaAction
359
360
361
</servlet-mapping>
362
aspectos utilizada em conjunto com a orientao a objetos, inerente linguagem Java. Segundo os mesmos autores (2006, p. 45), na programao orientada a aspectos (...) os interesses so programados em mdulos separados (...). Aps a programao, ocorre a combinao entre as classes e os aspectos. Eduardo Piveta (2001 apud Winck e Goetten Jnior, 2006) afirma que um sistema que utiliza a programao orientada a aspectos composto por trs componentes: linguagem de componentes, linguagem de aspectos e combinador de aspectos. O Demoiselle Framework utiliza como linguagem de componentes Java, como linguagem de aspectos a AspectJ e como combinador de aspectos o compilador da AspectJ. O mdulo Web contm um aspecto chamado InjectionAspect, cujo cdigo integral exibimos a seguir:
package br.gov.framework.demoiselle.web.layer.integration;
import br.gov.framework.demoiselle.web.layer.integration.InjectionManager;
363
Assim como na linguagem Java a unidade central a classe, na AspectJ a unidade central o aspecto. AspectJ tem sua sintaxe prpria, mas estende a linguagem Java, de modo que todo programa AspectJ um programa Java vlido. Ou melhor, todo aspecto referencia e gera cdigo Java vlido. O aspecto InjectionAspect tem por objetivo interceptar instanciaes de classe que implementem as interfaces IBusinessController, IViewController e IFacade e invocar uma instncia de InjectionManager. Ele faz essas interceptaes por meio da construo pointcut, que um ponto de atuao. Winck e Goetten Jnior (Winck e Goetten Jnior, 2006, p. 56) definem pontos de atuao como elementos do programa usados para definir um ponto de juno, como uma espcie de regra criada pelo programador para especificar eventos que sero considerados como um ponto de juno. Por sua vez, um ponto de juno ou entrada um identificador bem definido na execuo de um programa. Vamos compreender o funcionamento do injectionIBusinessController, que esclarecer os demais:
pointcut injectionIBusinessController(): execution(public br.gov.framework.demoiselle.core.layer.IBusinessController+.new(..));
ponto
de
atuao
A palavra execution refere-se a execuo de um mtodo, o qual se encontra entre os parnteses seguintes. Esse ponto de atuao diz que o aspecto ser disparado sempre que for gerada uma nova instncia de classe que implemente IBusinessController, ou, em outras palavras, quando o mtodo construtor de uma implementao de IBusinessController for executado. Aps a declarao de trs pontos de atuao, o cdigo tem uma construo before. Isso um adendo (advice). Winck e Goetten Jnior (2006, p. 57) definem adendo como um mecanismo similar a um mtodo, usado para declarar o cdigo que deve ser executado a cada ponto de juno em um ponto de atuao. Segundo eles, os adendos so compostos de duas partes, o ponto de atuao, que define as regras de captura dos pontos de juno e o cdigo que ser executado quando ocorrer um ponto de juno (definido pela primeira parte). O programador define em que momento o cdigo ser executado no adendo, em funo do ponto de juno. No caso do adendo before, o cdigo ser executado antes do ponto de juno. Para execut-lo depois usaramos after e para execuo durante o ponto de juno, o identificador around. Vamos analisar o trecho final de cdigo do aspecto, destacado a seguir:
before(): injectionIBusinessController() || injectionIViewController() || injectionIFacade() { InjectionManager manager = new InjectionManager(); manager.execute(thisJoinPoint.getTarget());
364
Esse adendo estabelece que para qualquer dos trs pontos de atuao (injectionIBusinessController, injectionIViewController e injectionFacade), seu cdigo ser executado, antes do ponto de atuao. No caso, uma instncia de InjectionManager ser invocada e chamar seu mtodo execute, passando como argumento o objeto do contexto atual. O mtodo execute de InjectionManager ir verificar todos os atributos do objeto interceptado. Para cada atributo, ele ir verificar a presena da anotao @Injection, definida pelo mdulo Core. Se ela estiver presente, ser passada como argumento para o construtor de InjectionContext, tambm definida pelo Core. O mtodo getFactory de InjectionManager retorna uma implementao do padro de projeto Abstract Factory. Ele verifica se o usurio fez uso da anotao @Factory, especificando uma classe para fabricar os objetos a serem criados. Caso no tenha feito, de acordo com a interface implementada pelo objeto interceptado ele retorna a respectiva classe de fbrica definida pelo mdulo Web. A fbrica de objetos ir retornar uma instncia de acordo com o contexto de injeo, determinado pela anotao. Essa instncia ser injetada no cdigo, mais exatamente, ser associada ao atributo em questo. Dessa forma, no h necessidade de criar instncias de objetos explicitamente, pois o compilador de aspectos ir gerar o cdigo. Isso torna o cdigo-fonte original independente da implementao.
365
configuraes JDBC e tambm a responsvel por inserir uma conexo no controle transacional definido pelo mdulo Core. Finalmente, a classe JDBCTransactionResource representa uma conexo JDBC e pode ser tratada pelo contexto de transao do framework.
366
String nome = rs.getString("nome"); Date nascimento = rs.getDate("nascimento"); String lotacao = rs.getString("lotacao"); result.add(new Funcionario(id, nome, nascimento, lotacao)); } } catch (SQLException e) { throw new ApplicationRuntimeException(ErrorMessage.FUNCIONARIO_005, e); } finally { JDBCUtil.getInstance().closeConnection(); } return result; } }
Para evitar que a troca de banco implique em alterao no cdigo-fonte das classes, a configurao das conexes JDBC feita no arquivo demoiselle.properties, como mostra o exemplo a seguir:
#Configurao para uso de JDBC framework.demoiselle.persistence.jdbc.driver=org.postgresql.Driver framework.demoiselle.persistence.jdbc.url=jdbc:postgresql://localhost/escola framework.demoiselle.persistence.jdbc.user=postgres framework.demoiselle.persistence.jdbc.pass=postgres
Para tratar o mapeamento objeto-relacional, o mdulo Persistence define uma interface chamada IORMDAO, que estende IDAO e define dois mtodos, findByExample() e findById(). possvel integrar qualquer framework especialista de persistncia que use ORM, desde que seja definida uma classe que implemente IORMDAO e outra que seja um recurso transacional. A verso 1.0.x do Demoiselle utiliza o Hibernante como framework de persistncia. O pacote br.gov.framework.demoiselle.persistence.hibernate define a integrao dos frameworks com trs classes: HibernateGenericDAO, HibernateUtil e HibernateTransactionResource. A partir da verso 1.1.x, o JPA foi adotado como padro de persistncia. O pacote br.gov.framework.demoiselle.persistence define a integrao do framework Demoiselle
367
com os provedores de persistncia JPA por meio de trs classes: JPAGenericDAO, EntityManagerProxy e JPATransactionResource.
368
369
estendem IDAO e as implementaes das mesmas. A classe WebDAOFactory usada por padro para construir os objetos por meio de conveno. A conveno que as interfaces e suas implementaes devem ter a mesma identificao, sendo que as interfaces devem ter o prefixo I e o sufixo DAO, enquanto as implementaes devem omitir o prefixo. Por exemplo, a interface IAlunoDAO deve ser implementada pela classe AlunoDAO. possvel modificar a fbrica que far a injeo de dependncias, estendendo WebDAOFactory e usando o parmetro factory da anotao @Factory, conforme exemplo a seguir:
public class EscolaDAOFactory extends WebDAOFactory { public IDAO create(InjectionContext ctx) { return //Lgica da Fbrica; } }
@Factory(factory=EscolaDAOFactory.class) public class AlunoDAOStubTest implements IFacade{ @Injection private IAlunoDAO alunoDAO; }
As operaes de persistncia fazem uso do contexto de transao, conforme vemos no cdigo seguinte:
public class MinhaClasse { @Injection IMeuDAO meuDao;
370
A classe HibernateUtil prov um conjunto de implementaes para camada de persistncia baseado no framework Hibernate. Ela integrada ao contexto de transao, de modo que possui um mecanismo transacional completo. O cdigo a seguir ilustra dessa classe para tratar tanto o commit quando o rollback de uma transao:
public class MinhaClasse { @Injection IMeuDAO meuDao;
public void inserir() { WebTransactionContext.getInstance().init(); try{ meuDao.insert(new MeuPojo()); HibernateUtil.getInstance().commit(); }catch (ApplicationRuntimeException e) { HibernateUtil.getInstance().rollback(); } WebTransactionContext.getInstance().end(); } }
A paginao de dados definida pelo mdulo Util usada pelo mdulo Persistence para manter o controle sobre os dados oriundos de uma consulta e recuperar da base apenas as informaes que sero exibidas na viso do usurio. Os benefcios da paginao so a reduo de custos de uso de memria, processamento e rede e a escalabilidade proporcionada. O cdigo a seguir ilustra o uso de paginao. O trecho de cdigo de uma classe herdeira de HibernateGenericDAO:
public void listar() { Page page = new Page(50, 1); listarBean(page); }
371
HibernateGenericDAO implementa um conjunto de funes (consulta, paginao, insero, alterao e excluso) para simplificar a criao de classes IDAO implementadas pela aplicao. Um exemplo de como se fazer consultas com HibernateGenericDAO pode ser visto no cdigo a seguir:
public class DisciplinaDAO extends HibernateGenericDAO<Disciplina> implements IdisciplinaDAO{
public PagedResult<Disciplina> listar(Page page) { return findHQL("from Disciplina order by nome asc", page); }
public Disciplina buscar(Disciplina professor) { List<Disciplina> retorno = find("from Disciplina order by nome asc"); if (retorno != null && retorno.size() > 0 ) return retorno.get(0); return null; }
372
disponibiliza uma implementao padronizada para ser utilizada nas aplicaes. a classe AbstractManagedBean. A classe AbstractManagedBeanConfig representa as configuraes da aplicao do total de linhas e a quantidade de pginas ao paginar dados. Para facilitar as operaes com managed beans, o framework disponibiliza a classe ManagedBeanUtil. O mdulo View tambm prov a classe PagedResultDataModel, um modelo de dados que converte um PagedResult para a representao grfica dos dados paginados.
373
Finalmente, o mdulo View define a classe CookieManager, um utilitrio que permite a realizao das operaes bsicas relacionadas a cookies na Web.
374
375
interface com o usurio ou uma pgina HTML com informaes do modelo. As alteraes nas informaes apresentadas pela vista so manipuladas pelo terceiro membro da trade MVC: o controlador. O controlador recebe a entrada do usurio, manipula o modelo e faz com que a vista seja atualizada apropriadamente. Dessa forma, a interface de usurio uma combinao da vista e do controlador.
Os padres que compem o catlogo JEE (Alur et alli, 2002, p.8), so agrupados em trs camadas, a saber: camada de apresentao, camada de negcios e camada de integrao. A camada de apresentao tem a responsabilidade de interceptar a entrada de dados e filtr-los, de estabelecer um controle nico para a entrada de dados e identificar os responsveis pelo atendimento s requisies de servio, que culminaro na exibio das vises da aplicao. A camada de negcios, segundo Alur et alli (2002, p. 19) trata da principal lgica de negcios da aplicao. Ela fornece as interfaces necessrias aos componentes de servios de negcios subjacentes. Finalmente, temos a camada de integrao, responsvel pelo sistema de informaes da empresa, incluindo os sistemas de banco de dados, o de processamento de transao, os sistemas herdados e os sistemas de planejamento de recurso da empresa. Nessa camada ocorre a integrao com sistemas que no so JEE e com os sistemas herdados.
376
7.4 Concluso
Um framework direciona o desenvolvedor aos problemas relacionados as regras de negcio do cliente e a apresentao das informaes e reduz o esforo utilizado para resolver detalhes de baixo nvel como segurana, acesso a dados e comunicao com outros ambientes. O Demoiselle Framework cumpre esse papel, ao implementar a padronizao da implementao e criar um ambiente propcio para a prtica da reusabilidade. A padronizao garante maior facilidade de suporte e absoro de sistemas e facilidade de integrao e disponibilizao de servios para os novos sistemas. Essa padronizao resultado da anlise, integrao e utilizao de tecnologias mais reconhecidas utilizadas pelas comunidades de desenvolvedores. O modelo de desenvolvimento baseado em componentes facilita o reuso de mtodos, prticas e processos padronizados, e termina por permitir o reuso de componentes de negcio. A sua caracterstica de ser aberto, com desenvolvimento compartilhado, e de agregar implementaes de software baseadas em padres e especificaes lhe d condies de ser uma ferramenta totalmente voltada a integrao de diferentes instituies e diferentes tecnologias. O Demoiselle Framework abre caminho para a estruturao da anlise de sistemas em domnios, de modo a criar uma gesto orientada a polticas de contedos e promoes de contedos. Porque a infraestrutura de software no o mais o problema, o que abre espao para os desenvolvedores se preocuparem em os problemas relacionados a um determinado domnio de conhecimento. O Demoiselle atende prioritariamente a trs domnios especficos: a integrao de organizaes do Governo com a sociedade, a integrao dentro das organizaes e a integrao de grupos de sistemas. Isso o torna uma plataforma de desenvolvimento de uso geral, que pode ser mantida e usufruda por toda a sociedade, e evoluda de modo a beneficiar a todos.
378
Editar projeto: Destina-se a manuteno das diversas camadas de uma aplicao que utiliza o Demoiselle (criao, edio e remoo). Realiza a gerao de DAOs, Business Controllers, Managed Beans, Fachadas, regras de navegao da aplicao e testes unitrios
Criar pginas: Destina-se a criao de pginas JSP, as pginas podem ser de trs tipos: Listagem, Edio e Visualizao.
Criar CRUD: Destina-se a criao de fluxos do tipo CRUD (Create; Remove; Update e Delete) de entidades a partir de um Pojo selecionado.
Adicionar caractersticas do Demoiselle: Destina-se a transformao de projetos existentes na workspace em projetos do tipo Demoiselle que utilizam o framework Demoiselle.
Remover caractersticas do Demoiselle: Destina-se a transformao de projetos do tipo Demoiselle existentes na workspace em projetos Web, removendo assim as caractersticas do framework Demoiselle.
379
CREATE TABLE autores ( id_autor serial NOT NULL, sobrenome character varying(20) NOT NULL, nome character varying(30), CONSTRAINT autores_pkey PRIMARY KEY (id_autor) ) WITH ( OIDS=FALSE ); ALTER TABLE autores OWNER TO postgres;
380
id_cliente serial NOT NULL, cpf character(11) NOT NULL, nome character varying(80) NOT NULL, apelido character varying(10) NOT NULL, senha character(8) NOT NULL, email character varying(40) NOT NULL,
CONSTRAINT clientes_pkey PRIMARY KEY (id_cliente), CONSTRAINT clientes_apelido_key UNIQUE (apelido), CONSTRAINT clientes_cpf_key UNIQUE (cpf) ) WITH ( OIDS=FALSE ); ALTER TABLE clientes OWNER TO postgres;
CREATE TABLE editoras ( id_editora serial NOT NULL, nome character varying(30) NOT NULL, CONSTRAINT editoras_pkey PRIMARY KEY (id_editora) ) WITH ( OIDS=FALSE ); ALTER TABLE editoras OWNER TO postgres;
CREATE TABLE funcionarios ( id_usuario serial NOT NULL, matricula character(8) NOT NULL,
381
nome character varying(80) NOT NULL, apelido character varying(10) NOT NULL, senha character(8) NOT NULL, email character varying(40) NOT NULL, CONSTRAINT funcionarios_pkey PRIMARY KEY (id_usuario), CONSTRAINT funcionarios_apelido_key UNIQUE (apelido) ) WITH ( OIDS=FALSE ); ALTER TABLE funcionarios OWNER TO postgres;
CREATE TABLE locais ( id_local serial NOT NULL, nome character varying(30) NOT NULL, CONSTRAINT locais_pkey PRIMARY KEY (id_local) ) WITH ( OIDS=FALSE ); ALTER TABLE locais OWNER TO postgres;
CREATE TABLE pedidos ( numero_pedido bigint NOT NULL, data_pedido date NOT NULL, id_cliente integer NOT NULL, CONSTRAINT pedidos_pkey PRIMARY KEY (numero_pedido), CONSTRAINT pedidos_id_cliente_fkey FOREIGN KEY (id_cliente) REFERENCES clientes (id_cliente) MATCH SIMPLE
382
ON UPDATE NO ACTION ON DELETE NO ACTION ) WITH ( OIDS=FALSE ); ALTER TABLE pedidos OWNER TO postgres;
CREATE TABLE telefones ( id_telefone serial NOT NULL, numero character varying(16) NOT NULL, tipo character varying(20) NOT NULL, CONSTRAINT telefones_pkey PRIMARY KEY (id_telefone) ) WITH ( OIDS=FALSE ); ALTER TABLE telefones OWNER TO postgres;
CREATE TABLE telefones_cliente ( id_telefone integer NOT NULL, id_cliente integer NOT NULL, CONSTRAINT telefones_cliente_pkey PRIMARY KEY (id_telefone, id_cliente), CONSTRAINT telefones_cliente_id_cliente_fkey FOREIGN KEY (id_cliente) REFERENCES clientes (id_cliente) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE NO ACTION, CONSTRAINT telefones_cliente_id_telefone_fkey FOREIGN KEY (id_telefone) REFERENCES telefones (id_telefone) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE NO ACTION )
383
CREATE TABLE telefones_funcionario ( id_telefone integer NOT NULL, id_funcionario integer NOT NULL, CONSTRAINT telefones_funcionario_pkey PRIMARY KEY (id_telefone, id_funcionario), CONSTRAINT telefones_funcionario_id_funcionario_fkey FOREIGN KEY (id_funcionario) REFERENCES funcionarios (id_usuario) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE NO ACTION, CONSTRAINT telefones_funcionario_id_telefone_fkey FOREIGN KEY (id_telefone) REFERENCES telefones (id_telefone) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE NO ACTION ) WITH ( OIDS=FALSE ); ALTER TABLE telefones_funcionario OWNER TO postgres;
CREATE TABLE livros ( id_livro serial NOT NULL, isbn character varying(13) NOT NULL, titulo character varying(255) NOT NULL, id_local integer NOT NULL, id_editora integer NOT NULL, ano integer NOT NULL, preco money NOT NULL,
384
CONSTRAINT livros_pkey PRIMARY KEY (id_livro), CONSTRAINT livros_id_editora_fkey FOREIGN KEY (id_editora) REFERENCES editoras (id_editora) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE NO ACTION, CONSTRAINT livros_id_local_fkey FOREIGN KEY (id_local) REFERENCES locais (id_local) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE NO ACTION ) WITH ( OIDS=FALSE ); ALTER TABLE livros OWNER TO postgres;
CREATE TABLE autores_livro ( id_autor integer NOT NULL, id_livro integer NOT NULL, ordem integer NOT NULL, CONSTRAINT autores_livro_pkey PRIMARY KEY (id_autor, id_livro), CONSTRAINT autores_livro_id_autor_fkey FOREIGN KEY (id_autor) REFERENCES autores (id_autor) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE NO ACTION, CONSTRAINT autores_livro_id_livro_fkey FOREIGN KEY (id_livro) REFERENCES livros (id_livro) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE NO ACTION ) WITH ( OIDS=FALSE ); ALTER TABLE autores_livro OWNER TO postgres;
385
CREATE TABLE itens_pedido ( id_item serial NOT NULL, id_pedido integer NOT NULL, id_livro integer NOT NULL, preco money NOT NULL, quantidade integer NOT NULL, CONSTRAINT itens_pedido_pkey PRIMARY KEY (id_item), CONSTRAINT itens_pedido_id_livro_fkey FOREIGN KEY (id_livro) REFERENCES livros (id_livro) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE NO ACTION, CONSTRAINT itens_pedido_id_pedido_fkey FOREIGN KEY (id_pedido) REFERENCES pedidos (numero_pedido) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE NO ACTION ) WITH ( OIDS=FALSE ); ALTER TABLE itens_pedido OWNER TO postgres;
386
387
388
Referncias Bibliogrficas
Allen, P. The Service Oriented Process. CBDI Journal. Fevereiro. 2007. Soa Best Practice Report. Disponvel em <http://www.cbdiforum.com/secure/interact/200702/service_oriented_process.php>. Acesso em 21/07/2009. APACHE SOFTWARE FOUNDATION. Open Letter to Sun Microsystems. Disponvel em <http://www.apache.org/jcp/sunopenletterfaq.html>. Acesso em 29/07/2009. Basham, B. Sierra, K. e Bates, B. Use a Cabea! Servlets & JSP. Rio de Janeiro. Alta Books, 2005. Booch, G. Rumbaugh J. Jacobson, I. The Unified Modeling Language User Guide, AddisonWesley, Reading, MA, 1998. BRASIL. Ministrio do Planejamento, Oramento e Gesto. Documento de Referncia da e-PING Verso 4.0. 16/12/2008. Disponvel em <http://www.governoeletronico.gov.br/anexos/e-ping-versao-4.0>. Acesso em 21/07/2009. Burnette, E. Houghton, A. Getting Started with Eclipse. Disponvel <http://refcardz.dzone.com/refcardz/getting-started-eclipse>. Acesso 23/04/2010. em em
Casett, O. Akamatu, D. M. Kirner, C. Paradigmas para construo de sistemas distribudos. Tema. n 114. Ano III. 1993. Disponvel em <http://www.serpro.gov.br/imprensa/publicacoes/tematec/1993/ttec13>. Acesso em 21/07/2009. Dai, N. WTP Tutorials Building and Running a Web Application. Disponvel em <http://www.eclipse.org/webtools/community/tutorials/BuildJ2EEWebApp/BuildJ2EEW ebApp.html>. Acesso em 26/04/2010. Deitel, H. M. E Deitel, P. J. Java: Como Programar. 6.ed. So Paulo, Pearson Prentice Hall, 2005. Fayad, M. Schmidt, D. Object-Oriented Application Frameworks. Communications of the ACM, New York, v. 40, n.10, p. 32-38, Oct. 1997. Fields, D. K. e Kolb, M.A. Desenvolvendo na Web com JavaServer Pages. Rio de Janeiro. Cincia Moderna, 2000. Fogel, K. Producing Open Source Software: How to Run a Successful Free Software Project. Disponvel em <http://producingoss.com/en/producingoss.pdf>. Acesso em 03/08/2009. Fowler, Martin. Inversion of Control Containers and the Dependency Injection pattern. Disponvel em <http://martinfowler.com/articles/injection.html>. Acesso em 14/08/2009.
389
Fowler, Martin. Padres de Arquitetura de Aplicaes Corporativas. Porto Alegre. Bookman, 2006. Fowler, M. POJO. Disponvel em <http://martinfowler.com/bliki/POJO.html>. Acesso em 04/05/2010. Fraga Filho, C. V. e Reis, J. M. Controla: Ferramenta de Apoio ao Processo de Desenvolvimento de Software em Pequenas Empresas. Anais do Conbratec, 2005. Gonalves, E. Desenvolvendo Aplicaes Web com JSP, Servlets, JavaServer Faces, Hibernate, EJB 3 Persistence e Ajax. Rio de Janeiro. Cincia Moderna, 2007. Grohs, E. M. et al. Framework JSerpro. Congresso Serpro de Tecnologia e Gesto Aplicadas a Servios Pblicos, 2007. Hall, M. e Brown, L. Core Servlets e JavaServer Pages. Volume 1: Tecnologias Core. Rio de Janeiro, Cincia Moderna, 2005. Hofmeister, C. Nord, R. Soni, D. Applied Software Architecture. Addison Wesley, 2000. Lakhani, K. R. e Wolf, R. G. Why Hackers Do What They Do: Understanding Motivation and Effort in Free/Open Source Software Projects. Disponvel em <http://freesoftware.mit.edu/papers/lakhaniwolf.pdf>. Acesso em 13/05/2009. Macias, A. M. Frameworks de Desenvolvimento Viso Geral. Tematec, Ano X, n XX, p. 1, 2008. Disponvel em <www.serpro.gov.br/clientes/serpro/serpro/imprensa/publicacoes/tematec/2008/ttec9 2_a>. Acesso em 28/07/2007. Mariaca, M. Gesto da Diversidade. Disponvel <http://imasters.uol.com.br/artigo/12603/tendencias/gestao_da_diversidade>. Acesso em 17/07/2009. em
McConnell, S. Code Complete: Um Guia Prtico para a Construo de Software. Porto Alegre. Bookman, 2005. McLaughkin, Brett. Pollice, Gary. e West, David. Anlise e Projeto Orientado ao Objeto. Rio de Janeiro. Alta Books, 2007. Miller, J. Padres na Prtica: Coeso e acoplamento. <http://msdn.microsoft.com/pt-br/magazine/cc947917.aspx>. 21/07/2009. Disponvel Acesso em em
Pacitti, Trcio. Paradigmas do Software Aberto. Rio de Janeiro. Livros Tcnicos e Cientficos, 2006. Paula Filho, Wilson P. Engenharia de Software: Fundamentos, Mtodos e Padres. 2.ed. Rio de Janeiro. Livros Tcnicos e Cientficos, 2003. Peltz, C. Web Services Orchestration and Choreography. Disponvel em <http://soa.syscon.com/node/39800>. Acesso em 21/07/2009.
390
Disponvel
em
Pressman, R. S. Engenharia de Software. 6.ed. So Paulo. McGraw-Hill, 2006. Raymond. Eric. S. A Catedral e o Bazar. Disponvel <http://www.dominiopublico.gov.br/download/texto/tl000001.pdf>. Acesso 03/08/2009. em em
Reese, G. Programao para banco de dados: JDBC e Java. 2.ed. So Paulo. Berkeley, 2001. Silberschatz, A. Knorth, H. F. Sudarshan, S. Sistema de Banco de Dados. 3.ed. So Paulo. Makron Books, 1999. Silveira, G. et al. Java para desenvolvimento Web. So Paulo. Caelum, 2005. Shore, J. Warden, S. A Arte do Desenvolvimento gil. Rio de Janeiro. Alta Books, 2008. Sommerville, I. Engenharia de Software. 8.ed. So Paulo. Pearson Addison-Wesley, 2007. Srinivasan, Raghu. N. WTP Tutorials JavaServer Faces Tools Tutorial. Disponvel em <http://www.eclipse.org/webtools/jsf/docs/tutorial/JSFTools_1_0_tutorial.html>. Acesso em 14/06/2010. Taurion, C. Software Livre no Ambiente Corporativo: Situao atual e tendncias. I Workshop sobre Software no Ambiente Corporativo. 2004. So Paulo. Disponvel em <http://www.poli.usp.br/pro/sl/IWSLAC/Apresentacao_Cezar_Taurion.pdf>. Acesso em 04/08/2009. WIKIPEDIA. Brimstone. Disponvel <http://en.wikipedia.org/wiki/Brimstone_(TV_series)>. Acesso em 11/09/2008. em
Winck, D. V. e Goetten JUNIOR, V. AspectJ: Programao Orientada a Aspectos com Java. So Paulo. Novatec, 2006.
391