org.finra.herd.service.impl.UploadDownloadServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.finra.herd.service.impl.UploadDownloadServiceImpl.java

Source

/*
* Copyright 2015 herd contributors
*
* Licensed 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.finra.herd.service.impl;

import java.util.Date;
import java.util.UUID;

import com.amazonaws.auth.policy.Policy;
import com.amazonaws.auth.policy.actions.S3Actions;
import com.amazonaws.services.securitytoken.model.Credentials;
import org.apache.commons.collections4.IterableUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import org.finra.herd.core.HerdDateUtils;
import org.finra.herd.core.helper.ConfigurationHelper;
import org.finra.herd.dao.S3Dao;
import org.finra.herd.dao.StsDao;
import org.finra.herd.dao.config.DaoSpringModuleConfig;
import org.finra.herd.dao.helper.AwsHelper;
import org.finra.herd.dao.helper.JsonHelper;
import org.finra.herd.model.ObjectNotFoundException;
import org.finra.herd.model.annotation.NamespacePermission;
import org.finra.herd.model.annotation.PublishNotificationMessages;
import org.finra.herd.model.api.xml.BusinessObjectData;
import org.finra.herd.model.api.xml.BusinessObjectDataCreateRequest;
import org.finra.herd.model.api.xml.BusinessObjectDataKey;
import org.finra.herd.model.api.xml.BusinessObjectDataStorageFileKey;
import org.finra.herd.model.api.xml.BusinessObjectDefinitionKey;
import org.finra.herd.model.api.xml.BusinessObjectDefinitionSampleDataFileKey;
import org.finra.herd.model.api.xml.BusinessObjectFormat;
import org.finra.herd.model.api.xml.DownloadBusinessObjectDataStorageFileSingleInitiationRequest;
import org.finra.herd.model.api.xml.DownloadBusinessObjectDataStorageFileSingleInitiationResponse;
import org.finra.herd.model.api.xml.DownloadBusinessObjectDefinitionSampleDataFileSingleInitiationRequest;
import org.finra.herd.model.api.xml.DownloadBusinessObjectDefinitionSampleDataFileSingleInitiationResponse;
import org.finra.herd.model.api.xml.DownloadSingleInitiationResponse;
import org.finra.herd.model.api.xml.NamespacePermissionEnum;
import org.finra.herd.model.api.xml.UploadBusinessObjectDefinitionSampleDataFileInitiationRequest;
import org.finra.herd.model.api.xml.UploadBusinessObjectDefinitionSampleDataFileInitiationResponse;
import org.finra.herd.model.api.xml.UploadSingleCredentialExtensionResponse;
import org.finra.herd.model.api.xml.UploadSingleInitiationRequest;
import org.finra.herd.model.api.xml.UploadSingleInitiationResponse;
import org.finra.herd.model.dto.CompleteUploadSingleParamsDto;
import org.finra.herd.model.dto.ConfigurationValue;
import org.finra.herd.model.dto.S3FileTransferRequestParamsDto;
import org.finra.herd.model.jpa.BusinessObjectDataEntity;
import org.finra.herd.model.jpa.BusinessObjectDataStatusEntity;
import org.finra.herd.model.jpa.BusinessObjectDefinitionEntity;
import org.finra.herd.model.jpa.BusinessObjectDefinitionSampleDataFileEntity;
import org.finra.herd.model.jpa.BusinessObjectFormatEntity;
import org.finra.herd.model.jpa.StorageEntity;
import org.finra.herd.model.jpa.StorageFileEntity;
import org.finra.herd.model.jpa.StorageUnitEntity;
import org.finra.herd.service.UploadDownloadHelperService;
import org.finra.herd.service.UploadDownloadService;
import org.finra.herd.service.helper.AlternateKeyHelper;
import org.finra.herd.service.helper.AttributeHelper;
import org.finra.herd.service.helper.AwsPolicyBuilder;
import org.finra.herd.service.helper.BusinessObjectDataDaoHelper;
import org.finra.herd.service.helper.BusinessObjectDataHelper;
import org.finra.herd.service.helper.BusinessObjectDefinitionDaoHelper;
import org.finra.herd.service.helper.BusinessObjectDefinitionHelper;
import org.finra.herd.service.helper.BusinessObjectFormatDaoHelper;
import org.finra.herd.service.helper.BusinessObjectFormatHelper;
import org.finra.herd.service.helper.KmsActions;
import org.finra.herd.service.helper.S3KeyPrefixHelper;
import org.finra.herd.service.helper.StorageDaoHelper;
import org.finra.herd.service.helper.StorageFileDaoHelper;
import org.finra.herd.service.helper.StorageHelper;
import org.finra.herd.service.helper.StorageUnitDaoHelper;
import org.finra.herd.service.helper.UploadDownloadHelper;

/**
 * The upload download service implementation.
 */
@Service
@Transactional(value = DaoSpringModuleConfig.HERD_TRANSACTION_MANAGER_BEAN_NAME)
public class UploadDownloadServiceImpl implements UploadDownloadService {
    private static final Logger LOGGER = LoggerFactory.getLogger(UploadDownloadServiceImpl.class);

    @Autowired
    private AlternateKeyHelper alternateKeyHelper;

    @Autowired
    private AttributeHelper attributeHelper;

    @Autowired
    private AwsHelper awsHelper;

    @Autowired
    private BusinessObjectDataDaoHelper businessObjectDataDaoHelper;

    @Autowired
    private BusinessObjectDataHelper businessObjectDataHelper;

    @Autowired
    private BusinessObjectFormatDaoHelper businessObjectFormatDaoHelper;

    @Autowired
    private BusinessObjectFormatHelper businessObjectFormatHelper;

    @Autowired
    private BusinessObjectDefinitionDaoHelper businessObjectDefinitionDaoHelper;

    @Autowired
    private BusinessObjectDefinitionHelper businessObjectDefinitionHelper;

    @Autowired
    private ConfigurationHelper configurationHelper;

    @Autowired
    private JsonHelper jsonHelper;

    @Autowired
    private S3KeyPrefixHelper s3KeyPrefixHelper;

    @Autowired
    private S3Dao s3Dao;

    @Autowired
    private StorageDaoHelper storageDaoHelper;

    @Autowired
    private StorageFileDaoHelper storageFileDaoHelper;

    @Autowired
    private StorageHelper storageHelper;

    @Autowired
    private StorageUnitDaoHelper storageUnitDaoHelper;

    @Autowired
    private StsDao stsDao;

    @Autowired
    private UploadDownloadHelper uploadDownloadHelper;

    @Autowired
    private UploadDownloadHelperService uploadDownloadHelperService;

