| Patterns Java
Autor: Pedro Rodolfo Kalva
Um exemplo prático de como desenvolver um aplicativo
com o uso de padrões
Muito se fala sobre padrões java, os famosos “patterns
java”, mas pouco se utiliza na prática, e isto pode ser ocasionado
por mitos que levam a crer que a aplicação dos padrões
em um sistema computacional seja muito difícil. A dificuldade que
muitos arquitetos de software e desenvolvedores encontram pode estar na
falta de informação. Muitos somente ouviram falar ou leram
algo a respeito sem entrar em detalhes, e acabaram com más impressões
a respeito dos padrões.
É fácil entender os padrões
O objetivo deste trabalho é mostrar como uma aplicação
de padrão pode ser feita de maneira muito simples e como ela vai
facilitar nossa vida. O padrão escolhido para ser apresentado é
o Intercepting Filter, um dos padrões apresentados no
livro Core J2EE Patterns. Este padrão é utilizado
para facilitar a posterior manutenção e ajudar um outro
desenvolvedor a “entender” o código sem ter participado
do projeto do sistema, mas logicamente é necessário que
o desenvolvedor conheça os padrões.
O detalhamento a seguir supõe que o leitor conheça um
pouco da linguagem Java e um pouco da tecnologia Servlets.
Segurança para as páginas da web
O intercepting filter é um padrão utilizado para
verificar a validade da solicitação que está sendo
feita para uma requisição de página na web. Toda
vez que um usuário abre um navegador Internet (browser)
e digita um endereço, uma solicitação é gerada
e entregue a um servidor na web, que por sua vez interpreta a solicitação
e devolve uma resposta (página web).
Imagine um site que contém uma área restrita,
onde o internauta deve fazer um cadastro e se identificar toda vez que
quiser acessar aquela parte do site. Tem-se que criar uma proteção
para que os não-identificados não obtenham sucesso
em uma tentativa mal-intencionada.
Isto pode ser feito com uma simples validação na sessão
do usuário. Sessão são informações
de um usuário que está navegando pelo site, elas ficam armazenadas
no servidor web agregadas a um identificador, que é enviado para
o navegador do usuário no momento do login e toda vez
que este usuário realizar uma nova solicitação, esta
identificação é enviada junto, assim o servidor web
pode identificar o usuário de uma forma única.
Muitas vezes são necessárias mais que uma simples validação
na sessão para ter uma autenticação válida,
pode ser que seja necessário saber se o usuário está
em dia com seu pagamento correspondente à assinatura no site ou
se o usuário tem um nível de acesso que permite o sucesso
na obtenção da página solicitada.
Solução, ou não, para o problema
É muito comum, ao analisar um código-fonte de um sistema,
encontrar vários “if’s” fazendo as validações
necessárias para autenticar o usuário, e pior ainda é
ver este treco de código repetido em todos os arquivos que necessitem
deste recurso. Quando precisar incluir uma nova verificação,
o desenvolvedor vai copiando em todos os locais que necessita da autenticação,
podendo ser penalizado pelo “esquecimento” em algum dos arquivos
que tinha necessidade da proteção.
Aplicando o padrão
A solução proposta utiliza o modelo mais simples deste
padrão (Intercepting Filter), seguindo o objetivo de que
o leitor possa entender como o padrão foi aplicado e, conseqüentemente,
para maiores detalhes o livro deve ser consultado. Uma representação
gráfica desta aplicação pode ser vista na figura
1.

Figura 1 - Diagrama do padrão Intercepting Filter
A solicitação chega até o servidor, que por sua
vez invoca o Servlet FornecePagina, que verifica qual é
a página que o usuário solicitou por meio de um parâmetro
chamado de “pagina”. Instancia a classe correspondente e antes
de efetuar seu processamento chama a classe FilterMananger, responsável
por gerenciar os filtros. Nela é instanciada a classe FilterChain
que funciona como um ordenador seqüencial de funcionalidades de verificação.
Estes verificadores são as classes Filter (DebugFilter,
LoginFilter e AuditFilter no diagrama). Se nenhuma exceção
ocorrer, então é chamado o método execute
da classe alvo, solicitada pelo usuário.
Interpretando o código-fonte
Pareceu difícil? Uma análise no código-fonte dos
exemplos deve clarear as idéias. Note que independentemente de
aplicar o padrão, a estrutura adotada consiste em fazer solicitações
sempre para o Servlet FornecePagina passando um parâmetro
denominado “pagina”, e o valor deste parâmetro é
o nome da classe que pretendemos instanciar e executar. A classe alvo
deve implementar a interface Filter, descrito na listagem 4.
Esta interface apenas implementa o método “execute”.
Um exemplo de solicitação poderia ser:

Se esta solicitação for feita por um navegador web, o
método doGet do Servlet FornecePagina será
invocado, chamando em seguida o método doPost que processará
a solicitação. Os métodos doGet e doPost
são chamados pelo navegador dependendo de como a solicitação
é feita, no nosso exemplo o tratamento é o mesmo para qualquer
um deles, portanto apontamos o método doGet para chamar
o método doPost.
Na seqüência é coletado o parâmetro “pagina”
que se não for enviada será gerada uma exceção
com a mensagem “não foi solicitado uma página”.
O valor atribuído a este atributo deve ser o nome de uma classe,
que será instanciada e sua referência será atribuída
à variável “target”. Se não fosse utilizar
o padrão para verificar se a solicitação pode invocar
a classe, os dois últimos comandos da listagem um poderia ser substituído
pelo comando:

