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.
Depois de alguns meses curtindo meu HTC Touch, resolvi aproveitar o fim do meu contrato com a Claro e alguns pontos que eu tinha na Clube Claro, para utilizar num deconto legal e comprar um Iphone 3G de 8Giga com direito a uma camiseta promocial com uma maça estampada ! rsrs
Abaixo uma foto comparativa entre os dois aparelhos.
A transição de um aparelho para o outro foi muito simples, pois já estava acostumado a utilizar touchscreen no Htc, então para me acostumar com a interface do iphone foi bem rápido, só preciso me acostumar que agora não preciso mais da caneta styllus para clicar em pequenas partes da tela rsrs.
Todos os contatos do htc estavam sincronizados com o Outlook que tenho no Desktop da minha esposa (utilizo linux no meu note), e com isso bastou uma sincronização do iphone pelo itunes (novamente utilizando desktop da patroa! :( … ) para que todos meus contatos fossem transferidos para ele.
A caixa do iphone é muito menor que a do touch, no mínimo umas 3 vezes e a documentação (manual e outras papeladas) que vem também é bem inferior e resumida. Uma outra coisa que senti falta no iphone, é que o htc touch vem com uma capa tipo luva de neoprene e uma película protetora para a tela. Em contrapartida o iphone é bem mais rápido e com a tela mais sensível que o htc.
Ainda não instalei nada da App Store, mas aceitos dicas de programas legais que não podem faltar no iphone.
E caso alguém saiba como utilizar algum app nativo do linux para sincronização ( itunes pelo wine não rolou! :’( ), por favor, me avise!!!
Interessados no HTC Touch completo (+cartao 2Giga +Carregador veicular), entrar em contato. :D
Atualmente estou desenvolvendo um sistema utilizando ADF Faces na IDE da Oracle JDeveloper 10.1.3.4 e passei por um problema interessante. Estava ocorrendo um problema na minha aplicação mas não era gerado nenhuma exception no console, onde após determinada ação da aplicação era impresso no console apenas:
15/01/2009 17:32:31 oracle.adf.controller.faces.lifecycle.FacesPageLifecycle addMessage WARNING: JBO-29000: null
Mensagem muito interessante e muito muito muito explicativa, não acham !?! :D
Depois de muito pesquisar, acabei encontrando uma forma de exibir o que ocasionou o JBO-29000 e é bem simples de implementar, a idéia principal é sobrescrever o métdo addMessage da classe FacesPageLifecycle e utilizá-la como o novo ADFPhaseListener do projeto.
Primeiro vou extender a classe FacesPageLifecycle e sobrescrever o metodo addMessage para ter o novo comportamento (mostrar a exception):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package br.com.rodrigolazoti.view.adf;
import javax.faces.context.FacesContext;
import oracle.adf.controller.faces.lifecycle.FacesPageLifecycle;
import oracle.binding.AttributeBinding;
public class MyPageLifecycle extends FacesPageLifecycle {
protected void addMessage(FacesContext context, AttributeBinding binding, Throwable error) {
super.addMessage( context, binding, error );
//aqui mostro o erro no console, log4j ou onde preferir
error.printStackTrace();
}
}
Agora eu extendo a classe ADFPhaseListener e retorno a minha classe criada acima ao invés de retorna a FacesPageLifecycle padrão do ADF:
1
2
3
4
5
6
7
8
9
10
11
12
13
package br.com.rodrigolazoti.view.adf;
import oracle.adf.controller.faces.lifecycle.ADFPhaseListener;
import oracle.adf.controller.v2.lifecycle.PageLifecycle;
public class MyPhaseListener extends ADFPhaseListener {
protected PageLifecycle createPageLifecycle() {
return new MyPageLifecycle();
}
}
Agora basta subistituir o ADFPhaseListener original pelo criado acima no arquivo de configuração do JSF (faces-config.xml):
1
2
3
<lifecycle>
<phase-listener>br.com.rodrigolazoti.view.adf.MyPhaseListener</phase-listener>
</lifecycle>
Com isso foi fácil descobrir o motivo da mensagem e o que estava acontecendo de errado na aplicação. ;)
Esse final de semana aproveitei para instalar a nova versão 8.10 do Ubuntu no meu notebook, eu já utilizava a versão 8.04 e estava muito satisfeito com ela, mas minha curiosidade foi maior e acabei não resistindo e instalando a nova versão.
Como não tive boas esperiências com atualização do S.O., resolvi fazer um backup dos meus arquivos e fazer uma instalação “do zero” no meu notebook. A Instalação ocorreu tranquilamente via interface gráfica e todo meu hardware foi detectado corretamente.
Ainda não tive muito tempo para avaliar todas as novidades e como esta versão está se comportando, mas por enquanto não tive grandes problemas.
Mas como nem tudo são flores uma coisa que me deixou um pouco preocupado foi que meu note demorava cerca de 7 minutos para desligar ou para reiniciar, contudo após algumas pesquisas acabei encontrando uma solução para este problema, na qual descrevo a seguir:
Edite o arquivo /etc/init.d/alsa-utils
1
sudo gedit /etc/init.d/alsa-utils
Após o texto “stop)” no arquivo (aproximadamente na linha 353), adicione:
1
2
ifconfig wlan0 down
ifconfig eth0 down
Então o arquivo deve ficar assim:
1
2
3
4
stop)
ifconfig wlan0 down
ifconfig eth0 down
EXITSTATUS=0
Com isso o problema foi resolvido e agora meu Ubuntu desliga e reinicia normalmente. :D
Rescentemente terminei a leitura do livro Pojos In Action de Chris Richardson, embora este livro seja de 2006 e utilize em seus exemplos versões antigas de frameworks como Spring 1.23, Hibernate 3.0 e EJB3 (Beta), não consigo deixar de recomendá-lo.
Pois é um ótimo livro para quem procura exemplos de como utilizar estes e alguns outros frameworks, assim como aplicar Test-driven Development (TDD) e alguns patterns no domain model.
Uns dos pontos que mais gostei no livro é a sua explicação sobre domain model, onde ele explica e exemplifica o pattern Facade, Exposed Domain Model e seus prós e contras ao aplicá-los.
Inclusive em muitos pontos do livro pode-se ver referências a padrões explicados no livro Patterns Of Enterprise Application Archittecture de Martin Fowler, na qual já comprei mais ainda não li e assim que concluir a sua leitura, irei dedicar um post exclusivo para este livro. ;)