    @PublishNotificationMessages
    @NamespacePermission(fields = { "#uploadSingleInitiationRequest?.sourceBusinessObjectFormatKey?.namespace",
            "#uploadSingleInitiationRequest?.targetBusinessObjectFormatKey?.namespace" }, permissions = NamespacePermissionEnum.WRITE)
    @Override
    public UploadSingleInitiationResponse initiateUploadSingle(
            UploadSingleInitiationRequest uploadSingleInitiationRequest) {
        // Validate and trim the request parameters.
        validateUploadSingleInitiationRequest(uploadSingleInitiationRequest);

        // Get the business object format for the specified parameters and make sure it exists.
        BusinessObjectFormatEntity sourceBusinessObjectFormatEntity = businessObjectFormatDaoHelper
                .getBusinessObjectFormatEntity(uploadSingleInitiationRequest.getSourceBusinessObjectFormatKey());

        // Get the target business object format entity for the specified parameters and make sure it exists.
        BusinessObjectFormatEntity targetBusinessObjectFormatEntity = businessObjectFormatDaoHelper
                .getBusinessObjectFormatEntity(uploadSingleInitiationRequest.getTargetBusinessObjectFormatKey());

        // Get the S3 managed "loading dock" storage entity and make sure it exists.
        StorageEntity sourceStorageEntity = storageDaoHelper
                .getStorageEntity(StorageEntity.MANAGED_LOADING_DOCK_STORAGE);

        // Get S3 bucket name for the storage. Please note that since those values are required we pass a "true" flag.
        String s3BucketName = storageHelper.getStorageBucketName(sourceStorageEntity);

        // Get the S3 managed "external" storage entity and make sure it exists.
        String targetStorageName;
        if (uploadSingleInitiationRequest.getTargetStorageName() != null) {
            targetStorageName = uploadSingleInitiationRequest.getTargetStorageName();
        } else {
            targetStorageName = configurationHelper
                    .getProperty(ConfigurationValue.S3_EXTERNAL_STORAGE_NAME_DEFAULT);
        }
        StorageEntity targetStorageEntity = storageDaoHelper.getStorageEntity(targetStorageName);

        assertTargetStorageEntityValid(targetStorageEntity);

        // Generate a random UUID value.
        String uuid = UUID.randomUUID().toString();

        // Create business object data key with partition value set to the generated UUID.
        BusinessObjectDataKey businessObjectDataKey = new BusinessObjectDataKey(
                uploadSingleInitiationRequest.getSourceBusinessObjectFormatKey().getNamespace(),
                uploadSingleInitiationRequest.getSourceBusinessObjectFormatKey().getBusinessObjectDefinitionName(),
                uploadSingleInitiationRequest.getSourceBusinessObjectFormatKey().getBusinessObjectFormatUsage(),
                uploadSingleInitiationRequest.getSourceBusinessObjectFormatKey().getBusinessObjectFormatFileType(),
                uploadSingleInitiationRequest.getSourceBusinessObjectFormatKey().getBusinessObjectFormatVersion(),
                uuid, null, BusinessObjectDataEntity.BUSINESS_OBJECT_DATA_INITIAL_VERSION);

        // Get a file upload specific S3 key prefix for the source storage based on the generated UUID.
        String sourceStorageDirectoryPath = s3KeyPrefixHelper.buildS3KeyPrefix(sourceStorageEntity,
                sourceBusinessObjectFormatEntity, businessObjectDataKey);
        String sourceStorageFilePath = String.format("%s/%s", sourceStorageDirectoryPath,
                uploadSingleInitiationRequest.getFile().getFileName());

        // Create a business object data create request.
        BusinessObjectDataCreateRequest sourceBusinessObjectDataCreateRequest = businessObjectDataHelper
                .createBusinessObjectDataCreateRequest(sourceBusinessObjectFormatEntity, uuid,
                        BusinessObjectDataStatusEntity.UPLOADING,
                        uploadSingleInitiationRequest.getBusinessObjectDataAttributes(), sourceStorageEntity,
                        sourceStorageDirectoryPath, sourceStorageFilePath,
                        uploadSingleInitiationRequest.getFile().getFileSizeBytes(), null);

        // Create a new business object data instance. Set the flag to false, since for the file upload service the file size value is optional.
        BusinessObjectData sourceBusinessObjectData = businessObjectDataDaoHelper
                .createBusinessObjectData(sourceBusinessObjectDataCreateRequest, false);

        // Get a file upload specific S3 key prefix for the target storage based on the generated UUID.
        String targetStorageDirectoryPath = s3KeyPrefixHelper.buildS3KeyPrefix(targetStorageEntity,
                targetBusinessObjectFormatEntity, businessObjectDataKey);
        String targetStorageFilePath = String.format("%s/%s", targetStorageDirectoryPath,
                uploadSingleInitiationRequest.getFile().getFileName());

        uploadDownloadHelperService.assertS3ObjectKeyDoesNotExist(
                storageHelper.getStorageBucketName(targetStorageEntity), targetStorageFilePath);

        // Create a target business object data based on the source business object data and target business object format.
        BusinessObjectDataCreateRequest targetBusinessObjectDataCreateRequest = businessObjectDataHelper
                .createBusinessObjectDataCreateRequest(targetBusinessObjectFormatEntity, uuid,
                        BusinessObjectDataStatusEntity.UPLOADING,
                        uploadSingleInitiationRequest.getBusinessObjectDataAttributes(), targetStorageEntity,
                        targetStorageDirectoryPath, targetStorageFilePath,
                        uploadSingleInitiationRequest.getFile().getFileSizeBytes(), null);

        // Create a target business object data instance. Set the flag to false, since for the file upload service the file size value is optional.
        BusinessObjectData targetBusinessObjectData = businessObjectDataDaoHelper
                .createBusinessObjectData(targetBusinessObjectDataCreateRequest, false);

        // Get decrypted AWS ARN of the role that is required to provide access to S3_MANAGED_LOADING_DOCK storage.
        String awsRoleArn = getStorageUploadRoleArn(sourceStorageEntity);

        // Get expiration interval for the pre-signed URL to be generated.
        Integer awsRoleDurationSeconds = getStorageUploadSessionDuration(sourceStorageEntity);

        String awsKmsKeyId = storageHelper.getStorageKmsKeyId(sourceStorageEntity);

        // Get the temporary security credentials to access S3_MANAGED_STORAGE.
        Credentials assumedSessionCredentials = stsDao.getTemporarySecurityCredentials(awsHelper.getAwsParamsDto(),
                String.valueOf(sourceBusinessObjectData.getId()), awsRoleArn, awsRoleDurationSeconds,
                createUploaderPolicy(s3BucketName, sourceStorageFilePath, awsKmsKeyId));

        // Create the response.
        UploadSingleInitiationResponse response = new UploadSingleInitiationResponse();
        response.setSourceBusinessObjectData(sourceBusinessObjectData);
        response.setTargetBusinessObjectData(targetBusinessObjectData);
        response.setFile(uploadSingleInitiationRequest.getFile());
        response.setUuid(uuid);
        response.setAwsAccessKey(assumedSessionCredentials.getAccessKeyId());
        response.setAwsSecretKey(assumedSessionCredentials.getSecretAccessKey());
        response.setAwsSessionToken(assumedSessionCredentials.getSessionToken());
        response.setAwsSessionExpirationTime(
                HerdDateUtils.getXMLGregorianCalendarValue(assumedSessionCredentials.getExpiration()));
        response.setAwsKmsKeyId(awsKmsKeyId);
        response.setTargetStorageName(targetStorageName);

        return response;
    }

