/*
* 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.exoplatform.services.jcr.impl.core.query.lucene;
import org.apache.lucene.analysis.Analyzer;
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;
import org.exoplatform.services.jcr.impl.core.query.IndexerIoModeHandler;
import org.exoplatform.services.jcr.impl.core.query.lucene.directory.DirectoryManager;
import java.io.IOException;
/**
* 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;
/**
* 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 directoryManager the directory manager.
* @throws IOException if an error occurs while opening / creating the
* index.
*/
PersistentIndex(String name, Analyzer analyzer, Similarity similarity, DocNumberCache cache,
final DirectoryManager directoryManager, IndexerIoModeHandler modeHandler) throws IOException
{
super(analyzer, similarity, directoryManager.getDirectory(name), cache, modeHandler);
this.name = name;
if (isExisting())
{
IndexMigration.migrate(PersistentIndex.this, directoryManager);
}
}
/**
* {@inheritDoc}
*/
@Override
int removeDocument(Term idTerm) throws IOException
{
int num = super.removeDocument(idTerm);
if (num > 0 && listener != null)
{
listener.documentDeleted(idTerm);
}
return num;
}
/**
* Merges the provided indexes into this index. After this completes, the
* index is optimized.
* <br>
* 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().maybeMerge();
}
/**
* 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(final 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 (int i = 0; i < files.length; i++)
{
IndexInput in = dir.openInput(files[i]);
try
{
IndexOutput out = dest.createOutput(files[i]);
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();
}
}
}
/**
* 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
{
// Ensure all in-memory changes are persisted before returning reader to merger
releaseWriterAndReaders();
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;
}
}