Author: mattryan Date: Thu Jul 18 17:07:38 2019 New Revision: 1863326 URL: http://svn.apache.org/viewvc?rev=1863326&view=rev Log: OAK-7998: Return null if requesting download URI on nonexistent binary
Modified: jackrabbit/oak/branches/1.10/oak-blob-cloud-azure/src/main/java/org/apache/jackrabbit/oak/blob/cloud/azure/blobstorage/AzureBlobStoreBackend.java jackrabbit/oak/branches/1.10/oak-blob-cloud/src/main/java/org/apache/jackrabbit/oak/blob/cloud/s3/S3Backend.java jackrabbit/oak/branches/1.10/oak-blob-plugins/src/test/java/org/apache/jackrabbit/oak/plugins/blob/datastore/directaccess/AbstractDataRecordAccessProviderTest.java jackrabbit/oak/branches/1.10/oak-doc/src/site/markdown/features/direct-binary-access.md Modified: jackrabbit/oak/branches/1.10/oak-blob-cloud-azure/src/main/java/org/apache/jackrabbit/oak/blob/cloud/azure/blobstorage/AzureBlobStoreBackend.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.10/oak-blob-cloud-azure/src/main/java/org/apache/jackrabbit/oak/blob/cloud/azure/blobstorage/AzureBlobStoreBackend.java?rev=1863326&r1=1863325&r2=1863326&view=diff ============================================================================== --- jackrabbit/oak/branches/1.10/oak-blob-cloud-azure/src/main/java/org/apache/jackrabbit/oak/blob/cloud/azure/blobstorage/AzureBlobStoreBackend.java (original) +++ jackrabbit/oak/branches/1.10/oak-blob-cloud-azure/src/main/java/org/apache/jackrabbit/oak/blob/cloud/azure/blobstorage/AzureBlobStoreBackend.java Thu Jul 18 17:07:38 2019 @@ -763,7 +763,27 @@ public class AzureBlobStoreBackend exten URI createHttpDownloadURI(@NotNull DataIdentifier identifier, @NotNull DataRecordDownloadOptions downloadOptions) { URI uri = null; + + // When running unit test from Maven, it doesn't always honor the @NotNull decorators + if (null == identifier) throw new NullPointerException("identifier"); + if (null == downloadOptions) throw new NullPointerException("downloadOptions"); + if (httpDownloadURIExpirySeconds > 0) { + + // Check if this identifier exists. If not, we want to return null + // even if the identifier is in the download URI cache. + + try { + if (! exists(identifier)) { + LOG.warn("Cannot create download URI for nonexistent blob {}; returning null", getKeyName(identifier)); + return null; + } + } + catch (DataStoreException e) { + LOG.warn("Cannot create download URI for blob {} (caught DataStoreException); returning null", getKeyName(identifier), e); + return null; + } + if (null != httpDownloadURICache) { uri = httpDownloadURICache.getIfPresent(identifier); } Modified: jackrabbit/oak/branches/1.10/oak-blob-cloud/src/main/java/org/apache/jackrabbit/oak/blob/cloud/s3/S3Backend.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.10/oak-blob-cloud/src/main/java/org/apache/jackrabbit/oak/blob/cloud/s3/S3Backend.java?rev=1863326&r1=1863325&r2=1863326&view=diff ============================================================================== --- jackrabbit/oak/branches/1.10/oak-blob-cloud/src/main/java/org/apache/jackrabbit/oak/blob/cloud/s3/S3Backend.java (original) +++ jackrabbit/oak/branches/1.10/oak-blob-cloud/src/main/java/org/apache/jackrabbit/oak/blob/cloud/s3/S3Backend.java Thu Jul 18 17:07:38 2019 @@ -17,6 +17,10 @@ package org.apache.jackrabbit.oak.blob.cloud.s3; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.collect.Iterables.filter; +import static java.lang.Thread.currentThread; + import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; @@ -92,10 +96,6 @@ import org.jetbrains.annotations.NotNull import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.collect.Iterables.filter; -import static java.lang.Thread.currentThread; - /** * A data store backend that stores data on Amazon S3. */ @@ -740,6 +740,21 @@ public class S3Backend extends AbstractS return null; } + // When running unit test from Maven, it doesn't always honor the @NotNull decorators + if (null == identifier) throw new NullPointerException("identifier"); + if (null == downloadOptions) throw new NullPointerException("downloadOptions"); + + try { + if (! exists(identifier)) { + LOG.warn("Cannot create download URI for nonexistent blob {}; returning null", getKeyName(identifier)); + return null; + } + } + catch (DataStoreException e) { + LOG.warn("Cannot create download URI for blob {} (caught DataStoreException); returning null", getKeyName(identifier), e); + return null; + } + URI uri = null; // if cache is enabled, check the cache if (httpDownloadURICache != null) { Modified: jackrabbit/oak/branches/1.10/oak-blob-plugins/src/test/java/org/apache/jackrabbit/oak/plugins/blob/datastore/directaccess/AbstractDataRecordAccessProviderTest.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.10/oak-blob-plugins/src/test/java/org/apache/jackrabbit/oak/plugins/blob/datastore/directaccess/AbstractDataRecordAccessProviderTest.java?rev=1863326&r1=1863325&r2=1863326&view=diff ============================================================================== --- jackrabbit/oak/branches/1.10/oak-blob-plugins/src/test/java/org/apache/jackrabbit/oak/plugins/blob/datastore/directaccess/AbstractDataRecordAccessProviderTest.java (original) +++ jackrabbit/oak/branches/1.10/oak-blob-plugins/src/test/java/org/apache/jackrabbit/oak/plugins/blob/datastore/directaccess/AbstractDataRecordAccessProviderTest.java Thu Jul 18 17:07:38 2019 @@ -81,10 +81,20 @@ public abstract class AbstractDataRecord // Direct download tests // @Test - public void testGetDownloadURIProvidesValidURI() { - DataIdentifier id = new DataIdentifier("testIdentifier"); - URI uri = getDataStore().getDownloadURI(id, DataRecordDownloadOptions.DEFAULT); - assertNotNull(uri); + public void testGetDownloadURIProvidesValidURIIT() throws DataStoreException { + DataRecord record = null; + ConfigurableDataRecordAccessProvider dataStore = getDataStore(); + try { + InputStream testStream = randomStream(0, 256); + record = doSynchronousAddRecord((DataStore) dataStore, testStream); + DataIdentifier id = record.getIdentifier(); + URI uri = getDataStore().getDownloadURI(id, DataRecordDownloadOptions.DEFAULT); + assertNotNull(uri); + } finally { + if (null != record) { + doDeleteRecord((DataStore) dataStore, record.getIdentifier()); + } + } } @Test @@ -219,6 +229,20 @@ public abstract class AbstractDataRecord } } + @Test + public void testGetDownloadURINonexistentBlobFailsIT() throws DataStoreException { + ConfigurableDataRecordAccessProvider dataStore = getDataStore(); + InputStream testStream = randomStream(0, 256); + + DataRecord record = doSynchronousAddRecord((DataStore) dataStore, testStream); + + doDeleteRecord((DataStore) dataStore, record.getIdentifier()); + + URI uri = dataStore.getDownloadURI(record.getIdentifier(), DataRecordDownloadOptions.DEFAULT); + + assertNull(uri); + } + // // Direct upload tests // Modified: jackrabbit/oak/branches/1.10/oak-doc/src/site/markdown/features/direct-binary-access.md URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.10/oak-doc/src/site/markdown/features/direct-binary-access.md?rev=1863326&r1=1863325&r2=1863326&view=diff ============================================================================== --- jackrabbit/oak/branches/1.10/oak-doc/src/site/markdown/features/direct-binary-access.md (original) +++ jackrabbit/oak/branches/1.10/oak-doc/src/site/markdown/features/direct-binary-access.md Thu Jul 18 17:07:38 2019 @@ -106,6 +106,11 @@ if (binary instanceof BinaryDownload) { Please note that only `Binary` objects returned from `Property.getBinary()`, `Property.getValue().getBinary()` or `Property.getValues() ... getBinary()` will support a functional `BinaryDownload`. +Also note that clients should always check whether the URI returned from the `getURI()` call is null. A null return value generally indicates that the feature is not available. But this situation is also possible in two other cases: + +* If the binary is stored in-line in the node store. If the binary is smaller than the minimum upload size, it will be stored in the node store instead of in cloud blob storage, and thus a direct download URI cannot be provided. +* If the data store implementation is using asynchronous uploads and the binary is still in cache. If a client adds a binary via the repository (i.e. not using the direct binary upload feature) and then immediately requests a download URI for it, it is possible that the binary is still in cache and not yet uploaded to cloud storage, and thus a direct download URI cannot be provided. + ### Upload The direct binary upload process is split into 3 phases: @@ -178,6 +183,8 @@ public class InitiateUploadServlet exten } ``` +Clients should always check whether the `BinaryUpload` returned from `valueFactory.initiateBinaryUpload()` is null, and also should handle the case where no upload URIs are returned. Either situation indicates that the feature is not supported. + #### 2. Upload The remote client will upload using the instructions according to the [upload algorithm described in BinaryUpload](http://jackrabbit.apache.org/api/trunk/org/apache/jackrabbit/api/binary/BinaryUpload.html).