    /**
     * Asserts that the given target storage entity has valid attributes.
     *
     * @param targetStorageEntity Target storage entity.
     */
    private void assertTargetStorageEntityValid(StorageEntity targetStorageEntity) {
        try {
            // Assert that the target storage has a bucket name
            storageHelper.getStorageBucketName(targetStorageEntity);
        } catch (IllegalStateException e) {
            throw new IllegalArgumentException(e.getMessage(), e);
        }

        try {
            // Assert that the target storage has a KMS key ID
            storageHelper.getStorageKmsKeyId(targetStorageEntity);
        } catch (IllegalStateException e) {
            throw new IllegalArgumentException(e.getMessage(), e);
        }
    }

    /**
     * Creates a restricted policy JSON string which only allows PutObject to the given bucket name and object key, and allows GenerateDataKey and Decrypt for
     * the given key ID. The Decrypt is required for multipart upload with KMS encryption.
     *
     * @param s3BucketName - The S3 bucket name to restrict uploads to
     * @param s3Key - The S3 object key to restrict the uploads to
     * @param awsKmsKeyId - The KMS key ID to allow access
     *
     * @return the policy JSON string
     */
    @SuppressWarnings("PMD.CloseResource") // These are not SQL statements so they don't need to be closed.
    private Policy createUploaderPolicy(String s3BucketName, String s3Key, String awsKmsKeyId) {
        return new AwsPolicyBuilder().withS3(s3BucketName, s3Key, S3Actions.PutObject)
                .withKms(awsKmsKeyId, KmsActions.GENERATE_DATA_KEY, KmsActions.DECRYPT).build();
    }

    @SuppressWarnings("PMD.CloseResource") // These are not SQL statements so they don't need to be closed.
    private Policy createUploaderPolicyNoKmsKey(String s3BucketName, String s3Key) {
        return new AwsPolicyBuilder().withS3(s3BucketName, s3Key, S3Actions.PutObject).build();
    }

    /**
     * Creates a restricted policy JSON string which only allows GetObject to the given bucket name and object key, and allows Decrypt for the given key ID.
     *
     * @param s3BucketName - The S3 bucket name to restrict uploads to
     * @param s3Key - The S3 object key to restrict the uploads to
     * @param awsKmsKeyId - The KMS key ID to allow access
     *
     * @return the policy JSON string
     */
    @SuppressWarnings("PMD.CloseResource") // These are not SQL statements so they don't need to be closed.
    private Policy createDownloaderPolicy(String s3BucketName, String s3Key, String awsKmsKeyId) {
        return new AwsPolicyBuilder().withS3(s3BucketName, s3Key, S3Actions.GetObject)
                .withKms(awsKmsKeyId, KmsActions.DECRYPT).build();
    }

    /**
     * Creates a restricted policy JSON string which only allows GetObject to the given bucket name and object key, and allows Decrypt for the given key ID.
     *
     * @param s3BucketName - The S3 bucket name to restrict uploads to
     * @param s3Key - The S3 object key to restrict the uploads to
     *
     * @return the policy JSON string
     */
    @SuppressWarnings("PMD.CloseResource") // These are not SQL statements so they don't need to be closed.
    private Policy createDownloaderPolicy(String s3BucketName, String s3Key) {
        return new AwsPolicyBuilder().withS3(s3BucketName, s3Key, S3Actions.GetObject).build();
    }

    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public CompleteUploadSingleMessageResult performCompleteUploadSingleMessage(String objectKey) {
        return performCompleteUploadSingleMessageImpl(objectKey);
    }

    /**
     * Performs the completion of upload single file. Runs in new transaction and logs the error if an error occurs.
     *
     * @param objectKey the object key.
     *
     * @return CompleteUploadSingleMessageResult
     */
    protected CompleteUploadSingleMessageResult performCompleteUploadSingleMessageImpl(String objectKey) {
        // Create an instance of complete upload single parameters DTO.
        CompleteUploadSingleParamsDto completeUploadSingleParamsDto = new CompleteUploadSingleParamsDto();

        // Prepare for the file move.
        // TODO: To make our implementation Mockito friendly, we need to re-write the upload download
        // TODO: helper methods to make them return the updated DTO back instead of being void methods.
        uploadDownloadHelperService.prepareForFileMove(objectKey, completeUploadSingleParamsDto);

        // Create an instance of the result message for complete upload single operation.
        CompleteUploadSingleMessageResult completeUploadSingleMessageResult = new CompleteUploadSingleMessageResult();
        completeUploadSingleMessageResult
                .setSourceBusinessObjectDataKey(completeUploadSingleParamsDto.getSourceBusinessObjectDataKey());
        completeUploadSingleMessageResult
                .setSourceOldBusinessObjectDataStatus(completeUploadSingleParamsDto.getSourceOldStatus());
        completeUploadSingleMessageResult
                .setSourceNewBusinessObjectDataStatus(completeUploadSingleParamsDto.getSourceNewStatus());
        completeUploadSingleMessageResult
                .setTargetBusinessObjectDataKey(completeUploadSingleParamsDto.getTargetBusinessObjectDataKey());
        completeUploadSingleMessageResult
                .setTargetOldBusinessObjectDataStatus(completeUploadSingleParamsDto.getTargetOldStatus());
        completeUploadSingleMessageResult
                .setTargetNewBusinessObjectDataStatus(completeUploadSingleParamsDto.getTargetNewStatus());

        // If both source and target business object data have RE-ENCRYPTING status, continue the processing.
        if (BusinessObjectDataStatusEntity.RE_ENCRYPTING.equals(completeUploadSingleParamsDto.getSourceNewStatus())
                && BusinessObjectDataStatusEntity.RE_ENCRYPTING
                        .equals(completeUploadSingleParamsDto.getTargetNewStatus())) {
            // Move the S3 file from the source to the target bucket.
            uploadDownloadHelperService.performFileMove(completeUploadSingleParamsDto);

            // Execute the steps required to complete the processing of the complete upload single message.
            uploadDownloadHelperService.executeFileMoveAfterSteps(completeUploadSingleParamsDto);

            // Delete the source file from the S3
            uploadDownloadHelperService.deleteSourceFileFromS3(completeUploadSingleParamsDto);

            // Update the result message.
            completeUploadSingleMessageResult
                    .setSourceNewBusinessObjectDataStatus(completeUploadSingleParamsDto.getSourceNewStatus());
            completeUploadSingleMessageResult
                    .setTargetNewBusinessObjectDataStatus(completeUploadSingleParamsDto.getTargetNewStatus());
        }

        // Log the result of the complete upload single operation.
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("completeUploadSingleMessageResult={}",
                    jsonHelper.objectToJson(completeUploadSingleMessageResult));
        }

