Por que eu devo ler este artigo:Este artigo trata os conceitos de servlets, visando apresentar a tecnologia aos desenvolvedores Java. Ele descreve os conceitos fundamentais de servlet e exibe uma aplicação prática demonstrando todo o potencial de utilização dos servlets.

Explore a programação web com Java, entendendo seus conceitos e características, e aprenda a desenvolver, configurar e instalar uma aplicação utilizando servlets.

Este artigo serve para desenvolvedores que desejam iniciar a construção de aplicações web com Java, através dos conceitos principais e do desenvolvimento de uma aplicação de exemplo.

Guia do artigo:

Muitos desenvolvedores quando desejam iniciar na construção de aplicações web em Java encontram, na maioria das vezes, frameworks prontos que facilitam o desenvolvimento, mas que possuem uma série de conceitos embutidos, que não são especificados. Entre eles, o conceito de servlets. Conhecer os servlets permite não somente entender melhor esses frameworks, como também fornece ao desenvolvedor uma capacidade maior de interação com eles.

Um servlet é uma classe Java que executa do lado do servidor, atendendo a requisições HTTP e construindo informações de resposta para o cliente. Todo o gerenciamento do ciclo de vida de um servlet (carregamento, inicialização, atendimento de requisições, finalização e descarregamento) é realizado pelo Servlet Container, que é um módulo de um servidor web. Além dos servlets, uma aplicação web pode conter arquivos estáticos (HTML, JavaScript, CSS, etc.) e dinâmicos (classes Java, bibliotecas, etc.), que devem seguir uma estrutura específica de diretórios.

Para exemplificar o conceito de servlets, este artigo apresenta uma aplicação simples, uma loja virtual, que consiste basicamente no processo de compra de CDs. O exemplo ilustra os passos necessários para a construção e configuração de servlets, criação das classes de negócio e arquivos HTML estáticos, além de mostrar como utilizar a IDE Eclipse para o desenvolvimento e execução da aplicação.

Apresentaremos em dois artigos os conceitos básicos para o desenvolvimento web na plataforma Java EE. Neste primeiro artigo abordaremos o conceito de servlets, enquanto no segundo falaremos sobre JSP. Vale ressaltar que todos os frameworks para web em Java utilizam de forma intrínseca essas tecnologias. Sendo assim, é de fundamental importância o conhecimento delas, objetivando dar aos desenvolvedores web a capacidade de explorar ao máximo as vantagens dos frameworks disponíveis no mercado.

Servidores Web e Servlets

Todo servidor que provê serviços para clientes remotos tem duas principais responsabilidades. A primeira é lidar com as requisições do cliente. A segunda é criar uma resposta que lhe será enviada como retorno. A primeira tarefa envolve programação no nível de sockets, extraindo informações das mensagens de requisição, e implementando protocolos cliente-servidor, tais como FTP e HTTP.

A segunda tarefa, criar a resposta, varia de serviço para serviço. Por exemplo, no caso do servidor FTP que requisita a transferência de um arquivo do servidor, a resposta é tão simples quanto localizar um arquivo numa máquina local. Por outro lado, servidores HTTP tem de criar uma resposta dinamicamente, a qual pode envolver tarefas complicadas como: retornar informações de um banco de dados, retornar informações verificando as regras de negócio existentes e apresentar os resultados de operações seguindo um padrão de formato desejado pelos diferentes clientes.

A melhor arquitetura para atender aos servidores HTTP é dividir o código de uma aplicação em duas partes executáveis: uma que execute as tarefas de rede (conexão, desconexão, transferência de dados, etc...) e outra que trabalhe com a lógica de aplicação, permitindo que ambos os executáveis tenham uma interface entre eles. Esse tipo de separação torna possível modificar o código na lógica da aplicação sem afetar o módulo de rede.

Antigamente, desenvolvedores implementavam esta arquitetura para servidores HTTP usando CGI (Common Gateway Interface). De um lado da interface temos o servidor web principal e do outro lado da interface os scripts CGI. O servidor web atua como o módulo de comunicação da rede e gerencia os clientes, enquanto o script CGI atua como o módulo de processamento de dados e entrega os resultados do processamento.

