package proj.zoie.impl.indexing.internal; /** * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache 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.apache.org/licenses/LICENSE-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. */ import java.io.IOException; import org.apache.log4j.Logger; import org.apache.lucene.index.DirectoryReader; import org.apache.lucene.index.IndexReader; import org.apache.lucene.store.Directory; import proj.zoie.api.DirectoryManager; import proj.zoie.api.DocIDMapper; import proj.zoie.api.ZoieMultiReader; import proj.zoie.api.indexing.IndexReaderDecorator; public class IndexReaderDispenser<R extends IndexReader> { private static final Logger log = Logger.getLogger(IndexReaderDispenser.class); private static final int INDEX_OPEN_NUM_RETRIES = 5; private volatile ZoieMultiReader<R> _currentReader; private volatile IndexSignature _currentSignature; private final IndexReaderDecorator<R> _decorator; private final DirectoryManager _dirMgr; private final DiskSearchIndex<R> _idx; public IndexReaderDispenser(DirectoryManager dirMgr, IndexReaderDecorator<R> decorator, DiskSearchIndex<R> idx) { _idx = idx; _dirMgr = dirMgr; _decorator = decorator; _currentSignature = null; try { IndexSignature sig = new IndexSignature(_dirMgr.getVersion()); if (sig != null) { getNewReader(); } } catch (IOException e) { log.error(e); } } public String getCurrentVersion() { return _currentSignature != null ? _currentSignature.getVersion() : null; } /** * constructs a new IndexReader instance * * @param indexPath * Where the index is. * @return Constructed IndexReader instance. * @throws IOException */ private ZoieMultiReader<R> newReader(DirectoryManager dirMgr, IndexReaderDecorator<R> decorator, IndexSignature signature) throws IOException { if (!dirMgr.exists()) { return null; } Directory dir = dirMgr.getDirectory(); if (!DirectoryReader.indexExists(dir)) { return null; } int numTries = INDEX_OPEN_NUM_RETRIES; ZoieMultiReader<R> reader = null; // try max of 5 times, there might be a case where the segment file is being updated while (reader == null) { if (numTries == 0) { log.error("Problem refreshing disk index, all attempts failed."); throw new IOException("problem opening new index"); } numTries--; try { if (log.isDebugEnabled()) { log.debug("opening index reader at: " + dirMgr.getPath()); } DirectoryReader srcReader = DirectoryReader.open(dir); try { reader = new ZoieMultiReader<R>(srcReader, decorator); _currentSignature = signature; } catch (IOException ioe) { // close the source reader if ZoieMultiReader construction fails if (srcReader != null) { srcReader.close(); } throw ioe; } } catch (IOException ioe) { try { Thread.sleep(100); } catch (InterruptedException e) { log.warn("thread interrupted."); continue; } } } return reader; } /** * get a fresh new reader instance * @return an IndexReader instance, can be null if index does not yet exit * @throws IOException */ public ZoieMultiReader<R> getNewReader() throws IOException { int numTries = INDEX_OPEN_NUM_RETRIES; ZoieMultiReader<R> reader = null; // try it for a few times, there is a case where lucene is swapping the segment file, // or a case where the index directory file is updated, both are legitimate, // trying again does not block searchers, // the extra time it takes to get the reader, and to sync the index, memory index is collecting // docs while (reader == null) { if (numTries == 0) { break; } numTries--; try { IndexSignature sig = new IndexSignature(_dirMgr.getVersion()); if (_currentReader == null) { reader = newReader(_dirMgr, _decorator, sig); break; } else { reader = _currentReader.reopen(); _currentSignature = sig; } } catch (IOException ioe) { try { Thread.sleep(100); } catch (InterruptedException e) { log.warn("thread interrupted."); continue; } } } // swap the internal readers if (_currentReader != reader) { if (reader != null) { DocIDMapper mapper = _idx._idxMgr._docIDMapperFactory.getDocIDMapper(reader); reader.setDocIDMapper(mapper); } // assume that this is the only place that _currentReader gets refreshed ZoieMultiReader<R> oldReader = _currentReader; _currentReader = reader; // we release our hold on the old reader so that it will be closed when // all the clients release their hold on it, the reader will be closed // automatically. log.info("swap disk reader and release old one from system"); if (oldReader != null) oldReader.decZoieRef(); } return reader; } public ZoieMultiReader<R> getIndexReader() { if (_currentReader != null) { return _currentReader; } else { return null; } } /** * Closes the factory. * */ public void close() { closeReader(); } /** * Closes the index reader */ public void closeReader() { if (_currentReader != null) { _currentReader.decZoieRef(); int count = _currentReader.getInnerRefCount(); log.info("final closeReader in dispenser and current refCount: " + count); if (count > 0) { log.warn("final closeReader call with reference count == " + count + " greater than 0. Potentially, " + "the IndexReaders are not properly return to ZoieSystem."); } _currentReader = null; } } }