Para aqueles que desejam entrar no mundo JEE Web e precisam de um ponta pé inicial, nada melhor que uma apresentação curta e grossa com uma visão simplificada do assunto.

Registre sua dúvida ou opinião sobre o mini-workshop http://fabricioepa.googlepages.com/jeequickstart-webtier

JBean File Storage

Agosto 20, 2008

Após um bomtempo sem publicar devido a minha dedicação ao projeto “Easy Accept Web” (mas isso fica para outro post), anuncio um novo componente para auxílio a manipulação e associação de caminhos de arquivos/diretórios com POJOS. Muito útil quando sua aplicação trabalha em conjunto com framework ORM (Hibernate like), onde se deseja por exemplo associar uma entidade com um determinado diretório de arquivos (para upload/download por exemplo), seu nome por enquanto é :

JBean File Storage

O componente ainda apesar de implementado ainda não está disponibilizado publicamente pois ainda necessita de revisão, documentação e algumas possíveis melhorias embora esta versão esteja presente em 2 projetos já em produção.

A idéia chave foi associar um caminho a um tipo java, eu mentiria se dissesse que a idéia foi completamente minha pois me baseei numa solução proposta por Luciano Logrado ao trabalharmos juntos em um outro projeto, digamos que eu incrementei a idéia para suportar polimorfismo e com isto será possível:

  • Associar um caminho a uma tipo (classe ou interface) e seus subtipos também estarão associados
  • Usar uma Expression Language (semelhante a do JSP 2.0) mínima para usar caminhos dinâmicos
  • Herdar um caminho dum outro POJO
  • Manter na hierarquia de diretórios a hierarquia nas associações dos POJOS

Estes dois últimos são importantes caso por exemplo existam dois POJO’s com relacionamento de pai/filho e ao excluir o pai os filhos devem ser excluídos:

Cliente ->* Conta

Desta forma o caminho de arquivos do cliente poderia ser:

$/Cliente_99

/Conta_33

MeuExtrato1.txt

/Conta_34

Extrato2008.txt

Com uma operação de exclusão do diretório do cliente haverá propagação no sistema de arquivo para os subdiretórios, ou seja, as contas. Além de manter a organização dos arquivos numa hierarquia de pastas de fácil entendimento visual e manipulação.

Veja detalhes do uso:

Leia o resto deste post »

Olá pessoal, estou publicando em minha página pessoal uma apresentação rápida que fiz sobre alguns fundamentos para desenvolvimento de projetos em equipes de maneira colaborativa.

Em suporte a este propósito utilizei os recursos do Subversion (SVN), por ser open-source e de livre distribuição, além de integrar novos recursos em relação ao famoso sistema de controle de versão CVS.

O foco do estudo foi relacionado aos usuários de um repositório, como também o recurso de integração com Java/Eclipse IDE.

Enjoy it =) !

Fundamentos do Controle de Versão e SVN

O referido erro também ocorre com mesagem semelhante:

Illegal attempt to associate a collection with two open sessions

Causa:

O erro é provocado quando um hiberObject associado a uma sessão tenta ser alterado por outra sessão do hibernate.

Solução:

Esqueça os métodos session.get/load/update você pode conseguir o mesmo efeito com session.refresh/merge.

Contexto do Problema:

Isso acontece devido a associação de um objeto gerenciado pelo hibernate (hiberOjbect) com a sessão que o carrega. Tecnicamente se um objeto foi carregado por uma sessão, até que o objeto seja desconectado (passando para o estado Detached) ele permanece associado a ela. O que pode ser feito invocando os métodos:


//Apenas se os relacionamentos do hiberOject
//que possuem cascade="evict" eles serão também desconectados
session.evict(hiberObject);
session.clear();
session.close();

Se outra sessão tentar alterá-lo sem ele estar destacado da última sessão, o erro será lançado.

Problemas Típicos:

