diff --git a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/fileresource/FileResourceService.java b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/fileresource/FileResourceService.java index bedcd14e40a0..92e43b63b40c 100644 --- a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/fileresource/FileResourceService.java +++ b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/fileresource/FileResourceService.java @@ -32,18 +32,33 @@ import java.io.InputStream; import java.io.OutputStream; import java.net.URI; +import java.time.Duration; import java.util.List; import java.util.NoSuchElementException; import java.util.Optional; import javax.annotation.CheckForNull; import javax.annotation.Nonnull; +import org.hisp.dhis.feedback.ConflictException; +import org.hisp.dhis.feedback.NotFoundException; /** * @author Halvdan Hoem Grelland */ public interface FileResourceService { + + @CheckForNull FileResource getFileResource(String uid); + /** + * Get the {@link FileResource} with the given ID + * + * @param uid the resource to fetch + * @return the file resource + * @throws NotFoundException when no such file resource exits + */ + @Nonnull + FileResource getExistingFileResource(String uid) throws NotFoundException; + /** * Lookup a {@link FileResource} by uid and {@link FileResourceDomain}. * @@ -83,7 +98,12 @@ public interface FileResourceService { void deleteFileResource(FileResource fileResource); - InputStream getFileResourceContent(FileResource fileResource); + @Nonnull + InputStream getFileResourceContent(FileResource fileResource) throws ConflictException; + + @Nonnull + InputStream getFileResourceContent(FileResource fileResource, Duration timeout) + throws ConflictException; /** Copy fileResource content to outputStream and Return File content length */ void copyFileResourceContent(FileResource fileResource, OutputStream outputStream) diff --git a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/fileresource/FileResourceStorageStatus.java b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/fileresource/FileResourceStorageStatus.java index 517550c01ec3..c2c77dad7aa4 100644 --- a/dhis-2/dhis-api/src/main/java/org/hisp/dhis/fileresource/FileResourceStorageStatus.java +++ b/dhis-2/dhis-api/src/main/java/org/hisp/dhis/fileresource/FileResourceStorageStatus.java @@ -33,6 +33,5 @@ public enum FileResourceStorageStatus { NONE, // No content stored PENDING, // In transit to store, not available - FAILED, // Storing the resource failed STORED // Is available from store } diff --git a/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/fileresource/DefaultFileResourceService.java b/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/fileresource/DefaultFileResourceService.java index 708222a7eccf..96c46639bcdc 100644 --- a/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/fileresource/DefaultFileResourceService.java +++ b/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/fileresource/DefaultFileResourceService.java @@ -27,6 +27,10 @@ */ package org.hisp.dhis.fileresource; +import static java.lang.System.currentTimeMillis; +import static java.time.Duration.ofMillis; +import static java.time.Duration.ofSeconds; + import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -43,7 +47,9 @@ import lombok.RequiredArgsConstructor; import org.hibernate.SessionFactory; import org.hisp.dhis.common.IllegalQueryException; +import org.hisp.dhis.feedback.ConflictException; import org.hisp.dhis.feedback.ErrorCode; +import org.hisp.dhis.feedback.NotFoundException; import org.hisp.dhis.fileresource.events.BinaryFileSavedEvent; import org.hisp.dhis.fileresource.events.FileDeletedEvent; import org.hisp.dhis.fileresource.events.FileSavedEvent; @@ -86,6 +92,15 @@ public class DefaultFileResourceService implements FileResourceService { // FileResourceService implementation // ------------------------------------------------------------------------- + @Nonnull + @Override + @Transactional(readOnly = true) + public FileResource getExistingFileResource(String uid) throws NotFoundException { + FileResource fr = fileResourceStore.getByUid(uid); + if (fr == null) throw new NotFoundException(FileResource.class, uid); + return fr; + } + @Override @Transactional(readOnly = true) public FileResource getFileResource(String uid) { @@ -228,26 +243,43 @@ public void deleteFileResource(FileResource fileResource) { } @Override - @Transactional(readOnly = true) - public InputStream getFileResourceContent(FileResource fileResource) { - return fileResourceContentStore.getFileResourceContent(fileResource.getStorageKey()); + @Nonnull + public InputStream getFileResourceContent(FileResource fileResource) throws ConflictException { + return getFileResourceContent(fileResource, ofSeconds(10)); + } + + @Nonnull + @Override + public InputStream getFileResourceContent(FileResource fileResource, java.time.Duration timeout) + throws ConflictException { + String key = fileResource.getStorageKey(); + InputStream content = fileResourceContentStore.getFileResourceContent(key); + long since = currentTimeMillis(); + while (content == null && !timeout.minus(ofMillis(currentTimeMillis() - since)).isNegative()) { + try { + Thread.sleep(50); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + content = fileResourceContentStore.getFileResourceContent(key); + } + if (content == null) + throw new ConflictException("File resource exists but content input stream was null"); + return content; } @Override - @Transactional(readOnly = true) public long getFileResourceContentLength(FileResource fileResource) { return fileResourceContentStore.getFileResourceContentLength(fileResource.getStorageKey()); } @Override - @Transactional(readOnly = true) public void copyFileResourceContent(FileResource fileResource, OutputStream outputStream) throws IOException, NoSuchElementException { fileResourceContentStore.copyContent(fileResource.getStorageKey(), outputStream); } @Override - @Transactional(readOnly = true) public byte[] copyFileResourceContent(FileResource fileResource) throws IOException, NoSuchElementException { return fileResourceContentStore.copyContent(fileResource.getStorageKey()); diff --git a/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/fileresource/JCloudsFileResourceContentStore.java b/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/fileresource/JCloudsFileResourceContentStore.java index 63c6e70f4702..0c9ada0ea604 100644 --- a/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/fileresource/JCloudsFileResourceContentStore.java +++ b/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/fileresource/JCloudsFileResourceContentStore.java @@ -194,6 +194,7 @@ public InputStream getFileResourceContent(String key) { final Blob blob = getBlob(key); if (blob == null) { + return null; } diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/deprecated/tracker/TrackedEntityInstanceController.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/deprecated/tracker/TrackedEntityInstanceController.java index 50f0b1fc0542..11a5d4704f32 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/deprecated/tracker/TrackedEntityInstanceController.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/deprecated/tracker/TrackedEntityInstanceController.java @@ -73,6 +73,7 @@ import org.hisp.dhis.dxf2.webmessage.WebMessage; import org.hisp.dhis.dxf2.webmessage.WebMessageException; import org.hisp.dhis.feedback.BadRequestException; +import org.hisp.dhis.feedback.ConflictException; import org.hisp.dhis.fieldfilter.FieldFilterParams; import org.hisp.dhis.fieldfilter.FieldFilterService; import org.hisp.dhis.fileresource.FileResource; @@ -205,7 +206,7 @@ public void getAttributeImage( @RequestParam(required = false) Integer height, @RequestParam(required = false) ImageFileDimension dimension, HttpServletResponse response) - throws WebMessageException { + throws WebMessageException, ConflictException { User user = currentUserService.getCurrentUser(); TrackedEntity trackedEntity = instanceService.getTrackedEntity(teiId); diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/metadata/MetadataImportJob.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/metadata/MetadataImportJob.java index 20c76b3ae303..1aaa4126861f 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/metadata/MetadataImportJob.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/metadata/MetadataImportJob.java @@ -82,7 +82,7 @@ public void execute(JobConfiguration config, JobProgress progress) { MetadataImportParams params = (MetadataImportParams) config.getJobParameters(); progress.startingStage("Loading file resource"); FileResource data = - progress.runStage(() -> fileResourceService.getFileResource(config.getUid())); + progress.runStage(() -> fileResourceService.getExistingFileResource(config.getUid())); progress.startingStage("Loading file content"); try (InputStream input = progress.runStage(() -> fileResourceService.getFileResourceContent(data))) { diff --git a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/imports/TrackerImportJob.java b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/imports/TrackerImportJob.java index 518fcf6b72fe..d144c60e0970 100644 --- a/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/imports/TrackerImportJob.java +++ b/dhis-2/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/tracker/imports/TrackerImportJob.java @@ -67,7 +67,7 @@ public void execute(JobConfiguration config, JobProgress progress) { TrackerImportParams params = (TrackerImportParams) config.getJobParameters(); progress.startingStage("Loading file resource"); FileResource data = - progress.runStage(() -> fileResourceService.getFileResource(config.getUid())); + progress.runStage(() -> fileResourceService.getExistingFileResource(config.getUid())); progress.startingStage("Loading file content"); try (InputStream input = progress.runStage(() -> fileResourceService.getFileResourceContent(data))) {