Caelum com as portas abertas no Rio de Janeiro

Por Nico Steppat em 25/09/08

A Caelum no Rio de Janeiro está inaugurada! Estamos muito contentes em agora ter um lugar próprio aqui, onde já temos diversos clientes.

Estamos na Rua Senador Dantas 80, Salas 307/308, no Centro da Cidade. É muito perto da estação de metrô Carioca (Saida Rua Senador Dantas).

Estamos finalizando as reformas nas salas, mas já dá para ver como vai ficar:

Você já pode entrar em contato conosco através do telefone (21) 2220-4156 ou mande um email, e aproveite os descontos especiais de inauguração. Estamos oferecendo os mesmos treinamentos com a mesma qualidade da sede de São Paulo.

As primeiras turmas já estão programadas para o mês de Outubro. Veja o calendário. Venha nos fazer uma visita. Em breve mais novidades!

Java Annotations: directing, enabling?

Por Fabio Kung em 22/09/08

Há algum tempo, quando trabalhava na Alemanha, costumava ter diversas conversas e discussões extremamente produtivas com um grande amigo e desenvolvedor, Tiago Silveira. Como não poderia deixar de ser, desenvolvimento de software era o preferido entre diversos assuntos interessantes.

Em uma de nossas conversas, falávamos sobre o uso de anotações em Java. A discussão foi tão produtiva, que chegamos a concordar que o uso de anotações pode ser encarado como atitude com relação ao desenvolvimento de software!

Foi o próprio Tiago que começou a falar sobre enabling vs directing e começamos a fazer algumas comparações com o uso de anotações em projetos famosos.

Anotações são apenas metadados (ou DecorativeData, como alguns podem preferir); informações extras sobre o código que está escrito. Eu costumo sempre fazer analogia com um post-it pendurado no meio do seu código fonte: anotações não executam código.

Na minha opinião, o bom uso de anotações segue o estilo “Enabling”. Não te direcionam para nenhum caminho, não indicam a execução de nenhum código. Pelo contrário, como metadados, apenas possibilitam diversos usos desta informação extra. Um bom exemplo seria a anotação @Entity, da Java Persistence API:

@Entity
public class Carro {
  // …
}

Diversos frameworks e até outras partes do seu sistema podem usar esta informação extra como bem entenderem. O próprio hibernate usa esta informação para saber que se trata de uma classe persistente, que deve ter representação no banco relacional, um validador poderia usar esta informação para decidir que objetos desta classe devem ser validados, um framework de aspectos poderia decidir aplicar um aspecto nesta classe baseado nesta informação extra, entre diversas outras possibilidades. Um outro exemplo interessante poderia ser a anotação @Transient, da própria especificação JPA:

public class Bicicleta {
  @Transient
  private double velocity;
}

Você poderia usar esta informação para decidir não mostrar este campo na interface gráfica (web ou desktop). Pode usar esta informação extra até para decidir que este campo não será enviado em emails, nem incluso em logs. Frameworks de ORM como o hibernate aproveitam esta informação para ignorar o atributo e não incluí-lo em nenhuma tarefa de persistência.

Poderia dar mais inúmeros exemplos de bons usos de anotações. O mais importante é a idéia de que a anotação possibilita diversos usos e não direciona - não força - nenhum caminho. A anotação não está associada a nenhuma execução de código. Funciona mesmo como um simples post-it.

A atitude alternativa seria o pensamento “Directing”. Neste caso, a anotação está diretamente relacionada a execução de algum código e serve apenas para este fim. Exemplo:

public class Sistema {

  @EnviaEmail(Emails.CONFIRMACAO)
  public void cadastra(Usuario usuario) {
    //…
  }

}

Neste caso não há outro uso para esta anotação. Perceba que ela já pressupõe a execução do código de envio de emails. Te limita a um pensamento particular, para um uso específico. Talvez seja um caso exagerado, mas não há diferença alguma com a chamada direta do método que envia o email:

public class Sistema {
 
