The language barrier

julho 27th, 2006

Acabo de escrever um novo post no meu blog em inglês sobre a barreira do idioma que impede que muitos desenvolvedores talentos possam mostrar seu verdadeiro potencial. Confiram!

Aceito na JSR 303

julho 18th, 2006

Fui aceito no Expert Group da JSR 303 – Bean Validation. Desta vez, o objetivo é definir anotações e uma API para permitir a validação de JavaBeans, independente da camada em que sejam usados. Este é um assunto em que planejava me ocupar de qualquer forma, já que o Commons Validator, da Apache, possui uma série de limitações e precisava implementar alguma solução melhor para o genesis. Assim que puder, divulgo mais informações…

Resolvendo ORA-01000: maximum open cursors exceeded

julho 4th, 2006

Junto com o clássico OutOfMemoryError, este é um dos erros que mais aparece em sistemas que usam Oracle. Resolver esse problema não é difícil, como vou explicar aqui.

Este erro é causado por Connections, Statements e ResultSets deixados abertos. Normalmente temos algo como:

Connection con = //obtem conexao;
PreparedStatement ps = con.prepareStatement("SELECT ...");
ResultSet rs = ps.executeQuery();

while (rs.next()) {
   // ...
}

rs.close();
ps.close();
con.close();

Note que eu estou sendo otimista, supondo que PreparedStatements sejam usados ao invés de concatenar Strings para executar consultas e que o método close() nas três instâncias esteja pelo menos no código. Se você não fez nem isso, corra atrás. :-)

O bug é que, quando ocorre uma exceção, durante o processamento do select, nenhum dos três objetos é fechado. Então, a primeira solução é colocar o fechamento num bloco try/finally, como mostrado abaixo:

Connection con = null;
PreparedStatement ps = null;
ResultSet rs =null;

try {
   con = //obtem conexao;
   ps = con.prepareStatement("SELECT ...");
   rs = ps.executeQuery();

   while (rs.next()) {
      // ...
   }
} finally {
   if (rs != null) {
      rs.close();
   }

   if (ps != null) {
      ps.close();
   }

   if (con != null) {
      con.close();
   }
}

Tudo certo, né? Errado. Se ocorrer uma exceção ao fechar rs, ps e con nunca serão fechados. Então a melhor solução seria:

Connection con = null;
PreparedStatement ps = null;
ResultSet rs =null;

try {
   con = //obtem conexao;
   ps = con.prepareStatement("SELECT ...");
   rs = ps.executeQuery();

   while (rs.next()) {
      // ...
   }
} finally {
   if (rs != null) {
      try {
          rs.close();
      } catch (SQLException sqe) {
          // logar excecao
      } catch (RuntimeException re) {
          // logar excecao
      }
   }

   if (ps != null) {
      try {
          ps.close();
      } catch (SQLException sqe) {
          // logar excecao
      } catch (RuntimeException re) {
          // logar excecao
      }
   }

   if (con != null) {
      try {
          con.close();
      } catch (SQLException sqe) {
          // logar excecao
      } catch (RuntimeException re) {
          // logar excecao
      }
   }
}

Ok, você vai me dizer, muito legal isso. Mas meu projeto tem milhares de classes e eu não sei onde eu esqueci de fazer isso. Ou, ainda, eu uso um framework O/RM ou JDBC que deveria resolver este problema pra mim. Bem, nesse caso, você tem como descobrir qual a instrução SQL do(s) cursor(es) que está(ão) abertos. Primeiro, você precisa do SID das sessões abertas pela sua aplicação, que você pode obter assim:

select o.sid, osuser, machine,
count(*) num_curs
from v$open_cursor o, v$session s
where o.sid=s.sid
group by o.sid, osuser, machine
order by num_curs;

Se estiver usando connection pool e um máquina só, pode-se usar estes dados para filtrar os resultados:

select o.sid, osuser, machine,
count(*) num_curs
from v$open_cursor o, v$session s
where o.sid=s.sid and
user_name = 'USUARIO_BANCO' and
machine = 'NOME_MAQUINA_APLICACAO'
group by o.sid, osuser, machine
order by  o.sid;

Utilize os SIDs na seguinte query:

select q.sql_text, count(*)
from v$open_cursor o, v$sql q
where q.hash_value=o.hash_value and o.sid IN (<SIDs>)
group by q.sql_text
order by 2;

Se o framework ou servidor de aplicações que você utiliza faz cache de statements (Hibernate e JBoss fazem, por default), é normal que exista um certo número de cursores com o mesmo SQL em aberto. Suspeite daquelas instruções que tenham um número de cursores muito maior que as outras e investigue de onde elas vêm. E se sua aplicação usa PL/SQL, saiba que as instruções contidas nas suas procedures/functions são contadas individualmente como cursores. Espero ter ajudado ;-)

