Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Programação WEB
Lição 1
Introdução à Programação WEB
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).
Programação WEB 2
JEDITM
Auxiliadores especiais
Coordenação do DFJUG
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
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.
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:
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
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.
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.
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
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.
É 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.
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.
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
Programação WEB 10
JEDITM
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.
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.
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.
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
Contém arquivos JAR de bibliotecas que podem ser utilizadas pela aplicação
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
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.
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.
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.*;
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
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.
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:
Programação WEB 6
JEDITM
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.
Após criar um novo projeto WEB, a tela será semelhante a figura a seguir:
Programação WEB 7
JEDITM
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.
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.
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.
Programação WEB 9
JEDITM
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
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).
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
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>
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();
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>
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.
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()
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
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.*;
Programação WEB 18
JEDITM
read = is.read(bufferArray);
}
// fechar os objetos utilizados
is.close();
os.close();
}
}
Programação WEB 19
JEDITM
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.
Observaremos novamente a estrutura de uma aplicação WEB como indicada pela especificação
Servlet:
Contém arquivos de bibliotecas (JAR) que podem ser utilizadas pela aplicação
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
A IDE informará se a operação de construção foi bem sucedida, e o local onde o arquivo WAR foi
criado.
Programação WEB 22
JEDITM
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
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.
Programação WEB 24
JEDITM
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>
...
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>.
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
<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.
<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.
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
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.
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
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);
}
}
Ambas servlets definidas acima usam uma página denominada login.html para formar a base da
Programação WEB 5
JEDITM
Ao executar a primeira Servlet, a que utiliza o método include, a seguinte saída é gerada:
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;
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");
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.
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.
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
// 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.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();
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
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.
Programação WEB 13
JEDITM
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"
}
}
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");
}
}
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.
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.
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;
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;
Programação WEB 18
JEDITM
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>
Programação WEB 19
JEDITM
6. Exercícios
6.1. Exercícios sobre redirecionamento de respostas
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:
Se houvesse uma ReceiverServlet mapeada para o local indicado dentro do método sendRedirect,
seria possível recuperar a mensagem? Justifique?
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:
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.
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
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
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.
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.
• 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.
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.
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.
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.
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]
Programação WEB 6
JEDITM
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
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
Programação WEB 7
JEDITM
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);
}
}
}
Programação WEB 8
JEDITM
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>
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
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
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 %>
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
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.*;
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>
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
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" %>
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
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.
é
<jsp:directive.page import="java.util.*" />
Por exemplo, para incluir uma barra de menu encontrada no diretório corrente, a diretiva será
escrita da seguinte forma:
<%@ include file="menubar.jsp"%>
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.
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 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;
}
}
Programação WEB 17
JEDITM
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.
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;
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;
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.
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
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
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.
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.
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.
● 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:
● 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:
● 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:
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.
● Pesquisar todas as linhas da tabela users com o nome iniciado por 'S'.
SELECT * from users where name like 'S%';
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.
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
É 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.
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.
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.
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
Programação WEB 12
JEDITM
...
Context ctxt = null;
DataSource ds = null;
try {
// criar uma instância de um contexto JNDI
ctxt = new InitialContext();
Uma vez que temos um exemplo de instância DataSource válido, pequemos um objeto
Connection tão simples quanto.
Connection conn = ds.getConnection();
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
Programação WEB 13
JEDITM
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");
}
Programação WEB 14
JEDITM
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
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.
Programação WEB 17
Módulo 6
Programação WEB
Lição 6
Páginas JSP Avançadas
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
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 (==, !=, <, <=, >, >=).
Programação WEB 5
JEDITM
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}
Programação WEB 6
JEDITM
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.
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.
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.
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" %>
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"/>
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);
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>
Programação WEB 11
JEDITM
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>
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:
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
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
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.
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
2.4. View
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
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:
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.*;
Programação WEB 9
JEDITM
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.*;
Programação WEB 10
JEDITM
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.
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
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.
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>.
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.
Programação WEB 16
JEDITM
Programação WEB 17
Módulo 6
Programação WEB
Lição 8
Tópicos avançados no Framework Struts
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.
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.
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
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.
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 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
Programação WEB 9
JEDITM
<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.*;
@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");
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.
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:
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.
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" ?>
Programação WEB 13
JEDITM
<tiles-definitions>
</tiles-definitions>
Após a realização desses passos, o Tiles está pronto para ser usado.
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
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.
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.
<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
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.
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.
Programação WEB 16
JEDITM
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.
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.
• 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
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
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>
...
Programação WEB 6
JEDITM
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.
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)
// 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>
Programação WEB 9
JEDITM
<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.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
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.
<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.
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.
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.
Programação WEB 13
JEDITM
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.
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
<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>
<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;
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
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.
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.
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();
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
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.
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.
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.
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>
<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.
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.
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;
<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>
<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
3.1. DateTimeConverter
Programação WEB 11
JEDITM
<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>
Programação WEB 12
JEDITM
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:
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.
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.
Programação WEB 15
JEDITM
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>
Programação WEB 16
Módulo 6
Programação WEB
Lição 11
Segurança na WEB
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.
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.
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
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.
Programação WEB 6
JEDITM
A seguir, selecione a opção HTTP Listeners e, no painel à direita, pressione o botão New.
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
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
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.
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
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
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.
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 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.
Programação WEB 11
JEDITM
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.
Programação WEB 12
JEDITM
Programação WEB 13
JEDITM
Instituto CTS
Patrocinador do DFJUG.
Sun Microsystems
Fornecimento de servidor de dados para o armazenamento dos vídeo-aulas.
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