DFimagem_pdf.jpg

Programando Servlets

Parte 1: Fundamentos e Técnicas Básicas

No primeiro artigo da série, apresentaremos os conceitos básicos como protocolo HTTP, API, ciclo de vida, cookies e sessões

A API de Servlets, criada em 1996 com o intuito de gerar dinamicamente código HTML, é um componente fundamental da plataforma J2EE. Com o passar do tempo, porém, outras tecnologias, como JSP e linguagens de template, mostraram-se mais apropriadas para a geração de HTML, e os servlets passaram a ser usados para tarefas de processamento de dados, como download e upload de arquivos e filtragem de requisições. Mas isso não significa que os servlets perderam importância. Muito pelo contrário. A API de Servlets é o alicerce de muitas outras tecnologias, como JSP, JSF, Portlets e Struts. A Tabela 1 lista as tecnologias J2EE relacionadas à camada web (web-tier) e suas especificações (JSRs).

Nesse artigo, vamos apresentar os conceitos básicos de servlets e uma introdução ao funcionamento de uma aplicação web. Este artigo complementa e se relaciona com a outra matéria de capa de edição, sobre JSP 2.0 e Tomcat 5, e em alguns pontos mostrará conceitos similares, mas sob a perspectiva unificadora dos servlets.

O protocolo HTTP

Antes de nos aprofundarmos na API de Servlets em si, é importante entender como funciona o protocolo HTTP, responsável pela comunicação, em nível de aplicação, entre o navegador e a aplicação web. Quando você digita uma URL no navegador, digamos, http://localhost:8080/jm18/alo?nomePessoa=Felipe, é feita uma requisição (request) ao servidor HTTP. Aqui localhost representa o seu próprio computador. O servidor então processa a requisição e envia uma resposta (response) ao navegador. A Figura 1 ilustra o processo.

Junto com a requisição, o navegador passa algumas informações para o servidor. No exemplo ilustrado, está sendo explicitamente passado um parâmetro nomePessoa, cujo valor é felipe. Implicitamente, são passadas informações sobre o navegador através de variáveis de header e, opcionalmente, cookies (veremos mais sobre estes adiante).