  public void cadastra(Usuario usuario) {
    mensageiro.enviaEmail(Emails.CONFIRMACAO, usuario);
    // …
  }
 
}

Em alguns casos pode ser bastante difícil, ou até subjetivo demais, decidir se o uso de uma anotação é directing ou enabling. Não acho que devemos tomar nada como verdade absoluta; o importante é ter consciência dos diversos pontos de vista diferentes. Assim podemos tomar as nossas decisões, sempre pesando vantagens e desvantagens.

E você, consegue lembrar de algum outro uso interessante de anotações? Enabling ou Directing?

Divisions com Hibernate: uso avançado da Criteria API

Por Lucas Cavalcanti em 11/09/08

Existe uma operação, não muito conhecida, mas muitas vezes necessária, em bancos de dados chamada divisão (division). Essa operação representa o seguinte tipo de consulta: Selecione os alunos que fizeram todos os cursos. Selecione os autores em que todos os seus livros têm mais de 200 páginas. E assim por diante.

Esse tipo de consulta precisa de alguns recursos avançados do SQL, então antes de mostrar como implementá-la vamos ver como implementar consultas um pouco mais simples, usando a Subqueries e a DetachedCriteria, que nos possibilitam consultas bastante poderosas usando a api da Criteria.

Bom, vamos começar com três entidades: Aluno, Curso, e um relacionamento de muitos pra muitos entre eles representado pela entidade Matrícula.

Vamos pensar um pouquinho como fazer a seguinte consulta: “Selecionar todos os alunos que estejam cursando Matemática ou Português“. Pensando em banco de dados, podemos fazer um join entre Alunos e Matrículas, e selecionar as linhas em que o curso é matemática ou é português. Precisamos também evitar que a busca retorne alunos repetidos. Vamos fazer isso com Criteria, recebendo a lista dos cursos que eu quero que o aluno esteja cursando algum deles:


