Archive for the ‘dicas’ Category

In my first english blog entry, I could not find a more appropriate topic. I’m still looking for a way to provide both Portuguese and English content on this blog.

Meanwhile, english readers can check my blog entries using the google translate. Google’s Language API is the reason of this post.

Lucene, is probably one of my favorites frameworks of all times, and I love everything related to it: Hadoop, Nutch, Solr and Hibernate Search.

I use Lucene whenever I can :) And one of the things we did within it was a Federated Search for JBoss Portal. We indexed all kind of documents uploaded to the CMS portal using interceptors. One of the problems we faced, was automatic language detection. Because Lucene needs an analyzer to proper index the document, we needed a specific analyzer for each language. Well, at the time we miserably failed on that. It was a restriction we did not gave much attention since we were only indexing portuguese documents.

This week started with this restriction on my mind. At first I thought that I could find an open source api for this. Only found a few desktop apps, all closed source.

What if I use some kind of classifier, for instance a Naive-Bayes classifier, to classify my documents? I could download a few hundred of documents from wikipedia, all from different languages, train it, and then use it. Wow! That seemed cool, but would require some effort (and I’m feeling lazy this week).

So I was checking GWT extensions (because GWT is the coolest thing ever happened to the presentation layer), and I found the translation API , which BTW have an method to detect the language. Now my problems are really solved. The API relies on REST and JSON which makes it really simple to use. I started to use it by extracting random pieces of text from the documents and asking google to classify it. I’ve used this approach to avoid hitting some quotes or an abstract in a paper, which could led to a wrong idiom detection. Once we have the correct language we can instantiate the appropriate Analyzer.

The code bellow uses JSONSimple to parse the JSON response from google.

try {
	String s = URLEncoder.encode("Há tantos burros mandando em homens de inteligência, que, às vezes, fico pensando que a burrice é uma Ciência", "UTF-8");
	URL url = new URL("http://ajax.googleapis.com/ajax/services/language/detect?v=1.0&q="+s);
	 BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()));
        String str;
        StringBuilder  buffer = new StringBuilder();
        while ((str = in.readLine()) != null) {
            buffer.append(str);
        }
        in.close();
        JSONObject obj = (JSONObject) ((JSONObject)JSONValue.parse(buffer.toString())).get("responseData");
        System.out.println(obj.get("language"));
        System.out.println(obj.get("confidence"));
 
 
} catch (UnsupportedEncodingException e) {
	// TODO Auto-generated catch block
	e.printStackTrace();
} catch (MalformedURLException e) {
	// TODO Auto-generated catch block
	e.printStackTrace();
} catch (IOException e) {
	// TODO Auto-generated catch block
		e.printStackTrace();
	}
}

The API not only provides the correct language, but also a confidence value.

Happy coding, and hope you enjoy this API as much as I did :)

Bem, essa aqui é uma dica simples e rápida :) . No meu último post falei sobre clusters, e por isso mencionei o serviço de bindings do jboss 4.2. Meu amigo Elton, fez uma pergunta se referindo ao 5.0, bem, aqui vai a resposta:

No JBoss AS 5.0, clustering ficou quase identico ao 4.2 (obrigado RH pela estratégia em time que esta vencendo agente não mexe), uma das poucas difereças é o VFS e o novo MicroContainer que injeta os serviços.

Assim sendo, toda configuração de bindigins, ficou centralizada no arquivo $SERVER_NAME/conf/bootstrap/bindings.xml

Abaixo um trecho

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<?xml version="1.0" encoding="UTF-8"?>
 
