/*
* 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;
import org.apache.jackrabbit.core.query.lucene.directory.DirectoryManager;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.index.IndexDeletionPolicy;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.Similarity;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.IndexInput;
import org.apache.lucene.store.IndexOutput;
/**
* Implements a lucene index which is based on a
* {@link org.apache.jackrabbit.core.fs.FileSystem}.
*/
class PersistentIndex extends AbstractIndex {
/** The name of this persistent index */
private final String name;
/**
* If non <code>null</code>, <code>listener</code> needs to be informed
* when a document is deleted.
*/
private IndexListener listener;
/**
* The index deletion policy. Old index generations are deleted when they
* reach a certain age.
*/
private final IndexDeletionPolicyImpl indexDelPolicy;
/**
* The current generation of this persistent index.
*/
private long generation;
/**
* Creates a new <code>PersistentIndex</code>.
*
* @param name the name of this index.
* @param analyzer the analyzer for text tokenizing.
* @param similarity the similarity implementation.
* @param cache the document number cache
* @param indexingQueue the indexing queue.
* @param directoryManager the directory manager.
* @param generationMaxAge age in seconds after which an index generation is
* deleted.
* @throws IOException if an error occurs while opening / creating the
* index.
*/
PersistentIndex(String name, Analyzer analyzer,
Similarity similarity, DocNumberCache cache,
IndexingQueue indexingQueue,
DirectoryManager directoryManager, long generationMaxAge)
throws IOException {
super(analyzer, similarity, directoryManager.getDirectory(name),
cache, indexingQueue);
this.name = name;
this.indexDelPolicy = new IndexDeletionPolicyImpl(this,
generationMaxAge * 1000);
if (isExisting()) {
IndexMigration.migrate(this, directoryManager, '\uFFFF');
}
}
/**
* @inheritDoc
*/
int removeDocument(Term idTerm) throws IOException {
int num = super.removeDocument(idTerm);
if (num > 0 && listener != null) {
listener.documentDeleted(idTerm);
}
return num;
}
/**
* @return the index deletion policy of this index.
*/
protected IndexDeletionPolicy getIndexDeletionPolicy() {
return indexDelPolicy;
}
/**
* Merges the provided indexes into this index. After this completes, the
* index is optimized.
* <p>
* The provided IndexReaders are not closed.
*
* @param readers the readers of indexes to add.
* @throws IOException if an error occurs while adding indexes.
*/
void addIndexes(IndexReader[] readers) throws IOException {
getIndexWriter().addIndexes(readers);
getIndexWriter().optimize();
}
/**
* Copies <code>index</code> into this persistent index. This method should
* only be called when <code>this</code> index is empty otherwise the
* behaviour is undefined.
*
* @param index the index to copy from.
* @throws IOException if an error occurs while copying.
*/
void copyIndex(AbstractIndex index) throws IOException {
// commit changes to directory on other index.
index.commit(true);
// simply copy over the files
byte[] buffer = new byte[1024];
Directory dir = index.getDirectory();
Directory dest = getDirectory();
String[] files = dir.listAll();
for (String file : files) {
IndexInput in = dir.openInput(file);
try {
IndexOutput out = dest.createOutput(file);
try {
long remaining = in.length();
while (remaining > 0) {
int num = (int) Math.min(remaining, buffer.length);
in.readBytes(buffer, 0, num);
out.writeBytes(buffer, num);
remaining -= num;
}
} finally {
out.close();
}
} finally {
in.close();
}
}
// refresh current generation
indexDelPolicy.readCurrentGeneration();
}
/**
* Returns a <code>ReadOnlyIndexReader</code> and registeres
* <code>listener</code> to send notifications when documents are deleted on
* <code>this</code> index.
*
* @param listener the listener to notify when documents are deleted.
* @return a <code>ReadOnlyIndexReader</code>.
* @throws IOException if the reader cannot be obtained.
*/
synchronized ReadOnlyIndexReader getReadOnlyIndexReader(IndexListener listener)
throws IOException {
ReadOnlyIndexReader reader = getReadOnlyIndexReader();
this.listener = listener;
return reader;
}
/**
* Removes a potentially registered {@link IndexListener}.
*/
synchronized void resetListener() {
this.listener = null;
}
/**
* Returns the number of documents in this persistent index.
*
* @return the number of documents in this persistent index.
* @throws IOException if an error occurs while reading from the index.
*/
int getNumDocuments() throws IOException {
return getIndexReader().numDocs();
}
/**
* Returns the name of this index.
* @return the name of this index.
*/
String getName() {
return name;
}
/**
* @return the current generation of this index.
*/
long getCurrentGeneration() {
return generation;
}
/**
* Sets the current generation of this index. This method should only be
* called by {@link IndexDeletionPolicyImpl}.
*
* @param generation the current generation.
*/
void setCurrentGeneration(long generation) {
this.generation = generation;
}
}