Aula 7: Servlets (II)
Diego Passos
Universidade Federal Fluminense
´
Ultima Aula
O que s˜ao Servlets.
Estrutura b´asica de um Servlet. Criac¸˜ao de Servlets simples. Contexto de um Servlet. Informac¸˜oes sobre requisic¸˜oes.
Nesta Aula
Respostas de requisic¸˜oes. Inicializac¸˜ao de Servlets. Filtros.
Respostas a Requisic¸˜
oes
Servlets recebem requisic¸˜oes. Clientes esperam respostas.
I Dados de uma consulta. I Confirmac¸˜ao de uma operac¸˜ao. I . . .
Para um Servlet HTTP, resposta cont´em trˆes partes:
I C´odigo de status. I Cabec¸alhos HTTP. I Corpo da resposta.
Respostas a Requisic¸˜
oes (II)
Manipulac¸˜ao da resposta ´e feita pelas interfaces ServletResponse e HttpServletResponse.
Incluem m´etodos para a criac¸˜ao e manipulac¸˜ao das trˆes partes da resposta de um Servlet. Exemplo de m´etodo de manipulac¸˜ao da resposta: setContentType().
I Recebe como parˆametro uma String especificando um MIME: e.g., “text/html”. I Configura o header Content-Type na resposta.
Respostas a Requisic¸˜
oes (III)
Interfaces ServletResponse e HttpServletResponse tamb´em s˜ao usadas para produzir stream de sa´ıda.
Dois m´etodos:
I getOutputStream(): retorna um ServletOutputStream para conte´udo bin´ario ou
texto.
I getWriter(): retorna um PrintWriter para conte´udo texto apenas.
Aten¸c˜ao ao usar o m´etodo getWriter():
I Ele inspeciona o Content-Type para determinar configurac¸˜oes de codificac¸˜ao de caracteres.
Respostas a Requisic¸˜
oes (IV)
H´a m´etodos espec´ıficos para a interface HttpServletResponse.
Estes m´etodos obviamente manipulam componentes espec´ıficos de respostas HTTP. Exemplo: manipulac¸˜ao de cabec¸alhos.
I setHeader(String,String): configura o valor de um cabec¸alho como uma string. I setIntHeader(String,int): configura o valor de um cabec¸alho como um inteiro.
I setDateHeader(String,long): configura o valor de um cabec¸alho como uma data
(milisegundos desde a Era Unix ).
Respostas a Requisic¸˜
oes (V)
´
E poss´ıvel ainda manipular o c´odigo de status da resposta. Feito atrav´es de dois m´etodos:
I setStatus(int): envia o c´odigo correspondente `a constante especificada.
I sendError(int, String): envia o c´odigo correspondente `a constante especificada e uma mensagem de erro.
F N˜ao pode ser usado se j´a houve sa´ıda enviada ao cliente.
Outro m´etodo ´util ´e o sendRedirect(String): envia um redirecionamento a outra URL. O parˆametro da func¸˜ao pode especificar:
I Uma URL absoluta.
I Um caminho relativo ao contexto do servlet (iniciado pelo caracter “/”). I Um caminho relativo `a URL atual (iniciado sem o caracter “/”).
Despacho de Requisic¸˜
oes
Muitas vezes em um Servlet e desej´avel delegar uma requisic¸˜ao a um outro servlet. Exemplos:
I Servlet principal apenas detecta qual servlet “secund´ario” deve ser chamado. I Servlet principal chama servlets secund´arios para obter “pedac¸os” da resposta.
API de Servlets permite este tipo de soluc¸˜ao atrav´es da classe RequestDispatcher. A partir de um ServletContext ´e poss´ıvel obter um RequestDispatcher:
ServletContext context = getServletContext();
RequestDispatcher d = getRequestDispatcher("/ServletSecundario");
Despacho de Requisic¸˜
oes (II)
De posse de um objeto do tipo RequestDispatcher ´e poss´ıvel fazer dois tipos de delegac¸˜ao:
I forward : a responsabilidade pela requisic¸˜ao ´e totalmente passada para o Servlet secund´ario. I include: apenas inclui a sa´ıda do Servlet secund´ario na sa´ıda do Servlet prim´ario.
A classe RequestDispatcher define dois m´etodos correspondentes: forward() e include().
Exemplo de delegac¸˜ao do tipo include: out.println("Uptime for our servers");
ServletContext context = getServletContext();
RequestDispatcher d = context.getRequestDispatcher("/servlet/ServerMonitor"); req.setAttribute("serverurl", new URL("http://www1.company.com"));
d.include(req, res);
req.setAttribute("serverurl", new URL("http://www2.company.com")); d.inc1ude(req, res);
Despacho de Requisic¸˜
oes (III)
Qual a diferenc¸a entre um despacho do tipo forward e um redirecionamento?
I Redirecionamento exige que cliente receba a resposta e fac¸a uma nova requisic¸˜ao. I No despacho, a mesma requisic¸˜ao ´e passada internamente ao servidor para o servlet em
quest˜ao. I Consequˆencias:
F Despacho tende a ser mais r´apido.
F Para o cliente, URL se mant´em como a da requisic¸˜ao original.
Note que o m´etodo getRequestDispatcher() pode ser usado para qualquer tipo de recurso dentro do contexto do Servlet.
I Arquivos HTML est´aticos, imagens, . . . I N˜ao apenas outros Servlets.
Lidando com Erros
O que fazer quando o Servlet encontra uma situac¸˜ao de erro ao processar uma requisic¸˜ao? H´a v´arias possibilidades.
A API de Servlets provˆe duas:
I Enviar uma mensagem de erro diretamente ao cliente. I Levantar uma excec¸˜ao.
Lidando com Erros: Enviando Mensagem ao Cliente
´E poss´ıvel imprimir mensagens de erro diretamente ao cliente.
I Da mesma forma que imprimimos o conte´udo de uma resposta qualquer.
No entanto, ´e mais elegante (e muitas vezes poderoso) utilizar um m´etodo espec´ıfico para isso:
I sendError(int): envia um c´odigo de erro HTTP ao cliente com uma mensagem de erro padr˜ao.
I sendError(int, String): envia um c´odigo de erro HTTP ao cliente com uma mensagem
de erro especificada.
Ambos os m´etodos podem ser acessados atrav´es da interface HttpServletResponse: response.sendError(HttpServletResponse.SC_NOT_FOUND);
response.sendError(HttpServletResponse.SC_NOT_FOUND,
"Could not find the specified file.");
Lidando com Erros: Gerando Excec¸˜
oes
Breve revis˜ao:
I Excec¸˜oes s˜ao a maneira padr˜ao de lidar com erros em Java.
I Um m´etodo pode se declarar capaz de gerar um determinado tipo de excec¸˜ao. I Neste caso, ele pode utilizar a palavra reservada throw, gerando uma nova excec¸˜ao. I M´etodo chamador recebe a excec¸˜ao e deve lidar com ela de alguma forma.
A API de Servlets provˆe dois tipos de excec¸˜ao:
I ServletException: reportar problemas gerais.
I UnavailableException: reportar que o Servlet se encontra indispon´ıvel.
Lidando com Erros: Gerando Excec¸˜
oes
O comportamento do servidor ao receber uma ServletException n˜ao ´e padronizado.
I Isto ´e, servidores diferentes podem tratar a excec¸˜ao de formas distintas.
Para a UnavailableException, no entanto, o comportamento ´e definido:
I O servidor (container) n˜ao deve repassar novas requisic¸˜oes. I Servlet est´a indispon´ıvel.
Pode-se especificar um “tempo de indisponibilidade” para a UnavailableException.
I Mas n˜ao h´a garantias de que o servidor ir´a tentar novamente ap´os este tempo.
Exemplos:
throw new UnavailableException(this, "Mensagem que especifica o problema."). throw new UnavailableException(120, this,
Inicializac¸˜
ao de Servlets
Relembrando:
I Quando um Servlet ´e iniciado pelo container, meu m´etodo init() ´e chamado. I Requisic¸˜oes s´o s˜ao passadas ao Servlet ap´os o fim do respectivo init().
Implementac¸˜ao padr˜ao do m´etodo init() n˜ao faz muito. Mas m´etodo pode ser re-implementando por cada Servlet. Permite realizar tarefas que s´o precisam ser feitas uma vez:
I Operac¸˜oes intensivas em E/S.
Inicializac¸˜
ao de Servlets (II)
H´a duas vers˜oes do m´etodo init():
I Uma sem parˆametros.
I Outra que recebe um objeto do tipo ServletConfig.
Pode-se reimplementar qualquer uma delas.
I Mas se reimplementarmos a vers˜ao com parˆametro, ´e obrigat´orio chamar a vers˜ao original.
Inicializac¸˜
ao de Servlets (IV): Parˆ
ametros de Configurac¸˜
ao
O objeto do tipo ServletConfig encapsula parˆametros de configurac¸˜ao espec´ıficos do Servlet.
Estes parˆametros podem ser acessados atrav´es de dois m´etodos:
I getInitParameter(String): retorna uma String com o valor do parˆametro especificado. I getInitParameterNames(): retorna uma enumerac¸˜ao com os nomes dos parˆametros
dispon´ıveis.
Os parˆametros (e seus valores) s˜ao configurados no descritor web.xml atrav´es da tag <init-param>.
Finalizac¸˜
ao de Servlets
Analogamente ao m´etodo init(), Servlets podem implementar um m´etodo destroy(). Este m´etodo ´e chamado sempre que o servidor quer “descarregar” um Servlet.
´
Inicializac¸˜
ao e Finalizac¸˜
ao do Contexto
A partir da vers˜ao 2.3, a API de Servlets disponibilizou a interface ServletContextListener.
Uma classe que implementa esta interface pode ser associada a um contexto. Neste caso, a classe ´e avisada sempre que o contexto ´e criado ou destru´ıdo.
I Atrav´es dos m´etodos contextInitialized e contextDestroyed.
A associac¸˜ao de uma classe a um contexto ´e feito no descritor web.xml: <web-app ...> <listener> <listener-class> MyAppServletContextListener </listener-class> </listener> </web-app>
Filtros
Filtros s˜ao objetos que podem ser colocados no caminho entre as requisic¸˜oes e Servlets (ou outros recursos em um servidor).
Filtros podem modificar a requisic¸˜ao recebida ou resposta enviada por um servlet.
I Exemplo: comprimir os dados da resposta.
Eles podem ainda realizar tarefas de monitoramento ou implementar pol´ıticas de seguranc¸a. Mais de um filtro pode ser utilizado, formando uma cadeia.
I Cada filtro executa sua ac¸˜ao e passa o controle para o pr´oximo filtro.
Cliente Container Filtro Filtro Filtro Servlet
Requisição Resposta
Filtros (II)
Filtros s˜ao implementados sobre a interface Filter. M´etodos relevantes:
I init(): inicializac¸˜ao do filtro. I destroy(): finalizac¸˜ao do filtro. I doFilter(): m´etodo principal do filtro.
No m´etodo doFilter(), filtro pode fazer qualquer processamento sobre a requisic¸˜ao. Em algum ponto, deve chamar o m´etodo doFilter() do pr´oximo filtro.
I Dispon´ıvel no objeto FilterChain, recebido como parˆametro.
Pode tamb´em interromper a cadeia e retornar um erro para o cliente. Pode realizar redirecionamentos.
Filtros: Um Exemplo M´ınimo
import javax.servlet.*;
public class GenericFilter implements javax.servlet.Filter { public FilterConfig filterConfig;
public void doFilter(final ServletRequest request, final ServletResponse response, FilterChain chain)
throws java.io.IOException, javax.servlet.ServletException { chain.doFilter(request,response);
}
public void init(final FilterConfig filterConfig) { this.filterConfig = filterConfig;
}
public void destroy() { }
Filtros: Exemplo de Codificac¸˜
ao de Caracteres
public void doFilter(ServletRequest request,
ServletResponse response, FilterChain chain) throws IOException, ServletException {
String encoding = selectEncoding(request); if (encoding != null)
request.setCharacterEncoding(encoding); chain.doFilter(request, response);
}
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
this.encoding = filterConfig.getInitParameter("encoding"); }
protected String selectEncoding(ServletRequest request) { return (this.encoding);
Filtros: Exemplo de Configurac¸˜
ao
<filter> <filter-name>Compression Filter</filter-name> <filter-class>CompressionFilter</filter-class> <init-param> <param-name>compressionThreshold</param-name> <param-value>10</param-value> </init-param> </filter>Filtros: Requisic¸˜
oes Despachadas
Originalmente, filtros s´o eram executados em requisic¸˜oes originadas em clientes.
Requisic¸˜oes despachadas atrav´es da interface getRequestDispatcher() n˜ao passavam por filtros.
Em determinados casos, este comportamento ´e desej´avel.
A partir da vers˜ao 2.4 da API de Servlets, isso passou a ser poss´ıvel.
<filter> <filter-name>Compression Filter</filter-name> <filter-class>CompressionFilter</filter-class> <init-param> <param-name>compressionThreshold</param-name> <param-value>10</param-value> </init-param> <dispatcher>FORWARD</dispatcher> <dispatcher>REQUEST</dispatcher> </filter>
Filtros: Como Alterar a Sa´ıda de Um Servlet
´
E f´acil fazer filtros adicionarem conte´udo antes ou depois da resposta de um Servlet. Mas como alterar o conte´udo que o Servlet gera?
Exemplo:
I Suponha que o Servlet chamado gera uma p´agina HTML. I Queremos adicionar alguma informac¸˜ao na p´agina.
I Mas ap´os a chamada do Servlet, este inclui um </html>.
Soluc¸˜ao: utilizac¸˜ao de wrappers.
Filtros: Como Alterar a Sa´ıda de Um Servlet (II)
Wrappers encapsulam objetos que representam respostas.
Sobreescrevem m´etodos como o getWriter() ou getOutputStream().
I Ao inv´es de enviarem o conte´udo diretamente para o cliente, pode enviar para algum buffer por exemplo.
Filtro cria o wrapper encapsulando o objeto de resposta original. Filtro chama o pr´oximo filtro (ou Servlet) passando o wrapper.
Quando o controle retorna para o filtro, ele pode inspecionar e alterar o conte´udo da resposta.
Filtros: Exemplo de Wrapper
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
PrintWriter out = response.getWriter();
CharResponseWrapper wrapper = new CharResponseWrapper( (HttpServletResponse)response);
chain.doFilter(request, wrapper);
if(wrapper.getContentType().equals("text/html")) { CharArrayWriter caw = new CharArrayWriter(); caw.write(wrapper.toString().substring(0,
wrapper.toString().indexOf("</body>")-1)); caw.write("<p>\nYou are visitor number
<font color=’red’>" + counter.getCounter() + "</font>"); caw.write("\n</body></html>"); response.setContentLength(caw.toString().length()); out.write(caw.toString()); } else out.write(wrapper.toString()); out.close(); }
public class CharResponseWrapper extends HttpServletResponseWrapper { private CharArrayWriter output; public String toString() {
return output.toString(); }
public CharResponseWrapper(
HttpServletResponse resp){ super(resp);
output = new CharArrayWriter(); }
public PrintWriter getWriter(){ return new PrintWriter(output); }