You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by mr...@apache.org on 2008/11/17 12:03:42 UTC

svn commit: r718218 - in /jackrabbit/trunk/jackrabbit-core/src: main/java/org/apache/jackrabbit/core/query/lucene/ main/java/org/apache/jackrabbit/core/query/lucene/directory/ test/java/org/apache/jackrabbit/core/query/lucene/directory/

Author: mreutegg
Date: Mon Nov 17 03:03:41 2008
New Revision: 718218

URL: http://svn.apache.org/viewvc?rev=718218&view=rev
Log:
JCR-1747: org.apache.jackrabbit.core.query.lucene.SearchIndex with in-memory lucene index

Added:
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/directory/
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/directory/DirectoryManager.java   (with props)
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/directory/FSDirectoryManager.java   (with props)
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/directory/IndexInputStream.java   (with props)
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/directory/IndexOutputStream.java   (with props)
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/directory/RAMDirectoryManager.java   (with props)
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/lucene/directory/
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/lucene/directory/DirectoryManagerTest.java   (with props)
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/lucene/directory/IndexInputStreamTest.java   (with props)
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/lucene/directory/IndexOutputStreamTest.java   (with props)
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/lucene/directory/TestAll.java   (with props)
Modified:
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/IndexInfos.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/IndexMigration.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/IndexingQueueStore.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/MultiIndex.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/PersistentIndex.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/RedoLog.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/SearchIndex.java

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/IndexInfos.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/IndexInfos.java?rev=718218&r1=718217&r2=718218&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/IndexInfos.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/IndexInfos.java Mon Nov 17 03:03:41 2008
@@ -19,16 +19,17 @@
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
 import java.io.IOException;
-import java.io.File;
-import java.io.FileInputStream;
 import java.io.InputStream;
-import java.io.FileOutputStream;
 import java.io.OutputStream;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
 import java.util.HashSet;
 
+import org.apache.lucene.store.Directory;
+import org.apache.jackrabbit.core.query.lucene.directory.IndexInputStream;
+import org.apache.jackrabbit.core.query.lucene.directory.IndexOutputStream;
+
 /**
  * Stores a sequence of index names.
  */
@@ -74,9 +75,10 @@
      *
      * @param dir the directory where to look for the index infos.
      * @return <code>true</code> if it exists; <code>false</code> otherwise.
+     * @throws IOException if an error occurs while reading from the directory.
      */
