public class

SnapshotDeletionPolicy

extends Object
implements IndexDeletionPolicy
package org.apache.lucene.index;

/**
 * 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.
 */

import java.util.Collection;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.IOException;
import org.apache.lucene.store.Directory;

/** A {@link IndexDeletionPolicy} that wraps around any other
 *  {@link IndexDeletionPolicy} and adds the ability to hold and
 *  later release a single "snapshot" of an index.  While
 *  the snapshot is held, the {@link IndexWriter} will not
 *  remove any files associated with it even if the index is
 *  otherwise being actively, arbitrarily changed.  Because
 *  we wrap another arbitrary {@link IndexDeletionPolicy}, this
 *  gives you the freedom to continue using whatever {@link
 *  IndexDeletionPolicy} you would normally want to use with your
 *  index.  Note that you can re-use a single instance of
 *  SnapshotDeletionPolicy across multiple writers as long
 *  as they are against the same index Directory.  Any
 *  snapshot held when a writer is closed will "survive"
 *  when the next writer is opened.
 *
 * <p><b>WARNING</b>: This API is a new and experimental and
 * may suddenly change.</p> */

public class SnapshotDeletionPolicy implements IndexDeletionPolicy {

  private IndexCommit lastCommit;
  private IndexDeletionPolicy primary;
  private String snapshot;

  public SnapshotDeletionPolicy(IndexDeletionPolicy primary) {
    this.primary = primary;
  }

  public synchronized void onInit(List<? extends IndexCommit> commits) throws IOException {
    primary.onInit(wrapCommits(commits));
    lastCommit = commits.get(commits.size()-1);
  }

  public synchronized void onCommit(List<? extends IndexCommit> commits) throws IOException {
    primary.onCommit(wrapCommits(commits));
    lastCommit = commits.get(commits.size()-1);
  }

  /** Take a snapshot of the most recent commit to the
   *  index.  You must call release() to free this snapshot.
   *  Note that while the snapshot is held, the files it
   *  references will not be deleted, which will consume
   *  additional disk space in your index. If you take a
   *  snapshot at a particularly bad time (say just before
   *  you call optimize()) then in the worst case this could
   *  consume an extra 1X of your total index size, until
   *  you release the snapshot. */
  public synchronized IndexCommit snapshot() {
    if (lastCommit == null) {
      throw new IllegalStateException("no index commits to snapshot !");
    }
    
    if (snapshot == null)
      snapshot = lastCommit.getSegmentsFileName();
    else
      throw new IllegalStateException("snapshot is already set; please call release() first");
    return lastCommit;
  }

  /** Release the currently held snapshot. */
  public synchronized void release() {
    if (snapshot != null)
      snapshot = null;
    else
      throw new IllegalStateException("snapshot was not set; please call snapshot() first");
  }

  private class MyCommitPoint extends IndexCommit {
    IndexCommit cp;
    MyCommitPoint(IndexCommit cp) {
      this.cp = cp;
    }

    @Override
    public String toString() {
      return "SnapshotDeletionPolicy.SnapshotCommitPoint(" + cp + ")";
    }

    @Override
    public String getSegmentsFileName() {
      return cp.getSegmentsFileName();
    }
    @Override
    public Collection<String> getFileNames() throws IOException {
      return cp.getFileNames();
    }
    @Override
    public Directory getDirectory() {
      return cp.getDirectory();
    }
    @Override
    public void delete() {
      synchronized(SnapshotDeletionPolicy.this) {
        // Suppress the delete request if this commit point is
        // our current snapshot.
        if (snapshot == null || !snapshot.equals(getSegmentsFileName()))
          cp.delete();
      }
    }
    @Override
    public boolean isDeleted() {
      return cp.isDeleted();
    }
    @Override
    public long getVersion() {
      return cp.getVersion();
    }
    @Override
    public long getGeneration() {
      return cp.getGeneration();
    }
    @Override
    public Map<String,String> getUserData() throws IOException {
      return cp.getUserData();
    }
    @Override
    public boolean isOptimized() {
      return cp.isOptimized();
    }
  }

  private List<IndexCommit> wrapCommits(List<? extends IndexCommit> commits) {
    final int count = commits.size();
    List<IndexCommit> myCommits = new ArrayList<IndexCommit>(count);
    for(int i=0;i<count;i++)
      myCommits.add(new MyCommitPoint(commits.get(i)));
    return myCommits;
  }
}