cm">

Dicas na Web

Exemplos, riscos e soluções com JSP e Servlets

Conheça, através de dicas e exemplos práticos, recursos como download e upload de arquivos, JavaBeans e encadeamento de requisições HTTP

A coluna desta edição será diferente do habitual. Em vez de focar uma nova tecnologia ou API e descrevê-la em detalhes, serão apresentadas dicas úteis sobre um conjunto de tecnologias bem conhecido do leitor: JSP e servlets. Veremos ainda soluções para problemas recorrentes no desenvolvimento web, e esclarecimentos sobre alguns procedimentos comuns e recursos pouco conhecidos.

Definindo o nome do arquivo em downloads

Um uso comum para servlets (e mesmo para páginas JSP) é a geração dinâmica de arquivos, tais como planilhas ou documentos PDF. Nessas situações, ao enviar o arquivo gerado, é preciso que o servlet especifique o tipo do conteúdo através do método response.setContentType(String tipo). Esse tipo segue no cabeçalho da resposta HTTP retornada pelo servlet e, de acordo com ele, o navegador poderá decidir o que fazer com o arquivo.

Normalmente o arquivo é exibido através de um plug-in, ou é oferecida a opção de salvá-lo no disco, mas nos casos onde a intenção é que o usuário faça o download, esse procedimento traz alguns problemas:

·         Não é garantido que o usuário tenha a opção de salvar o arquivo em vez de o navegador abrir automaticamente um plug-in;

·         Mesmo que o browser ofereça a opção de salvamento, o nome sugerido será o do servlet ou da página JSP (por exemplo, “contador.jsp”), sendo que o ideal seria um nome mais amigável, ou no mínimo com a extensão correta (como "contador-10.txt").

 

Para resolver esses problemas, a solução é definir a propriedade content-disposition do cabeçalho da resposta HTTP, com o valor attachment;filename=nome.extensão, como no trecho de código a seguir:

 

response.setHeader(

   "content-disposition",

   "attachment;filename=" + nomeArquivo

);

 

A Listagem 1 inclui uma página JSP que imprime um contador, de 1 até 10 (ou até o valor definido no parâmetro limite). Note que o navegador não exibe o conteúdo da página, em vez disso abre a janela com a opção de salvar o arquivo, com nome sugerido "contador-XX.txt" (sendo XX o limite).

A propriedade content-disposition é definida no protocolo HTTP (RFC 2183[1]), e não na API de servlets/JSP. Assim o funcionamento dessa dica depende da compatibilidade do navegador com os padrões W3C e pode não funcionar em navegadores mais antigos (nesses casos, o nome sugerido continuará sendo o nome do JSP).

Outra opção é definir no descritor web.xml um mapeamento com o nome desejado (como "relatorio.pdf" ou "contador.txt") para o servlet ou página JSP responsável pela geração do arquivo, o que funcionará independente do navegador utilizado. No entanto essa solução não tem a flexibilidade de permitir a definição do nome no momento de execução do servlet/JSP. Para um exemplo de como definir mapeamentos, veja a dica “O invoker do Tomcat”.

Pré-compilação de páginas JSP

Sabemos que, quando uma página JSP é acessada pela primeira vez, o container web compila a página gerando (e compilado) um servlet com o código Java correspondente – mais precisamente, é gerado um objeto que implementa a interface javax.servlet.jsp.JspPage, que estende a interface javax.servlet.Servlet. Após a compilação, o container executa o servlet gerado, chamando o método _jspService(); esse servlet é chamado nas próximas invocações à página JSP.

Em algumas situações, entretanto, é interessante compilar o servlet gerado previamente, de modo que o usuário não perceba a “demora” no primeiro acesso. Esse processo é chamado de pré-compilação. Os containers web com suporte a JSP 1.2 ou 2.0 suportam a pré-compilação de páginas JSP através do parâmetro HTTP jsp_precompile. Sempre que uma página for requisitada passando-se esse parâmetro, e o valor do parâmetro for true, false ou vazio, a página JSP será convertida para um servlet e compilada, mas o servlet não será executado. Veja alguns exemplos:

 

http://localhost:8080/jm12/contador.jsp?jsp_precompile                  

http://localhost:8080/jm12/contador.jsp?jsp_precompile=true          

