From 2740543abfec2973a6618f91ec4b5ace377f2ff8 Mon Sep 17 00:00:00 2001 From: CalDescent Date: Sun, 6 Feb 2022 13:04:58 +0000 Subject: [PATCH] Added "async" and "attempts" parameters to GET /arbitrary/{service}/{name}* endpoints. async = fail immediately with 404 if missing, and request in the background attempts = the number of times to request the data (synchronous mode only for now) --- .../api/resource/ArbitraryResource.java | 51 +++++++++++++------ .../qortal/arbitrary/ArbitraryDataReader.java | 12 ++++- .../arbitrary/ArbitraryDataRenderer.java | 2 +- 3 files changed, 47 insertions(+), 18 deletions(-) diff --git a/src/main/java/org/qortal/api/resource/ArbitraryResource.java b/src/main/java/org/qortal/api/resource/ArbitraryResource.java index f588e9c9..84e53200 100644 --- a/src/main/java/org/qortal/api/resource/ArbitraryResource.java +++ b/src/main/java/org/qortal/api/resource/ArbitraryResource.java @@ -576,14 +576,16 @@ public class ArbitraryResource { @PathParam("service") Service service, @PathParam("name") String name, @QueryParam("filepath") String filepath, - @QueryParam("rebuild") boolean rebuild) { + @QueryParam("rebuild") boolean rebuild, + @QueryParam("async") boolean async, + @QueryParam("attempts") Integer attempts) { // Authentication can be bypassed in the settings, for those running public QDN nodes if (!Settings.getInstance().isQDNAuthBypassEnabled()) { Security.checkApiCallAllowed(request); } - return this.download(service, name, null, filepath, rebuild); + return this.download(service, name, null, filepath, rebuild, async, attempts); } @GET @@ -609,14 +611,16 @@ public class ArbitraryResource { @PathParam("name") String name, @PathParam("identifier") String identifier, @QueryParam("filepath") String filepath, - @QueryParam("rebuild") boolean rebuild) { + @QueryParam("rebuild") boolean rebuild, + @QueryParam("async") boolean async, + @QueryParam("attempts") Integer attempts) { // Authentication can be bypassed in the settings, for those running public QDN nodes if (!Settings.getInstance().isQDNAuthBypassEnabled()) { Security.checkApiCallAllowed(request); } - return this.download(service, name, identifier, filepath, rebuild); + return this.download(service, name, identifier, filepath, rebuild, async, attempts); } @@ -1027,30 +1031,45 @@ public class ArbitraryResource { } } - private HttpServletResponse download(Service service, String name, String identifier, String filepath, boolean rebuild) { + private HttpServletResponse download(Service service, String name, String identifier, String filepath, boolean rebuild, boolean async, Integer maxAttempts) { ArbitraryDataReader arbitraryDataReader = new ArbitraryDataReader(name, ArbitraryDataFile.ResourceIdType.NAME, service, identifier); try { int attempts = 0; + if (maxAttempts == null) { + maxAttempts = 5; + } // Loop until we have data - while (!Controller.isStopping()) { - attempts++; - if (!arbitraryDataReader.isBuilding()) { - try { - arbitraryDataReader.loadSynchronously(rebuild); - break; - } catch (MissingDataException e) { - if (attempts > 5) { - // Give up after 5 attempts - throw ApiExceptionFactory.INSTANCE.createCustomException(request, ApiError.INVALID_CRITERIA, "Data unavailable. Please try again later."); + if (async) { + // Asynchronous + arbitraryDataReader.loadAsynchronously(false); + } + else { + // Synchronous + while (!Controller.isStopping()) { + attempts++; + if (!arbitraryDataReader.isBuilding()) { + try { + arbitraryDataReader.loadSynchronously(rebuild); + break; + } catch (MissingDataException e) { + if (attempts > maxAttempts) { + // Give up after 5 attempts + throw ApiExceptionFactory.INSTANCE.createCustomException(request, ApiError.INVALID_CRITERIA, "Data unavailable. Please try again later."); + } } } + Thread.sleep(3000L); } - Thread.sleep(3000L); } + java.nio.file.Path outputPath = arbitraryDataReader.getFilePath(); + if (outputPath == null) { + // Assume the resource doesn't exist + throw ApiExceptionFactory.INSTANCE.createCustomException(request, ApiError.FILE_NOT_FOUND, "File not found"); + } if (filepath == null || filepath.isEmpty()) { // No file path supplied - so check if this is a single file resource diff --git a/src/main/java/org/qortal/arbitrary/ArbitraryDataReader.java b/src/main/java/org/qortal/arbitrary/ArbitraryDataReader.java index 619e5330..bb5641c2 100644 --- a/src/main/java/org/qortal/arbitrary/ArbitraryDataReader.java +++ b/src/main/java/org/qortal/arbitrary/ArbitraryDataReader.java @@ -122,9 +122,19 @@ public class ArbitraryDataReader { * This adds the build task to a queue, and the result will be cached when complete * To check the status of the build, periodically call isCachedDataAvailable() * Once it returns true, you can then use getFilePath() to access the data itself. + * + * @param overwrite - set to true to force rebuild an existing cache * @return true if added or already present in queue; false if not */ - public boolean loadAsynchronously() { + public boolean loadAsynchronously(boolean overwrite) { + ArbitraryDataCache cache = new ArbitraryDataCache(this.uncompressedPath, overwrite, + this.resourceId, this.resourceIdType, this.service, this.identifier); + if (cache.isCachedDataAvailable()) { + // Use cached data + this.filePath = this.uncompressedPath; + return true; + } + return ArbitraryDataBuildManager.getInstance().addToBuildQueue(this.createQueueItem()); } diff --git a/src/main/java/org/qortal/arbitrary/ArbitraryDataRenderer.java b/src/main/java/org/qortal/arbitrary/ArbitraryDataRenderer.java index 445ff2f6..e4d90b79 100644 --- a/src/main/java/org/qortal/arbitrary/ArbitraryDataRenderer.java +++ b/src/main/java/org/qortal/arbitrary/ArbitraryDataRenderer.java @@ -76,7 +76,7 @@ public class ArbitraryDataRenderer { if (!arbitraryDataReader.isCachedDataAvailable()) { // If async is requested, show a loading screen whilst build is in progress if (async) { - arbitraryDataReader.loadAsynchronously(); + arbitraryDataReader.loadAsynchronously(false); return this.getLoadingResponse(service, resourceId); }