1ª parte A Technologia Java Server Pages Revendo a Aplicação de Compras On-line O que é uma Página JSP? Uma página JSP é uma página contendo HTML, WML, XML, ... com trechos de programas Java (elementos JSP) embutidos Simplificam a geração de conteúdo dinâmico porque Web Designers pode manipular as páginas com mais facilidade do que manipulando servlets A página JSP é automaticamente transformada em servlet e o servlet executa no servidor para gerar a resposta
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
1ª parte
A Technologia Java Server Pages
Revendo a Aplicação de Compras On-line
O que é uma Página JSP?
Uma página JSP é uma página contendo HTML, WML, XML, ... com trechos de programas Java (elementos JSP) embutidos Simplificam a geração de conteúdo dinâmico porque
Web Designers pode manipular as páginas com mais facilidade do que manipulando servlets
A página JSP é automaticamente transformada em servlet e o servlet executa no servidor para gerar a resposta
Segue abaixo um exemplo de uma aplicação com uma única página JSP Execute a aplicação
aqui: http://anjinho.dsc.ufpb.br:8000/data A aplicação mostra a data de acordo com várias
locales Seu browser poderá pedir a instalação do alfabeto
The date in <b><%=locale.getDisplayName()%></b> is
<b><%=date.getDate()%></b>
<% } %>
</body>
</html>
Segue o código de MyLocales.java
import java.util.*;
import java.text.DateFormat;
public class MyLocales {
HashMap locales;
ArrayList localeNames;
DateFormat dateFormatter;
public MyLocales() {
locales = new HashMap();
localeNames = new ArrayList();
Locale list[] = DateFormat.getAvailableLocales();
for (int i = 0; i < list.length; i++) {
locales.put(list[i].getDisplayName(), list[i]);
localeNames.add(list[i].getDisplayName());
}
Collections.sort(localeNames);
}
public Collection getLocaleNames() {
return localeNames;
}
public Locale getLocale(String displayName) {
return (Locale)locales.get(displayName);
}
}
Composição da aplicação DataApp
Compile tudo
C:\... >ant data
Buildfile: build.xml
init:
data:
[mkdir] Created dir: C:\...\build\data
[copy] Copying 3 files to C:\...\build\data
[javac] Compiling 2 source files to C:\...\build\data
BUILD SUCCESSFUL
Total time: 11 seconds
Chame o deploytool e execute as seguintes ações Criar a aplicação chamada DataApp
Selecione File->New->Application No file chooser, navegue até to src/data No campo "File Name", digite DataApp Clique em "New Application" Clique em "OK"
Criar o WAR e adicionar o Web Component à aplicação DataApp Selecione File->New->Web Component Selecione "DataApp" no combo box "Create new
WAR File in Application" Digite DataWAR no campo "WAR Display Name" Clique em "Edit" Navegue até build/data. Selecione index.jsp,
date.jsp, MyDate.class e MyLocales.class e clique em "Add" e então em "Finish"
Clique em "Next" Clique em "JSP" no radio button "Web Component"
e clique em "Next" Selecione index.jsp no combo box "JSP Filename" e
clique em "Finish" Fornecer a raiz do contexto (context root)
Selecione DataApp Selecione a orelha "Web Context" Digite "data"
Salve
Deployment da aplicação DataApp
Selecionar Tools/Deploy e faça o deployment da aplicação no servidor desejado
Execução da aplicação DataApp
Execute a aplicação aqui: http://anjinho.dsc.ufpb.br:8000/data
Composição e Deployment da Aplicação de
Compras On-line
Os exemplos de páginas JSP que veremos são baseados na aplicação de compras on-line (Duke's Bookstore), refeita para usar páginas JSP
As páginas usadas são mostradas na tabela abaixo
Funcionalidade Páginas JSP
Entrar na livraria bookstore.jsp
Criar o banner da livraria banner.jsp
Examinar o catálogo catalog.jsp
Adicionar um livro à cesta de compras catalog.jsp
bookdetails.jsp
Receber informação detalhada sobre um livro bookdetails.jsp
Mostrar a cesta de compras showcart.jsp
Remover um ou mais livros da cesta de compras
showcart.jsp
Comprar os livros presentes na cesta de
compras cashier.jsp
Receber uma confirmação de pedido receipt.jsp
Continuamos usando o banco de dados jdbc/BookDB Porém database.BookDB foi reescrito para ser um
JavaBean (não EJB) Desta forma, podemos usar melhor os recursos JSP
(usebean) database.BookDB não acessa o banco de dados
diretamente mas através de um Enterprise Bean (EJB) Veremos como EJBs funcionam adiante Por enquanto, esqueça da parte de acesso ao BD
Outra mudança é que a aplicação usa um applet para exibir um relógio digital
Antes de ver o código, vamos fazer a aplicação rodar ...
[javac] Compiling 6 source files to C:\...\build\webejb
livrosjsp:
[mkdir] Created dir: C:\...\build\livrosjsp
[copy] Copying 11 files to C:\...\build\livrosjsp
[javac] Compiling 8 source files to
C:\...\build\livrosjsp
[war] Building war:
C:\...\build\livrosjsp\livrosjsp.war
BUILD SUCCESSFUL
Total time: 11 seconds
Inicie o servidor J2EE (j2ee -verbose) Inicie o banco de dados (cloudscape -start)
Caso não tenha criado o banco de dados de livros, veja aqui
Este é o mesmo banco de dados usado com servlets (BookDB)
Inicie o deploytool Criar uma aplicação J2EE chamada LivrosJSPApp
Selecione File->New->Application No file chooser, navigue até src/livrosjsp No campo "File Name", digite LivrosJSPApp Clique em "New Application" Clique em OK
Adicione Livrosjsp.war à aplicação LivrosJSPApp Selecione File->Add to application->Web WAR No dialogo, navegue até build/livrosjsp. Selecione
livrosjsp.war. Clique em "Add Web WAR" Adicione o Enterprise Bean BookDBEJB à aplicação
Selecione File->New Enterprise Bean ou o botão "New Enterprise Bean"
Na combo box "Create New JAR File in Application", selecione LisvrosJSPApp
No campo "JAR Display Name", digite BookDBJAR Clique em Edit para adicionar arquivos ao conteúdo Neste dialog box (Edit Contents), navegue até o
diretório build/webejb e adicione os packages database e exception. Clique em OK e clique em Next
Escolha Session e Stateless como tipo de Bean Em Enterprise Bean Class, selecione
database.BookDBEJBImpl Na caixa "Remote Interfaces", selecione
database.BookDBEJBHome para "Remote Home Interface" e database.BookDBEJB para "Remote Interface"
No campo "Enterprise Bean Name", digite BookDBEJB Clique em Next e Clique em Finish
Adicione a BookDBEJB uma referência de recurso para o banco de dados Selecione o enterprise bean BookDBEJB Selecione a orelha "Resource Refs" Clique em Add Selecione javax.sql.DataSource na coluna Type Digite jdbc/BookDB no campo "Coded Name" Digite jdbc/BookDB no campo "JNDI Name"
Salve o BookDBJAR Selecione BookDBJAR Selecione "File-Save As" Navegue até o diretório build/webejb Digite bookDB.jar no campo "File name" Clique em "Save EJB JAR As"
Adicione uma referência á Enterprise Bean BookDBEJB Selecione LivrosJSPWAR Selecione a orelha "EJB Refs" Clique em Add
Digite ejb/BookDBEJB na coluna "Coded Name" Selecione Session na coluna Type Selecione Remote na coluna Interfaces Digite database.BookDBEJBHome na coluna "Home
Interface" Digite database.BookDBEJB na coluna "Local/Remote
Interface" Especifique nomes JNDI
Selecione LivrosJSPApp Selecione a orelha "JNDI Names" Na tabela "Application", localize o componente EJB e
digite BookDBEJB na coluna "JNDI Name" Na tabela "References", localize "EJB Ref", e digite
BookDBEJB na coluna "JNDI Name" Na tabela "References", locate o componente
"Resource" e digite jdbc/BookDB na coluna "JNDI Name"
Forneça o "context root" Selecione a orelha "Web Context" Enter ireallylovebooks
Salve Faça deployment da aplicação
Selecione Tools->Deploy Clique em Finish
Abra a URL da livraria http://anjinho.dsc.ufpb.br:8000/ireallylovebooks/enter A primeira navegação numa página JSP é mais lenta
pois o servlet está sendo criado e compilado, antes da execução
O Ciclo de Vida de uma Página JSP
Ao chamar uma página JSP, um servlet especial verifica se página é mais nova do que o servlet que a representa Se for, o servlet é regerado a partir da JSP e
recompilado Isso ocorre automaticamente Necessário para alterar aplicações sem desligar o
Portanto, uma página JSP é, na realidade, um servlet e muito da discussão sobre servlets se aplica aqui
Tradução e Compilação
A tradução de uma página JSP para um servlet ocorre de acordo com as seguintes regras básicas: Texto fora dos elementos JSp são impressos com
out.println(...) Diretivas <%@ ... %> controlam como o Web
Container traduz e executa a página JSP "Executar a página" significa "executar o servlet
gerado a partir da página" Elementos de script são inseridos em Java no código
fonte do servlet Veja detalhes adiante
Elementos como <jsp:XXX ... /> são convertidos em chamadas de métodos para componentes JavaBeans (não EJB) ou chamadas à API de servlet
Depois da tradução, compilação da página e carga do servlet: O método jspInit() é chamado
É comum fornecer código para este método para inicializar a página
Vide adiante O método _jspService() é chamado Quando o servlet é removido, jspDestroy() é chamado
Execução
As diretivas "page" podem controlar a execução da página
Bufferização
Há bufferização automática O seguinte método pode alterar o parâmetros do buffer
<%@ page buffer="none|xxxkb" %>
Tratamento de erros
Exceções podem ocorrer durante a execução da páginas JSP
A diretiva seguinte diz o que deve ser feito quando ocorre um erro
<%@ page errorPage="file_name" %>
Nossa aplicação usa a seguinte diretiva:
<%@ page errorPage="errorpage.jsp"%>
No início de errorpage.jsp, há a diretiva seguinte que diz que a página está tratando um erro
<%@ page isErrorPage="true" %>
Esta diretiva disponibiliza o objeto de exceção (da classe javax.servlet.jsp.JspException) para a página de erro para que você possa tratar a exceção adequadamente
A errorpage.jsp completa aparece abaixo A variável "exception" representa um objeto implícito
disponibilizado pelo container
<%--
Copyright 2001 Sun Microsystems, Inc. All Rights Reserved.
This software is the proprietary information of Sun
Microsystems, Inc.
Use is subject to license terms.
--%>
<%@ page isErrorPage="true" %>
<%@ page import="java.util.*" %>
<%
ResourceBundle messages =
(ResourceBundle)session.getAttribute("messages");
if (messages == null) {
Locale locale=null;
String language = request.getParameter("language");
É também possível tratar a inicialização usando ContextListener como fizemos com servlets Exercício para casa: altere a aplicação para usar um
ContextListener
Criação de Conteúdo Estático
Conteúdo estático (digamos HTML) é simplesmente escrito na página JSP
O default é HTML, mas o atributo contentType pode ser mudado para informar o formato adequado
Por exemplo, para gerar WML:
<%@ page contentType="text/vnd.wap.wml"%>
Criação de Conteúdo Dinâmico
Para gerar conteúdo dinâmico, acessam-se objetos Java usando elementos de script
Usando Objetos em Páginas JSP
Vários objetos podem ser acessados a partir de uma página JSP Alguns desses objetos são automaticamente
disponibilizados pelo container (objetos implícitos) Outros objetos são específicos para sua aplicação
Objetos implícitos
São criados pelo container Contêm informação relacionada com um pedido, uma
página, uma sessão ou uma aplicação inteira A tabela abaixo sumariza esses objetos
Variável Classe Descrição
application javax.servlet.ServletContext
O contexto do servlet da
página JSP e de qualquer Web Component contidos na
mesma aplicação
config javax.servlet.ServletConfig Informação de inicialização
para o servlet da página JSP
exception java.lang.Throwable Acessível apenas a partir de
uma página de erro
out javax.servlet.jsp.JspWriter O stream de saída
page java.lang.Object
A instância do servlet da
página JSP processando o pedido atual. Raramente
usado por autores de páginas
JSP
pageContext javax.servlet.jsp.PageContext O contexto de uma página
JSP. Provê uma API única para
gerenciar atributos com
escopo. Esta API é muito usada ao implementar "tag
handlers" (ver em outro capítulo)
request subtipo de
javax.servlet.ServletRequest
O pedido gatilhando a
execução da página JSP
response subtipo de
javax.servlet.ServletResponse
A resposta retornada ao
cliente. Raramente usado por autores de páginas JSP
session javax.servlet.http.HttpSession O objeto de sessão com o cliente
Objetos específicos de aplicação
Não coloque business logic na página JSP! É melhor encapsular o business logic em objetos
Melhor que sejam beans para facilitar a escrita de página JSP
Isso permite que Page Designers se concentrem em questões de apresentação
Há 4 formas de criar objetos numa página JSP A classe de servlet da página JSP pode ter, como
qualquer classe, variáveis de instância (atributos) e variáveis de classe (estáticas) Tais variáveis são declaradas em declarações (vide
adiante) e acessadas em scriptlets e expressões (vide adiante)
Atributos de objetos de escopo (nos escopos ServletContext, HttpSession, ServletRequest e PageContext) são criados e usados em scriptlets e expressões
Componentes JavaBeans podem ser criados e acessados usando elementos JSP Ver próximo capítulo
Objetos compartilhados
O container pode iniciar páginas JSPs em servlets multithreaded ou não
Isso é indicado na sua página com a diretiva
<%@ page isThreadSafe="true|false" %>
Com "true", o container poderá despachar pedidos de clientes diferentes para essa página em threads diferentes
O default é "true" Cuidado! Com "false", você não precisa se preocupar
com o acesso simultâneo a objetos com escopo de página mas deve continuar a tratar da concorrência em objetos em escopos de sessão e aplicação
Elementos de Script JSP
Elementos de scripts são usados para: Criar e acessar objetos Definir métodos Gerenciar o controle de fluxo
Um dos objetivos da tecnologia JSP é de separar os dados estáticos de templates HTML e o código necessário para gerar conteúdo dinâmico Portanto, evite programar na página JSP O uso de "custom tags", visto à frente, ajuda a
minimizar a programação A linguagem de script é Java mas pode ser qualquer
Usadas parar inserir no stream de saída um string correspondendo a uma expressão
<%= expressão na linguagem de script %>
Como exemplos, identifique o uso de expressões na página showcart.jsp, acima
Inclusão de Conteúdo numa Página JSP
Há duas formas de incluir conteúdo numa página JSP Durante a tradução da página Durante a execução da página
A inclusão durante a tradução é feita através de diretiva como já visto:
<%@ include file="initdestroy.jsp" %>
// ...
<%@ include file="banner.jsp" %>
A inclusão durante a execução é feita com elemento JSP:
<jsp:include page="date.jsp"/>
Este exemplo foi usado no exemplo "data", acima Quando usar a diretiva e quando usar o elemento JSP
para incluir? Nos casos acima, qualquer um serve Usar o elemento JSP é um pouco mais lento A decisão tem a ver com a freqüência de atualização
do recurso incluído (a página) Se você vai alterar o conteúdo da página incluída
com frequência, é melhor usar o elemento JSP pois a última versão sempre vai ser incluída
Isso só aconteceria com a diretiva se houvesse recompilação da página, o que não vai ocorrer automaticamente, porque a página original (que faz a inclusão) não foi alterada
Se a informação incluída mudar infrequentemente, pode usar a diretiva
Exemplo: se você tiver uma página mensagemDoDia.jsp, com uma mensagem que muda todos os dias e que é incluída em várias outras páginas JSP, pode ser mais conveniente usar o elemento JSP
Transferência de Controle para Outro Web Component
Antes de retornar informação para o cliente, uma página JSP pode transferir o controle para outra página:
<jsp:forward page="/main.jsp" />
Elemento Param
Num elemento "include" ou "forward", parâmetros adicionais (al´pem dos disponíveis no pedido original) podem ser passados:
<jsp:include page="..." >
<jsp:param name=”param1” value="value1"/>
</jsp:include>
Nossa aplicação usa <jsp:param ...>, mas em outro contexto que não veremos aqui (a inclusão de um applet)
Finalmente ...
Não mostraremos todo o código fonte da aplicação aqui: as novidades já foram tratadas
É responsabilidade do aluno estudar o código completo da aplicação
livros programa
Programação de Servlets
Aplicação de Compras On-line
Escreveremos uma aplicação mais completa e que usará recursos avançados de servlets
Em particular, queremos saber como: Escrever filtros que processam em estágios a
informação recebida/retornada por um servlet Manter estado do cliente, isto é, criar o conceito de
uma sessão numa aplicação
O Problema: Aplicação de Compras On-line
A aplicação é de compra de livros on-line Deve-se exibir o catálogo O catálogo deve poder fornecer detalhes sobre um livro Deve-se permitir colocar itens numa cesta de compras Deve ser possível comprar mais de uma cópia de um
livro Deve haver uma forma de verificar o conteúdo da cesta
de compras Deve-se permitir remover itens da cesta de compras Deve-se permitir que o usuário se encaminhe para o
caixa para pagar os livros Para efetuar a compra, o usuário deve fornecer seu
nome e número de cartão de crédito A aplicação não precisa contactar um site de aprovação
de crédito mas deve manter um log de cada compra A primeira página deve fornecer o link de um livro que o
staff da livraria está lendo A informação mantida para cada livro é:
Primeiro nome do autor Título do livro O preço do livro O ano de publicação do livro Uma descrição do livro
A aplicação deve exibir páginas em inglês ou português ou espanhol, dependendo das preferências do browser do usuário
Os preços dos livros devem ser exibidos na moeda local, isto é dependendo das preferências do browser do usuário Não faz sentido o preço do livro se US$10.75 para um
americano e R$10,75 para um brasileiro Porém, só queremos mostrar como tratar o assunto
de internacionalização de moedas Todas as páginas exibidas devem iniciar com um banner
comum A primeira página deve fornecer um contador de hits de
visitas Deve haver um log das visitas Uma página de erro adequada deve ser exibida na
ocorrência de problemas
A Demo
Exercite a aplicação aqui http://anjinho.dsc.ufpb.br:8000/ilovebooks/enter Isso é uma máquina Linux
(Tomará que o professor tenha deixado a aplicação executando antes da aula ...) Caso contrário, o presente material mostra como
implementar e fazer o deployment da aplicação e a demo poderá ser realizada no final
A aplicação poderá aparecer em 1 de 3 línguas, dependendo das preferências de linguagem do browser Altere as preferências de linguagem do browser para
verificar que a aplicação exibe páginas em várias línguas
Para entendermos a solução, temos que aprender alguns detalhes adicionais sobre como servlets funcionam
Mais informação sobre o ServletContext
Já falamos do ServletContext antes Ele serve basicamente para armazenar informação
relativa à aplicação como um todo Em particular, o ServletContext é usado para:
Conter parâmetros de inicialização da aplicação Armazenar recursos associados à aplicação
Uma conexão de Banco de Dados, por exemplo Armazenar qualquer atributo da aplicação como
objetos Fornecer acesso à funcionalidade de logging
Este último item é importante: Como fazer para depurar uma aplicação que executa
no servidor? Como fazer para logar informação por parte da
aplicação? Ambas as coisas podem ser feitas como segue:
contexto.log(String memnsagem);
Aqui, o "contexto" se refere ao ServletContext Ele pode ser obtido de várias formas:
// num Servlet qualquer
contexto = getServletContext();
// num ContextListener que recebeu um evento
contexto = event.getServletContext();
// num filtro (ver adiante)
contexto = filterConfig.getServletContext();
Onde vai o log? Depende do servidor sendo usado
Usando o J2EESDK que estou usando agora no Windows 2000, o arquivo é <J2EE_HOME>\logs\jpsauve\web\catalina.<data-de-hoje>.log
usando o J2SDKEE numa máquina Linux, o arquivo é /usr/local/j2sdkee1.3/logs/anjinho.dsc.ufpb.br/web/catalina.<data-de-hoje>.log
Definição de páginas de erro
Nas aplicações anteriores, a página padrão de erro gerada automaticamente quando o container recebe uma exceção do servlet é semelhante à página abaixo A página abaixo foi obtida ao listar os pedidos de
suporte com o Banco de Dados fora do ar
Gostaríamos de associar uma página de erro mais adequada às condições de erro
Esta associação deve ser feita durante a montagem da aplicação, usando o deploytool
Observe que o servlet deve bufferizar toda informação gerada na página porque pode haver uma exceção e a página de erro não deve ser enviada depois que metade da página "normal" já foi enviada
Incluindo o conteúdo de outro recurso na
resposta
Um servlet pode diretamente incluir outro recurso enquanto está executando
As duas formas de fazer isso são: Incluir o conteúdo de outro recurso Encaminhar (forward) o pedido para outro recurso
Usaremos a primeira forma para incluir um banner comum em todas as páginas
O primeiro passo para fazer umas dessas duas chamadas é de obter o RequestDispatcher do recurso desejado Depois, é só chamar o método include(...) do
RequestDispatcher
// Obtém o dispatcher; ele vai mandar o banner para o
O código acima será incluído em todos os servlets da aplicação
O alias "/banner" leva ao BannerServlet: Observe que podemos despachar o BannerServlet
tanto a partir de um método doGet ou doPost do servlet original
Por isso, o BannerServlet implementa service() em vez de doGet e doPost Normalmente, service() da classe mãe chama
doGet() ou doPost()
import java.io.*;
import java.util.*;
import java.sql.*;
import javax.servlet.*;
import javax.servlet.http.*;
import database.*;
import cart.*;
public class BannerServlet extends HttpServlet {
public void service (HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
PrintWriter out = response.getWriter();
out.println("<body bgcolor=\"#ffffff\">" +
"<center>" +
"<hr> <br> " +
"<h1>" +
"<font size=\"+3\" color=\"#CC0066\">Duke's
</font> <img src=\"" + request.getContextPath() +
"/duke.books.gif\">" +
"<font size=\"+3\"
color=\"black\">Bookstore</font>" +
"</h1>" +
"</center>" +
"<br> <hr> <br> ");
}
}
Observe a montagem do string para a imagem gif:
request.getContextPath() + "/duke.books.gif"
getContextPath é o path da aplicação na URL, no nosso caso: /ilovebooks
Isso é escolhido no deployment da aplicação
Internacionalização
Para tratar da internacionalização da informação, usaremos recursos do Java que não são particulares a servlets
Locales
A internacionalização de aplicações se baseia no conceito de locale
Uma Locale representa uma região específica, seja do ponto de vista geográfico, político ou cultural
Uma Locale consiste de até três partes:
Língua País Variante
Uma Locale pode ser mais genérica e não incluir Variante e/ou não incluir País
A língua é especificada com um código ISO639 de duas letras minúsculas pt: português es: espanhol etc.
O país é especificado com um código ISO3166 de duas letras maiúsculas BR: Brasil US: Estados Unidos
Os códigos de variantes são específicos a fabricantes e browsers WIN para Windows MAC para MacIntosh
Quando você navega usando um browser, este indica no pedido quais são as locales aceitáveis para o usuário Pode ter mais de uma locale configurada no browser A primeira é a mais importante Exemplo: meu browser está configurado para a Locale
Observações A Locale do browser é obtida do objeto request Sabendo a locale desejada, o ResourceBundle é obtido Este ResourceBundle contém as mensagens da
aplicação na língua apropriada
Já que queremos uma língua para uma sessão inteira, armazenamos o bundle no objeto session, com escopo de sessão (vide discussão de sessão adiante)
Exercício: Implemente a inicialização do ResourceBundle usando HttpSessionActivationListener, o que parece ser uma solução mais limpa e mais genérica Temos um bug na nossa implementação: se eu
não passar por BookStoreServlet e for para outro servlet, as mensagens não estarão inicializadas
Veja o que é a interface HttpSessionActivationListener na documentação
Não sei se é possível resolver com Listener de sessão: investigue! Pode cair num miniteste!
Ache pelo menos uma outra forma de resolver este bug. Tem que cheirar bem.
Formatação de Números
A formatação de moedas de acordo com a Locale pode ser vista abaixo:
package util;
import java.text.NumberFormat;
import java.util.*;
public class Currency {
private Locale locale;
private double amount;
public Currency() {
locale = null;
amount = 0.0;
}
public synchronized void setLocale(Locale l) {
locale = l;
}
public synchronized void setAmount(double a) {
amount = a;
}
public synchronized String getFormat() {
NumberFormat nf =
NumberFormat.getCurrencyInstance(locale);
return nf.format(amount);
}
}
Um servlet que queira tratar moedas o fariam assim: O servlet trata do preço de um livro
public class BookDetailsServlet extends HttpServlet {
// ...
// na inicialização
Currency c = (Currency)session.getAttribute("currency");
if (c == null) {
c = new Currency();
c.setLocale(request.getLocale());
session.setAttribute("currency", c);
}
// ...
// quando se deseja imprimir o valor de um livro a partir
do banco de dados
c.setAmount(bd.getPrice());
out.println(... + c.getFormat() + ...);
Filtros de Pedidos e Respostas
Mostraremos agora uma forma de afetar a ação de um servlet, mas sem que esse saiba!
Podemos montar cadeias de servlets que processam a informação gerada para o cliente
A cadeia consiste de "filtros" A palavra filtro á apropriada porque podemos montar
um pipeline de filtros para gerar a informação final
Na figura acima, estamos vendo o fluxo de informação até ser entregue ao cliente Como se pode ver, cada filtro pode atuar antes do
Web Component (no fluxo indo para a direita), oudepois do Web Component (no fluxo indo para a esquerda)
O que realmente ocorre é que cada filtro chama o da direita e pode atuar antes da chamada ou depois dela
Filtros são diferentes de Web Components porque eles não geram uma resposta completa Eles normalmente provêem funcionalidade que pode
ser "amarrada" a qualquer Web Component Portanto, o filtro não deve conhecer nada sobre os Web
Components com os quais agirá Eles poderão assim ser compostos com vários tipos de
Web Components Usos típicos de filtros:
Logar informação Ativar um mecanismo de segurança antes de executar
um servlet Converter o formato da saída de um servlet
Usaremos filtros para realizar três tarefas na nossa aplicação: Colocar o contador de visitas na primeira página Logar as visitas Logar as compras
Operação de um filtro Normalmente o Web Component obtém um Writer do
parâmetro "response" e escreve a resposta Para que filtros possam funcionar, precisamos
enganar o Web Component e entregar um objeto que ele acha é a "response" mas que, na realidade, ainda poderá ser manipulado pelo filtro depois que a reposta foi gerada
Para resolver isso, usa-se o Design Pattern "Decorator" ou "Wrapper" Um objeto envolve o objeto "response" original e
obedece à mesma interface Assim, o Web Component acha que está enviando a
resposta para o cliente mas a está entregando a um outro objeto que a bufferiza, permitindo que o filtro a examine e a altere
Por exemplo, aqui está um decorador que engana e bufferiza a resposta num array: O "engano" está sendo feito em getWriter() Em vez de entregar o Writer que vai para o cliente,
entrega-se outro Writer que bufferiza a resposta
package filters;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
public class CharResponseWrapper extends
HttpServletResponseWrapper {
private CharArrayWriter output;
public String toString() {
return output.toString();
}
public CharResponseWrapper(HttpServletResponse response) {
super(response);
output = new CharArrayWriter();
}
public PrintWriter getWriter() {
return new PrintWriter(output);
}
}
Agora, podemos ver o HitCounterFilter
package filters;
import java.io.*;
import java.sql.Timestamp;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import util.Counter;
public final class HitCounterFilter implements Filter {
private FilterConfig filterConfig = null;
public void init(FilterConfig filterConfig) throws
StringBuffer sb = new StringBuffer("HitCounterFilter(");
sb.append(filterConfig);
sb.append(")");
return (sb.toString());
}
}
Observações O filtro é inicializado com filterConfig que permite
acessar o ServletContext doFilter é onde toda a ação ocorre O hitCounter é um objeto de escopo de aplicação
armazenado no ServletContext O número de hits é logado pelo filtro Cada filtro deve chamar o doFilter do elemento
seguinte na cadeia Este filtro chama o doFilter do elemento seguinte
com um wrapper no lugar do objeto response original
Depois que o método doFilter volta, o filtro insere o contador de hits no lugar apropriado e escreve o resultado no objeto response original
A especificação da cadeia de filtros é feita em tempo de montagem da aplicação, como veremos adiante
Características importantes de filtros: Podem fazer pré-processamento ou pós-
processamento Podem gerar conteúdo diretamente sem passar o
pedido para o componente seguinte
Um filtro de segurança poderia retornar uma página de erro, por exemplo
Podem redirecionar pedidos Um filtro pode obter um request dispatcher do
ServletContext e fazer forward do pedido para uma nova URL
Podem formar cadeias Não servem apenas para pedidos HTTP Não servem apenas para servlets. Poderia ser para
Filtrar pedidos para um servlet Filtrar pedidos para uma página JSP Filtrar pedidos para qualquer URL arbitrária Filtrar pedidos para um conjunto de URLs que
casem com um padrão Filtrar pedidos para arquivos de gráficos Filtrar pedidos para todos os pedidos
Podem adicionar funcionalidade sem alterar os componentes originais Um filtro é um decorador
Manutenção do estado do cliente: o conceito de Sessão
HTTP não provê estado Os pedidos são independentes um do outro Não existe conceito de sessão
Em certas aplicações, precisamos que vários pedidos sejam acoplados Exemplo: pedidos de compras de uma "sessão" de
compras devem cair na mesma cesta de compras Como criar o conceito de sessão?
A API de servlets faz isso para nós (quase) automaticamente
Acesso à sessão
Sessões são representadas por um objeto HttpSession Você acessa a sessão usando request.getSession()
Isso retorna a sessão associada ao pedido, se ele tiver uma, ou cria uma nova sessão, caso contrário
Detalhe importante:
Devido à forma com a qual uma sessão é implementada (usando cookies, ou outro método), o getSession() pode alterar o header do objeto response
Portanto, chame getSession() antes de obter um PrintWriter do objeto response (se precisar)
Associação de atributos à sessão
Objetos são atribuídos à sessão através de nomes Como fizemos com ServletContext Tais objetos podem ser acessados por qualquer Web
Component que pertença à mesma aplicação (isto é, Web Context) e que esteja tratando de um pedido na mesma sessão
Na nossa aplicação, usamos a cesta de compra como atributo de sessão Assim, servlets diferentes mas cooperantes acessam a
mesma cesta de compras CatalogServlet adiciona itens à cesta ShowCartServlet mostra a cesta, remove itens da
cesta e esvazia a cesta CashierServlet calcula o valor total dos itens na cesta
public class CashierServlet extends HttpServlet {
public void doGet (HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
// Get the user's session and shopping cart
HttpSession session = request.getSession();
ResourceBundle messages =
(ResourceBundle)session.getAttribute("messages");
ShoppingCart cart =
(ShoppingCart)session.getAttribute("cart");
if (cart == null) {
cart = new ShoppingCart();
session.setAttribute("cart", cart);
}
// ...
double total = cart.getTotal();
// ...
Gerência de sessão
Não há forma de avisar via HTTP que a sessão acabou Portanto, cada sessão tem um timeout, manipulado com
os métodos getMaxInactiveInterval() e setMaxInactiveInterval()
Você também pode alterar o timeout no Deployment Descriptor No deploytool, escolha a orelha "General" e use a
caixa "Advanced" Se você quiser programaticamente terminar a sessão,
use o método invalidate() Fazemos isso no servlet que confirma a compra
public class ReceiptServlet extends HttpServlet {
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
// Get the user's session and shopping cart
HttpSession session = request.getSession(true);
ResourceBundle messages =
(ResourceBundle)session.getAttribute("messages");
// Payment received -- invalidate the session
session.invalidate();
// ...
Implementação do rastreamento de uma sessão
Se o HTTP não tem estado, de que forma criar o conceito de uma sessão?
Há dois métodos: O cliente aceita a gravação de "cookies" no disco e
envia esses cookies em cada pedido O Web Component pode reescrever todas as URLs
usadas na aplicação de forma a identificar a sessão (URL Rewriting)
Já que o cliente pode inibir o uso de cookies, sua aplicação tem que estar pronta para reescrever as URLs (o segundo método)
Para fazer isso, chame response.encodeURL(URL) para cada URL que você gera O método inclui a identificação da sessão apenas se
cookies estiverem desabilitados Veja um pedaço do ShowCartServlet:
// Where to go and what to do next
out.println("<p> <p><strong><a href=\"" +
response.encodeURL(request.getContextPath() +
"/catalog") +
"\">" + messages.getString("ContinueShopping") +
"</a> " +
"<a href=\"" +
response.encodeURL(request.getContextPath() +
"/cashier") +
"\">" + messages.getString("Checkout") + "</a>
" +
"<a href=\"" +
response.encodeURL(request.getContextPath() +
"/showcart?Clear=clear") +
"\">" + messages.getString("ClearCart") +
"</a></strong>");
Acima, a primeira URL poderá sair como segue
URL original (com cookies): localhost:8000/ilovebooks/catalog
Primeiro, repetimos o wrapper (decorador) discutido acima
package filters;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
public class CharResponseWrapper extends
HttpServletResponseWrapper {
private CharArrayWriter output;
public String toString() {
return output.toString();
}
public CharResponseWrapper(HttpServletResponse response) {
super(response);
output = new CharArrayWriter();
}
public PrintWriter getWriter() {
return new PrintWriter(output);
}
}
Em seguida, vejamos o filtro que trata do log de visitas e da inserção de um contador na página inicial da aplicação Veja que ainda não sabemos onde este filtro será
inserido! Ele pode ser usado para colocar um contador em
qualquer página
package filters;
import java.io.*;
import java.sql.Timestamp;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import util.Counter;
public final class HitCounterFilter implements Filter {
private FilterConfig filterConfig = null;
public void init(FilterConfig filterConfig) throws
Para entender o código dos servlets, temos que saber os aliases usados para cada servlet
Servlet Alias
BookStoreServlet /enter
BannerServlet /banner
CatalogServlet /catalog
BookDetailsServlet /bookdetails
ShowCartServlet /showcart
CashierServlet /cashier
ReceiptServlet /receipt
O servlet BookStoreServlet
É preferível examinar o código dos servlets juntamente com uma página gerada por cada servlet num browser Se a aplicação estiver instalada, acompanhe o código
juntamente com a aplicação em execução Neste servlets, observe a bufferização que permite não
misturar páginas quando há uma exceção e um desvio para a página de erro
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import database.*;
import exception.*;
/**
* An HTTP Servlet that overrides the service method to
return a
* simple web page.
*/
public class BookStoreServlet extends HttpServlet {
No campo "Resource to be Called", digite /errorpage.html
Repita para as exceções exception.BooksNotFoundException e javax.servlet.UnavailableException
Vamos criar as filter chains Selecione a orelha "Filter Mapping" Clique em "Edit Filter List" Clique em "Add" Selecione "filters.HitCounterFilter" na coluna "Filter
Class" Selecione "HitCounterFilter" na coluna "Display
Name" Clique em "Add" Selecione "filters.OrderFilter" na coluna "Filter
Class" Selecione "OrderFilter" na coluna "Display Name" Clique em "OK" Clique em "Add" Selecione "HitCounterFilter" na coluna "Filter Name" Selecione "Servlet" na coluna "Target Type" Selecione "BookStoreServlet" na coluna "Target" Repita para "OrderFilter" (Target type é "Servlet" e
o target é "ReceiptServlet")
Salve para criar o arquivo LivrosApp.ear (a aplicação) Envie o arquivo LivrosApp.ear para seu cliente final
(junto com a fatura ...) Na realidade, para testar, o desenvolvedor da
aplicação também faria um deployment, é claro
O Deployment da Aplicação
Na máquina de deployment
Adiciona referência de recurso para o banco de dados Cloudscape Selecione LivrosWAR Selecione a orelha "Resource Ref" Clique em "Add" Selecione "javax.sql.DataSource" na coluna Type
Insira "jdbc/BookDB" no campo "Coded Name" Insira "jdbc/BookDB" no campo "JNDI Name"
Em LivrosApp, defina um "Context root" com valor "/ilovebooks"
Salve
No servidor de banco de dados
Criação do banco de dados cloudscape -start ant create-livros-db
Os comandos SQL executados para criar o banco de dados estão em sql/books.sql:
DROP TABLE books;
CREATE TABLE books
(id VARCHAR(8)
CONSTRAINT pk_books PRIMARY KEY,
surname VARCHAR(24),
first_name VARCHAR(24),
title VARCHAR(96),
price FLOAT,
yr INT,
description VARCHAR(30));
DELETE FROM books;
INSERT INTO books VALUES('201', 'Duke', '',
'My Early Years: Growing up on *7',
10.75, 1995, 'What a cool book.');
INSERT INTO books VALUES('202', 'Jeeves', '',
'Web Servers for Fun and Profit', 10.75,
2000, 'What a cool book.');
INSERT INTO books VALUES('203', 'Masterson', 'Webster',
'Web Components for Web Developers',
17.75, 2000, 'What a cool book.');
INSERT INTO books VALUES('205', 'Novation', 'Kevin',
'From Oak to Java: The Revolution of a Language',
10.75, 1998, 'What a cool book.');
INSERT INTO books VALUES('206', 'Gosling', 'James',
'Java Intermediate Bytecodes', 10.75,
2000, 'What a cool book.');
INSERT INTO books VALUES('207', 'Thrilled', 'Ben',
'The Green Project: Programming for Consumer Devices',
10.75, 1998, 'What a cool book');
INSERT INTO books VALUES('208', 'Tru', 'Itzal',
'Duke: A Biography of the Java Evangelist',
10.75, 2001, 'What a cool book.');
No servidor J2EE
Configurar o servidor J2EE para que saiba sobre o nome JNDI jdbc/BookDB Ao entrar no ar, o servidor J2EE fará o bind
de jdbc/BookDB com a URL indicada
j2ee -stop
j2eeadmin -addJdbcDatasource jdbc/BookDB
jdbc:cloudscape:rmi:BookDB;create=true
Agora, o servidor J2EE pode entrar no ar:
j2ee -verbose
Uma das linhas impressas durante a inicialização será:
Binding DataSource, name = jdbc/BookDB, url =
jdbc:cloudscape:rmi:BookDB;create=true
Ainda na máquina de deployment
Agora passamos para a máquina do cliente final a partir da qual o deployment está sendo feito Essa máquina não precisa ser o servidor mas pode ser O deployment pode ser remoto
Use o deploytool e escolha Tools/Deploy Faça deploy de LivrosApp.ear no servidor desejado