domingo, 14 de outubro de 2007

Construindo uma janela de consulta genérica - poder de Java Reflection + Annotations

Olá,

Hoje estarei comentando sobre algo que fiz semana passada em um sistema web que estou desenvolvendo. Por se tratar de um ERP cuja aplicação é Form-based, vi a necessidade de construir um sistema de consulta genérica, onde dada uma classe POJO qualquer, ele montasse uma janela de consulta baseada em todos os atributos da mesma. Para implementar tal funcionalidade, mais uma vez o grande poderio da tecnologia Java garantiu que isso fosse implementado de forma elegante e prática.
Basicamente, o que desenvolvi foi um método que retorna uma lista de parâmetros de consulta para que a tela genérica possa ser renderizada. Para descobrir os atributos da classe POJO passada por parâmetro ao método utilizei Java Reflection e Annotations.
Consegui implementar isso, graças a utilização de uma DaoFactory genérica, que inclusive detalhei em um post anterior aqui do blog. Com ela, dada uma POJO qualquer, pode-se obter sua respectiva Dao, e com sua dao, executar o método retornarTodos().

O código deste método postei em um pastebin externo para facilitar a leitura. Deixei o código bem comentado, por isso acho que está auto explicativo. O link é: http://fr.pastebin.ca/736916
Só peço desculpas pela identação do código-fonte, no eclipse está perfeita, porém ao transferir para o textarea do pastebin ficou errado.

Se achar interessante e tiver alguma dúvida, por favor não hesite em enviar um recado ou e-mail.

[]'s

terça-feira, 11 de setembro de 2007

Herança "simples" com Hibernate

Olá pessoas,

Hoje comentarei sobre mapeamento de herança com o Hibernate através de annotations.
Conforme documentações e exemplos que encontramos por ai, sabemos que existem 3 tipos de mapeamento de herança:
  • Joined - Usado quando quer criar uma tabela para a classe genérica e demais tabelas que derivam dela, por exemplo: Pessoa, Pessoa Física, Pessoa Jurídica. Neste caso teriam 3 tabelas.
  • Single Table - Usado quando quer criar uma única tabela para armazenar os dados. No caso da Pessoa, Pessoa Física, Pessoa Jurídica, teria uma única tabela com todas os atributos.
  • Table Per Class - Usado quando quer criar uma tabela para cada subclasse. No caso da Pessoa, Pessoa Física, Pessoa Jurídica, teriam duas tabelas: Pessoa Fisica e Pessoa Juridica, os atributos da classe Pessoa seriam replicados nas duas tabelas.
A questão é que para utilizar estes tipos de mapeamento de herança é necessário anotar a classe mais genérica com @Entiety, ou seja deve ser uma entidade gerenciada pelo hibernate. Se uma classe é uma Entiety, obrigatoriamente ela deve conter um atributo anotado com @Id.
Se for utilizar o mapeamento de herança do tipo Table Per Class, não é possível determinar o atributo anotado com @Id como @GeneratedValue do tipo AUTO ou IDENTITY. Em alguns casos isso é um problema, para explicar melhor vou citar um exemplo:

Uma classe abstrata Modelo possui alguns atributos incluindo um ID.
Uma classe concreta ModeloCorporativo deriva de Modelo e possui mais alguns atributos.
A nível de base de dados, a tabela do modelo corporativo deve possuir todos os atributos da classe modelo e mais os seus atributos. Dentre estes atributos, está o ID, este deve ser gerado automaticamente pelo banco (via sequence, via auto_increment, etc)

A primeira vista, parece que o melhor tipo de mapeamento para esta herança é usar TablePerClass, mas dessa forma o atributo ID da classe abstrata não poderia estar anotado com @GeneratedValue(stategy=GenerationType.AUTO), pois isso é uma regra do hibernate (inclusive citada na documentação do hibernate annotations).
Então para resolver esta situação a forma mais indicada seria criar uma classe abstrata Modelo e anota-la com @MappedSuperclass, Este tipo de mapeamento de herança (que nunca aparece nos exemplos comuns de herança) não exige que a classe seja anotada com @Entiety, ou seja, o hibernate vai saber que esta classe só vai servir para "embutir" os atributos na tabela representada pelas subclasses.

