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; } }