HADOOP-7223. FileContext createFlag combinations are not clearly defined. Contributed by Suresh Srinivas.

git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@1092565 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Suresh Srinivas 2011-04-15 01:51:07 +00:00
parent 6e74a3592c
commit a8dbce1596
9 changed files with 190 additions and 83 deletions

View File

@ -1,4 +1,4 @@
Hadoop Change Log Hn jaadoop Change Log
Trunk (unreleased changes) Trunk (unreleased changes)
@ -139,6 +139,10 @@ Trunk (unreleased changes)
HADOOP-7207. fs member of FSShell is not really needed (boryas) HADOOP-7207. fs member of FSShell is not really needed (boryas)
HADOOP-7223. FileContext createFlag combinations are not clearly defined.
(suresh)
Release 0.22.0 - Unreleased Release 0.22.0 - Unreleased
INCOMPATIBLE CHANGES INCOMPATIBLE CHANGES

View File

@ -337,8 +337,9 @@ public abstract class ChecksumFs extends FilterFs {
int bytesPerSum = fs.getBytesPerSum(); int bytesPerSum = fs.getBytesPerSum();
int sumBufferSize = fs.getSumBufferSize(bytesPerSum, bufferSize); int sumBufferSize = fs.getSumBufferSize(bytesPerSum, bufferSize);
this.sums = fs.getRawFs().createInternal(fs.getChecksumFile(file), this.sums = fs.getRawFs().createInternal(fs.getChecksumFile(file),
EnumSet.of(CreateFlag.OVERWRITE), absolutePermission, sumBufferSize, EnumSet.of(CreateFlag.CREATE, CreateFlag.OVERWRITE),
replication, blockSize, progress, bytesPerChecksum, createParent); absolutePermission, sumBufferSize, replication, blockSize, progress,
bytesPerChecksum, createParent);
sums.write(CHECKSUM_VERSION, 0, CHECKSUM_VERSION.length); sums.write(CHECKSUM_VERSION, 0, CHECKSUM_VERSION.length);
sums.writeInt(bytesPerSum); sums.writeInt(bytesPerSum);
} }

View File

