Início > Programação > Criando um Interceptor do EJB 3 para Gravar a Trilha de Auditoria

Criando um Interceptor do EJB 3 para Gravar a Trilha de Auditoria

A programação orientada a aspectos é uma daquelas técnicas que todo o desenvolvedor de software deveria conhecer. Olhar uma arquitetura na ortogonal pode se mostrar muito útil para diminuir a quantidade de código e separar melhor os conceitos. Trabalhei pouco com AspectJ, mas tive mais contato com Spring AOP. Foi com o Spring AOP que entendi os conceitos de Advice, Join Point e Pointcut. Lendo mais sobre o tema, descobri as formas que um Advice assume em relação a execução de um método de negócio: Before, After e Around. Se você tem interesse em se aprofundar mais nesse tema, leia o livro Aspect-Oriented Analysis and Design: The Theme Approach.

No artigo Criando um Interceptor do Hibernate Com o Spring para Gravar a Trilha de Auditoria, mostrei como gravar uma trilha de auditoria com aqueles frameworks. Nesse artigo, mostrarei como obter o mesmo resultado utilizando EJB 3, mas com menos esforço. Para esse exemplo, descartarei nossa velha implementação do EmptyInterceptor do Hibernate e tudo que diga respeito ao Spring, mas utilizarei as mesmas estruturas básicas da versão com Spring e Hibernate:

  • Operacao: enum que indica o tipo de operação que será realizada (INSERT, UPDATE e DELETE);
  • Auditavel: interface que marca as entidades de onde o interceptador coletará informações para gerar a trilha;
  • TrilhaAuditoria: a entidade persistente que é a representação da trilha propriamente dita.

A anotação @Interceptors define quais interceptadores serão executados para todos os métodos individuais de uma classe. Ele também pode ser utilizado diretamente nos métodos que serão interceptados. Nas classes interceptadoras é possível fazer injeção de dependência, mais de um interceptador pode ser utilizado por vez e a sequência de execução dos interceptados é definida por uma cadeia de interceptadores. Por exemplo, uma aplicação pode necessitar autorizar um usuário e em seguida logar a autenticação. Pode-se definir um interceptador para autorização do usuário e outro para o log. Nosso interceptador se encarregará de logar: ele irá gravar a trilha de auditoria para as operações de Insert, Update e Delete realizadas no banco de dados através do DAO:

@Interceptors({ TrilhaInterceptor.class })
public abstract class AbstratctDAO{
   @OperacaoAuditavel(operacao = Operacao.INSERT)
   public void criar(Modelo entidade) {
      (...)
   }

   @OperacaoAuditavel(operacao = Operacao.UPDATE)
   public void alterar(Modelo entidade) {
      (...)
   }

   @OperacaoAuditavel(operacao = Operacao.DELETE)
   public void remover(Modelo entidade) {
      (...)
   }
}

O seu DAO concreto, além de estender AbstractDAO, deve ser anotado com @Stateless ou outra anotação que indique que ele é um EJB. Como você notou, há uma anotação chamada OperacaoAuditavel nos métodos que serão interceptados e auditados. Com algumas alterações, eu poderia alcançar o mesmo resultado anotando cada método com @Interceptors, mas criei minha anotação visando simplificar a implementação do interceptor e tornar a arquitetura mais elegante. Essa anotação indica que Operacao está sendo feita e que será gravada na trilha:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface OperacaoAuditavel {
	Operacao operacao();
}

Agora, vamos ver como fica a implementação do nosso interceptador. A única coisa especial em uma classe de interceptação é a presença de um método anotado com @AroundInvoke que recebe uma instância de InvocationContext. Essa anotação define um método interceptador cuja implementação acessa a implementação dos métodos de negócio. Em outras palavras, é um Advice. Esse método anotado recebe como parâmetro um InvocationContext que, dentre outras coisas, permite extrair informações do método de negócio e da classe onde ele está e, via método proceed(), permite delegar a execução para o próximo interceptador da cadeia:

public class TrilhaInterceptor {

   @PersistenceContext(unitName = "persistenceUnit")
   private EntityManager entityManager;

   @AroundInvoke
   public Object auditar(InvocationContext invocationContext) throws Exception {
      OperacaoAuditavel operacaoAuditavel = invocationContext.getMethod().
         getAnnotation(OperacaoAuditavel.class);
         Object object = invocationContext.proceed();
         if (operacaoAuditavel != null) {
            Object[] params = invocationContext.getParameters();
            Long idUsuarioUltimaAlteracao = /*obter id do usuario*/;
            for (Object modelo : params) {
               if (modelo instanceof Auditavel) {
                  Auditavel auditavel = (Auditavel) modelo;
                  Table table = auditavel.getClass().getAnnotation(Table.class);
                  String tableName = table.name();
                  TrilhaAuditoria trilha = new TrilhaAuditoria(operacaoAuditavel.operacao, 
                    table.name(), auditavel.getId(), idUsuarioUltimaAlteracao);
                  entityManager.persist(trilha );
               }
            }
        }
	return object;
   }
}

Note que na linha (4) é injetada a unidade de persistência para que seja possível gravar a trilha mais à frente. Na linha (10) é feita uma chamada ao método proceed() do InvocationContext para garantir que a entidade persistente que está sendo auditada tenha identificador de banco no caso de uma operação de Insert**. Por fim, na linha (15) toma-se o cuidado de verificar se a entidade é Auditavel antes de coletar suas informações para gerar a trilha.

Referências

1. https://docs.jboss.org/ejb3/app-server/tutorial/interceptor/interceptor.html
2. https://docs.oracle.com/javaee/6/tutorial/doc/gkeci.html
3. https://www.packtpub.com/books/content/ejb-31-introduction-interceptors
4. https://puspendu.wordpress.com/2012/08/29/ejb-3-audit-invocation-interceptors/
5. http://www.java2s.com/Code/Java/EJB3/GetSetParametersInInvocationContext.htm

Notas

**Claro, isso depende da sua estratégia de persistência. Eu costumo ter uma chave de banco artificial em cada entidade forte que é incrementada por uma Sequence que é invocada pelo framework de persistência pela anotação @GeneratedValue. Cheguei a conclusão de que essa é a estratégia mais eficaz quando se tem um banco relacional como o Oracle com seus DBAs de um lado e ORM do outro lado.

Anúncios

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s

%d blogueiros gostam disto: