Lucas Teixeira

@lucastex

Arquivo para a categoria ‘Java’

Criando um tipo de dados personalizado no Grails + Hibernate

com 11 comentários

Olá Pessoal,

Estou invadindo o Blog do meu grande amigo Lucas para postar uma solução que encontrei para um problema no mapeamento de dados de uma base de dados existente. Gostaria, em primeiro lugar, de agradecer o espaço cedido pelo Lucas e parabenizá-lo pelo excelente Blog.

Hoje em dia são poucos os projetos que precisamos desenvolver do zero — criar modelagem, tabelas e etc… — por isso é muito comum depararmos com padrões proprietários que muitas vezes não se “encaixam” na ferramenta de desenvolvimento. Não preciso dizer que não é uma tarefa fácil convencer os desenvolvedores a se adequarem aos novos padrões, então, se não pode com eles, una-se a eles.

Bom, vamos direto ao assunto, no meu projeto atual me deparei com um padrão que utiliza ‘S’ e ‘N’ para o mapeamento de propriedades booleanas no banco de dados Oracle. Na pesquisa que realizei encontrei várias pseudo-soluções mas a única que atendeu 100% as necessidades foi a implementação de um tipo de dados do Hibernate.

Abaixo a implementação da classe SNUserType, não tem segredo é apenas a implementação da interface org.hibernate.usertype.UserType. Salve este código no pacote persistence na pasta de src/groovy do seu projeto Grails.

package persistence;

import org.hibernate.*;
import org.hibernate.usertype.*;
import java.sql.*;
import java.util.*;
import java.io.Serializable;

public class SNUserType implements UserType {

   def SQL_TYPES = [Hibernate.YES_NO.sqlType()];

   public int[] sqlTypes() {
      return SQL_TYPES;
   }

   private Class targetClass;

   public void setParameterValues(Properties params) {
      String targetClassName = params.getProperty("targetClass");
      try {
         targetClass = Class.forName(targetClassName);
      } catch (ClassNotFoundException e) {
         throw new HibernateException("Class " + targetClassName + " not found ", e);
      }
   }

   public Class returnedClass() {
      return targetClass;
   }

   public boolean isMutable() {
      return false;
   }

   public Object deepCopy(Object value) {
      return value;
   }

   public Serializable disassemble(Object value) {
      return (Serializable) value;
   }

   public Object assemble(Serializable cached, Object owner) {
      return cached;
   }

   public Object replace(Object original, Object target, Object owner) {
      return original;
   }

   public boolean equals(Object x, Object y) {
      if (x == y)
         return true;
      if (x == null || y == null)
         return false;
      return x.equals(y);
   }

   public int hashCode(Object x) {
      return x.hashCode();
   }

   public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws SQLException {
      String value = rs.getString(names[0]);
      if ("S".equals(value))
         return true;
      else
         return false;
   }

   public void nullSafeSet(PreparedStatement ps, Object value, int index) throws HibernateException, SQLException {
      if (value == null) {
         ps.setNull(index, Hibernate.YES_NO.sqlType());
      } else {
         if((Boolean)value) {
            ps.setString(index, "S");
         } else {
            ps.setString(index, "N");
         }
      }
   }
}

Pronto, agora tudo que você precisa fazer é utilizar o novo tipo de dados no mapeamento de suas classes de domínio.

class Pessoa {

   String nome
   Boolean ativo

   static mapping = {
      ativo type: "persistence.SNUserType"
   }

}

Com isso, resolvi o problema de integração e concluímos este post.

Um abraço

Volnei

Written by Volnei Munhoz

July 19th, 2010 at 8:39 am

Como, e por que usar um DataSource JNDI.

com 5 comentários

Recebi uma pergunta esses dias por aqui.

Lucas,
gostaria de saber como trabalhar com arquivos .properties pra conexão com o banco de dados.
No Grails a gente nota que a conexão fica no código-fonte (DataSorce.class)…
Estou tentando descobrir como faço para ter um arquivo de propriedade com os parametros da conexão. Caso eu precise apontar para outro banco, não terei que recoompilar tudo.