1. 0 Quando se deseja usar um objeto em outra sessão sem fechar a primeira use o evict, lembrando que se os relacionamentos não possuirem cascade=”evict” o efeito do método não servirá para eles e provavelmente o mesmo erro ocorrerá para o “proxy” dos relcionamentos (que podem ser coleções causando Illegal attempt to associate a collection with two open sessions) .

2.0 Em Aplicações WEB Java tipicamente carregamos um hiberObject e o setamos em alguma variável do contexto web (request, session, sevletContext), após algum uso pela camada de apresentação fazemos outra chamada a camada do hibernate usando esse mesmo hiberObject onde ocorre falha com o dito erro. Neste caso temos dois pontos:

  • Se a sessão que o carregou anterior ainda estava aberta você recai no erro 1.0
  • Caso a velha sessão já tenha morrido, o seu hiberObject não está mais conectado a mesma, porém os seus relacionamentos ainda podem ter algum “proxy” para a velha sessão. Nesse caso é mais simples usar um dos metódos:

//Caso queira reconectar e recarregar
//no seu objeto os dados do banco
session.refresh(detachedHiberObject);

//Caso queira reconectar o objeto fazer um update no banco
//de dados com as novas informações setadas no objeto
connectedHiberObject = session.merge(detachedHiberObject);

O merge/refresh tem funções diferentes, mas ambos reconectam o objeto a uma sessão, seja a mesma ou não, sem provocar erro.

AutoBox/UnBox é a conversão de forma automática entre objetos e tipos primitivos, dicas:

  • Use somente quando houver “descasamento” entre tipos primitivos e objetos empacotadores
  • Não abuse: um Integer não substitui um int (a performance é pior)
  • Cuidado, unboxing com objetos null lança exceção

Probolema:

  • Generics não trabalha com tipos primitivos

Após executar o seguinte teste de performance, constatou-se uma diferênça em média de 25% com uso do autobox. O algoritimo de teste baseou-se em comparar as duas execuções:

  1. faça 1000 vezes
    • varra um ArrayList<Integer>, capturando o valor sem fazer autobox
  2. faça 1000 vezes
    • varra um ArrayList<Integer> usando enhanced-loop fazendo automaticamente unbox para int, capturando o valor e fazendo autbox novamente para um objeto Integer.

Resultado da execução:

___________________________________

autoBoxUnBox()…java.lang.NullPointerException
Sem autoboxing = 6531ms
Com autobox/unbox= 8734ms
Diferença de 26%

___________________________________

Conclusão:

Se sua aplicação é “Performance Driven”, estruturas como o IntHashMap visto no blog anterior entre outras coleções da API que não trabalhem com Generics podem ser a melhor saída para escovar milisegundos de processamento.

Abaixo segue a classe usada no teste:

Leia o resto deste post »

O java.util.HashMap como o próprio nome diz utiliza-se do hash code do objeto “chave” para mapear as entradas do mapa. Funciona basicamente assim:


HashMap map = new HashMap();
map.put("matricula.12345", "Fabricio");

A essência do funcionamento do método put() é :

public void put(Object key, Object value){
 int hash = geraHashCode(key);
 int  posicao = geraPosicaoNaTabela(hash);
 adicionaNaTabelaDeEntradas(posicao, value);
}

Quando porém estamos trabalhando com um mapa onde as chaves já são inteiros muitas operações do mapa podem ser simplificadas, solução: IntHashMap.
Algumas coporações como a Oracle, Apache, etc… possuem em suas “foundation classes” alguma implementações que trababalhem desta forma para obterem melhor performance. Afim de comparar o desempenho com HashMap executei dois teste simples

  1. Povoando o mapa com 1000000 chaves sequenciais (Puts)
  2. Recuperando todos os objetos do mapa (Gets), utilizou-se string como objeto para evitar autobox/unbox.

O resultado foram ganhos em média de 70% para os puts, e 30% para os gets efetuados no mapa. Veja o resultado de uma excução:

