Dimanche 26 février 2012 7 26 /02 /Fév /2012 19:13

 

Dans cet article, nous verronsr comment faire pour intégrer un moteur de recherche dans votre application web.

Nous n'aborderons pas dans cet article, le moteur de recherche google.

 

 

Context de l'étude

Dans le cadre d'un site de gestion de voyage, j'ai besoin de proposer une recherche d'article par nom de ville et pays.

L'application est écrite en java avec les frameworks suivant: JPA, JTA, EJB3.

Afin de rester je plus libre des infrastructures de déploiement, la persistance utilise uniquement le standard.

Le projet ne doit donc pas être rattaché à Hibernate ou Eclipse.

Le serveur de production est à ce jour un Glassfish 3.1.1 avec comme implémentation, par défaut de JPA, le framework EclipseLink.

La base de donnée est une MySql 5.1.5 accédé via une trasaction distribué (XAConnection).

 

Solutions - Tour d'horizon

Voici quelques solutions que j'ai pu trouver lors de mes recherches: 

  • Hibernae Search : Framework d'indexation des données, basé sur le standard JPA. Inconvénient, il impose l'utilisation du framework de même nom (Hibernate) comme solution d'ORM (Object-relation-mapping).
  • Lucene : Le plus connu des framework d'indexation et de moteur de recherche. Il est hébergé par la communauté apache fondation. Inconvénient, il ne supporte pas de manière native les normes JPA et JTA.
  • Compass : Surcouche au framework Lucene. Il a été conçu pour simplifier l'intégration avec différent standard tels que JPA, JTA et la plus part des ORM (Object relation mapping) du monde JAVA.
  • ElasticSearch : application type serveur, basé sur le framework Lucene. Son fondateur est le même que pour Compass. Il offre une plus grande facilité d'intégration et d'extension dans les environnements clouds. Inconvénient, il ne supporte pas encore les standard JPA et JTA.

 

La solution que j'ai donc choisi d'utiliser dans un premier temps est donc le framework Compass.

 

Présentation de Compass

Site du projet: http://www.compass-project.org

Version utilisé: 2.2.0

Les sources de mon proto: lien sur KENAI

Intégration avec maven

 

La dépendance à mettre dans votre projet:

<dependency>
<groupId>org.compass-project</groupId>
<artifactId>compass</artifactId>
<version>2.2.0</version>
<type>jar</type>
</dependency>

 

 

Le repository à mettre dans votre projet:

 

<repository>
<id>compass-project.org</id>
<name>Compass</name>
<url>http://repo.compass-project.org</url>
</repository>

Présenation du proto

L'objectif de ce proto était de tester le framework dans un environnement proche de celui de la production.

Pour répondre à cette problématique, j'ai décider le créer un EJB Singleton pour représenter mon service d'indexation et de recherche.

Pour la partie test, j'ai utilisé Glassfish 3.1.1 en version embedded afin d'être le plus proche possible de l'environnement cible.

La partie base de donnée a été configuré au sein du domaine glassfish. Un premier test a été réalisé avec la base Derby Embedded en mode datasource XA (Transaction distribué). Puis avec une base Mysql en version cible.

 

Couche service

package com.evasion.poc.compass;
 
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import javax.ejb.TransactionManagement;
import javax.ejb.TransactionManagementType;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import org.compass.annotations.config.CompassAnnotationsConfiguration;
import org.compass.core.*;
import org.compass.core.config.CompassConfiguration;
import org.compass.gps.CompassGps;
import org.compass.gps.device.jpa.JpaGpsDevice;
import org.compass.gps.impl.SingleCompassGps;
 
/**
 *
 * @author sebastien
 */
@Singleton
@Startup
@TransactionManagement(TransactionManagementType.BEAN)
public class MySingleton {

    private Compass compass = null;
    private CompassGps gps=null;

    @PostConstruct
    public void initialiser() {
            CompassConfiguration conf = new CompassAnnotationsConfiguration();
            conf.configure(getClass().getClassLoader().getResource("compass.cfg.xml"));
            conf.addClass(TestEntity.class);

            compass = conf.buildCompass();
            gps = new SingleCompassGps(compass);

            // Création du composant JPA
            EntityManagerFactory emf = Persistence.createEntityManagerFactory("CompassPU");
            JpaGpsDevice jpaDevice = new JpaGpsDevice("jpa", emf);
            jpaDevice.setInjectEntityLifecycleListener(true);
            jpaDevice.setNativeExtractor(new GlassfishV3NativeJpaExtractor());

            gps.addGpsDevice(jpaDevice);

            gps.start();
            gps.index();
            System.out.println("Start OK");
    }
    @PreDestroy
    public void preDestroy() {
   compass.close();
    }
 
    public Collection search(String searchTerms) {
        CompassSession session = compass.openSession();
        Collection result = null;
        CompassTransaction ctx = null;
        CompassHits hits;
        try {
            ctx = session.beginTransaction();
            hits = session.find(searchTerms);
            result = new ArrayList(hits.getLength());
            for (Iterator<CompassHit> it = hits.iterator(); it.hasNext();) {
                result.add(it.next().getData());
            }
 
            ctx.commit();
        } catch (CompassException ce) {
            if (ctx != null) {
                ctx.rollback();
            }
        } finally {
            session.close();
        }
        return result;
    }
}

 

Comme vous pouvez le voir, je n'utilise pas le mode de transaction managé par le conteneur. Les actions de démarrage de Compass et d'indexation ne supporte pas le mode de transaction global.

De même je n'utilise pas l'annotation @PersistenceUnit pour initialiser la propriété EntinyManagerFactory.

Couche test

Le test autométisé va être réalisé avec Junit4, GlassFish Embedded 3.1.1 et les bases Derby Embedded et Mysql 5.

