/********************************************************************************** * $URL: https://source.sakaiproject.org/svn/search/trunk/search-impl/impl/src/java/org/sakaiproject/search/index/impl/BaseIndexStorage.java $ * $Id: BaseIndexStorage.java 105078 2012-02-24 23:00:38Z ottenhoff@longsight.com $ *********************************************************************************** * * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008 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.index.impl; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Timer; import java.util.TimerTask; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.search.IndexSearcher; import org.sakaiproject.search.index.AnalyzerFactory; import org.sakaiproject.search.index.IndexReloadListener; import org.sakaiproject.search.index.IndexStorage; /** * @author ieb */ public abstract class BaseIndexStorage implements IndexStorage { private static final Log log = LogFactory.getLog(BaseIndexStorage.class); /** * the currently running index searcher */ private IndexSearcher runningIndexSearcher; private Object reloadObjectSemaphore = new Object(); private Timer indexCloseTimer = new Timer(true); private long reloadStart; private long reloadEnd; protected boolean diagnostics; private List<IndexReloadListener> indexReloadListeners = new ArrayList<IndexReloadListener>(); /** * The token analyzer */ private AnalyzerFactory analyzerFactory = null; public IndexSearcher getIndexSearcher(boolean reload) throws IOException { if (runningIndexSearcher == null || (reload)) { // there is a possiblity that we get more than one thread comming // through this block. // however this could oonly happen if more than one thread went // throught the next 3 lines // at the same time. // if more than one thread did go through the next 3 at the same // time, then the local segments // might get updated more than once. long lastUpdate = getLastUpdate(); if (lastUpdate > reloadStart || runningIndexSearcher == null) { reloadStart = System.currentTimeMillis(); long startLoad = System.currentTimeMillis(); if (log.isDebugEnabled()) { log.debug("Reloading Index, force=" + reload); //$NON-NLS-1$ } try { // dont leave closing the index searcher to // the // GC. // It // may // not happen fast enough. // this makes the assumption that getIndexSearcher is thread // safe. // Care has to be taken here as there may be an update on // the index being performed. IndexSearcher newRunningIndexSearcher = getIndexSearcher(); synchronized (reloadObjectSemaphore) { final IndexSearcher oldRunningIndexSearcher = runningIndexSearcher; runningIndexSearcher = newRunningIndexSearcher; reloadEnd = System.currentTimeMillis(); if (oldRunningIndexSearcher != null) { indexCloseTimer.schedule(new TimerTask() { @Override public void run() { try { closeIndexSearcher(oldRunningIndexSearcher); } catch (Exception ex) { log.error("Failed to close old searcher ", ex); //$NON-NLS-1$ } finally { cancel(); indexCloseTimer.purge(); } } }, 30 * 1000L); } } fireIndexReload(reloadStart, reloadEnd); } catch (IOException e) { reloadStart = reloadEnd; } long loadPause = System.currentTimeMillis() - startLoad; if (loadPause > 10 * 1000L) { log.warn("Reload of blocked this thread for " + loadPause + " ms "); } } else { if (log.isDebugEnabled()) { log.debug("No Reload lastUpdate " + lastUpdate //$NON-NLS-1$ + " < lastReload " + reloadStart); //$NON-NLS-1$ } } } return runningIndexSearcher; } /** * @return */ protected abstract IndexSearcher getIndexSearcher() throws IOException; /** * @param reloadStart2 * @param reloadEnd2 */ protected void fireIndexReload(long reloadStart, long reloadEnd) { for (Iterator<IndexReloadListener> itl = indexReloadListeners.iterator(); itl .hasNext();) { IndexReloadListener tl = itl.next(); tl.reloaded(reloadStart, reloadEnd); } } /* * (non-Javadoc) * * @see org.sakaiproject.search.index.IndexStorage#getLastLoad() */ public long getLastLoad() { return reloadEnd; } /* * (non-Javadoc) * * @see org.sakaiproject.search.index.IndexStorage#getLastLoadTime() */ public long getLastLoadTime() { return (reloadEnd - reloadStart); } /** * @see org.sakaiproject.search.index.IndexStorage#addReloadListener(IndexReloadListener * indexReloadListener) */ public void addReloadListener(IndexReloadListener indexReloadListener) { List<IndexReloadListener> tl = new ArrayList<IndexReloadListener>(); tl.addAll(indexReloadListeners); tl.add(indexReloadListener); indexReloadListeners = tl; } /** * @see org.sakaiproject.search.index.IndexStorage#removeReloadListener(IndexReloadListener * indexReloadListener) */ public void removeReloadListener(IndexReloadListener indexReloadListener) { List<IndexReloadListener> tl = new ArrayList<IndexReloadListener>(); tl.addAll(indexReloadListeners); tl.remove(indexReloadListener); indexReloadListeners = tl; } /** * @see org.sakaiproject.search.index.IndexStorage#forceNextReload() */ public void forceNextReload() { reloadStart = 0; } /* * (non-Javadoc) * * @see org.sakaiproject.search.api.Diagnosable#disableDiagnostics() */ public void disableDiagnostics() { diagnostics = false; } /* * (non-Javadoc) * * @see org.sakaiproject.search.api.Diagnosable#enableDiagnostics() */ public void enableDiagnostics() { diagnostics = true; } /* * (non-Javadoc) * * @see org.sakaiproject.search.api.Diagnosable#hasDiagnostics() */ public boolean hasDiagnostics() { return diagnostics; } /** * @return Returns the analzyserFactory. */ public AnalyzerFactory getAnalyzerFactory() { return analyzerFactory; } /** * @param analzyserFactory * The analzyserFactory to set. */ public void setAnalyzerFactory(AnalyzerFactory analzyserFactory) { this.analyzerFactory = analzyserFactory; } public Analyzer getAnalyzer() { return analyzerFactory.newAnalyzer(); } }