projeto jedi - programação para web - java - 178 páginas

178
Módulo 6 Programação WEB Lição 1 Introdução à Programação WEB Versão 1.0 - Nov/2007

Upload: augustonunes

Post on 15-Jun-2015

1.871 views

Category:

Documents


11 download

TRANSCRIPT

Page 1: Projeto JEDI - Programação para WEB - Java - 178 páginas

Módulo 6Programação WEB

Lição 1Introdução à Programação WEB

Versão 1.0 - Nov/2007

Page 2: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

AutorDaniel Villafuerte

EquipeRommel FeriaJohn Paul Petines

Necessidades para os ExercíciosSistemas Operacionais SuportadosNetBeans IDE 5.5 para os seguintes sistemas operacionais:

• Microsoft Windows XP Profissional SP2 ou superior• Mac OS X 10.4.5 ou superior• Red Hat Fedora Core 3 • Solaris™ 10 Operating System (SPARC® e x86/x64 Platform Edition)

NetBeans Enterprise Pack, poderá ser executado nas seguintes plataformas:• Microsoft Windows 2000 Profissional SP4• Solaris™ 8 OS (SPARC e x86/x64 Platform Edition) e Solaris 9 OS (SPARC e

x86/x64 Platform Edition) • Várias outras distribuições Linux

Configuração Mínima de HardwareNota: IDE NetBeans com resolução de tela em 1024x768 pixel

Sistema Operacional Processador Memória HD Livre

Microsoft Windows 500 MHz Intel Pentium III workstation ou equivalente

512 MB 850 MB

Linux 500 MHz Intel Pentium III workstation ou equivalente

512 MB 450 MB

Solaris OS (SPARC) UltraSPARC II 450 MHz 512 MB 450 MB

Solaris OS (x86/x64 Platform Edition)

AMD Opteron 100 Série 1.8 GHz 512 MB 450 MB

Mac OS X PowerPC G4 512 MB 450 MB

Configuração Recomendada de Hardware

Sistema Operacional Processador Memória HD Livre

Microsoft Windows 1.4 GHz Intel Pentium III workstation ou equivalente

1 GB 1 GB

Linux 1.4 GHz Intel Pentium III workstation ou equivalente

1 GB 850 MB

Solaris OS (SPARC) UltraSPARC IIIi 1 GHz 1 GB 850 MB

Solaris OS (x86/x64 Platform Edition)

AMD Opteron 100 Series 1.8 GHz 1 GB 850 MB

Mac OS X PowerPC G5 1 GB 850 MB

Requerimentos de SoftwareNetBeans Enterprise Pack 5.5 executando sobre Java 2 Platform Standard Edition Development Kit 5.0 ou superior (JDK 5.0, versão 1.5.0_01 ou superior), contemplando a Java Runtime Environment, ferramentas de desenvolvimento para compilar, depurar, e executar aplicações escritas em linguagem Java. Sun Java System Application Server Platform Edition 9.

• Para Solaris, Windows, e Linux, os arquivos da JDK podem ser obtidos para sua plataforma em http://java.sun.com/j2se/1.5.0/download.html

• Para Mac OS X, Java 2 Plataform Standard Edition (J2SE) 5.0 Release 4, pode ser obtida diretamente da Apple's Developer Connection, no endereço: http://developer.apple.com/java (é necessário registrar o download da JDK).

Para mais informações: http://www.netbeans.org/community/releases/55/relnotes.html

Programação WEB 2

Page 3: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

Colaboradores que auxiliaram no processo de tradução e revisãoAécio JúniorAlexandre MoriAlexis da Rocha SilvaAllan Souza NunesAllan Wojcik da SilvaAngelo de OliveiraAurélio Soares NetoBruno da Silva BonfimCarlos Fernando Gonçalves

Denis Mitsuo NakasakiEmanoel Tadeu da Silva FreitasFelipe GaúchoJacqueline Susann BarbosaJoão Vianney Barrozo CostaLuciana Rocha de OliveiraLuiz Fernandes de Oliveira Junior Marco Aurélio Martins BessaMaria Carolina Ferreira da Silva

Massimiliano GiroldiMauro Cardoso MortoniPaulo Oliveira Sampaio ReisPedro Henrique Pereira de AndradeRonie DotzlawSergio TerzellaThiago Magela Rodrigues DiasVanessa dos Santos AlmeidaWagner Eliezer Roncoletta

Auxiliadores especiais

Revisão Geral do texto para os seguintes Países:

• Brasil – Tiago Flach• Guiné Bissau – Alfredo Cá, Bunene Sisse e Buon Olossato Quebi – ONG Asas de Socorro

Coordenação do DFJUG

• Daniel deOliveira – JUGLeader responsável pelos acordos de parcerias• Luci Campos - Idealizadora do DFJUG responsável pelo apoio social• Fernando Anselmo - Coordenador responsável pelo processo de tradução e revisão,

disponibilização dos materiais e inserção de novos módulos• Rodrigo Nunes - Coordenador responsável pela parte multimídia• Sérgio Gomes Veloso - Coordenador responsável pelo ambiente JEDITM (Moodle)

Agradecimento EspecialJohn Paul Petines – Criador da Iniciativa JEDITM

Rommel Feria – Criador da Iniciativa JEDITM

Programação WEB 3

Page 4: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

1. ObjetivosBem-vindo a este curso de programação WEB. Começaremos com uma ampla introdução, pois é interesse das companhias (empresas) e dos programadores, conhecer sobre programação para a WEB.

Ao final desta lição, o estudante será capaz de:

• Descrever como funciona a WEB

• Definir a arquitetura do tipo Cliente-Servidor

• Entender sobre o protocolo HTTP

• Definir o básico sobre a arquitetura Java EE

• Saber o que são Servlets e Java Server Pages

Programação WEB 4

Page 5: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

2. Porque migrar para WEB?

2.1. Ambiente de Tecnologia Neutra

Um dos principais benefícios em aplicações para a Internet, é que a Internet é um ambiente de tecnologia neutra. A comunicação em qualquer aplicação na WEB é feita através de protocolos populares (HTTP/FTP), que não requerem que o usuário nem o cliente tenham qualquer sistema operacional em particular instalado em sua máquina ou seja desenvolvida em uma linguagem de programação específica. Tudo que os clientes necessitam é de um navegador WEB (denominado Browser), o acesso a aplicação e qualquer sistema operacional. Isto traz diversas possibilidades dentro de uma ampla gama de aplicações baseadas na WEB.

2.2. Facilidade de distribuição e atualização

Visto que o navegador WEB é o único programa instalado que os usuários irão necessitar, não há necessidade de fornecer programas adicionais através de CDs ou outra mídia. Assim, como não existe a necessidade do usuário repassar uma seqüência de instalação, possivelmente demorada, o que será necessário é o local e acesso a aplicação na internet, e tudo estará pronto para funcionar.

Outro benefício de se ter o código binário exato do programa, que residirá em um único servidor acessível, ao invés de instalado no computador do usuário, pois evita-se possíveis problemas comuns relatados com atualizações de programas, tais como a necessidade de checar periodicamente novas versões de programas. O problema de conseguir novas atualizações dos programas e eliminado completamente. O usuário não precisa ser informado sobre uma atualização do programa; tudo que será necessário é atualizar o código no servidor WEB e, automaticamente, todos os usuários irão usufruir da nova versão.

Programação WEB 5

Page 6: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

3. Arquitetura Cliente-Servidor

3.1. Cliente Pesado e Cliente Magro

Uma aplicação WEB é um tipo de aplicação que faz uso da chamada estrutura Cliente-Servidor. Neste tipo, um programa cliente conecta a um servidor para acessar informações que necessita para completar as tarefas que os usuários desejam para realizar. Há os que são chamados clientes magros, e os que são chamados clientes pesados.

Clientes magros são os que contém apenas o mínimo necessário para que o usuário execute o sistema. Em geral, é apenas uma casca. Todos as regras de negócio, dados, exceto os que são fornecidos pelo usuário, residem dentro de um servidor.

Clientes pesados são os que contém, além de uma interface, alguns, se não muitos, dos processos de regra de negócio requeridos pelas tarefas específicas dos usuários.

3.2. Arquitetura Cliente-Servidor na visão WEB

Na definição acima, podemos citar que o cliente utilizado para aplicações WEB são os que nós chamamos de clientes magros. O programa cliente, um navegador, neste caso, é apenas uma interface que o usuário utiliza para realizar tarefas. Tudo mais, como os dados que o usuário precisa para operar num determinado fluxo lógico de execução do programa, reside no servidor. De uma perspectiva mais focada na WEB, estas são as funções do servidor e do cliente:

3.2.1.Servidor WEB

O servidor recebe uma requisição do cliente através do navegador WEB e retorna uma resposta. Qualquer requisição vinda de um cliente inclui o nome e o endereço do item que o cliente está procurando, assim como, qualquer dado fornecido pelo usuário. O servidor assimila a requisição, a processa e retorna uma resposta dos dados procurados pelo cliente ou exibe um código de erro indicando que o item requisitado não existe no servidor.

3.2.2.Cliente WEB

É da responsabilidade do navegador prover ao usuário uma interface para exibir a resposta fornecida pelo servidor. Quando o usuário emite uma requisição para o servidor, por exemplo, buscar um documento ou submeter um formulário, o navegador deve enviar esta requisição de modo que o servidor possa entender.

Uma vez que o servidor finalizou o processo requisitado e enviou uma resposta, o navegador, que recebeu os dados requeridos da resposta do servidor, mostra a página resultante ao usuário.

Programação WEB 6

Figura 1: Funções do Servidor e do Cliente

Servidor processa as repostas com base nas requisições do cliente

Máquina rodando um WEB Server

Máquina rodando um Browser

Requisição do cliente (contém o nome e o

endereço de um item que o cliente está procurando)

Resposta do servidor (contém o documento requisitado ou um código de erro caso nada seja

encontrado)

Page 7: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

4. HTML

4.1. Como o browser sabe o que deve ser exibido ao usuário?

A maioria dos sites WEB não tem apenas um conteúdo simples de texto. Ao invés disso, emprega gráficos ou tem formas que acessam dados.

4.2. Como o navegador sabe o que deverá exibir?

A resposta reside em HTML, que são as iniciais para Hypertext Markup Language. HTML pode ser pensado como um conjunto de instruções para um navegador WEB que define como apresentar conteúdo para o usuário. É um padrão aberto, atualizado pela W3C ou a World Wide Web Consortium. Sendo este um padrão aberto, qualquer um pode acessá-lo. Isto também significa que os navegadores são desenvolvidos sobre esse padrão. Isso significa que todos os navegadores sabem o que fazer quando se deparam com HTML, embora alguns navegadores mais antigos possam ter problemas em interpretar algumas páginas que foram escritas usando novas versões de HTML que foram criadas após o desenvolvimento destes.

Programação WEB 7

Page 8: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

5. HTTP

5.1. Definição

HTTP significa Hypertext Transfer Protocol. É um protocolo de internet de rede de comunicações com características específicas da WEB, que funciona no topo de duas outras camadas de protocolo, TCP e IP.

TCP é um protocolo que é responsável por garantir que um arquivo enviado através de uma rede de comunicações seja entregue completamente e com sucesso no destino. IP é um protocolo que encaminha parte dos arquivos de um ponto para outro no seu destino.

O HTTP utiliza estes dois protocolos para ter certeza de que as requisições e respostas serão entregues corretamente ao final de cada pacote transferido.

O protocolo HTTP usa uma seqüência Requisição/Resposta (Request/Response): um cliente HTTP abre uma conexão e envia uma mensagem de requisição para um servidor HTTP. O servidor, então, retorna uma mensagem resposta, geralmente contendo o recurso que foi requerido. Após a entrega da resposta o servidor fecha a conexão fazendo uso de um protocolo stateless HTTP (não mantém qualquer informação de estado sobre a conexão).

O formato das mensagens Requisição/Resposta é semelhante e orientado. Ambos os tipos de mensagem consistem em:

• Um cabeçalho inicial• Zero ou mais cabeçalhos adicionais• Uma linha em branca (CRLF)• O corpo de mensagem (opcional). Ex. Um arquivo, dado ou serviço

5.2. Requisições HTTP

Requisições de um cliente para o servidor contêm a informação sobre o tipo de dado que o usuário necessita. Um dos itens de informação encapsulados no HTTP Request é a item method. Isto diz ao servidor como que este tipo de requisição está sendo feita, e como o resto da mensagem do cliente está formatada. Há dois métodos que serão encontrados: GET e POST.

5.3. Método GET

É o método mais simples que é usado principalmente para requisitar um recurso qualquer de um servidor, como uma página WEB, um arquivo de imagem gráfica, um documento, etc.

O método GET também pode ser utilizado para enviar dados para o servidor, embora isto tenha suas limitações. A quantidade de caracteres que podem ser encapsulados em uma requisição GET é limitada, então, para as situações onde muitas informações precisam ser enviadas para o servidor, é provável que nem todas as mensagens “sobreviverão“.

Outra limitação do método GET é no envio dos dados. Os dados enviados utilizando este método são simplesmente anexados à URL enviada ao servidor.

Por enquanto, pense na URL como um único endereço que é enviado ao servidor. Denota o destino ao qual será enviada a requisição. Um dos problemas encontrados neste método são as muitas URLs que podem ser exibidas na barra de navegação dos navegadores. Isto significa que dados confidenciais, tais como senhas ou informações do contato, serão expostas para qualquer pessoa.

A vantagem em se utilizar o método GET para enviar dados para o servidor é que a URL pode ser gravada como um bookmarker pelo navegador. Isto significa que o usuário pode simplesmente selecionar o item desejado e acessá-lo em vez de ter que passar por todo o processo novamente. Vale ressaltar que isto também pode ser perigoso, se a facilidade do bookmarker não é algo que os usuários deveriam ter.

Aqui está o que a URL criada com uma GET Request pode parecer:

Programação WEB 8

Page 9: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

http://jedi-master.dev.java.net/Servlets/NewsItemView?newsItemID=2359&filter=true

O item antes do sinal de interrogação (?) representa a URL original da requisição (neste caso é http://jedi-master.dev.java.net/Servlets/NewsItemView). Tudo após, são os parâmetros ou dados que são enviados juntos para o servidor. Olharemos mais atentamente para esta segunda parte. Estes são os parâmetros adicionados pela requisição:

newsItemID=2359&filter=true

Em requisições GET, parâmetros são codificados com pares nomes e valores. Não são enviados valores excedentes de dados para o servidor sem que este conheça especificamente seus nomes. Os pares nomes e valores são codificados como:

name=value

Além disso, caso exista mais de uma série de parâmetros, estes serão separados utilizando o símbolo &. Neste caso, os nomes dos parâmetros que estudamos são newsItemID e filter, e os valores 2359 e true, respectivamente.

5.4. Método POST

Outro método de requisição utilizado é o POST. Este tipo de requisição é utilizada de tal forma que as solicitações ao navegador podem se tornar complexas, isto é, para que o usuário, através do navegador, possa enviar muitos dados para o servidor. Formas complexas são geralmente envidas utilizando requisições POST, assim como, também, formas simples.

Uma diferença aparente entre os métodos GET e POST é o modo como eles enviam dados para o servidor. Como declarado antes, o método GET simplesmente anexa os dados à URL enviada. O método POST, por outro lado, esconde os dados dentro do corpo da mensagem enviada. Quando o servidor recebe a requisição e determina que ela é uma POST, procurará no corpo da mensagem pelos dados.

5.5. HTTP response (Resposta HTTP)

O HTTP response do servidor contém o cabeçalho e o corpo da mensagem, de maneira semelhante ao HTTP request. É utilizado um conjunto diferente de cabeçalhos. Os cabeçalhos contêm informações sobre a versão do protocolo HTTP que o servidor está vendo, assim como também o tipo de conteúdo que é escondido dentro do corpo da mensagem. O valor para o tipo de conteúdo é chamado de MIME-type. Informa ao navegador que a mensagem contém um HTML, imagem ou outros tipos de conteúdo.

5.6. Páginas Dinâmicas ou Estáticas

O tipo de conteúdo que pode ser oferecido por um servidor WEB pode ser estático ou dinâmico. Conteúdo estático é o conteúdo que não é modificado. Este tipo de conteúdo geralmente não executa nada quando o servidor é acessado e é criado durante sua requisição. Quando estes conteúdos são enviados através da resposta do servidor, são enviados exatamente o mesmo conteúdo armazenado no servidor. Exemplos de conteúdos estáticos incluem artigos de jornal arquivados, fotos de família, ou uma cópia de um documento.

O conteúdo dinâmico, por outro lado, muda de acordo com a requisição do cliente. Tais aplicações no servidor acessam este tipo de conteúdo seguindo um modelo pré-determinado, de acordo com o documento a ser enviado.

Este modelo é então preenchido de acordo com os parâmetros enviados pelo usuário e retornam ao cliente. É suficiente dizer que páginas dinâmicas tem muito mais flexibilidade e tem mais utilidade que as páginas estáticas. Aqui estão cenários onde o conteúdo dinâmico será a única que irá atender às necessidades do usuário.

• A página WEB é baseada em dados submetidos pelo usuário. Por exemplo, os resultados das páginas de pesquisa são gerados deste modo. Programas que processam

Programação WEB 9

Page 10: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

pedidos e sites de comércio fazem isto muito bem.

• Dados mudam freqüentemente. Uma página com a previsão do tempo ou novas notícias atualizadas constantemente podem construir a página dinamicamente, talvez retornando a uma página previamente construída se ela ainda estiver atualizada.

• A página WEB utiliza informações de um banco de dados. Realizado pelo acesso e busca ao banco de dados da empresa.

É importante perceber que o servidor WEB sozinho não tem capacidade de apresentar conteúdo dinâmico. Os servidores WEB precisaram separar das aplicações as informações que iriam armazenar informações pertinentes ao cliente (tais como dados coletados em formulários) dentro do depósito de páginas. Não se pode esperar que, a partir de um formulário, ter os dados do cliente, quando submetidos ao servidor, este conhecerá automaticamente o que deverá ser feito com estes dados.

Estamos, agora, no ponto da discussão onde podemos, explicitamente, apontar que é esta a questão principal das aplicações WEB e formam a base para o nosso curso.

Neste curso iremos nos voltar, primeiramente, às tecnologias baseadas em Java para criar aplicações WEB. Mais especificamente, faremo um uso excessivo de bibliotecas fornecidas a nível de especificação.

Programação WEB 10

Page 11: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

6. Java EE – Visão sobre a camada WEBA plataforma Java EE foi criada para o desenvolvimento de aplicações corporativas (baseada em componentes). O modelo de aplicação utilizada para esta plataforma é chamado Modelo de Aplicação Multi-Camadas Distribuídas, ou, simplesmente, multi-tier. O aspecto de distribuição deste modelo simplesmente significa que a maior parte das aplicações programadas e desenvolvidas com esta plataforma em mente podem ter diferentes componentes instalados em diferentes máquinas. A parte multi-tier significa que as aplicações são concebidas visando a separação entre as camadas dos vários componentes importantes da aplicação. Um exemplo de uma aplicação multi-tier é uma aplicação WEB: a camada de apresentação (representada pelo navegador), a camada de lógica de negócio (o programa que reside no servidor WEB), e a camada de dados (a base de dados que irá armazenar e gerenciar os dados da aplicação) são distintamente separadas, entretanto são necessários para se criar uma aplicação para o usuário.

Um dos níveis na plataforma Java EE, como previamente mencionado é a web-tier. Este nível é destinado a camada que interage com o navegador para criar o conteúdo dinâmico. Existem duas tecnologias dentro desta camada: Servlets e JavaServer Pages – JSP.

6.1. Servlets

A tecnologia Servlet foi a resposta inicial de Java para acrescentar a funcionalidade adicional aos servidores que utilizavam o modelo requisição/resposta. Possui a habilidade de ler dados contidos na requisição (request) passada ao servidor e gerar uma resposta dinâmica baseada nestes dados.

Servlet não é necessariamente limitada as situações baseadas em HTTP. Como declarado antes, são aplicáveis para qualquer caso que utilize o modelo requisição/resposta. As situações baseadas em HTTP são, atualmente, o uso desta tecnologia. Então, Java forneceu uma versão de classes específicas que implementam as características de HTTP.

6.2. JavaServer Pages

Uma das desvantagens de se utilizar a tecnologia de classes Servlets para gerar uma resposta ao cliente, é que primeiro essa resposta deve ser formatada em HTML para ser reenviada. Já que que Servlets são simplesmente classes em linguagem de Java, produzem resultados de modo semelhante a que outras classes Java fariam: através da impressão de caracteres em formato String pelo canal de saída, neste caso a HTTP response. Entretanto, HTML pode se tornar bastante complexo e ser muito difícil codificar HTML através do uso de uma String. Além disso, o emprego de recursos gráficos dedicados e páginas WEB programadas para ajudar na parte estática das

Programação WEB 11

Figura 2: A camada WEB da plataforma Java EE (Imagem do documento J2EE Tutorial)

Page 12: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

páginas é difícil. Estaríamos supondo que a equipe de desenvolvimento deva ter um bom conhecimento de Java.

Deste modo surgiu a tecnologia JavaServer Pages. A JSP se parece com HTML, tendo acesso a todas as habilidades dinâmicas das classes Servlets através do uso de scripts e linguagem de expressão. Visto que se parece muito com HTML, os projetista podem se concentrar no simples formato HTML e realizar as modificações necessárias e permite que os desenvolvedores ocupem-se do conteúdo dinâmico.

6.3. Contêiner

O conceito central de qualquer aplicação Java EE é o contêiner. Todo componente Java EE, inclui componentes WEB (Servlet ou JSP) que dependem da existência de um contêiner. Sem o contêiner apropriado, não seriam executados.

Talvez outro modo de explicar isto seria pensar sobre o modo normal de execução dos programas Java. Programas Java, para serem executados, devem ter um método principal definido. Isto marca o começo da execução do programa, sendo este método processado quando o programa é executado na linha de comando.

Como veremos mais tarde, a classe Servlet, não têm um método principal definido. E se existe algum definido (bad programming design) este não marca o começo da execução do programa. Quando o usuário faz uma requisição HTTP (http request) para uma classe Servlet, seu método não é chamado diretamente.

Em vez disso, o servidor passa a requisição não para a classe Servlet, mas para o contêiner na qual a Servlet está inserida. O contêiner é o único responsável pela chamada ao método apropriado na Servlet, dependendo do tipo de requisição do usuário.

6.4. Vantagens fornecidas pelo contêiner

Suporte de comunicações. O contêiner passa todo código necessário para a Servlet para se comunicar com o servidor WEB. Sem o contêiner, desenvolvedores precisariam escrever o código

Programação WEB 12

Figura 3: Contêineres da plataforma Java EE (Imagem do documento J2EE Tutorial)

Page 13: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

que iria criar uma conexão socket do servidor para a Servlet e vice-versa e ainda deveria gerenciar como eles falam um ao outro a cada momento.

Gerenciamento do Ciclo de Vida. O contêiner conhece o que se passa na vida de suas classes Servlets desde seu carregamento, instalação, inicialização e recolhimento pelo Garbage Collector.

Suporte a múltiplas tarefas. O contêiner controla a função de criar uma nova thread a cada momento que uma requisição para a classe Servlet é realizada. NOTA: o contêiner NÃO será responsável pelas thread de sua Servlet.

Declaração de Segurança. Um contêiner suporta o uso de um arquivo de configuração XML que pode repassar diretivas de segurança para sua aplicação WEB sem precisar de um código complexo qualquer dentro do Servlet.

Suporte JSP. Páginas JSP, para funcionarem, devem ser transformadas em uma classe Java (Servlet). O contêiner controla a tarefa de traduzir as páginas JSP para uma classe Servlet, compilar e executar.

Programação WEB 13

Page 14: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

7. Estrutura Básica de uma aplicação WEBPara um determinado contêiner reconhecer sua aplicação como aplicação WEB válida, esta deve se adequar a uma estrutura específica de diretório conforme a figura a seguir.

Figura 4: Estrutura do Directório de uma aplicação Java WEB

A figura mostra a estrutura do diretório requisitado pelo contêiner para que sua aplicação seja reconhecer. Alguns pontos relativos a esta estrutura são:

1. A Pasta Raiz (contém a aplicação) não precisa ser nomeada como Pasta Raiz. Pode ser, qualquer nome que se deseje, embora seja altamente recomendado que o nome desta pasta seja o nome da sua aplicação.

2. Qualquer outra pasta pode ser criada dentro desta estrutura do diretório. Por exemplo, para desenvolvedores que desejam organizar seus conteúdos, devem ser criadas pastas dentro da pasta inicial para organizar os arquivos.

3. As letras maiúsculas na pasta META-INF e WEB-INF são intencionais e obrigatórias assim como as letras minúsculas nas pastas classes e lib. Não seguir o formato correto em qualquer destas pastas resultará que sua aplicação não será capaz de localizar seus conteúdos.

4. Todo conteúdo da pasta WEB-INF não será visto a partir do navegador (por exemplo, acessando seu endereço). O contêiner automaticamente gerencia isto, ou seja, para o navegador, esta pasta não existe. Este mecanismo protege suas fontes confidenciais, como classe de arquivos Java, as configurações de aplicações, entre outros. O conteúdo desta pasta, somente pode ser acessado pela aplicação.

5. Deve existir um arquivo chamado web.xml dentro da pasta WEB-INF. Mesmo que, por exemplo, sua aplicação WEB tenha apenas conteúdo estático e não faça uso de classes Java ou arquivos externos, o contêiner ainda irá requerer que sua aplicação tenha estes dois itens.

Programação WEB 14

Contém HTML, imagens, e outros conteúdos estáticos, e JSPs

Contém meta-information sobre suas aplicações (opcional)

Contém todas as pastas que não serão vista no navegador

Contém classes de arquivos servlets criados para a sua aplicação

Contém arquivos JAR de bibliotecas que podem ser utilizadas pela aplicação

Arquivo XML que armazena as configuções da aplicação

Page 15: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

8. ExercícioResponda às seguintes questões:

• Que tipo de arquitetura as aplicações WEB usam? Quem são os participantes de tais estruturas e o quais são suas funções?

• Qual o tipo de linguagem utilizada para indicar ao navegador como apresentar conteúdo para o usuário?

• HTTP é um protocolo de conexão stateful ou stateless?

• Quais são os dois métodos HTTP Request mais utilizados? E como eles são diferentes? Quando é melhor usar um ao invés do outro?

• Qual componente é absolutamente necessário para ser possível executar aplicações WEB?

• Quais são os elementos não opcionais da estrutura de diretórios da aplicação WEB?

• Qual é o nome do arquivo XML utilizado para configurar aplicações WEB? Em qual diretório pode ser encontrado?

• Qual pasta contém os arquivos JAR das bibliotecas usadas pelas aplicações?

• Qual pasta irá conter os arquivos de classe Java usados pelas aplicações?

Programação WEB 15

Page 16: Projeto JEDI - Programação para WEB - Java - 178 páginas

Módulo 6Programação WEB

Lição 2Classes Servlets

Versão 1.0 - Nov/2007

Page 17: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

1. ObjetivosUma Servlet é uma classe Java usada para estender a capacidade dos servidores que hospedam aplicações acessadas via modelo de programação Requisição/Resposta. É uma classe Java que implementa a interface Servlet e aceita requisições que vêm de outras classes Java, clientes Web ou outros Servlets, gerando, então, respostas.

As Servlets também são conhecidas como HTTP Servlet. Isto porque os Servlets são comumente usados com o HTTP atualmente, não há um protocolo cliente-servidor específico.

Para iniciar o uso das Servlets será necessário ter conhecimentos sobre programação Java, conceitos sobre cliente-servidor, HTML básico e HTTP (HyperText Transfer Protocol). Para criar uma Servlet será necessário importar para a nossa classe Java as classes de extensão padrões que estão dentro dos pacotes javax.Servlet e javax.Servlet.http.

O pacote javax.servlet contém a estrutura básica de Servlet, enquanto que o pacote javax.Servlet.http é utilizado como uma extensão da tecnologia para as Servlets que realizam requisições HTTP.

Ao final desta lição, o estudante será capaz de:

• Obter uma visão geral da arquitetura Servlet

• Conhecer o ciclo de vida de uma Servlet

• Manipular requisições e respostas

• Configurar, empacotar e distribuir uma aplicação WEB

• Conhecer os parâmetros de aplicações WEB

Programação WEB 4

Page 18: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

2. Conceitos Iniciais

2.1. Visão Geral da Arquitetura Servlet

Antes da tecnologia Servlet, o meio mais comum de adicionar funcionalidades a um servidor WEB era através do uso de CGI (Common Gateway Interface ou Interface de Entrada Comum). CGI fornece uma interface do servidor para uma classe externa permitindo que esta invoque o servidor a tratar as requisições do cliente. Porém, o CGI foi desenhado de forma que cada chamada para um recurso CGI fosse criado um novo processo no servidor; informação significante para a classe é passada para este processo utilizando entradas padrões e variáveis de ambiente. Uma vez completada a requisição, o processo é terminado, devolvendo o recurso ao sistema. O problema que incorre neste tipo de abordagem é que isto impõe pesadas requisições aos recursos do sistema limitando o número de usuários que a aplicação pode atender ao mesmo tempo.

A Tecnologia Servlet foi projetada para contornar este problema inerente ao CGI e prover aos desenvolvedores uma solução robusta em Java para criar aplicações WEB. Ao invés de criar um processo peso-pesado no servidor a cada requisição do cliente, com Servlets há somente um processo para todas as requisições: o processo solicitado pelo contêiner da Servlet para executar. Quando uma nova requisição chega, o contêiner cria somente uma pequena thread para executar a Servlet.

Servlets também são carregados em memória somente uma vez, ou seja, o contêiner carrega-os em memória na inicialização do servidor ou na primeira vez que o Servlet for requisitado para atender a um cliente, diferente do CGI, que para cada requisição do cliente carrega e descarrega os dados da classe em memória. Uma vez carregado em memória, está pronto para tratar as requisições dos clientes.

2.2. Primeira vista sobre Servlet

O código a seguir mostra a estrutura de uma Servlet básica que trata as requisições GET, assim como exibe o tradicional exemplo 'Hello World'.

import java.io.*;import javax.servlet.*;import javax.servlet.http.*;public class HelloServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //"request" é utilizado para ler os dados do formulário HTML dos cabeçalhos // HTTP (um exemplo são os dados digitados e submetidos) // e outros dados que podem ser obtidos a partir da requisição do cliente. // "response" é para especificar a linha e o cabeçalho de resposta do HTTP // (exemplo: especificar o tipo do conteúdo, definir cookies). // Também contém métodos que permitem ao Servlet gerar respostas para // o cliente. PrintWriter out = response.getWriter(); out.println("<HTML> <TITLE>Hello Page</TITLE><BODY><br>"); out.println("<h1>Hello World!</h1>"); out.println("</BODY></HTML>"); //"out" para enviar o conteúdo para o browser. }}

A primeira parte do código trata da importação das classes em java.io (para PrintWriter, etc.), javax.Servlet e javax.Servlet.http. Os pacotes javax.servlet e javax.servlet.http fornecem

Programação WEB 5

Page 19: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

interfaces e classes para escrever as Servlets (contêm as classes HttpServlet, HttpServletRequest e HttpServletResponse).

Por extensão da HttpServlet, herda os métodos que são automaticamente chamados pelo servidor dependendo de certas condições que serão explicadas mais à frente. Por polimorfismo por overriding destes métodos podemos fazer com que nossa Servlet execute a funcionalidade que quisermos.

Neste caso, o método herdado do HttpServlet que sobrepomos é o método doGet. Simplificando, ele é o método que é invocado pelo contêiner sempre que uma requisição GET é feita de uma Servlet em particular. Relembrando do módulo anterior, navegação de site, recuperação de documento, visualização de páginas são exemplos de requisições GET. Logo, sempre que um usuário quiser ver a resposta da nossa Servlet deve invocar uma requisição GET.

Ao visualizarmos o código veremos que o método doGet utiliza dois parâmetros: um objeto HttpServletRequest e um objeto HttpServletResponse. Não obstante, estes objetos vêm de encontro com as necessidades do desenvolvedor. São criados e mantidos por um contêiner e são simplesmente passados no momento que este contêiner chama o método doGet. A respeito disso, o método doGet e outros métodos, que serão discutidos depois, são similares ao método public statis void main(Strings[] args) que utilizamos em classes Java baseadas em linhas de comando. Não será criado um array de String para passarmos com o método; pois já foi realizado pelo ambiente de execução.

Os objetos HttpServletRequest e HttpServletResponse atendem aos desenvolvedores através das seguintes funcionalidades:

• O objeto HttpServletRequest fornece acesso a todas as informações a respeito das requisições do cliente incluindo todos os parâmetros que são colocados nos cabeçalhos de requisições HTTP, métodos de requisições HTTP, entre outros.

• O objeto HttpServletResponse contém todos métodos necessários utilizados pelos desenvolvedores para produzir respostas que serão enviadas de volta ao cliente. Isto inclui métodos para definir o cabeçalho de resposta HTTP, declarar respostas do tipo MIME, bem como métodos de recuperação de instâncias de classes Java I/O as quais podemos utilizar diretamente para produzir a saída.

Voltando ao código, vemos que, com a exceção dos comentários, existem muitas linhas que usamos para executar a funcionalidade de exibir "Hello World!" ao usuário. Uma dessas linhas é:

PrintWriter out = response.getWriter();

e múltiplas chamadas para o método out.println(). Por enquanto, pensemos no objeto out como quem leva nosso texto de saída para o browser do cliente. Com isto em mente, é fácil ver como múltiplas chamadas ao método out.println() produz o seguinte conteúdo:

Figura 1: Saída do HelloServlet

Programação WEB 6

Page 20: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

2.3. Testando o exemplo Servlet

Até agora vimos a exibição de uma saída em uma Servlet exemplo. Para iniciarmos a abstração da implantação e configuração de Servlets faremos fazer uso de uma ferramenta automatizada disponibilizada por uma IDE. A IDE que utilizaremos utilizar em nosso exemplo é o Sun Studio Enterprise 8, que é livre para os membros do Sun Developer Network. Possui suporte completo para a tecnologia Servlets e especificações JSP, bem como outras características adicionais.

Daqui para frente assumiremos que o Enterprise 8 foi instalado com sucesso em seu computador.

Antes de tudo, para o nosso Servlet exemplo, precisaremos criar um novo projeto de aplicação WEB. Para fazer isto, na barra de menu, selecione New -> Project. Na tela que aparece em seguida, selecione a categoria Web. Na direita, escolha New Web Application.

A tela seguinte irá solicitar detalhes de nosso projeto. Para nosso primeiro teste com Servlets usaremos como nome "FirstServletProject" (Project Name), e utilizaremos os valores padrões para os outros campos.

Figura 2: Criando um Novo Projeto de Aplicação Web

Após criar um novo projeto WEB, a tela será semelhante a figura a seguir:

Programação WEB 7

Page 21: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

Figura 3: Novo Projeto de Aplicação Web

Para adicionar a nossa Servlet à aplicação, pressione o botão direito do mouse na opção Source Packages, selecione New -> Servlet. Se a Servlet não aparecer no menu, então selecione New -> File/Folder. Na tela seguinte, selecione a categoria Web e depois Servlet.

Figura 4: Adicionando Servlet para a aplicação

Programação WEB 8

Page 22: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

A IDE irá mostrar uma série de telas que questionarão sobre os detalhes da Servlet a ser criada. Na primeira tela, em Class Name digite FirstServlet. Em Package digite jedi.Servlet.

Figura 5: Modificando o nome da classe e o nome do pacote

Na tela que segue, mantenha os valores padrões e então pressione o botão Finish e isto irá resultar conforme a figura a seguir.

Figura 6: Servlet criada

Programação WEB 9

Page 23: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

Podemos perceber que a IDE criou e parcialmente implementou o método processRequest. Se clicarmos na caixa com um sinal de soma na parte esquerda inferior veremos que o processRequest é simplesmente um método que é chamado pelos métodos doGet e doPost. Isto significa que o conteúdo do método processRequest forma a base da funcionalidade de nosso Servlet.

Primeiro, removeremos todo o conteúdo do método processRequest. Então, copiaremos a nossa Servlet exemplo para dentro do método doGet.

Figura 7: Novo método processRequest

Para executar, pressione Shift + F6. A IDE irá então empacotar, instalar e invocar a Servlet e automaticamente chamar o navegador WEB.

Programação WEB 10

Page 24: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

3. Ciclo de Vida da Classe ServletUma Servlet é gerenciada através de um ciclo de vida bem definido descrito nas especificações Servlet. O ciclo de vida do Servlet descreve como ele é carregado, instanciado, inicializado, requisitado, destruído e finalmente coletado (retirado de memória – garbage collection). O ciclo de vida de um Servlet é controlado pelo contêiner em que ele é instalado.

O ciclo de vida da Servlet permite ao contêiner resolver os problemas de endereçamento e performance do CGI e as preocupações com segurança da API de programação de um servidor baixo-nível. Um contêiner deve poder executar todas as Servlets em uma única JVM (Java Virtual Machine – Máquina Virtual Java). Por esta razão, as Servlets podem eficientemente compartilhar dados entre si e evitar que uma acesse os dados particulares de outra. As Servlets podem também permitir persistência entre requisições como objetos instanciados, utilizando assim menos memória do que processos completamente empacotados.

Figura 8: Ciclo de vida da Servlet

A figura mostra os principais eventos na vida de uma Servlet. É importante notar que para cada um destes eventos há um método que será invocado pelo contêiner.

3.1. Instanciação

Neste estágio, a classe Servlet é carregada para a memória e uma instância é criada pelo contêiner.

Por padrão, um contêiner pratica o que chamamos de lazy loading (carregamento tardio). Utilizando este método, uma classe Servlet é carregada para a memória, instanciada e inicializada somente depois que uma requisição for feita. Isto diminui o tempo de inicialização da aplicação, entretanto significa também que haverá um pouco de atraso associado à primeira requisição de cada Servlet. Se este comportamento for indesejável, cada Servlet deve ser configurada para ser carregada juntamente com a aplicação. Isto será discutido posteriormente quando abordarmos o descritor de carregamento.

Como podemos ver na figura, uma Servlet passa pela fase de instanciação somente uma vez durante seu ciclo de vida. Isto significa que o atraso associado ao seu carregamento em memória acontece somente uma vez. Isto mostra a vantagem desta sobre outras tecnologias.

O método relevante que o contêiner irá chamar neste estágio será o construtor. Não é recomendado que se coloque qualquer código no construtor. A maioria das funcionalidades que os desenvolvedores querem adicionar aos construtores envolvem definição de objetos ou inicialização

Programação WEB 11

Page 25: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

de variáveis. Servlets possui uma fase separada para este tipo de atividade.

3.2. Inicialização

Nesta fase, Servlet é primordial para o uso na aplicação. Assim como a fase de instanciação, uma Servlet passa por este estágio somente uma vez. Isto só ocorre após a fase em que nosso objeto é inicializado ao ser chamado pela Servlet.

O método que é chamado pelo contêiner neste ponto é o método init(). A assinatura completa do método é apresentada abaixo.

public void init(ServletConfig config)

Como podemos ver, este método tem um parâmetro: uma instância do objeto ServletConfig. Este objeto contém informações sobre a configuração da Servlet bem como fornece meios para que a Servlet acesse informações da aplicação e seus recursos.

Como mencionado anteriormente, qualquer código de configuração ou inicialização não deve ser colocado na área do construtor, em vez disso, deve ser colocado dentro do método init. Se um desenvolvedor implementar este método, é importante que realize uma chamada ao super.init(config). Isto garante que a ação de inicialização padrão da Servlet seja executada e que sua configuração seja apropriadamente definida.

Após a inicialização, a Servlet estará apta a receber as requisições dos clientes.

Este método somente será chamado novamente quando o servidor recarregar a Servlet. O servidor não pode recarregar uma Servlet até que esta seja destruída através do método destroy.

3.3. Pronta

Esta é a fase quando uma Servlet está no auge do seu ciclo de vida. Nesta, a Servlet pode ser chamada repetidamente pelo contêiner para prover sua funcionalidade.

O método chamado pelo contêiner nesta fase é service() e possui a seguinte assinatura: public void service(ServletRequest req, ServletResponse res)

Os objetos ServletRequest e ServletResponse passados para este método provêem métodos para extrair informação das requisições dos usuários e métodos para gerar as respostas.

Um detalhe importante a ser observado, o contêiner realiza repetidas chamadas ao método service usando threads separadas. Geralmente, há somente uma instância ativa da Servlet consumindo espaço em memória e atendendo às diversas requisições. Esta é outra vantagem que o Servlet Java possui é também uma das principaisrazões porque uma Servlet (e o seu método service) é projetado para conter um thread-safe.

Para Servlets específicos HTTP (Servlets estendendo HttpServlet), os desenvolvedores não devem implementar o método service diretamente. Ao invés disto, devem implementar qualquer um dos seguintes métodos:

public void doGet(HttpServletRequest req, HttpServletResponse res)public void doPost(HttpServletRequest req, HttpServletResponse res)public void doPut(HttpServletRequest req, HttpServletResponse res)public void doTrace(HttpServletRequest req, HttpServletResponse res)

Cada um destes métodos corresponde a um método HTTP específico (GET, POST, ...). O método apropriado é então chamado quando o Servlet recebe a requisição HTTP.

Já que a maioria das chamadas HTTP, que os desenvolvedores devem se preocupar, são dos métodos GET ou POST, Servlets podem implementar doGet, doPost ou ambos.

3.4. Destruição

Em algumas ocasiões, quando o contêiner ficar sem memória ou detectar que a quantidade de

Programação WEB 12

Page 26: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

memória livre possui pode gerar algum problema, o contêiner tentará liberar memória destruindo uma ou mais instâncias da Servlet. Qual será removida é determinado pelo contêiner e o desenvolvedor não tem controle direto.

Um contêiner poderá liberar uma instância da Servlet como parte de seu processo de desligamento.

Quando a Servlet está para ser removida de um gerenciamento do contêiner, ela está na fase de destruição. O método chamado pelo contêiner, antes de realizar isto, é o destroy(). Aqui, na nossa Servlet deveria ser codificado para explicitamente liberar os recursos utilizados (ex. Conexões com o Banco de Dados).

3.5. Garbage Collection

Esta fase do ciclo de vida de uma Servlet é equivalente a de qualquer outro objeto Java. Relembrando que esta fase ocorre antes que um objeto seja retirado da memória. Os desenvolvedores não têm controle direto quando isso irá ocorrer.

O método do objeto chamado nesta fase é o finalize().

Programação WEB 13

Page 27: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

4. Manipulando Requisições e RespostasO principal objetivo de uma Servlet é prover conteúdo dinâmico para o usuário. Por definição, o conteúdo dinâmico muda em resposta a diversas condições. Exemplos dos quais são as requisições específicas de usuário, a hora, temperatura.

Para dar a Servlet acesso aos dados de uma requisição, esta é provida em uma instância do objeto ServletRequest que esconde estes detalhes. Servlets baseados em HTTP possuem uma subclasse, HTTPServletRequest, que provê métodos adicionais para obter informação específica do HTTP, como informação sobre cookies, detalhes de cabeçalhos, entre outros.

4.1. Dados e Parâmetros de Formulário

4.1.1.Método request.getParameter

Um dos cenários que freqüentemente requer mais frequentemente conteúdo dinâmico é quando queremos que nossa aplicação responda aos dados do usuários apresentados em um formulário.

Veja o exemplo seguinte:

Figura 9: Entrada do Nome do Usuário

Dado o seguinte formulário, mostrado na figura anterior, que obtém o nome do usuário, queremos produzir uma resposta customizada para qualquer nome submetido.

Para este e outros cenários similares, Java provê o método getParameter no objeto HttpServletRequest. Este método possui a seguinte assinatura:

public String getParameter(String parameterName)

Este método obtém o nome do parâmetro que desejamos obter e retorna o valor da String que representa este parâmetro.

Abaixo segue um código exemplo que obtém o nome do usuário e dá saída em um simples cumprimento, seguido pelo HTML usado para mostrar o formulário.

public class GetParameterServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // retorna o valor solicitado pelo usuário String userName = request.getParameter("userName"); // retorna um objeto PrintWriter e utiliza-o para a mensagem de saída PrintWriter out = response.getWriter(); out.println("<HTML><BODY><H1>"); out.println("HELLO AND WELCOME, " + userName + "!"); out.println("</H1></BODY></HTML>"); out.close(); }}

Temos a seguir, o código para HTML exemplo utilizado para mostrar o formulário:<HTML>

Programação WEB 14

Page 28: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

<TITLE>Halloa!</TITLE> <BODY> <form action="GetParameterServlet" method="post"> Enter user name: <input type="text" name="userName"/> <br/> <input type="submit" value="Greet me!"/> </form> </BODY></HTML>

Ao entrar com o valor "JEDI" no formulário, obtemos o seguinte resultado:

Figure 10: Saída do GetParameterServlet

4.1.2.Método request.getParameterValues

Existem momento que necessitamos recuperar dados de um formulário com múltiplos elementos com o mesmo nome. Neste caso, usando apenas o método getParameter descrito anteriormente, será retornado apenas o valor do primeiro elemento com aquele nome. Para recuperar todos os valores, usamos o método getParameterValues:

public String[] getParameterValues(String parameterName)

Este método é similar ao que acabamos de discutir. Também recebe o nome do parâmetro que queremos recuperar o valor. Desta vez, ao invés de uma única String, retorna um array de Strings.

Este é um exemplo:public class GetParameterValuesServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletExceptionm IOException{ String paramValues[] = request.getParameterValues("sports"); StringBuffer myResponse = new StringBuffer(); PrintWriter out = response.getWriter(); out.println("<HTML><TITLE>Your choices</TITLE>"); out.println("<BODY><H1>Your choices were : </H1>"); for (int i = 0; i < paramValues.length; i++) { out.println("<br/><li>"); out.println(paramValues[i]); } out.println("</BODY></HTML>"); }}

A seguir, temos o código para o HTML usado para mostrar o formulário:<html> <title>Choice selection</title> <body> <H1>What sports activities do you perform?</h1> <form action="GetParameterValuesServlet" method="get"> <input type="checkbox" name="sports" value="Biking"> Biking

Programação WEB 15

Page 29: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

<br/> <input type="checkbox" name="sports" value="Table Tennis"> Table Tennis <br/> <input type="checkbox" name="sports" value="Swimming"> Swimming <br/> <input type="checkbox" name="sports" value="Basketball"> Basketball <br/> <input type="checkbox" name="sports" value="Others"> Others <br/> <input type="submit"> </form> </body></html>

Figure 11: Recuperando dados de um formulário com múltiplos elementos

Se as opções “Biking", "Table Tennis", e "Swimming" fossem escolhidas, a saída seria:

Figure 12: Saída do GetParameterValuesServlet

4.1.3.request.getParameterNames

Algumas vezes necessitamos que a Servlet conheça o nome de um ou mais parâmetros do formulário sem ter que codificá-los dentro do Servlet. Neste caso, podemos usar o método

Programação WEB 16

Page 30: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

getParameterNames.public Enumeration getParameterNames()

O objeto Enumeration que este método retorna contém todos os nomes de parâmetros contidos nesta requisição do usuário.

4.2. Recuperando a informação da URL de Requisição

Relembrando, uma requisição HTTP é composta pela seguintes partes:http://[host]:[porta]/[caminhoDaRequisição]?[queryString]

Podemos recuperar os valores atuais de cada uma destas partes da requisição do usuário chamando alguns métodos do objeto HttpServletRequest.

• Host – request.getServerName()• Porta – request.getServerPort()• Caminho da Requisição – em Java, o caminho da requisição é dividida em 2

componentes lógicos :• Contexto – o contexto de uma aplicação WEB. Pode ser recuperado chamando

request.getContextPath()• Informação do caminho – o resto de uma requisição depois do nome do

contexto. Pode ser recuperado chamando request.getPathInfo()• Query String – request.getQueryString()

Então, por exemplo, com uma URL de requisição:http://www.myjedi.net:8080/HelloApp/greetUser?name=Jedi

Se chamarmos os métodos mencionados, os seguintes resultados serão exibidos:

request.getServerName() www.myjedi.netrequest.getServerPort() 8080request.getContextPath() HelloApprequest.getPathInfo() greetUserrequest.getQueryString() name=Jedi

4.3. Informação de Cabeçalho

Informação do cabeçalho pode ser recuperada pela Servlet chamando os seguintes métodos em HttpServletRequest:

• getHeader(String nome) – retorna o valor do cabeçalho especificado como um String. Se o cabeçalho especificado não existir, este método retorna null.

• getHeaders(String nome) – similar ao getHeader(). Entretanto, ele recupera todos os valores do cabeçalho especificado como um objeto Enumeration.

• getHeaderNames() - este método retorna os nomes de todos os cabeçalhos incluídos na requisição HTTP como um objeto Enumeration.

• getIntHeader(String nome) – retorna o valor de um cabeçalho especificado como um int. Se o cabeçalho especificado não existir na requisição, o método retorna -1.

• getDateHeader(String nome) – retorna o valor de um cabeçalho especificado como um valor long que representa um objeto Date. Se o cabeçalho especificado não existir, este método retorna -1. Se o cabeçalho não puder ser convertido em um objeto do tipo Date, este método lança uma IllegalArgumentException.

Programação WEB 17

Page 31: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

4.4. Geração da Saída

Em todos os exemplos anteriores, conseguimos gerar saídas dinâmicas para o usuário. Fizemos isto usando métodos do objeto HttpServletResponse.

Até agora, usamos o método getWriter. Este método retorna um objeto PrintWriter associado com nossa resposta para o usuário. Para ajudar a colocar as coisas na perspectiva correta, devemos lembrar que o objeto System.out que usamos regularmente para dar saída no conteúdo para o console também é uma instância do objeto PrintWriter. Ou seja, eles se comportam quase da mesma maneira: se você tiver uma instância do objeto PrintWriter, simplesmente chame os métodos print ou println para gerar a saída.

O código da nossa primeira classe Servlet será visto a seguir com o código de saída em negrito.import java.io.*;import javax.Servlet.*;import javax.Servlet.http.*;public class HelloServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // objeto "out" é utilizado para enviar o conteúdo para o browser PrintWriter out = response.getWriter(); out.println("<HTML> <TITLE>Hello Page</TITLE><BODY><br>"); out.println("<h1>Hello World!</h1>"); out.println("</BODY></HTML>"); }}

Outros métodos importantes no objeto HttpServletResponse são:

• setContentType – informa ao navegador do cliente o tipo MIME da saída que ele irá receber. Todo o conteúdo que geramos até agora foi HMTL. Poderíamos facilmente enviar outros tipo de conteúdo como JPEG, PDF, DOC, XLS, etc. Para conteúdo que não é texto, os métodos print do objeto PrintWriter que estamos usando até agora são insuficientes. Para gerar saída não textual, fazemos uso de outro método.

• getOutputStream – este método recupera uma instância do objeto OutputStream associado com nossa resposta para o usuário. Com o OutputStream, podemos usar os objetos e métodos padrão de E/S Java para produzir todos os tipos de saída.

Abaixo segue um código exemplo que irá gerar um arquivo JPG contido em uma aplicação web para o usuário.

public class JPEGOutputServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) { // definir um array de byte para armazenamento dos dados byte bufferArray[] = new byte[1024]; // retornar o ServletContext que será utilizado para o retorno ServletContext ctxt = getServletContext(); // informar ao browser que está enviando uma imagem response.setContentType("image/gif"); // criar o objeto de saída em stream que será produzida ServletOutputStream os = response.getOutputStream(); // criar o objeto de recurso para o input stream InputStream is = ctxt.getResource("/WEB-INF/images/logo.gif").openStream(); // ler o conteúdo do recurso de escrita e gerar a saída int read = is.read(bufferArray); while (read != -1) { os.write(bufferArray);

Programação WEB 18

Page 32: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

read = is.read(bufferArray); } // fechar os objetos utilizados is.close(); os.close(); }}

Programação WEB 19

Page 33: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

5. Configuração, Empacotamento e DistribuiçãoEm todos os exemplos realizados, usamos as ferramentas da IDE Sun Enterprise Studio 8 para facilitar os detalhes da configuração, do empacotamento e da instalação da aplicação WEB. Iremos agora, aprofundar estes detalhes.

5.1. Configuração da Aplicação WEB

A especificação Servlet, define um arquivo XML chamado web.xml que atua como um arquivo de configuração para as aplicações WEB. Este arquivo é chamado de descritor de distribuição.

<web-app version="2.4" 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 http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <servlet> <servlet-name>FirstServlet</servlet-name> <servlet-class>jedi.Servlet.FirstServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>FirstServlet</servlet-name> <url-pattern>/FirstServlet</url-pattern> </servlet-mapping> <session-config> <session-timeout> 30 </session-timeout> </session-config> <welcome-file-list> <welcome-file> index.jsp </welcome-file> </welcome-file-list></web-app>

Temos acima um exemplo do arquivo web.xml utilizado no projeto FirstServlet. Usaremos como ponto de partida na exploração deste arquivo.

NOTA: O arquivo web.xml da aplicação WEB construída pela IDE, pode ser encontrado expandindo a aba de Arquivos de Configuração na View -> Project.

<web-app>

Esta linha é utilizada como elemento raiz do arquivo de configuração e como uma declaração das informações necessárias (até seus atributos), para o contêiner poder reconhecer o arquivo como um arquivo descritor válido.

<servlet>

Cada instância deste elemento define uma Servlet para ser usada pela aplicação. Possui os seguintes nodes filhos:

• <servlet-name> - Um nome lógico fornecido pelo desenvolvedor que será usado para todas as referências futuras para a Servlet.

• <servlet-class> - O nome da classe Servlet completamente qualificado.

• <load-on-startup> – Opcional. Tendo uma entrada e valor para este elemento, informa ao contêiner que a Servlet deve ser construída e inicializada durante o início do contêiner ou aplicação, ultrapassando o lento carregamento do código. O valor para este elemento é um número que dita o ordem de carregamento comparando ao <load-on-startup> de outras Servlets.

<servlet-mapping>

Programação WEB 20

Page 34: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

Cada instância deste elemento define um mapeamento para uma Servlet. Possui os seguintes nodes filhos:

• <servlet-name> - O nome lógico da Servlet para ser mapeado. Deve ser definido previamente no arquivo descritor.

• <url-pattern> - A URL utilizada para que esta Servlet seja mapeada. O valor /*, fará com que todas as solicitações da aplicação seja redirecionada para seu Servlet. No exemplo dado, a URL é /FirstServlet. Isto significa que todas as solicitações de URL para http://[host]:[port]/FirstServletProject/FirstServlet será manipulado por FirstServlet.

Lembre-se que todas as definições de Servlet devem ser fornecidas antes de adicionar quaisquer Servlet-mappings.

<session-config>

Este elemento define os detalhes do gerenciamento da configuração da seção.

<welcome-file-list>

Este elemento define um componente WEB que será automaticamente carregado se o usuário entrar com uma solicitação para o servidor sem especificar um recurso em particular. Por exemplo, uma solicitação para http://[host]:[port]/FirstServletProject originará o arquivo aqui definido para ser carregado.

Mais de um arquivo pode ser especificado nesta lista. Somente o primeiro arquivo visível para o contêiner WEB será carregado.

5.2. Empacotando a Aplicação da WEB

Observaremos novamente a estrutura de uma aplicação WEB como indicada pela especificação Servlet:

Figura 13: Estrutura do Diretório de uma aplicação Java WEB

A aplicação pode ser instalada em um servidor fazendo uso de um arquivo de WAR (WEB ARchive). Arquivos WAR são o mesmo que JAR (Java ARchive); contêm o código de aplicação comprimido utilizando o formato ZIP.

5.2.1.Gerando arquivos WAR a partir de projetos existentes no Sun Studio Enterprise

É muito simples produzir o arquivo WAR contendo nossa aplicação web a partir de um projeto existente no Sun Studio Enterprise 8. Basta pressionar o botão direito do mouse no nome do projeto em Project view, e selecionar Build Project. A IDE então procederá o empacotamento da aplicação.

Programação WEB 21

Contém HTML, imagens, outros conteúdos estáticos, e JSP

Contém informações sobre a aplicação (opcional)

Contém pastas que não serão vistas no navegador

Contém classes Servlets criadas para a aplicação

Contém arquivos de bibliotecas (JAR) que podem ser utilizadas pela aplicação

Arquivo XML que armazena as configurações da aplicação

Page 35: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

Figura 14: Empacotando o projeto

A IDE informará se a operação de construção foi bem sucedida, e o local onde o arquivo WAR foi criado.

Figura 15: Construção com sucesso

Programação WEB 22

Page 36: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

5.2.2.Usando um arquivo de construção Ant para empacotar a aplicação

Além de usar uma IDE para empacotar a aplicação da WEB, podemos também fazer uso de uma ferramenta de construção para automatizar a compilação e o processo de empacotamento.

Uma ferramenta de construção que possui uma ampla utilização é chamada Apache Ant. É um projeto de código aberto da Apache Software Foundation, e seu download pode ser realizado no endereço http://ant.apache.org.

Basicamente, Ant lê em um arquivo de construção (chamado build.xml). Este arquivo de construção é composto de targets (alvos), as quais essencialmente definem atividades lógicas que podem ser executadas pelo arquivo de construção. Estes targets são por sua vez, compostos de uma ou mais tarefas que definem os detalhes de como os targets realizam suas atividades.

Requisitos do arquivo de construção:

• Deve ser locado na Raiz do Projeto de acordo com a estrutura do diretório recomendado pela Apache Software Foundation para desenvolvimento de aplicação WEB.

• Adicionalmente, deve existir também um diretório lib na Raiz do Projeto que conterá todas dependências JAR da aplicação.

• Deve existir um arquivo chamado build.properties no mesmo diretório que o roteiro de construção e deve conter valores para as seguintes propriedades:

• app.name – nome da aplicação / projeto

• appserver.home – o diretório de instalação da instância Sun Application Server 8.1

Segue a estrutura de diretório recomendado para desenvolvimento de aplicação web:

Figura 16: Estrutura de diretório recomendada para o desenvolvimento de aplicação WEB

Esta estrutura de diretório foi projetada para ser distinta da estrutura de diretório exigido pela especificação Servlet. A Apache recomenda por possuir as seguintes vantagens:

• O conteúdo de diretório de fonte são mais facilmente administrados, deslocado, ou aprovado se a versão de desenvolvimento não misturada.

• O controle de código de fonte é mais fácil de administrar em diretórios que contenham só arquivos de fonte (nenhuma classe compilada, etc.).

• Os arquivos que compõem a distribuição da aplicação é mais fácil selecionar quando a hierarquia de desenvolvimento for separada.

Pode ser difícil compreender à primeira vista. Para ajudar a compreensão, todo os requisitos para compilar e empacotar nosso exemplo FirstServlet é fornecido na pasta samples/FirstServlet.

Para realizar o empacotamento de uma aplicação usando esta estrutura, execute a seguinte instrução (no mesmo diretório contendo o arquivo de construção).

ant dist

Esta instrução chamará o target dist no arquivo de construção, o qual gerará o arquivo WAR e o colocará no diretório dist. Este arquivo WAR pode, então, ser carregado no contêiner usando as ferramentas administrativas oferecidas pelo contêiner.

Programação WEB 23

Contém a aplicação em uma estrutura de diretório conhecida pelo contêiner.

Localização do arquivo WAR

Documentação que é usada pelo time de desenvolvedores

Arquivos fonte Java

Conteúdo da aplicação (HTML, JSP), base da raiz do documento do projeto

Contém arquivos de configuração como o descritor de desenvolvimento (web.xml), descritores de biblioteca de tags, etc.

Page 37: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

5.3. Desenvolvimento em Servidor

Os contêiners Servlet geralmente contêm ferramentas administrativas que podem ser usadas para desenvolver aplicações WEB. Neste momento, veremos os passos exigidos para desenvolver o arquivo WAR gerado no aplicativo Sun Application Server 8.1.

• Primeiro passo, execute o log-in no console administrativo. Isto pode ser acessado através da seguinte URL em seu navegador: http://localhost:[ADMIN_PORT] onde ADMIN_PORT é a porta configurada durante a instalação para manipular os assuntos administrativos.

Figure 17: Instalando um arquivo WAR

• Segundo, selecione a aba Web Aplications, no painel à esquerda• Pressione o botão Deploy encontrado no painel à direita• Na tela seguinte, pressione o botão Browse para selecionar o arquivo WAR para instalar• Pressione o botão Next encontrado na parte superior à direita• Pressione o botão de Finish na próxima tela • Parabéns, sua aplicação foi instalada

Programação WEB 24

Page 38: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

6. Servlet e Parâmetros de Aplicação

6.1. ServletConfig e Parâmetros de Inicialização da Servlet

O objeto da classe ServletConfig é passado para uma Servlet específica durante sua fase de inicialização. Usando isto, uma Servlet pode recuperar informações específicas para si mesmo (parâmetros de inicialização). A Servlet também pode ganhar acesso a uma instância do objeto de ServletContext usando o objeto da classe ServletConfig.

Os parâmetros de inicialização são de grande uso, especialmente quando lidamos com informações que podem variar com cada desenvolvimento da aplicação. Além disso, fornecendo dados para a Servlet como parâmetros, evitamos a codificação desses parâmetros diretamente na Servlet, permite aos desenvolvedores a habilidade de mudar o comportamento da Servlet sem ter que recompilar o código.

Podemos adicionar parâmetros de inicialização para a Servlet especificando-os na definição do deployment descriptor do Servlet. A seguir, temos uma amostra:

...<Servlet> <Servlet-name>FirstServlet</Servlet-name> <Servlet-class>jedi.Servlet.FirstServlet</Servlet-class> <init-param> <param-name>debugEnabled</param-name> <param-value>true</param-value> </init-param></Servlet>...

As tags <init-param> e </init-param> informam ao contêiner que estamos começando e terminando a definição de parâmetro, respectivamente. <param-nome> define o nome do parâmetro, e <param-value> define seu valor.

Para ter acesso aos parâmetros da Servlet, devemos primeiro obter o acesso ao seu objeto ServletConfig, que pode ser feito recuperado solicitando ao método getServletConfig(). Após isso, o valor de parâmetro poderá ser recuperado através de uma String chamando o método getInitParameter() e fornecendo o valor de <param-nome> como o parâmetro.

public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ServletConfig config = getServletConfig(); String isDebugEnabled = config.getInitParameter("debugEnabled"); if (isDebugEnabled.equals("true") { ...}

A amostra de código acima ilustra o procedimento.

6.2. ServletContext e Parâmetros de Aplicação

O objeto de ServletContext informa a Servlet o acesso ao contexto de aplicação.

Pense sobre o contexto de aplicação como a área na qual as aplicações se movem. Esta área é fornecida pelo contêiner para cada aplicação WEB. Com cada contexto da aplicação sendo diferente uma do outra, uma aplicação não pode acessar o contexto de outra aplicação.

Ter acesso a este contexto é importante, porque através deste, a Servlet pode recuperar parâmetros da aplicação. É possível também armazenar dados que podem ser recuperados por quaisquer componentes na aplicação.

Do mesmo modo que parâmetros de inicialização podem ser fornecidos para Servlets individuais, eles também podem ser fornecidos para uso pela aplicação inteira.

<context-param>

Programação WEB 25

Page 39: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

<param-name>databaseURL</param-name> <param-value>jdbc:postgresql://localhost:5432/jedidb</param-value></context-param>

O código acima exemplifica como adicionar diferentes parâmetros de aplicação. O elemento XML usado aqui é <context-param>. Cada instância de tal elemento define um parâmetro para uso pela aplicação inteira. <param-name> e <param-value> trabalham da mesma maneira que a tag <init-param>.

NOTA: Novamente, lembre-se que a especificação é restrita quanto a ordenação de seus elementos dentro do descritor de desenvolvimento. Para manter o arquivo web.xml válido, todas as entradas <context-param> devem ser localizadas ANTES de quaisquer entradas <Servlet>.

O procedimento para recuperar os valores dos parâmetros é bem parecido com o utilizado para recuperar os parâmetros de Servlets específicas. É uma instância de ServletContext que a Servlet deve manipular. Isto pode ser recuperado chamando o método getServletContext() de uma instância do objeto ServletConfig da Servlet.

public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ServletContext ctxt = getServletConfig().getServletContext(); String jdbcURL = ctxt.getInitParameter("databaseURL"); // código customizado setURL(jdbcURL); ...}

6.3. Resumo

● A tecnologia Servlets são a solução Java para a produção de conteúdo dinâmico para a WEB em resposta a tecnologia CGI.

● Servlets possui várias vantagens sobre CGI, incluindo uma reduzida na área de ocupação de memória e menos despesa para cada solicitação.

● Uma Servlet está completamente administrada pelo seu contêiner. O único código necessário para os desenvolvedores é a funcionalidade da implementação.

● Para criar Servlets, os desenvolvedores devem criar subdivisões de classe de HttpServlet e seus lugares de implementações funcionais em quaisquer dos métodos, doGet ou doPost.

● Detalhes de pedido podem ser recuperados por HttpServletRequest, e métodos geradores de resposta podem ser acessados por HttpServletResponse. Estes são passados como parâmetros para doGet e doPost.

● Para instalar Servlets no contêiner WEB, é necessário criar arquivos WAR. Este processo de empacotamento pode ser automatizado por qualquer IDE ou pelo uso de uma ferramenta de construção.

● Descritores do desenvolvimento são partes essenciais da aplicação. Eles devem ser localizados no diretório de aplicação WEB-INF e seguir determinadas regras.

● Parâmetros podem ser fornecidos pela aplicação usando o descritor de desenvolvimento e recuperado usando os métodos em instâncias ServletConfig ou ServletContext.

Programação WEB 26

Page 40: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

7. Exercícios

7.1. Perguntas sobre o Ciclo de Vida da Servlet

Responda às seguintes perguntas sobre as fases da Servlet:

1. Quais são as 5 fases do ciclo de vida de uma Servlet?

2. Quais são os métodos associados a cada fase do ciclo de vida de uma Servlet?

3. Por que o código colocado no método service de uma Servlet precisa ser thread-safe?

4. Quando uma Servlet é destruída?

5. Quantas vezes o código colocado dentro do método init() do Servlet pode ser chamado? E o código dentro do método service()?

6. Caso a Servlet estenda a classe HttpServlet, quais métodos adicionais poderemos implementar para produzir a funcionalidade necessária?

7.2. Exercícios de Manipulação de Requisição/Geração de Resposta

1. Dado o seguinte formulário:

<HTML> <BODY> <form action="AddServlet" method="post"> Enter number 1 : <input type="text" name="operand1"/> </br> Enter number 2 : <input type="text" name="operand2"/> </br> <input type="submit" value="Perform addition"/> </form> </BODY> </HTML>

Crie uma Servlet chamado AddServlet que irá recuperar 2 números dados pelo usuário, adicione-os, e gere o resultado.

2. Dado o seguinte formulário:

<html> <title>Menu</title> <body> <H1>Which food items do you want to order?</h1> <form action="MenuSelectionServlet" method="post"> <table> <tr> <td><input type="checkbox" name="order" value="20"> Sundae </td> <td> P 20 </td> </tr><tr> <td><input type="checkbox" name="order" value="25"> Reg. Burger </td> <td> P 25 </td> </tr><tr> <td><input type="checkbox" name="order" value="15"> Dessert Pie </td> <td> P 15 </td> </tr><tr> <td><input type="checkbox" name="order" value="70"> Rice Meal </td> <td> P 70 </td> </tr><tr> <td><input type="submit"></td> </tr> </table> </form> </body></html>

Programação WEB 27

Page 41: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

Crie uma Servlet chamado MenuSelectionServlet que irá recuperar as seleções feitas pelo usuário, adicionar os seus valores, e retornar o resultado computado para o usuário.

7.3. Perguntas sobre implementação

1. Identifique o elemento no arquivo web.xml que será responsável por:

a) Conter o nome lógico usado para se referir a uma Servlet.b) Ser o elemento root do arquivo de configuração.c) Definir um mapeamento entre uma Servlet e uma solicitação do usurário.

2. Reorganize as seguintes informações no ordem correta de seu aparecimento dentro de um arquivo XML:

session-configservletservlet-mappingwelcome-file-list

3. Suponha termos uma Servlet cujo nome é TrialServlet, crie um mapeamento de modo que TrialServlet será chamada para cada pedido:

http://[host]/[context]/myExerciseServlet.

4. O que são arquivos WAR?

5. Dado um projeto WEB existente em nossa IDE, como um arquivo WAR pode ser gerado?

Programação WEB 28

Page 42: Projeto JEDI - Programação para WEB - Java - 178 páginas

Módulo 6Programação WEB

Lição 3Classes Servlets Avançadas

Versão 1.0 - Nov/2007

Page 43: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

1. ObjetivosNa lição anterior, reparamos como as classes Servlets podem ser utilizadas pelos desenvolvedores Java para receber requisições de clientes e produzir respostas dinamicamente. Também vimos como distribuir Servlets empacotando-as em arquivos WAR e carregando-as no contêiner WEB.

Ao final desta lição, o estudante será capaz de:

• Redirecionar respostas

• Identificar o escopo dos objetos

• Utilizar sessões e rastreamento de sessão

• Utilizar Filtros

Programação WEB 4

Page 44: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

2. Redirecionamento de RespostaExistem casos onde queremos que a servlet tenha somente a responsabilidade de fazer algum processamento inicial e deixe a geração do conteúdo a alguma outra entidade.

Suponhamos que seja obtido do usuário algum valor e o apresentemos com diversas visões baseadas no valor. Digamos também que tenhamos um site que, após processar a entrada do usuário, apresente diferentes páginas dependendo do perfil do usuário no sistema. Colocar toda a geração de conteúdo para todas as páginas em uma única servlet pode torná-la não gerenciável. Nestes casos, seria melhor a servlet redirecionar a geração da saída.

Existem dois modos que podemos utilizar para realizar esse redirecionamento.

● Através do objeto RequestDispatcher

● Através do método sendRedirect() encontrado no objeto HttpServletResponse

2.1. RequestDispatcher

É possível obter uma instância do objeto RequestDispatcher invocando o seguinte método:public RequestDispatcher getRequestDispatcher(String path)

Este método pode ser encontrado no objeto HttpServletRequest.

O parâmetro String que este método recebe é a localização da página HTML, JSP ou a servlet para a qual se queria disparar a requisição. Uma vez com a instância do objeto RequestDispatcher, existe a opção de se invocar um dos seguintes métodos:

public void include(ServletRequest req, ServletResponse res)public void forward(ServletRequest req, ServletResponse res)

Ambos métodos pegam o conteúdo produzido no local especificado e torna-o parte da resposta da servlet para o usuário. A principal diferença entre eles é que, o método forward faz com que a página alvo tenha toda a responsabilidade de gerar a resposta, enquanto que o include só incorpora o conteúdo da página alvo. Utilizando o método include, pode-se adicionar outro conteúdo a reposta ou mesmo incluir outra página alvo.

Para ilustrar a diferença entre os dois, são apresentadas duas Servlets abaixo. O código é virtualmente idêntico. A primeira faz o uso do método include enquanto a segunda faz uso do método forward.

public DispatchServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); PrintWriter out = response.getWriter(); out.println("Error occurred during login request processing"); RequestDispatcher rd = request.getRequestDispatcher("/login.html"); rd.include(request, response); } } public DispatchServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); PrintWriter out = response.getWriter(); out.println("Error occurred during login request processing"); RequestDispatcher rd = request.getRequestDispatcher("/login.html"); rd.forward(request, response); } }

Ambas servlets definidas acima usam uma página denominada login.html para formar a base da

Programação WEB 5

Page 45: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

saída dos dados. A definição desta página segue abaixo:<H1>Login Page</H1> <form action="DispatchServlet"> User name : <input type="text" name="loginName"><br/> Password : <input type="password" name="password"><br/> <input type="submit"/> </form>

Ao executar a primeira Servlet, a que utiliza o método include, a seguinte saída é gerada:

Figura 1: Saída de DispatchServlet utilizando o método include

Executando a segunda Servlet temos o seguinte resultado:

Figura 2: Saída de DispatchServlet utilizado método forward

Dos códigos praticamente idênticos, temos duas saídas diferentes. Ao utilizar o método include, a mensagem que é enviada para o usuário antes de se chamar o método. Este não é o caso para a saída do método forward onde a mensagem é apresentada antes da chamada do método não se tornar parte da saída.

Com o método forward, todo o conteúdo do buffer da resposta é limpo antes da chamada do método, e depois a resposta é imediatamente enviada. Nenhum conteúdo pode ser adicionado. Com o método include, todo o conteúdo colocado no buffer de resposta é mantido antes e depois da chamada do método.

Programação WEB 6

Page 46: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

Utilizando-se o método include, é possível que a servlet apresente a saída de diferentes fontes de uma só vez. É também permitido concatenar mensagens a outro conteúdo estático. Tome-se o seguinte exemplo:

public LoginServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { RequestDispatcher rd = null; // recupera os parâmetros do formulário do usuário String loginName = request.getParameter("loginName"); String password = request.getParameter("password"); User user = null; // cria o JavaBean implementando a funcionalidade de autenticação UserService service = new UserService(); user = service.authenticateUser(loginName, password); if (user == null) { // gera a mensagem de erro response.setContentType("text/html"); PrintWriter out = response.getWriter(); out.println("User does not exist with given login and/or password"); // retorna para a página de login do usuário rd = request.getRequestDispatcher("/login.html"); rd.include(request, response); out.close(); } else { // armazena o objeto User na sessão HttpSession session = request.getSession(); session.setAttribute(ApplicationConstants.USER_OBJECT, user); // constrói a resposta a partir de múltiplos componentes HTML rd = request.getRequestDispatcher("/header.html"); rd.include(request, response); rd = request.getRequestDispatcher("/mainContent.html"); rd.include(request, response); rd = request.getRequestDispatcher("/footer.html"); rd.include(request, response); } }

Como o método include somente adiciona conteúdo de outra fonte e não envia a resposta após sua chamada, podemos utilizá-lo repetidamente. Utilizando este princípio, a classe LoginServlet apresentada acima é capaz de criar uma resposta contendo três páginas diferentes.

2.2. sendRedirect

A outra forma de redirecionar a saída de uma entidade fora de uma servlet é o método sendRedirect. Este método tem a seguinte assinatura e pode ser encontrado no objeto HttpServletResponse:

public void sendRedirect(String relativePath)

O parâmetro que o método recebe é um String que representa o caminho para a página alvo a ser redirecionada para o usuário. A chamada a este método instrui o browser para enviar outra requisição HTTP para a página alvo especificada.

Isto torna a página alvo a única entidade responsável por gerar o conteúdo que, de certa forma, é de resultado similar ao se utilizar o método forward do objeto RequestDispatcher discutido anteriormente. Contudo, a requisição reenviada apresenta diversas diferenças práticas comparadas ao método forward:

Programação WEB 7

Page 47: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

• A URL no browser reflete o alvo especificado. Isto torna do método sendRedirect não desejável caso não se queira que o usuário esteja ciente do redirecionamento.

• Como efetivamente é uma nova requisição, os dados armazenados no objeto da requisição são descartados. Os parâmetros fornecidos pelo usuário, se existirem, devem ser resubmetidos caso a página alvo necessite destes. Os dados que estiverem armazenados no objeto request devem ser mantidos de alguma forma. Caso contrário, serão perdidos.

É recomendado então que o método include seja utilizado para permitir saída a partir de diversas fontes. O método forward deveria ser utilizado no redirecionamento para componentes cujo conteúdo seja gerado de forma dinâmica (ex: Servlets e JSPs), enquanto que o método sendRedirect deveria ser utilizado no redirecionamento para componentes que gerem conteúdo estático.

Programação WEB 8

Page 48: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

3. Objetos de EscopoA especificação servlet nos apresenta quatro escopos onde os dados podem ser armazenados, de forma que possam ser compartilhados entre os componentes. Eles estão listados abaixo em ordem crescente de visibilidade:

• Página – O escopo de página é definido para ser o escopo de uma página JSP simples. As variáveis declaradas dentro da página são visíveis somente nela e deixaram de ser visíveis uma vez que o serviço da página for finalizado. O objeto associado a esse escopo é o objeto PageContext.

• Requisição – O escopo de requisição é definido pela requisição simples proveniente do cliente. Os dados que são armazenados neste escopo são visíveis a todos componentes WEB que manipulam a requisição do cliente. Após a requisição do cliente ter sido finalizada, ou seja, o cliente recebeu uma resposta HTTP e finalizou a conexão com o servidor, todos os dados armazenados dentro deste escopo não são mais visíveis.

O objeto associado a este escopo é o objeto HttpServletRequest. As instâncias deste objeto estão prontamente disponíveis às Servlets como parâmetros que podem ser obtidos nos métodos de serviço invocados pelo contêiner através da requisição do cliente.

• Sessão – Este é o escopo que abrange uma “sessão” com o cliente. Esta sessão se inicia quando o cliente acessa a aplicação pela primeira vez e finaliza quando o usuário realiza o logout do sistema ou a partir de um timeout definido no servidor. Os dados deste escopo estão visíveis a todos componentes WEB que o cliente utilizar durante este intervalo. Os dados de uma sessão de usuário, contudo, não estão visíveis para outros usuários.

O objeto associado com este escopo é o objeto HttpSession. Uma instância deste objeto pode ser obtida utilizando o método getSession() do objeto HttpServletRequest.

• Aplicação – Este escopo abrange toda a aplicação. Os dados armazenados neste escopo são visíveis a todos os componentes, independente de requisição de usuário ou sessão que os estejam manipulando, e dura até a aplicação ser finalizada.

O objeto associado com esse escopo é o objeto ServletContext. Como citado anteriormente, ele pode ser obtido através da chamada do método getServletContext() do um objeto ServletConfig válido.

3.1. Armazenando e Recuperando Dados do Escopo

Todos os objetos do escopo mencionados acima contém métodos comuns para recuperar e armazenar dados neles. Para armazenar os dados, utilize o método:

public void setAttribute(String chave, Object valor);

O parâmetro String que o método recebe armazenará o Objeto associado a a este valor. Para recuperar os dados posteriormente, passar a mesma chave como parâmetro para o método:

public Object getAttribute(String chave);

Se não houver objeto a ser recuperado para uma dada chave, o valor null é retornado pelo método. Além disso, como o tipo de retorno é Object, fica a cargo do desenvolvedor fazer o casting para a classe apropriado e realizar checagem de tipo.

Para remover um atributo do escopo, simplesmente invoque o método removeAttribute() e passe a chave do atributo como parâmetro.

3.2. Cenário de Exemplo

Vamos analisar o seguinte cenário: a aplicação deverá exibir os detalhes de uma pessoa a partir de seu personalID. Este personalID será fornecido pelo usuário. Para promover a manutenibilidade, o componente que recuperará os detalhes pessoais será separado do componente que exibirá as informações para o usuário. Estes dois componentes farão a

Programação WEB 9

Page 49: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

comunicação utilizando o armazenamento de dados e as estruturas disponíveis no escopo de requisição.

public PersonalDataRetrievalServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // recupera o parâmetro personalID informado pelo usuário String personalID = request.getParameter("personalID"); // cria o objeto de negócios que manipula uma implementação para obtenção de // dados para um dado id. DataService service = new DataService(); // recupera o objeto person que contém os detalhes necessários Person person = service.retrievePersonalDetails(personalID); // armazena os dados no escopo requisição utilizando uma chave request.setAttribute(ApplicationConstants.PERSON, person); // forward da requisição para o componente que exibirá os detalhes RequestDispatcher dispatcher = request.getRequestDispatcher("/DisplayServlet"); dispatcher.forward(request, response); } } public DisplayServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // recupera o objeto Person que contém os detalhes Person person = (Person) request.getAttribute(ApplicationConstants.PERSON); // constrói a resposta baseado nas informações StringBuffer buffer = new StringBuffer(); buffer.append("<HTML><TITLE>Personal Info</TITLE>"); buffer.append("<BODY><H1>Details : </H1><br/>"); buffer.append("Name : "); buffer.append(person.getName()); buffer.append("<br> Address : "); buffer.append(person.getAddress()); buffer.append("</BODY>"); // exibe a resposta response.setContentType("text/html"); PrintWriter out = response.getWriter(); out.println(buffer.toString()); out.close(); } }

O código para o objeto DataService e o objeto Person não são listados aqui com suas implementações porquê não são relevantes para nosso exemplo. O que o exemplo acima mostra é como armazenar os dados de um objeto em uma servlet e como recuperar os mesmos dados a partir de outra servlet que possui acesso ao mesmo escopo.

Seguem algumas notas sobre o exemplo. Primeiro, a segunda servlet foi capaz de recuperar os dados da requisição porquê ela é parte da mesma requisição. Se fosse usado o método sendRedirect() como meio de redirecionamento de controle da servlet, o método getAttribute() retornaria null já que o método sendRedirect() teria feito com que o browser enviasse outra requisição. Isto efetivamente acabaria com o tempo de vida da requisição, e com os objetos armazenados dentro dela.

Segundo, é recomendado que as chaves utilizadas para armazenar e recuperar os dados estejam

Programação WEB 10

Page 50: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

disponíveis para a aplicação como constantes, conforme o exemplo acima. Isto assegura que a mesma chave seja utilizada para armazenamento e recuperação, reduzindo a possibilidade de erros que possam ser encontrados durante o desenvolvimento.

Programação WEB 11

Page 51: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

4. Rastreabilidade e Gerência de SessãoRecordemos que o protocolo HTTP foi projetado para ser um protocolo conectado, sem estado. Quando um browser enviar uma requisição para o servidor, ele abre uma conexão, envia uma requisição HTTP, consome a resposta HTTP, e então fecha a conexão. Como cada requisição de um browser é efetivamente enviada por diferentes conexões a cada vez, e o servidor não sabe dos clientes que o estão acessando, o problema da manutenção do estado para uma transação de um usuário em particular é um problema a ser enfrentado.

Uma solução para este problema é o servidor manter o conceito de uma “sessão de usuário“. Com uma sessão, o servidor é capaz de reconhecer um cliente no meio de múltiplas requisições. Com este característica, o servidor consegue agora ser capaz de manter o estado para um cliente.

Para tal sessão existir, o browser cliente deve ser capaz de enviar dados para o servidor além da requisição HTTP padrão. Estes dados permitem ao servidor identificar qual usuário está realizando a requisição, e manter seu estado conciso. Existem 3 soluções típicas para esse problema: cookies, URL-rewriting (reescrita de URL) e campos ocultos no formulário.

4.1. Métodos Tradicionais para manter o Estado da Sessão

4.1.1.Cookies

Cookies são pequenas estruturas de dados utilizados pelo servidor WEB que entregam dados para o browser cliente. Estes dados são armazenados pelo browser e, e em algumas circunstâncias, o browser retorna os dados de volta ao servidor WEB.

Ao utilizar cookies, os Servlets podem armazenar “identificadores das sessões” que podem ser utilizados para identificar o usuário como um participante de uma sessão em particular. Após o identificador ser gerado, ele é armazenado num objeto Cookie, e é enviado de volta ao browser cliente para ser armazenado. Este objeto Cookie pode então ser obtido toda vez a partir do objeto da requisição para determinar se o usuário está em determinada sessão.

Abaixo segue um trecho de código sobre como utilizar Cookies para rastreamento de sessões nas Servlets:

... // gerar um novo session ID para o usuário String sessionID = generateSessionID(); // criar novo mapa que será utilizado para armazenar os dados a serem mantidos na sessãoHashMap map = new HashMap(); // recuperar um mapa utilizado como contêiner para as informações do usuárioHashMap containerMap = retrieveSessionMaps(); // adicionar o novo mapa criado no mapa contendo todas as informações de sessão containerMap.put(sessionID, map); // criar o cookie que será armazenado no browser Cookie sessionCookie = new Cookie("JSESSIONID", sessionID); // adicionar o cookie à resposta e pedir ao browser para armazená-lo response.addCookie(sessionCookie); ...

Para obter o MAP contendo os dados da sessão, as Servlets obtêm o Cookie contendo o ID da sessão, e então obtêm o HashMap apropriado utilizando o identificador da sessão como uma chave.

Embora os Cookies sejam uma boa solução para o rastreamento, seu uso requer que os desenvolvedores manipulem diversos detalhes, como:

• gerar um id único para a sessão de cada usuário,

Programação WEB 12

Page 52: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

• recuperar o Cookie apropriado do Browser que contém o Session ID e

• definir um tempo apropriado para a expiração do Cookie.

Além dos detalhes acima, um problema com a utilização de Cookies é que alguns usuários desabilitam o suporte a Cookies no Browser por questões de segurança. São necessárias abordagens alternativas.

4.1.2.URL Rewriting (Reescrita de URL)

Nesta abordagem, o Browser cliente concatena um Session ID único ao fim de cada requisição que ele faz ao servidor. Este Session ID pode então ser lido, e a informação apropriada ao usuário é recuperada.

Esta é outra boa solução que funciona mesmo se o usuário desabilitar o uso de Cookies. Contudo, existem dois problemas associados com essa abordagem. Primeiro, o servidor deve assegurar que cada Session ID que ele fornece seja único. Segundo, a aplicação completa deve ser escrita de forma que o Session ID concatenado a todos os Links/URLs apontem para a aplicação. Qualquer requisição que não inclua o Session ID não seria considerada parte da sessão e não teria acesso às informações específicas da sessão.

4.1.3.Campos ocultos no fomulário

Nesta abordagem, um campo oculto no formulário é introduzido nos formulários HTML com o valor sendo definido a uma sessão particular. Entretanto, este método é muito limitado pelo fato de que somente pode ser utilizando quando há um formulário na página que o cliente esteja utilizando.

4.2. Rastreabilidade de Sessão em Servlets

A especificação de Servlets oferece uma API de alto nível para fornecer acesso ao rastreamento de sessão: a API HttpSession. Ao utilizar esta API, o desenvolvedor não precisa se preocupar com muitos dos detalhes mencionados acima: reconhecimento do Session ID, detalhes da manipulação de Cookies e detalhes da extração de informações da URL. Estes detalhes são transparentes ao desenvolvedor. E, além disso, o desenvolvedor conta com um local apropriado conveniente para armazenar os dados de uma sessão para o usuário. O uso correto da API HttpSession também permite à aplicação trocar para o método de reescrita de URL caso seja detectado que o suporte a Cookies no Browser cliente esteja desabilitado.

4.2.1.Obtendo uma instância do objeto HttpSession

O objeto HttpSession representa os dados da sessão associados a uma dada requisição por um cliente, este pode ser obtido chamando o método getSession() do objeto HttpServletRequest. O contêiner é então responsável por ler os dados do cliente (ou dos Cookies ou da reescrita de URL), e criar uma instância do objeto HttpSession.

Passando um valor booleano para o método getSession() (ex., getSession(true)), é possível especificar ao servidor se um novo objeto HttpSession deve ser automaticamente criado no caso de um usuário não ter uma sessão.

Um código de exemplo mostrando como obter uma instância do objeto HttpSession é apresentado a seguir:

public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { .... HttpSession session = request.getSession();

4.2.2.Armazenando e recuperando dados em uma sessão

Através da classe javax.servlet.http.HttpSession, os desenvolvedores não precisam mais gerenciar objetos explicitamente para armazenar dados em uma sessão de usuário, basta utilizar um dos seguintes métodos:

Programação WEB 13

Page 53: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

public void setAttribute(String key, Object value) public Object getAttribute(String key)

Este são os mesmos métodos que encontramos durante nossa discussão sobre diferentes escopos de objetos. Na verdade, os objetos HttpSession que estamos trabalhando agora representam o escopo de sessão discutido previamente.

Outro exemplo de como salvar e recuperar objetos no escopo de sessões é apresentado abaixo:public class LoginServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { User user = authenticateUser(); ... HttpSession session = request.getSession(); session.setAttribute("userObject", user); ... }}

O trecho de código acima mostra o tratamento de uma entrada do usuário através de uma Servlet. Uma atividade comum realizada por tais tratamentos, além da autenticação de usuários, é a armazenagem de um Objeto tipo user que representa o usuário no escopo da sessão. Este objeto pode ser recuperado por outras Servlets que precisarem de informações sobre o usuário atual, tais como:

public class InfoServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpSession session = request.getSession(); User user = (User)session.getAttribute("userObject"); // implementar aqui as operações necessárias sobre o objeto "user" }}

4.2.3.Removendo dados armazenados na sessão do usuário

Para remover dados armazenados no escopo de sessão, chame o método removeAttribute() e passe a chave (tipo String) associada ao valor em questão.

Um exemplo de servlet realizando tal remoção é apresentado abaixo. Neste cenário imaginário, a aplicação armazenou um valor temporário dentro da sessão associado à chave "tempData". Após ter realizado todas as operações necessárias com os valores associados a esta chave, o código remove o dado da sessão.

public class InfoProcessingServlet extends HttpServlet { public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpSession session = request.getSession(); session.removeAttribute("tempData"); // redirecionar a resposta para a próxima página web response.sendRedirect("next.html"); }}

4.2.4.Encerrando a sessão

Encerramento Automático através de timeout

Sessões são automaticamente encerradas após um intervalo pré-determinado de tempo (diz-se que “a sessão expira”). Este intervalo pode ser inspecionado e configurado no descritor de distribuição da aplicação (deployment descriptor).

O descritor de distribuição para o exemplo FirstServlet é apresentado abaixo para que possamos revisá-lo novamente.

...

Programação WEB 14

Page 54: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

</servlet-mapping> <session-config> <session-timeout> 30 </session-timeout> </session-config> <welcome-file-list> ...

O valor 30 do elemento <session-timeout> indica que o servidor deve esperar pelo menos 30 minutos de inatividade do usuário antes de encerrar uma sessão. Após este período, todos os objetos contidos no escopo de sessão serão removidos e o objeto HttpSession se tornará inválido.

Forçar o encerramento da sessão através de código (encerramento programático)

Além do período de time-out, uma sessão pode ser terminada programaticamente através do método invalidate() da classe HttpSession. O uso deste método é recomendado para situações onde o usuário não precisa mais fazer parte de uma sessão, como por exemplo quando realizamos a saída da aplicação.

Encerrar uma sessão programaticamente, ao invés de esperar o time-out, é também uma forma de liberar os recursos mais rapidamente. Isso também garante que qualquer informação sigilosa armazenada na sessão seja descarregada do escopo de sessão imediatamente, dificultando que tais informações sejam acessadas por outras pessoas.

Um exemplo de tratamento de logout por uma Servlet é apresentado abaixo: public class LogoutServlet extends HttpServlet { public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpSession session = request.getSession(); // realizar aqui outras tarefas do logout // invalidar a sessão do usuário session.invalidate(); // redirecionar o usuário para a página de login response.sendRedirect("login.jsp"); }}

4.2.5.Realizando Redirecionamento de URL (URL-Rewriting)

Por default, objetos HttpSession usam Cookies para manter o escopo de sessões. Entretanto, devemos desenvolver nossas aplicações adicionando suporte para redirecionamento de URLs, permitindo que a aplicação rode igualmente bem em navegadores que não suportam Cookies.

Podemos adicionar suporte a redirecionamento de URLs usando o método encodeURL() encontrado na interface javax.servlet.http.HttpServletResponse. Esse método recebe como parâmetro uma String representando um caminho (path) ou uma URL. O método, então, verifica se o suporte a Cookies está habilitado no navegador do usuário. Se o suporte a Cookies está habilitado, o método retorna a String recebida como parâmetro. Caso contrário, habilita o redirecionamento de URLs incluindo a identificação da sessão (Session ID) como um dos parâmetros da URL.

O código abaixo é um exemplo de como usar o método encodeURL:... String encodedURL = response.encodeURL("/welcome.jsp"); out.println("<A HREF='" + encodedURL + "'>Clique aqui para continuar</A>"); ...

Para fornecer a funcionalidade de redirecionamento de URLs em nossa aplicação WEB, devemos substituir todas as URLs exibidas ao usuário, como hyperlinks, por alguma página passada com o método encodeURL. Caminhos (paths) repassados ao método sendRedirect, discutido anteriormente, também devem ser novamente codificados – desta vez, usando o método encodeRedirectURL().

Programação WEB 15

Page 55: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

5. Filtros (Filter)Filtros são componentes WEB avançados, introduzidos desde a especificação Serlvet 2.3. São componentes que se interpõem entre uma requisição do cliente e um determinado recurso. Qualquer tentativa de recuperar o recurso deve passar pelo filtro. Um recurso pode ser qualquer conteúdo estático ou dinâmico (HTML, JSP, GIF, etc.).

Filtros funcionam interceptando as requisições do cliente ao servidor através de um encadeamento de filtros. Ou seja, existe uma série de filtros, através dos quais uma requisição passa, antes de, finalmente, acessar um determinado recurso. Quando a requisição passa pelo filtro, esse filtro pode processar os dados contidos na requisição e também decidir sobre o próximo passo da requisição – que pode ser encaminhada para um outro filtro (caso o filtro atual não seja o último da cadeia de filtros), acessar o recurso diretamente ou impedir que o usuário acesse o recurso desejado.

Figura 3: Ilustração de requisição de passagem por filtros

A figura acima apresenta a maneira pela qual as requisições passam através de filtros, antes de acessar seu ponto final (EndPoint) – um recurso.

Filtros são componentes úteis, visto que fornecem ao desenvolvedor uma forma simples de incluir processamento adicional, antes que os recursos de uma aplicação WEB sejam acessados. Esse tipo de funcionalidade também é possível de ser implementado através do redirecionamento de Servlets e requisições. Entretanto, o uso de redirecionamento é bem menos elegante, porque exige que o encadeamento dos processos seja programado diretamente no código das Servlets que compõem a aplicação – e cada vez que esse encadeamento mudar, serão necessárias modificações no código das Servlets para reconfigurar o encadeamento. Filtros, por sua vez, não sofrem essa limitação, pois é o contêiner que gerencia a seqüência de componentes a serem chamados, antes que uma determinada requisição acesse um recurso. Eventualmente, o contêiner pode ser reconfigurado alterando-se a quantidade e a ordem dos filtros, sem que haja necessidade de alterações no código-fonte da aplicação.

5.1. Criando um Filtro

Para criar um filtro, os desenvolvedores devem criar uma classe que implemente a interface javax.servlet.Filter. Essa interface define os seguintes métodos:

• void init(FilterConfig config) throws ServletException – esse método é chamado pelo contêiner durante a primeira vez que o filtro é carregado na memória. Códigos de inicialização devem ser colocados aqui, incluindo o uso de parâmetros de inicialização contidos no arquivo web.xml – recebidos no parâmetro FilterConfig.

• void destroy – esse método é chamado pelo contêiner quando o filtro é descarregado da memória. Isso ocorre normalmente quando a aplicação WEB é encerrada. O código de

Programação WEB 16

Page 56: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

liberação de recursos utilizados pelo filtro deve ser inseridos aqui – o encerramento de uma conexão ao banco de dados, por exemplo.

• void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException – esse método contém toda a funcionalidade do filtro, ou seja, as regras que devem ser verificadas, antes que uma requisição possa continuar seu caminho até um recurso. Esse método é chamado pelo contêiner quando o servidor decide que o filtro deve interceptar uma determinada requisição ou resposta ao usuário. Os parâmetros passados para esse método são instâncias das classes ServletRequest e ServletResponse do pacote javax.servlet.http e da classe FilterChain do pacote javax.servlet. Se o filtro estiver sendo usado por uma aplicação web (o caso mais comum), o desenvolvedor pode realizar o casting entre esses objetos para instâncias das classes HttpServletRequest e HttpServletResponse do pacote javax.servlet.http, respectivamente. Isso permite a recuperação de informações específicas do protocolo HTTP.

Assim como um servlet, o contêiner irá criar uma única instância da classe Filter e usar processamento multi-tarefa (multi-threading), para lidar com as requisições concorrentes de vários clientes. Isso significa que esse método deve ser programado no modo thread-safe.

Um exemplo de classe implementando um filtro é apresentado abaixo. Esse filtro usa o recurso de entrada disponível na classe ServletContext, para registrar todas as requisições do usuário que passam pelo filtro.

import java.io.IOException;import java.util.logging.Filter;import java.util.logging.LogRecord;import javax.servlet.FilterChain;import javax.servlet.FilterConfig;import javax.servlet.ServletContext;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;public class LoggingFilter implements Filter { private FilterConfig config; public void init(FilterConfig config) { this.config = config; } public void doFilter( ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException { // recupera o objeto ServletContext que sera usado para realizar o logging ServletContext context = config.getServletContext(); // cria um registro da URL acessada pelo usuário String logEntry = request.getServerName() + ":" + request.getServerPort(); logEntry += "/" + request.getLocalAddr() + "/" + request.getLocalName(); logEntry += "--> acessado pelo usuário em " + new java.util.Date(); // utilizando o recurso de logging disponível na classe ServletContext context.log(logEntry); // chama o próximo filtro na cadeia de filtros chain.doFilter(request, response); } public void destroy() {} public boolean isLoggable(LogRecord arg0) { throw new UnsupportedOperationException("Not supported yet."); }}

5.2. Encadeamento de Filtros (Filter Chains)

O encadeamento de filtros permite que filtros sejam aplicados a um recurso obedecendo a uma seqüência pré-determinada. A característica de encadeamento dos filtros permite uma lógica diferenciada no uso destes componentes. O encadeamento é a diferença básica entre os filtros e

Programação WEB 17

Page 57: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

os servlets.

O encadeamento de filtros permite uma separação clara entre as diversas camadas de processamento de requisições. Se, por exemplo, novas funcionalidades precisarem ser implementadas antes do processamento de uma requisição, basta criar um novo filtro. Uma vez que o filtro estiver configurado, basta adicioná-lo à cadeia de filtros aplicados à requisição antes que ela atinja o seu ponto final (um recurso). Isso não interfere em nenhum processamento que já estivesse sendo realizado sobre a requisição, a menos que o código do filtro tenha sido projetado para isso.

A seqüência da cadeia de filtros é determinada pela localização da declaração de um filtro no descritor de distribuição (deployment descriptor) e obedece à ordem crescente dos elementos <filter-mapping>. Esses elementos representam o mapeamento entre cada filtro e um recurso.

Conforme dito anteriormente, a classe javax.servlet.FilterChain representa a seqüência de filtros que serão chamados antes de uma requisição atingir seu ponto final. O único controle que o desenvolvedor tem sobre essa seqüência é o método doFilter da classe FilterChain: esse método chama o próximo filtro da seqüência ou diretamente o recurso, se o filtro for o último da seqüência. Esse método requer objetos das classes ServletRequest e ServletResponse, do pacote javax.servlet, como parâmetros. Novamente, como não há necessidade do programador se preocupar com o próximo filtro do encadeamento, ele pode se concentrar apenas na lógica da classe que está sendo produzida.

Se um desenvolvedor esquecer de chamar o método doFilter, o resto da cadeia de filtros (e eventualmente o recurso final) nunca será acessada. Se essa não for a funcionalidade desejada, deve-se ter atenção no momento de programar o código do filtro para garantir a chamada do método. Entretanto, existem casos em que desejamos realmente interromper a cadeia de filtros. Um bom exemplo é um filtro de segurança que evita o acesso indevido a recursos de uma aplicação caso alguns critérios não sejam obedecidos (senha inválida, etc.).

Um exemplo desse filtro é apresentado abaixo. O código descreve um filtro de segurança que verifica a existência de informações sobre o usuário armazenadas no escopo da sessão. Se não encontrar tais informações o filtro assume que o usuário não está mais logado ou então que a sua sessão expirou. Em qualquer um desses casos, o filtro responde à requisição, redirecionando o usuário à página de login.

import java.io.IOException;import java.io.PrintWriter;import java.util.logging.Filter;import java.util.logging.LogRecord;import javax.servlet.FilterChain;import javax.servlet.FilterConfig;import javax.servlet.RequestDispatcher;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpSession;public class SecurityFilter implements Filter { private FilterConfig config; public void init(FilterConfig config) { this.config = config; } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException { HttpServletRequest httpRequest = (HttpServletRequest)request; HttpServletResponse httpResponse = (HttpServletResponse)response; HttpSession session = httpRequest.getSession();

User user = (User) session.getAttribute("userObject"); if (user != null) {

Programação WEB 18

Page 58: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

// se o usuário atender a certas condições, permitir o acesso aos recursos chain.doFilter(request, response); } else { PrintWriter out = response.getWriter(); out.println("Bem-vindo, por favor, " + "faça o login antes de continuar utilizando a aplicação."); RequestDispatcher rd = request.getRequestDispatcher("login.jsp"); rd.include(request, response); } } public void destroy() {} public boolean isLoggable(LogRecord arg0) { throw new UnsupportedOperationException("Not supported yet."); }}

5.3. Configuração de filtros

A configuração de filtros é muito parecida com a configuração de Servlets. Existe uma sessão necessária para configurar cada filtro utilizado na aplicação bem como sessões para configurar o mapeamento entre os filtros e os padrões de URLs aos quais as cadeias de filtros serão aplicadas.

Um exemplo de configuração de filtro é apresentado abaixo: ... </context-param> <filter> <filter-name>LoggingFilter</filter-name> <filter-class>jedi.filters.LoggingFilter</filter-class> </filter> <filter-mapping> <filter-name>LoggingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>

Dado que a interpretação do arquivo web.xml sofre influência da ordem em que os elementos são declarados, recomendamos a declaração dos filtros após os elementos <context-param> e antes de qualquer definição sobre servlets. Também cuide para colocar os elementos <filter-mapping> após a definição dos filtros.

Por default, filtros não são aplicados a componentes web (outros servlets, JSP, etc.) acessados pela classe RequestDispatcher através de chamadas include ou forward. Filtros são aplicados apenas a requisições feitas diretamente pelo cliente. Esse comportamento, entretanto, pode ser modificado, incluindo-se um ou mais elementos <dispatch> ao mapeamento do filtro.

... </context-param> <filter> <filter-name>LoggingFilter</filter-name> <filter-class>jedi.filters.LoggingFilter</filter-class> </filter> <filter-mapping> <filter-name>LoggingFilter</filter-name> <url-pattern>/*</url-pattern> <dispatch>REQUEST</dispatch> <dispatch>INCLUDE</dispatch> </filter-mapping>

Elementos <dispatch> possuem um dos seguintes valores: REQUEST, INCLUDE, FORWARD e ERROR. Isso indica quando um filtro deve ser aplicado apenas a requisições do cliente, apenas em includes, apenas em requisições, apenas em caso de erro ou na combinação desses quatro valores.

Programação WEB 19

Page 59: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

6. Exercícios

6.1. Exercícios sobre redirecionamento de respostas

1) Quais são as duas formas de se realizar redirecionamento na API Servlet?

2) Qual é a diferença entre utilizar o método include e o método forward no objeto RequestDispatcher?

3) Qual é a diferença entre utilizar o método forward do objeto RequestDispatcher e o método sendRedirect utilizando o objeto HttpServletResponse?

6.2. Exercícios sobre escopo

1) Quais são os diferentes escopos disponíveis em uma aplicação WEB nos quais um objeto pode ser armazenado?

2) Qual objeto é associado ao escopo de aplicação? E ao escopo de sessão?3) Qual o tempo de vida/visibilidade dos objetos colocados no escopo de sessão? E no escopo

de requisição? 4) Dado o código abaixo:

public class SenderServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String message = "Can you hear me?"; request.setAttribute("messageKey", message); response.sendRedirect("ReceiverServlet"); } }

Se houvesse uma ReceiverServlet mapeada para o local indicado dentro do método sendRedirect, seria possível recuperar a mensagem? Justifique?

5) Crie uma implementação de ReceiverServlet que tente recuperar a mensagem armazenada dentro do escopo de requisição pela classe SenderServlet dada no exercício anterior.

6.3. Exercícios sobre rastreabilidade

1) A classe HttpSession fornecida pela Servlet API utiliza Cookies para controlar o escopo de sessão de usuário. Qual é a desvantagem dessa forma de controlar sessões de usuários?

2) Como um desenvolvedor pode recuperar uma instância da classe HttpSession?3) Considere o seguinte código:

public class InfoBean { private String nome; private String numero; // métodos get(...) e set(...) }

Se uma instância dessa classe foi armazenada em uma sessão sob a chave "infoBean", crie uma Servlet que recupere a instância e exiba os valores dos atributos nome e número.

6.4. Exercícios sobre filtros

1) Quais são os três métodos definidos na interface Filter que devem ser implementados em classes de filtro?

2) Quando configuramos um filtro no descritor de aplicações WEB, essa configuração deve vir antes ou depois da definição das Servlets?

3) Após um filtro ter realizado sua funcionalidade, como ele chama o próximo filtro da cadeia de filtros ou o recurso desejado?

Programação WEB 20

Page 60: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

4) Considere o seguinte cenário:

Temos uma aplicação WEB com funcionalidades administrativas que podem ser disponibilizadas aos seus usuários comuns. Se todos os recursos necessários a essas funcionalidades administrativas estão localizados no diretório admin de nossa aplicação, como configurar um filtro chamado AdminFilter tal que todas as requisições a recursos administrativos passem por esse filtro?

5) Crie uma classe que implementa o AdminFilter descrito acima. O filtro deve ser capaz de determinar quando um usuário é autorizado ou não a recuperar um valor lógico armazenado no escopo de sessão. O nome da chave usada para armazenar o valor lógico é isAdmin. Se o valor for true, o usuário está autorizado a acessar os recursos administrativos. Caso contrário, o usuário deve ser redirecionado à página de entrada, com uma mensagem informando que ele não tem as credenciais necessárias para acessar a parte administrativa do sistema.

Programação WEB 21

Page 61: Projeto JEDI - Programação para WEB - Java - 178 páginas

Módulo 6Programação WEB

Lição 4Páginas JSP Básicas

Versão 1.0 - Nov/2007

Page 62: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

1. ObjetivosNas lições anteriores, aprendemos como produzir conteúdo dinâmico para nossos usuários usando a Tecnologia Java por meio do uso de classes servlets. Entretanto, enquanto os desenvolvedores Java podem criar sites com conteúdo dinâmico usando servlet, existem desvantagens em fazê-lo.

Primeiro, utilizar servlet somente para produzir conteúdo HTML não torna o código limpo e de fácil manutenção. Strings a serem usadas para saída em HTML podem se tornar muito grandes – medindo várias linhas – e podem facilmente tornar-se um pesadelo de manutenção.

Segundo, usar servlet somente para produzir conteúdo HTML assume que o desenvolvedor está familiarizado com Java e HTML. Especialmente no caso de grandes organizações, projetistas de site são grupos separados de programadores. Ter que treinar os projetistas que possuam um entendimento básico de Java, ou para desenvolvedores Java obterem habilidade em projeto de site em HTML, pode consumir tempo e recursos caros para uma organização.

É aqui que a tecnologia JSP entra.

Ao final desta lição, o estudante será capaz de:

• Ter uma visão geral sobre páginas JSP

• Conhecer o ciclo de vida de páginas JSP

• Compreender a sintaxe e a semântica das páginas JSP

Programação WEB 4

Page 63: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

2. Visão Geral

2.1. O que é JSP?

JavaServer Pages (JSP) é uma tecnologia baseada em servlet usada na camada WEB para apresentar conteúdo dinâmico e estático. Ela é baseada em texto e contém, em sua maioria, modelo de texto em HTML misturado com tags que especificam conteúdo dinâmico.

2.2. Porque JSP?

• Considerando que JSP são documentos de texto como HTML, desenvolvedores evitam ter que formatar e manipular uma String longa para produzir saída. O conteúdo HTML não estará embutido dentro de código em Java. Isto torna mais fácil sua manutenção.

• JSP são familiares para qualquer um com conhecimento de HTML, porque possuem somente marcação dinâmica, isto é, tags. Isto torna possível para projetistas de site criar o modelo HTML do site restando aos desenvolvedores processando-o posteriormente a inclusão das tags para produzir o conteúdo dinâmico. Isto torna fácil o desenvolvimento de páginas WEB.

• JSP têm suporte interno para o uso de componentes de software reusáveis (JavaBeans). Estes não somente permitem que os desenvolvedores evitem “reinventar a roda” para cada aplicação. Ter suporte para componentes de software separados para manipular lógica promove separação de apresentação e lógica de negócios também.

• JSP, como parte da solução Java para desenvolvimento de aplicação WEB, são inerentemente multiplataforma e podem ser executadas em qualquer contêiner compatível, independente do distribuidor ou sistema operacional.

• Devido ao modo como JSP funciona, não necessita de compilação explícita pelo desenvolvedor. Esta compilação é feita pelo contêiner. Modificações na JSP é também detectadas automaticamente e resultam em uma nova compilação. Isto torna as páginas JSP relativamente simples de desenvolver.

2.3. JSP Exemplo

<HTML> <TITLE>Welcome</TITLE> <BODY> <H1>Greetings!</H1><br> Thank you for accessing our site. <br> The time is now <%= new java.util.Date()%> </BODY> </HTML>

Este é um simples arquivo em JSP que apresenta uma saudação genérica para os usuários do site, bem como informa a data e hora corrente de acesso. A figura 1 corresponde à saída do arquivo JSP anterior.

Figura 1: Saída da página welcome.jsp

Programação WEB 5

Page 64: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

Podemos ver que o arquivo JSP é em sua maioria de natureza HTML. A única parte representativa é este pedaço de instrução: <%=new java.util.Date()%>, responsável por exibir a data e hora corrente. Realizando este trabalho criando uma nova instância do objeto Date e exibindo sua String correspondente.

2.4. Executando o JSP Exemplo

2.4.1. Usando o IDE

Uma JSP pode executar sobre qualquer projeto WEB criado pela IDE. Assumindo a existência de um projeto, poderemos simplesmente adicionar um arquivo do tipo JSP na pasta Web Pages.

Figura 2: Página JSP para um projeto WEB

Isto especifica que a página JSP pode então ser diretamente executado a partir do IDE pressionado-se Shift+F6. Alternativamente, o projeto WEB pode ser empacotado como um arquivo WAR e transferido para o servidor. A página JSP pode então ser acessada digitando-se a seguinte URL:

http://[host]:[port]/[WEB_PROJECT_NAME]/[JSP_NAME]

2.4.2.Usando um arquivo de construção Ant

A página JSP pode também ser executada pelo empacotamento em um arquivo WAR usando uma ferramenta de construção (tal como esboçado na lição 2 – Classes Servlet), e então colocando o arquivo WAR em um servidor WEB. A estrutura do diretório é replicada abaixo para lembrete:

Figura 3: Estrutura de diretório recomendado para desenvolvimento de aplicação web.

Programação WEB 6

Armazenará a aplicação após executar o script de construção em uma estrutura de diretório reconhecido pelo contêiner.

Depois de executar o script construído, conterá o arquivo WAR.

Usado para armazenar a documentação que é utilizada pelo time de desenvolvimento.

Diretório dentro do qual todos os arquivos fonte Java devem ser colocados.

Diretório contendo todo conteúdo static da app (HTML, JSP), será a base da raiz dodocumento de seu projeto.Diretório contendo arquivos de configuração como o descritor de desenvolvimento (web.xml), descritores de biblioteca de tags, etc.

Page 65: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

3. Ciclo de vida JSP O WEB Server gerencia as páginas JSP de modo similar como as classes Servlets. Isto é feito através de um ciclo de vida bem definido.

JSP têm ciclos de vida de três fases: inicialização, serviço e destruição. Estes eventos de ciclo de vida são similares aos dos servlets, embora os métodos invocados pelo WEB Server sejam diferentes:

● jspInit() para a fase de inicialização

● _jspService() para a fase de serviço

● jspDestroy() para a fase de destruição

Figura 4: Ciclo de vida de 3 fases da JSP

Dado o JSP exemplo acima, parece confuso falar a respeito dos métodos jspInit ou _jspService(). A página JSP exemplo é apenas uma simples página de texto com a maior parte de conteúdo HTML. Não possui quaisquer métodos. Páginas JSP criam uma classe Servlet que são compiladas pelo WEB Server. Esta classe Servlet manipula todas as requisições para a página JSP. Esta tradução em Servlet e subseqüente compilação é feita de modo transparente pelo WEB Server. Não é necessário o desenvolvedor preocupar-se com o modo de como este procedimento é realizado.

Para ver os arquivos da classe Servlet gerados, o Sun Application Server 8.1 colocam estes arquivos no seguinte diretório:

${SERVER_HOME}/domains/domain1/generated/jsp/j2ee-modules/ ${WEB_APP_NAME}/org/apache/jsp

Abaixo está a classe Servlet equivalente ao nosso exemplo.package org.apache.jsp; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.jsp.*; public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase implements org.apache.jasper.runtime.JspSourceDependent { private static java.util.Vector _jspx_dependants; public java.util.List getDependants() { return _jspx_dependants; } public void _jspService(HttpServletRequest request, HttpServletResponse response) throws java.io.IOException, ServletException {

Programação WEB 7

Page 66: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

JspFactory _jspxFactory = null; PageContext pageContext = null; HttpSession session = null; ServletContext application = null; ServletConfig config = null; JspWriter out = null; Object page = this; JspWriter _jspx_out = null; PageContext _jspx_page_context = null; try { _jspxFactory = JspFactory.getDefaultFactory(); response.setContentType("text/html"); response.addHeader("X-Powered-By", "JSP/2.0"); pageContext = _jspxFactory.getPageContext(this, request, response, null, true, 8192, true); _jspx_page_context = pageContext; application = pageContext.getServletContext(); config = pageContext.getServletConfig(); session = pageContext.getSession(); out = pageContext.getOut(); _jspx_out = out; out.write("<HTML>\n"); out.write("\n"); out.write(" <TITLE>Welcome</TITLE>\n"); out.write("\n"); out.write(" <BODY>\n"); out.write(" <H1> Greetings!</H1> <br>\n"); out.write("\n"); out.write(" Thank you for accessing our site. <br>\n"); out.write("\n"); out.write(" The time is now "); out.print( new java.util.Date()); out.write("\n"); out.write("\n"); out.write(" </BODY>\n"); out.write("\n"); out.write("</HTML>"); } catch (Throwable t) { if (!(t instanceof SkipPageException)) { out = _jspx_out; if (out != null && out.getBufferSize() != 0) out.clearBuffer(); if (_jspx_page_context != null) _jspx_page_context.handlePageException(t); } } finally { if (_jspxFactory != null) _jspxFactory.releasePageContext(_jspx_page_context); } } }

Neste momento não é importante entender completamente o funcionamento classe acima. É importante conhecer como as páginas JSP são manipuladas da mesma forma que as classes Servlets, mesmo que isto não seja imediatamente óbvio. Outro ponto é que as páginas JSP são transformadas classes Servlets. Somente diferem no modo como um desenvolvedor produz o conteúdo. Páginas JSP são orientadas a texto, enquanto que as classes Servlets permite ao desenvolvedor aplicar fortes conhecimentos de Java.

Programação WEB 8

Page 67: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

4. Sintaxe JSP e Semântica Embora páginas JSP sejam baseadas em linguagem Java, sendo manipuladas como uma classe Java pela Servlet. A sintaxe permite aos desenvolvedores os usar de forma diferente daquela da especificação 2.0 de Java. Ao invés disso, segue regras definidas na especificação JSP. A seção seguinte descreve a sintaxe JSP em mais detalhes.

4.1. Elementos e Modelo de Dados

Componentes de todas JavaServer Pages podem ser qualificados em duas categorias gerais: elementos e modelo de dados. Elementos são informação produzida dinamicamente. Modelo de Dados são informação estática que são responsáveis pela apresentação. Na página hello.jsp, a expressão JSP <%=new java.util.Date()%> é o único elemento de dados e o resto são dados modelo de dados.

<html> <head> <title>Hello World!</title> </head> <body> <center> <h1>Hello World! It's <%= new java.util.Date()%></h1> </center> </body> </html>

4.1.1. Dois Tipos de Sintaxe

Dois estilos de criação de páginas JSP são suportados pelos WEB Server: o estilo JSP e o estilo XML. Ambos estão presentes nesse texto. Escolher um estilo ao invés da outro é apenas uma questão de preferência e padronização. A estilo JSP foi desenvolvido para ser de fácil criação. O estilo XML é simplesmente o estilo JSP modificado para ser compatível com a sintaxe XML. O estilo XML é preferido quando utilizamos uma ferramenta de criação de JSP. No entanto, a maioria prefere o estilo JSP por ser mais fácil de ler e entender. Esse texto irá utilizar o estilo JSP nos exemplos.

4.2. Elementos de Scripting

Como mencionado na lição anterior, páginas JSP devem ser vistas como documentos HTML ou XML com scripts JSP embutido. Elementos de scripting JSP permitem inserir instruções Java na classe Servlet gerada a partir da página JSP.

A forma mais simples de se criar uma JSP dinâmica é inserir diretamente elementos de scripting no modelo de dados.

Nessa lição iremos aprender os seguintes elementos de scripting JSP:

1. Scriptlets,

2. Expressões, e

3. Declarações.

4.2.1. Scriptlets

Proporcionam uma maneira de inserir diretamente pedaços de instruções Java em diferentes partes da modelo de dados e tem a seguinte forma:

<% Instruções Java; %>

Definir instruções Java entre <% e %> é o mesmo que estar codificando dentro de um método. Scriptlets são úteis para embutir instruções Java como comandos condicionais, de repetição, entre outros. Não existe um limite específico sobre a complexidade das instruções Java que podem ser

Programação WEB 9

Page 68: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

inseridas em scriptlets. No entanto, devemos ter grande precaução a utilização exagerada de scriptlets. Colocar processos computacionais pesados dentro de códigos scriptlets gera um problema para manutenção. Além disso, o excesso de utilização de scriptlets viola a regra de JSP de ser prioritariamente um componente da camada de apresentação.

Discutiremos mais adiante como poderemos utilizar JavaBeans para encapsular o resultado de dados passados por outro componente, reduzindo drasticamente a quantidade de scriptlets necessários em uma página. Mais adiante, iremos discutir como utilizar tags customizadas para executar tarefas comuns como decisão lógica, repetição, entre outras. Isso, combinado com a utilização dos JavaBeans, desta forma iremos diminuir a necessidade de scriptlet.

Se quisermos utilizar os caracteres "%>" dentro de um scriptlet, escrevemos "%\>" . Isso evitará que o compilador interprete os caracteres como tag de fechamento de scriptlet.

Abaixo temos dois exemplos que definem códigos Java dentro de tags HTML.

Método println

Figura 5: PrintlnScriplet.jsp

O exemplo mostra a implementação de instruções Java embutidas em uma página JSP. Esta página escreve o texto contido no atributo username no navegador WEB.

Comando for em scriptlet

Figura 6: LoopScriplet.jsp Este outro exemplo mostra a implementação do código Java de um laço for inserido dentro das tags de scriptlet (<%...%>). A saída que teremos no navegador após a execução dessa JSP deve ser o texto “This line is printed 10 times.” mostrado 10 vezes devido a iteração do comando for de 0 até 9 conforme mostrado na figura a seguir:

Programação WEB 10

Page 69: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

Figura 7: Saída do LoopScriplet.jsp

Note que o scriptlet não é enviado para o cliente, apenas sua saída. Tente ver o código da saída do JSP que foi produzida no browser para entender esse ponto. Tudo o que vemos são tags HTML mais a saída do scriptlet mas não vemos o scriptlet.

O estilo XML para escrever scriptlets é: <jsp:declaration> Código Java;</jsp:declaration>

4.2.2.Expressões

Expressões fornecem uma maneira de inserir valores Java diretamente na saída. Elas tem a seguinte forma:

<%= Expressão Java %>

Expressões nada mais são do que uma abreviação para out.println().

Note que o ponto-e-vírgula ( ; ) não aparece no final da instrução dentro da tag.

Qualquer expressão Java colocada entre <%= e %> é avaliada em tempo de execução, convertida para String e inserida na página. Uma expressão sempre envia uma String de texto para o cliente, mas o objeto produzido como resultado da expressão não necessariamente será uma instância do objeto String. Todas as instâncias de objetos não-String são convertidos para strings através de seus métodos membros herdados toString(). Se o resultado for primitivo, então uma representação String do valor primitivo será mostrada.

Como mencionado anteriormente, avaliações são efetuadas em tempo de execução (quando a página é requisitada). Isso dá às expressões acesso total às informações sobre a requisição (request).

Diversos objetos predefinidos estão disponíveis para os desenvolvedores de JSP para simplificar expressões. Esses objetos são chamados de objetos implícitos e serão discutidos em maiores detalhes posteriormente. Para o propósito das expressões, as mais importantes são:

• requisição (request), o HttpServletRequest;

• resposta (response), o HttpServletResponse;

• sessão (session), o HttpSession associado com à requisição (se houver); e

Programação WEB 11

Page 70: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

• saída (out), o PrintWriter (uma versão armazenada do tipo JspWriter) utilizado para enviar dados de saída para o cliente.

Por exemplo, para imprimir o nome da máquina (hostname), só precisamos incluir essa simples expressão JSP abaixo:

Nome da Máquina: <%= request.getRemoteHost() %>

O estilo XML para a tag <%= Expressão Java %> é:<jsp:expression> Expressão Java</jsp:expression>

4.2.3.Declarações

Declaração permitem definir métodos ou variáveis. Elas possuem o seguinte formato: <%! Código Java %>

Declarações são usadas para embutir código de modo semelhante aos scriptlets. No entanto, declarações são inseridas na parte principal (main body) da classe Servlet, fora do método _jspService() que processa a requisição. Por essa razão, os códigos embutidos em declarações podem ser usados para declarar métodos ou variáveis globais. Por outro lado, código das declarações NÃO são protegidos, a não ser que seja explicitamente programado pelo desenvolvedor da JSP, logo devemos tomar cuidado ao escrever declarações.

A seguir temos alguns lembretes simples sobre a utilização da utilização da tag de declaração:

• Antes da declaração deve ter <%! e ao final da declaração %>

• As instruções devem seguir a sintaxe Java padrão

• Declarações não geram saída mas usadas com expressões JSP ou scriptlets

Como declarações não geram qualquer saída, elas são normalmente usadas em conjunto com expressões JSP ou scriptlets. Por exemplo, aqui temos uma JSP que imprime o número de vezes que a página corrente foi requisitada desde que o servidor foi iniciado (ou a classe servlet foi mudada ou recarregada):

Exemplo

Figura 8: AccessCountDeclaration.jsp

A JSP é capaz de mostrar o número de visitas através da declaração de uma atributo com escopo na classe accessCount, utilizando um scriptlet para incrementar o valor do número de vezes que a página foi visitada e uma expressão para mostrar o valor.

A ilustração abaixo mostra um exemplo de saída dessa JSP carregada quatro vezes.

Programação WEB 12

Page 71: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

Figura 9: Saída da AccessCountDeclaration.jsp

Para entendermos melhor como uma página JSP é transformada em uma servlet, vamos examinar a saída da servlet do contêiner para a página AccessCountDeclaration.jsp. O contêiner gerou um arquivo Java chamado AccessCountDeclaration_jsp.java, a seguir veremos o conteúdo desse arquivo. Observe que a declaração, o scriptlet e a expressão foram destacadas para facilitar a referência.

AccessCountDeclaration_jsp.java

package org.apache.jsp.JSP; import javax.servlet.*;import javax.servlet.http.*;import javax.servlet.jsp.*; public final class AccessCountDeclaration_jsp extends org.apache.jasper.runtime.HttpJspBase implements org.apache.jasper.runtime.JspSourceDependent { private int accessCount = 0; private static java.util.Vector _jspx_dependants; public java.util.List getDependants() { return _jspx_dependants; } public void _jspService ( HttpServletRequest request, HttpServletResponse response) throws java.io.IOException, ServletException { JspFactory _jspxFactory = null; PageContext pageContext = null; HttpSession session = null; ServletContext application = null; ServletConfig config = null; JspWriter out = null; Object page = this; JspWriter _jspx_out = null; PageContext _jspx_page_context = null; try { _jspxFactory = JspFactory.getDefaultFactory(); response.setContentType("text/html"); pageContext = _jspxFactory.getPageContext(this, request, response, null, true, 8192, true); _jspx_page_context = pageContext; application = pageContext.getServletContext(); config = pageContext.getServletConfig(); session = pageContext.getSession(); out = pageContext.getOut(); _jspx_out = out; out.write("\n"); out.write("\n"); out.write("<html>\n"); out.write("<head>\n");

Programação WEB 13

Page 72: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

out.write("<title>Declaration Example</title>\n"); out.write("</head>\n"); out.write("<body>\n"); accessCount++; out.write("Accesses to page since server reboot: \n"); out.print( accessCount ); out.write("\n"); out.write("</body>\n"); out.write("</html>\n"); out.write("\n"); out.write("\n"); } catch (Throwable t) { if (!(t instanceof SkipPageException)) { out = _jspx_out; if (out != null && out.getBufferSize() != 0) out.clearBuffer(); if (_jspx_page_context != null) _jspx_page_context.handlePageException(t); } } finally { if (_jspxFactory != null) _jspxFactory.releasePageContext(_jspx_page_context); } } }

Note como a declaração para accessCount é colocada fora do método _jspservice() como um atributo. Isso torna accessCount disponível para qualquer outro método definido na JSP, e não apenas para o método _jspservice(). O código anterior nos mostrou a colocação das declarações, scriptlets e expressões no código fonte Java traduzido da página JSP.

O estilo XML para a tag <%! Instruções Java %> é

<jsp:declaration>Código Java;</jsp:declaration>

4.2.4.Texto Template

• Utilize <\% para ter <% na saída. • <%-- Comentário JSP --%> • <!-- Comentário HTML --> • Todos os outro textos não específicos a JSP são passados para a saída da página.

4.3. Objetos Predefinidos

Na discussão da tag de expressões, abordamos objetos JSP implícitos. Essa sessão descreve esses objetos em detalhe.

Objetos JSP implícitos são automaticamente declarados pelo contêiner e estão sempre disponíveis para utilização pelas expressões e scriptlets (mas não em declarações). A seguir temos a lista de objetos implícitos:

request: Instância da classe javax.servlet.http.HttpServletRequest, este objeto é associado com a requisição do cliente.

response: Instância da classe javax.servlet.http.HttpServletResponse, este objeto é associado com a resposta para o cliente.

pageContext: Objeto associado com a página corrente.

out: Instância da classe javax.servlet.jsp.JspWriter, este objeto pode ser usado para escrever ações e modelo dos dados em páginas JSP, parecido com objetos da classe PrintWriter que utilizamos na discussão da Servlet. O objeto out é inicializado automaticamente utilizando métodos de objetos da classe PageContext.

Programação WEB 14

Page 73: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

session: Instância da classe javax.servlet.http.HttpSession. É equivalente a chamarmos o método HttpServletRequest.getSession().

application: Instância da classe javax.servlet.ServletContext. É equivalente a chamarmo o método getServletConfig().getContext(). Esse objeto implícito é compartilhado por todos as Servlets e páginas JSP no servidor.

config: Instância da classe javax.servlet.ServletConfig para essa página. Igual as Servlets, JSP tem acesso aos parâmetros inicialmente definidos no Deployment Descriptor do servidor de aplicação.

4.4. Diretivas JSP

Diretivas são mensagens para o contêiner. Afetam toda estrutura da classe servlet. Geralmente tem a seguinte forma:

<%@ atributo da diretiva="valor" %>Uma lista de configuração de atributos pode também ser enumerada para uma simples diretiva como segue:

<%@ atributo1 da diretiva="valor1" atributo2="valor2" ... atributoN="valorN" %>

Nota: Espaços em branco após <%@ e depois %> são opcionais.

Diretivas NÃO produzem qualquer saída visível quando a página é requisitada mas elas mudam a forma que o contêiner processa a página. Por exemplo, podemos ocultar dados da sessão para uma página configurando uma diretiva (session) para falso.

Uma diretiva JSP dá informações especiais sobre a página para o contêiner. A diretiva pode ser page, include ou taglib e cada uma dessas diretivas tem seus próprios conjuntos de atributos.

4.4.1.Diretiva Page

A diretiva page define o processamento de informação da página. Permite importar classes, preparar super-classes Servlets e coisas assim.

As diretivas possuem atributos opcionais que provê ao mecanismo JSP uma forma especial de processar a informação como se segue; Nota: as letras maiúsculas e minúsculas são importantes nos nomes dos atributos:

Diretiva Descrição Exemplo de uso

extends Super-classe usada pelo mecanismo JSP para traduzir o servlet. Analogamente às extensões das palavras-chave da linguagem de programação em Java.

<%@ page extends = "com.taglib…"%>

language Indica qual a linguagem foi usada nos scriptlets, expressões e declarações encontradas nas páginas JSP. O único valor definido para este atributo é java.

<%@ page language="java"%>

import Importa pacotes de classes java para a página JSP corrente.

<%@ page import="java.util.*"%>

session Indica se a página faz uso de sessões. Por padrão toda JSP possui dados da session disponíveis. Alterando session para false implica em benefícios de performance.

O valor padrão é true.(verdadeiro)

buffer Controla o uso dos buffers de saída da página JSP. Se o valor for “none” então não existirão buffers e a saída será escrita diretamente no PrintWriter apropriado. Por padrão o valor é de 8kb.

<%@ page buffer="none"%>

Programação WEB 15

Page 74: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

Diretiva Descrição Exemplo de uso

autoFlush Quando atribuído true esvazia o buffer de saída logo quando ele ficar cheio.

<%@ page autoFlush="true"%>

isThreadSafe Indica se as transações de Servlet podem receber múltiplos pedidos. Se true, a nova thread será iniciada capturando requisições simultâneas, por padrão o valor é true.

info Desenvolvedores usam este atributo para adicionar informações e documento para a página. Tipicamente é usado para informar o nome autor do código, versão, copyright e datas.

<%@ page info="jedi.org test page, alpha version "%>

errorPage Define qual a pagina que será usada para tratar os erros, deve ser um endereço URL para uma página de erro.

<%@ page errorPage="/errors.jsp"%>

IsErrorPage Se for atribuído true transforma esta página JSP em uma página de erro. Esta página tem acesso ao objeto implícito exception.

A sintaxe XML para definir as diretivas é:<jsp:directive.directiveType attribute=value />

por exemplo o equivalente XML de:<%@ page import="java.util.*" %>

é <jsp:directive.page import="java.util.*" />

4.4.1.Diretiva include

A diretiva include define qual arquivo será incluído na página. Isso permite inserir um aquivo na classe servlet (inclui o conteúdo do arquivo dentro de outro arquivo) durante a tradução. Tipicamente a inclusão de arquivos é usada pelos componentes comuns a várias páginas como navegação, tabelas, cabeçalhos rodapés e etc.

A diretiva Include possui a seguinte sintaxe: <%@ include file="relativeURL"%>

Por exemplo, para incluir uma barra de menu encontrada no diretório corrente, a diretiva será escrita da seguinte forma:

<%@ include file="menubar.jsp"%>

4.4.1.Diretiva taglib

A biblioteca de tag é uma coleção de tags predefinidas. A diretiva taglib define a biblioteca de tags usada nesta página. As taglibs são escritas da seguinte forma:

<%@ taglib uri="tag library URI" prefix="tag Prefix"%>

Esta diretiva informa ao contêiner que será considerado um código predefinido como remarcação e como esse código de remarcação irá se apresentar.

Vejamos como exemplo o arquivo index.jsp na listagem seguinte. A primeira linha declara que o arquivo index.jsp usa o código predefinido "struts/template", identificando como "template" para simplificar a digitação. As linhas seguintes referenciam a taglib antepondo a remarcação corretamente.

Programação WEB 16

Page 75: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

index.jsp <%@ taglib uri="struts/template" prefix="template"%><template:insert template="/WEB-INF/view/template.jsp"> <template:put name="header" content="/WEB-INF/view/header.jsp"/> <template:put name="content" content="/WEB-INF/view/index_content.jsp"/> <template:put name="footer" content="/WEB-INF/view/footer.jsp"/> </template:insert>

Tags predefinidas foram introduzias na JSP 1.1 e permitem aos desenvolvedores JSP esconder dos web designers aspectos complexos do servidor.

4.5. JavaBeans em JSP

O uso dos JavaBeans em JSP não é definido pela especificação JSP. Entretanto, provêem funcionalidades interessantes. O uso de JavaBeans tem reduzido bastante a quantidade de elementos encontrados nas páginas JSP.

Primeiramente, JavaBeans são simplesmente classes Java que seguem um determinado padrão de codificação:

● por padrão, provê um construtor sem argumentos

● possui exclusivamente métodos gets e sets

Um exemplo de JavaBean é demonstrado a seguir:package jedi.beans; public class User { private String name; private String address; private String contactNo; public User() { } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public String getContactNo() { return contactNo; } public void setContactNo(String contactNo) { this.contactNo = contactNo; } }

4.5.1.Como são usados os JavaBeans em JSP?

Como um objeto de transferência de dados – JavaBeans são amplamente utilizados em páginas JSP como objeto de transferência de dados. Em muitas aplicações os processos são executados em uma Servlet, não em um JSP. Só os resultados do transcurso são passados para o JSP na forma de um ou mais JavaBeans.

Como um objeto auxiliar - Em algumas aplicações menores não é eficiente ter um servlet separado para processar os dados. Neste caso, é melhor colocar a funcionalidade dentro de um JavaBean.

Programação WEB 17

Page 76: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

4.5.2.JavaBeans relacionados com ações de JSP

JSP define ações padrão que simplificam o uso de JavaBeans. <jsp:useBean>

Para usar um componente JavaBean, deve-se habilitar seu uso por instanciação, o qual pode feito por intermédio das ações abaixo:

Os atributos das ações <jsp:useBean> são as seguintes:

● id – especifica o nome do bean e como ele será referenciado na página.

● scope – especifica a região na qual se pode armazenar uma instância do JavaBean. Pode ser anexado à página (page), à sessão (session), à requisição (request), ou à aplicação (application).

● class – especifica o nome da classe Java na qual o JavaBean é criado. Se foi especificado o nome do JavaBean não será preciso especificar a classe.

● beanName – Especifica o nome do bean que está armazenado no servidor. Refira-se a este atributo como a uma classe (por exemplo, com.projectalpha.PowerBean). Se especificado o atributo class, não é necessário especificar este atributo.

● type - especifica o tipo de variável de scripting devolvido pelo bean. O tipo tem que se relacionar à classe do bean.

A seguir, um exemplo de como usar um JavaBean em uma pagina JSP: <jsp:useBean id="user" scope="session" class="jedi.bean.User"/>

Quando a página encontra uma ação useBean, primeiro tentará verificar se já existe uma instância do JavaBean deste tipo determinado com a determinada extensão.

Caso não exista, o contêiner automaticamente cria uma nova instância do JavaBean usando o construtor padrão sem argumentos. Colocando o JavaBean no escopo determinado.

Caso a funcionalidade anterior seja expressada como um scriptlet, se pareceria com:<% jedi.bean.User user = (jedi.bean.User)session.getAttribute("user"); if (user == null) { user = new User(); session.setAttribute("user", user); } %>

Uma vez que o JavaBean tenha sido habilitado usando a ação jsp:useBean, pode ser usado dentro da pagina JSP como qualquer instância de objeto, apenas usando o nome especificado no atributo id. Vejamos o seguinte exemplo:

<jsp:useBean id="user" scope="session" class="jedi.bean.User"/><HTML> <BODY> Hello <%= user.getName() %> !! </BODY></HTML>

<jsp:getProperty>

Esta ação retorna o valor de uma propriedade específica dentro de um JavaBean e retorna imediatamente ao fluxo de resposta.

Esta ação possui dois atributos:

● name – Nome do JavaBean cuja propriedade será retornada. Ela deve possuir o mesmo valor do atributo id usado na ação anterior <jsp:useBean>

● property – Nome da propriedade cujo valor será retornado.

Programação WEB 18

Page 77: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

Se o desenvolvedor desejar recuperar o valor de uma propriedade de JavaBean sem colocar isto imediatamente no fluxo saída, não se terá outra escolha a não ser fazer uso de scriptlet ou expressão (a ser discutido em capítulos posteriores).

Seguindo os exemplos anteriores, para exibir a propriedade name através do JavaBean, faz-se uso da ação getProperty da seguinte forma:

<jsp:getProperty name="user" property="name"/>

<jsp:setProperty>

Esta ação permite aos desenvolvedores atribuir valores a propriedades do JavaBean sem que seja necessário escrever uma linha de código no scriptlet.

Esta ação possui os mesmos atributos da ação getProperty e dois adicionais:

● value – Valor que deve ser atribuído à propriedade. Pode ser um valor estático ou uma expressão avaliada durante a execução.

● param – Especifica o parâmetro de requisição pelo qual a propriedade irá retornar.

O desenvolvedor que faz uso desta ação da seguinte forma: deve especificar o valor, ou o atributo de param, mas, ao incluir ambos atributos na ação, irá causar uma exceção.

4.6. Capturando erros

Páginas JSP podem fazer uso da diretiva de página para especificar uma página que controlará qualquer exceção que não possa ser capturada. O atributo errorPage da diretiva de página pode ser passado por uma URL relativa à página de JSP, identificada como uma página de erro. A página assim designada pode fixar o atributo isErrorPage para processar o erro. Deste modo, será realizado acesso a um objeto implícito chamado exception – que contém detalhes sobre a exceção lançada.

Um exemplo disto é fornecido a seguir:<%@page errorPage="errorPage.jsp"%><HTML> <% String nullString = null; // chama um método de um objeto nulo, assim será lançada uma exception // e a página de erro será chamada. %> The length of the string is <%= nullString.length() %> </HTML>

errorPage.jsp:<%@page isErrorPage="true"%> <HTML> <TITLE>Error!</TITLE> <BODY> <H1>An Error has occurred.</H1><br/> Laçamento mas um erro ocorreu com a pagina acessada anteriormente, por favor contacte um membro do suporte e informe a mensagem: <%=exception.getMessage()%> que foi a causa do erro. </BODY> </HTML>

A primeira página usa a diretiva page que descreve uma pagina de erro que será usada quando uma exceção não puder ser capturada.

Na página de erro indicada, à diretiva de página isErrorPage é atribuída com o valor true. A página pode usar um objeto exception para mostrar a mensagem de erro gerada pela página chamada.

Programação WEB 19

Page 78: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

5. Exercícios1) Considere a seguinte classe:public class Recipe { private String recipeName; private String[] ingredients; private int cookTime; // métodos getter and setter aqui. }

Temos um cenário onde uma instância desta classe que foi armazenada em um escopo de requisição por intermédio da chave “currentRecipe". Crie uma pagina JSP que faça o seguinte:

a) Retorne uma instância de Recipe usando as ações JSP.

b) Mostre detalhes contidos na instância. Isso inclui mostrar cada elemento do array ingredients. detalhes devem ser mostrados usando ações JSP.

2) Usando a mesma classe do exercício anterior crie outra página que mostre os mesmos detalhes. Desta vez, faça uso de objetos implícitos para retornar a instância do objeto Recipe e use expressões para mostrar os detalhes.

3) Usando a mesma classe de definição do objeto Recipe realize as seguintes tarefas:

a) Crie uma pagina JSP que irá criar instâncias usando ações JSP.

b) Atribua à propriedade recipeName o valor "Monte Carlo Sandwich" e a propriedade cookTime o valor 0 (zero). atribua estas propriedades usando as melhores ações JSP.

c) Mostre os valores de instância deste objeto.

4) Uma página JSP chamada otherContent.jsp está localizada na raiz. É definida da seguinte forma:

<TABLE cellspacing="5"> <tr> <td colspan="2"> This is very important content </td> </tr><tr> <td colspan="2"> Inclusion successful! </td> </tr></TABLE>

Crie uma página JSP que irá incluir o conteúdo definido em otherContent.jsp.

Programação WEB 20

Page 79: Projeto JEDI - Programação para WEB - Java - 178 páginas

Módulo 6Programação WEB

Lição 5Conexão com Banco de Dados: SQL e JDBC

Versão 1.0 - Nov/2007

Page 80: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

1. ObjetivosA maioria das aplicações WEB utiliza algum tipo de armazenamento externo para gerar seu conteúdo dinamicamente. Esse meio de de armazenamento externo, na maioria das vezes, é encontrado na forma de um banco de dados relacional, devido a sua simplicidade e facilidade para se extrair dados.

Essa extração de dados de um banco relacional é realizado com o uso do SQL – Structure Query Language (Linguagem de Pesquisa Estruturada). Esta linguagem define uma sintaxe e várias palavras chaves que podem ser entendidas por sistemas de bancos de dados. A grande maioria dos bancos relacionais provê um programa cliente que permite a entrada de comandos em SQL em que o resultado é exibido na tela. Entretanto, aplicações WEB não podem utilizar esta interface em seus programas.

A API JDBC, que faz parte da plataforma J2EE, provê aos desenvolvedores um padrão, uma maneira programática de relacionamento com os bancos de dados relacionais. Fazendo uso desta API, os desenvolvedores podem executar consultas SQL e fazer uso de seus resultados para gerar conteúdo dinâmico para seu cliente.

Ao final desta lição, o estudante será capaz de:

• Construir um objeto da classe Connection utilizando a classe DriverManager ou DataSource

• Construir um objeto da classe Statement usando o método createStatement() disponível na classe Connection

• Executar consultas SQL usando um objeto da classe Statement e recuperar os resultados

• Encerrar os objetos do banco de dados

Programação WEB 4

Page 81: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

2. Bancos de dados Relacionais Bancos de dados relacionais são a escolha de armazenamento da maioria das aplicações baseadas na WEB que necessitam de um dinamismo sem seu conteúdo. A sintaxe básica necessária para recuperar e manipular dados que se encontram armazenados é de fácil aprendizado. Paralelamente, existe uma vasta indústria de suporte, várias opções de personalização disponíveis, que necessitam de pouco ou nenhum recurso técnico que podem ser encontrados na Internet.

Assim como o nome já diz, um banco de dados relacional armazena dados como uma série de informações relacionadas. Grupos relacionados são expressos na forma de tabelas. Cada tabela contém colunas que definem as propriedades de cada grupo de dados armazenados.

Este conceito pode ser visualizado pelo exemplo a seguir:

id nome endereco NumContato

14627895 Duke California 0924562788

65248987 Kimi Finland 8687243217Figura 1: Exemplo da estrutura de uma tabela

Neste exemplo, vemos uma tabela que é utilizada para armazenar dados do usuário. A tabela define 4 colunas: uma coluna id que armazena uma identificação que é única para cada usuário, uma coluna de nome, endereço, e uma outra para o número de contato. Cada linha da tabela representa uma única informação. Isto significa que existe um usuário chamado Duke, ao qual o endereço é na Califórnia, que possui o id 14627895 e o número de contato 0924562788.

As tabelas utilizadas num sistema de bancos de dados não são tão simples quanto a apresentada acima. As tabelas definidas num banco de dados são geralmente montadas com constraints lógicas que servem para preservar a consistência dos dados. Uma constraint é uma restrição de um tipo de dado: cada coluna é definida para ser de um tipo de dados específico. O sistema automaticamente rejeita a inserção de novos dados que não sejam compatíveis com o tipo de dado definido pela estrutura da tabela. Por exemplo, a coluna id pode ser definida internamente para ser do tipo integer. Tentar inserir novas linhas que contenham caracteres em seus valor para o campo id causará uma falha na inserção. Outra constraint geralmente imposta numa coluna é a unicidade: se uma coluna é definida como sendo “única” o sistema não aceitará a inserção de um novo dado que contenha um valor já existente no sistema.

As mecânicas de definição de uma tabela estão além do escopo desta discussão e serão deixados para posterior compreensão. Esta discussão será focada no acesso a dados e modificação numa dada tabela.

Programação WEB 5

Page 82: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

3. Sentenças SQL Como já foi mencionado anteriormente, operações em bancos de dados relacionais são realizadas por meio de SQL. Existem vários tipos de sentenças SQL. Apesar disto, duas serão abordadas nesta lição.

3.1. Recuperação de Dados

Este tipo de sentença é focada na leitura de dados de uma ou mais tabelas num banco de dados. Consultas deste tipo podem ser configuradas para que retornem TODOS os dados relacionados em uma tabela específica (ou grupo de tabelas), ou podem ser parametrizadas para que somente algumas linhas ou colunas sejam recuperadas por meio de valores informados.

Somente um comando SQL se encaixa neste tipo: o comando SELECT.

3.1.1.Comando SELECT

O comando SELECT é utilizado para consultas ao banco de dados sobre informações que serão retornadas em conjuntos de linhas.

A forma básica de um comando SELECT é:SELECT column(s) FROM tablename WHERE condition(s)

Na sintaxe acima, SELECT, FROM e WHERE são palavras chaves SQL, enquanto que column, tablename e condition são valores especificados pelo desenvolvedor.

• SELECT – marca o início do comando SELECT

• column (coluna) – o nome das colunas da qual os valores serão retornados. Se todas as colunas devem ser recuperadas, um asterisco(*) pode se utilizado no lugar do nome das colunas.

• Recuperando múltiplas colunas -> SELECT id, nome, endereco FROM ...

• Recuperando todas as colunas -> SELECT * FROM ...

• FROM – indica o nome da tabela de onde os dados serão recuperados. Um mecanismo para a recuperação de dados de múltiplas tabelas também está incluído na linguagem SQL, e será discutido em detalhes mais a frente.

• WHERE – é opcional e especifica condições que os registro deverão atender antes de serem incluídos no resultado. Mais de uma condição pode ser especificada; neste caso, as condições são separadas por uma palavra chave AND (exclusiva) ou OR (inclusiva) que executam as mesmas funções de seus equivalentes lógicos. Outros tipos de condições são permitidas e serão discutidas futuramente.

3.1.2.A cláusula FROM

A cláusula FROM numa sentença SELECT define a(s) tabela(s) de onde os dados serão reunidos. Se os dados vierem somente de uma única tabela, então o nome daquela tabela é simplesmente informado. Entretanto, se o dado vier de mais de uma tabela, uma operação conhecida como table join (junção de tabelas) deve ser executada.

Table joins podem ser executadas de inúmeros modos:

User UserDownloads

userid name address contactnum14627895 Duke San Francisco 0924562788 65248987 Kimi Finland 8687243217 84321874 Dante San Jose 6365498428

userid downloaditem downloaddate14627895 Courseware Notes Dec. 19, 200536542036 Exercises Feb. 11, 200684321874 Slides March 13, 2006

● listando todas as tabelas que serão unidas. No comando SQL poderiam ser separadas por

Programação WEB 6

Page 83: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

vírgulas. Apesar de ser mais simples, este método é também mais lento em termos de performance. O resultado obtido é um produto cartesiano das tabelas.

Exemplo: Dadas duas tabelas, User e UserDownloads, a união é executada por: SELECT * FROM user, userdownload [WHERE ...]

Se a vírgula for utilizada como delimitador, o resultado será como o mostrado a seguir:

userid name address contactnum userid downloaditem downloaddate14627895 Duke San Francisco 0924562788 14627895 Courseware Notes Dec. 19, 2005

14627895 Duke San Francisco 0924562788 36542036 Exercises Feb. 11, 2006

14627895 Duke San Francisco 0924562788 84321874 Slides March 13, 2006

65248987 Kimi Finland 8687243217 14627895 Courseware Notes Dec. 19, 2005

65248987 Kimi Finland 8687243217 36542036 Exercises Feb. 11, 2006

65248987 Kimi Finland 8687243217 84321874 Slides March 13, 2006

84321874 Dante San Jose 6365498428 14627895 Courseware Notes Dec. 19, 2005

84321874 Dante San Jose 6365498428 36542036 Exercises Feb. 11, 2006

84321874 Dante San Jose 6365498428 84321874 Slides March 13, 2006

3.1.3.União de Tabelas

● JOIN

Fazendo uso de um dos muitos JOINs. A sintaxe é: table1 JOIN table2 WHERE condition.

Condição especifica quais linhas de ambas as tabelas serão unidas para que o resultado possa ser gerado.

● LEFT JOIN

A performance é similar ao JOIN, exceto que todas as linhas de table1 serão retornadas a união, mesmo que não exista uma linha correspondente em table2 que atenda a condição. Utilizando o LEFT JOIN nestas tabelas, com a condição User.userid = UserDownloads.userid:

userid name address contactnum userid downloaditem downloaddate14627895 Duke San Francisco 0924562788 14627895 Courseware Notes Dec. 19, 2005

65248987 Kimi Finland 8687243217

84321874 Dante San Jose 6365498428 84321874 Slides March 13, 2006

● RIGHT JOIN

A performance é similar ao JOIN, exceto que todas as linhas de table2 serão retornadas a união, mesmo que não exista uma linha correspondente em table1 que atenda a condição. Utilizando o RIGHT JOIN nestas tabelas, com a mesma condição:

userid name address contactnum userid downloaditem downloaddate14627895 Duke San Francisco 0924562788 14627895 Courseware Notes Dec. 19, 2005

36542036 Exercises Feb. 11, 2006

84321874 Dante San Jose 6365498428 84321874 Slides March 13, 2006

● INNER JOIN

Somente as linhas de table1 e table2 que atendam à condição serão consideradas no resultado. Utilizando o INNER JOIN nestas tabelas, com a mesma condição:

userid name address contactnum userid downloaditem downloaddate14627895 Duke San Francisco 0924562788 14627895 Courseware Notes Dec. 19, 2005

84321874 Dante San Jose 6365498428 84321874 Slides March 13, 2006

Programação WEB 7

Page 84: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

Na maioria dos casos, a utilização de um INNER JOIN trará os resultados mais relevantes para as operações da junção de tabelas. Entretanto, em casos onde as linhas de uma determinada tabela devem aparecer no resultado não importando as condições da pesquisa, um LEFT JOIN ou RIGHT JOIN é considerado mais apropriado. Evite utilizar a vírgula como delimitador sempre que possível. Apesar da escrita ser mais fácil, a performance será muito baixa, valendo a pena gastar um pouco mais de tempo e utilizar o JOIN correta.

3.1.4.A cláusula WHERE

A cláusula WHERE numa sentença SELECT especifica uma condição que deve ser obedecida pelos registros numa tabela qualquer para que apareçam no resultado. Existem muitos operadores que podem ser utilizados para especificar uma condição:

• = - Verifica a igualdade entre dois operandos dados.

• <=, < - Verifica se o primeiro operando é menor ou igual, ou menor que o segundo operando.

• >=, > - Verifica se o primeiro operando é maior ou igual, ou maior que o segundo operando.

• like - Executa uma comparação entre os caracteres dos operandos. Utilizando este operador, dois caracteres podem ser utilizados para representar um valor desconhecido.

• % - Aplica-se a Strings de qualquer tamanho. Ex: 'A%s' encontrará todas as ocorrências de palavras que se iniciem por A e terminem com s.

• _ - Aplica-se a qualquer caractere. Ex: 'b_t' irá encontrar todas as ocorrências de palavras que se iniciem com 'b' e terminem com 't', mas que possuam somente um caractere entre eles.

A seguir, são dados alguns exemplos simples do uso da sentença SELECT.

● Recuperar todas as linhas e colunas da tabela de users.SELECT * from users;

● Pesquisar pelo endereço dos usuários que tenham o nome de Smith.SELECT address from users where name = 'Smith';

● Pesquisar todas as linhas da tabela users com o nome iniciado por 'S'.

SELECT * from users where name like 'S%';

A linguagem SQL não é case-sensitive em relação as suas palavras-chave (diferente de Java). Entretanto, é case-sensitive em relação aos parâmetros de comparação. A seguinte sentença trará uma coleção de registros diferente da apresentada anteriormente:

SELECT * from users where name ='sMith';

3.2. Manipulação de Dados

As sentenças que se encaixam neste tipo são utilizadas para modificar o estado dos dados no banco de dados. Existem muitas sentenças, cada uma sendo utilizada para uma manipulação especifica.

3.2.1.Comando INSERT

O comando INSERT é utilizado para inserir novos registros em uma determinada tabela num banco de dados.

INSERT INTO table-name VALUES (value1, value2, ...)

Acima é possível observar a estrutura básica do comando INSERT, onde table-name é o nome da tabela que irá armazenar os novos dados inseridos. Os valores após a palavra-chave VALUES é delimitado por vírgulas e serão adicionados à tabela. Em casos como este em que uma só tabela é

Programação WEB 8

Page 85: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

referenciada, a associação é feita com os campos passados como parâmetro baseados na ordenação das colunas da tabela.

Por exemplo, numa tabela chamada users, com as colunas userid, name, address, nesta ordem, o comando a seguir adicionará uma nova linha na tabela:

INSERT INTO users VALUES(199700651, 'Jedi Master', 'UP Ayala Technopark');

É muito importante notar que toda sentença que faz uso do comando INSERT, deve seguir todas as regras de integridade definidas pela tabela. Isto é, se um campo numa tabela é definido para ser not null, qualquer tentativa de inserir um valor nulo neste campo causará num erro.

3.2.2.Comando UPDATE

O comando UPDATE realiza uma modificação de registros em uma determinada tabela.UPDATE table-name SET column-value(s) WHERE condition(s)

Acima temos a forma básica do comando UPDATE, onde table-name é o nome da tabela que contém as linhas as serem modificadas e column-values é uma lista com os novos valores das colunas na tabela. Opcionalmente, uma lista de condições pode ser adicionada para especificar quais linhas da tabela serão modificadas. Se nenhuma condição for passada, o comando UPDATE será efetuado para todas as linhas existentes numa dada tabela.

Todo comando UPDATE deve seguir as regras de integridade impostas pelo banco de dados. Por exemplo, alterar o valor de um campo marcado como not null para um valor nulo resultará na não execução da sentença e numa mensagem de erro vinda do banco de dados relacional.

3.2.3.Comando DELETE

O comando DELETE elimina registros de uma determinada tabela. Observe que é oposto ao comando INSERT que adiciona novos registro a tabela.

DELETE FROM table-name WHERE condition(s)

Acima é apresentada a forma básica do comando DELETE, sendo table-name o nome da tabela que contém os dados que se deseja eliminar. Uma lista de condições pode ser opcionalmente especificada. Se nenhuma condição for dada, o comando irá excluir todos os registros da tabela mencionada.

Programação WEB 9

Page 86: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

4. JDBCJava fornece uma biblioteca padrão para que o acesso a bancos de dados seja efetuado, a chamada Java Database Connectivity ou, simplesmente, JDBC. Por meio desta os desenvolvedores podem acessar bases de dados não importando quem seja seu fabricante; os desenvolvedores de um JDBC provêem a implementação para as interfaces definidas nesta API, fornecendo o mesmo grupo de funcionalidades ao desenvolvedor do sistema.

As seguintes classes estão na API JDBC:

• java.sql.Connection – Representa a conexão com o banco de dados. Encapsula os detalhes de como a comunicação com o servidor é realizada.

• java.sql.DriverManager – Gerencia os drivers JDBC utilizados pela aplicação. Em conjunto com o endereço e a autenticação, pode fornecer objetos de conexão.

• javax.sql.DataSource – Abrange os detalhes (endereço, autenticação) de como a conexão com o banco de dados é obtida. É o mais recente e o preferido método de obtenção de objetos de conexão.

• java.sql.Statement – Fornece meios ao desenvolvedor para que se possa executar comandos SQL.

• java.sql.ResultSet – Representa o resultado de um comando SQL. Estes objetos normalmente são retornados por métodos.

4.1. java.sql.DriverManager

Utilizando esta classe, o desenvolvedor pode retornar um objeto de conexão que pode ser usado para executar tarefas relativas ao banco de dados. Dois passos são necessários para tal:

● Primeiro, o driver JDBC deve estar registrado com DriverManager. Isto pode ser feito utilizando o método Class.forName que carrega a classe do driver para a memória.

● Segundo, utilizando o método getConnection, mediante informação de uma URL, assim como a senha e o nome do usuários autenticados no banco de dados. A URL deve seguir a sintaxe requisitada pela implementação do banco de dados.

Abaixo vemos um exemplo de como se obtém uma conexão com um banco de dados PostgreSQL. Novamente, a URL e o driver específicos para a implementação são utilizados. Para outros bancos de dados, verifique a documentação fornecida.

String jdbcURL = "jdbc:postgresql://localhost:5432/jedi-db"; String user = "jedi"; String password = "j3d1master"; Connection conn = null; try { Class.forName("org.postgresql.Driver"); conn = DriverManager.getConnection(url, user, password); ... } catch (SQLException e) { // caso ocorra erro de SQL }

Apesar de ser um método válido para a obtenção de um objeto de conexão, necessita que o desenvolvedor faça o rastreamento de alguns detalhes, tais como: o nome da classe do driver, a url necessária para se efetuar a conexão, o nome de usuário e senhas para o acesso ao banco. Estes detalhes são os que mais estão sujeitos a mudanças a cada compilação da aplicação. Além disso, gerenciar a url e o nome do driver no código dificulta a aplicação a trocar a implementação de sua base de dados, se isto vier a ser necessário.

Programação WEB 10

Page 87: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

4.2. javax.sql.DataSource

Esta é uma interface definida na API JDBC desde sua versão 2. Hoje em dia, este é o modo recomendado para que se obtenham objetos de conexão. Recuperar objetos de conexão é uma tarefa bastante direta: simplesmente chame o método getConnection() de uma instância válida desta interface.

Sendo DataSource uma interface, uma instância não pode ser simplesmente criada pelo desenvolvedor utilizando o operador new. É recomendado que se deixe para o servidor da aplicação que gerencie a criação de objetos DataSource. Com isto permite-se que o servidor adicione algumas funcionalidades, tais como a connection pooling (reserva de conexões) de uma maneira que é transparente tanto para o desenvolvedor, quanto para o usuário final.

4.2.1.Configurando o Datasource no Sun Application Server

Cada servidor possui seu próprio procedimento para a configuração e gerenciamento de DataSources. São três passos para se configurar um DataSource no AppServer:

● Registrar o arquivo JAR contendo o driver JDBC

● Criar o pool de conexões para o banco de dados

● Registrar um DataSource que utilize este pool

4.2.2.Registrando o arquivo JAR

O primeiro passo é acessar o console de administração do servidor e disponibilizar o arquivo de conexão com o banco de dados para o Sun Application Server. Para isso, basta copiar o arquivo .jar para a pasta \lib aonde está instalado o servidor. Por exemplo, com o servidor Glassfish instalado em conjunto com o NetBeans 6.0 pode ser encontrado no Windows na pasta:

C:\Program Files\glassfish-v2\

4.2.3.Criando um pool de conexão

Para iniciar a criação de um pool de conexão, selecione Resources | JDBC | Connection Pools no painel a esquerda. Na tela exibida, pressione o botão New e uma tela similar a Figura 2 será mostrada.

Em Name entre com o nome como este pool de conexão será conhecido (exemplo: jediPool). Em Resource Type selecione javax.sql.DataSource. No Database Vendor selecione o banco de dados utilizado, caso o banco que esteja utilizando não apareça na lista deixe esta opção em branco.

Pressione o botão Next, no campo Datasource Classname, caso não venha preenchido por padrão, informe o nome da classe do seu banco, para o PostGreSQL:

org.postgresql.jdbc3.Jdbc3PoolingDataSource

Observe as propriedades para associar com este pool de conexões. Os parâmetros seguintes necessitam ter seus valores fornecidos:

• Password – Senha do banco de dados

• ServerName – Nome do servidor do banco de dados

• PortNumber – Número da porta

• DatabaseName – Nome do Banco de dados

• User – Nome do usuário

Depois de completar os valores, pressione o botão Finish.

Programação WEB 11

Page 88: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

Figura 2: Criando um Connection Pool

Selecione Resources | JDBC | Connection Pools | jediPool e pressione o botão ping a seguinte mensagem deve ser mostrada indicando que o pool de conexão foi criado com sucesso:

Figura 3: Conexão criada com sucesso

4.2.4.Registrando o Datasource

Para registrar um datasource, selecione Resources | JDBC | JDBC Resources no lado esquerdo do painel. Na tela que aparece, pressione o botão New.

Os campos devem ser preenchidos da seguinte maneira:

• Nome do JNDI – entre como nome lógico da sua aplicação recuperará o DataSource. É recomendado que este nome tenha JDBC como seu prefixo para facilitar para futuros administradores do servidor para identificar este elemento de fonte JDBC (ex. jdbc/jediPool)

• Nome do Pool – selecione o nome do pool de conexões criada anteriormente

• Descrição – entre com o texto descrevendo o DataSource (opcional)

Pressione o botão OK para finalizar.

Recuperando uma instância de um DataSource de uma aplicação servidora é simples e pode ser realizada usando unicamente algumas linhas de código da API JNDI. Java Naming Directory Interface (JNDI) é uma API Java padrão para acessar diretórios. Um diretório é um local centralizado onde a aplicação Java pode recuperar recursos externos usando um nome lógico.

Detalhes adicionais para JNDI e como este trabalho estão além do escopo desta lição. A única coisa que necessitamos conhecer é que o servidor de aplicação mantém um diretório para que seja publicado o DataSource que foi configurado anteriormente. Nossa própria aplicação pode executar um simples lookup de nome neste diretório para recuperar o recurso.

Para nossas finalidades, é bastante criarmos um contexto JNDI usando o construtor padrão. Este contexto JNDI abstrai os detalhes da conexão para o diretório, fazendo lookup do recurso numa simples chamada num método singular. Tome nota que o nome usado para o lookup na fonte deve ter o mesmo nome usado na configuração no DataSource.

Programação WEB 12

Page 89: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

... Context ctxt = null; DataSource ds = null; try { // criar uma instância de um contexto JNDI ctxt = new InitialContext(); // retornar o DataSource de um diretório com um nome lógico ds = (DataSource)ctxt.lookup("jdbc/PostgreSQLDS"); } catch (NamingException ne) { System.err("Specified DataSource cannot be found"); }

Uma vez que temos um exemplo de instância DataSource válido, pequemos um objeto Connection tão simples quanto.

Connection conn = ds.getConnection();

4.3. java.sql.Connection / java.sql.Statement

Os objetos java.sql.Connection representam conexões reais ao banco de dados. Uma vez que temos um exemplo de um objeto podemos criar uma instância do objeto Statement, para executar as declarações SQL.

O objeto do tipo Statement provê diversos métodos para executar as declarações SQL. Os métodos mais utilizados são:

• executeQuery – utilizado para a instruções de pesquisa no banco de dados (comando SELECT) e retorna o resultado da operação em um objeto do tipo ResultSet.

• executeUpdate – utilizado para as instruções de modificação do banco de dados (comandos CREATE, DROP, ALTER, INSERT, UPDATE ou DELETE) e retorna um tipo primitivo int com o número de linhas afetadas.

Abaixo está um exemplo de pedaço de um código mostrando como realizar este procedimento:Context ctxt = null; DataSource ds = null; Connection conn = null; Statement stmt = null; ResultSet rs = null; try { ctxt = new InitialContext(); ds = (DataSource)ctxt.lookup("jdbc/PostgreSQLDS"); conn = ds.getConnection(); stmt = conn.createStatement(); rs = stmt.executeQuery("SELECT * FROM users");} catch (NamingException e) { System.err.print("Cannot find named datasource"); } catch (SQLException se) { System.err.print("Error occurred while performing query"); }

4.4. java.sql.ResultSet

Um objeto ResultSet é o resultado de uma consulta em um banco de dados. Os dados dentro de um objeto ResultSet podem ser melhor visualizados com uma tabela. As informações podem ser recuperada uma coluna de cada vez, com o objeto ResultSet que mantem-se apontando para determinado registro.

Para percorrer cada linha no ResultSet, chamamos o método next(). Este método move-se a um ponto interno do objeto ResultSet para a próxima linha. Retorna true se a próxima linha for encontrada e false ao encontrar o final de arquivo (EOF).

while (rs.next()) {

Programação WEB 13

Page 90: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

// ler cada coluna}

Para recuperar os campos em cada linha, o objeto ResultSet fornece um certo de número de métodos de recuperação. Existe um método getString para recuperar dados de uma String, um método getInt para recuperar dados do tipo inteiro, getBoolean para recuperar dados tipo boolean, e assim sucessivamente. Em todos os casos esses métodos recebem um parâmetro, como o número da coluna da coluna que contém o dado ou o nome da coluna. Recomenda-se, entretanto, que nomes sejam usados para especificar a coluna para ser lida em vez do número de linhas. Isto faz com que a aplicação seja mais fácil de dar manutenção, pois é possível que a ordem da coluna seja alterada ao longo do tempo após o início do desenvolvimento.

Context ctxt = null; DataSource ds = null; Connection conn = null; Statement stmt = null; ResultSet rs = null; try { ctxt = new InitialContext(); ds = (DataSource)ctxt.lookup("jdbc/PostgreSQLDS"); conn = ds.getConnection(); stmt = conn.createStatement(); rs = stmt.executeQuery("SELECT * FROM users"); while (rs.next()) { String userName = rs.getString("name"); String address = rs.getString("address"); int userID = rs.getInt("userid"); // Outras operações com os dados retornados }} catch (NamingException e) { System.err("Cannot find named datasource"); } catch (SQLException se) { System.err("Error occurred while performing query"); }

4.5. Liberando recursos do sistema

Uma etapa muito importante, que é negligenciada freqüentemente, é a de liberar os recursos de banco de dados depois de uma operação ter sido completada. Isto deve ser feito explicitamente e é de responsabilidade do programador. Sem executar tal liberação, os recursos mencionados pela operação acima NÃO podem ser usados futuramente. Para aplicações de larga escala, isto pode rapidamente resultar em perda de disponibilidade de conexões.

A liberação de recursos pode ser feita pela chamada do método close() em cada um dos objetos Connection, Statement e ResultSet. Há uma ordem específica envolvida: o objeto do tipo ResultSet deve ser fechado primeiro, em seguida o objeto do tipo Statement e, finalmente o objeto do tipo Connection. Já que cada método de fechamento de cada objeto foi definido para lançar uma SQLException, deve-se envolver as instruções em um bloco try-catch.

Um erro comum que os desenvolvedores fazem é simplesmente colocar métodos de fechamento dentro do corpo do bloco try-catch. Aqui está um exemplo:

Context ctxt = null; DataSource ds = null; Connection conn = null; Statement stmt = null; ResultSet rs = null;try { ctxt = new InitialContext(); ds = (DataSource)ctxt.lookup("jdbc/PostgreSQLDS"); conn = ds.getConnection(); stmt = conn.createStatement(); rs = stmt.executeQuery("SELECT * FROM users"); while (rs.next()) { String userName = rs.getString("name");

Programação WEB 14

Page 91: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

String address = rs.getString("address"); int userID = rs.getInt("userid"); // Outras operações com os dados retornados } rs.close(); stmt.close(); conn.close();} catch (NamingException e) { System.err("Cannot find named datasource"); } catch (SQLException se) { System.err("Error occurred while performing query"); }

O problema com esta abordagem é que condiciona o sucesso um único endereço. Nos casos onde uma exceção ocorre no código os recursos de sistema NÃO serão liberados corretamente. Um caminho melhor será colocar a clausula finally, para assegurar que que os recursos sejam liberados não importando o que aconteça.

A solução para o problema é apresentada a seguir:Context ctxt = null; DataSource ds = null; Connection conn = null; Statement stmt = null; ResultSet rs = null; try { ctxt = new InitialContext(); ds = (DataSource)ctxt.lookup("jdbc/PostgreSQLDS"); conn = ds.getConnection(); stmt = conn.createStatement(); rs = stmt.executeQuery("SELECT * FROM users"); while (rs.next()) { String userName = rs.getString("name"); String address = rs.getString("address"); int userID = rs.getInt("userid"); // Outras operações com os dados retornados } } catch (NamingException e) { System.err("Cannot find named datasource"); } catch (SQLException se) { System.err("Error occurred while performing query"); } finally { try { if (rs != null) rs.close(); } catch (SQLException e) {} try { if (stmt != null) stmt.close(); } catch (SQLException e) {} try { if (conn != null) conn.close(); } catch (SQLException e) {} }

Os questionamentos sobre se o objeto é nulo são necessários apenas nos casos em que a condição de erro ocorra antes de um ou mais objetos terem sido apropriadamente instanciados. Cada método close deve também ser separado numa clausula try-catch para assegurar que um erro causado na tentativa de fechamento de um objeto não atrapalhe o fechamento dos outros objetos.

Programação WEB 15

Page 92: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

5. Exercício

5.1. Usuários

Considere a seguinte tabela:

USERSuserid gender firstname lastname login password

14627895 M Jose Saraza jsaraza Asdfrewq167

65248987 M Rosario Antonio rantonio qwer4679

52317568 F Milagros Paguntalan mpaguntalan ukelllll3

72324489 M Frank Masipiquena fmasipiquena Df23efzsxf2341

1. Executar as instruções SQL necessárias para:

a) Recuperar todos os usuários masculinos.b) Recuperar todos os usuários com o primeiro nome iniciando com a letra F.c) Alterar o nome de login do registro com um userid de 65248987 para rtonio.d) Eliminar todos os registros femininos.e) Inserir o seguinte registro na tabela:

userid gender firstname lastname login password

69257824 F Anne Sacramento asacramento k1lasdoj24f

2. Criar uma classe LoginHandler. Deverá conter a seguinte assinatura:public boolean isUserAuthorized(String loginName, String password)

Dentro do corpo do método, crie e implemente uma conexão para o banco de dados exemplo, e verifique novamente a tabela usuário se existe um registro que tenha o mesmo login e password que são dados nos parâmetros. Use a classe DriverManager para obter a conexão com o banco de dados.

3. Criar um servlet de nome UserEntryServlet que forneça o seguinte formulário:<HTML> <BODY> <table> <form action="UserEntryServlet" method="post"> <tr> <td>User ID:</td> <td><input type="text" name="userID"/></td> </tr><tr> <td>First name</td> <td><input type="text" name="firstName"/></td> </tr><tr> <td>Last name</td> <td><input type="text" name="lastName"/></td> </tr><tr> <td>Login name</td> <td><input type="text" name="loginName"/></td> </tr><tr> <td>Password</td> <td><input type="text" name="userID"/></td> </tr> </table> </form> </BODY></HTML>

Programação WEB 16

Page 93: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

Usar os valores informados no formulário e criar um novo registro na tabela de usuários no banco de dados exemplo. Para conectar à base de dados, configure o Application Server para a fonte de dados.

4. Criar um servlet dando o nome de UserRemovalServlet esperando um parâmetro "userID". Eliminar o registro que corresponda a esse registro.

Programação WEB 17

Page 94: Projeto JEDI - Programação para WEB - Java - 178 páginas

Módulo 6Programação WEB

Lição 6Páginas JSP Avançadas

Versão 1.0 - Nov/2007

Page 95: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

1. ObjetivosEm lições anteriores, aprendemos sobre a sintaxe básica das páginas JSP: como utilizar scriptlets, expressões, Javabeans e tags JSP pré-definidas para fornecer dados dinâmicos junto a elementos estáticos. Também estudamos como utilizar diretrizes para manipulação de páginas através de instruções do servidor. Vimos ainda que as páginas JSP servem como esboço, ou seja, papel preliminar dentro de uma aplicação WEB – atuam como camada de apresentação.

Outro ponto abordado em lições anteriores foram páginas JSP que produzem conteúdo dinâmico enquanto nos preocupamos apenas com a programação de elementos. Esta lição dirige-se aos responsáveis pela camada de apresentação que, não necessariamente, precisam ter experiência em Java ou em qualquer outra linguagem. A utilização de formato baseado em texto reduziu muito as linhas de código dos designers. O conteúdo das páginas JSP tornou-se muito semelhante ao padrão HTML. A lógica é processada fora das páginas JSP em classes Servlets que retornam os resultados dos JavaBeans reduzindo as linhas de código. Até agora a utilização de código Java (com scriptlets) dentro das páginas JSP não era indicada. Dessa forma, algumas funcionalidades não poderiam ser executadas pelos elementos de páginas JSP, como a iteração de uma lista e a ramificação da lógica (instruções if-else).

Nesta lição, discutiremos dois elementos de páginas JSP que reduzirão a necessidade incluir scriptlets e expressões em páginas JSP. Apresentaremos também um método alternativo de executar a funcionalidades que havíamos delegado inicialmente aos scriptlets e às expressões em um formato que é mais simples e mais acessível para aqueles com pouco conhecimento de programação.

Ao final desta lição, o estudante será capaz de:

• Compreender a linguagem de expressão das páginas JSP

• Utilizar a biblioteca JSTL

Programação WEB 4

Page 96: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

2. Linguagem de expressão das páginas JSPO primeiro dos dois elementos de JSP que exploraremos nesta lição é a linguagem de expressão de JSP (EL). Esta linguagem de expressão foi introduzida com a especificação de JSP 2.0 e fornece uma sintaxe simples e limpa para a escrita de expressões que executam a lógica ou o acesso simples a valores.

A utilidade da linguagem de expressão e JSP será melhor compreendida no exemplo a seguir. Considerando os métodos discutidos na lição anterior e se quiséssemos recuperar a propriedade de um JavaBean, poderíamos utilizar:

<% String nome = user.getSobrenome(); %>ou

<jsp:getProperty nome="usuario" property="sobrenome"/>

Usando o primeiro método, os colaboradores são expostos às construções de programação Java: para recuperar um atributo de um JavaBean, os colaboradores têm que empregar métodos getter de um JavaBean. O colaborador necessita também estar ciente do tipo de propriedade Java. O segundo método é mais neutro: o acesso à propriedade do JavaBean é feita através de tags que são similares às tags de HTML. Entretanto, esta forma de recuperar a propriedade de um JavaBean é longa e trabalhosa. Além disso, este método apresenta sua saída diretamente ao browser do cliente; nenhuma manipulação pode ser executada no valor recuperado.

Usando EL, a propriedade JavaBean pode ser acessada com: ${usuario.sobreNome}

Na construção acima o desenvolvedor não precisa conhecer sintaxe Java, pois é curta e objetiva. A construção contém somente o atributo e a propriedade a ser recuperada e pode ser usada igualmente para a saída, indicando diretamente ao usuário o valor.

2.1. Sintaxe EL

2.1.1.Literais

Linguagem de expressões foi desenvolvida para ter a sintaxe simples. Basicamente, uma construção EL pode ser literal ou uma expressão incluída entre ${ e }.

EL suporta os seguintes tipos de literais:

• Boolean • Long • Float • String • null

Atributos EL são tratados como se fossem código Java. Por exemplo, valores lógicos podem ser true ou false (verdadeiro ou falso), Strings precisam estar entre aspas duplas (") ou aspas simples ('), e o valor null define um valor inexistente.

2.1.2.Operadores

São compostos pelos operadores básicos (+, -, *, /, %), os lógicos (&&, ||, !), e os de comparação (==, !=, <, <=, >, >=).

2.1.3.Palavras reservadas

EL também define as palavras reservadas que imitam a funcionalidade de alguns dos operadores: and (&&), eq (==), gt (>), ge (>=), or (||), not (!), ne (!=), lt (<), le (<=), div (/) e mod (%).

Outras palavras reservadas disponíveis:

• true – corresponde ao valor do atributo lógico.

Programação WEB 5

Page 97: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

• false - corresponde ao valor da atributo lógico.• instanceof – similar à palavra reservada em Java. • null – similar à palavra reservada em Java. Determina um valor nulo.• empty – pode ser usado em diversos casos. Por exemplo, quando se declara um atributo

do tipo String e determina que não terá valor, ou até mesmo um vetor ou uma instância.

Abaixo, exemplos de expressões EL.

${'JEDI'} – palavra 'JEDI' sendo atribuída a uma String${5 + 37} – retorna o valor 42 ${ (10 % 5) == 2} – retorna verdadeiro${empty ''} - retorna verdadeiro

2.2. Acessando atributos e propriedades

Apesar de avaliar expressões literais simples ser útil, EL mostra sua importância ao acessar e processar atributos e propriedades em escopos diferentes.

O acesso ao atributo é simples com EL (simplesmente referenciada pelo nome). As propriedades, os métodos, e as disposições do JavaBean podem ser acessadas usando o nome do atributo “.” notação.

Exemplo:${usuario.sobreNome}

Este acesso ao JavaBean pode ser referenciado pelo nome do usuário e recupera o valor de sua propriedade (sobreNome). Note que o escopo do JavaBean não importa. EL executa a pesquisa, verificando no escopo da página, do pedido, da sessão, e da aplicação para ver se há um JavaBean com o nome especificado. Se tentarmos acessar o nome de um JavaBean que não exista dentro de nenhum escopo, vazio será retornado.

Os métodos/propriedades são acessados da mesma maneira. Se quisermos recuperar o comprimento do sobrenome de um usuário, nós podemos primeiramente recuperar a propriedade do sobrenome chamando então o método .length, como no exemplo abaixo:

${usuario.sobreNome.length}

2.3. Objetos implícitos EL

Não obstante a pesquisa automática de nomes facilitar a codificação, especificar as variáveis de espaço explicitamente faz com que a construção seja fácil de entender para um futuro desenvolvedor. EL disponibiliza diversos objetos implícitos que representam um mapa dos objetos dentro dos diferentes escopos, como a seguir.

• pageScope • requestScope • sessionScope • applicationScope

Por exemplo, se nosso objeto 'usuario' foi localizado dentro do escopo da sessão:${sessionScope.usuario.sobreNome}

Além do exemplo acima, EL define também os seguintes objetos implícitos:

• param – representa um mapa de parâmetros da requisição com nomes e valores. Por exemplo, para invocar o parâmetro ${param.loginNome} seria equivalente a instrução request.getParameter(“loginNome”). Os nomes armazenados aqui apontam para um único valor em tipo String.

• paramValues – um mapa que conecta os argumentos dos nomes a vetores de Strings que representa os valores para o nome. Invocar ${paramValues.escolhas} é equivalente a instrução request.getParameterValues(“escolhas”).

• header – um mapa que representa os cabeçalhos disponíveis de uma determinada

Programação WEB 6

Page 98: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

requisição. Seus índices são similares àqueles recuperados chamando o método do getHeader de um objeto ServletRequest.

• headerValues - um mapa que representa os cabeçalhos disponíveis de uma determinada requisição. Seus índices são similares àqueles recuperados chamados pelo método getHeaders de um objeto ServletRequest.

• cookies – retorna os cookies disponíveis em uma requisição. Isto é similar a invocar o método dos getCookies de um objeto de HttpServletRequest.

2.4. A notação []

Com exceção da notação “.”, EL também fornece a notação “[]” em variáveis, em métodos e em acesso a vetores. Em muitas maneiras as duas notações são similares. Por exemplo, $ {usuario.sobreNome} é o mesmo que ${usuario [sobreNome]}.

Programação WEB 7

Page 99: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

3. JSTL A linguagem de expressões de JSP fornece um método simples e conveniente de acesso a propriedades de um escopo para executar de modo simples as expressões. Entretanto, não elimina o uso das scriptlets dentro de nossas páginas JSP. Por isso, apresentamos as custom tags, com a biblioteca padrão do Java.

3.1. Custom Tags

Um dos pontos fortes da especificação de JSP é que ela permite um grau de customização e de extensão com o uso de tags feitos sob medida. Existem tags especializadas dentro da especificação que executam tarefas específicas: as tags de JSP padrão <jsp:useBean>, <jsp:getProperty> e <jsp:setProperty> são exemplos. As tags fornecem uma funcionalidade reusável dentro de uma página JSP usando uma relação muito simples ao ocultar sua execução interna. Os desenvolvedores podem criar suas próprias tags para executar seu código com este tipo do benefício.

Com esta reusabilidade compacta, custom tags disponibilizadas em determinado JAR podem ser usadas em qualquer aplicação WEB. Não teríamos nenhuma vantagem se as custom tags não pudessem ser usadas por vários frameworks ou servidores JSP. Existem várias bibliotecas tags (tag libraries) disponíveis e é impossível evitar que tenham alguma sobreposição nas funcionalidades que fornecem.

Java reconhece isso e, em cooperação com a comunidade de programação, forneceu uma biblioteca padrão de tags que se dirige às áreas de sobreposição, chamada de Java Standard Tag Library ou JSTL.

3.2. Incluindo JSTL em nossa aplicação

Para incluir a biblioteca JSTL em nossa aplicação, no NetBeans acesse Properties do Projeto, em seguida selecione a opção Libraries e pressione o botão Add Library..., selecione a opção JSTL 1.1, pressione o botão Add Library e por fim pressione o botão OK.

Existem vários pacotes de bibliotecas JSTL. Somente a biblioteca core será discutida nesta lição.

3.3. Biblioteca Core

Core fornece funcionalidades úteis para todo o projeto WEB. Core pode ser sub-categorizada em:

• Tag de uso geral• Repetição • Condicional• Manipulação de URL

Obs.:Para cada página JSP que possuir a biblioteca Core, a seguinte diretriz deve ser adicionada à página:

<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

Isto vincula as tags dentro da biblioteca Core usando o prefixo c.

3.3.1.Tags de uso geral

As tags de uso geral na Core executam tarefas comuns, embora uma delas tenha se tornado irrelevante com a liberação da especificação de JSP 2.0. As tags que compreendem o uso geral são: out, set, remove e catch.

<c:out>

A tag <c:out> recebe uma expressão, avalia seu conteúdo e retorna o resultado diretamente ao objeto corresponde à saída da página. Esta tag funciona da mesma maneira que a tag <jsp:getProperty>, com a exceção que um JavaBean não necessita acessar a funcionalidade.

Programação WEB 8

Page 100: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

Entretanto a especificação JSP 2.0, esta tag tornou-se obsoleta: as expressões EL podem ser avaliadas diretamente na stream de saída em qualquer parte da página JSP sem utilizar nenhuma tag.

<c:set>

Esta tag executa a mesma funcionalidade de uma tag <jsp:setProperty> que é capaz de ajustar valores dentro de um JavaBean. Pode também ajustar um atributo dentro de um escopo especificado que pode ser usado mais tarde pela JSP ou em outra parte da aplicação.

Esta ação tem os seguintes atributos:

• value – o valor que será ajustado no JavaBean especificado. Pode ser uma expressão EL• var – o nome de um atributo que será declarado• scope – define o escopo do atributo especificado pelo atributo var. Os valores podem ser:

page, request, session ou application • target – o nome do JavaBean cuja a propriedade será ajustada• property – o nome da propriedade dentro do JavaBean que receberá o valor

Como foi mencionado antes, há dois usos preliminares para esta tag. Para ajustar o valor dentro de um JavaBean, a tag utiliza somente value (valor), target (alvo) e os atributos da propriedade:

<c:set target="usuario" property="nome" value="JEDI"/>

A chamada anterior é equivalente a empregar a seguinte expressão de JSP:<% user.setName("JEDI"); %>

Para declarar um atributo em um escopo definido, a tag <c:set> utiliza somente o valor, var, e os atributos do escopo, embora o atributo do escopo seja opcional. Quando o atributo do escopo for omitido, será utilizado o escopo de página.

<c:set var="minhaString" value="Este é um teste String" scope="session"/>

No intuito de limitar a JSP para finalidades de apresentação, devemos limitar o uso desta tag. Ajustar propriedades do JavaBean ou ajustar variáveis em vários escopo são procedimentos que devem ser executados em outras partes do código, pois não são atribuições da camada de apresentação.

<c:remove>

Esta tag fornece uma maneira de remover os atributos de um escopo definido. Possui dois parâmetros:

• scope – o escopo do atributo a ser removido. • var – o nome do atributo a ser removido do escopo definido.

Use esta tag para remover o atributo no escopo da sessão anterior criado pela tag <c:set>:<c:remove var="minhaString" scope="session"/>

Como a tag <c:set>, o uso desta tag deve ser evitado. Remover as variáveis de um escopo não é atribuição dos objetos da camada de apresentação; este tipo da procedimento deve ser executado em outra parte da aplicação.

<c:catch>

A tag <c:catch> fornece a funcionalidade da manipulação de erros em áreas específicas de uma página JSP. É simples utilizar: coloca-se a instrução JSP dentro da tag <c:catch>

Este tag tem somente um atributo:

• var – define o nome que será usado na exceção. O atributo criado terá o escopo da página; isto é, será acessível mesmo depois que o bloco terminar.

Por exemplo, para obter exceções inesperadas dentro da página JSP, o seguinte código poderia

Programação WEB 9

Page 101: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

ser colocado:

<c:catch var="exception"> <%-- Forçamos um erro aqui para a funcionalidade desta Tag --%> <% if (true) { throw new Exception("Eu sou um erro inesperado"); } %></c:catch><%-- A tag seguinte é discutida com mais ênfase na seção condicional --%><c:if test="${! empty exception}"> Ocorreu um erro na aplicação : ${exception.message}</c:if>

3.3.2.Repetição

As repetições de JSTL são compostas por do-while, while e for em nossa página de JSP como scriptlets. JSTL fornece duas tags: forEach e forTokens.

<c:forEach>

Geralmente esta tag é mais utilizada para a repetição. Permite acesso por interação aos arrays, instâncias de java.util.Collection, java.util.Iterator, java.util.Map e java.util.Enumeration. Percorre cada elemento expondo o valor atual da repetição no código da página JSP contido na tag.

Esta tag possui os seguintes atributos:

• var – define o nome do atributo usado para exibir o valor atual da repetição na tag. Seu tipo é dependente da coleção que está sendo processada

• items – define a coleção da repetição. Pode ser especificado como uma expressão EL• varStatus – (opcional) define o nome do atributo que pode ser acessado pelo laço para

pegar o status do laço atual• begin – (opcional) um valor interno que define o índice que será usado como o ponto de

início da repetição. Se este valor não for fornecido, o índice de repetição começa com 0. Pode ser uma expressão de runtime

• end – (opcional) um valor inteiro que define o índice que será usado como ponto de parada do laço

• step – (opcional) um valor do tipo int que define o incremento a ser utilizado através das iterações. Por exemplo, definir este valor para 2 significa que os elementos serão acessados de 2 em 2, definir o valor 3 significa que os elementos serão acessados a cada 2 elementos contando do primeiro

Um uso comum para esta tag é interar sobre os resultados de um processamento realizado pela aplicação (provavelmente uma servlet). Peguemos como exemplo o seguinte cenário: temos um módulo da aplicação que recupera de um banco de dados os detalhes do usuário que resulta em uma categoria específica de procura. Naturalmente, queremos realizar o acesso lógico ao banco de dados através de uma servlet e passar os dados para apresentação pela JSP.

Abaixo, segue um código retirado de uma servlet que irá lidar com o acesso:... // carrega os parâmetros de usuário em um Map Map parameters = loadUserParameters(); UserDataService service = new UserDataService(); // realiza uma pesquisa em banco e armazena os resultados em uma Collection. Collection results = service.searchUsers(parameters); // armazena os resultados para pesquisa futura request.setAttribute("searchResults", results); // repassa a requisição para a JSP exibir request.getRequestDispatcher("/results.jsp").forward(request, response); ...

A página JSP a seguir é responsável por exibir o resultado. Assumiremos que o conteúdo de uma

Programação WEB 10

Page 102: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

coleção são instâncias de um objeto da classe User a qual tem um nome e um endereço String acessíveis via métodos get públicos.

<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <H1>Os seguintes usuários estão de acordo com seu critério de pesquisa : </H1> <br/> <c:forEach var="user" items="${requestScope.searchResults}"> <li> ${user.name} - ${user.address} </c:forEach>

Na página JSP, utilizamos a tag forEach para iterar sobre os resultados da procura. Isso é possível ao apontar a tag forEach para a instância da coleção armazenada no escopo da requisição usando EL. Então, será exposto cada elemento da coleção utilizando-se a variável de usuário que foi definida pelo atributo var e usando EL para exibir os valores.

Compare com o código abaixo como seria sem uso da JSTL: <H1>Os usuários abaixo coincidem com seu critério de pesquisa : </H1> <br/> <% Collection results = (Collection) request.getAttribute("searchResults"); Iterator iter = results.iterator(); while (iter.hasNext()) { User user = (User) iter.next(); %> <li> <%= user.getName() %> - <%= user.getAddress() %> <% } %>

É obvio que a versão em JSTL é muito mais compreensiva, especialmente para os designers de sites sem conhecimento em Java.

<c:forTokens>

Outra tag para repetição provida pela JSTL é a <forTokens>. Esta tag recebe uma String e analisa e extrai seu conteúdo em tokens baseados em um dado delimitador. Todos os atributos de uma tag forEach são compartilhados por esta tag. Além destes, o seguinte atributo também está disponível:

• delims – define o delimitador a ser passado quando extrair a String em tokens.

O atributo items agora possui um novo propósito nesta tag. Define a String a ser quebrada.

Segue um exemplo abaixo: <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> ...<c:forTokens items="item1,item2,item3,item4" delims="," var="item"> <li> ${item} </c:forTokens>

As instruções JSP anteriores resultam na seguinte página:

Programação WEB 11

Page 103: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

Figura 1: Saída resultante

3.3.3.Condicionais

O conjunto de tags nesta categoria imita a funcionalidade provida pelos conhecidos if e else-if que podem ser encontrados no padrão Java. Usar estas tags através do padrão Java permite um código mais limpo. Mudar dos scripts executados no servidor para uma saída normal em HTML que cria condicionais com scriptlets mais difíceis de compreender e gerenciar.

Exitem dois conjuntos principais de condicionais: a tag <c:if> que imita um simples if do Java e a tag <c:choose> relacionado as tags <c:when> e <c:otherwise>. Desta forma, estas tags relacionadas imitam a funcionalidade de um código switch.

<c:if>

A tag <c:if> permite a execução de seu conteúdo caso o valor da expressão a ser avaliada seja verdadeira. Caso seja falsa, nunca será executada.

Amostra: <c:if test="${empty sessionScope.user}"> Não está atualmente logado! Favor corrigir antes de continuar a adição.</c:if>

<c:choose>, <c:when>, <c:otherwise>

Estas três tags trabalham juntas para prover funcionalidade semelhante ao comando if-else em sua aplicação. Basicamente, uma ou mais tags são colocadas na tag “choose”. A tag “choose” avalia cada atributo de teste da tag “when” e executa a tag “when” apropriada quando avaliada como verdadeira. Caso não exista uma tag “when” relacionada com a condição, “choose” irá chamar a tag “otherwise”, se incluída no código.

Amostra: <c:choose> <c:when test="${userGrade >95}">Proeminente!</c:when> <c:when test="${userGrade > 80}">Bom!</c:when> <c:when test="${userGrade < 60}">Falhou!</c:when> <c:otherwise>Parabéns...</c:otherwise> </c:choose>

3.3.4.Manipulação de URL

A JSTL disponibiliza algumas tags personalizadas que fornecem a manipulação básica de URL. Estas tags constroem e melhoram as ações já disponíveis na JSP.

<c:import>

A tag <c:import> funciona da mesma forma que a tag include da JSP, de forma melhorada. A tag include da JSP permite que somente páginas dentro da aplicação WEB sejam referenciadas e

Programação WEB 12

Page 104: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

incluídas com o conteúdo da página atual. Já a tag <c:import> permite aos desenvolvedores especificarem URLs absolutas que apontam para recursos externos.

O atributo a ser usado por esta tag é:

• url – o valor da URL para importar. Pode ser tanto uma URL relativa apontando para um recurso em uma aplicação WEB como uma URL absoluta que aponta para um recurso externo.

Esta tag contém vários outros atributos. Entretanto, são usados para modificar o conteúdo de um recurso incluído, que seria melhor se fosse feito fora da página JSP.

<c:param>

A tag <c:param> é versátil usada em conjunto com um número de outras tags com o grupo de manipulação de URL. Enfatizando, esta tag NÃO pode ser usada sozinha, mas somente como uma tag filha de outras tags disponíveis na JSTL. Sendo usada para disponibilizar um modo de incluir os parâmetros e valores de uma requisição através de uma URL.

Os atributos usados por esta tag são:

• name – indica o nome do parâmetro de requisição. • value – indica o valor de um parâmetro de requisição.

A utilidade desta tag é melhor ilustrada com um rápido exemplo. Vamos considerar a tag que discutimos:

<c:import url="myResource.jsp?username=JEDI Program&location=here"/>

Caso queira incluir um recurso dinâmico para valores definidos como parâmetros de requisição, veja o trecho de código abaixo. Entretanto, o problema com o exemplo abaixo é que não está de acordo com a regras de codificação de URL. Existem diversas regras para codificar as URLs. Uma delas diz que os escopos precisam ser escritos usando um caractere especial. A tag <c:param> é capaz de abstrair estas regras de codificação. Em vez de se preocupar em como reescrever a URL com um conjunto de regras de codificação, podemos simplesmente fazer:

<c:import url="myResource.jsp"> <c:param name="username" value="Programa JEDI"/> <c:param name="location" value="here"/></c:import>

<c:url>

A tag <c:url> é usada para prover um modo de codificar automaticamente a URL com as informações necessárias para determinada sessão. Lembre-se da discussão anterior sobre gerenciamento de sessões que dita que a reescrita da URL é um modo de permitir uso de sessões caso os cookies estejam desabilitados.

O atributo relevante nesta tag é:

• value – URL a ser processada.

Para esclarecimento, esta tag nem sempre permite codificar as URLs. Isto é feito SOMENTE quando se detecta que o browser do cliente não suporta cookies.

Abaixo segue um exemplo de uso da tag:<HTML> <BODY> Clique <a href=" <c:url value="continue.jsp"/> ">aqui</a> para entrar na aplicação. </BODY></HTML>

Assim como a tag “import”, esta tag pode ter nenhuma ou várias tags <c:param> relacionadas aos parâmetros de inclusão.

Programação WEB 13

Page 105: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

4. Exercícios1) Considere que um componente de sua aplicação WEB adicionou um Vector em um escopo

de sessão contendo uma ou mais instâncias de um objeto MenuItem definido abaixo:public class MenuItem { private String itemName; private String description; private double price; // implementação de métodos get e set aqui}

Criar uma página JSP que irá obter um Vector que irá interagir sobre seu conteúdo mostrando cada propriedade dos objetos. Use EL e JSTL em sua implementação JSP.

2) Considere o cenário abaixo para uma página JSP que você irá criar: a página espera um parâmetro chamado isDebug que retorna um valor verdadeiro (true) ou falso (false). Caso o valor do parâmetro seja avaliado como verdadeiro, então seja exibir os seguinte itens:

- o nome do usuário logado atualmente - o ID de usuário atualmente logado

Assuma que estes valores são obtidos de um objeto user contendo as propriedades name e userID, respectivamente. A instância User foi armazenada no escopo de sessão usando a chave “userObject”.

De qualquer forma, sendo ou não verdadeiro o parâmetro isDebug, a página deve mostrar o seguinte conteúdo:

“Grato por usar esta aplicação. Atualmente, a página requisitada está em construção”.

3) Construir uma aplicação que permita que os usuários naveguem através de uma lista de classes e obtenha seus detalhes. Uma servlet pode ser utilizada para realizar uma pesquisa dada uma chave como parâmetro que está disponível e acessível no mapeamento /SearchServlet.

Criar uma página JSP que irá disponibilizar aos usuários uma caixa de texto para digitar a chave de procura. Caso a página seja submetida, este formulário irá transferir seu controle para a Servlet mencionada anteriormente. Do lado da caixa de texto, a página irá prover links nomeados de A a Z. Quando clicados, será submetida uma requisição para a servlet, onde os valores das chaves de pesquisa irão definir as letras que o resultado representa.

Criar uma implementação para esta página utilizando JSTL e EL. NÃO representar de forma fixa cada link da página individualmente, ou seja, os links serão criados dinamicamente. Dica: interaja sobre a lista de letras para evitar um código fixo dos links na página.

Programação WEB 14

Page 106: Projeto JEDI - Programação para WEB - Java - 178 páginas

Módulo 6Programação WEB

Lição 7Introdução a MVC e ao Framework Struts

Versão 1.0 - Nov/2007

Page 107: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

1. ObjetivosA arquitetura Model-View-Controller (MVC) é um padrão arquitetural comprovadamente eficaz em projetos de desenvolvimento. São definidos três componentes separados – Model (modelo), View (visão) e Controller (controle) – e dividi-se o projeto nestes componentes.

Ao final desta lição, o estudante será capaz de:

• Compreender o funcionamento da arquitetura MVC

• Utilizar o framework Struts no desenvolvimento de aplicações

Programação WEB 4

Page 108: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

2. Introdução à arquitetura Model-View-Controller

2.1. Motivação

Em toda aplicação, a parte do código mais sujeita a mudança é a porção da interface de usuário. A interface é o aspecto mais visível ao usuário e com o qual o usuário interage. Sendo assim, é o alvo mais provável de pedidos de mudança ou de melhorias da usabilidade.

Ter sua lógica de negócio firmemente acoplada com a interface de usuário leva a processos de alterações da interface mais complexos e sujeitos os erros. As mudanças a uma parte têm o potencial de trazer conseqüências em cascata no restante da aplicação.

2.2. Solução

O padrão MVC fornece uma solução para este problema dividindo a aplicação nos componentes Model, View e Controller, desacoplando estes de quaisquer outros ao fornecer um conjunto de recomendações sobre suas interações.

Figura 1: O padrão MVC

O diagrama acima mostra os três componentes definidos pelo padrão MVC assim como as suas interações previstas. Vamos analisar parte por parte.

2.3. Model

O padrão MVC define uma camada chamada Model que representa os dados usados pela aplicação, assim como as operações de negócio associadas a eles. Definindo a Model como uma camada separada, detalhes como recuperação, persistência e manipulação dos dados são abstraídas do restante da aplicação.

Há diversos benefícios com este tipo de abordagem. Primeiramente, isto assegura que os detalhes dos dados e das operações nos dados podem ser encontrados em uma área bem definida (a Model) em vez de estarem dispersos na aplicação. Isto prova-se benéfico durante a fase de manutenção da aplicação. Em segundo lugar, tendo-se os detalhes dos dados totalmente

Programação WEB 5

- Encapsular estado das aplicações- Responder ao estado das consultas- Expor as funcionalidades da aplicação- Notificar mudanças na view

- Solicitar a model- Requerer alterações para a model- Enviar solicitações para o controller- Permitir o controle de um view

- Definir a integração da aplicação- Mapear as ações do usuário- Selecionar a view para resposta- Um para cada funcionalidade

Page 109: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

separados de qualquer implementação de interface, os componentes da Model podem ser reaproveitados mais facilmente em outras aplicações que necessitam de uma funcionalidade similar.

2.4. View

Esta camada compreende todos os detalhes da implementação da interface de usuário. Aqui os componentes gráficos fornecem as representações do estado interno da aplicação e oferecem aos usuários as formas de interagir com a aplicação. Nenhuma outra camada interage com o usuário, somente a View.

Ter uma camada View separada, fornece diversos benefícios. Por exemplo, é mais fácil incluir a presença de um grupo de design separado na equipe de desenvolvimento. Este grupo de design pode se concentrar completamente no estilo, look & feel da aplicação sem ter que se preocupar a respeito de outros detalhes.

Além disso, ter uma camada View separada, torna possível fornecer múltiplas interfaces à aplicação. Considerando que a funcionalidade do núcleo da aplicação encontra-se em algum outro lugar (na Model), múltiplas interfaces podem ser criadas (baseadas no Swing, baseadas na WEB, baseadas na console), todas podem ter diferentes look & feel e todas podem simplesmente utilizar os componentes da Model com suas funcionalidades.

2.5. Controller

Por último, a arquitetura MVC inclui a camada do componente Controller. Esta camada contém detalhes sobre o fluxo de programa/transição da tela e é também responsável por capturar os eventos gerados pelo usuário na camada View e, possivelmente, atualizar os componentes da Model usando dados fornecidos pelo usuário.

Há diversos benefícios em se ter uma camada separada para a Controller. Primeiro, tendo um componente da aplicação separado para conter os detalhes da transição de tela, componentes da View podem ser projetados de maneira que não necessitem estar cientes um do outro. Isto facilita a múltiplas equipes independentes de desenvolvimento que trabalham simultaneamente. As interações entre os componentes da View são abstraídas na Controller.

Segundo, tendo uma camada separada que atualize os componentes da Model, detalhes são removidos da camada de apresentação. A camada de apresentação pode se especializar em sua finalidade preliminar de apresentar dados ao usuário. Os detalhes de como os dados do usuário mudam o estado da aplicação são escondidos dentro do componente da Controller. Isto fornece uma separação limpa entre a lógica de apresentação e a lógica de negócio.

Não podemos afirmar que o padrão MVC possua somente benefícios e nenhum efeito colateral. Dividir a aplicação em três componentes separados resulta em aumento de complexidade. Para pequenas aplicações que não se beneficiam do acoplamento fraco da Model, pode ser excessivo o uso deste padrão. Entretanto, é melhor lembrar que as aplicações freqüentemente começam pequenas e tornam-se sistemas complexos. Assim, deve-se sempre buscar o acoplamento fraco.

Programação WEB 6

Page 110: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

3. Arquitetura MVC para a WEB: A Arquitetura Model 2A arquitetura MVC foi modificada ligeiramente e adaptada para o uso em aplicações WEB. A arquitetura resultante foi chamada, então, de arquitetura Model 2.

As aplicações Model 2 têm tipicamente o seguinte:

• Uma servlet Controller que fornece um ponto de acesso único ao restante da aplicação. Este Controller é responsável por fornecer a gerência central do fluxo da aplicação e dos serviços como a manipulação da segurança e a gerência do usuário. Este tipo de controlador é frequentemente chamado de Front Controller.

• A servlet Controller usa tipicamente configurações XML para determinar o fluxo da aplicação e o processamento do comando. Também emprega, geralmente, os componentes de ajuda que servem como objetos Command. Isto significa que tais componentes de ajuda estão associados com às ações do usuário e são criados/chamados para gerenciar aquelas ações enquanto ocorrem, chamando os componentes da Model quando necessário. Isto serve para desacoplar a servlet Controller da Model.

Usar tal arquitetura foi provado ser vantajoso para nossas aplicações. Implementá-la pode ser feito mais facilmente mediante o uso de frameworks existentes. Estes frameworks fornecem muitos dos detalhes de configuração de modo que possamos concentrar nossa atenção em tarefas mais importantes. Fornecem também úteis funcionalidades adicionais úteis.

Neste módulo, trataremos dos dois frameworks mais populares: Struts e JavaServer Faces (JSF). Debateremos primeiramente sobre o Struts em seguida a JSF.

Programação WEB 7

Page 111: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

4. STRUTSStruts é um framework de código aberto que é disponibilizado e gerenciado pela Apache Software Foundation. Temos abaixo uma representação de como o Struts gerencia a arquitetura Model 2:

Figura 2: Struts e a arquitetura Model 2

Vamos examinar os objetos fornecidos pelo framework para cada um dos componentes Model, View e Controller.

4.1. Controller

4.1.1.ActionServlet

No centro da implementação do Controller do framework Struts encontra-se a ActionServlet. Esta serve como uma servlet Front Controller e fornece um único ponto de acesso ao restante da aplicação. Contém também a lógica de manipulação da requisição do cliente – através da requisição HTTP do cliente e, baseado na requisição, ou redireciona o usuário diretamente à página WEB ou despacha a requisição ao objeto gerenciador chamado Actions que será, então, responsável por determinar o resultado da resposta.

Programação WEB 8

Page 112: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

A ActionServlet conhece todos estes detalhes – qual Action chamar para gerenciar determinada requisição, qual componente de View deve ser chamado em seguida – lendo esta informação de um arquivo de configuração XML, geralmente nomeado struts-config.xml.

Esta servlet é fornecida pelo framework Struts. Tudo o que é necessário para incluí-la em nossa aplicação é configurá-la corretamente no descritor de implementação da aplicação.

Abaixo está um trecho de web.xml exibindo como configurar o ActionServlet para o uso:... <servlet> <servlet-name>action</servlet-name> <servlet-class> org.apache.struts.action.ActionServlet </servlet-class> <init-param> <param-name>application</param-name> <param-value>ApplicationResources</param-value> </init-param> <init-param> <param-name>config</param-name> <param-value>/WEB-INF/struts-config.xml</param-value> </init-param> </servlet> ... <servlet-mapping> <servlet-name>action</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping>

4.1.2.Action

Como mencionamos antes, algumas requisições do cliente são delegadas às instâncias de objetos da Action por nossa classe servlet Front Controller. Todos os objetos Action definem um método chamado execute() e é este o método que é chamado pela ActionServlet para gerenciar a requisição.

O framework Struts fornece aos desenvolvedores somente a classe base Action. Para incluir objetos Action como gerenciadores de requisições em sua aplicação, os desenvolvedores devem estender esta classe base e fornecer uma implementação para o método execute().

Uma atividade comum em aplicações WEB é o início de uma sessão do usuário. Abaixo é mostrada uma implementação da classe LoginAction que poderia ser utilizada para gerenciar tais requisições.

package actions;import forms.LoginForm;import javax.servlet.http.*;import org.apache.struts.action.*;public class LoginAction extends Action { public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { // faz o cast do objeto genérico ActionForm // para a implementação específica ActionForm // configurada para esta Action LoginForm loginForm = (LoginForm) form; // Recupera os dados especificados pelo usuário. String loginName = loginForm.getLoginName(); String password = loginForm.getPassword();

Programação WEB 9

Page 113: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

// Verifica se é o usuário correto if (!(loginName.equals("JEDI") && password.equals("JEDI"))) return mapping.findForward("failure"); // Armazena o resultado no session scope para uso no restante da aplicação HttpSession session = request.getSession(); session.setAttribute("USER", loginName); // o usuário efetuou o login com sucesso. Despacha o usuário para // o restante da aplicação. return mapping.findForward("success"); }}

Observe que a implementação acima emprega o uso de um objeto de negócio, chamado UserService, para a autenticação do usuário e não fornece diretamente sua própria implementação no método execute(). Instâncias de Action devem ser criadas desta maneira – a funcionalidade central deve ser delegada aos objetos de negócio (que podem ser considerados parte da Model), não implementada na própria Action. As únicas atividades que uma Action deve executar são:

• Recuperar as informações fornecidas pelo JavaBean de usuário do ActionForm associado.

• Traduzir dados do formulário em parâmetros requeridos pelos objetos de negócio que implementam a funcionalidade.

• Recuperar o resultado da operação do objeto de negócio e determinar a View seguinte para onde o usuário deve ser encaminhado.

• Opcionalmente, armazenar os resultados dos dados da operação de negócio na sessão ou solicitar objetos que serão utilizados pelo restante da aplicação.

Convém lembrar que ao codificar os exemplos dos objetos Action, que o framework irá criar uma única cópia do objeto e usá-lo para facilitar todas as requisições. Isto significa que devemos sempre codificar a Action para ser thread-safe e certificar em utilizar sempre variáveis locais e não variáveis de classe.

Instâncias de Action são capazes de instruir a ActionServlet para qual componente de View delegar a resposta retornando instâncias de objetos ActionForward. Actions têm o acesso a estes objetos de ActionForward com o uso de um objeto ActionMapping, que encapsula os dados de mapeamentos de caminhos lógicos para cada Action. Estes mapeamentos são lidos do arquivo de configuração pela ActionServlet, que é responsável por enviar a ActionMapping necessária à Action. Deste modo, para instruir a ActionServlet a passar o controle para um caminho lógico chamado success, nossa Action executa a seguinte instrução:

return mapping.findForward("success");

4.1.3.ActionForm

O framework Struts fornece uma classe chamada ActionForm. Instâncias desta classe são usadas para facilitar a recuperação dos dados dos formulários preenchidos pelo usuário através das instâncias de Action que gerenciam os eventos de formulário.

Cada instância de ActionForm representa um formulário ou um conjunto de formulários, define as propriedades que correspondem aos elementos do(s) formulário(s) que representam, e as expõem usando métodos setters e getters publicamente acessíveis. Actions que necessitam dos dados dos formulários, simplesmente chamam os métodos getters da instância de ActionForm.

Struts fornece a definição base da classe; os desenvolvedores têm a responsabilidade de criar suas próprias implementações.

Abaixo é listado o ActionForm usado no exemplo acima:import org.apache.struts.action.*; public class LoginForm extends ActionForm { private String loginName;

Programação WEB 10

Page 114: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

private String password; public String getLoginName() { return loginName; } public void setLoginName(String loginName) { this.loginName = loginName; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }

Ao codificar ActionForms, devemos lembrar de:

• Definir propriedades (com métodos get e set) para cada elemento representado no formulário.

• NÃO colocar nenhuma lógica de negócio no ActionForm. São concebidos meramente para transferir dados entre componentes da View e do Controller e por isso não são utilizados pela lógica de negócio.

• Opcionalmente, incluir um método de validação dos dados antes que o controle passe para a Action.

4.1.4.Arquivo struts-config.xml

Atua como arquivo de configuração para os componentes do framework Struts. Podemos definir qual Action é chamada para cada requisição, que componente de formulário usar para cada Action e o mapeamento de nomes lógicos para caminhos reais, entre outros. Abaixo, temos uma cópia do arquivo struts-config.xml usado para o exemplo acima:

<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE struts-config PUBLIC"-//Apache Software Foundation//DTD Struts Configuration 1.2//EN""http://jakarta.apache.org/struts/dtds/struts-config_1_2.dtd"><struts-config> <form-beans> <form-bean name="loginForm" type="login.LoginForm"/> </form-beans> <action-mappings> <action name="loginForm" path="/login" scope="request" type="login.LoginAction"> <forward name="success" path="/success.jsp"/> <forward name="failure" path="/failure.jsp"/> </action> </action-mappings> </struts-config>

Descreveremos, a seguir, cada um desses elementos.

<!DOCTYPE ...>

Define o arquivo XML como sendo um arquivo de configuração para utilização pelo framework Struts. Excluir esta linha, ou mesmo digitá-la errado, resultará em erros quando a aplicação carregar.

<struts-config>

Elemento raiz do arquivo de configuração. Todos os outros elementos são filhos desse elemento.

<form-beans>

Programação WEB 11

Page 115: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

Marca o início e o fim das definições das instâncias de uma classe ActionForms. Elementos <form-beans> DEVEM ser colocados como filhos deste elemento.

<form-bean>

Define uma instância de ActionForm que pode ser utilizada pela aplicação. Tem dois atributos:

• name – o nome lógico a ser associado com a classe ActionForm

• type – o nome completo da classe ActionForm.

<action-mappings>

Marca o início e o fim das definições de ações e seus mapeamentos. Todos os elementos <action> DEVEM ser colocados como filhos deste elemento.

<action>

Define uma instância de um objeto Action para utilização pela aplicação. A maior parte dos elementos de ação implementa os seguintes atributos:

• name – o nome do elemento <form-bean> a ser utilizado nesta ação.

• path – o caminho relativo ao contexto a ser utilizado por esta Action. Qualquer requisição a este caminho resulta na chamada da Action definida.

• scope – contexto do escopo onde nossa ActionForm pode ser acessada. Isto informa onde a ActionServlet deverá armazenar a instância da classe ActionForm.

• type – o nome de classe Action.

<forward>

Ações podem ter nenhum ou muitos elementos de redireção. Este elemento define o mapeamento lógico entre um nome e um caminho na nossa aplicação.

Tem os seguintes atributos:

• name – o nome lógico do elemento de redireção que pode ser utilizado pela instância de Action.

• path – o caminho para o componente de visualização associado a este redirecionador.

4.1.5.Resumo do que se deve fazer para a camada Controller:

Executar uma única vez:

• Configure o ActionServlet no descritor de instalação da nossa aplicação

Para cada processador de formulário adicionado à aplicação:

• Crie um objeto ActionForm que representará todos os dados obtidos do formulário.

• Crie um objeto Action que, no seu método de execução, defina como o formulário será processado.

• Crie uma entrada de configuração para o objeto ActionForm em struts-config.xml, dentro da seção <form-beans>.

• Crie uma entrada de configuração para o objeto Action em struts-config.xml, dentro da seção <action-mappings>.

• Configure todos os redirecionadores usados pela Action, colocando elementos <forward> dentro do corpo de definição <action> da ação.

4.2. Model (Modelo)

O framework Struts não fornece explicitamente nenhum componente dentro de Model. Quais objetos utilizar como componentes Model é deixado a critério do desenvolvedor, apesar de serem normalmente JavaBeans ou, eventualmente, Entreprise JavaBeans (EJB).

Programação WEB 12

Page 116: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

4.3. View (Visualização)

Struts pode utilizar qualquer tecnologia da camada de apresentação, apesar de, na maioria dos casos, utilizar JSP e/ou HTML. O que o Struts fornece para esta camada é um conjunto de bibliotecas de tags que permite utilizar as facilidades do Struts para popular e validar automaticamente os formulários.

4.3.1.struts-html

Struts fornece uma biblioteca de tags chamada struts-html que imita muitas das funcionalidades das tags HTML padrão, mas traz também novas funcionalidades.

Esta biblioteca de tags é mais utilizada na criação de formulários da aplicação. Observaremos o exemplo do formulário descrito a seguir.

<%@page contentType="text/html" pageEncoding="UTF-8"%><%@ taglib uri="http://jakarta.apache.org/struts/tags-html" prefix="html" %><html> <head> <title>Login Page</title> </head> <body> <h1> Login Page </h1> <br/> <html:form action="/login"> User Name : <html:text property="loginName"/> <br/> Password : <html:password property="password"/> <br/> <html:submit/> </html:form> </body></html>

Este e o formulário utilizado no exemplo anterior. Observe como os elementos HTML padronizados como <form>, <input type="text">, <input type="password"> e <input type="submit"> foram substituídos pela biblioteca de tags struts-html. Iremos, a seguir, descrever estas tags.

<html:form>

A tag <html:form> renderiza um formulário em HTML. Cada tag do formulário é associada a um mapeamento de ações definidas pelo atributo action. O valor deste atributo action especifica o caminho do mapeamento da ação.

Revendo o arquivo de configuração utilizado no exemplo anterior,... <action name=”loginForm” path=”/login” scope=”request” type=”login.LoginAction”> ...

Notamos que o valor do atributo path coincide com o valor no atributo action na tag <html:form>. Isso indica que este formulário, quando submetido, será enviado à classe LoginAction para seu processamento.

Uma das condições para termos uma tag <html:form> válida é que cada um dos campos que ela contenha deve ser definido no ActionForm especificado para o mapeamento da ação particular.

Outros atributos possíveis:

• method - pode ser tanto POST como GET. Define o método HTTP a ser utilizado ao submeter o formulário

<html:text>

Esta tag renderiza o campo padrão de entrada de texto do HTML. O atributo property especifica

Programação WEB 13

Page 117: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

qual propriedade no ActionForm está ligada a ele. Uma vez que o valor do atributo no exemplo acima é “loginName”, este campo de texto é vinculado à propriedade loginName do formulário LoginForm.

Outros atributos disponíveis para esta tag:

• size – define o tamanho do campo de texto a ser mostrado

• maxlength – define o comprimento máximo do valor informado pelo usuário

<html:password>

Esta tag renderiza um campo de senha padrão do HTML. O atributo property especifica a qual propriedade do ActionForm está ligada. No nosso exemplo, este campo está ligado à propriedade password do LoginForm.

Em nossas discussões anteriores sobre as tags <html:text> e <html:password>, foi mencionado que eles estão ligados a propriedades da instância da classe ActionForm associadas ao formulário. Isto significa, em termos práticos, que os valores informados nestes campos serão automaticamente atribuídos às propriedades correspondentes ao objeto ActionForm. Além disso, se houvessem valores prévios no objeto ActionForm (o formulário já ter sido acessado), os campos do formulário seriam automaticamente preenchidos com valores do objeto ActionForm.

Outras tags na biblioteca de tags struts-html:

<html:hidden>

Renderiza um campo de formulário HTML oculto.

Exemplo de utilização:<html:hidden property="hiddenField"/>

<html:radio>

Renderiza um controle HTML do tipo radio button.

Exemplo de utilização:<html:radio property="radioField"/>

<html:select>, <html:option>

A tag <html:select> é utilizada para renderizar uma lista de seleção. As opções para esta lista de seleção são especificadas usando as tags <html:option>.

Exemplo de utilização:<html:form action="/sampleFormAction"> <html:select property="selectField" size="5"> <html:option value="0 value">0 Label</html:option> <html:option value="1 value">1 Label</html:option> <html:option value="2 value">2 Label</html:option> </html:select> </html:form>

O exemplo acima gera uma lista de seleção com tamanho de 5 itens (size="5"). Observe que o corpo da tag <html:option> atua como um rótulo para este item da lista, enquanto o atributo value especifica o valor que será repassado à ação quando ocorrer sua submissão.

<html:checkbox>, <html:textarea>

São utilizados para renderizar campos de caixa de seleção e de área de texto, respectivamente.

4.3.2.Resumo de coisas a fazer para a camada View:

Executar somente uma vez:

• Configure as bibliotecas de tags para utilização no deployment descriptor da aplicação.

• Coloque os arquivos JAR contendo a implementação das bibliotecas de tags no diretório

Programação WEB 14

Page 118: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

WEB-INF/lib da aplicação.

Para cada formulário a ser criado:

• Adicione a diretiva taglib apropriada à página JSP para permitir a utilização da biblioteca de tags struts-html.

• No lugar da tag <form>, utilizar a tag <html:form>. Especifique no seu atributo Action e o caminho da Action que processará o formulário.

• No lugar das tags de campo HTML (por exemplo a tag <input type="text">), utilize as tags incluídas na biblioteca de tags struts-html que desempenham funcionalidade semelhante (por exemplo a tag <html:text>).

• Assegure-se que todos os campos de entrada presentes na definição do formulário estejam presentes como propriedades no objeto ActionForm associado com esta requisição. NÃO é necessário que todas as propriedades no objeto sejam mostradas como campos, mas requer que todos os campos estejam presentes como propriedades.

• Lembre-se de fechar a tag <html:form>.

4.4. Entendendo o Struts como um todo

Para compreender como o framework Struts funciona como um todo, vamos tomar como exemplo o cenário visto acima: a entrada de um usuário no sistema.

Antes mesmo de o usuário entrar no sítio, o ActionServlet carrega o arquivo de configuração e lê os detalhes. Assim, quando o usuário acessar o formulário de login, o framework já sabe qual ActionForm associada que armazenará seus detalhes e qual Action deve ser processada na submissão do formulário.

Quando a página de login carregar, as tags struts-html que utilizamos tentam renderizar os campos HTML. Se o ActionForm para este formulário não existir, a página não será mostrada. Se houver mais campos no formulário do que propriedades no ActionForm para lhes dar suporte, a página também não será mostrada. Se o ActionForm existir, as tags verão se há algum valor armazenado no ActionForm. Se houver, os campos do formulário são prenchidos com esses dados. Se não, os campos do formulário serão deixados vazios e o usuário verá um formulário em branco.

Quando um formulário é submetido, os valores nos campos do formulário são automaticamente atribuídos ao objeto ActionForm pelo framework Struts. Este objeto é, então, passado ao Action handler apropriado, juntamente com o objeto ActionMapping que traz detalhes dos mapeamentos, como especificados no arquivo de configuração.

O objeto Action executa o seu processamento e, então, avisa ao ActionServlet para onde ir em seguida, especificando um dos redirecionamentos configurados no mapeamento. O ActionServlet, então, redireciona o usuário para aquela página.

Programação WEB 15

Page 119: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

5. Exercícios

5.1. Material Educacional

Crie uma aplicação WEB baseada em Struts. A aplicação será usada para gerenciar e administrar um endereço WEB que permite o download de material educacional. Há dois níveis de usuário para esta aplicação: usuário comum e usuário administrador.

As atividades disponíveis para os administradores são:

a) Gerenciamento do download de material – inclusão, alteração e exclusão de downloads

b) Gerenciamento dos usuários – inclusão, alteração e exclusão de usuários

c) Relatório de atividade dos usuários – os administradores têm acesso a uma página que mostra uma lista de usuários classificados pela quantidade de downloads em ordem decrescente. Ao selecionar um usuário nesta lista, será mostrado um histórico detalhado dos downloads deste usuário

As atividades disponíveis para os usuário comuns são:

a) Navegação pelos materiais para download

b) Seleção de material para baixar – sua implementação NÃO necessita realmente prover material para download. A aplicação deve ter um link que, ao ser selecionado, simulará um download.

Ambos tipos terão um ponto comum de início para a aplicação: uma página de entrada que receberá o nome do usuário e sua senha pessoal. A aplicação terá, então, que determinar o nível de autorização deste usuário e redirecioná-lo à página apropriada.

Descrição das Telas

Além da página de entrada, que pode ser implementada simplesmente por dois elementos de entrada de texto e um botão de submissão, há diversas outras telas que compõe esta aplicação.

Para os administradores, a seqüencia de telas pode ser descrita pelo seguinte mini-roteiro:

● Página de Conteúdo Principal – apresenta três links, cada um correspondendo a um grande grupo de atividades permitidas a um administrador

● Página de Gerenciamento de Downloads – apresenta ao administrador opções para adicionar, atualizar ou apagar material de download

● Página para Adicionar Material para Download – contém um formulário com os elementos de entrada necessários para inserir um novo material para download

● Página para Atualizar Material para Download – contém um formulário com os elementos de entrada necessários para atualizar um material existente para download

● Página para Apagar Material para Download – contém um formulário com um campo de texto que receberá o identificador do material como entrada para exclusão

● Página de Gerenciamento de Usuário – apresenta ao administrador opções para adicionar, atualizar ou apagar um usuário

● Página para Adicionar Usuário – contém um formulário com os elementos de entrada necessários para adicionar um novo usuário

● Página para Atualizar Usuário – contém um formulário com os elementos de entrada necessários para atualizar um usuário existente

● Página para Apagar Usuário – contém um formulário com um campo de texto que receberá como entrada o identificador do usuário para exclusão

● Página de Atividade dos Usuários – lista de usuários em ordem decrescente de acordo com a quantidade de downloads. Clicar num usuário da lista levará o administrador a uma

Programação WEB 16

Page 120: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

página detalhada de downloads do usuário

● Página Detalhada de Downloads do Usuário – apresenta um histórico detalhado para um usuário em particular

● Página de Erro do Administrador – página apresentada em caso de qualquer erro na aplicação. Mostra a natureza do erro ao administrador

O lado do usuário será mais simples, contendo as seguintes páginas:

● Página de Navegação pela Lista de Material de Download – a lista contém somente o nome do download. Clicar em um determinado nome envia o usuário a uma página mais detalhada

● Página Detalhada do Material para Download – contém detalhes completos sobre o item de download selecionado. Esta página contém um link que pode ser utilizado para realizar o download do material

● Página de Erro – página apresentada em case de qualquer erro na aplicação. Mostra a natureza do erro do usuário

Programação WEB 17

Page 121: Projeto JEDI - Programação para WEB - Java - 178 páginas

Módulo 6Programação WEB

Lição 8Tópicos avançados no Framework Struts

Versão 1.0 - Nov/2007

Page 122: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

1. ObjetivosNa lição anterior, trabalhamos com o básico do framework Struts. Aprendemos como incluir o framework Struts em nossa aplicação configurando o ActionServlet para manipular as requisições. Também aprendemos como criar instâncias de classes Action que servem como manipuladores de ações para submissões de forms e outras requisições de usuário. Vimos como criar classes ActionForm que oferecem uma maneira fácil de transferir dados de forms para os ActionHandlers apropriados. Finalmente, vimos como usar as bibliotecas de tag incluídas a fim de auxiliar na junção dos forms HTML às páginas JSP dentro do framework.

Ao final desta lição, o estudante será capaz de:

• Usar os DynaActionForms para minimizar o número de classes que precisamos desenvolver

• Prover validação do lado servidor em nossa aplicação com o framework Validator

• Compartimentalizar a camada de apresentação com o framework Tiles

Programação WEB 4

Page 123: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

2. DynaActionFormsEm grandes aplicações, o número de classes que precisam ser criadas e mantidas pode se tornar extremamente alto. Struts suporta classes que contribuem, e muito, para esse elevado número, especialmente com respeito a seus ActionForms, os quais requerem uma sólida implementação para unir todos os forms na aplicação. Vários desenvolvedores se encontram-se em apuros por essa restrição já que ActionForms são, na maioria das vezes, JavaBeans com métodos get e set para cada um dos campos do form que ele precisa representar.

Struts traz uma solução no lançamento de sua versão 1.1 chamada DynaActionForms. DynaActionForms se comportam-se exatamente como ActionForms, nos quais uma instância pode ser obtida e seus métodos são chamados pelos manipuladores de ação que precisam de seus dados. A principal diferença é que cada DynaActionForm não é definido ou declarado como uma classe separada. Um DynaActionForm é simplesmente configurado dentro do arquivo struts-config.xml.

Abaixo um exemplo de como configurar e declarar um DynaActionForms. Fizemos uso do exemplo da lição anterior, criando aqui um ActionForm que irá manipular os dados requisitados para o login do usuário.

<?xml version="1.0"?> <!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN" "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd"> <struts-config> <form-beans> <form-bean name="loginForm" type="org.apache.struts.action.DynaActionForm"> <form-property name="loginName" type="java.lang.String"/> <form-property name="password" type="java.lang.String"/> </form-bean> </form-beans> <action-mappings> <action name="loginForm" path="/login" scope="request" type="action.LoginAction"> <forward name="success" path="/success.jsp"/> <forward name="failure" path="/failure.jsp"/> </action> </action-mappings> </struts-config>

Como podemos ver, criar um DynaActionForm é bastante simples. Em alguns casos, declarar um DynaActionForm dentro do arquivo de configuração é mais simples e mais fácil do que escrever uma instância da classe ActionForm. Não precisamos mais listar todas as propriedades do form e criar os métodos get e set para cada uma delas. Com DynaActionForms, simplesmente declaramos o nome da propriedade e o tipo. É responsabilidade do framework prover uma instância de trabalho baseada nessa informação.

Fornecer essa informação de configuração é a único pré-requisito para se fazer uso dos DynaActionForms. Não há modificações que precisem ser feitas em nenhuma das instâncias Action. Eles ainda têm uma instância ActionForm válida que podem retornar dados.

A seguir estão os tipos de dados Java suportados por DynaActionForm:

• java.lang.BigDecimal • java.lang.BigInteger • boolean e java.lang.Boolean • char e java.lang.Character • double e java.lang.Double • float e java.lang.Float

Programação WEB 5

Page 124: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

• int e java.lang.Integer • long e java.lang.Long • short e java.lang.Short • java.lang.String • java.lang.Date • java.lang.Time • java.sql.TimeStamp

Usar DynaActionForms pode ser mais conveniente, mas não são sempre a melhor solução. Ainda há casos onde usar ActionForms é mais apropriado.

• DynaActionForms suportam apenas um conjunto limitado de tipos Java. Se nosso form precisa armazenar informação expressa como um tipo de dado diferente dos suportados, então os ActionForms ainda são o melhor caminho. Um exemplo disso seria se usássemos objetos Java desenvolvidos como propriedades do form.

• DynaActionForms não suportam o conceito de herança. Não há como criar uma definição base para um form que possa ser estendida posteriormente.

Programação WEB 6

Page 125: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

3. ValidadoresValidação é uma atividade que deve ser executada em todos os casos de entrada de dados. Através da validação, podemos checar o formato e o conteúdo dos valores informados pelo usuário. Usuários, apesar de tudo, nem sempre informam a entrada correta: letras podem ser inseridas em um campo numérico e vice-versa; em um campo que requer 3 dígitos são inseridos apenas 2, e assim por diante. É responsabilidade da aplicação estar ciente disso e manipular tais erros de entrada a despeito além de qualquer erro resultante de processamento da lógica de negócio (por exemplo, a senha não está correta para determinado login).

Struts alivia a carga do desenvolvedor em realizar essa validação fornecendo um famework de validação chamado de Validator Framework. O uso desse framework trás alguns benefícios:

• Várias regras de validação pré-definidas. Há um conjunto comum de verificações que devem ser executadas em qualquer número de aplicações como a verificação que pode ser de formato, de tamanho, de existência entre outras. O framework fornece os componentes necessários para que os desenvolvedores não precisem criar código que irão manipular esses tipos comuns de validação. Geralmente, os componentes que o framework fornece são suficientes para a maioria das aplicações, embora validadores customizados também possam ser criados.

• Elimina redundância no código de validação. O framework separa os componentes que executam a validação dos que necessitam de validação. Ao invés de ter múltiplos componentes incorporando o mesmo código de validação, a funcionalidade pode ser mantida em um componente de validação externo e separado que pode ser reutilizado por toda a aplicação.

• Um único ponto de manutenção. Desenvolvedores não precisam mais percorrer toda a aplicação para checar as regras de validação que eles utilizam em seus vários componentes. Todas essas regras são declaradas em arquivos de configuração fornecidos pelo framework.

Há alguns passos necessários para incluir a funcionalidade do Validator em uma aplicação Struts:

• Configurar o Plug-in do Validator.• Declarar os forms que requerem validação e o tipo de validação que eles necessitam.• Criar as mensagens que serão exibidas no caso de falha na validação.• Modificar o arquivo struts-config para permitir validação automática.

3.1. Configurando o Plug-In do Validator

Esse passo é necessário para tornar o framework Struts ciente do uso do framework Validator. Tudo que precisa ser feito é adicionar algumas poucas linhas ao nosso arquivostruts-config.xml. Abaixo está uma amostra:

... <forward name="success" path="/success.jsp"/> <forward name="failure" path="/failure.jsp"/> </action> </action-mappings>

<plug-in className="org.apache.struts.validator.ValidatorPlugIn"> <set-property property="pathnames" value="/WEB-INF/validator-rules.xml, /WEB-INF/validation.xml"/> </plug-in> </struts-config>

A propriedade pathnames informa ao framework onde ele pode achar os arquivos de configuração de que precisa. Há dois arquivos de configuração: validator-rules.xml e validation.xml.

Programação WEB 7

Page 126: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

3.2. validator-rules.xml

Esse arquivo de configuração define as classes que implementam o código de validação. O framework vem com um exemplo desse arquivo com classes pré-definidas de validação já configuradas.

A seguir, uma lista dos nomes lógicos dos validadores que vêm com o framework:

• required – propriedades marcadas como required devem ter ao menos um caractere. • mask – propriedades sujeitas ao validador de máscara devem combinar com a expressão

regular que submetemos usando o parâmetro mask.• minlength – se aplicado a uma propriedade, deve ter um tamanho igual ou maior que o

valor do parâmetro min que passamos. • maxlength - se aplicado a uma propriedade, deve ter um tamanho igual ou menor que o

valor do parâmetro max que passamos. • range – a propriedade deve estar entre os valores fornecidos através dos parâmetros min

e max. • byte, short, integer, long, float, double – a propriedade deve poder ser convertida para

o tipo de dado primitivo específico. • date – valida se o valor da propriedade é uma data válida. • creditCard – valida se o valor da propriedade pode ser um número de cartão de crédito

válido.• email – valida se o valor da propriedade pode ser um endereço de email válido.

Esses validadores pré-definidos são suficientes para a maioria dos propósitos de validação. Nos casos em que os requisitos de validação da aplicação não podem ser manipulados por esses validadores, implementações customizadas podem ser criadas, e suas definições adicionadas ao arquivo validator-rules.xml.

3.3. validation.xml

Esse arquivo de configuração declara ao framework quais forms requerem validação e quais regras de validação se deseja implementar. O framework apenas fornece a estrutura do arquivo. Desenvolvedores terão que configurar esse arquivo para tirar proveito da funcionalidade do framework.

3.3.1.Configurando o arquivo validation.xml

O arquivo validation.xml fornece uma estrutura que podemos usar para especificar nossa configuração de validação. A seguir são mostrados os elementos XML que o define:

<formset>

O elemento raiz desse arquivo de configuração.

<form>

Cada formulário da aplicação, que requer validação, deve ter pelo menos um elemento <form> correspondente. O atributo name pode mapear para um formulário a ser configurado no struts-config.xml. Esse elemento pode conter um ou mais elementos <field>.

<field>

Cada elemento field representa uma propriedade no form que requer validação. Esse elemento tem os seguintes atributos:

• property – o nome da propriedade no form que será validada.

• depends – a lista, separada por vírgulas, dos validadores que serão aplicados à propriedade. Os nomes dos validadores são definidos dentro do arquivo validator-rules.xml.

<arg0>

Define a chave para a mensagem de erro que será exibida no caso da propriedade não passar nas

Programação WEB 8

Page 127: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

regras de validação. A chave e a mensagem de erro que será mostrada devem ser encontradas em um pacote de recursos que o desenvolvedor deve criar.

<var>

Alguns validadores requerem que certos valores sejam passados para eles como argumentos para que possam funcionar corretamente. Esses argumentos podem ser fornecidos usando um ou mais elementos var, no qual cada elemento var corresponde a um argumento passado ao validador. Esse elemento define dois elementos filho:

• <var-name> - define o nome do argumento a ser fornecido.

• <var-value> - define o valor do argumento.

Um exemplo de tal arquivo de configuração é fornecido abaixo. <form-validation> <formset> <form name="loginForm"> <field property="loginName" depends="required"> <arg0 key="error.loginname.required"/> </field> <field property="password" depends="required,minlength"> <arg0 key="error.password.required"/> <var> <var-name>minlength</var-name> <var-value>4</var-value> </var> </field> </form> </formset> </form-validation>

O trecho do arquivo fornecido configura o loginForm usado em nossos exemplos anteriores. loginForm corresponde diretamente a um ActionForm que é definido dentro do arquivo de configuração do Struts sob o nome loginForm. Ele é configurado utilizando o loginForm com o atributo name do elemento <form>.

Depois do elemento <form>, descobrimos que há dois elementos <field> definidos. O primeiro elemento <field> configura a propriedade loginName do nosso form, o segundo configura a propriedade password. Podemos determinar quem é quem olhando o valor do atributo property.

3.4. Definindo o pacote de recursos

O elemento <arg0> define uma chave que necessita combinar com uma entrada em um pacote de recursos. Essa entrada é, então, usada para determinar a mensagem que será exibida ao usuário. O framework Validator faz uso do mesmo pacote de recursos que o framework Struts, o qual, por default, pode ser encontrado no diretório WEB-INF/classes sob o nome ApplicationResources.properties. Se tal pacote de recursos não existe em sua aplicação, crie um arquivo de texto com esse nome.

Entradas no pacote de recursos são simplesmente pares chave=valor. Para o exemplo de configuração abaixo, podemos ter o seguinte conteúdo:

error.loginname.required=Por favor informe seu login error.password.required=Informada senha em branco ou com menos de 4 caracteres

O último passo é permitir ao framework de validação modificar nossas classes ActionForm e DynaActionForm para fazer uso das classes fornecidas.

Final da struts-config.xml: <form-beans> <form-bean name="loginForm" type="org.apache.struts.validator.DynaValidatorForm"> <form-property name="loginName" type="java.lang.String"/>

Programação WEB 9

Page 128: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

<form-property name="password" type="java.lang.String"/> </form-bean> </form-beans> <action-mappings> <action name="loginForm" path="/login" scope="request" type="action.LoginAction" validate="true" input="/loginStruts.jsp"> <forward name="success" path="/success.jsp"/> <forward name="failure" path="/failure.jsp"/> </action> <action path="/Login" forward="/loginStruts.jsp"/> </action-mappings> <message-resources parameter="ApplicationResource"/> <plug-in className="org.apache.struts.validator.ValidatorPlugIn"> <set-property property="pathnames" value="/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml"/> </plug-in>

Final da LoginAction.java:package action;import javax.servlet.http.*;import org.apache.struts.action.*;public class LoginAction extends Action { @Override public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { DynaActionForm dForm = (DynaActionForm) form; String loginName = (String) dForm.get("loginName"); String password = (String) dForm.get("password"); if (!(loginName.equals("JEDI") && password.equals("JEDI"))) { return mapping.findForward("failure"); } HttpSession session = request.getSession(); session.setAttribute("USER", loginName); return mapping.findForward("success"); }}

Final da LoginStruts.jsp:<%@page contentType="text/html" pageEncoding="UTF-8"%><%@ taglib uri="http://jakarta.apache.org/struts/tags-html" prefix="html" %><html:html> <head> <title>Login Page</title> </head> <body> <h1> Login Page </h1> <br/> <html:form action="/login"> User Name:

Programação WEB 10

Page 129: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

<html:text property="loginName"/> <html:errors property="loginName"/> <br/> Password: <html:password property="password"/> <html:errors property="password"/> <br/> <html:submit/> </html:form> </body></html:html>

Programação WEB 11

Page 130: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

4. TilesOutro framework que trabalha especialmente bem com Struts é o framework Tiles. Usando Tiles, podemos facilmente definir templates e instâncias de telas os que podemos usar em nossa aplicação.

4.1. O que são templates?

De forma simples, uma página de template é um modelo projeto de desenho de página que pode ser reusado por qualquer outra página em sua aplicação. Fazer uso de templates fornece à sua aplicação um look and feel mais consistente.

Para melhor entender o conceito de templates, vamos dar uma olhada em algumas páginas de uma aplicação WEB:

Figura 1: Página exemplo de uma aplicação WEB

Figura 2: Página exemplo de uma aplicação WEB

Programação WEB 12

Page 131: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

Navegando nas páginas, podemos notar que há vários elementos de projeto comuns a todas elas. Todas compartilham o mesmo conjunto de links de navegação à esquerda, o mesmo conjunto de imagens na parte superior da página e o mesmo conjunto de imagens nos links na parte inferior da página. O que está diferente entra as páginas é apenas o conteúdo apresentado no meio desta.

Imagine como faríamos para implementar um conjunto de páginas similares. Se fôssemos implementar cada página tal qual como as vemos, isto é, exatamente uma página correspondendo para cada uma das telas que vemos, teríamos problemas com a sua manutenção no futuro. Isto ocorreria, pois ocorre uma grande quantidade de código duplicado – se mais tarde alguém decidisse que as imagens de cima não estão muito boas ou que o menu de navegação à esquerda não está suficientemente intuitivo, as mudanças teriam que ser reproduzidas manualmente por todas as páginas.

Como desenvolvedores que somos, sabemos o suficiente para distribuir em entidades separadas código que poderia ser aproveitado através da aplicação. Este conceito também pode ser aplicado nas páginas JSP/HTML: os links de navegação podem ser implementados em uma página, as imagens do cabeçalho em outra, assim como o rodapé. Estas páginas podem, então, ser adicionadas à página que implementaria o conteúdo do corpo do texto. Isto pode ser realizado sem o uso de qualquer framework, usando a ação <jsp:include> que discutimos na leitura básica de JSP.

Uma solução melhor seria ter o conteúdo do texto em um fragmento JSP separado, criar uma página JSP que defina a formatação com o layout padrão das páginas e simplesmente deixar espaços reservados para outros elementos que venham a ser incluídos na página. Um desenvolvedor que esteja implementando uma tela teria apenas que fazer uso desta página “modelo” e definiria uma página de conteúdo que seria inserida no devido espaço reservado.

Os benefícios desta solução são óbvios: quaisquer mudanças que teriam que ser feitas em um aspecto da camada de apresentação, digamos, a página de cabeçalho, seria aplicado em todas as páginas ao modificarmos uma única página. Se quiser alterar o layout do seu sítio, enquanto ainda mantém o seu conteúdo, isto poderia ser feito simplesmente alterando a página de modelo.

Aprenderemos como fazer esta modelagem usando o framework Tiles.

4.2. Preparando Tiles

Antes que possamos nos beneficiar do que o framework Tiles pode oferecer, precisamos realizar alguns passos necessários.

1. Adicionar as seguintes linhas ao struts-config.xml imediatamente antes do elemento de fechamento </struts-config>:

<plug-in className="org.apache.struts.tiles.TilesPlugin" > <set-property property="definitions-config" value="/WEB-INF/tiles-defs.xml" /> <set-property property="moduleAware" value="true" /></plug-in>

Isto informa ao Struts que queremos fazer uso do Tiles e o arquivo de configuração que será lido pode ser achado no diretório /WEB-INF, com o nome tiles-defs.xml.

2. Copiar os arquivos struts-tiles.tld e tiles-config.dtd do diretório lib do Struts para o diretório /WEB-INF da nossa aplicação. O primeiro arquivo contém informações das tags personalizadas que precisaremos usar para o Tiles, e o segundo define a estrutura do arquivo de configuração tiles-defs.xml.

3. Criar um arquivo de configuração em branco que será preenchido mais tarde.

4. Criar um arquivo padrão xml com o nome tiles-defs.xml e salvá-lo no diretório WEB-INF. Com o seguinte conteúdo:

<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE tiles-definitions PUBLIC

Programação WEB 13

Page 132: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

"-//Apache Software Foundation//DTD Tiles Configuration 1.1//EN""http://jakarta.apache.org/struts/dtds/tiles-config_1_1.dtd"><tiles-definitions> </tiles-definitions>

Após a realização desses passos, o Tiles está pronto para ser usado.

4.3. Criando um modelo de Layout

O primeiro passo na construção de um modelo é a identificação dos componentes que serão colocados nele. Por exemplo, a maioria das páginas WEB têm um cabeçalho, rodapé, barra de menu e corpo.

Se fôssemos desenhar um esboço rápido, tal layout se pareceria com o do diagrama a seguir.

Figura 3: Layout básico

Para criar uma página de modelo implementando este layout, siga estes passos:

1. Crie uma nova página JSP (/layout/basicLayout.jsp).

2. Importe a biblioteca de tags do Tiles.

3. Crie o HTML implementando o layout, deixando em branco a implementação dos componentes reais.

4. Use a tag <tiles:insert> para agir como espaços reservados para os componentes do layout.

O HTML que implementa o layout pode ser simples como uma tabela, com os componentes modelados como células da tabela conforme mostrado no JSP abaixo:

<%@ taglib uri="http://jakarta.apache.org/struts/tags-tiles" prefix="tiles" %> <HTML> <HEAD> <TITLE><tiles:getAsString name="title"/></TITLE> </HEAD> <BODY> <TABLE border="0" width="100%" cellspacing="5"> <TR> <TD colspan="2"><tiles:insert attribute="header"/></TD> </TR><TR> <TD width="140" valign="top"><tiles:insert attribute="menu"/></TD> <TD valign="top" align="left"><tiles:insert attribute="body"/></TD> </TR><TR> <TD colspan="2"><tiles:insert attribute="footer" /></TD> </TR> </TABLE> </BODY> </HTML>

Programação WEB 14

Page 133: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

Há duas tags personalizadas no exemplo acima: <tiles:insert> e <tiles:getAsString>.

• <tiles:insert> - tem vários usos no Tiles. Neste caso, inserir um fragmento JSP que se refere ao nome fornecido no atributo attribute. O fragmento corresponde ao nome será especificado na definição de tela que fará uso deste modelo.

• <tiles:getAsString> - esta tag recupera, como tipo String, um valor fornecido em uma definição de tela usando o nome no atributo name.

4.4. Criando definições de tela

Assim que tivermos um modelo, podemos fazer uso deste para definirmos completamente uma tela. A criação de definições de tela pode ser feito de duas maneiras no framework Tiles: as definições podem ser definidas dentro de páginas JSP, ou dentro de um arquivo XML reconhecido pelo framework.

4.4.1.Criando uma definição usando páginas JSP

A criação de uma definição dentro de uma página JSP faz uso de algumas tags personalizadas fornecidas pelo Tiles:

• <tiles:definition> - tag base usada para definir uma tela. O valor do atributo id é o nome pelo qual esta definição pode ser referenciada pelos outros componentes. O valor do atributo page é a localização do arquivo de modelo que será usado como base.

• <tiles:put> - esta tag é usada para associar um valor a um atributo específico dentro de um modelo. O atributo name especifica o nome da localização de destino dentro do modelo. O atributo value define o texto ou a localização do fragmento JSP a ser usado.

Considere o exemplo abaixo: <%@ taglib uri="http://jakarta.apache.org/struts/tags-tiles" prefix="tiles" %> <tiles:definition id="welcomePage" page="/layout/basicLayout.jsp"> <tiles:put name="title" value="Welcome!"/> <tiles:put name="header" value="/header.jsp"/> <tiles:put name="footer" value="/footer.jsp"/> <tiles:put name="menu" value="/menu.jsp"/> <tiles:put name="body" value="/welcome.jsp"/> </tiles:definition>

Neste exemplo, criamos uma definição de tela para uma página de boas vindas. Ela faz uso do layout que definimos previamente e coloca título, cabeçalho, rodapé, menu e componentes de texto especificados em locais definidos no modelo.

Por padrão, esta definição é visível apenas dentro da página JSP contendo a declaração. Para mudar isto, podemos fornecer um valor ao atributo opcional scope da tag <tiles:definition>. Os valores possíveis são: page, request, session e application.

4.4.2.Criando uma definição usando um arquivo de configuração XML

A segunda maneira de se criar uma definição de tela é colocando uma entrada de configuração no arquivo de configuração tiles-defs.xml que criamos inicialmente. A sintaxe para se criar esta entrada é muito parecida com a <tiles:definition> usada previamente:

<!DOCTYPE tiles-definitions PUBLIC "-//Apache Software Foundation//DTD Tiles Configuration 1.1//EN" "http://jakarta.apache.org/struts/dtds/tiles-config_1_1.dtd"> <tiles-definitions> <definition name="welcomePage" page="/layout/basicLayout.jsp"> <put name="title" value="Welcome!"/> <put name="header" value="/header.jsp"/> <put name="footer" value="/footer.jsp"/> <put name="menu" value="/menu.jsp"/> <put name="body" value="/welcome.jsp"/> </definition>

Programação WEB 15

Page 134: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

<!-- ... mais definições ... --> </tiles-definitions>

Qual é a diferença entre os dois métodos? Ambos os métodos carregam a definição como um JavaBean na memória. Entretanto, o método JSP apenas carrega a definição depois que o fragmento JSP que a contém tenha sido acessado e, por padrão, torna-o visível apenas na mesma página. Esta condição torna a reutilização da definição de tela pela aplicação um pouco mais problemática, já que deve-se tomar um cuidado extra para evitar a carga e a descarga da definição de, e para a memória, desnecessariamente.

O método XML, por outro lado, faz com que a definição de tela esteja disponível para toda a aplicação imediatamente após a inicialização. O Tile cuida da persistência em memória. A única coisa que os componentes precisam para fazer uso da definição é que seja fornecido o seu nome.

Outro benefício da utilização do método XML para a criação de definição de tela é que as próprias definições podem ser usadas como ActionForwards pelo framework Struts. Isto pode reduzir drasticamente o número de páginas JSP necessárias à sua aplicação.

4.4.3.Usando as definições de tela

A criação da definição de tela não é suficiente para que ela seja exibida ao usuário. Para colocar uma definição em uso, podemos usar a tag <tiles:insert> e fornecer o nome da definição que deve ser mostrada:

<%@ taglib uri="http://jakarta.apache.org/struts/tags-tiles" prefix="tiles" %> <tiles:insert beanName="welcomePage" flush="true"/>

A página acima é tudo o que é necessário para exibir a tela de boas vindas ao usuário.

Um dos problemas desta abordagem é que aumenta o número de páginas JSP necessárias para mostrar telas diferentes ao usuário: com exceção do componente com o corpo do texto, cada tela requereria uma página separada que faria uso da definição de tela. Para aplicações que requerem 100 ou mais telas, o número de páginas que teriam que ser criadas é o dobro!

Uma melhor abordagem é fazer uso das definições como os alvos do ActionForwards. Incluindo o Tiles como um plug-in para o Struts (um dos passos preparatórios que realizamos). Struts torna ciente das definições de tela criadas usando o Tiles. O nome das telas pode ser usado no lugar de localizações reais nas tags <forward>. Considere o exemplo abaixo:

<action-mappings> <action name="loginForm" path="/login" scope="request" type="login.LoginAction" validation="true"> <forward name="success" path="/welcomePage"/> <forward name="failure" path="/failure.jsp"/> </action> </action-mappings>

Aqui modificamos o nosso exemplo anterior do struts-config.xml tal que a condição lógica de sucesso é mapeada para a nossa definição de welcomePage. Como podemos ver, esta abordagem é simples, fácil de usar, e diminui o número de páginas JSP que necessitariam ser criadas.

4.4.4.Estendendo definições

Uma das vantagens do Tiles sobre outros métodos de modelagem é permitir a extensão das definições de tela. A extensão de tela funciona de maneira parecida com a herança de classes em Java: a tela estendida herda todas as propriedades e atributos da definição pai. Isto nos permite criar uma definição base de tela que declara valores padrões para atributos que podem ser estendidas para definições especializadas em páginas específicas.

Tome o seguinte exemplo: <definition name="basicDefinition" page="/layout/basicLayout.jsp">

Programação WEB 16

Page 135: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

<put name="header" value="/header.jsp"/> <put name="footer" value="/footer.jsp"/> <put name="menu" value="/menu.jsp"/> </definition> <definition name="welcomePage" extends="basicDefinition"> <put name="title" value="Welcome!"/> <put name="body" value="/welcome.jsp"/> </definition> <definition name="otherPage" extends="basicDefinition"> <put name="title" value="My title"/> <put name="body" value="/otherContent.jsp"/> </definition>

Criando-se uma tela base que pode ser estendida, evitamos a repetição da definição de valores de atributos. Além disso, quando uma mudança precisa ser feita com relação a qual componente é colocado em um dos atributos base, esta mudança é, imediatamente e automaticamente, propagada para todas as telas simplesmente fazendo a alteração na definição da tela base.

Programação WEB 17

Page 136: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

5. ExercíciosO exercício deste capítulo será construído com base no exercício apresentado na lição anterior.

1. Usar o framework Validator para incluir um código de validação para todos os formulários.

2. Converter todas as ActionForms usadas para o equivalente em DynaActionForm.

3. Implementar as telas definidas usando o framework Tiles. O layout a ser usado é o layout básico definido nesta lição. Esta atividade consiste de várias tarefas:

a) Separar o conteúdo criado previamente para cada página em páginas “corpo” distintas.

b) Criar páginas de barras laterais contendo uma lista de links aplicáveis para cada subseção da aplicação. Por exemplo, se o usuário é um administrador e está na Página de Conteúdo Principal, deve ser capaz de ver links para a página de Gerenciamento de download de material, Gerenciamento de Usuário e Atividades do Usuário, assim como um link que permita ao usuário sair da sua seção da aplicação.

De modo geral, as barras laterais devem ser implementadas tal que:

• Apresentar um link para a página pai conforme definido na definição de fluxo de tela no exercício anterior.

• Apresentar links para o próximo conjunto de páginas que podem ser acessadas de acordo com o mesmo fluxo de telas.

• Apresentar um link que permita ao usuário sair da sua seção da aplicação.

c) Criar definições Tiles para cada uma das telas da aplicação e usá-las no lugar de ActionForwards.

Programação WEB 18

Page 137: Projeto JEDI - Programação para WEB - Java - 178 páginas

Módulo 6Programação WEB

Lição 9MVC Frameworks II – JavaServer Faces

Versão 1.0 - Nov/2007

Page 138: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

1. ObjetivosNa lição anterior, vimos o framework Struts, um sistema open-source (código aberto) para aplicações WEB que implementa a estrutura MVC-2. Nesta lição iremos analisar outro framework MVC: JavaServer Faces (JSF)

Ao final desta lição, o estudante será capaz de:

• Entender a estrutura MVC para a JSF

• Visão de outros componentes de tags JSF

Programação WEB 4

Page 139: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

2. Introdução ao Framework JSFJSF é um framework para a construção de interfaces de usuário para aplicações da WEB. É construído sobre o conceito introduzido pelo Struts e traz consigo os benefícios de uma estrutura que separa claramente a lógica de negócio em um padrão baseado em componentes de interface do usuário parecido em muitos maneiras com a Swing.

Abaixo está a figura detalhando como o sistema funciona:

Figura 1: Framework JavaServerFaces (imagem do livro Mastering JavaServerFaces, Wiley Publishing)

Programação WEB 5

Page 140: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

Como se pode ver, JSF também tem uma clara separação entre os componentes para a camada Model, View e Controller. Como Struts, JSF tem uma servlet front controller, chamado FacesServlet, responsável por receber solicitações de clientes e então executar as ações apropriadas necessárias que são ditadas pelo sistema. Outra semelhança entre Struts e JSF é que ambos fazem uso das Actions Handlers separada do servlet front controller embora JSF reconheça essa pequena diferença quando comparada ao Struts.

JSF e Struts agem similarmente em relação à camada View. Struts fornece apenas um conjunto de biblioteca de tags que agregam funcionalidades HTML padrão. JSF, por outro lado, fornece seu próprio conjunto de componentes e interfaces, juntamente com uma biblioteca de tags para exibir estes componentes como tags e um componente de interpretação que traduz componentes gráficos em HTML.

Vamos examinar os diferentes aspectos de JSF.

2.1. Controller

A camada Controller de JSF é feita por uma Servlet Controller denominada FacesServlet, um conjunto de arquivos de configuração XML e um conjunto de Actions Handlers.

2.1.1.FacesServlet

É responsável por aceitar solicitações de clientes e executar operações necessárias para produzir a resposta. Estas operações incluem preparar os componentes gráficos requeridos pela solicitação, atualizando o estado dos componentes, passar as Actions Handlers requeridas e traduzir os componentes gráficos que são parte da resposta.

Fornecido pelo sistema, JSF requer apenas configurações em um descritor das aplicações, antes que esteja pronto para ser utilizado.

A listagem a seguir, mostra um fragmento de como configurar o FacesServlets para a aplicação....<servlet> <servlet-name>FacesServlet</servlet-name> <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> ... <servlet-mapping> <servlet-name>FacesServlet</servlet-name> <url-pattern>*.jsf</url-pattern> </servlet-mapping> ...

2.1.2.Action Handlers

Foi mencionado anteriormente, que JSF faz uso de Actions Handlers independente da camada controller (front controller servlet), semelhante ao Struts. JSF executa essa função de modo diferente.

Em JSF, existem dois modos possíveis para se criar uma Action Handler. O primeiro vincula um método Java para servir como Action Handler. E o segundo, cria uma instância implementando a interface ActionListener.

2.1.3.Método Application

Método que é vinculado a um componente gráfico para servir como Action Handler, é chamado um método Application. Mais tarde, na camada View, veremos como esta ligação é realizada.

Existem algumas regras que devem ser observadas para se criar um método Application:

• O método deve ser declarado como público

• O método não deve ter parâmetros

Programação WEB 6

Page 141: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

• O tipo de retorno do método deve ser um objeto do tipo String

A seguir, temos um método que podemos usar para reconhecer um evento resultante na tentativa de identificação de um usuário.

... public String performLogin() { // Se o nome não foi informado if (loginName == null || loginName.length() < 1) { return "failure"; } // Criar um objeto de negócio para verificar a autorização User user = null; UserService service = new UserService(); user = service.login(loginName, password); // Caso o usuário seja null retorna não autorizado if (user == null) { return "failure"; } // Retorna uma instância de FacesContext FacesContext ctx = FacesContext.getCurrentInstance(); // Resultado de uma seção para usar outros componentes Map sessionMap = ctx.getExternalContext().getSessionMap(); sesionMap.put(ApplicationConstants.USER_OBJECT, user); // Login completo com sucesso return "success"; } ...

Uma das vantagens neste tipo de abordagem, é a diminuição do número de objetos que os desenvolvedores necessitam manter.

Este método pode estar em qualquer JavaBean reconhecido pelo sistema, embora possa ser encontrado no programa e ser utilizado como um backing model para a página. Mais a frente, iremos abordar sobre backing model.

O objeto do tipo String retornado pelo método informa ao FacesServlet o que será mostrado pelo usuário. Objetos Strings são nomes lógicos e algumas vezes chamados outcomes. Estes outcomes são verificados através das regras de navegação definidas no arquivo de configuração.

2.1.4.Trabalhando com o escopo Session na JSF

JSF tem um método diferente para acessar um contexto de sessão. Em exemplos anteriores vimos que devemos restaurar o objeto instanciado da classe HttpSession para manipular um contexto de sessão. Por exemplo, em Struts, registramos um objeto HttpServletRequest passado como parâmetro para executar um determinado método.

Visto que os métodos Application são definidos de modo que não levam em conta nenhum parâmetro, não existe nenhum arquivo válido com um objeto HttpServletRequest para restaurar um objeto HttpSession. JSF contorna esta limitação fornecendo o acesso ao contexto da sessão (ou outros contextos) com o uso de um objeto instanciado da classe FacesContext.

Em demonstrações anteriores salvamos um objeto no contexto de sessão. Em JSF utilizamos as seguintes instruções reproduzidas a seguir.

FacesContext ctx = FacesContext.getCurrentInstance(); ... Map sessionMap = ctx.getExternalContext().getSessionMap(); sessionMap.put(ApplicationConstants.USER_OBJECT, user);

Como podemos observar neste exemplo, obter um arquivo válido de um objeto FacesContext é simples. Chamamos um método estático da classe FacesContext. Um quadro representativo de

Programação WEB 7

Page 142: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

objetos posicionados em um contexto de sessão pode então ser retomado pelo usuário.

2.1.5.ActionListener

O outro modo de executar um action handler na JSF é criar uma implementação da interface ActionListener. Esta interface define um único método:

public void processAction(ActionEvent event)

A ActionEvent passada como um parâmetro no método fornece uma implementação de acesso para o componente que dispara o evento. É semelhante ao modo como os eventos funcionam em programação Swing.

Abaixo está um exemplo de implementação da Action Listener utilizado para as ações de entrada do usuário.

public class PerformedActionListener implements ActionListener { public void processAction(ActionEvent event) { // Retornar um componente que dispara um evento UICommand component = (UICommand)event.getComponent(); // Retornar o nome de um botão ou um link String commandName = (String)component.getValue(); // Criar um objeto de negócio LoggingService service = new LoggingService(); // Operação de login service.logUserAction(commandName); } }

Na maior parte do tempo, é mais apropriado utilizar métodos de aplicação para servir como Action Handlers. Primeiro, podem estar localizados no mesmo contexto que serve como backing model de um formulário, e assim ter acesso mais fácil aos dados fornecidos pelo usuário. Segundo, estar no backing model permite o desenvolvimento do grupo juntamente com os dados e os métodos capazes de retornar outcomes que informam a FacesServlet a próxima exibição da tela. ActionListeners podem trazer apenas o usuário para a página original após reconhecer o evento. No entanto, ActionListeners são a escolha mais apropriada caso se tenha uma determinada funcionalidade que será utilizada novamente através de múltiplos códigos de ação.

Veremos mais adiante a possibilidade em se ter um método de aplicação e um ou mais ActionListeners para atuar como handlers para uma action em particular. É então possível obter aspectos melhores de ambos os acessos e ter um método de aplicação para executar o gerenciamento específico de uma ação.

2.1.6.faces-config.xml

Serve como um arquivo de configuração primária para a camada controller da JSF. Ao contrário de uma aplicação em Struts. JSF não contém qualquer configuração de entrada para as Actions Handlers. Não contém configurações de entrada para as regras de navegação bem como para os JavaBeans que serão reconhecidos pelo sistema.

A seguir temos uma amostra de tais arquivos de configuração para uma aplicação de implementação de um caso de uso para a entrada em uma aplicação.

<faces-config version="1.2" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd">

<managed-bean> <description> This bean serves as the backing model for our login form </description> <managed-bean-name>loginPage</managed-bean-name>

Programação WEB 8

Page 143: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

<managed-bean-class>sample.LoginPageBean</managed-bean-class> <managed-bean-scope>request</managed-bean-scope> <managed-property> <description> The property that will store the user's login name </description> <property-name>loginName</property-name> <null-value/> </managed-property> <managed-property> <description> The property that will store the user's password </description> <property-name>password</property-name> <null-value/> </managed-property> </managed-bean> <navigation-rule> <from-view-id>/login.jsf</from-view-id> <navigation-case> <description> Any failure result should bring the user to an error page </description> <from-outcome>failure</from-outcome> <to-view-id>/error.jsp</to-view-id> </navigation-case> <navigation-case> <description> Successful login should bring user to welcome page </description> <from-outcome>success</from-outcome> <to-view-id>/welcome.jsp</to-view-id> </navigation-case> </navigation-rule> </faces-config>

Vejamos alguns dos elementos acima:

<!DOCTYPE ...

Usado para indicar que este arquivo serve como um arquivo de configuração JSF. Qualquer falha para incluir essa linha, ou um tipo irreconhecido, resultará em um erro.

<faces-config>

Serve como um elemento raiz para o arquivo XML. Todos os outros elementos deverão ser filhos desse.

<managed-bean>

Cada elemento managed-based serve para definir um JavaBean que será controlado e reconhecido pelo sistema. Possui os seguintes elementos filhos:

• <description> - Facilita a leitura e a compreensão dos arquivos de configuração• <managed-bean-name> - Nome lógico pelo qual uma instância pode ser acessada ou

usada dentro de um sistema. Deve ser único para cada classe• <managed-bean-class> - Qualifica totalmente o nome do JavaBean gerenciado• <managed-bean-scope> - Escopo no qual o componente deve ser armazenado. Pode ser

request, session, application ou pode ser deixado em branco. Não ter um valor significa que o estado do arquivo será armazenado como request

• <managed-property> - Declara o valor a ser usado para iniciar propriedades no JAVABEAN. Tem os seguintes elementos filhos:• <property-name> - Nome do JavaBean a ser gerenciado.• <property-class> - Define o tipo, completamente qualificado, da propriedade (elemento

opcional)

Programação WEB 9

Page 144: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

• <null-value/> - Define um valor da propriedade como nulo, ao invés de usar o elemento value como um valor null-value

• <value> - Define um valor específico para a propriedade para um valor

<navigation-rule>

Este elemento é usado para definir mapeamentos lógicos para um momento decisivo na sua aplicação. Podem também ser usados para definir regras específicas para uma página em particular ou definir as regras que podem ser usadas por qualquer página na aplicação. Possui os seguintes elementos filhos:

• <from-view-id> - Define a página para o qual esta regra se aplicará. Se não especificada essa regra se aplicará para toda a aplicação (opcional)

• <navigation-case> - Define o resultado para a regra de navegação. Tem os seguintes elementos filhos (como conseqüência):

• <from-outcome> - Define o resultado que determina uma ação que tornará a navegação ativa

• <to-view-id> - Define a próxima página que será exibida se a navegação se tornar ativa

Existem outros elementos disponíveis para o arquivo de configuração da JSF. Consulte sua documentação de implementação JSF para obter maiores detalhes.

2.1.7.Sumário para realizar uma camada Controller

Passos para a configuração:

● Configurar o FaceServlet para usar em sua aplicação.

● Para cada página WEB contendo os componentes JSF UI:

● Criar uma configuração de entrada para o programa gerenciado que servirá como o modelo backing model da página.

● Criar regras de navegação que definem onde a aplicação poderia ir para a próxima parte.

2.2. Model

Em JSF é necessário que se tenha áreas que armazenarão o estado dos componentes visuais em cada uma das páginas. Estas áreas são chamadas de backing model. Estas áreas não são parte da camada Model observadas estritamente abaixo da perspectiva na estrutura MVC. MVC define a camada Model para ser o conjunto de áreas que implementam um centro lógico para os assuntos da aplicação. Entretanto, quando pensa apenas nos componentes visuais, faz sentido chamar estas áreas de Model, especialmente se compararmos com a implementação MVC de áreas dos componente gráficos de Swing. Relembrando, em Swing, a camada de tradução é a camada View, o estado dos componentes é a camada Model e a ação da parte gerenciadora de eventos é a camada Controller.

Embora sejam consideradas como parte da Model, deve-se tomar cuidado no desenvolvimento destas áreas, de tal modo que elas não influenciem na funcionalidade central de sua aplicação (o modelo real). É melhor manter em mente que estes componentes são feitos para se guardar os componentes gráficos e podem ser utilizados para definir as operações básicas que acessam os dados armazenados que podem servir como métodos de aplicação. Não são feitos para executar processo pesado de regras de negócio, ou qualquer processo que pode ser usado novamente em outras aplicações.

é fácil criar um backing model para a página contendo componentes gráficos JSF. Criar um JavaBean com propriedades correspondentes para cada componente na página. São muito semelhantes aos objetos ActionForms do Struts, com a exceção de não ser necessário estender a base fornecida pelo sistema.

A seguir temos um exemplo de um backing model para um formulário login:

Programação WEB 10

Page 145: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

public class LoginPageBean { private String loginName; private String password; public String getLoginName() { return loginName; } public void setLoginName(String loginName) { this.loginName = loginName; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }

Esta camada model, pode, então ser acessada pelas páginas após ter sido configurada no arquivo faces-config.xml. A configuração de entrada para este arquivo é detalhada a seguir.

<managed-bean> <description> This bean serves as the backing model for our login form </description> <managed-bean-name>loginPage</managed-bean-name> <managed-bean-class>sample.LoginPageBean</managed-bean-class> <managed-bean-scope>request</managed-bean-scope> <managed-property> <description> The property that will store the user's login name </description> <property-name>loginName</property-name> <null-value/> </managed-property> <managed-property> <description> The property that will store the user's password </description> <property-name>password</property-name> <null-value/> </managed-property> </managed-bean>

2.3. View

A camada View é, indubitavelmente, onde JSF deixa sua marca. Nesta camada, não apenas temos uma biblioteca de tags, para utilizar com JSP, como também obtemos um conjunto de componentes e uma API padronizada para seu acesso e manipulação.

Discussões de componentes Java podem ser muito complexas, se tentarmos explicar tudo ao mesmo tempo. Ao invés disso, lidamos com os fundamentos básicos e incorporamos idéias mais complicadas à medida que avançamos.

2.3.1.Integração JSF-JSP

Podemos iniciar a View dos componentes JSF pelo usuário. Seguindo uma lista de páginas JSP contendo componentes JSF baseados no exemplo anteriormente visto, temos:

<%@taglib uri ="http://java.sun.com/jsf/core/" prefix="f" %> <%@taglib uri ="http://java.sun.com/jsf/html" prefix="h" %> <HTML> <TITLE>Login Page</TITLE> <BODY> Please login with your login name and password : <br/><br/>

Programação WEB 11

Page 146: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

<f:view> <h:form id="simpleForm"> <h:outputLabel for="loginName"> <h:outputText value="Login Name:"/> </h:outputLabel> <h:inputText id="loginName" value="#{loginPage.loginName}"/><br/> <h:outputLabel for="password"> <h:outputText value="Password : "/> </h:outputLabel> <h:inputSecret id="password" value="#{loginPage.password}"/><br/> <h:commandButton action="#{loginPage.performLogin}" value="Login"/> </h:form> </f:view> </BODY> </HTML>

Para exibirmos componentes JSF em nossas páginas JSP. Precisamos incluir duas bibliotecas de tags: core e html.

A biblioteca core define as funcionalidades básicas, tal como, gerenciar os componentes JSF que sejam capazes salvar o estado. A biblioteca html define tags que ordenam o navegador como criar os componentes JSF dentro dos seus equivalentes em HTML. Uma vez que incluímos estas duas bibliotecas de tags, realizaremos o trabalho do mesmo modo. Olharemos para as tag utilizadas no exemplo anterior:

• <f:view> Definido na biblioteca core. Todas os tags dos componentes JSF devem ser inseridas dentro desta tag. Fornece um local para implementações JSF serem capazes de salvar o estado dos componentes

• <h:form> - Definido na biblioteca HTML. Cria um formulário em HTML• <outputLabel> - Define um componente label que é associado a outro componente JSF.

Este componente é associado a outro que recebe o valor do usuário, exibe como seu label o emissor na tag <outputText>

• <h:outputText> - Cria uma tag de texto dentro do valor atribuído dentro do seu equivalente HTML

• <h:inputText> - Cria um elemento HTML para receber informações do tipo texto• <h:inputSecret> - Criar um elemento HTML do tipo de senha• <h:commandButton> - Criar um botão HTML, por padrão SUBMIT

A seguir veremos a página resultante da JSF anterior.

Figura 2: HTML da página de login

Esta página produz o seguinte código HTML:<HTML> <TITLE>Login Page</TITLE> <BODY> Please login with your login name and password : <br/><br/> <form id="simpleForm" method="post" action="/JSFApplication/index.jsf"

Programação WEB 12

Page 147: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

enctype="application/x-www-form-urlencoded"> <label for="simpleForm:loginName"> Login Name: </label> <input id="simpleForm:loginName" type="text" name="simpleForm:loginName" /><br/> <label for="simpleForm:password"> Password : </label> <input id="simpleForm:password" type="password" name="simpleForm:password" value="" /> <br/> <input type="submit" name="simpleForm:_id5" value="Login" /> <input type="hidden" name="simpleForm" value="simpleForm" /></form> </form> </BODY></HTML>

Podemos ver na página HTML mostrada, como a implementação JSF cria os complementos como definidos na biblioteca de tags.

Os elementos do formulário são definidos para utilizar o método POST quando na ação de submissão do formulário, apontando para uma action point na mesma página. Também, notaremos que os valores id dos elementos HTML devem ser informados com o nome do formulário. Isto assegura que o nome dos elementos do formulário são únicos dentro da aplicação, que é necessário para as operações JSF.

2.3.2.Value Binding

São os componentes gráficos que geram a exibição requerem um backing model seja capaz de armazenar dados que os usuários informarão. A implementação destes backing models foi discutida anteriormente. A única questão que resta é como conectar os componentes aos backing models.

...<h:inputText id="loginName" value="#{loginPage.loginName}"/><br/> <h:outputLabel for="password"> <h:outputText value="Password : "/> </h:outputLabel> <h:inputSecret id="password" value="#{loginPage.password}"/><br/> ...

A notação # serve como valor para o tipo atribuído e liga as propriedades ao nosso JavaBean loginPage com os nossos componentes visuais. Como nosso componente texto é unido ao loginName apropriado e o componente InputSecret é ligado a senha apropriada. Neste caso, LOGINPAGE refere-se a uma instancia de loginPage que armazenará os dados.

A página JSF é capaz de associar o identificador loginPage ao arquivo loginPageBean devido a nossa entada no faces-config.xml. A entrada relevante é apresentada abaixo.

Assim que a LoginPageBean é declarada para ser um arquivo gerenciado no sistema, um arquivo de LoginPageBean será criado e instalado na configuração armazenada (se uma delas já não existir) quando a página JSF é avaliada. Na page submission, os valores que o usuário anotou seriam automaticamente instalados dentro das propriedades do arquivo.

2.3.3.Registrando Action Handlers para os componentes da View

JSF introduz o conceito de programação baseada em eventos para o ambiente WEB. Alguns dos componentes gráficos fornecidos pela JSF, a partir de uma ação apropriada durante a entrada do usuário e gera eventos que podem ser processados pelas Actions Handlers.

Em nosso exemplo anterior, temos um componente JSF <h:commandButton>. Em qualquer momento que o usuário pressionar o botão, que este componente representa, um evento é gerado dentro do sistema e pode ser processado pela Registered Handlers.

Este conceito pode ser melhor entendido se comparado com outro modelo de programação

Programação WEB 13

Page 148: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

baseado em eventos: Swing. Em Swing, ao executar alguns processos, em qualquer momento, o usuário pressiona um botão e registra uma chamada, um ActionListener, que implementa uma determinada funcionalidade associada. De maneira semelhante será com JSF, para executar algumas funcionalidades relacionadas com o pressionamento em um botão, registramos Actions Handlers com o componente commnandButton.

O modo que fazemos este registro de um handler para um componente commandButton é mostrado a seguir.

...<h:commandButton action="#{loginPage.performLogin}" value="Login"/> ...

Encontramos uma notação # semelhante a que usamos para conectar uma propriedade a um componente visual. Esta ação é semelhante a quando estamos conectando um método chamado PerformLogin encontrado em uma classe referenciada com o nome LoginPage ao botão. Nesse momento, ao invés de armazenar o valor de um componente, o método bound atua como um Action Handler para o pressionamento do botão.

2.3.4.Navegação de Página

JSF determina a próxima tela que será exibida para o usuário após a submissão do formulário utilizando um valor de retorno do método que serve como um Action Handler para o botão apresentado. Um valor do tipo String é visto nas regras de navegação definidas dentro do arquivo de configuração da JSF.

A entrada relevante na configuração de arquivo é mostrada a seguir.<navigation-rule> <from-view-id>/login.jsf</from-view-id> <navigation-case> <description> Any failure result should bring the user to an error page </description> <from-outcome>failure</from-outcome> <to-view-id>/error.jsp</to-view-id> </navigation-case> <navigation-case> <description> Successful login should bring user to welcome page </description> <from-outcome>success</from-outcome> <to-view-id>/welcome.jsp</to-view-id> </navigation-case> </navigation-rule>

Então, na página JSF de entrada (login.jsf), se o método performLogin retornar um resultado failure, o usuário será redirecionado para a página error.jsp.

Como alternativa para usar uma Action Handler, também é possível fornecer estaticamente uma regra de navegação a ser chamada:

... <h:commandButton action="failure" value="Login"/> ...

Pressionar este botão redirecionará o usuário para a página error.jsp sem que seja necessário fornecer qualquer processo.

Programação WEB 14

Page 149: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

3. Outros componentes de tags JSFExistem ainda muitas outras ações disponíveis dentro da JSF, a partir do uso de suas tags, que usamos em nossos exemplos anteriores para criar componentes HTML.

<h:messages>

Esta tag atua em um modo semelhante à tag <html:errors/> no arquitetura Struts. Apresenta mensagens da aplicação dentro do texto na página. Por exemplo, se um controle receptor informar um problema interno, possivelmente devido a um erro de validação, pode ser exibido usando esta tag.

<h:dataTable>

Esta tag exibe uma tabela em HTML (correspondente a tag <table>). O que torna esta tag útil é obter uma informação a partir de um banco de dados (ou através de uma coleção de objetos) mostrá-a automaticamente, criando os elementos necessários <tr> e <td>. Estas últimas são definidas de acordo com uma tag filha denominada <h:column>.

Inserido dentro de uma tag form. O modelo segue que cada linha corresponde a uma linha da tabela.

<h:dataTable id="myTable" value="#{personBean.personArray}" var="person" border="1" cellspacing="1" rowClasses="myTableRow"> <h:column> <h:outputText value="#{person.firstName}"/> </h:column> <h:column> <h:outputText value=#{person.lastName}"/> </h:column></h:dataTable>

Este poderia ser o exemplo do HTML gerado (depende dos dados contidos em myTable).<table border="1" cellspacing="1"> <tr class="myTableRow"> <td>Ewan</td> <td>Coba</td> </tr> <tr class="myTableRow"> <td>Devon</td> <td>Shire</td> </tr> <tr class="myTableRow"> <td>California</td> <td>San Diego</td> </tr></table>

Estes são alguns atributos da tag <h:dataTable>:

• value - Define a coleção ou banco de dados a ser pesquisado. Isto poderia ser também uma propriedade dentro de um programa gerenciado por um método que retorna o conteúdo necessário.

• var - Define o nome da variável que irá expor o conteúdo da coleção ou banco de dados sendo pesquisado. Esta variável é visível apenas dentro do caractere <h:dataTable>. Uma vez fora do corpo deste caractere, esta variável não pode mais ser acessada.

• border, cellspacing – Atributos padrão de tabelas HTML. O valor estabelecido nestes atributos será transmitido à tag table gerada.

• rowClasses – Uma lista, separada por vírgulas, de tipos para o estilo CSS (Cascading Style Sheets) que podem ser alternadamente aplicados para cada linha da tabela.

<f:verbatim>

A tag <h:dataTable> permite apenas tags JSF dentro dos seus corpos. Isso significa que as tags

Programação WEB 15

Page 150: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

padrões ou ainda tags JSTL não funcionam dentro desta. Isso pode ser obtido com uso da tag <f:verbatim>.

Programação WEB 16

Page 151: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

4. ExercíciosUma empresa necessita manter os dados de entrada e saída de seus funcionários. Para realizar isto, precisa de uma aplicação WEB para registrar as movimentações de seus funcionários. Entretanto, é necessário que isto seja feito de modo transparente e também que o registro será visível para todos.

Eventualmente, foi sugerida a seguinte idéia de pagina:

A página principal da aplicação consiste de uma área com um campo para entrada de dados, um botão no canto superior esquerdo e uma tabela de registros que ocupará o resto da pagina.

Exemplo do 1º caso:

O funcionário A chega ao trabalho. Insere sua matrícula no campo e pressiona o botão para confirmar sua ação. Uma nova linha será adicionada ao topo da tabela, contendo seu nome, a palavra chave IN, assim como a hora de entrada.

Exemplo do 2º caso:

O funcionário B chega ao trabalho e tenta submeter sua matrícula da mesma maneira que o funcionário A. Entretanto, acidentalmente comete um erro na entrada dos dados. A aplicação o leva a uma outra página aonde será informado que a matrícula não é válida. Será apresentado um link para retornar a página principal e corrigir o erro.

Exemplo do 3º caso:

O funcionário A está saindo do trabalho para almoçar. Então informa o número de sua matrícula e pressiona o botão para confirmar. A aplicação reconhece que sua entrada anterior foi o IN. O aplicativo então insere uma nova linha na tabela, com seu nome, a hora de saída e a palavra chave OUT.

Criar a aplicação a partir da sinopse descrita, o JavaBean padrão de funcionário está descrito a seguir.

public class LoginPageBean { private String loginName; private String password; public String getLoginName() { return loginName; } public void setLoginName(String loginName) { this.loginName = loginName; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }

Esse modelo pode ser acessado por outras páginas após ter suas propriedades configuradas no arquivo faces-config.xml. A configuração de entrada para esse JavaBean é mostrada a seguir.

<managed-bean> <description> This bean serves as the backing model for our login form </description> <managed-bean-name>loginPage</managed-bean-name> <managed-bean-class>sample.LoginPageBean</managed-bean-class> <managed-bean-scope>request</managed-bean-scope> <managed-property> <description> The property that will store the user's login name </description>

Programação WEB 17

Page 152: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

<property-name>loginName</property-name> <null-value/> </managed-property> <managed-property> <description> The property that will store the user's password </description> <property-name>password</property-name> <null-value/> </managed-property> </managed-bean>

Programação WEB 18

Page 153: Projeto JEDI - Programação para WEB - Java - 178 páginas

Módulo 6Programação WEB

Lição 10Tópicos Avançados de JavaServer Faces

Versão 1.0 - Nov/2007

Page 154: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

1. ObjetivosNa lição anterior, aprendemos sobre a JavaServer Faces e como este framework combina uma servlet front controller, action handlers, e um conjunto de componentes visuais para simplificar a tarefa de desenvolver uma aplicação sob a arquitetura de MVC (Model - View - Controller). Vimos como JSF pode criar um estilo, parecido com a biblioteca Swing com sua arquitetura de componentes baseada em eventos, para programação no ambiente WEB.

Nesta lição discutiremos o objeto FacesContext, que obtém todas as informações do contexto dentro da JSF. Veremos outros dois conjuntos de componentes fornecidos para uso neste framework: Componentes Validadores e para conversão de tipo. Aprenderemos como criar tais componentes, e como desenvolver nosso conjunto de tags personalizadas para que os componentes criados possam ser representados mais facilmente nas páginas JSP.

Ao final desta lição, o estudante será capaz de:

• Conhecer a classe FacesContext• Entender os componentes validadores e para conversão de tipo• Criar componentes e como desenvolver um conjunto de tags personalizadas

Programação WEB 4

Page 155: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

2. FacesContextNa lição anterior, vimos como fazer uso do objeto FacesContext para acessar uma coleção de objetos correntes na sessão do usuário. Nesta lição entraremos em mais detalhes e veremos outros usos para este objeto.

Como item de revisão, uma instância do objeto de FacesContext pode ser obtida chamando o método getCurrentInstance() disponível na classe FacesContext.

2.1. FacesContext e a Árvore de Componentes

Para cada uma das visões fazemos uso dos elementos gráficos de JSF, existe um modelo disponível em estrutura de árvore de componente. A especificação JSF requer que todas as implementações armazenem uma estrutura de árvore de componente dentro do objeto FacesContext. Esta estrutura permite, aos desenvolvedores, o acesso a todos os componentes de interface do usuário e suas informações.

Existem diversas tarefas que podemos empregar este tipo de acesso a árvore de componentes. Através de programação, adicionamos componentes dinamicamente para a camada View, mudamos ou adicionamos componentes de conversão e validação ou ainda, podemos remover componentes.

Freqüentemente, usamos esta capacidade para um redirecionamento de tela dentro do framework. Isto pode ser feito modificando à árvore de componentes atual que é acessada pelo usuário pela árvore de componentes situada em outra página.

A seguir, temos uma amostra de um trecho que realiza esta tarefa (a linha relevante aparece em negrito).

...String targetPage = "/newPage.jsp";FacesContext context = FacesContext.getCurrentInstance();context.getApplication().getViewHandler().createView(context, targetPage);...

O método createView cria uma cópia da árvore de componentes que compõe o targetPage e o coloca como a árvore de componentes atual. Uma vez que o framework exibe o conteúdo novamente, exibirá o conteúdo da página.

2.2. FacesContext e o ExternalContext

O objeto ExternalContext, acessível pela classe FacesContext, permite o acesso ao ambiente atual do framework, em nosso caso (uma aplicação WEB), permite o acesso ao objeto de HttpServletRequest representando o requerimento atual, um objeto do tipo Map armazenado na sessão do usuário, como também ao objeto ServletContext, representando o contexto da aplicação WEB.

Para recuperar o objeto de HttpServletRequest:FacesContext context = FacesContext.getCurrentInstance();HttpServletRequest request = (HttpServletRequest)context.getExternalContext() .getRequest();

Infelizmente, não existe nenhum modo para acessar diretamente o objeto HttpSession associado à sessão. Entretanto, os objetos armazenados dentro desta podem ser acessados por um objeto do tipo Map, do seguinte modo:

Map sessionMap = context.getExternalContext().getSessionMap();

Para recuperar o ServletContext representado na aplicação, temos a seguinte instrução:ServletContext servletContext = (ServletContext)context.getExternalContext().getContext();

Programação WEB 5

Page 156: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

Uma vez que temos acesso a estes objetos, podemos então fazer uso de outras técnicas de programação WEB.

2.3. Validadores

Na lição sobre o framework Struts, discutimos a validação e sua importância em qualquer aplicação baseada em dados. Qualquer resultado da aplicação será obtido através da informação e que os dados corretos estão sendo informados.

JSF também reconhece esta necessidade de validação e fornece um conjunto de validadores que podemos usar em nossa aplicação. Fornece também vários caminhos para unir o código de validação aos componentes de entrada de dados.

2.4. JSF Validadores Padrão

JSF fornece três validadores padrões:

● DoubleRangeValidator – verificar se o valor no campo de entrada pode ser convertido para um tipo double e seu valor está entre o mínimo e o máximo de determinados valores fornecidos. Tag: ValidateDoubleRange. Atributos: minimum e maximum.

● LengthValidator – verificar se o valor no campo de entrada pode ser convertido para uma String e que seu tamanho está entre o mínimo e o máximo de determinados valores fornecidos. Tag: ValidateLength. Atributos: minimum e maximum.

● LongRangeValidator - verifica se o valor no campo de entrada pode ser convertido para um tipo long, e seu valor está entre o mínimo e o máximo de determinados valores fornecidos. Atributos: minimum e maximum.

As tags JSP para o validadores podem ser encontradas dentro da biblioteca de tags. Para fazer uso de tags dentro da biblioteca, devemos adicionar a seguinte linha no topo da página:

<%@taglib uri ="http://java.sun.com/jsf/core/" prefix="f" %>

2.5. Usando os validadores padrões

Fazer uso dos validadores padrões, basta inserir a tag de JSP do validador dentro do corpo do componente de entrada. Observe o seguinte exemplo.

... <h:outputLabel for="password"> <h:outputText value="Password : "/> </h:outputLabel> <h:inputSecret id="password" value="#{loginPage.password}"> <f:validateLength minimum="4"/> </h:inputSecret> <br/> <h:commandButton action="#{loginPage.performLogin}" value="Login"/></h:form>...

Este é o código JSP para a página de login que implementamos na lição anterior usando JSF. Será modificado de tal forma que o campo senha aceita somente uma entrada com o tamanho mínimo de 4 caracteres. As modificações são:

● A tag que define o campo de senha <h:inputSecret> foi modificada de modo que a tag não é mais fechada propriamente na mesma linha que era declarada. Agora faz uso de uma outra tag </h:inputSecret> para fechar corretamente.

● Para que a tag que define a validação funcionar <f:validateLength>, foi inserida entre a abertura e fechamento da tag <h:inputSecret>.

Para aceitar uma entrada que restrinja pelo tamanho máximo em vez de um tamanho mínimo, devemos substituir o atributo minimum pelo atributo maximum, e especificar este tamanho.

Programação WEB 6

Page 157: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

Podemos também fazer uso de dois atributos simultaneamente, definindo um intervalo no qual uma entrada seja aceita. Este trabalho será o mesmo feito com as tags <f:validateLongRange> e <f:validateDoubleRange>, a única diferença serão os tipos de dados aceitos.

2.6. Validação customizada

Os validadores padrões são bastante limitados em relação quanto ao que podem realizar. Provavelmente necessitaremos criar determinados códigos de validação em nossa aplicação para verificar estes dados corretamente. Existem os seguintes caminhos para definirmos isso:

a) Estender a classe do componente visual que aceita a entrada de forma que anulamos seu método de validação

b) Criar um método de validação externa

c) Criar nossas implementações validadoras separadas, registrá-las no framework e então inserí-las no componente gráfico

O problema com a primeira opção é que é não portátil. A validação só pode funcionar quando usado um componente visual específico. Nesta lição, estaremos concentrando nas segunda e terceira opções.

2.7. Criando um método de validação externa

Como primeira opção discutida para criarmos a validação personalizada, veremos a criação de um método de validação externa. Este procedimento é semelhante ao criar um método Aplicattion que lidará com eventos de ação. Também precisamos criar um método para obter um conjunto de regras dentro de um JavaBean administrado pelo framework e depois ligar esse método ao componente gráfico apropriado.

2.7.1.Assinatura de método

O método criado, deve ser ajustado conforme as seguintes regras:

● O método deve ser declarado como public e com um tipo de retorno void

● Não existe nenhuma restrição quanto ao nome do método

● Obter parâmetros na seguinte ordem: objeto FacesContext, Componente UIInput e um Object contendo o valor.

● Deve ser declarado uma proteção de retorno (throws) para ValidatorException.

Para criar um método de validação personalizada para nosso campo de senha, vista no exemplo anterior, que aceite somente letras e números, a assinatura do método seria similar a seguinte instrução.

public void validatePassword(FacesContext ctxt, UIInput component, Object value) throws ValidatorException

2.7.2.Implementação do método

Na nossa implementação do método, podemos fazer uso de dados fornecidos pelos parâmetros. O objeto FacesContext fornece um acesso aos recursos externos, como os objetos de request e escopo da sessão, como também a outros objetos dentro do framework JSF. O componente visual UIInput é a instância do componente de entrada que requer a validação; tendo uma instância deste objeto, obtemos o acesso ao estado do componente. Finalmente, o Object é o valor deste dentro do componente que requer a validação.

O processo de validação é simples. Caso o framework não receba qualquer ValidatorExceptions, a entrada é aceita. Caso o framework receba um ValidatorException o processo é interrompido e a página contendo o componente de entrada é novamente reapresentada.

A seguir, temos uma amostra da implementação para este método.public void validatePassword(FacesContext ctxt, UIInput component, Object value)

Programação WEB 7

Page 158: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

throws ValidatorException { if (null == value) return; if (!(isPasswordValid(value.toString()))) { FacesMessage message = new FacesMessage("Input error", "Password is not valid"); throw new ValidatorException(message); }}protected boolean isPasswordValid(String password) { ...

Uma nova classe no trecho acima é mostrada, a FacesMessage. Esta classe modela uma mensagem dentro da JSF. O primeiro parâmetro em seu construtor é um título, enquanto que o segundo são os detalhes da mensagem. Isto é utilizado para indicar a JSF que a mensagem será reconhecida como uma mensagem de erro.

Para ser realmente capaz de exibir uma mensagem de erro gerada, devemos adicionar uma tag <h:messages> ou <h:message> na página JSP conforme mostrado a seguir.

... <h:outputLabel for="password"> <h:outputText value="Password : "/> </h:outputLabel> <h:inputSecret id="password" value="#{loginPage.password}"> <f:validateLength minimum="4"/> </h:inputSecret> &nbsp;<h:message for="password" styleClass="error"/> <br/> <h:commandButton action="#{loginPage.performLogin}" value="Login"/></h:form>...

A tag <h:message> só exibe as mensagens para um componente específico, indicado pelo valor do atributo for. Também podemos especificar a classe de estilo CSS que será aplicada à mensagem. Uma tag <h:messages> exibirá todas as mensagens disponíveis para todos os componentes na página.

2.7.3.Criando uma implementação separada do validador

Em vez de criar nosso método dentro de um JavaBean, podemos criar uma classe separada que implementa a interface do validador. Esta interface define um único método, o método para validar, cuja assinatura é idêntica ao método de validação externo, isto é, public, retorno void e os mesmos parâmetros: FacesContext, UIInput e Object.

Em termos de implementação, não é diferente de uma implementação de um método Validador separado por um método de validação externa. O que diferencia está no modo como são utilizados. Um método de validação externa é usado mais para a validação de um código específico por um componente em particular, enquanto uma implementação separada do validador é usada para conter o código de validação com um propósito comum e será extensivamente reusado por toda sua aplicação ou mesmo outras aplicações.

2.8. Registrando um componente validador

Para usar um componente validador personalizado, devemos primeiro registrá-lo para ser reconhecido pela JSF. Isto é realizado colocando uma entrada de configuração no arquivo faces-config.xml.

Vejamos o seguinte exemplo: <validator> <description>A validator for checking the password field</description> <validator-id>AlternatingValidator</validator-id> <validator-class>jedi.sample.jsf.validator.AlternatingValidator</validator-class>

Programação WEB 8

Page 159: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

</validator>

Como podemos observar, para configurar a entrada para um novo validador basta definir um identificador pelo qual será referenciado na JSF e o nome da classe deve ser totalmente qualificado para implementar a funcionalidade da validação.

2.8.1.Usando um componente validador

Para usar o componente validador registrado, usamos a tag <f:validator> e fornecemos o atributo validatorId como identificação do validador, conforme demonstrado a seguir:

... <h:outputLabel for="password"> <h:outputText value="Password : "/> </h:outputLabel> <h:inputSecret id="password" value="#{loginPage.password}"> <f:validator validatorId="AlternatingValidator"/> </h:inputSecret> &nbsp; <h:message for="password" styleClass="error"/> <br/> <h:commandButton action="#{loginPage.performLogin}" value="Login"/></h:form>...

2.9. Adicionando atributos para nosso validador

Se observarmos o validador padrão validateLength fornecido pela JSF, podemos ver que este contém dois atributos pela qual a operação poderá ser modificada futuramente. De modo semelhante, podemos ter atributos para o validador de modo personalizado.

Para obtermos esta forma, adicionamos uma propriedade dentro de nossa classe para cada um dos atributos, isto sustenta e implementar os métodos padrões getter e setter. Em seguida, incluir uma entrada para cada atributo dentro da configuração de entrada do validador.

package jedi.sample.jsf.validator;public class AlternatingValidator implements Validator { private Integer interval; public Integer getInterval() { return interval; } public void setInterval(Integer interval) { this.interval = interval; } public void validate(FacesContext ctxt, UIInput component, Object value) throws ValidatorException { if (null == value || interval == null) return; if (!(isPasswordValid(value.toString()))) { FacesMessage message = new FacesMessage("Input error", "Password is not valid"); throw new ValidatorException(message); } } protected boolean isPasswordValid(String password) {...

<validator> <description>A validator for checking the password field</description> <validator-id>AlternatingValidator</validator-id> <validator-class>jedi.sample.jsf.validator.AlternatingValidator</validator-class> <attribute> <attribute-name>interval</attribute-name> <attribute-class>java.lang.Integer</attribute-class>

Programação WEB 9

Page 160: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

</attribute></validator>

Um valor que pode ser fornecido para este atributo e fazer uso da tag <f:attribute>: ... <h:outputLabel for="password"> <h:outputText value="Password : "/> </h:outputLabel> <h:inputSecret id="password" value="#{loginPage.password}"> <f:validator validatorId=”AlternatingValidator”/> <f:attribute name=”interval” value=”5”/> </h:inputSecret> &nbsp; <h:message for=”password” styleClass=”error”/> <br/> <h:commandButton action="#{loginPage.performLogin}" value="Login"/></h:form>...

Programação WEB 10

Page 161: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

3. Componentes de ConversãoOs componentes de conversão também são componentes importantes do conjunto fornecido pela JSF. Oferecem uma solução para um problema comum dos programadores WEB: como converter os valores informados pelo usuário de sua representação String nativa em um formato ou tipo apropriado usado internamente pelo servidor. São bidirecionais, tipos int podem ser usados para mudar a forma como dados internos são expostos para o usuário final. Os componentes de conversão podem fazer isto definindo um método getAsString() e getAsObject() que são chamados pela JSF na hora apropriada. O método getAsString() é chamado para fornecer um tipo String dos dados, enquanto que o método getAsObject() é utilizado para converter uma String para um um objeto determinado.

Os componentes de conversão são associados para introduzir um determinado tipo, fazendo uso do atributo de conversão embutido em alguns dos componentes de entrada fornecidos pela JSF. Detalharemos isto no exemplo a seguir, que modifica uma tag inputText de forma que permita naturalmente a conversão de um objeto tipo Integer:

<h:inputText converter=”Integer” value=”#{myFormBean.myIntProperty}”/>

Além do componente inputText, o atributo conveter está sustentado pela tag inputSecret, inputHidden, e o componentes outputText.

O objeto do Integer é a identificação atribuída a um dos elementos conversores padrões fornecidos pela JSF. Outros componentes de conversão padrões são listados a seguir:

Classe de Conversão ID de ConversãoBigDecimalConverter BigDecimalBigIntegerConverter BigIntegerIntegerConverter IntegerShortConverter ShortByteConverter ByteShortConverter ShortCharacterConverter CharacterFloatConverter FloatDoubleConverter DoubleBooleanConverter BooleanDateTimeConverter DateTimeNumberConverter Number

Nesse conjunto de componentes de conversão, DateTimeConverter e NumberConverter merecem atenção especial. Os outros conjuntos de componentes de conversão não são configuráveis. Só podem ser usados como no exemplo acima. Os componentes DateTimeConverter e NumberConverter, no entanto, podem utilizar tags especiais e, através dessas tags, podemos manipular os atributos com os quais o desenvolvedor pode customizar seus comportamentos.

3.1. DateTimeConverter

O DateTimeConverter pode ser usado para converter dados de entrada em instâncias de java.util.Date. Ele prove atributos com os quais o desenvolvedor pode especificar o formato usado na conversão. É importante notar que o formato especificado é obrigatório SOMENTE quando o usuário fornece um dado de entrada. Isto é, se o usuário não fornecer um dado de entrada no formato especificado dos atributos, um erro de conversão irá ocorrer e o framework irá paralisar o processamento de dados.

Estes são os atributos disponíveis:

• dateStyle – um dos estilos de data definidos pelo java.text.DateFormat. Os valores possíveis são: default, short, long, medium ou full.

Programação WEB 11

Page 162: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

• parseLocale – a localidade a ser usada como referência durante a conversão.

• timeStyle – um dos estilos de data definido pelo java.text.DateFormat. Os valores possíveis são: default, short, medium, long ou full.

• timeZone – a time zone utilizada.

• type – uma String que define quando a saída será uma data, uma instância do tipo time, ou ambos. Os valores possíveis, são: date, time e both. O valor padrão é date.

• pattern – o padrão de formatação a ser usado na conversão. Se um valor para esse atributo for definido, o sistema irá ignorar qualquer valor para dateStyle, timeStyle e type.

Esses atributos são disponibilizados para o desenvolvedor através da tag <f:convertDateTime>.

Vamos fazer um pequeno exemplo de utilização do DateTimeConverter. Primeiro, crie uma aplicação WEB em branco pronta para JSF, como nas instruções da lição anterior. Então, vamos começar criando um JavaBean que irá guardar a data que iremos inserir:

import java.util.Date;public class DateBean { private Date date; public Date getDate() { return date; } public void setDate(Date date) { this.date = date; }}

Então, vamos adicionar suas configurações iniciais no arquivo faces-config.xml:<managed-bean> <description> Modelo de suporte para o exemplo de conversão de data </description> <managed-bean-name>dateBean</managed-bean-name> <managed-bean-class>jedi.sample.DateBean</managed-bean-class> <managed-bean-scope>request</managed-bean-scope> <managed-property> <property-name>date</property-name> <null-value/> </managed-property></managed-bean>

Finalmente, criamos o arquivo JSP que irá gerar a view: <%@taglib uri="http://java.sun.com/jsf/core" prefix="f"%><%@taglib uri="http://java.sun.com/jsf/html" prefix="h"%><f:view> <h:form id="testForm"> <h:outputLabel for="dateField">Date : </h:outputLabel> <h:inputText id="dateField" value="#{dateBean.date}" required="true"> <f:convertDateTime pattern="M/d/yy"/> </h:inputText> <br/> <h:message for="dateField"/> <br/> <h:commandButton id="button" value="Press me!!!"/> </h:form></f:view>

Alguns comentários sobre a página JSP de exemplo:

• Para nosso DateTimeConverter, especificamos um padrão de M/d/yy. Como o componente de conversão obriga esse padrão para o usuário, o usuário deve inserir uma data como 1/13/06, antes de poder continuar. Para outros possíveis padrões, procure na

Programação WEB 12

Page 163: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

documentação API por java.text.SimpleDateFormat.

• Adicionamos um atributo required com valor true para o campo de entrada. Desta forma asseguramos que o usuário não pode enviar dados de entrada em branco.

• Utilizamos uma tag <h:message> associada ao campo de entrada Date. Isto assegura que qualquer mensagem gerada pelo campo de entrada (como aqueles gerados por erros de conversão) serão mostrados.

• O atributo action do commandButton foi deliberadamente deixado em branco, por isso podemos nos focar apenas nas mensagens geradas pelo componente de conversão.

Carregando a aplicação web em um servidor compatível (no nosso caso uma instância do AppServer 8.1) e acessando a página que acabamos de criar, irá resultará na seguinte tela, mostrada abaixo:

Figura 1: Título do JSP de exemplo

Testando o dado de entrada January 13, 2006 irá resultar no seguinte:

Figura 2: Resultado do JSP de exemplo utilizando um componente de conversão

Informando uma data no formato apropriado irá resultar em nenhuma mudança na nossa página. Isso indicará que o dado foi convertido com sucesso e armazenado em nosso JavaBean.

E se não utilizássemos um componente de conversão?

Para ilustrar o benefício do componente de conversão, modificaremos nossa página JSP para retirá-lo:

<%@taglib uri="http://java.sun.com/jsf/core" prefix="f"%><%@taglib uri="http://java.sun.com/jsf/html" prefix="h"%><f:view> <h:form id="testForm"> <h:outputLabel for="dateField">Date : </h:outputLabel> <h:inputText id="dateField" value="#{dateBean.date}" required="true"/> <br/> <h:message for="dateField"/> <br/> <h:commandButton id="button" value="Press me!!!"/> </h:form></f:view>

Programação WEB 13

Page 164: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

Agora, entrando uma data com qualquer formato irá causar um erro:

Figura 3: Resultado da JSP de exemplo sem o componente de conversão

Isso ocorre porque a propriedade associada ao campo de entrada é um objeto do tipo Date. O framework não pode mudar automaticamente a representação do objeto String do campo de entrada para um objeto tipo Date, então procura por um componente de conversão que irá tratar isso. Como nenhum componente de conversão é encontrado, é mostrado um erro null (nulo).

3.2. NumberConverter

Outro componente de conversão especial que iremos ver é o NumberConverter. Esse componente de conversão é usado para converter números ou obrigar formatações especiais. Os seguintes atributos são usados para podermos controlar o comportamento desse componente de conversão:

• currencyCode – código monetário tipo ISO 4217 a ser utilizado ao formatar.• currencySymbol – o símbolo monetário a ser usado ao formatar.• groupingUsed – indica se o valor utilizará separadores de grupo (por exemplo, ponto

depois de cada 3 dígitos).• integerOnly – indica que apenas a parte inteira de um número deve ser mostrada. Isso

significa que a parte decimal será truncada antes do dado ser armazenado em uma propriedade escondida (bound).

• locale – a localidade usada para referência de formação.• maxIntegerDigits – o número máximo de dígitos mostrados ou usados na parte inteira

do número.• maxFractionDigits – o número máximo de dígitos mostrados ou usados na parte decimal

do número.• minFractionDigits – o número mínimo de dígitos mostrados ou usados na parte decimal

do número.• minIntegerDigits – o número mínimo de dígitos mostrados ou usados na parte inteira do

número.• pattern – o padrão a ser utilizado quando formatamos ou mostramos o número. Para mais

detalhes sobre os padrões permitidos, verifique no JavaDoc por java.text.NumberFormat.• type – indica quando o número a ser convertido deve ser tratado como moeda, percentual

ou número. Para mais detalhes, verifique no JavaDoc por java.text.NumberFormat.

É importante ressaltar que ao utilizar NumberConverter, que, como no DateTimeConverter, qualquer padrão ou símbolo indicado nos atributos são tratados como regras obrigados para o usuário. Se os dados de entrada do usuário não seguirem o padrão ou não apresentarem os símbolos solicitados, um erro de conversão irá ocorrer e o processamento dos dados será paralisado.

Um dos problemas com os componentes de conversão padrões fornecidos com JSF é que, eles apenas executam conversões padrão ou obrigarão padrões de entrada para o usuário. Por exemplo, o componente NumberConverter, se especificarmos um valor 'P' no atributo currencySymbol, qualquer usuário que não entrar 'P' junto com números encontrará um erro.

Programação WEB 14

Page 165: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

Felizmente, JSF fornece uma maneira fácil para desenvolvedores criarem seus componentes de conversão customizados a serem utilizados no framework.

3.3. Componentes de Conversão Customizados

Componentes de conversão customizados podem ser criados através da criação de uma classe que implementa a interface Converter. Essa interface define dois métodos:

● public Object getAsObject(FacesContext ctxt, UIComponent component, String input)

● public Object getAsString(FacesContext ctxt, UIComponent component, Object source)

Consideremos o seguinte cenário:

● Uma página com um formulário que requer dados de entrada do usuário com valores monetários. Gostaríamos de armazenar esses dados como objetos Double para facilitar o processamento mais tarde. No entanto, o campo de entrada deve ser flexível o suficiente para tratar apenas números (ex. 2000), ou incluir grupos de símbolos de grupos (2.000). Outra particularidade é como a página irá reverter o controle para si, os dados deverão ser representados contendo símbolos de agrupamentos.

Alguém poderá pensar que, como essa operação envolve números e símbolos de grupos, poderíamos simplesmente utilizar o NumberConverter fornecido com o framework. No entanto, o NumberConverter obriga qualquer padrão que fornecemos para ele, então ao solicitar o tratamento de valores monetários com símbolos de grupos, ele poderia apenas tratar valores monetários com símbolos de grupos. Dados de entradas numéricos resultariam em erros de conversão. Também não é possível ter múltiplos componente de conversão para um simples componente de entrada de dados. Então não podemos simplesmente utilizar duas instâncias diferentes de NumberConverter. É com esse cenário em mente que os exemplos para criação de componentes de conversão customizados foram criados.

3.3.1.Método getAsObject

Esse método é chamado pelo framework para o componente de conversão associado quando precisa converter os dados de entrada do usuário de sua representação textual em outro tipo de objeto. O conceito na implementação desse método é simples: atua na operação de processamento do argumento String fornecido e retorna um Object que irá representá-lo no servidor.

Considerando o cenário descrito, nossa tarefa é converter uma entrada em tipo String em um objeto do tipo Double, não importando se o número é apenas numérico ou com símbolos de grupos.

A implementação para esse método é mostrada a seguir: public Object getAsObject(FacesContext facesContext, UIComponent uIComponent, String str) { Double doubleObject = null; try { doubleObject = Double.valueOf(str); } catch (NumberFormatException nfe) { // Se a exceção ocorrer // uma causa provável é a existência de símbolos de agrupamento // Recuperar uma instância do objeto NumberFormat // e a faz reconhecer símbolos agrupados NumberFormat formatter = NumberFormat.getNumberInstance(); formatter.setGroupingUsed(true); // Utilizar o objeto NumberFormat para recuperar o valor numérico do // dado de entrada try { Number number = formatter.parse(str); if (number.doubleValue() <= Long.MAX_VALUE) { Long value = (Long) number;

Programação WEB 15

Page 166: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

doubleObject = new Double(value.doubleValue()); } else { doubleObject = (Double)number; } } catch (ParseException pe) { // Se o código atingir esse ponto, nenhum dos formatos suportados // foram seguidos. // Criar uma mensagem de erro e a mostra para o usuário através de // um objeto exception FacesMessage message = new FacesMessage("Formato não Suportado", "Este campo suporta apenas números (5000), " + "ou números com vírgula (5,000)"); throw new ConverterException(message); } } return doubleObject;}

3.3.2.Método getAsString

Esse método faz conversões bidirecionais. Ele dita a representação em String do dado interno relevante. O conceito por trás da implementação desse objeto é mais simples do que a do método getAsObject acima. Ela mostra o valor armazenado no objeto tipo Double como um número com símbolos de grupos. Essa implementação é mais simples porque sabemos com clareza o formato exato do nosso dado de entrada.

Aqui está a implementação: public String getAsString(FacesContext context, UIComponent component, Object source) { Double doubleValue = (Double)source; NumberFormat formatter = NumberFormat.getNumberInstance(); formatter.setGroupingUsed(true); return formatter.format(doubleValue);}

3.4. Usando um componente de conversão customizado

Depois de termos criado o componente de conversão customizado, teremos que configurar a JSF para que, então, reconheça sua existência. Isso é feito, adicionando-se uma entrada de configuração no arquivo faces-config.xml, que define uma estrutura rígida para seus elementos. Entradas de configuração para componentes de conversão aparecem depois de todas as entradas de validadores, mas antes das entradas dos JavaBeans gerenciados.

Abaixo temos a entrada de configuração para o componente de conversão que acabamos de criar.<converter> <description>Customizado para aceitar dados monetários</description> <converter-id>myConverter</converter-id> <converter-class>jedi.sample.MyCustomConverter</converter-class></converter>

Depois de termos criado uma entrada de configuração para o componente de conversão, a utilizaremos para nosso elemento de entrada especificando o id do componente de conversão em uma tag <f:converter>, da seguinte forma:

<h:inputText id="amount" value="#{numberBean.amount}"> <f:converter converterId="myConverter"/></h:inputText>

Programação WEB 16

Page 167: Projeto JEDI - Programação para WEB - Java - 178 páginas

Módulo 6Programação WEB

Lição 11Segurança na WEB

Versão 1.0 - Nov/2007

Page 168: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

1. ObjetivosEste módulo de programação WEB não estaria completo sem uma discussão sobre como deixar seguras as aplicações desenvolvidas. Características atraentes, grande usabilidade e boa manutenção parecem pequenos se a aplicação falhar em confiabilidade junto aos clientes WEB.

Ao final desta lição, o estudante será capaz de:

• Obter segurança na comunicação entre servidor e cliente usando Session Socket Layer• Aprender medidas para evitar as maiores falhas em aplicações WEB

Programação WEB 4

Page 169: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

2. SSLO SSL transformou-se de fato no padrão de fato em segurança na comunicação entre clientes e servidores. Representa uma camada de Socket Segura. É uma camada de protocolo que se interpõe entre a camada padrão de TCP/IP e a camada acima, os protocolos de aplicação como o HTTP. Permite ao servidor se autenticar no cliente e depois disso codifica o canal de comunicação.

Uma discussão completa sobre os mecanismos da SSL está fora do escopo deste material. A finalidade é entender que o uso desta tecnologia garante um canal seguro entre o servidor e o cliente. Confidência é preservada mais adiante automaticamente devido à medida em que se detecte a parte incluída como parte do SSL.

2.1. Configurando o SSL nas aplicações

Para apreciar os beneficios da SSL nas aplicações, precisamos configurar o ambiente em que o servidor está rodando para que aceite conexões com SSL. Diferentes contêineres de servlets possuem diferentes formas de configuração. Nesta lição aprenderemos como configurar o Sun Application Server 8.1.

2.1.1.Certificados

Um das primeiras coisas que precisamos configurar em nosso servidor para comunicações baseadas em SSL é um certificado de segurança válido. Pense em um certificado como um passaporte que identifica o proprietário e contém outra informações importantes. Certificados normalmente são emitidos através de Autoridades Certificadoras (Certification Authorities ou CA). Uma CA atua como um escritório para a emissão de passaportes pois valida a identidade do dono do certificado e os sinais do certificado de forma que este não possa ser forjado ou falsificado.

Há várias Autoridades Certificadoras famosas. Uma das mais populares é a Verisign. É decisão do administrador qual CA utilizar como servidora de um certificado.

Na ausência de um certificado de uma CA confiável, um certificado temporário pode ser criado utilizando as ferramentas incluídas dentro da Java SDK. Observe, porém, que clientes perspicazes não continuam com transações confidenciais uma vez que descobrem que o certificado utilizado é criado por nós mesmos.

2.1.2.Gerando o Certificado com a Chave Particular

É mais fácil executar o seguinte conjunto de operações no mesmo diretório que o servidor utiliza para armazenar seus certificados. Este pode ser encontrado na pasta %APP_SERVER_HOME%/domains/domain1/config.

No diretório especificado, execute a seguinte a linha de comando:keytool -genkey -alias keyAlias -keyalg RSA -keypass keypassword -storepass storepassword -keystore keystore.jks

• keyAlias – o apelido ou ID que este certificado utilizará.• keypassword – a senha para a chave particular que será utilizada na encriptação.• storepassword – a senha para o keystore.

Pode ser um pouco confuso compreender a necessidade de se ter duas senhas na criação de um novo certificado. Porém, lembremos de que todas as entradas de chave residem no que é chamado um keystore. Um keystore pode armazenar uma ou mais chaves. keypassword indica a senha da chave particular que o nosso certificado usará, enquanto storepassword indica a senha do keystore que conterá a chave. O diretório em que estamos operando já tem um arquivo keystore, entretanto, com uma senha existente. Assim precisaremos fixar um novo storepass.

Esta senha pode ser mudada mais tarde, usando o aplicativo keytool:keytool -keystore keystore.jks -storepass newPassword

Programação WEB 5

Page 170: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

2.1.3.Criando um Certificado

Depois de gerada a chave que será usada pelo certificado, podemos criar o próprio arquivo de certificado:

keytool -export -alias keyAlias -storepass storepassword -file certificateFileName -keystore keystore.jks

A instrução anterior utiliza o aplicativo keytool para gerar o arquivo de certificado que usa a chave particular indicada por keyAlias que está contida no keystore.

2.1.4.Gerenciando o Certificado

Para o servidor de aplicação reconhecer o certificado criado há pouco, precisamos adicioná-lo este na lista de certificados confiáveis. O servidor contém um arquivo nomeado que cacerts.jks que contém os certificados confiáveis. Podemos adicionar nosso arquivo usando o aplicativo keytool:

keytool -import -v -trustcacerts-alias keyAlias-file certificateFileName-keystore cacerts.jks -keypass keypassword

Será solicitado a senha do Application Server.

2.1.5.Criando segurança com um HTTP listener

Depois que criamos um certificado e o registramos com sucesso para uso no servidor de aplicação, podemos criar um HTTP listener que fará uso do certificado. Dessa forma, o servidor de aplicações poderá ser usado para comunicações seguras.

Para fazer isto, primeiro crie uma entrada no Console de Administração do Sun Application Server. Nas opções a esquerda expanda a aba nomeada Configuration e expanda a opção HTTP Service:

Figura 1 - Console de Administração

Programação WEB 6

Page 171: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

A seguir, selecione a opção HTTP Listeners e, no painel à direita, pressione o botão New.

Figura 2 - Criando um HTTP Listener

Figura 3 - Mudando as configurações gerais

Programação WEB 7

Page 172: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

As telas anteriores são o resultado do preenchimento com novos valores. Os valores que podem mudar são o nome do listener e a porta que serão utilizados de acordo com as preferências do administrador.

Reinicie o servidor e a configuração nova poderá ser utilizada acessando o endereço:https://serverAddress:listenerPort/index.html

Com os dados fornecidos neste exemplo, o endereço seria:https://localhost:8091/index.html

Para utilizar comunicação segura entre cliente e servidor, redirecione o usuário para a porta do listener seguro e desta forma obter o acesso a aplicação.

Programação WEB 8

Page 173: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

3. Falhas de Segurança em Aplicações WEBA Open Web Application Security Project (Segurança de Projeto Aberto de Aplicação WEB), ou simplesmente OWASP é um projeto open-source criado por uma entidade sem fins lucrativos que se propõe a achar causas da não segurança em softwares e encontrar soluções. Vejamos a seguir, uma lista com tópicos de falhas de segurança em aplicações WEB. Recomenda-se que qualquer aplicação WEB seja criada protegida dessas falhas como um padrão mínimo de segurança.

Olhemos esta lista, e vejamos como podemos proteger nossas aplicações.

3.1. Entrada inválida

Todas as aplicações WEB recebem dados através de uma requisição HTTP realizada pelo usuário e fazem uso desses dados informados para executar suas operações. Hackers podem manipular qualquer parte da requisição (uma consulta, informação em Cookies, cabeçalhos) para tentar evitar o mecanismo de segurança de um sítio ou o fluxo de controle de normal.

Há diferentes tipos de ataques relacionados a este problema. Que serão discutidos de forma mais profunda:

• Cross Site Scripting• Buffer Overflows• Falhas na injeção

Existem alguns detalhes que devem ser observados ao se controlar a validação da aplicação. Primeiro, não é suficiente em qualquer aplicação WEB confiar somente em scripting executados no lado do cliente (por exemplo JavaScript). Este tipo de código normalmente submete de forma estática quando descobre que a informação é inválida. Porém, nada faz para impedir que hackers geram suas próprias requisições de HTTP independentes. Resumindo, usar somente validação no lado cliente deixa a aplicação WEB aberta para ataques.

Segundo, algumas aplicações usam uma abordagem "negativa" em validação: tentam descobrir se existe algum elemento prejudicial nos parâmetros de requisição. O problema com este tipo de abordagem é que só pode proteger contra um conjunto limitado de ataques. São prevenidos só os ataques reconhecidos pelo código de validação. Há um número crescente de hackers com métodos a usar para evitar a segurança por uma informação não validada; é possível que um novo método não seja reconhecido pela aplicação e sobreponha este tipo de validação e cause destruição. É sempre melhor fazer uso de uma aproximação "positiva": definir um formato ou padrão para valores possíveis e assegurar que a informação recebida respeite esta regra.

3.2. Controle de Acesso Quebrado

Há muitas aplicações que categorizam seus usuários em papéis diferentes e provêem níveis diferentes de interação ou tipos diferentes de conteúdo para cada uma dessas categorias. Como por exemplo, a maioria das aplicações define um papel de usuário e um papel de administrador: só o papel de administrador é autorizado a ter acesso a páginas ou executar ações que são responsabilidade da administração do sítio.

O problema é que algumas aplicações não obrigam esta autorização efetivamente. Como exemplo, alguns programas executam através de um posto de fiscalização dentro do fluxo de tela habitual que só usuários registrados podem passar: o usuário deverá provar que é autorizado através de um nome e uma senha. Porém, essa conferencia não obriga a conferência mais de uma vez após passar este posto de fiscalização: uma vez que um usuário consegue "saltar" este posto, eles podem executar as operações livremente.

Outros problemas relacionados ao controle de acesso incluem:

• Chaves inseguras – alguns sítios fazem uso de algum tipo de chave ou recorrem a seus usuários ou funções. Estas chaves podem ser descobertas, porém e se um hacker puder fazer uso delas para se passar por um usuário autorizado, então o sítio está aberto ao ataque.

Programação WEB 9

Page 174: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

• Permissões de arquivo – a maioria dos servidores de aplicação WEB confiam em um arquivo externo para proporcionar uma lista de usuários autorizados a quais recursos podem ou não ter acesso. Se este arquivo for acessível externamente, então os hackers podem modificá-lo para se incluírem na lista de usuários permitidos.

O que podemos fazer para resolver estes problemas? Para uma solução mais simples, podemos desenvolver filtros ou componentes semelhantes aos que podem ser aplicados através recursos sensíveis. Estes devem assegurar que serão autorizados por usuários certificados. Se proteger de chaves inseguras, devemos desenvolver a aplicação de forma a não confiar no segredo de qualquer chave para proporcionar o controle de acesso. Em relação ao problema do arquivo de permissão, estes devem ser colocados em locais inacessíveis para os navegadores WEB e, além disso, marcados no mesmo nível do sistema operacional disponíveis a papéis específicos.

3.3. Autenticação Quebrada e Gerenciamento de Seção

Autenticação e administração de sessão significa controlar a autenticação de usuários e a administração de sessões ativas. Há várias áreas na qual isso pode ser negligenciado, e deste modo, expor a vulnerabilidade do sistema:

• Tamanho da Senha - Forçar que a senha da aplicação tenha um tamanho mínimo, o que pode ser conferido verificando o comprimento da senha e sua complexidade. Considere uma aplicação que deixe seus usuários registrados criarem suas próprias senhas: não se deve permitir senhas menores que 5 caracteres e palavras simples que podem ser adivinhadas por hackers.

• Uso da Senha – Nossa aplicação deve obrigar um número de máximo de tentativas que um usuário pode efetuar durante a sua entrada no sistema por um determinado período de tempo. Isto protege nossa aplicação de ataques como o de forçar a senha onde os hackers tentam repetidamente novas senhas. Caso um número determinado de tentativas aconteça, isso deveria ser registrado para fornecer aos administradores uma indicação para alertar de possíveis ataques.

• Armazenamento da senha – De forma alguma deve-se armazenar senhas dentro da aplicação. Preferencialmente devem ser armazenadas em um banco de dados externo, um arquivo protegido, entre outros. Devem ser registradas em um formato codificado. Para que aquela informação sensível não se espalhe no caso de uma brecha dentro da aplicação.

Outro assunto relacionado a senhas é que nunca devem ser deixadas no código de fonte da aplicação.

• Sessão com Proteção de Chave – Existem servidores que usam chave de sessão para identificar um usuário participante em uma sessão. Porém, esta sessão de chave pode ser recuperada por alguém na mesma rede, de tal forma que outra pessoa possa se fazer passar por um cliente autorizado.

Uma das melhores maneiras de se prevenir uma chave de sessão seja recuperada por alguém na mesma rede é colocar comunicações entre o servidor e o cliente em um canal de SSL protegido.

3.4. Cross Site Scripting

Cross Site Scripting acontece quando alguém conseguir utilizar uma aplicação WEB para enviar códigos maliciosos para outro usuário. Isto pode ser feito embutindo o conteúdo ativo (como JavaScript, objetos ActiveX ou Flash) em uma requisição que gera uma resposta em HTML para ser vista por outro usuário. Uma vez que outros usuários têm acesso a este conteúdo, os navegadores não sabem que estes não serão confiáveis.

Um dos melhores modos de se prevenir ataques Cross Site Scripting é validar todos os dados que entram nas requisições do usuário (cabeçalhos, cookies, parâmetros do usuário, entre outras). Uma abordagem "negativa" que não deveria ser usada é tentar filtrar todas as formas de conteúdo ativo embutido, pois não é uma forma cem por cento efetiva.

Programação WEB 10

Page 175: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

3.5. Buffer Overflows

Ataques podem usar Buffer Overflows para corromper a ordem de execução de uma aplicação WEB. Isso pode ser feito enviando requisições cuidadosamente desenvolvidas que ordenam que o servidor execute um código arbitrário.

Problemas de Buffer Overflows normalmente são difíceis de descobrir e de explorar através de hackers. Porém, os atacantes ainda conseguem identificar este tipo de vulnerabilidade contando com vários produtos. Entretanto, graças ao projeto do ambiente de aplicações Java que são executados dentro de um servidor padrão Java EE, estão imunes a estes tipos de ataque.

Para assegurar nossa segurança, entretanto, é sempre melhor monitorar constantemente a disponibilidade de correções e novas versões dos produtos que estamos utilizando.

3.6. Falhas de Injeção

Um tipo popular de vulnerabilidade é denominada de Falhas de Injeção na qual os hackers podem enviar ou "injetar" chamadas ao sistema operacional ou para recursos externos como os bancos de dados.

Uma falha de injeção comum é a SQL injection. Examine a seguinte URL: http://someServer/someApp/someAction?searchString=jedi

Esta URL acima é possivelmente gerada por uma aplicação que possui um formulário em que há uma busca na base de dados por uma chave chamada 'jedi'. Implementações que não validam suas entradas de dados provavelmente se pareceriam com o código SQL a seguir:

select * from someTable where someField='value'

Onde value é o valor do parâmetro searchString recebido diretamente da requisição HTTP. E se, por exemplo, algum hacker digitar diretamente a seguinte URL:

http://someServer/someApp/someAction?searchString=jedi'%20AND%20true;%20DROP%20DATABASE;'

A query SQL gerada seria então algo como: select * from someTable where someField='jedi' AND true;DROP DATABASE;''

A primeira sentença seria aceita devido a "AND true" na cláusula where do comando select. A segunda sentença seria então executada, ocasionando muitos danos à aplicação.

Este é um tipo de ataque feito através de entradas inválidas. Com exceção á rigorosa validação de informações em requisições do usuário, existem duas outras precauções que podemos ter:

• Em vez de usar sentenças simples como: SELECT, INSERT, UPDATE e DELETE, crie funções que executam funcionalidades equivalentes. As funções tem o benefício de tratar os parâmetros como dados, ao contrário de simplesmente executar o conteúdo. Requerem que os parâmetros estejam no tipo específico declarado. Elas fornecem uma quantidade significativa de proteção contra injeção de SQL, embora a validação também deva ser executada.

• Fornecer à aplicação somente a quantidade mínima de privilégios que necessitam para executar as funcionalidades desejadas. Por exemplo, se a aplicação for inicialmente uma aplicação de visualização de dados, não dê privilégios para INSERT, UPDATE ou DELETE. Não deixe a aplicação WEB acessar a base de dados usando usuário com privilégio de administrador. Isto pode minimizar os danos que os hackers podem fazer.

3.7. Armazenamento Inseguro

Aplicações WEB usualmente necessitam armazenar informações sigilosas como senhas, informações de cartão de crédito e outras. Devido a essa natureza sigilosa, estes itens são geralmente (o que deveria ser obrigatoriamente) encriptados para impedir acessos não

Programação WEB 11

Page 176: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

permitidos. Entretanto, tais implementações encriptadas às vezes são fracas ou vulneráveis a ataques persistentes.

Estes são alguns erros geralmente cometidos :

• Falha ao encriptar os dados críticos.• Armazenamento inseguro de chaves, certificados e senhas.• Armazenamento impróprio de segredos na memória.• Funções de randomizações pobres.• Escolha ruim dos algoritmos para encriptar.• Tentar inventar novos algoritmos para encriptar.

Considere o seguinte cenário: existe uma aplicação que, na modelagem dos atributos e estados do usuário, inclua a senha como parte do objeto Usuário. Entretanto, após a entrada do usuário em um mecanismo de autenticação, a aplicação armazena o objeto Usuário na sessão. O problema com esse cenário é que a senha pode agora facilmente ser recuperada por alguém que consiga quebrar ou penetrar na sessão do usuário.

Uma boa política para evitar armazenamento inapropriado de informações sigilosas é não modelar a senha como atributo da classe que representa o usuário se esta classe precisa ser armazenada em algum lugar na aplicação. Em vez de encriptar o número do cartão de crédito do usuário, simplesmente solicite-o sempre que necessário.

Também é melhor empregar algoritmos externos para encriptar em vez de desenvolver um. Certifique-se de que o algoritmo que você usará foi submetido a exame público e se provou ser de confiança.

3.8. Negação de Serviço

Negação de Serviço (Denial of Service ou DoS) refere-se a ataques maliciosos realizados por hackers utilizando concorrência múltipla de requisições ao servidor. Devido ao grande número de requisições, o servidor torna-se ocupado e fica indisponível para executar outros serviços aos usuários.

Os ataques de DoS fazem mais que apenas consumir a banda do servidor. Podem também consumir recursos limitados importantes tais como a memória, conexão a base de dados, elevar o tempo de resposta do processador, serviços ou recursos específicos da aplicação.

Normalmente é muito difícil proteger completamente a aplicação contra esse tipo de ataque, porque não existe uma solução 100% segura. Uma boa regra é limitar o número de recursos disponibilizados aos usuários ao mínimo necessário. Pode ser também uma boa idéia estabelecer cotas que determinam a carga que um usuário pode impor ao sistema.

Um exemplo dessa limitação de recursos é característica comum entre as implementações de bulletin boards. Limitam o usuário a executar operações de buscas uma vez a cada 20 segundos. Isto certifica que o usuário não irá consumir uma grande parcela de conexões à base de dados disponível.

Outra solução possível é projetar a aplicação WEB de forma tal que os usuários não autorizados tenham pouco ou nenhum acesso a conteúdo que necessite da base de dados ou algum outro recurso caro.

3.9. Gerenciamento inseguro de configurações

Usualmente o grupo de desenvolvimento da aplicação é diferente do grupo que possui a responsabilidade de administração do servidor que hospeda a aplicação. Infelizmente, há um limite de segurança que se pode ter na aplicação quando a segurança do servidor é em grande parte a determinante em como sua aplicação é considerada segura. Uma configuração imprópria no lado do servidor pode invalidar todos os esforços dos desenvolvedores para proteger a aplicação.

Estes são alguns erros em configurações de servidores que provam esta problemática:

• Falhas por Unpatched Software no servidor – os administradores não estão cientes de

Programação WEB 12

Page 177: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

liberações de novos releases de patches para o servidor. • Falhas de segurança no servidor que permitem a listagem de diretórios ou os chamados

“directory traversal attacks”.• Backups desnecessários ou arquivos de exemplo incluindo scripts, aplicações, arquivos de

configuração e páginas WEB. • Permissões impróprias de arquivos e diretórios. • Serviços desnecessários como administração remota ou gerenciamento de conteúdo

permitidos e esquecidos. • Usuários default com senhas default.• Acesso a funções administrativas ou de depuração.• Excessiva informação em mensagens de erro.• Configuração ruim de certificados SSL e parâmetros para encriptar.• Uso de auto-assinaturas em certificados para prover autenticações.• Uso de certificados default.• Autenticação imprópria com sistemas externos.

Programação WEB 13

Page 178: Projeto JEDI - Programação para WEB - Java - 178 páginas

JEDITM

Parceiros que tornaram JEDITM possível

Instituto CTSPatrocinador do DFJUG.

Sun MicrosystemsFornecimento de servidor de dados para o armazenamento dos vídeo-aulas.

Java Research and Development Center da Universidade das FilipinasCriador da Iniciativa JEDITM.

DFJUGDetentor dos direitos do JEDITM nos países de língua portuguesa.

Banco do BrasilDisponibilização de seus telecentros para abrigar e difundir a Iniciativa JEDITM.

PolitecSuporte e apoio financeiro e logístico a todo o processo.

BorlandApoio internacional para que possamos alcançar os outros países de língua portuguesa.

Instituto Gaudium/CNBBFornecimento da sua infra-estrutura de hardware de seus servidores para que os milhares de alunos possam acessar o material do curso simultaneamente.

Programação WEB 14