Back-End

24 out, 2013

Auditoria a Spring MVC Webapp com AspectJ – Parte 01

Publicidade

Se você é como eu, então você vai ter dias de programação em que tudo parece ir muito bem. Você escreve o código e os testes, e eles simplesmente funcionam. E então há aqueles outros dias, os realmente ruins, em que você sabe que tudo o que você escreveu está tão certo quanto poderia estar, mas o código se recusa a funcionar: alguma coisa está obviamente errada, mas você não tem ideia do quê. Eu tive um desses dias ao escrever o código para este artigo. A ideia era demonstrar como usar Spring e AspectJ para auditar as visitas de um usuário a uma tela.

Auditar visitas de um usuário a uma tela é uma daquelas poucas preocupações transversais que a Programação Orientada a Aspectos (AOP) resolve muito bem. A ideia, no caso do meu código de demonstração, é que você adiciona uma anotação para os controllers apropriados e, cada vez que um usuário visita uma página, então a visita é gravada. Usando essa técnica, você pode construir uma imagem das telas mais populares e, portanto, os pedaços mais populares de funcionalidade em seu aplicativo. Saber esses detalhes faz com que seja mais fácil decidir para onde apontar o seu esforço de desenvolvimento, uma vez que não vale a pena desenvolver os pedaços de seu aplicativo que quase ninguém usa.

Eu já falei sobre AspectJ e AOP antes nos artigos abaixo, o que é ótimo, já que eles demonstram o básico, mas eles não são um trabalho real de aplicação Spring MVC . Meus artigos anteriores, diretamente do meu site, foram:

Desta vez, eu pensei em criar uma aplicação Spring MVC em pleno funcionamento com uma preocupação transversal útil AOP.

Para o código de demonstração, eu criei um simples aplicativo Spring MVC que tem duas telas: uma home page e uma página de ajuda. Além disso, eu criei uma simples anotação: @Audit, que é usada para marcar um controller como aquele que precisa de auditoria (não todos eles, especialmente se você optar por auditar os pontos de função, em vez de telas individuais ) e para dizer ao conselho do objeto a id da tela id como demonstrado no trecho de código abaixo:

  @Audit("Home")
  @RequestMapping(value = "/", method = RequestMethod.GET)
  public String home(Locale locale, Model model) {

Isto é, até tudo começar a falhar…

O plano de ataque era escrever a minha simples anotação @Audit e manipulá-la usando uma classe simples AuditAdvice com um método anotado com a anotação @Before do AspectJ. Então eu fingiria que isso era uma classe de conselho real, o que significa delegar a auditoria real a um objeto AuditService autowired.

Comecei criando um exemplo de aplicativo Spring MVC usando o modelo de projeto Spring:

spring-1

Então eu coloquei todo o código junto e esperei que apenas funcionasse, mas isso não aconteceu: o Spring não iria, não importa o que eu tentasse, fazer autowire no AuditService na classe AuditAdvice. Isso significava que quando o meu método anotado @Before era chamado, ele lançava uma NullPointerException.

Quando você está na situação em que suspeita fortemente de que o seu código está certo e ele simplesmente não funciona, então uma das áreas de investigação é o arquivo POM e a configuração do projeto.

O negócio é que quando você usa a API de alguém, a configuração do projeto ou outra ferramenta, você tende a confiar mais nele (a) do que no seu próprio código. Eu acho que as razões para isso são que eles são normalmente escritos por uma organização altamente respeitável, o que faz você meio que achar que eles têm alguma forma mágica de escrever um código muito bom, além de que geralmente ele contém um monte de coisas que você de fato não entende.

Isso é realmente irracional, já que a API, a ferramenta, o arquivo de configuração etc. foram certamente escritos por programadores como eu e você, que provavelmente comentem tantos erros como nós.

O problema com o POM do projeto Spring MVC é que ele é um pouco obsoleto e cheio de coisas que você simplesmente não precisa em um aplicativo padrão Spring MVC Java, além de não haver links para qualquer documentação de plugin, para descobrir o que tudo isso faz e o que as diferentes configurações dizem é difícil.

Eu escrevi um artigo chamado Dissecting Spring’s MVC Project POM há algum tempo, que tenta explicar como funciona o POM da aplicação template do Spring MVC.

Estas são as mudanças que eu tive que fazer no template POM padrão do Spring MVC para fazer meu código funcionar.

1) Atualize a versão do Spring utilizado pelo projeto para a mais recente: 3.2.3 (na época da elaboração deste artigo).

2) Atualize a versão do AspectJ para 1.7.1 (na época da elaboração deste artigo).

3) Retire o número da versão do Spring Roo.

Isso irá criar as seguintes propriedades de versão:

<org.springframework-version>3.2.3.RELEASE</org.springframework-version>
<org.aspectj-version>1.7.1</org.aspectj-version>

4) Retire outras dependências do Spring Roo:

<!-- Roo dependencies -->
<dependency>
 <groupId>org.springframework.roo</groupId>
 <artifactId>org.springframework.roo.annotations</artifactId>
 <version>${org.springframework.roo-version}</version>
 <scope>provided</scope>
</dependency>

Este não é um projeto Roo, e eu odeio configuração desnecessária.

5) Retire as referências aos repositórios do Spring:

<repositories>
 <!-- For testing against latest Spring snapshots -->
 <repository>
  <id>org.springframework.maven.snapshot</id>
  <name>Spring Maven Snapshot Repository</name>
  <url>http://maven.springframework.org/snapshot</url>
  <releases><enabled>false</enabled></releases>
  <snapshots><enabled>true</enabled></snapshots>
 </repository>
 <!-- For developing against latest Spring milestones -->
 <repository>
  <id>org.springframework.maven.milestone</id>
  <name>Spring Maven Milestone Repository</name>
  <url>http://maven.springframework.org/milestone</url>
  <snapshots><enabled>false</enabled></snapshots>
 </repository>
</repositories>

6) Este exemplo usa o nome padrão do arquivo WAR, então remova a referência para o plugin maven-war:

<plugin>
 <groupId>org.apache.maven.plugins</groupId>
 <artifactId>maven-war-plugin</artifactId>
 <configuration>
  <warName>abc</warName>
 </configuration>
</plugin>

7) Atualize o plugin Surefire para remover as referências do Roo:

