/* Index ECM Engine - A system for managing the capture (when created
* or received), classification (cataloguing), storage, retrieval,
* revision, sharing, reuse and disposition of documents.
*
* Copyright (C) 2008 Regione Piemonte
* Copyright (C) 2008 Provincia di Torino
* Copyright (C) 2008 Comune di Torino
*
* 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,
* 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
package it.doqui.index.ecmengine.business.personalization.multirepository.index.lucene;
import it.doqui.index.ecmengine.business.personalization.multirepository.util.EcmEngineMultirepositoryConstants;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Set;
import org.alfresco.repo.search.impl.lucene.LuceneAnalyser;
import org.alfresco.repo.search.impl.lucene.LuceneConfig;
import org.alfresco.repo.search.impl.lucene.LuceneIndexException;
import org.alfresco.repo.search.impl.lucene.index.IndexInfo;
import org.alfresco.repo.search.impl.lucene.index.TransactionStatus;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.apache.log4j.Logger;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.Term;
/**
* Classe che proxa tutte le chiamate a {@link IndexInfo} di alfresco. Tutti i metodi sono stateless e ricevono sempre il deltaid e il repopath che
* servono al'inexInfo sottostante per accedere al giusto indice. Questi sono gli unici due parametri con cui l'indexInfo pu� referenziare un indice
* locale: se l'indice non � presente viene creato. Questo comportamento permette di ricreare su ogni macchina la stessa strutura di indici. Tutti i
* membri della classe sono transient in quanto NON devono essere condivisi da terracotta. Tutti i metodi sono synchronized in modo che un solo thread
* alla volta possa accedervi, anche in configurazione distribuita. Molte chiamate sono semplicemente una replica di ci� che prima veniva fatto con
* IndexInfo. Si faccia riferimento a {@link IndexInfo} per maggiori dettagli.
*
* @see IndexInfo
* @author Roberto Franchini
*/
public class IndexInfoProxyServiceNoTC implements IndexInfoProxyServiceInterface {
private static transient Logger s_logger = Logger.getLogger(EcmEngineMultirepositoryConstants.MULTIREPOSITORY_INDEX_LUCENE_LOG_CATEGORY);
/**
* The Lucene configuration options.
*/
private transient LuceneConfig config;
/**
* Il sottostante indexInfo
*/
private transient IndexInfo indexInfo;
/**
* The dictionary service.
*/
private static transient DictionaryService dictionaryService;
/**
* Metodo di inizializzazione. E' chiamato localmente (e.g.: sulla macchina) per settare il valore dei membri della classe ed ottenere il corretto
* IndexInfo
*
* @param dictionaryService
* @param luceneConfig
* @param repoPath
*/
public synchronized void init(DictionaryService dictionaryService, LuceneConfig luceneConfig, String repoPath) {
setDictionaryService(dictionaryService);
this.config = luceneConfig;
initIndexInfo(repoPath);
if(s_logger.isDebugEnabled()){
s_logger.debug("[IndexInfoProxyServiceNoTC::init] (" +this +") Status: " + toStringStatic() + " - isSafe: " + isIntegritySafe());
s_logger.debug("[IndexInfoProxyServiceNoTC::init] DST: " +this.dictionaryService );
s_logger.debug("[IndexInfoProxyServiceNoTC::init] DS: " +dictionaryService );
s_logger.debug("[IndexInfoProxyServiceNoTC::init] LC: " +luceneConfig );
s_logger.debug("[IndexInfoProxyServiceNoTC::init] RP: " +repoPath );
}
}
/**
* Dato il repoPath inizializza l'Indexinfo. Viene chiamato priam di ogni operazione per ottenere l'indexInfo che gestisce il repoPath. Questa
* operazione viene fatta per essere certi che il repoPath di una macchina appartenente al cluster sia replicato anche sulle altre.
*
* @param repoPath il path dello store degli indici
*/
private synchronized void initIndexInfo(String repoPath) {
if(s_logger.isDebugEnabled()){
s_logger.debug("[IndexInfoProxyServiceNoTC::initIndexInfo] (" +this +") IndexInfo : " +repoPath);
}
String basePath = config.getIndexRootLocation() + File.separator + repoPath;
final File baseDir = new File(basePath);
if(s_logger.isDebugEnabled()){
s_logger.debug("[IndexInfoProxyServiceNoTC::initIndexInfo] (" +this +") IndexInfo pre : " + indexInfo);
}
indexInfo = IndexInfo.getIndexInfo(baseDir, config);
if(s_logger.isDebugEnabled()){
s_logger.debug("[IndexInfoProxyServiceNoTC::initIndexInfo] (" +this +") IndexInfo post: " + indexInfo);
}
}
/**
* Metodo di utilit� per verificare che il proxy sia "integro". Utile in debug.
*
* @return true se il proxy � in situazione integra
*/
public synchronized boolean isIntegritySafe() {
return config != null && indexInfo != null;
}
/**
* Inserisce un documento lucene in un indice identificato da deltaId e repoPath
*
* @param doc il documento da inserire
* @param deltaId dell'indice da usare
* @param repoPath path dello store degli indici
* @throws LuceneIndexException
* @throws IOException
*/
public synchronized void insert(Document doc, String deltaId, String repoPath) throws LuceneIndexException, IOException {
IndexWriter writer = getDeltaWriter(deltaId, repoPath);
insert(doc, writer);
}
/**
* Inserisce una lista di documenti lucene in un indice identificato da deltaId e repoPath
*
* @param docs lista dei documenti da inserire
* @param deltaId dell'indice da usare
* @param repoPath path dello store degli indici
* @throws LuceneIndexException
* @throws IOException
*/
public synchronized void insert(List<Document> docs, String deltaId, String repoPath) throws LuceneIndexException, IOException {
IndexWriter writer = getDeltaWriter(deltaId, repoPath);
for (Document doc : docs) {
try {
insert(doc, writer);
} catch (IOException e) {
s_logger.error("[IndexInfoProxyServiceNoTC::insert] (" +this +") Failed to add document to index: " +e);
throw new LuceneIndexException("Failed to add document to index", e);
}
}
}
/**
* Metodo di utilit� privato che inserisce un singolo documento lucene nell'indice su cui � aperto il writer
*
* @param doc documento lucene da inserire
* @param writer da utilizzare per inserire il documento
* @throws LuceneIndexException
* @throws IOException
*/
private synchronized void insert(Document doc, IndexWriter writer) throws LuceneIndexException, IOException {
if(s_logger.isDebugEnabled()){
s_logger.debug("[IndexInfoProxyServiceNoTC::insert] (" +this +") Writing doc: " + doc.getField("ID") + " [To directory: " + writer.getDirectory() + "]");
//s_logger.debug("[IndexInfoProxyServiceNoTC::insert] " + "Document: ["+doc.toString().replaceAll(" ", "\r\n")+"]");
}
writer.addDocument(doc);
}
/**
* Cacella una lista di documenti identificati dalla loro id (lucene id)
*
* @param docs lista di id
* @param deltaId dell'indice da usare
* @param repoPath path dello store degli indici
* @throws LuceneIndexException
* @throws IOException
*/
public synchronized void delete(List<Integer> docs, String deltaId, String repoPath) throws LuceneIndexException, IOException {
IndexReader reader = getDeltaReader(deltaId, repoPath);
for (Integer doc : docs) {
try {
reader.deleteDocument(doc.intValue());
} catch (IOException e) {
throw new LuceneIndexException("Failed to add document to index", e);
}
}
}
/**
* Cancella una lista di documenti indentificati da un Term
*
* @param term il Term che identifica i documenti
* @param deltaId dell'indice da usare
* @param repoPath path dello store degli indici
* @throws LuceneIndexException
* @throws IOException
*/
public synchronized void delete(Term term, String deltaId, String repoPath) throws LuceneIndexException, IOException {
if(s_logger.isDebugEnabled()){
s_logger.debug("[IndexInfoProxyServiceNoTC::delete] (" +this +") deltaId: " + deltaId);
}
getDeltaReader(deltaId, repoPath).deleteDocuments(term);
}
/**
* Cancella un singolo documento identificato dalla id lucene
*
* @param doc id del documento
* @param deltaId dell'indice da usare
* @param repoPath path dello store degli indici
* @throws LuceneIndexException
* @throws IOException
*/
public synchronized void delete(int doc, String deltaId, String repoPath) throws LuceneIndexException, IOException {
if(s_logger.isDebugEnabled()){
s_logger.debug("[IndexInfoProxyServiceNoTC::delete] (" +this +") deltaId: " + deltaId);
}
getDeltaReader(deltaId, repoPath).deleteDocument(doc);
}
/**
* Ottiene dall'IndexInfo il main reader per un certo RepoPath
*
* @param repoPath path dello store degli indici
* @return il reader
* @throws LuceneIndexException
* @throws IOException
*/
public synchronized IndexReader getReader(String repoPath) throws LuceneIndexException, IOException {
if(s_logger.isDebugEnabled()){
s_logger.debug("[IndexInfoProxyServiceNoTC::getReader] (" +this +") Status: " + toStringStatic() + " - isSafe: " + isIntegritySafe());
}
initIndexInfo(repoPath);
return indexInfo.getMainIndexReferenceCountingReadOnlyIndexReader();
}
/**
* @param deltaId dell'indice da usare
* @param repoPath path dello store degli indici
* @return il reader
* @throws LuceneIndexException
* @throws IOException
*/
public synchronized IndexReader getDeltaReader(String deltaId, String repoPath) throws LuceneIndexException, IOException {
if(s_logger.isDebugEnabled()){
s_logger.debug("[IndexInfoProxyServiceNoTC::getDeltaReader] (" +this +") deltaId: " + deltaId);
}
initIndexInfo(repoPath);
return indexInfo.getDeltaIndexReader(deltaId);
}
/**
* Chiude il reader associato ad un certo repoPath e deltaId
*
* @param deltaId dell'indice da usare
* @param repoPath path dello store degli indici
* @throws IOException
*/
public synchronized void closeDeltaReader(String deltaId, String repoPath) throws IOException {
if(s_logger.isDebugEnabled()){
s_logger.debug("[IndexInfoProxyServiceNoTC::closeDeltaReader] (" +this +") deltaId: " + deltaId);
}
initIndexInfo(repoPath);
indexInfo.closeDeltaIndexReader(deltaId);
}
/**
* Dati deltaId e repoPath ottiene un writer per l'indice lucene cos� identificato
*
* @param deltaId dell'indice da usare
* @param repoPath path dello store degli indici
* @return il writer aperto sull'indice corretto
* @throws LuceneIndexException
* @throws IOException
*/
private synchronized IndexWriter getDeltaWriter(String deltaId, String repoPath) throws LuceneIndexException, IOException {
if(s_logger.isDebugEnabled()){
s_logger.debug("[IndexInfoProxyServiceNoTC::getDeltaWriter] (" +this +") DS(" +(dictionaryService==null?"null":"object") +") deltaId: " + deltaId);
}
initIndexInfo(repoPath);
return indexInfo.getDeltaIndexWriter(deltaId, new LuceneAnalyser(dictionaryService, config.getDefaultMLIndexAnalysisMode()));
}
/**
* Dati deltaId e repoPath chiude writer per l'indice lucene cos� identificato
*
* @param deltaId dell'indice da usare
* @param repoPath path dello store degli indici
* @throws IOException
*/
public synchronized void closeDeltaWriter(String deltaId, String repoPath) throws IOException {
if(s_logger.isDebugEnabled()){
s_logger.debug("[IndexInfoProxyServiceNoTC::closeDeltaWriter] (" +this +") deltaId: " + deltaId);
}
initIndexInfo(repoPath);
indexInfo.closeDeltaIndexWriter(deltaId);
}
/**
* Il "salvataggio" di un delta implica la chiusura di reader e writer aperti
*
* @param deltaId dell'indice da usare
* @param repoPath path dello store degli indici
* @throws LuceneIndexException
* @throws IOException
*/
public synchronized void saveDelta(String deltaId, String repoPath) throws LuceneIndexException, IOException {
if(s_logger.isDebugEnabled()){
s_logger.debug("[IndexInfoProxyServiceNoTC::saveDelta] (" +this +") deltaId: " + deltaId);
}
closeDeltaReader(deltaId, repoPath);
closeDeltaWriter(deltaId, repoPath);
}
/**
* Ritorna in numero di documenti presenti delta di un certorepository
*
* @param deltaId dell'indice da usare
* @param repoPath path dello store degli indici
* @return il numero di documenti presenti
* @throws LuceneIndexException
* @throws IOException
*/
public synchronized long getDocCount(String deltaId, String repoPath) throws LuceneIndexException, IOException {
initIndexInfo(repoPath);
long nDoc = getDeltaWriter(deltaId, repoPath).docCount();
return nDoc;
}
/**
* Chiude il mainReader associato ad un repository
*
* @param repoPath path dello store degli indici
* @throws LuceneIndexException
* @throws IOException
*/
public synchronized void closeMainReader(String repoPath) throws LuceneIndexException, IOException {
if(s_logger.isDebugEnabled()){
s_logger.debug("[IndexInfoProxyServiceNoTC::closeMainReader] (" +this +") Status: " + toString() + " - isSafe: " + isIntegritySafe());
}
// Creo un nuovo riferimento al main
IndexReader main = getReader(repoPath);
// Chiudo il primo invocato dalla chiamata getReader()
main.close();
// Chiudo il secondo che era quello che dovevo effettivamente chiudere
main.close();
}
/**
* Setta sull'indexInfo sottostante lo stato di un dato deltaId. Utilizzato per gestire la transazionalit� sui delta
*
* @param deltaId dell'indice da usare
* @param repoPath path dello store degli indici
* @param state
* @throws IOException
*/
public synchronized void setStatus(String deltaId, String repoPath, TransactionStatus state) throws IOException {
if(s_logger.isDebugEnabled()){
s_logger.debug("[IndexInfoProxyServiceNoTC::setStatus] (" +this +") deltaId: " + deltaId + " - TransactionStatus: " + state);
}
initIndexInfo(repoPath);
// MB: Eccezione in caso di stato attivo
// Si e' verificato che creando un tenant, a volte vengono fatte 2 operazioni, nello stesso giro di
// transazione, sullo stesto STORE. Questo crea l'utilizzo di 2 delta indexer.
// Solo che essendo creati su ThreadLocal, c'e' una cache che restituisce 2 volte lo stesso indexer
// La cosa e' cosa e' corretta, se non fosse che, impostando 2 volte a ACTIVE il deta, Alfresco torna
// una eccezione.
// Per evitare l'eccezione, evitiamo di impostare 2 volte ad active un indice :)
// Prima di impostare, leggo lo stato attuale
/*
TransactionStatus ts = indexInfo.getIndexEntryStatus( deltaId );
if( ts!=null && // Ho il delta
ts==TransactionStatus.ACTIVE && // Il vecchio valore e' ACTIVE
state==TransactionStatus.ACTIVE ){ // Il nuovo valore e' ACTIVE
// Non imposto lo stato, dato che Alfresco mi darebbe eccezione
s_logger.debug("[IndexInfoProxyServiceNoTC::setStatus] (" +this +") deltaId: " + deltaId + " - mantenuto TransactionStatus: " + state);
} else {
s_logger.debug("[IndexInfoProxyServiceNoTC::setStatus] (" +this +") deltaId: " + deltaId + " - DA: " +ts +" A: " + state);
indexInfo.setStatus(deltaId, state, null, null);
}
*/
indexInfo.setStatus(deltaId, state, null, null);
}
/**
* Setta sull'indexInfo sottostante lo stato di un dato deltaId. Utilizzato per gestire la transazionalit� sui delta
*
* @param deltaId dell'indice da usare
* @param repoPath path dello store degli indici
* @param toDelete
* @param documents
* @param deleteNodesOnly
* @throws IOException
*/
public synchronized void setPreparedState(String deltaId, String repoPath, Set<String> toDelete, long documents, boolean deleteNodesOnly) throws IOException {
if(s_logger.isDebugEnabled()){
s_logger.debug("[IndexInfoProxyServiceNoTC::setPreparedState] (" +this +") deltaId: " + deltaId);
}
initIndexInfo(repoPath);
indexInfo.setPreparedState(deltaId, toDelete, documents, deleteNodesOnly);
}
/**
* Ottiene dal indexInfo un reader in readOnly
*
* @param deltaId dell'indice da usare
* @param repoPath path dello store degli indici
* @param deletions
* @param deleteOnlyNodes
* @return IndexReader
* @throws IOException
*/
public synchronized IndexReader getMainIndexReferenceCountingReadOnlyIndexReader(String deltaId, String repoPath, Set<String> deletions, boolean deleteOnlyNodes) throws IOException {
if(s_logger.isDebugEnabled()){
s_logger.debug("[IndexInfoProxyServiceNoTC::getMainIndexReferenceCountingReadOnlyIndexReader] (" +this +") Deltaid: " +deltaId + " - repoPath: " +repoPath);
}
initIndexInfo(repoPath);
return indexInfo.getMainIndexReferenceCountingReadOnlyIndexReader(deltaId, deletions, deleteOnlyNodes);
}
/**
* Setter per la luceneConfig FIXME: non pi� usato
*
* @param config
*/
//public synchronized void setConfig(LuceneConfig config) {
//this.config = config;
//}
/**
* Setter per indexInfo FIXME: non pi� usato
*
* @param indexInfo
*/
//public synchronized void setIndexInfo(IndexInfo indexInfo) {
//this.indexInfo = indexInfo;
//}
/**
* Setter per dictionaryService usato localmente e non condiviso fra le macchine
*
* @param dictionaryService
*/
public synchronized void setDictionaryService(DictionaryService dictionaryService) {
if(s_logger.isDebugEnabled()){
s_logger.debug("[IndexInfoProxyServiceNoTC::setDictionaryService] (" +this +") setDictionaryService: " +dictionaryService);
}
// Imposta il dictionary service solo se null
if( this.dictionaryService==null ){
this.dictionaryService = dictionaryService;
}
if( dictionaryService!=null ){
if( !(this.dictionaryService==dictionaryService) ){
s_logger.error("[IndexInfoProxyServiceNoTC::setDictionaryService] (" +this +") setDictionaryService: " +this.dictionaryService);
s_logger.error("[IndexInfoProxyServiceNoTC::setDictionaryService] (" +this +") setDictionaryService: " +dictionaryService);
}
}
}
/**
* Un toString che riporta lo stato interno del proxy. Utile per logging e debug.
*
* @return String
*/
public synchronized String toStringStatic() {
return " - luceneConfig: " + (config != null) + "- dictionaryService: " + (dictionaryService != null);
}
}