Voici le code source du test:

 

import com.evasion.poc.compass.MySingleton;
import com.evasion.poc.compass.TestEntity;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Resource;
import javax.ejb.embeddable.EJBContainer;
import javax.naming.Context;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.PersistenceContext;
import javax.transaction.UserTransaction;
import org.junit.*;
import static org.junit.Assert.*;
 
/**
 *
 * @author sebastien
 */
public class MySingletonTest {
 
    private static UserTransaction utx;
 
    private static EntityManagerFactory emf;
    private static EntityManager em;
 
    private static EJBContainer ejbContainer;
 
    private static MySingleton singleton;
 
    public MySingletonTest() {
    }
 
    @BeforeClass
    public static void setUpClass() throws Exception {
        //Map<String, Object> properties = new HashMap<String, Object>();
        //properties.put(EJBContainer.MODULES, new File("target/classes"));
        ejbContainer = EJBContainer.createEJBContainer();
        Context ctx = ejbContainer.getContext();
        // le nom JNDI d'un EJB dépend du serveur d'applications utilisé :
        // jboss     : "HelloWorldService/local"
        // glassfish : "java:global/classes.ext/HelloWorldService"
        String serviceName = "java:global/classes/" + MySingleton.class.getSimpleName();
        singleton = (MySingleton) ctx.lookup(serviceName);
        utx = (UserTransaction) ctx.lookup("java:comp/UserTransaction");
        em = Persistence.createEntityManagerFactory("CompassPU").createEntityManager();
    }
 
    @AfterClass
    public static void tearDownClass() throws Exception {
        ejbContainer.close();
    }
    TestEntity bean1, bean2;
 
    @Before
    public void setUp() throws Exception{
        utx.begin();
        em.getTransaction().begin();
        bean1 = new TestEntity();
        bean1.setName("bean1");
        em.persist(bean1);
 
        bean2 = new TestEntity();
        bean2.setName("bean2");
        em.persist(bean2);
        em.flush();
        em.getTransaction().commit();
        utx.commit();
    }
 
    @After
    public void tearDown() throws Exception{
        utx.begin();

        em.remove(bean1);
        em.remove(bean2);
        utx.commit();
    }
 
    /**
     * Test of search method, of class MySingleton.
     */
    @Test
    public void testSearch() throws Exception {
        System.out.println("search");
        String searchTerms = "bean";
        Collection expResult = new ArrayList(2);
        expResult.add(bean1);
        expResult.add(bean2);
        Collection result = singleton.search(searchTerms);
        assertEquals(expResult, result);
    }
}

Problématique rencontré

Durant mes tests, j'ai eu quelque problème de TimeOut de lock lors de recherche. Pour contourné ceci j'ai rallongé le timeout.

En revnache bien plus embettant, lors que l'on utilise Mysql en mode XA, alors le framework ne parviens pas à créer les tables d'indexation et à réalisé certaines opération de mise à jour. L'explication de cela réside du fait que le driver Mysql lève une expcetion si l'on tente un create, delete de table ou un commit sur une connection XA.

En revanche cela marche plutôt bien sans le XA.

Par sglon - Publié dans : Informatique - Communauté : Développeur en herbe
Ecrire un commentaire - Voir les 0 commentaires
Dimanche 1 mai 2011 7 01 /05 /Mai /2011 17:23

Bonjour,

 

Suite à de nombreux problème de mémoire et en particulier une trop grande utilisation du swap, voici quelque modification de conf que j'ai réalisé sur mon serveur.

Voici le lien vers le site qui ma aidé à faire ma conf: link

 

Voici la commande passé : sysctl -w vm.swappiness=25

 

Si certains de mes lecteurs savent comment interdire à une application JAVA d'utiliser le SWAP ou optimiser l'utilisation de la mémoire d'un serveur glassfish et apache en mod_jk, je suis preneur.

 

A ce jour, il semble que le mod_jk créé trop de process et par la même occasion utilise trop le swap.

 

Que puis-je faire pour limiter cet effet.

 

 

 

Par sglon - Publié dans : Informatique - Communauté : Développeur en herbe
Ecrire un commentaire - Voir les 0 commentaires
Vendredi 29 octobre 2010 5 29 /10 /Oct /2010 11:00

Voici ma dernière lubie...

Installer Apache en frontal de mon serveur d'application Glassfish V3.0.1 et mettre en place https;

 

Voici un site qui m'a permit de créer mes certificats:

http://www.startssl.com/

 

pour la partie interconnexion Apache - Glassfish, voici le lien que j'ai utilisé:

http://docs.sun.com/app/docs/doc/820-4496/gfaad?l=en&a=view

 

Pour le moment je n'ai pas terminé le déployement du HTTPS mais je continuerai à mettre des infos

Par sglon - Publié dans : Informatique - Communauté : Développeur en herbe
Ecrire un commentaire - Voir les 0 commentaires
Dimanche 5 septembre 2010 7 05 /09 /Sep /2010 23:17
Par sglon - Publié dans : Informatique - Communauté : Développeur en herbe
Ecrire un commentaire - Voir les 1 commentaires
Vendredi 3 septembre 2010 5 03 /09 /Sep /2010 14:49

Voici que c'est fait pour moi; Je viens d'ajouter mon serveur au pool NTP.

 

voici quelques liens utiles:

http://www.pool.ntp.org/fr/

Par sglon - Publié dans : Informatique - Communauté : Développeur en herbe
Ecrire un commentaire - Voir les 0 commentaires
Créer un blog gratuit sur over-blog.com - Contact - C.G.U. - Rémunération en droits d'auteur - Signaler un abus - Articles les plus commentés