Quem enviou foi o Felipe Juliani.

Neste caso, devemos usar ao invés das conexões declaradas no DataSource.groovy, uma declaração de conexão com banco de dados via JNDI.

JNDI é uma árvore de ‘nomes’ que referenciam ‘recursos externos’. O que isso quer dizer? Basicamente, que a sua aplicação poderá pegar uma configuração de fora da aplicação, diretamente de um “lugar” na JVM que alguém colocou. Seria mais ou menos um clipboard compartilhado, só que de objetos é claro :)

Então para o caso acima, nada melhor que deixar toda essa ‘configuração’ de conexão com o banco de dados do lado de fora da aplicação e fazer com que ela vá buscar apenas pelo ‘nome’ desta conexão. Pronto, desta maneira a configuração fica externa a nossa aplicação e feita diretamente no nosso container.

Bom, eu particularmente vejo três grandes razões para o uso de DataSources JNDI. A primeira é quando devemos tirar do desenvolvedor a (ir)responsabilidadade de dimensionar/configurar a utilização de banco de dados. Isso é um trabalho de infra estrutura, e se em algum determinado momento infra estrutura resolver aumentar o pool de conexões de banco da aplicação, consegue fazer isto sem encostar na aplicação, diretamente no container onde ela está rodando.

Outro motivo é a melhor utilização de recursos de banco de dados. Vamos imaginar um cluster de servidores de aplicação com 3 nós. Cada um dos nós roda uma instância da sua aplicação, que está configurada (diretamente no DataSources.groovy) com um pool de 10 conexões, ou seja, só de subir as aplicações, você terá 30 conexões com o banco já feitas. Com DataSources neste caso, todas as instâncias da aplicação poderiam ir buscar conexões com o banco de dados no mesmo DataSource, configurado uma única vez. Com isso, não precisamos necessariamente possuir 30 conexões abertas com o banco, pois quando uma instância necessita de todas elas, outra instância pode estar usando apenas 3 ou 4.  É claro que para isso, além dos servidores e da aplicação, o seu DataSource também precisa estar deployado no cluster todo.

E o terceiro motivo, é o apontado pelo Felipe acima, que precisa deixar uma maneira fácil de trocar o banco de dados da aplicação. Com estes DataSources JNDI fica fácil também, já que a url do banco, driver, e credenciais estão do lado de fora, na configuração do DataSource.
E para criar este DataSource?
Bom, o primeiro passo é levantar em que container você está rodando a sua aplicação, pois cada um tem a sua maneira particular de configuração, seja jetty, tomcat, jboss ou weblogic. Além das diferenças durante a criação do DataSource, temos também diferenças na ‘formação’ do nome deles. No caso do weblogic por exemplo, o mais simplista neste quesito, você poderia ter um datasource com o nome de “PedidosDS”, já no JBoss, ele fica prefixado desta maneira: “java:<nome_datasource>”.

E depois disso, na configuração da sua aplicação Grails, na closure do environment específico que você quer, basta descrevê-lo desta maneira:

production {
   dataSource {
      jndiName = "<nome_datasource>"
   }
}

Vale lembrar que estes dias postei sobre como criar um datasource no jboss e usá-lo em uma aplicação grails. Não deixe de ler também.

Written by Lucas Teixeira

March 4th, 2010 at 3:36 am

Data corrente no nome do artefato gerado com ant

sem comentários

Algumas pessoas costumam versionar (e manter guardado) o histórico de versões geradas pelo seu projeto. Uma coisa que não se deve esquecer, é de renomear os pacotes de modo que quando necessário encontrar um específico, seja fácil.

O que eu faço, é sempre renomear o pacote para: projeto-yyyyMMddHHmmss.[jar/war/ear] . Como ant, fica fácil fazer isso usando uma task chamada tstamp, que recupera o timestamp corrente e grava em uma variável seguindo o pattern que você escolher. Segue exemplo que uso aqui.

