391
INTRODUÇÃO AO DEMOISELLE FRAMEWORK Uma abordagem comparativa de aplicações Web em Java orientada ao reuso Flávio Gomes da Silva Lisboa com a colaboração de Luciana Campos Mota Esta obra utiliza a licença Creative Commons Attribution-NonCommercial-ShareAlike License

Introducao Ao Demoiselle Framework Versao 1 PDF

Embed Size (px)

Citation preview

Page 1: Introducao Ao Demoiselle Framework Versao 1 PDF

INTRODUÇÃO AO DEMOISELLE FRAMEWORK

Uma abordagem comparativa de aplicações Web em Java orientada ao reuso

Flávio Gomes da Silva Lisboa

com a colaboração de Luciana Campos Mota

Esta obra utiliza a licença

Creative Commons Attribution-NonCommercial-ShareAlike License

Page 2: Introducao Ao Demoiselle Framework Versao 1 PDF

Lisboa, Flávio Gomes da Silva Introdução ao Demoiselle Framework: Uma abordagem comparativa de Utilização do padrão MVC para o desenvolvimento de aplicações Web em Java orientada ao reuso. Brasília. SERPRO, 2010.

III Congresso Internacional Software Livre e Governo Eletrônico.

1.Framework 2.Java 3.Reuso. 4.Web

Page 3: Introducao Ao Demoiselle Framework Versao 1 PDF

DedicatóriaEste 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.

3

Page 4: Introducao Ao Demoiselle Framework Versao 1 PDF

SumárioDedicatória..............................................................................................................................3Agradecimentos.......................................................................................................................8Sobre o autor...........................................................................................................................9Prefácio.................................................................................................................................10Introdução.............................................................................................................................12

1.1 A Arte da Guerra no Desenvolvimento de Software..................................................131.2 Frameworks................................................................................................................151.2 O Desafio dos Frameworks........................................................................................17

A loja virtual de livros...........................................................................................................192.1 Levantamento de requisitos........................................................................................192.2 O Sonho de Nabucodonosor.......................................................................................24

Modelagem............................................................................................................................263.1 Descrevendo os Casos de Uso....................................................................................26

3.1.1 Caso de Uso Adicionar Item ao Carrinho...........................................................273.1.2 Caso de Uso Editar Item do Carrinho.................................................................283.1.3 Caso de Uso Excluir Item do Carrinho...............................................................283.1.4 Caso de Uso Buscar Livro..................................................................................293.1.5 Caso de Uso Fechar Pedido................................................................................293.1.6 Caso de Uso Cadastrar Cliente...........................................................................303.1.7 Caso de Uso Manter Cadastro............................................................................313.1.8 Caso de Uso Autenticar Usuário.........................................................................313.1.9 Caso de Uso Verificar Permissão de Funcionário..............................................323.1.10 Caso de Uso Gerenciar Carrinho......................................................................333.1.11 Caso de Uso Gerar Relatórios...........................................................................333.1.12 Diagramas de Casos de Uso..............................................................................34

3.3 Diagrama Entidade Relacionamento..........................................................................353.4 Diagrama de Classes...................................................................................................363.5 A Implementação........................................................................................................36

Livraria com JSP e JDBC.....................................................................................................384.1 Servlets.......................................................................................................................384.2 JSP..............................................................................................................................434.3 Ambiente de desenvolvimento...................................................................................464.4 Configurando o servidor no Eclipse...........................................................................464.5 Criando um projeto Web............................................................................................504.6 Estrutura do projeto Web............................................................................................524.7 MVC...........................................................................................................................534.8 Camada de Persistência..............................................................................................55

4.8.1 Criando os Modelos............................................................................................554.8.2. Criando os DAOs...............................................................................................724.8.3 Testando a Camada de Persistência..................................................................1084.8.4 Classes e Arquivos Auxiliares da Camada de Persistência..............................111

4.9.Camada de Controle.................................................................................................112

4

Page 5: Introducao Ao Demoiselle Framework Versao 1 PDF

4.9.1 Servlet de Autenticação....................................................................................1154.9.2 Classe Abstrata para Servlets Autenticados......................................................1184.9.3 Servlet de Gravação..........................................................................................119

4.9.3.1 Classe de Controle de Acesso...................................................................1324.9.3.2 Constantes com os Papéis.........................................................................1344.9.3.3 Arquivo com os Papéis dos Usuários........................................................1344.9.3.4 Constantes com os Nomes dos Cadastros.................................................135

4.9.4 Servlet de Remoção..........................................................................................1354.10 Camada de Visão....................................................................................................143

4.10.1 Páginas JSP.....................................................................................................1434.10.2 Classes Auxiliares da Camada de Visão.........................................................157

4.11 Conclusões..............................................................................................................183Livraria com JSF e Hibernate.............................................................................................186

5.1 Hibernate..................................................................................................................1865.2 JSF ...........................................................................................................................1865.3 Configuração do Web Page Editor...........................................................................1875.4 Criando um Projeto JavaServer Faces......................................................................1885.5 Instalando e Usando JBoss Tools para Configurar o Hibernate...............................1925.6 Camada de Persistência............................................................................................198

5.6.1 Criando Modelos com Anotações.....................................................................1985.6.2 Criando DAOs sem JDBC................................................................................2185.6.3 Testando a Camada de Persistência..................................................................2355.6.4 Classes e Arquivos Auxiliares da Camada de Persistência..............................236

5.7 Camada de Controle.................................................................................................2375.7.1 Generalização....................................................................................................2375.7.2 ManagedBeans..................................................................................................2445.7.3 Registro no faces-config.xml............................................................................2625.7.4 Classes Auxiliares da Camada de Controle......................................................265

5.8 Camada de Visão......................................................................................................2685.8.1 Criando as Páginas JSF.....................................................................................2685.8.2 Páginas de Cadastro..........................................................................................2735.8.3 Páginas de Edição.............................................................................................2795.8.4 Páginas de Listagem.........................................................................................291

5.9 Executando a Aplicação...........................................................................................3055.10 Conclusões..............................................................................................................306

Livraria com Demoiselle Framework.................................................................................3086.1 Demoiselle Framework............................................................................................3086.2 Demoiselle Infra.......................................................................................................3096.3 Criação do Projeto Demoiselle Framework.............................................................3096.4 Camada de Persistência............................................................................................312

6.4.1 Criando os Modelos .........................................................................................3126.4.2 Criando os DAOs..............................................................................................3156.4.3 Testando a Camada de Persistência..................................................................3166.4.4 Classes e Arquivos Auxilares da Camada de Persistência...............................317

6.5 Camada de Negócio..................................................................................................321

5

Page 6: Introducao Ao Demoiselle Framework Versao 1 PDF

6.5.1 Controle de Acesso...........................................................................................3226.6 Camada de Controle e Visão....................................................................................323

6.6.1 Autenticação.....................................................................................................3266.7 Conclusão.................................................................................................................326

Demoiselle: Framework de Arquitetura..............................................................................3287.1 Fundamentos.............................................................................................................328

7.1.1 Diversidade.......................................................................................................3287.1.2 Elementos de decisão........................................................................................3297.1.3 Estratégia para a área de tecnologia..................................................................3297.1.4 Diretivas da arquitetura tecnológica.................................................................331

7.1.4.1 Computação distribuída............................................................................3317.1.4.2 Aplicações baseadas em componentes......................................................3327.1.4.3 Processos orientados a eventos.................................................................3337.1.4.4 Acoplamento fraco de funções de negócio...............................................3337.1.4.5 Infraestrutura para suporte a decisões.......................................................3337.1.4.6 Automação de processos...........................................................................3347.1.4.7 Acesso por Internet...................................................................................3347.1.4.8 Software Livre...........................................................................................335

7.1.5 Demoiselle com Desenvolvimento Colaborativo............................................3357.1.6 Orientação a Especificações.............................................................................338

7.1.6.1 JAAS.........................................................................................................3397.1.6.2 JCA e JCE.................................................................................................3407.1.6.3 Servlet.......................................................................................................3417.1.6.4 JSF.............................................................................................................342

7.2 Estrutura do Projeto Demoiselle...............................................................................3437.3 Arquitetura................................................................................................................346

7.3.1 Objetivos e Restrições Arquiteturais ...............................................................3487.3.1.1 Extensibilidade..........................................................................................3487.3.1.2 Reusabilidade............................................................................................3497.3.1.3 Manutenibilidade......................................................................................3507.3.1.4 Desempenho..............................................................................................3507.3.1.5 Estabilidade e Confiabilidade...................................................................350

7.3.2 Perspectiva Estrutural da Solução.....................................................................3517.3.2.1 Módulo Core.............................................................................................351

7.3.2.1.1 Integração entre Camadas.................................................................3517.3.2.1.2 Contexto de Mensagens.....................................................................3547.3.2.1.3 Exceção Padronizada.........................................................................3557.3.2.1.4 Contexto de Segurança......................................................................3567.3.2.1.5 Entidades...........................................................................................3577.3.2.1.6 Contexto de Transação......................................................................3577.3.2.1.7 Acionadores.......................................................................................3587.3.2.1.8 Localizador de Contextos..................................................................359

7.3.2.2 Módulo Util...............................................................................................3607.3.2.2.1 Carregamento de Configuração.........................................................3607.3.2.2.2 Paginação de Dados...........................................................................362

6

Page 7: Introducao Ao Demoiselle Framework Versao 1 PDF

7.3.2.3 Módulo Web.............................................................................................3637.3.2.3.1 Contexto de Segurança......................................................................3647.3.2.3.2 Contexto de Mensagens.....................................................................3647.3.2.3.3 Integração entre Camadas.................................................................3657.3.2.3.4 Contexto de Transação......................................................................3677.3.2.3.5 Inicialização do Ambiente.................................................................3687.3.2.3.6 Redirecionamento Baseado em URL................................................3697.3.2.3.7 Injeção de Dependências com Aspectos...........................................372

7.3.2.4 Módulo Persistence...................................................................................3757.3.2.5 Módulo View............................................................................................3847.3.2.3 Modelo de Arquitetura em Camadas do Demoiselle................................386

7.4 Conclusão ................................................................................................................389Demoiselle Wizard..............................................................................................................391Script para criação do banco de dados................................................................................392Script para apagar as tabelas...............................................................................................399Driver JDBC para PostgreSQL...........................................................................................400Referências Bibliográficas..................................................................................................401

7

Page 8: Introducao Ao Demoiselle Framework Versao 1 PDF

AgradecimentosO primeiro agradecimento é para Deus, que criou o Universo. Sem o Universo seria complicado escrever o livro.

O segundo agradecimento é para José Maria Leocádio, 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 são para os funcionários dos Centros de Documentação e Informação do Serpro, que forneceram a bibliografia necessária para a redação do livro e renovaram os livros com prestatividade até que o trabalho estivesse concluído:

• Jussara Justino José Fonseca

• Lisania Medianeira Mathias Costas

• Márcia Zago

• Marina Costacurta Falavinha

• Marilza Fernandes Trindade

• Salvador Fernandes de Azevedo

Muito obrigado a todos!

8

Page 9: Introducao Ao Demoiselle Framework Versao 1 PDF

Sobre o autorFlávio Gomes da Silva Lisboa é bacharel em Ciência da Computação com pós-graduação em Aplicações Corporativas usando Orientação a Objetos e Tecnologia Java pela Universidade Tecnológica Federal do Paraná.

Atuou como programador em empresas privadas de automação comercial e serviços de informática, e foi funcionário de carreira do Banco do Brasil, onde chegou a analista na diretoria internacional.

Atualmente é analista de desenvolvimento de sistemas da Coordenação Estratégica de Tecnologia do Serviço Federal de Processamento de Dados (Serpro). É membro do time oficial de tradução do Zend Framework. Tem experiência na área de Ciência da Computação, com ênfase em Software Livre, atuando principalmente nos seguintes temas: Java, PHP, padrões, frameworks, MVC e objetos.

9

Page 10: Introducao Ao Demoiselle Framework Versao 1 PDF

PrefácioEste livro traz uma introdução ao Demoiselle Framework. Ele não pressupõe um domínio de JEE, e tenta explicar de maneira prática qual a necessidade do uso de frameworks para desenvolvimento.

O principal objetivo deste livro é ensinar a criar aplicações com uma arquitetura que permita o reuso de componentes e facilite sua manutenção.

Iremos progredir de uma programação com preocupação mais imediatista até uma que seja orientada a resolver problemas mais genéricos.

Para tanto, utilizaremos uma abstração de um problema real. A solução do problema terá três implementações. A primeira será feita com Java puro, sem o auxílio de frameworks. A segunda utilizará frameworks JSF e Hibernate para a persistência de dados. A terceira utilizará o framework Demoiselle. A intenção é aprender como desacoplar produtos específicos da aplicação por meio de uma camada de alto nível baseada em especificações.

Essa última frase complexa significa o seguinte: como conseguir reaproveitar código entre aplicações e gastar menos, ao evitar modificações no código fonte.

A metodologia deste deste livro consiste em:

1. Desenvolver uma aplicação (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 aplicação usando Demoiselle Framework. Ou seja, este livro é o “pré-Demoiselle Framework”.

Não é objetivo aqui oferecer uma referência completa sobre o Demoiselle Framework, mas sim dar ao programador a base para que ele comece a utilizar a plataforma. O maior propósito deste livro é incluir o leitor no time dos capacitados a iniciar a utilização do Demoiselle Framework.

Antes de partir para a apresentação do problema, iremos fazer uma pequena introdução ao assunto que justifica esse livro, a manutenção de software. Os demais conceitos necessários serão passados no momento em que eles forem utilizados na codificação.

Este livro se baseia na versão 5 de Java, o que não impede que o código aqui exposto funcione para versões superiores, é apenas o requisito mínimo. Este livro também pressupõe conhecimentos básicos de linguagem Java, orientação a objetos e HTML. Ele

10

Page 11: Introducao Ao Demoiselle Framework Versao 1 PDF

contém o necessário para programadores que não conhecem Servlets, JSP, JSF, Hibernate e muito menos Demoiselle Framework.

11

Page 12: Introducao Ao Demoiselle Framework Versao 1 PDF

IntroduçãoNão vale a pena criar um projeto rígido e difícil de manter em um esforço de economizar tempo e dinheiro no início, se você continuamente for forçado a reservar recursos de desenvolvimento para manter o projeto no futuro.

Duane Fields e Mark Kolb.

Neste capítulo, introduziremos o problema da manutenção 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 primórdios da computação eletrônica, a palavra software referia-se somente a uma sequência de instruções a serem seguidas e/ou executadas por uma máquina denominada hardware (o computador propriamente dito), na manipulação, redirecionamento ou modificação de um dado/informação ou acontecimento. Mas esse conceito sofreu uma modificação, pois a crescente indústria de software verificou que seu produto final não estava acompanhando a evolução do hardware.

Hoje, o termo software abrange um conjunto de produtos desenvolvidos durante um processo contínuo de construção, que inclui não somente o programa de computador, mas também manuais, especificações, planos de teste e quaisquer outras formas de documentação 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 prática. Entre elas, estão:

Definição do problema;

Desenvolvimento dos requisitos;

Planejamento da construção;

Arquitetura do software ou projeto de alto nível;

Projeto detalhado;

Codificação e depuração;

Testes unitários;

Testes de integração;

Integração;

12

Page 13: Introducao Ao Demoiselle Framework Versao 1 PDF

Testes de sistema;

Manutenção corretiva.”

Além disso, Grohs(2007, p.6) coloca mais quatro questões específicas, dentro do contexto de sistemas multiplataformas e baseados em software livre, que hoje possuem uma grande base instalada:

1. “Como aumentar a complexidade das soluções, sem deixá-las difíceis 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 mão de eficiência) 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 questões precisam ser dadas em um tempo realizável para a empresa e aceitável pelo cliente. É necessário dar conta de todas as atividades pertinentes ao processo de desenvolvimento de software no menor tempo possível, dado que a demanda por implementações e mudanças só tende a crescer (a última mais que a primeira).

Alguns defendem que a criação de software é uma arte, como Donald Knuth, na obra “The Art of Computer Programming”. É claro que a acepção de software dele é limitada, refere-se mais a um contexto matemático dominante nos primórdios da programação de computadores. O conceito de software, após Pressman (2006), é de um produto de engenharia. Se existe algo de arte no software, podemos dizer que é uma Arte da Guerra.

1.1 A Arte da Guerra no Desenvolvimento de Software

Já disse Sun Tzu: “a velocidade é a essência da guerra”.

Projetos de software, assim como quaisquer outros projetos, são esforços temporários, com início e fim definidos. Eles são planejados, executados e controlados, e entregam produtos, serviços ou resultados exclusivos. São desenvolvidos em etapas e continuam por incremento com uma elaboração progressiva. São realizados por pessoas (pessoas comuns, não entidades cósmicas, seres imortais ou deuses) e o mais importante: contam com recursos limitados.

Sun Tzu aconselha: “na guerra, preze pela vitória rápida e evite as operações prolongadas.” Segundo ele, “o tempo prolongado irá desgastar as armas, desmoralizar suas tropas, exaurir suas forças e consumir todos os recursos disponíveis”.

13

Page 14: Introducao Ao Demoiselle Framework Versao 1 PDF

Projetos têm de ser os mais curtos possíveis, porque só assim serão realizáveis. Da mesma forma que na guerra, quanto mais tempo os desenvolvedores demorarem para terminar o projeto, menos eles acreditarão que conseguirão fazê-lo. O orçamento também é limitado, pelo cliente, e pelo capital alocado antes que o mesmo efetue o pagamento pelo serviço. O tempo não é reutilizável. Depois que ele passou, o que foi consumido em recursos já se perdeu. Não é como no seriado Brimstone, onde o detetive Ezekiel Stone acorda toda manhã com os mesmos 36 dólares e 27 centavos que tinha quando morreu.

“O conhecedor dos terríveis 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 término.” Da mesma forma, o desenvolvedor de software deve utilizar os meios necessários para obter um rápido desenvolvimento. Porque assim como pode haver uma nova guerra, pode haver um novo projeto. E da mesma forma que um exército 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 começou, a Alemanha tinha um grave problema. O falecido chanceler Bismarck havia tentado por todos os meios tecer uma rede de alianças que favorecesse o Império Alemão no caso de um conflito armado no continente, mas teve que assistir decepcionado à aliança entre a república da França e o Império Russo. Isso deixava o exército alemão com a possibilidade de uma guerra em duas frentes.

E o que os militares germânicos mais temiam terminou por ocorrer. Quando o Império Austro-Húngaro declarou guerra à Sérvia, culpando-a pelo assassinato do arquiduque Francisco Ferdinando, a Alemanha fez o mesmo, por ser aliada dos austríacos. Mas a Rússia era aliada da Sérvia, e assim declarou guerra à Àustria e à Alemanha. E como a França era aliada da Rússia, declarou guerra à Alemanha. Conclusão: os alemães teriam que combater dois exércitos ao mesmo tempo.

O exército alemão tentou repetir o sucesso do general Helmuth Von Moltke, que em apenas quatro meses invadiu e derrotou a França em 1870. Os generais germânicos pensavam em derrotar rapidamente os franceses para poder voltar e se concentrar nos russos. Mas infelizmente, os britânicos vieram em auxílio dos franceses e atrasaram o avanço alemão. Nesse ínterim, o lado leste do Império Alemão foi atacado pelos russos. Isso obrigou os germânicos 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 Rússia se render em 1917, não houve tempo para os alemães se reorganizarem, e a entrada dos norte-americanos no conflito nesse mesmo ano terminou por esgotar os recursos do Império Alemão.

Longe de ser apenas um evento bélico, a guerra em duas frentes é uma realidade no desenvolvimento de software. E, assim como os alemães, os gerentes e líderes de

14

Page 15: Introducao Ao Demoiselle Framework Versao 1 PDF

projeto têm de tentar evitar a sustentação simultânea de dois projetos por um tempo longo. Claro que aqui estamos tratando de projetos grandes, com escopos bem diferentes, os quais são análogos a dois adversários que atacam por lados diferentes. Há a situação em que os projetos possuem uma certa convergência, similaridade, o que pode ser metaforizado na situação bélica de atacar dois exércitos em uma só frente.

1.2 Frameworks

De acordo com Martin(1995), “para conseguir um rápido desenvolvimento, novos sistemas devem ser construídos a partir de classes que já existam e que possam ser adaptadas às circunstâncias. Um desenvolvimento baseado em repositórios é necessário – com ferramentas automatizadas. Estas ferramentas devem tornar rápido e fácil o trabalho de se personalizar uma interface gráfica padrão. Sistemas devem ser construídos de forma que possam ser mudados rápida e facilmente, sem os problemas de manutenção do passado.”

Em consonância com essa afirmação, o principal propósito de um framework é ajudar no processo de desenvolvimento de aplicações. Ele permite que as aplicações sejam desenvolvidas mais rapidamente e mais facilmente, e deve resultar em uma aplicação de qualidade superior. A figura 1 mostra uma hierarquia típica de uma aplicação Java web baseada em frameworks.

Segundo Fayad (1997), "framework é um conjunto de classes que colaboram para realizar uma responsabilidade para um domínio de um subsistema da aplicação."

A necessidade de construir software de forma cada vez mais ágil e a exigência da criação 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.

Vários frameworks podem ser usados na construção de um único aplicativo de software. Para facilitar a escolha e o uso de vários frameworks durante o desenvolvimento de software, e garantir a integração, evolução e manutenção dos mesmos, Macias (2008) propõe uma estrutura chamada Framework Integrador.

15

Page 16: Introducao Ao Demoiselle Framework Versao 1 PDF

Figura 1: Arquitetura de uma aplicação Java web com framework

De acordo com Macias, um framework define parâmetros de projeto; a forma como é realizada a divisão 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 aplicações que os utilizam. Além disso, fornece padrões de projetos - estruturas essenciais para o desenvolvimento de projetos de aplicações de software - uma espinha dorsal e o continente para os componentes criados nas aplicações e que irão funcionar dentro delas.

Como os frameworks absorvem as decisões de projeto que são comuns ao seu domínio de aplicação, eles priorizam a reutilização de projetos ao invés da reutilização de código, apesar de incluírem classes concretas que podem ser imediatamente utilizadas.

O uso de frameworks e a reutilização em nível de projeto implicam em uma inversão de controle entre um código específico de aplicação e o software na qual ela se baseia. No caso de aplicações que não usam frameworks e sim bibliotecas ou DLLs (Dynamic-link library), o código principal é construído na aplicação que realiza chamadas ao trecho que se deseja utilizar. Já no caso de aplicações que utilizam frameworks, o trecho principal reutilizável está nele, que chama o código escrito pelo desenvolvedor, como mostrado na figura 2.1.

16

Page 17: Introducao Ao Demoiselle Framework Versao 1 PDF

1.2 O Desafio dos Frameworks

Erich Gamma (Venners, 2005) define framework como o mais alto nível de reuso. Considerando isso, construir um novo framework, em vez de usar um existente, seria ignorar o objetivo de alcançar máxima reusabilidade.

Além disso, há um conflito entre a miríade de características oferecida por cada framework, específico para certos espaços de problemas, e a necessidade de uma empresa. Finalmente, há o problema de gerenciar a obsolescência dos produtos atuais e absorver novas tecnologias.

Um framework genérico não pode pensar em ser mais uma implementação que competirá com as existentes, mas um middleware que integre as soluções existentes. Isso resolveria o problema de gerenciar diferentes soluções para produzir software.

Mas permanece ainda a questão da obsolescência e da inovação. O software nunca é um produto acabado. Dessa forma, temos que responder a questão de como uma infraestrutura para construir software pode acompanhar o desenvolvimento tecnológico sem criar estruturas que aprisionem o desenvolvedor.

A resposta, nesse caso, é criar uma interface baseada em especificações técnicas, e não em implementações.

As Java Specification Requests (JSRs) são especificações escritas para padronizar a tecnologia Java. JSRs estabelecem padrões para a implementação de produtos, e sempre disponibilizam uma implementação de referência. Diferentes distribuidores podem ter diferentes produtos que seguem as mesmas JSRs, e sua API se comunica com as aplicações de modo indistinguível.

Sendo assim, um framework que pretenda ser genérico para a criação de aplicações na tecnologia Java deve ser baseada nas JSRs, e não em produtos de software específicos.

Neste livro, tentaremos convencê-lo da necessidade de usar um framework, começando por um exemplo em que não usamos nenhum. Em seguida tentaremos convencê-lo de que não é suficiente usar um framework (pois há vários) e que não basta juntar vários frameworks de qualquer modo.

No próximo capítulo, introduziremos o problema que serve de insumo para nossas implementações.

17

Page 18: Introducao Ao Demoiselle Framework Versao 1 PDF

CAPÍTULO 2

A loja virtual de livrosNeste capítulo iremos simular, de forma bastante simplificada, o início de um

processo de desenvolvimento de software.

2.1 Levantamento de requisitos

McConnel (2005) diz que o problema de software é um problema perverso porque você precisa resolvê-lo uma vez para defini-lo claramente, e então resolvê-lo mais uma vez para criar uma solução que funcione. Que funcione apenas, não que funcione da melhor forma.

De forma mais simples, o que ele quer dizer é que o cliente e o fornecedor de software não tem, a princípio, o mesmo entendimento sobre o produto a ser desenvolvido.

Isso na verdade é um reflexo da dificuldade de levantar requisitos junto ao cliente.

Para ilustrar essa dificuldade, iremos recorrer a uma analogia com um episódio do livro Alice no País das Maravilhas. Após deixar a casa da Duquesa, Alice encontra uma bifurcação. Nesse ponto, o Gato de Cheshire aparece no galho de uma árvore. Alice pergunta qual caminho ela deve tomar. O gato responde que isso depende onde ela quer chegar. Alice responde que não sabe. Então o gato diz que, nesse caso, qualquer caminho serve.

Apesar desse diálogo parecer tolo, ele na verdade já ocorreu muitas vezes e ainda ocorre no processo de desenvolvimento de software. O cliente quer algo, mas não sabe exatamente o quê. Teoricamente, neste caso, qualquer coisa produzida o atenderia.

Mas certamente o cliente, por mais que não saiba o que quer, não aceitará qualquer coisa, pois ele saber muito bem o que não quer. Sendo assim, é necessário um processo de desenvolvimento bem definido e estruturado, que garanta que se chegue a um produto de software que atenda as necessidades do cliente.

Nós estamos considerando um processo de desenvolvimento com quatro fases: concepção, análise e projeto, construção e encerramento.

18

Page 19: Introducao Ao Demoiselle Framework Versao 1 PDF

Figura 2: Alice e o Gato de Cheshire (ilustração de John Tenniel)

19

Page 20: Introducao Ao Demoiselle Framework Versao 1 PDF

A concepção é a fase na qual identificamos as necessidades do cliente e analisamos se o projeto é viável. Esta é a hora certa para desistir de um projeto de software. Qualquer decisão 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 física e trabalhar apenas virtualmente.

O analista de negócios da Laele, Tião, faz uma entrevista com o dono da Alexandria e alguns de seus funcionários. A partir daí ele obtém uma lista simplificada de requisitos:

1. O sistema da livraria compreende duas interfaces: uma para o cliente e outra para o funcionário da loja;

2. A interface do cliente é a padrão, e deve ser apresentada se nada em contrário for especificado;

3. A interface do cliente deverá apresentar na página inicial uma relação aleatória de livros;

4. Essa relação deverá apresentar a imagem da capa do livro, o título, o(s) autor(es), o preço e um botão para colocar o livro em um carrinho de compras.

5. O carrinho de compras é criado quando o cliente seleciona o primeiro livro. Ele não 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 página inicial também deverá apresentar uma caixa de busca, que permita a recuperação de livros pelo ISBN, título, autor e editora.

9. O resultado das buscas, se exceder um determinado limite de livros, deverá ser distribuído em várias páginas. O limite padrão de livros a serem visualizados é quatro.

10. A interface deverá apresentar um botão para fechar o pedido de venda em todas as páginas, assim como um link para ver o conteúdo do carrinho de compras. O botão de fechamento do pedido deverá levar o cliente para uma página onde ele

20

Page 21: Introducao Ao Demoiselle Framework Versao 1 PDF

escolherá a forma de pagamento, dentre quatro: depósito em conta, débito automático, cartão de crédito e boleto bancário.

11. Após confirmar a forma de pagamento, o cliente deverá obrigatoriamente se autenticar, se já não estiver autenticado.

12. A autenticação é 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 não tiver cadastro, ele pode se cadastrar a qualquer momento. Para isso, deve haver um link chamado Cadastrar. Ele só pode estar disponível se o cliente estiver desconectado.

15. O link Cadastrar apresentará um formulário onde o cliente registrará seu CPF, um nome, um apelido, seu e-mail e uma senha de 8 números ou letras.

16. Com a confirmação da compra pelo cliente, deve ser gerado um pedido de venda.

17. Com a geração do pedido de venda, o carrinho de compras deve ser esvaziado.

18. A interface do funcionário apresenta opções para incluir, alterar e excluir registros das tabelas do sistema.

19. O sistema possui controle de acesso, pois os funcionários têm acesso e privilégios limitados.

20. Há um funcionário administrador, que tem acesso a tudo. O administrador é quem define os acessos e privilégios.

21. A interface do funcionário também gera um relatório de pedidos de um determinado período (dia, mês, ano).

Todos esses requisitos são funcionais, ou, de acordo com Sommerville (2007, p. 80), são requisitos que descrevem o que o sistema deve fazer. Para não tornar o exemplo complexo demais, descartaremos requisitos não funcionais.

2.2 O Sonho de Nabucodonosor

Você deve ter percebido que faltam algumas coisas nos requisitos do sistema de livraria que vemos em qualquer sistema de livraria na Internet. É claro que não temos a pretensão de criar neste livro um sistema de software real, mas apenas um exemplo que permita explorar as principais características de aplicações web em Java.

21

Page 22: Introducao Ao Demoiselle Framework Versao 1 PDF

Mas é oportuno salientar a importância 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 estão 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 análise incorreta, que produz um projeto incorreto, que por sua vez produz uma implementação incorreta. E os testes serão realizados sobre falsas premissas, o que os tornará inválidos. Finalmente, o pior caso é chegar-se à conclusão do projeto com um produto que não atende os requisitos.

Segundo Pinho (2010), para minimizar os problemas relacionados ao levantamento de requisitos, várias técnicas foram desenvolvidas. A entrevista, usada no exemplo, é uma técnica tradicional, cuja eficiência depende da experiência do entrevistador e tem mais utilidade quando as partes interessadas tem muitos conhecimentos subjetivos (conceitos estritamente ligados ao seu negócio) e estão dispostos a serem entrevistados.

A academia e o mercado de TI têm reconhecido a importância da Engenharia de Requisitos, tanto que já existe grande literatura sobre o assunto, eventos específicos e produtos de software para auxiliar essa disciplina. O engenheiro de requisitos é um profissional que precisa ter um conhecimento técnico abrangente e ao mesmo tempo uma capacidade de relacionamento pessoal que ajude a extrair as informações necessárias e relevantes para a elaboração de um projeto de software.

A escolha das técnicas mais adequadas de levantamento de requisitos para um projeto é de suma importância. Mas tão importante é conseguir estabelecer um bom entendimento com o cliente, para que ele também fique ciente do quão 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 história de Nabucodonosor, o fundador do segundo império babilônico, relatada na Bíblia (Daniel 2:1-5). Ele teve um sonho estranho uma noite e acordou perturbado. Mandou chamar os sábios e magos e pediu que eles interpretassem o sonho. Eles perguntaram qual era o sonho, para poderem interpretá-lo. O rei disse que não se lembrava, mas se eles não interpretassem o sonho, iria mandar matá-los. É algo similar a mandar criar um sistema, mas não saber ao certo o que ele tem de fazer, e ainda ameaçar o desenvolvedor com um processo judicial.

22

Page 23: Introducao Ao Demoiselle Framework Versao 1 PDF

CAPÍTULO 3

Modelagem (colaboração de Luciana Campos Mota)

Este capítulo tratará da formalização de requisitos e da modelagem do sistema de livraria virtual. A partir da lista de requisitos do capítulo anterior, serão criados os casos uso e os diagramas de classe.

3.1 Descrevendo os Casos de Uso

Segundo Sommerville (2007, p. 102), casos de uso constituem uma técnica baseada em cenários para levantamento de requisitos. De acordo com Booch et al (1998, p. 222) um caso de uso é uma descrição de um conjunto de sequências de ações, incluindo variantes, que um sistema executa e que produzem um resultado de valor observável para um ator.

Um caso de uso pode descrever um único objetivo e as possíveis coisas que podem ocorrer até que o usuário alcance o objetivo proposto.

A lista de requisitos que colhemos até agora nos apresenta uma visão superficial do que o que cliente deseja. Precisamos refinar o entendimento do problema, e obter um insumo mais detalhado que sirva para a fase de elaboração do software.

Sommerville (p. 103) afirma que casos de uso identificam as interações individuais com o sistema. Ele define o conteúdo de um caso de uso como tendo:

• Uma hipótese inicial;

• Uma descrição passo a passo do fluxo normal de eventos;

• Uma descrição passo a passo do tratamento de erros;

• Uma descrição passo a passo de atividades alternativas;

• Qual o estado do sistema após o término.

Esse conteúdo, com algumas variações, pode ser encontrado em vários autores de Engenharia de Software.

Os casos de uso podem ser representados graficamente por diagramas de casos de uso. As interações descritas nos casos de uso podem ser representadas por diagramas de sequência.

23

Page 24: Introducao Ao Demoiselle Framework Versao 1 PDF

Vamos criar os casos de uso de nosso sistema, a partir da lista inicial de requisitos. O objetivo

3.1.1 Caso de Uso Adicionar Item ao Carrinho

• Hipótese inicial: O cliente está visualizando a página inicial do sistema, com a lista de livros. A lista pode ser aleatória ou resultado de uma busca;

• Fluxo normal de eventos:

1. O cliente clica no botão Comprar localizado abaixo da imagem do livro.

2. O livro é adicionado ao carrinho de compras.

3. Ele é direcionado para uma página que exibe o conteúdo do carrinho de compras, com a soma dos valores dos itens, um botão Fechar Pedido e um botão Continuar Comprando.

4. Ele clica no botão Continuar Comprando e é redirecionado para a página inicial do sistema.

• Tratamento de erros:

◦ No caso de alguma página não ser encontrada, o cliente deve ser direcionado para uma página de erro padrão, e a exceção deve ser gravada em log.

• Atividades alternativas:

1. Quando estiver na página de conteúdo 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 página do carrinho também deve exibir a caixa de busca, permitindo que o usuário procure por outros livros sem ter de voltar para a página inicial.

• Estado do sistema após o término: item adicionado ao carrinho de compras.

3.1.2 Caso de Uso Editar Item do Carrinho

• Hipótese 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 botão Continuar Comprando e é redirecionado para a página inicial do sistema.

24

Page 25: Introducao Ao Demoiselle Framework Versao 1 PDF

• Tratamento de erros:

◦ No caso de alguma página não ser encontrada, o cliente deve ser direcionado para uma página de erro padrão, e a exceção deve ser gravada em log.

• Atividades alternativas:

1. O cliente pode remover um item do carrinho – caso de uso 3.1.3.

2. A página do carrinho também deve exibir a caixa de busca, permitindo que o usuário procure por outros livros sem ter de voltar para a página inicial.

• Estado do sistema após o término: conteúdo do carrinho de compras modificado.

3.1.3 Caso de Uso Excluir Item do Carrinho

• Hipótese 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 botão Continuar Comprando e é redirecionado para a página inicial do sistema.

• Tratamento de erros:

◦ No caso de alguma página não ser encontrada, o cliente deve ser direcionado para uma página de erro padrão, e a exceção deve ser gravada em log.

• Atividades alternativas:

1. O cliente executa o caso de uso 3.1.5. - Fechar Pedido.

2. A página do carrinho também deve exibir a caixa de busca, permitindo que o usuário procure por outros livros sem ter de voltar para a página inicial.

• Estado do sistema após o término: item excluído do carrinho de compras.

3.1.4 Caso de Uso Buscar Livro

• Hipótese inicial: O cliente está na página inicial, preenche a caixa de texto de busca, seleciona um critério de busca e clica sobre o botão Buscar.

• Fluxo normal de eventos:

1. O cliente preenche a caixa de busca com as informações do livro.

25

Page 26: Introducao Ao Demoiselle Framework Versao 1 PDF

2. O cliente seleciona o critério de busca desejado (nome do livro, nome do autor ou ISBN)

3. O cliente clica no botão Buscar Livro.

4. O sistema apresenta uma página com os livros que atendem ao critério selecionado.

• Tratamento de erros:

◦ No caso de alguma página não ser encontrada, ou houver algum problema de conexão com o banco de dados, o cliente deve ser direcionado para uma página de erro padrão, e a exceção deve ser gravada em log.

• Atividades alternativas:

O cliente encerra o caso de uso

• Estado do sistema após o término: apresentação de uma listagem de livros.

3.1.5 Caso de Uso Fechar Pedido

• Hipótese inicial: O cliente está na página do carrinho e clica sobre o botão 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, não conseguirá adicionar mais itens.

2. O cliente é direcionado para uma página onde escolhe uma forma de pagamento dentre quatro: depósito em conta, débito automático, cartão de crédito e boleto bancário.

3. Após escolher a forma de pagamento, o cliente confirma o pedido.

4. O sistema grava o pedido de venda e informa o número do pedido. O cliente pode voltar a tela inicial do sistema.

• Tratamento de erros:

◦ No caso de alguma página não ser encontrada, ou houver algum problema de conexão com o banco de dados, o cliente deve ser direcionado para uma página de erro padrão, e a exceção deve ser gravada em log.

• Atividades alternativas:

Não há.

26

Page 27: Introducao Ao Demoiselle Framework Versao 1 PDF

• Estado do sistema após o término: pedido de venda gravado.

3.1.6 Caso de Uso Cadastrar Cliente

• Hipótese inicial: O cliente clicou no botão Cadastrar na página inicial.

• Fluxo normal de eventos:

1. O sistema exibe um formulário pedindo nome, apelido, e-mail e senha.

2. O cliente preenche os dados e confirma a gravação.

3. O sistema grava os dados no banco.

4. O cliente é redirecionado para a página onde se encontrava anteriormente.

• Tratamento de erros:

◦ No caso de alguma página não ser encontrada, ou houver algum problema de conexão com o banco de dados, o cliente deve ser direcionado para uma página de erro padrão, e a exceção deve ser gravada em log.

• Atividades alternativas:

A partir de 2, se os dados não forem validados, o formulário pode ser exibido novamente com mensagem indicando quais são os problemas.

• Estado do sistema após o término: cadastro de cliente com registro adicionado.

3.1.7 Caso de Uso Manter Cadastro

• Hipótese inicial: Um funcionário se autenticou no módulo administrativo do sistema e deseja realizar uma operação em um cadastro.

• Fluxo normal de eventos:

1. O funcionário seleciona um cadastro.

2. O funcionário edita o registro.

3. O funcionário grava as alterações.

5. O funcionário volta para o menu principal do sistema.

