Index: jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/AbstractExcerpt.java =================================================================== --- jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/AbstractExcerpt.java (revision 696338) +++ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/AbstractExcerpt.java (working copy) @@ -144,7 +144,7 @@ return null; } } finally { - reader.close(); + Util.closeOrRelease(reader); } } @@ -217,7 +217,7 @@ } finally { // only close reader if this method opened one if (reader == null) { - r.close(); + Util.closeOrRelease(r); } } rewritten = true; Index: jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/AbstractIndex.java =================================================================== --- jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/AbstractIndex.java (revision 696338) +++ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/AbstractIndex.java (working copy) @@ -20,6 +20,8 @@ import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.index.Term; +import org.apache.lucene.index.SerialMergeScheduler; +import org.apache.lucene.index.LogDocMergePolicy; import org.apache.lucene.store.Directory; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; @@ -109,6 +111,12 @@ private IndexingQueue indexingQueue; /** + * Flag that indicates whether there was an index present in the directory + * when this AbstractIndex was created. + */ + private boolean isExisting; + + /** * Constructs an index with an analyzer and a * directory. * @@ -131,8 +139,9 @@ this.directory = directory; this.cache = cache; this.indexingQueue = indexingQueue; + this.isExisting = IndexReader.indexExists(directory); - if (!IndexReader.indexExists(directory)) { + if (!isExisting) { indexWriter = new IndexWriter(directory, analyzer); // immediately close, now that index has been created indexWriter.close(); @@ -152,6 +161,17 @@ } /** + * Returns true if this index was openend on a directory with + * an existing index in it; false otherwise. + * + * @return true if there was an index present when this index + * was created; false otherwise. + */ + boolean isExisting() { + return isExisting; + } + + /** * Adds documents to this index and invalidates the shared reader. * * @param docs the documents to add. @@ -248,7 +268,7 @@ if (readOnlyReader != null) { if (readOnlyReader.getDeletedDocsVersion() == modCount) { // reader up-to-date - readOnlyReader.incrementRefCount(); + readOnlyReader.acquire(); return readOnlyReader; } else { // reader outdated @@ -256,12 +276,12 @@ // not in use, except by this index // update the reader readOnlyReader.updateDeletedDocs(modifiableReader); - readOnlyReader.incrementRefCount(); + readOnlyReader.acquire(); return readOnlyReader; } else { // cannot update reader, it is still in use // need to create a new instance - readOnlyReader.close(); + readOnlyReader.release(); readOnlyReader = null; } } @@ -280,7 +300,7 @@ sharedReader = new SharedIndexReader(cr); } readOnlyReader = new ReadOnlyIndexReader(sharedReader, deleted, modCount); - readOnlyReader.incrementRefCount(); + readOnlyReader.acquire(); return readOnlyReader; } @@ -305,6 +325,9 @@ indexWriter.setMaxFieldLength(maxFieldLength); indexWriter.setUseCompoundFile(useCompoundFile); indexWriter.setInfoStream(STREAM_LOGGER); + indexWriter.setRAMBufferSizeMB(IndexWriter.DISABLE_AUTO_FLUSH); + indexWriter.setMergeScheduler(new SerialMergeScheduler()); + indexWriter.setMergePolicy(new LogDocMergePolicy()); } return indexWriter; } @@ -326,7 +349,7 @@ */ protected synchronized void commit(boolean optimize) throws IOException { if (indexReader != null) { - indexReader.commitDeleted(); + indexReader.flush(); } if (indexWriter != null) { log.debug("committing IndexWriter."); @@ -346,6 +369,20 @@ * Closes this index, releasing all held resources. */ synchronized void close() { + releaseWriterAndReaders(); + if (directory != null) { + try { + directory.close(); + } catch (IOException e) { + directory = null; + } + } + } + + /** + * Releases all potentially held index writer and readers. + */ + protected void releaseWriterAndReaders() { if (indexWriter != null) { try { indexWriter.close(); @@ -364,25 +401,20 @@ } if (readOnlyReader != null) { try { - readOnlyReader.close(); + readOnlyReader.release(); } catch (IOException e) { log.warn("Exception closing index reader: " + e.toString()); } + readOnlyReader = null; } if (sharedReader != null) { try { - sharedReader.close(); + sharedReader.release(); } catch (IOException e) { log.warn("Exception closing index reader: " + e.toString()); } + sharedReader = null; } - if (directory != null) { - try { - directory.close(); - } catch (IOException e) { - directory = null; - } - } } /** @@ -393,12 +425,12 @@ protected synchronized void invalidateSharedReader() throws IOException { // also close the read-only reader if (readOnlyReader != null) { - readOnlyReader.close(); + readOnlyReader.release(); readOnlyReader = null; } // invalidate shared reader if (sharedReader != null) { - sharedReader.close(); + sharedReader.release(); sharedReader = null; } } Index: jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/CachingMultiIndexReader.java =================================================================== --- jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/CachingMultiIndexReader.java (revision 696338) +++ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/CachingMultiIndexReader.java (working copy) @@ -65,11 +65,9 @@ * * @param subReaders the sub readers. * @param cache the document number cache. - * @throws IOException if an error occurs while reading from the indexes. */ public CachingMultiIndexReader(ReadOnlyIndexReader[] subReaders, - DocNumberCache cache) - throws IOException { + DocNumberCache cache) { super(subReaders); this.cache = cache; this.subReaders = subReaders; @@ -150,23 +148,30 @@ /** * Increments the reference count of this reader. Each call to this method - * must later be acknowledged by a call to {@link #close()} + * must later be acknowledged by a call to {@link #release()}. */ - synchronized void incrementRefCount() { + synchronized void acquire() { refCount++; } /** - * Decrements the reference count and closes the underlying readers if this - * reader is not in use anymore. - * @throws IOException if an error occurs while closing this reader. + * {@inheritDoc} */ - protected synchronized void doClose() throws IOException { + public synchronized final void release() throws IOException { if (--refCount == 0) { - super.doClose(); + close(); } } + /** + * {@inheritDoc} + */ + protected synchronized void doClose() throws IOException { + for (int i = 0; i < subReaders.length; i++) { + subReaders[i].release(); + } + } + //-------------------------< MultiIndexReader >----------------------------- /** Index: jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/CommittableIndexReader.java =================================================================== --- jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/CommittableIndexReader.java (revision 696338) +++ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/CommittableIndexReader.java (working copy) @@ -60,15 +60,6 @@ //------------------------< additional methods >---------------------------- /** - * Commits the documents marked as deleted to disc. - * - * @throws IOException if an error occurs while writing. - */ - void commitDeleted() throws IOException { - commit(); - } - - /** * @return the modification count of this index reader. */ long getModificationCount() { Index: jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/ConsistencyCheck.java =================================================================== --- jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/ConsistencyCheck.java (revision 696338) +++ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/ConsistencyCheck.java (working copy) @@ -22,7 +22,6 @@ import org.apache.jackrabbit.core.state.ChildNodeEntry; import org.apache.jackrabbit.core.NodeId; import org.apache.jackrabbit.uuid.UUID; -import org.apache.lucene.index.IndexReader; import org.apache.lucene.document.Document; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -153,7 +152,7 @@ Set multipleEntries = new HashSet(); // collect all documents UUIDs documentUUIDs = new HashSet(); - IndexReader reader = index.getIndexReader(); + CachingMultiIndexReader reader = index.getIndexReader(); try { for (int i = 0; i < reader.maxDoc(); i++) { if (i > 10 && i % (reader.maxDoc() / 5) == 0) { @@ -174,7 +173,7 @@ } } } finally { - reader.close(); + reader.release(); } // create multiple entries errors @@ -212,7 +211,7 @@ } } } finally { - reader.close(); + reader.release(); } } Index: jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/FieldNames.java =================================================================== --- jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/FieldNames.java (revision 696338) +++ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/FieldNames.java (working copy) @@ -115,14 +115,14 @@ /** * Returns a named value for use as a term in the index. The named - * value is of the form: fieldName + '\uFFFF' + value + * value is of the form: fieldName + '[' + value * * @param fieldName the field name. * @param value the value. * @return value prefixed with field name. */ public static String createNamedValue(String fieldName, String value) { - return fieldName + '\uFFFF' + value; + return fieldName + '[' + value; } /** @@ -131,10 +131,9 @@ * does not contain a name prefix, this method return 0. * * @param namedValue the named value as created by {@link #createNamedValue(String, String)}. - * @return the length of the field prefix including the separator char - * (\uFFFF). + * @return the length of the field prefix including the separator char '['. */ public static int getNameLength(String namedValue) { - return namedValue.indexOf('\uFFFF') + 1; + return namedValue.indexOf('[') + 1; } } Index: jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/IndexMerger.java =================================================================== --- jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/IndexMerger.java (revision 696338) +++ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/IndexMerger.java (working copy) @@ -305,13 +305,13 @@ // force initializing of caches time = System.currentTimeMillis(); - index.getReadOnlyIndexReader().close(); + index.getReadOnlyIndexReader().release(); time = System.currentTimeMillis() - time; log.debug("reader obtained in {} ms", new Long(time)); } finally { for (int i = 0; i < readers.length; i++) { try { - readers[i].close(); + Util.closeOrRelease(readers[i]); } catch (IOException e) { log.warn("Unable to close IndexReader: " + e); } Index: jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/IndexMigration.java =================================================================== --- jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/IndexMigration.java (revision 0) +++ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/IndexMigration.java (revision 0) @@ -0,0 +1,216 @@ +/* + * 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. + */ +package org.apache.jackrabbit.core.query.lucene; + +import org.apache.lucene.index.Term; +import org.apache.lucene.index.TermEnum; +import org.apache.lucene.index.FilterIndexReader; +import org.apache.lucene.index.IndexReader; +import org.apache.lucene.index.CorruptIndexException; +import org.apache.lucene.index.TermPositions; +import org.apache.lucene.index.IndexWriter; +import org.apache.lucene.store.FSDirectory; +import org.apache.lucene.store.NoLockFactory; +import org.apache.lucene.document.Document; +import org.apache.lucene.document.FieldSelector; +import org.apache.lucene.document.Fieldable; +import org.apache.lucene.document.Field; +import org.apache.jackrabbit.core.fs.local.FileUtil; +import org.slf4j.LoggerFactory; +import org.slf4j.Logger; + +import java.io.IOException; +import java.io.File; + +/** + * IndexMigration implements a utility that migrates a Jackrabbit + * 1.4.x index to version 1.5. Until version 1.4.x, indexes used the character + * '\uFFFF' to separate the name of a property from the value. As of Lucene + * 2.3 this does not work anymore. See LUCENE-1221. Jackrabbit >= 1.5 uses + * the character '[' as a separator. Whenever an index is opened from disk, a + * quick check is run to find out whether a migration is required. See also + * JCR-1363 for more details. + */ +public class IndexMigration { + + /** + * The logger instance for this class. + */ + private static final Logger log = LoggerFactory.getLogger(IndexMigration.class); + + /** + * Checks if the given index needs to be migrated. + * + * @param index the index to check and migration if needed. + * @param indexDir the directory where the index is stored. + * @throws IOException if an error occurs while migrating the index. + */ + public static void migrate(PersistentIndex index, File indexDir) throws IOException { + log.debug("Checking {} ...", indexDir.getAbsolutePath()); + ReadOnlyIndexReader reader = index.getReadOnlyIndexReader(); + try { + if (IndexFormatVersion.getVersion(reader).getVersion() >= + IndexFormatVersion.V3.getVersion()) { + // index was created with Jackrabbit 1.5 or higher + // no need for migration + log.debug("IndexFormatVersion >= V3, no migration needed"); + return; + } + // assert: there is at least one node in the index, otherwise the + // index format version would be at least V3 + TermEnum terms = reader.terms(new Term(FieldNames.PROPERTIES, "")); + try { + Term t = terms.term(); + if (t.text().indexOf('\uFFFF') == -1) { + log.debug("Index already migrated"); + return; + } + } finally { + terms.close(); + } + } finally { + reader.release(); + } + + // if we get here then the index must be migrated + log.debug("Index requires migration {}", indexDir.getAbsolutePath()); + + // make sure readers are closed, otherwise the directory + // cannot be deleted + index.releaseWriterAndReaders(); + + File migrationDir = new File(indexDir.getAbsoluteFile().getParentFile(), indexDir.getName() + "_v2.3"); + if (migrationDir.exists()) { + FileUtil.delete(migrationDir); + } + if (!migrationDir.mkdirs()) { + throw new IOException("failed to create directory " + + migrationDir.getAbsolutePath()); + } + FSDirectory fsDir = FSDirectory.getDirectory(migrationDir, + NoLockFactory.getNoLockFactory()); + try { + IndexWriter writer = new IndexWriter(fsDir, new JackrabbitAnalyzer()); + try { + IndexReader r = new MigrationIndexReader( + IndexReader.open(index.getDirectory())); + try { + writer.addIndexes(new IndexReader[]{r}); + writer.close(); + } finally { + r.close(); + } + } finally { + writer.close(); + } + } finally { + fsDir.close(); + } + FileUtil.delete(indexDir); + if (!migrationDir.renameTo(indexDir)) { + throw new IOException("failed to move migrated directory " + + migrationDir.getAbsolutePath()); + } + log.info("Migrated " + indexDir.getAbsolutePath()); + } + + //---------------------------< internal helper >---------------------------- + + /** + * An index reader that migrates stored field values and term text on the + * fly. + */ + private static class MigrationIndexReader extends FilterIndexReader { + + public MigrationIndexReader(IndexReader in) { + super(in); + } + + public Document document(int n, FieldSelector fieldSelector) + throws CorruptIndexException, IOException { + Document doc = super.document(n, fieldSelector); + Fieldable[] fields = doc.getFieldables(FieldNames.PROPERTIES); + if (fields != null) { + doc.removeFields(FieldNames.PROPERTIES); + for (int i = 0; i < fields.length; i++) { + String value = fields[i].stringValue(); + value = value.replace('\uFFFF', '['); + doc.add(new Field(FieldNames.PROPERTIES, value, + Field.Store.YES, Field.Index.NO_NORMS)); + } + } + return doc; + } + + public TermEnum terms() throws IOException { + return new MigrationTermEnum(in.terms()); + } + + public TermPositions termPositions() throws IOException { + return new MigrationTermPositions(in.termPositions()); + } + + private static class MigrationTermEnum extends FilterTermEnum { + + public MigrationTermEnum(TermEnum in) { + super(in); + } + + public Term term() { + Term t = super.term(); + if (t == null) { + return t; + } + if (t.field().equals(FieldNames.PROPERTIES)) { + String text = t.text(); + return t.createTerm(text.replace('\uFFFF', '[')); + } else { + return t; + } + } + + TermEnum unwrap() { + return in; + } + } + + private static class MigrationTermPositions extends FilterTermPositions { + + public MigrationTermPositions(TermPositions in) { + super(in); + } + + public void seek(Term term) throws IOException { + if (term.field().equals(FieldNames.PROPERTIES)) { + char[] text = term.text().toCharArray(); + text[term.text().indexOf('[')] = '\uFFFF'; + super.seek(term.createTerm(new String(text))); + } else { + super.seek(term); + } + } + + public void seek(TermEnum termEnum) throws IOException { + if (termEnum instanceof MigrationTermEnum) { + super.seek(((MigrationTermEnum) termEnum).unwrap()); + } else { + super.seek(termEnum); + } + } + } + } +} Property changes on: jackrabbit-core\src\main\java\org\apache\jackrabbit\core\query\lucene\IndexMigration.java ___________________________________________________________________ Added: svn:eol-style + native Index: jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/JackrabbitIndexReader.java =================================================================== --- jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/JackrabbitIndexReader.java (revision 0) +++ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/JackrabbitIndexReader.java (revision 0) @@ -0,0 +1,118 @@ +/* + * 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. + */ +package org.apache.jackrabbit.core.query.lucene; + +import org.apache.lucene.index.FilterIndexReader; +import org.apache.lucene.index.IndexReader; +import org.apache.jackrabbit.uuid.UUID; + +import java.io.IOException; + +/** + * JackrabbitIndexReader wraps an index reader and + * {@link ReleaseableIndexReader#release() releases} the underlying reader + * when a client calls {@link #close()} on this reader. This allows reusing + * of the underlying index reader instance. + */ +public final class JackrabbitIndexReader + extends FilterIndexReader + implements HierarchyResolver, MultiIndexReader { + + /** + * The hierarchy resolver. + */ + private final HierarchyResolver resolver; + + /** + * The underlying index reader exposed as a {@link MultiIndexReader}. + */ + private final MultiIndexReader reader; + + /** + * Creates a new JackrabbitIndexReader. The passed index reader + * must also implement the interfaces {@link HierarchyResolver} and + * {@link MultiIndexReader}. + * + * @param in the underlying index reader. + * @throws IllegalArgumentException if in does not implement + * {@link HierarchyResolver} and + * {@link MultiIndexReader}. + */ + public JackrabbitIndexReader(IndexReader in) { + super(in); + if (!(in instanceof MultiIndexReader)) { + throw new IllegalArgumentException("IndexReader must also implement MultiIndexReader"); + } + if (!(in instanceof HierarchyResolver)) { + throw new IllegalArgumentException("IndexReader must also implement HierarchyResolver"); + } + this.resolver = (HierarchyResolver) in; + this.reader = (MultiIndexReader) in; + } + + //--------------------------< FilterIndexReader >--------------------------- + + /** + * Calls release on the underlying {@link MultiIndexReader} instead of + * closing it. + * + * @throws IOException if an error occurs while releaseing the underlying + * index reader. + */ + protected void doClose() throws IOException { + reader.release(); + } + + //------------------------< HierarchyResolver >----------------------------- + + /** + * {@inheritDoc} + */ + public int getParent(int n) throws IOException { + return resolver.getParent(n); + } + + //-------------------------< MultiIndexReader >----------------------------- + + /** + * {@inheritDoc} + */ + public IndexReader[] getIndexReaders() { + return reader.getIndexReaders(); + } + + /** + * {@inheritDoc} + */ + public ForeignSegmentDocId createDocId(UUID uuid) throws IOException { + return reader.createDocId(uuid); + } + + /** + * {@inheritDoc} + */ + public int getDocumentNumber(ForeignSegmentDocId docId) throws IOException { + return reader.getDocumentNumber(docId); + } + + /** + * {@inheritDoc} + */ + public void release() throws IOException { + reader.release(); + } +} Property changes on: jackrabbit-core\src\main\java\org\apache\jackrabbit\core\query\lucene\JackrabbitIndexReader.java ___________________________________________________________________ Added: svn:eol-style + native Index: jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/MultiIndex.java =================================================================== --- jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/MultiIndex.java (revision 696338) +++ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/MultiIndex.java (working copy) @@ -298,11 +298,11 @@ resetVolatileIndex(); // set index format version - IndexReader reader = getIndexReader(); + CachingMultiIndexReader reader = getIndexReader(); try { version = IndexFormatVersion.getVersion(reader); } finally { - reader.close(); + reader.release(); } indexingQueue.initialize(this); @@ -349,11 +349,11 @@ if (indexNames.size() == 0) { return volatileIndex.getNumDocuments(); } else { - IndexReader reader = getIndexReader(); + CachingMultiIndexReader reader = getIndexReader(); try { return reader.numDocs(); } finally { - reader.close(); + reader.release(); } } } @@ -441,7 +441,7 @@ synchronized (updateMonitor) { updateInProgress = false; updateMonitor.notifyAll(); - closeMultiReader(); + releaseMultiReader(); } } } @@ -504,7 +504,7 @@ synchronized (updateMonitor) { updateInProgress = false; updateMonitor.notifyAll(); - closeMultiReader(); + releaseMultiReader(); } } return num; @@ -538,14 +538,14 @@ } } } catch (IOException e) { - // close readers obtained so far + // release readers obtained so far for (Iterator it = indexReaders.entrySet().iterator(); it.hasNext();) { Map.Entry entry = (Map.Entry) it.next(); ReadOnlyIndexReader reader = (ReadOnlyIndexReader) entry.getKey(); try { - reader.close(); + reader.release(); } catch (IOException ex) { - log.warn("Exception closing index reader: " + ex); + log.warn("Exception releasing index reader: " + ex); } ((PersistentIndex) entry.getValue()).resetListener(); } @@ -674,7 +674,7 @@ synchronized (updateMonitor) { updateInProgress = false; updateMonitor.notifyAll(); - closeMultiReader(); + releaseMultiReader(); } } } @@ -694,7 +694,7 @@ public CachingMultiIndexReader getIndexReader() throws IOException { synchronized (updateMonitor) { if (multiReader != null) { - multiReader.incrementRefCount(); + multiReader.acquire(); return multiReader; } // no reader available @@ -721,7 +721,7 @@ (ReadOnlyIndexReader[]) readerList.toArray(new ReadOnlyIndexReader[readerList.size()]); multiReader = new CachingMultiIndexReader(readers, cache); } - multiReader.incrementRefCount(); + multiReader.acquire(); return multiReader; } } @@ -751,7 +751,7 @@ // commit / close indexes try { - closeMultiReader(); + releaseMultiReader(); } catch (IOException e) { log.error("Exception while closing search index.", e); } @@ -895,7 +895,7 @@ } /** - * Closes the {@link #multiReader} and sets it null. If the + * Releases the {@link #multiReader} and sets it null. If the * reader is already null this method does nothing. When this * method returns {@link #multiReader} is guaranteed to be null * even if an exception is thrown. @@ -904,12 +904,12 @@ * A caller must ensure that it is the only thread operating on this multi * index, or that it holds the {@link #updateMonitor}. * - * @throws IOException if an error occurs while closing the reader. + * @throws IOException if an error occurs while releasing the reader. */ - void closeMultiReader() throws IOException { + void releaseMultiReader() throws IOException { if (multiReader != null) { try { - multiReader.close(); + multiReader.release(); } finally { multiReader = null; } @@ -1143,7 +1143,7 @@ synchronized (updateMonitor) { updateInProgress = false; updateMonitor.notifyAll(); - closeMultiReader(); + releaseMultiReader(); } } } Index: jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/MultiIndexReader.java =================================================================== --- jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/MultiIndexReader.java (revision 696338) +++ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/MultiIndexReader.java (working copy) @@ -25,7 +25,7 @@ * MultiIndexReader exposes methods to get access to the contained * {@link IndexReader}s of this MultiIndexReader. */ -public interface MultiIndexReader { +public interface MultiIndexReader extends ReleaseableIndexReader { /** * @return the IndexReaders that are contained in this @@ -53,5 +53,4 @@ * @throws IOException if an error occurs while reading from the index. */ int getDocumentNumber(ForeignSegmentDocId docId) throws IOException; - } Index: jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/NodeIndexer.java =================================================================== --- jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/NodeIndexer.java (revision 696338) +++ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/NodeIndexer.java (working copy) @@ -483,11 +483,10 @@ field.setOmitNorms(true); return field; } else { - Field field = new Field(FieldNames.PROPERTIES, + return new Field(FieldNames.PROPERTIES, FieldNames.createNamedValue(fieldName, internalValue), Field.Store.NO, Field.Index.NO_NORMS, Field.TermVector.NO); - return field; } } Index: jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/PersistentIndex.java =================================================================== --- jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/PersistentIndex.java (revision 696338) +++ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/PersistentIndex.java (working copy) @@ -55,8 +55,6 @@ * @param indexingQueue the indexing queue. * @throws IOException if an error occurs while opening / creating the * index. - * @throws IOException if an error occurs while opening / creating - * the index. */ PersistentIndex(String name, File indexDir, Analyzer analyzer, Similarity similarity, DocNumberCache cache, @@ -65,6 +63,9 @@ super(analyzer, similarity, FSDirectory.getDirectory(indexDir, new NativeFSLockFactory(indexDir)), cache, indexingQueue); this.name = name; + if (isExisting()) { + IndexMigration.migrate(this, indexDir); + } } /** Index: jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/ReadOnlyIndexReader.java =================================================================== --- jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/ReadOnlyIndexReader.java (revision 696338) +++ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/ReadOnlyIndexReader.java (working copy) @@ -16,7 +16,6 @@ */ package org.apache.jackrabbit.core.query.lucene; -import org.apache.lucene.index.FilterIndexReader; import org.apache.lucene.index.TermDocs; import org.apache.lucene.index.Term; import org.apache.lucene.index.TermPositions; @@ -30,7 +29,7 @@ * ReadOnlyIndexReader will always show all documents that have * not been deleted at the time when the index reader is created. */ -class ReadOnlyIndexReader extends FilterIndexReader { +class ReadOnlyIndexReader extends RefCountingIndexReader { /** * The underlying shared reader. @@ -50,11 +49,6 @@ private long deletedDocsVersion; /** - * A reference counter. When constructed the refCount is one. - */ - private int refCount = 1; - - /** * Creates a new index reader based on reader at * modificationTick. * @@ -71,26 +65,11 @@ this.reader = reader; this.deleted = deleted; this.deletedDocsVersion = deletedDocsVersion; - // register this - reader.addClient(this); + // acquire underlying reader + reader.acquire(); } /** - * Increments the reference count on this index reader. The reference count - * is decremented on {@link #close()}. - */ - synchronized void incrementRefCount() { - refCount++; - } - - /** - * @return the current reference count value. - */ - synchronized int getRefCount() { - return refCount; - } - - /** * @return version of the deleted docs. */ long getDeletedDocsVersion() { @@ -201,22 +180,6 @@ } /** - * Unregisters this reader from the shared index reader if the reference - * count for this reader drops to zero. Specifically, this method does - * not close the underlying index reader, because it is shared by - * multiple ReadOnlyIndexReaders. - * - * @throws IOException if an error occurs while closing the reader. - */ - protected void doClose() throws IOException { - synchronized (this) { - if (--refCount == 0) { - reader.removeClient(this); - } - } - } - - /** * Wraps the underlying TermDocs and filters out documents * marked as deleted.
* If term is for a {@link FieldNames#UUID} field and this Index: jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/Recovery.java =================================================================== --- jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/Recovery.java (revision 696338) +++ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/Recovery.java (working copy) @@ -170,6 +170,6 @@ // now we are consistent again -> flush index.flush(); - index.closeMultiReader(); + index.releaseMultiReader(); } } Index: jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/RefCountingIndexReader.java =================================================================== --- jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/RefCountingIndexReader.java (revision 0) +++ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/RefCountingIndexReader.java (revision 0) @@ -0,0 +1,71 @@ +/* + * 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. + */ +package org.apache.jackrabbit.core.query.lucene; + +import org.apache.lucene.index.FilterIndexReader; +import org.apache.lucene.index.IndexReader; + +import java.io.IOException; + +/** + * RefCountingIndexReader... + */ +public class RefCountingIndexReader + extends FilterIndexReader + implements ReleaseableIndexReader { + + /** + * A reference counter. When constructed the refCount is one. + */ + private int refCount = 1; + + public RefCountingIndexReader(IndexReader in) { + super(in); + } + + /** + * Increments the reference count on this index reader. The reference count + * is decremented on {@link #release()}. + */ + synchronized final void acquire() { + refCount++; + } + + /** + * @return the current reference count value. + */ + synchronized int getRefCount() { + return refCount; + } + + //-----------------------< ReleaseableIndexReader >-------------------------- + + /** + * {@inheritDoc} + */ + public synchronized final void release() throws IOException { + if (--refCount == 0) { + close(); + } + } + + //-----------------------< FilterIndexReader >-------------------------- + + protected void doClose() throws IOException { + Util.closeOrRelease(in); + } +} Property changes on: jackrabbit-core\src\main\java\org\apache\jackrabbit\core\query\lucene\RefCountingIndexReader.java ___________________________________________________________________ Added: svn:eol-style + native Index: jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/ReleaseableIndexReader.java =================================================================== --- jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/ReleaseableIndexReader.java (revision 0) +++ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/ReleaseableIndexReader.java (revision 0) @@ -0,0 +1,35 @@ +/* + * 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. + */ +package org.apache.jackrabbit.core.query.lucene; + +import java.io.IOException; + +/** + * ReleaseableIndexReader... + */ +public interface ReleaseableIndexReader { + + /** + * Releases this index reader and potentially frees resources. In contrast + * to {@link org.apache.lucene.index.IndexReader#close()} this method + * does not necessarily close the index reader, but gives the implementation + * the opportunity to do reference counting. + * + * @throws IOException if an error occurs while releasing the index reader. + */ + public void release() throws IOException; +} Property changes on: jackrabbit-core\src\main\java\org\apache\jackrabbit\core\query\lucene\ReleaseableIndexReader.java ___________________________________________________________________ Added: svn:eol-style + native Index: jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/SearchIndex.java =================================================================== --- jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/SearchIndex.java (revision 696338) +++ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/SearchIndex.java (working copy) @@ -706,7 +706,7 @@ super.close(); } finally { PerQueryCache.getInstance().dispose(); - reader.close(); + Util.closeOrRelease(reader); } } }; @@ -852,13 +852,14 @@ parentReader = ((SearchIndex) parentHandler).index.getIndexReader(); } - CachingMultiIndexReader reader = index.getIndexReader(); + IndexReader reader; if (parentReader != null) { - CachingMultiIndexReader[] readers = {reader, parentReader}; - return new CombinedIndexReader(readers); + CachingMultiIndexReader[] readers = {index.getIndexReader(), parentReader}; + reader = new CombinedIndexReader(readers); } else { - return reader; + reader = index.getIndexReader(); } + return new JackrabbitIndexReader(reader); } /** @@ -1181,7 +1182,7 @@ int found = 0; long time = System.currentTimeMillis(); try { - IndexReader reader = index.getIndexReader(); + CachingMultiIndexReader reader = index.getIndexReader(); try { Term aggregateUUIDs = new Term( FieldNames.AGGREGATED_NODE_UUID, ""); @@ -1206,7 +1207,7 @@ tDocs.close(); } } finally { - reader.close(); + reader.release(); } } catch (Exception e) { log.warn("Exception while retrieving aggregate roots", e); @@ -1237,7 +1238,7 @@ */ private int[] starts; - public CombinedIndexReader(CachingMultiIndexReader[] indexReaders) throws IOException { + public CombinedIndexReader(CachingMultiIndexReader[] indexReaders) { super(indexReaders); this.subReaders = indexReaders; this.starts = new int[subReaders.length + 1]; @@ -1271,6 +1272,15 @@ return readers; } + /** + * {@inheritDoc} + */ + public void release() throws IOException { + for (int i = 0; i < subReaders.length; i++) { + subReaders[i].release(); + } + } + //---------------------------< internal >------------------------------- /** Index: jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/SharedIndexReader.java =================================================================== --- jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/SharedIndexReader.java (revision 696338) +++ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/SharedIndexReader.java (working copy) @@ -30,22 +30,9 @@ * clients are disconnected AND the SharedIndexReaders * close() method itself has been called. */ -class SharedIndexReader extends FilterIndexReader { +class SharedIndexReader extends RefCountingIndexReader { /** - * Set to true if this index reader should be closed, when - * all connected clients are disconnected. - */ - private boolean closeRequested = false; - - /** - * Map of all registered clients to this shared index reader. The Map - * is rather used as a Set, because each value is the same Object as its - * associated key. - */ - private final Map clients = new IdentityHashMap(); - - /** * Creates a new SharedIndexReader which is based on * in. * @param in the underlying IndexReader. @@ -79,46 +66,6 @@ } /** - * Registeres client with this reader. As long as clients are - * registered, this shared reader will not release resources on {@link - * #close()} and will not actually close but only marks itself to close when - * the last client is unregistered. - * - * @param client the client to register. - */ - public synchronized void addClient(Object client) { - clients.put(client, client); - } - - /** - * Unregisters the client from this index reader. - * - * @param client a client of this reader. - * @throws IOException if an error occurs while detaching the client from - * this shared reader. - */ - public synchronized void removeClient(Object client) throws IOException { - clients.remove(client); - if (clients.isEmpty() && closeRequested) { - super.doClose(); - } - } - - /** - * Closes this index if no client is registered, otherwise this reader is - * marked to close when the last client is disconnected. - * - * @throws IOException if an error occurs while closing. - */ - protected synchronized void doClose() throws IOException { - if (clients.isEmpty()) { - super.doClose(); - } else { - closeRequested = true; - } - } - - /** * Simply passes the call to the wrapped reader as is.
* If term is for a {@link FieldNames#UUID} field and this * SharedIndexReader does not have such a document, @@ -140,5 +87,4 @@ public CachingIndexReader getBase() { return (CachingIndexReader) in; } - } Index: jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/Util.java =================================================================== --- jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/Util.java (revision 696338) +++ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/Util.java (working copy) @@ -21,6 +21,7 @@ import org.apache.lucene.search.Query; import org.apache.lucene.search.TermQuery; import org.apache.lucene.index.Term; +import org.apache.lucene.index.IndexReader; import org.slf4j.LoggerFactory; import org.slf4j.Logger; @@ -108,4 +109,21 @@ ex.initCause(t); return ex; } + + /** + * Depending on the type of the reader this method either + * closes or releases the reader. The reader is released if it implements + * {@link ReleaseableIndexReader}. + * + * @param reader the index reader to close or release. + * @throws IOException if an error occurs while closing or releasing the + * index reader. + */ + public static void closeOrRelease(IndexReader reader) throws IOException { + if (reader instanceof ReleaseableIndexReader) { + ((ReleaseableIndexReader) reader).release(); + } else { + reader.close(); + } + } } Index: jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/VolatileIndex.java =================================================================== --- jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/VolatileIndex.java (revision 696338) +++ jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/VolatileIndex.java (working copy) @@ -156,6 +156,9 @@ * Commits pending documents to the index. */ private void commitPending() throws IOException { + if (pending.isEmpty()) { + return; + } super.addDocuments((Document[]) pending.values().toArray( new Document[pending.size()])); pending.clear(); Index: pom.xml =================================================================== --- pom.xml (revision 696338) +++ pom.xml (working copy) @@ -811,7 +811,7 @@ org.apache.lucene lucene-core - 2.2.0 + 2.3.2 org.apache.derby