Index: src/java/org/apache/lucene/index/SegmentReader.java
===================================================================
--- src/java/org/apache/lucene/index/SegmentReader.java	(revision 927380)
+++ src/java/org/apache/lucene/index/SegmentReader.java	(working copy)
@@ -58,6 +58,9 @@
   private boolean normsDirty = false;
   private int pendingDeleteCount;
 
+  // The registered plugins
+  private Map<String, SegmentPlugin> plugins = new HashMap<String, SegmentPlugin>();
+
   private boolean rollbackHasChanges = false;
   private boolean rollbackDeletedDocsDirty = false;
   private boolean rollbackNormsDirty = false;
@@ -290,6 +293,13 @@
   }
 
   /**
+   * Get a plugin by name.
+   */
+  public SegmentPlugin getPlugin(String name) {
+    return plugins.get(name);
+  }
+
+  /**
    * Sets the initial value 
    */
   private class FieldsReaderLocal extends CloseableThreadLocal<FieldsReader> {
@@ -558,6 +568,14 @@
    * @throws CorruptIndexException if the index is corrupt
    * @throws IOException if there is a low-level IO error
    */
+  public static SegmentReader get(boolean readOnly, SegmentInfo si, int termInfosIndexDivisor, Map<String, SegmentPluginFactory> plugins) throws CorruptIndexException, IOException {
+    return get(readOnly, si.dir, si, BufferedIndexInput.BUFFER_SIZE, true, termInfosIndexDivisor, plugins);
+  }
+
+  /**
+   * @throws CorruptIndexException if the index is corrupt
+   * @throws IOException if there is a low-level IO error
+   */
   public static SegmentReader get(boolean readOnly,
                                   Directory dir,
                                   SegmentInfo si,
@@ -565,6 +583,22 @@
                                   boolean doOpenStores,
                                   int termInfosIndexDivisor)
     throws CorruptIndexException, IOException {
+    return get(readOnly, dir, si, readBufferSize, doOpenStores, termInfosIndexDivisor, null);
+  }
+
+
+  /**
+   * @throws CorruptIndexException if the index is corrupt
+   * @throws IOException if there is a low-level IO error
+   */
+  public static SegmentReader get(boolean readOnly,
+                                  Directory dir,
+                                  SegmentInfo si,
+                                  int readBufferSize,
+                                  boolean doOpenStores,
+                                  int termInfosIndexDivisor,
+                                  Map<String, SegmentPluginFactory> plugins)
+    throws CorruptIndexException, IOException {
     SegmentReader instance = readOnly ? new ReadOnlySegmentReader() : new SegmentReader();
     instance.readOnly = readOnly;
     instance.si = si;
@@ -579,6 +613,17 @@
       }
       instance.loadDeletedDocs();
       instance.openNorms(instance.core.cfsDir, readBufferSize);
+
+      // Initialize plugins
+      if (plugins != null) {
+        for (Map.Entry<String, SegmentPluginFactory> entry : plugins.entrySet()) {
+          final SegmentPlugin plugin = entry.getValue().open(instance);
+          if (plugin != null) {
+            instance.plugins.put(entry.getKey(), plugin);
+          }
+        }
+      }
+
       success = true;
     } finally {
 
@@ -730,6 +775,14 @@
       // any norms that have changed:
       clone.openNorms(si.getUseCompoundFile() ? core.getCFSReader() : directory(), readBufferSize);
 
+      // Reopen plugins
+      for (Map.Entry<String, SegmentPlugin> entry : plugins.entrySet()) {
+        final SegmentPlugin plugin = entry.getValue().reopen(clone);
+        if (plugin != null) {
+          clone.plugins.put(entry.getKey(), entry.getValue().reopen(clone));
+        }
+      }
+
       success = true;
     } finally {
       if (!success) {
@@ -795,6 +848,11 @@
     if (core != null) {
       core.decRef();
     }
+
+    // Close the plugins
+    for (SegmentPlugin plugin : plugins.values()) {
+      plugin.close();
+    }
   }
 
   static boolean hasDeletions(SegmentInfo si) throws IOException {
Index: src/java/org/apache/lucene/index/ReadOnlyDirectoryReader.java
===================================================================
--- src/java/org/apache/lucene/index/ReadOnlyDirectoryReader.java	(revision 927380)
+++ src/java/org/apache/lucene/index/ReadOnlyDirectoryReader.java	(working copy)
@@ -23,13 +23,13 @@
 import java.util.Map;
 
 class ReadOnlyDirectoryReader extends DirectoryReader {
-  ReadOnlyDirectoryReader(Directory directory, SegmentInfos sis, IndexDeletionPolicy deletionPolicy, int termInfosIndexDivisor) throws IOException {
-    super(directory, sis, deletionPolicy, true, termInfosIndexDivisor);
+  ReadOnlyDirectoryReader(Directory directory, SegmentInfos sis, IndexDeletionPolicy deletionPolicy, int termInfosIndexDivisor, final Map<String, SegmentPluginFactory> plugins) throws IOException {
+    super(directory, sis, deletionPolicy, true, termInfosIndexDivisor, plugins);
   }
 
   ReadOnlyDirectoryReader(Directory directory, SegmentInfos infos, SegmentReader[] oldReaders, int[] oldStarts,  Map<String,byte[]> oldNormsCache, boolean doClone,
-                          int termInfosIndexDivisor) throws IOException {
-    super(directory, infos, oldReaders, oldStarts, oldNormsCache, true, doClone, termInfosIndexDivisor);
+                          int termInfosIndexDivisor, final Map<String, SegmentPluginFactory> plugins) throws IOException {
+    super(directory, infos, oldReaders, oldStarts, oldNormsCache, true, doClone, termInfosIndexDivisor, plugins);
   }
   
   ReadOnlyDirectoryReader(IndexWriter writer, SegmentInfos infos, int termInfosIndexDivisor) throws IOException {
Index: src/java/org/apache/lucene/index/SegmentPluginFactory.java
===================================================================
--- src/java/org/apache/lucene/index/SegmentPluginFactory.java	(revision 0)
+++ src/java/org/apache/lucene/index/SegmentPluginFactory.java	(revision 0)
@@ -0,0 +1,15 @@
+package org.apache.lucene.index;
+
+import java.io.IOException;
+
+/**
+ * Factory for creating a plugin for SegmentReader.
+ */
+public abstract class SegmentPluginFactory {
+
+  /**
+   * Initialize a segment plugin for <code>reader</code>.
+   */
+  public abstract SegmentPlugin open(SegmentReader reader) throws IOException;
+
+}
\ No newline at end of file

Property changes on: src/java/org/apache/lucene/index/SegmentPluginFactory.java
___________________________________________________________________
Added: svn:eol-style
   + native

Index: src/java/org/apache/lucene/index/DirectoryReader.java
===================================================================
--- src/java/org/apache/lucene/index/DirectoryReader.java	(revision 927380)
+++ src/java/org/apache/lucene/index/DirectoryReader.java	(working copy)
@@ -63,25 +63,32 @@
   private int maxDoc = 0;
   private int numDocs = -1;
   private boolean hasDeletions = false;
+  private Map<String, SegmentPluginFactory> plugins; // factory for initializing SegmentReaders
 
   static IndexReader open(final Directory directory, final IndexDeletionPolicy deletionPolicy, final IndexCommit commit, final boolean readOnly,
                           final int termInfosIndexDivisor) throws CorruptIndexException, IOException {
+    return open(directory, deletionPolicy, commit, readOnly, termInfosIndexDivisor, null);
+  }
+
+  static IndexReader open(final Directory directory, final IndexDeletionPolicy deletionPolicy, final IndexCommit commit, final boolean readOnly,
+                          final int termInfosIndexDivisor, final Map<String, SegmentPluginFactory> plugins) throws CorruptIndexException, IOException {
     return (IndexReader) new SegmentInfos.FindSegmentsFile(directory) {
       @Override
       protected Object doBody(String segmentFileName) throws CorruptIndexException, IOException {
         SegmentInfos infos = new SegmentInfos();
         infos.read(directory, segmentFileName);
         if (readOnly)
-          return new ReadOnlyDirectoryReader(directory, infos, deletionPolicy, termInfosIndexDivisor);
+          return new ReadOnlyDirectoryReader(directory, infos, deletionPolicy, termInfosIndexDivisor, plugins);
         else
-          return new DirectoryReader(directory, infos, deletionPolicy, false, termInfosIndexDivisor);
+          return new DirectoryReader(directory, infos, deletionPolicy, false, termInfosIndexDivisor, plugins);
       }
     }.run(commit);
   }
 
   /** Construct reading the named set of readers. */
-  DirectoryReader(Directory directory, SegmentInfos sis, IndexDeletionPolicy deletionPolicy, boolean readOnly, int termInfosIndexDivisor) throws IOException {
+  DirectoryReader(Directory directory, SegmentInfos sis, IndexDeletionPolicy deletionPolicy, boolean readOnly, int termInfosIndexDivisor, final Map<String, SegmentPluginFactory> plugins) throws IOException {
     this.directory = directory;
+    this.plugins = plugins;
     this.readOnly = readOnly;
     this.segmentInfos = sis;
     this.deletionPolicy = deletionPolicy;
@@ -102,7 +109,7 @@
     for (int i = sis.size()-1; i >= 0; i--) {
       boolean success = false;
       try {
-        readers[i] = SegmentReader.get(readOnly, sis.info(i), termInfosIndexDivisor);
+        readers[i] = SegmentReader.get(readOnly, sis.info(i), termInfosIndexDivisor, plugins);
         success = true;
       } finally {
         if (!success) {
@@ -125,6 +132,7 @@
   DirectoryReader(IndexWriter writer, SegmentInfos infos, int termInfosIndexDivisor) throws IOException {
     this.directory = writer.getDirectory();
     this.readOnly = true;
+    this.plugins = writer.getSegmentPlugins();
     this.segmentInfos = infos;
     segmentInfosStart = (SegmentInfos) infos.clone();
     this.termInfosIndexDivisor = termInfosIndexDivisor;
@@ -178,9 +186,10 @@
 
   /** This constructor is only used for {@link #reopen()} */
   DirectoryReader(Directory directory, SegmentInfos infos, SegmentReader[] oldReaders, int[] oldStarts,
-                  Map<String,byte[]> oldNormsCache, boolean readOnly, boolean doClone, int termInfosIndexDivisor) throws IOException {
+                  Map<String,byte[]> oldNormsCache, boolean readOnly, boolean doClone, int termInfosIndexDivisor, final Map<String, SegmentPluginFactory> plugins) throws IOException {
     this.directory = directory;
     this.readOnly = readOnly;
+    this.plugins = plugins;
     this.segmentInfos = infos;
     this.termInfosIndexDivisor = termInfosIndexDivisor;
     if (!readOnly) {
@@ -226,7 +235,7 @@
           assert !doClone;
 
           // this is a new reader; in case we hit an exception we can close it safely
-          newReader = SegmentReader.get(readOnly, infos.info(i), termInfosIndexDivisor);
+          newReader = SegmentReader.get(readOnly, infos.info(i), termInfosIndexDivisor, plugins);
         } else {
           newReader = newReaders[i].reopenSegment(infos.info(i), doClone, readOnly);
         }
@@ -438,9 +447,9 @@
   private synchronized DirectoryReader doReopen(SegmentInfos infos, boolean doClone, boolean openReadOnly) throws CorruptIndexException, IOException {
     DirectoryReader reader;
     if (openReadOnly) {
-      reader = new ReadOnlyDirectoryReader(directory, infos, subReaders, starts, normsCache, doClone, termInfosIndexDivisor);
+      reader = new ReadOnlyDirectoryReader(directory, infos, subReaders, starts, normsCache, doClone, termInfosIndexDivisor, plugins);
     } else {
-      reader = new DirectoryReader(directory, infos, subReaders, starts, normsCache, false, doClone, termInfosIndexDivisor);
+      reader = new DirectoryReader(directory, infos, subReaders, starts, normsCache, false, doClone, termInfosIndexDivisor, plugins);
     }
     return reader;
   }
Index: src/java/org/apache/lucene/index/SegmentPlugin.java
===================================================================
--- src/java/org/apache/lucene/index/SegmentPlugin.java	(revision 0)
+++ src/java/org/apache/lucene/index/SegmentPlugin.java	(revision 0)
@@ -0,0 +1,24 @@
+package org.apache.lucene.index;
+
+import java.io.IOException;
+
+/**
+ * Base class for plugins for SegmentReader.
+ */
+public abstract class SegmentPlugin {
+
+
+  /**
+   * Reopen this SegmentPlugin for <code>newReader</code>.
+   */
+  public abstract SegmentPlugin reopen(SegmentReader newReader) throws IOException;
+
+
+  /**
+   * Close this SegmentPlugin.
+   */
+  public abstract void close() throws IOException;
+
+
+}
+

Property changes on: src/java/org/apache/lucene/index/SegmentPlugin.java
___________________________________________________________________
Added: svn:eol-style
   + native

Index: src/java/org/apache/lucene/index/IndexReader.java
===================================================================
--- src/java/org/apache/lucene/index/IndexReader.java	(revision 927380)
+++ src/java/org/apache/lucene/index/IndexReader.java	(working copy)
@@ -263,6 +263,36 @@
   }
 
   /** Expert: returns an IndexReader reading the index in
+   *  the given Directory, with a custom {@link
+   *  IndexDeletionPolicy}.  You should pass readOnly=true,
+   *  since it gives much better concurrent performance,
+   *  unless you intend to do write operations (delete
+   *  documents or change norms) with the reader.
+   * @param directory the index directory
+   * @param deletionPolicy a custom deletion policy (only used
+   *  if you use this reader to perform deletes or to set
+   *  norms); see {@link IndexWriter} for details.
+   * @param readOnly true if no changes (deletions, norms) will be made with this IndexReader
+   * @param termInfosIndexDivisor Subsamples which indexed
+   *  terms are loaded into RAM. This has the same effect as {@link
+   *  IndexWriter#setTermIndexInterval} except that setting
+   *  must be done at indexing time while this setting can be
+   *  set per reader.  When set to N, then one in every
+   *  N*termIndexInterval terms in the index is loaded into
+   *  memory.  By setting this to a value > 1 you can reduce
+   *  memory usage, at the expense of higher latency when
+   *  loading a TermInfo.  The default value is 1.  Set this
+   *  to -1 to skip loading the terms index entirely.
+   * @param plugins map of plugins to initialize for SegmentReaders
+   * @throws CorruptIndexException if the index is corrupt
+   * @throws IOException if there is a low-level IO error
+   */
+  public static IndexReader open(final Directory directory, IndexDeletionPolicy deletionPolicy, boolean readOnly, int termInfosIndexDivisor, final Map<String, SegmentPluginFactory> plugins) throws CorruptIndexException, IOException {
+    return open(directory, deletionPolicy, null, readOnly, termInfosIndexDivisor, plugins);
+  }
+
+
+  /** Expert: returns an IndexReader reading the index in
    *  the given Directory, using a specific commit and with
    *  a custom {@link IndexDeletionPolicy}.  You should pass
    *  readOnly=true, since it gives much better concurrent
@@ -312,10 +342,46 @@
     return open(commit.getDirectory(), deletionPolicy, commit, readOnly, termInfosIndexDivisor);
   }
 
+  /** Expert: returns an IndexReader reading the index in
+   *  the given Directory, using a specific commit and with
+   *  a custom {@link IndexDeletionPolicy}.  You should pass
+   *  readOnly=true, since it gives much better concurrent
+   *  performance, unless you intend to do write operations
+   *  (delete documents or change norms) with the reader.
+   * @param commit the specific {@link IndexCommit} to open;
+   * see {@link IndexReader#listCommits} to list all commits
+   * in a directory
+   * @param deletionPolicy a custom deletion policy (only used
+   *  if you use this reader to perform deletes or to set
+   *  norms); see {@link IndexWriter} for details.
+   * @param readOnly true if no changes (deletions, norms) will be made with this IndexReader
+   * @param termInfosIndexDivisor Subsamples which indexed
+   *  terms are loaded into RAM. This has the same effect as {@link
+   *  IndexWriter#setTermIndexInterval} except that setting
+   *  must be done at indexing time while this setting can be
+   *  set per reader.  When set to N, then one in every
+   *  N*termIndexInterval terms in the index is loaded into
+   *  memory.  By setting this to a value > 1 you can reduce
+   *  memory usage, at the expense of higher latency when
+   *  loading a TermInfo.  The default value is 1.  Set this
+   *  to -1 to skip loading the terms index entirely.
+   * @param plugins map of plugins to initialize for segments
+   * @throws CorruptIndexException if the index is corrupt
+   * @throws IOException if there is a low-level IO error
+   */
+  public static IndexReader open(final IndexCommit commit, IndexDeletionPolicy deletionPolicy, boolean readOnly, int termInfosIndexDivisor, final Map<String, SegmentPluginFactory> plugins) throws CorruptIndexException, IOException {
+    return open(commit.getDirectory(), deletionPolicy, commit, readOnly, termInfosIndexDivisor, plugins);
+  }
+
+
   private static IndexReader open(final Directory directory, final IndexDeletionPolicy deletionPolicy, final IndexCommit commit, final boolean readOnly, int termInfosIndexDivisor) throws CorruptIndexException, IOException {
-    return DirectoryReader.open(directory, deletionPolicy, commit, readOnly, termInfosIndexDivisor);
+    return DirectoryReader.open(directory, deletionPolicy, commit, readOnly, termInfosIndexDivisor, null);
   }
 
+  private static IndexReader open(final Directory directory, final IndexDeletionPolicy deletionPolicy, final IndexCommit commit, final boolean readOnly, int termInfosIndexDivisor, final Map<String, SegmentPluginFactory> plugins) throws CorruptIndexException, IOException {
+    return DirectoryReader.open(directory, deletionPolicy, commit, readOnly, termInfosIndexDivisor, plugins);
+  }
+
   /**
    * Refreshes an IndexReader if the index has changed since this instance 
    * was (re)opened. 
Index: src/java/org/apache/lucene/index/IndexWriter.java
===================================================================
--- src/java/org/apache/lucene/index/IndexWriter.java	(revision 927380)
+++ src/java/org/apache/lucene/index/IndexWriter.java	(working copy)
@@ -265,6 +265,7 @@
 
   private DocumentsWriter docWriter;
   private IndexFileDeleter deleter;
+  private Map<String, SegmentPluginFactory> segmentPlugins = java.util.Collections.emptyMap();
 
   private Set<SegmentInfo> segmentsToOptimize = new HashSet<SegmentInfo>();           // used by optimize to note those needing optimization
 
@@ -606,7 +607,7 @@
         // TODO: we may want to avoid doing this while
         // synchronized
         // Returns a ref, which we xfer to readerMap:
-        sr = SegmentReader.get(false, info.dir, info, readBufferSize, doOpenStores, termsIndexDivisor);
+        sr = SegmentReader.get(false, info.dir, info, readBufferSize, doOpenStores, termsIndexDivisor, segmentPlugins);
         readerMap.put(info, sr);
       } else {
         if (doOpenStores) {
@@ -4850,6 +4851,21 @@
     return mergedSegmentWarmer;
   }
 
+  /**
+   * Set the plugins to initialize for a newly opened segment.
+   */
+  public void setSegmentPlugins(Map<String, SegmentPluginFactory> plugins) {
+    segmentPlugins = plugins;
+  }
+
+  /**
+   * Get the plugins for newly opened segments
+   */
+  public Map<String, SegmentPluginFactory> getSegmentPlugins() {
+    return segmentPlugins;
+  }
+
+
   private void handleOOM(OutOfMemoryError oom, String location) {
     if (infoStream != null) {
       message("hit OutOfMemoryError inside " + location);
