# HG changeset patch # User Earwin Burrfoot # Date 1269243135 -10800 # Node ID 7fc8fc1c9082d098bece0279ab377c3e59350600 # Parent 9e755a9782abbb4b6b9350d2c96916b6b5b94447 [mq]: LUCENE-2339 diff --git a/CHANGES.txt b/CHANGES.txt --- a/CHANGES.txt +++ b/CHANGES.txt @@ -207,6 +207,13 @@ documents (previously this was hardwired to 5), using IndexWriterConfig.setMaxThreadStates. (Mike McCandless) +* LUCENE-2339: Directory gains copyTo(destDir) and copyTo(destDir, filenames). + First method is analogous to old copy(srcDir, destDir), which is now deprecated. + Second method allows to copy only selected files, a single commit for example. + Implementations can override new method and optimize when transferring between + dirs of same type. See FSDirectory for example. (Earwin Burrfoot via + Mike McCandless) + Optimizations * LUCENE-2075: Terms dict cache is now shared across threads instead @@ -266,6 +273,9 @@ TermAttributeImpl, move DEFAULT_TYPE constant to TypeInterface, improve null-handling for TypeAttribute. (Uwe Schindler) +* LUCENE-2339: We now use NIO Channels when copying between two FSDirectories + (Earwin Burrfoot via Mike McCandless) + Build * LUCENE-2124: Moved the JDK-based collation support from contrib/collation diff --git a/src/java/org/apache/lucene/store/Directory.java b/src/java/org/apache/lucene/store/Directory.java --- a/src/java/org/apache/lucene/store/Directory.java +++ b/src/java/org/apache/lucene/store/Directory.java @@ -21,6 +21,11 @@ import java.io.Closeable; import java.util.Collection; +import java.util.ArrayList; +import static java.util.Arrays.asList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; import org.apache.lucene.index.IndexFileNameFilter; /** A Directory is a flat list of files. Files may be written once, when they @@ -179,48 +184,53 @@ return this.toString(); } + /** - * Copy contents of a directory src to a directory dest. - * If a file in src already exists in dest then the - * one in dest will be blindly overwritten. + *

Copy all files of this directory to destination directory. All conflicting files at destination are overwritten

+ *

NOTE: this method only copies files that look like index files (ie, have extensions matching the known + * extensions of index files). + *

NOTE: the source directory should not change while this method is running. Otherwise the results are + * undefined and you could easily hit a FileNotFoundException.

* - *

NOTE: the source directory cannot change - * while this method is running. Otherwise the results - * are undefined and you could easily hit a - * FileNotFoundException. - * - *

NOTE: this method only copies files that look - * like index files (ie, have extensions matching the - * known extensions of index files). - * - * @param src source directory - * @param dest destination directory - * @param closeDirSrc if true, call {@link #close()} method on source directory - * @throws IOException + * @param to destination directory */ - public static void copy(Directory src, Directory dest, boolean closeDirSrc) throws IOException { - final String[] files = src.listAll(); + public final void copyTo(Directory to) throws IOException { + List filenames = new ArrayList(asList(listAll())); IndexFileNameFilter filter = IndexFileNameFilter.getFilter(); + for (Iterator iterator = filenames.iterator(); iterator.hasNext();) + if (!filter.accept(null, iterator.next())) + iterator.remove(); + + copyTo(to, filenames); + } + + /** + *

Copy given files of this directory to destination directory. All conflicting files at destination are overwritten

+ *

NOTE: the source directory should not change while this method is running. Otherwise the results are + * undefined and you could easily hit a FileNotFoundException.

+ *

NOTE: implementations can check if destination directory is of the same type as 'this' and perform optimized copy

+ * + * @param to destination directory + * @param filenames file names to be copied + */ + public void copyTo(Directory to, Collection filenames) throws IOException { byte[] buf = new byte[BufferedIndexOutput.BUFFER_SIZE]; - for (int i = 0; i < files.length; i++) { - - if (!filter.accept(null, files[i])) - continue; + for (String filename : filenames) { IndexOutput os = null; IndexInput is = null; try { // create file in dest directory - os = dest.createOutput(files[i]); + os = to.createOutput(filename); // read current file - is = src.openInput(files[i]); + is = openInput(filename); // and copy to dest directory long len = is.length(); long readCount = 0; while (readCount < len) { - int toRead = readCount + BufferedIndexOutput.BUFFER_SIZE > len ? (int)(len - readCount) : BufferedIndexOutput.BUFFER_SIZE; + int toRead = readCount + BufferedIndexOutput.BUFFER_SIZE > len ? (int) (len - readCount) : BufferedIndexOutput.BUFFER_SIZE; is.readBytes(buf, 0, toRead); os.writeBytes(buf, toRead); readCount += toRead; @@ -236,7 +246,26 @@ } } } - if(closeDirSrc) + } + + /** + * Copy contents of a directory src to a directory dest. If a file in src already exists in dest then the one in dest + * will be blindly overwritten. + *

+ *

NOTE: the source directory cannot change while this method is running. Otherwise the results are + * undefined and you could easily hit a FileNotFoundException. + *

+ *

NOTE: this method only copies files that look like index files (ie, have extensions matching the known + * extensions of index files). + * + * @param src source directory + * @param dest destination directory + * @param closeDirSrc if true, call {@link #close()} method on source directory + */ + @Deprecated + public static void copy(Directory src, Directory dest, boolean closeDirSrc) throws IOException { + src.copyTo(dest); + if (closeDirSrc) src.close(); } diff --git a/src/java/org/apache/lucene/store/FSDirectory.java b/src/java/org/apache/lucene/store/FSDirectory.java --- a/src/java/org/apache/lucene/store/FSDirectory.java +++ b/src/java/org/apache/lucene/store/FSDirectory.java @@ -18,9 +18,12 @@ */ import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; import java.io.FilenameFilter; import java.io.IOException; import java.io.RandomAccessFile; +import java.nio.channels.FileChannel; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -29,6 +32,7 @@ import static java.util.Collections.synchronizedSet; import java.util.HashSet; import java.util.Set; +import org.apache.lucene.util.IOUtils; import org.apache.lucene.util.ThreadInterruptedException; import org.apache.lucene.util.Constants; @@ -422,6 +426,27 @@ return chunkSize; } + @Override + public void copyTo(Directory to, Collection filenames) throws IOException { + if (to instanceof FSDirectory) { + FSDirectory target = (FSDirectory) to; + + for (String filename : filenames) { + target.ensureCanWrite(filename); + FileChannel input = null; + FileChannel output = null; + try { + input = new FileInputStream(new File(directory, filename)).getChannel(); + output = new FileOutputStream(new File(target.directory, filename)).getChannel(); + output.transferFrom(input, 0, input.size()); + } finally { + IOUtils.closeSafely(input, output); + } + } + } else + super.copyTo(to, filenames); + } + protected static class FSIndexOutput extends BufferedIndexOutput { private final FSDirectory parent; private final String name; diff --git a/src/java/org/apache/lucene/util/IOUtils.java b/src/java/org/apache/lucene/util/IOUtils.java new file mode 100644 --- /dev/null +++ b/src/java/org/apache/lucene/util/IOUtils.java @@ -0,0 +1,19 @@ +package org.apache.lucene.util; + +import java.io.Closeable; +import java.io.IOException; + +public class IOUtils { + /** + * Closes all given Closeable objects, ignoring exceptions and nulls + * @param objects + */ + public static void closeSafely(Closeable... objects) { + for (Closeable object : objects) + try { + if (object != null) + object.close(); + } catch (IOException ignored) { + } + } +}