-    boolean exists(File dir) {
-        return new File(dir, name).exists();
+    boolean exists(Directory dir) throws IOException {
+        return dir.fileExists(name);
     }
 
     /**
@@ -94,8 +96,8 @@
      * @param dir the directory from where to read the index infos.
      * @throws IOException if an error occurs.
      */
-    void read(File dir) throws IOException {
-        InputStream in = new FileInputStream(new File(dir, name));
+    void read(Directory dir) throws IOException {
+        InputStream in = new IndexInputStream(dir.openInput(name));
         try {
             DataInputStream di = new DataInputStream(in);
             counter = di.readInt();
@@ -115,14 +117,13 @@
      * @param dir the directory where to write the index infos.
      * @throws IOException if an error occurs.
      */
-    void write(File dir) throws IOException {
+    void write(Directory dir) throws IOException {
         // do not write if not dirty
         if (!dirty) {
             return;
         }
 
-        File nu = new File(dir, name + ".new");
-        OutputStream out = new FileOutputStream(nu);
+        OutputStream out = new IndexOutputStream(dir.createOutput(name + ".new"));
         try {
             DataOutputStream dataOut = new DataOutputStream(out);
             dataOut.writeInt(counter);
@@ -134,13 +135,10 @@
             out.close();
         }
         // delete old
-        File old = new File(dir, name);
-        if (old.exists() && !old.delete()) {
-            throw new IOException("Unable to delete file: " + old.getAbsolutePath());
-        }
-        if (!nu.renameTo(old)) {
-            throw new IOException("Unable to rename file: " + nu.getAbsolutePath());
+        if (dir.fileExists(name)) {
+            dir.deleteFile(name);
         }
+        dir.renameFile(name + ".new", name);
         dirty = false;
     }
 

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/IndexMigration.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/IndexMigration.java?rev=718218&r1=718217&r2=718218&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/IndexMigration.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/IndexMigration.java Mon Nov 17 03:03:41 2008
@@ -23,18 +23,16 @@
 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.store.Directory;
 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.apache.jackrabbit.core.query.lucene.directory.DirectoryManager;
 import org.slf4j.LoggerFactory;
 import org.slf4j.Logger;
 
 import java.io.IOException;
-import java.io.File;
 
 /**
  * <code>IndexMigration</code> implements a utility that migrates a Jackrabbit
@@ -56,11 +54,14 @@
      * Checks if the given <code>index</code> needs to be migrated.
      *
      * @param index the index to check and migration if needed.
-     * @param indexDir the directory where the index is stored.
+     * @param directoryManager the directory manager.
      * @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());
+    public static void migrate(PersistentIndex index,
+                               DirectoryManager directoryManager)
+            throws IOException {
+        Directory indexDir = index.getDirectory();
+        log.debug("Checking {} ...", indexDir);
         ReadOnlyIndexReader reader = index.getReadOnlyIndexReader();
         try {
             if (IndexFormatVersion.getVersion(reader).getVersion() >=
@@ -87,24 +88,20 @@
         }
 
         // if we get here then the index must be migrated
-        log.debug("Index requires migration {}", indexDir.getAbsolutePath());
+        log.debug("Index requires migration {}", indexDir);
 
         // 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());
+        String migrationName = index.getName() + "_v2.3";
+        if (directoryManager.hasDirectory(migrationName)) {
+            directoryManager.delete(migrationName);
         }
-        FSDirectory fsDir = FSDirectory.getDirectory(migrationDir,
-                NoLockFactory.getNoLockFactory());
+
+        Directory migrationDir = directoryManager.getDirectory(migrationName);
         try {
-            IndexWriter writer = new IndexWriter(fsDir, new JackrabbitAnalyzer());
+            IndexWriter writer = new IndexWriter(migrationDir, new JackrabbitAnalyzer());
             try {
                 IndexReader r = new MigrationIndexReader(
                         IndexReader.open(index.getDirectory()));
@@ -118,14 +115,14 @@
                 writer.close();
             }
         } finally {
-            fsDir.close();
+            migrationDir.close();
         }
-        FileUtil.delete(indexDir);
-        if (!migrationDir.renameTo(indexDir)) {
+        directoryManager.delete(index.getName());
+        if (!directoryManager.rename(migrationName, index.getName())) {
             throw new IOException("failed to move migrated directory " +
-                    migrationDir.getAbsolutePath());
+                    migrationDir);
         }
-        log.info("Migrated " + indexDir.getAbsolutePath());
+        log.info("Migrated " + index.getName());
     }
 
     //---------------------------< internal helper >----------------------------

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/IndexingQueueStore.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/IndexingQueueStore.java?rev=718218&r1=718217&r2=718218&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/IndexingQueueStore.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/IndexingQueueStore.java Mon Nov 17 03:03:41 2008
@@ -16,8 +16,8 @@
  */
 package org.apache.jackrabbit.core.query.lucene;
 
-import org.apache.jackrabbit.core.fs.FileSystem;
-import org.apache.jackrabbit.core.fs.FileSystemException;
+import org.apache.jackrabbit.core.query.lucene.directory.IndexInputStream;
+import org.apache.lucene.store.Directory;
 import org.slf4j.LoggerFactory;
 import org.slf4j.Logger;
 
@@ -31,7 +31,7 @@
 /**
  * <code>IndexingQueueStore</code> implements a store that keeps the uuids of
  * nodes that are pending in the indexing queue. Until Jackrabbit 1.4 this store
- * was also persisted to a {@link FileSystem}. Starting with 1.5 the pending
+ * was also persisted to disk. Starting with 1.5 the pending
  * nodes are marked directly in the index with a special field.
  * See {@link FieldNames#REINDEXING_REQUIRED}.
  */
@@ -58,33 +58,28 @@
     private static final String REMOVE = "REMOVE";
 
     /**
-     * The UUID Strings of the pending documents.
+     * Name of the file that contains the indexing queue log.
      */
-    private final Set pending = new HashSet();
+    private static final String INDEXING_QUEUE_FILE = "indexing_queue.log";
 
     /**
-     * The file system from where to read pending document UUIDs.
+     * The UUID Strings of the pending documents.
      */
-    private final FileSystem fs;
+    private final Set pending = new HashSet();
 
     /**
-     * The name of the file for the pending document UUIDs.
+     * The directory from where to read pending document UUIDs.
      */
-    private final String fileName;
+    private final Directory dir;
 
     /**
-     * Creates a new <code>IndexingQueueStore</code> using the given file
-     * system.
+     * Creates a new <code>IndexingQueueStore</code> using the given directory.
      *
-     * @param fs       the file system to use.
-     * @param fileName the name of the file where to write the pending UUIDs
-     *                 to.
-     * @throws FileSystemException if an error ocurrs while reading pending
-     *                             UUIDs.
-     */
-    IndexingQueueStore(FileSystem fs, String fileName) throws FileSystemException {
-        this.fs = fs;
-        this.fileName = fileName;
+     * @param directory the directory to use.
+     * @throws IOException if an error ocurrs while reading pending UUIDs.
+     */
+    IndexingQueueStore(Directory directory) throws IOException {
+        this.dir = directory;
         readStore();
     }
 
@@ -119,11 +114,11 @@
     public void close() {
         if (pending.isEmpty()) {
             try {
-                if (fs.exists(fileName)) {
-                    fs.deleteFile(fileName);
+                if (dir.fileExists(INDEXING_QUEUE_FILE)) {
+                    dir.deleteFile(INDEXING_QUEUE_FILE);
                 }
-            } catch (FileSystemException e) {
-                log.warn("unable to delete " + fileName);
+            } catch (IOException e) {
+                log.warn("unable to delete " + INDEXING_QUEUE_FILE);
             }
         }
     }
@@ -134,39 +129,35 @@
      * Reads all pending UUIDs from the file and puts them into {@link
      * #pending}.
      *
-     * @throws FileSystemException if an error occurs while reading.
+     * @throws IOException if an error occurs while reading.
      */
-    private void readStore() throws FileSystemException {
-        if (fs.exists(fileName)) {
+    private void readStore() throws IOException {
+        if (dir.fileExists(INDEXING_QUEUE_FILE)) {
+            InputStream in = new IndexInputStream(dir.openInput(INDEXING_QUEUE_FILE));
+            BufferedReader reader = new BufferedReader(
+                    new InputStreamReader(in, ENCODING));
             try {
-                InputStream in = fs.getInputStream(fileName);
-                BufferedReader reader = new BufferedReader(
-                        new InputStreamReader(in, ENCODING));
-                try {
-                    String line;
-                    while ((line = reader.readLine()) != null) {
-                        int idx = line.indexOf(' ');
-                        if (idx == -1) {
-                            // invalid line
-                            log.warn("invalid line in {}: {}", fileName, line);
+                String line;
+                while ((line = reader.readLine()) != null) {
+                    int idx = line.indexOf(' ');
+                    if (idx == -1) {
+                        // invalid line
+                        log.warn("invalid line in {}: {}", INDEXING_QUEUE_FILE, line);
+                    } else {
+                        String cmd = line.substring(0, idx);
+                        String uuid = line.substring(idx + 1, line.length());
+                        if (ADD.equals(cmd)) {
+                            pending.add(uuid);
+                        } else if (REMOVE.equals(cmd)) {
+                            pending.remove(uuid);
                         } else {
-                            String cmd = line.substring(0, idx);
-                            String uuid = line.substring(idx + 1, line.length());
-                            if (ADD.equals(cmd)) {
-                                pending.add(uuid);
-                            } else if (REMOVE.equals(cmd)) {
-                                pending.remove(uuid);
-                            } else {
-                                // invalid line
-                                log.warn("invalid line in {}: {}", fileName, line);
-                            }
+                            // invalid line
+                            log.warn("invalid line in {}: {}", INDEXING_QUEUE_FILE, line);
                         }
                     }
-                } finally {
-                    in.close();
                 }
-            } catch (IOException e) {
-                throw new FileSystemException(e.getMessage(), e);
+            } finally {
+                in.close();
             }
         }
     }

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/MultiIndex.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/MultiIndex.java?rev=718218&r1=718217&r2=718218&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/MultiIndex.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/MultiIndex.java Mon Nov 17 03:03:41 2008
@@ -17,8 +17,7 @@
 package org.apache.jackrabbit.core.query.lucene;
 
 import org.apache.jackrabbit.core.NodeId;
-import org.apache.jackrabbit.core.fs.FileSystemException;
-import org.apache.jackrabbit.core.fs.local.LocalFileSystem;
+import org.apache.jackrabbit.core.query.lucene.directory.DirectoryManager;
 import org.apache.jackrabbit.core.state.ItemStateException;
 import org.apache.jackrabbit.core.state.ItemStateManager;
 import org.apache.jackrabbit.core.state.NoSuchItemStateException;
@@ -35,11 +34,10 @@
 import org.apache.lucene.document.Document;
 import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.index.Term;
+import org.apache.lucene.store.Directory;
 
 import javax.jcr.RepositoryException;
 import java.io.IOException;
-import java.io.File;
-import java.io.FileFilter;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
@@ -90,16 +88,6 @@
     private static final PathFactory PATH_FACTORY = PathFactoryImpl.getInstance();
 
     /**
-     * Default name of the redo log file
-     */
-    private static final String REDO_LOG = "redo.log";
-
-    /**
-     * Name of the file that contains the indexing queue log.
-     */
-    private static final String INDEXING_QUEUE_FILE = "indexing_queue.log";
-
-    /**
      * Names of active persistent index directories.
      */
     private final IndexInfos indexNames = new IndexInfos("indexes");
@@ -123,9 +111,14 @@
     private final NamespaceMappings nsMappings;
 
     /**
-     * The base filesystem to store the index.
+     * The directory manager.
+     */
+    private final DirectoryManager directoryManager;
+
+    /**
+     * The base directory to store the index.
      */
-    private final File indexDir;
+    private final Directory indexDir;
 
     /**
      * The query handler
@@ -223,24 +216,20 @@
     /**
      * Creates a new MultiIndex.
      *
-     * @param indexDir the base file system
      * @param handler the search handler
      * @param excludedIDs   Set&lt;NodeId> that contains uuids that should not
      *                      be indexed nor further traversed.
-     * @param mapping the namespace mapping to use
      * @throws IOException if an error occurs
      */
-    MultiIndex(File indexDir,
-               SearchIndex handler,
-               Set excludedIDs,
-               NamespaceMappings mapping) throws IOException {
-
-        this.indexDir = indexDir;
+    MultiIndex(SearchIndex handler,
+               Set excludedIDs) throws IOException {
+        this.directoryManager = handler.getDirectoryManager();
+        this.indexDir = directoryManager.getDirectory(".");
         this.handler = handler;
         this.cache = new DocNumberCache(handler.getCacheSize());
-        this.redoLog = new RedoLog(new File(indexDir, REDO_LOG));
+        this.redoLog = new RedoLog(indexDir);
         this.excludedIDs = new HashSet(excludedIDs);
-        this.nsMappings = mapping;
+        this.nsMappings = handler.getNamespaceMappings();
 
         if (indexNames.exists(indexDir)) {
             indexNames.read(indexDir);
@@ -255,34 +244,26 @@
         merger.setMergeFactor(handler.getMergeFactor());
         merger.setMinMergeDocs(handler.getMinMergeDocs());
 
-        IndexingQueueStore store;
-        try {
-            LocalFileSystem fs = new LocalFileSystem();
-            fs.setRoot(indexDir);
-            fs.init();
-            store = new IndexingQueueStore(fs, INDEXING_QUEUE_FILE);
-        } catch (FileSystemException e) {
-            throw Util.createIOException(e);
-        }
+        IndexingQueueStore store = new IndexingQueueStore(indexDir);
 
         // initialize indexing queue
         this.indexingQueue = new IndexingQueue(store);
 
         // open persistent indexes
         for (int i = 0; i < indexNames.size(); i++) {
-            File sub = new File(indexDir, indexNames.getName(i));
+            String name = indexNames.getName(i);
             // only open if it still exists
             // it is possible that indexNames still contains a name for
             // an index that has been deleted, but indexNames has not been
             // written to disk.
-            if (!sub.exists()) {
-                log.debug("index does not exist anymore: " + sub.getAbsolutePath());
+            if (!directoryManager.hasDirectory(name)) {
+                log.debug("index does not exist anymore: " + name);
                 // move on to next index
                 continue;
             }
-            PersistentIndex index = new PersistentIndex(indexNames.getName(i),
-                    sub, handler.getTextAnalyzer(), handler.getSimilarity(),
-                    cache, indexingQueue);
+            PersistentIndex index = new PersistentIndex(name,
+                    handler.getTextAnalyzer(), handler.getSimilarity(),
+                    cache, indexingQueue, directoryManager);
             index.setMaxMergeDocs(handler.getMaxMergeDocs());
             index.setMergeFactor(handler.getMergeFactor());
             index.setMinMergeDocs(handler.getMinMergeDocs());
@@ -580,16 +561,14 @@
         }
 
         // otherwise open / create it
-        File sub;
         if (indexName == null) {
-            sub = newIndexFolder();
-            indexName = sub.getName();
-        } else {
-            sub = new File(indexDir, indexName);
+            do {
+                indexName = indexNames.newName();
+            } while (directoryManager.hasDirectory(indexName));
         }
-        PersistentIndex index = new PersistentIndex(indexName, sub,
+        PersistentIndex index = new PersistentIndex(indexName,
                 handler.getTextAnalyzer(), handler.getSimilarity(),
-                cache, indexingQueue);
+                cache, indexingQueue, directoryManager);
         index.setMaxMergeDocs(handler.getMaxMergeDocs());
         index.setMergeFactor(handler.getMergeFactor());
         index.setMinMergeDocs(handler.getMinMergeDocs());
@@ -608,8 +587,10 @@
      *
      * @param indexName the name of the index segment.
      * @return <code>true</code> if it exists; otherwise <code>false</code>.
+     * @throws IOException if an error occurs while checking existence of
+     *          directory.
      */
-    synchronized boolean hasIndex(String indexName) {
+    synchronized boolean hasIndex(String indexName) throws IOException {
         // check existing
         for (Iterator it = indexes.iterator(); it.hasNext();) {
             PersistentIndex idx = (PersistentIndex) it.next();
@@ -618,7 +599,7 @@
             }
         }
         // check if it exists on disk
-        return new File(indexDir, indexName).exists();
+        return directoryManager.hasDirectory(indexName);
     }
 
     /**
@@ -920,18 +901,15 @@
      * Enqueues unused segments for deletion in {@link #deletable}. This method
      * does not synchronize on {@link #deletable}! A caller must ensure that it
      * is the only one acting on the {@link #deletable} map.
+     *
+     * @throws IOException if an error occurs while reading directories.
      */
-    private void enqueueUnusedSegments() {
+    private void enqueueUnusedSegments() throws IOException {
         // walk through index segments
-        File[] segmentDirs = indexDir.listFiles(new FileFilter() {
-            public boolean accept(File pathname) {
-                return pathname.isDirectory() && pathname.getName().startsWith("_");
-            }
-        });
-        for (int i = 0; i < segmentDirs.length; i++) {
-            String name = segmentDirs[i].getName();
-            if (!indexNames.contains(name)) {
-                deletable.add(name);
+        String[] dirNames = directoryManager.getDirectoryNames();
+        for (int i = 0; i < dirNames.length; i++) {
+            if (dirNames[i].startsWith("_") && !indexNames.contains(dirNames[i])) {
+                deletable.add(dirNames[i]);
             }
         }
     }
@@ -1078,8 +1056,7 @@
         synchronized (deletable) {
             for (Iterator it = deletable.iterator(); it.hasNext(); ) {
                 String indexName = (String) it.next();
-                File dir = new File(indexDir, indexName);
-                if (deleteIndex(dir)) {
+                if (directoryManager.delete(indexName)) {
                     it.remove();
                 } else {
                     log.info("Unable to delete obsolete index: " + indexName);
@@ -1093,52 +1070,14 @@
      * in Jackrabbit versions >= 1.5.
      */
     private void removeDeletable() {
-        File deletable = new File(indexDir, "deletable");
-        if (deletable.exists()) {
-            deletable.delete();
-        }
-    }
-
-    /**
-     * Deletes the index <code>directory</code>.
-     *
-     * @param directory the index directory to delete.
-     * @return <code>true</code> if the delete was successful,
-     *         <code>false</code> otherwise.
-     */
-    private boolean deleteIndex(File directory) {
-        // trivial if it does not exist anymore
-        if (!directory.exists()) {
-            return true;
-        }
-        // delete files first
-        File[] files = directory.listFiles();
-        for (int i = 0; i < files.length; i++) {
-            if (!files[i].delete()) {
-                return false;
+        String fileName = "deletable";
+        try {
+            if (indexDir.fileExists(fileName)) {
+                indexDir.deleteFile(fileName);
             }
+        } catch (IOException e) {
+            log.warn("Unable to remove file 'deletable'.", e);
         }
-        // now delete directory itself
-        return directory.delete();
-    }
-
-    /**
-     * Returns an new index folder which is empty.
-     *
-     * @return the new index folder.
-     * @throws IOException if the folder cannot be created.
-     */
-    private File newIndexFolder() throws IOException {
-        // create new index folder. make sure it does not exist
-        File sub;
-        do {
-            sub = new File(indexDir, indexNames.newName());
-        } while (sub.exists());
-
-        if (!sub.mkdir()) {
-            throw new IOException("Unable to create directory: " + sub.getAbsolutePath());
-        }
-        return sub;
     }
 
     /**

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/PersistentIndex.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/PersistentIndex.java?rev=718218&r1=718217&r2=718218&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/PersistentIndex.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/PersistentIndex.java Mon Nov 17 03:03:41 2008
@@ -22,12 +22,10 @@
 import org.apache.lucene.store.Directory;
 import org.apache.lucene.store.IndexInput;
 import org.apache.lucene.store.IndexOutput;
-import org.apache.lucene.store.FSDirectory;
-import org.apache.lucene.store.NativeFSLockFactory;
 import org.apache.lucene.search.Similarity;
+import org.apache.jackrabbit.core.query.lucene.directory.DirectoryManager;
 
 import java.io.IOException;
-import java.io.File;
 
 /**
  * Implements a lucene index which is based on a
@@ -45,26 +43,27 @@
     private IndexListener listener;
 
     /**
-     * Creates a new <code>PersistentIndex</code> based on the file system
-     * <code>indexDir</code>.
+     * Creates a new <code>PersistentIndex</code>.
+     *
      * @param name the name of this index.
-     * @param indexDir the directory to store the 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.
      * @throws IOException if an error occurs while opening / creating the
      *  index.
      */
-    PersistentIndex(String name, File indexDir, Analyzer analyzer,
+    PersistentIndex(String name, Analyzer analyzer,
                     Similarity similarity, DocNumberCache cache,
-                    IndexingQueue indexingQueue)
+                    IndexingQueue indexingQueue,
+                    DirectoryManager directoryManager)
             throws IOException {
-        super(analyzer, similarity, FSDirectory.getDirectory(indexDir, new NativeFSLockFactory(indexDir)),
+        super(analyzer, similarity, directoryManager.getDirectory(name),
                 cache, indexingQueue);
         this.name = name;
         if (isExisting()) {
-            IndexMigration.migrate(this, indexDir);
+            IndexMigration.migrate(this, directoryManager);
         }
     }
 

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/RedoLog.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/RedoLog.java?rev=718218&r1=718217&r2=718218&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/RedoLog.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/RedoLog.java Mon Nov 17 03:03:41 2008
@@ -18,6 +18,9 @@
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.apache.lucene.store.Directory;
+import org.apache.jackrabbit.core.query.lucene.directory.IndexOutputStream;
+import org.apache.jackrabbit.core.query.lucene.directory.IndexInputStream;
 
 import java.io.BufferedReader;
 import java.io.BufferedWriter;
@@ -26,10 +29,7 @@
 import java.io.InputStreamReader;
 import java.io.OutputStreamWriter;
 import java.io.Writer;
-import java.io.File;
-import java.io.FileInputStream;
 import java.io.OutputStream;
-import java.io.FileOutputStream;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -50,6 +50,11 @@
     private static final Logger log = LoggerFactory.getLogger(RedoLog.class);
 
     /**
+     * Default name of the redo log file
+     */
+    private static final String REDO_LOG = "redo.log";
+
+    /**
      * Implements a {@link ActionCollector} that counts all entries and sets
      * {@link #entryCount}.
      */
@@ -60,9 +65,9 @@
     };
 
     /**
-     * The log file
+     * The directory where the log file is stored.
      */
-    private final File logFile;
+    private final Directory dir;
 
     /**
      * The number of log entries in the log file
@@ -75,17 +80,14 @@
     private Writer out;
 
     /**
-     * Creates a new <code>RedoLog</code> instance based on the file
-     * <code>logFile</code>
-     * @param log the redo log file.
-     */
-    RedoLog(File log) throws IOException {
-        this.logFile = log;
-        // create the log file if not there
-        if (!log.exists()) {
-            log.getParentFile().mkdirs();
-            log.createNewFile();
-        }
+     * Creates a new <code>RedoLog</code> instance, which stores its log in the
+     * given directory.
+     *
+     * @param dir the directory where the redo log file is located.
+     * @throws IOException if an error occurs while reading the redo log.
+     */
+    RedoLog(Directory dir) throws IOException {
+        this.dir = dir;
         read(ENTRY_COUNTER);
     }
 
@@ -157,8 +159,7 @@
             out.close();
             out = null;
         }
-        // truncate file
-        new FileOutputStream(logFile).close();
+        dir.deleteFile(REDO_LOG);
         entryCount = 0;
     }
 
@@ -169,7 +170,7 @@
      */
     private void initOut() throws IOException {
         if (out == null) {
-            OutputStream os = new FileOutputStream(logFile, true);
+            OutputStream os = new IndexOutputStream(dir.createOutput(REDO_LOG));
             out = new BufferedWriter(new OutputStreamWriter(os));
         }
     }
@@ -182,7 +183,10 @@
      * log file.
      */
     private void read(ActionCollector collector) throws IOException {
-        InputStream in = new FileInputStream(logFile);
+        if (!dir.fileExists(REDO_LOG)) {
+            return;
+        }
+        InputStream in = new IndexInputStream(dir.openInput(REDO_LOG));
         try {
             BufferedReader reader = new BufferedReader(new InputStreamReader(in));
             String line;

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/SearchIndex.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/SearchIndex.java?rev=718218&r1=718217&r2=718218&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/SearchIndex.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/SearchIndex.java Mon Nov 17 03:03:41 2008
@@ -28,6 +28,8 @@
 import org.apache.jackrabbit.core.query.ExecutableQuery;
 import org.apache.jackrabbit.core.query.QueryHandler;
 import org.apache.jackrabbit.core.query.QueryHandlerContext;
+import org.apache.jackrabbit.core.query.lucene.directory.DirectoryManager;
+import org.apache.jackrabbit.core.query.lucene.directory.FSDirectoryManager;
 import org.apache.jackrabbit.core.state.NodeState;
 import org.apache.jackrabbit.core.state.NodeStateIterator;
 import org.apache.jackrabbit.core.state.ItemStateManager;
@@ -405,6 +407,16 @@
     private Similarity similarity = Similarity.getDefault();
 
     /**
+     * The name of the directory manager class implementation.
+     */
+    private String directoryManagerClass = FSDirectoryManager.class.getName();
+
+    /**
+     * The directory manager.
+     */
+    private DirectoryManager directoryManager;
+
+    /**
      * Indicates if this <code>SearchIndex</code> is closed and cannot be used
      * anymore.
      */
@@ -437,8 +449,7 @@
 
         extractor = createTextExtractor();
         synProvider = createSynonymProvider();
-
-        File indexDir = new File(path);
+        directoryManager = createDirectoryManager();
 
         if (context.getParentHandler() instanceof SearchIndex) {
             // use system namespace mappings
@@ -446,7 +457,7 @@
             nsMappings = sysIndex.getNamespaceMappings();
         } else {
             // read local namespace mappings
-            File mapFile = new File(indexDir, NS_MAPPING_FILE);
+            File mapFile = new File(new File(path), NS_MAPPING_FILE);
             if (mapFile.exists()) {
                 // be backward compatible and use ns_mappings.properties from
                 // index folder
@@ -463,7 +474,7 @@
         indexingConfig = createIndexingConfiguration(nsMappings);
         analyzer.setIndexingConfig(indexingConfig);
 
-        index = new MultiIndex(indexDir, this, excludedIDs, nsMappings);
+        index = new MultiIndex(this, excludedIDs);
         if (index.numDocs() == 0) {
             Path rootPath;
             if (excludedIDs.isEmpty()) {
@@ -849,6 +860,13 @@
     }
 
     /**
+     * @return the directory manager for this search index.
+     */
+    public DirectoryManager getDirectoryManager() {
+        return directoryManager;
+    }
+
+    /**
      * Returns an index reader for this search index. The caller of this method
      * is responsible for closing the index reader when he is finished using
      * it.
@@ -1002,6 +1020,31 @@
     }
 
     /**
+     * @return an initialized {@link DirectoryManager}.
+     * @throws IOException if the directory manager cannot be instantiated or
+     *          an exception occurs while initializing the manager.
+     */
+    protected DirectoryManager createDirectoryManager()
+            throws IOException {
+        try {
+            Class clazz = Class.forName(directoryManagerClass);
+            if (!DirectoryManager.class.isAssignableFrom(clazz)) {
+                throw new IOException(directoryManagerClass +
+                        " is not a DirectoryManager implementation");
+            }
+            DirectoryManager df = (DirectoryManager) clazz.newInstance();
+            df.init(this);
+            return df;
+        } catch (IOException e) {
+            throw e;
+        } catch (Exception e) {
+            IOException ex = new IOException();
+            ex.initCause(e);
+            throw ex;
+        }
+    }
+
+    /**
      * Creates a file system resource to the synonym provider configuration.
      *
      * @return a file system resource or <code>null</code> if no path was
@@ -1898,6 +1941,23 @@
         return maxVolatileIndexSize;
     }
 
+    /**
+     * @return the name of the directory manager class.
+     */
+    public String getDirectoryManagerClass() {
+        return directoryManagerClass;
+    }
+
+    /**
+     * Sets name of the directory manager class. The class must implement
+     * {@link DirectoryManager}.
+     *
+     * @param className the name of the class that implements directory manager.
+     */
+    public void setDirectoryManagerClass(String className) {
+        this.directoryManagerClass = className;
+    }
+
     //----------------------------< internal >----------------------------------
 
     /**

Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/directory/DirectoryManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/directory/DirectoryManager.java?rev=718218&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/directory/DirectoryManager.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/directory/DirectoryManager.java Mon Nov 17 03:03:41 2008
@@ -0,0 +1,96 @@
+/*
+ * 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.directory;
+
+import org.apache.lucene.store.Directory;
+import org.apache.jackrabbit.core.query.lucene.SearchIndex;
+
+import java.io.IOException;
+
+/**
+ * <code>DirectoryManager</code> defines an interface for managing directory
+ * instances used by the search index.
+ */
+public interface DirectoryManager {
+
+    /**
+     * Initializes the directory manager with a reference to the search index.
+     *
+     * @param handler the query handler implementation.
+     * @throws IOException if an error occurs while initializing the directory
+     *          manager.
+     */
+    void init(SearchIndex handler) throws IOException;
+
+    /**
+     * Checks if there exists a directory with the given <code>name</code>.
+     *
+     * @param name the name of a directory.
+     * @return <code>true</code> if the directory exists; <code>false</code>
+     *          otherwise.
+     * @throws IOException if an error occurs while looking up directories.
+     */
+    boolean hasDirectory(String name) throws IOException;
+
+    /**
+     * Gets the directory with the given <code>name</code>. If the directory
+     * does not yet exist then it will be created.
+     *
+     * @param name the name of a directory.
+     * @return the directory.
+     * @throws IOException if an error occurs while getting or creating the
+     *          directory.
+     */
+    Directory getDirectory(String name) throws IOException;
+
+    /**
+     * Returns the names of the currently available directories.
+     *
+     * @return names of the currently available directories.
+     * @throws IOException if an error occurs while retrieving the directory
+     *          names.
+     */
+    String[] getDirectoryNames() throws IOException;
+
+    /**
+     * Deletes the directory with the given name.
+     *
+     * @param name the name of the directory to delete.
+     * @return <code>true</code> if the directory could be deleted successfully,
+     *          <code>false</code> otherwise. This method also returns
+     *          <code>false</code> when the directory with the given
+     *          <code>name</code> does not exist.
+     */
+    boolean delete(String name);
+
+    /**
+     * Renames a directory.
+     *
+     * @param from the name of the directory to rename.
+     * @param to the new name for the directory.
+     * @return <code>true</code> if the directory was successfully renamed.
+     *          Returns <code>false</code> if there is no directory with name
+     *          <code>from</code> or there already exists a directory with name
+     *          <code>to</code> or an error occurs while renaming the directory.
+     */
+    boolean rename(String from, String to);
+
+    /**
+     * Frees resources associated with this directory manager.
+     */
+    void dispose();
+}

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/directory/DirectoryManager.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/directory/FSDirectoryManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/directory/FSDirectoryManager.java?rev=718218&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/directory/FSDirectoryManager.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/directory/FSDirectoryManager.java Mon Nov 17 03:03:41 2008
@@ -0,0 +1,112 @@
+/*
+ * 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.directory;
+
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.store.FSDirectory;
+import org.apache.lucene.store.NativeFSLockFactory;
+import org.apache.jackrabbit.core.query.lucene.SearchIndex;
+
+import java.io.IOException;
+import java.io.File;
+import java.io.FileFilter;
+
+/**
+ * <code>FSDirectoryManager</code> implements a directory manager for
+ * {@link FSDirectory} instances.
+ */
+public class FSDirectoryManager implements DirectoryManager {
+
+    /**
+     * The base directory.
+     */
+    private File baseDir;
+
+    /**
+     * {@inheritDoc}
+     */
+    public void init(SearchIndex handler) throws IOException {
+        baseDir = new File(handler.getPath());
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean hasDirectory(String name) throws IOException {
+        return new File(baseDir, name).exists();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Directory getDirectory(String name)
+            throws IOException {
+        File dir = new File(baseDir, name);
+        return FSDirectory.getDirectory(dir, new NativeFSLockFactory(dir));
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String[] getDirectoryNames() throws IOException {
+        File[] dirs = baseDir.listFiles(new FileFilter() {
+            public boolean accept(File pathname) {
+                return pathname.isDirectory();
+            }
+        });
+        String[] names = new String[dirs.length];
+        for (int i = 0; i < dirs.length; i++) {
+            names[i] = dirs[i].getName();
+        }
+        return names;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean delete(String name) {
+        File directory = new File(baseDir, name);
+        // trivial if it does not exist anymore
+        if (!directory.exists()) {
+            return true;
+        }
+        // delete files first
+        File[] files = directory.listFiles();
+        for (int i = 0; i < files.length; i++) {
+            if (!files[i].delete()) {
+                return false;
+            }
+        }
+        // now delete directory itself
+        return directory.delete();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean rename(String from, String to) {
+        File src = new File(baseDir, from);
+        File dest = new File(baseDir, to);
+        return src.renameTo(dest);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void dispose() {
+    }
+}

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/directory/FSDirectoryManager.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/directory/IndexInputStream.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/directory/IndexInputStream.java?rev=718218&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/directory/IndexInputStream.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/directory/IndexInputStream.java Mon Nov 17 03:03:41 2008
@@ -0,0 +1,90 @@
+/*
+ * 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.directory;
+
+import org.apache.lucene.store.IndexInput;
+
+import java.io.InputStream;
+import java.io.IOException;
+
+/**
+ * <code>IndexInputStream</code> implements an {@link InputStream} that wraps
+ * a lucene {@link IndexInput}.
+ */
+public class IndexInputStream extends InputStream {
+
+    /**
+     * The underlying index input.
+     */
+    private final IndexInput in;
+
+    /**
+     * The length of the index input.
+     */
+    private final long len;
+
+    /**
+     * The position where the next read will occur.
+     */
+    private long pos;
+
+    /**
+     * Creates a new index input stream wrapping the given lucene index
+     * <code>input</code>.
+     *
+     * @param input the index input to wrap.
+     */
+    public IndexInputStream(IndexInput input) {
+        this.in = input;
+        this.len = input.length();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public int read() throws IOException {
+        byte[] buf = new byte[1];
+        if (read(buf, 0, 1) == -1) {
+            return -1;
+        } else {
+            return buf[0] & 0xff;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public int read(byte b[], int off, int len) throws IOException {
+        if (pos >= this.len) {
+            // EOF
+            return -1;
+        }
+        int num = (int) Math.min(len - off, this.len - pos);
+        in.readBytes(b, off, num);
+        pos += num;
+        return num;
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p/>
+     * Closes the underlying index input.
+     */
+    public void close() throws IOException {
+        in.close();
+    }
+}

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/directory/IndexInputStream.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/directory/IndexOutputStream.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/directory/IndexOutputStream.java?rev=718218&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/directory/IndexOutputStream.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/directory/IndexOutputStream.java Mon Nov 17 03:03:41 2008
@@ -0,0 +1,82 @@
+/*
+ * 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.directory;
+
+import java.io.OutputStream;
+import java.io.IOException;
+
+import org.apache.lucene.store.IndexOutput;
+
+/**
+ * <code>IndexOutputStream</code> wraps an {@link IndexOutput} and exposes it
+ * as a regular {@link OutputStream}.
+ */
+public class IndexOutputStream extends OutputStream {
+
+    /**
+     * The underlying index output.
+     */
+    private final IndexOutput out;
+
+    /**
+     * Creates a new index output stream and wraps the given
+     * <code>output</code>. Bytes will always be written at the end of the
+     * <code>output</code>.
+     *
+     * @param output the lucene index output.
+     * @throws IOException if an error occurs while seeking to the end of the
+     *          index <code>output</code>.
+     */
+    public IndexOutputStream(IndexOutput output)
+            throws IOException {
+        this.out = output;
+        this.out.seek(output.length());
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void write(int b) throws IOException {
+        byte[] buf = new byte[]{(byte) (b & 0xff)};
+        write(buf, 0, 1);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void write(byte b[], int off, int len) throws IOException {
+        out.writeBytes(b, off, len);
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p/>
+     * Flushes the underlying index output.
+     */
+    public void flush() throws IOException {
+        out.flush();
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p/>
+     * Closes the underlying index output.
+     */
+    public void close() throws IOException {
+        out.close();
+    }
+}

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/directory/IndexOutputStream.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/directory/RAMDirectoryManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/directory/RAMDirectoryManager.java?rev=718218&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/directory/RAMDirectoryManager.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/directory/RAMDirectoryManager.java Mon Nov 17 03:03:41 2008
@@ -0,0 +1,109 @@
+/*
+ * 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.directory;
+
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.store.RAMDirectory;
+import org.apache.jackrabbit.core.query.lucene.SearchIndex;
+
+import java.util.Map;
+import java.util.HashMap;
+import java.io.IOException;
+
+/**
+ * <code>RAMDirectoryManager</code> implements a directory manager for
+ * {@link RAMDirectory} instances.
+ */
+public class RAMDirectoryManager implements DirectoryManager {
+
+    /**
+     * Map of directories. Key=String(directory name), Value=Directory.
+     */
+    private final Map directories = new HashMap();
+
+    /**
+     * {@inheritDoc}
+     */
+    public void init(SearchIndex handler) throws IOException {
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean hasDirectory(String name) throws IOException {
+        synchronized (directories) {
+            return directories.containsKey(name);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Directory getDirectory(String name) {
+        synchronized (directories) {
+            Directory dir = (Directory) directories.get(name);
+            if (dir == null) {
+                dir = new RAMDirectory();
+                directories.put(name, dir);
+            }
+            return dir;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String[] getDirectoryNames() throws IOException {
+        synchronized (directories) {
+            return (String[]) directories.keySet().toArray(
+                    new String[directories.size()]);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean delete(String name) {
+        synchronized (directories) {
+            directories.remove(name);
+        }
+        return true;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean rename(String from, String to) {
+        synchronized (directories) {
+            if (directories.containsKey(to)) {
+                return false;
+            }
+            Directory dir = (Directory) directories.remove(from);
+            if (dir == null) {
+                return false;
+            }
+            directories.put(to, dir);
+            return true;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void dispose() {
+    }
+}

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/query/lucene/directory/RAMDirectoryManager.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/lucene/directory/DirectoryManagerTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/lucene/directory/DirectoryManagerTest.java?rev=718218&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/lucene/directory/DirectoryManagerTest.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/lucene/directory/DirectoryManagerTest.java Mon Nov 17 03:03:41 2008
@@ -0,0 +1,101 @@
+/*
+ * 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.directory;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.io.File;
+
+import org.apache.jackrabbit.core.query.lucene.SearchIndex;
+import org.apache.lucene.store.Directory;
+
+import junit.framework.TestCase;
+
+/**
+ * <code>DirectoryManagerTest</code> performs tests on directory manager
+ * implementations.
+ */
+public class DirectoryManagerTest extends TestCase {
+
+    private static final Collection IMPLEMENTATIONS = Arrays.asList(
+            new Class[]{FSDirectoryManager.class, RAMDirectoryManager.class});
+
+    private static final SearchIndex INDEX = new SearchIndex();
+
+    static {
+        INDEX.setPath(new File(new File("target"), "directory-factory-test").getAbsolutePath());
+    }
+
+    public void testHasDirectory() throws Exception {
+        execute(new Callable(){
+            public void call(DirectoryManager directoryManager) throws Exception {
+                Directory dir = directoryManager.getDirectory("test");
+                assertTrue(directoryManager.hasDirectory("test"));
+                dir.close();
+            }
+        });
+    }
+
+    public void testDelete() throws Exception {
+        execute(new Callable(){
+            public void call(DirectoryManager directoryManager) throws Exception {
+                directoryManager.getDirectory("test").close();
+                directoryManager.delete("test");
+                assertFalse(directoryManager.hasDirectory("test"));
+            }
+        });
+    }
+
+    public void testGetDirectoryNames() throws Exception {
+        execute(new Callable(){
+            public void call(DirectoryManager directoryManager) throws Exception {
+                directoryManager.getDirectory("test").close();
+                assertTrue(Arrays.asList(directoryManager.getDirectoryNames()).contains("test"));
+            }
+        });
+    }
+
+    public void testRename() throws Exception {
+        execute(new Callable(){
+            public void call(DirectoryManager directoryManager) throws Exception {
+                directoryManager.getDirectory("test").close();
+                directoryManager.rename("test", "renamed");
+                assertTrue(directoryManager.hasDirectory("renamed"));
+                assertFalse(directoryManager.hasDirectory("test"));
+            }
+        });
+    }
+
+    private void execute(Callable callable) throws Exception {
+        for (Iterator it = IMPLEMENTATIONS.iterator(); it.hasNext(); ) {
+            Class clazz = (Class) it.next();
+            DirectoryManager dirMgr = (DirectoryManager) clazz.newInstance();
+            dirMgr.init(INDEX);
+            try {
+                callable.call(dirMgr);
+            } finally {
+                dirMgr.dispose();
+            }
+        }
+    }
+
+    private interface Callable {
+
+        public void call(DirectoryManager directoryManager) throws Exception;
+    }
+}

Propchange: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/lucene/directory/DirectoryManagerTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/lucene/directory/IndexInputStreamTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/lucene/directory/IndexInputStreamTest.java?rev=718218&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/lucene/directory/IndexInputStreamTest.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/lucene/directory/IndexInputStreamTest.java Mon Nov 17 03:03:41 2008
@@ -0,0 +1,69 @@
+/*
+ * 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.directory;
+
+import java.io.IOException;
+import java.io.BufferedInputStream;
+import java.io.InputStream;
+import java.util.Random;
+
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.store.RAMDirectory;
+import org.apache.lucene.store.IndexOutput;
+
+import junit.framework.TestCase;
+
+/**
+ * <code>IndexInputStreamTest</code> performs tests on {@link IndexInputStream}.
+ */
+public class IndexInputStreamTest extends TestCase {
+
+    public void testIndexInputStream() throws IOException {
+        checkStream(0, 0);
+        checkStream(0, 128);
+        checkStream(128, 0);
+        checkStream(128, 128);
+        checkStream(127, 128);
+        checkStream(129, 128);
+        checkStream(300, 128);
+    }
+
+    private void checkStream(int size, int buffer) throws IOException {
+        Random rand = new Random();
+        byte[] data = new byte[size];
+        rand.nextBytes(data);
+        Directory dir = new RAMDirectory();
+        IndexOutput out = dir.createOutput("test");
+        out.writeBytes(data, data.length);
+        out.close();
+        InputStream in = new IndexInputStream(dir.openInput("test"));
+        if (buffer != 0) {
+            in = new BufferedInputStream(in, buffer);
+        }
+        byte[] buf = new byte[3];
+        int len;
+        int pos = 0;
+        while ((len = in.read(buf)) > -1) {
+            for (int i = 0; i < len; i++, pos++) {
+                assertEquals(data[pos], buf[i]);
+            }
+        }
+        in.close();
+        // assert length
+        assertEquals(data.length, pos);
+    }
+}

Propchange: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/lucene/directory/IndexInputStreamTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/lucene/directory/IndexOutputStreamTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/lucene/directory/IndexOutputStreamTest.java?rev=718218&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/lucene/directory/IndexOutputStreamTest.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/lucene/directory/IndexOutputStreamTest.java Mon Nov 17 03:03:41 2008
@@ -0,0 +1,76 @@
+/*
+ * 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.directory;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.BufferedOutputStream;
+import java.util.Random;
+
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.store.RAMDirectory;
+import org.apache.lucene.store.IndexInput;
+
+import junit.framework.TestCase;
+
+/**
+ * <code>IndexOutputStreamTest</code> performs tests on {@link IndexOutputStream}.
+ */
+public class IndexOutputStreamTest extends TestCase {
+
+    public void testIndexOutputStream() throws IOException {
+        checkStream(0, 0);
+        checkStream(0, 128);
+        checkStream(128, 0);
+        checkStream(127, 128);
+        checkStream(128, 128);
+        checkStream(129, 128);
+        checkStream(300, 128);
+    }
+
+    private void checkStream(int size, int buffer) throws IOException {
+        Random rand = new Random();
+        byte[] data = new byte[size];
+        rand.nextBytes(data);
+        Directory dir = new RAMDirectory();
+        OutputStream out = new IndexOutputStream(dir.createOutput("test"));
+        if (buffer != 0) {
+            out = new BufferedOutputStream(out, buffer);
+        }
+        out.write(data);
+        out.close();
+
+        byte[] buf = new byte[3];
+        int pos = 0;
+        IndexInput in = dir.openInput("test");
+        for (;;) {
+            int len = (int) Math.min(buf.length, in.length() - pos);
+            in.readBytes(buf, 0, len);
+            for (int i = 0; i < len; i++, pos++) {
+                assertEquals(data[pos], buf[i]);
+            }
+            if (len == 0) {
+                // EOF
+                break;
+            }
+        }
+        in.close();
+
+        // assert length
+        assertEquals(data.length, pos);
+    }
+}

Propchange: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/lucene/directory/IndexOutputStreamTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/lucene/directory/TestAll.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/lucene/directory/TestAll.java?rev=718218&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/lucene/directory/TestAll.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/lucene/directory/TestAll.java Mon Nov 17 03:03:41 2008
@@ -0,0 +1,44 @@
+/*
+ * 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.directory;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * Test suite that includes all testcases for the directory module.
+ */
+public class TestAll extends TestCase {
+
+    /**
+     * Returns a <code>Test</code> suite that executes all tests inside this
+     * package.
+     *
+     * @return a <code>Test</code> suite that executes all tests inside this
+     *         package.
+     */
+    public static Test suite() {
+        TestSuite suite = new TestSuite("Directory tests");
+
+        suite.addTestSuite(IndexInputStreamTest.class);
+        suite.addTestSuite(IndexOutputStreamTest.class);
+        suite.addTestSuite(DirectoryManagerTest.class);
+
+        return suite;
+    }
+}
\ No newline at end of file

Propchange: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/lucene/directory/TestAll.java
------------------------------------------------------------------------------
    svn:eol-style = native