Skip to content

Commit f597c6e

Browse files
authored
Merge pull request #2 from oracle-devrel/paralell
added support for parallel operations
2 parents f614e9e + 3371ce3 commit f597c6e

File tree

2 files changed

+217
-21
lines changed

2 files changed

+217
-21
lines changed

oci-java-sdk-simple-wrappers/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ SOFTWARE. -->
3939
<groupId>com.oracle.timg.demo</groupId>
4040
<artifactId>oci-java-sdk-simple-wrappers</artifactId>
4141
<name>ocicore</name>
42-
<version>1.0.0</version>
42+
<version>1.0.1</version>
4343
<properties>
4444
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
4545
<version.ocisdk>3.17.0</version.ocisdk>

oci-java-sdk-simple-wrappers/src/main/java/com/oracle/timg/oci/objectstorage/ObjectStorageProcessor.java

Lines changed: 216 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,13 @@ Software and the Larger Work(s), and to sublicense the foregoing rights on
4040
import java.io.IOException;
4141
import java.io.InputStream;
4242
import java.net.HttpURLConnection;
43+
import java.util.Arrays;
4344
import java.util.Collection;
4445
import java.util.HashMap;
4546
import java.util.Map;
4647
import java.util.Set;
4748
import java.util.TreeSet;
49+
import java.util.stream.Collectors;
4850

4951
import com.oracle.bmc.identity.model.Compartment;
5052
import com.oracle.bmc.model.BmcException;
@@ -89,6 +91,7 @@ Software and the Larger Work(s), and to sublicense the foregoing rights on
8991
@Slf4j
9092
public class ObjectStorageProcessor {
9193

94+
private static final int DELETED_CODE = 204;
9295
private UploadManager uploadManager;
9396
private DownloadManager downloadManager;
9497
private final AuthenticationProcessor authProcessor;
@@ -567,7 +570,8 @@ public boolean deleteObject(@NonNull String bucketName, String prefix, @NonNull
567570
DeleteObjectResponse response = objectstorageClient.deleteObject(request);
568571
log.debug("Delete object " + fullObjectName + " from bucket " + bucketName + " has reponse code "
569572
+ response.get__httpStatusCode__());
570-
return response.get__httpStatusCode__() == HttpURLConnection.HTTP_OK;
573+
return (response.get__httpStatusCode__() == DELETED_CODE)
574+
|| (response.get__httpStatusCode__() == HttpURLConnection.HTTP_OK);
571575
} catch (BmcException e) {
572576
log.warn("Can't delete object, msg is " + e.getLocalizedMessage());
573577
return false;
@@ -642,15 +646,20 @@ public String uploadFile(@NonNull String bucketName, String objectPrefix, @NonNu
642646
@NonNull File localFile) throws IOException {
643647
String fullObjectName = objectPrefix == null ? objectName
644648
: objectPrefix + pathSeparatorInObjectStorage + objectName;
645-
// share the upload manager and allow lazy instantiation
646-
if (uploadManager == null) {
647-
UploadConfiguration uploadConfiguration = UploadConfiguration.builder().allowMultipartUploads(true)
648-
.allowParallelUploads(true).build();
649-
uploadManager = new UploadManager(objectstorageClient, uploadConfiguration);
649+
// share the upload manager and allow lazy instantiation, synchrnonize to allow
650+
// for parallel operation(s)
651+
synchronized (this) {
652+
if (uploadManager == null) {
653+
log.debug("Initialiing upload manager");
654+
UploadConfiguration uploadConfiguration = UploadConfiguration.builder().allowMultipartUploads(true)
655+
.allowParallelUploads(true).build();
656+
uploadManager = new UploadManager(objectstorageClient, uploadConfiguration);
657+
}
650658
}
651659
PutObjectRequest putObjectRequest = PutObjectRequest.builder().bucketName(bucketName).namespaceName(namespace)
652660
.objectName(fullObjectName).build();
653661
UploadRequest uploadRequest = UploadRequest.builder(localFile).allowOverwrite(true).build(putObjectRequest);
662+
log.debug("Uploading " + localFile.getPath() + " to bucket " + bucketName + " object names " + fullObjectName);
654663
UploadResponse uploadResponse = uploadManager.upload(uploadRequest);
655664
String uploadedMD5 = uploadResponse.getContentMd5();
656665
return uploadedMD5 == null ? uploadResponse.getMultipartMd5() : uploadedMD5;
@@ -687,11 +696,58 @@ public String uploadDirectory(@NonNull String bucketName, String objectPrefix, @
687696
File[] dirEntries = localStartingPoint.listFiles();
688697
String res = "";
689698
for (File dirEntry : dirEntries) {
699+
log.debug("Uploading from directory " + localStartingPoint + " object " + dirEntry.getName());
690700
res += uploadObject(bucketName, objectPrefix, dirEntry) + "\n";
691701
}
692702
return res;
693703
}
694704

705+
/**
706+
* Uploads all of the object or directories containing objects, uploads
707+
* sequentially
708+
*
709+
* @param bucketName - must not be null
710+
* @param objectPrefix - if provided will be applied to all uploaded
711+
* objects
712+
* @param localStartingPoints - must not be null and must be a file or directory
713+
* @param parallelUploads - if true then each of the localStarting points is
714+
* uploaded in parallel (or at least as parallel as
715+
* the object storage system supports
716+
* @return the relative file names and their MD5 checksums
717+
* @throws IOException
718+
*/
719+
public String uploadObjects(@NonNull String bucketName, String objectPrefix,
720+
@NonNull Collection<File> localStartingPoints) throws IOException {
721+
return uploadObjects(bucketName, objectPrefix, localStartingPoints, false);
722+
}
723+
724+
/**
725+
* Uploads all of the object or directories containing objects, uploads
726+
* sequentially or in parallel depending on the parallelUploads
727+
*
728+
* @param bucketName - must not be null
729+
* @param objectPrefix - if provided will be applied to all uploaded
730+
* objects
731+
* @param localStartingPoints - must not be null and must be a file or directory
732+
* @param parallelUploads - if true then each of the localStarting points is
733+
* uploaded in parallel (or at least as parallel as
734+
* the object storage system supports
735+
* @return the relative file names and their MD5 checksums
736+
* @throws IOException
737+
*/
738+
public String uploadObjects(@NonNull String bucketName, String objectPrefix,
739+
@NonNull Collection<File> localStartingPoints, boolean parallelUploads) throws IOException {
740+
return (parallelUploads ? localStartingPoints.parallelStream() : localStartingPoints.stream())
741+
.map(localStartingPoint -> {
742+
try {
743+
return uploadObject(bucketName, objectPrefix, localStartingPoint);
744+
} catch (IOException e) {
745+
return "Problem uploading to bucket " + bucketName + " wth prefix " + objectPrefix
746+
+ " from local start " + localStartingPoint.getPath();
747+
}
748+
}).collect(Collectors.joining("\n"));
749+
}
750+
695751
/**
696752
* Uploads an object or a directory containing objects
697753
*
@@ -749,9 +805,11 @@ public int downloadFile(@NonNull String bucketName, String objectPrefix, @NonNul
749805
@NonNull File localFile) throws IOException {
750806
String fullObjectName = objectPrefix == null ? objectName : objectPrefix + objectName;
751807
// share the download manager and allow lazy instantiation
752-
if (downloadManager == null) {
753-
DownloadConfiguration downloadConfiguration = DownloadConfiguration.builder().build();
754-
downloadManager = new DownloadManager(objectstorageClient, downloadConfiguration);
808+
synchronized (this) {
809+
if (downloadManager == null) {
810+
DownloadConfiguration downloadConfiguration = DownloadConfiguration.builder().build();
811+
downloadManager = new DownloadManager(objectstorageClient, downloadConfiguration);
812+
}
755813
}
756814
GetObjectRequest request = GetObjectRequest.builder().bucketName(bucketName).namespaceName(namespace)
757815
.objectName(fullObjectName).build();
@@ -767,10 +825,138 @@ public int downloadFile(@NonNull String bucketName, String objectPrefix, @NonNul
767825
}
768826

769827
/**
770-
* Downloads the object to the driectory represented by localStartingPoint , if
828+
* Downloads each of the object names to the directory represented by
829+
* localStartingPoint, if the object name includes path elements e.g.
830+
* /user/tim/file1.txt then if needed the /user/tim directory will be created
831+
* under the local starting point.
832+
*
833+
* The downloads will happen sequentially
834+
*
835+
* If the the object names represent an object then only object read permission
836+
* is needed, however if the object represents a "folder" (In OCI object storage
837+
* terminology) then inspect permission is needed on the bucket as well to
838+
* identify the objects it contains
839+
*
840+
* @param bucketName - must not be null
841+
* @param objectPrefix - if non null will be applied to the object name
842+
* before starting the download
843+
* @param objectNames - must not be null
844+
* @param localStartingPoint - must not be null, must be a directory
845+
* @return number of objects downloaded
846+
* @throws IOException
847+
*/
848+
public long downloadFilesCreatePath(@NonNull String bucketName, String objectPrefix, @NonNull String objectNames[],
849+
@NonNull File localStartingPoint) throws IOException {
850+
return downloadFilesCreatePath(bucketName, objectPrefix, objectNames, localStartingPoint, false);
851+
}
852+
853+
/**
854+
* Downloads each of the object names to the directory represented by
855+
* localStartingPoint, if the object name includes path elements e.g.
856+
* /user/tim/file1.txt then if needed the /user/tim directory will be created
857+
* under the local starting point.
858+
*
859+
* The downloads will happen in parallel at the level of the objectNames if
860+
* parallelDownloads is set to true. This means that if there were say two
861+
* object names both would be downloaded in parallel (upto to the limits set by
862+
* the object storage) however if there were multiple objects "under" a
863+
* specified object name than those would happen sequentially
864+
*
865+
* If the the object names represent an object then only object read permision
866+
* is needed, however if the object represents a "folder" (In OCI object storage
867+
* terminology) then inspect permision is needed on the bucket as well to
868+
* identify the objects it contains
869+
*
870+
* @param bucketName - must not be null
871+
* @param objectPrefix - if non null will be applied to the object name
872+
* before starting the download
873+
* @param objectNames - must not be null
874+
* @param localStartingPoint - must not be null, must be a directory
875+
* @parallelDownloads - if true then allow parallel downloads
876+
* @return number of objects downloaded
877+
* @throws IOException
878+
*/
879+
public long downloadFilesCreatePath(@NonNull String bucketName, String objectPrefix, @NonNull String objectNames[],
880+
@NonNull File localStartingPoint, boolean parallelDownloads) throws IOException {
881+
return downloadFilesCreatePath(bucketName, objectPrefix, Arrays.asList(objectNames), localStartingPoint,
882+
parallelDownloads);
883+
884+
}
885+
886+
/**
887+
* Downloads each of the object names to the directory represented by
888+
* localStartingPoint, if the object name includes path elements e.g.
889+
* /user/tim/file1.txt then if needed the /user/tim directory will be created
890+
* under the local starting point.
891+
*
892+
* The downloads will happen sequentially
893+
*
894+
* If the the object names represent an object then only object read permision
895+
* is needed, however if the object represents a "folder" (In OCI object storage
896+
* terminology) then inspect permision is needed on the bucket as well to
897+
* identify the objects it contains
898+
*
899+
* @param bucketName - must not be null
900+
* @param objectPrefix - if non null will be applied to the object name
901+
* before starting the download
902+
* @param objectNames - must not be null
903+
* @param localStartingPoint - must not be null, must be a directory
904+
* @return number of objects downloaded
905+
* @throws IOException
906+
*/
907+
public long downloadFilesCreatePath(@NonNull String bucketName, String objectPrefix,
908+
@NonNull Collection<String> objectNames, @NonNull File localStartingPoint) throws IOException {
909+
return downloadFilesCreatePath(bucketName, objectPrefix, objectNames, localStartingPoint, false);
910+
}
911+
912+
/**
913+
* Downloads each of the object names to the directory represented by
914+
* localStartingPoint, if the object name includes path elements e.g.
915+
* /user/tim/file1.txt then if needed the /user/tim directory will be created
916+
* under the local starting point.
917+
*
918+
* The downloads will happen in parallel at the level of the objectNames if
919+
* parallelDownloads is set to true. This means that if there were say two
920+
* object names both would be downloaded in parallel (upto to the limits set by
921+
* the object storage) however if there were multiple objects "under" a
922+
* specified object name than those would happen sequentially
923+
*
924+
* If the the object names represent an object then only object read permision
925+
* is needed, however if the object represents a "folder" (In OCI object storage
926+
* terminology) then inspect permision is needed on the bucket as well to
927+
* identify the objects it contains
928+
*
929+
* @param bucketName - must not be null
930+
* @param objectPrefix - if non null will be applied to the object name
931+
* before starting the download
932+
* @param objectNames - must not be null
933+
* @param localStartingPoint - must not be null, must be a directory
934+
* @parallelDownloads - if true then allow parallel downloads
935+
* @return number of objects downloaded
936+
* @throws IOException
937+
*/
938+
public long downloadFilesCreatePath(@NonNull String bucketName, String objectPrefix,
939+
@NonNull Collection<String> objectNames, @NonNull File localStartingPoint, boolean parallelDownloads)
940+
throws IOException {
941+
return (parallelDownloads ? objectNames.parallelStream() : objectNames.stream()).map(objectName -> {
942+
try {
943+
return downloadFileCreatePath(bucketName, objectPrefix, objectName, localStartingPoint);
944+
} catch (IOException e) {
945+
return -1;
946+
}
947+
}).filter(bytesDownloaded -> bytesDownloaded >= 0).count();
948+
}
949+
950+
/**
951+
* Downloads the object to the directory represented by localStartingPoint , if
771952
* the object name includes path elements e.g. /user/tim/file1.txt then if
772953
* needed the /user/tim directory will be created under the local starting point
773954
*
955+
* If the the object names represent an object then only object read permision
956+
* is needed, however if the object represents a "folder" (In OCI object storage
957+
* terminology) then inspect permision is needed on the bucket as well to
958+
* identify the objects it contains
959+
*
774960
* @param bucketName - must not be null
775961
* @param objectPrefix - if non null will be applied to the object name
776962
* before starting the download
@@ -781,7 +967,7 @@ public int downloadFile(@NonNull String bucketName, String objectPrefix, @NonNul
781967
*/
782968
public int downloadFileCreatePath(@NonNull String bucketName, String objectPrefix, @NonNull String objectName,
783969
@NonNull File localStartingPoint) throws IOException {
784-
log.info("Downloading into bucket " + bucketName + " object with prefix " + objectPrefix + " and name "
970+
log.debug("Downloading into bucket " + bucketName + " object with prefix " + objectPrefix + " and name "
785971
+ objectName + " with local starting point of " + localStartingPoint.getPath());
786972
if (!localStartingPoint.isDirectory()) {
787973
throw new IOException(
@@ -790,7 +976,7 @@ public int downloadFileCreatePath(@NonNull String bucketName, String objectPrefi
790976
String relativeDirectoryPath = objectName;
791977
// if it starts with a path delimiter remove it (allow for multiple)
792978
while (relativeDirectoryPath.startsWith(pathSeparatorInObjectStorage)) {
793-
log.info("removed " + pathSeparatorInObjectStorage + " from start of objectName path");
979+
log.debug("removed " + pathSeparatorInObjectStorage + " from start of objectName path");
794980
relativeDirectoryPath = relativeDirectoryPath.substring(pathSeparatorInObjectStorage.length());
795981
}
796982
// get the path element of the object, we need to determine if we will be
@@ -803,21 +989,20 @@ public int downloadFileCreatePath(@NonNull String bucketName, String objectPrefi
803989
relativeDirectoryPath = objectName.substring(0, lastPathSeparator);
804990
// make sure any paths are in the local FS format
805991
relativeDirectoryPath = relativeDirectoryPath.replace(pathSeparatorInObjectStorage, File.separator);
806-
log.info("The directory path element to be added is " + relativeDirectoryPath);
992+
log.debug("The directory path element to be added is " + relativeDirectoryPath);
807993
File endDir = new File(localStartingPoint.getPath() + File.separator + relativeDirectoryPath);
808-
log.info("crreating local directory including local start og " + localStartingPoint.getPath()
994+
log.debug("creating local directory including local start of " + localStartingPoint.getPath()
809995
+ " and relative dir of " + relativeDirectoryPath + " is " + endDir.getPath());
810996
endDir.mkdirs();
811997
String finalObjectName = objectName.substring(lastPathSeparator);
812998
downloadTarget = new File(endDir.getPath() + File.separator + finalObjectName);
813-
log.info("tried to create the directory tree for download target of " + downloadTarget.getPath());
999+
log.debug("tried to create the directory tree for download target of " + downloadTarget.getPath());
8141000
} else {
8151001
downloadTarget = new File(localStartingPoint.getPath() + File.separator + objectName);
816-
log.info(downloadTarget.getPath() + " is a file in the prefix with no path to no need to create the tree");
1002+
log.debug(downloadTarget.getPath() + " is a file in the prefix with no path to no need to create the tree");
8171003
}
818-
log.info("Downloading to " + bucketName + " from prefix " + objectPrefix + " with name " + objectName
1004+
log.debug("Downloading to " + bucketName + " from prefix " + objectPrefix + " with name " + objectName
8191005
+ " to local file " + downloadTarget.getPath());
820-
;
8211006
return downloadFile(bucketName, objectPrefix, objectName, downloadTarget);
8221007
}
8231008

@@ -850,8 +1035,8 @@ public int downloadFileCreatePath(@NonNull String bucketName, String objectPrefi
8501035
*/
8511036

8521037
public String downloadObject(@NonNull String bucketName, String objectPrefix, @NonNull File localStartingPoint) {
853-
log.info("Downloading from bucket " + bucketName + " under prefix " + objectPrefix + " to local starting point "
854-
+ localStartingPoint.getPath());
1038+
log.debug("Downloading from bucket " + bucketName + " under prefix " + objectPrefix
1039+
+ " to local starting point " + localStartingPoint.getPath());
8551040
// get the object names
8561041
Set<String> objectNames = listObjectNamesInBucket(bucketName, objectPrefix);
8571042
String result = "";
@@ -865,4 +1050,15 @@ public String downloadObject(@NonNull String bucketName, String objectPrefix, @N
8651050
}
8661051
return result;
8671052
}
1053+
1054+
/**
1055+
* Tests a given objectName to ensure that it doesn't contain and path / folder
1056+
* structures as defined by object storage
1057+
*
1058+
* @param objectName
1059+
* @return
1060+
*/
1061+
public boolean isObjectNameOnly(@NonNull String objectName) {
1062+
return objectName.lastIndexOf(DEFAULT_PATH_SEPARATOR_IN_OBJECT_STORAGE) < 0;
1063+
}
8681064
}

0 commit comments

Comments
 (0)