Uma forma simples de gerar log de uma aplicação é utilizando bibliotecas como Log4J ou Commons Logging, mas ficar incluindo em todas as classes necessárias, chamadas a métodos para gerar log pode ser uma tarefa muito repetitiva.
O Spring framework resolve esse problema utilizando AOP (programação orientada a aspectos), onde podemos criar uma classe que inspeciona e gera os logs de chamadas a métodos de outras classes, assim economizamos tempo e poupa-se código também.
Utilizando a espeficicação EJB 3.0 podemos também nos beneficiar de um recurso parecido com o utilizando pelo Spring na qual chama-se Interceptors, com ele podemos interceptar chamadas aos métodos de negócio dos nossos Sessions Beans e Message Driven Beans.
Para demonstrar seu funcionamento, Vou criar um pequeno exemplo utilizando um Stateless Session Bean e um Interceptor utilizando Log4J, porém este post não visa explicar como configurar um projeto EJB e a ferramente Log4J, mas simplesmente demonstrar como utilizar Interceptors em uma simples aplicação que utiliza EJB.
Primeiro vou criar o Interceptor, o método intercept tem a finalidade de gerar o log de todas as classes que ele interceptar, o método é definico com a anotação @AroundInvoke, este método pode se encontrar em uma classe separada (como no exemplo a seguir) ou no próprio session bean, lembrando apenas que só pode ter um único método anotado por classe, mas nada impede de termos várias classes de interceptors.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package br.com.rodrigolazoti;
import javax.interceptor.AroundInvoke;
import javax.interceptor.InvocationContext;
import org.apache.log4j.Logger;
public class LoggerInterceptor {
@AroundInvoke
public Object intercept( InvocationContext invocationContext ) throws Exception {
Logger log = Logger.getLogger( "myProject" );
String methodName = invocationContext.getMethod().getName();
String className = invocationContext.getTarget().getClass().getName();
log.debug( "Calling Method: " + className + "." + methodName );
long timeBefore = System.currentTimeMillis();
try {
return invocationContext.proceed();
}
catch ( Exception e ) {
log.error( "Error on calling method " + className + "." + methodName );
log.error( "Root cause: ", e );
throw e;
}
finally {
long timeAfter = System.currentTimeMillis();
log.info( "Method " + className + "." + methodName + " called in " + ( timeAfter - timeBefore ) + "ms" );
}
}
}
Agora vou criar o interface remota do meu session bean:
1
2
3
4
5
6
7
8
9
10
11
package br.com.rodrigolazoti;
import javax.ejb.Remote;
@Remote
public interface MyServiceBean {
Integer sum( Integer[] values );
String createWelcomeMessage( String name );
}
Finalmente a implementação do session bean, repare que utilizo a anotação @Interceptors, onde posso definir um ou mais interceptors para esta classe, podemos também definir um interceptor para um método ou até mesmo excluir um interceptor de um método ou classe utilizando a anotação @ExcludeClassInterceptors. Utilizando a configuração via XML podemos definir um interceptor para um grupo de ejb’s por exemplo.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package br.com.rodrigolazoti;
import javax.ejb.Stateless;
import javax.interceptor.Interceptors;
@Stateless( name = "MyServiceBean" )
@Interceptors( { LoggerInterceptor.class } )
public class MyServiceBeanBean implements MyServiceBean {
public Integer sum( Integer[] values ) {
Integer result = 0;
for ( Integer value: values )
result += value;
return result;
}
public String createWelcomeMessage( String name ) {
StringBuilder message = new StringBuilder();
message.append( "Welcome " + name + "!" );
return message.toString();
}
}
Vou criar um arquivo chamado log4j.properties, para configurar o comportamento da saída dos logs gerados pelo Log4J:
1
2
3
4
log4j.category.myProject=DEBUG
log4j.appender.myProject=org.apache.log4j.ConsoleAppender
log4j.appender.myProject.layout=org.apache.log4j.PatternLayout
log4j.appender.myProject.layout.ConversionPattern=%d{HH:mm:ss} %-5p [%C{1}] %m%n
E para terminar o exemplo vou criar um client para testar meu EJB e assim verificar se os logs foram gerados:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package br.com.rodrigolazoti;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class MyServiceBeanClient {
public static void main( String[] args ) {
try {
final Context context = getInitialContext();
MyServiceBean myServiceBean = ( MyServiceBean ) context.lookup( "MyServiceBean" );
Integer[] numbers = { 4, 6, 334, 2, 3, 65, 2, 54, 23, 1 };
String message = myServiceBean.createWelcomeMessage( "Rodrigo Lazoti" );
Integer result = myServiceBean.sum( numbers );
System.out.println( message );
System.out.println( result );
}
catch ( Exception ex ) {
ex.printStackTrace();
}
}
private static Context getInitialContext() throws NamingException {
return new InitialContext();
}
}
Pronto, é so iniciar o Application Server (JBoss, OC4J, WebLogic e etc…), fazer o deploy da aplicação e executar o teste. Esta é a saída apresentada no console do application server:
1
2
3
4
10:46:17 DEBUG [LoggerInterceptor] Calling Method: br.com.rodrigolazoti.MyServiceBeanBean.createWelcomeMessage
10:46:17 INFO [LoggerInterceptor] Method br.com.rodrigolazoti.MyServiceBeanBean.createWelcomeMessage called in 0ms
10:46:17 DEBUG [LoggerInterceptor] Calling Method: br.com.rodrigolazoti.MyServiceBeanBean.sum
10:46:17 INFO [LoggerInterceptor] Method br.com.rodrigolazoti.MyServiceBeanBean.sum called in 0ms
Agora basta inserir a anotação @Interceptors( { LoggerInterceptor.class } ) em outros EJB’s Sessions Beans para que o Interceptor gere log para as chamadas aos seus métodos.