Como a requisição é enviada no formato texto, podemos verificar o que é enviado usando o nosso "pseudo servidor web" mostrado na Listagem 1. Acessando esse servidor, por exemplo, a partir de um navegador Mozilla rodando no Linux (e passando a URL http://localhost:8080/alo?nomePessoa=Felipe), temos:

 

> java jm18.pentefino.ServidorHTTP 8080

### Criando servidor na porta 8080

### Conexao estabelecida

### Recebendo dados:

GET /aloServlet?nomePessoa=Felipe HTTP/1.1

Host: localhost:2424

User-Agent: Mozilla/5.0

           (X11; U; Linux i686; en-US; rv:1.6) Gecko/20040116

Accept: text/xml,application/xml,application/xhtml+xml,

        text/html;q=0.9,text/plain;q=0.8,image/png,image/jpeg,

        image/gif;q=0.2,*/*;q=0.1

Accept-Language: en-us,en;q=0.5

Accept-Encoding: gzip,deflate

Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7

Keep-Alive: 300

Connection: keep-alive

 

### Dados ok

### Enviando dados:

### Dados ok

 

Nesta requisição, a primeira linha indica ao servidor o recurso que está sendo acessado (/aloServlet?nomePessoa=Felipe), além da versão do protocolo e o método usado – HTTP/1.1 e GET. As linhas seguintes são variáveis de cabeçalho (header), seguidas por uma linha em branco, que separa o cabeçalho do corpo da requisição.

 

Nota: O corpo de uma requisição HTTP só é usado em requisições do tipo POST; é no corpo que os bytes de um arquivo são transmitidos em um upload, por exemplo.

 

O servidor envia uma resposta, também em formato texto. A estrutura é similar à requisição: a primeira linha indica o código da resposta (veja a Tabela 2), sendo seguida por variáveis de cabeçalho, uma linha em branco e o corpo. O corpo contém os dados da resposta, como código HTML ou bytes de um arquivo, no caso de um download.

Usando o cliente da Listagem 2 em vez do navegador web, o comando a ser executado e a resposta para o exemplo anterior seria:

 

> java jm18.pentefino.ServidorHTTP 8080

### Criando servidor na porta 8080

### Conexao estabelecida

### Recebendo dados:

GET /aloServlet?nomePessoa=Felipe HTTP/1.1

 

### Dados ok

### Enviando dados:

### Dados ok

 

E no cliente:

 

> java jm18.pentefino.ClienteHTTP http://localhost:8080/aloServlet?nomePessoa=Felipe

### Tentando conexao com servidor localhost na porta 8080

### Conexao estabelecida. Enviando query /aloServlet?nomePessoa=Felipe

### Recebendo resposta

### Dados enviados pelo servidor:

Requisicao tratada com sucesso!

### Dados ok


Note que o cliente da Listagem 2 não envia nenhuma variável de cabeçalho. Por isso não seria um cliente válido para servidores reais, como mostra o seguinte exemplo:

 

> java jm18.pentefino.ClienteHTTP http://javamagazine.com.br:80

### Tentando conexao com servidor javamagazine.com.br na porta 80

### Conexao estabelecida. Enviando query ?null

### Recebendo resposta

### Dados enviados pelo servidor:

HTTP/1.1 400 Bad Request

Date: Tue, 07 Sep 2004 18:17:08 GMT

Server: Apache

Connection: close

Transfer-Encoding: chunked

Content-Type: text/html; charset=iso-8859-1

 

127

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">

<HTML><HEAD>

<TITLE>400 Bad Request</TITLE>

</HEAD><BODY>

<H1>Bad Request</H1>

Your browser sent a request that this server could not understand.<P>

client sent HTTP/1.1 request without hostname (see RFC2616 section 14.23): /<P>

</BODY></HTML>

 

0

 

### Dados ok

 

Para finalizar, é importante lembrar que o protocolo HTTP é sem-estado (stateless). Ou seja, não existe uma conexão permanente entre o servidor e o navegador, e portanto o servidor não sabe se uma conexão seguinte está relacionada com a anterior. Para contornar essa restrição, foram criados os cookies. Cookies são informações armazenadas no computador do usuário que são opcionalmente enviadas em cada requisição pelo navegador, processados pelo servidor e recebidos de volta na resposta (veja a Figura 2).

Servlets e aplicações web

Na Figura 1, vimos que o navegador faz uma requisição que será processada de alguma maneira pelo servidor. A forma como esse processamento é feito depende do servidor e da URL. Por exemplo, um servidor web Apache pode ser configurado para servir páginas estáticas em HTML e páginas dinâmicas em PHP, e para repassar as requisições de JSPs a um servidor Tomcat rodando na mesma máquina. Como se vê, a API de Servlets é uma dentre várias tecnologias de processamento de requisições HTTP para geração de páginas dinâmicas.

Os servlets não ficam sozinhos. Eles são gerenciados por um Container Web, responsável pela criação e destruição dos servlets e pela delegação de requisições HTTP para os servlets existentes, além da criação de um pool de threads para o processamento das requisições (cada requisição é atendida em uma thread própria). A Figura 3 ilustra o relacionamento entre o servidor HTTP, o container web e os servlets.

Outro conceito importante é o de Aplicação Web (Web Application, ou abreviadamente webapp). Todo servlet está contido numa aplicação web, que pode conter ainda outros recursos como páginas JSP e arquivos estáticos (páginas HTML, imagens JPG e GIF etc.), além de um diretório especial chamado WEB-INF (cujo conteúdo não é exposto por uma URL e só pode ser acessado pelos servlets) – veja a Figura 4. Para ser instalada, uma aplicação web é tipicamente condensada em um arquivo WAR (Web ARchive). Esse formato condensado é mais comumente usado na aplicação final em produção, e o formato “expandido”, durante o desenvolvimento.

Um container pode gerenciar várias aplicações web, cada uma em seu Contexto. Um contexto é identificado por um "diretório" na URL. Por exemplo, a URL http://localhost:8080/jm18/alo refere-se ao recurso alo da aplicação web cujo contexto mapeia para o "diretório" jm18.

Um servlet também é responsável pelo tratamento de URLs da aplicação web. No exemplo anterior, poderíamos criar um servlet chamado AloServlet e associá-lo com a URL /alo. A associação entre um servlet e uma URL é chamada de mapeamento de servlets (servlet mapping). Para entender como é definido o mapeamento entre um servlet e as URLs que ele atende, consulte o quadro “Elementos básicos do descritor web”.

API e ciclo de vida de servlets

A API de servlets é representada pelos pacotes javax.servlet e javax.servlet.http, cujas principais classes/interfaces são mostradas na Figura 5. A interface javax.servlet.Servlet é o coração da API. Possui três métodos associados com o ciclo de vida de um servlet: init(), service() e destroy().

O ciclo de vida de um servlet é simples, quando comparado com outras tecnologias Java (como EJB ou JSF):

1.    Antes da primeira requisição, o container cria uma instância do servlet e chama seu método init(). De forma geral esse método é usado para a alocação de recursos, como conexões com um banco de dados ou referências a EJBs.

2.    Quando uma requisição é recebida, o container cria novos objetos ServletRequest e ServletResponse e os passa para o método service() do servlet. Aqui vale notar que comumente múltiplas requisições são processadas de forma simultânea, em threads diferentes, mas por instâncias compartilhadas do servlet (veja mais no quadro “Servlets, threads e boas práticas”).

3.    Quando o servlet não for mais necessário (por exemplo, quando a aplicação web for removida do container), é chamado o método destroy(). Neste devem ser liberados os recursos alocados em init().

 

Observe que a interface Servlet não define comportamento especificamente associado ao protocolo HTTP. Isso possibilita que um servlet ser utilizado por qualquer protocolo. Porém, como na prática a maioria dos servlets é usada apenas por aplicações web, a API fornece a classe abstrata especializada javax.servlet.http.HttpServlet. HttpServlet estende javax.servlet.GenericServlet que implementa a maior parte dos métodos da interface Servlet, e também o método service(), que chama o método correspondente do servlet (doGet(), doPost(), doDelete() etc.) de acordo com o tipo de requisição HTTP recebida. A Figura 6 ilustra o tratamento de uma requisição GET.

Dessa forma, a maneira mais fácil de desenvolver um servlet em aplicações web, e a que você verá sendo utilizada na grande maioria das vezes, é estender HttpServlet e implementar os métodos necessários. Veja um exemplo na Listagem 3.

 

Nota: Para compilar a classe deste exemplo preciso incluir o arquivo servlet-api.jar  (disponível, por exemplo, no diretório commons/lib da instalação do Tomcat) para o classpath e o bytecode gerado deve ser copiado para o diretório WEB-INF/classes da aplicação web (ou para um JAR localizado no diretório WEB-INF/lib).

HttpServletRequest e HttpServletResponse

O servlet da Listagem 3 tem pouca utilidade, já que não recebe nada como entrada, não produz nada como saída e não interage com outros recursos. Vamos então criar um servlet um pouco mais interessante, que recebe um nome como parâmetro e imprime na tela uma saudação. Para testá-lo, acessamos a mesma URL do primeiro exemplo, a saber, http://localhost:8080/jm18/alo?nomePessoa=Felipe. Desta vez, no entanto, usaremos o servidor Tomcat.

O primeiro passo é decidir como o servlet será acessado – através de um comando GET, POST ou ambos? Para simplificar optaremos pelo GET apenas, implementando o método doGet(). Dentro deste usamos o objeto request para obter o parâmetro e o objeto response para enviar a saída de volta ao navegador.

Para obter o parâmetro usamos request.getParameter().  Para enviar a saída o processo é um pouco mais longo: 1) definir a resposta como sendo do tipo text/html, com response.setContentType(); 2) obter o writer de saída com o método response.getWriter(); 3) escrever a resposta; 4) fechar o writer.

A Listagem 4 contém o código para o servlet; a Listagem 5 inclui o descritor web usado para todos os exemplos do artigo.

 

Nota: No web.xml de exemplo, os elementos <servlet> e <servlet-mapping> estão intercalados – isso só é possível a partir da especificação de Servlets 2.4. Para versões anteriores da especificação, todos os elementos <servlet> tinham que ser definidos antes dos elementos <servlet-mapping>.

 

Note que, neste exemplo, o usuário precisa saber que o servlet espera um parâmetro chamado nomePessoa, o que é pouco intuitivo. A solução para tal problema é o uso de formulários HTML (veja um exemplo na Figura 7). Um formulário HTML deve conter ao menos os seguintes elementos:

1.    Um tag <form>, indicando para qual página os dados do formulário serão enviados através do atributo action

2.    Uma ou mais tags de entrada de dados, como <input>, <textarea> e <select>

3.    Um tag para submissão dos dados do formulário, como <input type=”submit”>

 

A Listagem 6 contém o código HTML do formulário mostrado na Figura 7.

...

Quer ler esse conteúdo completo? Tenha acesso completo