• Tratamento de erros:

◦ No caso de alguma página não ser encontrada, ou houver algum problema de conexão com o banco de dados, o funcionário deve ser direcionado para uma página de erro padrão, e a exceção deve ser gravada em log.

• Atividades alternativas:

27

Page 28: Introducao Ao Demoiselle Framework Versao 1 PDF

A partir de 1, o funcionário pode procurar um registro.

A partir de 2, o funcionário pode incluir um registro.

A partir de 2, o funcionário pode remover um registro.

A partir de 2, o funcionário pode pesquisar um registro.

A realização de qualquer operação depende de permissão.

• Estado do sistema após o término: cadastro com registro modificado.

3.1.8 Caso de Uso Autenticar Usuário

• Hipótese inicial: O usuário está na página de autenticação.

• Fluxo normal de eventos:

1. O usuário preenche os dados de login (apelido e senha).

2. O usuário clica sobre o botão Autenticar.

3. O sistema valida os dados.

4. O usuário é direcionado para a tela correspondente dependendo de seu perfil (cliente, funcionário).

• Tratamento de erros:

◦ No caso de alguma página não ser encontrada, ou houver algum problema de conexão com o banco de dados, o funcionário deve ser direcionado para uma página de erro padrão, e a exceção deve ser gravada em log.

• Atividades alternativas:

A partir de 3, se os dados não forem válidos, o funcionário será redirecionado para o formulário de autenticação.

• Estado do sistema após o término: usuário autenticado no sistema.

3.1.9 Caso de Uso Verificar Permissão de Funcionário

• Hipótese inicial: O funcionário solicita acesso a um recurso do módulo administrativo.

• Fluxo normal de eventos:

1. O sistema verifica as permissões do funcionário.

2. O sistema direciona o funcionário para a página responsável pela interação com o recurso solicitado e registra o acesso no log.

28

Page 29: Introducao Ao Demoiselle Framework Versao 1 PDF

• Tratamento de erros:

◦ No caso de alguma página não ser encontrada, ou houver algum problema de conexão com o banco de dados, o funcionário deve ser direcionado para uma página de erro padrão, e a exceção deve ser gravada em log.

• Atividades alternativas:

A partir de 1, se o funcionário não tiver permissão para manipular o recurso, ele será redirecionado para a página do menu principal, com mensagem avisando que ele não tem permissão. A tentativa acessar um recurso não permitido será gravada em log.

• Estado do sistema após o término: registro adicionado no log do sistema.

3.1.10 Caso de Uso Gerenciar Carrinho

• Hipótese inicial: O usuário do tipo cliente já está autenticado no sistema e clica sobre o carrinho de compras.

• Fluxo normal de eventos:

1. O cliente verifica o conteúdo do carrinho.

2. Ele clica no botão Continuar Comprando e é redirecionado para a página inicial do sistema.

• Tratamento de erros:

◦ No caso de alguma página não ser encontrada, o cliente deve ser direcionado para uma página de erro padrão, e a exceção 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 página do carrinho também deve exibir a caixa de busca, permitindo que o usuário procure por outros livros sem ter de voltar para a página inicial.

• Estado do sistema após o término: atualização dos itens do carrinho.

3.1.11 Caso de Uso Gerar Relatórios

• Hipótese inicial: O usuário (do tipo funcionário) deseja gerar um relatório de pedidos de determinado dia/mês/ano.

• Fluxo normal de eventos:

29

Page 30: Introducao Ao Demoiselle Framework Versao 1 PDF

1. O usuário(funcionário) se autentica no sistema.

2. No módulo administrativo, ele clica no botão Relatórios.

3. Na página de relatórios, será solicitado para que ele indique o período desejado, data início e data fim.

4. Após informar o período, ele clica em Gerar Relatório.

5. O relatório será gerado e impresso na tela.

• Tratamento de erros:

◦ No caso de alguma página não ser encontrada, o cliente deve ser direcionado para uma página de erro padrão, e a exceção deve ser gravada em log.

• Atividades alternativas:

1. O usuário pode imprimir o relatório na impressora ou em arquivo.

• Estado do sistema após o término: relatórios gerados.

30

Page 31: Introducao Ao Demoiselle Framework Versao 1 PDF

3.1.12 Diagramas de Casos de Uso

A figura 3 faz a representação dos casos de uso descritos anteriormente, envolvendo os atores e todas as funcionalidades do sistema.

Conforme Booch et al (1998, p. 226), uma generalização é uma relação de herança direta entre classes. A generalização pode ser usada quando dois ou mais casos de uso possuem comportamento, estrutura e finalidades comuns. Sendo assim, é possível descrever as partes compartilhadas em um novo caso de uso, especializado pelos casos de uso filho. A figura 3.2 demonstra uma generalização do caso de uso Gerenciar Carrinho, onde os casos de uso Excluir Item do Carrinho, Adicionar Item ao Carrinho e Editar Item do Carrinho são especializações do Caso de uso (pai) Gerenciar Carrinhho.

31

Figura 3: Casos de Uso da Aplicação

Page 32: Introducao Ao Demoiselle Framework Versao 1 PDF

3.3 Diagrama Entidade Relacionamento

Uma informação importante é se o cliente já possui uma base de dados, para ser utilizada com a aplicação. Se ele já possui, é necessário analisar a documentação dela, ou criar uma se não existir.

No nosso exemplo, a livraria já tem uma base de dados. E ao ser questionado sobre isso, o seu Omar diz que tem que usar essa base, porque todos os livros já estão cadastrados, além dos dados de clientes e funcionários. Essa base foi construída por um ex-funcionário, que criou um pequeno sistema para a loja, para atendimento presencial, mas não o concluiu. O sistema atual se resume apenas a fazer a busca de livros.

Assim, antes de modelarmos as classes do sistema, precisamos saber qual é a estrutura da base de dados. Para isso, usamos um diagrama entidade-relacionamento.

Segundo Silberschatz et (1999, p. 21), o modelo entidade-relacionamento (E-R) “foi desenvolvido para facilitar o projeto do banco de dados, permitindo a especificação do esquema da empresa, que representa toda a estrutura lógica do banco de dados”. Eles ainda acrescentam que “o modelo E-R é um dos modelos com maior capacidade semântica; os aspectos semânticos do modelo se referem à tentativa de representar o significado dos dados”. Finalmente, eles afirmam que “o modelo E-R é extremamente útil para mapear, sobre um esquema conceitual, o significado e interações das empresas reais”.

A figura 5 mostra as tabelas do banco de dados mantido pela livraria:

32

Figura 4: Exemplo de generalização do Caso de uso Gerenciar Carrinho

Page 33: Introducao Ao Demoiselle Framework Versao 1 PDF

Esse diagrama omite o nome dos relacionamentos, mostrando apenas a cardinalidade dos mesmos. Na verdade, o nome do relacionamento, para a aplicação, é indiferente. O fato de colocá-lo no diagrama tem um propósito de utilidade: verificar as regras de negócio com o cliente.

3.4 Diagrama de Classes

Booch et al (1998, p. 105) afirma que os diagramas de classe são os diagramas mais comuns encontrados nos sistemas de modelagem objeto-relacional. Conforme o autor, um diagrama deste tipo deve mostrar um conjunto de classes, interfaces e colaborações e os relacionamentos entre eles.

O diagrama de classes da figura 6 é uma consequência do levantamento de requisitos apontado no capítulo 2, da definição de casos de usos e das informações obtidas a partir do diagrama entidade-relacionamento. Classes contém atributos e métodos, mas neste momento, vamos nos concentrar nos dados a serem manipulados.

3.5 A Implementação

A modelagem feita neste capítulo teve por objetivo principal oferecer uma visão completa do sistema, mas não realizaremos sua implementação completa.

Um motivo é que iremos fazer três implementações diferentes, e desenvolver uma aplicação completa três vezes tornaria este livro muito extenso. Outro motivo é que o propósito deste livro é tão somente ensinar práticas de reuso que facilitem a manutenção de aplicações Java Web, e não apresentar uma aplicação para ser vendida. A aplicação aqui é o meio de ensino, e não o fim.

Iremos nos restringir a alguns casos de uso que serão suficientes para realizar a comparação entre três alternativas de desenvolvimento Java: sem frameworks, com JSF

33

Figura 5: Diagrama Entidade-Relacionamento da livraria

Page 34: Introducao Ao Demoiselle Framework Versao 1 PDF

e Hibernate e com Demoiselle. Desenvolver todos os casos de uso não acrescentaria em nada ao objetivo aqui almejado, apenas traria repetições desnecessárias.

Por isso, nos capítulos a seguir, serão implementados apenas os casos de uso Manter Cadastro, Autenticar Usuário e Verificar Permissão de Funcionário, que compõem grande parte da metade administrativa da aplicação.

34

Figura 6: Diagrama de classes do Sistema Livraria Virtual

Page 35: Introducao Ao Demoiselle Framework Versao 1 PDF

CAPÍTULO 4

Livraria com JSP e JDBCNeste capítulo, faremos a implementação de alguns casos de uso descritos no capítulo anterior, usando a tecnologia JavaServer Pages (JSP). JSP é uma evolução dos servlets Java, baseada em linguagens de scripts largamente utilizadas, como ASP e PHP.

Faremos uma pequena introdução aos servlets e à JSP, para em seguida iniciar a implementação da parte administrativa da livraria.

Para acompanhar este capítulo, você precisa ter instalado o JDK 6.

4.1 Servlets

Servlets são aplicações Java executadas em servidores web. Eles são consequência de uma demanda por tecnologias de conteúdo dinâmico. Nesta seção iremos apresentar o contexto da criação 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 comunicação entre um cliente e um servidor. Essa comunicação é, a princípio, assíncrona. O cliente faz uma requisição ao servidor e este envia uma resposta. O cliente não precisa esperar pela resposta para enviar outra requisição. Por outro lado, o servidor não envia resposta alguma se não receber uma requisição.

O objeto requisitado sempre é um arquivo de texto. O protocolo TCP “é responsável 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 transferência e roteamento dos blocos, ou pacotes, de um hospedeiro para outro até que eles cheguem ao seu destino.

A estrutura básica do protocolo HTTP atende bem ao serviço de páginas estáticas. Um computador que faz o papel de cliente envia uma requisição de um arquivo (geralmente uma página HTML) e o servidor web a devolve. Um software chamado navegador interpreta o conteúdo do arquivo e exibe algo interessante para o usuário final.

35

Page 36: Introducao Ao Demoiselle Framework Versao 1 PDF

Esse serviço de páginas estáticas foi o princípio da utilização da web, mas somente um conjunto de páginas HTML não permite criar uma aplicação de software, em que haja interação com o usuário final. Uma aplicação não deve somente prover texto pré-definido, mas processar dados fornecidos pelo usuário 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 conteúdo dinâmico. Em vez de uma resposta fixa, um sistema web dinâmico oferece respostas personalizadas, com base em parâmetros e eventos disparados pela página.

Além disso, para que um conjunto de páginas web possa funcionar como uma aplicação, é preciso de mecanismos que mantenham estados entre requisições. O protocolo HTTP não mantém estado, assim é necessário uma aplicação no servidor que consiga fazer isso.

Segundo Fields e Kolb (2000, p. 3), “o primeiro padrão para conteúdo de web dinâmico se baseava na Common Gateway Interface, ou CGI”. O CGI especifica como servidores web passam informações de requisições para programas externos. Esses programas externos, rodados pelo servidor, geram respostas em tempo de execução. Para o cliente, não muda nada, ele continua recebendo uma página web. O que muda é que no servidor essa página não existia originalmente (e não existirá mais depois do envio da resposta).

No CGI, ao receber uma requisição, o servidor web verifica se ela se refere a um arquivo estático (página HTML) ou a um arquivo associado a uma aplicação. Essa associação deve estar configurada no servidor. Ao identificar que o arquivo solicitado deve ser processado por uma aplicação externa, o servidor gera um processo para executar a aplicação, e recebe a saída desta. Ao recebê-la, ele simplesmente a envia para o cliente, como faria com o conteúdo de qualquer arquivo ordinário gravado em disco.

O problema do CGI é que ele cria um novo processo para cada nova requisição, pois os programas de CGI rodam fora do software servidor da web. Isso torna sua aplicabilidade limitada para uso em grande escala de aplicações baseadas na web.

Em 1996, a Sun Introduziu os servlets. Servlets são aplicações Java, como dissemos no início da seção. A diferença entre eles e os programas tradicionais de CGI (incluindo programas Java padrão), é que todos os servlets associados com um servidor da Web rodam dentro de um único processo. A JVM cria uma thread para cada requisição. Como as threads em Java têm muito menos overhead que os processos completos, e são executados dentro da memória do processador (já alocada pela JVM), a execução de um servlet é muito mais eficiente do que o processamento de um programa CGI.

36

Page 37: Introducao Ao Demoiselle Framework Versao 1 PDF

Apresentaremos um pequeno exemplo de servlet que servirá para explicar porque não o utilizamos diretamente, ou melhor, porque não utilizamos somente servlets para criar aplicações 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 execução 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 requisições para um Container externo.

Para o nosso primeiro exemplo, não utilizaremos um servidor web, mas somente um Container Servlet. Nosso Container será o Apache Tomcat, versão 6.x. Ele pode ser obtido em http://tomcat.apache.org. Baixe a distribuição binária e descompacte em um diretório de sua preferência.

Para executar o Tomcat, entre no subdiretório bin de sua instalação 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 padrão que usam a porta 80.

Para parar o Tomcat, basta executar o arquivo shutdown.bat (windows) ou shutdown.sh (linux).

Vamos criar um projeto de servlet. Escolha um local (qualquer um menos o diretório do Tomcat) e crie uma pasta chamada ExemploServlet. Dentro dessa pasta crie três subpastas: src, classes e etc.

A pasta src conterá os arquivos de código-fonte (.java);

A pasta classes conterá os arquivos de bytecode (.class);

A pasta etc conterá a configuração dos servlets (arquivo web.xml).

A estrutura de pastas ficará assim:

ExemploServlet

classes

etc

src

Dentro da pasta src, crie a classe AloMundoServlet, com este código:

import javax.servlet.*;

import javax.servlet.http.*;

import java.io.*;

37

Page 38: Introducao Ao Demoiselle Framework Versao 1 PDF

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 método doGet é executado em resposta a uma requisição HTTP GET. Nesse método, criamos um objeto que encapsula a saída para o cliente.

No diretório etc, iremos criar o arquivo web.xml. Esse arquivo é um deployment descriptor (DD), e cada aplicação web em Java precisa ter um. Um DD pode declarar vários servlets (que compõem a aplicação). O código 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

Page 39: Introducao Ao Demoiselle Framework Versao 1 PDF

<servlet-mapping>

<servlet-name>AloMundo</servlet-name>

<url-pattern>/alomundo</url-pattern>

</servlet-mapping>

</web-app>

A tag servlet-name é um apelido que serve para fazer a ligação entre a url invocada e o servlet a ser executado. Essa estrutura permite que várias requisições sejam direcionadas para um mesmo servlet.

Compile o servlet com o seguinte comando:

javac -cp [diretório do tomcat]/lib/servlet-api.jar -d classes

src/AloMundoServlet.java

No diretório do Tomcat, há uma subpasta chamada webapps. Dentro dela, crie um diretório com o nome da aplicação web. No nosso caso, será exemploservlet. Dentro desse diretório, crie o subdiretório WEB-INF e copie o arquivo web.xml para dentro dele. Também crie dentro de WEB-INF o subdiretório 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 comparação 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 símbolo estranho no lugar da letra acentuada dependendo da codificação de seu navegador.

O uso exclusivo de servlets para criar aplicações web cria dificuldades de lidar com a camada de apresentação. Todo o texto HTML a ser enviado para o navegador teria de ser colocado como argumento do método println. A formatação do HTML dentro de uma

39

Page 40: Introducao Ao Demoiselle Framework Versao 1 PDF

String literal o torna ilegível para o webdesigner. Sem contar que exige que ele conheça Java.

É claro que aqui fizemos tudo manualmente. O uso de um ambiente integrado de desenvolvimento facilitaria a tarefa, mas não resolveria o problema da mistura de código HTML com Java.

4.2 JSP

De acordo com Fields e Kolb (2000, p. 9), “a desvantagem potencial de usar servlets é que todo o conteúdo de documentos, tanto estático quanto dinâmico, reside no código fonte do programa. Como resultado, qualquer modificação em tal documento necessita de intervenção de um programador”.

Para resolver este, problema, segundo Gonçalves (2007, p. 115), a Sun Microsystems criou uma tecnologia baseada em servlets chamada JavaServer Pages (JSP), que permite separar o código HTML de Java. Essa separação evita que o programador tenha de inserir muito código para produzir apresentação e não obriga o webdesigner a conhecer (pelo menos não profundamente) a linguagem Java.

Faremos uma rápida introdução ao JSP reescrevendo o exemplo da seção anterior. Não nos deteremos aqui com muitas explicações porque o intuito deste livro, citando Silveira et al (2005, p. 1) “é ensinar de maneira elegante, mostrando apenas o que é necessário no momento correto e poupando o leitor de assuntos que não costumam ser de seu interesse em determinadas fases do aprendizado”.

Vamos criar um projeto de servlet. Escolha um local (qualquer um menos o diretório do Tomcat) e crie uma pasta chamada ExemploServlet. Dentro dessa pasta crie três subpastas: src, classes e etc.

A pasta src conterá os arquivos de código-fonte (.java);

A pasta classes conterá os arquivos de bytecode (.class);

A pasta etc conterá a configuração 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 código:

<%@page language="java" contentType="text/html" pageEncoding="UTF-8"

40

Page 41: Introducao Ao Demoiselle Framework Versao 1 PDF

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 extensão .jsp são interpretados pelo Container Java. No arquivo criado anteriormente, temos uma parte dinâmica na página, a data. Ela é gerada por duas classes Java, importadas no início do arquivo. Além disso, temos uma chamada a um Servlet por meio de uma requisição HTTP GET feita pelo formulário HTML.

Dentro da pasta src, crie a classe ControllerAloMundoServlet, com este código:

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

Page 42: Introducao Ao Demoiselle Framework Versao 1 PDF

}

Como pode perceber, esse Servlet é idêntico ao anterior, exceto pelo fato de que não envia as tags HTML que formam a página.

Como no exemplo anterior, iremos criar o arquivo web.xml no diretório etc. O código é 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>

Compile o servlet com o seguinte comando:

javac -cp [diretório do tomcat]/lib/servlet-api.jar -d classes

src/ControllerAloMundoServlet.java

Crie um diretório dentro da subpasta webapps do Tomcat com o nome de exemplojsp. Copie para dentro dele o arquivo index.jsp. Dentro de exemplojsp, crie o subdiretório WEB-INF e copie o arquivo web.xml para dentro dele. Também crie dentro de WEB-INF o subdiretório classes e copie para dentro dele o ControllerAloMundoServlet.class.

Você vai ter uma estrutura assim:

webapps

exemplojsp

index.jsp

WEB-INF

web.xml

42

Page 43: Introducao Ao Demoiselle Framework Versao 1 PDF

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 relação a programas CGI, ao mesmo tempo em que promove uma clara divisão do trabalho entre webdesigner e programador.

Após essa introdução a servlets e JSP, vamos dar início a implementação do nosso projeto.

4.3 Ambiente de desenvolvimento

Para ter produtividade e controle no desenvolvimento de nossa aplicação, iremos utilizar o Eclipse IDE for Java EE Developers. Ele servirá para criar as três versões de nossa aplicação. Ele está disponível no endereço http://www.eclipse.org/downloads, em distribuições para Windows, Mac e Linux. Neste livro, iremos utilizar a versão Galileo. Se tiver dúvidas com relação a instalação e inicialização do Eclipse, consulte os apêndices.

4.4 Configurando o servidor no Eclipse

Durante o desenvolvimento de uma aplicação Web, nós precisamos de um servidor para testar, depurar e rodar nosso projeto. E precisamos de uma forma automatizada de fazer isso, para termos produtividade. Já vimos como é trabalhoso ter de mover os arquivos do projeto de um servlet para o ambiente de execução do servidor (o processo chamado de publicação).

Iremos utilizar o ambiente integrado do Eclipse para não só criar nosso projeto, como também distribuir a aplicação para o servidor e executá-la de forma simples e rápida.

Utilizaremos o Tomcat como nosso servidor. Para fazer isso, entre no menu Window->Preferences. Procure o item Server, no quadro à esquerda. Abra-o, e selecione o item Runtime Environments, conforme mostra a figura 7.

43

Page 44: Introducao Ao Demoiselle Framework Versao 1 PDF

Clique no botão Add, no quadro à direita, para adicionar um ambiente de tempo de execução para servidor Web. Esse ambiente não é o servidor propriamente dito, mas sim o ambiente, bibliotecas e infraestrutura que um servidor precisa. Como estamos usando o Apache Tomcat 6.0, é este que selecionaremos na lista de ambientes para servidores disponíveis, conforme a figura 8.

A lista de ambientes fornecida, assim como toda a configuração de servidores, faz parte do plugin WTP (Web Tools Platform), que consiste em um conjunto de ferramentas e APIs para o desenvolvimento de aplicações Web.

O ambiente de servidor para Apache Tomcat do Eclipse provê a habilidade de executar a aplicação de dentro do projeto sem a necessidade de publicá-los no servidor. Na fase de desenvolvimento, isso é extremamente produtivo.

Após selecionar a marca e versão do servidor, precisamos indicar onde ele está instalado, para que o Eclipse possa iniciá-lo e interrompê-lo. Clique no botão Next, e na tela seguinte, conforme mostra a figura 9, indique o diretório onde o Tomcat está instalado.

Você pode controlar vários servidores Web por meio do Eclipse. Mas sua aplicação só poderá estar associada a um deles por vez.

44

Figura 7: Configuração do ambiente de servidor

Page 45: Introducao Ao Demoiselle Framework Versao 1 PDF

45

Figura 8: Seleção do servidor Web JEE

Page 46: Introducao Ao Demoiselle Framework Versao 1 PDF

46

Figura 9: Indicação do diretório do servidor Web

Page 47: Introducao Ao Demoiselle Framework Versao 1 PDF

4.5 Criando um projeto Web

Agora vamos criar o projeto Web. Acesse o menu File->New->Project. Na lista de tipos de projeto abra a opção Web, e dentro dela, selecione Dynamic Web Project, conforme mostra a figura 10.

Na tela seguinte (figura 11), fazemos a configuração do projeto. O nome será livrariajsp. O campo Target runtime se refere a qual ambiente será usado em tempo de execução da aplicação. Usaremos o ambiente do Apache Tomcat que configuramos anteriormente. O campo Dynamic web module version é versão da especificação de servlets. Utilizaremos a versão 2.5.

47

Figura 10: Criação de uma projeto Web JEE

Page 48: Introducao Ao Demoiselle Framework Versao 1 PDF

Figura 11: Configuração do projeto Web JEE

48

Page 49: Introducao Ao Demoiselle Framework Versao 1 PDF

4.6 Estrutura do projeto Web

Após a criação do projeto, você pode visualizar sua estrutura por meio da visão Project Explorer (figura 12).

Nessa visão, os servlets ficam dentro de Java Resources:src e as páginas JSP ficam dentro de WebContent. Mas usaremos outra visão para trabalhar com nosso projeto. Entre no menu Window->Show View->Other. Selecione a visão Package Explorer, dentro de Java, conforme mostra a figura 13.

O Eclipse deve provavelmente abrir a visão Package Explorer na parte inferior direita da tela. Você pode arrastá-la com o mouse pela barra de título até a parte esquerda da tela, para que fique na mesma posição que Project Explorer. A estrutura do projeto deve ficar como mostra a figura 4.8.

A visão Package Explorer é mais adequada para trabalhar com Java, pois mostra a hierarquia de pacotes em uma estrutura em árvore. Obviamente, mesmo com essa visão, conforme o projeto cresce, a localização dos arquivos fica mais complicada. Para

49

Figura 12: Estrutura do projeto na visão Project Explorer

Page 50: Introducao Ao Demoiselle Framework Versao 1 PDF

procurar um arquivo qualquer do projeto, inclusive oculto pelo sistema operacional, você pode usar a combinação de teclas CTRL+SHIFT+R.

Na visão 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. Não adianta autenticar um usuário se ele não tem nada para fazer. Também não é possível verificar permissão se não há recurso definido.

A aplicação precisa de um ponto de partida. Por isso, vamos criar a página 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

Figura 13: Selecionando a visão Package Explorer

Page 51: Introducao Ao Demoiselle Framework Versao 1 PDF

Conforme já vimos, o nome do arquivo será index.jsp. O wizard cria um esqueleto de página, que temos de preencher. Como não será implementada a interface com o cliente da livraria, apenas o módulo de administração, esse arquivo terá somente um hyperlink para a página do menu de administração do sistema.

<a href="admin.jsp">Administração</a>

Ainda não nos preocupamos em definir uma arquitetura para a aplicação, mas teríamos de ter feito isso antes de partir para a implementação. Antes de fazer algo mais substancial, vamos pensar na arquitetura.

Neste momento, vamos introduzir um padrão de projeto para dividir as responsabilidades de nossa aplicação em três camadas: Modelo, Controlador e Visão. Esse é o padrão de projeto MVC, descrito por Fowler (2006, p. 315-317).

• O Modelo irá se encarregar dos dados e da lógica de negócio.

51

Figura 14: Criação de página JSP

Page 52: Introducao Ao Demoiselle Framework Versao 1 PDF

• O Controlador irá se encarregar de capturar as requisições, invocar os Modelos e enviar a saída para a Visão.

• A Visão irá exibir os dados para o usuário. É a camada onde estão os arquivos JSP.

Este padrão de projeto é um padrão de alto nível, ou seja, ele trata a aplicação como um todo. Com relação à camada de Modelo, faz-se necessária ainda uma separação entre o armazenamento dos dados em memória para manipulação e as operações de persistência no banco de dados. Por isso, iremos utilizar, em nossa aplicação, uma variação do MVC. Em vez da camada de Modelo, utilizaremos uma camada de Persistência. Essa, por sua vez, será subdividida em duas camadas: Modelo e DAO.

4.8 Camada de Persistência

4.8.1 Criando os Modelos

Os Modelos, de uma forma simples e aplicada ao nosso exemplo, são as classes que irão representar as tabelas do banco de dados.

A criação dos modelos na camada de fonte de dados segue uma orientação de Fowler (2006, p. 53), o qual afirma que “é sábio separar o acesso SQL da lógica de domínio e colocá-la em classes separadas”. Ele diz que “uma boa maneira de organizar essas classes é baseá-las na estrutura da tabela do banco de dados” de modo que tenhamos uma classe por tabela.

O que irá diferenciar essas classes das demais para a aplicação? Nada, se não utilizarmos algo que as relacione entre si. A pergunta é, como a aplicação poderá saber, a qualquer momento, que uma classe é um modelo?

Existem duas respostas. A primeira é criar uma superclasse da qual todos os modelos irão herdar. Mas isso só vale a pena se existem comportamentos genéricos (métodos implementados) que possam ser herdados. E nossos modelos irão seguir o padrão POJO, criado por Martin Fowler, Rebecca Parsons e John Mackenzie. Esse padrão especifica classes que possuem apenas atributos e métodos para acessá-los.

A segunda resposta é criar uma interface que todos os modelos implementem. Uma interface serve, a princípio, para obrigar classes a implementarem métodos com uma determinada assinatura. A interface é uma maneira de imitar herança múltipla, pois uma classe pode implementar várias interfaces.

Aqui a interface servirá, além disso, como uma identificação. Assim, no pacote models.generic, criaremos a interface IModel:

IModel

52

Page 53: Introducao Ao Demoiselle Framework Versao 1 PDF

public interface IModel {

public Integer getId();

public String getPrimaryKey();

}

O objetivo dos métodos de IModel é obrigar as classes que a implementarem a retornar o nome do campo na tabela que é a chave primária (getPrimaryKey) e o valor para o registro corrente (getId).

Temos sempre que tentar identificar características e comportamentos similares, de modo a criar código centralizado e fácil de manter. Por isso, vamos criar algumas classes abstratas no pacote models.generic, baseando-nos em algumas observações do diagrama de classes do capítulo 3.

As classes que implementarem IModel deverão ter os atributos id e primaryKey, e pares de métodos 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 métodos. Nós a chamaremos de AbstractModel. Ela implementa IModel, de forma que as subclasses não tenham de fazer isso.

AbstractModel

public abstract class AbstractModel implements IModel{

protected Integer id;

protected String primaryKey;

public String getPrimaryKey() {

return primaryKey;

}

public Integer getId() {

return id;

}

public void setId(Integer id) {

this.id = id;

}

53

Page 54: Introducao Ao Demoiselle Framework Versao 1 PDF

}

Algumas classes (como Autor e Editora) tem mais em comum do que apenas o id. Elas também compartilham um atributo nome. Para evitar replicação de código, 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. Nós 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

Page 55: Introducao Ao Demoiselle Framework Versao 1 PDF

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 máximo a replicação de código, criaremos uma classe abstrata para manipular um atributo do tipo Telefone: AbstractTelefone.

55

Page 56: Introducao Ao Demoiselle Framework Versao 1 PDF

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 generalizações, podemos enfim criar os modelos, na raiz do pacote models.

Autor

public class Autor extends AbstractOnlyName implements Comparator<Autor> {

private String sobrenome;

public Autor() {

this.primaryKey = "id_autor";

}

public String getSobrenome() {

return sobrenome;

}

public void setSobrenome(String sobrenome) {

this.sobrenome = sobrenome;

}

public boolean equals(AutorLivro autorLivro) {

if (getId() == autorLivro.getAutor().getId()) {

56

Page 57: Introducao Ao Demoiselle Framework Versao 1 PDF

return true;

}

return false;

}

public int compare(Autor autor1, Autor autor2) {

return (autor2.getSobrenome().compareTo(autor1.getSobrenome()));

}

}

No construtor do modelo Autor, assim como nos demais que veremos adiante, temos a inicialização do atributo primaryKey, que é o nome da chave primária na tabela mapeada.

Essa classe Autor em especial implementa a interface Comparator, para que posteriormente possamos fazer a ordenação de uma coleção de Autores pelo sobrenome.

Cliente

public class Cliente extends AbstractUsuario {

public Cliente() {

this.primaryKey = "id_cliente";

}

private String cpf;

public String getCpf() {

return cpf;

}

public void setCpf(String cpf) {

57

Page 58: Introducao Ao Demoiselle Framework Versao 1 PDF

this.cpf = cpf;

}

}

Funcionario

public class Funcionario extends AbstractUsuario {

private String matricula;

public Funcionario() {

this.primaryKey = "id_usuario";

}

public String getMatricula() {

return matricula;

}

public void setMatricula(String matricula) {

this.matricula = matricula;

}

}

Editora

public class Editora extends AbstractOnlyName {

public Editora() {

this.primaryKey = "id_editora";

}

}

58

Page 59: Introducao Ao Demoiselle Framework Versao 1 PDF

Local

public class Local extends AbstractOnlyName {

public Local() {

this.primaryKey = "id_local";

}

}

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>();

public Livro() {

this.primaryKey = "id_livro";

}

public Set<Autor> getAutores() {

return autores;

}

public void setAutores(Set<Autor> autores) {

this.autores = autores;

}

59

Page 60: Introducao Ao Demoiselle Framework Versao 1 PDF

public String getIsbn() {

return isbn;

}

public void setIsbn(String isbn) {

this.isbn = isbn;

}

public String getTitulo() {

return titulo;

}

public void setTitulo(String titulo) {

this.titulo = titulo;

}

public Local getLocal() {

return local;

}

public void setLocal(Local local) {

this.local = local;

}

public Editora getEditora() {

return editora;

}

public void setEditora(Editora editora) {

this.editora = editora;

}

60

Page 61: Introducao Ao Demoiselle Framework Versao 1 PDF

public Integer getAno() {

return ano;

}

public void setAno(Integer ano) {

this.ano = ano;

}

public Float getPreco() {

return preco;

}

public void setPreco(Float preco) {

this.preco = preco;

}

}

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;

public Pedido() {

this.primaryKey = "numero_pedido";

}

61

Page 62: Introducao Ao Demoiselle Framework Versao 1 PDF

public Set<ItemPedido> getItems() {

return items;

}

public void setItems(Set<ItemPedido> items) {

this.items = items;

}

public Integer getNumeroPedido() {

return numeroPedido;

}

public void setNumeroPedido(Integer numeroPedido) {

this.numeroPedido = numeroPedido;

}

public GregorianCalendar getDataPedido() {

return dataPedido;

}

public void setDataPedido(GregorianCalendar dataPedido) {

this.dataPedido = dataPedido;

}

public Cliente getCliente() {

return this.cliente;

}

public void setCliente(Cliente cliente) {

this.cliente = cliente;

}

62

Page 63: Introducao Ao Demoiselle Framework Versao 1 PDF

public String getPrimaryKey() {

return this.primaryKey;

}

public Integer getId() {

return this.numeroPedido;

}

}

ItemPedido

public class ItemPedido extends AbstractModel {

private Pedido pedido;

private Livro livro;

private Float preco;

private Integer quantidade;

public ItemPedido() {

this.primaryKey = "id_item";

}

public Pedido getPedido() {

return pedido;

}

public void setPedido(Pedido pedido) {

this.pedido = pedido;

}

63

Page 64: Introducao Ao Demoiselle Framework Versao 1 PDF

public Livro getLivro() {

return livro;

}

public void setLivro(Livro livro) {

this.livro = livro;

}

public Float getPreco() {

return preco;

}

public void setPreco(Float preco) {

this.preco = preco;

}

public Integer getQuantidade() {

return quantidade;

}

public void setQuantidade(Integer quantidade) {

this.quantidade = quantidade;

}

}

Telefone

public class Telefone extends AbstractModel {

private String numero;

private String tipo;

64

Page 65: Introducao Ao Demoiselle Framework Versao 1 PDF

public Telefone() {

this.primaryKey = "id_telefone";

}

public String getNumero() {

return numero;

}

public void setNumero(String numero) {

this.numero = numero;

}

public String getTipo() {

return tipo;

}

public void setTipo(String tipo) {

this.tipo = tipo;

}

}

Existem alguns relacionamentos muitos-para-muitos entre algumas tabelas. Eles estão refletidos nos atributos que são coleções de objetos. Para facilitar o trabalho da próxima 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

Page 66: Introducao Ao Demoiselle Framework Versao 1 PDF

private Integer ordem;

public Autor getAutor() {

return autor;

}

public void setAutor(Autor autor) {

this.autor = autor;

}

public Livro getLivro() {

return livro;

}

public void setLivro(Livro livro) {

this.livro = livro;

}

public Integer getOrdem() {

return ordem;

}

public void setOrdem(Integer ordem) {

this.ordem = ordem;

}

public boolean equals(AutorLivro autorLivro) {

if (getAutor().getId() == autorLivro.getAutor().getId()

&& getLivro().getId() == autorLivro.getLivro().getId()) {

return true;

}

66

Page 67: Introducao Ao Demoiselle Framework Versao 1 PDF

return false;

}

public int compare(AutorLivro autorLivro1, AutorLivro autorLivro2) {

return (autorLivro2.getAutor().getSobrenome().compareTo(autorLivro1

.getAutor().getSobrenome()));

}

public String getPrimaryKey() {

return null;

}

public Integer getId() {

return null;

}

}

A classe AutorLivro implementa a interface Comparator, para que posteriormente possamos fazer a ordenação de uma coleção de Autores pelo sobrenome.

TelefoneCliente

public class TelefoneCliente extends AbstractTelefone {

private Cliente cliente;

public Cliente getCliente() {

return cliente;

}

67

Page 68: Introducao Ao Demoiselle Framework Versao 1 PDF

public void setCliente(Cliente cliente) {

this.cliente = cliente;

}

public String getPrimaryKey() {

return null;

}

public Integer getId() {

return null;

}

}

TelefoneFuncionario

public class TelefoneFuncionario extends AbstractTelefone {

private Funcionario funcionario;

public Funcionario getFuncionario() {

return funcionario;

}

public void setFuncionario(Funcionario funcionario) {

this.funcionario = funcionario;

}

68

Page 69: Introducao Ao Demoiselle Framework Versao 1 PDF

public String getPrimaryKey() {

return null;

}

public Integer getId() {

return null;

}

}

4.8.2. Criando os DAOs

Os modelos servem tão somente para que nossa aplicação consiga manipular dados das tabelas na forma de objetos. Mas alguém tem que levar os dados das tabelas do banco de dados para os objetos em memória. Para isso, iremos utilizar um padrão de projeto do Core J2EE, chamado DAO. Esse padrão define uma separação entre os dados e os métodos de acesso a eles.

Criaremos um pacote chamado dao. Dentro dele iremos criar uma classe para carregar (e persistir) os dados de cada modelo. Mas antes disso, iremos identificar alguns comportamentos genéricos para evitar replicação de código.

Essa generalização ficará no pacote dao.generic. O primeiro alvo de generalização é a SQL. Em vez de colocar comandos SQL diretamente em cada classe DAO, podemos centralizar a construção de comandos SQL em uma única classe. Isso é possível porque as operações de banco de dados são as mesmas para todos os DAOs, precisamos apenas indicar sobre qual tabela e quais registros estamos operando em determinado momento.

Sendo assim, criaremos uma classe chamada DAOEngine, cujo propósito é executar comandos SQL.

DAOEngine

public class DAOEngine {

private static String url;

private static String username;

private static String password;

private static String driver;

69

Page 70: Introducao Ao Demoiselle Framework Versao 1 PDF

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 = "Não conseguiu estabelecer conexão com o banco de dados";

private DAOEngine() {

}

public static String getLastException() {

return Message.getLastException();

}

O método openConnnection abre uma conexão de banco de dados baseada em configurações 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();

try {

properties.load(in);

in.close();

driver = properties.getProperty("driver");

url = properties.getProperty("url");

username = properties.getProperty("username");

password = properties.getProperty("password");

} catch (Exception e) {

70

Page 71: Introducao Ao Demoiselle Framework Versao 1 PDF

driver = "org.postgresql.Driver";

url = "jdbc:postgresql://localhost:5432/livraria";

username = "postgres";

password = "postgres";

}

try {

Class.forName(driver);

connection = DriverManager.getConnection(url, username, password);

return true;

} catch (ClassNotFoundException e) {

Message.setLastException(e.getMessage());

e.printStackTrace();

} catch (SQLException e) {

Message.setLastException(e.getMessage());

e.printStackTrace();

}

return false;

}

O método isConnected diz se há uma conexão aberta.

private static boolean isConnected() {

return (connection != null);

}

O método insert faz o encapsulamento da instrução 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 + "(";

for (int i = 0; i < fields.length; i++) {

71

Page 72: Introducao Ao Demoiselle Framework Versao 1 PDF

names = names + fields[i][NAME] + ",";

values = values + fields[i][VALUE] + ",";

}

if (names.substring(names.length() - 1).equals(","))

names = names.substring(0, names.length() - 1);

if (values.substring(values.length() - 1).equals(","))

values = values.substring(0, values.length() - 1);

