LUCENE-2339: deprecate static Directory.copy in favor non-static Directory.copyTo; if it's FSDir -> FSDir, use nio's FileChannel.transferTo

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@927761 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Michael McCandless 2010-03-26 10:34:41 +00:00
parent 4319c22796
commit 29963c7c7e
5 changed files with 161 additions and 39 deletions

View File

@ -216,6 +216,11 @@ New features
convenient classes in case you want to disable segment merges by IndexWriter
without tweaking a particular MergePolicy parameters, such as mergeFactor.
MergeScheduler's methods are now public. (Shai Erera via Mike McCandless)
* LUCENE-2339: Deprecate static method Directory.copy in favor of
Directory.copyTo, and use nio's FileChannel.transferTo when copying
files between FSDirectory instances. (Earwin Burrfoot via Mike
McCandless).
Optimizations

View File

@ -22,7 +22,13 @@ import java.io.Closeable;
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 @@ public abstract class Directory implements Closeable {
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><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
*/
public static void copy(Directory src, Directory dest, boolean closeDirSrc) throws IOException {
final String[] files = src.listAll();
/**
* <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>
*
* @param to destination directory
*/
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();
}

View File

@ -18,9 +18,12 @@ package org.apache.lucene.store;
*/
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 java.util.Collections;
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 @@ public abstract class FSDirectory extends Directory {
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;

View File

@ -0,0 +1,64 @@
package org.apache.lucene.util;
/**
* 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.io.Closeable;
import java.io.IOException;
/** @lucene.internal */
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;
}
}

View File

@ -17,7 +17,7 @@ public class RefCntRamDirectory extends RAMDirectory {
public RefCntRamDirectory(Directory dir) throws IOException {
this();
Directory.copy(dir, this, false);
dir.copyTo(this);
}
public void incRef() {