Armazenando valor:

<tstamp>
   <format property="build-datetime" pattern="yyyyMMddHHmmss"/>
</tstamp>

Pronto, agora basta usar a variável build-datetime como outra qualquer,

<target name="dist" depends="compile" description="gera o jar com a distribuição">
   <jar jarfile="${dist}/${project}-${build-datetime}.jar" basedir="${build}"/>
</target>

É uma boa também para guardar os arquivos em estruturas de pastas separadas por dia/mes/ano.

yyyyMMddHHmmss

Written by Lucas Teixeira

October 28th, 2009 at 3:35 pm

Postado em Ant, Java

Com as tags , , ,

Criando um Transformer customizado para o Solr

com 14 comentários

Solr é um framework uma ferramenta para a construção de servidores de indexação e busca on top of índices lucene.

Possui todas as funcionalidades que existem em um sistema moderno de busca, como paginação, highlight de campos, flexão das palavras e etc. Na minha opinião, de tudo que trabalhei nos últimos anos, sem dúvida, formam o conjunto mais poderoso de frameworks.

O Solr abstrai a camada Java do Lucene e disponibiliza uma interface http para a execução da consulta, fazendo com que fique muito fácil a integração com sistemas não-java.

Um dos recursos avançados de indexação com Solr são os Transformers a serem usados juntamente com o DataImportHandler. Com eles, você pode processar o texto antes de ser indexado. (Um outro post sobre configuração básica de Solr e de DIH deve vir em breve).

Semana passada, precisei de um transformer que pudesse retirar todas as tags HTML do texto a ser indexado (malditos editores rich text em javascript). Como o projeto aqui está usando Solr 1.3 não tive a possibilidade de usar o HTMLStripTransformer que virá no Solr 1.4 (e está enroscado pra sair). Então acabei tendo que criar um semelhante para a funcionalidade.

A criação de transformers é muito simples (imho, simplista até demais, fazendo com que as vezes, o desenvolvedor se perca durante a implementação). Deve ser desenvolvida uma classe simples, que implementa um método com a seguinte assinatura:

public Map<String, Object> transformRow(Map<String, Object> aRow, Context context)

Quando disse que é simplista demais, é pelo fato de que, (imho novamente), condições restritivas como esta deveriam estar documentadas em interfaces, mas não, quando necessário, o método é chamado via reflection, tendo certeza que ele está implementado.

No meu caso, eu precisaria além de implementar o Transformer, deixar explícito no arquivo de configuração do DIH (data-config.xml) quais os campos que deveriam receber o tratamento desta tag, fiz isso adicionando a tag removeHtml no nó de cada campo (não, este xml não passa por validação).

<field name="txt" column="texto" removeHtml="true" />

Com isto, estamos deixando claro para o DIH que o valor que retornar da query na coluna texto será repassado ao Solr para indexação no field txt (definido no schema.xml) e terá processamento do meu HtmlTransformer. Ah, não podemos esquecer da declaração do transformer para a entidade:

<entity name="posts" transformer="br.com.lucastex.dih.transformer.HtmlTransformer" />