        return completeUploadSingleMessageResult;
    }

    /*
     * The result of completeUploadSingleMessage, contains the source and target business object data key, old and new status.
     */
    public static class CompleteUploadSingleMessageResult {
        private BusinessObjectDataKey sourceBusinessObjectDataKey;

        private String sourceOldBusinessObjectDataStatus;

        private String sourceNewBusinessObjectDataStatus;

        private BusinessObjectDataKey targetBusinessObjectDataKey;

        private String targetOldBusinessObjectDataStatus;

        private String targetNewBusinessObjectDataStatus;

        public BusinessObjectDataKey getSourceBusinessObjectDataKey() {
            return sourceBusinessObjectDataKey;
        }

        public void setSourceBusinessObjectDataKey(BusinessObjectDataKey sourceBusinessObjectDataKey) {
            this.sourceBusinessObjectDataKey = sourceBusinessObjectDataKey;
        }

        public String getSourceOldBusinessObjectDataStatus() {
            return sourceOldBusinessObjectDataStatus;
        }

        public void setSourceOldBusinessObjectDataStatus(String sourceOldBusinessObjectDataStatus) {
            this.sourceOldBusinessObjectDataStatus = sourceOldBusinessObjectDataStatus;
        }

        public String getSourceNewBusinessObjectDataStatus() {
            return sourceNewBusinessObjectDataStatus;
        }

        public void setSourceNewBusinessObjectDataStatus(String sourceNewBusinessObjectDataStatus) {
            this.sourceNewBusinessObjectDataStatus = sourceNewBusinessObjectDataStatus;
        }

        public BusinessObjectDataKey getTargetBusinessObjectDataKey() {
            return targetBusinessObjectDataKey;
        }

        public void setTargetBusinessObjectDataKey(BusinessObjectDataKey targetBusinessObjectDataKey) {
            this.targetBusinessObjectDataKey = targetBusinessObjectDataKey;
        }

        public String getTargetOldBusinessObjectDataStatus() {
            return targetOldBusinessObjectDataStatus;
        }

        public void setTargetOldBusinessObjectDataStatus(String targetOldBusinessObjectDataStatus) {
            this.targetOldBusinessObjectDataStatus = targetOldBusinessObjectDataStatus;
        }

        public String getTargetNewBusinessObjectDataStatus() {
            return targetNewBusinessObjectDataStatus;
        }

        public void setTargetNewBusinessObjectDataStatus(String targetNewBusinessObjectDataStatus) {
            this.targetNewBusinessObjectDataStatus = targetNewBusinessObjectDataStatus;
        }
    }

    /**
     * Validates the upload single initiation request. This method also trims the request parameters.
     *
     * @param request the upload single initiation request
     */
    private void validateUploadSingleInitiationRequest(UploadSingleInitiationRequest request) {
        Assert.notNull(request, "An upload single initiation request must be specified.");

        // Validate and trim the source business object format key.
        businessObjectFormatHelper.validateBusinessObjectFormatKey(request.getSourceBusinessObjectFormatKey());

        // Validate and trim the target business object format key.
        businessObjectFormatHelper.validateBusinessObjectFormatKey(request.getTargetBusinessObjectFormatKey());

        // Validate and trim the attributes.
        attributeHelper.validateAttributes(request.getBusinessObjectDataAttributes());

        // Validate and trim the file information.
        Assert.notNull(request.getFile(), "File information must be specified.");
        Assert.hasText(request.getFile().getFileName(), "A file name must be specified.");
        request.getFile().setFileName(request.getFile().getFileName().trim());

        String targetStorageName = request.getTargetStorageName();
        if (targetStorageName != null) {
            request.setTargetStorageName(targetStorageName.trim());
        }
    }

    @NamespacePermission(fields = "#namespace", permissions = NamespacePermissionEnum.READ)
    @Override
    public DownloadSingleInitiationResponse initiateDownloadSingle(String namespace,
            String businessObjectDefinitionName, String businessObjectFormatUsage,
            String businessObjectFormatFileType, Integer businessObjectFormatVersion, String partitionValue,
            Integer businessObjectDataVersion) {
        // Create the business object data key.
        BusinessObjectDataKey businessObjectDataKey = new BusinessObjectDataKey(namespace,
                businessObjectDefinitionName, businessObjectFormatUsage, businessObjectFormatFileType,
                businessObjectFormatVersion, partitionValue, null, businessObjectDataVersion);

        // Validate the parameters
        businessObjectDataHelper.validateBusinessObjectDataKey(businessObjectDataKey, true, true);

        // Retrieve the persisted business object data
        BusinessObjectDataEntity businessObjectDataEntity = businessObjectDataDaoHelper
                .getBusinessObjectDataEntity(businessObjectDataKey);

        // Make sure the status of the business object data is VALID
        businessObjectDataHelper.assertBusinessObjectDataStatusEquals(BusinessObjectDataStatusEntity.VALID,
                businessObjectDataEntity);

        // Get the external storage registered against this data
        // Validate that the storage unit exists
        StorageUnitEntity storageUnitEntity = IterableUtils.get(businessObjectDataEntity.getStorageUnits(), 0);

        // Validate that the storage unit contains only 1 file
        assertHasOneStorageFile(storageUnitEntity);

        String s3BucketName = storageHelper.getStorageBucketName(storageUnitEntity.getStorage());
        String s3ObjectKey = IterableUtils.get(storageUnitEntity.getStorageFiles(), 0).getPath();

        // Get the temporary credentials
        Credentials downloaderCredentials = getExternalDownloaderCredentials(storageUnitEntity.getStorage(),
                String.valueOf(businessObjectDataEntity.getId()), s3ObjectKey);

        // Generate a pre-signed URL
        Date expiration = downloaderCredentials.getExpiration();
        S3FileTransferRequestParamsDto s3BucketAccessParams = storageHelper
                .getS3BucketAccessParams(storageUnitEntity.getStorage());
        String presignedUrl = s3Dao.generateGetObjectPresignedUrl(s3BucketName, s3ObjectKey, expiration,
                s3BucketAccessParams);

        // Construct and return the response
        DownloadSingleInitiationResponse response = new DownloadSingleInitiationResponse();
        response.setBusinessObjectData(
                businessObjectDataHelper.createBusinessObjectDataFromEntity(businessObjectDataEntity));
        response.setAwsAccessKey(downloaderCredentials.getAccessKeyId());
        response.setAwsSecretKey(downloaderCredentials.getSecretAccessKey());
        response.setAwsSessionToken(downloaderCredentials.getSessionToken());
        response.setAwsSessionExpirationTime(HerdDateUtils.getXMLGregorianCalendarValue(expiration));
        response.setPreSignedUrl(presignedUrl);
        return response;
    }

    @NamespacePermission(fields = "#namespace", permissions = NamespacePermissionEnum.WRITE)
    @Override
    public UploadSingleCredentialExtensionResponse extendUploadSingleCredentials(String namespace,
            String businessObjectDefinitionName, String businessObjectFormatUsage,
            String businessObjectFormatFileType, Integer businessObjectFormatVersion, String partitionValue,
            Integer businessObjectDataVersion) {
        // Create the business object data key.
        BusinessObjectDataKey businessObjectDataKey = new BusinessObjectDataKey(namespace,
                businessObjectDefinitionName, businessObjectFormatUsage, businessObjectFormatFileType,
                businessObjectFormatVersion, partitionValue, null, businessObjectDataVersion);

        // Validate and trim the business object data key.
        businessObjectDataHelper.validateBusinessObjectDataKey(businessObjectDataKey, true, true);

        // Get the business object data for the key.
        BusinessObjectDataEntity businessObjectDataEntity = businessObjectDataDaoHelper
                .getBusinessObjectDataEntity(businessObjectDataKey);

        // Ensure the status of the business object data is "uploading" in order to extend credentials.
        if (!(businessObjectDataEntity.getStatus().getCode().equals(BusinessObjectDataStatusEntity.UPLOADING))) {
            throw new IllegalArgumentException(String.format(String.format(
                    "Business object data {%s} has a status of \"%s\" and must be \"%s\" to extend "
                            + "credentials.",
                    businessObjectDataHelper.businessObjectDataKeyToString(businessObjectDataKey),
                    businessObjectDataEntity.getStatus().getCode(), BusinessObjectDataStatusEntity.UPLOADING)));
        }

        // Get the S3 managed "loading dock" storage entity and make sure it exists.
        StorageEntity storageEntity = storageDaoHelper.getStorageEntity(StorageEntity.MANAGED_LOADING_DOCK_STORAGE);

        String s3BucketName = storageHelper.getStorageBucketName(storageEntity);

        // Get the storage unit entity for this business object data in the S3 managed "loading dock" storage and make sure it exists.
        StorageUnitEntity storageUnitEntity = storageUnitDaoHelper
                .getStorageUnitEntity(StorageEntity.MANAGED_LOADING_DOCK_STORAGE, businessObjectDataEntity);

        // Validate that the storage unit contains exactly one storage file.
        assertHasOneStorageFile(storageUnitEntity);

        // Get the storage file entity.
        StorageFileEntity storageFileEntity = IterableUtils.get(storageUnitEntity.getStorageFiles(), 0);

        // Get the storage file path.
        String storageFilePath = storageFileEntity.getPath();

        String awsRoleArn = getStorageUploadRoleArn(storageEntity);

        Integer awsRoleDurationSeconds = getStorageUploadSessionDuration(storageEntity);

        String awsKmsKeyId = storageHelper.getStorageKmsKeyId(storageEntity);

        // Get the temporary security credentials to access S3_MANAGED_STORAGE.
        Credentials assumedSessionCredentials = stsDao.getTemporarySecurityCredentials(awsHelper.getAwsParamsDto(),
                String.valueOf(businessObjectDataEntity.getId()), awsRoleArn, awsRoleDurationSeconds,
                createUploaderPolicy(s3BucketName, storageFilePath, awsKmsKeyId));

        // Create the response.
        UploadSingleCredentialExtensionResponse response = new UploadSingleCredentialExtensionResponse();
        response.setAwsAccessKey(assumedSessionCredentials.getAccessKeyId());
        response.setAwsSecretKey(assumedSessionCredentials.getSecretAccessKey());
        response.setAwsSessionToken(assumedSessionCredentials.getSessionToken());
        response.setAwsSessionExpirationTime(
                HerdDateUtils.getXMLGregorianCalendarValue(assumedSessionCredentials.getExpiration()));

        return response;
    }

    /**
     * Asserts that the given storage unit entity contains exactly one storage file.
     *
     * @param storageUnitEntity - storage unit to check
     *
     * @throws IllegalArgumentException when the number of storage files is not 1
     */
    private void assertHasOneStorageFile(StorageUnitEntity storageUnitEntity) {
        Assert.isTrue(storageUnitEntity.getStorageFiles().size() == 1, String.format(
                "Found %d registered storage files when expecting one in \"%s\" storage for the business object data {%s}.",
                storageUnitEntity.getStorageFiles().size(), storageUnitEntity.getStorage().getName(),
                businessObjectDataHelper
                        .businessObjectDataEntityAltKeyToString(storageUnitEntity.getBusinessObjectData())));
    }

    /**
     * Gets a temporary session token that is only good for downloading the specified object key from the given bucket for a limited amount of time.
     *
     * @param storageEntity The storage entity of the external storage
     * @param sessionName the session name to use for the temporary credentials.
     * @param s3ObjectKey the S3 object key of the path to the data in the bucket.
     *
     * @return {@link Credentials} temporary session token
     */
    private Credentials getExternalDownloaderCredentials(StorageEntity storageEntity, String sessionName,
            String s3ObjectKey) {
        return stsDao.getTemporarySecurityCredentials(awsHelper.getAwsParamsDto(), sessionName,
                getStorageDownloadRoleArn(storageEntity), getStorageDownloadSessionDuration(storageEntity),
                createDownloaderPolicy(storageHelper.getStorageBucketName(storageEntity), s3ObjectKey,
                        storageHelper.getStorageKmsKeyId(storageEntity)));
    }

    /**
     * Gets a temporary session token that is only good for downloading the specified object key from the given bucket for a limited amount of time.
     *
     * @param storageEntity The storage entity of the external storage.
     * @param sessionName The session name to use for the temporary credentials.
     * @param s3ObjectKey The S3 object key of the path to the data in the bucket.
     *
     * @return {@link Credentials} temporary session token
     */
    private Credentials getDownloaderCredentialsNoKmsKey(StorageEntity storageEntity, String sessionName,
            String s3ObjectKey) {
        return stsDao.getTemporarySecurityCredentials(awsHelper.getAwsParamsDto(), sessionName,
                getStorageDownloadRoleArn(storageEntity), getStorageDownloadSessionDuration(storageEntity),
                createDownloaderPolicy(storageHelper.getStorageBucketName(storageEntity), s3ObjectKey));
    }

    /**
     * Gets a temporary session token that is only good for downloading the specified object key from the given bucket for a limited amount of time.
     *
     * @param storageEntity The storage entity of the external storage.
     * @param sessionName The session name to use for the temporary credentials.
     * @param awsPolicyBuilder The AWS policy builder.
     *
     * @return {@link Credentials} temporary session token
     */
    private Credentials getDownloaderCredentials(StorageEntity storageEntity, String sessionName,
            AwsPolicyBuilder awsPolicyBuilder) {
        return stsDao.getTemporarySecurityCredentials(awsHelper.getAwsParamsDto(), sessionName,
                getStorageDownloadRoleArn(storageEntity), getStorageDownloadSessionDuration(storageEntity),
                awsPolicyBuilder.build());
    }

    /**
     * Gets the storage's upload session duration in seconds. Defaults to the configured default value if not defined.
     *
     * @param storageEntity The storage entity
     *
     * @return Upload session duration in seconds
     */
    private Integer getStorageUploadSessionDuration(StorageEntity storageEntity) {
        return storageHelper.getStorageAttributeIntegerValueByName(
                configurationHelper.getProperty(ConfigurationValue.S3_ATTRIBUTE_NAME_UPLOAD_SESSION_DURATION_SECS),
                storageEntity, configurationHelper.getProperty(
                        ConfigurationValue.AWS_S3_DEFAULT_UPLOAD_SESSION_DURATION_SECS, Integer.class));
    }

    /**
     * Gets the storage's upload role ARN. Throws if not defined.
     *
     * @param storageEntity The storage entity
     *
     * @return Upload role ARN
     */
    private String getStorageUploadRoleArn(StorageEntity storageEntity) {
        return storageHelper.getStorageAttributeValueByName(
                configurationHelper.getProperty(ConfigurationValue.S3_ATTRIBUTE_NAME_UPLOAD_ROLE_ARN),
                storageEntity, true);
    }

    /**
     * Gets the storage's download session duration in seconds. Defaults to the configured default value if not defined.
     *
     * @param storageEntity The storage entity
     *
     * @return Download session duration in seconds
     */
    private Integer getStorageDownloadSessionDuration(StorageEntity storageEntity) {
        return storageHelper.getStorageAttributeIntegerValueByName(
                configurationHelper.getProperty(
                        ConfigurationValue.S3_ATTRIBUTE_NAME_DOWNLOAD_SESSION_DURATION_SECS),
                storageEntity, configurationHelper.getProperty(
                        ConfigurationValue.AWS_S3_DEFAULT_DOWNLOAD_SESSION_DURATION_SECS, Integer.class));
    }

    /**
     * Gets the storage's download role ARN. Throws if not defined.
     *
     * @param storageEntity The storage entity
     *
     * @return Download role ARN
     */
    private String getStorageDownloadRoleArn(StorageEntity storageEntity) {
        return storageHelper.getStorageAttributeValueByName(
                configurationHelper.getProperty(ConfigurationValue.S3_ATTRIBUTE_NAME_DOWNLOAD_ROLE_ARN),
                storageEntity, true);
    }

    @Override
    public DownloadBusinessObjectDefinitionSampleDataFileSingleInitiationResponse initiateDownloadSingleSampleFile(
            DownloadBusinessObjectDefinitionSampleDataFileSingleInitiationRequest request) {
        // Validate and trim the request parameters.
        validateDownloadBusinessObjectDefinitionSampleDataFileSingleInitiationRequest(request);

        // Get the business object definition sample data file key.
        BusinessObjectDefinitionSampleDataFileKey businessObjectDefinitionSampleDataFileKey = request
                .getBusinessObjectDefinitionSampleDataFileKey();

        // Get the business object definition key.
        BusinessObjectDefinitionKey businessObjectDefinitionKey = new BusinessObjectDefinitionKey(
                businessObjectDefinitionSampleDataFileKey.getNamespace(),
                businessObjectDefinitionSampleDataFileKey.getBusinessObjectDefinitionName());

        // Get the business object definition entity and ensure it exists.
        BusinessObjectDefinitionEntity businessObjectDefinitionEntity = businessObjectDefinitionDaoHelper
                .getBusinessObjectDefinitionEntity(businessObjectDefinitionKey);

        // Get the sample data file exists for the business object definition and ensure it exists.
        BusinessObjectDefinitionSampleDataFileEntity businessObjectDefinitionSampleDataFileEntity = getBusinessObjectDefinitionSampleDataFileEntity(
                businessObjectDefinitionEntity, businessObjectDefinitionSampleDataFileKey);

        // Retrieve the storage related information.
        StorageEntity storageEntity = businessObjectDefinitionSampleDataFileEntity.getStorage();
        String s3BucketName = storageHelper.getStorageBucketName(storageEntity);
        String s3ObjectKey = businessObjectDefinitionSampleDataFileKey.getDirectoryPath()
                + businessObjectDefinitionSampleDataFileKey.getFileName();

        String sessionID = UUID.randomUUID().toString();
        // Get the temporary credentials.
        Credentials downloaderCredentials = getDownloaderCredentialsNoKmsKey(storageEntity, sessionID, s3ObjectKey);

        // Generate a pre-signed URL.
        Date expiration = downloaderCredentials.getExpiration();
        S3FileTransferRequestParamsDto s3BucketAccessParams = storageHelper.getS3BucketAccessParams(storageEntity);
        String presignedUrl = s3Dao.generateGetObjectPresignedUrl(s3BucketName, s3ObjectKey, expiration,
                s3BucketAccessParams);

        // Create the download business object definition sample data file single initiation response.
        DownloadBusinessObjectDefinitionSampleDataFileSingleInitiationResponse response = new DownloadBusinessObjectDefinitionSampleDataFileSingleInitiationResponse();
        response.setBusinessObjectDefinitionSampleDataFileKey(new BusinessObjectDefinitionSampleDataFileKey(
                businessObjectDefinitionEntity.getNamespace().getCode(), businessObjectDefinitionEntity.getName(),
                businessObjectDefinitionSampleDataFileEntity.getDirectoryPath(),
                businessObjectDefinitionSampleDataFileEntity.getFileName()));
        response.setAwsS3BucketName(s3BucketName);
        response.setAwsAccessKey(downloaderCredentials.getAccessKeyId());
        response.setAwsSecretKey(downloaderCredentials.getSecretAccessKey());
        response.setAwsSessionToken(downloaderCredentials.getSessionToken());
        response.setAwsSessionExpirationTime(HerdDateUtils.getXMLGregorianCalendarValue(expiration));
        response.setPreSignedUrl(presignedUrl);

        // Return the response.
        return response;
    }

    /**
     * Validates the download business object definition sample data file single initiation request.
     *
     * @param request the download business object definition sample data file single initiation request request
     */
    private void validateDownloadBusinessObjectDefinitionSampleDataFileSingleInitiationRequest(
            DownloadBusinessObjectDefinitionSampleDataFileSingleInitiationRequest request) {
        Assert.notNull(request,
                "A download business object definition sample data file single initiation request must be specified.");
        validateBusinessObjectDefinitionSampleDataFileKey(request.getBusinessObjectDefinitionSampleDataFileKey());
    }

    /**
     * Validates a business object definition sample data file key. This method also trims the key parameters.
     *
     * @param key the business object definition sample data file key
     */
    private void validateBusinessObjectDefinitionSampleDataFileKey(BusinessObjectDefinitionSampleDataFileKey key) {
        Assert.notNull(key, "A business object definition sample data file key must be specified.");
        key.setNamespace(alternateKeyHelper.validateStringParameter("namespace", key.getNamespace()));
        key.setBusinessObjectDefinitionName(alternateKeyHelper
                .validateStringParameter("business object definition name", key.getBusinessObjectDefinitionName()));
        Assert.hasText(key.getDirectoryPath(), "A directory path must be specified.");
        key.setDirectoryPath(key.getDirectoryPath().trim());
        Assert.hasText(key.getFileName(), "A file name must be specified.");
        key.setFileName(key.getFileName().trim());
    }

    /**
     * Gets a business object definition sample data file entity per specified parameters.
     *
     * @param businessObjectDefinitionEntity the business object definition entity
     * @param businessObjectDefinitionSampleDataFileKey the business object definition sample data file key
     *
     * @return the business object definition sample data file entity
     */
    private BusinessObjectDefinitionSampleDataFileEntity getBusinessObjectDefinitionSampleDataFileEntity(
            BusinessObjectDefinitionEntity businessObjectDefinitionEntity,
            BusinessObjectDefinitionSampleDataFileKey businessObjectDefinitionSampleDataFileKey) {
        BusinessObjectDefinitionSampleDataFileEntity businessObjectDefinitionSampleDataFileEntity = null;

        for (BusinessObjectDefinitionSampleDataFileEntity sampleDataFileEntity : businessObjectDefinitionEntity
                .getSampleDataFiles()) {
            if (sampleDataFileEntity.getDirectoryPath()
                    .equals(businessObjectDefinitionSampleDataFileKey.getDirectoryPath())
                    && sampleDataFileEntity.getFileName()
                            .equals(businessObjectDefinitionSampleDataFileKey.getFileName())) {
                businessObjectDefinitionSampleDataFileEntity = sampleDataFileEntity;
                break;
            }
        }

        if (businessObjectDefinitionSampleDataFileEntity == null) {
            throw new ObjectNotFoundException(String.format(
                    "Business object definition with name \"%s\" and namespace \"%s\" does not have the specified sample file registered with file name \"%s\" in"
                            + " directory path \"%s\"",
                    businessObjectDefinitionSampleDataFileKey.getBusinessObjectDefinitionName(),
                    businessObjectDefinitionSampleDataFileKey.getNamespace(),
                    businessObjectDefinitionSampleDataFileKey.getFileName(),
                    businessObjectDefinitionSampleDataFileKey.getDirectoryPath()));
        }

        return businessObjectDefinitionSampleDataFileEntity;
    }

    /**
     * Validate upload business object definition sample file request
     *
     * @param request the sample data file upload initiation request
     */
    private void validateUploadBusinessObjectDefinitionSampleDataFileInitiationRequest(
            UploadBusinessObjectDefinitionSampleDataFileInitiationRequest request) {
        Assert.notNull(request, "An upload initiation request must be specified.");
        BusinessObjectDefinitionKey businessObjectDefinitionKey = request.getBusinessObjectDefinitionKey();
        // Perform validation and trim.
        businessObjectDefinitionHelper.validateBusinessObjectDefinitionKey(businessObjectDefinitionKey);
    }

    @NamespacePermission(fields = "#request.businessObjectDefinitionKey.namespace", permissions = {
            NamespacePermissionEnum.WRITE_DESCRIPTIVE_CONTENT, NamespacePermissionEnum.WRITE })
    @Override
    public UploadBusinessObjectDefinitionSampleDataFileInitiationResponse initiateUploadSampleFile(
            UploadBusinessObjectDefinitionSampleDataFileInitiationRequest request) {
        validateUploadBusinessObjectDefinitionSampleDataFileInitiationRequest(request);

        BusinessObjectDefinitionKey businessObjectDefinitionKey = request.getBusinessObjectDefinitionKey();
        // Get the business object definition entity and ensure it exists.
        BusinessObjectDefinitionEntity businessObjectDefinitionEntity = businessObjectDefinitionDaoHelper
                .getBusinessObjectDefinitionEntity(businessObjectDefinitionKey);
        businessObjectDefinitionKey.setNamespace(businessObjectDefinitionEntity.getNamespace().getCode());
        businessObjectDefinitionKey.setBusinessObjectDefinitionName(businessObjectDefinitionEntity.getName());

        UploadBusinessObjectDefinitionSampleDataFileInitiationResponse response = new UploadBusinessObjectDefinitionSampleDataFileInitiationResponse();
        StorageEntity storageEntity = storageDaoHelper.getStorageEntity(StorageEntity.SAMPLE_DATA_FILE_STORAGE);

        String s3BucketName = storageHelper.getStorageBucketName(storageEntity);
        String s3EndPoint = storageHelper.getS3BucketAccessParams(storageEntity).getS3Endpoint();
        String awsRoleArn = getStorageUploadRoleArn(storageEntity);
        String sessionID = UUID.randomUUID().toString();
        String s3KeyPrefix = s3KeyPrefixHelper.buildS3KeyPrefix(storageEntity, businessObjectDefinitionKey);
        s3KeyPrefix = StringUtils.appendIfMissing(s3KeyPrefix, "/");
        //need to add star for aws authorization
        String s3Path = s3KeyPrefix + "*";

        Integer awsRoleDurationSeconds = getStorageUploadSessionDuration(storageEntity);

        Credentials assumedSessionCredentials = stsDao.getTemporarySecurityCredentials(awsHelper.getAwsParamsDto(),
                sessionID, awsRoleArn, awsRoleDurationSeconds, createUploaderPolicyNoKmsKey(s3BucketName, s3Path));

        response.setAwsAccessKey(assumedSessionCredentials.getAccessKeyId());
        response.setAwsSecretKey(assumedSessionCredentials.getSecretAccessKey());
        response.setAwsSessionToken(assumedSessionCredentials.getSessionToken());
        response.setAwsSessionExpirationTime(
                HerdDateUtils.getXMLGregorianCalendarValue(assumedSessionCredentials.getExpiration()));

        response.setAwsS3BucketName(s3BucketName);
        response.setBusinessObjectDefinitionKey(businessObjectDefinitionKey);
        response.setS3Endpoint(s3EndPoint);
        response.setS3KeyPrefix(s3KeyPrefix);
        return response;
    }

    @NamespacePermission(fields = "#downloadBusinessObjectDataStorageFileSingleInitiationRequest.businessObjectDataStorageFileKey.namespace", permissions = NamespacePermissionEnum.READ)
    @Override
    public DownloadBusinessObjectDataStorageFileSingleInitiationResponse initiateDownloadSingleBusinessObjectDataStorageFile(
            DownloadBusinessObjectDataStorageFileSingleInitiationRequest downloadBusinessObjectDataStorageFileSingleInitiationRequest) {
        // Validate and trim the request.
        uploadDownloadHelper.validateAndTrimDownloadBusinessObjectDataStorageFileSingleInitiationRequest(
                downloadBusinessObjectDataStorageFileSingleInitiationRequest);

        // Get the business object data storage file key.
        BusinessObjectDataStorageFileKey businessObjectDataStorageFileKey = downloadBusinessObjectDataStorageFileSingleInitiationRequest
                .getBusinessObjectDataStorageFileKey();

        // Retrieve and validate that the business object data exists.
        BusinessObjectDataKey businessObjectDataKey = getBusinessObjectDataKeyFromBusinessObjectDataStorageFileKey(
                businessObjectDataStorageFileKey);
        BusinessObjectDataEntity businessObjectDataEntity = businessObjectDataDaoHelper
                .getBusinessObjectDataEntity(businessObjectDataKey);

        // Retrieve and validate that the storage unit exists
        StorageUnitEntity storageUnitEntity = storageUnitDaoHelper
                .getStorageUnitEntity(businessObjectDataStorageFileKey.getStorageName(), businessObjectDataEntity);

        // Get the storage file entity and ensure it exists.
        StorageFileEntity storageFileEntity = storageFileDaoHelper.getStorageFileEntity(storageUnitEntity,
                businessObjectDataStorageFileKey.getFilePath(), businessObjectDataKey);

        // Get S3 bucket access parameters.
        StorageEntity storageEntity = storageFileEntity.getStorageUnit().getStorage();

        // Retrieve the storage related information.
        String s3BucketName = storageHelper.getStorageBucketName(storageEntity);
        String s3ObjectKey = businessObjectDataStorageFileKey.getFilePath();

        // Create an AWS policy builder.
        AwsPolicyBuilder awsPolicyBuilder = new AwsPolicyBuilder().withS3(s3BucketName, s3ObjectKey,
                S3Actions.GetObject);

        // Get the storage kms key id.
        String storageKmsKeyId = storageHelper.getStorageAttributeValueByName(
                configurationHelper.getProperty(ConfigurationValue.S3_ATTRIBUTE_NAME_KMS_KEY_ID), storageEntity,
                false, true);

        /*
         * Only add KMS policies if the storage specifies a KMS ID
         */
        if (storageKmsKeyId != null) {
            awsPolicyBuilder.withKms(storageKmsKeyId.trim(), KmsActions.DECRYPT);
        }

        // Create a sessionId.
        String sessionId = UUID.randomUUID().toString();

        // Get the temporary credentials.
        Credentials downloaderCredentials = getDownloaderCredentials(storageEntity, sessionId, awsPolicyBuilder);

        // Generate a pre-signed URL.
        Date expiration = downloaderCredentials.getExpiration();
        S3FileTransferRequestParamsDto s3BucketAccessParams = storageHelper.getS3BucketAccessParams(storageEntity);
        String preSignedUrl = s3Dao.generateGetObjectPresignedUrl(s3BucketName, s3ObjectKey, expiration,
                s3BucketAccessParams);

        // Convert the business object format entity to the business object format model object
        BusinessObjectFormat businessObjectFormat = businessObjectFormatHelper
                .createBusinessObjectFormatFromEntity(businessObjectDataEntity.getBusinessObjectFormat());

        // Create a business object data storage file key for the download business object data storage file single initiation response.
        BusinessObjectDataStorageFileKey businessObjectDataStorageFileKeyForResponse = new BusinessObjectDataStorageFileKey(
                businessObjectFormat.getNamespace(), businessObjectFormat.getBusinessObjectDefinitionName(),
                businessObjectFormat.getBusinessObjectFormatUsage(),
                businessObjectFormat.getBusinessObjectFormatFileType(),
                businessObjectFormat.getBusinessObjectFormatVersion(), businessObjectDataEntity.getPartitionValue(),
                businessObjectDataHelper.getSubPartitionValues(businessObjectDataEntity),
                businessObjectDataEntity.getVersion(), storageUnitEntity.getStorageName(),
                storageFileEntity.getPath());

        // Create the download business object data storage file single initiation response.
        DownloadBusinessObjectDataStorageFileSingleInitiationResponse downloadBusinessObjectDataStorageFileSingleInitiationResponse = new DownloadBusinessObjectDataStorageFileSingleInitiationResponse();
        downloadBusinessObjectDataStorageFileSingleInitiationResponse
                .setBusinessObjectDataStorageFileKey(businessObjectDataStorageFileKeyForResponse);
        downloadBusinessObjectDataStorageFileSingleInitiationResponse.setAwsS3BucketName(s3BucketName);
        downloadBusinessObjectDataStorageFileSingleInitiationResponse
                .setAwsAccessKey(downloaderCredentials.getAccessKeyId());
        downloadBusinessObjectDataStorageFileSingleInitiationResponse
                .setAwsSecretKey(downloaderCredentials.getSecretAccessKey());
        downloadBusinessObjectDataStorageFileSingleInitiationResponse
                .setAwsSessionToken(downloaderCredentials.getSessionToken());
        downloadBusinessObjectDataStorageFileSingleInitiationResponse
                .setAwsSessionExpirationTime(HerdDateUtils.getXMLGregorianCalendarValue(expiration));
        downloadBusinessObjectDataStorageFileSingleInitiationResponse.setPreSignedUrl(preSignedUrl);

        // Return the download business object data storage file single initiation response.
        return downloadBusinessObjectDataStorageFileSingleInitiationResponse;
    }

    /**
     * Gets a business object data key from a specified business object data storage file key.
     *
     * @param businessObjectDataStorageFileKey the business object data storage file key
     *
     * @return the business object data key
     */
    private BusinessObjectDataKey getBusinessObjectDataKeyFromBusinessObjectDataStorageFileKey(
            BusinessObjectDataStorageFileKey businessObjectDataStorageFileKey) {
        return new BusinessObjectDataKey(businessObjectDataStorageFileKey.getNamespace(),
                businessObjectDataStorageFileKey.getBusinessObjectDefinitionName(),
                businessObjectDataStorageFileKey.getBusinessObjectFormatUsage(),
                businessObjectDataStorageFileKey.getBusinessObjectFormatFileType(),
                businessObjectDataStorageFileKey.getBusinessObjectFormatVersion(),
                businessObjectDataStorageFileKey.getPartitionValue(),
                businessObjectDataStorageFileKey.getSubPartitionValues(),
                businessObjectDataStorageFileKey.getBusinessObjectDataVersion());
    }
}