@ -17,49 +17,63 @@
*/ */
package org.apache.hadoop.fs; package org.apache.hadoop.fs;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.EnumSet;
import org.apache.hadoop.HadoopIllegalArgumentException;
import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.classification.InterfaceStability; import org.apache.hadoop.classification.InterfaceStability;
/**************************************************************** /****************************************************************
*CreateFlag specifies the file create semantic. Users can combine flags like:<br> * CreateFlag specifies the file create semantic. Users can combine flags like: <br>
*<code> * <code>
* EnumSet.of(CreateFlag.CREATE, CreateFlag.APPEND) * EnumSet.of(CreateFlag.CREATE, CreateFlag.APPEND)
* <code> * <code>
* and pass it to {@link org.apache.hadoop.fs.FileSystem #create(Path f, FsPermission permission,
* EnumSet<CreateFlag> flag, int bufferSize, short replication, long blockSize,
* Progressable progress)}.
*
* <p> * <p>
* Combine {@link #OVERWRITE} with either {@link #CREATE} *
* or {@link #APPEND} does the same as only use * Use the CreateFlag as follows:
* {@link #OVERWRITE}. <br>
* Combine {@link #CREATE} with {@link #APPEND} has the semantic:
* <ol> * <ol>
* <li> create the file if it does not exist; * <li> CREATE - to create a file if it does not exist,
* <li> append the file if it already exists. * else throw FileAlreadyExists.</li>
* <li> APPEND - to append to a file if it exists,
* else throw FileNotFoundException.</li>
* <li> OVERWRITE - to truncate a file if it exists,
* else throw FileNotFoundException.</li>
* <li> CREATE|APPEND - to create a file if it does not exist,
* else append to an existing file.</li>
* <li> CREATE|OVERWRITE - to create a file if it does not exist,
* else overwrite an existing file.</li>
* </ol>
*
* Following combination is not valid and will result in
* {@link HadoopIllegalArgumentException}:
* <ol>
* <li> APPEND|OVERWRITE</li>
* <li> CREATE|APPEND|OVERWRITE</li>
* </ol> * </ol>
*****************************************************************/ *****************************************************************/
@InterfaceAudience.Public @InterfaceAudience.Public
@InterfaceStability.Stable @InterfaceStability.Evolving
public enum CreateFlag { public enum CreateFlag {
/** /**
* create the file if it does not exist, and throw an IOException if it * Create a file. See javadoc for more description
* already exists * already exists
*/ */
CREATE((short) 0x01), CREATE((short) 0x01),
/** /**
* create the file if it does not exist, if it exists, overwrite it. * Truncate/overwrite a file. Same as POSIX O_TRUNC. See javadoc for description.
*/ */
OVERWRITE((short) 0x02), OVERWRITE((short) 0x02),
/** /**
* append to a file, and throw an IOException if it does not exist * Append to a file. See javadoc for more description.
*/ */
APPEND((short) 0x04); APPEND((short) 0x04);
private short mode; private final short mode;
private CreateFlag(short mode) { private CreateFlag(short mode) {
this.mode = mode; this.mode = mode;
@ -68,4 +82,49 @@ public enum CreateFlag {
short getMode() { short getMode() {
return mode; return mode;
} }
/**
* Validate the CreateFlag and throw exception if it is invalid
* @param flag set of CreateFlag
* @throws HadoopIllegalArgumentException if the CreateFlag is invalid
*/
public static void validate(EnumSet<CreateFlag> flag) {
if (flag == null || flag.isEmpty()) {
throw new HadoopIllegalArgumentException(flag
+ " does not specify any options");
}
final boolean append = flag.contains(APPEND);
final boolean overwrite = flag.contains(OVERWRITE);
// Both append and overwrite is an error
if (append && overwrite) {
throw new HadoopIllegalArgumentException(
flag + "Both append and overwrite options cannot be enabled.");
}
}
/**
* Validate the CreateFlag for create operation
* @param path Object representing the path; usually String or {@link Path}
* @param pathExists pass true if the path exists in the file system
* @param flag set of CreateFlag
* @throws IOException on error
* @throws HadoopIllegalArgumentException if the CreateFlag is invalid
*/
public static void validate(Object path, boolean pathExists,
EnumSet<CreateFlag> flag) throws IOException {
validate(flag);
final boolean append = flag.contains(APPEND);
final boolean overwrite = flag.contains(OVERWRITE);
if (pathExists) {
if (!(append || overwrite)) {
throw new FileAlreadyExistsException("File already exists: "
+ path.toString()
+ ". Append or overwrite option must be specified in " + flag);
}
} else if (!flag.contains(CREATE)) {
throw new FileNotFoundException("Non existing file: " + path.toString()
+ ". Create option is not specified in " + flag);
}
}
} }

View File

@ -511,7 +511,7 @@ public final class FileContext {
* writing into the file. * writing into the file.
* *
* @param f the file name to open * @param f the file name to open
* @param createFlag gives the semantics of create: overwrite, append etc. * @param createFlag gives the semantics of create; see {@link CreateFlag}
* @param opts file creation options; see {@link Options.CreateOpts}. * @param opts file creation options; see {@link Options.CreateOpts}.
* <ul> * <ul>
* <li>Progress - to report progress on the operation - default null * <li>Progress - to report progress on the operation - default null
@ -2057,7 +2057,10 @@ public final class FileContext {
OutputStream out = null; OutputStream out = null;
try { try {
in = open(qSrc); in = open(qSrc);
out = create(qDst, EnumSet.of(CreateFlag.OVERWRITE)); EnumSet<CreateFlag> createFlag = overwrite ? EnumSet.of(
CreateFlag.CREATE, CreateFlag.OVERWRITE) :
EnumSet.of(CreateFlag.CREATE);
out = create(qDst, createFlag);
IOUtils.copyBytes(in, out, conf, true); IOUtils.copyBytes(in, out, conf, true);
} catch (IOException e) { } catch (IOException e) {
IOUtils.closeStream(out); IOUtils.closeStream(out);

View File

@ -718,23 +718,20 @@ public abstract class FileSystem extends Configured implements Closeable {
short replication, long blockSize, Progressable progress, short replication, long blockSize, Progressable progress,
int bytesPerChecksum) throws IOException { int bytesPerChecksum) throws IOException {
boolean pathExists = exists(f);
CreateFlag.validate(f, pathExists, flag);
// Default impl assumes that permissions do not matter and // Default impl assumes that permissions do not matter and
// nor does the bytesPerChecksum hence // nor does the bytesPerChecksum hence
// calling the regular create is good enough. // calling the regular create is good enough.
// FSs that implement permissions should override this. // FSs that implement permissions should override this.
if (exists(f)) { if (pathExists && flag.contains(CreateFlag.APPEND)) {
if (flag.contains(CreateFlag.APPEND)) {
return append(f, bufferSize, progress); return append(f, bufferSize, progress);
} else if (!flag.contains(CreateFlag.OVERWRITE)) {
throw new IOException("File already exists: " + f);
}
} else {
if (flag.contains(CreateFlag.APPEND) && !flag.contains(CreateFlag.CREATE))
throw new IOException("File already exists: " + f.toString());
} }
return this.create(f, absolutePermission, flag.contains(CreateFlag.OVERWRITE), bufferSize, replication, return this.create(f, absolutePermission,
flag.contains(CreateFlag.OVERWRITE), bufferSize, replication,
blockSize, progress); blockSize, progress);
} }

View File

@ -264,28 +264,6 @@ public class RawLocalFileSystem extends FileSystem {
return out; return out;
} }
@Override
protected FSDataOutputStream primitiveCreate(Path f,
FsPermission absolutePermission, EnumSet<CreateFlag> flag,
int bufferSize, short replication, long blockSize, Progressable progress,
int bytesPerChecksum) throws IOException {
if(flag.contains(CreateFlag.APPEND)){
if (!exists(f)){
if(flag.contains(CreateFlag.CREATE)) {
return create(f, false, bufferSize, replication, blockSize, null);
}
}
return append(f, bufferSize, null);
}
FSDataOutputStream out = create(f, flag.contains(CreateFlag.OVERWRITE),
bufferSize, replication, blockSize, progress);
setPermission(f, absolutePermission);
return out;
}
public boolean rename(Path src, Path dst) throws IOException { public boolean rename(Path src, Path dst) throws IOException {
if (pathToFile(src).renameTo(pathToFile(dst))) { if (pathToFile(src).renameTo(pathToFile(dst))) {
return true; return true;

View File

@ -21,8 +21,8 @@ package org.apache.hadoop.fs;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.Iterator;
import org.apache.hadoop.HadoopIllegalArgumentException;
import org.apache.hadoop.fs.Options.CreateOpts; import org.apache.hadoop.fs.Options.CreateOpts;
import org.apache.hadoop.fs.Options.Rename; import org.apache.hadoop.fs.Options.Rename;
import org.apache.hadoop.fs.permission.FsPermission; import org.apache.hadoop.fs.permission.FsPermission;
@ -32,6 +32,7 @@ import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import static org.apache.hadoop.fs.FileContextTestHelper.*; import static org.apache.hadoop.fs.FileContextTestHelper.*;
import static org.apache.hadoop.fs.CreateFlag.*;
/** /**
* <p> * <p>
@ -155,7 +156,7 @@ public abstract class FileContextMainOperationsBaseTest {
// Now open a file relative to the wd we just set above. // Now open a file relative to the wd we just set above.
Path absolutePath = new Path(absoluteDir, "foo"); Path absolutePath = new Path(absoluteDir, "foo");
fc.create(absolutePath, EnumSet.of(CreateFlag.CREATE)).close(); fc.create(absolutePath, EnumSet.of(CREATE)).close();
fc.open(new Path("foo")).close(); fc.open(new Path("foo")).close();
@ -645,7 +646,7 @@ public abstract class FileContextMainOperationsBaseTest {
fc.mkdir(path.getParent(), FsPermission.getDefault(), true); fc.mkdir(path.getParent(), FsPermission.getDefault(), true);
FSDataOutputStream out = fc.create(path, EnumSet.of(CreateFlag.CREATE), FSDataOutputStream out = fc.create(path, EnumSet.of(CREATE),
CreateOpts.repFac((short) 1), CreateOpts CreateOpts.repFac((short) 1), CreateOpts
.blockSize(getDefaultBlockSize())); .blockSize(getDefaultBlockSize()));
out.write(data, 0, len); out.write(data, 0, len);
@ -670,31 +671,93 @@ public abstract class FileContextMainOperationsBaseTest {
} }
@Test @Test(expected=HadoopIllegalArgumentException.class)
public void testOverwrite() throws IOException { public void testNullCreateFlag() throws IOException {
Path path = getTestRootPath(fc, "test/hadoop/file"); Path p = getTestRootPath(fc, "test/file");
fc.create(p, null);
fc.mkdir(path.getParent(), FsPermission.getDefault(), true); Assert.fail("Excepted exception not thrown");
createFile(path);
Assert.assertTrue("Exists", exists(fc, path));
Assert.assertEquals("Length", data.length, fc.getFileStatus(path).getLen());
try {
fc.create(path, EnumSet.of(CreateFlag.CREATE));
Assert.fail("Should throw IOException.");
} catch (IOException e) {
// Expected
} }
FSDataOutputStream out = fc.create(path,EnumSet.of(CreateFlag.OVERWRITE)); @Test(expected=HadoopIllegalArgumentException.class)
public void testEmptyCreateFlag() throws IOException {
Path p = getTestRootPath(fc, "test/file");
fc.create(p, EnumSet.noneOf(CreateFlag.class));
Assert.fail("Excepted exception not thrown");
}
@Test(expected=FileAlreadyExistsException.class)
public void testCreateFlagCreateExistingFile() throws IOException {
Path p = getTestRootPath(fc, "test/testCreateFlagCreateExistingFile");
createFile(p);
fc.create(p, EnumSet.of(CREATE));
Assert.fail("Excepted exception not thrown");
}
@Test(expected=FileNotFoundException.class)
public void testCreateFlagOverwriteNonExistingFile() throws IOException {
Path p = getTestRootPath(fc, "test/testCreateFlagOverwriteNonExistingFile");
fc.create(p, EnumSet.of(OVERWRITE));
Assert.fail("Excepted exception not thrown");
}
@Test
public void testCreateFlagOverwriteExistingFile() throws IOException {
Path p = getTestRootPath(fc, "test/testCreateFlagOverwriteExistingFile");
createFile(p);
FSDataOutputStream out = fc.create(p, EnumSet.of(OVERWRITE));
writeData(fc, p, out, data, data.length);
}
@Test(expected=FileNotFoundException.class)
public void testCreateFlagAppendNonExistingFile() throws IOException {
Path p = getTestRootPath(fc, "test/testCreateFlagAppendNonExistingFile");
fc.create(p, EnumSet.of(APPEND));
Assert.fail("Excepted exception not thrown");
}
@Test
public void testCreateFlagAppendExistingFile() throws IOException {
Path p = getTestRootPath(fc, "test/testCreateFlagAppendExistingFile");
createFile(p);
FSDataOutputStream out = fc.create(p, EnumSet.of(APPEND));
writeData(fc, p, out, data, 2 * data.length);
}
@Test
public void testCreateFlagCreateAppendNonExistingFile() throws IOException {
Path p = getTestRootPath(fc, "test/testCreateFlagCreateAppendNonExistingFile");
FSDataOutputStream out = fc.create(p, EnumSet.of(CREATE, APPEND));
writeData(fc, p, out, data, data.length);
}
@Test
public void testCreateFlagCreateAppendExistingFile() throws IOException {
Path p = getTestRootPath(fc, "test/testCreateFlagCreateAppendExistingFile");
createFile(p);
FSDataOutputStream out = fc.create(p, EnumSet.of(CREATE, APPEND));
writeData(fc, p, out, data, 2*data.length);
}
@Test(expected=HadoopIllegalArgumentException.class)
public void testCreateFlagAppendOverwrite() throws IOException {
Path p = getTestRootPath(fc, "test/nonExistent");
fc.create(p, EnumSet.of(APPEND, OVERWRITE));
Assert.fail("Excepted exception not thrown");
}
@Test(expected=HadoopIllegalArgumentException.class)
public void testCreateFlagAppendCreateOverwrite() throws IOException {
Path p = getTestRootPath(fc, "test/nonExistent");
fc.create(p, EnumSet.of(CREATE, APPEND, OVERWRITE));
Assert.fail("Excepted exception not thrown");
}
private static void writeData(FileContext fc, Path p, FSDataOutputStream out,
byte[] data, long expectedLen) throws IOException {
out.write(data, 0, data.length); out.write(data, 0, data.length);
out.close(); out.close();
Assert.assertTrue("Exists", exists(fc, p));
Assert.assertTrue("Exists", exists(fc, path)); Assert.assertEquals("Length", expectedLen, fc.getFileStatus(p).getLen());
Assert.assertEquals("Length", data.length, fc.getFileStatus(path).getLen());
} }
@Test @Test
@ -1057,7 +1120,7 @@ public abstract class FileContextMainOperationsBaseTest {
//HADOOP-4760 according to Closeable#close() closing already-closed //HADOOP-4760 according to Closeable#close() closing already-closed
//streams should have no effect. //streams should have no effect.
Path src = getTestRootPath(fc, "test/hadoop/file"); Path src = getTestRootPath(fc, "test/hadoop/file");
FSDataOutputStream out = fc.create(src, EnumSet.of(CreateFlag.CREATE), FSDataOutputStream out = fc.create(src, EnumSet.of(CREATE),
Options.CreateOpts.createParent()); Options.CreateOpts.createParent());
out.writeChar('H'); //write some data out.writeChar('H'); //write some data
@ -1091,7 +1154,7 @@ public abstract class FileContextMainOperationsBaseTest {
} }
protected void createFile(Path path) throws IOException { protected void createFile(Path path) throws IOException {
FSDataOutputStream out = fc.create(path, EnumSet.of(CreateFlag.CREATE), FSDataOutputStream out = fc.create(path, EnumSet.of(CREATE),
Options.CreateOpts.createParent()); Options.CreateOpts.createParent());
out.write(data, 0, data.length); out.write(data, 0, data.length);
out.close(); out.close();

View File

@ -140,7 +140,8 @@ public class DataGenerator extends Configured implements Tool {
* a length of <code>fileSize</code>. The file is filled with character 'a'. * a length of <code>fileSize</code>. The file is filled with character 'a'.
*/ */
private void genFile(Path file, long fileSize) throws IOException { private void genFile(Path file, long fileSize) throws IOException {
FSDataOutputStream out = fc.create(file, EnumSet.of(CreateFlag.OVERWRITE), FSDataOutputStream out = fc.create(file,
EnumSet.of(CreateFlag.CREATE, CreateFlag.OVERWRITE),
CreateOpts.createParent(), CreateOpts.bufferSize(4096), CreateOpts.createParent(), CreateOpts.bufferSize(4096),
CreateOpts.repFac((short) 3)); CreateOpts.repFac((short) 3));
for(long i=0; i<fileSize; i++) { for(long i=0; i<fileSize; i++) {

View File

@ -584,7 +584,8 @@ public class LoadGenerator extends Configured implements Tool {
*/ */
private void genFile(Path file, long fileSize) throws IOException { private void genFile(Path file, long fileSize) throws IOException {
long startTime = System.currentTimeMillis(); long startTime = System.currentTimeMillis();
FSDataOutputStream out = fc.create(file, EnumSet.of(CreateFlag.OVERWRITE), FSDataOutputStream out = fc.create(file,
EnumSet.of(CreateFlag.CREATE, CreateFlag.OVERWRITE),
CreateOpts.createParent(), CreateOpts.bufferSize(4096), CreateOpts.createParent(), CreateOpts.bufferSize(4096),
CreateOpts.repFac((short) 3)); CreateOpts.repFac((short) 3));
executionTime[CREATE] += (System.currentTimeMillis()-startTime); executionTime[CREATE] += (System.currentTimeMillis()-startTime);