Infelizmente, este modelo sofria com problemas de performance, como por exemplo: a cada nova requisição era necessário a criação de um novo processo e, após executar o script CGI, o mesmo era destruído; essa estratégia se torna, então, ineficiente. Assim, o melhor caminho é ter o suporte ao servidor separado do módulo executável. Dessa forma, o módulo executável pode ser carregado em memória e inicializado somente uma vez – quando o servidor é iniciado. Cada requisição pode, agora, utilizar o módulo executável já em memória.

Os módulos executáveis separados do módulo servidor são conhecidos como Server extensions. Em plataformas nativas, Server extensions são escritos usando API´s em C providas pelas distribuições de servidores. Por exemplo, o Netscape Server provê o Netscape Server Application Programming Interface (NSAPI), e o Internet Information Server (IIS) da Microsoft provê o Internet Server Application Programing Interface (ISAPI).

Em Java, Server extensions são escritos usando a API Servlet e o módulo de extensão do servidor é chamado servlet. Um servlet, então, é um programa que possui uma lógica de negócio e executa do lado do servidor.

Servlet Container

Um servidor web usa um módulo separado para carregar e rodar o servlet. Este módulo especializado, que é dedicado para gerenciamento de servlets, é chamado de Servlet Container ou servlet engine. A Figura 1 mostra os diferentes componentes existentes em um servidor web. Arquivos HTML são armazenados no sistema de arquivos, servlets executam dentro de um Servlet Container e os dados estão armazenados na base de dados.

Todos os componentes de uma aplicação baseada na web
Figura 1. Todos os componentes de uma aplicação baseada na web

O browser envia as requisições do cliente para o servidor web. Se o objetivo da requisição é encontrar um arquivo estático (tipicamente HTML ou imagens), o servidor o identifica diretamente. Se o objetivo é um servlet, o servidor delega a requisição para o Servlet Container, o qual encaminha as requisições para o servlet de destino uma após a outra. Os servlets podem usar o sistema de arquivos, banco de dados, e outros recursos para gerar saídas dinâmicas.

Conceitualmente, um Servlet Container é parte do servidor web, mesmo que ele possa executar em um processo separado. Dessa forma, servlets containers são classificados seguindo três tipos:

  1. StandAlone – nesse tipo, servlets containers são tipicamente servidores web escritos em Java onde os dois módulos – o servidor web principal e o Servlet Container – são partes integrantes de um mesmo programa. Por exemplo, um servlet container, como o Tomcat, executando sobre a JVM como uma aplicação Java. Ele trata as requisições estáticas, como HTML, e as requisições para servlets;
  2. In-process – aqui, o servidor web principal e o Servlet Container estão em diferentes programas, mas o container executa dentro do processo do servidor principal como um plug-in. Nesse caso, podemos utilizar como exemplo o Tomcat executando dentro do servidor web Apache. O Apache carrega uma JVM que executa o Tomcat. Assim, o Apache trata as requisições de conteúdo estático, enquanto o Tomcat trata as requisições de servlet;
  3. Out-of-process – como no servidor in-process, o servidor web principal e o Servlet Container são programas diferentes. Porém, o servidor web executa em um processo, enquanto o Servlet Container executa em outro processo. Nesse caso, para o servidor web comunicar com o Servlet Container, ele usa um plug-in, que é geralmente provido pelo distribuidor do Servlet Container. Esse exemplo é caracterizado pelo Tomcat executando como um processo separado (em máquinas diferentes, por exemplo) sendo configurado para receber as requisições do servidor web Apache.

A arquitetura do HTTPServlets

A tecnologia Java Servlet é comumente usada para representar a lógica de negócio de uma aplicação web, embora servlets possam também conter lógica de apresentação. A especificação servlet se aplica para qualquer protocolo, mas na prática, a maioria dos servlets é escrita para o protocolo HTTP. Nesse contexto, todas as vezes que nos referirmos a servlets estaremos falando de HTTPServlets.

O protocolo HTTP consiste de requisições do cliente para o servidor e respostas do servidor para o cliente. Um web browser envia uma requisição HTTP para um servidor web em qualquer uma das seguintes situações:

  1. Um usuário clicou em um hyperlink apresentado em uma página HTML;
  2. Um usuário preencheu um formulário em uma página HTML e o submeteu;
  3. Um usuário entrou com uma URL na barra de endereços do browser e pressionou Enter. Outros eventos levam o browser a enviar uma requisição para o servidor web. Por exemplo, uma função JavaScript pode chamar um método reload(), levando o browser a enviar uma mensagem ao servidor para recarregar o arquivo. Entretanto, todos esses eventos resumem-se a um dos três eventos listados acima, porque a função de tais métodos é simular programaticamente uma ação do usuário.

Por default, o browser utiliza o método HTTP GET em todos os eventos acima, mas nós podemos customizar o comportamento do browser para usar um método HTTP diferente. Três são os possíveis métodos de requisição HTTP:

  1. GET – usado para buscar arquivos HTML ou imagens, pois apenas o nome do arquivo precisa ser enviado;
  2. POST – usado para enviar uma quantidade grande de dados, fazer upload de arquivos e capturar o usuário e a senha em um formulário. Ao contrário do GET suas informações não são apresentadas na barra de endereços;
  3. HEAD – é parecido com o GET, exceto pelo fato de que o servidor retorna apenas o cabeçalho da resposta e não a página inteira. Isso torna o HEAD mais eficiente que o GET em casos onde precisamos somente do cabeçalho para obtermos a resposta desejada.

Para cada método HTTP existe um método correspondente na classe HTTPServlet com uma assinatura geral, representado na classe por: protected void doXXX(HttpServletRequest, HttpServletResponse) throws ServletException, IOException; onde doXXX() depende do método HTTP. Neste caso teríamos: doGet(), doPost() e doHead().

Quando uma requisição chega ao Servlet Container ele invoca um método chamado service, que identifica qual o método da requisição HTTP, e invoca o doXXX() correspondente ao método no servlet. Por exemplo, se o método invocado pela página HTML for o POST, será invocado o método doPost() do servlet.

Todo método doXXX() possui dois parâmetros: um objeto HttpServletRequest e um HttpServletResponse.

O HttpServletRequest permite-nos analisar a requisição. Ele provê uma visualização dos dados enviados pelo browser. Esses dados incluem parâmetros, meta informações e um stream de dados no formato texto ou binário. Ele possui três métodos básicos que nos permitem recuperar os parâmetros da requisição, apresentados na Tabela 1.

Tabela 1. Lista de métodos para recuperar parâmetros da requisição.
Métodos Descrição
String getParameter (String paramName) Retorna um valor associado a um parâmetro.
String[] getParameterValues (String paramName) Retorna todos os valores associados a um parâmetro. Pode ser utilizado para componentes que permitem múltipla seleção de valores.
Enumeration getParameterNames() Método usado quando você não conhece o nome dos parâmetros. Utilize a enumeração para obter os parâmetros e seus respectivos valores.

Para enviar informações de volta para o browser utilizamos um objeto HttpServletResponse. Ele aceita os dados que o servlet precisa para enviar para o cliente e o formata em uma mensagem HTTP, conforme a especificação HTTP.

HttpServletResponse é uma classe que estende a ServletResponse. Ela possui alguns métodos genéricos que são utilizados para enviar informações ao browser diretamente no formato HTML. Um dos métodos mais utilizados é getWriter(). Ele retorna um objeto da classe java.io.PrintWriter, extensivamente usado pelos servlets para gerar páginas HTML dinamicamente.

Depois de analisar uma requisição, um servlet pode decidir que é necessário redirecionar o browser para outro site ou outra página. Para tal função, a classe HttpServletResponse provê o método sendRedirect(<caminho>), onde <caminho> é o endereço da página para onde a resposta será redirecionada.

Ciclo de Vida

Nesse momento, deve estar bem claro que um servlet recebe uma requisição, a processa e envia uma resposta tratada em um método doXXX(). Além disso, há um pouco mais para ser entendido sobre o ciclo de vida de um servlet. Antes que um servlet possa tratar uma requisição do cliente, o container servlet deve executar alguns passos para disponibilizar o servlet em um estado no qual ele esteja pronto para responder a tais requisições. O primeiro passo é carregar e instanciar a classe servlet, nesse momento o servlet é considerado no estado carregado.

O segundo passo é inicializar a instância do servlet. Uma vez no estado inicializado, o container pode invocar o método para tratamento das requisições do cliente (doXXX()). Em algum momento o container pode querer destruir a instancia do servlet, mudando este para o estado destruído. Finalmente, quando o container servlet é desligado, ele deve descarregar a instancia do servlet. Estes estados constituem o cliclo de vida de um servlet. A Figura 2 apresenta o ciclo.

Ciclo de Vida do Servlet
Figura 2. Ciclo de Vida do Servlet

Escopos: request, session e application.

Para que um servlet possa compartilhar dados, ele coloca esses dados em um local conhecido, o qual atua como um container, e outros servlets acessam os dados deste local. Estes containers são os objetos: ServletRequest, HttpSession e ServletContext. Todos os três objetos provêm um método setAttribute (String name, Object value) (coloca o dado no container) e um método Object getAttribute (String name) (para acesso aos dados).

Embora os dados possam ser compartilhados usando qualquer um desses containers, existe uma diferença na visibilidade dos dados presentes neles. Objetos compartilhados usando ServletRequest são acessíveis somente durante a chamada da requisição (esse objeto não pode ser acessado numa requisição seguinte, mesmo que esta seja feita ao mesmo servlet), ou seja, valem somente durante a chamada do servlet e sua resposta.

Objetos compartilhados usando HttpSession são acessíveis somente para o tempo de vida da sessão (valem enquanto a sessão do usuário estiver ativa, por exemplo, uma validação de usuário – se a sessão expirar ele deve logar novamente), e os objetos compartilhados usando ServletContext são acessíveis pelo tempo de vida da sua aplicação web (os dados ficam disponíveis para qualquer servlet da aplicação independente da sessão em que tenha sido colocado).

Estrutura de diretórios de uma aplicação web

Uma aplicação web consiste de muitos recursos, incluindo servlets, classes utilitárias, bibliotecas de terceiros, arquivos HTML, etc. Esses recursos devem ser organizados de tal forma que possam ser interpretados corretamente pelo container web. Uma típica estrutura de uma aplicação web teria a seguinte forma:

WebApp

  • html (contém os arquivos HTML);
  • images (contém os arquivos GIFs, JPEGs, BMPs);
  • javascript (contém os arquivos *.js);
  • css (contém os arquivos de estilo);
  • index.html (arquivo default da aplicação);
  • WEB-INF:
    • classes (contém as classes da aplicação estruturadas em pacotes);
    • lib (bibliotecas de terceiros ou classes da aplicação empacotadas em um JAR);
    • web.xml (descritor da aplicação).

Os arquivos HTML, javascript, de imagem e de estilo são todos arquivos públicos, devem se encontrar logo abaixo do diretório root da aplicação, podendo ser organizados em pastas. As classes e bibliotecas devem estar dentro do diretório WEB-INF, exatamente na estrutura apresentada, além do arquivo descritor da aplicação web.xml. Esse arquivo deve estar presente em toda aplicação web. Ele contém toda a informação necessária para o Servlet Container rodar a aplicação, tais como: declarações e mapeamento dos servlets, propriedades, autorização e requisitos de segurança entre outros.

A Listagem 3 ilustra um exemplo simples da utilização desse arquivo. As linhas 1 e 2 correspondem ao cabeçalho do arquivo XML e a linha 6 ao nome da aplicação. A tag <servlet> mapeia um alias para o servlet lojavirtual.ListaCdsServlet e a tag <servlet-mapping> mapeia quais URLs dentro do contexto da aplicação serão atendidas por este servlet.

WAR (Web Archive File)

Conforme foi visto, uma aplicação web consiste de um conjunto de vários arquivos (HTML, javascripts, css, servlets, classes de negócio, bibliotecas externas, etc.) agrupados em uma estrutura pré-definida. Para empacotar uma aplicação e permitir que esta possa ser migrada para outros servidores web, esses arquivos devem ser empacotados em um único arquivo JAR, mas com extensão WAR. O arquivo WAR é tratado de forma específica por um container web.

Desenvolvendo uma aplicação web