Main.testSunMap()…
Main.testIntMap()…

‘Puts’ execution time (milliseconds):
sunMap: 3328 intMap: 922, Percentage of gain: 73%

‘Gets’ execution time (milliseconds):
sunMap: 234 intMap: 140, Percentage of gain: 41%

Conclusão
Se suas chaves são inteiros use IntHashMap. Os resultados foram obtidos usando java 5, o mesmo exemplo foi executado com 1.4 e obteve em média os mesmos valores, no entanto se em sua aplicação você não tomar os devidos cuidados no uso do autoboxing/unboxing de valores primitivos para objetos, a diferença de ganho vai ser ainda em muito melhor para o IntHashMap devido a perda de performance executando o boxing.

Leia o resto deste post »

Você já se perguntou como algumas IDE’s configuram o classpath da sua aplicação dinamicamente em tempo de execução?

Aqui vai uma solução técnica alternativa (that means GDD – Gambiarra Driven Development :P ).

A classe java.net.URLClassLoader é o classloader default do sistema para carregar o jars do classpath quando o aplicativo java é iniciado.
Uma maneira de adicionar jars extras seria invocando no classloder o método addURL(URL url), o que faz o jar localizado por está URL ser carregado na JVM em execuçao. O problema é que este método é protegido (protected) e portanto, a menos que você extenda o URLClassLoader, sobreescreva o método mudando a visibilidade para público e configure seu classloder como default do sistema você não poderá invocá-lo na instância do classloader.
Mas como nem tudo é impossível na computação, você pode contornar o problema usando reflexão e acessando o método protegido. Para isso basta recuperar o método “addURL” declarado e invocar setAccessible(true).

A classe abaixo mostra como você mata os dois coelhos com uma única cajadada:


public class ClassPathHacker {

private static final Class[] parameters = new Class[]{URL.class};

public static void addFile(String s) throws IOException {
 File f = new File(s);
 addFile(f);
}//end method

public static void addFile(File f) throws IOException {
 addURL(f.toURL());
}//end method

public static void addURL(URL u) throws IOException {

	URLClassLoader sysloader = (URLClassLoader)ClassLoader.getSystemClassLoader();
 Class sysclass = URLClassLoader.class;

	try {
 	Method method = sysclass.getDeclaredMethod("addURL",parameters);
 	method.setAccessible(true);
 	method.invoke(sysloader,new Object[]{ u });
 } catch (Throwable t) {
 	t.printStackTrace();
 	throw new IOException("Error, could not add URL to system classloader");
 }//end try catch

}//end method

}//end class

Baseado no Fórum:

http://forum.java.sun.com/thread.jsp?forum=32&thread=300557&tstart=0&trange=15

Proxy4jdbc3 – The Proxy for JDBC 3

É um componente de software desenvolvido para interceptar o SQL executado por um aplicativo Java através das chamadas sobre a JDBC. Uma solução rápida e simples para quem deseja depurar, fazer logging ou perfilar a execução do SQL sem ter que modificar a arquitetura da aplicação.

Para usá-lo primeiro são necessários apenas 4 passos:

  1. É necessário saber qual o componente provedor das conexões com os dados da sua aplicação, exemplo: Driver Manager, DataSource, algum Componente de Pooling, etc…,
  2. Conhecido o provedor, então deve se escolher o componente correspondente de Proxy. Obs.: Você também pode estender ou criar seu componente customizado.
  3. Implementar o Proxy criando a solução vendor-especifc, i.e, especifica do seu SGBD. No pacote proxy4jdbc3.solutions.* você encontrará soluções-exemplo.
  4. Substitua a referência ao provedor de conexão pela referência ao Proxy.


O componente foi desenvolvido sobre a JDBC 3, e está ainda em fase de teste (beta).

Maiores informações em: http://proxy4jdbc3.sourceforge.net/