<deployment xmlns="urn:jboss:bean-deployer:2.0">
 
   <classloader><inject bean="bindings-classloader:0.0.0"/></classloader>
 
   <classloader name="bindings-classloader" xmlns="urn:jboss:classloader:1.0" export-all="NON_EMPTY" import-all="true">
      <root>${jboss.common.lib.url}jboss-bindingservice.jar</root>
   </classloader>
 
   <bean name="ServiceBindingManager" class="org.jboss.services.binding.ServiceBindingManager">
 
      <annotation>@org.jboss.aop.microcontainer.aspects.jmx.JMX(name="jboss.system:service=ServiceBindingManager", exposedInterface=org.jboss.services.binding.ServiceBindingManagerMBean.class, registerDirectly=true)</annotation>
 
      <constructor>
         <!-- The name of the set of bindings to use for this server -->
         <parameter>${jboss.service.binding.set:ports-default}</parameter>
 
         <!-- The named sets of bindings -->
         <parameter>
            <bean name="ServiceBindingStore" class="org.jboss.services.binding.impl.PojoServiceBindingStore">
 
               <!-- Base bindings that are used to create bindings for each set -->
               <property name="standardBindings"><inject bean="StandardBindings"/></property>
 
               <!-- The sets of bindings -->
               <property name="serviceBindingSets">
                  <set>
                     <inject bean="PortsDefaultBindings"/>
                     <inject bean="Ports01Bindings"/>
                     <inject bean="Ports02Bindings"/>
                     <inject bean="Ports03Bindings"/>
                  </set>
               </property>
            </bean>
         </parameter>
      </constructor>
 
   </bean>

Note que nas linhas 27-32 estão definidas as configurações de portas, logo abaixo (omitido no post) estão cada uma das configurações com os respectivos nomes (ports-default,ports-01,ports-02,ports-03).

Para alterar as portas da sua instância, basta modificar a linha 17, após boss.service.binding.set. Pronto, agora ao iniciar seu JBoss ele já estará rodando em outra configuração de porta. Bem mais simples não é? Por default o AS agora já executa o binding service.

Espero que o pequeno post seja de ajuda.

Abraços

Bem, esta é uma dica que apesar de simples, muita gente não conhece. O Hibernate pode ser configurado para gerar estatísticas de uso da SessionFactory. Em meus projetos já é comum rodar estas estatísticas uma vez que o projeto entra em homologação e ate mesmo em produção. Estas estatísticas vão ajudá-lo em alguns pontos:

  • Encontrar queries que podem estar consumindo muito tempo
  • Identificar possíveis objetos que podem ser colocados em cache
  • Verificar se os caches de 2nd level estão realmente sendo utilizados
  • Identificar possíveis campos que podem ser definidos como índices em suas tabelas

Por maior que seja nosso esforço como arquitetos de software, é muito difícil prevermos quais consultas serão mais usadas, quais objetos deveremos manter em cache, e quando sua equipe for grande, por mais que você tente, peça, xingue, sempre vai existir um desenvolvedor que não executa as queries no Hibernate tools e verifica se o SQL gerado é realmente a versão mais otimizada. Se eu ganhasse 1 real para cada join desnecessário que já encontrei em queries de projetos quando faço revisão de código, eu provavelmente poderia adiantar minha aposentadoria.

Fica aqui então uma forma de como verificar as estatísticas do hibernate. A abordagem que vou adotar é de disponibilizar um ManagedBean que irá exportar como serviço o HibernateStatistics. Vamos lá.

A aplicação de exemplo a ser monitorada, é uma aplicação de aprovação de horas (usando seam + jbpm_ que foi demonstrada neste blog neste link

Primeiro precisamos da nossa interface de MBean:

package com.furiousbob.hibernate.stats;
 
public interface HibernateStatsMBean {
	public void start() throws Exception;
	public void stop() throws Exception;
	public void create()throws Exception;
	public void destroy();
}

Bem, alguns poderão questionar aqui que poderíamos usar o ServiceMBeanSupport do JBoss, mas eu preferi manter a forma tradicional de mbeans ;)

Agora nossa implementação:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package com.furiousbob.hibernate.stats;
 
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.naming.InitialContext;
import javax.persistence.EntityManager;
 
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.jmx.StatisticsService;
import org.jboss.mx.util.MBeanServerLocator;
import org.jboss.system.ServiceMBeanSupport;
 
 
public class HibernateStats implements HibernateStatsMBean {
 