Para ilustrar os conceitos de servlets explorados no artigo, vamos apresentar o exemplo de uma loja de CDs virtual. O exemplo é bastante simplificado, consiste basicamente no processo de compra de CDs dividida em quatro etapas:

  1. O cliente solicita visualizar o catálogo de CDs disponíveis para compra;
  2. O cliente seleciona os CDs que deseja comprar;
  3. O cliente informa os dados de compra: nome, endereço de entrega e número de cartão;
  4. O cliente confirma os dados.

Utilizando a IDE Eclipse

Iremos utilizar a IDE Eclipse, versão Ganymede (ver Links), para auxiliar no desenvolvimento da aplicação de exemplo. Também utilizaremos o container web Tomcat, na versão 6, para rodar a aplicação. Para criar uma aplicação web no Eclipse basta realizar os seguintes passos:

  1. Na perspectiva “Java EE”, clique com o botão direito na área de projetos e selecione a opção New -> Dynamic Web Project (Figura 3);
  2. Preencha o nome do projeto, no caso do exemplo, LojaVirtual, e associe o projeto ao Tomcat. Como ainda não existe a referência nesse ponto, clique em New na opção de Target Runtime (Figura 4);
  3. Selecione Apache Tomcat v6.0 da lista de servidores exibidos (Figura 5) e informe o diretório de instalação onde este se encontra (Figura 6);
  4. Após ter configurado o Tomcat, clique em Next. Será exibida uma tela de confirmação com as propriedades da aplicação web. Mantenha as definições default da ferramenta e clique em Finish.
Criando uma aplicação web com o Eclipse IDE
Figura 3. Criando uma aplicação web com o Eclipse IDE
Criando uma aplicação web com o Eclipse IDE
Figura 4. Criando uma aplicação web com o Eclipse IDE
Selecionando um servidor web no Eclipse IDE
Figura 5. Selecionando um servidor web no Eclipse IDE
Especificando um container web no Eclipse IDE
Figura 6. Especificando um container web no Eclipse IDE

Estrutura do projeto

O Eclipse gera o projeto seguindo a estrutura de diretórios de um projeto web, como pode ser visto na Figura 7. Segue uma descrição do conteúdo dos diretórios mais relevantes:

  • LojaVirtual: diretório raiz da aplicação. Irá representar o contexto da aplicação no servidor web;
  • src: contém os arquivos fontes da aplicação, as classes de negócio, os servlets, etc. Esse diretório não precisa estar presente no servidor web;
  • WebContent: contém os arquivos HTML da aplicação;
  • WEB-INF: contém as classes compiladas e as bibliotecas externas utilizadas pela aplicação. Contém também o descritor web.xml;
Estrutura de pastas da aplicação web
Figura 7. Estrutura de pastas da aplicação web

Exibindo o catálogo de CDs

A página inicial da aplicação é bastante simples, apenas contém um link para exibir o catálogo de CDs, observe a Figura 8. Ao clicar no link o sistema irá chamar um servlet para exibir o catálogo de CDs. A idéia de ser um servlet nesse ponto consiste em gerar dinamicamente a lista de CDs cadastrados, pois caso fosse uma página HTML estática, o arquivo teria que ser atualizado toda vez que um novo cd entrasse no cadastro, podendo gerar inconsistência além de esforço desnecessário.

Para conseguir realizar a chamada ao servlet precisamos dos seguintes passos:

  • Desenvolver o servlet que realiza a exibição do catálogo, apresentado na Listagem 2. Como o servlet será invocado através de um link, o método doGet() deve ser implementado;
  • Fazer o mapeamento do servlet no descritor web.xml. Aqui devemos informar como o servlet será identificado pelo container web (Listagem 3). Conforme explicado previamente, criamos uma tag <servlet> para associar a classe servlet ListaCdsServlet a um alias e associamos esse alias à url /listacds;
  • Desenvolver o HTML que efetuará a chamada ao servlet (Listagem 1). Note que o link aponta para /LojaVirtual/listacds (linha 8), o que significa que o container web irá procurar dentro do contexto LojaVirtual o padrão listacds, que corresponde ao servlet ListaCdsServlet.