sql = sql + names + ") VALUES (" + values + ")";

return executeSQL(sql);

}

A execução propriamente dita do comando SQL é feita pelo método executeSQL. Isso é porque esse trecho de código é comum para os métodos de inclusão, alteração e remoção de registros.

private static boolean executeSQL(String sql) {

if (!connectionEstablished())

return false;

try {

statement = connection.createStatement();

boolean result = statement.execute(sql);

statement.close();

connection.close();

connection = null;

return result;

} catch (SQLException e) {

Message.setLastException(e.getMessage());

72

Page 73: Introducao Ao Demoiselle Framework Versao 1 PDF

connection = null;

}

return false;

}

A atualização de registros é feita pelo método update, que encapsula a instrução 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]

+ ",";

}

if (settings.substring(settings.length() - 1).equals(","))

settings = settings.substring(0, settings.length() - 1);

sql = sql + settings + " where " + where;

return executeSQL(sql);

}

O método delete encapsula o comando SQL DELETE.

public static boolean delete(String table, String where) {

String sql = "DELETE FROM " + table + "";

sql = sql + " where " + where;

73

Page 74: Introducao Ao Demoiselle Framework Versao 1 PDF

return executeSQL(sql);

}

O método fetchAll obtém um conjunto de registros de uma tabela baseado em uma restrição (where). Esse conjunto de registros pode estar ordenados (order), e serem um subconjunto dos que atendem a restrição (limit e offset).

public static ResultSet fetchAll(String table, String where, String order,

Integer limit, Integer offset) {

String sql;

if (connection != null)

try {

connection.close();

connection = null;

} catch (SQLException e1) {

connection = null;

}

if (!openConnnection())

return 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

Page 75: Introducao Ao Demoiselle Framework Versao 1 PDF

Message.setLastException(e.getMessage());

}

return null;

}

O método connectionEstablished verifica se a conexão com o banco de dados está aberta. Caso não esteja, tenta se conectar. Em caso de falha, armazena uma mensagem padrão 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 método lastValue obtém o último valor gravado em um campo do tipo inteiro. A sua finalidade é recuperar o conteúdo de campos gerados automaticamente pela banco de dados.

public static Integer lastValue(String table, String field) {

String sql;

if (!connectionEstablished())

return null;

sql = "SELECT * FROM " + table + " ORDER BY " + field + " DESC";

try {

statement = connection.createStatement();

ResultSet rs = statement.executeQuery(sql);

if (rs.next()) {

75

Page 76: Introducao Ao Demoiselle Framework Versao 1 PDF

return rs.getInt(field);

}

} catch (SQLException e) {

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 conexão com o banco. Isso é importante, porque se for necessário alterar os dados de conexão, não é preciso modificar a classe, mas tão 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 irão executar as mesmas operações, sobre tabelas diferentes. Assim, vamos garantir que as assinaturas dos métodos 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 métodos de inclusão (insert), atualização (update) e remoção (delete) de registros.

O método fetchOne serve para retornar um objeto único, correspondente a um registro da tabela. Ele recebe e retorna uma implementação de IModel.

O método fetchAll retorna uma coleção de objetos a partir de um critério de consulta, que é a expressão utilizada pela cláusula WHERE da expressão SQL INSERT. Observe que usamos um tipo List, da API Collections de Java. O curinga ? é usado para permitir

76

Page 77: Introducao Ao Demoiselle Framework Versao 1 PDF

que as classes que implementam esse método 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 generalização será criar uma classe abstrata que implemente os métodos de inclusão, atualização e remoção definidos por IDAO. Contudo, essa classe não irá implementar diretamente a interface, pois os métodos de consulta ficarão a cargo de cada DAO.

A superclasse dos DAOs de nossa aplicação se chamará AbstractDAO.

AbstractDAO

public abstract class AbstractDAO {

protected String table;

public String getTable() {

return table;

}

public boolean insert(IModel model) {

String[][] fields = this.getFields(model);

return DAOEngine.insert(this.table, fields);

}

public boolean update(IModel model) {

String[][] fields = this.getFields(model);

return DAOEngine

.update(this.table, fields, this.getDefaultWhere(model));

}

public boolean delete(IModel model) {

return DAOEngine.delete(this.table, this.getDefaultWhere(model));

77

Page 78: Introducao Ao Demoiselle Framework Versao 1 PDF

}

public String getDefaultWhere(IModel model)

{

return model.getPrimaryKey()+" = "+model.getId();

}

public abstract String[][] getFields(IModel model);

}

O método getDefaultWhere retorna uma condição padrão, usada para busca de objetos únicos. O método getFields retorna um array com os campos editáveis da tabela e os valores a serem gravados.

Com essas generalizações, nossas classes DAO, a princípio, só precisarão implementar os métodos de busca, e os que fornecem os nomes de campos da tabela e critérios de consulta.

AutorDAO

public class AutorDAO extends AbstractDAO implements IDAO {

public AutorDAO() {

this.table = "autores";

}

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

Page 79: Introducao Ao Demoiselle Framework Versao 1 PDF

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;

}

public IModel fetchOne(IModel model) {

String where = this.getDefaultWhere(model);

List<Autor> list = this.fetchAll(where, null, null, null);

if (list != null && list.size() > 0)

return list.get(0);

else

return null;

}

@Override

public String[][] getFields(IModel model) {

Autor autor = (Autor) model;

String[][] fields = new String[2][2];

79

Page 80: Introducao Ao Demoiselle Framework Versao 1 PDF

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 {

public AutorLivroDAO() {

this.table = "autores_livro";

}

@Override

public String getDefaultWhere(IModel model) {

return "id_autor = " + ((AutorLivro) model).getAutor().getId()

+ " and id_livro = " + ((AutorLivro) model).getLivro().getId();

}

@Override

public String[][] getFields(IModel model) {

AutorLivro autorLivro = (AutorLivro) model;

String[][] fields = new String[3][2];

fields[0][0] = "id_autor";

80

Page 81: Introducao Ao Demoiselle Framework Versao 1 PDF

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);

try {

while (rs.next()) {

AutorLivro autorLivro = new AutorLivro();

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

Page 82: Introducao Ao Demoiselle Framework Versao 1 PDF

}

return list;

} catch (SQLException e) {

e.printStackTrace();

}

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;

}

public IModel fetchOne(IModel model) {

String where = this.getDefaultWhere(model);

82

Page 83: Introducao Ao Demoiselle Framework Versao 1 PDF

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

Page 84: Introducao Ao Demoiselle Framework Versao 1 PDF

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

Page 85: Introducao Ao Demoiselle Framework Versao 1 PDF

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 EditoraDAO() {

this.table = "editoras";

}

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

Page 86: Introducao Ao Demoiselle Framework Versao 1 PDF

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;

}

public IModel fetchOne(IModel model) {

String where = this.getDefaultWhere(model);

List<Editora> list = this.fetchAll(where, null, null, null);

if (list != null && list.size() > 0)

return list.get(0);

else

return null;

}

@Override

public String[][] getFields(IModel model) {

Editora editora = (Editora) model;

String[][] fields = new String[1][2];

86

Page 87: Introducao Ao Demoiselle Framework Versao 1 PDF

fields[0][0] = "nome";

fields[0][1] = "'" + editora.getNome() + "'";

return fields;

}

}

FuncionarioDAO

public class FuncionarioDAO extends AbstractDAO implements IDAO {

public FuncionarioDAO() {

this.table = "funcionarios";

}

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

Page 88: Introducao Ao Demoiselle Framework Versao 1 PDF

list.add(funcionario);

}

return list;

} catch (SQLException e) {

e.printStackTrace();

}

return null;

}

public IModel fetchOne(IModel model) {

String where = this.getDefaultWhere(model);

List<Funcionario> list = this.fetchAll(where, null, null, null);

if (list != null && list.size() > 0)

return list.get(0);

else

return null;

}

@Override

public String[][] getFields(IModel model) {

Funcionario Funcionario = (Funcionario) model;

String[][] fields = new String[5][2];

fields[0][0] = "matricula";

fields[0][1] = "'" + Funcionario.getMatricula() + "'";

fields[1][0] = "nome";

88

Page 89: Introducao Ao Demoiselle Framework Versao 1 PDF

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 {

@Override

public String[][] getFields(IModel model) {

ItemPedido itemPedido = (ItemPedido) model;

String[][] fields = new String[4][2];

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

Page 90: Introducao Ao Demoiselle Framework Versao 1 PDF

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);

try {

while (rs.next()) {

ItemPedido itemPedido = new ItemPedido();

itemPedido.setId(rs.getInt("id_item"));

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"));

list.add(itemPedido);

}

return list;

90

Page 91: Introducao Ao Demoiselle Framework Versao 1 PDF

} catch (SQLException e) {

e.printStackTrace();

}

return null;

}

public IModel fetchOne(IModel model) {

String where = this.getDefaultWhere(model);

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 LivroDAO() {

this.table = "livros";

}

public List<Livro> fetchAll(String where, String order, Integer limit,

Integer offset) {

List<Livro> list = new ArrayList<Livro>();

91

Page 92: Introducao Ao Demoiselle Framework Versao 1 PDF

ResultSet rs = DAOEngine.fetchAll(this.table, where, order, limit,

offset);

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"));

String preco = rs.getString("preco");

livro.setPreco(formataPreco(preco));

livro.setAutores(new AutorLivroDAO().fetchAll("id_livro = "

+ livro.getId()));

list.add(livro);

}

92

Page 93: Introducao Ao Demoiselle Framework Versao 1 PDF

return list;

} catch (SQLException e) {

e.printStackTrace();

}

return null;

}

private Float formataPreco(String preco) {

int i;

for (i = 0; i < preco.length(); i++) {

if (Character.isDigit(preco.charAt(i))) {

break;

}

}

preco = preco.substring(i);

preco = preco.replace(',', '.');

return Float.parseFloat(preco);

}

public IModel fetchOne(IModel model) {

String where = this.getDefaultWhere(model);

List<Livro> list = this.fetchAll(where, null, null, null);

if (list != null && list.size() > 0)

return list.get(0);

93

Page 94: Introducao Ao Demoiselle Framework Versao 1 PDF

else

return null;

}

@Override

public String[][] getFields(IModel model) {

Livro livro = (Livro) model;

String[][] fields = new String[6][2];

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

Page 95: Introducao Ao Demoiselle Framework Versao 1 PDF

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;

}

public IModel fetchOne(IModel model) {

String where = this.getDefaultWhere(model);

List<Local> list = this.fetchAll(where, null, null, null);

if (list != null && list.size() > 0)

95

Page 96: Introducao Ao Demoiselle Framework Versao 1 PDF

return list.get(0);

else

return null;

}

@Override

public String[][] getFields(IModel model) {

Local local = (Local) model;

String[][] fields = new String[1][2];

fields[0][0] = "nome";

fields[0][1] = "'" + local.getNome() + "'";

return fields;

}

}

PedidoDAO

public class PedidoDAO extends AbstractDAO implements IDAO {

@Override

public String[][] getFields(IModel model) {

Pedido pedido = (Pedido) model;

String[][] fields = new String[2][2];

fields[0][0] = "data_pedido";

fields[0][1] = pedido.getDataPedido().toString();

fields[1][0] = "id_cliente";

fields[1][1] = pedido.getCliente().getId().toString();

96

Page 97: Introducao Ao Demoiselle Framework Versao 1 PDF

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;

}

public IModel fetchOne(IModel model) {

97

Page 98: Introducao Ao Demoiselle Framework Versao 1 PDF

String where = this.getDefaultWhere(model);

List<Pedido> list = this.fetchAll(where, null, null, null);

if (list != null && list.size() > 0)

return list.get(0);

else

return null;

}

public Integer ultimoNumeroPedido() {

return new Integer(DAOEngine.lastValue("pedido", "numero_pedido"));

}

}

TelefoneClienteDAO

public class TelefoneClienteDAO extends AbstractDAO implements IDAO {

public TelefoneClienteDAO() {

this.table = "telefones_cliente";

}

@Override

public String getDefaultWhere(IModel model) {

return "id_cliente = " + ((TelefoneCliente) model).getCliente().getId()

+ " and id_telefone = "

+ ((TelefoneCliente) model).getTelefone().getId();

}

@Override

public String[][] getFields(IModel model) {

98

Page 99: Introducao Ao Demoiselle Framework Versao 1 PDF

TelefoneCliente telefoneCliente = (TelefoneCliente) model;

String[][] fields = new String[2][2];

fields[0][0] = "id_cliente";

fields[0][1] = telefoneCliente.getCliente().getId().toString();

fields[1][0] = "id_telefone";

fields[1][1] = telefoneCliente.getTelefone().getId().toString();

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);

try {

while (rs.next()) {

TelefoneCliente telefoneCliente = new TelefoneCliente();

Cliente cliente = new Cliente();

cliente.setId(rs.getInt("id_cliente"));

cliente = (Cliente) new ClienteDAO().fetchOne(cliente);

telefoneCliente.setCliente(cliente);

Telefone telefone = new Telefone();

telefone.setId(rs.getInt("id_telefone"));

telefone = (Telefone) new TelefoneDAO().fetchOne(telefone);

99

Page 100: Introducao Ao Demoiselle Framework Versao 1 PDF

telefoneCliente.setTelefone(telefone);

list.add(telefoneCliente);

}

return list;

} catch (SQLException e) {

e.printStackTrace();

}

return null;

}

public IModel fetchOne(IModel model) {

String where = this.getDefaultWhere(model);

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 {

public TelefoneDAO() {

this.table = "telefones";

}

100

Page 101: Introducao Ao Demoiselle Framework Versao 1 PDF

@Override

public String getDefaultWhere(IModel model) {

return "id_telefone = " + ((Telefone) model).getId();

}

@Override

public String[][] getFields(IModel model) {

Telefone telefone = (Telefone) model;

String[][] fields = new String[2][2];

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);

try {

while (rs.next()) {

Telefone telefone = new Telefone();

telefone.setId(rs.getInt("id_telefone"));

101

Page 102: Introducao Ao Demoiselle Framework Versao 1 PDF

telefone.setNumero(rs.getString("numero"));

telefone.setTipo(rs.getString("tipo"));

list.add(telefone);

}

return list;

} catch (SQLException e) {

e.printStackTrace();

}

return null;

}

public IModel fetchOne(IModel model) {

String where = this.getDefaultWhere(model);

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 {

public TelefoneFuncionarioDAO() {

this.table = "telefones_funcionario";

}

102

Page 103: Introducao Ao Demoiselle Framework Versao 1 PDF

@Override

public String getDefaultWhere(IModel model) {

return "id_funcionario = "

+ ((TelefoneFuncionario) model).getFuncionario().getId()

+ " and id_telefone = "

+ ((TelefoneFuncionario) model).getTelefone().getId();

}

@Override

public String[][] getFields(IModel model) {

TelefoneFuncionario telefoneFuncionario = (TelefoneFuncionario) model;

String[][] fields = new String[2][2];

fields[0][0] = "id_funcionario";

fields[0][1] = telefoneFuncionario.getFuncionario().getId().toString();

fields[1][0] = "id_telefone";

fields[1][1] = telefoneFuncionario.getTelefone().getId().toString();

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);

try {

while (rs.next()) {

103

Page 104: Introducao Ao Demoiselle Framework Versao 1 PDF

TelefoneFuncionario telefoneFuncionario = new TelefoneFuncionario();

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.add(telefoneFuncionario);

}

return list;

} catch (SQLException e) {

e.printStackTrace();

}

return null;

}

public IModel fetchOne(IModel model) {

String where = this.getDefaultWhere(model);

List<TelefoneFuncionario> list = this.fetchAll(where, null, null, null);

if (list != null && list.size() > 0)

return list.get(0);

else

return null;

}

104

Page 105: Introducao Ao Demoiselle Framework Versao 1 PDF

}

4.8.3 Testando a Camada de Persistência

Não podemos esperar que a aplicação esteja pronta pra saber se tudo está funcionando. Dessa forma, nós precisamos testar cada unidade funcional da aplicação para garantir que ela funcione separadamente, para depois partir para os testes de integração (que são as implementações dos casos de testes descritos no capítulo 3).

JUnit é um framework para criação de testes automatizados. O Eclipse facilita não só a criação como a execução e depuração de testes escritos com esse framework. Para criar um teste unitário, basta entrar acessar o menu File->New->Other e dentro de Java procurar o item JUnit, exibido na figura 15.

O correto seria criarmos um teste unitário, para saber se a camada de persistência funciona de forma isolada. Mas para isso, teríamos de criar um simulacro do banco de dados. A criação de simulacros, denominados geralmente de mocks, é um assunto à parte. Como o tema testes aqui não é o foco principal, em vez de fazer testes unitários iremos criar testes de integração.

105

Figura 15: Criação de caso de teste em JUnit

Page 106: Introducao Ao Demoiselle Framework Versao 1 PDF

Um teste de integração é 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 persistência poderiam ser testados de forma isolada cada um, mas aqui vamos testar ambos simultaneamente.

Como exemplo, iremos criar um teste de integração para a persistência do modelo Autor. Temos de criar métodos de teste que consigam verificar todas as funcionalidades presentes nas classes envolvidas. Seguindo o padrão 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("Maurício");

TestAutorDAO.autor.setSobrenome("de Sousa");

TestAutorDAO.autorDAO.insert(autor);

Autor autor = TestAutorDAO.autorDAO.fetchAll("nome = 'Maurício' and sobrenome = 'de Sousa'").get(0);

assertNotNull(autor);

if (autor!=null)

{

TestAutorDAO.autor = autor;

}

System.out.println(autor);

}

public void testUpdate()

{

TestAutorDAO.autor.setNome("Carlos");

TestAutorDAO.autor.setSobrenome("Gomes");

106

Page 107: Introducao Ao Demoiselle Framework Versao 1 PDF

TestAutorDAO.autorDAO.update(autor);

Autor autor = (Autor) new AutorDAO().fetchOne(TestAutorDAO.autor);

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 três métodos de teste conseguimos cobrir todas as funcionalidades presentes no modelo e no DAO.

4.8.4 Classes e Arquivos Auxiliares da Camada de Persistência

A classe DAOEngine cria uma conexão com base em um arquivo chamado connection.properties. Mostramos a seguir qual seria o conteúdo desse arquivo.

connection.properties

# configuração do banco de dados

driver = org.postgresql.Driver

username = postgres

password = postgres

url = jdbc:postgresql://localhost:5432/livraria

A classe DAOEngine também utiliza uma classe para guardar mensagens de exceção. É a classe Message, que fica no pacote messages. Ela na verdade servirá a todas as camadas da aplicação. Seu código segue adiante.

Message

public class Message {

private static String lastException = "";

private static String lastInfo = "";

107

Page 108: Introducao Ao Demoiselle Framework Versao 1 PDF

private Message(){};

public static String getLastException() {

String message = lastException;

lastException = null;

return message;

}

public static String getLastInfo() {

String message = lastInfo;

lastInfo = "";

return message;

}

public static boolean isException() {

return !lastException.equals("");

}

public static boolean isInfo() {

return !lastInfo.equals("");

}

public static void setLastException(String lastException) {

Message.lastException = lastException;

}

public static void setLastInfo(String lastInfo) {

Message.lastInfo = lastInfo;

}

}

108

Page 109: Introducao Ao Demoiselle Framework Versao 1 PDF

4.9.Camada de Controle

O controle da aplicação será feito por servlets. Nossa aplicação terá quatro servlets de controle. Um, chamado GravarServlet, será responsável por operações de gravação (inclusão e alteração de registros). Outro, chamado RemoverServlet, será responsável pela exclusão de registros. O terceiro, chamado AutenticarServlet, será responsável pela autenticação do usuário e pela sua saída do sistema. Os dois primeiros servlets irão herdar de AuthServlet, que irá encapsular a verificação da autenticação do usuário.

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, além de criar o esqueleto da classe Servlet, também cria as entradas no arquivo web.xml. Mas a URL padrão é o próprio nome do servlet. Iremos alterar para um padrão de nome com letras minúsculas. No arquivo web.xml, dentro de WebContent/WEB-INF, edite a configuração 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

Page 110: Introducao Ao Demoiselle Framework Versao 1 PDF

<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

Page 111: Introducao Ao Demoiselle Framework Versao 1 PDF

Vamos agora editar os Servlets.

4.9.1 Servlet de Autenticação

O servlet de autenticação é responsável por identificar o usuário perante o sistema e lhe dar o acesso primário. Acesso primário porque a autenticação é a primeira condição para o usuário realizar operações na parte administrativa do sistema. A segunda é ter a permissão requerida para uma determinada operação.

public class AutenticarServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

111

Figura 16: Criando um servlet no Eclipse

Page 112: Introducao Ao Demoiselle Framework Versao 1 PDF

public AutenticarServlet() {

super();

}

protected void doGet(HttpServletRequest request,

HttpServletResponse response) throws ServletException, IOException {

doPost(request, response);

}

protected void doPost(HttpServletRequest request,

HttpServletResponse response) throws ServletException, IOException {

String operacao = request.getParameter("operacao");

if (operacao.equals(Operacoes.LOGIN)) {

login(request, response);

}

if (operacao.equals(Operacoes.LOGOUT)) {

logout(request, response);

}

}

private void logout(HttpServletRequest request, HttpServletResponse response) {

getServletContext().removeAttribute("funcionario");

try {

response.sendRedirect("login.jsp");

} catch (IOException e) {

tratarErro(response, e);

}

112

Page 113: Introducao Ao Demoiselle Framework Versao 1 PDF

}

private void login(HttpServletRequest request, HttpServletResponse response) {

String apelido = request.getParameter("apelido");

String senha = request.getParameter("senha");

FuncionarioDAO dao = new FuncionarioDAO();

try {

List<Funcionario> funcionarios = dao.fetchAll("apelido = '"

+ apelido + "' and senha ='" + senha + "'", null, null,

null);

if (funcionarios != null && funcionarios.size() == 1) {

getServletContext().setAttribute("funcionario",

funcionarios.get(0));

response.sendRedirect("admin.jsp");

} else {

Message.setLastInfo("Apelido ou senha inválidos!");

response.sendRedirect("login.jsp");

}

} catch (Exception e) {

Message.setLastException(e.getMessage());

tratarErro(response, e);

}

}

113

Page 114: Introducao Ao Demoiselle Framework Versao 1 PDF

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";

}

4.9.2 Classe Abstrata para Servlets Autenticados

Esta é a classe que encapsula a lógica de verificação da autenticação do usuário. Em vez de cada classe servlet fazer a verificação por conta própria, criando código replicado, elas simplesmente herdam de AuthServlet. Como a verificação de autenticação está no método service, que é executado antes de doGet e doPost, garantimos que nenhuma operação será executada se o usuário não estiver autenticado.

public abstract class AuthServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

@Override

protected void service(HttpServletRequest request,

HttpServletResponse response) throws ServletException, IOException {

if (getServletContext().getAttribute("funcionario") == null) {

Message.setLastInfo("Você precisa estar autenticado para usar o recurso!");

response.sendRedirect("login.jsp");

114

Page 115: Introducao Ao Demoiselle Framework Versao 1 PDF

} else {

super.service(request, response);

}

}

public AuthServlet() {

super();

}

protected void doGet(HttpServletRequest request,

HttpServletResponse response) throws ServletException, IOException {

}

protected void doPost(HttpServletRequest request,

HttpServletResponse response) throws ServletException, IOException {

}

}

4.9.3 Servlet de Gravação

O servlet de gravação é responsável por recepcionar o envio de formulários por HTTP POST e providenciar a inclusão ou alteração de registro na respectiva tabela. Por isso, a lógica central está no método doPost.

O método doPost invoca uma instância única de AuthorizationController, classe responsável pelas informações de permissão a recursos. Se o usuário autenticado tem os papéis necessários para executar a operação, é executado o método de gravação apropriado. Caso contrário, o usuário é redirecionado para a página do menu administrativo.

public class GravarServlet extends AuthServlet {

private static final long serialVersionUID = 1L;

public GravarServlet() {

super();

115

Page 116: Introducao Ao Demoiselle Framework Versao 1 PDF

}

protected void doGet(HttpServletRequest request,

HttpServletResponse response) throws ServletException, IOException {

}

protected void doPost(HttpServletRequest request,

HttpServletResponse response) throws ServletException, IOException {

Funcionario funcionario = (Funcionario) getServletContext().getAttribute("funcionario");

AuthorizationController ac = AuthorizationController.getInstance(funcionario.getApelido());

if (!ac.hasRole(new String[]{Papeis.GRAVADOR,Papeis.ADMINISTRADOR}))

{

Message.setLastInfo("Você não tem permissão para usar este recurso!");

response.sendRedirect("admin.jsp");

return;

}

String cadastro = request.getParameter("cadastro");

String retornar = request.getParameter("retornar");

if (retornar != null) {

response.sendRedirect("cadastro.jsp?cadastro=" + cadastro);

return;

}

if (request.getCharacterEncoding() == null)

request.setCharacterEncoding("UTF-8");

116

Page 117: Introducao Ao Demoiselle Framework Versao 1 PDF

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

Page 118: Introducao Ao Demoiselle Framework Versao 1 PDF

if (cadastro.equals(Cadastros.TELEFONE_FUNCIONARIO)) {

gravarTelefoneFuncionario(request, response);return;

}

}

private void gravarTelefoneFuncionario(HttpServletRequest request,

HttpServletResponse response) {

Funcionario funcionario = new Funcionario();

funcionario.setId(Integer.parseInt(request.getParameter("id_funcionario")));

Telefone telefone = new Telefone();

telefone.setNumero(request.getParameter("numero"));

telefone.setTipo(request.getParameter("tipo"));

TelefoneDAO telefoneDAO = new TelefoneDAO();

telefoneDAO.insert(telefone);

telefone.setId(DAOEngine.lastValue(telefoneDAO.getTable(),

"id_telefone"));

TelefoneFuncionario telefoneFuncionario = new TelefoneFuncionario();

telefoneFuncionario.setFuncionario(funcionario);

telefoneFuncionario.setTelefone(telefone);

new TelefoneFuncionarioDAO().insert(telefoneFuncionario);

try {

response.sendRedirect("edicao-funcionario.jsp?id="

+ request.getParameter("id_funcionario"));

} catch (IOException e) {

118

Page 119: Introducao Ao Demoiselle Framework Versao 1 PDF

tratarErro(response, e);

}

}

private void gravarTelefoneCliente(HttpServletRequest request,

HttpServletResponse response) {

Cliente cliente = new Cliente();

cliente.setId(Integer.parseInt(request.getParameter("id_cliente")));

Telefone telefone = new Telefone();

telefone.setNumero(request.getParameter("numero"));

telefone.setTipo(request.getParameter("tipo"));

TelefoneDAO telefoneDAO = new TelefoneDAO();

telefoneDAO.insert(telefone);

telefone.setId(DAOEngine.lastValue(telefoneDAO.getTable(),

"id_telefone"));

TelefoneCliente telefoneCliente = new TelefoneCliente();

telefoneCliente.setCliente(cliente);

telefoneCliente.setTelefone(telefone);

new TelefoneClienteDAO().insert(telefoneCliente);

try {

response.sendRedirect("edicao-cliente.jsp?id="

+ request.getParameter("id_cliente"));

} catch (IOException e) {

tratarErro(response, e);

119

Page 120: Introducao Ao Demoiselle Framework Versao 1 PDF

}

}

private void gravarAutorLivro(HttpServletRequest request,

HttpServletResponse response) {

AutorLivroDAO dao = new AutorLivroDAO();

AutorLivro autorLivro = new AutorLivro();

autorLivro.setAutor(new Autor());

autorLivro.getAutor().setId(

Integer.parseInt(request.getParameter("id_autor")));

autorLivro.setLivro(new Livro());

autorLivro.getLivro().setId(

Integer.parseInt(request.getParameter("id_livro")));

AutorLivro autorLivroExistente = (AutorLivro) dao.fetchOne(autorLivro);

if (autorLivroExistente == null) {

List<AutorLivro> autoresLivro = new ArrayList<AutorLivro>();

autoresLivro = dao.fetchAll("id_livro = "

+ autorLivro.getLivro().getId(), "ordem", null, null);

Integer ordem = autoresLivro == null ? 1 : autoresLivro.size() + 1;

autorLivro.setOrdem(ordem);

dao.insert(autorLivro);

}

120

Page 121: Introducao Ao Demoiselle Framework Versao 1 PDF

try {

response.sendRedirect("edicao-livro.jsp?id="

+ autorLivro.getLivro().getId());

} catch (IOException e) {

tratarErro(response, e);

}

}

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());

try {

response.sendRedirect("fechamento.jsp");

} catch (IOException e) {

tratarErro(response, e);

}

121

Page 122: Introducao Ao Demoiselle Framework Versao 1 PDF

}

private void gravarTelefone(HttpServletRequest request,

HttpServletResponse response) {

Telefone telefone = new Telefone();

telefone = (Telefone) trataId(request, telefone);

telefone.setNumero(request.getParameter("numero"));

telefone.setTipo(request.getParameter("tipo"));

TelefoneDAO dao = new TelefoneDAO();

if (telefone.getId() == null) {

dao.insert(telefone);

} else {

dao.update(telefone);

}

try {

response.sendRedirect("cadastro.jsp?cadastro=telefone");

} catch (IOException e) {

tratarErro(response, e);

}

}

private void gravarCliente(HttpServletRequest request,

HttpServletResponse response) {

Cliente cliente = new Cliente();

cliente = (Cliente) trataId(request, cliente);

122

Page 123: Introducao Ao Demoiselle Framework Versao 1 PDF

cliente.setApelido(request.getParameter("apelido"));

cliente.setEmail(request.getParameter("email"));

cliente.setCpf(request.getParameter("cpf"));

cliente.setNome(request.getParameter("nome"));

cliente.setSenha(request.getParameter("senha"));

ClienteDAO dao = new ClienteDAO();

if (cliente.getId() == null) {

dao.insert(cliente);

} else {

dao.update(cliente);

}

try {

response.sendRedirect("cadastro.jsp?cadastro=cliente");

} catch (IOException e) {

tratarErro(response, e);

}

}

private void gravarLocal(HttpServletRequest request,

HttpServletResponse response) {

Local local = new Local();

local = (Local) trataId(request, local);

local.setNome(request.getParameter("nome"));

LocalDAO dao = new LocalDAO();

if (local.getId() == null) {

123

Page 124: Introducao Ao Demoiselle Framework Versao 1 PDF

dao.insert(local);

} else {

dao.update(local);

}

try {

response.sendRedirect("cadastro.jsp?cadastro=local");

} catch (IOException e) {

tratarErro(response, e);

}

}

private void gravarLivro(HttpServletRequest request,

HttpServletResponse response) {

Livro livro = new Livro();

livro = (Livro) trataId(request, 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"));

Local local = new Local();

local.setId(Integer.parseInt(request.getParameter("id_local")));

local = (Local) new LocalDAO().fetchOne(local);

124

Page 125: Introducao Ao Demoiselle Framework Versao 1 PDF

livro.setLocal(local);

livro.setPreco(Float.parseFloat(request.getParameter("preco").replace(

',', '.')));

livro.setTitulo(request.getParameter("titulo"));

LivroDAO dao = new LivroDAO();

if (livro.getId() == null) {

dao.insert(livro);

livro.setId(DAOEngine.lastValue(dao.getTable(), "id_livro"));

} else {

dao.update(livro);

}

try {

response.sendRedirect("edicao-livro.jsp?id=" + livro.getId());

} catch (IOException e) {

tratarErro(response, e);

}

}

private void gravarFuncionario(HttpServletRequest request,

HttpServletResponse response) {

Funcionario funcionario = new Funcionario();

funcionario = (Funcionario) trataId(request, funcionario);

funcionario.setApelido(request.getParameter("apelido"));

funcionario.setEmail(request.getParameter("email"));

funcionario.setMatricula(request.getParameter("matricula"));

funcionario.setNome(request.getParameter("nome"));

125

Page 126: Introducao Ao Demoiselle Framework Versao 1 PDF

funcionario.setSenha(request.getParameter("senha"));

FuncionarioDAO dao = new FuncionarioDAO();

if (funcionario.getId() == null) {

dao.insert(funcionario);

} else {

dao.update(funcionario);

}

try {

response.sendRedirect("cadastro.jsp?cadastro=funcionario");

} catch (IOException e) {

tratarErro(response, e);

}

}

private void gravarAutor(HttpServletRequest request,

HttpServletResponse response) {

Autor autor = new Autor();

autor = (Autor) trataId(request, autor);

autor.setNome(request.getParameter("nome"));

autor.setSobrenome(request.getParameter("sobrenome"));

AutorDAO dao = new AutorDAO();

if (autor.getId() == null) {

dao.insert(autor);

} else {

dao.update(autor);

}

126

Page 127: Introducao Ao Demoiselle Framework Versao 1 PDF

try {

response.sendRedirect("cadastro.jsp?cadastro=autor");

} catch (IOException e) {

tratarErro(response, e);

}

}

private void gravarEditora(HttpServletRequest request,

HttpServletResponse response) {

Editora editora = new Editora();

editora = (Editora) trataId(request, editora);

editora.setNome(request.getParameter("nome"));

EditoraDAO dao = new EditoraDAO();

if (editora.getId() == null) {

dao.insert(editora);

} else {

dao.update(editora);

}

try {

response.sendRedirect("cadastro.jsp?cadastro=editora");

} catch (IOException e) {

tratarErro(response, e);

}

}

127

Page 128: Introducao Ao Demoiselle Framework Versao 1 PDF

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();

}

}

}

4.9.3.1 Classe de Controle de Acesso

GravarServlet faz uso de uma classe chamada AuthorizationController. Essa classe contém a lógica que recupera os papéis do usuário autenticado. AuthorizationController implementa o padrão de projeto Singleton, não permitindo que mais de uma instância seja criada.

public class AuthorizationController {

private static AuthorizationController instance = null;

private static Set<String> roles = new HashSet<String>();

private AuthorizationController(String user)

{

}

128

Page 129: Introducao Ao Demoiselle Framework Versao 1 PDF

public static AuthorizationController getInstance(String user)

{

if (instance == null)

{

instance = new AuthorizationController(user);

}

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

Page 130: Introducao Ao Demoiselle Framework Versao 1 PDF

public boolean hasRole(String[] userRoles)

{

for (int i=0;i<userRoles.length;i++)

{

if (roles.contains(userRoles[i]))

return true;

}

return false;

}

}

4.9.3.2 Constantes com os Papéis

O uso de constantes é recomendado para o teste de controle de acesso, pois padroniza os nomes para os programadores e evita erros de digitação.

public class Papeis {

public static final String ADMINISTRADOR = "administrador";

public static final String GRAVADOR = "gravador";

public static final String REMOVEDOR = "removedor";

public static final String LEITOR = "leitor";

}

4.9.3.3 Arquivo com os Papéis dos Usuários

A classe AuthorizationController faz uso de um arquivo de configuração, onde colocamos quais são os papéis dos usuários. A seguir mostramos um exemplo de como pode ser o conteúdo desse arquivo.

authorization.properties

# papeis dos usuarios

hugo = leitor

jose = leitor,gravador

luis = leitor, gravar, removedor

130

Page 131: Introducao Ao Demoiselle Framework Versao 1 PDF

patricia = administrador

4.9.3.4 Constantes com os Nomes dos 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";

}

4.9.4 Servlet de Remoçãopublic class RemoverServlet extends AuthServlet {

private static final long serialVersionUID = 1L;

public RemoverServlet() {

super();

}

protected void doGet(HttpServletRequest request,

HttpServletResponse response) throws ServletException, IOException {

Funcionario funcionario = (Funcionario) getServletContext()

.getAttribute("funcionario");

131

Page 132: Introducao Ao Demoiselle Framework Versao 1 PDF

AuthorizationController ac = AuthorizationController

.getInstance(funcionario.getApelido());

if (!ac

.hasRole(new String[] { Papeis.REMOVEDOR, Papeis.ADMINISTRADOR })) {

Message.setLastInfo("Você não tem permissão para usar este recurso!");

response.sendRedirect("admin.jsp");

return;

}

String cadastro = request.getParameter("cadastro");

String paramId = request.getParameter("id");

Integer id = null;

if (paramId != null) {

id = Integer.parseInt(paramId);

}

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

Page 133: Introducao Ao Demoiselle Framework Versao 1 PDF

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

Page 134: Introducao Ao Demoiselle Framework Versao 1 PDF

.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);

new TelefoneDAO().delete(telefoneFuncionario.getTelefone());

try {

response.sendRedirect("edicao-funcionario.jsp?id=" + idFuncionario);

} catch (IOException e) {

tratarErro(response, e);

}

}

private void removerTelefoneCliente(HttpServletResponse response,

Integer idCliente, Integer idTelefone) {

TelefoneCliente telefoneCliente = new TelefoneCliente();

telefoneCliente.setCliente(new Cliente());

telefoneCliente.setTelefone(new Telefone());

134

Page 135: Introducao Ao Demoiselle Framework Versao 1 PDF

telefoneCliente.getCliente().setId(idCliente);

telefoneCliente.getTelefone().setId(idTelefone);

new TelefoneClienteDAO().delete(telefoneCliente);

new TelefoneDAO().delete(telefoneCliente.getTelefone());

try {

response.sendRedirect("edicao-cliente.jsp?id=" + idCliente);

} catch (IOException e) {

tratarErro(response, e);

}

}

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

Page 136: Introducao Ao Demoiselle Framework Versao 1 PDF

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

Page 137: Introducao Ao Demoiselle Framework Versao 1 PDF

}

}

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

Page 138: Introducao Ao Demoiselle Framework Versao 1 PDF

} catch (IOException e) {

tratarErro(response, e);

}

}

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

Page 139: Introducao Ao Demoiselle Framework Versao 1 PDF

4.10 Camada de Visão

A camada de Visão será composta por páginas HTML e classes Java auxiliares para a geração de conteúdo dinâmico. As classes auxiliares são necessárias para que as páginas JSP evitem conter código de processamento e tentem se limitar à apresentação dos dados.

A página admin.jsp contém uma estrutura de decisão que verifica se o usuário está autenticado. Caso não esteja, ele é redirecionado para a página de autenticação (login.jsp). Esse mesmo procedimento é feito em todas as páginas JSP, exceto a de autenticação. Essa página contém os links para os cadastros mantidos pelo sistema.

Lembrando que todas as páginas JSP ficam dentro de WebContent.

4.10.1 Páginas JSP

admin.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"

pageEncoding="UTF-8"%>

<%@page import="messages.Message"%>

<jsp:useBean id="view" class="view.ViewController"></jsp:useBean>

