Upload
jose-santos
View
98
Download
3
Embed Size (px)
Citation preview
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
Cirílico (ISO-8859-5)
O código fonte da página JSP segue abaixo Ela contém HTML e elementos JSP (destacados)
<%@ page import="java.util.*" %>
<%@ page import="MyLocales" %>
<%@ page contentType="text/html; charset=ISO-8859-5" %>
<html>
<head><title>Localized Dates</title></head>
<body bgcolor="white">
<jsp:useBean id="locales" scope="application"
class="MyLocales"/>
<form name="localeForm" action="index.jsp" method="post">
<b>Locale:</b>
<select name=locale>
<%
Iterator i = locales.getLocaleNames().iterator();
String selectedLocale = request.getParameter("locale");
while (i.hasNext()) {
String locale = (String)i.next();
if (selectedLocale != null &&
selectedLocale.equals(locale) ) {
%>
<option selected><%=locale%></option>
<%
} else {
%>
<option><%=locale%></option>
<%
}
}
%>
</select>
<input type="submit" name="Submit" value="Get Date">
</form>
<p>
<jsp:include page="date.jsp" flush="true" />
</body>
</html>
Alguns comentários: Diretivas (<%@ page ... %>) importam classes do
package java.util e a classe MyLocales class e estabeleçam o "content type" returnado pela página
O elemento jsp:useBean cria um objeto contendo uma coleção de locales e inicializa uma variável que referencia este objeto
Scriptlets (<% ... %>): Recuperam o valor do parâmetro "locale" Iteram na coleção de nomes de locales Inserem texto HTML condicional na saída
Expressões (<%= ... %>) inserem o valor de uma variável como string na resposta
O elemento jsp:include envia um pedido para outra página (date.jsp) inclui sua resposta na resposta da página que faz a chamada
Segue o conteúdo de date.jsp
<%@ page import="java.util.*" %>
<%@ page import="MyDate,MyLocales" %>
<html>
<body bgcolor="white">
<jsp:useBean id="date" class="MyDate"/>
<jsp:useBean id="locales" scope="application"
class="MyLocales"/>
<%
Locale locale =
locales.getLocale(request.getParameter("locale"));
if (locale != null) {
%>
<jsp:setProperty name="date" property="locale"
value="<%=locale%>"/>
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 ...
Composição da Aplicação
Compile tudo
C:\...>ant livrosjsp
Buildfile: build.xml
init:
web-ejb:
[mkdir] Created dir: C:\...\build\webejb
[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
servidor para aplicações de missão crítica (24x7)
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");
if (language != null) {
if (language.equals("English")) {
locale=new Locale("en", "");
} else {
locale=new Locale("pt", "");
}
} else
locale=new Locale("en", "");
messages =
ResourceBundle.getBundle("BookStoreMessages", locale);
session.setAttribute("messages", messages);
}
%>
<html>
<head>
<title><%=messages.getString("ServerError")%></title>
</head>
<body bgcolor="white">
<h3>
<%=messages.getString("ServerError")%>
</h3>
<p>
<%= exception.getMessage() %>
</body>
</html>
Inicialização e Finalização de uma Página JSP
A inicialização é feita em jspInit() e a finalização em jspDestroy()
Os servlets que precisam acessar o banco de dados (catalog.jsp, bookdetails, showcart.jsp, bookstore.jsp) incluem uma página de inicialização
<%@ include file="initdestroy.jsp" %>
Esta página define os métodos jspInit() e jspDestroy() jspInit obtém acesso a um EJB que acessa o banco de
dados Detalhes sobre EJB serão vistos em outro capítulo
<%@ page import="database.*" %>
<%@ page errorPage="errorpage.jsp" %>
<%@ page import="javax.ejb.*, javax.naming.*,
javax.rmi.PortableRemoteObject,
java.rmi.RemoteException,
database.BookDB, database.BookDBEJB,
database.BookDBEJBHome"
%>
<%!
private BookDBEJB bookDBEJB;
public void jspInit() {
bookDBEJB =
(BookDBEJB)getServletContext().getAttribute("bookDBEJB");
if (bookDBEJB == null) {
try {
InitialContext ic = new InitialContext();
Object objRef =
ic.lookup("java:comp/env/ejb/BookDBEJB");
BookDBEJBHome home =
(BookDBEJBHome)PortableRemoteObject.narrow(objRef,
database.BookDBEJBHome.class);
bookDBEJB = home.create();
getServletContext().setAttribute("bookDBEJB",
bookDBEJB);
} catch (RemoteException ex) {
System.out.println("Couldn't create database bean."
+ ex.getMessage());
} catch (CreateException ex) {
System.out.println("Couldn't create database bean."
+ ex.getMessage());
} catch (NamingException ex) {
System.out.println("Unable to lookup home: "+
"java:comp/env/ejb/BookDBEJB."+ ex.getMessage());
}
}
}
public void jspDestroy() {
bookDBEJB = null;
}
%>
É 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
outra que possa chamar objetos Java
<%@ page language="linguagem de script" %>
Se precisar importar classes ou pacotes:
<%@ page import="packagename.*, fully_qualified_classname" %>
Por exemplo, na nossa aplicação, showcart.jsp faz o seguinte:
<%@ page import="java.util.*, cart.*" %>
Declarações
Usadas para declarar variáveis e métodos A sintaxe é:
<%! declaração na linguagem de script %>
Exemplo: aqui está (novamente) o arquivo initdestroy.jsp, incluído em várias páginas JSP
<%--
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 import="database.*" %>
<%@ page errorPage="errorpage.jsp" %>
<%@ page import="javax.ejb.*, javax.naming.*,
javax.rmi.PortableRemoteObject,
java.rmi.RemoteException,
database.BookDB, database.BookDBEJB,
database.BookDBEJBHome"
%>
<%!
private BookDBEJB bookDBEJB;
public void jspInit() {
bookDBEJB =
(BookDBEJB)getServletContext().getAttribute("bookDBEJB");
if (bookDBEJB == null) {
try {
InitialContext ic = new InitialContext();
Object objRef =
ic.lookup("java:comp/env/ejb/BookDBEJB");
BookDBEJBHome home =
(BookDBEJBHome)PortableRemoteObject.narrow(objRef,
database.BookDBEJBHome.class);
bookDBEJB = home.create();
getServletContext().setAttribute("bookDBEJB",
bookDBEJB);
} catch (RemoteException ex) {
System.out.println("Couldn't create database bean."
+ ex.getMessage());
} catch (CreateException ex) {
System.out.println("Couldn't create database bean."
+ ex.getMessage());
} catch (NamingException ex) {
System.out.println("Unable to lookup home: "+
"java:comp/env/ejb/BookDBEJB."+ ex.getMessage());
}
}
}
public void jspDestroy() {
bookDBEJB = null;
}
%>
Scriptlets
Um scriptlet contém um fragmento de código
<%
comandos na linguagem de script
%>
Uma variável criada num scriptlet pode ser acessada em qualquer lugar da página JSP
Exemplo: observe como showcart.jsp itera nos itens que estão na cesta de compras (trecho em destaque)
<%@ include file="initdestroy.jsp" %>
<%@ page import="java.util.*, cart.*" %>
<%
ResourceBundle messages =
(ResourceBundle)session.getAttribute("messages");
%>
<jsp:useBean id="bookDB" class="database.BookDB" scope="page"
>
<jsp:setProperty name="bookDB" property="database"
value="<%=bookDBEJB%>" />
</jsp:useBean>
<jsp:useBean id="cart" scope="session"
class="cart.ShoppingCart"/>
<jsp:useBean id="currency" class="util.Currency"
scope="session">
<jsp:setProperty name="currency" property="locale"
value="<%=request.getLocale()%>"/>
</jsp:useBean>
<html>
<head><title><%=messages.getString("TitleShoppingCart")%></ti
tle></head>
<%@ include file="banner.jsp" %>
<%
String bookId = request.getParameter("Remove");
if (bookId != null) {
cart.remove(bookId);
bookDB.setBookId(bookId);
BookDetails book = bookDB.getBookDetails();
%>
<font color="red"
size="+2"><%=messages.getString("CartRemoved")%><em><%=book.g
etTitle()%>
</em>
<br> <br>
</font>
<%
}
if (request.getParameter("Clear") != null) {
cart.clear();
%>
<font color="red" size="+2"><strong>
<%=messages.getString("CartCleared")%>
</strong><br> <br></font>
<%
}
// Print a summary of the shopping cart
int num = cart.getNumberOfItems();
if (num > 0) {
%>
<font
size="+2"><%=messages.getString("CartContents")%><%=num%>
<%=(num==1 ? messages.getString("CartItem") :
messages.getString("CartItems"))%>
</font><br>
<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();
while (i.hasNext()) {
ShoppingCartItem item = (ShoppingCartItem)i.next();
BookDetails book = (BookDetails)item.getItem();
%>
<tr>
<td align="right" bgcolor="#ffffff">
<%=item.getQuantity()%>
</td>
<td bgcolor="#ffffaa">
<strong><a
href="<%=request.getContextPath()%>/bookdetails?bookId=<%=boo
k.getBookId()%>">
<%=book.getTitle()%></a></strong>
</td>
<td bgcolor="#ffffaa" align="right">
<jsp:setProperty name="currency" property="amount"
value="<%=book.getPrice()%>"/>
<jsp:getProperty name="currency"
property="format"/> </td>
<td bgcolor="#ffffaa">
<strong>
<a
href="<%=request.getContextPath()%>/showcart?Remove=<%=book.g
etBookId()%>"><%=messages.getString("RemoveItem")%></a></stro
ng>
</td></tr>
<%
// End of while
}
%>
<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">
<jsp:setProperty name="currency" property="amount"
value="<%=cart.getTotal()%>"/>
<jsp:getProperty name="currency" property="format"/>
</td>
</td><td><br></td></tr></table>
<p> <p>
<strong><a
href="<%=request.getContextPath()%>/catalog"><%=messages.getS
tring("ContinueShopping")%></a>
<a
href="<%=request.getContextPath()%>/cashier"><%=messages.getS
tring("Checkout")%></a>
<a
href="<%=request.getContextPath()%>/showcart?Clear=clear"><%=
messages.getString("ClearCart")%></a></strong>
<%
} else {
%>
<font size="+2"><%=messages.getString("CartEmpty")%></font>
<br> <br>
<center><a
href="<%=request.getContextPath()%>/catalog"><%=messages.getS
tring("Catalog")%></a> </center>
<%
// End of if
}
%>
</body>
</html>
Expressões
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 é:
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 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
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:
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
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), 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
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("hitCo
unter");
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 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
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
Não há novidade aqui
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("hitCo
unter");
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
Vamos compilar tudo:
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
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
Execute a aplicação: