Archive for agosto, 2006

Vale a pena abstrair?

quarta-feira, agosto 30th, 2006

Um post do Paulo sobre abstração de DAOs me fez lembrar de escrever sobre este assunto.

Durante muitos anos, eu fui adepto do desacoplamento e da abstração a todo custo. Eram factories com interface, que eram implementadas por uma abstract factory, que achava a própria implementação delas através de properties (sim, isso foi antes dos containers de DI, era uma boa prática pra época) e que sempre produziam interfaces e por aí vai. Os DAOs tinham interfaces e suas factories também e tudo era lindo.

Isso foi há pouco mais de 5 anos e hoje faz uns 2 anos e meio que parei com isso. Por quê? Bem, a análise do que eu havia feito naquela época me mostrou que ter esse princípio como norma geral é uma péssima idéia. Vou tomar como exemplo o casos dos DAOs.

No começo, isso fazia algum sentido, porque a idéia dos DAOs era permitir que você trocasse de banco de dados. Se seu sistema usava só JDBC puro e não havia triggers e afins no banco, no caso da remota possibilidade de trocar de banco, havia alguma vantagem nisso. Agora, quantos de vocês já viram isso acontecer? Mesmo quando acontece, dependendo das diferenças de funcionalidades utilizadas dos dois bancos e da forma como a abstração foi implementada, uma reescrita de tudo acaba sendo necessária. Além disso, nos grande maioria dos casos em que a mudança não ocorre, o quanto isso atrasa o ciclo de desenvolvimento? E aquele monte de interfaces inúteis, que só servem para deixar seu deploy mais lento e seu code completion mais poluído? Fora o fato de fazer os juniors entenderem por que isso é necessário. Obviamente, eu não estou considerando aqui o caso de produtos “genéricos” ou ferramentas que realmente tem que funcionar com mais de um banco desde o primeiro dia e, mesmo assim, estou supondo que o Hibernate ainda não existia.

Quando o Hibernate e seus semelhantes surgiram, aí as coisas passaram a fazer muito menos sentido. O problema principal que era poder usar outros bancos foi coberto. Aí as pessoas começaram a raciocinar: e se eu quiser trocar o Hibernate? Bem, se você quiser fazer isso, provavelmente sua aplicação inteira vai acabar mudando, a menos que você vá mudar para EJB 3 (o que, por sinal, não faz sentido na maioria dos casos). Escondendo o Hibernate você perde acesso a funcionalidades avançadas e outras delas “vazam” pelo código, como o uso de Transfer Objects para evitar a carga lazy de algumas propriedades e coisas do tipo.

O fato é que nos últimos anos todos nós ouvimos que devemos fazer a coisa mais simples que funciona. E os sistemas não funcionariam sem essa parafernalha toda? Então, por que estamos construindo algo de que não sabemos se vamos precisar? Da próxima vez que achar que precisa de interfaces ou abstrair uma implementação, verifique o custo-benefíio antes de sair codificando.

Duvidas sobre o binding Swing do genesis

quinta-feira, agosto 24th, 2006

Começaram a aparecer dúvidas sobre o post que fiz a respeito do binding Swing suportado pelo genesis. Aproveito para responder aqui as perguntas do Tetsuo, para que fique mais visível para quem visita o blog:

Há como fazer o binding com outras propriedades dos componentes, como ‘enabled’, ’selected’? Por exemplo chamar ‘form.setNome_enabled(false)’ para desabilitar um textfield. Isto seria perfeito para fazer ’subcutaneous testing’ da lógica das telas sem ter que instanciar componente visual algum, já que toda a lógica poderia ser feita diretamente no bean.

Isso é suportado pelo genesis há cerca de dois anos, de forma direta. Uma coisa coisa é o suporte condicional do genesis, que permite controlar a visibilidade e habilitação de widgets, além de permitir a limpeza dos dados e a chamada automática de métodos sob alguma condição.

Por exemplo, se você tivesse um form onde a pessoa deve inserir o nome do cônjuge (i.e., esposo/esposa), mas quer que esse campo esteja disponível somente quando o indivíduo for casado, pode-se fazer:


@Form
public class ExemploEnabledWhenForm {
   private boolean casado;
   private String nomeConjuge;

   public boolean isCasado() {
      return casado;
   }

   public void setCasado(boolean casado) {
      this.casado = casado;
   }

   @EnabledWhen("form.casado")
   public String getNomeConjuge() {
      return nomeConjuge;
   }

   public void setNomeConjuge(String nomeConjuge) {
      this.nomeConjuge = nomeConjuge;
   }
}

Esse exemplo encontra-se na documentação do genesis.

Outra coisa é trabalhar com seleções e com o populamento de widgets como tabelas, comboboxes e listas. Para isso, o genesis provê a anotação @DataProvider, que permite definir qual propriedade manterá a opção selecionada, como no exemplo abaixo, também retirado da documentação do projeto:


@Form
public class ExemploDataProviderForm {
   private Estado estado;

   public Estado getEstado() {
      return estado;
   }

   public void setEstado(Estado estado) {
      this.estado = estado;
   }

   @DataProvider(objectField="estado")
   public List populaEstados() {
      // retorna uma List contendo instâncias de Estado
   }

   // ...
}

Uma outra dúvida: quais as dependências mínimas para rodar a aplicação no cliente? Sem banco, sem thinlet, só swing+genesis. Tenho que embutir o runtime do werkz? jxpath? beanutils?

Bem, isso depende muito das funcionalidades que você usar. A melhor maneira é verificar no desktop_build.xml da versão que você baixar quais jars do path run.standard.classpath você precisa. Ê fácil deduzir quais uma vez que você tenha lido a documentação do genesis.

Aproveito para lembrar que o genesis possui uma lista de discussão em português em usuarios@genesis.dev.java.net, onde outras pessoas que usam o framework poderão responder e tirar proveito das suas perguntas (e das nossas respostas). Para assinar, basta mandar um email para usuarios-subscribe@genesis.dev.java.net e responder a mensagem que o servidor lhe enviará.

Suporte SWT no HEAD

quarta-feira, agosto 23rd, 2006

Depois da release, foi integrado no HEAD suporte a SWT no binding. Pode-se usar o binding assim:


Shell shell = new Shell(SWT.TITLE | SWT.CLOSE);
// configure o shell
SwtBinder  binder = new SwtBinder(shell, form = new UserListForm(), this);
binder.bind();

O primeiro parâmetro deve ser um org.eclipse.swt.widgets.Composite. O exemplo useradmin já foi atualizado também para incluir a versão SWT. Para os mais curiosos, segue um link para a tela de listagem de usuários do exemplo. Agora estamos atualizando a doc para cobrir o SWT. Aguardem mais novidades.

genesis 3.0-EA3: suporte Swing e Java 5

terça-feira, agosto 22nd, 2006

Finalmente, após oito meses de trabalho e contratempos, está disponível a versão 3.0-EA3 do genesis. Toda a documentação foi reformulada e há versões em inglês e português.

As novidades mais legais dessa versão são o binding Swing e o suporte a anotações do Java 5 (que também funcionam com Java 1.4 da mesma maneira). O conceito do binding no genesis é único porque você pode trabalhar com JavaBeans sem amarrar a lógica da sua aplicação a API gráfica (Swing e Thinlet, no momento). Por exemplo, pode-se codificar um form assim :


@Form
public class LoginForm {
   private String usuario;
   private String senha;

   public String getUsuario() {
      return usuario;
   }

   public void setUsuario(String usuario) {
      this.usuario = usuario;
   }

   public String getSenha() {
      return senha;
   }

   public void setSenha(String senha) {
      this.senha = senha;
   }

   @Action
   public void login() {
      System.out.println(usuario);
      System.out.println(senha);
   }

   @Action
   public void limpar() {
      setUsuario(null);
      setSenha(null);
   }
}

E ligá-lo a uma interface gráfica em Swing assim:


@ViewHandler
public class LoginSwingView extends JDialog {
   public LoginSwingView() {
      super(new JFrame(), "Login");
      initComponents();

      SwingBinder binder = new SwingBinder(this, new LoginForm());
      binder.bind();
   }

   private void initComponents() {
      getContentPane().setLayout(new GridLayout(2, 1));

      JPanel panelDados = new JPanel();
      panelDados.setLayout(new GridLayout(2, 2, 5, 5));

      JLabel labelUsuario = new JLabel();
      labelUsuario.setText("Usuário");
      panelDados.add(labelUsuario);

      JTextField usuario = new JTextField();
      usuario.setName("usuario");
      panelDados.add(usuario);

      JLabel labelSenha = new JLabel();
      labelSenha.setText("Senha");
      panelDados.add(labelSenha);

      JPasswordField senha = new JPasswordField();
      senha.setName("senha");
      panelDados.add(senha);

      getContentPane().add(panelDados);

      JPanel panelBotoes = new JPanel();

      JButton login = new JButton();
      login.setText("Login");
      login.setName("login");
      panelBotoes.add(login);

      JButton limpar = new JButton();
      limpar.setText("Limpar");
      limpar.setName("limpar");
      panelBotoes.add(limpar);

      getContentPane().add(panelBotoes);

      pack();

      setLocationRelativeTo(null);

      addWindowListener(new WindowAdapter() {
         public void windowClosing(WindowEvent e) {
            System.exit(0);
         }
      });
   }

   public static void main(String args[]) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            new LoginSwingView().setVisible(true);
         }
      });
   }
}

Ou seja, diferente de outros frameworks, o genesis não requer o uso de componentes proprietários e você pode usar tranquilamente o Matisse ou o VEP para desenhar suas telas, bastando setar o name dos componentes de acordo com o seu form. Mais detalhes sobre como o binding funciona podem ser encontrados na documentação.

Closures em Java

sexta-feira, agosto 18th, 2006

Alguns dos indivíduos mais inteligentes da comunidade, como Gilad Bracha, Neal Gafter e James Gosling escreveram uma proposta para adicionar closures a linguagem Java, conforme postado pelo Peter von der Ahé, que também participou da escrita do PDF. Espero que criem logo essa JSR… :-)

Efeitos colaterais da mudanca de class literal no Java 5

quinta-feira, agosto 17th, 2006

Acabo de postar no meu blog no java.net sobre a mudança no tratamento de class literals no Java 5 e seus efeitos colaterais nocivos. Basicamente, o uso de uma expressão como MinhaClasse.class não garante que a classe foi inicializada, i.e., que seu bloco static e membros estáticos foram inicializados. Confira!

Convertendo Strings para objetos

quarta-feira, agosto 9th, 2006

Aproveitando esse blog para fazer uma pesquisa de opinião: como vocês fazem parsing e/ou geram aqueles arquivos texto ou mensagens que tem até doc do Word do layout de tão complexos que são? Fazem na mão, usam uma API que retorna “tokens” convertidos ou o que?

Pergunto porque desenvolvi uma API pequena (7 classes so far) em que você configura o layout via xml e como mapear as propriedades para seus beans e ele te retorna um bean ou uma Collection. Ou seja, ao invés de fazer uma leitura nojentinha com posições, conversões e afins, você carrega o xml com um método, cria um objeto capaz de ler o layout, chama um parse(Reader) e tem a sua NotaFiscal com instâncias de ItemNotaFiscal e instâncias de outras classes do seu domínio configuradas.

Se houver interesse, pretendo (não é promessa!) liberar como open-source. Aguardo o feedback :-)

Quando o Google te atrapalha :-)

quarta-feira, agosto 9th, 2006

Engraçado como é ruim ter um post bem colocado no Google para um assunto genérico. Se você pesquisar por como fazer cronogramas, o meu post “A arte de fazer cronogramas” aparece em segundo ou terceiro (varia de vez em quando). E, com isso, como vocês podem ver nos comentários do post, aparecem pessoas querendo saber como montar escalas ou coisa do gênero :-)

Quando um OutOfMemoryError nao e falta de memoria…

sexta-feira, agosto 4th, 2006

Hoje de manhã, Bruno Borges, co-worker da Summa, me mostrou um site duma empresa famosa que exibia um stack trace com OutOfMemoryError. Contudo, a mensagem exibida me lembrou de um problema que solucionei no ano passado e que mostra que nem sempre um OutOfMemoryError tem a ver com falta de memória propriamente dita:


java.lang.OutOfMemoryError: unable to create new native thread

Normalmente esse erro é causado por um problema um tanto quanto raro em Java: thread leaks. Isso pode acontecer se threads auxiliares são criadas com frequência e se elas não forem corretamente encerradas. Geralmente as threads encontram-se em wait(), mas a condição que deveria acordá-las nunca mais vai ocorrer.

A melhor forma de resolver esse problema é utilizar um profiler para identificar onde as threads que permanecem ativas são criadas e depois utilizar um debugger para investigar por que elas não são encerradas como esperado. Taí a dica ;-)

Resolvendo problemas de lock acessando email

quarta-feira, agosto 2nd, 2006

Resolvi esses dias um problema interessante. Num determinado projeto, existem serviços de integração rodando num JBoss e alguns deles são baseados no processamento de arquivos anexados em emails. No entanto, as vezes essas threads simplesmente paravam de emitir log e, como o número de transações do sistema é alto e o log é rotacionado após um certo tamanho, não era possível determinar a causa.

Antes mesmo que eu reconfigurasse o log para poder diagnosticar o que estava acontecendo, percebi que a thread congelava após tentar efetuar login no servidor SMTP. Verifiquei a API em busca de uma maneira padrão de realizar as operações com timeout, mas não encontrei. Contudo, a implementação da Sun possui duas propriedades que permitem controlar o timeout do acesso a uma caixa de mensagens POP3: mail.pop3.connectiontimeout e mail.pop3.timeout, conforme documentado aqui.

A parte “assustadora” dessa correção foi descobrir que o padrão dessas propriedades é nunca causar timeout. Logo, as threads iam ficar congeladas até o próximo deploy/restart/reboot. Mais uma lição aprendida.