public List<Aluno> alunosCursandoAlgumDessesCursos(List<Curso> cursos) {
  Criteria criteria = session.createCriteria(Aluno.class);
  //join com as matrículas
  criteria.createCriteria("matriculas""m");
  
  //usando a disjunction para fazer um ’ou’ entre vários elementos
  Disjunction ou = Restrictions.disjunction();
  for (Curso curso : cursos) {
    ou.add(Restrictions.eq("m.curso", curso);
  }
  criteria.add(ou);

  //eliminando resultados repetidos
  criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
  return criteria.list();
}

Ou podemos fazer algo bem mais interessante, que é usar a restrição in, que retorna verdadeiro se a propriedade dada é igual a algum dos elementos da coleção que passarmos pra ela. Nesse caso trocaríamos o Disjunction por simplesmente:

criteria.add(Restrictions.in("m.curso", cursos));

Bem fácil! Agora vamos mudar só um pouquinho a consulta para: “Selecione todos os alunos que estiverem cursando Português E Matemática“. Poderíamos inocentemente mudar a Disjunction para Conjunction no método anterior. Mas isso não funciona! Por quê? Porque se fizermos isso, estaríamos mudando a consulta para algo do tipo: “Selecione os alunos que tenham uma matrícula que é em Português e em Matemática ao mesmo tempo“. E isso não é possível. Temos que mudar essa consulta para algo do tipo: “Selecione todos os alunos para os quais exista uma matricula no curso Português e exista uma matrícula no curso Matemática“.

Existe uma operação em SQL que faz exatamente isso: o exists. Ela retorna verdadeiro se a subconsulta que estiver depois dela retornar algum resultado. Para fazer isso precisamos então criar subconsultas em Criteria, e o jeito de fazer isso é usando a classe Subqueries, que fabrica Criterions que envolvem a criação de subconsultas.

Para usar qualquer método da Subqueries precisamos de uma DetachedCriteria. Essa DetachedCriteria é um tipo especial de Criteria que não precisa da session do hibernate para ser criada. Dentro dela temos acesso a todos os alias e propriedades da Criteria principal, e o uso é o mesmo que faríamos para Criterias normais.

Já que temos a Subqueries na mão, vamos implementar a consulta, recebendo a lista dos cursos que queremos que o aluno esteja matriculado em todos eles:


public List<Aluno> alunosCursandoTodosEssesCursos(List<Curso> cursos) {
  Criteria criteria = session.createCriteria(Aluno.class, "a");
  Conjunction e = Restrictions.conjunction();
  for (Curso c : cursos) {
    e.add(Subqueries.exists(
      DetachedCriteria.forClass(Matricula.class, "m")
        .setProjection(Projections.id())
        .add(Restrictions.eqProperty("a.id""m.aluno.id"))
        .add(Restrictions.eq("m.curso",c))));
  }
  criteria.add(e);
  return criteria.list();
}

Ou seja, queremos que exista uma matrícula do aluno da Criteria principal para cada curso da lista passada.

Mas vamos pensar no seguinte: Essa lista de cursos provavelmente veio de outra consulta no banco, por que não usar essa consulta, ao invés da lista de cursos?! O jeito de fazer isso é usando o operador division que falamos no começo do post. Ele é meio complicado de implementar, pois você tem que pensar meio ao contrário do normal. Por exemplo, para implementar a consulta “Selecione os alunos que estão matriculados em todos os cursos” precisamos transformá-la para: “Selecione os alunos para os quais não exista nenhum curso para o qual não exista matrícula desse aluno para esse curso“, ou seja: um aluno que não exista nenhum curso em que ele não esteja matriculado. É estranho mas é assim mesmo que é feito. A Subqueries também possui o método notExists, então podemos fazer a seguinte consulta, que traz os alunos que fazem todos os cursos:


public List<Aluno> alunosCursandoTodosOsCursos() {
  Criteria criteria = session.createCriteria(Aluno.class, "a");
  criteria.add(Subqueries.notExists(
      DetachedCriteria.forClass(Curso.class, "c")
        .setProjection(Projections.id())
        .add(Subqueries.notExists(
            DetachedCriteria.forClass(Matricula.class, "m")
              .setProjection(Projections.id())
              .add(Restrictions.eqProperty("m.curso.id""c.id"))
              .add(Restrictions.eqProperty("m.aluno.id""a.id")                
        ))
      ));
  return criteria.list();
}

Não é um bicho de sete cabeças, mas também não é nada trivial. O código fica meio poluído por causa das chamadas estáticas, mas se você fizer o import static dos métodos a coisa melhora um pouquinho.

As restrições que você tinha colocado para buscar a lista de cursos dos métodos anteriores, você pode colocar na DetachedCriteria de Cursos, que vai funcionar do jeito que é esperado. Por exemplo: “Selecione os alunos que estejam matriculados em um curso noturno” vira “Selecione os alunos para os quais não exista algum curso noturno em que ele não esteja matriculado“. Mais ainda: você pode colocar restrições pertinentes na DetachedCriteria da matrícula, que também vai funcionar da forma esperada. Por exemplo: “Selecione os alunos que estejam com a matricula paga em todos os cursos” vira “Selecione os alunos para os quais não existe algum curso em que não exista matrícula paga nesse curso“.

Existem muitos casos em que o operador division salva sua vida então, mesmo que ele seja meio complicadinho, é bom saber que ele existe e ter uma boa referência de como implementá-lo =).

Além da Subqueries, existe outra classe muito útil que fabrica Criterions e Projections relacionados a uma propriedade fixa: a Property. Vale a pena olhar o javadoc do hibernate e ver a quantidade de opções de consultas que temos disponíveis. Existe um bug no hibernate que te obriga a setar uma Projection nas DetachedCriterias quando usadas dentro das Subqueries, se isso não é feito o hibernate nos presenteia com uma NullPointerException.

Vazamento de memória e de conexões

Por Guilherme Silveira em 02/09/08

Descrevo aqui a minha aventura junto com diversos desenvolvedores da Caelum: Cauê Guerra, Filipe Sabella, Anderson Leite, Pedro Mariano e o Pedro Matiello para resolver um grande problema que ocorria em um dos nossos projetos.

Assim como quando um avião cai, não é um bug que gera um grande problema, mas sim uma série de fatores que contribuem para a dificuldade na hora de encontrar complicações estruturais de um projeto.

Vamos as fatos, que incluem bugs e vazamentos em bibliotecas famosas, profiling e open call hierarchy de eclipse.

Sintoma: o servidor web parava de responder para qualquer página, exceto a tela de login

Nesse caso, o servidor entrava na tela de login mas ao tentar submete-la ou acessar qualquer outra funcionalidade o browser não recebia resposta alguma do servidor web.

A primeira coisa a pensar era que o processador estaria a 100%, que é o caso comum em laços infinitos ou threads gulosas além da conta. Mas um comando rápido no servidor (top) mostrava que o processador estava a 0%. Claro, se estivesse a 100%, teríamos duas opções:

  1. rodar o profiler e detectar o método que estaria consumindo 100% do cpu
  2. imprimir todas as threads do sistema (com Thread.métodos estáticos) e ver o stack trace de cada uma delas… detectaríamos o mesmo ponto.

Mas como nosso problema não era esse, uma possível causa levantada pelo Rafael Cosentino foi de que poderia haver algum deadlock, travando todas as requisições. Pensando na dificuldade de detectar tal falha, e acreditando que o problema estava relacionado com o consumo de memóriaapesar de não haver OutOfMemoryErrors), decidimos usar o JProfiler.