	private final String  SessionFactoryName = "java:/AprovacaoHorasSessionFactory";
 
 
	public void start() throws Exception {
		InitialContext ctx = new InitialContext();
		SessionFactory sessionFactory = (SessionFactory)ctx.lookup(SessionFactoryName);
		MBeanServer server = MBeanServerLocator.locateJBoss();
		ObjectName on = new ObjectName("Hibernate:type=statistics,application=AprovacaoHoras");
		StatisticsService service = new StatisticsService();
		service.setSessionFactory(sessionFactory);
		server.registerMBean(service, on);
	}
 
	public void stop() throws Exception {
		MBeanServer server = MBeanServerLocator.locateJBoss();
		ObjectName on = new ObjectName("Hibernate:type=statistics,application=AprovacaoHoras");
		server.unregisterMBean(on);
	}
 
	public void create() throws Exception {
	}
 
	public void destroy() {
	}

Como podem notar é realmente muito simples registrar o servico de stats do Hibernate. Note que estamos usando o nome da SessionFactory de forma fixa dentro do código, você poderia por exemplo, definir isto como um atributo de seu MBean de forma que este código possa ser usado em várias aplicações. O nome do MBean também reflete o nome da aplicação a ser monitorada, e é outro candidato a se tornar uma propriedade configurável.

A última parte de nosso exemplo é o arquivo jboss-service.xml que deve ser colocado na pasta META-INF de sua aplicação

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="UTF-8"?>
<server>
 
   <mbean name="MyApp:Hibernate=Stats" code="com.furiousbob.hibernate.stats.HibernateStats">
 		<depends>jboss:service=Naming</depends>
 		<depends>persistence.units:ear=AprovacaoHoras.ear,unitName=AprovacaoHoras</depends>
   </mbean>
</server>

Este arquivo irá informar que nosso Mbean possui uma dependência com a PersistenceUnit. E esta também deve sofrer uma pequena modificação, devemos adicionar as propriedades abaixo em nosso arquivo de configuração de persistencia:

<property name="hibernate.generate_statistics" value="true"/>
<property name="hibernate.session_factory_name" value="java:/AprovacaoHorasSessionFactory"/>

Agora precisamos de empacotar nossas classes como um Service ARchive (SAR). Um SAR é nada mais que um JAR com o jboss-service.xml em seu META-INF. Ao final da operação você deverá ter um arquivo SAR com a seguinte estrutura:

vinicius@cybertron:~/workspace/HibernateStats/dist$ jar -tvf HibernateStats.sar
     0 Wed Mar 04 09:09:40 BRT 2009 META-INF/
   106 Wed Mar 04 09:09:38 BRT 2009 META-INF/MANIFEST.MF
   305 Wed Mar 04 09:02:46 BRT 2009 META-INF/jboss-service.xml
     0 Wed Mar 04 09:03:02 BRT 2009 com/
     0 Wed Mar 04 09:03:02 BRT 2009 com/furiousbob/
     0 Wed Mar 04 09:03:02 BRT 2009 com/furiousbob/hibernate/
     0 Wed Mar 04 09:03:02 BRT 2009 com/furiousbob/hibernate/stats/
  2082 Wed Mar 04 09:03:34 BRT 2009 com/furiousbob/hibernate/stats/HibernateStats.class
   290 Wed Mar 04 09:03:02 BRT 2009 com/furiousbob/hibernate/stats/HibernateStatsMBean.class

Agora, acesse seu jmx-console de você deverá encontrar uma tela contendo um link para seu novo mbean. Existem dois mebans novos o primeiro:

AprovacaoHoras:Hibernate=Stats é o seu MBean que registra o serviço de stats

Hibernate:application=AprovacaoHoras,type=statistics: é o serviço de stats que você poderá coletar as métricas

Pronto, agora basta acessar o serviço e começar a coletar algumas métricas de seu Hibernate.

Aproveitem