<%if (getServletContext().getAttribute("funcionario") == null) response.sendRedirect("login.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>Módulo Administrativo</title>

</head>

<body>

<h1>Manutenção de Cadastros</h1>

<h2><%=Message.getLastInfo()%></h2>

<table border="0">

<tr><td><a href="/livrariajsp/cadastro.jsp?cadastro=autor">Autores</a></td></tr>

<tr><td><a href="/livrariajsp/cadastro.jsp?cadastro=cliente">Clientes</a></td></tr>

<tr><td><a href="/livrariajsp/cadastro.jsp?cadastro=editora">Editora</a></td></tr>

<tr><td><a href="/livrariajsp/cadastro.jsp?cadastro=funcionario">Funcionários</a></td></tr>

139

Page 140: Introducao Ao Demoiselle Framework Versao 1 PDF

<tr><td><a href="/livrariajsp/cadastro.jsp?cadastro=livro">Livros</a></td></tr>

<tr><td><a href="/livrariajsp/cadastro.jsp?cadastro=local">Locais</a></td></tr>

<tr><td><a href="/livrariajsp/autenticar?operacao=logout">Sair</a></td></tr>

</table>

</body>

</html>

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>Autenticação</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

Page 141: Introducao Ao Demoiselle Framework Versao 1 PDF

<input type="submit" value="entrar">

</form>

</body>

</html>

Todas as páginas de listagem dos cadastros são iguais. Por isso não precisamos criar um arquivo para cada uma. O arquivo cadastro.jsp faz a exibição de qualquer cadastro por meio de parâmetro 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

Page 142: Introducao Ao Demoiselle Framework Versao 1 PDF

</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("valorprocurado"),request.getParameter("ordem"),pagina)%>

<p align="center"><a href="/livrariajsp/admin.jsp">Módulo Administrativo</a></p>

</body>

</html>

<%}}%>

As páginas de edição já contêm peculiaridades que ficam difíceis 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

Page 143: Introducao Ao Demoiselle Framework Versao 1 PDF

<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 ? "Inclusão" : "Alteração"%></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

Page 144: Introducao Ao Demoiselle Framework Versao 1 PDF

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 ? "Inclusão" : "Alteração"%></title>

</head>

<body>

<h1>Cadastro de Clientes</h1>

<table border="0">

<tr>

<td>

<h2>Dados Básicos</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

Page 145: Introducao Ao Demoiselle Framework Versao 1 PDF

<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

Page 146: Introducao Ao Demoiselle Framework Versao 1 PDF

<%}}%>

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 ? "Inclusão" : "Alteração"%></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

Page 147: Introducao Ao Demoiselle Framework Versao 1 PDF

<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 ? "Inclusão" : "Alteração"%></title>

</head>

<body>

<h1>Cadastro de Funcionários</h1>

<table border="0">

147

Page 148: Introducao Ao Demoiselle Framework Versao 1 PDF

<tr>

<td>

<form action="/livrariajsp/gravar?cadastro=funcionario" method="post">

<%model = (Funcionario) view.getModel(model);%>

Matrícula:

<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

Page 149: Introducao Ao Demoiselle Framework Versao 1 PDF

<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

Page 150: Introducao Ao Demoiselle Framework Versao 1 PDF

<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 ? "Inclusão" : "Alteração"%></title>

</head>

<body>

<h1>Cadastro de Livros</h1>

<table border="0">

<tr>

<td>

<h2>Dados Básicos</h2>

<form action="/livrariajsp/gravar?cadastro=livro" method="post">

<%model = (Livro) view.getModel(model);%>

ISBN:

<input type="text" name="isbn" value="<%=model.getIsbn()%>"><br/>

Título:

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

Preço:

<input type="text" name="preco" value="<%=String.valueOf(model.getPreco()).replace('.',',')%>"><br/>

150

Page 151: Introducao Ao Demoiselle Framework Versao 1 PDF

<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

Page 152: Introducao Ao Demoiselle Framework Versao 1 PDF

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 ? "Inclusão" : "Alteração"%></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

Page 153: Introducao Ao Demoiselle Framework Versao 1 PDF

<input type="submit" name="retornar" value="Retornar">

</form>

</body>

</html>

<%}}%>

4.10.2 Classes Auxiliares da Camada de Visão

Como se pôde perceber, todas as páginas JSP de nossa aplicação fazem referência a uma classe chamada ViewController. Essa classe fornece a infraestrutura de processamento para as páginas JSP.

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

Page 154: Introducao Ao Demoiselle Framework Versao 1 PDF

}

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);

}

public String getOpcoesDeBusca(String cadastro)

{

return opcoesFactory.getOpcoesDeBusca(cadastro);

154

Page 155: Introducao Ao Demoiselle Framework Versao 1 PDF

}

public String getOpcoesRelacionadas(String cadastro)

{

return getOpcoesRelacionadas(cadastro, null);

}

public String getOpcoesRelacionadas(String cadastro,Integer selecionado)

{

return opcoesFactory.getOpcoesRelacionadas(cadastro,selecionado);

}

public String getOpcoesAutores()

{

return opcoesFactory.getOpcoesAutores(null);

}

public String getOpcoesAutores(String StringId)

{

if (StringId == null)

return opcoesFactory.getOpcoesAutores(-1);

else

return opcoesFactory.getOpcoesAutores(Integer.parseInt(StringId));

}

public String getTiposTelefone()

{

return opcoesFactory.getTiposTelefone();

}

public String getTelefonesCliente(String paramId)

155

Page 156: Introducao Ao Demoiselle Framework Versao 1 PDF

{

Integer id = Integer.parseInt(paramId);

return opcoesFactory.getTelefonesCliente(id);

}

public String getTelefonesFuncionario(String paramId)

{

Integer id = Integer.parseInt(paramId);

return opcoesFactory.getTelefonesFuncionario(id);

}

public String getOpcoesFuncionarios(Integer idFuncionario)

{

return opcoesFactory.getOpcoesFuncionarios(idFuncionario);

}

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

Page 157: Introducao Ao Demoiselle Framework Versao 1 PDF

return Message.getLastInfo();

}

return "";

}

public boolean hasRole(Funcionario funcionario,String papel)

{

if (funcionario == null) return false;

AuthorizationController ac = AuthorizationController.getInstance(funcionario.getApelido());

if (!ac.hasRole(new String[]{papel}))

{

Message.setLastInfo("Você não tem permissão para usar este recurso!");

return false;

}

return true;

}

}

ViewController faz uso de três classes especializadas para prover conteúdo dinâmico para as páginas: ListaFactory, ModelFactory e OpcoesFactory.

ListaFactory tem por objetivo prover métodos para a criação das listagens dos cadastros e dos campos do formulário de pesquisa.

ListaFactory

public class ListaFactory extends ModelFactory {

private Integer registrosPorPagina = 20;

public void setRegistrosPorPagina(Integer registrosPorPagina) {

this.registrosPorPagina = registrosPorPagina;

157

Page 158: Introducao Ao Demoiselle Framework Versao 1 PDF

}

public String getLista(String cadastro, String where, String order,

Integer pagina) {

String tabela = "<table border=\"1\">";

Integer numeroDeDegistros = pagina == null ? null : registrosPorPagina;

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

Page 159: Introducao Ao Demoiselle Framework Versao 1 PDF

+ getListaLocal(where, order, numeroDeDegistros, pagina);

}

if (cadastro.equals(Cadastros.TELEFONE)) {

tabela = tabela

+ getListaTelefone(where, order, numeroDeDegistros, pagina);

}

tabela = tabela + "</table>";

return tabela;

}

private String getListaTelefone(String where, String order,

Integer numeroDeDegistros, Integer pagina) {

String tabela = "";

List<Telefone> lista = new TelefoneDAO().fetchAll(where, order,

numeroDeDegistros, pagina);

String cadastro = "telefone";

tabela = tabela + "<thead>";

tabela = tabela + "<th>Id</th>";

tabela = tabela + "<th><a href=\"/livrariajsp/cadastro.jsp?cadastro="

+ cadastro + "&ordem=numero\">Número</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

Page 160: Introducao Ao Demoiselle Framework Versao 1 PDF

for (Iterator<Telefone> iterator = lista.iterator(); iterator.hasNext();) {

Telefone telefone = (Telefone) iterator.next();

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 = "";

List<Local> lista = new LocalDAO().fetchAll(where, order,

numeroDeDegistros, pagina);

String cadastro = "local";

tabela = tabela + "<thead>";

tabela = tabela + "<th>Id</th>";

tabela = tabela + "<th><a href=\"/livrariajsp/cadastro.jsp?cadastro="

+ cadastro + "&ordem=nome\">Nome</a></th>";

160

Page 161: Introducao Ao Demoiselle Framework Versao 1 PDF

tabela = tabela + "<th>Excluir</th>";

tabela = tabela + "</thead>";

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 = "";

List<Livro> lista = new LivroDAO().fetchAll(where, order,

numeroDeDegistros, pagina);

String cadastro = "livro";

tabela = tabela + "<thead>";

tabela = tabela + "<th>Id</th>";

161

Page 162: Introducao Ao Demoiselle Framework Versao 1 PDF

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\">TÃtulo</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\">Preço</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

Page 163: Introducao Ao Demoiselle Framework Versao 1 PDF

i++;

}

return tabela;

}

private String getListaFuncionario(String where, String order,

Integer numeroDeDegistros, Integer pagina) {

String tabela = "";

List<Funcionario> lista = new FuncionarioDAO().fetchAll(where, order,

numeroDeDegistros, pagina);

String cadastro = "funcionario";

tabela = tabela + "<thead>";

tabela = tabela + "<th>Id</th>";

tabela = tabela + "<th><a href=\"/livrariajsp/cadastro.jsp?cadastro="

+ cadastro + "&ordem=matricula\">MatrÃcula</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

Page 164: Introducao Ao Demoiselle Framework Versao 1 PDF

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 = "";

List<Cliente> lista = new ClienteDAO().fetchAll(where, order,

numeroDeDegistros, pagina);

String cadastro = "cliente";

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

Page 165: Introducao Ao Demoiselle Framework Versao 1 PDF

+ 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

Page 166: Introducao Ao Demoiselle Framework Versao 1 PDF

private String getListaEditora(String where, String order, Integer limit,

Integer offset) {

String tabela = "";

List<Editora> lista = new EditoraDAO().fetchAll(where, order, limit,

offset);

String cadastro = "editora";

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

Page 167: Introducao Ao Demoiselle Framework Versao 1 PDF

return tabela;

}

private String getListaAutor(String where, String order, Integer limit,

Integer offset) {

String tabela = "";

List<Autor> lista = new AutorDAO()

.fetchAll(where, order, limit, offset);

String cadastro = "autor";

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

Page 168: Introducao Ao Demoiselle Framework Versao 1 PDF

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 (campoProcurado == null || valorProcurado == null)

return getLista(cadastro, null, order, pagina);

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

Page 169: Introducao Ao Demoiselle Framework Versao 1 PDF

where = campoProcurado + " LIKE '%" + valorProcurado + "%'";

}

if (cadastro.equals(Cadastros.TELEFONE)) {

where = campoProcurado + " LIKE '%" + valorProcurado + "%'";

}

return getLista(cadastro, where, order, pagina);

}

}

A finalidade da classe ModelFactory é prover métodos que retornem modelos para as páginas de edição. Esses modelos podem ser vazios se for uma inclusão.

ModelFactory

public class ModelFactory {

public Autor getAutor(Autor model) {

AutorDAO dao = new AutorDAO();

model = (Autor) dao.fetchOne(model);

if (model == null) {

model = new Autor();

model.setNome("");

model.setSobrenome("");

}

return model;

}

public Editora getEditora(Editora model) {

EditoraDAO dao = new EditoraDAO();

model = (Editora) dao.fetchOne(model);

169

Page 170: Introducao Ao Demoiselle Framework Versao 1 PDF

if (model == null) {

model = new Editora();

model.setNome("");

}

return model;

}

public IModel getLocal(Local model) {

LocalDAO dao = new LocalDAO();

model = (Local) dao.fetchOne(model);

if (model == null) {

model = new Local();

model.setNome("");

}

return model;

}

public IModel getCliente(Cliente model) {

ClienteDAO dao = new ClienteDAO();

model = (Cliente) dao.fetchOne(model);

if (model == null) {

model = new Cliente();

model.setApelido("");

model.setCpf("");

model.setEmail("");

model.setNome("");

model.setSenha("");

170

Page 171: Introducao Ao Demoiselle Framework Versao 1 PDF

}

return model;

}

public IModel getFuncionario(Funcionario model) {

FuncionarioDAO dao = new FuncionarioDAO();

model = (Funcionario) dao.fetchOne(model);

if (model == null) {

model = new Funcionario();

model.setApelido("");

model.setEmail("");

model.setMatricula("");

model.setNome("");

model.setSenha("");

}

return model;

}

public IModel getTelefone(Telefone model) {

TelefoneDAO dao = new TelefoneDAO();

model = (Telefone) dao.fetchOne(model);

if (model == null) {

model = new Telefone();

model.setNumero("");

model.setTipo(Telefones.RESIDENCIAL);

}

return model;

171

Page 172: Introducao Ao Demoiselle Framework Versao 1 PDF

}

public IModel getLivro(Livro model) {

LivroDAO dao = new LivroDAO();

model = (Livro) dao.fetchOne(model);

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 métodos que preencham caixas de seleção de formulários.

OpcoesFactory

public class OpcoesFactory {

public String getOpcoesDeBusca(String cadastro) {

String opcoes = "";

172

Page 173: Introducao Ao Demoiselle Framework Versao 1 PDF

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\">Matrícula</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\">Título</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\">Número</option>";

opcoes = opcoes + "<option value=\"tipo\">Tipo</option>";

}

173

Page 174: Introducao Ao Demoiselle Framework Versao 1 PDF

return opcoes;

}

public String getOpcoesRelacionadas(String cadastro, Integer selecionado) {

String opcoes = "";

if (cadastro.equals(Cadastros.LOCAL)) {

List<Local> locais = new LocalDAO().fetchAll(null, "nome", null,

null);

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

Page 175: Introducao Ao Demoiselle Framework Versao 1 PDF

opcoes = opcoes

+ "<option value=\""

+ editora.getId()

+ "\" "

+ (selecionado == null ? ""

: (editora.getId() == selecionado ? "selected"

: "")) + ">" + editora.getNome()

+ "</option>";

}

}

return opcoes;

}

public String getOpcoesAutores(Integer id) {

String opcoes = "";

String where = (id == null ? null : "id_livro = " + id);

if (where == null) {

List<Autor> autores = new AutorDAO().fetchAll(null, null, null,

null);

Collections.sort(autores, new Autor());

for (Iterator<Autor> iterator = autores.iterator(); iterator

.hasNext();) {

Autor autor = (Autor) iterator.next();

opcoes = opcoes + "<option value=\"" + autor.getId() + "\">"

+ autor.getSobrenome() + "," + autor.getNome()

175

Page 176: Introducao Ao Demoiselle Framework Versao 1 PDF

+ "</option>";

}

} else {

List<AutorLivro> autoresLivro = new AutorLivroDAO().fetchAll(where,

null, null, null);

Collections.sort(autoresLivro, new AutorLivro());

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;

}

public String getTiposTelefone() {

String opcoes = "";

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) {

176

Page 177: Introducao Ao Demoiselle Framework Versao 1 PDF

} catch (IllegalAccessException e) {

}

opcoes = opcoes + "<option value=\"" + tipo + "\">" + tipo

+ "</option>";

}

return opcoes;

}

public String getTelefonesCliente(Integer id) {

String opcoes = "";

TelefoneClienteDAO dao = new TelefoneClienteDAO();

List<TelefoneCliente> telefones = dao.fetchAll("id_cliente = " + id,

null, null, null);

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

Page 178: Introducao Ao Demoiselle Framework Versao 1 PDF

TelefoneDAO telefoneDao = new TelefoneDAO();

Telefone telefone = new Telefone();

List<TelefoneFuncionario> telefones = dao.fetchAll("id_funcionario = "

+ id, null, null, null);

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;

}

public String getOpcoesFuncionarios(Integer idFuncionario) {

String opcoes = "";

FuncionarioDAO dao = new FuncionarioDAO();

List<Funcionario> funcionarios = dao.fetchAll(null, "nome", null, null);

for (Iterator<Funcionario> iterator = funcionarios.iterator(); iterator

.hasNext();) {

Funcionario funcionario = (Funcionario) iterator.next();

opcoes = opcoes + "<option value=\"" + funcionario.getId() + "\">"

178

Page 179: Introducao Ao Demoiselle Framework Versao 1 PDF

+ funcionario.getNome() + " - "

+ funcionario.getMatricula() + "</option>";

}

return opcoes;

}

}

4.11 Conclusões

Terminamos assim a implementação das três camadas propostas na seção 4.7: Persistência, Controle e Visão. A estrutura final de pacotes da aplicação pode ser vista na figura 17.

179

Figura 17: Estrutura de pacotes da aplicação livrariajsp

Page 180: Introducao Ao Demoiselle Framework Versao 1 PDF

Essa implementação torna funcionais os casos de uso Manter Cadastro, Autenticar Usuario e Verificar Permissão de Funcionário. A arquitetura adotada procurou reutilizar o máximo de estruturas, para facilitar a manutenção futura e permitir que o sistema cresça.

Contudo, percebe-se que a maior parte do código que implementamos não é o que realmente interessa para o cliente, mas sim o necessário para a aplicação funcionar. E apesar de tentarmos generalizar diversas porções de código, ainda há muita repetição.

O próximo capítulo mostra como Hibernate e JSF já trazem prontas funcionalidades que tivemos de implementar neste capítulo.

Uma única observação. A figura 17 mostra um pacote util, que não foi tratado aqui. Sua finalidade é armazenar classes utilitárias que sejam usadas por todas as camadas. Como não implementamos todos os casos de uso, ele terminou por ficar vazio.

180

Page 181: Introducao Ao Demoiselle Framework Versao 1 PDF

CAPÍTULO 5

Livraria com JSF e HibernateNeste capítulo, faremos a implementação dos mesmos casos de uso tratados no capítulo anterior, usando a tecnologia JavaServer Pages (JSF) para a camada de visão e Hibernate para a camada de persistência.

Faremos uma pequena introdução a JSF e Hibernate, para em seguida iniciar a implementação da parte administrativa da livraria.

Para acompanhar este capítulo, você precisa ter instalado o Eclipse Galileo for JEE Developers.

5.1 Hibernate

Segundo Bauer e King (2007, p. 31), “Hibernate é uma ferramenta de mapeamento objeto relacional [ORM] completa que fornece todos os benefícios de um ORM”. Esses benefícios são: produtividade, manutenibilidade, performance e independência de fornecedor.

Ainda segundo os autores, “o Hibernate Core é o serviço básico para a persistência”. Ele faz mapeamento utilizando arquivos XML e possui uma linguagem de consulta chamada HQL, “assim como interfaces de consulta programática 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 anotações. O pacote Hibernate Annotations, usado em cima do Core, reduz significativamente a quantidade de linhas de código necessárias para fazer ORM. Por isso, utilizaremos Hibernate Annotations em nossa implementação.

Os conceitos necessários para utilização do Hibernate serão vistos conforme criarmos a camada de Persistência. A versão 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 construção de aplicativos Web em Java, cuja especificação foi desenvolvida pelo grupo de peritos JSR-127 sob a supervisão do Java Community Process”.

181

Page 182: Introducao Ao Demoiselle Framework Versao 1 PDF

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 usuário, um modelo de programação orientado a eventos e um modelo de componentes que permite a adição de novos componentes por desenvolvedores independentes.

O funcionamento de JSF se baseia no redirecionamento das requisições para um servlet chamado FacesServlet. Esse redirecionamento é feito no arquivo web.xml. FacesServlet cria um objeto chamado FacesContext, que por sua vez contém os objetos ServletContext, ServletRequest e ServletResponse, que lhe são passados pelo contêiner Web. Em seguida, FacesServlet cria o objeto Lyfecycle, que processa FacesContext em seis fases. São elas:

• Reconstituição da árvore de componentes;

• Aplicação dos valores da requisição;

• Processamento de validações;

• Atualização de valores de modelos;

• Invocação do aplicativo;

• Renderização da resposta.

JSF permite que o programador crie ouvidores (listeners) que são verificados entre quaisquer duas fases do ciclo de vida de processamento da requisição. Esses ouvidores podem inclusive alterar o curso do fluxo de processamento.

A utilização de JSF será vista durante a implementação do projeto deste capítulo. A versão da especificação a ser utilizada aqui é a 1.2.

5.3 Configuração do Web Page Editor

Nossa primeira tarefa será configurar o Web Page Editor como editor padrão de páginas JSP. Isso é necessário porque esse editor, ao contrário do JSP Editor, possui funcionalidades para edição de JSF.

Para mudar o editor padrão de JSP, acesse o menu Window->Preferences. No item General, procure Editors, e dentro deste, File Associations. Procure na caixa de listagem a extensão *.jsp. Ao selecioná-la, serão exibidos na listagem abaixo os editores associados. Selecione Web Page Editor, conforme mostra a figura 18, e clique em Default.

182

Page 183: Introducao Ao Demoiselle Framework Versao 1 PDF

5.4 Criando um Projeto JavaServer Faces

Vamos criar um projeto JavaServer Faces no Eclipse. Para isso, acesse o menu File->New->Project e selecione o item Web. Dentro dele, escolha Dynamic Project Web. Até aqui é o mesmo procedimento que fizemos para o projeto anterior.

Após escolher o tipo de projeto, somos levados a janela de configuração. O nome do projeto será livrariajsf. Como já configuramos um servidor de aplicação, podemos selecionar Apache Tomcat v6.0 na caixa Target runtime.

Agora vem a parte nova. Na caixa de seleção Configuration, selecione o item JavaServer Faces v1.2 Project. A tela deverá estar como mostra a figura 19.

Após isso, clique em Next até chegar à tela JSF Capabilities, onde iremos selecionar a implementação JSF a ser utilizada.

Você pode usar (teoricamente) qualquer implementação de JSF. Aqui nós iremos utilizar a implementação de referência, um subprojeto do GlassFish chamado Mojarra.

183

Figura 18: Alteração do editor de páginas JSP

Page 184: Introducao Ao Demoiselle Framework Versao 1 PDF

Toda especificação do JCP tem uma implementação de referência livre e um kit de compatibilidade de tecnologia. Isso ajuda os fornecedores de software a criarem suas implementações de uma determinada JSR e verificar se ela está de acordo com o que foi especificado.

184

Figura 19: Criação de um projeto JSF

Page 185: Introducao Ao Demoiselle Framework Versao 1 PDF

A implementação de referência não é o melhor produto de software que pôde ser produzido a partir da especificação, mas sim aquele que está totalmente de acordo com ela e que funciona.

A especificação cria um padrão e estabelece um mínimo de funcionalidades. Mas isso não impede que cada fornecedor crie uma implementação que possua mais recursos do que os exigidos por uma JSR.

Dessa forma, os consumidores ficam protegidos, pois a JSR assegura que todas as implementações irão operar da mesma maneira. A escolha por uma ou outra implementação não significa aprisionamento de software, desde que, é claro, a aplicação crie dependências apenas para os recursos previstos pela especificação.

Os fornecedores de software, por sua vez, podem concorrer entre si oferecendo funcionalidades que extrapolem as definidas na JSR, e criando ferramentas auxiliares.

Após essa defesa do modelo tecnológico do JCP, vamos incluir as bibliotecas JSF em nossa aplicação.

185

Figura 20: Download da implementação JSF

Page 186: Introducao Ao Demoiselle Framework Versao 1 PDF

Selecione User Library na caixa de seleção Type e clique no botão Download libraries... O Eclipse irá fazer uma busca e mostrará uma tela (figura 20), onde você pode escolher uma biblioteca JSF de sua preferência. Nós selecionaremos o Mojarra para o nosso projeto. Após aceitar a licença, basta aguardar o download.

Terminado o download, você deve ter uma tela igual à da figura 21. Basta clicar no botão Finish e o projeto estará criado e configurado para trabalhar com JSF.

5.5 Instalando e Usando JBoss Tools para Configurar o Hibernate

Para facilitar nosso trabalho, iremos utilizar um plugin Eclipse chamado JBoss Tools, que entre outras coisas, oferece suporte para Hibernate. Ele pode ser obtido no site

186

Figura 21: Configuração do JSF para o projeto

Page 187: Introducao Ao Demoiselle Framework Versao 1 PDF

http://www.jboss.org/tools/download. A versão utilizada aqui é a 3.1, para o Eclipse 3.5.2.

A instalação de plug-ins no Eclipse torna-se bem simples quando se usa update sites. Por meio do menu Help->Install New Software, é possível indicar uma URL a partir da qual o Eclipse irá baixar e instalar um plug-in. Os update sites do JBoss Tools estão disponíveis na página de downloads, já citada anteriormente.

Após 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.

187

Figura 22: Configuração do Hibernate no Eclipse

Page 188: Introducao Ao Demoiselle Framework Versao 1 PDF

Esse item irá abrir uma tela que nos ajudará a criar os arquivos de configuração do Hibernate. Primeiro temos de selecionar o tipo de configuração. Iremos usar anotações nas classes para fazer o mapeamento objeto-relacional, por isso selecionaremos no campo Type a opção Annotations (jdk 1.5+).

Precisamos indicar uma conexão com banco de dados. Isso é feito no frame Database Connection. Se já não houver uma conexão criada, use o botão New para criar uma. É possível testar facilmente a nova conexão por meio de um botão da tela de dados de conexão.

No frame Property file, criaremos o arquivo de propriedades do Hibernate. Basta clicar no botão Setup desse frame e escolher a opção Create new. Tome cuidado para criar o arquivo dentro do diretório src do projeto, que está no classpath.

No frame Configuration File, criaremos o arquivo de configuração do Hibernate. Clique no botão Setup e ele abrirá um formulário no qual colocaremos os dados de configuração, conforme mostra a figura 23.

O campo Session factory name não é obrigatório. Vamos preeenchê-lo apenas para mostrar no arquivo de configuração o que é gerado. O imprescindível no formulário é 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 conexão, é preciso completá-lo com o nome do banco de dados.

Após preencher o formulário do arquivo de configuração, devemos ter uma tela igual à da figura 24. Observe que os dois arquivos de configuração estão no diretório src do projeto, para serem localizados Basta clicar em Finish e os arquivos hibernate.cfg.xml e hibernate.properties serão criados na raiz do projeto.

Isso configura o Hibernate, mas não o instala. O modo ideal de fazer a instalação do Hibernate é usado Maven, mas este assunto ficará para o próximo capítulo. Por ora, vamos baixar manualmente o Hibernate e configurar o Build Path do projeto para localizar as bibliotecas.

É necessário baixar dois arquivos a partir de http://sourceforge.net/projects/hibernate/files, um contendo o núcleo do Hibernate e outro que contém o suporte a anotações.

O primeiro está na pasta hibernate3, e o segundo na pasta hibernate-annotations. Iremos baixar a versão 3.3.1GA de ambos. Faça o download dos arquivos e os descompacte em um diretório de sua preferência. 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

Page 189: Introducao Ao Demoiselle Framework Versao 1 PDF

Tome cuidado, porque há arquivos .jar na raiz das instalações e dentro do diretório lib de cada uma. No caso do Hibernate Core, inclua os .jar dos subdiretórios required e bytecode. Do subdiretório optional, inclua o jar de ehcache.

189

Page 190: Introducao Ao Demoiselle Framework Versao 1 PDF

190

Figura 23: Configuração do arquivo hibernate.cfg.xml

Page 191: Introducao Ao Demoiselle Framework Versao 1 PDF

191

Figura 24: Console de configuração do Hibernate no Eclipse

Page 192: Introducao Ao Demoiselle Framework Versao 1 PDF

Um grande problema da linguagem Java é que ela sozinha não faz nada. Para fazer aplicações ú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 cabeça. Nós faremos a inclusão manual das dependências neste projeto para deixar clara a dificuldade. O meio mais elegante para fazer gestão de dependências será mostrado no próximo capítulo.

O Hibernate tem algumas dependências de APIs de terceiros. São elas: SLF4, Log4J e Commons Logging. Elas podem ser obtidas, respectivamente nos seguintes endereços:

• http://www.slf4j.org/dist

• http://archive.apache.org/dist/logging/log4j/1.2.14

• Logging: http://commons.apache.org/logging/download_logging.cgi

As versões necessárias para o Hibernate aqui utilizado são: SLF4 1.5.2, Log4J 1.2.14 e Commons Logging 1.1.1.

Após descompactar essas bibliotecas em um diretório de sua preferência, 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 três APIs citadas.

Para ter certeza de que não falta nada, listamos a seguir os arquivos .jar necessários para o funcionamento do Hibernate, com o caminho diretório completo da instalação:

• 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

Page 193: Introducao Ao Demoiselle Framework Versao 1 PDF

• hibernate-distribution-3.3.1.GA/lib/bytecode/cglib/hibernate-cglib-repack-2.1_3.jar

• hibernate-distribution-3.3.1.GA/lib/optional/ehcache/ehcache-1.2.3.jar

• slf4j-1.5.2/slf4j-log4j12-1.5.2.jar

• logging-log4j-1.2.14/dist/lib/log4j-1.2.14.jar

• commons-logging-1.1.1/commons-logging-1.1.1.jar

• commons-logging-1.1.1/commons-logging-adapters-1.1.1.jar

• commons-logging-1.1.1/commons-logging-api-1.1.1.jar

• commons-logging-1.1.1/commons-logging-tests.jar

Com esses procedimentos, agora podemos configurar os modelos de nossa aplicação.

5.6 Camada de Persistência

5.6.1 Criando Modelos com Anotações

Iremos copiar os pacotes models e models.generic do projeto livrariajsp para o livrariajsf e fazer as alterações para que eles utilizem o Hibernate.

A configuração do atributo primaryKey no construtor não é mais necessária. Na verdade, o atributo primaryKey não é mais necessário, nem seus métodos de leitura e gravação, por isso podemos removê-los da classe AbstractModel e da interface IModel.

A própria classe AbstractModel será eliminada, pois como cada entidade terá um gerador de sequência diferente para seu campo id, não será possível manter o atributo correspondente genérico.

A interface IModel permanece, pois os métodos de acesso e modificação (getId e setId) são utilizados por AbstractDAO.

Os construtores dos modelos farão a inicialização dos atributos agora, para serem manipulados pelo JSF.

A classe AbstractOnlyName agora não herda mais de AbstractModel, que foi eliminada. Ela implementa IModel diretamente. Além disso, essa classe agora recebe uma anotação @MappedSuperclass do pacote javax.persistence. Essa anotação informa ao Hibernate que os campos desta classe devem ser considerados para a criação da tabela mapeada pela classe.

AbstractOnlyName define um atributo genérico chamado nome. Usamos a anotação @Column de javax.persistence para informar ao Hibernate que esse atributo será um

193

Page 194: Introducao Ao Demoiselle Framework Versao 1 PDF

campo na tabela mapeada pela classe concreta que estende AbstractOnlyName. Com o parâmetro 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 também utiliza as anotações @MappedSuperclass e @Column.

AbstractUsuario

@MappedSuperclass

public abstract class AbstractUsuario extends AbstractOnlyName {

@Column(name = "apelido", length = 10)

protected String apelido;

@Column(name = "senha", length = 8)

protected String senha;

@Column(name = "email", length = 40)

protected String email;

194

Page 195: Introducao Ao Demoiselle Framework Versao 1 PDF

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;

}

}

Já tivemos uma redução de duas classes, em relação à aplicação anterior. E ao contrário dela, construída com JDBC, desta vez não precisaremos de uma classe abstrata para definir o atributo telefone. O motivo é que não haverá necessidade de classes auxiliares para definir os relacionamentos das tabelas, porque o Hibernate faz isso com o uso de anotações, que são metadados que afetam o modo pelo qual os programas Java são tratados por ferramentas e bibliotecas.

195

Page 196: Introducao Ao Demoiselle Framework Versao 1 PDF

Iremos daqui para frente mostrar como ficam os modelos usando anotações. Após a apresentação do código-fonte, serão discutidas as anotações utilizadas na respectiva classe. Todas as anotações utilizadas doravante são do pacote javax.persistence.

Comecemos pelo modelo Autor. A única modificação, além das anotações, é no construtor. Todos os atributos, exceto o que mapeia a chave primária, 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;

@Column(name = "sobrenome", length = 20)

private String sobrenome;

public Autor() {

this.nome = "";

this.sobrenome = "";

}

public String getSobrenome() {

return sobrenome;

}

public void setSobrenome(String sobrenome) {

196

Page 197: Introducao Ao Demoiselle Framework Versao 1 PDF

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;

}

public Integer getId() {

return id;

}

}

A anotação @Entity identifica essa classe como uma entidade, ou seja, os seus dados são provenientes de uma entidade do banco de dados, de uma tabela. A anotação @Table, por meio do parâmetro name, identifica o nome da tabela no banco de dados.

A anotação @SequenceGenerator define uma sequência, um elemento do banco de dados responsável pela geração de séries numéricas baseadas em uma regra. O parâmetro name cria um nome para ser utilizado pela classe. O parâmetro @sequenceName leva o nome da sequência no banco de dados.

A anotação @Id identifica qual atributo está mapeando a chave primária da tabela. Conforme já vimos, @Column identifica os atributos que mapeiam campos de uma tabela.

A anotação @GeneratedValue indica ao Hibernate que o atributo em questão terá seus valores gerados automaticamente. A fonte dos valores é definida pelo parâmetro generator, que neste caso é “AutorSequence”, especificado por @SequenceGenerator. O parâmetro strategy indica como o valor será criado pelo gerador.

197

Page 198: Introducao Ao Demoiselle Framework Versao 1 PDF

A anotação @columnDefinition serve para forçarmos o Hibernate a criar um campo exatamente da maneira como queremos. No caso, não 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 também é inteiro, mas tem uma sequência associada a ele.

Vamos agora para o próximo 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;

@Column(name = "cpf", length = 11)

private String cpf;

@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

Page 199: Introducao Ao Demoiselle Framework Versao 1 PDF

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 anotação @ManyToMany indica que o atributo representa um relacionamento de muitos-para-muitos.

O argumento cascade configura as operações em cascata, ou seja, quais operações sobre a tabela principal terão repercussão na tabela relacionada. A constante CascadeType.ALL informa que todas as operações na tabela principal, incluindo atualização e remoção, serão propagadas para as tabelas dependentes. Neste caso, se um cliente for removido, todos os seus telefones também serão removidos.

O argumento fetch informa ao Hibernate como será a recuperação 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 não será imediatamente populado quando um objeto Cliente for recuperado, mas apenas quando for invocado seu método de leitura.

199

Page 200: Introducao Ao Demoiselle Framework Versao 1 PDF

A anotação @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 serão os campos na tabela de relacionamento que serão a chave primária da tabela principal (a tabela da classe que está definindo o relacionamento). A anotação @JoinColumn, similar a a @Column, permite informar todos os detalhes sobre um campo, incluindo nome e tipo de dados. O argumento name dessa anotação indica o nome da chave estrangeira. O argumento inverseJoinColumns define quais serão os campos na tabela de relacionamento que serão a chave primária da tabela dependente.

As anotações da classe Funcionario são 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;

@Column(name = "matricula", length = 8)

private String matricula;

@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

Page 201: Introducao Ao Demoiselle Framework Versao 1 PDF

this.apelido = "";

this.email = "";

this.matricula = "";

this.nome = "";

this.senha = "";

this.telefones = new HashSet<Telefone>();

}

public String getMatricula() {

return matricula;

}

public void setMatricula(String matricula) {

this.matricula = matricula;

}

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;

}

201

Page 202: Introducao Ao Demoiselle Framework Versao 1 PDF

}

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;

public Editora() {

this.nome = "";

}

public void setId(Integer id) {

this.id = id;

}

public Integer getId() {

return 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

Page 203: Introducao Ao Demoiselle Framework Versao 1 PDF

@Column(name = "id_local", columnDefinition = "serial")

private Integer id;

public Local() {

this.nome = "";

}

public void setId(Integer id) {

this.id = id;

}

public Integer getId() {

return id;

}

}

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;

@Column(name = "isbn", length = 13)

private String isbn;

@Column(name = "titulo", length = 255)

203

Page 204: Introducao Ao Demoiselle Framework Versao 1 PDF

private String titulo;

@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;

@Column(name = "ano")

private Integer ano;

@Column(name = "preco")

private Float preco;

@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

Page 205: Introducao Ao Demoiselle Framework Versao 1 PDF

public Set<Autor> getAutores() {

return autores;

}

public void setAutores(Set<Autor> autores) {

this.autores = autores;

}

public String getIsbn() {

return isbn;

}

public void setIsbn(String isbn) {

this.isbn = isbn;

}

public String getTitulo() {

return titulo;

}

public void setTitulo(String titulo) {

this.titulo = titulo;

}

public Local getLocal() {

return local;

}

public void setLocal(Local local) {

this.local = local;

205

Page 206: Introducao Ao Demoiselle Framework Versao 1 PDF

}

public Editora getEditora() {

return editora;

}

public void setEditora(Editora editora) {

this.editora = editora;

}

public Integer getAno() {

return ano;

}

public void setAno(Integer ano) {

this.ano = ano;

}

public Float getPreco() {

return preco;

}

public void setPreco(Float preco) {

this.preco = preco;

}

public Integer getId() {

return this.id;

}

public void setId(Integer id) {

206

Page 207: Introducao Ao Demoiselle Framework Versao 1 PDF

this.id = id;

}

}

A anotação @OneToOne define um relacionamento de um-para-um. As informações sobre o relacionamento são dadas pela anotação @JoinColumn, já vista anteriormente. Aqui é necessário configurar o argumento referencedColumnName, que é o nome da chave primária na tabela dependente.

Pedido

@Entity

@Table(name = "pedidos")

public class Pedido implements IModel {

@Id

@Column(name = "numero_pedido")

private Integer numeroPedido;

@Column(name = "data_pedido")

@Temporal(TemporalType.DATE)

private Calendar dataPedido;

@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

Page 208: Introducao Ao Demoiselle Framework Versao 1 PDF

this.numeroPedido = 0;

}

public Set<ItemPedido> getItems() {

return items;

}

public void setItems(Set<ItemPedido> items) {

this.items = items;

}

public Integer getNumeroPedido() {

return numeroPedido;

}

public void setNumeroPedido(Integer numeroPedido) {

this.numeroPedido = numeroPedido;

}

public Calendar getDataPedido() {

return dataPedido;

}

public void setDataPedido(Calendar dataPedido) {

this.dataPedido = dataPedido;

}

public Cliente getCliente() {

return this.cliente;

}

208

Page 209: Introducao Ao Demoiselle Framework Versao 1 PDF

public void setCliente(Cliente cliente) {

this.cliente = cliente;

}

public Integer getId() {

return this.getNumeroPedido();

}

public void setId(Integer id) {

this.setNumeroPedido(id);

}

}

A anotação @Temporal é específica para tratar campos de data e hora. A constante TemporalType.DATE indica que o campo terá o tipo de dados de data padrão do banco de dados utilizado.

A anotação @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

Page 210: Introducao Ao Demoiselle Framework Versao 1 PDF

@OneToOne

@JoinColumn(name = "id_livro", referencedColumnName = "id_livro", columnDefinition = "integer NOT NULL")

private Livro livro;

@Column(name = "preco")

private Float preco;

@Column(name = "quantidade")

private Integer quantidade;

public ItemPedido() {

this.livro = new Livro();

this.pedido = new Pedido();

this.preco = 0.0f;

this.quantidade = 0;

}

