/********************************************************************************** * $URL: https://source.sakaiproject.org/svn/search/trunk/search-impl/impl/src/java/org/sakaiproject/search/indexer/impl/TransactionalIndexWorker.java $ * $Id: TransactionalIndexWorker.java 105078 2012-02-24 23:00:38Z ottenhoff@longsight.com $ *********************************************************************************** * * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009 The Sakai Foundation * * Licensed under the Educational Community License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.opensource.org/licenses/ECL-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * **********************************************************************************/ package org.sakaiproject.search.indexer.impl; import java.io.IOException; import java.io.Reader; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.lucene.document.Document; import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.index.Term; import org.sakaiproject.component.api.ServerConfigurationService; import org.sakaiproject.component.cover.ComponentManager; import org.sakaiproject.search.api.EntityContentProducer; import org.sakaiproject.search.api.SearchIndexBuilder; import org.sakaiproject.search.api.SearchService; import org.sakaiproject.search.api.rdf.RDFIndexException; import org.sakaiproject.search.api.rdf.RDFSearchService; import org.sakaiproject.search.indexer.api.IndexUpdateTransaction; import org.sakaiproject.search.indexer.api.IndexWorker; import org.sakaiproject.search.indexer.api.IndexWorkerDocumentListener; import org.sakaiproject.search.indexer.api.IndexWorkerListener; import org.sakaiproject.search.indexer.api.NoItemsToIndexException; import org.sakaiproject.search.model.SearchBuilderItem; import org.sakaiproject.search.transaction.api.IndexTransaction; import org.sakaiproject.search.transaction.api.IndexTransactionException; import org.sakaiproject.search.transaction.api.TransactionIndexManager; import org.sakaiproject.search.util.DigestStorageUtil; import org.sakaiproject.search.util.DocumentIndexingUtils; import org.sakaiproject.site.api.Site; import org.sakaiproject.site.api.ToolConfiguration; import org.sakaiproject.site.cover.SiteService; import org.sakaiproject.thread_local.api.ThreadLocalManager; /** * @author ieb Unit test * @see org.sakaiproject.search.indexer.impl.test.TransactionalIndexWorkerTest */ public class TransactionalIndexWorker implements IndexWorker { private static final Log log = LogFactory.getLog(TransactionalIndexWorker.class); /** * dependency */ private SearchIndexBuilder searchIndexBuilder; /** * dependency */ private TransactionIndexManager transactionIndexManager; /** * dependency */ private ServerConfigurationService serverConfigurationService; /** * dependency */ private RDFSearchService rdfSearchService; private SearchService searchService; public void setSearchService(SearchService searchService) { this.searchService = searchService; } /** * dependency */ private List<IndexWorkerListener> indexWorkerListeners = new ArrayList<IndexWorkerListener>(); /** * dependency */ private List<IndexWorkerDocumentListener> indexWorkerDocumentListeners = new ArrayList<IndexWorkerDocumentListener>(); private ThreadLocalManager threadLocalManager; public void init() { } public void destroy() { } public int process(int batchSize) { // get a list to perform this transaction IndexTransaction t = null; try { long start = System.currentTimeMillis(); Map<String, Object> m = new HashMap<String, Object>(); m.put(SearchBuilderQueueManager.BATCH_SIZE, batchSize); t = transactionIndexManager.openTransaction(m); int n = processTransaction(t); t.prepare(); long transactionID = t.getTransactionId(); t.commit(); long end = System.currentTimeMillis(); long time = end-start; if ( time == 0 ) { log.info("Indexed "+n+" documents in "+time+" ms into save point "+transactionID); } else { double dps = n*1000; dps = dps /(1.0*time); log.info("Indexed "+n+" documents in "+time+" ms "+dps+" documents/second into save point "+transactionID); } return n; } catch (NoItemsToIndexException nodx) { log.info("No Items To Index "); if (t != null) { try { t.rollback(); } catch (Exception ex) { log.warn("Transaction Rollback Failed ", ex); } } return 0; } catch (IndexTransactionException iex) { if (t == null) { log.warn("Transaction Failed to open ", iex); } else { log.warn("Transaction Failed ", iex); try { t.rollback(); } catch (Exception ex) { log.warn("Transaction Rollback Failed ", ex); } } return -1; } finally { if (t != null) { try { t.close(); } catch (Exception ex) { log.warn("Transaction Close Failed ", ex); } } } } private int processTransaction(IndexTransaction transaction) throws IndexTransactionException { IndexWriter indexWrite = null; IndexReader indexReader = null; int nprocessed = 0; DigestStorageUtil digestStorageUtil = new DigestStorageUtil(searchService); try { fireIndexStart(); Map<String, SearchBuilderItem> finalState = new HashMap<String, SearchBuilderItem>(); for (Iterator<SearchBuilderItem> tditer = ((IndexUpdateTransaction) transaction) .lockedItemIterator(); tditer.hasNext();) { SearchBuilderItem sbi = tditer.next(); finalState.put(sbi.getId(),sbi); } for (SearchBuilderItem sbi : finalState.values()) { if (log.isDebugEnabled()) { log.debug("Item [" + sbi.getName() + "] state [" + (sbi.isLocked()?"Locked to "+sbi.getLock():SearchBuilderItem.states[sbi.getSearchstate()]) + " action [" + SearchBuilderItem.actions[sbi.getSearchaction()] + "]"); } if (SearchBuilderItem.ACTION_ADD.equals(sbi.getSearchaction()) ) { indexReader = ((IndexUpdateTransaction) transaction).getIndexReader(); int ndel = indexReader.deleteDocuments(new Term( SearchService.FIELD_REFERENCE, sbi.getName())); if (log.isDebugEnabled()) { log.debug(ndel + " index documents deleted"); } } else if (SearchBuilderItem.ACTION_DELETE .equals(sbi.getSearchaction())) { if (log.isDebugEnabled()) { log.debug("-------------------Delete "+sbi.getId()); } indexReader = ((IndexUpdateTransaction) transaction) .getIndexReader(); int ndel = indexReader.deleteDocuments(new Term( SearchService.FIELD_REFERENCE, sbi.getName())); if (log.isDebugEnabled()) { log.debug(ndel + " index documents deleted"); } digestStorageUtil.deleteAllDigests(sbi.getName()); nprocessed++; } } for (SearchBuilderItem sbi : finalState.values()) { //is the component manager shutting down? if (ComponentManager.hasBeenClosed()) { log.warn("component Manager is shuting down won't attempt to index"); break; } Reader contentReader = null; String ref = null; try { if (log.isDebugEnabled()) { log.debug("Item [" + sbi.getName() + "] state [" + (sbi.isLocked()?"Locked to "+sbi.getLock():SearchBuilderItem.states[sbi.getSearchstate()]) + " action [" + SearchBuilderItem.actions[sbi.getSearchaction()] + "]"); } if (SearchBuilderItem.ACTION_ADD.equals(sbi.getSearchaction())) { ref = sbi.getName(); fireStartDocument(ref); try { // Entity entity = ref.getEntity(); EntityContentProducer sep = searchIndexBuilder .newEntityContentProducer(ref); boolean indexDoc = true; if (searchIndexBuilder.isOnlyIndexSearchToolSites()) { try { String siteId = sep.getSiteId(sbi.getName()); Site s = SiteService.getSite(siteId); ToolConfiguration t = s .getToolForCommonId("sakai.search"); //$NON-NLS-1$ if (t == null) { indexDoc = false; log.debug("Not indexing " //$NON-NLS-1$ + sbi.getName() + " as it has no search tool"); //$NON-NLS-1$ } } catch (Exception ex) { indexDoc = false; log.debug("Not indexing " + sbi.getName() //$NON-NLS-1$ + " as it has no site", ex); //$NON-NLS-1$ } } if (indexDoc && sep != null && sep.isForIndex(ref) && sep.getSiteId(ref) != null) { Document doc = DocumentIndexingUtils.createIndexDocument(ref, digestStorageUtil, sep, serverConfigurationService.getServerUrl(), contentReader); indexWrite = ((IndexUpdateTransaction) transaction) .getIndexWriter(); indexWrite.addDocument(doc); log.debug("Done Indexing Document " + doc); //$NON-NLS-1$ processRDF(ref, sep); sbi.setSearchstate(SearchBuilderItem.STATE_COMPLETED); nprocessed++; } else { if (log.isDebugEnabled()) { if (!indexDoc) { log .debug("Ignored Document: Filtered out by site " + ref); //$NON-NLS-1$ } else if (sep == null) { log .debug("Ignored Document: No EntityContentProducer " + ref); //$NON-NLS-1$ } else if (!sep.isForIndex(ref)) { log .debug("Ignored Document: Marked as Ignore " + ref); //$NON-NLS-1$ } else if (sep.getSiteId(ref) == null) { log.debug("Ignored Document: No Site ID " + ref); //$NON-NLS-1$ } else { log .debug("Ignored Document: Reason Unknown " + ref); //$NON-NLS-1$ } } sbi.setSearchstate(SearchBuilderItem.STATE_FAILED); } } catch (Exception e1) { log.warn(" Failed to index document for " + ref + " cause: " //$NON-NLS-1$ + e1.getMessage(), e1); sbi.setSearchstate(SearchBuilderItem.STATE_FAILED); } } } finally { if (contentReader != null) { try { contentReader.close(); } catch (IOException ioex) { log.debug(ioex); } } fireEndDocument(ref); } } } catch (Exception ex) { log.error("Failed to Add Documents ", ex); throw new IndexTransactionException(ex); } finally { fireIndexEnd(); } return nprocessed; } /** * */ private void fireIndexStart() { for (Iterator<IndexWorkerListener> itl = indexWorkerListeners.iterator(); itl .hasNext();) { IndexWorkerListener iwl = itl.next(); iwl.indexWorkerStart(this); } } /** * @param ref */ private void fireStartDocument(String ref) { for (Iterator<IndexWorkerDocumentListener> itl = indexWorkerDocumentListeners .iterator(); itl.hasNext();) { IndexWorkerDocumentListener iwl = itl.next(); iwl.indexDocumentStart(this, ref); } } /** * */ private void fireEndDocument(String ref) { for (Iterator<IndexWorkerDocumentListener> itl = indexWorkerDocumentListeners .iterator(); itl.hasNext();) { IndexWorkerDocumentListener iwl = itl.next(); iwl.indexDocumentEnd(this, ref); } } /** * */ private void fireIndexEnd() { for (Iterator<IndexWorkerListener> itl = indexWorkerListeners.iterator(); itl .hasNext();) { IndexWorkerListener iwl = itl.next(); iwl.indexWorkerEnd(this); } } private void processRDF(String ref, EntityContentProducer sep) throws RDFIndexException { if (rdfSearchService != null) { String s = sep.getCustomRDF(ref); if (s != null) { rdfSearchService.addData(s); } } } public void addIndexWorkerListener(IndexWorkerListener indexWorkerListener) { List<IndexWorkerListener> tl = new ArrayList<IndexWorkerListener>(); tl.addAll(indexWorkerListeners); tl.add(indexWorkerListener); indexWorkerListeners = tl; } public void removeIndexWorkerListener(IndexWorkerListener indexWorkerListener) { List<IndexWorkerListener> tl = new ArrayList<IndexWorkerListener>(); tl.addAll(indexWorkerListeners); tl.remove(indexWorkerListener); indexWorkerListeners = tl; } public void addIndexWorkerDocumentListener( IndexWorkerDocumentListener indexWorkerDocumentListener) { List<IndexWorkerDocumentListener> tl = new ArrayList<IndexWorkerDocumentListener>(); tl.addAll(indexWorkerDocumentListeners); tl.add(indexWorkerDocumentListener); indexWorkerDocumentListeners = tl; } public void removeIndexWorkerDocumentListener( IndexWorkerDocumentListener indexWorkerDocumentListener) { List<IndexWorkerDocumentListener> tl = new ArrayList<IndexWorkerDocumentListener>(); tl.addAll(indexWorkerDocumentListeners); tl.remove(indexWorkerDocumentListener); indexWorkerDocumentListeners = tl; } /** * @return the indexWorkerDocumentListeners */ public List<IndexWorkerDocumentListener> getIndexWorkerDocumentListeners() { return indexWorkerDocumentListeners; } /** * @param indexWorkerDocumentListeners * the indexWorkerDocumentListeners to set */ public void setIndexWorkerDocumentListeners( List<IndexWorkerDocumentListener> indexWorkerDocumentListeners) { this.indexWorkerDocumentListeners = indexWorkerDocumentListeners; } /** * @return the indexWorkerListeners */ public List<IndexWorkerListener> getIndexWorkerListeners() { return indexWorkerListeners; } /** * @param indexWorkerListeners * the indexWorkerListeners to set */ public void setIndexWorkerListeners(List<IndexWorkerListener> indexWorkerListeners) { this.indexWorkerListeners = indexWorkerListeners; } /** * @return the rdfSearchService */ public RDFSearchService getRdfSearchService() { return rdfSearchService; } /** * @param rdfSearchService * the rdfSearchService to set */ public void setRdfSearchService(RDFSearchService rdfSearchService) { this.rdfSearchService = rdfSearchService; } /** * @return the searchIndexBuilder */ public SearchIndexBuilder getSearchIndexBuilder() { return searchIndexBuilder; } /** * @param searchIndexBuilder * the searchIndexBuilder to set */ public void setSearchIndexBuilder(SearchIndexBuilder searchIndexBuilder) { this.searchIndexBuilder = searchIndexBuilder; } /** * @return the serverConfigurationService */ public ServerConfigurationService getServerConfigurationService() { return serverConfigurationService; } /** * @param serverConfigurationService * the serverConfigurationService to set */ public void setServerConfigurationService( ServerConfigurationService serverConfigurationService) { this.serverConfigurationService = serverConfigurationService; } /** * @return the transactionIndexManager */ public TransactionIndexManager getTransactionIndexManager() { return transactionIndexManager; } /** * @param transactionIndexManager * the transactionIndexManager to set */ public void setTransactionIndexManager(TransactionIndexManager transactionIndexManager) { this.transactionIndexManager = transactionIndexManager; } /** * @return the threadLocalManager */ public ThreadLocalManager getThreadLocalManager() { return threadLocalManager; } /** * @param threadLocalManager the threadLocalManager to set */ public void setThreadLocalManager(ThreadLocalManager threadLocalManager) { this.threadLocalManager = threadLocalManager; } }