http://localhost:8080/jm12/contador.jsp?jsp_precompile=false

http://localhost:8080/jm12/contador.jsp?limite=10&jsp_precompile=false

 

Se o parâmetro for passado com um valor inválido, a página não compilará e o container lançará uma exceção. As seguintes chamadas, gerarão um erro:

 

http://localhost:8080/jm12/contador.jsp?jsp_precompile=sim

http://localhost:8080/jm12/contador.jsp?jsp_precompile=no

 

Há outras formas, dependentes do container, de pré-compilar páginas JSP, que permitem até que o código fonte do JSP não esteja presente no servidor de produção. Consulte a documentação do container para mais detalhes.

Obtendo as versões de JSP e Servlets

As tecnologias JSP e servlets evoluíram muito desde suas primeiras versões. Por exemplo, a API de servlets 2.3 introduziu o conceito de filtros (interface javax.sevlet.Filter); com o JSP 1.2, vieram as tags de iteração (interface javax.servlet.jsp.tagext.IteratorTag).

Para garantir a compatibilidade de suas aplicações web, portanto, é sempre importante saber as versões das APIs suportadas nos containers web utilizados. Os métodos getMajorVersion() e getMinorVersion() da classe javax.servlet.ServletContext permitem obter a versão da API de servlets, e a classe ServletContext fornece ainda um método para descrever o nome do servidor e sua versão: getServerInfo().

Já para a versão da especificação JSP, é preciso obter um objeto javax.servlet.jsp.JspEngine e chamar seu método getSpecificationVersion(). A Listagem 2 contém uma página JSP que pode ser usada para obter essas informações; veja o resultado dessa página executada em dois diferentes containers web:

 

Servidor: Apache Tomcat/4.1.29
Servlet: 2.3
JSP: 1.2

Servidor: Apache Tomcat/5.0.16
Servlet: 2.4
JSP: 2.0

Upload de arquivos

Outra funcionalidade freqüentemente requisitada em aplicações web é o upload de arquivos via HTTP. Este recurso se tornou possível após a criação do RFC 1867 (veja links), que definiu a requisição do tipo multipartes e o novo tipo file para o tag input. Uma requisição multipartes HTTP pode conter, além dos dados do formulário, o conteúdo de um ou mais arquivos.

Embora o upload de arquivos seja uma tarefa corriqueira, a API de servlets/JSP não fornece os mecanismos necessários para o tratamento de requisições multipartes. Essa funcionalidade deve ser implementada pelo desenvolvedor, ou através de bibliotecas de terceiros. Até pouco tempo atrás era difícil escolher uma biblioteca livre ou mesmo freeware para o tratamento de uploads; cada uma tinha algum tipo de restrição de licença (como uso apenas não-comercial, por exemplo). Felizmente essa situação mudou com o componente FileUpload do projeto Jakarta Commons, que, além de ser software livre, já está se tornando um padrão para upload de arquivos em aplicações Java para web.

A Listagem 3 mostra um servlet que faz o upload de um arquivo localizado no diretório WEB-INF/upload da aplicação web e exibe uma mensagem confirmando a operação. O servlet de exemplo usa o seguinte formulário HTML multipartes:   

   

<form action="/jm12/servlet/jm12.dicas.UploadArquivoServlet"
      method="post" enctype="multipart/form-data">

  Seu nome: <input type="text" name="nome"><br>

  Localização do arquivo:

  <input type="file" name="arquivo"><br><br>

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

</form>

 

Podemos quebrar o procedimento nos seguintes passos:

 

1.Verificar se a requisição é multipartes:

 

if (FileUpload.isMultipartContent(request)) {

 

2.Criar uma instância de org.apache.commons.fileupload.FileUpload ou de uma de suas subclasses:

 

DiskFileUpload upload = new DiskFileUpload();

 

3.Obter uma lista dos dados do formulário, ou o conteúdo de arquivos da requisição, através do método upload.parseRequest(HttpRequest request), que retorna uma lista de objetos do tipo org.apache.commons.fileupload.FileItem:

 

List itens = upload.parseRequest(request);

 

4. Percorrer a lista de itens retornada, checando para cada item se ele é um campo do formulário ou o conteúdo de um arquivo que foi baixado (feito upload): ...

Quer ler esse conteúdo completo? Tenha acesso completo