O primeiro passo foi rodar o profiler no Stella Boleto para verificar se ele estava com algum leak, pois sabíamos que o mesmo consome uma memória razoável durante o processo de geração do boleto, devido a imagem de background de alta resolução ser carregada num BufferedImage. Memória a qual deveria ser liberada. Ao rodar o profiler, o Caue Guerra, executou diversas threads em paralelo com invocações complexas do Stella Boletos. Durante e após as execuções, não houve memory leak algum, e o garbage collector consumia os objetos corretamente.

Sem nenhum ponto especial, teríamos que fazer o profiling da aplicação inteira.

Foram várias tentativas com profiling remoto offline, o código java invocava a api de profiling para depois analisarmos os resultados em nossas máquinas, porém um bug do JProfiler com o Apache Tomcat 6.0.16 inviabilizou tal análise. Fomos então rodar a aplicação no Jetty 7 (apontando para a mesma configuração do tomcat) sem que nenhum usuário da aplicação live notasse a mudança: uma migração transparente de servlet containers executada com êxito, assim como ocorreu no GUJ.

Em paralelo, o Tomcat foi atualizado para 6.0.18, que funciona com a última versão do JProfiler corretamente e levantamos o mesmo com apenas 64 megas de ram (para estressar o garbage collector mais frequentemente). Rodando o Tomcat em outra porta, mas acessando o mesmo banco de produção, permitiu que testássemos junto ao cliente exatamente as funcionalidade que ele julgava “lentas”.

Em algum ponto durante o uso do JProfiler, o mesmo indicou que não havia nenhum deadlock de threads, bastando para ele verificar se existem threads esperando por monitores de maneira cruzadas.

Primeiro sinal de leak: Executamos algumas tarefas no sistema e vimos que o número de objetos crescia de maneira adequada, porém, ao rodar o GC, alguns objetos ficavam para trás… objetos que são entidades do projeto! É comum o número de Strings, arrays de char e de bytes crescerem, mas entidades do nosso próprio sistema? E agora? Por que isso acontece? Alguém continuou mantendo referências para objetos do projeto que não deveriam estar referenciados. Novamente o profiler vai ajudar.

Segundo sinal de leak: Cutucando mais um pouco percebemos que algumas Sessions do hibernate tambem não estavam sendo fechadas nem coletadas! Essa costuma ser uma forte indicação de vazamento de conexão. E no caso do Hibernate, isso poderia vir a gerar mais leak ainda, dado que muitas entidades ficariam presas no cache de primeiro nível, já que a sessão continuava aberta.

Objetos do hibernate apos o gc

Mas olhando a fundo, percebemos que a maior parte dessas sessões, exceto uma, estava injetada em objetos de modelo na sessao web. Isto é, são sessões usadas pelos nossos Repositories, que faz todo o sentido para o projeto e não é um memory leak, pois a sessão que era referenciada deveria estar fechada e com isso os objetos de cache de primeiro nível dele, coletados.

Sendo assim, partimos para testar funcionalidades que acreditavamos ter consumo grande de memória.

Primeiro sinal de consumo excessivo de memória: Encontramos uma funcionalidade que levantava cerca de 40 mil objetos na memória. Com isso, o consumo era de cerca de 40 megas de objetos só nesse select… mas o Tomcat estava configurado para 64 megas? O que acontecia? O consumo de memória aumentava, o garbage collector era invocado de sua maneira parcial, até que o consumo chegava ao topo, quando garbage collector full era rodado, pedindo mais memória para o sistema operacional.

Mas 64 ram não era espaço suficiente, então rodava o garbage collector full novamente, passando por todos os objetos da memória novamente, liberando mais um pouco e deixando um pouco mais de espaço, mas ainda não o suficiente. Cada vez mais, em intervalos de poucos segundos, o garbage collector era rodado e liberava menos espaço novo do que antes, tudo isso ainda na mesma requisição que usaria 40 megas… até que os espaços são tão pequenos que o processador fica a 100% de processamento, até acabar a memória e lançar o OutOfMemoryError. Nesse meio tempo, o servidor fica praticamente sem dar respostas, dado o alto uso de CPU.

Garbage collector rodando apos requisicao que criava diversos objetos

Garbage collector rodando apos requisicao que criava diversos objetos: note o garbage collector full rodado no final.

 Ok, encontramos um problema. Solução: filtrar esses 40 mil objetos para trazer menos objetos para a memória de uma vez só. No nosso caso uma simples busca por esses objetos foi o suficiente. O cliente não queria paginação e já havia recusado a paginação que havia sido feita no Sprint anterior.

Em outros casos, poderiamos usar resultados do hibernate que armazenam uma referencia somente para o objeto atual da lista, podendo garbage coletar os anteriores (uma StatelessSession, conforme discutido nos 7 hábitos dos desenvolvedores Hibernate altamente eficazes).

Esse ainda não era o grande problema, pois o nosso sintoma travava o servidordeixando o processador a 0%, e não a 100%.

Memory leak temporário encontrado: Voltando ao problema de leak anterior: o profiler mostrou que alguns objetos que estavam sobrando e não sendo garbage coletados. Com um pouco mais de procura, um heap dump mostra todas as referencias de um determinado objeto e, procurando quem referenciava aqueles que não iam para o garbage collector, encontramos que a implementação padrão do grupo Apache para a JSTL na tag c:forEach mantinha uma referencia para o último objeto iterado. Oops. Como assim?
 

c:forEach leak

c:forEach leak

A tag c:forEach, para seguir a especificação da JSTL, mantem uma referência para o último objeto iterado, mesmo após a iteração (as tags ficam em um pool do servidor de aplicação e são recicladas). Por isso, a referência era fixa e todo aquele pedaco da árvore de referÊncia dos objetos não poderia ser coletado até o proximo c:forEach usar aquela instância do pool novamente.

E agora o que isso pode causar?

Primeiro, as instâncias da tag só vão manter a referência por um tempo determinado. Quando utilizadas novamente, aquela árvore de objetos poderá ser coletada… o único problema aconteceria com um número muito grande de usuários executando uma query no servidor ao mesmo tempo. Isso não costuma acontecer em projetos de 1000 usuários, portanto não é uma preocupação no momento.

Novamente não resolvemos o problema principal, mas achamos mais um detalhe importante sobre uma biblioteca que utilizamos no dia a dia sem medo. Solução: não se preocupar pois o projeto não terá um número tão grande de usuários simultaneos para causar algum problema. Continuar usando a JSTL.

Por fim, as estatísticas do Hibernate Session Factory mostravam que raramente uma conexão se perdia. Após mais de 5000 conexões estabelecidas, três estavam abertas. Isto é, a conexão atual, e mais duas… As estatísticas do hibernate é uma das maneira mais simples de obter resultados sobre leaks de conexões, queries mal escritas ou possíveis pontos de bom uso do cache de segundo nível. Oolhando as estatísticas, confirmamos o session leak (connection leak):

c:forEach leak enquanto não apontar para outro objeto

session leak

Session leak é um dos erros mais comuns em projetos com o Hibernate. Infelizmente encontramos muitos projetos que acessam conexões em métodos estáticos, sem controle algum de quem invoca esse método e de quem libera essa sessão. Nesse projeto, o acesso a camada ORM era feita através de um Interceptador, como no Open Session in View, que é a maneira que mais gostamos de trabalhar, pois dificulta session leaks e facilita a criação de testes para seu código, já que será usado alguma maneira de inversão de controle.

Causa encontrada: Usando o Open Call Hieranchy do Eclipse, encontramos rapidamente 3 referências para abertura de sessões que não fecham ela de maneira adequada (com try/finally mal escritos)…

Solução: Abrir e fechar sessões corretamente!

Mas o que um connection leak pode causar no meu projeto? Tudo depende da configuração do seu connection pool. No caso do c3p0, o mais famoso e amplamente utilizado, existem algumas configurações que você pode fazer como, por exemplo, o número minimo de conexões no pool, c3p0.minPoolSize.

Porem, ao usar hibernate essa propriedade muda para hibernate.c3p0.min_size. Note que o hibernate
decidiu não seguir o padrão! Em uma conversa com o Diego Plentz, commiter do Hibernate e colaborador da Caelum, não encontramos rapidamente um motivo pelo qual o padrão não foi seguido. Talvez algum problema de conflito de propriedades configuradas em um mesmo arquivo, ou talvez para manter compatibilidade com o Hibernate 2.

O que acontecia nesse caso específico? A aplicação usava algumas configurações corretamente, como o minimo e máximo de conexões (nos testes de integração e de aceite, o máximo era 3). Mas uma outra configuração importantíssima estava escrita de maneira errada dentro de uma String do XML: configurar o tempo máximo que uma conexão que não está sendo usada seja eliminada, justamente para ignorar leaks. Algo como: se a conexão está idle, por, digamos, 1 minuto, considere que foi um leak, e traga ela de volta para o pool.

Causa encontrada: Como exatamente essa configuração estava sendo usada no valor errado, junto com o memory leak da implementação da JSTL, após a funcionalidade com session leak acontecer 3 vezes nos testes de integração, não existia mais nenhuma conexão disponível. O mesmo acontecia em produção com um número maior de vezes. Então as threads ficavam esperando uma conexao do connection pool.

Isso podia ser visto na visualização de threads do profiler: a cada nova requisição com o servidor sem responder, uma nova thread era criada pelo tomcat para atender a requisição, mas essa thread ficava em WAITING, isto é, ela estva esperando uma conexão ser liberada. Pela view de monitor era possível
verificar que ela aguardava algo do c3p0… a conexão… pronto! 

Threads aguardando monitores/sleeping etc

Threads aguardando monitores/sleeping etc

Encontramos o problema… as threads ficavam paradas (0% de processador) esperando por conexões que nunca chegariam (WAITING)! Um problema que veio por causa da configuração do c3p0 com o problema do connection leak. A solução é composta por duas tarefas: corrigir o session/connection leak e corrigir a configuração do c3p0. Encontramos facilmente o erro graças aos testes de integração.

Ufa! Prontos para o próximo desafio…