diff --git a/score-server/src/main/java/bio/overture/score/server/repository/s3/S3DownloadService.java b/score-server/src/main/java/bio/overture/score/server/repository/s3/S3DownloadService.java index 017201ff..122a419b 100644 --- a/score-server/src/main/java/bio/overture/score/server/repository/s3/S3DownloadService.java +++ b/score-server/src/main/java/bio/overture/score/server/repository/s3/S3DownloadService.java @@ -37,6 +37,7 @@ import com.amazonaws.AmazonServiceException; import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.model.GetObjectRequest; +import com.amazonaws.services.s3.model.ObjectMetadata; import com.amazonaws.services.s3.model.S3Object; import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.databind.JsonMappingException; @@ -99,32 +100,7 @@ public ObjectSpecification download( checkArgument(offset > -1L); - // Retrieve our meta file for object id - val objectSpec = getSpecification(objectId); - - // Short-circuit in default case - if (!forExternalUse && (offset == 0L && length < 0L)) { - return excludeUrls ? removeUrls(objectSpec) : objectSpec; - } - - // Calculate range values - // To retrieve to the end of the file - if (!forExternalUse && (length < 0L)) { - length = objectSpec.getObjectSize() - offset; - } - - // Validate offset and length parameters: - // Check if the offset + length > length - that would be too big - if ((offset + length) > objectSpec.getObjectSize()) { - throw new InternalUnrecoverableError( - "Specified parameters exceed object size (object id: " - + objectId - + ", offset: " - + offset - + ", length: " - + length - + ")"); - } + var objectSpec = getSpecification(objectId); // Construct ObjectSpecification for actual object in /data logical folder val objectKey = ObjectKeys.getObjectKey(dataDir, objectId); @@ -136,21 +112,52 @@ public ObjectSpecification download( } else { parts = partCalculator.divide(offset, length); } + if (objectSpec == null) { + ObjectMetadata metadata = + s3Client.getObjectMetadata( + bucketNamingService.getStateBucketName(objectId), objectKey.getKey()); + fillPartUrls(objectKey, parts, false, forExternalUse); + objectSpec = + new ObjectSpecification( + objectKey.getKey(), objectId, objectId, parts, metadata.getContentLength(), metadata.getETag(), false); + } + + // Short-circuit in default case + if (!forExternalUse && (offset == 0L && length < 0L)) { + return excludeUrls ? removeUrls(objectSpec) : objectSpec; + } - fillPartUrls(objectKey, parts, objectSpec.isRelocated(), forExternalUse); + // Calculate range values + // To retrieve to the end of the file + if (!forExternalUse && (length < 0L)) { + length = objectSpec.getObjectSize() - offset; + } - val spec = - new ObjectSpecification( - objectKey.getKey(), - objectId, - objectId, - parts, - length, - objectSpec.getObjectMd5(), - objectSpec.isRelocated()); + // Validate offset and length parameters: + // Check if the offset + length > length - that would be too big + if ((offset + length) > objectSpec.getObjectSize()) { + throw new InternalUnrecoverableError( + "Specified parameters exceed object size (object id: " + + objectId + + ", offset: " + + offset + + ", length: " + + length + + ")"); + } + fillPartUrls(objectKey, parts, objectSpec.isRelocated(), forExternalUse); - return excludeUrls ? removeUrls(spec) : spec; + val spec = + new ObjectSpecification( + objectKey.getKey(), + objectId, + objectId, + parts, + length, + objectSpec.getObjectMd5(), + objectSpec.isRelocated()); + return excludeUrls ? removeUrls(spec) : spec; } catch (Exception e) { log.error( "Failed to download objectId: {}, offset: {}, length: {}, forExternalUse: {}, excludeUrls: {} : {} ", @@ -186,8 +193,6 @@ void checkPublishedAnalysisState(MetadataEntity entity) { } } } - - // This really is a misleading method name - should be retrieveMetaFile() or something public ObjectSpecification getSpecification(String objectId) { val objectKey = ObjectKeys.getObjectKey(dataDir, objectId); val objectMetaKey = ObjectKeys.getObjectMetaKey(dataDir, objectId); @@ -225,6 +230,8 @@ public ObjectSpecification getSpecification(String objectId) { objectKey, e); throw new NotRetryableException(e); + } catch (IdNotFoundException e) { + return null; } } diff --git a/score-server/src/test/java/bio/overture/score/server/service/download/ObjectDownloadServiceTest.java b/score-server/src/test/java/bio/overture/score/server/service/download/ObjectDownloadServiceTest.java index 03e292ba..f2ff8d1f 100644 --- a/score-server/src/test/java/bio/overture/score/server/service/download/ObjectDownloadServiceTest.java +++ b/score-server/src/test/java/bio/overture/score/server/service/download/ObjectDownloadServiceTest.java @@ -27,7 +27,6 @@ import bio.overture.score.core.util.ObjectKeys; import bio.overture.score.core.util.SimplePartCalculator; import bio.overture.score.server.config.ServerConfig; -import bio.overture.score.server.exception.IdNotFoundException; import bio.overture.score.server.metadata.MetadataEntity; import bio.overture.score.server.metadata.MetadataService; import bio.overture.score.server.repository.s3.S3BucketNamingService; @@ -104,7 +103,7 @@ public void setUpMockService() { when(mockService.getEntity(objectId)).thenReturn(metadataEntity); } - @Test(expected = IdNotFoundException.class) + @Test(expected = NullPointerException.class) public void it_takes_two_to_fail_with_not_found() { // stubbing appears before the actual execution val firstException = new AmazonServiceException("Didn't find Object Id in bucket");