O código ficaria assim:
@MappedSuperclass
public abstract class Modelo {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Integer id;

// outros atributos e getters e setters omitidos.
}

@Entity
public class ModeloCorporativo extends Modelo {
private String mascara;

// getters e setters omitidos
}

Desta forma a tabela ModeloCorporativo terá todos os atributos da classe Modelo e mais o seu atributo mascara. O importante é que o atributo ID herdado da classe Modelo pode ser anotado com @GeneratedValue(strategy=GenerationType.AUTO) e, de acordo com o banco de dados, terá seu id gerado automaticamente
Qualquer dúvida posta aí.
[]'s

quinta-feira, 30 de agosto de 2007

Fábrica de DAOs com Reflection

Olá a todos,
Sabemos que uma boa prática na modelagem de arquiteturas de software é a serapação das responsabilidades em camadas bem definidas. Uma destas camadas, talvez a que esteja mais relacionada com os recursos da infra-estrutura, é a de Persistencia dos dados. Para esta camada, é comum utilizar-se o pattern Data Access Object (DAO), que tem o objetivo de encapsular estes acessos aos recursos de armazenamento de dados (normalmente bancos de dados relacionais).
Além da separação de camadas, ouve-se muito falar que é importante manter um fraco acoplamento entre elas, tornando assim, mais independentes para futuras manutenções. Mas como garantir este fraco acoplamento? Segue um exemplo de utilização de uma DAO na camada de negócios:

ClienteDao dao = new ClienteDao(HibernateUtil.getSession());
dao.adiciona(cliente);

Implementando desta forma, caso seja necessário refatorar a classe de acesso a dados de Clientes, ou mesmo criar uma nova versão desta classe que não utilizasse hibernate, mas sim acesso direto via JDBC, o que teria que ser feito? Teríamos que criar uma nova classe, por exemplo: ClienteDaoJDBC e implementar todos os metodos novamente. Até aí tudo bem, nada problemático. Mas o problema está na utilização da classe, imagine que quem for cliente da classe ClienteDao terá que mudar o tipo da variável/atriuto para ClienteDaoJDBC. Aí que está o Alto acoplamento.
Para resolver isso é interessante criar interfaces para as DAOs, e então as classes ClienteDao e ClienteDaoJDBC implementariam estas interfaces. Quem fosse utilizar as DAOs usariam variável/atributo do tipo da interface: IFClienteDao por exemplo. Com isso já resolvemos o problema de ter que alterar o tipo da variável/atributo, mas ainda temos o alto acoplamento representado pela instrução new, conforme o código abaixo:

IFClienteDao dao = new ClienteDaoJDBC();

Para resolvermos o problema do alto acoplamento, devemos então criar uma Fábrica de Daos. Uma classe que fosse responsável por instanciar nossas DAOs e tirar esta responsabilidade do cliente das classes DAOs, assim o dia que for necessário alterar a DAO a ser usada, seria só alterar nesta Fábrica. Este conceito de Fábrica segue o pattern Factory definido pelo GoF.
Uma fábrica de Daos poderia ser uma singleton, pois não parece ser necessário ter mais de uma instancia desta classe, então o código ficaria:

public class DaoFactory {
// controle da singleton
private static DaoFactory factory = new DaoFactory();

// construtor private para não permitir instanciar a factory diretamente com new.
private DaoFactory() {}

// através deste método é que se obtem a fábrica.
// excepcionalmente este método recebe um atributo session pois este pode mudar
// durante o tempo de vida da aplicação se assim o hibernate desejar :D
public static DaoFactory getInstance(Session session) {
factory.setSession(session);
return factory;
}

public IFClienteDao getClienteDao() {
return new ClienteDaoJDBC();
}
}