Pronto, a classe está agora carregada e pronta para ser chamada quando um campo necessitar. Agora, vamos a implementação da classse. No meu caso, ela ficou bem trivial, pois o campo em questão nunca será multivalorado (ou seja, não preciso tratar a possibilidade do argumento ser um List, mas apenas uma String.

package br.com.lucastex.dih.transformer;

import java.util.Map;

import org.apache.commons.lang.StringEscapeUtils;
import org.apache.solr.handler.dataimport.Context;
import org.apache.solr.handler.dataimport.DataImporter;
import org.apache.solr.handler.dataimport.RegexTransformer;
import org.apache.solr.handler.dataimport.Transformer;

public class HtmlTransformer extends Transformer {

  public static final String TAG = "removeHtml";

  public Map<String, Object> transformRow(Map<String, Object> aRow, Context context) {

    for (Map<String, String> map : context.getAllEntityFields()) {
      if (!Boolean.TRUE.toString().equals(map.get(TAG)))
        continue;
      String columnName = map.get(DataImporter.COLUMN);
      String sourceColumnName = map.get(RegexTransformer.SRC_COL_NAME);
      if (sourceColumnName == null)
        sourceColumnName = columnName;
      Object value = aRow.get(sourceColumnName);
      if (value instanceof String) {
        String result = stripHtml((String) value);
        aRow.put(columnName, result);
      }
    }
    return aRow;
  }

  private String stripHtml(String text) {
    try {
      String cleanText = StringEscapeUtils.unescapeHtml(text.replaceAll("\\<.*?\\>", " "));
      cleanText = cleanText.trim().replaceAll("\n"," ");
      cleanText = cleanText.replaceAll("\t"," ");
      cleanText = cleanText.replaceAll("[\\s]+", " ");
      return cleanText;
    } catch (Exception e) {
      //trata exception de acordo com seu contexto solr
    }
    return text;
  }
}

Bom, o código é bem auto-explicativo. Basicamente recebe o além do contexto em execução do Solr/DIH, um Map de String e Object, que contém todos os campos e valores da linha que está sendo analisada/indexada no momento. Vale lembrar que este modelo de passagem de parâmetro é feito exatamente como descrito no Pattern BCDR.

Enfim, as linhas 18 e 19 garantem que o código só será executado para campos que tenham declarado a tag removeHtml=true como descrito anteriormente.

A linha 24 recupera o valor original do campo, ou seja, o que foi retornado pela query no banco de dados, e nas linhas 26 e 27 o resultado é tratado (através da chamada para o método stripHtml) e devolvida ao Map.

Ou seja, este é o momento onde você tem acesso a todos os campos que estão vindo do banco antes de irem ao índice. É nesta hora que você duplica seus campos, faz tratamentos, aplica padrões e templates, enfim trabalha a informação bruta original do banco de dados.

Já o método de remoção de tags HTML, não é nada além de um conjunto de regexes encadeadas  com uma ajudinha do StringEscapeUtils (que saudade deste post) do Commons-lang.

Simples como

Written by Lucas Teixeira

October 26th, 2009 at 6:59 pm

Maravilhas do Groovy: A propriedade metaClass

com 12 comentários

Uma das facilidades que o groovy também traz, é a possibilidade de adicionar métodos em nossas classes em tempo de execução através da propriedade metaClass dos objetos.

Agora mesmo, eu precisava de um recurso para criar “slugs” (essas URLs amigáveis que o WordPress cria) de titulos de artigos. Tradicionalmente, o processo é criar aquelas classes **Utils.java com todos os métodos utilitários, mas com a metaprogramação, o mais usual passa a ser adicionar o métodos nas próprias classes que geram este comportamento.

No exemplo abaixo, eu adicionei o método slug() em runtime dentro da classe String e a partir de agora, qualquer objeto da classe java.lang.String possui o método slug(), com o comportamento descrito abaixo.

String.metaClass.slug { ->
    def s = delegate.toLowerCase()
    s = s.replaceAll(/[^a-z0-9\s-]/, "").replaceAll(/\s+/, " ").trim()
    if (s.length() > 45)
        s = s.substring(0, 45).trim()
    s.replaceAll(/\s/, "-")
 }

Em primeiro, definimos uma varíavel interna ’s’ com o valor da própria string que está sendo usada (através da propriedade delegate), após isso, aplicamos a primeira regex de caracteres especiais, outra para substituir os espaços em excesso e cortamos a string caso ela tenha mais que 45 caracteres. Por fim, substituímos os espaços por dashes.

Você pode rodar este script neste endereço, basta clicar em “Execute script”

Written by Lucas Teixeira

October 25th, 2009 at 12:55 pm

Get Adobe Flash playerPlugin by wpburn.com wordpress themes