Página inicial da Loja Virtual
Figura 8. Página inicial da Loja Virtual

A classe ListaCdsServlet herda de HttpServlet e reimplementa os métodos init e doGet. No método init() são criadas algumas instâncias de CDs que são armazenadas em um map. A utilização do map foi feita apenas para simplificar o artigo, numa aplicação real essa parte seria substituída por um acesso a banco de dados, por exemplo. As instâncias de CDs são, então, colocadas como atributo no escopo de aplicação (application).

Conforme foi explicitado nos conceitos, esse escopo é compartilhado em todo o contexto da aplicação. Dessa forma, a hash contendo os CDs pode ser acessada por qualquer servlet da aplicação. No método doGet() é montada a página contendo o catálogo de CDs. Para isso são setados os atributos listaCds e listaEditavel no request.

O valor desses atributos serão lidos por outro servlet, ListaCdsIncludeServlet, responsável por montar uma tabela contendo os dados de CDs a partir de uma lista e manter cada linha da tabela editável, oferecendo um checkbox de seleção, caso o atributo listaEditavel seja true.

A idéia de construir um servlet para montar a tabela é poder reutilizá-lo posteriormente na confirmação dos dados, quando é exibida uma página contendo os CDs selecionados pelo usuário no momento da compra. Dessa forma, a criação da tabela dinâmica com os dados de CDs fica centralizada em um único servlet, promovendo o princípio de reuso e facilitando a manutenção.

Desenvolvendo a página com o formulário de requisição dos dados de compra

O código deste servlet é bastante simples, ele recupera os identificadores dos CDs selecionados pelo usuário, e recupera os CDs do catálogo para cada identificador, criando assim uma lista de objetos de CDs selecionados pelo usuário. Finalmente, coloca essa lista na sessão do usuário (linha 21) e redireciona a requisição para o arquivo HTML que contém o formulário (Listagem 5). Esse é um exemplo típico de uso do objeto de sessão do usuário, pois os CDs selecionados devem ser visíveis apenas durante a sessão do usuário.

Página que exibe o catálogo dos CDs
Figura 9. Página que exibe o catálogo dos CDs

Desenvolvendo a página de confirmação dos dados da compra

Ao preencher e enviar os dados de compra (Figura 10) o servlet ConfirmaDadosServlet é invocado e então o método doPost() é executado. O servlet recupera da requisição os dados de compra e monta a página de confirmação, que consiste nos dados do cliente, na lista de CDs selecionados e do valor total da compra (Figura 11). Para a construção da tabela com os CDs selecionados na compra, novamente o servlet ListaCdsIncludeServlet é utilizado.

Os atributos listaCds e listaEditavel passados no request são preenchidos respectivamente com a lista de cds recuperada da sessão do usuário e valor false, sinalizando que a tabela não será editável, ou seja, será gerada sem o checkbox para cada linha da tabela.

Página que exibe o formulário dos dados de compra
Figura 10. Página que exibe o formulário dos dados de compra
i
Página de confirmação da compra
Figura 11. Página de confirmação da compra

Executando a aplicação

Lembre-se que já configuramos a aplicação para utilizar o container web Tomcat no momento de sua criação. Dessa forma, para executar a aplicação devemos primeiramente iniciar o servidor, bastando para isso, ir para a aba Servers e iniciar o servidor Tomcat, de acordo com a Figura 12. Uma vez o servidor iniciado, digite a url http://localhost:8080/LojaVirtual/ em um browser. A página inicial será exibida (index.html). O Eclipse também oferece a possibilidade de iniciar o servidor em modo debug, de forma a depurar a aplicação.

Em todo o processo de desenvolvimento, é necessário muitas vezes parar o servidor para efetuar alterações nos arquivos HTML, css ou mesmo em servlets, classes de negócio, etc. De uma forma geral, o container detecta automaticamente alterações em páginas estáticas (HTMLs, javascripts, estilos, imagens, etc.), entretanto para alterações em classes (servlets, classes de negócio) e bibliotecas externas é desejável restartar o servidor (parar e iniciar novamente) para que as atualizações reflitam na execução da aplicação.

Iniciando o servidor Tomcat
Figura 12. Iniciando o servidor Tomcat
Conclusões

