Index: topia2/src/java/org/codelutin/topia/framework/TopiaContextImpl.java diff -u topia2/src/java/org/codelutin/topia/framework/TopiaContextImpl.java:1.35 topia2/src/java/org/codelutin/topia/framework/TopiaContextImpl.java:1.36 --- topia2/src/java/org/codelutin/topia/framework/TopiaContextImpl.java:1.35 Wed Sep 27 16:06:34 2006 +++ topia2/src/java/org/codelutin/topia/framework/TopiaContextImpl.java Mon Oct 9 14:20:18 2006 @@ -23,9 +23,9 @@ * * @author poussin * - * @version $Revision: 1.35 $ + * @version $Revision: 1.36 $ * - * Last update: $Date: 2006/09/27 16:06:34 $ by : $Author: bpoussin $ + * Last update: $Date: 2006/10/09 14:20:18 $ by : $Author: bpoussin $ */ package org.codelutin.topia.framework; @@ -48,7 +48,6 @@ import java.sql.Statement; import java.text.DateFormat; import java.text.SimpleDateFormat; -import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.Iterator; @@ -152,6 +151,8 @@ static final private String TOPIA_PERSISTENCE_PROPERTIES_FILE = "topia.persistence.properties.file"; static final private String TOPIA_SECURITY_MANAGER = "topia.security.manager"; + + static final private String TOPIA_INDEX_ENGIN = "topia.index.engin"; /** * Le pere de ce context, les contexts initaux n'ont pas de context pere @@ -207,6 +208,8 @@ protected List transactionEvents = new LinkedList(); + protected IndexEngin indexEngin = null; + /** * Manager pour la sécurité */ @@ -220,6 +223,7 @@ public TopiaContextImpl(Properties config) { this.config = config; initSecurity(); + initIndexEngin(); } /** @@ -1081,7 +1085,7 @@ } try { TopiaEntityEvent event = new TopiaEntityEvent(this, entity); - transactionEvents.add(new TopiaTransactionEvent(this, entityClass, id)); + transactionEvents.add(TopiaTransactionEvent.create(this, entityClass, id)); for(Iterator l=getListeners().iterator(entity.getClass()); l.hasNext();) { l.next().entityCreated(event); } @@ -1104,7 +1108,7 @@ } try { TopiaEntityEvent event = new TopiaEntityEvent(this, entity); - transactionEvents.add(new TopiaTransactionEvent(this, entityClass, id)); + transactionEvents.add(TopiaTransactionEvent.update(this, entityClass, id)); for(Iterator l=getListeners().iterator(entity.getClass()); l.hasNext();) { l.next().entityUpdated(event); } @@ -1124,7 +1128,7 @@ } try { TopiaEntityEvent event = new TopiaEntityEvent(this, entity); - transactionEvents.add(new TopiaTransactionEvent(this, entityClass, id)); + transactionEvents.add(TopiaTransactionEvent.delete(this, entityClass, id)); for(Iterator l=getListeners().iterator(entity.getClass()); l.hasNext();) { l.next().entityDeleted(event); } @@ -1143,8 +1147,13 @@ log.info("onCommited"); } try { + TopiaTransactionEvents e = null; for(Iterator l=getTransactionListeners().iterator(); l.hasNext();) { - l.next().commit(new TopiaTransactionEvents(this, transactionEvents)); + // on cree l'event que si on en a besoin + if (e == null) { + e = new TopiaTransactionEvents(this, transactionEvents); + } + l.next().commit(e); } // poussin FIXME: il faut bien prevenir notre pere, mais je ne pense // pas que ce soit le bon moyen. Je pense qu'il faut une methode @@ -1153,12 +1162,18 @@ if (getParentContext() != null) { getParentContext().fireOnCommited(); } + if (isIndexEnabled()) { + getIndexEngin().indexMap(); + } } catch (Exception eee) { if (log.isWarnEnabled()) { log.warn("Can't fire event commit for entity", eee); } } finally { transactionEvents.clear(); + if (isIndexEnabled()) { + getIndexEngin().clearIndexMap(); + } } } @@ -1184,47 +1199,12 @@ } } finally { transactionEvents.clear(); + if (isIndexEnabled()) { + getIndexEngin().clearIndexMap(); + } } } - - // static protected class TopiaInterceptor extends EmptyInterceptor { - // - // protected TopiaContextImplementor context = null; - // - // private static final long serialVersionUID = -6787010517746197446L; - // - // public TopiaInterceptor(TopiaContextImplementor context) { - // this.context = context; - // } - // - // @Override - // public boolean onLoad(Object entity, Serializable id, Object[] state, - // String[] propertyNames, Type[] types) { - // boolean result = super.onLoad(entity, id, state, propertyNames, - // types); - // context.fireVetoableLoad(id); - // return result; - // } - // - // @Override - // public void onDelete(Object entity, Serializable id, Object[] state, - // String[] propertyNames, Type[] types) { - // super.onDelete(entity, id, state, propertyNames, types); - // context.fireVetoableDelete(id); - // context.fireOnDeleted(entity); - // } - // - // @Override - // public boolean onSave(Object entity, Serializable id, Object[] state, - // String[] propertyNames, Type[] types) { - // boolean result = super.onSave(entity, id, state, propertyNames, - // types); - // context.fireVetoableUpdate(id); - // context.fireOnUpdated(entity); - // return result; - // } - // } - + /** * Object permettant de faire le lien entre les events hibernate et topia * @@ -1352,8 +1332,12 @@ TopiaContextImplementor context = getContext(rootContext, event .getSession()); if (context != null) { + Object id = event.getId(); context.fireOnUpdated(event.getPersister().getMappedClass( - EntityMode.POJO), event.getId(), event.getEntity()); + EntityMode.POJO), id, event.getEntity()); + if (context.isIndexEnabled()) { + context.getIndexEngin().addForIndexation(id, event.getState()); + } } } @@ -1366,8 +1350,12 @@ TopiaContextImplementor context = getContext(rootContext, event .getSession()); if (context != null) { + Object id = event.getId(); context.fireOnDeleted(event.getPersister().getMappedClass( - EntityMode.POJO), event.getId(), event.getEntity()); + EntityMode.POJO), id, event.getEntity()); + if (context.isIndexEnabled()) { + context.getIndexEngin().addForIndexation(id, null); + } } } } @@ -1377,23 +1365,61 @@ * propriétés du context. Si le type n'est pas connu, la variable reste à * null. */ + protected void initIndexEngin() { + Properties props = getConfig(); + indexEngin = null; + if (props != null) { + String classIndexEngin = props.getProperty(TOPIA_INDEX_ENGIN); + try { + Class forName = Class.forName(classIndexEngin); + Object newInstance = forName.newInstance(); + indexEngin = (IndexEngin) newInstance; + indexEngin.init(this); + } catch (Throwable eee) { + if(log.isErrorEnabled()) { + log.error("IndexEngin inconnu : " + classIndexEngin); + } + if(log.isDebugEnabled()) { + log.debug(eee); + } + } + } + } + + public boolean isIndexEnabled() { + return getIndexEngin() != null; + } + + public IndexEngin getIndexEngin() { + TopiaContextImplementor parent = getParentContext(); + if(parent != null) { + return parent.getIndexEngin(); + } else { + return indexEngin; + } + } + + /** + * Initialise le TopiaSecurityManager en fonction des + * propriétés du context. Si le type n'est pas connu, la variable reste à + * null. + */ protected void initSecurity() { Properties props = getConfig(); securityManager = null; if (props != null) { - String classSecurityManger = props.getProperty(TOPIA_SECURITY_MANAGER); + String classIndexEngin = props.getProperty(TOPIA_SECURITY_MANAGER); try { - Class forName = Class.forName(classSecurityManger); - Constructor constructor = forName.getConstructor(TopiaContext.class); - Object newInstance = constructor.newInstance(this); + Class forName = Class.forName(classIndexEngin); + Object newInstance = forName.newInstance(); securityManager = (TopiaSecurityManager) newInstance; - securityManager.init(); + securityManager.init(this); } catch (Throwable eee) { - if(log.isWarnEnabled() || log.isErrorEnabled()) { - log.warn("Type de sécurité inconnu : " + classSecurityManger); - } if(log.isErrorEnabled()) { - log.error(eee); + log.error("IndexEngin inconnu : " + classIndexEngin); + } + if(log.isDebugEnabled()) { + log.debug(eee); } } } Index: topia2/src/java/org/codelutin/topia/framework/TopiaContextImplementor.java diff -u topia2/src/java/org/codelutin/topia/framework/TopiaContextImplementor.java:1.10 topia2/src/java/org/codelutin/topia/framework/TopiaContextImplementor.java:1.11 --- topia2/src/java/org/codelutin/topia/framework/TopiaContextImplementor.java:1.10 Mon Sep 18 15:53:13 2006 +++ topia2/src/java/org/codelutin/topia/framework/TopiaContextImplementor.java Mon Oct 9 14:20:18 2006 @@ -23,10 +23,10 @@ * Created: 3 janv. 2006 21:27:24 * * @author poussin - * @version $Revision: 1.10 $ + * @version $Revision: 1.11 $ * - * Last update: $Date: 2006/09/18 15:53:13 $ - * by : $Author: ruchaud $ + * Last update: $Date: 2006/10/09 14:20:18 $ + * by : $Author: bpoussin $ */ package org.codelutin.topia.framework; @@ -171,18 +171,21 @@ /** * Appeler apres la creation d'une entity * @param entity l'entite qui vient d'etre creee + * @param fields la valeur des champs de l'entity tel que stocké */ public void fireOnCreated(Class entityClass, Object id, Object entity); /** * Appeler apres la mise a jour d'une entity * @param entity l'entite qui vient d'etre modifee + * @param fields la valeur des champs de l'entity tel que stocké */ public void fireOnUpdated(Class entityClass, Object id, Object entity); /** * Appeler apres l'effacement d'une entity * @param entity l'entite qui vient d'etre effacée + * @param fields la valeur des champs de l'entity tel que stocké */ public void fireOnDeleted(Class entityClass, Object id, Object entity); Index: topia2/src/java/org/codelutin/topia/framework/IndexEngin.java diff -u /dev/null topia2/src/java/org/codelutin/topia/framework/IndexEngin.java:1.1 --- /dev/null Mon Oct 9 14:20:24 2006 +++ topia2/src/java/org/codelutin/topia/framework/IndexEngin.java Mon Oct 9 14:20:18 2006 @@ -0,0 +1,55 @@ +/* *##% + * Copyright (C) 2006 + * Code Lutin, Cédric Pineau, Benjamin Poussin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + *##%*/ + +/* * + * IndexEngin.java + * + * Created: 8 oct. 06 17:15:00 + * + * @author poussin + * @version $Revision: 1.1 $ + * + * Last update: $Date: 2006/10/09 14:20:18 $ + * by : $Author: bpoussin $ + */ + +package org.codelutin.topia.framework; + +import java.util.Map; + +import org.apache.lucene.search.Hits; + + +/** + * @author poussin + * + */ + +public interface IndexEngin extends TopiaService { + + public void addForIndexation(Object id, Object [] fields); + public void indexMap(); + public void clearIndexMap(); + + public Hits search(String queryText); + public Hits search(Map query); + +} + + Index: topia2/src/java/org/codelutin/topia/framework/LuceneIndexer.java diff -u /dev/null topia2/src/java/org/codelutin/topia/framework/LuceneIndexer.java:1.1 --- /dev/null Mon Oct 9 14:20:24 2006 +++ topia2/src/java/org/codelutin/topia/framework/LuceneIndexer.java Mon Oct 9 14:20:18 2006 @@ -0,0 +1,271 @@ +/* *##% + * Copyright (C) 2006 + * Code Lutin, Cédric Pineau, Benjamin Poussin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + *##%*/ + +/* * + * LuceneIndexer.java + * + * Created: 8 oct. 06 15:48:37 + * + * @author poussin + * @version $Revision: 1.1 $ + * + * Last update: $Date: 2006/10/09 14:20:18 $ + * by : $Author: bpoussin $ + */ + +package org.codelutin.topia.framework; + +import java.io.File; +import java.io.IOException; +import java.io.StringReader; +import java.util.Date; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Properties; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.analysis.SimpleAnalyzer; +import org.apache.lucene.document.Field; +import org.apache.lucene.index.IndexReader; +import org.apache.lucene.index.IndexWriter; +import org.apache.lucene.index.Term; +import org.apache.lucene.queryParser.ParseException; +import org.apache.lucene.queryParser.QueryParser; +import org.apache.lucene.search.Hits; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.Searcher; +import org.codelutin.topia.TopiaNotFoundException; +import org.codelutin.topia.persistence.TopiaId; +import org.hibernate.HibernateException; +import org.hibernate.metadata.ClassMetadata; + +/** + * To use this indexer you must have two properties defined in config file: + *
  • topia.index.engin=org.codelutin.topia.framework.LuceneIndexer + *
  • topia.index.lucene.directory=[/path/to/index/directory] + * + * @author poussin + * + */ +public class LuceneIndexer implements IndexEngin { + + private static final String TOPIA_ID = "topiaId"; + + /** to use log facility, just put in your code: log.info(\"...\"); */ + static private Log log = LogFactory.getLog(LuceneIndexer.class); + + protected File directory = null; + protected TopiaContextImplementor context; + /** contient les objets a reindexer car creer, modifier ou supprimer. key: id, value: fields values or null for deletion */ + protected Map indexationMap = new HashMap(); + + /** + * + */ + public LuceneIndexer() { + } + + public void addForIndexation(Object id, Object [] fields) { + indexationMap.put(id, fields); + } + + public void init(TopiaContextImplementor context) { + this.context = context; + Properties prop = context.getConfig(); + String dirname = prop.getProperty("topia.index.lucene.directory"); + directory = new File(dirname); + directory.mkdirs(); + } + + public void clearIndexMap() { + indexationMap.clear(); + } + + public void indexMap() { + try { + boolean create = false; + if (!IndexReader.indexExists(directory)) { + // si l'index n'existe pas, on force la creation + create = true; + } else { + IndexReader reader = IndexReader.open(directory); + + for (Map.Entry e : indexationMap.entrySet()) { + String id = e.getKey().toString(); + removeIndex(reader, id); + } + reader.close(); + } + + IndexWriter writer = new IndexWriter(directory, new SimpleAnalyzer(), create); + for (Map.Entry e : indexationMap.entrySet()) { + String id = e.getKey().toString(); + Object [] fields = e.getValue(); + if (fields != null) { + try { + index(writer, id, fields); + } catch (HibernateException eee) { + if (log.isWarnEnabled()) { + log.warn("Can't index: " + id); + if (log.isDebugEnabled()) { + log.debug("StackTrace", eee); + } + } + } catch (TopiaNotFoundException eee) { + if (log.isWarnEnabled()) { + log.warn("Can't index: " + id); + if (log.isDebugEnabled()) { + log.debug("StackTrace", eee); + } + } + } + } + } + writer.close(); + } catch (IOException eee) { + if (log.isWarnEnabled()) { + log.warn("Can't index"); + if (log.isDebugEnabled()) { + log.debug("StackTrace", eee); + } + } + } + } + + /** + * @param id identifiant de l'objet a indexer + * @param fields les champs de l'objet + * @throws IOException + * @throws TopiaNotFoundException + * @throws HibernateException + */ + private void index(IndexWriter writer, String id, Object [] fields) throws IOException, HibernateException, TopiaNotFoundException { + String classname = TopiaId.getClassNameAsString(id); + ClassMetadata cm = context.getHibernateFactory().getClassMetadata(classname + "Impl"); + String [] names = cm.getPropertyNames(); + + org.apache.lucene.document.Document doc = new org.apache.lucene.document.Document(); + doc.add(new Field("class", classname, Field.Store.YES, Field.Index.TOKENIZED)); + doc.add(new Field("topiaId", id, Field.Store.YES, Field.Index.TOKENIZED)); + StringBuffer all = new StringBuffer(); + for (int i=0; i 0) { + doc.add(new Field("__all__", all.toString(), Field.Store.NO, Field.Index.TOKENIZED)); + } + writer.addDocument(doc); + } + + /** + * @param ididentifiant de l'objet a supprimer + * @throws IOException + */ + private void removeIndex(IndexReader reader, String id) throws IOException { + Term term = new Term(TOPIA_ID, id); + reader.deleteDocuments(term); + } + + public Hits search(String queryText) { + Hits result = null; + if (IndexReader.indexExists(directory)) { + try { + Searcher searcher = new IndexSearcher(directory.getAbsolutePath()); + Analyzer analyzer = new SimpleAnalyzer(); + + if (queryText.equals("") || queryText.length() == 0) { + // la chaine passée en parametre est vide ! + log.debug("requete vide, pas de resultat a renvoyer."); + return result; + } + QueryParser parser = new QueryParser("__all__", analyzer); + Query luceneQuery = parser.parse(queryText); + if (log.isDebugEnabled()) { + log.debug("Recherche du terme : " + + luceneQuery.toString()); + } + result = searcher.search(luceneQuery); + searcher.close(); + } catch (IOException ioe) { + if (log.isDebugEnabled()) { + log.debug(ioe.getMessage(), ioe); + } + } catch (ParseException pe) { + if (log.isDebugEnabled()) { + log.debug(pe.getMessage(), pe); + } + } + } else { + // l'index n'a pas encore ete créé ! + if (log.isDebugEnabled()) { + log.debug("Index inexistant, pas de resultats à renvoyer !"); + } + } + //retourne les resultats trouves + return result; + } + + /** + * @param query Map that contains field name as key and query in value for + * this field + * @return + */ + public Hits search(Map query) { + Hits result = null; + // construction de la requete lucene + String queryText = ""; + Iterator keys = query.keySet().iterator(); + while (keys.hasNext()) { + String key = (String) keys.next(); + String value = (String) query.get(key); + if (value != null) { + // on decoupe la valeur pour separer les chaines, + // sinon lucene cherche la chaine complete au lieu + // des mots + String[] st = value.split("\\s"); + for (int i = 0; i < st.length; i++) { + String token = st[i]; + if ((token != null) && (!token.equals(""))) { + queryText += key + ":" + st[i] + " "; + } + } + } + } + result = search(queryText); + + //retourne les resultats trouves + return result; + } +} + + Index: topia2/src/java/org/codelutin/topia/framework/TopiaService.java diff -u /dev/null topia2/src/java/org/codelutin/topia/framework/TopiaService.java:1.1 --- /dev/null Mon Oct 9 14:20:24 2006 +++ topia2/src/java/org/codelutin/topia/framework/TopiaService.java Mon Oct 9 14:20:18 2006 @@ -0,0 +1,45 @@ +/* *##% + * Copyright (C) 2006 + * Code Lutin, Cédric Pineau, Benjamin Poussin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + *##%*/ + +/* * + * TopiaService.java + * + * Created: 8 oct. 06 17:15:52 + * + * @author poussin + * @version $Revision: 1.1 $ + * + * Last update: $Date: 2006/10/09 14:20:18 $ + * by : $Author: bpoussin $ + */ + +package org.codelutin.topia.framework; + + +/** + * @author poussin + * + */ +public interface TopiaService { + + public void init(TopiaContextImplementor context); + +} + +