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 é:
- Identificação única
- Sobrenome do autor
- 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
- (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
A Teoria necessária sobre Servlets e Java para Resolver o Problema
- 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 usuário
RequestDispatcher dispatcher =
getServletContext().getRequestDispatcher("/banner");
if (dispatcher != null) {
dispatcher.include(request, response);
}
- 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:
-
- 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 pt-BR
Resource Bundles
- Mensagens e outros recursos (objetos) que dependem da locale podem ser armazenados num
ResourceBundle
- A classe Resource Bundle é interessante porque simplifica a localização de um
ResourceBundle que mais se adeque às Locales que o usuário prefere
- Uma vez o bundle localizado, Strings e outros objetos podem ser obtidos do bundle
- Os recursos podem ser uma classe ou um arquivo de Properties
- Um bundle contendo objetos localizados para o Português do Brasil é uma classe
Xpto_pt_BR
- Na ausência desta classe, o arquivo Xpto_pt_BR.properties pode
conter os strings
- Se a aplicação pedir o bundle para pt-BR e este bundle não existir, poderei receber o
bundle Xpto_pt que é o mais próximo disponível
- Se nem Xpto_pt_BR, nem Xpto_pt existir, então receberei Xpto que é equivalente a
Xpto_en_US
- O uso de bundles pode ser visto nas classes abaixo (usando classes):
- Exercício: altere isso para usar arquivo de properties
package messages;
import java.util.*;
public class BookstoreMessages extends ListResourceBundle {
public Object[][] getContents() {
return contents;
}
static final Object[][] contents = {
{"ServerError", "Your request cannot be completed. The server got the following error: "},
{"TitleServerError", "Server Error"},
{"TitleShoppingCart", "Shopping Cart"},
{"What", "What We\'re Reading"},
// ...
{"Submit", "Submit Information"},
{"Catalog", "Back to the Catalog"},
{"ThankYou", "Thank you for purchasing your books from us "},
};
}
package messages;
import java.util.*;
public class BookstoreMessages_pt extends ListResourceBundle {
public Object[][] getContents() {
return contents;
}
static final Object[][] contents = {
{"ServerError", "Seu pedido não pode ser completado. O servidor recebeu o seguinte erro: "},
{"TitleServerError", "Erro de Servidor"},
{"TitleShoppingCart", "Cesta de Compras"},
{"What", "O que Estamos Lendo"},
// ...
{"Submit", "Submeter Informação"},
{"Catalog", "Voltar ao Catálogo"},
{"ThankYou", "Obrigado por comprar seus livros conosco "},
};
}
public class BookStoreServlet extends HttpServlet {
// ...
public void doGet (HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
// ...
HttpSession session = request.getSession();
ResourceBundle messages = (ResourceBundle)session.getAttribute("messages");
if (messages == null) {
Locale locale=request.getLocale();
messages = ResourceBundle.getBundle("messages.BookstoreMessages", locale);
session.setAttribute("messages", messages);
}
// ...
out.println("<b>" + messages.getString("What") + "</b>");
- 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), ou depois 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 ServletException {
this.filterConfig = filterConfig;
}
public void destroy() {
this.filterConfig = null;
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
if (filterConfig == null)
return;
HttpServletRequest hr = (HttpServletRequest)request;
HttpSession session = hr.getSession();
ResourceBundle messages = (ResourceBundle)session.getAttribute("messages");
if (messages == null) {
Locale locale=request.getLocale();
messages = ResourceBundle.getBundle("messages.BookstoreMessages", locale);
session.setAttribute("messages", messages);
}
StringWriter sw = new StringWriter();
PrintWriter writer = new PrintWriter(sw);
Counter counter = (Counter)filterConfig.getServletContext().getAttribute("hitCounter");
writer.println();
writer.println("=======================================================");
writer.println("The number of hits is: " + counter.incCounter());
writer.println("=======================================================");
// Log the resulting string
writer.flush();
filterConfig.getServletContext().log(sw.getBuffer().toString());
PrintWriter out = response.getWriter();
CharResponseWrapper wrapper = new CharResponseWrapper((HttpServletResponse)response);
chain.doFilter(request, wrapper);
CharArrayWriter caw = new CharArrayWriter();
caw.write(wrapper.toString().substring(0, wrapper.toString().indexOf("</body>")-1));
caw.write("<p>\n<center><center>" + messages.getString("Visitor") + "<font color='red'>" + counter.getCounter() + "</font><center>");
caw.write("\n</body></html>");
response.setContentLength(caw.toString().length());
out.write(caw.toString());
out.close();
}
public String toString() {
if (filterConfig == null)
return ("HitCounterFilter()");
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
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
- URL reescrita:
localhost:8000/ilovebooks/catalog;jsessionid=22D9E3C451F5C0C951F9A4A32422F6A2
A Solução
- Temos informação suficiente para desenvolver uma solução
- Examinemos novamente nossa lista de requisitos e como podemos solucionar cada caso
Requisito |
Solução |
- A aplicação é de compra de livros on-line
|
- Solução J2EE com Web Components e browser como cliente
- O servlet principal de entrada é BookStoreServlet que
exibe a primeira página
|
- Deve-se exibir o catálogo
|
- Servlet chamado CatalogServlet com o catálogo num BD
acessado via JDBC
|
- O catálogo deve poder fornecer detalhes sobre um livro
|
- Servlet chamado BookDetailsServlet acessando o BD. O
servlet é chamado com URL especial /bookdetails?bookId=<bookId>
|
- Deve-se permitir colocar itens numa cesta de compras
|
- A cesta de compras é armazenada na sessão
|
- Deve ser possível comprar mais de uma cópia de um livro
|
- A cesta de compras sabe diferenciar entre um novo item sendo adicionado e incrementar a
quantidade de um item já presente
|
- Deve haver uma forma de verificar o conteúdo da cesta de compras
|
- Servlet chamado ShowCartServlet
|
- Deve-se permitir remover itens da cesta de compras
|
- Servlet chamado ShowCartServlet com URL especial /showcart?Remove=<bookId>
- Para esvaziar a cesta, a URL é /showcart?Clear=clear
|
- Deve-se permitir que o usuário se encaminhe para o caixa para pagar os livros
|
- Servlet chamado CashierServlet
|
- Para efetuar a compra, o usuário deve fornecer seu nome e número de cartão de
crédito
|
- Servlet chamado CashierServlet produz uma página com
formulário para recolher a informação
|
- A aplicação não precisa contactar um site de aprovação de crédito mas deve manter
um log de cada compra
|
- O servlet ReceiptServlet estará sujeito a uma cadeia de
filtro (filter chain) e um filtro tratará de logar a informação com contexto.log(...)
|
- A primeira página deve fornecer o link de um livro que o staff da livraria está lendo
|
- Hardcoded no servlet BookStoreServlet
|
- A informação mantida para cada livro é:
- Identificação única
- Sobrenome do autor
- 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
|
- Isso afeta a definição do Banco de Dados (BookDB) mantido no Cloudscape
|
- A aplicação deve exibir páginas em inglês ou português ou espanhol, dependendo das
preferências do browser do usuário
|
- Uso de ResourceBundles com escolha baseada na Locale
- BookstoreMessages (inglês)
- BookstoreMessages_pt (português)
- BookstoreMessages_es (espanhol)
|
- Os preços dos livros devem ser exibidos na moeda local, isto é dependendo das
preferências do browser do usuário
|
- Uso de NumberFormat.getCurrencyInstance(locale)
|
- Todas as páginas exibidas deve iniciar com um banner comum
|
- Cada servlet chama (inclui) o BannerServlet
|
- A primeira página deve fornecer um contador de hits de visitas
|
- O BookStoreServlet faz parte de um filter chain com um
filtro tratando de inserir o contador depois que o servlet gerou sua informação
|
- Deve haver um log das visitas
|
- O mesmo filtro acima (que insere um contador de visitas) também as loga usando
contexto.log(...)
|
- Uma página de erro adequada deve ser exibida na ocorrência de problemas
|
- Uma página errorpage.html dizendo que a aplicação não está disponível será usada
e associada aos Web Components durante a composição da aplicação
|
- Podemos agora examinar o código completo da aplicação e tecer alguns comentários
adicionais
O package util
Classe Currency
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);
}
}
Classe Counter
- A classe Counter é usada para manter o contador de visitas e o contador de compras
- Os dois objetos terão escopo de aplicação
- No código abaixo, observe o uso de synchronized, já que há concorrência no acesso
aos counters por parte de vários servlets de sessões diferentes
- Lembre que o servidor J2EE tratará cada pedido num thread diferente, potencialmente
package util;
public class Counter {
private int counter;
public Counter() {
counter = 0;
}
public synchronized int getCounter() {
return counter;
}
public synchronized int setCounter(int c) {
counter = c;
return counter;
}
public synchronized int incCounter() {
return(++counter);
}
}
O package messages
- Contém os 3 ResourceBundles que já vimos acima
O package listeners
- Contém o ContextListener usado para inicializar e encerrar a aplicação
- A inicialização trata de 3 coisas:
- Obter acesso ao banco de dados
- Criar o counter de visitas
- Criar o counter de compras
- O listener será amarrado ao container (cadastrado como listener) usando o deploytool
package listeners;
import database.BookDB;
import javax.servlet.*;
import util.Counter;
public final class ContextListener
implements ServletContextListener {
private ServletContext context = null;
public void contextInitialized(ServletContextEvent event) {
context = event.getServletContext();
try {
BookDB bookDB = new BookDB();
context.setAttribute("bookDB", bookDB);
} catch (Exception ex) {
context.log("Couldn't create bookstore database bean: " + ex.getMessage());
}
context.setAttribute("hitCounter", new Counter());
context.setAttribute("orderCounter", new Counter());
}
public void contextDestroyed(ServletContextEvent event) {
context = event.getServletContext();
BookDB bookDB = (BookDB)context.getAttribute("bookDB");
bookDB.remove();
context.removeAttribute("bookDB");
context.removeAttribute("hitCounter");
context.removeAttribute("orderCounter");
}
}
O package database
package database;
import java.sql.*;
import javax.sql.*;
import javax.naming.*;
import java.util.*;
import exception.*;
public class BookDB {
private ArrayList books;
Connection con;
private String dbName = "java:comp/env/jdbc/BookDB";
public BookDB () throws Exception {
try {
InitialContext ic = new InitialContext();
DataSource ds = (DataSource) ic.lookup(dbName);
con = ds.getConnection();
} catch (Exception ex) {
throw new Exception("Couldn't open connection to database: " + ex.getMessage());
}
}
public void remove () {
try {
con.close();
} catch (SQLException ex) {
System.out.println(ex.getMessage());
}
}
public int getNumberOfBooks() throws BooksNotFoundException {
return getBooks().size();
}
public Collection getBooks() throws BooksNotFoundException {
books = new ArrayList();
try {
String selectStatement = "select * from books";
PreparedStatement prepStmt = con.prepareStatement(selectStatement);
ResultSet rs = prepStmt.executeQuery();
while (rs.next()) {
BookDetails bd = new BookDetails(rs.getString(1), rs.getString(2), rs.getString(3), rs.getString(4),
rs.getFloat(5), rs.getInt(6), rs.getString(7));
books.add(bd);
}
prepStmt.close();
} catch (SQLException ex) {
throw new BooksNotFoundException(ex.getMessage());
}
Collections.sort(books);
return books;
}
public BookDetails getBookDetails(String bookId) throws BookNotFoundException {
try {
String selectStatement = "select * from books where id = ? ";
PreparedStatement prepStmt = con.prepareStatement(selectStatement);
prepStmt.setString(1, bookId);
ResultSet rs = prepStmt.executeQuery();
if (rs.next()) {
BookDetails bd = new BookDetails(rs.getString(1), rs.getString(2), rs.getString(3), rs.getString(4),
rs.getFloat(5), rs.getInt(6), rs.getString(7));
prepStmt.close();
return bd;
} else {
prepStmt.close();
throw new BookNotFoundException("Couldn't find book: " + bookId);
}
} catch (SQLException ex) {
throw new BookNotFoundException("Couldn't find book: " + bookId + ex.getMessage());
}
}
}
- Em BookDetails, observe que a ordenação será por título
package database;
public class BookDetails implements Comparable {
private String bookId = null;
private String title = null;
private String firstName = null;
private String surname = null;
private float price = 0.0F;
private int year = 0;
private String description = null;
public BookDetails(String bookId, String surname, String firstName, String title,
float price, int year, String description) {
this.bookId = bookId;
this.title = title;
this.firstName = firstName;
this.surname = surname;
this.price = price;
this.year = year;
this.description = description;
}
public String getTitle() {
return title;
}
public float getPrice() {
return price;
}
public int getYear() {
return year;
}
public String getDescription() {
return description;
}
public String getBookId() {
return this.bookId;
}
public String getFirstName() {
return this.firstName;
}
public String getSurname() {
return this.surname;
}
public int compareTo(Object o) {
BookDetails n = (BookDetails)o;
int lastCmp = title.compareTo(n.title);
return (lastCmp);
}
}
O package cart
package cart;
import java.util.*;
import database.BookDetails;
public class ShoppingCart {
HashMap items = null;
int numberOfItems = 0;
public ShoppingCart() {
items = new HashMap();
}
public synchronized void add(String bookId, BookDetails book) {
if(items.containsKey(bookId)) {
ShoppingCartItem scitem = (ShoppingCartItem) items.get(bookId);
scitem.incrementQuantity();
} else {
ShoppingCartItem newItem = new ShoppingCartItem(book);
items.put(bookId, newItem);
}
numberOfItems++;
}
public synchronized void remove(String bookId) {
if(items.containsKey(bookId)) {
ShoppingCartItem scitem = (ShoppingCartItem) items.get(bookId);
scitem.decrementQuantity();
if(scitem.getQuantity() <= 0)
items.remove(bookId);
numberOfItems--;
}
}
public synchronized Collection getItems() {
return items.values();
}
protected void finalize() throws Throwable {
items.clear();
}
public synchronized int getNumberOfItems() {
return numberOfItems;
}
public synchronized double getTotal() {
double amount = 0.0;
for(Iterator i = getItems().iterator(); i.hasNext(); ) {
ShoppingCartItem item = (ShoppingCartItem) i.next();
BookDetails bookDetails = (BookDetails) item.getItem();
amount += item.getQuantity() * bookDetails.getPrice();
}
return roundOff(amount);
}
private double roundOff(double x) {
long val = Math.round(x*100); // cents
return val/100.0;
}
public synchronized void clear() {
items.clear();
numberOfItems = 0;
}
}
package cart;
public class ShoppingCartItem {
Object item;
int quantity;
public ShoppingCartItem(Object anItem) {
item = anItem;
quantity = 1;
}
public void incrementQuantity() {
quantity++;
}
public void decrementQuantity() {
quantity--;
}
public Object getItem() {
return item;
}
public int getQuantity() {
return quantity;
}
}
O package filters
- 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 ServletException {
this.filterConfig = filterConfig;
}
public void destroy() {
this.filterConfig = null;
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain)
throws IOException, ServletException {
if (filterConfig == null)
return;
HttpServletRequest hr = (HttpServletRequest)request;
HttpSession session = hr.getSession();
ResourceBundle messages = (ResourceBundle)session.getAttribute("messages");
if (messages == null) {
Locale locale=request.getLocale();
messages = ResourceBundle.getBundle("messages.BookstoreMessages", locale);
session.setAttribute("messages", messages);
}
StringWriter sw = new StringWriter();
PrintWriter writer = new PrintWriter(sw);
Counter counter = (Counter)filterConfig.getServletContext().getAttribute("hitCounter");
writer.println();
writer.println("=======================================================");
writer.println("The number of hits is: " + counter.incCounter());
writer.println("=======================================================");
// Log the resulting string
writer.flush();
filterConfig.getServletContext().log(sw.getBuffer().toString());
PrintWriter out = response.getWriter();
CharResponseWrapper wrapper = new CharResponseWrapper((HttpServletResponse)response);
chain.doFilter(request, wrapper);
CharArrayWriter caw = new CharArrayWriter();
caw.write(wrapper.toString().substring(0, wrapper.toString().indexOf("</body>")-1));
caw.write("<p>\n<center><center>" + messages.getString("Visitor") + "<font color='red'>" + counter.getCounter() + "</font><center>");
caw.write("\n</body></html>");
response.setContentLength(caw.toString().length());
out.write(caw.toString());
out.close();
}
public String toString() {
if (filterConfig == null)
return ("HitCounterFilter()");
StringBuffer sb = new StringBuffer("HitCounterFilter(");
sb.append(filterConfig);
sb.append(")");
return (sb.toString());
}
}
- Finalmente, o filtro que loga as compras feitas
package filters;
import java.io.*;
import java.sql.Timestamp;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import database.BookDetails;
import cart.*;
import util.*;
public final class OrderFilter implements Filter {
private FilterConfig filterConfig = null;
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
}
public void destroy() {
this.filterConfig = null;
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain)
throws IOException, ServletException {
if (filterConfig == null)
return;
// Render the generic servlet request properties
StringWriter sw = new StringWriter();
PrintWriter writer = new PrintWriter(sw);
ServletContext context = filterConfig.getServletContext();
Counter counter = (Counter)context.getAttribute("orderCounter");
HttpServletRequest hsr = (HttpServletRequest)request;
HttpSession session = hsr.getSession();
ShoppingCart cart = (ShoppingCart)session.getAttribute("cart");
Currency c = (Currency)session.getAttribute("currency");
c.setAmount(cart.getTotal());
writer.println();
writer.println("=======================================================");
writer.println("The total number of orders is: " + counter.incCounter());
writer.println("This order Received at " +
(new Timestamp(System.currentTimeMillis())));
writer.println();
writer.print("Purchased by: " + request.getParameter("cardname"));
writer.println();
writer.print("Total: " + c.getFormat());
writer.println();
int num = cart.getNumberOfItems();
if (num > 0) {
Iterator i = cart.getItems().iterator();
while (i.hasNext()) {
ShoppingCartItem item = (ShoppingCartItem) i.next();
BookDetails bookDetails = (BookDetails) item.getItem();
writer.print("ISBN: " + bookDetails.getBookId());
writer.print(" Title: " + bookDetails.getTitle());
writer.print(" Quantity: " + item.getQuantity());
writer.println();
}
}
writer.println("=======================================================");
// Log the resulting string
writer.flush();
context.log(sw.getBuffer().toString());
chain.doFilter(request, response);
}
public String toString() {
if (filterConfig == null)
return ("OrderFilter()");
StringBuffer sb = new StringBuffer("OrderFilter(");
sb.append(filterConfig);
sb.append(")");
return (sb.toString());
}
}
- Um conteúdo típico de log seria como segue:
...
BookStoreServlet: init
=======================================================
The number of hits is: 1
=======================================================
BannerServlet: init
CatalogServlet: init
BookDetailsServlet: init
ShowCartServlet: init
CashierServlet: init
ReceiptServlet: init
=======================================================
The total number of orders is: 1
This order Received at 2001-10-20 14:53:20.695
Purchased by: Gwen Canigetit
Total: R$ 32,25
ISBN: 208 Title: Duke: A Biography of the Java Evangelist Quantity: 2
ISBN: 205 Title: From Oak to Java: The Revolution of a Language Quantity: 1
=======================================================
=======================================================
The number of hits is: 2
=======================================================
=======================================================
The total number of orders is: 2
This order Received at 2001-10-20 14:55:00.669
Purchased by: Gwen Canigetit
Total: R$ 10,75
ISBN: 208 Title: Duke: A Biography of the Java Evangelist Quantity: 1
=======================================================
=======================================================
The number of hits is: 3
=======================================================
=======================================================
The total number of orders is: 3
This order Received at 2001-10-20 18:51:10.704
Purchased by: papi
Total: R$ 82,25
ISBN: 201 Title: My Early Years: Growing up on *7 Quantity: 1
ISBN: 208 Title: Duke: A Biography of the Java Evangelist Quantity: 1
ISBN: 207 Title: The Green Project: Programming for Consumer Devices Quantity: 1
ISBN: 206 Title: Java Intermediate Bytecodes Quantity: 1
ISBN: 205 Title: From Oak to Java: The Revolution of a Language Quantity: 1
ISBN: 203 Title: Web Components for Web Developers Quantity: 1
ISBN: 202 Title: Web Servers for Fun and Profit Quantity: 1
=======================================================
=======================================================
The number of hits is: 4
=======================================================
Os servlets
- 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 {
private BookDB bookDB;
public void init() throws ServletException {
bookDB = (BookDB)getServletContext().getAttribute("bookDB");
if (bookDB == null)
throw new UnavailableException("Couldn't get database.");
}
public void destroy() {
bookDB.remove();
bookDB = null;
}
public void doGet (HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
HttpSession session = request.getSession();
ResourceBundle messages = (ResourceBundle)session.getAttribute("messages");
if (messages == null) {
Locale locale=request.getLocale();
messages = ResourceBundle.getBundle("messages.BookstoreMessages", locale);
session.setAttribute("messages", messages);
}
// set content-type header before accessing the Writer
response.setContentType("text/html");
response.setBufferSize(8192);
PrintWriter out = response.getWriter();
// then write the data of the response
out.println("<html>" +
"<head><title>Duke's Bookstore</title></head>");
// Get the dispatcher; it gets the banner to the user
RequestDispatcher dispatcher =
getServletContext().getRequestDispatcher("/banner");
if (dispatcher != null)
dispatcher.include(request, response);
try {
BookDetails bd = bookDB.getBookDetails("203");
//Left cell -- the "book of choice"
out.println("<b>" + messages.getString("What") + "</b>" +
"<p>" + "<blockquote>" +
"<em><a href=\"" +
response.encodeURL(request.getContextPath() + "/bookdetails?bookId=203") +
"\">" + bd.getTitle() + "</a></em>" + messages.getString("Talk") + "</blockquote>");
//Right cell -- various navigation options
out.println("<p><a href=\"" +
response.encodeURL(request.getContextPath() + "/catalog") +
"\"><b>" + messages.getString("Start") + "</b></a></font><br>" +
"<br> " +
"<br> " +
"<br> " +
"</body>" +
"</html>");
} catch (BookNotFoundException ex) {
response.resetBuffer();
throw new ServletException(ex);
}
out.close();
}
public String getServletInfo() {
return "The BookStore servlet returns the main web page " +
"for Duke's Bookstore.";
}
}
O servlet BannerServlet
- Chamado pelos outros servlets
import java.io.*;
import java.util.*;
import java.sql.*;
import javax.servlet.*;
import javax.servlet.http.*;
import database.*;
import cart.*;
/**
* This is a simple example of an HTTP Servlet. It responds to the GET
* method of the HTTP protocol.
*/
public class BannerServlet extends HttpServlet {
public void doGet (HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
writeBanner(request, response);
}
public void doPost (HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
writeBanner(request, response);
}
private void writeBanner (HttpServletRequest request,
HttpServletResponse response)
throws 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> ");
}
}
O servlet CatalogServlet
- Observe as várias URLs usadas para controlar o processo de manipulação da cesta de
compras
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import database.*;
import cart.*;
import util.Currency;
import exception.*;
/**
* This is a simple example of an HTTP Servlet. It responds to the GET
* method of the HTTP protocol.
*/
public class CatalogServlet extends HttpServlet {
private BookDB bookDB;
public void init() throws ServletException {
bookDB = (BookDB)getServletContext().getAttribute("bookDB");
if (bookDB == null)
throw new UnavailableException("Couldn't get database.");
}
public void destroy() {
bookDB.remove();
bookDB = null;
}
public void doGet (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");
ShoppingCart cart = (ShoppingCart)session.getAttribute("cart");
// If the user has no cart, create a new one
if (cart == null) {
cart = new ShoppingCart();
session.setAttribute("cart", cart);
}
// set content-type header before accessing the Writer
response.setContentType("text/html");
response.setBufferSize(8192);
PrintWriter out = response.getWriter();
// then write the data of the response
out.println("<html>" +
"<head><title>" + messages.getString("TitleBookCatalog") + "</title></head>");
// Get the dispatcher; it gets the banner to the user
RequestDispatcher dispatcher =
getServletContext().getRequestDispatcher("/banner");
if (dispatcher != null)
dispatcher.include(request, response);
//Information on the books is from the database through its front end
// Additions to the shopping cart
String bookId = request.getParameter("bookId");
if (bookId != null) {
try {
BookDetails book = bookDB.getBookDetails(bookId);
cart.add(bookId, book);
out.println("<p><h3>" + "<font color=\"#ff0000\">" + messages.getString("CartAdded1") +
"<i>" + book.getTitle() + "</i> " +
messages.getString("CartAdded2") + "</font></h3>");
} catch (BookNotFoundException ex) {
response.reset();
throw new ServletException(ex);
}
}
//Give the option of checking cart or checking out if cart not empty
if (cart.getNumberOfItems() > 0) {
out.println("<p><strong><a href=\"" +
response.encodeURL(request.getContextPath() + "/showcart") +
"\">" + messages.getString("CartCheck") + "</a> " +
"<a href=\"" +
response.encodeURL(request.getContextPath() + "/cashier") +
"\">" + messages.getString("Buy") + "</a>" +
"</p></strong>");
}
// Always prompt the user to buy more -- get and show the catalog
out.println("<br> " +
"<h3>" + messages.getString("Choose") + "</h3>" +
"<center> <table>");
try {
Collection coll = bookDB.getBooks();
Iterator i = coll.iterator();
Currency c = (Currency)session.getAttribute("currency");
if (c == null) {
c = new Currency();
c.setLocale(request.getLocale());
session.setAttribute("currency", c);
}
while (i.hasNext()) {
BookDetails book = (BookDetails)i.next();
bookId = book.getBookId();
c.setAmount(book.getPrice());
//Print out info on each book in its own two rows
out.println("<tr>" +
"<td bgcolor=\"#ffffaa\">" +
"<a href=\"" +
response.encodeURL(request.getContextPath() + "/bookdetails?bookId=" + bookId) +
"\"> <strong>" + book.getTitle() +
" </strong></a></td>" +
"<td bgcolor=\"#ffffaa\" rowspan=2>" +
c.getFormat() +
" </td>" +
"<td bgcolor=\"#ffffaa\" rowspan=2>" +
"<a href=\"" +
response.encodeURL(request.getContextPath() + "/catalog?bookId=" + bookId)
+ "\"> " + messages.getString("CartAdd") + " </a></td></tr>" +
"<tr>" +
"<td bgcolor=\"#ffffff\">" +
" " + messages.getString("By") + "<em> " + book.getFirstName() +
" " + book.getSurname() + "</em></td></tr>");
}
} catch (BooksNotFoundException ex) {
response.reset();
throw new ServletException(ex);
}
out.println("</table></center></body></html>");
out.close();
}
public String getServletInfo() {
return "The Catalog servlet adds books to the user's " +
"shopping cart and prints the catalog.";
}
}
O servlet BookDetailsServlet
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import database.*;
import cart.*;
import util.Currency;
import exception.*;
/**
* This is a simple example of an HTTP Servlet. It responds to the GET
* method of the HTTP protocol.
*/
public class BookDetailsServlet extends HttpServlet {
private BookDB bookDB;
public void init() throws ServletException {
bookDB = (BookDB)getServletContext().getAttribute("bookDB");
if (bookDB == null)
throw new UnavailableException("Couldn't get database.");
}
public void destroy() {
bookDB = null;
}
public void doGet (HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
HttpSession session = request.getSession(true);
ResourceBundle messages = (ResourceBundle)session.getAttribute("messages");
// set headers and buffer size before accessing the Writer
response.setContentType("text/html");
response.setBufferSize(8192);
PrintWriter out = response.getWriter();
// then write the response
out.println("<html>" +
"<head><title>" + messages.getString("TitleBookDescription") + "</title></head>");
// Get the dispatcher; it gets the banner to the user
RequestDispatcher dispatcher =
getServletContext().getRequestDispatcher("/banner");
if (dispatcher != null)
dispatcher.include(request, response);
//Get the identifier of the book to display
String bookId = request.getParameter("bookId");
if (bookId != null) {
// and the information about the book
try {
BookDetails bd = bookDB.getBookDetails(bookId);
Currency c = (Currency)session.getAttribute("currency");
if (c == null) {
c = new Currency();
c.setLocale(request.getLocale());
session.setAttribute("currency", c);
}
c.setAmount(bd.getPrice());
//Print out the information obtained
out.println("<h2>" + bd.getTitle() + "</h2>" +
" " + messages.getString("By") + " <em>" + bd.getFirstName() + " " +
bd.getSurname() + "</em> " +
"(" + bd.getYear() + ")<br> <br>" +
"<h4>" + messages.getString("Critics")+ "</h4>" +
"<blockquote>" + bd.getDescription() +
"</blockquote>" +
"<h4>" + messages.getString("Price") + c.getFormat() + "</h4>" +
"<p><strong><a href=\"" +
response.encodeURL(request.getContextPath() + "/catalog?bookId=" + bookId) +
"\">" + messages.getString("CartAdd") + "</a> " +
"<a href=\"" +
response.encodeURL(request.getContextPath() + "/catalog") + "\">" + messages.getString("ContinueShopping") + "</a></p></strong>");
} catch (BookNotFoundException ex) {
response.resetBuffer();
throw new ServletException(ex);
}
}
out.println("</body></html>");
out.close();
}
public String getServletInfo() {
return "The BookDetail servlet returns information about" +
"any book that is available from the bookstore.";
}
}
O servlet ShowCartServlet
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import database.*;
import cart.*;
import util.Currency;
import exception.*;
/**
* An HTTP servlet that displays the contents of a customer's shopping
* cart at Duke's Bookstore. It responds to the GET and HEAD methods of
* the HTTP protocol. This servlet calls other servlets.
*/
public class ShowCartServlet extends HttpServlet {
private BookDB bookDB;
public void init() throws ServletException {
bookDB = (BookDB)getServletContext().getAttribute("bookDB");
if (bookDB == null)
throw new UnavailableException("Couldn't get database.");
}
public void destroy() {
bookDB.remove();
bookDB = null;
}
public void doGet (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");
ShoppingCart cart = (ShoppingCart)session.getAttribute("cart");
// If the user has no cart, create a new one
if (cart == null) {
cart = new ShoppingCart();
session.setAttribute("cart", cart);
}
// set content type header before accessing the Writer
response.setContentType("text/html");
response.setBufferSize(8192);
PrintWriter out = response.getWriter();
//Print out the response
out.println("<html>" +
"<head><title>" + messages.getString("TitleShoppingCart") + "</title></head>" );
// Get the dispatcher; it gets the banner to the user
RequestDispatcher dispatcher =
getServletContext().getRequestDispatcher("/banner");
if (dispatcher != null)
dispatcher.include(request, response);
/* Handle any pending deletes from the shopping cart and
indicate the outcome as part of the response */
String bookId = request.getParameter("Remove");
BookDetails bd;
if (bookId != null) {
try {
bd = bookDB.getBookDetails(bookId);
cart.remove(bookId);
out.println("<font color=\"#ff00000\" size=\"+2\">" +
messages.getString("CartRemoved") + "<strong>" + bd.getTitle() +
"</strong> <br> <br>" +
"</font>");
} catch (BookNotFoundException ex) {
response.reset();
throw new ServletException(ex);
}
} else if (request.getParameter("Clear") != null) {
cart.clear();
out.println("<font color=\"#ff0000\" size=\"+2\"><strong>" +
messages.getString("CartCleared") +
"</strong> <br> <br> </font>");
}
// Print a summary of the shopping cart
int num = cart.getNumberOfItems();
if (num > 0) {
out.println("<font size=\"+2\">" +
messages.getString("CartContents") + num + (num==1 ? messages.getString("CartItem") : messages.getString("CartItems")) +
"</font><br> ");
// Return the Shopping Cart
out.println("<table>" +
"<tr>" +
"<th align=left>" + messages.getString("ItemQuantity") + "</TH>" +
"<th align=left>" + messages.getString("ItemTitle") + "</TH>" +
"<th align=left>" + messages.getString("ItemPrice") + "</TH>" +
"</tr>");
Iterator i = cart.getItems().iterator();
Currency c = (Currency)session.getAttribute("currency");
if (c == null) {
c = new Currency();
c.setLocale(request.getLocale());
session.setAttribute("currency", c);
}
while (i.hasNext()) {
ShoppingCartItem item = (ShoppingCartItem) i.next();
bd = (BookDetails) item.getItem();
c.setAmount(bd.getPrice());
out.println("<tr>" +
"<td align=\"right\" bgcolor=\"#ffffff\">" +
item.getQuantity() +
"</td>" +
"<td bgcolor=\"#ffffaa\">" +
"<strong><a href=\"" +
response.encodeURL(request.getContextPath() + "/bookdetails?bookId=" + bd.getBookId()) +
"\">" + bd.getTitle() + "</a></strong>" +
"</td>" +
"<td bgcolor=\"#ffffaa\" align=\"right\">" +
c.getFormat() +
"</td>" +
"<td bgcolor=\"#ffffaa\">" +
"<strong>" +
"<a href=\"" +
response.encodeURL(request.getContextPath() + "/showcart?Remove=" + bd.getBookId()) +
"\">" + messages.getString("RemoveItem") + "</a></strong>" +
"</td></tr>");
}
c.setAmount(cart.getTotal());
// Print the total at the bottom of the table
out.println("<tr><td colspan=\"5\" bgcolor=\"#ffffff\">" +
"<br></td></tr>" +
"<tr>" +
"<td colspan=\"2\" align=\"right\"" +
"bgcolor=\"#ffffff\">" +
messages.getString("Subtotal") + "</td>" +
"<td bgcolor=\"#ffffaa\" align=\"right\">" +
c.getFormat() + "</td>" +
"</td><td><br></td></tr></table>");
// 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>");
} else {
// Shopping cart is empty!
out.println("<font size=\"+2\">" +
messages.getString("CartEmpty") + "</font>" +
"<br> <br>" +
"<center><a href=\"" +
response.encodeURL(request.getContextPath() + "/catalog") +
"\">" + messages.getString("Catalog") + "</a> </center>");
}
out.println("</body> </html>");
out.close();
}
public String getServletInfo() {
return "The ShowCart servlet returns information about" +
"the books that the user is in the process of ordering.";
}
}
O servlet CashierServlet
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import cart.*;
import util.Currency;
/**
* An HTTP Servlet that responds to the GET method of the
* HTTP protocol. It returns a form to the user that gathers data.
* The form POSTs to another servlet.
*/
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);
}
// set content-type header before accessing Writer
response.setContentType("text/html");
PrintWriter out = response.getWriter();
Currency c = (Currency)session.getAttribute("currency");
if (c == null) {
c = new Currency();
c.setLocale(request.getLocale());
session.setAttribute("currency", c);
}
c.setAmount(cart.getTotal());
// then write the data of the response
out.println("<html>" +
"<head><title>" + messages.getString("TitleCashier") + "</title></head>");
// Get the dispatcher; it gets the banner to the user
RequestDispatcher dispatcher =
getServletContext().getRequestDispatcher("/banner");
if (dispatcher != null)
dispatcher.include(request, response);
// Print out the total and the form for the user
out.println("<p>" + messages.getString("Amount") +
"<strong>" + c.getFormat() + "</strong>" +
"<p>" + messages.getString("Purchase") +
"<form action=\"" +
response.encodeURL(request.getContextPath() + "/receipt") +
"\" method=\"post\">" +
"<table>" +
"<tr>" +
"<td><strong>" + messages.getString("Name")+ "</strong></td>" +
"<td><input type=\"text\" name=\"cardname\"" +
"value=\"Gwen Canigetit\" size=\"19\"></td>" +
"</tr>" +
"<tr>" +
"<td><strong>" + messages.getString("CCNumber") + "</strong></td>" +
"<td>" +
"<input type=\"text\" name=\"cardnum\" " +
"value=\"xxxx xxxx xxxx xxxx\" size=\"19\"></td>" +
"</tr>" +
"<tr>" +
"<td></td>" +
"<td><input type=\"submit\"" +
"value=\"" + messages.getString("Submit") + "\"></td>" +
"</tr>" +
"</table>" +
"</form>" +
"</body>" +
"</html>");
out.close();
}
public String getServletInfo() {
return "The Cashier servlet takes the user's name and " +
"credit card number so that the user can buy the books.";
}
}
A página errorpage.html
<html>
<head>
<title>Server Error</title>
</head>
<body bgcolor="white">
<h2>The application is unavailable. Please try later.</h2>
</body>
</html>
A Composição da Aplicação
C:\...\src>ant livros
Buildfile: build.xml
init:
livros:
[mkdir] Created dir: C:\...\build\livros
[copy] Copying 2 files to C:\...\build\livros
[javac] Compiling 22 source files to C:\...\build\livros
BUILD SUCCESSFUL
Total time: 7 seconds
- Agora, vamos compor a aplicação usando o deploytool
- Criar nova aplicação LivrosApp no diretório src/livros
- Criar um novo Web component LivrosWAR com conteúdo
- Todos os arquivos .class dos 7 servlets, duke.books.gif, e errorpage.html
- Adicione todos os pacotes (cart, database, exception, filters, listeners, messages e
util)
- Tipo Servlet, com classe de servlet BannerServlet, alias /banner
- Adicionar a LivrosWAR os outros servlets com classe e alias mostrados na tabela acima
- Na orelha "Event Listeners", adicione ContextListener
- Na orelha "File Refs", clique em Add no painel "Error mapping"
- No campo Error/Exception, digite exception.BookNotFoundException
- 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
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
- Execute a aplicação:
livros programa