blob: 18070f07378c54eac8c3cec0ef21a690db6318a3 [file] [log] [blame]
# HG changeset patch
# User Earwin Burrfoot <earwin@gmail.com>
# Date 1269243135 -10800
# Node ID f3fe62541f59574c4438ae431f0134a6e1e7f369
# Parent d70436a7f00d49cc95d9516d466cc451509838aa
[mq]: LUCENE-2339
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
@@ -22,7 +22,13 @@
import java.util.Collection;
import java.util.Collections;
+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;
+import org.apache.lucene.util.IOUtils;
/** A Directory is a flat list of files. Files may be written once, when they
* are created. Once a file is created it may only be opened for read, or
@@ -183,64 +189,83 @@
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.
+ * <p>Copy all files of this directory to destination directory. All conflicting files at destination are overwritten</p>
+ * <p><b>NOTE:</b> this method only copies files that look like index files (ie, have extensions matching the known
+ * extensions of index files).
+ * <p><b>NOTE:</b> the source directory should not change while this method is running. Otherwise the results are
+ * undefined and you could easily hit a FileNotFoundException. </p>
*
- * <p><b>NOTE:</b> the source directory cannot change
- * while this method is running. Otherwise the results
- * are undefined and you could easily hit a
- * FileNotFoundException.
- *
- * <p><b>NOTE:</b> 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 <code>true</code>, 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<String> filenames = new ArrayList<String>();
IndexFileNameFilter filter = IndexFileNameFilter.getFilter();
+ for (String name : listAll())
+ if (filter.accept(null, name))
+ filenames.add(name);
+
+ copyTo(to, filenames);
+ }
+
+ /**
+ * <p>Copy given files of this directory to destination directory. All conflicting files at destination are overwritten</p>
+ * <p><b>NOTE:</b> the source directory should not change while this method is running. Otherwise the results are
+ * undefined and you could easily hit a FileNotFoundException. </p>
+ * <p><b>NOTE:</b> implementations can check if destination directory is of the same type as 'this' and perform optimized copy</p>
+ *
+ * @param to destination directory
+ * @param filenames file names to be copied
+ */
+ public void copyTo(Directory to, Collection<String> 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;
+ IOException priorException = 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;
}
+ } catch (IOException ioe) {
+ priorException = ioe;
} finally {
- // graceful cleanup
- try {
- if (os != null)
- os.close();
- } finally {
- if (is != null)
- is.close();
- }
+ IOUtils.closeSafely(priorException, os, is);
}
}
- 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.
+ * <p/>
+ * <p><b>NOTE:</b> the source directory cannot change while this method is running. Otherwise the results are
+ * undefined and you could easily hit a FileNotFoundException.
+ * <p/>
+ * <p><b>NOTE:</b> 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 <code>true</code>, call {@link #close()} method on source directory
+ * @deprecated should be replaced with src.copyTo(dest); [src.close();]
+ */
+ @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,30 @@
return chunkSize;
}
+ @Override
+ public void copyTo(Directory to, Collection<String> filenames) throws IOException {
+ if (to instanceof FSDirectory) {
+ FSDirectory target = (FSDirectory) to;
+
+ for (String filename : filenames) {
+ target.ensureCanWrite(filename);
+ FileChannel input = null;
+ FileChannel output = null;
+ IOException priorException = 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());
+ } catch (IOException ioe) {
+ priorException = ioe;
+ } finally {
+ IOUtils.closeSafely(priorException, 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,46 @@
+package org.apache.lucene.util;
+
+import java.io.Closeable;
+import java.io.IOException;
+
+public class IOUtils {
+ /**
+ * <p>Closes all given <tt>Closeable</tt>s, suppressing all thrown exceptions. Some of the <tt>Closeable</tt>s
+ * may be null, they are ignored. After everything is closed, method either throws <tt>priorException</tt>,
+ * if one is supplied, or the first of suppressed exceptions, or completes normally.</p>
+ * <p>Sample usage:<br/>
+ * <pre>
+ * Closeable resource1 = null, resource2 = null, resource3 = null;
+ * ExpectedException priorE = null;
+ * try {
+ * resource1 = ...; resource2 = ...; resource3 = ...; // Aquisition may throw ExpectedException
+ * ..do..stuff.. // May throw ExpectedException
+ * } catch (ExpectedException e) {
+ * priorE = e;
+ * } finally {
+ * closeSafely(priorE, resource1, resource2, resource3);
+ * }
+ * </pre>
+ * </p>
+ * @param priorException <tt>null</tt> or an exception that will be rethrown after method completion
+ * @param objects objects to call <tt>close()</tt> on
+ */
+ public static <E extends Exception> void closeSafely(E priorException, Closeable... objects) throws E, IOException {
+ IOException firstIOE = null;
+
+ for (Closeable object : objects) {
+ try {
+ if (object != null)
+ object.close();
+ } catch (IOException ioe) {
+ if (firstIOE == null)
+ firstIOE = ioe;
+ }
+ }
+
+ if (priorException != null)
+ throw priorException;
+ else if (firstIOE != null)
+ throw firstIOE;
+ }
+}