Servlets, o coração do Java para web
A internet tem sido a grande plataforma para muitos projetos de software da atualidade. Não
é à toa que grande parte dos sistemas de hoje, independente de possuir suas interfaces
desktop, mobile ou web (rodando no browser), faz uso do modelo cliente-servidor. Este
modelo é caracterizado pelo fato da aplicação possuir módulos que executam em diferentes
máquinas. Um módulo - servidor - geralmente é responsável pelas regras de negócio,
persistência dos dados ou processamentos mais custosos, como, por exemplo, cálculos
matemáticos complexos. Já o módulo cliente geralmente contém apenas a camada de
apresentação da aplicação, a qual possui a interface com o usuário. A comunicação entre
cliente e servidor, por sua vez, normalmente é feita através de uma rede utilizando-se de
algum protocolo de comunicação, como o HTTP.
Com base nisso, neste artigo veremos como construir esse módulo servidor utilizando Java,
mais especificamente a Servlet API. Aqui iremos descobrir como os servlets estendem as
possibilidades na construção de uma aplicação web. Veremos que, em adição às páginas HTML
estáticas, por meio do uso da Servlet API é possível realizar processamento lógico no servidor,
mantendo a simplicidade e portabilidade oferecidas pela linguagem de marcação de
hipertexto no lado cliente.
Também aprenderemos a trabalhar com dados compartilhados, gerenciar a sessão do usuário
e delegar o tratamento de requisições. Tudo isso através da construção de exemplos práticos e
um pequeno projeto.
A Servlet API
O Java tem sido amplamente utilizado no desenvolvimento de sistemas web devido a
características como maturidade, segurança, robustez e confiabilidade. E é no coração do
desenvolvimento web em Java, que encontramos a Servlet API. Atualmente em sua versão 3.0,
esta API fornece diversos recursos que dão suporte e tornam possível a construção de
aplicações web dinâmicas utilizando Java.
A Servlet API surgiu da necessidade de se criar conteúdo dinâmico para as páginas web. Na
época isto já era feito utilizando outras tecnologias, como o famoso CGI e até mesmo com
Java, através de Applets. Mas ainda não era o ideal, devido às limitações que tais tecnologias
carregam consigo. Para resolver esse problema no universo Java, foi criada então a Servlet API.
Através dela é possível implementar aplicações web utilizando todos os recursos do Java,
como APIs de acesso a banco de dados, sistemas de arquivos e outros recursos impossíveis de
serem utilizados apenas com páginas HTML comuns.
Tudo isso sem sobrecarregar a máquina do usuário, uma vez que todo esse processamento é
realizado no lado servidor da aplicação, cabendo ao browser (cliente) apenas processar o
HTML gerado pelo back-end, não sendo necessária a instalação de nenhum tipo de plugin,
como o Flash Player, Silverlight ou mesmo o Java Plugin, este último utilizado para executar
applets no browser.
Common Gateway Interface: Tecnologia onde scripts eram executados no servidor a fim de
gerar conteúdo dinâmico. Foi sendo substituída principalmente devido a sua baixa
escalabilidade e portabilidade.
Applets: Tecnologia Java que permite a criação de conteúdo dinâmico, porém do lado cliente -
no browser mais especificamente. Por rodar no cliente, possui diversas limitações, como a
necessidade de um plugin para poder ser executado, o “peso” do applet, que precisa ser
baixado com todas as dependências e carregado na página, restrições de segurança para
acesso ao sistema de arquivos, bancos de dados, entre outros pontos negativos.
Servlets
De modo resumido, um Servlet é uma classe Java que roda no servidor web e é capaz de
processar as requisições feitas pelos clientes (browsers) gerando uma resposta dinâmica,
geralmente no formato HTML. Para isso essa classe deve implementar a interface
javax.servlet.Servlet, a qual define os métodos do ciclo de vida desses objetos no servidor,
sendo o principal deles o método service(), no qual é efetuado o tratamento da requisição.
Além dessa interface básica, a API disponibiliza algumas classes que simplificam o
desenvolvimento, uma vez que estas já provêm implementações para boa parte dos métodos
da interface, além de oferecer pontos de extensão para que o desenvolvedor possa
implementar suas próprias regras.
No caso das aplicações web, onde basicamente é usado o protocolo HTTP nas requisições, a
API disponibiliza a classe HttpServlet para ser estendida. Esta já faz todo o tratamento das
regras do protocolo e deixa a cargo do desenvolvedor apenas a lógica do tratamento da
requisição. A classe HttpServlet define métodos no formato doXxxx() para cada método HTTP -
GET, POST, CONNECT, OPTIONS, HEAD, TRACE, PUT e DELETE - e ao desenvolvedor basta
sobrescrever os métodos que desejar.
Os métodos doGet() e doPost() são os mais comumente implementados, uma vez que seus
correspondentes, GET e POST, no HTTP são os mais utilizados.
Os passos para criar um servlet HTTP são bem simples, a saber:
1. Criar uma classe que estenda HttpServet;
2. Escolher os métodos HTTP que serão tratados, sobrescrevendo o método da classe
HttpServlet correspondente (Ex.: doPost() para tratar requisições do tipo POST);
3. Informar ao container quais URLs serão tratadas. Para isso basta anotar a classe com a
anotação @WebServlet, que recebe como parâmetro o padrão de URL a ser tratado
pelo servlet.
A Servlet API também oferece a classe GenericServlet, que pode ser estendida para se criar um
servlet genérico, independente de protocolo. Contudo, na prática, essa possibilidade
geralmente é barrada pelos servidores de aplicação, os quais só trabalham com o protocolo
HTTP.
Hello Servlet World
Agora que já vimos um pouco sobre servlets, vamos verificar como tudo isso funciona na
prática.
Para tal, criaremos um pequeno servlet que simplesmente imprime uma mensagem de boas-
vindas no browser, seguindo a linha dos famosos Hello, Worlds! Nosso servlet terá ainda a
possibilidade de receber um parâmetro com o nome do usuário na requisição, para assim
exibir uma saudação nominal.
Ambiente
Para dar início aos nossos exemplos, vamos montar nosso ambiente de desenvolvimento, que
consistirá basicamente de um IDE e um servidor de aplicação. Para os exemplos deste artigo,
utilizaremos Eclipse e Tomcat, respectivamente.
No caso do Eclipse, vamos utilizar a distribuição Java EE, que já vem pré-configurada com
plugins para o desenvolvimento web (veja a seção Links). Para isso, baixe o arquivo e
descompacte-o num diretório de sua preferência.
O Apache Tomcat será o servidor de aplicação que adotaremos nos exemplos. Para fazer uso
da Servlet API 3.0, é preciso utilizar a versão 7 deste servidor (veja Links). Portanto, baixe o
Tomcat 7 e siga os passos abaixo para adicioná-lo ao Eclipse:
1. No menu, acesse File > New > Other... > Server;
2. Escolha a opção Apache > Apache Tomcat v7.0 e clique em Next;
3. Informe a localização do diretório de instalação do Tomcat e clique em Finish.
Projeto
Com o ambiente devidamente configurado, vamos criar um projeto para desenvolver os
exemplos. Portanto, crie um novo projeto web no Eclipse acessando o menu File > New >
Project... > Dynamic Web Project.
Em seguida, dê um nome ao projeto, selecione o servidor no qual o projeto será executado
(Target runtime) e a versão da Servlet API (Dynamic web module version) que será utilizada, de
acordo com a Figura 1.
O primeiro servlet
Criaremos agora nosso primeiro servlet. A Listagem 1 mostra o código de HelloServlet. Como
podemos observar, esta classe estende HttpServlet e implementa o método doGet(). Deste
modo, sempre que o servlet receber uma requisição do tipo HTTP GET, o método doGet() será
invocado pelo servidor de aplicação.
Através da anotação @WebServlet, informamos ao servidor que a classe em questão é um
servlet. A String “/serv/hello” indica que o servlet tratará as requisições para este path.
Também é possível utilizar padrões ao invés de um path específico, caso queiramos que um
mesmo servlet trate mais de uma URL. Exemplos de padrões são: /app/servlets/*, *.jpg, *.jsf,
*.action, etc.
Path: O caminho ou path é a parte da URL que indica que recurso específico está sendo
acessado. Uma URL em geral tem o formato: <Protocolo>://<Domínio>[:<Porta>]/<Path>. Para
a URL http://google.com/maps, por exemplo, http é o protocolo, google.com é o domínio e
/maps é o path. Quando a porta não é informada, é utilizada a porta padrão do protocolo, 80
no caso do HTTP.
Nas versões anteriores da Servlet API não existia a possibilidade de configurar os servlets
utilizando annotations.
Para tal propósito era preciso declarar o servlet e os mapeamentos para o mesmo no arquivo
web.xml, localizado na pasta WEB-INF do projeto. Era necessária a criação de uma tag
<servlet>, onde era informado o nome e a classe do servlet, e tags <servlet-mapping>, onde
eram informados os padrões de URL que cada servlet iria tratar. Este tipo de configuração
ainda é suportado na nova versão da API.
No método doGet() utilizamos o objeto PrintWriter, responsável por escrever os dados na
saída a ser retornada ao browser. Obtemos esse objeto através do método
response.getWriter() e o utilizamos para escrever a mensagem de boas vindas ao usuário.
Nesse momento estamos de fato gerando o conteúdo dinâmico, que será visualizado pelo
cliente. No nosso exemplo, é possível perceber que estamos montando um texto no formato
HTML, o qual será renderizado no browser que efetuou a requisição.
Na prática, dificilmente encontraremos um servlet que gere o código HTML diretamente,
como no nosso exemplo, por questões de clareza e manutenibilidade do código de
apresentação. Para tal propósito, foram criados o JavaServer Pages (JSP) e afins. Entretanto,
para não nos desviarmos do tema do artigo, não utilizaremos o JSP e deixaremos o código de
layout no próprio servlet.
Note que utilizamos o método request.getParameter() para recuperar um parâmetro da
requisição. Verificamos então se o parâmetro name, com o nome do usuário, foi passado. Se
positivo, montamos uma mensagem nominal de boas vindas. Caso contrário, montamos uma
mensagem genérica.
Listagem 1. Código da classe HelloServlet.
package br.com.exemplo.servlets;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/serv/hello")
public class HelloServlet extends HttpServlet {
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
String userName = request.getParameter("name");
StringBuilder greeting = new StringBuilder("Hello, ");
greeting.append((userName != null) ? userName : "Guest");
greeting.append("!");
StringBuilder html = new StringBuilder();
html.append("<html>");
html.append(" <head>");
html.append(" <title>Hello Servlets</title>");
html.append(" </head>");
html.append(" <body>");
html.append(" <h1>" + greeting + "</h1>");
html.append(" </body>");
html.append("</html>");
response.getWriter().print(html.toString());
}
}
Vamos então testar nosso servlet. Para isso, inicialize o servidor a partir do Eclipse, o que pode
ser feito na aba Servers, selecionando o servidor e clicando no botão Start, como mostra a
Figura 2.
Caso a aba Servers não esteja sendo exibida, acesse Window > Show View > Other... e
selecione Servers.
Agora abra o browser de sua preferência e acesse o endereço do servlet que acabamos de
criar: http://localhost:8080/HelloServlets/serv/hello, onde http://localhost:8080 é o endereço
do servidor, HelloServlets é o contexto da nossa aplicação (por padrão, o mesmo nome do
projeto no Eclipse) e serv/hello é o caminho que mapeamos para o servlet HelloServlet.
O resultado deve ser semelhante ao mostrado na Figura 3. Para testar a saudação nominal é
preciso informar na URL o valor do parâmetro name. Sendo assim, altere a URL para algo como
http://localhost:8080/HelloServlets/serv/hello?name=Jorge%20Amado e verifique o resultado.
Modularizando o tratamento da requisição
A Servlet API permite que um servlet delegue, total ou parcialmente, o tratamento de uma
requisição.
Assim, é possível encaminhar a requisição recebida, com ou sem alteração, para ser tratada
por outro componente, que pode ser outro servlet ou uma página JSP ou JSF, por exemplo.
Para demonstrar tal caso, criaremos a seguir uma versão mais rebuscada do nosso exemplo,
deixando a aplicação mais modularizada, separando as regras de negócio da montagem do
HTML resultante.
A classe HelloBusinessServlet (exibida na Listagem 2), que representa um servlet mapeado
para tratar requisições à URL /serv/hello2, executa apenas a lógica de negócio da aplicação,
que compreende montar uma saudação baseada na hora do dia e na presença de um
parâmetro com o nome do usuário na requisição. Note que, no método doGet(), após montar
o texto da saudação, criamos um atributo na requisição através do método
request.setAttribute(). Dessa forma é possível inserir informações na requisição que podem
ser utilizadas pelos componentes que vierem a tratá-la na sequência. No nosso caso,
montamos o texto e encaminhamos a requisição para ser tratada por outro servlet, associado
à URL serv/msg, que será responsável por construir o layout da página de resposta. Para
encaminharmos uma requisição utilizamos o método forward() da interface
RequestDispatcher, cuja instância é obtida através do método request.getRequestDispatcher(),
como podemos observar no exemplo.
Na Listagem 3 é mostrado o código da classe MessageServlet, no qual simplesmente
montamos o HTML da resposta que será exibida ao usuário.
Observe que utilizamos o método request.getAttribute() para recuperar a mensagem de
saudação, criada no servlet anterior.
Listagem 2. Código da classe HelloBusinessServlet - responsável por executar as
regras de negócio da aplicação.
package br.com.exemplo.servlets;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/serv/hello2")
public class HelloBusinessServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String userName = request.getParameter("name");
StringBuilder greeting = new StringBuilder("Hello, ");
greeting.append((userName != null) ? UserName : "Guest");
greeting.append("!");
Calendar cal = Calendar.getInstance();
int hour = cal.get(Calendar.HOUR_OF_DAY);
greeting.append(" Good ");
if (hour > 4 && hour < 12) {
greeting.append("Morning");
} else if (hour < 18) {
greeting.append("Afternoon");
} else {
greeting.append("Evening");
}
greeting.append("!");
request.setAttribute("text", greeting.toString());
request.getRequestDispatcher("/serv/msg").forward(request, response);
}
}
Listagem 3. Código da classe MessageServlet - responsável por exibir uma
mensagem na tela.
package br.com.exemplo.servlets;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/serv/msg")
public class MessageServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String messageText = (String) request.getAttribute("text");
StringBuilder html = new StringBuilder();
html.append("<html>");
html.append(" <head>");
html.append(" <title>Hello Servlets - Message</title>");
html.append(" </head>");
html.append(" <body>");
html.append(" <h1>" + messageText + "</h1>");
html.append(" </body>");
html.append("</html>");
response.getWriter().print(html.toString());
}
Com essa pequena alteração, vimos que é possível criar aplicações modularizadas utilizando
servlets. Numa aplicação real, geralmente iremos encontrar servlets sendo utilizados em
conjunto com outras tecnologias, como HTML, JSP, JSF, AJAX, etc. Dessa forma as
responsabilidades podem ser mais bem distribuídas, possibilitando a criação de aplicações
poderosas, com todos os recursos do Java, e bem estruturadas, sem misturar regras de
negócio com a lógica de apresentação.
Compartilhando dados além das requisições
No nosso último exemplo, vimos como compartilhar dados entre diferentes servlets, tratando
uma mesma requisição através dos métodos getAttribute() e setAttribute() da classe
HttpRequest. Com o auxílio destes métodos foi possível ler e escrever atributos no escopo da
requisição. Entretanto, do ponto de vista do servidor, cada requisição é única e não tem
nenhum tipo de ligação com as demais, uma vez que este atende a requisições de diferentes
clientes vindas de diferentes locais em diferentes momentos. Dessa forma não é possível a
troca de dados entre requisições distintas utilizando esses métodos.
Para viabilizar tal nível de compartilhamento, são oferecidos dois outros escopos, que vão
além da requisição: os escopos de sessão e de aplicação; ambos acessíveis através de classes
da Servlet API.
Escopo de Sessão
Para melhor entendermos o conceito de “escopo de sessão” ou “sessão do usuário”, vamos
imaginar um exemplo bastante comum do nosso dia a dia: um carrinho de compras. Neste
caso, o servidor tem que manter o estado dos dados enviados em cada requisição que adiciona
um item ao carrinho. Além disso, é preciso manter várias instâncias do carrinho, pois vários
usuários podem estar comprando ao mesmo tempo, fazendo com que o servidor tenha que
saber qual carrinho é de qual usuário.
Quando falamos em sessão do usuário é justamente desse tipo de situação que estamos nos
referindo. A sessão nada mais é do que um conjunto de informações de um determinado
usuário que fica armazenado no servidor. Essas informações continuam acessíveis, por um
determinado período, aos servlets entre as diversas requisições subsequentes que o usuário
venha a fazer.
A sessão possui um tempo máximo de inatividade, ou seja, quando o servidor percebe que não
está mais recebendo requisições de um determinado usuário por um tempo x, configurado no
servidor ou aplicação, esse elimina os dados daquela sessão.
Para manipular os dados da sessão de um usuário, a Servlet API fornece a interface
HttpSession, a qual também possui os métodos getAttribute() e setAttribute().
Um uso bastante comum do escopo de sessão é para o controle de acesso.
Neste caso, geralmente é criado um atributo com os dados do usuário autenticado na sessão,
e a cada requisição é possível verificar se este atributo está presente, ou seja, se existe um
usuário autenticado e se o mesmo tem permissão para executar a ação solicitada.
A sessão pode ser eliminada através do método HttpSession.invalidate(), ou quando o tempo
máximo de inatividade é atingido.
Na realidade, a sessão é associada ao browser que está efetuando as requisições, e não ao
usuário em si, IP ou coisa parecida. Isto pode ser verificado facilmente acessando um site de
compras qualquer, colocando alguns itens no carrinho e em seguida acessando o mesmo site a
partir de um browser diferente. O carrinho no segundo browser provavelmente estará vazio.
O que acontece é que o servidor gera uma chave de sessão e a envia ao cliente no cabeçalho
da resposta de uma determinada requisição, geralmente após a aplicação solicitar que seja
criada a sessão para o usuário. O cliente (browser), por sua vez, passa a enviar essa chave nas
requisições subsequentes e dessa forma o servidor consegue saber quais novas requisições
partiram desse mesmo cliente. Tudo isso é feito de forma transparente ao usuário.
Escopo de aplicação
Há, ainda, algumas situações onde a aplicação tem a necessidade de compartilhar dados num
escopo mais abrangente, de forma global, para que estes possam ser manipulados por
qualquer servlet presente na mesma.
Nestes casos podemos utilizar a interface ServletContext, a qual também possui os métodos
getAttribute() e setAttribute(). É possível obter a instância de ServletContext através do
método request.getServletContext().
Projeto Exemplo
Para que possamos ver na prática como tudo isso funciona, vamos aplicar os conceitos em
um pequeno projeto. A aplicação que desenvolveremos será um livro de visitas bastante
simples. Nela é exibido um mural de mensagens e é dada ao usuário a opção de escrever novas
mensagens. A Figura 4 mostra a tela principal da aplicação em funcionamento.
Para começar, crie um novo projeto web no Eclipse e dê o nome de “Guestbook”. Assim
como no projeto anterior, escolha o servidor Tomcat 7 que configuramos e a versão 3.0 da
Servlet API.
As Listagens 4 e 5 mostram o código das classes Message e MessagesManager, que são as
classes de negócio da nossa aplicação. A classe Message é um POJO simples que representa
uma mensagem no nosso livro de visitas. A mensagem é composta pelo nome e e-mail do
autor, conteúdo e data. A classe MessagesManager possui apenas dois métodos de negócio:
add(), que adiciona uma nova mensagem na lista de mensagens; e getMessages(), que
simplesmente retorna a lista com as mensagens.
Note que nossa aplicação, por se tratar de um exemplo didático, mantém a lista de mensagens
em memória. Em um sistema real as mensagens provavelmente seriam armazenadas e
recuperadas de uma base de dados.
Listagem 4. Código da classe Message - representa uma mensagem do usuário.
package br.com.exemplo.guestbook.bean;
import java.util.Date;
public class Message {
private String authorName;
private String authorEmail;
private String text;
private Date date;
public Message(String authorName, String authorEmail, String text, Date date) {
this.authorName = authorName;
this.authorEmail = authorEmail;
this.text = text;
this.date = date;
}
// gets e sets omitidos.
}
Listagem 5. Código da classe MessagesManager - cuida das regras de negócio
relacionadas às mensagens do usuário.
package br.com.exemplo.guestbook;
import java.util.ArrayList;
import java.util.List;
import br.com.exemplo.guestbook.bean.Message;
public class MessagesManager {
private List<Message> messages = new ArrayList<Message>();
public void add(Message message) {
messages.add(message);
}
public List<Message> getMessages() {
return messages;
}
}
Com as classes de negócio prontas, vamos criar o servlet responsável por gerar o código HTML
da página inicial da nossa aplicação (ver Figura 4), a qual irá exibir a lista das mensagens
postadas pelos usuários, além de algumas informações estatísticas da aplicação.
A Listagem 6 mostra a classe ListMessagesServlet. Primeiro marcamos a classe como um
servlet, através da anotação @WebServlet, na qual informamos também a URL que será
tratada por esse servlet. No método doGet(), primeiramente recuperamos a sessão do usuário
através do método request.getSession(), e desta recuperamos o atributo messageCount, que
contém o número de mensagens postadas pelo usuário. Caso o valor do atributo seja nulo,
assumimos o valor 0. Da mesma forma, recuperamos o ServletContext, a partir do qual
recuperamos o atributo manager, que é uma instância da classe MessagesManager, vista
anteriormente. Caso este não esteja com valor nulo, recuperamos do mesmo a lista de
mensagens e a atribuímos à variável messages. Na variável totalMessageCount armazenamos
o tamanho da lista, que corresponde ao número total de mensagens da aplicação.
Criamos então a URL de acesso ao formulário de inclusão de novas mensagens para que
possamos montar o hiperlink para esse formulário corretamente no código HTML.
Para isso, invocamos o método request.getContextPath() para recuperar o path da aplicação
e o concatenarmos com o caminho do arquivo form.html.
O restante do código simplesmente monta o código HTML que será exibido como resposta ao
cliente e em seguida o escreve na saída (no objeto response). Note que temos um loop para
iterar sobre as mensagens e para cada uma é chamado o método formatMessage(). É neste
método que montamos um texto HTML formatado contendo as informações da mensagem
recebida por parâmetro.
Listagem 6. Código da classe ListMessagesServlet - Monta a página inicial da aplicação, com a
lista de mensagens.
Nosso próximo passo será criar o formulário para que o usuário possa cadastrar uma nova
mensagem (ver Figura 5). A Listagem 7 mostra o código do arquivo form.html. Este arquivo
deve ser criado na pasta WebContent do projeto. Nele, criamos um formulário HTML com os
campos correspondentes às propriedades da mensagem que desejamos cadastrar, dos quais
extrairemos os valores dessas propriedades. Atente para os valores dos atributos name dos
elementos input e textarea, pois serão utilizados no servlet que efetuará o cadastro da
mensagem.
No elemento <form>, o atributo action informa a URL para a qual os dados do formulário
devem ser submetidos, e o atributo method indica o método HTTP a ser utilizado. No nosso
caso, será utilizado o método POST.
Listagem 7. Código da página form.html - formulário para inclusão de nova
mensagem.
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Livro de visitas - Nova Mensagem</title>
</head>
<body>
<h1>Deixe sua mensagem</h1>
<form action="serv/messages/new" method="post">
<fieldset>
<legend>Nova mensagem:</legend>
<label for="txtName">Nome:</label>
<input name="txtName" />
<br />
<label for="txtEmail">E-mail:</label>
<input name="txtEmail" />
<br />
<label for="txaMessage">Mensagem:</label>
<textarea rows="5" cols="50" name="txaMessage"></textarea>
<br />
</fieldset>
<input type="submit" value="Enviar" />
</form>
</body>
</html>
A classe NewMessageServlet, mostrada na Listagem 8, será responsável por receber os dados
do formulário HTML, criar um objeto Message utilizando esses dados e salvar esta nova
mensagem na “base de dados”. Dessa vez implementamos o método doPost(), pois já que
utilizamos o valor post no atributo method do formulário HTML, o browser irá enviar uma
requisição do tipo HTTP POST.
No método doPost(), inicialmente recuperamos os valores dos parâmetros da requisição
através do método request.getParameter(). Perceba que os nomes dos parâmetros são os
mesmos nomes dos campos no formulário HTML. Em seguida, criamos uma nova mensagem
com os valores recuperados acrescidos da data corrente.
Na sequência, recuperamos, assim como na classe ListMessagesServlet, o contexto da
aplicação e, a partir deste, a instância da classe MessagesManager. Caso a instância ainda não
tenha sido criada, nós a criamos e a salvamos no contexto recuperado. Repare que utilizamos
um bloco sincronizado (synchronized) para garantir que essa sequência de operações sempre
aconteça de forma atômica, evitando assim problemas relacionados à concorrência.
Por padrão, sempre que recebe uma requisição, o servidor cria uma thread para tratá-la.
No entanto, este só cria uma instância do servlet caso ainda não exista uma, fazendo assim
com que a mesma instância trate várias requisições. Dessa forma, o desenvolvedor deve ter
cautela ao manipular, em seu servlet, dados compartilhados.
Na sequência, adicionamos a nova mensagem à nossa “base de dados”, utilizando o método
add() do objeto MessagesManager que acabamos de recuperar.
Após isso, recuperamos a sessão HTTP e o atributo messageCount contendo a quantidade de
mensagens criadas pelo usuário. Entretanto, dessa vez utilizamos o método setAttribute() para
configurar um novo valor, incrementando o valor anterior.
Finalmente invocamos o método layout(), que será responsável por montar o HTML de
resposta a ser exibido ao usuário. Nesse método, primeiro montamos a URL para voltar à
página de listagem, utilizando o path da aplicação, recuperado pelo método
request.getContextPath(), concatenado ao path mapeado pelo servlet ListMessagesServlet. Em
seguida montamos o código HTML e invocamos o método response.getWriter().print() para
escrevê-lo na resposta que será enviada ao usuário.
Listagem 8. Código da classe NewMessageServlet - cuida das regras de negócio relacionadas às
mensagens do usuário.
package br.com.exemplo.guestbook.servlet;
import java.io.IOException;
import java.util.Calendar;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import br.com.exemplo.guestbook.MessagesManager;
import br.com.exemplo.guestbook.bean.Message;
@WebServlet("/serv/messages/new")
public class NewMessageServlet extends HttpServlet {
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
String authorName = request.getParameter("txtName");
String authorEmail = request.getParameter("txtEmail");
String text = request.getParameter("txaMessage");
Message message = new Message(authorName, authorEmail, text, Calendar.getInstance().getTime());
ServletContext context = request.getServletContext();
MessagesManager manager = null;
synchronized (this) {
manager = (MessagesManager) context.getAttribute("manager");
if (manager == null) {
manager = new MessagesManager();
context.setAttribute("manager", manager);
}
}
manager.add(message);
HttpSession session = request.getSession();
Object value = session.getAttribute("messageCount");
int userMessageCount = value != null ? (Integer) value : 0;
session.setAttribute("messageCount", ++userMessageCount);
layout(request, response);
}
private void layout(HttpServletRequest request, HttpServletResponse response)
throws IOException {
String destination = request.getContextPath() + "/serv/messages/list";
StringBuilder html = new StringBuilder();
html.append("<html>");
html.append(" <head>");
html.append(" <title>Guestbook</title>");
html.append(" </head>");
html.append(" <body>");
html.append(" <h1>Mensagem adicionada com sucesso!</h1>");
html.append(" <a href='" + destination + "'>Continuar</a>");
html.append(" </body>");
html.append("</html>");
response.getWriter().print(html.toString());
}
}
Por fim, vamos criar o arquivo index.html na pasta WebContent com o conteúdo mostrado na
Listagem 9. Aqui utilizamos a tag HTML <meta> com o atributo http-equiv=“Refresh” para
redirecionar o usuário para a página de listagem das mensagens. Por padrão, o servidor
carrega a página index.html automaticamente quando o endereço da aplicação é digitado.
Dessa forma, quando o usuário acessar o endereço http://localhost:8080/Guestbook, o
servidor carregará automaticamente a página index.html, que por sua vez irá redirecionar o
usuário para a página com a lista de mensagens, não sendo necessário a este escrever o
caminho completo da página (http://localhost:8080/Guestbook/serv/messages/list).
Com isso concluímos o desenvolvimento do nosso projeto exemplo. Para testá-lo, execute a
aplicação, crie novas mensagens e as visualize na tela de listagem. Para simular diferentes
usuários, podemos utilizar vários browsers, ou mesmo acessar a aplicação de outras máquinas,
simulando assim um ambiente mais real.
Listagem 9. Código do arquivo index.html - página inicial da aplicação.
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Guestbook</title>
<meta http-equiv="Refresh" content="0;url=serv/messages/list" />
</head>
<body></body>
</html>
Conclusões
Este artigo mostrou como criar aplicações web em Java utilizando a Servlet API, em sua
versão 3.0. Aqui o leitor aprendeu a criar servlets HTTP, tratar parâmetros da requisição,
escrever a resposta em HTML para o usuário, delegar requisições para outros servlets e
compartilhar dados entre servlets nos escopos de requisição, sessão e aplicação. Esse
conhecimento serve de base para todo o universo de desenvolvimento web em Java; desde as
páginas JSP até os diversos frameworks presentes no mercado, como Struts, Spring MVC e JSF.
Autor
Paulo César Coutinho
Analista de Sistemas no SERPRO. Tem experiência no desenvolvimento e projeto de aplicações
Java, C/C++ e Flex, PL/SQL e Oracle Forms. Possui as certificações SCJP 5 e SCWCD 1.4