public Livro getLivro() {

return livro;

}

public void setLivro(Livro livro) {

this.livro = livro;

}

public Float getPreco() {

return preco;

}

public void setPreco(Float preco) {

210

Page 211: Introducao Ao Demoiselle Framework Versao 1 PDF

this.preco = preco;

}

public Integer getQuantidade() {

return quantidade;

}

public void setQuantidade(Integer quantidade) {

this.quantidade = quantidade;

}

public Integer getId() {

return this.id;

}

public void setId(Integer id) {

this.id = id;

}

public void setPedido(Pedido pedido) {

this.pedido = pedido;

}

public Pedido getPedido() {

return pedido;

}

}

Telefone

@Entity

@Table(name = "telefones")

211

Page 212: Introducao Ao Demoiselle Framework Versao 1 PDF

@SequenceGenerator(name = "TelefoneSequence", sequenceName = "telefones_id_telefone_seq")

public class Telefone implements IModel {

@Id

@GeneratedValue(generator = "TelefoneSequence", strategy = GenerationType.SEQUENCE)

@Column(name = "id_telefone", columnDefinition = "serial")

private Integer id;

@Column(name = "numero", length = 16)

private String numero;

@Column(name = "tipo", length = 20)

private String tipo;

public Telefone() {

this.numero = "";

this.tipo = "";

}

public String getNumero() {

return numero;

}

public void setNumero(String numero) {

this.numero = numero;

}

public String getTipo() {

return tipo;

}

212

Page 213: Introducao Ao Demoiselle Framework Versao 1 PDF

public void setTipo(String tipo) {

this.tipo = tipo;

}

public Integer getId() {

return this.id;

}

public void setId(Integer id) {

this.id = id;

}

}

Como os relacionamentos foram definidos por meio de anotações, os três modelos auxiliares criados na aplicação anterior são desnecessários. E assim, eliminamos mais três classes.

5.6.2 Criando DAOs sem JDBC

Em relação ao capítulo anterior, teremos uma grande modificação no que diz respeito a camada de DAOs. A primeira se refere à classe que encapsula os comandos SQL. Como o Hibernate faz isso, ela não é mais necessária. Em seu lugar, utilizaremos uma classe que cria, recupera e fecha a sessão com o banco de dados via API do Hibernate. É a classe HibernateUtil, que ficará no pacote dao.generic

HibernateUtil

public class HibernateUtil {

public static final SessionFactory sessionFactory;

static {

try {

// Cria SessionFactory a partir de hibernate.cfg.xml

sessionFactory = new AnnotationConfiguration().configure()

.buildSessionFactory();

} catch (Throwable ex) {

// A mensagem de erro deve ser gravada em log, em uma aplicação real

System.err.println("Initial SessionFactory creation failed." + ex);

213

Page 214: Introducao Ao Demoiselle Framework Versao 1 PDF

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 variável 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 não tem nenhuma

return s;

}

public static void closeSession() throws HibernateException {

Session s = (Session) session.get();

if (s != null)

s.close();

session.set(null);

}

214

Page 215: Introducao Ao Demoiselle Framework Versao 1 PDF

}

O método ultimoNumeroDoPedido de DAOEngine não será mais necessário, porque o Hibernate atualiza automaticamente o atributo id do objeto Pedido com o valor criado no registro após a inclusão.

A interface IDAO será mantida, sem modificações:

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. Não é mais necessário um atributo para guardar o nome da tabela (nem um método para recuperá-lo), pois essa informação está no modelo, por meio de anotação. Agora é possível definir um método genérico de busca, utilizando a linguagem de consulta orientada a objetos do Hibernate, o HQL. Também não é mais necessário um método para recuperar os nomes dos campos (getFields), pois o Hibernate cuida disso.

AbstractDAO

public abstract class AbstractDAO implements IDAO {

/**

* Método padrão para incluir registros

*

* @param model

*/

public boolean insert(IModel model) {

return save(model);

}

215

Page 216: Introducao Ao Demoiselle Framework Versao 1 PDF

/**

* Método padrão 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;

}

}

/**

* Método padrão para remover registros

*

* @param model

*/

public boolean delete(IModel model) {

216

Page 217: Introducao Ao Demoiselle Framework Versao 1 PDF

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;

}

}

/**

* Método para obter a condição padrão 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

Page 218: Introducao Ao Demoiselle Framework Versao 1 PDF

}

if (order == null) {

order = "";

} else {

order = " order by " + order;

}

Query hqlQuery = HibernateUtil.currentSession().createQuery(

"from " + entity + where + order);

if (limit != null) {

hqlQuery.setMaxResults(limit);

}

if (offset != null) {

hqlQuery.setFirstResult(offset);

}

return hqlQuery.list();

}

}

As classes DAO agora ficam muito mais simples, porque precisam implementar apenas dois métodos, sendo que o método fetchOne é uma extração de dados de fetchAll, e este último apenas invoca o método de consulta da superclasse com o argumento do nome do modelo.

AutorDAO

public class AutorDAO extends AbstractDAO {

@SuppressWarnings("unchecked")

218

Page 219: Introducao Ao Demoiselle Framework Versao 1 PDF

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;

}

public IModel fetchOne(IModel model) {

String where = this.getDefaultWhere(model);

List<Autor> list = this.fetchAll(where, null, null, null);

if (list != null && list.size() > 0)

return list.get(0);

else

return null;

}

}

219

Page 220: Introducao Ao Demoiselle Framework Versao 1 PDF

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;

}

public IModel fetchOne(IModel model) {

String where = this.getDefaultWhere(model);

List<Cliente> list = this.fetchAll(where, null, null, null);

if (list != null && list.size() > 0)

return list.get(0);

else

return null;

}

}

220

Page 221: Introducao Ao Demoiselle Framework Versao 1 PDF

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;

}

public IModel fetchOne(IModel model) {

String where = this.getDefaultWhere(model);

List<Editora> list = this.fetchAll(where, null, null, null);

if (list != null && list.size() > 0)

return list.get(0);

else

return null;

}

}

221

Page 222: Introducao Ao Demoiselle Framework Versao 1 PDF

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;

}

public IModel fetchOne(IModel model) {

String where = this.getDefaultWhere(model);

List<Funcionario> list = this.fetchAll(where, null, null, null);

if (list != null && list.size() > 0)

return list.get(0);

else

return null;

}

}

222

Page 223: Introducao Ao Demoiselle Framework Versao 1 PDF

223

Page 224: Introducao Ao Demoiselle Framework Versao 1 PDF

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;

}

public IModel fetchOne(IModel model) {

String where = this.getDefaultWhere(model);

List<ItemPedido> list = this.fetchAll(where, null, null, null);

if (list != null && list.size() > 0)

return list.get(0);

else

return null;

}

}

224

Page 225: Introducao Ao Demoiselle Framework Versao 1 PDF

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;

}

public IModel fetchOne(IModel model) {

String where = this.getDefaultWhere(model);

List<Livro> list = this.fetchAll(where, null, null, null);

if (list != null && list.size() > 0)

return list.get(0);

else

return null;

}

}

225

Page 226: Introducao Ao Demoiselle Framework Versao 1 PDF

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;

}

public IModel fetchOne(IModel model) {

String where = this.getDefaultWhere(model);

List<Local> list = this.fetchAll(where, null, null, null);

if (list != null && list.size() > 0)

return list.get(0);

else

return null;

}

}

226

Page 227: Introducao Ao Demoiselle Framework Versao 1 PDF

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;

}

public IModel fetchOne(IModel model) {

String where = this.getDefaultWhere(model);

List<Pedido> list = this.fetchAll(where, null, null, null);

if (list != null && list.size() > 0)

return list.get(0);

else

return null;

}

}

227

Page 228: Introducao Ao Demoiselle Framework Versao 1 PDF

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;

}

public IModel fetchOne(IModel model) {

String where = this.getDefaultWhere(model);

List<Telefone> list = this.fetchAll(where, null, null, null);

if (list != null && list.size() > 0)

return list.get(0);

else

return null;

}

}

Não precisamos mais de DAOs para tratar os relacionamentos, pois estes são tratados dentro dos DAOs dos modelos principais, que os definem com anotações. Assim, eliminamos mais três classes.

228

Page 229: Introducao Ao Demoiselle Framework Versao 1 PDF

5.6.3 Testando a Camada de Persistência

Iremos refazer o exemplo de teste de integração da classe AutorDAO, para mostrar como fica com o uso de Hibernate.

TestAutorDAO

public class TestAutorDAO extends TestCase {

private static Autor autor;

private static Autor autorRecuperado;

private static AutorDAO autorDAO = new AutorDAO();

/**

* Testa se um registro é incluído com sucesso

*/

public void testInsert() {

autor = new Autor();

autor.setNome("Maurício");

autor.setSobrenome("de Sousa");

TestAutorDAO.autorDAO.insert(autor);

Autor autorRecuperado = (Autor) TestAutorDAO.autorDAO.fetchOne(autor);

assertNotNull(autorRecuperado.getId());

System.out.println(autor);

}

/**

* Testa se um registro é atualizado com sucesso

229

Page 230: Introducao Ao Demoiselle Framework Versao 1 PDF

*/

public void testUpdate() {

autor.setNome("Carlos");

autor.setSobrenome("Gomes");

autorDAO.update(autor);

autorRecuperado = (Autor) autorDAO.fetchOne(autor);

assertEquals(autor.getId(), autorRecuperado.getId());

System.out.println(autor);

}

/**

* Teste se um registro é apagado com sucesso

*/

public void testDelete() {

autorDAO.delete(autor);

autorRecuperado = (Autor) autorDAO.fetchOne(autor);

assertNull(autorRecuperado);

}

}

5.6.4 Classes e Arquivos Auxiliares da Camada de Persistência

O arquivo connection.properties utilizado no capítulo anterior não é mais necessário, porque as informações sobre a conexão com o banco estão no arquivo hibernate.cfg.xml.

A classe Message do pacote messages pode ser totalmente reaproveitada, sem nenhuma modificação.

230

Page 231: Introducao Ao Demoiselle Framework Versao 1 PDF

5.7 Camada de Controle

5.7.1 Generalização

O controle da aplicação será feito por classes ManagedBean, de acordo com a arquitetura do JSF, que cuida das camadas de controle e visão.

Teremos um ManagedBean para cada cada modelo cujos dados constituírem um cadastro. Esses ManagedBeans terão três operações comuns: listar dados, gravar dados e remover dados. Por estabelecer um padrão, criaremos uma interface para esses ManagedBeans, com métodos correspondentes a essas operações:

IManagedBean

public interface IManagedBean {

public String gravar();

public String remover();

public ArrayList<? extends IModel> getLista();

}

Como já sabemos que teremos de verificar a autenticação do usuário, e isso é feito na camada de controle, vamos criar uma classe abstrata para os ManagedBeans que faça essa verificação.

AuthController

public abstract class AuthController {

public AuthController(boolean checkUser)

{

if (checkUser)

if (!isAuthenticated()) {

throw new SecurityException("Usuário não autenticado");

}

}

protected boolean isAuthenticated()

{

231

Page 232: Introducao Ao Demoiselle Framework Versao 1 PDF

return getSession().getAttribute("funcionario") != null;

}

@SuppressWarnings("static-access")

protected HttpSession getSession() {

FacesContext facesContext = FacesContext.getCurrentInstance();

HttpServletRequest request = (HttpServletRequest) facesContext

.getCurrentInstance().getExternalContext().getRequest();

HttpSession session = request.getSession();

return session;

}

}

O construtor invoca o método isAuthenticated se receber um valor verdadeiro, indicado que deve ser verificada a autenticação. Caso o usuário não esteja autenticado, é lançada uma exceção.

O método isAuthenticated recupera os dados do usuário da sessão, 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;

public AbstractManagedBean(boolean checkUser) {

super(checkUser);

232

Page 233: Introducao Ao Demoiselle Framework Versao 1 PDF

}

public abstract ArrayList<? extends IModel> getLista();

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

Page 234: Introducao Ao Demoiselle Framework Versao 1 PDF

Message.setLastException(e.getMessage());

}

return new CadastroMB().getCadastro();

}

public IModel getBean() {

return bean;

}

public void setBean(IModel bean) {

this.bean = bean;

}

public IDAO getDao() {

return dao;

}

public void setDao(IDAO dao) {

this.dao = dao;

}

public String getOrder() {

return order;

}

public void setOrder(String order) {

this.order = order;

}

public String getWhere() {

return where;

234

Page 235: Introducao Ao Demoiselle Framework Versao 1 PDF

}

public void setWhere(String where) {

this.where = where;

}

public String getSearchKey() {

return searchKey;

}

public void setSearchKey(String searchKey) {

this.searchKey = searchKey;

}

protected void prepareWhere()

{

this.where = this.where == null ? null : this.where+" like '%"+this.searchKey+"%'";

}

protected void cleanWhereAndOrder()

{

this.where = null;

this.order = null;

}

}

Perceba, comparativamente, que essa classe AbstractManagedBean é muito mais simples do que os servlets GravarSelect e RemoverServlet.

Só que os ManagedBeans não herdarão diretamente de AbstractManagedBean. Devemos lembrar também que precisamos fazer o controle de acesso aos recursos da

235

Page 236: Introducao Ao Demoiselle Framework Versao 1 PDF

aplicação. Por isso, iremos definir uma classe que contenha a verificação das permissões.

A classe AcessControllerMB estende AbstractManagedBean e aplica o controle de acesso aos métodos de gravação e remoção. Ela reutiliza a classe AuthorizationController, criada na aplicação anterior. Mas aqui nós movemos essa classe para o pacote controllers.generic, por entender que ela faz parta da infraestrutura genérica dos ManagedBeans.

AccessControllerMB

public abstract class AccessControllerMB extends AbstractManagedBean {

protected AuthorizationController ac;

public AccessControllerMB(boolean checkUser) {

super(checkUser);

Funcionario funcionario = (Funcionario) getSession().getAttribute("funcionario");

this.ac = AuthorizationController.getInstance(funcionario.getApelido());

}

public String gravar()

{

if (!ac.hasRole(new String[] {Papeis.GRAVADOR,Papeis.ADMINISTRADOR}))

{

throw new SecurityException("O usuário não tem acesso a este recurso [gravar]");

}

return super.gravar();

}

public String remover()

{

236

Page 237: Introducao Ao Demoiselle Framework Versao 1 PDF

if (!ac.hasRole(new String[] {Papeis.REMOVEDOR,Papeis.ADMINISTRADOR}))

{

throw new SecurityException("O usuário não tem acesso a este recurso [remover]");

}

return super.remover();

}

}

Especificamente para as classes que possuem o atributo telefone, será necessário um método que faça a recuperação 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

Page 238: Introducao Ao Demoiselle Framework Versao 1 PDF

return items;

}

}

Agora podemos criar os ManagedBeans da aplicação.

5.7.2 ManagedBeans

AutorMB

public class AutorMB extends AccessControllerMB{

public AutorMB()

{

super(true);

this.bean = new Autor();

this.dao = new AutorDAO();

}

public Autor getAutor() {

return (Autor) bean;

}

public void setAutor(Autor autor) {

this.bean = autor;

}

@SuppressWarnings("unchecked")

public ArrayList<Autor> getLista() {

if (!ac.hasRole(new String[] {Papeis.LEITOR,Papeis.ADMINISTRADOR}))

{

throw new SecurityException("O usuário não tem acesso a este recurso [listar]");

}

238

Page 239: Introducao Ao Demoiselle Framework Versao 1 PDF

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());

}

public void setCliente(Cliente cliente) {

this.bean = cliente;

}

public Cliente getCliente() {

return (Cliente) bean;

}

public void setTelefone(Telefone telefone) {

this.telefone = telefone;

}

239

Page 240: Introducao Ao Demoiselle Framework Versao 1 PDF

public Telefone getTelefone() {

return telefone;

}

public String adicionarTelefone() {

Set<Telefone> telefones = new HashSet<Telefone>();

this.telefoneDAO.insert(telefone);

telefone = (Telefone) telefoneDAO.fetchOne(telefone);

telefones.add(telefone);

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);

this.telefone = new Telefone();

return AliasNavigationRules.EDITAR_CLIENTE;

}

public String removerTelefone() {

this.telefone = (Telefone) telefoneDAO.fetchOne(this.telefone);

((Cliente) this.bean).getTelefones().remove(this.telefone);

this.dao.update(this.bean);

240

Page 241: Introducao Ao Demoiselle Framework Versao 1 PDF

telefoneDAO.delete(this.telefone);

this.telefone = new Telefone();

return AliasNavigationRules.EDITAR_CLIENTE;

}

@SuppressWarnings("unchecked")

