From 6ed335a018a1ddfeb262ab84476441edb319dfae Mon Sep 17 00:00:00 2001 From: Steve Loughran Date: Wed, 5 Oct 2016 15:01:15 +0100 Subject: [PATCH] HADOOP-12667 s3a to support createNonRecursive API. Contributed by Sean Mackrory --- .../apache/hadoop/fs/s3a/S3AFileSystem.java | 27 ++++++++ .../hadoop/fs/s3a/ITestS3AMiscOperations.java | 63 +++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 hadoop-tools/hadoop-aws/src/test/java/org/apache/hadoop/fs/s3a/ITestS3AMiscOperations.java diff --git a/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/S3AFileSystem.java b/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/S3AFileSystem.java index b22f8c6429c..54560a08ebd 100644 --- a/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/S3AFileSystem.java +++ b/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/S3AFileSystem.java @@ -26,6 +26,7 @@ import java.io.InterruptedIOException; import java.net.URI; import java.util.ArrayList; import java.util.Date; +import java.util.EnumSet; import java.util.List; import java.util.Map; import java.util.Objects; @@ -59,6 +60,7 @@ import org.apache.commons.lang.StringUtils; import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.classification.InterfaceStability; import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.CreateFlag; import org.apache.hadoop.fs.FSDataInputStream; import org.apache.hadoop.fs.FSDataOutputStream; import org.apache.hadoop.fs.FileAlreadyExistsException; @@ -512,6 +514,31 @@ public class S3AFileSystem extends FileSystem { null); } + /** + * {@inheritDoc} + * @throws FileNotFoundException if the parent directory is not present -or + * is not a directory. + */ + @Override + public FSDataOutputStream createNonRecursive(Path path, + FsPermission permission, + EnumSet flags, + int bufferSize, + short replication, + long blockSize, + Progressable progress) throws IOException { + Path parent = path.getParent(); + if (parent != null) { + // expect this to raise an exception if there is no parent + if (!getFileStatus(parent).isDirectory()) { + throw new FileAlreadyExistsException("Not a directory: " + parent); + } + } + return create(path, permission, + flags.contains(CreateFlag.OVERWRITE), bufferSize, + replication, blockSize, progress); + } + /** * Append to an existing file (optional operation). * @param f the existing file to be appended. diff --git a/hadoop-tools/hadoop-aws/src/test/java/org/apache/hadoop/fs/s3a/ITestS3AMiscOperations.java b/hadoop-tools/hadoop-aws/src/test/java/org/apache/hadoop/fs/s3a/ITestS3AMiscOperations.java new file mode 100644 index 00000000000..59fcb05729c --- /dev/null +++ b/hadoop-tools/hadoop-aws/src/test/java/org/apache/hadoop/fs/s3a/ITestS3AMiscOperations.java @@ -0,0 +1,63 @@ +/** + * 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. + */ + +package org.apache.hadoop.fs.s3a; + +import org.apache.hadoop.fs.FSDataOutputStream; +import org.apache.hadoop.fs.FileAlreadyExistsException; +import org.apache.hadoop.fs.Path; +import org.apache.hadoop.fs.contract.ContractTestUtils; +import org.junit.Test; + +import java.io.FileNotFoundException; +import java.io.IOException; + +/** + * Tests of the S3A FileSystem which don't have a specific home and can share + * a filesystem instance with others.. + */ +public class ITestS3AMiscOperations extends AbstractS3ATestBase { + + @Test + public void testCreateNonRecursiveSuccess() throws IOException { + Path shouldWork = path("nonrecursivenode"); + try(FSDataOutputStream out = createNonRecursive(shouldWork)) { + out.write(0); + out.close(); + } + assertIsFile(shouldWork); + } + + @Test(expected = FileNotFoundException.class) + public void testCreateNonRecursiveNoParent() throws IOException { + createNonRecursive(path("/recursive/node")); + } + + @Test(expected = FileAlreadyExistsException.class) + public void testCreateNonRecursiveParentIsFile() throws IOException { + Path parent = path("/file.txt"); + ContractTestUtils.touch(getFileSystem(), parent); + createNonRecursive(new Path(parent, "fail")); + } + + private FSDataOutputStream createNonRecursive(Path path) throws IOException { + return getFileSystem().createNonRecursive(path, false, 4096, + (short) 3, (short) 4096, + null); + } +}