Seguindo o objetivo de utilizar os padrões, a classe FilterMananger
(listagem 2) é instanciada e invocado o seu método processFilter,
passando como parâmetro a classe a ser executada, além das
referências request e response.
Seguindo em frente e analisando o código da classe FilterMananger,
ela instancia a classe FilterChain e invoca o método processFilter
que, não ocorrendo problemas, irá invocar a classe alvo,
requisitada pelo usuário.
A classe FilterChain (listagem 3) tem a responsabilidade de
fazer verificações seqüenciais de filtros. É
nesta classe que devem ser incluídas as novas classes verificadoras.
Basta criar uma classe que implementa a interface Filter e incluir
através do método addFilter desta classe. No método
processFilter cada um dos filtros cadastrados será chamado
seqüencialmente. Note que os filtros podem ser cadastrados também
na classe FilterManager.
As outras classes DebugFilter, LoginFilter e AuditFilter
(listagens 5, 6 e 7 respectivamente) são, neste exemplo, bastante
parecidas e a única coisa que fazem é imprimir na janela
do console uma mensagem para provar que foi executada, mas numa aplicação
real cada um destes filtros irá executar uma operação
específica para autenticar a solicitação.
Conclusões
Com a utilização do padrão, todas as solicitações
passam pelo processo de verificação que tem os filtros acopláveis,
se houver mudança no processo de autenticação, basta
criar uma nova classe e acoplar ao FilterChain. Se houver muitos
filtros e estes forem independentes uns dos outros, então pode
ser interessante abrir threads para facilitar o trabalho.
Caso queira utilizar este recurso em páginas JSP, uma boa idéia
seria utilizar TAGLIB, criando-se uma tag específica de autenticação
e colocando-a em todas as páginas que necessitam de autenticação.
Pode dar um pouco mais de trabalho para criar o processo de autenticação
padronizado, mas é muito mais fácil de um outro desenvolvedor
entender o que ocorre, auxiliado da documentação do sistema
(projeto). Assim, a manutenção é reduzida com probabilidade
quase nula do desenvolvedor “esquecer” de atualizar em alguma
página ou arquivo. Roger S. Presmmam cita em seu livro Engenharia
de Software: “a manutenção é responsável
por cerca de 80% do custo total do software, durante todo seu ciclo de
vida”.
1. FornecePagina.java
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import java.io.IOException;
public class FornecePagina extends
HttpServlet{
public void init(ServletConfig
conf) throws ServletException {
super.init(conf);
}
public void doGet(HttpServletRequest
request,HttpServletResponse response)
throws ServletException,IOException {
doPost(request,response);
}
public void doPost(HttpServletRequest request,HttpServletResponse response)
throws ServletException,IOException {
// pega a página destino
Filter target;
String sPagina = request.getParameter(“pagina”);
if(sPagina==null)
throw new ServletException(“Não foi solicitado uma página!”);
try {
Class cls=Class.forName(sPagina);
target =(Filter)cls.newInstance();
}
catch(Exception _e) {
throw new ServletException(“Não existe a página solicitada!”);
}
// envia para a classe FilterManager
FilterManager filterManager=new FilterManager();
filterManager.processFilter(target,request,response);
}
}
2. FilterManager.java
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletException;
import java.io.IOException;
public class FilterManager{
public void processFilter(Filter target,
HttpServletRequest request,HttpServletResponse response)
throws ServletException, IOException {
// chama a FilterChain
FilterChain filterChain = new FilterChain();
filterChain.processFilter(request,response);
// Se não ocorreu nenhuma
exceção, a página pode ser liberada
target.execute(request,response);
}
}
3. FilterChain.java
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletException;
import java.io.IOException;
import java.util.Vector;
import java.util.Iterator;
public class FilterChain{
private Vector myFilters = new
Vector();
public FilterChain() {
// neste exemplo mais simples os filtros são incluidos aqui
addFilter(new DebugFilter());
addFilter(new LoginFilter());
addFilter(new AuditFilter());
}
public void processFilter(HttpServletRequest
request,
HttpServletResponse response)
throws ServletException, IOException {
Filter filter;
// chama sequenciamente cada
um dos filtros
Iterator filters = myFilters.iterator();
while(filters.hasNext())
{
filter=(Filter)filters.next();
filter.execute(request,response);
}
}
public void addFilter(Filter
filter){
myFilters.add(filter);
}
}
4. Filter.java
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletException;
import java.io.IOException;
public interface Filter
{
public void execute(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException;
}
5. DebugFilter.java
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletException;
import java.io.IOException;
public class DebugFilter implements
Filter{
public void execute(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
// implementação
do filtro
System.out.println(“DebugFilter”);
}
}
6. LoginFilter.java
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletException;
import java.io.IOException;
public class LoginFilter implements
Filter{
public void execute(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
// implementação do filtro
System.out.println(“LoginFilter”);
//throw new ServletException(“Login inválido !”);
}
}
7. AuditFilter.java
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletException;
import java.io.IOException;
public class AuditFilter implements
Filter{
public void execute(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
// implementação
do filtro
System.out.println(“AuditFilter”);
}
}
8. PaginaExemplo.java
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletException;
import java.io.PrintWriter;
import java.io.IOException;
public class PaginaExemplo implements
Filter{
public void execute(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
PrintWriter out = response.getWriter();
// Implementação da página
out.println(“<html><body>Página solicitada”);
out.println(“</body></html>”);
}
}
Referências
1. ALUR, D. et al. Core J2EE Patterns. Rio de Janeiro: Campus, 2002.
2. BODOFF, S.;GREEN, D. Tutorial do J2EE. Rio de Janeiro: Campus, 2002.
3. GAMMA, E. et al. Padrões de projeto: soluções
reutilizáveis de software orientado a objetos. Porto Alegre: Bookman,
2000.
4. PRESSMAN, R. S. Engenharia de software.
Rio de Janeiro: Mc Graw Hill, 2002.
kalva@pr.gov.br
prkalva@ppgia.pucpr.br

|