Com esta fábrica é possível obter DAOs sem saber o que é preciso disponibilizar à ela, mais uma vez o papel de encapsulamento ajudando.
Mas ainda tem-se um pequeno problema, conforme o sistema for crescendo, muitas daos são criadas e muitos getXXXDao() vão sendo adicionados à fabrica. Quando ocorre estes métodos repetitivos, já é possível imaginar que poderia-se criar um getDao genérico que retornasse a Dao respectiva de uma classe persistente, por exemplo: "Quero que retorne a DAO referente a persistencia dos dados da classe Cliente", então a fábrica retornaria a ClienteDaoJDBC por exemplo. Para isso pode ser utilizado Java Reflection conforme o código a seguir:

private static DaoFactory factory = new DaoFactory();
private Session session;
private Map daoMap = new HashMap();

public Dao getDao(Class persistentClass) {
if (daoMap.containsKey(persistentClass)) {
return daoMap.get(persistentClass);
}
else {
if (persistentClass.isAnnotationPresent(DaoClass.class)) {
DaoClass daoClassInfo = (DaoClass)persistentClass.getAnnotation(DaoClass.class);
Dao dao = null;
try {
dao = daoClassInfo.value().getConstructor(Session.class, Class.class).newInstance(this.session, persistentClass);

if (daoClassInfo.singleton()) {
daoMap.put(persistentClass, dao);
}

return dao;
}
catch (Exception e) {
throw new DaoFactoryException("A classe Dao não pode ser instanciada. Erro: " + e.getMessage());
}
}
else {
throw new DaoFactoryException("Anotação @DaoClass ausente");
}
}
}
Não adicionei comentários neste código pois está bem legível. Espero que tenha sido útil.

[]'s

terça-feira, 21 de agosto de 2007

JBoss Rules (Drools)

Olá a todos,

Hoje estarei publicando apenas links de documentos que possuo aqui em casa sobre um framework da JBoss estou estudando atualmente, chamado JBoss Rules. Não vou me estender muito com explicações, pois ainda não tenho muitas certezas em relação à utilização deste framework. Mesmo sem essas certezas, continuo apostando que este JBoss Rules irá trazer muitos benefícios aos meus projetos, que buscam uma arquitetura orientada a serviços (SOA) e regras de negócios que podem mudar frequentemente. Também estou achando interessante a integração deste framework com o JBPM, que é um framework para trabalhar com Business Process Management em Java (WorkFlows avançados).

Em poucas palavras, o JBoss Rules é um framework Open Source para "externalizar" regras de negócio e/ou lógicas corporativas a fim de tornar o processo de manutenção e novas implementações mais rápido e seguro. Com essas regras externas, que podem ser escritas até em uma linguagem natural, qualquer stakeholder pode ler e interpretar, podendo assim garantir que elas realmente façam o que deveriam fazer.

Seguem os links:
- http://www.inf.furb.br/~gfronza/JBoss_Rules.pdf
- http://www.inf.furb.br/~gfronza/Apresentacao_JBoss_Rules.pdf


Mais informações sobre o framework podem ser encontrados no site da JBoss

Se tiverem interesse sobre o assunto podemos trocar ideias por e-mail ou IM.
[]'s

terça-feira, 14 de agosto de 2007

"AutoRelacionamento" com Hibernate

Hoje tive que implementar o menu de uma aplicação que estou fazendo e precisava de uma classe que representasse uma tabela mais ou menos assim:

CREATE TABLE sis_menu (
id bigint(20) NOT NULL,
acao varchar(255) default NULL,
chave varchar(255) NOT NULL,
idMenuPai bigint(20) default NULL,
PRIMARY KEY (`id`),
KEY `fk_id_pai` (`idMenuPai`),
FOREIGN KEY (`idMenuPai`) REFERENCES `sis_menu` (`id`)
)

Ou seja, tinha que fazer um relacionamento da tabela sis_menu com ela mesma para ter uma hierarquia dos itens do menu.

