|
@@ -20,18 +20,12 @@ package org.apache.hadoop.fs.aliyun.oss;
|
|
|
|
|
|
import static org.apache.hadoop.fs.aliyun.oss.Constants.*;
|
|
import static org.apache.hadoop.fs.aliyun.oss.Constants.*;
|
|
|
|
|
|
-import com.aliyun.oss.ClientException;
|
|
|
|
-import com.aliyun.oss.common.auth.CredentialsProvider;
|
|
|
|
-import com.aliyun.oss.common.auth.DefaultCredentialProvider;
|
|
|
|
-import com.aliyun.oss.common.auth.DefaultCredentials;
|
|
|
|
-import java.io.ByteArrayInputStream;
|
|
|
|
import java.io.FileNotFoundException;
|
|
import java.io.FileNotFoundException;
|
|
import java.io.IOException;
|
|
import java.io.IOException;
|
|
import java.net.URI;
|
|
import java.net.URI;
|
|
import java.util.ArrayList;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.List;
|
|
|
|
|
|
-import org.apache.commons.lang.StringUtils;
|
|
|
|
import org.apache.hadoop.conf.Configuration;
|
|
import org.apache.hadoop.conf.Configuration;
|
|
import org.apache.hadoop.fs.FSDataInputStream;
|
|
import org.apache.hadoop.fs.FSDataInputStream;
|
|
import org.apache.hadoop.fs.FSDataOutputStream;
|
|
import org.apache.hadoop.fs.FSDataOutputStream;
|
|
@@ -39,30 +33,13 @@ import org.apache.hadoop.fs.FileAlreadyExistsException;
|
|
import org.apache.hadoop.fs.FileStatus;
|
|
import org.apache.hadoop.fs.FileStatus;
|
|
import org.apache.hadoop.fs.FileSystem;
|
|
import org.apache.hadoop.fs.FileSystem;
|
|
import org.apache.hadoop.fs.Path;
|
|
import org.apache.hadoop.fs.Path;
|
|
-import org.apache.hadoop.fs.aliyun.oss.AliyunOSSUtils.UserInfo;
|
|
|
|
import org.apache.hadoop.fs.permission.FsPermission;
|
|
import org.apache.hadoop.fs.permission.FsPermission;
|
|
-import org.apache.hadoop.security.ProviderUtils;
|
|
|
|
import org.apache.hadoop.util.Progressable;
|
|
import org.apache.hadoop.util.Progressable;
|
|
|
|
|
|
-import com.aliyun.oss.ClientConfiguration;
|
|
|
|
-import com.aliyun.oss.OSSClient;
|
|
|
|
-import com.aliyun.oss.OSSException;
|
|
|
|
-import com.aliyun.oss.common.comm.Protocol;
|
|
|
|
-import com.aliyun.oss.model.AbortMultipartUploadRequest;
|
|
|
|
-import com.aliyun.oss.model.CannedAccessControlList;
|
|
|
|
-import com.aliyun.oss.model.CompleteMultipartUploadRequest;
|
|
|
|
-import com.aliyun.oss.model.CompleteMultipartUploadResult;
|
|
|
|
-import com.aliyun.oss.model.CopyObjectResult;
|
|
|
|
-import com.aliyun.oss.model.DeleteObjectsRequest;
|
|
|
|
-import com.aliyun.oss.model.InitiateMultipartUploadRequest;
|
|
|
|
-import com.aliyun.oss.model.InitiateMultipartUploadResult;
|
|
|
|
-import com.aliyun.oss.model.ListObjectsRequest;
|
|
|
|
import com.aliyun.oss.model.OSSObjectSummary;
|
|
import com.aliyun.oss.model.OSSObjectSummary;
|
|
import com.aliyun.oss.model.ObjectListing;
|
|
import com.aliyun.oss.model.ObjectListing;
|
|
import com.aliyun.oss.model.ObjectMetadata;
|
|
import com.aliyun.oss.model.ObjectMetadata;
|
|
-import com.aliyun.oss.model.PartETag;
|
|
|
|
-import com.aliyun.oss.model.UploadPartCopyRequest;
|
|
|
|
-import com.aliyun.oss.model.UploadPartCopyResult;
|
|
|
|
|
|
+
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.LoggerFactory;
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
|
|
@@ -75,12 +52,8 @@ public class AliyunOSSFileSystem extends FileSystem {
|
|
LoggerFactory.getLogger(AliyunOSSFileSystem.class);
|
|
LoggerFactory.getLogger(AliyunOSSFileSystem.class);
|
|
private URI uri;
|
|
private URI uri;
|
|
private Path workingDir;
|
|
private Path workingDir;
|
|
- private OSSClient ossClient;
|
|
|
|
- private String bucketName;
|
|
|
|
- private long uploadPartSize;
|
|
|
|
- private long multipartThreshold;
|
|
|
|
|
|
+ private AliyunOSSFileSystemStore store;
|
|
private int maxKeys;
|
|
private int maxKeys;
|
|
- private String serverSideEncryptionAlgorithm;
|
|
|
|
|
|
|
|
@Override
|
|
@Override
|
|
public FSDataOutputStream append(Path path, int bufferSize,
|
|
public FSDataOutputStream append(Path path, int bufferSize,
|
|
@@ -91,9 +64,7 @@ public class AliyunOSSFileSystem extends FileSystem {
|
|
@Override
|
|
@Override
|
|
public void close() throws IOException {
|
|
public void close() throws IOException {
|
|
try {
|
|
try {
|
|
- if (ossClient != null) {
|
|
|
|
- ossClient.shutdown();
|
|
|
|
- }
|
|
|
|
|
|
+ store.close();
|
|
} finally {
|
|
} finally {
|
|
super.close();
|
|
super.close();
|
|
}
|
|
}
|
|
@@ -125,23 +96,33 @@ public class AliyunOSSFileSystem extends FileSystem {
|
|
}
|
|
}
|
|
|
|
|
|
return new FSDataOutputStream(new AliyunOSSOutputStream(getConf(),
|
|
return new FSDataOutputStream(new AliyunOSSOutputStream(getConf(),
|
|
- ossClient, bucketName, key, progress, statistics,
|
|
|
|
- serverSideEncryptionAlgorithm), (Statistics)(null));
|
|
|
|
|
|
+ store, key, progress, statistics), (Statistics)(null));
|
|
}
|
|
}
|
|
|
|
|
|
@Override
|
|
@Override
|
|
public boolean delete(Path path, boolean recursive) throws IOException {
|
|
public boolean delete(Path path, boolean recursive) throws IOException {
|
|
- FileStatus status;
|
|
|
|
try {
|
|
try {
|
|
- status = getFileStatus(path);
|
|
|
|
|
|
+ return innerDelete(getFileStatus(path), recursive);
|
|
} catch (FileNotFoundException e) {
|
|
} catch (FileNotFoundException e) {
|
|
- if (LOG.isDebugEnabled()) {
|
|
|
|
- LOG.debug("Couldn't delete " + path + ": Does not exist!");
|
|
|
|
- }
|
|
|
|
|
|
+ LOG.debug("Couldn't delete {} - does not exist", path);
|
|
return false;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
+ }
|
|
|
|
|
|
- String key = pathToKey(status.getPath());
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Delete an object. See {@link #delete(Path, boolean)}.
|
|
|
|
+ *
|
|
|
|
+ * @param status fileStatus object
|
|
|
|
+ * @param recursive if path is a directory and set to
|
|
|
|
+ * true, the directory is deleted else throws an exception. In
|
|
|
|
+ * case of a file the recursive can be set to either true or false.
|
|
|
|
+ * @return true if delete is successful else false.
|
|
|
|
+ * @throws IOException due to inability to delete a directory or file.
|
|
|
|
+ */
|
|
|
|
+ private boolean innerDelete(FileStatus status, boolean recursive)
|
|
|
|
+ throws IOException {
|
|
|
|
+ Path f = status.getPath();
|
|
|
|
+ String key = pathToKey(f);
|
|
if (status.isDirectory()) {
|
|
if (status.isDirectory()) {
|
|
if (!key.endsWith("/")) {
|
|
if (!key.endsWith("/")) {
|
|
key += "/";
|
|
key += "/";
|
|
@@ -150,54 +131,34 @@ public class AliyunOSSFileSystem extends FileSystem {
|
|
FileStatus[] statuses = listStatus(status.getPath());
|
|
FileStatus[] statuses = listStatus(status.getPath());
|
|
// Check whether it is an empty directory or not
|
|
// Check whether it is an empty directory or not
|
|
if (statuses.length > 0) {
|
|
if (statuses.length > 0) {
|
|
- throw new IOException("Cannot remove directory" + path +
|
|
|
|
|
|
+ throw new IOException("Cannot remove directory " + f +
|
|
": It is not empty!");
|
|
": It is not empty!");
|
|
} else {
|
|
} else {
|
|
// Delete empty directory without '-r'
|
|
// Delete empty directory without '-r'
|
|
- ossClient.deleteObject(bucketName, key);
|
|
|
|
- statistics.incrementWriteOps(1);
|
|
|
|
|
|
+ store.deleteObject(key);
|
|
}
|
|
}
|
|
} else {
|
|
} else {
|
|
- ListObjectsRequest listRequest = new ListObjectsRequest(bucketName);
|
|
|
|
- listRequest.setPrefix(key);
|
|
|
|
- listRequest.setMaxKeys(maxKeys);
|
|
|
|
-
|
|
|
|
- while (true) {
|
|
|
|
- ObjectListing objects = ossClient.listObjects(listRequest);
|
|
|
|
- statistics.incrementReadOps(1);
|
|
|
|
- List<String> keysToDelete = new ArrayList<String>();
|
|
|
|
- for (OSSObjectSummary objectSummary : objects.getObjectSummaries()) {
|
|
|
|
- keysToDelete.add(objectSummary.getKey());
|
|
|
|
- }
|
|
|
|
- DeleteObjectsRequest deleteRequest =
|
|
|
|
- new DeleteObjectsRequest(bucketName);
|
|
|
|
- deleteRequest.setKeys(keysToDelete);
|
|
|
|
- ossClient.deleteObjects(deleteRequest);
|
|
|
|
- statistics.incrementWriteOps(1);
|
|
|
|
- if (objects.isTruncated()) {
|
|
|
|
- listRequest.setMarker(objects.getNextMarker());
|
|
|
|
- } else {
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+ store.deleteDirs(key);
|
|
}
|
|
}
|
|
} else {
|
|
} else {
|
|
- ossClient.deleteObject(bucketName, key);
|
|
|
|
- statistics.incrementWriteOps(1);
|
|
|
|
|
|
+ store.deleteObject(key);
|
|
}
|
|
}
|
|
- //TODO: optimize logic here
|
|
|
|
|
|
+
|
|
|
|
+ createFakeDirectoryIfNecessary(f);
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private void createFakeDirectoryIfNecessary(Path f) throws IOException {
|
|
try {
|
|
try {
|
|
- Path pPath = status.getPath().getParent();
|
|
|
|
|
|
+ Path pPath = f.getParent();
|
|
FileStatus pStatus = getFileStatus(pPath);
|
|
FileStatus pStatus = getFileStatus(pPath);
|
|
- if (pStatus.isDirectory()) {
|
|
|
|
- return true;
|
|
|
|
- } else {
|
|
|
|
|
|
+ if (pStatus.isFile()) {
|
|
throw new IOException("Path " + pPath +
|
|
throw new IOException("Path " + pPath +
|
|
" is assumed to be a directory!");
|
|
" is assumed to be a directory!");
|
|
}
|
|
}
|
|
} catch (FileNotFoundException fnfe) {
|
|
} catch (FileNotFoundException fnfe) {
|
|
// Make sure the parent directory exists
|
|
// Make sure the parent directory exists
|
|
- return mkdir(bucketName, pathToKey(status.getPath().getParent()));
|
|
|
|
|
|
+ mkdir(pathToKey(f.getParent()));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -211,22 +172,15 @@ public class AliyunOSSFileSystem extends FileSystem {
|
|
return new FileStatus(0, true, 1, 0, 0, qualifiedPath);
|
|
return new FileStatus(0, true, 1, 0, 0, qualifiedPath);
|
|
}
|
|
}
|
|
|
|
|
|
- ObjectMetadata meta = getObjectMetadata(key);
|
|
|
|
|
|
+ ObjectMetadata meta = store.getObjectMetadata(key);
|
|
// If key not found and key does not end with "/"
|
|
// If key not found and key does not end with "/"
|
|
if (meta == null && !key.endsWith("/")) {
|
|
if (meta == null && !key.endsWith("/")) {
|
|
// Case: dir + "/"
|
|
// Case: dir + "/"
|
|
key += "/";
|
|
key += "/";
|
|
- meta = getObjectMetadata(key);
|
|
|
|
|
|
+ meta = store.getObjectMetadata(key);
|
|
}
|
|
}
|
|
if (meta == null) {
|
|
if (meta == null) {
|
|
- // Case: dir + "/" + file
|
|
|
|
- ListObjectsRequest listRequest = new ListObjectsRequest(bucketName);
|
|
|
|
- listRequest.setPrefix(key);
|
|
|
|
- listRequest.setDelimiter("/");
|
|
|
|
- listRequest.setMaxKeys(1);
|
|
|
|
-
|
|
|
|
- ObjectListing listing = ossClient.listObjects(listRequest);
|
|
|
|
- statistics.incrementReadOps(1);
|
|
|
|
|
|
+ ObjectListing listing = store.listObjects(key, 1, "/", null);
|
|
if (!listing.getObjectSummaries().isEmpty() ||
|
|
if (!listing.getObjectSummaries().isEmpty() ||
|
|
!listing.getCommonPrefixes().isEmpty()) {
|
|
!listing.getCommonPrefixes().isEmpty()) {
|
|
return new FileStatus(0, true, 1, 0, 0, qualifiedPath);
|
|
return new FileStatus(0, true, 1, 0, 0, qualifiedPath);
|
|
@@ -242,22 +196,6 @@ public class AliyunOSSFileSystem extends FileSystem {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- /**
|
|
|
|
- * Return object metadata given object key.
|
|
|
|
- *
|
|
|
|
- * @param key object key
|
|
|
|
- * @return return null if key does not exist
|
|
|
|
- */
|
|
|
|
- private ObjectMetadata getObjectMetadata(String key) {
|
|
|
|
- try {
|
|
|
|
- return ossClient.getObjectMetadata(bucketName, key);
|
|
|
|
- } catch (OSSException osse) {
|
|
|
|
- return null;
|
|
|
|
- } finally {
|
|
|
|
- statistics.incrementReadOps(1);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
@Override
|
|
@Override
|
|
public String getScheme() {
|
|
public String getScheme() {
|
|
return "oss";
|
|
return "oss";
|
|
@@ -288,7 +226,6 @@ public class AliyunOSSFileSystem extends FileSystem {
|
|
* Initialize new FileSystem.
|
|
* Initialize new FileSystem.
|
|
*
|
|
*
|
|
* @param name the uri of the file system, including host, port, etc.
|
|
* @param name the uri of the file system, including host, port, etc.
|
|
- *
|
|
|
|
* @param conf configuration of the file system
|
|
* @param conf configuration of the file system
|
|
* @throws IOException IO problems
|
|
* @throws IOException IO problems
|
|
*/
|
|
*/
|
|
@@ -296,153 +233,15 @@ public class AliyunOSSFileSystem extends FileSystem {
|
|
super.initialize(name, conf);
|
|
super.initialize(name, conf);
|
|
|
|
|
|
uri = java.net.URI.create(name.getScheme() + "://" + name.getAuthority());
|
|
uri = java.net.URI.create(name.getScheme() + "://" + name.getAuthority());
|
|
- workingDir =
|
|
|
|
- new Path("/user",
|
|
|
|
- System.getProperty("user.name")).makeQualified(uri, null);
|
|
|
|
-
|
|
|
|
- bucketName = name.getHost();
|
|
|
|
-
|
|
|
|
- ClientConfiguration clientConf = new ClientConfiguration();
|
|
|
|
- clientConf.setMaxConnections(conf.getInt(MAXIMUM_CONNECTIONS_KEY,
|
|
|
|
- MAXIMUM_CONNECTIONS_DEFAULT));
|
|
|
|
- boolean secureConnections = conf.getBoolean(SECURE_CONNECTIONS_KEY,
|
|
|
|
- SECURE_CONNECTIONS_DEFAULT);
|
|
|
|
- clientConf.setProtocol(secureConnections ? Protocol.HTTPS : Protocol.HTTP);
|
|
|
|
- clientConf.setMaxErrorRetry(conf.getInt(MAX_ERROR_RETRIES_KEY,
|
|
|
|
- MAX_ERROR_RETRIES_DEFAULT));
|
|
|
|
- clientConf.setConnectionTimeout(conf.getInt(ESTABLISH_TIMEOUT_KEY,
|
|
|
|
- ESTABLISH_TIMEOUT_DEFAULT));
|
|
|
|
- clientConf.setSocketTimeout(conf.getInt(SOCKET_TIMEOUT_KEY,
|
|
|
|
- SOCKET_TIMEOUT_DEFAULT));
|
|
|
|
-
|
|
|
|
- String proxyHost = conf.getTrimmed(PROXY_HOST_KEY, "");
|
|
|
|
- int proxyPort = conf.getInt(PROXY_PORT_KEY, -1);
|
|
|
|
- if (!proxyHost.isEmpty()) {
|
|
|
|
- clientConf.setProxyHost(proxyHost);
|
|
|
|
- if (proxyPort >= 0) {
|
|
|
|
- clientConf.setProxyPort(proxyPort);
|
|
|
|
- } else {
|
|
|
|
- if (secureConnections) {
|
|
|
|
- LOG.warn("Proxy host set without port. Using HTTPS default 443");
|
|
|
|
- clientConf.setProxyPort(443);
|
|
|
|
- } else {
|
|
|
|
- LOG.warn("Proxy host set without port. Using HTTP default 80");
|
|
|
|
- clientConf.setProxyPort(80);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- String proxyUsername = conf.getTrimmed(PROXY_USERNAME_KEY);
|
|
|
|
- String proxyPassword = conf.getTrimmed(PROXY_PASSWORD_KEY);
|
|
|
|
- if ((proxyUsername == null) != (proxyPassword == null)) {
|
|
|
|
- String msg = "Proxy error: " + PROXY_USERNAME_KEY + " or " +
|
|
|
|
- PROXY_PASSWORD_KEY + " set without the other.";
|
|
|
|
- LOG.error(msg);
|
|
|
|
- throw new IllegalArgumentException(msg);
|
|
|
|
- }
|
|
|
|
- clientConf.setProxyUsername(proxyUsername);
|
|
|
|
- clientConf.setProxyPassword(proxyPassword);
|
|
|
|
- clientConf.setProxyDomain(conf.getTrimmed(PROXY_DOMAIN_KEY));
|
|
|
|
- clientConf.setProxyWorkstation(conf.getTrimmed(PROXY_WORKSTATION_KEY));
|
|
|
|
- } else if (proxyPort >= 0) {
|
|
|
|
- String msg = "Proxy error: " + PROXY_PORT_KEY + " set without " +
|
|
|
|
- PROXY_HOST_KEY;
|
|
|
|
- LOG.error(msg);
|
|
|
|
- throw new IllegalArgumentException(msg);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- String endPoint = conf.getTrimmed(ENDPOINT_KEY, "");
|
|
|
|
- ossClient =
|
|
|
|
- new OSSClient(endPoint, getCredentialsProvider(name, conf), clientConf);
|
|
|
|
|
|
+ workingDir = new Path("/user",
|
|
|
|
+ System.getProperty("user.name")).makeQualified(uri, null);
|
|
|
|
|
|
|
|
+ store = new AliyunOSSFileSystemStore();
|
|
|
|
+ store.initialize(name, conf, statistics);
|
|
maxKeys = conf.getInt(MAX_PAGING_KEYS_KEY, MAX_PAGING_KEYS_DEFAULT);
|
|
maxKeys = conf.getInt(MAX_PAGING_KEYS_KEY, MAX_PAGING_KEYS_DEFAULT);
|
|
- uploadPartSize = conf.getLong(MULTIPART_UPLOAD_SIZE_KEY,
|
|
|
|
- MULTIPART_UPLOAD_SIZE_DEFAULT);
|
|
|
|
- multipartThreshold = conf.getLong(MIN_MULTIPART_UPLOAD_THRESHOLD_KEY,
|
|
|
|
- MIN_MULTIPART_UPLOAD_THRESHOLD_DEFAULT);
|
|
|
|
-
|
|
|
|
- if (uploadPartSize < 5 * 1024 * 1024) {
|
|
|
|
- LOG.warn(MULTIPART_UPLOAD_SIZE_KEY + " must be at least 5 MB");
|
|
|
|
- uploadPartSize = 5 * 1024 * 1024;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (multipartThreshold < 5 * 1024 * 1024) {
|
|
|
|
- LOG.warn(MIN_MULTIPART_UPLOAD_THRESHOLD_KEY + " must be at least 5 MB");
|
|
|
|
- multipartThreshold = 5 * 1024 * 1024;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (multipartThreshold > 1024 * 1024 * 1024) {
|
|
|
|
- LOG.warn(MIN_MULTIPART_UPLOAD_THRESHOLD_KEY + " must be less than 1 GB");
|
|
|
|
- multipartThreshold = 1024 * 1024 * 1024;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- String cannedACLName = conf.get(CANNED_ACL_KEY, CANNED_ACL_DEFAULT);
|
|
|
|
- if (!cannedACLName.isEmpty()) {
|
|
|
|
- CannedAccessControlList cannedACL =
|
|
|
|
- CannedAccessControlList.valueOf(cannedACLName);
|
|
|
|
- ossClient.setBucketAcl(bucketName, cannedACL);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- serverSideEncryptionAlgorithm =
|
|
|
|
- conf.get(SERVER_SIDE_ENCRYPTION_ALGORITHM_KEY, "");
|
|
|
|
-
|
|
|
|
setConf(conf);
|
|
setConf(conf);
|
|
}
|
|
}
|
|
|
|
|
|
- /**
|
|
|
|
- * Create the default credential provider, or load in one explicitly
|
|
|
|
- * identified in the configuration.
|
|
|
|
- * @param name the uri of the file system
|
|
|
|
- * @param conf configuration
|
|
|
|
- * @return a credential provider
|
|
|
|
- * @throws IOException on any problem. Class construction issues may be
|
|
|
|
- * nested inside the IOE.
|
|
|
|
- */
|
|
|
|
- private CredentialsProvider getCredentialsProvider(URI name,
|
|
|
|
- Configuration conf) throws IOException {
|
|
|
|
- CredentialsProvider credentials;
|
|
|
|
-
|
|
|
|
- String className = conf.getTrimmed(ALIYUN_OSS_CREDENTIALS_PROVIDER_KEY);
|
|
|
|
- if (StringUtils.isEmpty(className)) {
|
|
|
|
- Configuration newConf =
|
|
|
|
- ProviderUtils.excludeIncompatibleCredentialProviders(conf,
|
|
|
|
- AliyunOSSFileSystem.class);
|
|
|
|
- String accessKey =
|
|
|
|
- AliyunOSSUtils.getPassword(newConf, ACCESS_KEY,
|
|
|
|
- UserInfo.EMPTY.getUser());
|
|
|
|
- String secretKey =
|
|
|
|
- AliyunOSSUtils.getPassword(newConf, SECRET_KEY,
|
|
|
|
- UserInfo.EMPTY.getPassword());
|
|
|
|
- credentials =
|
|
|
|
- new DefaultCredentialProvider(
|
|
|
|
- new DefaultCredentials(accessKey, secretKey));
|
|
|
|
-
|
|
|
|
- } else {
|
|
|
|
- try {
|
|
|
|
- LOG.debug("Credential provider class is:" + className);
|
|
|
|
- Class<?> credClass = Class.forName(className);
|
|
|
|
- try {
|
|
|
|
- credentials =
|
|
|
|
- (CredentialsProvider)credClass.getDeclaredConstructor(
|
|
|
|
- URI.class, Configuration.class).newInstance(this.uri, conf);
|
|
|
|
- } catch (NoSuchMethodException | SecurityException e) {
|
|
|
|
- credentials =
|
|
|
|
- (CredentialsProvider)credClass.getDeclaredConstructor()
|
|
|
|
- .newInstance();
|
|
|
|
- }
|
|
|
|
- } catch (ClassNotFoundException e) {
|
|
|
|
- throw new IOException(className + " not found.", e);
|
|
|
|
- } catch (NoSuchMethodException | SecurityException e) {
|
|
|
|
- throw new IOException(String.format("%s constructor exception. A " +
|
|
|
|
- "class specified in %s must provide an accessible constructor " +
|
|
|
|
- "accepting URI and Configuration, or an accessible default " +
|
|
|
|
- "constructor.", className, ALIYUN_OSS_CREDENTIALS_PROVIDER_KEY), e);
|
|
|
|
- } catch (ReflectiveOperationException | IllegalArgumentException e) {
|
|
|
|
- throw new IOException(className + " instantiation exception.", e);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return credentials;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
/**
|
|
/**
|
|
* Check if OSS object represents a directory.
|
|
* Check if OSS object represents a directory.
|
|
*
|
|
*
|
|
@@ -456,10 +255,10 @@ public class AliyunOSSFileSystem extends FileSystem {
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * Turns a path (relative or otherwise) into an OSS key.
|
|
|
|
|
|
+ * Turn a path (relative or otherwise) into an OSS key.
|
|
*
|
|
*
|
|
- * @param path the path of the file
|
|
|
|
- * @return the key of the object that represent the file
|
|
|
|
|
|
+ * @param path the path of the file.
|
|
|
|
+ * @return the key of the object that represents the file.
|
|
*/
|
|
*/
|
|
private String pathToKey(Path path) {
|
|
private String pathToKey(Path path) {
|
|
if (!path.isAbsolute()) {
|
|
if (!path.isAbsolute()) {
|
|
@@ -492,18 +291,12 @@ public class AliyunOSSFileSystem extends FileSystem {
|
|
key = key + "/";
|
|
key = key + "/";
|
|
}
|
|
}
|
|
|
|
|
|
- ListObjectsRequest listObjectsRequest =
|
|
|
|
- new ListObjectsRequest(bucketName);
|
|
|
|
- listObjectsRequest.setPrefix(key);
|
|
|
|
- listObjectsRequest.setDelimiter("/");
|
|
|
|
- listObjectsRequest.setMaxKeys(maxKeys);
|
|
|
|
-
|
|
|
|
if (LOG.isDebugEnabled()) {
|
|
if (LOG.isDebugEnabled()) {
|
|
LOG.debug("listStatus: doing listObjects for directory " + key);
|
|
LOG.debug("listStatus: doing listObjects for directory " + key);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ ObjectListing objects = store.listObjects(key, maxKeys, "/", null);
|
|
while (true) {
|
|
while (true) {
|
|
- ObjectListing objects = ossClient.listObjects(listObjectsRequest);
|
|
|
|
statistics.incrementReadOps(1);
|
|
statistics.incrementReadOps(1);
|
|
for (OSSObjectSummary objectSummary : objects.getObjectSummaries()) {
|
|
for (OSSObjectSummary objectSummary : objects.getObjectSummaries()) {
|
|
Path keyPath = keyToPath(objectSummary.getKey())
|
|
Path keyPath = keyToPath(objectSummary.getKey())
|
|
@@ -539,7 +332,8 @@ public class AliyunOSSFileSystem extends FileSystem {
|
|
if (LOG.isDebugEnabled()) {
|
|
if (LOG.isDebugEnabled()) {
|
|
LOG.debug("listStatus: list truncated - getting next batch");
|
|
LOG.debug("listStatus: list truncated - getting next batch");
|
|
}
|
|
}
|
|
- listObjectsRequest.setMarker(objects.getNextMarker());
|
|
|
|
|
|
+ objects = store.listObjects(key, maxKeys, "/",
|
|
|
|
+ objects.getNextMarker());
|
|
statistics.incrementReadOps(1);
|
|
statistics.incrementReadOps(1);
|
|
} else {
|
|
} else {
|
|
break;
|
|
break;
|
|
@@ -558,27 +352,17 @@ public class AliyunOSSFileSystem extends FileSystem {
|
|
/**
|
|
/**
|
|
* Used to create an empty file that represents an empty directory.
|
|
* Used to create an empty file that represents an empty directory.
|
|
*
|
|
*
|
|
- * @param bucket the bucket this directory belongs to
|
|
|
|
* @param key directory path
|
|
* @param key directory path
|
|
- * @return true if directory successfully created
|
|
|
|
|
|
+ * @return true if directory is successfully created
|
|
* @throws IOException
|
|
* @throws IOException
|
|
*/
|
|
*/
|
|
- private boolean mkdir(final String bucket, final String key)
|
|
|
|
- throws IOException {
|
|
|
|
|
|
+ private boolean mkdir(final String key) throws IOException {
|
|
String dirName = key;
|
|
String dirName = key;
|
|
- ObjectMetadata dirMeta = new ObjectMetadata();
|
|
|
|
- byte[] buffer = new byte[0];
|
|
|
|
- ByteArrayInputStream in = new ByteArrayInputStream(buffer);
|
|
|
|
- dirMeta.setContentLength(0);
|
|
|
|
if (!key.endsWith("/")) {
|
|
if (!key.endsWith("/")) {
|
|
dirName += "/";
|
|
dirName += "/";
|
|
}
|
|
}
|
|
- try {
|
|
|
|
- ossClient.putObject(bucket, dirName, in, dirMeta);
|
|
|
|
- return true;
|
|
|
|
- } finally {
|
|
|
|
- in.close();
|
|
|
|
- }
|
|
|
|
|
|
+ store.storeEmptyFile(dirName);
|
|
|
|
+ return true;
|
|
}
|
|
}
|
|
|
|
|
|
@Override
|
|
@Override
|
|
@@ -595,14 +379,14 @@ public class AliyunOSSFileSystem extends FileSystem {
|
|
} catch (FileNotFoundException e) {
|
|
} catch (FileNotFoundException e) {
|
|
validatePath(path);
|
|
validatePath(path);
|
|
String key = pathToKey(path);
|
|
String key = pathToKey(path);
|
|
- return mkdir(bucketName, key);
|
|
|
|
|
|
+ return mkdir(key);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
* Check whether the path is a valid path.
|
|
* Check whether the path is a valid path.
|
|
*
|
|
*
|
|
- * @param path the path to be checked
|
|
|
|
|
|
+ * @param path the path to be checked.
|
|
* @throws IOException
|
|
* @throws IOException
|
|
*/
|
|
*/
|
|
private void validatePath(Path path) throws IOException {
|
|
private void validatePath(Path path) throws IOException {
|
|
@@ -631,8 +415,8 @@ public class AliyunOSSFileSystem extends FileSystem {
|
|
" because it is a directory");
|
|
" because it is a directory");
|
|
}
|
|
}
|
|
|
|
|
|
- return new FSDataInputStream(new AliyunOSSInputStream(getConf(), ossClient,
|
|
|
|
- bucketName, pathToKey(path), fileStatus.getLen(), statistics));
|
|
|
|
|
|
+ return new FSDataInputStream(new AliyunOSSInputStream(getConf(), store,
|
|
|
|
+ pathToKey(path), fileStatus.getLen(), statistics));
|
|
}
|
|
}
|
|
|
|
|
|
@Override
|
|
@Override
|
|
@@ -696,126 +480,31 @@ public class AliyunOSSFileSystem extends FileSystem {
|
|
} else {
|
|
} else {
|
|
copyFile(srcPath, dstPath);
|
|
copyFile(srcPath, dstPath);
|
|
}
|
|
}
|
|
- if (srcPath.equals(dstPath)) {
|
|
|
|
- return true;
|
|
|
|
- } else {
|
|
|
|
- return delete(srcPath, true);
|
|
|
|
- }
|
|
|
|
|
|
+
|
|
|
|
+ return srcPath.equals(dstPath) || delete(srcPath, true);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
* Copy file from source path to destination path.
|
|
* Copy file from source path to destination path.
|
|
- * (the caller should make sure srcPath is a file and dstPath is valid.)
|
|
|
|
|
|
+ * (the caller should make sure srcPath is a file and dstPath is valid)
|
|
*
|
|
*
|
|
- * @param srcPath source path
|
|
|
|
- * @param dstPath destination path
|
|
|
|
- * @return true if successfully copied
|
|
|
|
|
|
+ * @param srcPath source path.
|
|
|
|
+ * @param dstPath destination path.
|
|
|
|
+ * @return true if file is successfully copied.
|
|
*/
|
|
*/
|
|
private boolean copyFile(Path srcPath, Path dstPath) {
|
|
private boolean copyFile(Path srcPath, Path dstPath) {
|
|
String srcKey = pathToKey(srcPath);
|
|
String srcKey = pathToKey(srcPath);
|
|
String dstKey = pathToKey(dstPath);
|
|
String dstKey = pathToKey(dstPath);
|
|
- return copyFile(srcKey, dstKey);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * Copy an object from source key to destination key.
|
|
|
|
- *
|
|
|
|
- * @param srcKey source key
|
|
|
|
- * @param dstKey destination key
|
|
|
|
- * @return true if successfully copied
|
|
|
|
- */
|
|
|
|
- private boolean copyFile(String srcKey, String dstKey) {
|
|
|
|
- ObjectMetadata objectMeta =
|
|
|
|
- ossClient.getObjectMetadata(bucketName, srcKey);
|
|
|
|
- long dataLen = objectMeta.getContentLength();
|
|
|
|
- if (dataLen <= multipartThreshold) {
|
|
|
|
- return singleCopy(srcKey, dstKey);
|
|
|
|
- } else {
|
|
|
|
- return multipartCopy(srcKey, dataLen, dstKey);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * Use single copy to copy an oss object.
|
|
|
|
- *
|
|
|
|
- * @param srcKey source key
|
|
|
|
- * @param dstKey destination key
|
|
|
|
- * @return true if successfully copied
|
|
|
|
- * (the caller should make sure srcPath is a file and dstPath is valid)
|
|
|
|
- */
|
|
|
|
- private boolean singleCopy(String srcKey, String dstKey) {
|
|
|
|
- CopyObjectResult copyResult =
|
|
|
|
- ossClient.copyObject(bucketName, srcKey, bucketName, dstKey);
|
|
|
|
- LOG.debug(copyResult.getETag());
|
|
|
|
- return true;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * Use multipart copy to copy an oss object.
|
|
|
|
- * (the caller should make sure srcPath is a file and dstPath is valid)
|
|
|
|
- *
|
|
|
|
- * @param srcKey source key
|
|
|
|
- * @param dataLen data size of the object to copy
|
|
|
|
- * @param dstKey destination key
|
|
|
|
- * @return true if successfully copied, or false if upload is aborted
|
|
|
|
- */
|
|
|
|
- private boolean multipartCopy(String srcKey, long dataLen, String dstKey) {
|
|
|
|
- int partNum = (int)(dataLen / uploadPartSize);
|
|
|
|
- if (dataLen % uploadPartSize != 0) {
|
|
|
|
- partNum++;
|
|
|
|
- }
|
|
|
|
- InitiateMultipartUploadRequest initiateMultipartUploadRequest =
|
|
|
|
- new InitiateMultipartUploadRequest(bucketName, dstKey);
|
|
|
|
- ObjectMetadata meta = new ObjectMetadata();
|
|
|
|
- if (!serverSideEncryptionAlgorithm.isEmpty()) {
|
|
|
|
- meta.setServerSideEncryption(serverSideEncryptionAlgorithm);
|
|
|
|
- }
|
|
|
|
- initiateMultipartUploadRequest.setObjectMetadata(meta);
|
|
|
|
- InitiateMultipartUploadResult initiateMultipartUploadResult =
|
|
|
|
- ossClient.initiateMultipartUpload(initiateMultipartUploadRequest);
|
|
|
|
- String uploadId = initiateMultipartUploadResult.getUploadId();
|
|
|
|
- List<PartETag> partETags = new ArrayList<PartETag>();
|
|
|
|
- try {
|
|
|
|
- for (int i = 0; i < partNum; i++) {
|
|
|
|
- long skipBytes = uploadPartSize * i;
|
|
|
|
- long size = (uploadPartSize < dataLen - skipBytes) ?
|
|
|
|
- uploadPartSize : dataLen - skipBytes;
|
|
|
|
- UploadPartCopyRequest partCopyRequest = new UploadPartCopyRequest();
|
|
|
|
- partCopyRequest.setSourceBucketName(bucketName);
|
|
|
|
- partCopyRequest.setSourceKey(srcKey);
|
|
|
|
- partCopyRequest.setBucketName(bucketName);
|
|
|
|
- partCopyRequest.setKey(dstKey);
|
|
|
|
- partCopyRequest.setUploadId(uploadId);
|
|
|
|
- partCopyRequest.setPartSize(size);
|
|
|
|
- partCopyRequest.setBeginIndex(skipBytes);
|
|
|
|
- partCopyRequest.setPartNumber(i + 1);
|
|
|
|
- UploadPartCopyResult partCopyResult =
|
|
|
|
- ossClient.uploadPartCopy(partCopyRequest);
|
|
|
|
- statistics.incrementWriteOps(1);
|
|
|
|
- partETags.add(partCopyResult.getPartETag());
|
|
|
|
- }
|
|
|
|
- CompleteMultipartUploadRequest completeMultipartUploadRequest =
|
|
|
|
- new CompleteMultipartUploadRequest(bucketName, dstKey,
|
|
|
|
- uploadId, partETags);
|
|
|
|
- CompleteMultipartUploadResult completeMultipartUploadResult =
|
|
|
|
- ossClient.completeMultipartUpload(completeMultipartUploadRequest);
|
|
|
|
- LOG.debug(completeMultipartUploadResult.getETag());
|
|
|
|
- return true;
|
|
|
|
- } catch (OSSException | ClientException e) {
|
|
|
|
- AbortMultipartUploadRequest abortMultipartUploadRequest =
|
|
|
|
- new AbortMultipartUploadRequest(bucketName, dstKey, uploadId);
|
|
|
|
- ossClient.abortMultipartUpload(abortMultipartUploadRequest);
|
|
|
|
- return false;
|
|
|
|
- }
|
|
|
|
|
|
+ return store.copyFile(srcKey, dstKey);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
* Copy a directory from source path to destination path.
|
|
* Copy a directory from source path to destination path.
|
|
* (the caller should make sure srcPath is a directory, and dstPath is valid)
|
|
* (the caller should make sure srcPath is a directory, and dstPath is valid)
|
|
*
|
|
*
|
|
- * @param srcPath source path
|
|
|
|
- * @param dstPath destination path
|
|
|
|
- * @return true if successfully copied
|
|
|
|
|
|
+ * @param srcPath source path.
|
|
|
|
+ * @param dstPath destination path.
|
|
|
|
+ * @return true if directory is successfully copied.
|
|
*/
|
|
*/
|
|
private boolean copyDirectory(Path srcPath, Path dstPath) {
|
|
private boolean copyDirectory(Path srcPath, Path dstPath) {
|
|
String srcKey = pathToKey(srcPath);
|
|
String srcKey = pathToKey(srcPath);
|
|
@@ -835,21 +524,18 @@ public class AliyunOSSFileSystem extends FileSystem {
|
|
return false;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
- ListObjectsRequest listObjectsRequest = new ListObjectsRequest(bucketName);
|
|
|
|
- listObjectsRequest.setPrefix(srcKey);
|
|
|
|
- listObjectsRequest.setMaxKeys(maxKeys);
|
|
|
|
-
|
|
|
|
- ObjectListing objects = ossClient.listObjects(listObjectsRequest);
|
|
|
|
|
|
+ ObjectListing objects = store.listObjects(srcKey, maxKeys, null, null);
|
|
statistics.incrementReadOps(1);
|
|
statistics.incrementReadOps(1);
|
|
// Copy files from src folder to dst
|
|
// Copy files from src folder to dst
|
|
while (true) {
|
|
while (true) {
|
|
for (OSSObjectSummary objectSummary : objects.getObjectSummaries()) {
|
|
for (OSSObjectSummary objectSummary : objects.getObjectSummaries()) {
|
|
String newKey =
|
|
String newKey =
|
|
dstKey.concat(objectSummary.getKey().substring(srcKey.length()));
|
|
dstKey.concat(objectSummary.getKey().substring(srcKey.length()));
|
|
- copyFile(objectSummary.getKey(), newKey);
|
|
|
|
|
|
+ store.copyFile(objectSummary.getKey(), newKey);
|
|
}
|
|
}
|
|
if (objects.isTruncated()) {
|
|
if (objects.isTruncated()) {
|
|
- listObjectsRequest.setMarker(objects.getNextMarker());
|
|
|
|
|
|
+ objects = store.listObjects(srcKey, maxKeys, null,
|
|
|
|
+ objects.getNextMarker());
|
|
statistics.incrementReadOps(1);
|
|
statistics.incrementReadOps(1);
|
|
} else {
|
|
} else {
|
|
break;
|
|
break;
|
|
@@ -863,4 +549,7 @@ public class AliyunOSSFileSystem extends FileSystem {
|
|
this.workingDir = dir;
|
|
this.workingDir = dir;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ public AliyunOSSFileSystemStore getStore() {
|
|
|
|
+ return store;
|
|
|
|
+ }
|
|
}
|
|
}
|