Vocês ja ouviram certamente aquela fraze: se morder a lingua morre envenenado …
Pois é, quase aconteceu comigo esta semana …
Quando algum programa esta com problemas eu sou o primeiro a perguntar se é a ultima versão, se não é digo para atualizar, pois acredito que só se pode reclamar dos bugs da ultima versão de alguma coisa, pois o problema pode ja ter sido corrigido …
Estava apanhando a alguns dias para uma aplicação que estava travando sem motivo aparente.
Até que resolvi criar um aspecto para fazer um trace do código, e como a principio eu não tinha nem ideia de onde a app estava travando (tah, eu até tinha uma noção de depois do que era, mas o que exatamente eu não sabia, e podia ser qualquer uma das classes usadas daquele ponto para baixo) o aspecto ficou mais ou menos assim:
public aspect MemoryLeakDetector {
private static Logger logger = Logger.getLogger(MemTrace.class);
private static final String SPACE = "__";
ThreadLocal cd = new ThreadLocal() {
protected Object initialValue() {
return new MemTrace();
}
};
/**
* The constructors in those classes - but only the ones with 3 arguments.
*/
pointcut constructors(): execution(new(..)) && !within(MemoryLeakDetector) && !within(MemTrace) && !within(ThreadLocal);
/**
* This specifies all the message executions.
*/
pointcut methods(): execution(* *(..)) && !within(MemoryLeakDetector) && !within(MemTrace) && !within(ThreadLocal);
before(): methods(){
traceBefore(thisJoinPoint, false);
}
after(): methods(){
traceAfter(thisJoinPoint, false);
}
before(): constructors(){
traceBefore(thisJoinPoint, true);
}
after(): constructors(){
traceAfter(thisJoinPoint, true);
}
private void traceAfter(JoinPoint jp, boolean isConstructor) {
MemTrace cl = (MemTrace) cd.get();
if (logger.isInfoEnabled()) {
String cls = jp.getSignature().getDeclaringTypeName();
String met = jp.getSignature().getName();
String threadName = Thread.currentThread().getName();
long free = Runtime.getRuntime().freeMemory();
long max = Runtime.getRuntime().maxMemory();
long total = Runtime.getRuntime().totalMemory();
long milis = System.currentTimeMillis();
String msg = StringUtils.join(new Object[] {
StringUtils.repeat(SPACE, cl.get()), "<",
Integer.valueOf(cl.get()), threadName, cls, met,
Long.valueOf(free), Long.valueOf(max), Long.valueOf(total),
Long.valueOf(milis) }, " | ");
logger.info(msg);
if (logger.isDebugEnabled()) {
msg = StringUtils.join(jp.getArgs(), ", ");
logger.info(StringUtils.repeat(SPACE, cl.get()) + msg);
}
}
cl.dec();
}
private void traceBefore(JoinPoint jp, boolean isConstructor) {
MemTrace cl = (MemTrace) cd.get();
cl.inc();
if (logger.isInfoEnabled()) {
String cls = jp.getSignature().getDeclaringTypeName();
String met = jp.getSignature().getName();
String threadName = Thread.currentThread().getName();
long free = Runtime.getRuntime().freeMemory();
long max = Runtime.getRuntime().maxMemory();
long total = Runtime.getRuntime().totalMemory();
long milis = System.currentTimeMillis();
String msg = StringUtils.join(new Object[] {
StringUtils.repeat(SPACE, cl.get()), ">",
Integer.valueOf(cl.get()), threadName, cls, met,
Long.valueOf(free), Long.valueOf(max), Long.valueOf(total),
Long.valueOf(milis) }, " | ");
logger.info(msg);
if (logger.isDebugEnabled()) {
if (jp.getArgs() != null && jp.getArgs().length > 0)
msg = StringUtils.join(jp.getArgs(), ", ");
else
msg = " ";
logger.info(StringUtils.repeat(SPACE, cl.get()) + msg);
}
}
}
}
public class MemTrace {
private int depth = 0;
public void inc() {
depth += 1;
}
public void dec() {
depth -= 1;
}
public int get() {
return depth;
}
}
Bom, descobri que o problema era o envio de e-mails, que eu estava fazendo utilizando o Commons-Email.
primeiro passo foi tentar passar alguns parametros para o JavaMail no inicio da aplicação …
o script de inicialização fiou parecido com isto:
java -cp $CP -Dmail.smtp.connectiontimeout=20000 -Dmail.smtp.timeout=20000 ….
Pois é, não adiantou absolutamente nada …
A aplicação continuou travando no meio do envio de uma mensagem (coisa que é feita dentro de um loop que é chamado de dentro de uma thread, que ….)
E não era nem no primeiro e-mail, isto simplesmente acontecia em algum momento no tempo …
Segunda tentativa, removi o Commons-Email e substitui pelo suporte a JavaMail do Spring Framework, o código até ficou bem simples …
Mas continuou tudo na mesma :S
Ja estava quase entrando em desespero quando resolvi finalmente ir ao site do JavaMail …
A versão do jar que estava no sistema é a 1.3.1 que vem junto com o Spring Framework 2.0rc2, no site do JavaMail tinha a versão 1.4 para baixar.
Pois resumindo toda a história, substitui o mail.jar pelo que veio junto com o JavaMail 1.4 e pronto, tudo funcionando novamente
E eu que ja estava até pensando em criar um sistema de pooling de threads com timeout de execução, tipo se a tarefa demorasse mais do que o timeout especificado a thread seria derrubada
não foi necessário, foi só fazer o que eu sempre falo, utilizar a versão mais atual da biblioteca …
Bom, este post foi apenas para lembrar quem se der o trabalho de ler …
Se nada funcionar: leia o manual
se continuar não funcionando: faça um upgrade que deve resolver
se ainda não deu certo: ai é hora de arregaçar as mangas, chutar o balde e arrumar de verdade o que ainda não funciona, se esta não for uma opção …
As vezes uma gambi chinelona destas qe eu comentei antes podem salvar a sua vida, mas lembre de documentar bem quando fizer isto …
PS.: AspectJ rocks!! a unica coisa chata é ter que compilar novamente a app, por que o loadtime weaving não ta as mil maravilhas ainda