Para isso escrevi a seguinte classe POJO com anotações do hibernate:

@Entity
@Table(name="sis_menu")
public class Menu {

@Id @GeneratedValue
private Long id;

@NotEmpty(message="menu.chaveEmpty")
private String chave;

private String acao;

@OneToMany(mappedBy="menuPai", fetch=FetchType.LAZY)
@Cascade(CascadeType.ALL)
private List\ subMenus;

@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name="idMenuPai", insertable=true, updatable=true)
@Fetch(FetchMode.JOIN)
@Cascade(CascadeType.SAVE_UPDATE)
private Menu menuPai;

// getters e setters
}

Não cheguei nesta solução sozinho, o que fiz foi googlear até achar isso. Acabei encontrando um PDF em portugues muito bom que ensina como utilizar mapeamentos no hibernate. Segue o link:
ftp://users.dca.ufrn.br/hibernate_anotacoes/T03_Relacionamentos_1.pdf

segunda-feira, 13 de agosto de 2007

Alterando variável java.library.path em tempo de execução

Olá a todos,

Estou postando aqui um trecho de código que eu e dois colegas (Israel e Gustavo) encontramos para solucionar um problema bem simples. Estavamos desenvolvendo uma ferramenta que utilizava o framework JOGL, e além do jogl.jar tinhamos que carregar uma dll também do jogl. Para carregar esta dll tinhamos então que colocar elas, ou no path do sistema operacional (nem um pouco indicado pois no nosso caso nem teriamos como gravar arquivos nos diretórios do C:/... por motivos de falta de permissão) apontada pela variável java.library.path, ou colocar na raiz da aplicação. Mas queríamos que as dlls ficassem dentro da pasta lib do nosso projeto. Para resolver isso procuramos na web uma forma de alterar a variável java.library.path somente em tempo de execução e ao finalizar o aplicativo esta variável seria reinicializada com o valor anterior.

A idéia é simples e o código também:

try {
Class clazz = ClassLoader.class;
Field field = clazz.getDeclaredField("sys_paths");

boolean accessible = field.isAccessible();
if (!accessible)
field.setAccessible(true);
Object original = field.get(clazz);

field.set(clazz, null);
try {
System.setProperty("java.library.path", System.getProperty("java.library.path") + ";" + new File("").getAbsolutePath() + File.separator + "lib");
System.loadLibrary("jogl");
}
finally {
field.set(clazz, original);
field.setAccessible(accessible);
}
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}

Prometo que os próximos códigos irei postar em um Pastbin, mas ainda não achei nenhum decente...

[]'s

domingo, 12 de agosto de 2007

Motivo da criação deste Blog...

Decidi criar este blog, inicialmente para ter um local na rede onde eu pudesse expor algumas idéias, problemas já passados para resolver determinados problemas, utilização com ou sem sucesso de algumas tecnologias, enfim, tudo que possa ajudar outras pessoas a tomarem decisões sobre a escolha de tecnologias na área de TI.

Tentarei escrever sobre algumas experiências que já tive na área de computação. Atualmente sou desenvolvedor Java EE e SE, mas utilizo também outras tecnologias como: Lua, OpenLaszlo, etc.

Gosto muito de trocar idéias sobre como modelar arquiteturas de software, estudar metodologias de desenvolvimento ágil, como XP, Scrum, e o recente (pelo menos para mim) OpenUp da IBM juntamente com o Eclipse. Gosto também de conhecer novos frameworks que proporcionam produtividade e sobretudo qualidade.

Sobre Java Web atualmente utilizo as tecnologias:
- VRaptor - Controlador MVC
- Hibernate - JPA
- JSP + EL + Taglibs
- JBoss + Tomcat - Servidor de Aplicações + Servelt Container

Também estou estudando, e documentando bem, como criar plugins para o Eclipse. Sobre isso quero focar, principalmente, na parte da criação de editores (com syntax check e syntax highlight), pois o resto é trivial e já possuem diversas documentações que podem ser encontradas no google ;).

É isso... abraços