public List<SelectItem> getTelefonesCliente() {

List<SelectItem> items = new ArrayList<SelectItem>();

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 usuário não 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

Page 242: Introducao Ao Demoiselle Framework Versao 1 PDF

EditoraMB

public class EditoraMB extends AccessControllerMB{

public EditoraMB()

{

super(true);

this.bean = new Editora();

this.dao = new EditoraDAO();

}

public Editora getEditora() {

return (Editora) bean;

}

public void setEditora(Editora editora) {

this.bean = editora;

}

@SuppressWarnings("unchecked")

public ArrayList<Editora> getLista() {

if (!ac.hasRole(new String[] {Papeis.LEITOR,Papeis.ADMINISTRADOR}))

{

throw new SecurityException("O usuário não 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

Page 243: Introducao Ao Demoiselle Framework Versao 1 PDF

}

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();

}

public void setFuncionario(Funcionario funcionario) {

this.bean = funcionario;

}

public Funcionario getFuncionario() {

return (Funcionario) bean;

}

public void setTelefone(Telefone telefone) {

this.telefone = telefone;

}

public Telefone getTelefone() {

return telefone;

}

public String adicionarTelefone()

243

Page 244: Introducao Ao Demoiselle Framework Versao 1 PDF

{

return AliasNavigationRules.EDITAR_CLIENTE;

}

public String removerTelefone()

{

return AliasNavigationRules.EDITAR_CLIENTE;

}

@SuppressWarnings("unchecked")

public ArrayList<Funcionario> getLista() {

if (!ac.hasRole(new String[] {Papeis.LEITOR,Papeis.ADMINISTRADOR}))

{

throw new SecurityException("O usuário não 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;

}

@SuppressWarnings("unchecked")

public List<SelectItem> getTelefonesFuncionario() {

List<SelectItem> items = new ArrayList<SelectItem>();

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

Page 245: Introducao Ao Demoiselle Framework Versao 1 PDF

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();

}

public Livro getLivro() {

return (Livro) bean;

}

public void setLivro(Livro livro) {

this.bean = livro;

}

245

Page 246: Introducao Ao Demoiselle Framework Versao 1 PDF

@SuppressWarnings("unchecked")

public ArrayList<Livro> getLista() {

if (!ac.hasRole(new String[] {Papeis.LEITOR,Papeis.ADMINISTRADOR}))

{

throw new SecurityException("O usuário não 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;

}

public String adicionarAutor() {

Set<Autor> autores = new HashSet<Autor>();

autor = (Autor) autorDAO.fetchOne(autor);

autores.add(autor);

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);

this.autor = new Autor();

return AliasNavigationRules.EDITAR_LIVRO;

}

246

Page 247: Introducao Ao Demoiselle Framework Versao 1 PDF

public String removerAutor() {

this.autor = (Autor) autorDAO.fetchOne(this.autor);

((Livro) this.bean).getAutores().remove(this.autor);

this.dao.update(this.bean);

this.autor = new Autor();

return AliasNavigationRules.EDITAR_LIVRO;

}

@SuppressWarnings("unchecked")

public List<SelectItem> getAutoresLivro() {

List<SelectItem> items = new ArrayList<SelectItem>();

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;

}

@SuppressWarnings("unchecked")

public List<SelectItem> getAutores() {

List<SelectItem> items = new ArrayList<SelectItem>();

List<Autor> lista = this.autorDAO.fetchAll(null, null, null, null);

247

Page 248: Introducao Ao Demoiselle Framework Versao 1 PDF

Collections.sort(lista, new Autor());

for (Iterator iterator = lista

.iterator(); iterator.hasNext();) {

Autor autor = (Autor) iterator.next();

items.add(new SelectItem(autor.getId(), autor.getSobrenome() + ", "

+ autor.getNome()));

}

return items;

}

@SuppressWarnings("unchecked")

public List<SelectItem> getEditoras() {

List<SelectItem> items = new ArrayList<SelectItem>();

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;

}

@SuppressWarnings("unchecked")

public List<SelectItem> getLocais() {

List<SelectItem> items = new ArrayList<SelectItem>();

for (Iterator iterator = this.localDAO.fetchAll(null, null, null, null)

.iterator(); iterator.hasNext();) {

248

Page 249: Introducao Ao Demoiselle Framework Versao 1 PDF

Local local = (Local) iterator.next();

items.add(new SelectItem(local.getId(), local.getNome()));

}

return items;

}

public Autor getAutor() {

return autor;

}

public void setAutor(Autor autor) {

this.autor = autor;

}

public AutorDAO getAutorDAO() {

return autorDAO;

}

public void setAutorDAO(AutorDAO autorDAO) {

this.autorDAO = autorDAO;

}

public String gravar()

{

String uri = super.gravar();

((Livro) this.bean).setEditora(new Editora());

((Livro) this.bean).setLocal(new Local());

return uri;

}

249

Page 250: Introducao Ao Demoiselle Framework Versao 1 PDF

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{

public LocalMB()

{

super(true);

this.bean = new Local();

this.dao = new LocalDAO();

}

public Local getLocal() {

return (Local) bean;

}

public void setLocal(Local local) {

this.bean = local;

}

@SuppressWarnings("unchecked")

public ArrayList<Local> getLista()

250

Page 251: Introducao Ao Demoiselle Framework Versao 1 PDF

{

if (!ac.hasRole(new String[] {Papeis.LEITOR,Papeis.ADMINISTRADOR}))

{

throw new SecurityException("O usuário não 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 específico irá lidar com o controle do menu principal do módulo 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;

public CadastroMB() {

super(true);

}

public String autores() {

this.setCadastro(Cadastros.AUTOR);

return this.getCadastro();

}

public String clientes() {

this.setCadastro(Cadastros.CLIENTE);

return this.getCadastro();

}

251

Page 252: Introducao Ao Demoiselle Framework Versao 1 PDF

public String editoras() {

this.setCadastro(Cadastros.EDITORA);

return this.getCadastro();

}

public String funcionarios() {

this.setCadastro(Cadastros.FUNCIONARIO);

return this.getCadastro();

}

public String livros() {

this.setCadastro(Cadastros.LIVRO);

return this.getCadastro();

}

public String locais() {

this.setCadastro(Cadastros.LOCAL);

return this.getCadastro();

}

public void setCadastro(String cadastro) {

CadastroMB.cadastro = cadastro;

}

public String getCadastro() {

return CadastroMB.cadastro;

}

public String editar() {

if (CadastroMB.cadastro.equals(Cadastros.AUTOR)) {

252

Page 253: Introducao Ao Demoiselle Framework Versao 1 PDF

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

Page 254: Introducao Ao Demoiselle Framework Versao 1 PDF

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 "";

}

public String admin() {

return AliasNavigationRules.ADMINISTRACAO;

}

public String cadastroAtual() {

return this.getCadastro();

}

}

Um ManagedBean ficará encarregado de controlar a entrada e saída do usuário do módulo administrativo.

LoginMB

public class LoginMB extends AuthController{

private Funcionario funcionario;

private FuncionarioDAO funcionarioDAO;

public void setFuncionario(Funcionario funcionario) {

this.funcionario = funcionario;

}

254

Page 255: Introducao Ao Demoiselle Framework Versao 1 PDF

public Funcionario getFuncionario() {

return funcionario;

}

public LoginMB() {

super(false);

this.funcionario = new Funcionario();

this.funcionarioDAO = new FuncionarioDAO();

}

public String login() {

ArrayList<Funcionario> lista = (ArrayList<Funcionario>) this.funcionarioDAO

.fetchAll("apelido='" + this.funcionario.getApelido()

+ "' and senha='" + this.funcionario.getSenha() + "'",

null, null, null);

if (lista == null || lista.size() == 0) {

Message.setLastException("Dados inválidos!");

return AliasNavigationRules.LOGIN;

}

HttpSession session = getSession();

session.setAttribute("funcionario", funcionario);

return AliasNavigationRules.ADMINISTRACAO;

}

public boolean autenticado() {

HttpSession session = getSession();

Funcionario funcionario = (Funcionario) session

255

Page 256: Introducao Ao Demoiselle Framework Versao 1 PDF

.getAttribute("funcionario");

return (funcionario != null);

}

public String logout() {

HttpSession session = getSession();

session.setAttribute("funcionario", null);

session.removeAttribute("funcionario");

this.funcionario = new Funcionario();

return AliasNavigationRules.LOGOUT;

}

}

Finalmente, um último ManagedBean ficará encarregado de recuperar as informações sobre exceções lançadas.

ExceptionMB

public class ExceptionMB {

public String getException()

{

return Message.getLastException();

}

}

5.7.3 Registro no faces-config.xml

É preciso registrar os ManagedBeans concretos, os que serão utilizados diretamente nas páginas JSF, no arquivo faces.config.xml. A figura 25 mostra como adicionar ManagedBeans pelo editor gráfico.

256

Page 257: Introducao Ao Demoiselle Framework Versao 1 PDF

Após a inclusão dos Managed Beans, a tela deve ser igual à da figura 26.

Para não haver dúvidas, o trecho de código correspondente à essa configuração é este:

<managed-bean>

<managed-bean-name>autorMB</managed-bean-name>

<managed-bean-class>controllers.AutorMB</managed-bean-class>

<managed-bean-scope>session</managed-bean-scope>

</managed-bean>

<managed-bean>

257

Figura 25: Inclusão de Managed Beans no arquivo faces-config.xml

Page 258: Introducao Ao Demoiselle Framework Versao 1 PDF

<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

Page 259: Introducao Ao Demoiselle Framework Versao 1 PDF

<managed-bean-class>controllers.LocalMB</managed-bean-class>

<managed-bean-scope>session</managed-bean-scope>

</managed-bean>

<managed-bean>

<managed-bean-name>loginMB</managed-bean-name>

<managed-bean-class>controllers.LoginMB</managed-bean-class>

<managed-bean-scope>session</managed-bean-scope>

</managed-bean>

5.7.4 Classes Auxiliares da Camada de Controle

As classes Cadastros, Papeis e Telefones, do pacote constants da aplicação em JSP serão reaproveitadas. A classe Cadastros será modificada, recebendo como acréscimo as constantes referentes às páginas de listagem.

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

Page 260: Introducao Ao Demoiselle Framework Versao 1 PDF

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 contém os apelidos para páginas 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 navegação é a definição dos possíveis destinos a partir de uma única origem. Ou seja, especificamos, a partir de uma página, para onde o usuário pode ir. Um caso de navegação, por sua vez, define um dos destinos de uma regra de navegação.

No nosso caso, teremos apenas uma regra de navegação.

As regras e casos de navegação no faces-config.xml podem ser definidos pelo editor gráfico, conforme figura 26. O trecho de código 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

Page 261: Introducao Ao Demoiselle Framework Versao 1 PDF

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

Page 262: Introducao Ao Demoiselle Framework Versao 1 PDF

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

5.8 Camada de Visão

5.8.1 Criando as Páginas JSF

Agora criaremos a camada de visão da aplicação, que será constituída exclusivamente de páginas JSF. Não será mais necessária a existência de classes auxiliares como fizemos na aplicação JSP.

JSF depende de JSTL (JavaServer Pages Standard Tag Library). Por isso a aplicação precisa da API e de uma implementação dessa especificação. Você pode obter tanto a API quanto a implementação da JSTL na seguinte URL:

262

Page 263: Introducao Ao Demoiselle Framework Versao 1 PDF

https://jstl.dev.java.net/download.html

Aqui foi utilizada a versão 1.2. Após baixar o arquivo jar da biblioteca em um diretório de sua preferência, 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.

A primeira página contém apenas o link para a página de administração, ou melhor, para o formulário de autenticação.

index.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"

pageEncoding="UTF-8"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

263

Figura 26: Regras de Navegação no faces-config.xml

Page 264: Introducao Ao Demoiselle Framework Versao 1 PDF

<title>Sistema de Livraria Virtual</title>

</head>

<body>

<a href="login.jsf">Administração</a>

</body>

</html>

A página de login já contém código 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 instância dessa classe e capturar a referência para ter acesso aos métodos e atributos públicos.

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>Autenticação</title>

</head>

<body>

<h1>Autenticação</h1>

<h:form>

<h:outputText value="#{exceptionMB.exception}"/>

</h:form>

<h:form>

264

Page 265: Introducao Ao Demoiselle Framework Versao 1 PDF

<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 visão propriamente dita, a página HTML que será enviada para o navegador.

A tag h:form define um formulário controlado pelo JSF.

A tag h:outputText representa um componente gráfico, no caso um texto não editável pelo usuário. A tag h:inputText, por outro lado, é uma caixa de texto para edição. Com o atributo value, configuramos qual atributo do ManagedBean está relacionado com o componente gráfico. O JSF recuperará os valores dos atributos da instância para os componentes gráficos. Qualquer alteração dos dados de um componente de entrada implicará na modificação automática dos respectivos atributos, assim que o formulário for submetido.

A tag h:inputSecret gera um campo específico para senhas, onde os caracteres digitados não são exibidos.

265

Page 266: Introducao Ao Demoiselle Framework Versao 1 PDF

A tag h:commandButton cria um botão de submissão. Este, uma vez acionado, faz a associação dos dados dos componentes gráficos com os respectivos atributos dos ManagedBeans e executa o método especificado no argumento action.

Após o login, o usuário tem acesso ao menu de administração.

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>Módulo Administrativo</title>

</head>

<body>

<h1>Manutenção 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="Funcionários" /></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

Page 267: Introducao Ao Demoiselle Framework Versao 1 PDF

</h:form>

</body>

</html>

</f:view>

</jsp:root>

5.8.2 Páginas de Cadastro

Ao contrário da aplicação anterior, onde havia uma única página para todos os cadastros, aqui utilizaremos uma página para cada cadastro. Isso acontece porque temos um ManagedBean para cada cadastro, e ele interage com uma página própria.

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

Page 268: Introducao Ao Demoiselle Framework Versao 1 PDF

<f:subview id="listing">

<c:import url="lista-autor.jsp" />

</f:subview>

<h:form>

<h:commandLink action="#{cadastroMB.admin}">Módulo 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

Page 269: Introducao Ao Demoiselle Framework Versao 1 PDF

<f:subview id="listing">

<c:import url="lista-cliente.jsp" />

</f:subview>

<h:form>

<h:commandLink action="#{cadastroMB.admin}">Módulo 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

Page 270: Introducao Ao Demoiselle Framework Versao 1 PDF

<f:subview id="listing">

<c:import url="lista-editora.jsp" />

</f:subview>

<h:form>

<h:commandLink action="#{cadastroMB.admin}">Módulo 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 Funcionários</title>

</head>

<body>

<h1>Cadastro de Funcionários</h1>

<h:form>

<h:commandButton action="#{cadastroMB.editar}" value="Incluir" />

</h:form>

270

Page 271: Introducao Ao Demoiselle Framework Versao 1 PDF

<f:subview id="listing">

<c:import url="lista-funcionario.jsp" />

</f:subview>

<h:form>

<h:commandLink action="#{cadastroMB.admin}">Módulo 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

Page 272: Introducao Ao Demoiselle Framework Versao 1 PDF

<f:subview id="listing">

<c:import url="lista-livro.jsp" />

</f:subview>

<h:form>

<h:commandLink action="#{cadastroMB.admin}">Módulo 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

Page 273: Introducao Ao Demoiselle Framework Versao 1 PDF

<f:subview id="listing">

<c:import url="lista-local.jsp" />

</f:subview>

<h:form>

<h:commandLink action="#{cadastroMB.admin}">Módulo 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 conteúdo de outra página JSF, importada para a seção indicada. Isso permite dividir as responsabilidades entre páginas. No caso, a área de listagem é criada por uma página específica para isso, e a leitura e compreensão do arquivo fica mais fácil.

A tag h:commandLink cria um hiperlink, associado com um método de um ManagedBean.

5.8.3 Páginas de Edição

Cada página de cadastro aponta para uma página de edição.

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

Page 274: Introducao Ao Demoiselle Framework Versao 1 PDF

<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

Page 275: Introducao Ao Demoiselle Framework Versao 1 PDF

<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 Básicos</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

Page 276: Introducao Ao Demoiselle Framework Versao 1 PDF

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

Page 277: Introducao Ao Demoiselle Framework Versao 1 PDF

<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 opções da caixa de listagem a partir de uma coleção 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

Page 278: Introducao Ao Demoiselle Framework Versao 1 PDF

<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

Page 279: Introducao Ao Demoiselle Framework Versao 1 PDF

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 Funcionários</title>

</head>

<body>

<h1>Cadastro de Funcionários</h1>

<table border="0">

<tr>

<td>

<h2>Dados Básicos</h2>

<h:form>

<table>

<tr>

<td>Matrícula:</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

Page 280: Introducao Ao Demoiselle Framework Versao 1 PDF

<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

Page 281: Introducao Ao Demoiselle Framework Versao 1 PDF

<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

Page 282: Introducao Ao Demoiselle Framework Versao 1 PDF

<table border="0">

<tr>

<td>

<h2>Dados Básicos</h2>

<h:form>

<table>

<tr>

<td>ISBN:</td>

<td><h:inputText value="#{livroMB.bean.isbn}" /></td>

</tr>

<tr>

<td>Título:</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

Page 283: Introducao Ao Demoiselle Framework Versao 1 PDF

<td>Ano:</td>

<td><h:inputText value="#{livroMB.bean.ano}"/></td>

</tr>

<tr>

<td>Preço:</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

Page 284: Introducao Ao Demoiselle Framework Versao 1 PDF

<h:commandButton value="Remover autor"

action="#{livroMB.removerAutor}" />

</h:form></td>

</tr>

</table>

</body>

</html>

</f:view>

</jsp:root>

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

Page 285: Introducao Ao Demoiselle Framework Versao 1 PDF

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

5.8.4 Páginas de Listagem

Como já vimos, cada página de cadastro importa uma página de listagem. Vamos ver primeiro uma das páginas para discutir os componentes que ela utiliza.

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

Page 286: Introducao Ao Demoiselle Framework Versao 1 PDF

<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

Page 287: Introducao Ao Demoiselle Framework Versao 1 PDF

<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 constrói uma tabela HTML populada com o conteúdo de uma coleção de dados iterável. O argumento value aponta para o método que retorna a coleção, enquanto row armazena, a cada iteração, o objeto atual da coleção.

A tag h:column define uma coluna para a tabela. A tag f:facet cria um cabeçalho para a coluna.

A tag f:setPropertyActionListener atualiza um atributo, definido pelo argumento target, com o valor especificado pelo argumento value. A atualização é feita quando ocorre o componente que contém a tag sofre uma ação.

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

Page 288: Introducao Ao Demoiselle Framework Versao 1 PDF

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

Page 289: Introducao Ao Demoiselle Framework Versao 1 PDF

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

Page 290: Introducao Ao Demoiselle Framework Versao 1 PDF

</h:form>

</h:column>

</h:dataTable>

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

Page 291: Introducao Ao Demoiselle Framework Versao 1 PDF

<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="Matrícula" />

<f:selectItem itemValue="nome" itemLabel="Nome" />

<f:selectItem itemValue="apelido" itemLabel="Apelido" />

<f:selectItem itemValue="email" itemLabel="e-mail" />

</h:selectOneMenu>

291

Page 292: Introducao Ao Demoiselle Framework Versao 1 PDF

<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="Matrícula">

<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

Page 293: Introducao Ao Demoiselle Framework Versao 1 PDF

<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

Page 294: Introducao Ao Demoiselle Framework Versao 1 PDF

<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="Título" />

<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

Page 295: Introducao Ao Demoiselle Framework Versao 1 PDF

<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="TÃtulo">

<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

Page 296: Introducao Ao Demoiselle Framework Versao 1 PDF

</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="Preço" />

</f:facet>

296

Page 297: Introducao Ao Demoiselle Framework Versao 1 PDF

<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

Page 298: Introducao Ao Demoiselle Framework Versao 1 PDF

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

Page 299: Introducao Ao Demoiselle Framework Versao 1 PDF

5.9 Executando a Aplicação

Lembre-se de que uma aplicação Java é uma grande rede de dependências entre bibliotecas. Ao executar a aplicação com a opção Run/Run on Server, o Eclipse irá tentar publicar a aplicação no servidor selecionado, o que significa que ele vai transferir para o servidor todas as classes compiladas, e APIs necessárias.

No processo de desenvolvimento, isso pode falhar com mais frequência do que se possa imaginar. Então, se por acaso for lançada uma exceção alertando sobre a ausência da classe ConfigureListener, copie os arquivos .jar do JSF para o diretório de bibliotecas do servidor de aplicação. Por exemplo, no nosso caso, copiaríamos para o diretório lib do Tomcat.

A complexidade do gerenciamento de dependências no Java leva à uma questão: é necessário uma infraestrutura que ajude a manter as dependências, garantindo que tudo que foi criado no desenvolvimento passe para o ambiente de produção, e ainda que haja um controle das versões de cada API e biblioteca das quais a aplicação depende.

299

Page 300: Introducao Ao Demoiselle Framework Versao 1 PDF

5.10 Conclusões

Terminamos assim a implementação da aplicação em três camadas usando Hibernate e JSF. A estrutura final de pacotes da aplicação pode ser vista na figura 27.

Iremos comparar agora essa aplicação com a anterior, que utilizava somente Servlets/JSP e JDBC, e fazer nossas considerações.

Com o uso de Hibernate, eliminamos a dependência da aplicação com a SQL do banco de dados. Além disso, a classe HibernateUtil é muito mais simples do que a DAOEngine, porque todas as funcionalidades que precisamos criar para a última já estão disponíveis por meio do objeto de sessão da primeira.

300

Figura 27: Estrutura de pacotes da aplicação livrariajsf

Page 301: Introducao Ao Demoiselle Framework Versao 1 PDF

O Hibernate provê um controle de transação, o que permite que recuperemos o estado do banco em caso de falha.

O uso das anotações do Hibernate para o mapeamento objeto-relacional permitiu a eliminação de cinco classes da camada de modelo.

A princípio, pode parecer que a camada de controle ficou mais complexa, porque livrariajsp tinha cinco classes nessa camada, e livrariajsf tem catorze. No entanto, deve-se considerar que somente duas classes de livrariajsp, GravarServlet e RemoverServlet tinham muitos métodos, além de misturar várias regras de negócio. 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 domínio de cada problema (no caso, um determinado cadastro) foi estabelecido de forma bem definida.

As abstrações da camada de controle permitem que o programador tenha várias visões de alto nível sobre a camada de controle, focando as funcionalidades gerais, comportamentos genéricos entre alguns ManagedBeans, controle de acesso e autenticação.

O uso de JSF tornou desnecessário o pacote view, criado em livrariajsp, porque o ManagedBean provê todo o controle necessário sobre os dados da camada de visão. Assim, a camada de visão em livrariajsf é constituída tão somente de páginas Web.

As abstrações proporcionadas pelo Hibernate e pela implementação JSF permitiram, para esta aplicação livrariajsf, que o programador ficasse mais focado na implementação de regras de negócio, e menos com questões de infraestrutura. Embora alguns elementos tenham se fragmentado (classes de controle e páginas JSF), a possibilidade de reuso aumentou.

No próximo capítulo, veremos como fica ainda mais fácil lidar com essas questões de infraestrutura de aplicação com o uso de um framework integrador.

301

Page 302: Introducao Ao Demoiselle Framework Versao 1 PDF

CAPÍTULO 6

Livraria com Demoiselle FrameworkNeste capítulo, faremos a implementação dos mesmos casos de uso tratados no capítulo anterior, usando o Demoiselle Framework.

Este capítulo se concentrará em mostrar como utilizar o Demoiselle Framework, deixando a fundamentação teórica para o capítulo seguinte.

Para acompanhar este capítulo, você precisa ter instalado o Demoiselle Eclipse 3.5.

6.1 Demoiselle Framework

Por uma questão didática, e para não sobrecarregar sua mente de forma precipitada, iremos apresentar aqui apenas uma visão geral do que é o Demoiselle Framework. O próximo capítulo se aprofundará em sua arquitetura.

Demoiselle Framework é uma integração de várias tecnologias de desenvolvimento de software para Java e também consiste em uma arquitetura de referência para aplicações. Seu objetivo é prover uma infraestrutura para a construção de aplicações Java para Web.

Demoiselle Framework provê ao desenvolvedor Java independência de fornecedores, por meio da adoção de padrões aceitos mundialmente. Demoiselle Framework se baseia nas especificações do JCP, de modo a não ficar aprisionado por implementações particulares.

Podemos destacar quatro características importantes de aplicações construídas com o Demoiselle Framework:

• As camadas são independentes, porque referências a objetos são obtidas indiretamente por meio de programação orientada a aspectos (AOP).

• A aplicação é independente da interface, pois utiliza JSF.

• A aplicação é independente de banco de dados, pois utiliza JPA.

• Os serviços consumidos por todas as camadas estão disponíveis por meio de contextos.

302

Page 303: Introducao Ao Demoiselle Framework Versao 1 PDF

6.2 Demoiselle Infra

O projeto Demoiselle Infra mantém um conjunto de pacotes de software destinados a auxiliar no preparo dos ambientes de desenvolvimento, homologação baseados no Demoiselle Framework. Atualmente estão disponíveis pacotes exclusivamente para a plataforma GNU/Linux e distribuições baseadas no Debian como o Ubuntu.

Para criar nossa aplicação com Demoiselle Framework, usaremos o Demoiselle Eclipse, que está disponível como um pacote Debian. O Demoiselle Eclipse inclui o Eclipse Galileo, os plugins .m2 para Maven, AJDT para AspectJ e Demoiselle Wizard. Além disso, ele já traz todas as dependências necessárias para uma aplicação Demoiselle Framework.

A instalação do ambiente de desenvolvimento do Demoiselle (demoiselle-dev) é bastante simples para usuários Debian. O primeiro passo é incluir a seguinte linha no arquivo /etc/apt/sources.list:

deb ftp://sagres.c3sl.ufpr.br/demoiselle hardy stable

O segundo passo é atualizar os índices do APT (Advanced Package Tool) por meio do seguinte comando do terminal:

apt-get update

O último passo é instalar os pacotes do Demoiselle com o comando a seguir:

apt-get install demoiselle-dev

Isso criará no menu principal da interface gráfica um subitem dentro de 'Programação', chamado Demoiselle Eclipse 3.5.

6.3 Criação do Projeto Demoiselle Framework

Até agora, tivemos que criar a estrutura dos projetos manualmente. Com Demoiselle Framework, é possível criar um projeto a partir de uma estrutura pré-montada, um arquétipo de aplicação. Isso é possível graças ao Maven, um software para gestão de projetos escrito em Java.

O Maven pode recuperar uma estrutura definida por um arquétipo, que nada mais é que um arquivo .jar com classes que seguem um padrão pré-definido. A partir de um arquétipo, o Maven pode criar pacotes, diretórios e arquivos, com nomes e conteúdo construídos de acordo com parâmetros.

Em seu sub-projeto de componentes, o Demoiselle possui um arquétipo para aplicações Web, que já contém as dependências para os componentes mais utilizados. É o demoiselle-archetype-webapp-sample. Utilizaremos aqui a versão 1.1.0.

303

Page 304: Introducao Ao Demoiselle Framework Versao 1 PDF

O primeiro passo é criar um projeto Maven. Selecione o projeto Maven no menu File->New->Project, conforme a figura 28.

Após selecionar o tipo de projeto Maven, clique no botão Next. Você pode ignorar a tela imediatamente seguinte, pois não há nada para ser alterado. Clique em Next novamente. A próxima tela é a seleção de arquétipos. Os arquétipos são recuperados por meio de um arquivo chamado archetype-catalog.xml. O campo Catalog informa onde o Eclipse deve procurar por esse arquivo. Escolha a opção Demoiselle, que aponta para o catálogo de arquétipos residente no diretório do pacote demoiselle-dev. O diretório padrão é /opt/demoiselle/tool/maven2-local-repo.

304

Figura 28: Criação de projeto Maven

Page 305: Introducao Ao Demoiselle Framework Versao 1 PDF

Ao selecionar o catálogo, são listados todos os arquétipos registrados nele. Escolha o demoiselle-archetype-webapp-sample 1.1.0, conforme a figura 29.

Na próxima tela nós configuramos a raiz dos pacotes da aplicação, composta de duas partes, Group Id e Artifact Id, sendo este último o nome do projeto. O nosso Group Id será samples.web e o Artifact Id será livrariademoiselle, conforme a figura 30.

Após clicar no botão Finish, basta aguardar enquanto o Maven cria o projeto a partir das definições constantes no arquétipo. Após a conclusão do processo, você deve ter um projeto com a estrutura apresentada na figura 31.

Além de ter essa funcionalidade de criar projetos a partir de arquétipos, o Maven também provê um meio simples e fácil de controlar as dependências da aplicação. Todas as dependências são registradas em um arquivo chamado pom.xml. Em vez de ter o trabalho de baixar cada API ou biblioteca para um diretório do JAVA_HOME, ou adicionar pelo Build Path, basta incluir uma tag nesse arquivo com o site e a versão da dependência. O próprio Maven se encarregará de baixá-la, e manter tudo em um diretório centralizado.

No caso do demoiselle-dev, o diretório é o /opt/demoiselle/tool/maven2-local-repo. A organização das dependências em um diretório padrão ajuda os programadores,

305

Figura 29: Seleção do catálogo de arquétipos e arquétipo Maven

Page 306: Introducao Ao Demoiselle Framework Versao 1 PDF

evitando quebras na distribuição da aplicação por falta de algum arquivo. E também permite um controle eficaz dos números de versão de cada API ou biblioteca utilizadas.

Após a criação do projeto, precisamos configurar a aplicação e criar as classes de cada camada.

6.4 Camada de Persistência

6.4.1 Criando os Modelos

Iremos utilizar a abstração do Demoiselle Framework para o Hibernate. Primeiro, Tudo o que fizemos na aplicação livrariajsf para a camada de modelos será reaproveitado. As únicas mudanças serão os nomes dos pacotes e o fato de que a interface IModel herdará de IPojo, que é a interface padrão dos modelos no Demoiselle Framework.

As generalizações dos POJOs ficarão no pacote samples.web.livrariademoiselle.bean. Abaixo, a única interface alterada:

IModel

public interface IModel extends IPojo {

public Integer getId();

306

Figura 30: Parâmetros do arquétipo Maven

Page 307: Introducao Ao Demoiselle Framework Versao 1 PDF

public void setId(Integer id);

}

307

Figura 31: Estrutura de um projeto Demoiselle Framework

Page 308: Introducao Ao Demoiselle Framework Versao 1 PDF

As classes modelo concretas ficararão no pacote samples.web.livrariademoiselle.bean.implementation.

Também precisamos incluir a dependência ao banco PostgreSQL no arquivo pom.xml. Dentro da tag dependencies, inclua o seguinte trecho de código:

<dependency>

<groupId>postgresql</groupId>

<artifactId>postgresql</artifactId>

<version>8.4-701.jdbc3</version>

</dependency>

6.4.2 Criando os DAOs

Demoiselle Framework utiliza a especificação JPA para conexão com banco de dados e mapeamento objeto-relacional. Isso confere liberdade de escolha para o programador, porque qualquer implementação de JPA pode ser utilizada na camada de persistência.

Os capítulos anteriores trouxeram o fundamento necessário para a utilização do Demoiselle. Por isso não iremos agora repetir o que não for realmente necessário. Iremos mostrar como se cria um DAO no Demoiselle Framework, usando JPA, e você estará habilitado a criar os demais, pois seguem o mesmo padrão.

Demoiselle Framework realiza injeção de dependências entre camadas. Isso significa que classes específicas não são utilizadas diretamente. Em seu lugar são usadas interfaces. As interfaces dizem quais métodos são esperados, e em tempo de compilação, a implementação adequada é 'injetada' na variável de referência.

Por isso, primeiro criamos uma interface para o DAO, e então fazemos a classe que a implementa. Vamos utilizar como exemplo o DAO do modelo Autor.

A interface é a seguinte:

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

Page 309: Introducao Ao Demoiselle Framework Versao 1 PDF

A classe que implementa essa interface fica assim:

AutorDAO

public class AutorDAO extends JPAExtensionDAO<Autor> implements

IAutorDAO {

public Autor buscar(Autor autor) {

return findById(autor.getId());

}

public PagedResult<Autor> filtrar(Autor autor, Page pagina) {

return findByExample(autor, pagina);

}

public PagedResult<Autor> listar(Page pagina) {

return findByJPQL("select a FROM Autor a ORDER BY a.nome ASC", pagina);

}

public List<Autor> listar() {

return findAll();

}

}

Basta agora seguir os mesmos passos para criar os demais DAOs.

6.4.3 Testando a Camada de Persistência

O teste integrado que verifica se a camada de persistência funciona é similar ao que foi feito para a aplicação livrariajsf. Vamos indicar aqui apenas o que é diferente. Prosseguindo com o mesmo exemplo, iremos usar o teste de persistência do modelo Autor.

A primeira mudança é no atributo estático do DAO do modelo Autor. Não colocaremos mais o construtor da classe AutorDAO. Em vez disso, escreveremos assim:

@Injection

309

Page 310: Introducao Ao Demoiselle Framework Versao 1 PDF

private static IAutorDAO autorDAO;

Isso indica que o atributo autorDAO sofrerá uma injeção de dependência, ou seja, a instância será atribuída em tempo de compilação. Para que isso ocorra, é preciso que a classe de teste implemente uma interface do framework chamada IFacade.

Outra mudança é a inicialização (e encerramento) do contexto de transação. Como o teste é executado fora do framework, precisamos fazer isso explicitamente. Incluímos o código do contexto de transação nos métodos setUp e tearDown da classe de teste, que, respectivamente, são executados antes e depois de cada método de teste. Eis o código:

public void setUp()

{

WebTransactionContext.getInstance().init();

}

public void tearDown()

{

WebTransactionContext.getInstance().end();

}

As duas últimas alterações são nos nomes dos métodos utilizados. Em vez de fetchOne, usaremos buscar, pois este último é o que foi definido pela interface IAlunoDAO. E em vez do método delete para remover, usaremos remove.

6.4.4 Classes e Arquivos Auxilares da Camada de Persistência

Estamos utilizando a implementação TopLink do JPA. A seguir, temos o conteúdo do arquivo persistence.xml, que fica no diretório META-INF. Esse arquivo tem um propósito similar ao hibernate.cfg.xml do Hibernate. Ele contém a configuração do JPA para a aplicação. Além da configuração do provedor de persistência TopLink, o arquivo contém comentada a configuração para o EclipseLink, mostrando como as propriedades são padronizadas.

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

Page 311: Introducao Ao Demoiselle Framework Versao 1 PDF

<persistence-unit name="LivrariaPU" transaction-type="RESOURCE_LOCAL">

<!-- JPA Provider Definition -->

<!--

<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>

-->

<provider>oracle.toplink.essentials.PersistenceProvider</provider>

<class>samples.web.livrariademoiselle.bean.implementation.Autor</class>

<class>samples.web.livrariademoiselle.bean.implementation.Cliente</class>

<class>samples.web.livrariademoiselle.bean.implementation.Editora</class>

<class>samples.web.livrariademoiselle.bean.implementation.Funcionario</class>

<class>samples.web.livrariademoiselle.bean.implementation.ItemPedido</class>

<class>samples.web.livrariademoiselle.bean.implementation.Livro</class>

<class>samples.web.livrariademoiselle.bean.implementation.Local</class>

<class>samples.web.livrariademoiselle.bean.implementation.Pedido</class>

<class>samples.web.livrariademoiselle.bean.implementation.Telefone</class>

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

Page 312: Introducao Ao Demoiselle Framework Versao 1 PDF

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 persistência 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 dependências da implementação JPA. Para o caso da TopLink, o seguinte trecho de código deve ser incluído dentro da tag dependencies:

<dependency>

<groupId>toplink.essentials</groupId>

<artifactId>toplink-essentials</artifactId>

312

Page 313: Introducao Ao Demoiselle Framework Versao 1 PDF

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

Além disso, especificamente para o Tomcat, é necessário definir uma classe que customize a sessão para o provedor de persistência. Essa classe está referenciada no arquivo persistence.xml, é a TopLinkSessionCustomizer, cujo código é 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();

}

private void prepareForPostgreSQL() {

PostgreSQLPlatform.setShouldIgnoreCaseOnFieldComparisons(true);

}

}

313

Page 314: Introducao Ao Demoiselle Framework Versao 1 PDF

6.5 Camada de Negócio

Demoiselle Framework propõe uma estrutura diferente para camadas. Em vez de seguir estritamente o MVC, ele introduz uma camada chamada Negócio entre a Persistência e o Controle. O objetivo dessa camada é conter as regras de negócio, de forma a desacoplá-las do modelo. Assim, a camada de persistência se limita a prover os dados, sem armazenar a lógica para tratá-los.

Assim como foi para com os DAOs, os objetos de negócio são criados por classes que implementam interfaces padronizadas.

Vamos usar o modelo Autor como exemplo. As regras de negócio de Autor ficariam em uma classe chamada AutorBC, que implementaria uma classe IAutorBC, herdeira de IBusinessController. A classe de negócio utiliza injeção de dependência para manipular o DAO de Autor sem ficar preso à implementação.

IAutorDAO fica no pacote samples.web.livrariademoiselle.business enquanto AutorDAO, sua implementação, fica no pacote samples.web.livrariademoiselle.business.implementation.

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;

public void alterar(Autor autor) {

autorDAO.insert(autor);

}

314

Page 315: Introducao Ao Demoiselle Framework Versao 1 PDF

public Autor buscar(Autor autor) {

return autorDAO.buscar(autor);

}

public void excluir(Autor autor) {

autorDAO.remove(autor);

}

public PagedResult<Autor> filtrar(Autor autor, Page pagina) {

return autorDAO.filtrar(autor, pagina);

}

public void incluir(Autor autor) {

autorDAO.insert(autor);

}

public PagedResult<Autor> listar(Page pagina) {

return autorDAO.listar(pagina);

}

}

6.5.1 Controle de Acesso

Demoiselle Framework utiliza a especificação JAAS para fazer controle de permissões baseado em papéis.

Assim como fizemos na aplicação livrariajsf, podemos ter uma classe de constantes, com os nomes dos papéis. Mas em vez de utilizar uma estrutura de decisão do tipo SE... ENTÃO... SENÃO..., utilizamos uma anotação.

Supondo que tenhamos a classe AliasRole no pacote constant, podemos utiilizar a seguinte anotação para restringir o acesso ao método excluir de AutorBC:

@RequiredRole(roles={AliasRole.ADMINISTRADOR, AliasRole.REMOVEDOR})

315

Page 316: Introducao Ao Demoiselle Framework Versao 1 PDF

public void excluir(Autor autor) {

autorDAO.remove(autor);

}

6.6 Camada de Controle e Visão

A camada de Controle e Visão do Demoiselle Framework é totalmente baseada em JSF. Por isso quase tudo o que foi criado na aplicação livrariajsf será reaproveitado. Assim, só discutiremos as mudanças necessárias.

O controle de acesso as páginas não será realizado com estruturas de decisão SE... ENTÃO... SENÃO. Nenhuma página, exceto a de login, será acessível se o usuário não estiver autenticado. Para isso, todas as páginas JSF que criamos serão movidas para o diretório src/main/webapp/private/pages. No arquivo faces-config.xml basta acrescentar o prefixo /private/pages/ nas tags <to-view-id>.

Os ManagedBeans irão herdar da classe AbstractManagedBean, para que possamos fazer a injeção de dependência do objeto de negócio. Não será mais possível ter os métodos gravar e remover centralizados, por causa do mecanismo de injeção, mas sua implementação será bem simples.

A seguir temos um exemplo do ManagedBean para o modelo Autor.

AutorMB

public class AutorMB extends AbstractManagedBean {

@Injection

private IAutorBC autorBC;

private Autor bean;

private PagedResultDataModel<Autor> listAutor;

public AutorMB() {

this.bean = new Autor();

this.listAutor = new PagedResultDataModel<Autor>();

}

public void atualizaListaAutor() {

316

Page 317: Introducao Ao Demoiselle Framework Versao 1 PDF

Page primeiraPagina = new Page(getRows(), 1);

PagedResult<Autor> result = autorBC.listar(primeiraPagina);

listAutor.bind(result);

}

public void setBean(Autor bean) {

this.bean = bean;

}

public Autor getBean() {

return bean;

}

public void setListAutor(PagedResultDataModel<Autor> listAutor) {

this.listAutor = listAutor;

}

public PagedResultDataModel<Autor> getListAutor() {

return listAutor;

}

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

Page 318: Introducao Ao Demoiselle Framework Versao 1 PDF

addMessage(e.getObjectMessage(), e);

}

WebMessageContext.getInstance().clear();

return AliasNavigationRule.ALIAS_AUTOR_EDITAR;

}

public String remover() {

try {

autorBC.excluir(this.bean);

for (IMessage imsg : WebMessageContext.getInstance().getMessages()) {

addMessage(imsg);

}

} catch (ApplicationRuntimeException e) {

addMessage(e.getObjectMessage(), e);

}

WebMessageContext.getInstance().clear();

return AliasNavigationRule.ALIAS_AUTOR_LISTAR;

}

}

O método addMessage não é da classe AbstractManagedBean. Ele é importado estaticamente do seguinte pacote:

br.gov.framework.demoiselle.view.faces.util.ManagedBeanUtil

6.6.1 Autenticação

Com Demoiselle Framework, não precisamos incluir a lógica de autenticação em nossas classes de controle. Demoiselle utiliza JAAS para autenticação, assim precisamos apenas configurar os usuários e papéis no arquivo de configuração do servidor de aplicação.

No caso do Apache Tomcat, por exemplo, bastaria incluir as seguintes linhas (para o caso da aplicação livraria) no arquivo tomcat-users.xml:

318

Page 319: Introducao Ao Demoiselle Framework Versao 1 PDF

<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 Conclusão

Com o uso de Demoiselle Framework, conseguimos obter diversas vantagens em nossa aplicação, com relação ao reuso de componentes e independência de fornecedores.

Com o uso de Hibernate na aplicação anterior, já tínhamos conseguido independência do banco de dados. Agora, com o uso de JPA, ficamos independentes do provedor do mecanismo de persistência e mapeamento objeto-relacional.

Demoiselle Framework introduz a camada de Negócio, removendo tanto do Modelo quanto do Controle a responsabilidade por tratar as regras de negócio da aplicação. Essa é na verdade a camada onde deve ser gasto o maior esforço, sendo que as demais devem reaproveitar tudo o que for possível.

Como se pode notar, JSF já havia trazido melhorias com relação ao uso de JSP e Servlets. O Demoiselle Framework reaproveita todas a capacidade das implementações JSF e ainda adiciona a injeção de dependência, que torna a camada de Controle e Visão desacoplada das demais, assim como ocorre com a camada de Negócio.

As questões relativas à segurança da aplicação, como autenticação e autorização, são facilitadas pelo Demoiselle Framework graças ao uso da especificação JAAS.

Por meio do uso de padrões, Demoiselle Framework realmente facilita o desenvolvimento de aplicações Java EE e estimula a reutilização de código e componentes.

O próximo e último capítulo trará uma visão geral e profunda sobre as motivações que levaram à criação do Demoiselle Framework, e também discorrerá de forma mais profunda sobre sua arquitetura.

De qualquer modo, para maiores informações sobre o Demoiselle Framework, o ponto de partida é o portal www.frameworkdemoiselle.gov.br.

319

Page 320: Introducao Ao Demoiselle Framework Versao 1 PDF

CAPÍTULO 7

Demoiselle: Framework de ArquiteturaNeste capítulo, apresentamos os fundamentos do framework Demoiselle. Inicialmente discutirmos os elementos de diversidade, decisão e a estratégia de governo para a área de tecnologia que serviram de insumo para o estabelecimento de um projeto de infraestrutura de software que definiu uma arquitetura de referência para a criação de aplicações.

Será abordada a característica de desenvolvimento colaborativo do projeto Demoiselle, assim como a questão do reuso de componentes. A estrutura do projeto e suas motivações serão apresentadas, bem como a arquitetura do framework. Neste ponto, todos os módulos que o compõem serão vistos com detalhes.

7.1 Fundamentos

7.1.1 Diversidade

Segundo Mariaca (2009), “a diversidade é uma das palavras de ordem na empresa moderna, entendida como o estilo estrutural da organização que representa alto valor estratégico e elemento de competitividade”.

O desenvolvimento de tecnologia da informação para governo, como um negócio, envolve diversidade. Os elementos dessa diversidade podem ser:

Equipes: desenvolvimento de sistemas, administração de centro de dados, infraestrutura de redes, logística, recursos humanos, consultoria jurídica, etc.

Clientes: executivo, legislativo, judiciário, ministérios, empresas, autarquias, fundações, universidades, etc.

Plataformas: livres e proprietárias, abertas e fechadas, de grande e pequeno porte, Web, desktop, dispositivos móveis, etc.

Demandas: sistemas corporativos, críticos, em tempo real, integração de sistemas, manutenção do legado, etc.

Pessoas: há uma miríade de itens diversificados quando se trata de pessoas, entre formação profissional, experiência de vida, crenças pessoais, vocação, etc.

320

Page 321: Introducao Ao Demoiselle Framework Versao 1 PDF

Recursos: a única certeza em relação aos recursos é que eles serão limitados. Fora isso, dependen do da combinação dos elementos anteriores, pode-se ter mais ou menos disponibilidade de recursos.

7.1.2 Elementos de decisão

5 pilares foram escolhidos pelo Serpro para definir a estratégia de tecnologia de tecnologia do governo: os atores institucionais, o conhecimento maduro, a integração, a escalabilidade e a sustentabilidade.

Os atores institucionais são o servidor público, que é visto como o trabalhador do conhecimento, as firmas e o cidadão. A tecnologia da informação deve proporcionar a esses três globalidade, conexão, mobilidade e facilidade de acesso.

O conhecimento maduro é preferível às soluções ad hoc. A preocupação em criar com componentes reutilizáveis deve permear as decisões sobre estratégia tecnológica.

Com relação a integração, ela se refere a três aspectos: a eliminação do insulamento de sistemas, a busca e recuperação de informação e a preservação de identidade e privacidade.

O crescimento econômico e social se reflete na necessidade do crescimento dos sistemas de informação. O governo não tem condições reais de fazer sua estrutura de atendimento crescer fisicamente na proporção que a população necessita, e precisa do suporte de sistemas de informação que possibilitem o chamado governo eletrônico. A preocupação do pilar da escalabilidade é garantir a manutenção dos serviços disponíveis por meios eletrônicos e adição de novos sem que haja uma perda de performance que se reflita em um atendimento ruim para o cidadão.

A estratégia de tecnologia também deve ser sustentável ao longo do tempo, de modo que o Estado não tenha de onerar a população para manter a qualidade e disponibilidade dos serviços.

7.1.3 Estratégia para a área de tecnologia

Os elementos de decisão abordados anteriormente serviram de base para definir uma estratégia tecnológica para o governo. Essa estratégia consiste em quatro objetivos: evoluir a cultura de desenvolvimento de aplicações, criar uma capacidade de manutenção e extensão própria, definir uma infraestrutura tecnológica de aplicações e criar um alinhamento com o movimento de software livre.

Com relação a cultura de desenvolvimento de aplicações, a estratégia do Serpro é praticar um desenvolvimento compartilhado, integrado, produtivo e gerenciável, que faça uso preferencial de tecnologias abertas.

321

Page 322: Introducao Ao Demoiselle Framework Versao 1 PDF

O governo trabalha com muitas restrições de recursos, e não é possível depender de terceiros para manter sistemas de informação ou estender suas funcionalidades. Pois isso a necessidade do governo de ter a capacidade de realizar essas tarefas por conta própria, por meio dos órgãos e empresas desenvolvedores de sistemas.

A criação, manutenção e integração de sistemas é um processo contínuo, o que certamente gera experiência e conhecimento. Por outro lado, os aspectos ligados a infraestrutura das aplicações não interessam ao cliente, mas são alvo de esforço por parte do desenvolvedor. Finalmente, segundo Pacitti (2006, p. 45), “o software (...) torna-se cada vez mais complicado, caro, menos confiável, difícil de configurá-lo e mantê-lo”. Em função disso, a definição de uma infraestrutura mínima para a criação de aplicações permite que o desenvolvedor ignore os detalhes de baixo nível, mantendo o foco na implementação das regras de negócio.

Conforme os requisitos definidos para o governo eletrônico (www.governoeletronico.gov.br), essa infraestrutura de aplicações deve ser distribuída, escalável e habilitada para a Web (mas não restrita à mesma), que proporcione alta disponibilidade, seja preparada para contingência e tenha um baixo custo de propriedade.

Por último, mas não menos importante, a estratégia 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 decisões dos governos e da administração pública dependerem dos sistemas de software”. Ele afirma que a opção de alguns países desenvolvidos pelo software livre se dá pelos aspectos técnicos, econômicos e de segurança nacional do mesmo.

De acordo com Pacitti (2006, p. 41), o software livre proporciona aos governos:

● “Maior segurança e confiança no sistema de software in house”;

● “Diminuição da dependência econômica e tecnológica causada pelo uso de softwares produzidos fora do país”;

● “Economia trazida pelo fato de o software ser produzido por poucos órgãos públicos especializados de excelência e daí poder ser o mesmo software utilizado e aproveitado pelos demais órgãos não especializados do mesmo governo”.

7.1.4 Diretivas da arquitetura tecnológica

Para cumprir os objetos estratégicos da seção anterior, fez-se necessária a definição de uma arquitetura de referência para aplicações. Segundo Fowler (2006, p. 24), “a arquitetura é subjetiva, uma compreensão do projeto de um sistema compartilhada pelos desenvolvedores experientes em um projeto. Esta compreensão compartilhada frequentemente se apresenta na forma dos componentes mais importantes do sistema

322

Page 323: Introducao Ao Demoiselle Framework Versao 1 PDF

e de como eles interagem. Também diz respeito a decisões, pois os desenvolvedores gostariam de tomar as decisões certas desde o início, já que elas são vistas como difíceis de alterar”.

A subjetividade pode levar a criação de uma miríade de arquiteturas diferentes, para projetos diferentes de equipes diferentes, que, no entanto, estão em uma mesma estrutura governamental. O reuso e consequentemente a manutenção são prejudicados. Uma arquitetura de referência dá aos desenvolvedores um ponto de partida e induz a uma compreensão uniformizada dos sistemas de informação.

A arquitetura de referência definida para a criação da infraestrutura de aplicações do governo estabeleceu oito diretivas:

1. Computação distribuída

2. Aplicações baseadas em componentes

3. Processos orientados a eventos

4. Acoplamento fraco de funções de negócio

5. Infraestrutura para suporte a decisões

6. Automação de processos

7. Acesso por Internet

8. Software livre

7.1.4.1 Computação distribuída

Segundo Casetti et al [CaAK93], um sistema distribuído “constitui-se de um conjunto de processadores autônomos conectados através de um subsistema de comunicação, que cooperam-se através da troca de mensagens. Esse tipo de sistema deve apresentar duas características inerentes: 1) a transparência na sua utilização, ou seja, a capacidade de apresentar-se aos seus usuários como uma entidade única, e 2) o alto grau de tolerância a faltas (falhas)”.

Ainda segundo o mesmo autor, existem em geral “três aspectos de software que podem estar distribuídos: dados, programas e controle.

No tocante a distribuição de dados, pode-se ter: sistemas de arquivos distribuídos e sistemas de banco de dados distribuídos.

No que diz respeito a programas, pode-se ter: programas centralizados e programas distribuídos. Um programa centralizado é aquele que é executado em uma arquitetura na qual cada um dos processadores pode executar qualquer instrução desse programa. Já um programa distribuído é aquele que se encontra espalhado por várias memórias

323

Page 324: Introducao Ao Demoiselle Framework Versao 1 PDF

primárias, sendo que cada uma é acessada por um processador diferente, o qual executa a parte do programa que se encontra na memória primária a ele associada.

A distribuição do controle está relacionada diretamente com a distribuição do próprio sistema operacional. O que distingue um sistema distribuído de um sistema de arquitetura clássica é a distribuição do controle. O controle é centralizado quando a execução de um programa, em qualquer instante, está sob os cuidados de um único elemento processador. Já, quando o controle é distribuído, a execução de um programa está sob os cuidados de mais um elemento processador.”

A diretiva de computação distribuída da arquitetura de referência visa estabelecer uma infraestrutura de software escalável, preparada para o aumento de usuários, para o crescimento dos dados persistentes e para a expansão do escopo de funcionalidades. Ela advém também da distribuição geográfica do governo brasileiro, em um país de grande extensão territorial. Além disso, visa criar aplicações com suporte para diversas tecnologias, baseadas em padrões de interoperabilidade e alinhadas com a e-Ping (http://www.governoeletronico.gov.br/acoes-e-projetos/e-ping-padroes-de-interoperabilidade).

7.1.4.2 Aplicações baseadas em componentes

O esforço de desenvolvimento de software exige diversos recursos, como pessoas e ambiente de desenvolvimento (ferramentas de hardware e de software). Um terceiro recurso torna-se decisivo para diminuir o esforço de desenvolvimento: os componentes de software reusável.

A arquitetura de referência orienta a prática da engenharia de software baseada em componentes. Pressman (2006, p. 323) afirma que essa engenharia “enfatiza reusabilidade – isto é, a criação e o reuso de blocos construtivos de software. Esses blocos construtivos, frequentemente chamados componentes, devem, ser catalogados para facilitar a referência, padronizados para facilitar a aplicação e validados para facilitar a integração.”

Deve-se buscar a construção de componentes de experiência plena que, segundo Pressman, são “especificações, projetos, código ou dados de teste existentes desenvolvidos para projetos anteriores, que são similares ao software a ser construído para o presente projeto.” Essa categoria de componentes exige modificações de risco relativamente baixo.

7.1.4.3 Processos orientados a eventos

Processos orientados a eventos referem-se ao uso de uma arquitetura orientada a serviços. Processo, neste caso, “é um conjunto de tarefas que é iniciada por um evento, transforma informação e produz uma saída. Algumas atividades podem ser

324

Page 325: Introducao Ao Demoiselle Framework Versao 1 PDF

condicionais, ou alternativas, ou rodar em paralelo, ou precisam ser repetidas; raramento é uma simples cadeia” (Allen, 2007)

A implementação dos processos orientados a evento promove a diminuição do emprego de transferência de arquivos e filas de entrada, de processos dinâmicos e o desenho (projeto) simples de aplicações.

7.1.4.4 Acoplamento fraco de funções de negócio

Segundo Miller (2009), “acoplamento entre classes ou subsistemas é uma medida da interconexão entre essas classes ou subsistemas. O acoplamento forte significa que as classes relacionadas precisam conhecer detalhes internos umas das outras, as alterações se propagam pelo sistema, e o sistema é potencialmente mais difícil de entender.”

O acoplamento fraco, em contrapartida, tem como metas:

1. Tornar o código mais fácil de ler;

2. Tornar as classes mais simples para o consumo de outros desenvolvedores, ocultando a parte "feia" do funcionamento interno em APIs bem projetadas;

3. Isolar possíveis alterações em uma área pequena do código;

4. Reutilizar classes em contextos completamente novos.

7.1.4.5 Infraestrutura para suporte a decisões

O suporte à decisões significa que a infraestrutura de software deve gerar aplicações que forneçam ao servidor público, encarado como um “trabalhador do conhecimento”, acesso às informações de que necessita para a tomada de decisões. A plataforma de construção de software deve facilitar a recuperação de informações de situação, exceções, registros históricos e qualquer conhecimento relevante ao servidor público, usuário do sistema. A infraestrutura deve criar meios para implementação de inteligência computacional, que capacite as aplicações a fazerem associações de dados e predição com base nos mesmos.

7.1.4.6 Automação de processos

Com relação à automação de processos, a plataforma de construção de software deve facilitar o gerenciamento de processos de negócio, permitir transações de longa duração, possibilitar a integração com barramentos de serviços. A plataforma também deve permitir a composição de processos de negócio por meio de web services, seja por coreografia ou orquestração.

Segundo Peltz (2003), a orquestração refere-se a um processo de negócio executável que pode interagir com Web services internos e externos. A orquestração descreve

325

Page 326: Introducao Ao Demoiselle Framework Versao 1 PDF

como Web services podem interagir em um nível de mensagem, incluindo a lógica de negócios e a ordem de execução das interações. Essas interações podem abranger aplicações e/ou organizações, e resultar em um processo transacional de vida longa. Com a orquestração, o processo sempre é controlado da perspectiva de uma das partes do negócio.

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 interação. A coreografia segue uma sequencia de mensagens que pode envolver múltiplas partes e múltiplas fontes. Ela é associada com as trocas de mensagem públicas que ocorrem entre múltiplos Web services.

Peltz afirma que a orquestração difere da coreografia quando descreve um fluxo de processo entre serviços, controlado por uma única parte. Na coreografia nenhuma parte verdadeiramente é dona da conversa.

7.1.4.7 Acesso por Internet

Dada a existência de um enorme legado de aplicações de governo, sendo que a possibilidade de migração é muito remota, a integração dos sistemas legados com interfaces Web é necessária para permitir melhor navegabilidade, usabilidade e consolidação de dados de fontes diversas, além do acesso a partir de qualquer lugar, por meio de conexão a Internet e disponibilidade de um navegador.

O acesso por Internet também faz parte das políticas gerais descritas no Documento de Referência da e-PING (Brasil, 2008), o qual afirma que “todos os sistemas de informação da administração pública deverão estar alinhados com as principais especificações usadas na Internet e com a World Wide Web”, e ainda que os navegadores (browsers) devem constituir o principal meio de acesso a esses sistemas. O documento diz que “todos os sistemas de informação de governo deverão ser acessíveis, preferencialmente, por meio de tecnologia baseada em browser”, sendo que “outras interfaces são permitidas em situações específicas, como em rotinas de atualização e captação de dados onde não haja alternativa tecnológica disponível baseada em navegadores. ”

O acesso por Internet também coloca para a infraestrutura de software os desafios de permitir a navegação baseada em semântica, por meio de aplicações sensíveis ao modo de navegação (Web 2.0) e ao contexto (Web 3.0). As aplicações podem ter interfaces baseadas em navegador, para acesso direto por usuários, ou interfaces de aplicações (web services) para integração com outros sistemas.

7.1.4.8 Software Livre

A infraestrutura de software deve ser constituída preferencialmente de software livre e deve ser licenciada como software livre de modo que proporcione:

326

Page 327: Introducao Ao Demoiselle Framework Versao 1 PDF

● Compartilhamento de soluções;

● Desenvolvimento em rede;

● Redução de custos;

● Simplificação de práticas de segurança;

● Gerenciamento compartilhado de conhecimento;

● Estímulo à inovação;

● Sustentabilidade a longo prazo.

7.1.5 Demoiselle com Desenvolvimento Colaborativo

Um das acepções da palavra colaborar, segundo o dicionário Michaelis, é “cooperar para a realização de qualquer coisa”. O mesmo dicionário afirma que cooperar, por sua vez, significa “agir ou trabalhar junto com outro ou outros para um fim comum”. Nesse entendimento, a princípio, todo desenvolvimento de software que envolva trabalho em equipe é colaborativo.

No entanto, o termo desenvolvimento colaborativo, como é utilizado pelas comunidades de software livre e aberto, tem um significado mais restrito. Para chegar a esse significado, vamos fazer uma breve análise do desenvolvimento de software, revendo alguns conceitos de capítulos anteriores.

Pressman (2006, p. 1) afirma que o software de computador é um produto. No entanto, o mesmo autor reconhece que o software não é fabricado no sentido clássico da engenharia, mas apenas desenvolvido por um processo de engenharia, e não se desgasta com o tempo, apesar de se deteriorar. A deterioração de software é o crescimento da introdução de falhas pela manutenção ao longo do tempo.

Sommerville (2007, p. 3) refina o conceito de Pressman, afirmando que o software em si não é um produto, pois é “abstrato e intangível”, “não é limitado por materiais ou controlado por leis da física ou por processos de manufatura”. O autor declara que o software gera produtos, que podem ser comercializados, na forma de um sistema de software. Em sua visão (2007, p. 4), um sistema de software consiste geralmente de um conjunto de programas, arquivos de configuração utilizados para configurar esses programas, documentação do sistema, documentação do usuário e sítios Web com informações atualizadas sobre o produto.

Os produtos de software podem ser genéricos ou feitos sob encomenda. No primeiro caso, a organização que os desenvolve controla sua especificação. No segundo, a especificação é desenvolvida e controlada pelo comprador do sistema de software.

327

Page 328: Introducao Ao Demoiselle Framework Versao 1 PDF

Independentemente do tipo de produto de software, Somerville (2007, p. 275) afirma que no final do primeiro milênio da Era Cristã e nos anos iniciais do segundo, houve um movimento da indústria 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 produção e manutenção de software, entregas mais rápidas de sistemas e aumento da qualidade do software”. Segundo ele, “cada vez mais empresas veem seu software como um ativo valioso e estão promovendo o reuso para aumentar seu retorno sobre investimentos em software”.

Para, Somerville (2007, p. 276), o reuso traz como benefícios o aumento de confiança no software, a redução do risco de processo, o uso eficiente de especialistas, a conformidade com padrões e a aceleração do desenvolvimento. Mas o mesmo autor alerta (Somm07, p. 277) para os problemas que podem advir com o reuso, se não houver planejamento. Vamos destacar alguns deles.

Um dos problemas é o aumento do custo de manutenção, provocado pela indisponibilidade do código-fonte de um sistema ou componente de software reusável. Uma solução para isso é a adoção preferencial de softwares livres, que trazem como pressuposto o código-fonte aberto.

Outro problema é a criação e manutenção de uma biblioteca de componentes reusáveis. E aqui tocamos num ponto crucial. Além das questões que envolvem as técnicas de classificação, catalogação e recuperação de componentes, e da compreensão e adaptação dos mesmos, está a replicação de componentes.

Se uma equipe de uma empresa cria uma biblioteca de componentes reusáveis, ela deve ficar disponível para que todos os desenvolvedores da empresa possam utilizá-la. Além disso, é preciso permitir que os usuários dos componentes possam contribuir com sua evolução. Se os usuários não puderem ver mudanças de que necessitam implementadas nos componentes existentes, eles criarão novos componentes. A dimensão do problema pode se tornar maior se o código-fonte não estiver disponível, o que implicará na reescrita de funcionalidades já implementadas.

Se o desdobramento de um projeto de componente reusável em vários pode ocorrer dentro de uma organização, que dirá entre organizações 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 experiências e beneficiando-se da expertise de cada uma.

Para que a segunda estratégia tenha êxito, um fator importante é a motivação dos contribuidores do software. Na visão tradicional da Engenharia de Software, os desenvolvedores criam código por imposição, em seu horário de trabalho. O modelo de desenvolvimento colaborativo propõe uma nova abordagem, onde o desenvolvedor contribui espontaneamente com código, em seu tempo livre. E, uma vez que o software seja livre, esse desenvolvedor não precisa pertencer ao quadro da empresa.

328

Page 329: Introducao Ao Demoiselle Framework Versao 1 PDF

Isso abre possibilidades para que uma companhia tenha a sua disposição um número maior de desenvolvedores do que poderia manter em uma estrutura formal. E aqui chegamos ao ponto que abrimos no início desta seção: o significado de desenvolvimento colaborativo para as comunidades de software livre.

Os desenvolvedores que contribuem com o software em seu tempo livre e que não fazem parte da empresa não são obrigados a contribuir. Segundo Taurion (2004), eles o fazem por status, satisfação pessoal e benefícios indiretos, como empregabilidade em outras empresas. Segundo pesquisa realizada por Lakhani (2005, p. 27), 44,9% dos contribuidores de comunidades de software livre escrevem código para os projetos porque acham isso intelectualmente estimulante.

Mas Fogel (2007, p. 126) afirma que essas razões não constituem toda a verdade. Segundo ele, as pessoas gastam tempo em software livre por razões que excedem um desejo abstrato de produzir bom código. Ele diz que além dessa motivação, e do desafio com valor educacional de trabalhar em problemas difíceis, está o desejo intrínseco 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 ações que incentivem a colaboração e o compartilhamento do conhecimento. O Demoiselle Framework e seus subprojetos são mantidos em um ambiente de desenvolvimento colaborativo, no Sourceforge, com ferramentas que permitem o controle de versões, o registro e acompanhamento de bugs e novas funcionalidades, a comunicação por meio de listas de discussão, fóruns e wikipages e a criação de desdobramentos (ramos) do projeto, como estímulo a inovação.

7.1.6 Orientação a Especificações

Segundo McLaughlin, Pollice e West (2007, p. 81), a única constante da análise e projeto de software, a única coisa com que se pode contar quando se escreve software, é a mudança. De acordo com os autores, não importa se uma aplicação foi maravilhosamente bem projetada, pois com o tempo ela sempre crescerá e mudará. Novas soluções para problemas serão descobertas, as linguagens de programação evoluirão e os clientes irão aparecer com novos requisitos que obrigarão a alteração do código das aplicações.

Nesse cenário, criar uma infraestrutura de software para padronizar o desenvolvimento e promover o reuso de componentes torna-se um grande desafio. Como construir algo genérico o suficiente, mas ao mesmo tempo flexível para administrar a manutenção de software?

Tendo em vista essa questão, o projeto Demoiselle tomou como um de seus princípios o fundamento da arquitetura em especificações tecnológicas, e não em

329

Page 330: Introducao Ao Demoiselle Framework Versao 1 PDF

implementações específicas de software. As especificações utilizadas como fundamento para o Demoiselle são as JSRs.

Segundo a Apache Foundation (2009), uma JSR (Java Specification Request) é um veículo formal pelo qual as tecnologias Java são criadas ou atualizadas. Uma JSR é proposta por qualquer membro do JCP (Java Community Process), que então fica conhecido como líder da especificação. O líder da especificação organiza um grupo de especialistas e esse grupo trabalha para criar a especificação. O grupo de especialistas deve criar também uma implementação de referência assim como um kit de compatibilidade de tecnologia.

O JCP é a organização que governa as tecnologias Java. Inicialmente criada pela Sun Microsystems, ela é composta de um comitê executivo formado por 32 representantes de empresas, indivíduos e acadêmicos que representam milhares de membros. Por meio do JCP, é que as JSRs são criadas.

Uma especificação tecnológica permite independência de fornecedor, pois sendo aberta, qualquer um pode fazer sua implementação, que deverá operar seguindo os mesmos padrões. Além disso, a manutenção de uma especificação por um organismo independente (JCP é uma organização não governamental), garante estabilidade aos padrões estabelecidos. A especificação diz o que a tecnologia deve fazer, e não o como. Assim, ela consegue manter um padrão ao mesmo tempo que permite a evolução tecnológica.

Além das especificações ditadas pelas JSRs, o Demoiselle se orienta para o uso de APIs que fazem parte da distribuição padrão da tecnologia Java, de modo a evitar a dependência direta de componentes de terceiros. As especificações e APIs que fundamentam o Demoiselle são JAAS, JCA e JCE, JSF e Servlet.

7.1.6.1 JAAS

O texto a seguir é uma tradução livre do guia de referência do JAAS, disponível na URL:

http://java.sun.com/javase/6/docs/technotes/guides/security/jaas/JAASRefGuide.html.

“O serviço de autenticação e autorização Java (JAAS) foi introduzido como um pacote opctional (extensão) para o Java 2 SDK, Standard Edition (J2SDK), v 1.3. JAAS foi integrado ao J2SDK 1.4.

JAAS pode ser usado para dois propósitos:

Para autenticação de usuários, para determinar de modo seguro e confiável quem está atualmente executando o códugo Java, independente do código estar rodando em uma aplicação, um applet, um bean, ou um servlet;

Para autorização de usuários para garantir que eles tem os direitos de controle de acesso (permissões) requeridas para fazer as ações realizadas.

330

Page 331: Introducao Ao Demoiselle Framework Versao 1 PDF

JAAS implementa uma versão Java do framework padrão Pluggable Authentication Module (PAM). (...).

Tradicionalmente, Java provê controle de acesso baseado em código-fonte (controle de acesso baseado em de onde o código veio e quem assinou o código). Faltava, entretanto, a habilidade adicional de impor controles de acesso baseados em quem executa o código. JAAS provides provê um framework que aumenta a arquitetura de segurança de Java com tal suporte.

A autenticação JAAS é executada de um modo plugável. Isso permite que aplicações permaneçam independentes de tecnologias de autenticação subjacentes. Tecnologias de autenticação novas ou atualizadas podem ser plugadas na aplicação sem obrigar modificações na própria aplicação. Aplicação habilitam o processo de autenticação pela instanciação de um objeto LoginContext, que por sua vez referencia um Configuration para determinar a(s) tecnologia(s) de autenticação, ou os LoginModule(s), a serem usados para executar a autenticação. LoginModules típicos devem estar prontos para ler verificar um nome de usuário e senha. Outros podem ler e verificar uma voz ou uma impressão digital, por exemplo.

Uma vez que o usuário ou serviço executando o código tenha sido autenticado, o componente de autorização JAAS trabalha em conjunto com o modelo de controle de acesso do Java SE para proteger o acesso a recursos sensíveis. Ao contrário da J2SDK 1.3 e anteriores, onde as decisões de controle de acesso são baseadas somente na localidade do código e nos assinantes do código (um objeto CodeSource), na J2SDK 1.4 as decisões de controle de acesso são baseadas tanto na execução de código CoudeSource quanto no usuário ou serviço rodando o código, que é representado por um objeto Subject. Subject é atualizado por um LoginModule com objetos Principals relevantes e credenciais se a autenticação foi bem sucedida”.

Um objeto CodeSource estende o conceito de um codebase para encapsular não somente a URL, mas também os certificados que foram usados para verificar a assinatura de código originária daquela localização.

7.1.6.2 JCA e JCE

O texto a seguir é uma tradução livre do guia de referência da JCA, disponível na URL http://java.sun.com/j2se/1.4.2/docs/guide/security/CryptoSpec.html.

“A API Security é uma API central da linguagem de programação Java, construída em torno do pacote java.security (e seus subpacotes). Esta API é projetada para permitir que os desenvolvedores incorporem tanto funcionalidades de baixo nível quanto de alto nível em seus programas.

O primeiro lançamento da API Security na JDK 1.1 introduziu a "Java Cryptography Architecture" (JCA), um framework para acessar e desenvolver funcionalidades

331

Page 332: Introducao Ao Demoiselle Framework Versao 1 PDF

criptográficas para a plataforma Java. Na JDK 1.1, a JCA incluía APIs para assinaturas digitais e sumários de mensagens.

Nos lançamentos sebsequentes, a Java 2 SDK estendeu de forma significativa a Java Cryptography Architecture, como descrito neste documento. Ela também 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 configurável e controle de acesso extensível.

A Java Cryptography Architecture engloba as partes da API Java 2 SDK Security relacionadas a criptografia, assim como um conjunto de convenções e especificações fornecidas neste documento. Isso inclui um provedor de arquitetura que permite implementações de criptografia múltiplas e interoperáveis.

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 (extensão) para a Java 2 SDK, Standard Edition, versões 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 tradução livre de excertos da especificação Servlet 2.5, disponível na URL:

http://jcp.org/aboutJava/communityprocess/mrel/jsr154/index.html

Essa especificação define os conceitos, arquitetura e padrões para servlets e servlet containers.

Segundo a especificação JSR 154, “um servlet é um componente Web baseado em tecnologia Java, gerenciado por um recipiente, que gera conteúdo dinâmico. Como outros componentes baseados em tecnologia Java, servlets são classes Java independentes de plataforma que são compilados para um código 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, são extensões do servidor Web que oferecem funcionalidade de servlet. Servlets interagem com os clientes Web através de um paradigma de solicitação/resposta implementado pelo servlet container”.

A mesma especificação detalha o conceito de servlet container. “O servlet container é uma parte de um servidor Web ou servidor de aplicação que fornece o serviços de rede através da qual os pedidos e as respostas são enviadas, decodifica pedidos baseados em MIME, e formata respostas baseadas em MIME. Um servlet container também contém e gera servlets através do seu ciclo de vida.

332

Page 333: Introducao Ao Demoiselle Framework Versao 1 PDF

Um servlet container pode ser construído em um servidor Web, ou instalado como um componente suplementar do servidor Web através dessa API de extensão nativa do servidor API. Servlet con tainers também podem ser construídos ou, eventualmente, instalados em servidores de aplicação habilitados para Web.

Todos os servlet containers devem suportar HTTP como um protocolo para solicitações e respostas, mas protocolos adicionais de solicitação/resposta, tais como HTTPS (HTTP sobre SSL) podem ser suportados. As versões exigidas das especificações HTTP que um container deve implementar são 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 solicitações 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 solicitações, sem entregá-las para o servlet de acordo com a RFC2616.

Um servlet container pode impor restrições de segurança 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 restrições devem ser colocadas usando a arquitetura de permissão definida pela plataforma Java 2. Por exemplo, servidores de aplicação high-end podem limitar a criação de um objeto Thread para garantir que outros componentes do container não serão afetados de forma negativa.

J2SE 1.3 é a versão mínima da plataforma Java com as quais servlet containers devem ser construídos.”

7.1.6.4 JSF

O texto a seguir é uma tradução livre da página da JSF, disponível na URL http://java.sun.com/javaee/javaserverfaces.

“Desenvolvida através do Java Community Process sob JSR - 314, tecnologia JavaServer Faces estabelece o padrão para a construção de interfaces de usuário do lado servidor. Com as contribuições do grupo de peritos, as APIs JavaServer Faces estão sendo projetadas de modo que possam ser aproveitadas por ferramentas que irão tornar o desenvolvimento de aplicativos web ainda mais fácil. Vários fornecedores de ferramentas foram respeitados membros do grupo de especialistas da especificação JSR-314, que desenvolveu o JavaServer Faces 1.0. Esses fornecedores estão comprometidos em apoiar a tecnologia JavaServer Faces em suas ferramentas, promovendo a adoção do padrão de tecnologia JavaServer Faces.”

“A tecnologia JavaServer Faces inclui:

● Um conjunto de APIs para representar os componentes UI e gerenciar o seu estado, a manipulação de eventos e validação de entrada, definição de navegação da página, e apoiar a internacionalização e acessibilidade.

333

Page 334: Introducao Ao Demoiselle Framework Versao 1 PDF

● Uma biblioteca de tags JavaServer Pages (JSP) personalizada para expressar uma interface JavaServer Faces dentro de uma página JSP.

Projetada para ser flexível, a tecnologia JavaServer Faces aproveita a interface padrão 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 incluídas com a tecnologia JavaServer Faces encapsulam a funcionalidade do componente, e não a apresentação específica de um cliente, permitindo assim que os componentes JavaServer Faces UI a ser prestado a vários dispositivos do cliente. Ao combinar a funcionalidade do componente de UI com geradores customizados, que geram atributos para um componente específico do usuário, os desenvolvedores podem construir tags personalizadas para um dispositivo cliente em particular. Como uma conveniência, 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 aplicações.

Como a facilidade de uso é o principal objetivo, a arquitetura do JavaServer Faces define claramente uma separação entre a lógica da aplicação e a apresentação enquanto torna fácil conectar a camada de apresentação com o código 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 também oferece um modelo de programação simples para juntar os partes. Por exemplo, os desenvolvedores de página web sem conhecimentos de programação podem usar os componentes de tags UI de JavaServer Faces para ligar o código do aplicativo com uma página web sem escrever scripts. ”

7.2 Estrutura do Projeto Demoiselle

O projeto Demoiselle está estruturado de forma a proporcionar fraco acoplamento de funcionalidades e reuso por meio de componentes.

Figura 32: Projeto Demoiselle Framework e subprojetos

334

Page 335: Introducao Ao Demoiselle Framework Versao 1 PDF

O framework, a infraestrutura básica para criação de aplicações, é o projeto principal. Sua arquitetura será detalhada no próximo capítulo. Em linhas gerais, o framework oferece uma interface que integra e abstrai o uso de várias tecnologias baseando-se em especificações.

A evolução das especificações Java deve manter o framework com um mínimo de implementação, ou até mesmo reduzi-la. As implementações dos outros subprojetos, por outro lado, tendem a crescer.

Para estabelecer padronização, sem criar impedimentos para a evolução tecnológica, o framework concentra-se em definir uma arquitetura de referência. Ela foi construída a partir do conhecimento e da experiência de um grupo de arquitetos, tendo como base as especificações de interoperabilidade da e-PING e um direcionamento estratégico do uso de tecnologias.

Segundo Hofmeister (2000, p. 8), uma arquitetura de referência define tipos de elementos de uma arquitetura e como eles interagem. Ela também define um mapeamento de funcionalidades para elementos arquiteturais. A arquitetura de referência é uma especificação, portanto. Assim, é necessário um produto de software, que implemente os padrões definidos por ela. O framework é esse produto. Mas além de implementar a arquitetura de referência, o framework também serve para evoluir a mesma. A utilização do framework, ao longo do tempo, gera questionamentos, que são usados para avaliar e manter a arquitetura.

Figura 33: Insumos e produto da arquitetura de referência

Hofmeister (2000, p. 3) afirma que “embora ter uma boa arquitetura de software não garante que um produto atenda seus requisitos, ter uma arquitetura pobremente projetada ou mal definida torna impossível atender os requisitos do produto”. A arquitetura poupa tempo ao desenvolvedor, quando define tecnologias a serem utilizadas, dentro de uma miríade de possibilidades. Ela poupa tempo do projetista, quando oferece a estrutura básica para uma aplicação. Ela ajuda nas tomadas de decisões do projeto, pois cria um escopo bem definido de questões a serem tratadas. E ela cria condições para o reuso ao estabelecer padrões de implementação.

335

Page 336: Introducao Ao Demoiselle Framework Versao 1 PDF

Figura 34: Produtos da arquitetura

Cinco projetos orbitam ao redor do framework: Demoiselle Components, Demoiselle Wizard, Demoiselle Sample, Demoiselle Process e Demoiselle Infra.

Demoiselle Components abriga a construção de componentes fracamente acoplados ao framework. Isso quer dizer que o framework não depende dos componentes. Os componentes, por outro lado, podem ser dependentes do framework, se fizerem uso de seus padrões. Não é possível obrigar todos os componentes a implementar interfaces e classes abstratas do framework, ou fazer uso de suas anotações e aspectos, porque nem sempre isso é necessário para prover funcionalidades em nível mais alto. Isso permite que componentes também possam ser utilizados de forma independente do framework, de forma que o reuso de código não dependa exclusivamente da infraestrutura.

Demoiselle Wizard consiste na produção de ferramentas que facilitem a criação de aplicações que implementem os padrões Demoiselle por meio da geração e edição automática de código. O projeto nasceu com a produção de um plugin específico para o IDE Eclipse, mas o objetivo é que sejam construídas ferramentas que atendam outros ambientes de desenvolvimento para a plataforma Java. O framework não depende de ferramentas de geração de código, mas como seu foco é padronização, a produtividade é alcançada verdadeiramente com o uso de ambientes integrados de desenvolvimento.

Demoiselle Sample reúne implementações de referência do Demoiselle Framework. São aplicações de exemplo que utilizam os recursos oferecidos pelo framework e servem como ponto de partida para desenvolvedores e estudantes.

Demoiselle Process tem por objetivo criar um processo de desenvolvimento de sistemas que seja orientado para a arquitetura de referência do Demoiselle. A utilização do framework independe de processo de desenvolvimento, mas a existência de um que já assuma uma arquitetura padrão resulta em mais agilidade na construção de aplicações.

336

Page 337: Introducao Ao Demoiselle Framework Versao 1 PDF

Demoiselle Infra tem como objetivo ajudar desenvolvedores a instalar e configurar o ambiente de desenvolvimento e técnicos de suporte a criarem e manterem um ambiente de produção.

7.3 Arquitetura

Demoiselle Framework é uma solução para desenvolvimento de aplicações em Java construída pelo Serpro, para padronizar a construção de suas aplicações e promover a geração de componentes de software reutilizáveis. Ele consiste em duas partes, um framework arquitetural e as dependências estruturais desse framework. O foco, neste caso é o framework arquitetural, mas apresentaremos uma visão 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 são essenciais para um domínio de problemas específicos” (Horstmann, 2007, p. 312) e que “constroem um projeto reutilizável 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 orientação arquitetônica do software, através do particionamento do projeto em classes abstratas e da definição de suas responsabilidades e colaborações”. Segundo Gamma, o desenvolvedor pode customizar o framework “para uma aplicação particular, através da especialização e da composição de instâncias de suas classes”.

Demoiselle Framework, além das definições 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 divisão em camadas, pois, segundo Fowler (2006, p. 37-38), isso permite a compreensão de uma parte do sistema como um todo coerente sem a necessidade de saber muito sobre as demais partes. Além disso, a utilização de camadas permite a fácil substituição de implementações, a minimização de dependências entre partes das aplicações, e a definição de padrões que podem gerar código reutilizável.

A camada mais baixa do Demoiselle é o sistema operacional. Essa camada não é passível da imposição de padrão, porque a escolha do sistema operacional depende de vários fatores, como os tipos de aplicação que serão executadas, a necessidade de customização (adaptação do sistema) e a disponibilidade de recursos financeiros (para pagamento de licenças e contrato de suporte) ou humanos (para manutenção do sistema por conta própria).

A imposição de um sistema operacional para um framework voltado ao governo é inviável, pois aprisiona o Estado a uma tecnologia e tende a onerar o cidadão. Dado isso, as aplicações devem ser capazes de rodar em qualquer sistema operacional (ou

337

Page 338: Introducao Ao Demoiselle Framework Versao 1 PDF

pelo menos na maioria disponível/usada no mercado). A tecnologia Java permite a portabilidade pela implementação do conceito de máquina virtual, que constitui a segunda camada mais baixa do Demoiselle.

A próxima camada mais acima é denominada plataforma. Como a implementação atual do Demoiselle está direcionada a construção de aplicações Web, a camada de plataforma se constitui de servidores de aplicação Web que sigam a especificação Servlets 2.5 (JCP, 2000). A criação de aplicações que não sejam Web (Desktop, embarcadas em dispositivos móveis) implicará em mudanças nessa camada. Em seus projetos, o Serpro fez uso de JBoss e Tomcat.

A próxima camada mais superior, dos frameworks de fundamento, constitui-se da reunião de especificações da tecnologia Java que norteiam as implementações de software utilizadas pelo framework. A ideia é que as aplicações não fiquem presas a produtos de software específicos. A adoção dos padrões definidos pelas especificações 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 mutação do Demoiselle Framework. Os frameworks especialistas são integrados pela última camada no topo, o framework arquitetural, que constitui efetivamente o Demoiselle. O gerenciamento de mudanças deve se concentrar nessas duas camadas superiores, sendo que o framework arquitetural deve permanecer estável.

A ideia é proteger as aplicações do impacto das mudanças, conservando a mesma interface para as classes e métodos utilizados, mas alterando os componentes que implementam as funcionalidades conforme for necessário. O intento é tirar do desenvolvedor a preocupação com as implementações de baixo nível, que constituem a infraestrutura de software.

Todas as tecnologias integradas pelo Demoiselle podem ser perfeitamente usadas sem o framework. Porém, nesse caso, o desenvolvedor divide a sua preocupação com diversas estruturas, precisa gerenciar isso por conta própria e torna as aplicações dependentes de determinadas tecnologias, fortemente sujeitas à mudança. Ao programar para o Demoiselle Framework, o desenvolvedor consegue criar uma estrutura de software reutilizável, de duas formas. Primeiro, se algum framework especialista tiver de ser substituído, a aplicação irá ignorar essa mudança, pois sua interface com o framework permanecerá inalterada. A mudança ocorrerá na implementação do framework que faz a integração, sendo que o resultado final permanecerá o mesmo.

7.3.1 Objetivos e Restrições Arquiteturais

Doravante, quando nos referirmos a Demoiselle, estaremos tratando do framework arquitetural. A arquitetura do Demoiselle é norteada e restringida por um conjunto de

338

Page 339: Introducao Ao Demoiselle Framework Versao 1 PDF

objetivos, a saber, extensibilidade, reusabilidade, manutenibilidade, desempenho, estabilidade e confiabilidade.

A arquitetura atual foi desenhada para atender somente aplicações Web não distribuídas.

7.3.1.1 Extensibilidade

Seria absurdo presumir a possibilidade da criação de uma infraestrutura de software que pudesse atender às demandas de todas as aplicações. Essa é uma dificuldade do estabelecimento e aceitação de padrões. Tentativas de cercar todos os problemas tendem ao fracasso, pois não é possível prever as funcionalidades necessárias para atender aplicações futuras.

De modo a convergir a padronização das aplicações, pré-requisito para o objetivo tratado a seguir, e a capacidade dos sistemas de crescerem, a arquitetura do framework define a existência de pontos de extensão seja por meio de interfaces, abstrações ou pela utilização de padrões de projetos.

As principais interfaces da arquitetura são:

IDAO: Interface para as classes que implementam o padrão de projetos DAO (Alur et alli, 2002, p. 346), no qual um objeto é responsável por extrair e encapsular todos os acessos à origem de dados e gerenciar a conexão com a origem de dados para obter e armazenar dados. Classes que implementam IDAO não acessam outras camadas da aplicação, apenas a de persistência;

IFacade: Interface para classes que implementam o padrão Façade (Gamma et alli, p. 179), cujo objetivo é unificar subsistemas por meio de uma interface de nível mais alto e tornar mais fácil o uso dos mesmos;

IBusinessController: Interface para classes que definem objetos da camada de negócios (Alur et alli, 2002, p. 50) que acessam a camada de persistência através de classes que implementam a interface IDAO. Objetos que implementam IBusinessController podem acessar funcionalidades de outros sistemas ou módulos através de implementações de IFacade;

IViewController: Interface para classes que fundem os conceitos de visão e controle do MVC (2006, p. 315). Objetos dessas classes terão acesso somente a instâncias de classes que implementam IFacade e IBusinessController;

Uma característica inerente dos frameworks, citada por Fowler (Fowl05) é que “os métodos definidos por um usuário para adaptar o framework às suas necessidades são frequentemente chamados de dentro do próprio framework, em vez do código de aplicação do usuário. O framework frequentemente faz o papel do programa principal na coordenação e sequenciamento de atividades da aplicação. Essa inversão de

339

Page 340: Introducao Ao Demoiselle Framework Versao 1 PDF

controle dá aos frameworks o poder de servir como esqueletos extensíveis. Os métodos fornecidos pela adaptação do usuário para os algoritmos genéricos definidos no framework para uma aplicação particular”.

7.3.1.2 Reusabilidade

Segundo Paula Filho (2009, p. 256), “quando uma organização começa a usar processos definidos de desenvolvimento de software, os maiores ganhos iniciais resultam da redução dos defeitos introduzidos em cada iteração. Isso ocorre por causa da redução do desperdício de tempo e dinheiro, principalmente aquele que é causada por defeitos de requisitos, análise e desenho. A partir daí, ganhos significativos de produtividade só são conseguidos por meio da reutilização”.

Em face disso, a arquitetura de uma infraestrutura de software deve favorecer o reuso, a partir da especificação de artefatos comuns em diversos projetos. O Demoiselle Framework propõe dois principais artefatos de reuso, uma arquitetura de referência e componentes fracamente acoplados.

A arquitetura de referência é uma estrutura padrão de aplicação em camadas. A partir do padrão de projeto MVC (Fowler, 2006, p. 315) e do modelo J2EE (Alur et alli, 2002, p. 114), o Demoiselle propõe três camadas: Visão e Controle, Negócios e Persistência. Visão e Controle é a fusão das camadas homônimas do padrão MVC, em um entendimento de que são indissociáveis, exceto em aplicações servidoras de serviços. Negócios centraliza a maior parte do processamento de negócios para a aplicação. Persistência corresponde a camada de Modelo do MVC e de Integração do J2EE.

Camadas podem ser reaproveitadas entre aplicações, geralmente com adaptações. O Demoiselle Framework propõe a redução ao mínimo de adaptações com o uso de injeção de dependências (Fowler, 2004). Esse é um padrão cuja ideia básica consiste em um objeto separado, um montador, que realiza a instanciação da implementação apropriada de uma interface, de modo que uma camada não dependa de classes específicas. A injeção de dependências no Demoiselle é feito com o uso de orientação a aspectos, implementada por AspectJ.

A extensibilidade por meio de componentes fracamente acoplados serve também a reutilização. A proposta do Demoiselle é que as aplicações sejam criadas com blocos de código 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 código, ao induzir a construção de aplicações pela composição de funcionalidades.

A criação de componentes para o Demoiselle segue a orientação de McConnell (2005, p. 78), na qual “a responsabilidade de cada bloco de construção deve ser bem-definida. Um bloco de construção deve ter uma área de responsabilidade e deve saber o

340

Page 341: Introducao Ao Demoiselle Framework Versao 1 PDF

mínimo possível sobre as áreas de responsabilidade de outros blocos de construção (fraco acoplamento)”.

7.3.1.3 Manutenibilidade

A arquitetura divide responsabilidades entre módulos lógicos, que serão vistos adiante, com o intuito de causar o menor impacto no todo em caso de manutenção das aplicações. Isso é alcançado com duas abordagens: diminuição do acoplamento e foco da manutenção em pontos específicos.

7.3.1.4 Desempenho

O projeto do Demoiselle identificou, com base na experiência das equipes de desenvolvimento do Serpro, pontos críticos de performance, tais como integração entre camadas e controle de transações. De modo a diminuir os riscos de perda de desempenho, na manutenção e evolução de aplicações, o framework implementa os pontos críticos como funcionalidades, que fazem parte dos pontos específicos de manutenção citados anteriormente.

7.3.1.5 Estabilidade e Confiabilidade

Estabilidade aqui não se refere à garantir a execução das aplicações, mas sim que seu comportamento continue sendo o esperado após a realização de manutenções. Para garantir isso, foi decidido que o framework não dependeria de implementações específicas de determinadas funcionalidades, e sim que ele seria fundamentado em especificações tecnológicas. Desse modo, as aplicações ficam protegidas de mudanças em produtos de software, pois as implementações podem ser substituídas por outras que sigam as mesmas especificações.

A estabilidade do framework garante a confiabilidade das aplicações, pois ao manter as mesmas condições estabelecidas (especificações), a tendência é que o sistema prossiga normalmente na execução de suas funções.

7.3.2 Perspectiva Estrutural da Solução

Apresentaremos aqui uma visão lógica do framework arquitetural, os módulos lógicos nos quais está dividido e quais os elementos de projeto presentes nesses módulos. O framework consiste em cinco módulos: core, util, web, persistence e view. Esses módulos são bibliotecas de classes Java, disponibilizadas independentemente como arquivos .jar.

7.3.2.1 Módulo Core

Este é o módulo que contém o conjunto de especificações que dão base estrutural ao framework. Seu objetivo é tornar possível a padronização, extensão e integração entre as camadas das aplicações baseadas no framework.

341

Page 342: Introducao Ao Demoiselle Framework Versao 1 PDF

O pacote br.gov.framework.demoiselle.core.layer define as abstrações para os objetos de cada camada. Ele contém as interfaces vistas nos objetivos e restrições arquiteturais: IViewController, IBusinessController, IDAO e IFacade. À exceção de IDAO, as demais interfaces não contém declaração de métodos, o que pode parecer estranho, já que interfaces servem para definir comportamentos padronizados por meio de código genérico reusável.

O que ocorre é as interfaces do módulo Core possuem outro propósito: servir para identificar os pontos de injeção de dependências.

Geralmente as aplicações farão uso das três primeiras interfaces. IFacade serve para definir uma fachada quando a aplicação trata de integração entre módulos ou integração de subsistemas.

O Core não possui nenhuma funcionalidade imediatamente utilizável. Ele apenas define os padrões do framework, de modo que outros módulos tem de estendê-lo para tornar o framework funcional.

7.3.2.1.1 Integração entre Camadas

O pacote br.gov.framework.demoiselle.core.layer.integration define uma abstração para o mecanismo de integração entre camadas.

342

Figura 35: Pacotes do Core e suas dependências

Page 343: Introducao Ao Demoiselle Framework Versao 1 PDF

Figura 36: Padrão de Camadas

O mecanismo de integração atua na camada de visão injetando objetos de negócio por meio de uma fábrica (Gamma et alli, 2000, p. 95) do próprio framework ou alguma fábrica definida pela aplicação. Essa fábrica pode utilizar um proxy (Gamma et alli, 2000, p. 198) do framework ou da aplicação para instanciar objetos de negócio.

De forma similar, na camada de regras de negócio, o mecanismo atua injetando objetos de persistência.

Três interfaces são definidas neste pacote: IFactory, IBusinessControllerFactory e IDAOFactory.

IFactory é uma abstração de fábricas de objetos injetados. IBusinessControllerFactory e IDAOFactory são especializações de IFactory, respectivamente para objetos de negócio e objetos de persistência.

Beneficiando-se das funcionalidades oferecidas pela versão 5 de Java, o framework define no módulo Core duas anotações (annotations): Injection e Factory. A primeira é uma anotação de propriedade que contém informações a respeito da injeção de dependência. A segunda é uma anotação de classe ou pacote usada para definir a implementação da fábrica utilizada na injeção de dependência.

A classe InjectionContext responsabiliza-se por manter as informações necessárias para a fábrica realizar a criação de objetos.

343

Page 344: Introducao Ao Demoiselle Framework Versao 1 PDF

O módulo Core especifica quem trata a injeção de dependência, mas a forma como a injeção será realizada deve ser definida pelos módulos que implementam as abstrações do Core. O módulo Web que implementa a injeção de dependência por padrão.

Isso não impede que ilustremos como se dá o fraco acoplamento proporcionado pelo uso desse padrão. O código a seguir é uma classe que implementa a interface IViewController, que não contém nenhum comportamento definido. Isso tão 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 ordinário, que um aspecto pode ser aplicado neste ponto, ou mais especificamente, nessa classe.

Observe que a classe MeuMB define um atributo cujo tipo não é uma classe, e sim uma interface. IMeuBC é uma extensão de IBusinessController e também constitui um join point. Antes do atributo, porém, há uma anotação @Injection. O que vai ocorrer quando da compilação dessa classe, é que o compilador de aspectos identificará pela anotação que o atributo precisa receber uma instância. Aí ocorre a injeção de código. Na linha imediatamente a seguir ao da declaração é incluído um pedaço de código (advice) que faz a atribuição de uma classe que implementa IMeuBC para meuBC.

public class MeuMB implements IViewController{

@Injection

private IMeuBC meuBC;

}

A classe que será instanciada é definida pelo módulo que implementa o Core. Pode-se induzir que a classe tenha que seguir um padrão de nome para ser localizada. E isso pode levar a crer que é difícil flexibilizar a aplicação para que determinados atributos instanciem classes fora do padrão. Mas é facil criar exceções à regra, pela passagem do parâmetro name para a anotação @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

Page 345: Introducao Ao Demoiselle Framework Versao 1 PDF

Figura 37: Integração entre camadas

7.3.2.1.2 Contexto de Mensagens

O pacote br.gov.framework.demoiselle.core.message define uma abstração de mensagens trocadas durante uma requisição entre as camadas dos sistemas. Essa abstração permite criar um contexto de mensagens para que todas as camadas definidas pelas arquitetura de referência possam manipular mensagens. Ela também auxilia a exibição das mensagens para o usuário, seja na forma de tela, arquivo texto (log), em banco de dados ou na junção dessas opções. Cada implementação dessa especificação deve prover uma solução de acesso ao contexto de mensagem.

Esse pacote define duas interfaces, IMessage e IMessageContext. A primeira é uma abstração da unidade de mensagem, enquanto a segunda abstrai o contexto de mensagem. O pacote ainda conta com um tipo enumerado chamado Severity, que padroniza as severidades genéricas.

345

Page 346: Introducao Ao Demoiselle Framework Versao 1 PDF

Figura 38: Contexto de Mensagens

7.3.2.1.3 Exceção Padronizada

O pacote br.gov.framework.demoiselle.core.exception define a classe ApplicationRuntimeException como a exceção padrão para ser utilizada pelas aplicações. Essa classe encapsula uma instância de IMessage, constituindo uma mensagem de erro padronizada que pode ser tratada facilmente pelos módulos do framework. ApplicationRuntimeException é uma exceção não verificada, portanto não precisa obrigatoriamente ser capturada ou declarada.

A seguir temos um exemplo de lançamento da exceção padronizada, com a passagem de uma mensagem definida pelo usuário.

public void MetodoBC(){

if ( /*Condição para lançamento de exceção*/ ){

throw new ApplicationRuntimeException(ErrorMessage.ERRO_01);

}

}

346

Page 347: Introducao Ao Demoiselle Framework Versao 1 PDF

Figura 39: Exceção padronizada

7.3.2.1.4 Contexto de Segurança

O framework especifica uma forma padronizada de acesso aos dados da aplicação com restrições de segurança por intermédio de autenticação e autorizada baseada em papéis. Cada implementação desta especificação do Core deve prover uma solução de acesso ao contexto de segurança. A abstração do contexto de segurança é baseada na especificação JAAS, uma API que permite que aplicações escritas na plataforma J2EE usem serviços de controle de autenticação e autorização sem necessidade de estarem fortemente dependentes desses serviços.

A seguir temos um exemplo de uso do contexto de segurança:

ISecurityContext contexto = ContextLocator.getInstance().getSecurityContext();

if (contexto.isUserInRole("Administrador")){

...

}

A utilização do método getInstance() assinala o uso do padrão de projeto Singleton (Gamma et alli, 2000, p. 130), o que é coerente, visto que as informações relativas a segurança 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 propõe uma abstração para as entidades da aplicação, a interface IPojo, que força a utilização do padrão Value Object (Alur et alli, 2002, p. 232).

347

Page 348: Introducao Ao Demoiselle Framework Versao 1 PDF

Figura 40: Contexto de Segurança

Figura 41: Padrão para entidades

7.3.2.1.6 Contexto de Transação

O pacote br.gov.framework.demoiselle.core.transaction especifica o mecanismo de controle transacional e define um contexto de transação que atua no início e no fim de cada ação. Seu funcionamento depende de um tipo definido pela enumeração TransactionType, que pode ser LOCAL ou JTA (Java Transaction API). LOCAL indica que a aplicação será responsável pelo gerenciamento da transação, enquanto JTA indica que a aplicação dependerá de uma implementação JTA disponível no contêiner.

Esse pacote possui duas interfaces, ITransactionResource e ITransactionContext. A primeira define um recurso a ser registrado no contexto de transação, enquanto a segunda representa o contexto de transação responsável por registrar o início e o fim de cada ação e registrar os recursos transacionais.

7.3.2.1.7 Acionadores

O pacote br.gov.framework.demoiselle.core.action define um mecanismo padronizado de ações a serem executadas pela aplicação. Essas ações são definidas como funções estruturais da aplicação, como carregamento de configuração e inicialização de ambiente.

As interfaces desse pacote são IActionManager, ILoaderAction e IAction. A primeira define um padrão para classes que gerenciam ações, com métodos para configurar o

348

Page 349: Introducao Ao Demoiselle Framework Versao 1 PDF

carregador da ação (que deve implementer ILoaderAction). O carregador da ação contém uma coleção de tipos IAction, que per sua vez representam ações.

Figura 42: Contexto de Transação

Um exemplo de ação padronizada pode ser visto a seguir:

public class MinhaAplicacaoAction implements IAction {

private static Logger log = Logger.getLogger(MinhaAplicacaoAction.class);

public void execute() {

log.debug("Lendo arquivos de configuração");

}

}

Uma classe que implemente ILoaderAction, por exemplo MeuCarregadorAction, recupera as ações com seu método getActions(), que retorna uma coleção do tipo IAction. Uma implementação de IActionManager, por sua vez, dispara todas as ações pela chamada a seus métodos execute(), algo extremamente simples para a estrutura for trazida pelo Java 5, que itera sobre coleções.

349

Page 350: Introducao Ao Demoiselle Framework Versao 1 PDF

Figura 43: Acionadores

7.3.2.1.8 Localizador de Contextos

O pacote br.gov.framework.demoiselle.core.context define uma classe chamada ContextLocator. Essa classe é fundamental para que as aplicações consigam ter um acesso fácil e centralizado aos contextos de Mensagem, Transação e Segurança, e ao mesmo tempo garante que eles tenham, cada um, uma única instância, pela aplicação do padrão Singleton.

As implementações de cada contexto devem obrigatoriamente utilizar o localizador de contextos como canal de acesso, para que todas as camadas da aplicação possam utilizar os contextos.

Figura 44: Localizador de Contextos

350

Page 351: Introducao Ao Demoiselle Framework Versao 1 PDF

7.3.2.2 Módulo Util

Este módulo que contém componentes utilitários da maior amplitude genérica, que visam facilitar o trabalho de outras funcionalidades do framework. Consiste de dois pacotes, um para carregamento de configuração e outro para paginação de dados.

7.3.2.2.1 Carregamento de Configuração

O pacote br.gov.framework.demoiselle.util.config define um mecanismo padronizado de carregamento de configuração que permite carregar variáveis configuradas no ambiente, arquivos XML ou arquivos de propriedades para um objeto. Esse mecanismo é utilizado em vários outros componentes do framework e pode ser utilizado também pelas aplicações.

A classe ConfigurationLoader é o utilitário que executa o carregamento das configurações nas classes. Uma anotação chamada ConfigKey é usada nas classes para identificar os atributos que podem ser carregados a partir de uma configuração. A limitação dos recursos aceitos pelo mecanismo é feita pelo tipo enumerado ConfigType. Como sério candidato a exceções, o pacote contém uma exceção própria, ConfigurationException, que herda de RuntimeException, o que a torna também não verificável.

O trecho de código a seguir traz exemplos das declarações de carregamento de configuração possíveis:

@ConfigKey (name = "key", type=ConfigType.SYSTEM)

private String stringValueSystem;

@ConfigKey (name = "framework.stringValue", type=ConfigType.XML,

resourceName="configuration.xml")

351

Figura 45: Módulo Util

Page 352: Introducao Ao Demoiselle Framework Versao 1 PDF

private String stringValueXML;

@ConfigKey (name = "framework.stringValue", type=ConfigType.PROPERTIES,

resourceName="configuration.properties")

private String stringValueProperties;

Esses atributos poderiam, por exemplo, pertencer a uma classe chamada MeuConfig. O carregamento e uso da configuração se daria da forma a seguir:

public void meuMetodo() {

MeuConfig meuConfig = new MeuConfig();

ConfigurationLoader.load(meuConfig);

System.out.print( meuConfig.getMinhaPropriedade());

}

Figura 46: Carregamento de Configuração

352

Page 353: Introducao Ao Demoiselle Framework Versao 1 PDF

7.3.2.2.2 Paginação de Dados

As aplicações geralmente necessitam trafegar resultados entre as camadas de forma paginada garantindo o desempenho da aplicação. Esse mecanismo é implementado no pacote br.gov.framework.demoiselle.util.page com um objeto que permite configurar os dados da página que será requisitada (instância de Page) e um objeto que contém os resultados de forma paginada (instância de PageResult).

Figura 47: Paginação de dados

7.3.2.3 Módulo Web

Este módulo contém implementações de algumas especificações do módulo Core e também utilitários comuns de aplicações Web não distribuídas que facilitam o tratamento de sessões de usuário e suas requisições.

353

Page 354: Introducao Ao Demoiselle Framework Versao 1 PDF

Figura 48: Módulo Web

7.3.2.3.1 Contexto de Segurança

O pacote br.gov.framework.demoiselle.web.security define duas classes para implementar o mecanismo de acesso aos dados com segurança, WebSecurityContext e WebSecurityServletRequestListener. A primeira implementa o contexto de segurança através do padrão Singleton, além de gerenciar os dados de segurança vinculados a thread corrente. A segunda é responsável por repassar o objeto “request” para a instância da primeira.

354

Page 355: Introducao Ao Demoiselle Framework Versao 1 PDF

Figura 49: Contexto de Segurança implementado pelo Módulo Web

7.3.2.3.2 Contexto de Mensagens

O pacote br.gov.framework.demoiselle.web.security define a classe WebMessageContext para implementar o contexto de mensagens. O código a seguir ilustra o lançamento de mensagens para o contexto:

IMessageContext contextoMsg = ContextLocator.getInstance().getMessageContext();

public class MeuBC implements IBusinessController {

public void meuMetodo(){

contextoMsg.addMessage(InfoMessage.Mensagem);

}

}

O método getMessageContext() devolve uma instância de WebContextMessage, mas isso não importa para a aplicação, desde que a classe implemente IMessageContext. Isso permite que esse código fique protegido contra mudanças caso o contexto de mensagens seja modificado.

A recuperação de mensagens pode ser feita pela chamada ao método getMessages() do contexto de mensagens, que retorna uma coleção de instâncias de IMessage.

355

Page 356: Introducao Ao Demoiselle Framework Versao 1 PDF

Figura 50: Contexto de Mensagens implementado pelo Módulo Web

7.3.2.3.3 Integração entre Camadas

O módulo Web implementa a especificação de integração de camadas proposto pelo módulo Core no pacote br.gov.framework.demoiselle.web.layer.integration. O mecanismo implementado utiliza AOP para detectar os pontos de integração. Permite que sejam implementadas novas fábricas de acordo com a necessidade da aplicação. Os objetos são fabricados por meio de convenção de nomes entre as interfaces e sua implementação.

WebAbstractFactory é a abstração de uma fábrica genérica do módulo Web, seguindo o padrão de projeto Abstract Factory (Gamma, 2000, p. 95). Todas as outras fábricas desse módulo devem especializar essa classe. WebBusinessControllerFactory e WebDAOFactory implementam respectivamente as interfaces IBusinessController e IDAOFactory, definidas no Core.

A classe responsável pelo mecanismo de injeção de dependências é InjectionManager que faz uso das definições de injeção do Core. InjectionManager é chamada quando o aspecto InjectionAspect intercepta instanciações de classes que implementam IBusinessController, IViewController e Ifacade.

O subpacote br.gov.framework.demoiselle.web.layer.integration oferece uma implementação opcional para desenvolver objetos criados pela injeção com um proxy (Gamma et alli, 2000, p. 198). A interface IProxy define o comportamento padrão do proxy chamado pela injeção de dependência (InjectionManager). Sua implementação

356

Page 357: Introducao Ao Demoiselle Framework Versao 1 PDF

deve ser registrada no arquivo de configuração do framework e carregada através da classe ProxyConfig.

A classe WebProxy é a implementação básica da interface IProxy. Uma classe de configuração chamada ProxyConfig diz qual classe implementa a interface IProxy. A injeção de dependências faz uso do controlador de chamadas de métodos WebInvocationHandler na hora de envolver o objeto criado em um proxy.

Figura 51: Integração entre camadas implementada pelo módulo Web

7.3.2.3.4 Contexto de Transação

O módulo Web implementa a especificação do contexto transacional proposto no módulo Core. O mecanismo implementado utiliza AOP para prover um mecanismo transparente de gerenciamento de transação.

É possível utilizar o controle transacional do contêiner (JTA). Para isso deve existir uma implementação de um mecanismo de lookup via JNDI. A interface

357

Page 358: Introducao Ao Demoiselle Framework Versao 1 PDF

IJNDITransactionManagerLookup define as informações para o mecanismo JNDI localizar uma UserTransaction do contêiner com suporte JTA. O módulo Web provê uma implementação para essa interface, voltada para o JBoss: JbossTransactionManagerLookup.

A implementação padrão do contexto transacional é a classe WebTransactionContext. A classe WebTransactionAction define a ação de inicialização em aplicações Web onde o contexto transacional é configurado. WebTransactionActionConfig define as configurações padrão do contexto transacional definido pela aplicação em arquivo externo.

A classe WebTransactionServletRequestListener é o controlador do contexto transacional. Ela é responsável por iniciar e finalizar, normalmente ou com erro, o contexto transacional. É acionado em todas as requisições Web.

7.3.2.3.5 Inicialização do Ambiente

A inicialização de ambiente implementa a especificação de ações proposta no módulo Core, essa inicialização ocorre sempre que o contêiner iniciar a aplicação. O módulo Web necessita que algumas ações sejam executadas e essas ações estão implementadas no pacote br.gov.framework.demoiselle.web.init. Os componentes e aplicações baseadas no framework podem implementar outras ações e adicioná-las para que sejam executadas na inicialização do ambiente.

A interface IinicializationAction define o comportamento de uma ação de inicialização de aplicações Web. A classe WebInicializationServletContextListener executa todas as ações configuradas ao inicializar o contêiner web/servlet. A classe WebInicializationLoaderAction carrega todas as classes de ações, baseada nas configurações representadas pela classe WebInicializationLoaderActionConfig.

O código a seguir é um exemplo de classe de inicialização do ambiente:

public class MinhaAction implements IInitializationAction {

public void execute() {

log.debug("Inicializando minha action");

}

public void setServletContext(ServletContext context) {

}

}

358

Page 359: Introducao Ao Demoiselle Framework Versao 1 PDF

O método execute() dessa classe será invocado automaticamente na inicialização, desde que a classe seja referenciada no arquivo demoiselle.properties, conforme exemplo a seguir:

framework.demoiselle.web.initialization.action=MinhaAction

7.3.2.3.6 Redirecionamento Baseado em URL

O módulo Web implementa a especificação do contexto transacional proposto no módulo Core. O mecanismo implementado utiliza AOP para prover um mecanismo transparente de gerenciamento de transação.

A implementação do redirecionamento consiste em cinco passos, que ilustraremos a seguir.

359

Figura 52: Contexto de transação implementado pelo módulo Web

Page 360: Introducao Ao Demoiselle Framework Versao 1 PDF

Figura 53: Inicialização do ambiente

Inicialmente, temos de criar as ações de redirecionamento. Um exemplo é a classe adiante, MinhaRedirectAction:

public class MinhaRedirectAction implements IRedirectAction {

private ServletReq uest request;

private ServletResponse response;

public String getParameter() {

return "MinhaActionParameter";

}

public String getValue() {

return "MinhaActionValue";

}

public void setRequest(ServletRequest req) { this.request = req; }

360

Page 361: Introducao Ao Demoiselle Framework Versao 1 PDF

public void setResponse(ServletResponse resp) { this.response = resp; }

public void execute() {

/*Minha execução*/

}

}

Em seguida, é necessário cadastrar as ações no arquivo demoiselle.properties:

# --- Web configuration ---

framework.demoiselle.web.redirect.action=MinhaRedirectAction01

framework.demoiselle.web.redirect.action=MinhaRedirectAction02

framework.demoiselle.web.redirect.action=MinhaRedirectAction03

Posteriormente, a classe WebRedirectServlet deve ser mapeada no arquivo web.xml:

<servlet>

<servlet-name>

WebRedirectServlet

</servlet-name>

<servlet-class>

br.gov.framework.demoiselle.web.redirect.WebRedirectServlet

</servlet-class>

</servlet>

<servlet-mapping>

<servlet-name>

WebRedirectServlet

</servlet-name>

<url-pattern>

/redirect

</url-pattern>

361

Page 362: Introducao Ao Demoiselle Framework Versao 1 PDF

</servlet-mapping>

Finalmente, na página JSP, ou JSF, a ação de redirecionamento é chamada desta forma:

<a href="minhaAplicacao/redirect?MinhaActionParameter=MinhaActionValue">

Chamar Minha Action

</a>

Figura 54: Implementação do redirecionamento baseado em URL

7.3.2.3.7 Injeção de Dependências com Aspectos

Martin Fowler (2004) afirma que a ideia básica da injeção de dependências é ter um objeto separado, um montador, que popula um campo em uma classe com uma implementação apropriada para uma interface. O Demoiselle Framework implementa a injeção de dependências em seu módulo Web, por meio de programação orientada a aspectos.

Segundo Winck e Goetten Júnior (2006, p. 48) “um aspecto é o mecanismo disponibilizado pela programação orientada a aspectos para agrupar fragmentos de código referente aos componentes não funcionais em uma unidade no sistema”. De acordo com os autores, “o principal objetivo da POA [programação orientada a objetos] consiste em separar o código referente ao negócio do sistema dos interesses transversais, de uma forma bem definida e centralizada.”

Winck e Goetten Júnior afirmam que (2006, p. 43) “a orientação a aspectos trabalha em paralelo com outro paradigma”. No caso do Demoiselle Framework, a orientação a

362

Page 363: Introducao Ao Demoiselle Framework Versao 1 PDF

aspectos é utilizada em conjunto com a orientação a objetos, inerente à linguagem Java. Segundo os mesmos autores (2006, p. 45), “na programação orientada a aspectos (...) os interesses são programados em módulos separados (...). Após a programação, ocorre a combinação entre as classes e os aspectos”.

Eduardo Piveta (2001 apud Winck e Goetten Júnior, 2006) afirma que um sistema que utiliza a programação orientada a aspectos é composto por três 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 módulo Web contém um aspecto chamado InjectionAspect, cujo código integral exibimos a seguir:

package br.gov.framework.demoiselle.web.layer.integration;

import br.gov.framework.demoiselle.web.layer.integration.InjectionManager;

public aspect InjectionAspect {

pointcut injectionIBusinessController(): execution(public br.gov.framework.demoiselle.core.layer.IBusinessController+.new(..));

pointcut injectionIViewController(): execution(public br.gov.framework.demoiselle.core.layer.IViewController+.new(..));

pointcut injectionIFacade(): execution(public br.gov.framework.demoiselle.core.layer.IFacade+.new(..));

before(): injectionIBusinessController() || injectionIViewController() || injectionIFacade() {

InjectionManager manager = new InjectionManager();

manager.execute(thisJoinPoint.getTarget());

}

}

363

Page 364: Introducao Ao Demoiselle Framework Versao 1 PDF

Assim como na linguagem Java a unidade central é a classe, na AspectJ a unidade central é o aspecto. AspectJ tem sua sintaxe própria, mas estende a linguagem Java, de modo que todo programa AspectJ é um programa Java válido. Ou melhor, todo aspecto referencia e gera código Java válido.

O aspecto InjectionAspect tem por objetivo interceptar instanciações de classe que implementem as interfaces IBusinessController, IViewController e IFacade e invocar uma instância de InjectionManager.

Ele faz essas interceptações por meio da construção pointcut, que é um ponto de atuação. Winck e Goetten Júnior (Winck e Goetten Júnior, 2006, p. 56) definem pontos de atuação como “elementos do programa usados para definir um ponto de junção, como uma espécie de regra criada pelo programador para especificar eventos que serão considerados como um ponto de junção”. Por sua vez, um ponto de junção ou entrada é um identificador bem definido na execução de um programa.

Vamos compreender o funcionamento do ponto de atuação injectionIBusinessController, que esclarecerá os demais:

pointcut injectionIBusinessController(): execution(public br.gov.framework.demoiselle.core.layer.IBusinessController+.new(..));

A palavra execution refere-se a execução de um método, o qual se encontra entre os parênteses seguintes. Esse ponto de atuação diz que o aspecto será disparado sempre que for gerada uma nova instância de classe que implemente IBusinessController, ou, em outras palavras, quando o método construtor de uma implementação de IBusinessController for executado.

Após a declaração de três pontos de atuação, o código tem uma construção before. Isso é um adendo (advice). Winck e Goetten Júnior (2006, p. 57) definem adendo como um “mecanismo similar a um método, usado para declarar o código que deve ser executado a cada ponto de junção em um ponto de atuação”. Segundo eles, os adendos são compostos de duas partes, “o ponto de atuação, que define as regras de captura dos pontos de junção” e “o código que será executado quando ocorrer um ponto de junção (definido pela primeira parte)”.

O programador define em que momento o código será executado no adendo, em função do ponto de junção. No caso do adendo before, o código será executado antes do ponto de junção. Para executá-lo depois usaríamos after e para execução durante o ponto de junção, o identificador around.

Vamos analisar o trecho final de código do aspecto, destacado a seguir:

before(): injectionIBusinessController() || injectionIViewController() || injectionIFacade() {

InjectionManager manager = new InjectionManager();

manager.execute(thisJoinPoint.getTarget());

364

Page 365: Introducao Ao Demoiselle Framework Versao 1 PDF

}

Esse adendo estabelece que para qualquer dos três pontos de atuação (injectionIBusinessController, injectionIViewController e injectionFacade), seu código será executado, antes do ponto de atuação.

No caso, uma instância de InjectionManager será invocada e chamará seu método execute, passando como argumento o objeto do contexto atual.

O método execute de InjectionManager irá verificar todos os atributos do objeto interceptado. Para cada atributo, ele irá verificar a presença da anotação @Injection, definida pelo módulo Core. Se ela estiver presente, será passada como argumento para o construtor de InjectionContext, também definida pelo Core.

O método getFactory de InjectionManager retorna uma implementação do padrão de projeto Abstract Factory. Ele verifica se o usuário fez uso da anotação @Factory, especificando uma classe para fabricar os objetos a serem criados. Caso não tenha feito, de acordo com a interface implementada pelo objeto interceptado ele retorna a respectiva classe de fábrica definida pelo módulo Web.

A fábrica de objetos irá retornar uma instância de acordo com o contexto de injeção, determinado pela anotação. Essa instância será “injetada” no código, mais exatamente, será associada ao atributo em questão.

Dessa forma, não há necessidade de criar instâncias de objetos explicitamente, pois o compilador de aspectos irá gerar o código. Isso torna o código-fonte original independente da implementação.

7.3.2.4 Módulo Persistence

O módulo Persistence trata as funções de persistência das aplicações e sua integração com outros módulos. Ele provê a integração da aplicação sistemas gerenciadores de dados e garante transparência às demais camadas da aplicação na recuperação, armazenamento e tratamento das informações.

A interface IDAO do módulo Core, em oposição as outras interfaces do pacote br.gov.framework.demoiselle.core.layer, define quatro operações. Ela é a base para o módulo de persistência do framework.

A persistência no Demoiselle Framework enfoca duas abordagens: o acesso direto por JDBC e o mapeamento objeto-relacional. O acesso a sistemas de bancos de dados não-relacionais, geralmente legado, pode ser feito por componentes.

Um pacote com três classes trata do desenvolvimento com drivers JDBC. JDBCGenericDAO implementa alguns métodos de IDAO, para diminuir o trabalho do desenvolvedor e padronizar o código. A classe JDBCUtil é um utilitário para as

365

Page 366: Introducao Ao Demoiselle Framework Versao 1 PDF

configurações JDBC e também a responsável por inserir uma conexão no controle transacional definido pelo módulo Core. Finalmente, a classe JDBCTransactionResource representa uma conexão JDBC e pode ser tratada pelo contexto de transação do framework.

Figura 55: Módulo Persistence

O uso de JDBCUtil para gerenciar conexões pode ser visto neste exemplo de consulta:

public class FuncionarioDAO extends JDBCGenericDAO<Funcionario> implements IFuncionarioDAO {

public List<Funcionario> listar() {

List<Funcionario> result = new ArrayList<Funcionario>();

PreparedStatement prepStmt = null;

Connection con = null;

try {

con = JDBCUtil.getInstance().getConnection();

prepStmt = con.prepareStatement("SELECT * FROM Funcionario");

ResultSet rs = prepStmt.executeQuery();

while (rs.next()) {

long id = rs.getLong("id_funcionario");

366

Page 367: Introducao Ao Demoiselle Framework Versao 1 PDF

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 alteração no código-fonte das classes, a configuração das conexões JDBC é feita no arquivo demoiselle.properties, como mostra o exemplo a seguir:

#Configuração 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 módulo Persistence define uma interface chamada IORMDAO, que estende IDAO e define dois métodos, findByExample() e findById(). É possível integrar qualquer framework especialista de persistência que use ORM, desde que seja definida uma classe que implemente IORMDAO e outra que seja um recurso transacional.

A versão 1.0.x do Demoiselle utiliza o Hibernante como framework de persistência. O pacote br.gov.framework.demoiselle.persistence.hibernate define a integração dos frameworks com três classes: HibernateGenericDAO, HibernateUtil e HibernateTransactionResource.

A partir da versão 1.1.x, o JPA foi adotado como padrão de persistência. O pacote br.gov.framework.demoiselle.persistence define a integração do framework Demoiselle

367

Page 368: Introducao Ao Demoiselle Framework Versao 1 PDF

com os provedores de persistência JPA por meio de três classes: JPAGenericDAO, EntityManagerProxy e JPATransactionResource.

Figura 56: Persistência com JDBC

A matéria-prima da camada de persistência são classes Pojo, que representam as entidades da aplicação. Um exemplo é a classe Aluno, a seguir:

package br.gov.framework.demoiselle.escola.bean;

public class Aluno implements IPojo{

private Long id;

public Aluno() {}

public Long getId() {

368

Page 369: Introducao Ao Demoiselle Framework Versao 1 PDF

return id;

}

public void setId(Long id) {

this.id = id;

}

}

Figura 57: Persistência com Hibernate

A arquitetura de referência define para as aplicações um pacote br.gov.demoiselle.nomedaaplicacao.persistence, no qual ficam as interfaces que

369

Page 370: Introducao Ao Demoiselle Framework Versao 1 PDF

estendem IDAO e as implementações das mesmas. A classe WebDAOFactory é usada por padrão para construir os objetos por meio de convenção. A convenção é que as interfaces e suas implementações devem ter a mesma identificação, sendo que as interfaces devem ter o prefixo I e o sufixo DAO, enquanto as implementações devem omitir o prefixo. Por exemplo, a interface IAlunoDAO deve ser implementada pela classe AlunoDAO.

É possível modificar a fábrica que fará a injeção de dependências, estendendo WebDAOFactory e usando o parâmetro factory da anotação @Factory, conforme exemplo a seguir:

public class EscolaDAOFactory extends WebDAOFactory {

public IDAO create(InjectionContext ctx) {

return //Lógica da Fábrica;

}

}

@Factory(factory=EscolaDAOFactory.class)

public class AlunoDAOStubTest implements IFacade{

@Injection

private IAlunoDAO alunoDAO;

}

As operações de persistência fazem uso do contexto de transação, conforme vemos no código seguinte:

public class MinhaClasse {

@Injection

IMeuDAO meuDao;

public void listar() {

WebTransactionContext.getInstance().init();

meuDao.listar();

WebTransactionContext.getInstance().end();

}

}

370

Page 371: Introducao Ao Demoiselle Framework Versao 1 PDF

A classe HibernateUtil provê um conjunto de implementações para camada de persistência baseado no framework Hibernate. Ela é integrada ao contexto de transação, de modo que possui um mecanismo transacional completo. O código a seguir ilustra dessa classe para tratar tanto o commit quando o rollback de uma transação:

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 paginação de dados definida pelo módulo Util é usada pelo módulo Persistence para manter o controle sobre os dados oriundos de uma consulta e recuperar da base apenas as informações que serão exibidas na visão do usuário. Os benefícios da paginação são a redução de custos de uso de memória, processamento e rede e a escalabilidade proporcionada. O código a seguir ilustra o uso de paginação. O trecho de código é de uma classe herdeira de HibernateGenericDAO:

public void listar() {

Page page = new Page(50, 1);

listarBean(page);

}

public PagedResult<Aluno> listarBean(Page page) {

return findHQL("from Bean", page);

371

Page 372: Introducao Ao Demoiselle Framework Versao 1 PDF

}

HibernateGenericDAO implementa um conjunto de funções (consulta, paginação, inserção, alteração e exclusão) para simplificar a criação de classes IDAO implementadas pela aplicação. Um exemplo de como se fazer consultas com HibernateGenericDAO pode ser visto no código 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;

}

public PagedResult<Disciplina> filtrar(Disciplina prof, Page page) {

return findByExample(prof, page);

}

public List<Disciplina> listar() {

return findHQL("from Disciplina order by nome asc");

}

}

7.3.2.5 Módulo View

Este módulo contém implementações de componentes específicos de interface com usuário baseados na especificação JSF. Uma vez que a especificação JSF define a utilização de um controlador MVC denominado managed bean, o módulo View

372

Page 373: Introducao Ao Demoiselle Framework Versao 1 PDF

disponibiliza uma implementação padronizada para ser utilizada nas aplicações. É a classe AbstractManagedBean.

A classe AbstractManagedBeanConfig representa as configurações da aplicação do total de linhas e a quantidade de páginas ao paginar dados.

Para facilitar as operações com managed beans, o framework disponibiliza a classe ManagedBeanUtil. O módulo View também provê a classe PagedResultDataModel, um modelo de dados que converte um PagedResult para a representação gráfica dos dados paginados.

373

Page 374: Introducao Ao Demoiselle Framework Versao 1 PDF

Figura 58: Módulo View

Figura 59: Controle de visão

Finalmente, o módulo View define a classe CookieManager, um utilitário que permite a realização das operações básicas relacionadas a cookies na Web.

374

Page 375: Introducao Ao Demoiselle Framework Versao 1 PDF

7.3.2.3 Modelo de Arquitetura em Camadas do Demoiselle

A arquitetura de referência do Demoiselle Framework propõe para as aplicações uma estrutura baseada na combinação das camadas do padrão MVC com as camadas propostas pelos padrões da plataforma JEE.

Figura 60: Utilitários da camada de visão

Figura 61: Manipulador de Cookies

O padrão MVC, segundo Fowler (2006, p. 315) “considera três papéis”. O modelo, a vista e o controlador. De acordo com o autor, “o modelo é um objeto que representa alguma informação sobre o domínio. É um objeto não-visual contendo todos os dados e comportamento que não os usados pela interface de usuário”. A vista, por sua vez, “representa a exibição do modelo na interface com o usuário. Assim, se nosso modelo for um objeto cliente nossa vista poderia ser um frame cheio de controles para a

375

Page 376: Introducao Ao Demoiselle Framework Versao 1 PDF

interface com o usuário ou uma página HTML com informações do modelo”. As alterações nas informações apresentadas pela vista “são manipuladas pelo terceiro membro da tríade MVC: o controlador. O controlador recebe a entrada do usuário, manipula o modelo e faz com que a vista seja atualizada apropriadamente. Dessa forma, a interface de usuário é uma combinação da vista e do controlador”.

Figura 62: Camadas do Padrão MVC

Os padrões que compõem o catálogo JEE (Alur et alli, 2002, p.8), são agrupados em três camadas, a saber: camada de apresentação, camada de negócios e camada de integração. A camada de apresentação 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 responsáveis pelo atendimento às requisições de serviço, que culminarão na exibição das visões da aplicação. A camada de negócios, segundo Alur et alli (2002, p. 19) “trata da principal lógica de negócios da aplicação. Ela fornece as interfaces necessárias aos componentes de serviços de negócios subjacentes”. Finalmente, temos a camada de integração, “responsável pelo sistema de informações da empresa, incluindo os sistemas de banco de dados, o de processamento de transação, os sistemas herdados e os sistemas de planejamento de recurso da empresa”. Nessa camada ocorre a integração com sistemas que não são JEE e com os sistemas herdados.

376

Page 377: Introducao Ao Demoiselle Framework Versao 1 PDF

Figura 63: Camadas da plataforma JEE

O modelo de camadas da arquitetura de referência do Demoiselle combina os modelos anteriores de modo a aproveitar o melhor de cada um. A camada de vista do MVC e de apresentação do JEE são praticamente as mesmas. A camada de controle do MVC pode ser combinada com a camada referente a interface com o usuário, pois a requisição de serviço sempre vem por uma interface e o resultado da solicitação também é exibido por meio de uma. Dentro desse entendimento, o Demoiselle propõe para as aplicações uma camada chamada Controle e Visão. Essa é a camada mais superior da arquitetura de referência.

A camada de negócios é aproveitada integralmente pelo Demoiselle, como camada de regras de negócio. E as camadas de modelo do MVC e integração do JEE são combinadas na camada mais inferior da arquitetura de referência do Demoiselle: a de persistência.

Figura 64: Camadas de aplicação Demoiselle

377

Page 378: Introducao Ao Demoiselle Framework Versao 1 PDF

7.4 Conclusão

Um framework direciona o desenvolvedor aos problemas relacionados as regras de negócio do cliente e a apresentação das informações e reduz o esforço utilizado para resolver detalhes de baixo nível como segurança, acesso a dados e comunicação com outros ambientes.

O Demoiselle Framework cumpre esse papel, ao implementar a padronização da implementação e criar um ambiente propício para a prática da reusabilidade.

A padronização garante maior facilidade de suporte e absorção de sistemas e facilidade de integração e disponibilização de serviços para os novos sistemas. Essa padronização é resultado da análise, integração e utilização de tecnologias mais reconhecidas utilizadas pelas comunidades de desenvolvedores.

O modelo de desenvolvimento baseado em componentes facilita o reuso de métodos, práticas e processos padronizados, e termina por permitir o reuso de componentes de negócio.

A sua característica de ser aberto, com desenvolvimento compartilhado, e de agregar implementações de software baseadas em padrões e especificações lhe dá condições de ser uma ferramenta totalmente voltada a integração de diferentes instituições e diferentes tecnologias.

O Demoiselle Framework abre caminho para a estruturação da análise de sistemas em domínios, de modo a criar uma gestão orientada a políticas de conteúdos e promoções de conteúdos. Porque a infraestrutura de software não é o mais o problema, o que abre espaço para os desenvolvedores se preocuparem em os problemas relacionados a um determinado domínio de conhecimento.

O Demoiselle atende prioritariamente a três domínios específicos: a integração de organizações do Governo com a sociedade, a integração dentro das organizações e a integração de grupos de sistemas. Isso o torna uma plataforma de desenvolvimento de uso geral, que pode ser mantida e usufruída por toda a sociedade, e evoluída de modo a beneficiar a todos.

378

Page 379: Introducao Ao Demoiselle Framework Versao 1 PDF

APÊNDICE A

Demoiselle WizardA criação de classes para a aplicação livrariademoiselle foi feita manualmente, mas existe um plugin para Eclipse que ajuda na geração de código no padrão Demoiselle Framework.

O plugin Demoiselle Wizard pode ser instalado via updatesite pela URL http://demoiselle.sourceforge.net/wizard/updatesite. Ele possui as seguintes funcionalidades:

• Configurar projeto: Permite a configuração de variáveis e mensagens da aplicação, está distribuída em quatro grupos: Configuração do Hibernate, Configuração de JPA, configuração dos arquivos de recursos da aplicação e o cadastro das mensagens da aplicação.

• Editar projeto: Destina-se a manutenção das diversas camadas de uma aplicação que utiliza o Demoiselle (criação, edição e remoção). Realiza a geração de DAOs, Business Controllers, Managed Beans, Fachadas, regras de navegação da aplicação e testes unitários

• Criar páginas: Destina-se a criação de páginas JSP, as páginas podem ser de três tipos: Listagem, Edição e Visualização.

• Criar CRUD: Destina-se a criação de fluxos do tipo CRUD (Create; Remove; Update e Delete) de entidades a partir de um Pojo selecionado.

• Adicionar características do Demoiselle: Destina-se a transformação de projetos existentes na workspace em projetos do tipo Demoiselle que utilizam o framework Demoiselle.

• Remover características do Demoiselle: Destina-se a transformação de projetos do tipo Demoiselle existentes na workspace em projetos Web, removendo assim as características do framework Demoiselle.

379

Page 380: Introducao Ao Demoiselle Framework Versao 1 PDF

APÊNDICE B

Script para criação do banco de dadosO modo mais fácil de instalar o PostgreSQL é usando os pacotes binários. A página http://www.postgresql.org/download contém uma seção chamada Binary packages, onde você pode encontrar distribuições para os sistemas operacionais mais utilizados. Esses pacotes geralmente contém o pgAdmin III, uma interface gráfica para administração de bancos PostgreSQL.

CREATE DATABASE livraria

WITH OWNER = postgres

ENCODING = 'UTF8'

LC_COLLATE = 'pt_BR.UTF-8'

LC_CTYPE = 'pt_BR.UTF-8'

CONNECTION LIMIT = -1;

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;

CREATE TABLE clientes

(

380

Page 381: Introducao Ao Demoiselle Framework Versao 1 PDF

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

Page 382: Introducao Ao Demoiselle Framework Versao 1 PDF

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

Page 383: Introducao Ao Demoiselle Framework Versao 1 PDF

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

Page 384: Introducao Ao Demoiselle Framework Versao 1 PDF

WITH (

OIDS=FALSE

);

ALTER TABLE telefones_cliente OWNER TO postgres;

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

Page 385: Introducao Ao Demoiselle Framework Versao 1 PDF

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

Page 386: Introducao Ao Demoiselle Framework Versao 1 PDF

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

Page 387: Introducao Ao Demoiselle Framework Versao 1 PDF

APÊNDICE C

Script para apagar as tabelasdrop table itens_pedido;

drop table pedidos;

drop table autores_livro;

drop table livros;

drop table autores;

drop table editoras;

drop table locais;

drop table telefones_cliente;

drop table clientes;

drop table telefones_funcionario;

drop table funcionarios;

drop table telefones;

387

Page 388: Introducao Ao Demoiselle Framework Versao 1 PDF

APÊNDICE D

Driver JDBC para PostgreSQLO driver JDBC para PostgreSQL está disponível em http://jdbc.postgresql.org/download.html. Nas aplicações deste livro foi utilizado a versão 8.4 para JDBC 3. O driver foi instalado no diretório de bibliotecas da JDK (jre/lib/ext), para que não fosse necessária modificação do Build Path. Lembrando que o JDBC 3 é para a JDK 5. Para usar na JDK 6, é necessário o JDBC 4.

388

Page 389: Introducao Ao Demoiselle Framework Versao 1 PDF

Referências BibliográficasAllen, P. The Service Oriented Process. CBDI Journal. Fevereiro. 2007. Soa Best Practice

Report. Disponível em <http://www.cbdiforum.com/secure/interact/2007-02/service_oriented_process.php>. Acesso em 21/07/2009.

APACHE SOFTWARE FOUNDATION. Open Letter to Sun Microsystems. Disponível em <http://www.apache.org/jcp/sunopenletterfaq.html>. Acesso em 29/07/2009.

Basham, B. Sierra, K. e Bates, B. Use a Cabeça! Servlets & JSP. Rio de Janeiro. Alta Books, 2005.

Booch, G. Rumbaugh J. Jacobson, I. The Unified Modeling Language User Guide, Addison-Wesley, Reading, MA, 1998.

BRASIL. Ministério do Planejamento, Orçamento e Gestão. Documento de Referência da e-PING – Versão 4.0. 16/12/2008. Disponível 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. Disponível em <http://refcardz.dzone.com/refcardz/getting-started-eclipse>. Acesso em 23/04/2010.

Casett, O. Akamatu, D. M. Kirner, C. Paradigmas para construção de sistemas distribuídos. Tema. nº 114. Ano III. 1993. Disponível 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. Disponível em <http://www.eclipse.org/webtools/community/tutorials/BuildJ2EEWebApp/BuildJ2EEWebApp.html>. Acesso em 26/04/2010.

Deitel, H. M. E Deitel, P. J. Java: Como Programar. 6.ed. São 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. Ciência Moderna, 2000.

Fogel, K. Producing Open Source Software: How to Run a Successful Free Software Project. Disponível em <http://producingoss.com/en/producingoss.pdf>. Acesso em 03/08/2009.

Fowler, Martin. Inversion of Control Containers and the Dependency Injection pattern. Disponível em <http://martinfowler.com/articles/injection.html>. Acesso em 14/08/2009.

389

Page 390: Introducao Ao Demoiselle Framework Versao 1 PDF

Fowler, Martin. Padrões de Arquitetura de Aplicações Corporativas. Porto Alegre. Bookman, 2006.

Fowler, M. POJO. Disponível 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.

Gonçalves, E. Desenvolvendo Aplicações Web com JSP, Servlets, JavaServer Faces, Hibernate, EJB 3 Persistence e Ajax. Rio de Janeiro. Ciência Moderna, 2007.

Grohs, E. M. et al. Framework JSerpro. Congresso Serpro de Tecnologia e Gestão Aplicadas a Serviços Públicos, 2007.

Hall, M. e Brown, L. Core Servlets e JavaServer Pages. Volume 1: Tecnologias Core. Rio de Janeiro, Ciência 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. Disponível em <http://freesoftware.mit.edu/papers/lakhaniwolf.pdf>. Acesso em 13/05/2009.

Macias, A. M. Frameworks de Desenvolvimento – Visão Geral. Tematec, Ano X, nº XX, p. 1, 2008. Disponível em <www.serpro.gov.br/clientes/serpro/serpro/imprensa/publicacoes/tematec/2008/ttec92_a>. Acesso em 28/07/2007.

Mariaca, M. Gestão da Diversidade. Disponível em <http://imasters.uol.com.br/artigo/12603/tendencias/gestao_da_diversidade>. Acesso em 17/07/2009.

McConnell, S. Code Complete: Um Guia Prático para a Construção de Software. Porto Alegre. Bookman, 2005.

McLaughkin, Brett. Pollice, Gary. e West, David. Análise e Projeto Orientado ao Objeto. Rio de Janeiro. Alta Books, 2007.

Miller, J. Padrões na Prática: Coesão e acoplamento. Disponível em <http://msdn.microsoft.com/pt-br/magazine/cc947917.aspx>. Acesso em 21/07/2009.

Pacitti, Tércio. Paradigmas do Software Aberto. Rio de Janeiro. Livros Técnicos e Científicos, 2006.

Paula Filho, Wilson P. Engenharia de Software: Fundamentos, Métodos e Padrões. 2.ed. Rio de Janeiro. Livros Técnicos e Científicos, 2003.

Peltz, C. Web Services Orchestration and Choreography. Disponível em <http://soa.sys-con.com/node/39800>. Acesso em 21/07/2009.

390

Page 391: Introducao Ao Demoiselle Framework Versao 1 PDF

Pinho, V. D. M. Processo de Desenvolvimento Demoiselle. Disponível em <http://demoiselle-proc.sourceforge.net>. Acesso em 29/03/2010.

Pressman, R. S. Engenharia de Software. 6.ed. São Paulo. McGraw-Hill, 2006.

Raymond. Eric. S. A Catedral e o Bazar. Disponível em <http://www.dominiopublico.gov.br/download/texto/tl000001.pdf>. Acesso em 03/08/2009.

Reese, G. Programação para banco de dados: JDBC e Java. 2.ed. São Paulo. Berkeley, 2001.

Silberschatz, A. Knorth, H. F. Sudarshan, S. Sistema de Banco de Dados. 3.ed. São Paulo. Makron Books, 1999.

Silveira, G. et al. Java para desenvolvimento Web. São 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. São Paulo. Pearson Addison-Wesley, 2007.

Srinivasan, Raghu. N. WTP Tutorials – JavaServer Faces Tools Tutorial. Disponível 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: Situação atual e tendências. I Workshop sobre Software no Ambiente Corporativo. 2004. São Paulo. Disponível em <http://www.poli.usp.br/pro/sl/IWSLAC/Apresentacao_Cezar_Taurion.pdf>. Acesso em 04/08/2009.

WIKIPEDIA. Brimstone. Disponível em <http://en.wikipedia.org/wiki/Brimstone_(TV_series)>. Acesso em 11/09/2008.

Winck, D. V. e Goetten JUNIOR, V. AspectJ: Programação Orientada a Aspectos com Java. São Paulo. Novatec, 2006.

391