← Back to team overview

dhis2-devs team mailing list archive

[Branch ~dhis2-devs-core/dhis2/trunk] Rev 20543: File store: where applicable (external BlobStore such as AWS S3) GET to dataValues/files now retu...

 

------------------------------------------------------------
revno: 20543
committer: Halvdan Hoem Grelland <halvdanhg@xxxxxxxxx>
branch nick: dhis2
timestamp: Wed 2015-10-07 00:00:49 +0200
message:
  File store: where applicable (external BlobStore such as AWS S3) GET to dataValues/files now returns a redirect with a signed, time limited GET request directly to the external store. Greatly reduces server load and latency when using a third party storage provider. The signed requests are statically limited to a 5 minute life span.
modified:
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/fileresource/FileResourceContentStore.java
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/fileresource/FileResourceService.java
  dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/fileresource/DefaultFileResourceService.java
  dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/fileresource/JCloudsFileResourceContentStore.java
  dhis-2/dhis-web/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/DataValueController.java


--
lp:dhis2
https://code.launchpad.net/~dhis2-devs-core/dhis2/trunk

Your team DHIS 2 developers is subscribed to branch lp:dhis2.
To unsubscribe from this branch go to https://code.launchpad.net/~dhis2-devs-core/dhis2/trunk/+edit-subscription
=== modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/fileresource/FileResourceContentStore.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/fileresource/FileResourceContentStore.java	2015-09-21 14:33:06 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/fileresource/FileResourceContentStore.java	2015-10-06 22:00:49 +0000
@@ -30,6 +30,8 @@
 
 import com.google.common.io.ByteSource;
 
+import java.net.URI;
+
 /**
  * @author Halvdan Hoem Grelland
  */
@@ -57,4 +59,11 @@
      * @param key the key.
      */
     void deleteFileResourceContent( String key );
+
+    /**
+     * Create a signed GET request which gives access to the content.
+     * @param key the key.
+     * @return a URI containing the signed GET request or null if signed requests are not supported.
+     */
+    URI getSignedGetContentUri( String key );
 }

=== modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/fileresource/FileResourceService.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/fileresource/FileResourceService.java	2015-10-02 22:42:59 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/fileresource/FileResourceService.java	2015-10-06 22:00:49 +0000
@@ -30,6 +30,7 @@
 
 import com.google.common.io.ByteSource;
 
+import java.net.URI;
 import java.util.List;
 
 /**
@@ -50,4 +51,6 @@
     boolean fileResourceExists( String uid );
     
     void updateFileResource( FileResource fileResource );
+
+    URI getSignedGetFileResourceContentUri( String uid );
 }

=== modified file 'dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/fileresource/DefaultFileResourceService.java'
--- dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/fileresource/DefaultFileResourceService.java	2015-10-02 22:42:59 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/fileresource/DefaultFileResourceService.java	2015-10-06 22:00:49 +0000
@@ -36,6 +36,7 @@
 
 import com.google.common.io.ByteSource;
 
+import java.net.URI;
 import java.util.List;
 
 /**
@@ -144,6 +145,19 @@
         fileResourceStore.update( fileResource );
     }
 
+    @Override
+    public URI getSignedGetFileResourceContentUri( String uid )
+    {
+        FileResource fileResource = getFileResource( uid );
+
+        if ( fileResource == null )
+        {
+            return null;
+        }
+
+        return fileResourceContentStore.getSignedGetContentUri( getRelativeStorageKey( fileResource ) );
+    }
+
     // ---------------------------------------------------------------------
     // Supportive methods
     // ---------------------------------------------------------------------

=== modified file 'dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/fileresource/JCloudsFileResourceContentStore.java'
--- dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/fileresource/JCloudsFileResourceContentStore.java	2015-10-01 09:45:57 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/fileresource/JCloudsFileResourceContentStore.java	2015-10-06 22:00:49 +0000
@@ -37,15 +37,21 @@
 import org.hisp.dhis.external.location.LocationManager;
 import org.hisp.dhis.hibernate.HibernateConfigurationProvider;
 import org.jclouds.ContextBuilder;
+import org.jclouds.blobstore.BlobRequestSigner;
 import org.jclouds.blobstore.BlobStore;
 import org.jclouds.blobstore.BlobStoreContext;
+import org.jclouds.blobstore.LocalBlobRequestSigner;
 import org.jclouds.blobstore.domain.Blob;
+import org.jclouds.blobstore.internal.RequestSigningUnsupported;
 import org.jclouds.domain.Credentials;
 import org.jclouds.domain.Location;
 import org.jclouds.filesystem.reference.FilesystemConstants;
+import org.jclouds.http.HttpRequest;
+import org.joda.time.Minutes;
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.net.URI;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -65,6 +71,8 @@
 
     private static final Pattern CONTAINER_NAME_PATTERN = Pattern.compile( "^((?!-)[a-zA-Z0-9-]{1,63}(?<!-))+$" );
 
+    private static final long FIVE_MINUTES_IN_SECONDS = Minutes.minutes( 5 ).toStandardDuration().getStandardSeconds();
+
     private BlobStore blobStore;
     private BlobStoreContext blobStoreContext;
     private String container;
@@ -202,6 +210,7 @@
     // FileResourceContentStore implementation
     // -------------------------------------------------------------------------
 
+    @Override
     public ByteSource getFileResourceContent( String key )
     {
         final Blob blob = getBlob( key );
@@ -241,6 +250,7 @@
         return isEmptyOrFailed ? null : byteSource;
     }
 
+    @Override
     public String saveFileResourceContent( String key, ByteSource content, long size, String contentMd5 )
     {
         Blob blob = createBlob( key, content, size, contentMd5 );
@@ -255,11 +265,36 @@
         return key;
     }
 
+    @Override
     public void deleteFileResourceContent( String key )
     {
         deleteBlob( key );
     }
 
+    @Override
+    public URI getSignedGetContentUri( String key )
+    {
+        BlobRequestSigner signer = blobStoreContext.getSigner();
+
+        if ( !requestSigningSupported( signer ) )
+        {
+            return null;
+        }
+
+        HttpRequest httpRequest = null;
+
+        try
+        {
+            httpRequest = signer.signGetBlob( container, key, FIVE_MINUTES_IN_SECONDS );
+        }
+        catch ( UnsupportedOperationException uoe )
+        {
+            return null;
+        }
+
+        return httpRequest.getEndpoint();
+    }
+
     // -------------------------------------------------------------------------
     // Supportive methods
     // -------------------------------------------------------------------------
@@ -320,4 +355,9 @@
     {
         return containerName != null && CONTAINER_NAME_PATTERN.matcher( containerName ).matches();
     }
+
+    private boolean requestSigningSupported( BlobRequestSigner signer )
+    {
+        return !( signer instanceof RequestSigningUnsupported ) && !( signer instanceof LocalBlobRequestSigner );
+    }
 }

=== modified file 'dhis-2/dhis-web/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/DataValueController.java'
--- dhis-2/dhis-web/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/DataValueController.java	2015-10-06 19:48:48 +0000
+++ dhis-2/dhis-web/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/DataValueController.java	2015-10-06 22:00:49 +0000
@@ -30,6 +30,7 @@
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.net.URI;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
@@ -470,6 +471,23 @@
         response.setContentLength( Math.round( fileResource.getContentLength() ) );
         response.setHeader( HttpHeaders.CONTENT_DISPOSITION, "filename=" + fileResource.getName() );
 
+        // ---------------------------------------------------------------------
+        // Attempt to build signed URL request for content and redirect
+        // ---------------------------------------------------------------------
+
+        URI signedGetUri = fileResourceService.getSignedGetFileResourceContentUri( uid );
+
+        if ( signedGetUri != null )
+        {
+            response.setStatus( HttpServletResponse.SC_FOUND );
+            response.setHeader( HttpHeaders.LOCATION, signedGetUri.toASCIIString() );
+            return;
+        }
+
+        // ---------------------------------------------------------------------
+        // Request signing is not available, stream content back to client
+        // ---------------------------------------------------------------------
+
         InputStream inputStream = null;
 
         try