<plugin>
 <groupId>org.apache.maven.plugins</groupId>
 <artifactId>maven-surefire-plugin</artifactId>
 <configuration>
  <junitArtifactName>junit:junit</junitArtifactName>
  <!-- Remove the excludes -->
  <excludes>
   <exclude>**/*_Roo_*</exclude>
  </excludes>
 </configuration>
</plugin>

8) Adicione aspectjweaver como uma dependência abaixo de aspectjrt:

<dependency>
 <groupId>org.aspectj</groupId>
 <artifactId>aspectjweaver</artifactId>
 <version>${org.aspectj-version}</version>
</dependency>

9) Remova a referência do plugin AspectJ:

<plugin>
 <groupId>org.codehaus.mojo</groupId>
 <artifactId>aspectj-maven-plugin</artifactId>
 <!-- Have to use version 1.2 since version 1.3 does not appear to work with ITDs -->
 <version>1.2</version>
 <dependencies>
  <!-- You must use Maven 2.0.9 or above or these are ignored (see MNG-2972) -->
  <dependency>
   <groupId>org.aspectj</groupId>
   <artifactId>aspectjrt</artifactId>
   <version>${org.aspectj-version}</version>
  </dependency>
  <dependency>
   <groupId>org.aspectj</groupId>
   <artifactId>aspectjtools</artifactId>
   <version>${org.aspectj-version}</version>
  </dependency>
 </dependencies>
 <executions>
  <execution>
   <goals>
    <goal>compile</goal>
    <goal>test-compile</goal>
   </goals>
  </execution>
 </executions>
 <configuration>
  <outxml>true</outxml>
  <source>${java-version}</source>
  <target>${java-version}</target>
 </configuration>
</plugin>

Essa é a referência que causa todos os problemas. Sem ele, os valores padrão são usados e a aplicação funciona.

10) Atualize tomcat-maven-plugin para implementação automática.

<plugin>
 <groupId>org.codehaus.mojo</groupId>
 <artifactId>tomcat-maven-plugin</artifactId>
 <version>1.1</version>
 <configuration>
  <server>myserver</server>
  <url>http://localhost:8080/manager/text</url>
 </configuration>
</plugin>

Isto deixa o seguinte arquivo POM funcionando:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
 <modelVersion>4.0.0</modelVersion>
 <groupId>com.captaindebug</groupId>
 <artifactId>audit</artifactId>
 <packaging>war</packaging>
 <version>1.0.0-BUILD-SNAPSHOT</version>
 <properties>
  <java-version>1.7</java-version>
  <org.springframework-version>3.2.3.RELEASE</org.springframework-version>
  <org.aspectj-version>1.7.1</org.aspectj-version>
  <org.slf4j-version>1.5.10</org.slf4j-version>
 </properties>
 <dependencies>
  <!-- Spring -->
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-context</artifactId>
   <version>${org.springframework-version}</version>
   <exclusions>
    <!-- Exclude Commons Logging in favor of SLF4j -->
    <exclusion>
     <groupId>commons-logging</groupId>
     <artifactId>commons-logging</artifactId>
    </exclusion>
   </exclusions>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-webmvc</artifactId>
   <version>${org.springframework-version}</version>
  </dependency>

  <!-- AspectJ -->
  <dependency>
   <groupId>org.aspectj</groupId>
   <artifactId>aspectjrt</artifactId>
   <version>${org.aspectj-version}</version>
  </dependency>
  <dependency>
   <groupId>org.aspectj</groupId>
   <artifactId>aspectjweaver</artifactId>
   <version>${org.aspectj-version}</version>
  </dependency>

  <!-- Logging -->
  <dependency>
   <groupId>org.slf4j</groupId>
   <artifactId>slf4j-api</artifactId>
   <version>${org.slf4j-version}</version>
  </dependency>
  <dependency>
   <groupId>org.slf4j</groupId>
   <artifactId>jcl-over-slf4j</artifactId>
   <version>${org.slf4j-version}</version>
   <scope>runtime</scope>
  </dependency>
  <dependency>
   <groupId>org.slf4j</groupId>
   <artifactId>slf4j-log4j12</artifactId>
   <version>${org.slf4j-version}</version>
   <scope>runtime</scope>
  </dependency>
  <dependency>
   <groupId>log4j</groupId>
   <artifactId>log4j</artifactId>
   <version>1.2.15</version>
   <exclusions>
    <exclusion>
     <groupId>javax.mail</groupId>
     <artifactId>mail</artifactId>
    </exclusion>
    <exclusion>
     <groupId>javax.jms</groupId>
     <artifactId>jms</artifactId>
    </exclusion>
    <exclusion>
     <groupId>com.sun.jdmk</groupId>
     <artifactId>jmxtools</artifactId>
    </exclusion>
    <exclusion>
     <groupId>com.sun.jmx</groupId>
     <artifactId>jmxri</artifactId>
    </exclusion>
   </exclusions>
   <scope>runtime</scope>
  </dependency>

  <!-- @Inject -->
  <dependency>
   <groupId>javax.inject</groupId>
   <artifactId>javax.inject</artifactId>
   <version>1</version>
  </dependency>

  <!-- Servlet -->
  <dependency>
   <groupId>javax.servlet</groupId>
   <artifactId>servlet-api</artifactId>
   <version>2.5</version>
   <scope>provided</scope>
  </dependency>
  <dependency>
   <groupId>javax.servlet.jsp</groupId>
   <artifactId>jsp-api</artifactId>
   <version>2.1</version>
   <scope>provided</scope>
  </dependency>
  <dependency>
   <groupId>javax.servlet</groupId>
   <artifactId>jstl</artifactId>
   <version>1.2</version>
  </dependency>

  <!-- Test -->
  <dependency>
   <groupId>junit</groupId>
   <artifactId>junit</artifactId>
   <version>4.7</version>
   <scope>test</scope>
  </dependency>

 </dependencies>
 <build>
  <plugins>
   <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
     <source>${java-version}</source>
     <target>${java-version}</target>
    </configuration>
   </plugin>
   <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <configuration>
     <junitArtifactName>junit:junit</junitArtifactName>
    </configuration>
   </plugin>
   <plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>tomcat-maven-plugin</artifactId>
    <version>1.1</version>
    <configuration>
     <server>myserver</server>
     <url>http://localhost:8080/manager/text</url>
    </configuration>
   </plugin>

  </plugins>
 </build>
</project>

Finalmente… tendo configurado o projeto corretamente, a próxima coisa a fazer é passar para o código, que é algo que eu vou fazer da próxima vez.

***

Artigo traduzido pela Redação iMasters, com autorização do autor. Publicado originalmente em http://www.captaindebug.com/2013/06/auditing-spring-mvc-webapp-with-aspectj.html#.Uj9pIRaxOON