JustJava 2006 – Prazo para submissao de palestras estendido!

junho 27th, 2006

O prazo para submissão de palestras para o JustJava 2006 foi estendido até esta sexta-feira. Não perca a oportunidade de palestrar no maior evento Java do Brasil!

Javadoc em pt-BR !

junho 26th, 2006

Ótima notícia. A primeira versão do Javadoc do Java 5 em português do Brasil está disponível online. Parabéns à equipe do projeto de tradução. Embora a documentação das APIs do Java SE seja apenas a ponta do iceberg do problema muito maior – o acesso completo às informações técnicas necessárias para aqueles que não sabem ler inglês -, essa ponta já é um enorme obstáculo vencido.

Aceito na JSR 296

junho 23rd, 2006

Fui aceito no Expert Group da JSR 296 – Swing Application Framework. A idéia é prover um framework minimalista para desenvolvimento Swing e algumas idéias estão muito alinhadas com o genesis, como uma anotação @Action. Espero que o genesis possa cooperar com essa JSR ou se tornar uma implementação open-source dela, assim como aconteceu com o Hibernate. Vamos ver :-D

CAPTCHAs e acessibilidade

junho 23rd, 2006

Com o spam e as ameaças de segurança crescendo diariamente, algumas medidas têm sido adotadas pela maioria dos sites para que estes se protejam. Contudo, muitos usuários “válidos” não podem acessar estes sites devido aos dispositivos de defesa implementados.

Um dos melhores exemplos disso é o CAPTCHA, que a maioria das pessoas conhece e não sabe o nome. CAPTCHA é uma sigla que significa Completely Automated Public Turing test to tell Computers and Humans Apart, i.e., Teste de Turing Completamente Automatizado para Diferenciar Computadores e Humanos. A implementação mais comum – e problemática – é perguntar ao usuário o que está escrito numa imagem, que normalmente está distorcida. O objetivo é impedir que “robôs” escritos para mandar spam não consigam ser bem-sucedidos, mas não é isso que acontece, na verdade.

A questão é que este tipo de CAPTCHA, além de poder ser burlado, simplesmente barra a entrada de diversos usuários nos sites. Sim, pessoas com deficiência visual, como (quase) cegos, daltônicos e afins, usam – e precisam usar – a web diariamente. Para isso, contam com o auxílio de softwares – e de hardware especial, em alguns casos -, que conseguem interpretar textos em páginas e aplicativos, emitindo sons ou algum outro tipo de sinal compreensível pelo deficiente. E se você acha que estas pessoas já não conseguem fazer muita coisa com o micro, é porque você nunca viu como os softwares bem implementados funcionam. Eu já conheci pessoalmente alguns indivíduos com este tipo de problema que eram analistas e desenvolvedores – e muito bons, por sinal.

Existem algumas alternativas, como CAPTCHAs baseados na interpretação de sons ou de instruções textuais, mas que podem limitar as pessoas que possuem dois tipos de deficiência, por exemplo. Por isso, a melhor idéia é não utilizar CAPTCHAs se possível ou prover o maior número de opções possível, disponibilizando ainda uma forma de um usuário deficiente que porventura não consiga acessar o seu site ou aplicativo possa avisá-lo disso. Os deficientes agradecem :-)

Como não liderar geeks

junho 21st, 2006

Sensacional post sobre como não liderar geeks, embora eu ache que se aplique a qualquer pessoa técnica. Dica do Bruno Borges, vulgo miojo, mais um consultor da Summa.

Instalando automaticamente o Java WebStart

junho 14th, 2006

Foi publicado um artigo sobre como detectar e instalar automaticamente o Java WebStart no site da Sun. Essa funcionalidade está disponível desde o Java 5, mas é desconhecida da grande maioria.

Embora o JWS torne possível a fácil distribuição de novas versões da aplicação, a própria instalação do JWS é um desafio em ambientes não controlados. As técnicas descritas no artigo tornam isto mais fácil.

Usando um SecurityManager sem definir permissoes

junho 13th, 2006

Para se trabalhar com RMI no cliente usando carregamento dinâmico de classes, é necessário ter um SecurityManager definido na JVM. Todo mundo que já usou (ou brincou) com RMI sabe disso, mas também sabe do inconveniente que é ter de definir um arquivo de permissões e ter que configurá-lo no launcher da aplicação só para fazer testes.

Tive que fazer um teste com isso ontem, mas me ocorreu uma idéia: se as exceções de segurança são lançadas pelo próprio SecurityManager, o que me impediria de fazer isso:

System.setSecurityManager(new RMISecurityManager() {
   public void checkPermission(Permission p) {
   }
});

somente para testes?

Bem, isso funciona e é bastante prático, mas não faça isso numa aplicação de produção. Mais uma lição aprendida ;-)