Entender os conceitos de servlets é fundamental no processo de construção de uma aplicação web utilizando Java. Embora já existam inúmeros frameworks prontos no mercado que facilitam o desenvolvimento de aplicações, todos utilizam intrinsecamente esses conceitos. Dessa forma, o entendimento permitirá ao desenvolvedor uma maior capacidade de interagir com esses frameworks.

Esse artigo apresentou o modelo de comunicação do servlet, seu ciclo de vida, a estrutura de diretórios de uma aplicação web e seu funcionamento através de um exemplo.

No próximo artigo abordaremos os conceitos de JSP (Java Server Pages), descrevendo sua integração com servlets e a utilização de taglibs, que visam melhorar a legibilidade e manutenibilidade da aplicação.

Listagem 1. Código HTML da página principal da aplicação.

<html>

  <head>

    <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">

    <title>Loja virtual</title>

  </head>

  <body>

    <h2>Loja Virtual</h2>

    <a href="/LojaVirtual/listacds" title="Catálogo de cds">Ver catálogo de cds</a>

  </body>

</html>
Listagem 2. Servlet que exibe o catálogo de Cds.

package lojavirtual;

 …

 public class ListaCdsServlet extends HttpServlet {



   @Override

   public void init() throws ServletException {

     super.init();

                

     //inicializa o map com o catálogo de cds

     CD cd1 = new CD(1L, "Novelas", "DJavan", 25.0);

     CD cd2 = new CD(2L, "Ao Vivo (vol. 1)", "DJavan", 30.0);

     CD cd3 = new CD(3L, "Ao Vivo (vol. 2)", "DJavan", 30.0);

     CD cd4 = new CD(4L, "Lilás", "DJavan", 20.0);

     CD cd5 = new CD(5L, "Acústico MTV", "Capital Inicial", 23.0);

     CD cd6 = new CD(6L, "Acústico MTV", "Kid Abelha", 21.0);

     CD cd7 = new CD(7L, "Infinito Particular", "Marisa Monte", 35.0);

     CD cd8 = new CD(8L, "Universo ao meu redor", "Marisa Monte", 40.0);

     CD cd9 = new CD(9L, "Greatest hits", "Queen", 20.0);

     CD cd10 = new CD(10L, "Queen Collection", "Queen", 45.0);

                  

     HashMap<Long, CD> mapCds = new HashMap<Long, CD>();

     mapCds.put(cd1.getCodigo(), cd1);

     mapCds.put(cd2.getCodigo(), cd2);

     mapCds.put(cd3.getCodigo(), cd3);

     mapCds.put(cd4.getCodigo(), cd4);

     mapCds.put(cd5.getCodigo(), cd5);

     mapCds.put(cd6.getCodigo(), cd6);

     mapCds.put(cd7.getCodigo(), cd7);

     mapCds.put(cd8.getCodigo(), cd8);

     mapCds.put(cd9.getCodigo(), cd9);

     mapCds.put(cd10.getCodigo(), cd10);

                  

     getServletContext().setAttribute("catalogoCds", mapCds);

   }


   @Override

   protected void doGet(HttpServletRequest req, HttpServletResponse resp)

     throws ServletException, IOException {



     resp.setContentType("text/html; charset=ISO-8859-1");

     PrintWriter out = resp.getWriter();

               

     //cria uma página de retorno com a lista de cds

     out.println("<html>");

     out.println("<body>");

     out.println("<h2>Lista de CDs</h2>");

47               

     out.println("<form name=formLista action='/LojaVirtual/dadosCompra' method=POST>");

               

     HashMap<Long, CD> mapCds = (HashMap<Long, CD>)    

         getServletContext().getAttribute("catalogoCds");

     req.setAttribute("listaCds", mapCds.values());

     req.setAttribute("listaEditavel", true);



     //chama o servlet que monta a tabela de cds
     req.getRequestDispatcher("listaCdsInclude").include(req, resp);

               

     out.println("<input type=submit value='Adicionar ao carrinho'>");

     out.println("</form>");

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

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

   }

 }
 
