Carrinho com

106
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

Transcript of Carrinho com

Page 1: Carrinho com

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

Page 2: Carrinho com

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)

Page 3: Carrinho com

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) ) {

%>

Page 4: Carrinho com

<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 5: Carrinho com

<%@ 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();

Page 6: Carrinho com

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

Page 7: Carrinho com

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

Page 8: Carrinho com

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

Page 9: Carrinho com

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

Page 10: Carrinho com

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

Page 11: Carrinho com

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)

Page 12: Carrinho com

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

Page 13: Carrinho com

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");

Page 14: Carrinho com

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() %>

Page 15: Carrinho com

</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");

Page 16: Carrinho com

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

Page 17: Carrinho com

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

Page 18: Carrinho com

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" %>

Page 19: Carrinho com

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

Page 20: Carrinho com

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) {

Page 21: Carrinho com

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

<%

Page 22: Carrinho com

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" %>

Page 23: Carrinho com

<%

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>&nbsp;<br>

</font>

<%

}

if (request.getParameter("Clear") != null) {

cart.clear();

%>

<font color="red" size="+2"><strong>

<%=messages.getString("CartCleared")%>

</strong><br>&nbsp;<br></font>

<%

}

// Print a summary of the shopping cart

Page 24: Carrinho com

int num = cart.getNumberOfItems();

if (num > 0) {

%>

<font

size="+2"><%=messages.getString("CartContents")%><%=num%>

<%=(num==1 ? messages.getString("CartItem") :

messages.getString("CartItems"))%>

</font><br>&nbsp;

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

Page 25: Carrinho com

<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"/>&nbsp;</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>

Page 26: Carrinho com

<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>&nbsp;<p>

<strong><a

href="<%=request.getContextPath()%>/catalog"><%=messages.getS

tring("ContinueShopping")%></a>&nbsp;&nbsp;&nbsp;

<a

href="<%=request.getContextPath()%>/cashier"><%=messages.getS

tring("Checkout")%></a>&nbsp;&nbsp;&nbsp;

<a

href="<%=request.getContextPath()%>/showcart?Clear=clear"><%=

messages.getString("ClearCart")%></a></strong>

<%

} else {

%>

<font size="+2"><%=messages.getString("CartEmpty")%></font>

<br>&nbsp;<br>

<center><a

href="<%=request.getContextPath()%>/catalog"><%=messages.getS

tring("Catalog")%></a> </center>

<%

// End of if

}

Page 27: Carrinho com

%>

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

Page 28: Carrinho com

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

Page 29: Carrinho com

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

Page 30: Carrinho com

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

Page 31: Carrinho com

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

Page 32: Carrinho com

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

Page 33: Carrinho com

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

Page 34: Carrinho com

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

Page 35: Carrinho com

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> &nbsp;" +

"<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> &nbsp; <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

Page 36: Carrinho com

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

Page 37: Carrinho com

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 = {

Page 38: Carrinho com

{"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"},

Page 39: Carrinho com

// ...

{"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

Page 40: Carrinho com

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;

}

Page 41: Carrinho com

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

Page 42: Carrinho com

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:

Page 43: Carrinho com

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

Page 44: Carrinho com

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 {

Page 45: Carrinho com

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();

Page 46: Carrinho com

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());

Page 47: Carrinho com

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

Page 48: Carrinho com

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:

Page 49: Carrinho com

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();

Page 50: Carrinho com

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

Page 51: Carrinho com

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> &nbsp; <p><strong><a href=\"" +

response.encodeURL(request.getContextPath() +

"/catalog") +

"\">" + messages.getString("ContinueShopping") +

"</a> &nbsp; &nbsp; &nbsp;" +

"<a href=\"" +

response.encodeURL(request.getContextPath() +

"/cashier") +

"\">" + messages.getString("Checkout") + "</a>

&nbsp; &nbsp; &nbsp;" +

"<a href=\"" +

response.encodeURL(request.getContextPath() +

"/showcart?Clear=clear") +

"\">" + messages.getString("ClearCart") +

"</a></strong>");

Acima, a primeira URL poderá sair como segue

Page 52: Carrinho com

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

Page 53: Carrinho com

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

Page 54: Carrinho com

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);

}

}

Page 55: Carrinho com

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

Page 56: Carrinho com

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());

}

Page 57: Carrinho com

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;

Page 58: Carrinho com

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();

Page 59: Carrinho com

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()) {

Page 60: Carrinho com

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;

Page 61: Carrinho com

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;

}

