Author: glandais Date: 2007-12-04 18:31:27 +0000 (Tue, 04 Dec 2007) New Revision: 36 Added: trunk/simexplorer-si-storage/src/java/org/cemagref/simexplorer/si/storage/database/ trunk/simexplorer-si-storage/src/java/org/cemagref/simexplorer/si/storage/database/Database.java trunk/simexplorer-si-storage/src/java/org/cemagref/simexplorer/si/storage/database/lucene/ trunk/simexplorer-si-storage/src/java/org/cemagref/simexplorer/si/storage/database/lucene/LuceneConstants.java trunk/simexplorer-si-storage/src/java/org/cemagref/simexplorer/si/storage/database/lucene/LuceneDatabase.java Log: Database interface and Lucene implementation Added: trunk/simexplorer-si-storage/src/java/org/cemagref/simexplorer/si/storage/database/Database.java =================================================================== --- trunk/simexplorer-si-storage/src/java/org/cemagref/simexplorer/si/storage/database/Database.java (rev 0) +++ trunk/simexplorer-si-storage/src/java/org/cemagref/simexplorer/si/storage/database/Database.java 2007-12-04 18:31:27 UTC (rev 36) @@ -0,0 +1,48 @@ +package org.cemagref.simexplorer.si.storage.database; + +import java.util.Map; +import java.util.Set; + +import org.cemagref.simexplorer.si.storage.entities.LoggableElement; + +public abstract class Database { + + public Database() throws Throwable { + super(); + init(); + } + + protected abstract void init() throws Throwable; + + // Create / Update + + public abstract void pushElement(LoggableElement element) throws Throwable; + + // Read + + public abstract LoggableElement findElement(String uuid, Integer majorVersion, + Integer minorVersion) throws Throwable; + + public abstract LoggableElement getElementLatestVersion(String uuid) + throws Throwable; + + public abstract Set<LoggableElement> findElementsById(String uuid) throws Throwable; + + public abstract Set<LoggableElement> findElementsByProperties(Map<String, String> properties) throws Throwable; + + public abstract Set<LoggableElement> findElementsByProperties(Map<String, String> properties, Class entityClass) throws Throwable; + + // Delete + + public void deleteElement(LoggableElement element) throws Throwable { + deleteElement(element.getUuid(), element.getMajorVersion(), element + .getMinorVersion()); + } + + public abstract void deleteElement(String uuid) throws Throwable; + + public abstract void deleteElement(String uuid, Integer majorVersion, + Integer minorVersion) throws Throwable; + + +} Added: trunk/simexplorer-si-storage/src/java/org/cemagref/simexplorer/si/storage/database/lucene/LuceneConstants.java =================================================================== --- trunk/simexplorer-si-storage/src/java/org/cemagref/simexplorer/si/storage/database/lucene/LuceneConstants.java (rev 0) +++ trunk/simexplorer-si-storage/src/java/org/cemagref/simexplorer/si/storage/database/lucene/LuceneConstants.java 2007-12-04 18:31:27 UTC (rev 36) @@ -0,0 +1,11 @@ +package org.cemagref.simexplorer.si.storage.database.lucene; + +public interface LuceneConstants { + + public static final String KEY_CLASSNAME = "simexplorer.classname"; + public static final String KEY_UUID = "simexplorer.uuid"; + public static final String KEY_DESCRIPTION = "simexplorer.description"; + public static final String KEY_MAJORVERSION = "simexplorer.version.major"; + public static final String KEY_MINORVERSION = "simexplorer.version.minor"; + +} Added: trunk/simexplorer-si-storage/src/java/org/cemagref/simexplorer/si/storage/database/lucene/LuceneDatabase.java =================================================================== --- trunk/simexplorer-si-storage/src/java/org/cemagref/simexplorer/si/storage/database/lucene/LuceneDatabase.java (rev 0) +++ trunk/simexplorer-si-storage/src/java/org/cemagref/simexplorer/si/storage/database/lucene/LuceneDatabase.java 2007-12-04 18:31:27 UTC (rev 36) @@ -0,0 +1,250 @@ +package org.cemagref.simexplorer.si.storage.database.lucene; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; + +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; + +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.analysis.PerFieldAnalyzerWrapper; +import org.apache.lucene.analysis.SimpleAnalyzer; +import org.apache.lucene.document.Document; +import org.apache.lucene.document.Field; +import org.apache.lucene.index.CorruptIndexException; +import org.apache.lucene.index.IndexReader; +import org.apache.lucene.index.IndexWriter; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.BooleanClause; +import org.apache.lucene.search.BooleanQuery; +import org.apache.lucene.search.Hits; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.Searcher; +import org.apache.lucene.search.TermQuery; +import org.cemagref.simexplorer.si.storage.dao.ElementDAOFactory; +import org.cemagref.simexplorer.si.storage.dao.factories.LoggableElementFactory; +import org.cemagref.simexplorer.si.storage.database.Database; +import org.cemagref.simexplorer.si.storage.entities.LoggableElement; +import org.cemagref.simexplorer.si.storage.tools.Version; +import org.w3c.dom.Element; + +public class LuceneDatabase extends Database implements LuceneConstants {// implements + // LuceneDatabaseInterface + // { + + private static Analyzer analyser; + private static IndexWriter writer; + private static Searcher searcher; + private static boolean initok = false; + + public LuceneDatabase() throws Throwable { + super(); + } + + protected void init() throws Throwable { + if (!initok) { + analyser = new PerFieldAnalyzerWrapper(new SimpleAnalyzer()); + // analyser.addAnalyzer("firstname", new KeywordAnalyzer()); + // analyser.addAnalyzer("lastname", new KeywordAnalyzer()); + + String directory = "./index/"; + + boolean create = false; + if (!IndexReader.indexExists(directory)) { + // si l'index n'existe pas, on force la creation + create = true; + } + writer = new IndexWriter(directory, analyser, create); + searcher = new IndexSearcher(directory); + initok = true; + } + } + + @Override + public void pushElement(LoggableElement element) throws Throwable { + LoggableElementFactory elementFactory = (LoggableElementFactory) ElementDAOFactory + .getFactory(element.getClass()); + Document document = saveLuceneElement(elementFactory, element); + + LoggableElement oldElement = findElement(element.getUuid(), element + .getMajorVersion(), element.getMinorVersion()); + if (oldElement != null) { + // update + deleteElement(element); + } + + writer.addDocument(document); + } + + @Override + public LoggableElement findElement(String uuid, Integer majorVersion, + Integer minorVersion) throws Throwable { + + Map<String, String> properties = new HashMap<String, String>(); + properties.put(KEY_UUID, uuid); + properties.put(KEY_MAJORVERSION, majorVersion.toString()); + properties.put(KEY_MINORVERSION, minorVersion.toString()); + + Hits hits = findHits(properties); + if (hits.length() == 0) { + return null; + } else { + return convertDocumentToElement(hits.doc(0)); + } + } + + @Override + public LoggableElement getElementLatestVersion(String uuid) + throws Throwable { + LoggableElement element = null; + + Map<String, String> properties = new HashMap<String, String>(); + properties.put(KEY_UUID, uuid); + Hits hits = findHits(properties); + if (hits.length() > 0) { + List<Version> versions = new ArrayList<Version>(); + for (int i = 0; i < hits.length(); i++) { + Document doc = hits.doc(i); + Version version = new Version(); + version.setMajorVersion(new Integer(doc.get(KEY_MAJORVERSION))); + version.setMinorVersion(new Integer(doc.get(KEY_MINORVERSION))); + version.setVersionnedObject(doc); + } + Collections.sort(versions); + Collections.reverse(versions); + + Document latestDoc = (Document) versions.get(0) + .getVersionnedObject(); + LoggableElementFactory elementFactory = (LoggableElementFactory) ElementDAOFactory + .getFactory(latestDoc.get(KEY_CLASSNAME)); + element = loadLuceneElement(elementFactory, latestDoc); + } + + return element; + } + + @Override + public Set<LoggableElement> findElementsById(String uuid) throws Throwable { + Map<String, String> properties = new HashMap<String, String>(); + properties.put(KEY_UUID, uuid); + return convertHitsToElements(findHits(properties)); + } + + @Override + public Set<LoggableElement> findElementsByProperties( + Map<String, String> properties) throws Throwable { + return convertHitsToElements(findHits(properties)); + } + + @Override + public Set<LoggableElement> findElementsByProperties( + Map<String, String> properties, Class entityClass) throws Throwable { + Map<String, String> props = new HashMap<String, String>(); + props.putAll(properties); + props.put(KEY_CLASSNAME, entityClass.getName()); + return convertHitsToElements(findHits(properties)); + } + + public void deleteElement(String uuid) throws Throwable { + writer.deleteDocuments(new Term(KEY_UUID, uuid)); + } + + public void deleteElement(String uuid, Integer majorVersion, + Integer minorVersion) throws Throwable { + Term[] terms = new Term[3]; + terms[0] = new Term(KEY_UUID, uuid); + terms[1] = new Term(KEY_MAJORVERSION, uuid); + terms[2] = new Term(KEY_MINORVERSION, uuid); + writer.deleteDocuments(terms); + } + + private Hits findHits(Map<String, String> properties) throws Throwable { + // FIXME disablecoords? + BooleanQuery query = new BooleanQuery(false); + for (Entry<String, String> kv : properties.entrySet()) { + query.add(new TermQuery(new Term(kv.getKey(), kv.getValue())), + BooleanClause.Occur.MUST); + } + return searcher.search(query); + } + + private LoggableElement convertDocumentToElement(Document document) + throws Throwable { + String className = document.get(KEY_CLASSNAME); + LoggableElementFactory factory = (LoggableElementFactory) ElementDAOFactory + .getFactory(className); + return loadLuceneElement(factory, document); + } + + private Set<LoggableElement> convertHitsToElements(Hits hits) + throws Throwable { + Set<LoggableElement> elements = new HashSet<LoggableElement>(); + for (int i = 0; i < hits.length(); i++) { + Document document = hits.doc(i); + elements.add(convertDocumentToElement(document)); + } + return elements; + } + + private LoggableElement loadLuceneElement(LoggableElementFactory factory, Document document) throws Throwable { + String strXMLElement = document.get("xmlElement"); + + InputStream is = new ByteArrayInputStream(strXMLElement.getBytes()); + org.w3c.dom.Document xmlDocument = factory.getBuilder().parse(is); + + LoggableElement element = factory.loadXMLElement((Element) xmlDocument.getFirstChild()); + + return element; + } + + private Document saveLuceneElement(LoggableElementFactory factory, LoggableElement element) throws Throwable { + Document document = new Document(); + + // FIXME save searchable/metadata fields + document.add(new Field(KEY_UUID, element.getUuid().getBytes(), + Field.Store.YES)); + document.add(new Field(KEY_DESCRIPTION, element.getDescription().getBytes(), + Field.Store.YES)); + document.add(new Field(KEY_CLASSNAME, element.getClass().getName().getBytes(), + Field.Store.YES)); + document.add(new Field(KEY_MINORVERSION, element.getMinorVersion().toString().getBytes(), + Field.Store.YES)); + document.add(new Field(KEY_MAJORVERSION, element.getMajorVersion().toString().getBytes(), + Field.Store.YES)); + + org.w3c.dom.Document xmlDocument = factory.getBuilder().newDocument(); + // Propriétés du DOM + xmlDocument.setXmlVersion("1.0"); + xmlDocument.setXmlStandalone(true); + // Création de l'arborescence du DOM + Element racine = xmlDocument.createElement(element.getClass().getName()); + factory.saveXMLElement(xmlDocument, racine, element); + xmlDocument.appendChild(racine); + DOMSource domSource = new DOMSource(xmlDocument); + StringWriter writer = new StringWriter(); + StreamResult result = new StreamResult(writer); + TransformerFactory tf = TransformerFactory.newInstance(); + Transformer transformer = tf.newTransformer(); + transformer.transform(domSource, result); + String stringResult = writer.toString(); + document.add(new Field("xmlElement", stringResult, Field.Store.COMPRESS, Field.Index.NO)); + + return document; + } + + public void shutDown() throws Throwable { + writer.close(); + } +}