You are on page 1of 178

Módulo 6

Programação WEB

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

Versão 1.0 - Nov/2007


JEDITM

Autor Necessidades para os Exercícios


Daniel Villafuerte Sistemas Operacionais Suportados
NetBeans IDE 5.5 para os seguintes sistemas operacionais:
• Microsoft Windows XP Profissional SP2 ou superior
• Mac OS X 10.4.5 ou superior
Equipe • Red Hat Fedora Core 3
Rommel Feria • Solaris™ 10 Operating System (SPARC® e x86/x64 Platform Edition)
John Paul Petines 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 Hardware


Nota: IDE NetBeans com resolução de tela em 1024x768 pixel
Sistema Operacional Processador Memória HD Livre
Microsoft Windows 500 MHz Intel Pentium III 512 MB 850 MB
workstation ou equivalente
Linux 500 MHz Intel Pentium III 512 MB 450 MB
workstation ou equivalente
Solaris OS (SPARC) UltraSPARC II 450 MHz 512 MB 450 MB
Solaris OS (x86/x64 AMD Opteron 100 Série 1.8 GHz 512 MB 450 MB
Platform Edition)
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 1 GB 1 GB
workstation ou equivalente
Linux 1.4 GHz Intel Pentium III 1 GB 850 MB
workstation ou equivalente
Solaris OS (SPARC) UltraSPARC IIIi 1 GHz 1 GB 850 MB
Solaris OS (x86/x64 AMD Opteron 100 Series 1.8 GHz 1 GB 850 MB
Platform Edition)
Mac OS X PowerPC G5 1 GB 850 MB

Requerimentos de Software
NetBeans 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
JEDITM

Colaboradores que auxiliaram no processo de tradução e revisão


Aécio Júnior Denis Mitsuo Nakasaki Massimiliano Giroldi
Alexandre Mori Emanoel Tadeu da Silva Freitas Mauro Cardoso Mortoni
Alexis da Rocha Silva Felipe Gaúcho Paulo Oliveira Sampaio Reis
Allan Souza Nunes Jacqueline Susann Barbosa Pedro Henrique Pereira de Andrade
Allan Wojcik da Silva João Vianney Barrozo Costa Ronie Dotzlaw
Angelo de Oliveira Luciana Rocha de Oliveira Sergio Terzella
Aurélio Soares Neto Luiz Fernandes de Oliveira Junior Thiago Magela Rodrigues Dias
Bruno da Silva Bonfim Marco Aurélio Martins Bessa Vanessa dos Santos Almeida
Carlos Fernando Gonçalves Maria Carolina Ferreira da Silva Wagner 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 Especial
John Paul Petines – Criador da Iniciativa JEDITM
Rommel Feria – Criador da Iniciativa JEDITM

Programação WEB 3
JEDITM

1. Objetivos
Bem-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
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
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

Resposta do servidor (contém o


documento requisitado ou um
código de erro caso nada seja Servidor
encontrado) processa as
repostas com
base nas
requisições
do cliente

Requisição do cliente
(contém o nome e o
endereço de um item que o
Máquina rodando cliente está procurando)
um Browser
Máquina rodando
um WEB Server

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

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

6. Java EE – Visão sobre a camada WEB


A 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.

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

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

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

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

7. Estrutura Básica de uma aplicação WEB


Para 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.

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

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
JEDITM

8. Exercício
Responda à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
Módulo 6
Programação WEB

Lição 2
Classes Servlets

Versão 1.0 - Nov/2007


JEDITM

1. Objetivos
Uma 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
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
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
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
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
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
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
JEDITM

3. Ciclo de Vida da Classe Servlet


Uma 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
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
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
JEDITM

4. Manipulando Requisições e Respostas


O 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
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
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
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.net
request.getServerPort() 8080
request.getContextPath() HelloApp
request.getPathInfo() greetUser
request.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
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
JEDITM

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

Programação WEB 19
JEDITM

5. Configuração, Empacotamento e Distribuição


Em 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
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:

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

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

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.

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
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
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
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
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
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-config
servlet
servlet-mapping
welcome-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
Módulo 6
Programação WEB

Lição 3
Classes Servlets Avançadas

Versão 1.0 - Nov/2007


JEDITM

1. Objetivos
Na 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
JEDITM

2. Redirecionamento de Resposta
Existem 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
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
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
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
JEDITM

3. Objetos de Escopo
A 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
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
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
JEDITM

4. Rastreabilidade e Gerência de Sessão


Recordemos 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ão
HashMap map = new HashMap();

// recuperar um mapa utilizado como contêiner para as informações do usuário


HashMap 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
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
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
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
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
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
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
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
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
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
Módulo 6
Programação WEB

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

Versão 1.0 - Nov/2007


JEDITM

1. Objetivos
Nas 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
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
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:

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 do
documento de seu projeto.
Diretório contendo arquivos de configuração como o descritor de desenvolvimento
(web.xml), descritores de biblioteca de tags, etc.

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

Programação WEB 6
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
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
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
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
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
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
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
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
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 <%@ page extends =
o servlet. Analogamente às extensões das palavras- "com.taglib…"%>
chave da linguagem de programação em Java.

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

import Importa pacotes de classes java para a página JSP <%@ page
corrente. import="java.util.*"%>

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