Page 62: Carrinho com

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() {

Page 63: Carrinho com

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

}

}

Page 64: Carrinho com

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;

}

Page 65: Carrinho com

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;

Page 66: Carrinho com

}

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();

}

Page 67: Carrinho com

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;

Page 68: Carrinho com

}

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("=============================================

==========");

Page 69: Carrinho com

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(");

Page 70: Carrinho com

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;

Page 71: Carrinho com

}

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();

Page 72: Carrinho com

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);

}

Page 73: Carrinho com

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

Page 74: Carrinho com

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

=======================================================

Page 75: Carrinho com

=======================================================

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

Page 76: Carrinho com

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 {

Page 77: Carrinho com

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

Page 78: Carrinho com

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

Page 79: Carrinho com

out.println("<p><a href=\"" +

response.encodeURL(request.getContextPath()

+ "/catalog") +

"\"><b>" + messages.getString("Start") +

"</b></a></font><br>" +

"<br> &nbsp;" +

"<br> &nbsp;" +

"<br> &nbsp;" +

"</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.*;

Page 80: Carrinho com

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 {

Page 81: Carrinho com

PrintWriter out = response.getWriter();

out.println("<body bgcolor=\"#ffffff\">" +

"<center>" +

"<hr> <br> &nbsp;" +

"<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> &nbsp; <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.*;

/**

Page 82: Carrinho com

* 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");

Page 83: Carrinho com

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

Page 84: Carrinho com

// 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>&nbsp;&nbsp;&nbsp;" +

"<a href=\"" +

response.encodeURL(request.getContextPath()

+ "/cashier") +

"\">" + messages.getString("Buy") + "</a>"

+

"</p></strong>");

Page 85: Carrinho com

}

// Always prompt the user to buy more -- get and show the

catalog

out.println("<br> &nbsp;" +

"<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=\"" +

Page 86: Carrinho com

response.encodeURL(request.getContextPath() +

"/bookdetails?bookId=" + bookId) +

"\"> <strong>" + book.getTitle() +

"&nbsp; </strong></a></td>" +

"<td bgcolor=\"#ffffaa\" rowspan=2>" +

c.getFormat() +

"&nbsp; </td>" +

"<td bgcolor=\"#ffffaa\" rowspan=2>" +

"<a href=\"" +

response.encodeURL(request.getContextPath() +

"/catalog?bookId=" + bookId)

+ "\"> &nbsp;" +

messages.getString("CartAdd") + "&nbsp;</a></td></tr>" +

"<tr>" +

"<td bgcolor=\"#ffffff\">" +

"&nbsp; &nbsp;" +

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>");

Page 87: Carrinho com

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 {

Page 88: Carrinho com

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>");

Page 89: Carrinho com

// 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>" +

"&nbsp;" + messages.getString("By") + "

<em>" + bd.getFirstName() + " " +

Page 90: Carrinho com

bd.getSurname() + "</em> &nbsp; &nbsp; "

+

"(" + bd.getYear() + ")<br> &nbsp; <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>&nbsp;&nbsp;&nbsp;" +

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

+

Page 91: Carrinho com

"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.");

Page 92: Carrinho com

}

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);

Page 93: Carrinho com

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> &nbsp; <br>" +

"</font>");

} catch (BookNotFoundException ex) {

Page 94: Carrinho com

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>&nbsp; <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>&nbsp;");

// 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>");

Page 95: Carrinho com

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() +

Page 96: Carrinho com

"</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> &nbsp; <p><strong><a href=\"" +

response.encodeURL(request.getContextPath()

+ "/catalog") +

Page 97: Carrinho com

"\">" +

messages.getString("ContinueShopping") + "</a> &nbsp; &nbsp;

&nbsp;" +

"<a href=\"" +

response.encodeURL(request.getContextPath()

+ "/cashier") +

"\">" + messages.getString("Checkout") +

"</a> &nbsp; &nbsp; &nbsp;" +

"<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> &nbsp; <br>" +

"<center><a href=\"" +

response.encodeURL(request.getContextPath()

+ "/catalog") +

"\">" + messages.getString("Catalog") +

"</a> </center>");

}

out.println("</body> </html>");

out.close();

}

Page 98: Carrinho com

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();

Page 99: Carrinho com

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 =

Page 100: Carrinho com

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>" +

Page 101: Carrinho com

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

Page 102: Carrinho com

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

Page 103: Carrinho com

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

Page 104: Carrinho com

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', '',

Page 105: Carrinho com

'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

Page 106: Carrinho com

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: