@@ -40,11 +40,13 @@ Software and the Larger Work(s), and to sublicense the foregoing rights on
4040import java .io .IOException ;
4141import java .io .InputStream ;
4242import java .net .HttpURLConnection ;
43+ import java .util .Arrays ;
4344import java .util .Collection ;
4445import java .util .HashMap ;
4546import java .util .Map ;
4647import java .util .Set ;
4748import java .util .TreeSet ;
49+ import java .util .stream .Collectors ;
4850
4951import com .oracle .bmc .identity .model .Compartment ;
5052import com .oracle .bmc .model .BmcException ;
@@ -89,6 +91,7 @@ Software and the Larger Work(s), and to sublicense the foregoing rights on
8991@ Slf4j
9092public 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