buffer Controla o uso dos buffers de saída da página JSP. Se <%@ page buffer="none"%>
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.

Programação WEB 15
JEDITM

Diretiva Descrição Exemplo de uso


autoFlush Quando atribuído true esvazia o buffer de saída logo <%@ page
quando ele ficar cheio. 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 <%@ page info="jedi.org
informações e documento para a página. Tipicamente test page, alpha version
é usado para informar o nome autor do código, "%>
versão, copyright e datas.

errorPage Define qual a pagina que será usada para tratar os <%@ page
erros, deve ser um endereço URL para uma página de errorPage="/errors.jsp"%>
erro.

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

5. Exercícios
1) 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
Módulo 6
Programação WEB

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

Versão 1.0 - Nov/2007


JEDITM

1. Objetivos
A 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
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 8687243217
Figura 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
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 contactnum userid downloaditem downloaddate


14627895 Duke San Francisco 0924562788 14627895 Courseware Notes Dec. 19, 2005
65248987 Kimi Finland 8687243217 36542036 Exercises Feb. 11, 2006
84321874 Dante San Jose 6365498428 84321874 Slides March 13, 2006

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

Programação WEB 6
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 downloaddate


14627895 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 downloaddate


14627895 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 downloaddate


14627895 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 downloaddate


14627895 Duke San Francisco 0924562788 14627895 Courseware Notes Dec. 19, 2005
84321874 Dante San Jose 6365498428 84321874 Slides March 13, 2006

Programação WEB 7
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
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
JEDITM

4. JDBC
Java 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
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
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
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
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
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
JEDITM

5. Exercício
5.1. Usuários

Considere a seguinte tabela:


USERS
userid 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
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
Módulo 6
Programação WEB

Lição 6
Páginas JSP Avançadas

Versão 1.0 - Nov/2007


JEDITM

1. Objetivos
Em 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
JEDITM

2. Linguagem de expressão das páginas JSP


O 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
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
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
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
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
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
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
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
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
JEDITM

4. Exercícios
1) 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
Módulo 6
Programação WEB

Lição 7
Introdução a MVC e ao Framework Struts

Versão 1.0 - Nov/2007


JEDITM

1. Objetivos
A 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
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.

- Encapsular estado das aplicações


- Responder ao estado das consultas
- Expor as funcionalidades da aplicação
- Notificar mudanças na view

- Solicitar a model - Definir a integração da aplicação


- Requerer alterações para a model - Mapear as ações do usuário
- Enviar solicitações para o controller - Selecionar a view para resposta
- Permitir o controle de um view - Um para cada funcionalidade

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

3. Arquitetura MVC para a WEB: A Arquitetura Model 2


A 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
JEDITM

4. STRUTS
Struts é 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
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
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
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
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
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
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
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
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
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
Módulo 6
Programação WEB

Lição 8
Tópicos avançados no Framework Struts

Versão 1.0 - Nov/2007


JEDITM

1. Objetivos
Na 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
JEDITM

2. DynaActionForms
Em 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
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
JEDITM

3. Validadores
Validaçã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
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
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
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
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
JEDITM

4. Tiles
Outro 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
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
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
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
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
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
JEDITM

5. Exercícios
O 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
Módulo 6
Programação WEB

Lição 9
MVC Frameworks II – JavaServer Faces

Versão 1.0 - Nov/2007


JEDITM

1. Objetivos
Na 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
JEDITM

2. Introdução ao Framework JSF


JSF é 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
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
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
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
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
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
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
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
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
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
JEDITM

3. Outros componentes de tags JSF


Existem 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
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
JEDITM

4. Exercícios
Uma 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
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
Módulo 6
Programação WEB

Lição 10
Tópicos Avançados de JavaServer Faces

Versão 1.0 - Nov/2007


JEDITM

1. Objetivos
Na 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
JEDITM

2. FacesContext
Na 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
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
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
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
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
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
JEDITM

3. Componentes de Conversão
Os 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ão
BigDecimalConverter BigDecimal
BigIntegerConverter BigInteger
IntegerConverter Integer
ShortConverter Short
ByteConverter Byte
ShortConverter Short
CharacterConverter Character
FloatConverter Float
DoubleConverter Double
BooleanConverter Boolean
DateTimeConverter DateTime
NumberConverter 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
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
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
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
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
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
Módulo 6
Programação WEB

Lição 11
Segurança na WEB

Versão 1.0 - Nov/2007


JEDITM

1. Objetivos
Este 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
JEDITM

2. SSL
O 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
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
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
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
JEDITM

3. Falhas de Segurança em Aplicações WEB


A 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
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
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
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
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
JEDITM

Parceiros que tornaram JEDITM possível

Instituto CTS
Patrocinador do DFJUG.

Sun Microsystems
Fornecimento de servidor de dados para o armazenamento dos vídeo-aulas.

Java Research and Development Center da Universidade das Filipinas


Criador da Iniciativa JEDITM.

DFJUG
Detentor dos direitos do JEDITM nos países de língua portuguesa.

Banco do Brasil
Disponibilização de seus telecentros para abrigar e difundir a Iniciativa JEDITM.

Politec
Suporte e apoio financeiro e logístico a todo o processo.

Borland
Apoio internacional para que possamos alcançar os outros países de língua
portuguesa.

Instituto Gaudium/CNBB
Fornecimento 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