Author: mattryan
Date: Thu Sep 12 01:37:17 2019
New Revision: 1866816

URL: http://svn.apache.org/viewvc?rev=1866816&view=rev
Log:
OAK-8524: Add CDN support for AzureDataStore.

This includes the addition of two new configuration options to
support CDNs, and documentation in oak-doc coverig it.

Added:
    
jackrabbit/oak/trunk/oak-blob-cloud-azure/src/test/java/org/apache/jackrabbit/oak/blob/cloud/azure/blobstorage/AzureDataRecordAccessProviderCDNTest.java
Modified:
    
jackrabbit/oak/trunk/oak-blob-cloud-azure/src/main/java/org/apache/jackrabbit/oak/blob/cloud/azure/blobstorage/AzureBlobStoreBackend.java
    
jackrabbit/oak/trunk/oak-blob-cloud-azure/src/main/java/org/apache/jackrabbit/oak/blob/cloud/azure/blobstorage/AzureConstants.java
    
jackrabbit/oak/trunk/oak-blob-cloud-azure/src/test/java/org/apache/jackrabbit/oak/blob/cloud/azure/blobstorage/AzureDataRecordAccessProviderTest.java
    
jackrabbit/oak/trunk/oak-blob-cloud-azure/src/test/java/org/apache/jackrabbit/oak/blob/cloud/azure/blobstorage/AzureDataStoreUtils.java
    
jackrabbit/oak/trunk/oak-doc/src/site/markdown/features/direct-binary-access.md

Modified: 
jackrabbit/oak/trunk/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/trunk/oak-blob-cloud-azure/src/main/java/org/apache/jackrabbit/oak/blob/cloud/azure/blobstorage/AzureBlobStoreBackend.java?rev=1866816&r1=1866815&r2=1866816&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-blob-cloud-azure/src/main/java/org/apache/jackrabbit/oak/blob/cloud/azure/blobstorage/AzureBlobStoreBackend.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-blob-cloud-azure/src/main/java/org/apache/jackrabbit/oak/blob/cloud/azure/blobstorage/AzureBlobStoreBackend.java
 Thu Sep 12 01:37:17 2019
@@ -818,10 +818,16 @@ public class AzureBlobStoreBackend exten
                     headers.setContentDisposition(contentDisposition);
                 }
 
+                String domain = getDirectDownloadBlobStorageDomain();
+                if (null == domain) {
+                    throw new NullPointerException("Could not determine domain 
for direct download");
+                }
+
                 uri = createPresignedURI(key,
                         EnumSet.of(SharedAccessBlobPermissions.READ),
                         httpDownloadURIExpirySeconds,
-                        headers);
+                        headers,
+                        domain);
                 if (uri != null && httpDownloadURICache != null) {
                     httpDownloadURICache.put(identifier, uri);
                 }
@@ -917,13 +923,24 @@ public class AzureBlobStoreBackend exten
             }
 
             String key = getKeyName(newIdentifier);
+            String domain = getDirectUploadBlobStorageDomain();
+            if (null == domain) {
+                throw new NullPointerException("Could not determine domain for 
direct upload");
+            }
+
             EnumSet<SharedAccessBlobPermissions> perms = 
EnumSet.of(SharedAccessBlobPermissions.WRITE);
             Map<String, String> presignedURIRequestParams = Maps.newHashMap();
             presignedURIRequestParams.put("comp", "block");
             for (long blockId = 1; blockId <= numParts; ++blockId) {
                 presignedURIRequestParams.put("blockId",
                         Base64.encode(String.format("%06d", blockId)));
-                uploadPartURIs.add(createPresignedURI(key, perms, 
httpUploadURIExpirySeconds, presignedURIRequestParams));
+                uploadPartURIs.add(
+                        createPresignedURI(key,
+                                perms,
+                                httpUploadURIExpirySeconds,
+                                presignedURIRequestParams,
+                                domain)
+                );
             }
 
             try {
@@ -1021,33 +1038,60 @@ public class AzureBlobStoreBackend exten
         return record;
     }
 
+    private String getDefaultBlobStorageDomain() {
+        String accountName = 
properties.getProperty(AzureConstants.AZURE_STORAGE_ACCOUNT_NAME, "");
+        if (Strings.isNullOrEmpty(accountName)) {
+            LOG.warn("Can't generate presigned URI - Azure account name not 
found in properties");
+            return null;
+        }
+        return String.format("%s.blob.core.windows.net", accountName);
+    }
+
+    private String getDirectDownloadBlobStorageDomain() {
+        String domain = 
properties.getProperty(AzureConstants.PRESIGNED_HTTP_DOWNLOAD_URI_DOMAIN_OVERRIDE,
 null);
+        if (Strings.isNullOrEmpty(domain)) {
+            domain = getDefaultBlobStorageDomain();
+        }
+        return domain;
+    }
+
+    private String getDirectUploadBlobStorageDomain() {
+        String domain = 
properties.getProperty(AzureConstants.PRESIGNED_HTTP_UPLOAD_URI_DOMAIN_OVERRIDE,
 null);
+        if (null == domain) {
+            getDefaultBlobStorageDomain();
+        }
+        return domain;
+    }
+
     private URI createPresignedURI(String key,
                                    EnumSet<SharedAccessBlobPermissions> 
permissions,
                                    int expirySeconds,
-                                   SharedAccessBlobHeaders optionalHeaders) {
-        return createPresignedURI(key, permissions, expirySeconds, 
Maps.newHashMap(), optionalHeaders);
+                                   SharedAccessBlobHeaders optionalHeaders,
+                                   String domain) {
+        return createPresignedURI(key, permissions, expirySeconds, 
Maps.newHashMap(), optionalHeaders, domain);
     }
 
     private URI createPresignedURI(String key,
                                    EnumSet<SharedAccessBlobPermissions> 
permissions,
                                    int expirySeconds,
-                                   Map<String, String> additionalQueryParams) {
-        return createPresignedURI(key, permissions, expirySeconds, 
additionalQueryParams, null);
+                                   Map<String, String> additionalQueryParams,
+                                   String domain) {
+        return createPresignedURI(key, permissions, expirySeconds, 
additionalQueryParams, null, domain);
     }
 
     private URI createPresignedURI(String key,
                                    EnumSet<SharedAccessBlobPermissions> 
permissions,
                                    int expirySeconds,
                                    Map<String, String> additionalQueryParams,
-                                   SharedAccessBlobHeaders optionalHeaders) {
+                                   SharedAccessBlobHeaders optionalHeaders,
+                                   String domain) {
         SharedAccessBlobPolicy policy = new SharedAccessBlobPolicy();
         Date expiry = Date.from(Instant.now().plusSeconds(expirySeconds));
         policy.setSharedAccessExpiryTime(expiry);
         policy.setPermissions(permissions);
 
-        String accountName = 
properties.getProperty(AzureConstants.AZURE_STORAGE_ACCOUNT_NAME, "");
-        if (Strings.isNullOrEmpty(accountName)) {
-            LOG.warn("Can't generate presigned URI - Azure account name not 
found in properties");
+        if (Strings.isNullOrEmpty(domain)) {
+            LOG.warn("Can't generate presigned URI - no Azure domain provided 
(is Azure account name configured?)");
             return null;
         }
 
@@ -1063,8 +1107,8 @@ public class AzureBlobStoreBackend exten
                                     null);
             // Shared access signature is returned encoded already.
 
-            String uriString = 
String.format("https://%s.blob.core.windows.net/%s/%s?%s";,
-                    accountName,
+            String uriString = String.format("https://%s/%s/%s?%s";,
+                    domain,
                     containerName,
                     key,
                     sharedAccessSignature);

Modified: 
jackrabbit/oak/trunk/oak-blob-cloud-azure/src/main/java/org/apache/jackrabbit/oak/blob/cloud/azure/blobstorage/AzureConstants.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-blob-cloud-azure/src/main/java/org/apache/jackrabbit/oak/blob/cloud/azure/blobstorage/AzureConstants.java?rev=1866816&r1=1866815&r2=1866816&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-blob-cloud-azure/src/main/java/org/apache/jackrabbit/oak/blob/cloud/azure/blobstorage/AzureConstants.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-blob-cloud-azure/src/main/java/org/apache/jackrabbit/oak/blob/cloud/azure/blobstorage/AzureConstants.java
 Thu Sep 12 01:37:17 2019
@@ -107,5 +107,17 @@ public final class AzureConstants {
      */
     public static final String PRESIGNED_HTTP_DOWNLOAD_URI_VERIFY_EXISTS = 
"presignedHttpDownloadURIVerifyExists";
 
+    /**
+     * Domain name to use for direct downloads instead of the default Azure 
blob storage domain.
+     * This is usually used when an installation has configured a CDN domain 
for binary downloads.
+     */
+    public static final String PRESIGNED_HTTP_DOWNLOAD_URI_DOMAIN_OVERRIDE = 
"presignedHttpDownloadURIDomainOverride";
+
+    /**
+     * Domain name to use for direct uploads instead of the default Azure blob 
storage domain.
+     * This is usually used when an installation has configured a CDN domain 
for binary uploads.
+     */
+    public static final String PRESIGNED_HTTP_UPLOAD_URI_DOMAIN_OVERRIDE = 
"presignedHttpUploadURIDomainOverride";
+
     private AzureConstants() { }
 }

Added: 
jackrabbit/oak/trunk/oak-blob-cloud-azure/src/test/java/org/apache/jackrabbit/oak/blob/cloud/azure/blobstorage/AzureDataRecordAccessProviderCDNTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-blob-cloud-azure/src/test/java/org/apache/jackrabbit/oak/blob/cloud/azure/blobstorage/AzureDataRecordAccessProviderCDNTest.java?rev=1866816&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-blob-cloud-azure/src/test/java/org/apache/jackrabbit/oak/blob/cloud/azure/blobstorage/AzureDataRecordAccessProviderCDNTest.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-blob-cloud-azure/src/test/java/org/apache/jackrabbit/oak/blob/cloud/azure/blobstorage/AzureDataRecordAccessProviderCDNTest.java
 Thu Sep 12 01:37:17 2019
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.jackrabbit.oak.blob.cloud.azure.blobstorage;
+
+import static 
org.apache.jackrabbit.oak.blob.cloud.azure.blobstorage.AzureConstants.PRESIGNED_HTTP_DOWNLOAD_URI_DOMAIN_OVERRIDE;
+import static 
org.apache.jackrabbit.oak.blob.cloud.azure.blobstorage.AzureConstants.PRESIGNED_HTTP_DOWNLOAD_URI_VERIFY_EXISTS;
+import static 
org.apache.jackrabbit.oak.blob.cloud.azure.blobstorage.AzureConstants.PRESIGNED_HTTP_UPLOAD_URI_DOMAIN_OVERRIDE;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import java.io.File;
+import java.net.URI;
+import java.util.Properties;
+
+import com.google.common.base.Strings;
+import org.apache.jackrabbit.core.data.DataIdentifier;
+import 
org.apache.jackrabbit.oak.plugins.blob.datastore.directaccess.ConfigurableDataRecordAccessProvider;
+import 
org.apache.jackrabbit.oak.plugins.blob.datastore.directaccess.DataRecordDownloadOptions;
+import 
org.apache.jackrabbit.oak.plugins.blob.datastore.directaccess.DataRecordUpload;
+import org.jetbrains.annotations.NotNull;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+public class AzureDataRecordAccessProviderCDNTest extends 
AzureDataRecordAccessProviderTest {
+    @ClassRule
+    public static TemporaryFolder homeDir = new TemporaryFolder(new 
File("target"));
+
+    private static AzureDataStore cdnDataStore;
+
+    private static String DOWNLOAD_URI_DOMAIN = AzureDataStoreUtils
+            .getDirectAccessDataStoreProperties()
+            .getProperty(PRESIGNED_HTTP_DOWNLOAD_URI_DOMAIN_OVERRIDE, null);
+    private static String UPLOAD_URI_DOMAIN = AzureDataStoreUtils
+            .getDirectAccessDataStoreProperties()
+            .getProperty(PRESIGNED_HTTP_UPLOAD_URI_DOMAIN_OVERRIDE, null);
+
+    private static String cdnSetupNotice = String.format(
+            "%s\n%s %s '%s' %s '%s' %s %s",
+            "No override domains configured - skipping Azure CDN tests.",
+            "To run these tests, set up an Azure CDN in the Azure console or 
command line,",
+            "then set the CDN domain as the property value for",
+            PRESIGNED_HTTP_DOWNLOAD_URI_DOMAIN_OVERRIDE,
+            "and/or",
+            PRESIGNED_HTTP_UPLOAD_URI_DOMAIN_OVERRIDE,
+            "in your Azure configuration file, and then provide this file to 
the",
+            "test via the -Dazure.config command-line switch"
+    );
+
+    @BeforeClass
+    public static void setupDataStore() throws Exception {
+        assumeTrue(cdnSetupNotice, isCDNConfigured());
+        cdnDataStore = AzureDataStoreUtils.setupDirectAccessDataStore(homeDir,
+                expirySeconds, expirySeconds);
+    }
+
+    private static boolean isCDNConfigured() {
+        return ! Strings.isNullOrEmpty(DOWNLOAD_URI_DOMAIN) && ! 
Strings.isNullOrEmpty(UPLOAD_URI_DOMAIN);
+    }
+
+    private static AzureDataStore createDataStore(@NotNull Properties 
properties) throws Exception {
+        return AzureDataStoreUtils.setupDirectAccessDataStore(homeDir, 
expirySeconds, expirySeconds, properties);
+    }
+
+    @Override
+    protected ConfigurableDataRecordAccessProvider getDataStore() {
+        return cdnDataStore;
+    }
+
+    @Override
+    protected ConfigurableDataRecordAccessProvider getDataStore(@NotNull 
Properties overrideProperties) throws Exception {
+        return 
createDataStore(AzureDataStoreUtils.getDirectAccessDataStoreProperties(overrideProperties));
+    }
+
+    // CDN Tests
+    @Test
+    public void testCDNDownloadURIContainsDownloadDomain() throws Exception {
+        Properties properties = new Properties();
+        properties.put(PRESIGNED_HTTP_DOWNLOAD_URI_VERIFY_EXISTS, "false");
+        ConfigurableDataRecordAccessProvider ds = getDataStore(properties);
+        DataIdentifier id = new DataIdentifier("identifier");
+        URI downloadUri = ds.getDownloadURI(id, 
DataRecordDownloadOptions.DEFAULT);
+        assertNotNull(downloadUri);
+        assertEquals(DOWNLOAD_URI_DOMAIN, downloadUri.getHost());
+    }
+
+    @Test
+    public void testCDNUploadURIContainsUploadDomain() throws Exception {
+        Properties properties = new Properties();
+        properties.put(PRESIGNED_HTTP_DOWNLOAD_URI_VERIFY_EXISTS, "false");
+        ConfigurableDataRecordAccessProvider ds = getDataStore(properties);
+        DataRecordUpload upload = ds.initiateDataRecordUpload(ONE_MB, 10);
+        assertNotNull(upload);
+        assertTrue(upload.getUploadURIs().size() > 0);
+        for (URI uri : upload.getUploadURIs()) {
+            assertEquals(UPLOAD_URI_DOMAIN, uri.getHost());
+        }
+    }
+}

Modified: 
jackrabbit/oak/trunk/oak-blob-cloud-azure/src/test/java/org/apache/jackrabbit/oak/blob/cloud/azure/blobstorage/AzureDataRecordAccessProviderTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-blob-cloud-azure/src/test/java/org/apache/jackrabbit/oak/blob/cloud/azure/blobstorage/AzureDataRecordAccessProviderTest.java?rev=1866816&r1=1866815&r2=1866816&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-blob-cloud-azure/src/test/java/org/apache/jackrabbit/oak/blob/cloud/azure/blobstorage/AzureDataRecordAccessProviderTest.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-blob-cloud-azure/src/test/java/org/apache/jackrabbit/oak/blob/cloud/azure/blobstorage/AzureDataRecordAccessProviderTest.java
 Thu Sep 12 01:37:17 2019
@@ -19,7 +19,6 @@
 package org.apache.jackrabbit.oak.blob.cloud.azure.blobstorage;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assume.assumeTrue;
 
 import java.io.File;
 import java.io.IOException;
@@ -55,25 +54,11 @@ public class AzureDataRecordAccessProvid
 
     @BeforeClass
     public static void setupDataStore() throws Exception {
-        assumeTrue(AzureDataStoreUtils.isAzureConfigured());
-        dataStore = (AzureDataStore) AzureDataStoreUtils
-            .getAzureDataStore(getProperties(), 
homeDir.newFolder().getAbsolutePath());
-        dataStore.setDirectDownloadURIExpirySeconds(expirySeconds);
-        dataStore.setDirectUploadURIExpirySeconds(expirySeconds);
-    }
-
-    private static Properties getProperties() {
-        Properties props = AzureDataStoreUtils.getAzureConfig();
-        props.setProperty("cacheSize", "0");
-        return props;
+        dataStore = AzureDataStoreUtils.setupDirectAccessDataStore(homeDir, 
expirySeconds, expirySeconds);
     }
 
     private static AzureDataStore createDataStore(@NotNull Properties 
properties) throws Exception {
-        AzureDataStore ds = (AzureDataStore) AzureDataStoreUtils
-                .getAzureDataStore(properties, 
homeDir.newFolder().getAbsolutePath());
-        ds.setDirectDownloadURIExpirySeconds(expirySeconds);
-        ds.setDirectUploadURIExpirySeconds(expirySeconds);
-        return ds;
+        return AzureDataStoreUtils.setupDirectAccessDataStore(homeDir, 
expirySeconds, expirySeconds, properties);
     }
 
     @Override
@@ -83,10 +68,7 @@ public class AzureDataRecordAccessProvid
 
     @Override
     protected ConfigurableDataRecordAccessProvider getDataStore(@NotNull 
Properties overrideProperties) throws Exception {
-        Properties mergedProperties = new Properties();
-        mergedProperties.putAll(getProperties());
-        mergedProperties.putAll(overrideProperties);
-        return createDataStore(mergedProperties);
+        return 
createDataStore(AzureDataStoreUtils.getDirectAccessDataStoreProperties(overrideProperties));
     }
 
     @Override

Modified: 
jackrabbit/oak/trunk/oak-blob-cloud-azure/src/test/java/org/apache/jackrabbit/oak/blob/cloud/azure/blobstorage/AzureDataStoreUtils.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-blob-cloud-azure/src/test/java/org/apache/jackrabbit/oak/blob/cloud/azure/blobstorage/AzureDataStoreUtils.java?rev=1866816&r1=1866815&r2=1866816&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-blob-cloud-azure/src/test/java/org/apache/jackrabbit/oak/blob/cloud/azure/blobstorage/AzureDataStoreUtils.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-blob-cloud-azure/src/test/java/org/apache/jackrabbit/oak/blob/cloud/azure/blobstorage/AzureDataStoreUtils.java
 Thu Sep 12 01:37:17 2019
@@ -18,13 +18,14 @@
  */
 package org.apache.jackrabbit.oak.blob.cloud.azure.blobstorage;
 
+import static com.google.common.base.StandardSystemProperty.USER_HOME;
+import static org.junit.Assume.assumeTrue;
+
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.URI;
-import java.nio.file.Files;
-import java.nio.file.Paths;
 import java.time.Instant;
 import java.time.ZoneOffset;
 import java.time.format.DateTimeFormatter;
@@ -34,7 +35,6 @@ import java.util.Properties;
 import javax.net.ssl.HttpsURLConnection;
 
 import com.google.common.base.Predicate;
-import com.google.common.base.StandardSystemProperty;
 import com.google.common.base.Strings;
 import com.google.common.collect.Maps;
 import com.microsoft.azure.storage.blob.CloudBlobContainer;
@@ -42,11 +42,13 @@ import org.apache.commons.io.IOUtils;
 import org.apache.jackrabbit.core.data.DataStore;
 import org.apache.jackrabbit.oak.commons.PropertiesUtil;
 import org.apache.jackrabbit.oak.plugins.blob.datastore.DataStoreUtils;
+import 
org.apache.jackrabbit.oak.plugins.blob.datastore.directaccess.ConfigurableDataRecordAccessProvider;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.junit.rules.TemporaryFolder;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import static com.google.common.base.StandardSystemProperty.USER_HOME;
-
 /**
  * Extension to {@link DataStoreUtils} to enable Azure extensions for cleaning 
and initialization.
  */
@@ -126,6 +128,48 @@ public class AzureDataStoreUtils extends
         return ds;
     }
 
+    public static <T extends DataStore> T setupDirectAccessDataStore(
+            @NotNull final TemporaryFolder homeDir,
+            int directDownloadExpirySeconds,
+            int directUploadExpirySeconds)
+            throws Exception {
+        return setupDirectAccessDataStore(homeDir, 
directDownloadExpirySeconds, directUploadExpirySeconds, null);
+    }
+
+    @SuppressWarnings("unchecked")
+    public static <T extends DataStore> T setupDirectAccessDataStore(
+            @NotNull final TemporaryFolder homeDir,
+            int directDownloadExpirySeconds,
+            int directUploadExpirySeconds,
+            @Nullable final Properties overrideProperties)
+            throws Exception {
+        assumeTrue(isAzureConfigured());
+        DataStore ds = (T) 
getAzureDataStore(getDirectAccessDataStoreProperties(overrideProperties), 
homeDir.newFolder().getAbsolutePath());
+        if (ds instanceof ConfigurableDataRecordAccessProvider) {
+            ((ConfigurableDataRecordAccessProvider) 
ds).setDirectDownloadURIExpirySeconds(directDownloadExpirySeconds);
+            ((ConfigurableDataRecordAccessProvider) 
ds).setDirectUploadURIExpirySeconds(directUploadExpirySeconds);
+        }
+        return (T) ds;
+    }
+
+    public static Properties getDirectAccessDataStoreProperties() {
+        return getDirectAccessDataStoreProperties(null);
+    }
+
+    public static Properties getDirectAccessDataStoreProperties(@Nullable 
final Properties overrideProperties) {
+        Properties mergedProperties = new Properties();
+        mergedProperties.putAll(getAzureConfig());
+        if (null != overrideProperties) {
+            mergedProperties.putAll(overrideProperties);
+        }
+
+        // set properties needed for direct access testing
+        if (null == mergedProperties.getProperty("cacheSize", null)) {
+            mergedProperties.put("cacheSize", "0");
+        }
+        return mergedProperties;
+    }
+
     public static void deleteContainer(String containerName) throws Exception {
         if (Strings.isNullOrEmpty(containerName)) {
             log.warn("Cannot delete container with null or empty name. 
containerName={}", containerName);

Modified: 
jackrabbit/oak/trunk/oak-doc/src/site/markdown/features/direct-binary-access.md
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-doc/src/site/markdown/features/direct-binary-access.md?rev=1866816&r1=1866815&r2=1866816&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-doc/src/site/markdown/features/direct-binary-access.md 
(original)
+++ 
jackrabbit/oak/trunk/oak-doc/src/site/markdown/features/direct-binary-access.md 
Thu Sep 12 01:37:17 2019
@@ -14,7 +14,7 @@
    See the License for the specific language governing permissions and
    limitations under the License.
   -->
-  
+
 # Direct Binary Access
 
 `@since Oak 1.10`
@@ -32,7 +32,7 @@ The following diagram shows the 3 involv
 ![](direct-binary-access-block-diagram.png)
 
 Further background of the design of this feature can be found [on the 
wiki](https://jackrabbit.apache.org/archive/wiki/JCR/Direct-Binary-Access_115513390.html).
- 
+
 ## Requirements
 
 To use this feature, Oak must be configured with a 
[BlobStore](../plugins/blobstore.html) that supports this feature.
@@ -78,7 +78,7 @@ Binary binary = ntResource.getProperty("
 
 if (binary instanceof BinaryDownload) {
     BinaryDownload binaryDownload = (BinaryDownload) binary;
-    
+
     BinaryDownloadOptions.BinaryDownloadOptionsBuilder builder = 
BinaryDownloadOptions.builder()
         // would typically come from a JCR node name
         .withFileName(ntFile.getName())
@@ -88,18 +88,18 @@ if (binary instanceof BinaryDownload) {
     if (ntResource.hasProperty("jcr:encoding")) {
         builder.withCharacterEncoding(ntResource.getProperty("jcr:encoding"));
     }
-    
+
     // if you need to prevent the browser from potentially executing the 
response
     // (for example js, flash, html), you can enforce a download with this 
option
     // builder.withDispositionTypeAttachment();
-        
+
     URI uri = binaryDownload.getURI(builder.build());
-    
+
     if (uri == null) {
         // feature not available
         // ...
     }
-    
+
     // use uri in <img src="uri"> or send in response to remote client
     // ...
 }
@@ -138,22 +138,22 @@ public class InitiateUploadServlet exten
 
    public void doPost(HttpServletRequest request, HttpServletResponse response)
                throws IOException, ServletException {
-               
+
         final Session session = // .. retrieve session for request
 
         // allows to limit number of returned URIs in case the response 
message size is limited
         // use -1 for unlimited
         final int maxURIs = 50;
-        
+
         final String path = request.getParameter("path");
         final long filesize = Long.parseLong(request.getParameter("filesize"));
 
         ValueFactory vf = session.getValueFactory();
         if (vf instanceof JackrabbitValueFactory) {
             JackrabbitValueFactory valueFactory = (JackrabbitValueFactory) vf;
-            
+
             BinaryUpload upload = valueFactory.initiateBinaryUpload(filesize, 
maxURIs);
-            
+
             if (upload == null) {
                 // feature not available, must pass binary via InputStream 
through vf.createBinary()
                 // ...
@@ -161,7 +161,7 @@ public class InitiateUploadServlet exten
                 JSONObject json = new JSONObject();
                 json.put("minPartSize", upload.getMinPartSize());
                 json.put("maxPartSize", upload.getMaxPartSize());
-                
+
                 JSONArray uris = new JSONArray();
                 Iterator<URI> iter = upload.getUploadURIs();
                 while (iter.hasNext()) {
@@ -171,7 +171,7 @@ public class InitiateUploadServlet exten
 
                 // provide the client with a complete URL to request later, 
pass through the path
                 json.put("completeURL", "/complete-upload?uploadToken=" + 
upload.getUploadToken() + "&path=" + path);
-                
+
                 response.setContentType("application/json");
                 response.setCharacterEncoding("UTF-8");
                 response.getWriter().write(json.toString());
@@ -206,27 +206,27 @@ public class CompleteUploadServlet exten
 
    public void doPost(HttpServletRequest request, HttpServletResponse response)
                throws IOException, ServletException {
-               
+
         final Session session = // .. retrieve session for request
-               
+
         final String path = request.getParameter("path");
         final String uploadToken = request.getParameter("uploadToken");
 
         ValueFactory vf = session.getValueFactory();
         if (vf instanceof JackrabbitValueFactory) {
             JackrabbitValueFactory valueFactory = (JackrabbitValueFactory) vf;
-            
+
             Binary binary = valueFactory.completeBinaryUpload(uploadToken);
-            
+
             Node ntFile = JcrUtils.getOrCreateByPath(path, "nt:file", session);
             Node ntResource = ntFile.addNode("jcr:content", "nt:resource");
-            
+
             ntResource.setProperty("jcr:data", binary);
-            
+
             // also set jcr:mimeType etc.
-            
+
             session.save();
-            
+
         } else {
             // feature not available - not unexpected if initiate-upload worked
         }
@@ -234,3 +234,33 @@ public class CompleteUploadServlet exten
 }
 ```
 
+# CDN Support
+
+`@since Oak 1.18 (AzureDataStore)`
+
+Oak can be configured to make use of CDNs if desired.  Configuring a CDN for 
use with Oak can provide clients with accelerated blob access times as blobs 
are accessed via more local caches instead of from the origin blob store.
+
+## Preconditions
+
+The following conditions must be true to leverage a CDN:
+
+* You must be using `AzureDataStore`.  (`S3DataStore` will be supported at a 
future date but is not currently supported.)
+* You must have Direct Binary Access enabled - CDNs only offer a benefit with 
direct access URIs.
+* You must have a CDN configured that uses your cloud blob storage container 
as the origin.
+
+## Configuration
+
+Add one or both of the following configuration options to the data store 
configuration file:
+
+| Property | Type | Default | Description |
+|----------|------|---------|-------------|
+| `presignedHttpDownloadURIDomainOverride`   | String  | null     | When this 
property is set, the domain provided will be used for direct download URIs 
instead of the default direct download domain. |
+| `presignedHttpUploadURIDomainOverride` | String  | null     | When this 
property is set, the domain provided will be used for direct upload URIs 
instead of the default direct upload domain. |
+
+When set, the property value should be a valid fully-qualified domain name, 
e.g. "mycdndomain.azureedge.net".
+
+## Uses
+
+CDNs may be used for direct upload as well as direct download, if the CDN in 
question supports such behavior.  CDNs that support this behavior include AWS 
CloudFront, all Azure CDN offerings, and some other third-party CDNs do as 
well; however, these capabilities are the responsibility of the service 
providers, not Oak.  Check with your CDN provider for authoritative information 
on suitability; comprehensive testing is recommended.
+
+Note that you are not required to configure both domains, nor is it required 
that both domains be the same.  For example, if one CDN offers the best 
download performance and another CDN offers the best upload performance, you 
may choose to implement both and set each configuration parameter to a 
different domain.  Likewise, you are not required to set them both.  If you 
only wish to use CDNs for download but not upload, simply configure the 
download parameter with the CDN domain and don't configure an upload domain.


Reply via email to