/*
 * 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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.Term;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.IndexInput;
import org.apache.lucene.store.IndexOutput;

import java.io.IOException;
import java.io.File;

/**
 * Implements a lucene index which is based on a
 * {@link org.apache.jackrabbit.core.fs.FileSystem}.
 */
class PersistentIndex extends AbstractIndex {

    /** The logger instance for this class */
    private static final Logger log = LoggerFactory.getLogger(PersistentIndex.class);

    /** Name of the write lock file */
    private static final String WRITE_LOCK = IndexWriter.WRITE_LOCK_NAME;

    /** Name of the commit lock file */
    private static final String COMMIT_LOCK = IndexWriter.COMMIT_LOCK_NAME;

    /** The name of this persistent index */
    private final String name;

    /** Set to <code>true</code> if this index encountered locks on startup */
    private boolean lockEncountered = false;

    /**
     * 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> based on the file system
     * <code>indexDir</code>.
     * @param name the name of this index.
     * @param indexDir the directory to store the index.
     * @param create if <code>true</code> an existing index is deleted.
     * @param analyzer the analyzer for text tokenizing.
     * @param cache the document number cache
     * @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, boolean create,
                    Analyzer analyzer, DocNumberCache cache,
                    IndexingQueue indexingQueue)
            throws IOException {
        super(analyzer, FSDirectory.getDirectory(indexDir, create),
                cache, indexingQueue);
        this.name = name;

        // check if index is locked, probably from an unclean repository
        // shutdown
        File writeLock = new File(indexDir, WRITE_LOCK);
        if (writeLock.exists()) {
            lockEncountered = true;
            log.warn("Removing write lock on search index.");
            if (!writeLock.delete()) {
                log.error("Unable to remove write lock on search index.");
            }
        }
        File commitLock = new File(indexDir, COMMIT_LOCK);
        if (commitLock.exists()) {
            lockEncountered = true;
            log.warn("Removing commit lock on search index.");
            if (!commitLock.delete()) {
                log.error("Unable to remove write lock on search index.");
            }
        }
    }

    /**
     * @inheritDoc
     */
    int removeDocument(Term idTerm) throws IOException {
        int num = super.removeDocument(idTerm);
        if (num > 0 && listener != null) {
            listener.documentDeleted(idTerm);
        }
        return num;
    }

    /**
     * Returns <code>true</code> if this index encountered a lock on the file
     * system during startup. This indicates a unclean shutdown.
     *
     * @return <code>true</code> if this index encountered a lock on startup;
     *         <code>false</code> otherwise.
     */
    boolean getLockEncountered() {
        return lockEncountered;
    }

    /**
     * Merges another index into this persistent index. Before <code>index</code>
     * is merged, {@link AbstractIndex#commit()} is called on that
     * <code>index</code>.
     *
     * @param index the other index to merge.
     * @throws IOException if an error occurs while merging.
     */
    void mergeIndex(AbstractIndex index) throws IOException {
        // commit changes to directory on other index.
        index.commit();
        // merge index
        getIndexWriter().addIndexes(new Directory[]{
            index.getDirectory()
        });
        invalidateSharedReader();
    }

    /**
     * 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.list();
        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 {
        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;
    }
}

@@ -1746,36 +1746,21 @@
 K = 
-IndexWriter.COMMIT_LOCK_NAME
+%22commit.lock%22
 ;%0A%0A 

Upgrade to Lucene 2.1 - remove reference to field COMMIT_LOCK_NAME in IndexWriter that was removed in 2.1