Listagem 3. Mapeamento de servlet no descritor web.xml.

 <?xml version="1.0" encoding="UTF-8"?>

 <web-app id="WebApp_ID" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

   <display-name>LojaVirtual</display-name>

   <servlet>

     <servlet-name>listaCds</servlet-name>

     <servlet-class>lojavirtual.ListaCdsServlet</servlet-class>

   </servlet>

   <servlet-mapping>

     <servlet-name>listaCds</servlet-name>

     <url-pattern>/listacds</url-pattern>

   </servlet-mapping>

 </web-app>
 
Listagem 4. Servlet que exibe o formulário para o preenchimento dos dados de compra.

 package lojavirtual;

 ...

 @SuppressWarnings("serial")

 public class DadosCompraServlet extends HttpServlet {



   @Override

   protected void doPost(HttpServletRequest req, HttpServletResponse resp)

     throws ServletException, IOException {



     //recupera os codigos dos cds selecionados

     String[] codigosCds = req.getParameterValues("chkCodigosCds");

               

     //recupera os cds do catalogo e adiciona os selecionados nas sessao

     HashMap<Long, CD> mapCds = (HashMap<Long, CD>)

         getServletContext().getAttribute("catalogoCds");

     List<CD> cdsSelecionados = new ArrayList<CD>();

     for (String codigo : codigosCds) {
       CD cd = mapCds.get(new Long(codigo));

       cdsSelecionados.add(cd);

     }

     HttpSession session = req.getSession();

     session.setAttribute("cdsSelecionados", cdsSelecionados);

               

     //chama a página para o preenchimento dos dados de compra

     req.getRequestDispatcher("dadosCompra.html").forward(req, resp);

   }

 }
Listagem 5. Página HTML contendo o formulário dos dados de compra.

<html>

  <head>

    <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">

    <title>Loja virtual</title>

  </head>

  <body>

    <h2>Dados de compra:</h2>

    <form name="formDados" action="/LojaVirtual/confirmaDados" method="POST">

      <table>

        <tr>

          <td>Nome:</td>

          <td><input type="text" name="nome"></td>

        </tr>

        <tr>

          <td>Endereço de entrega:</td>

          <td><input type="text" name="endereco"></td>

        </tr>

        <tr>

          <td>Número do cartão:</td>

          <td><input type="text" name="cartao"></td>

        </tr>

      </table>

      <input type="submit" value="Enviar dados"> 

    </form>

  </body>

</html>
Listagem 6. Servlet que exibe a tela de confirmação de dados.

 package lojavirtual;

 ...

 public class ConfirmaDadosServlet extends HttpServlet {



   @Override

   protected void doPost(HttpServletRequest req, HttpServletResponse resp)

     throws ServletException, IOException {



     //recupera os dados de compra

     String nome = req.getParameter("nome");

     String endereco = req.getParameter("endereco");

     String cartao = req.getParameter("cartao");

               

     //exibe uma pagina para confirmacao dos dados de entrega e cds selecionados

     PrintWriter out = resp.getWriter();

              

     out.println("<html>");

     out.println("<body>");

     out.println("<h2>Confirmação dos dados</h2>");

               

     out.println("<table>");

     out.println("<tr>");

     out.println("<td>Nome:</td>");

     out.println("<td>" + nome + "</td>");

     out.println("</tr>");

     out.println("<tr>");

     out.println("<td>Endereço:</td>");

     out.println("<td>" + endereco + "</td>");

     out.println("</tr>");

     out.println("<tr>");

     out.println("<td>Número do cartão:</td>");

     out.println("<td>" + cartao + "</td>");

     out.println("</tr>");

     out.println("</table>");

               

     out.println("<br><b>Lista de Cds</b>");

               

     List<CD> cdsSelecionados = (List<CD>) req.getSession().getAttribute("cdsSelecionados");

     req.setAttribute("listaCds", cdsSelecionados);

     req.setAttribute("listaEditavel", false);

     req.getRequestDispatcher("listaCdsInclude").include(req, resp);

               

     //calcula o total

     double total = 0;

     for (CD cd : cdsSelecionados) {

       total += cd.getPreco();

     }

               

     out.println("Total da compra: " + total);

     out.println("<br><br><input type=submit value='Confirmar dados'>");

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

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

     

   }

 }