text:
package org.finra.herd.service;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.io.IOException;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.persistence.PersistenceException;

import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.hibernate.engine.jdbc.spi.SqlExceptionHelper;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.util.CollectionUtils;

import org.finra.herd.core.Command;
import org.finra.herd.model.AlreadyExistsException;
import org.finra.herd.model.ObjectNotFoundException;
import org.finra.herd.model.api.xml.Attribute;
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.Storage;
import org.finra.herd.model.api.xml.StorageDirectory;
import org.finra.herd.model.api.xml.StorageFile;
import org.finra.herd.model.api.xml.StorageUnit;
import org.finra.herd.model.api.xml.StorageUnitCreateRequest;
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.BusinessObjectFormatEntity;
import org.finra.herd.model.jpa.StorageEntity;
import org.finra.herd.model.jpa.StoragePlatformEntity;
import org.finra.herd.model.jpa.StorageUnitEntity;
import org.finra.herd.model.jpa.StorageUnitStatusEntity;

/**
 * This class tests the createBusinessObjectData functionality within the business object data REST controller.
 */
public class BusinessObjectDataServiceCreateBusinessObjectDataTest extends AbstractServiceTest
{
    protected static Logger logger = Logger.getLogger(BusinessObjectDataServiceCreateBusinessObjectDataTest.class);

    /**
     * Sets up the test environment.
     */
    @Before
    public void setupEnv() throws IOException
    {
        // Create a local temp directory.
        localTempPath = Files.createTempDirectory(null);
    }

    /**
     * Cleans up the local temp directory and S3 test path that we are using.
     */
    @After
    public void cleanEnv() throws IOException
    {
        try
        {
            // Clean up the local directory.
            FileUtils.deleteDirectory(localTempPath.toFile());

            // Clean up the destination S3 folder.
            S3FileTransferRequestParamsDto s3FileTransferRequestParamsDto = getTestS3FileTransferRequestParamsDto();
            s3FileTransferRequestParamsDto.setS3KeyPrefix(testS3KeyPrefix);
            s3Dao.deleteDirectory(s3FileTransferRequestParamsDto);
        }
        catch (Exception ex)
        {
            // If an exception is thrown by one of the @Test methods, some cleanup operations could also fail. This is why we are just logging a warning here.
            logger.warn("Unable to cleanup environment.", ex);
        }
    }

    @Test
    public void testCreateBusinessObjectData()
    {
        // Create an initial version of the business object data.
        BusinessObjectDataCreateRequest businessObjectDataCreateRequest = getNewBusinessObjectDataCreateRequest();
        BusinessObjectData resultBusinessObjectData = businessObjectDataService.createBusinessObjectData(businessObjectDataCreateRequest);

        // Verify the results.
        validateBusinessObjectData(businessObjectDataCreateRequest, INITIAL_DATA_VERSION, true, resultBusinessObjectData);
    }

    @Test
    public void testCreateBusinessObjectDataMissingRequiredParameters()
    {
        // Create relative database entities.
        createBusinessObjectFormatEntity(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, FORMAT_DESCRIPTION, true,
            PARTITION_KEY);
        createStorageEntity(STORAGE_NAME);

        BusinessObjectDataCreateRequest request;
        List<StorageFile> storageFiles;

        // Try to create a business object data instance when business object definition name is not specified.
        request =
            createBusinessObjectDataCreateRequest(NAMESPACE_CD, BLANK_TEXT, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, PARTITION_KEY,
                PARTITION_VALUE, BDATA_STATUS, STORAGE_NAME, testS3KeyPrefix, getTestStorageFiles(testS3KeyPrefix, LOCAL_FILES));
        try
        {
            businessObjectDataService.createBusinessObjectData(request);
            fail("Should throw an IllegalArgumentException when business object definition name is not specified.");
        }
        catch (IllegalArgumentException e)
        {
            assertEquals("A business object definition name must be specified.", e.getMessage());
        }

        // Try to create a business object data instance when business object format usage is not specified.
        request = createBusinessObjectDataCreateRequest(NAMESPACE_CD, BOD_NAME, BLANK_TEXT, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, PARTITION_KEY,
            PARTITION_VALUE, BDATA_STATUS, STORAGE_NAME, testS3KeyPrefix, getTestStorageFiles(testS3KeyPrefix, LOCAL_FILES));
        try
        {
            businessObjectDataService.createBusinessObjectData(request);
            fail("Should throw an IllegalArgumentException when business object format usage is not specified.");
        }
        catch (IllegalArgumentException e)
        {
            assertEquals("A business object format usage name must be specified.", e.getMessage());
        }

        // Try to create a business object data instance when business object format file type is not specified.
        request =
            createBusinessObjectDataCreateRequest(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, BLANK_TEXT, INITIAL_FORMAT_VERSION, PARTITION_KEY, PARTITION_VALUE,
                BDATA_STATUS, STORAGE_NAME, testS3KeyPrefix, getTestStorageFiles(testS3KeyPrefix, LOCAL_FILES));
        try
        {
            businessObjectDataService.createBusinessObjectData(request);
            fail("Should throw an IllegalArgumentException when business object format file type is not specified.");
        }
        catch (IllegalArgumentException e)
        {
            assertEquals("A business object format file type must be specified.", e.getMessage());
        }

        // Try to create a business object data instance when business object format version is not specified.
        request = createBusinessObjectDataCreateRequest(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, null, PARTITION_KEY, PARTITION_VALUE,
            BDATA_STATUS, STORAGE_NAME, testS3KeyPrefix, getTestStorageFiles(testS3KeyPrefix, LOCAL_FILES));
        try
        {
            businessObjectDataService.createBusinessObjectData(request);
            fail("Should throw an IllegalArgumentException when business object format version is not specified.");
        }
        catch (IllegalArgumentException e)
        {
            assertEquals("A business object format version must be specified.", e.getMessage());
        }

        // Try to create a business object data instance when business object format partition key is not specified.
        request = createBusinessObjectDataCreateRequest(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, BLANK_TEXT,
            PARTITION_VALUE, BDATA_STATUS, STORAGE_NAME, testS3KeyPrefix, getTestStorageFiles(testS3KeyPrefix, LOCAL_FILES));
        try
        {
            businessObjectDataService.createBusinessObjectData(request);
            fail("Should throw an IllegalArgumentException when business object format partition key is not specified.");
        }
        catch (IllegalArgumentException e)
        {
            assertEquals("A business object format partition key must be specified.", e.getMessage());
        }

        // Try to create a business object data instance when business object data partition value is not specified.
        request = createBusinessObjectDataCreateRequest(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, PARTITION_KEY,
            BLANK_TEXT, BDATA_STATUS, STORAGE_NAME, testS3KeyPrefix, getTestStorageFiles(testS3KeyPrefix, LOCAL_FILES));
        try
        {
            businessObjectDataService.createBusinessObjectData(request);
            fail("Should throw an IllegalArgumentException when business object data partition value is not specified.");
        }
        catch (IllegalArgumentException e)
        {
            assertEquals("A business object data partition value must be specified.", e.getMessage());
        }

        // Try to create a business object data instance when request contains no storage units element.
        request = createBusinessObjectDataCreateRequest(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, PARTITION_KEY,
            PARTITION_VALUE, BDATA_STATUS, STORAGE_NAME, testS3KeyPrefix, getTestStorageFiles(testS3KeyPrefix, LOCAL_FILES));
        request.setStorageUnits(null);
        try
        {
            businessObjectDataService.createBusinessObjectData(request);
            fail("Should throw an IllegalArgumentException when when request contains no storage units element.");
        }
        catch (IllegalArgumentException e)
        {
            assertEquals("At least one storage unit must be specified.", e.getMessage());
        }

        // Try to create a business object data instance when no storage units are specified.
        request = createBusinessObjectDataCreateRequest(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, PARTITION_KEY,
            PARTITION_VALUE, BDATA_STATUS, STORAGE_NAME, testS3KeyPrefix, getTestStorageFiles(testS3KeyPrefix, LOCAL_FILES));
        request.setStorageUnits(new ArrayList<StorageUnitCreateRequest>());
        try
        {
            businessObjectDataService.createBusinessObjectData(request);
            fail("Should throw an IllegalArgumentException when when no storage units are specified.");
        }
        catch (IllegalArgumentException e)
        {
            assertEquals("At least one storage unit must be specified.", e.getMessage());
        }

        // Try to create a business object data instance when request contains an empty storage unit element.
        request = createBusinessObjectDataCreateRequest(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, PARTITION_KEY,
            PARTITION_VALUE, BDATA_STATUS, STORAGE_NAME, testS3KeyPrefix, getTestStorageFiles(testS3KeyPrefix, LOCAL_FILES));
        List<StorageUnitCreateRequest> storageUnits = new ArrayList<>();
        storageUnits.add(null);
        request.setStorageUnits(storageUnits);
        try
        {
            businessObjectDataService.createBusinessObjectData(request);
            fail("Should throw an IllegalArgumentException when when no storage units are specified.");
        }
        catch (IllegalArgumentException e)
        {
            assertEquals("A storage unit can't be null.", e.getMessage());
        }

        // Try to create a business object data instance when storage name is not specified for a storage unit.
        request = createBusinessObjectDataCreateRequest(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, PARTITION_KEY,
            PARTITION_VALUE, BDATA_STATUS, STORAGE_NAME, testS3KeyPrefix, getTestStorageFiles(testS3KeyPrefix, LOCAL_FILES));
        request.getStorageUnits().get(0).setStorageName(BLANK_TEXT);
        try
        {
            businessObjectDataService.createBusinessObjectData(request);
            fail("Should throw an IllegalArgumentException when storage name is not specified for a storage unit.");
        }
        catch (IllegalArgumentException e)
        {
            assertEquals("A storage name is required for each storage unit.", e.getMessage());
        }

        // Try to create a business object data instance when both storage directory and storage files are not specified.
        request = createBusinessObjectDataCreateRequest(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, PARTITION_KEY,
            PARTITION_VALUE, BDATA_STATUS, STORAGE_NAME, null, null);
        try
        {
            businessObjectDataService.createBusinessObjectData(request);
            fail("Should throw an IllegalArgumentException when both storage directory and storage files are not specified.");
        }
        catch (IllegalArgumentException e)
        {
            assertEquals("A storage directory or at least one storage file must be specified for each storage unit.", e.getMessage());
        }

        // Try to create a business object data instance when storage directory element is present, but the actual directory path value is not specified.
        request = createBusinessObjectDataCreateRequest(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, PARTITION_KEY,
            PARTITION_VALUE, BDATA_STATUS, STORAGE_NAME, BLANK_TEXT, getTestStorageFiles(testS3KeyPrefix, LOCAL_FILES));
        try
        {
            businessObjectDataService.createBusinessObjectData(request);
            fail("Should throw an IllegalArgumentException when storage directory element is present, but the actual directory path value is not specified.");
        }
        catch (IllegalArgumentException e)
        {
            assertEquals("A storage directory path must be specified.", e.getMessage());
        }

        // Try to create a business object data instance when storage file element is present, but the actual file path value is not specified.
        storageFiles = getTestStorageFiles(testS3KeyPrefix, Arrays.asList(LOCAL_FILE));
        storageFiles.get(0).setFilePath(BLANK_TEXT);
        request = createBusinessObjectDataCreateRequest(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, PARTITION_KEY,
            PARTITION_VALUE, BDATA_STATUS, STORAGE_NAME, testS3KeyPrefix, storageFiles);
        try
        {
            businessObjectDataService.createBusinessObjectData(request);
            fail("Should throw an IllegalArgumentException when storage file element is present, but the actual file path value is not specified.");
        }
        catch (IllegalArgumentException e)
        {
            assertEquals("A file path must be specified.", e.getMessage());
        }

        // Try to create a business object data instance when storage file size is not specified.
        storageFiles = getTestStorageFiles(testS3KeyPrefix, Arrays.asList(LOCAL_FILE));
        storageFiles.get(0).setFileSizeBytes(null);
        request = createBusinessObjectDataCreateRequest(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, PARTITION_KEY,
            PARTITION_VALUE, BDATA_STATUS, STORAGE_NAME, testS3KeyPrefix, storageFiles);
        try
        {
            businessObjectDataService.createBusinessObjectData(request);
            fail("Should throw an IllegalArgumentException when storage file size is not specified.");
        }
        catch (IllegalArgumentException e)
        {
            assertEquals("A file size must be specified.", e.getMessage());
        }
    }

    @Test
    public void testCreateBusinessObjectDataMissingOptionalParameters()
    {
        // Create relative database entities.
        createBusinessObjectFormatEntity(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, FORMAT_DESCRIPTION, true,
            PARTITION_KEY);
        createStorageEntity(STORAGE_NAME);

        // Create an initial version of business object data without specifying any of the optional parameters except for namespace.
        BusinessObjectDataCreateRequest request =
            createBusinessObjectDataCreateRequest(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, PARTITION_KEY,
                PARTITION_VALUE, BDATA_STATUS, STORAGE_NAME, testS3KeyPrefix, null);
        assertNull(request.getSubPartitionValues());
        request.setStatus(BLANK_TEXT);
        assertNull(request.getAttributes());
        assertNull(request.getStorageUnits().get(0).getStorageFiles());
        request.setCreateNewVersion(null);
        assertNull(request.getBusinessObjectDataParents());
        BusinessObjectData resultBusinessObjectData = businessObjectDataService.createBusinessObjectData(request);

        // Verify the results.
        validateBusinessObjectData(request, INITIAL_DATA_VERSION, true, resultBusinessObjectData);
    }

    @Test
    public void testCreateBusinessObjectDataNoStorageDirectory()
    {
        // Create an initial version of the business object data without specifying a storage directory.
        BusinessObjectDataCreateRequest request = getNewBusinessObjectDataCreateRequest();
        request.getStorageUnits().get(0).setStorageDirectory(null);
        BusinessObjectData resultBusinessObjectData = businessObjectDataService.createBusinessObjectData(request);

        // Verify the results.
        validateBusinessObjectData(request, INITIAL_DATA_VERSION, true, resultBusinessObjectData);
    }

    @Test
    public void testCreateBusinessObjectDataNoStorageFiles()
    {
        // Create the initial version of the business object data without specifying storage files.
        BusinessObjectDataCreateRequest request = getNewBusinessObjectDataCreateRequest();
        request.getStorageUnits().get(0).setStorageFiles(null);
        BusinessObjectData resultBusinessObjectData = businessObjectDataService.createBusinessObjectData(request);

        // Verify the results.
        validateBusinessObjectData(request, INITIAL_DATA_VERSION, true, resultBusinessObjectData);
    }

    @Test
    public void testCreateBusinessObjectDataNoAttributes()
    {
        // Create business object data with no attribute definitions and no attributes which is valid.
        BusinessObjectDataCreateRequest businessObjectDataCreateRequest = getNewBusinessObjectDataCreateRequest(false);
        businessObjectDataService.createBusinessObjectData(businessObjectDataCreateRequest);
    }

    @Test
    public void testCreateBusinessObjectDataTrimParameters()
    {
        // Create relative database entities.
        BusinessObjectFormatEntity businessObjectFormatEntity =
            createBusinessObjectFormatEntity(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, FORMAT_DESCRIPTION, true,
                PARTITION_KEY);
        createBusinessObjectDataStatusEntity(BDATA_STATUS);
        createStorageEntity(STORAGE_NAME);

        // Build a business object data create request with some of the request parameters having leading and trailing whitespace characters.
        BusinessObjectDataCreateRequest request =
            createBusinessObjectDataCreateRequest(NAMESPACE_CD, addWhitespace(BOD_NAME), addWhitespace(FORMAT_USAGE_CODE), addWhitespace(FORMAT_FILE_TYPE_CODE),
                INITIAL_FORMAT_VERSION, addWhitespace(PARTITION_KEY), addWhitespace(PARTITION_VALUE), addWhitespace(BDATA_STATUS), addWhitespace(STORAGE_NAME),
                addWhitespace(testS3KeyPrefix), getTestStorageFiles(testS3KeyPrefix, LOCAL_FILES));
        for (StorageFile storageFile : request.getStorageUnits().get(0).getStorageFiles())
        {
            storageFile.setFilePath(addWhitespace(storageFile.getFilePath()));
        }
        BusinessObjectData resultBusinessObjectData = businessObjectDataService.createBusinessObjectData(request);

        // Verify the results.
        validateBusinessObjectData(businessObjectFormatEntity, PARTITION_VALUE, NO_SUBPARTITION_VALUES, INITIAL_DATA_VERSION, true, BDATA_STATUS, STORAGE_NAME,
            testS3KeyPrefix, getTestStorageFiles(testS3KeyPrefix, SORTED_LOCAL_FILES),
            CollectionUtils.isEmpty(request.getAttributes()) ? new ArrayList<Attribute>() : request.getAttributes(), resultBusinessObjectData);
    }

    @Test
    public void testCreateBusinessObjectBusinessObjectStatusCodeNoExists()
    {
        // Create database entities required for testing.
        createBusinessObjectFormatEntity(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, FORMAT_VERSION, FORMAT_DESCRIPTION, true,
            PARTITION_KEY);
        createStorageEntity(STORAGE_NAME);

        // Build a business object data create request with a non-existing business object data status code.
        String invalidStatusCode = "I_DO_NOT_EXIST";
        BusinessObjectDataCreateRequest businessObjectDataCreateRequest =
            createBusinessObjectDataCreateRequest(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, FORMAT_VERSION, PARTITION_KEY,
                PARTITION_VALUE, invalidStatusCode, STORAGE_NAME, testS3KeyPrefix, getTestStorageFiles(testS3KeyPrefix, Arrays.asList(LOCAL_FILE)));

        try
        {
            // Try to create a business object data instance using non-existing business object data status code.
            businessObjectDataService.createBusinessObjectData(businessObjectDataCreateRequest);
            fail("Should throw an ObjectNotFoundException when business object data status code does not exist.");
        }
        catch (ObjectNotFoundException e)
        {
            assertEquals(String.format("Business object data status \"%s\" doesn't exist.", invalidStatusCode), e.getMessage());
        }
    }

    @Test
    public void testCreateBusinessObjectDataBusinessObjectFormatNoExists()
    {
        // Create database entities required for testing.
        createBusinessObjectFormatEntity(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, FORMAT_VERSION, FORMAT_DESCRIPTION, true,
            PARTITION_KEY);
        createBusinessObjectDataStatusEntity(BDATA_STATUS);
        createStorageEntity(STORAGE_NAME);

        // Build a business object data create request with a non-existing business object format.
        String invalidBusinessObjectFormatUsage = "I_DO_NOT_EXIST";
        BusinessObjectDataCreateRequest businessObjectDataCreateRequest =
            createBusinessObjectDataCreateRequest(NAMESPACE_CD, BOD_NAME, invalidBusinessObjectFormatUsage, FORMAT_FILE_TYPE_CODE, FORMAT_VERSION,
                PARTITION_KEY, PARTITION_VALUE, BDATA_STATUS, STORAGE_NAME, testS3KeyPrefix, getTestStorageFiles(testS3KeyPrefix, Arrays.asList(LOCAL_FILE)));

        try
        {
            // Try to create a business object data instance using non-existing business object format.
            businessObjectDataService.createBusinessObjectData(businessObjectDataCreateRequest);
            fail("Should throw an ObjectNotFoundException when a non-existing business object format is specified.");
        }
        catch (ObjectNotFoundException e)
        {
            assertEquals(getExpectedBusinessObjectFormatNotFoundErrorMessage(NAMESPACE_CD, BOD_NAME, invalidBusinessObjectFormatUsage, FORMAT_FILE_TYPE_CODE,
                FORMAT_VERSION), e.getMessage());
        }
    }

    @Test
    public void testCreateBusinessObjectDataInvalidPartitionKey()
    {
        // Create relative database entities.
        createBusinessObjectFormatEntity(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, FORMAT_DESCRIPTION, true,
            PARTITION_KEY);
        createBusinessObjectDataStatusEntity(BDATA_STATUS);

        // Try to create a business object data instance when an invalid partition key value is specified.
        BusinessObjectDataCreateRequest request =
            createBusinessObjectDataCreateRequest(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION,
                "INVALID_PARTITION_KEY", PARTITION_VALUE, BDATA_STATUS, STORAGE_NAME, testS3KeyPrefix,
                getTestStorageFiles(testS3KeyPrefix, Arrays.asList(LOCAL_FILE)));
        try
        {
            businessObjectDataService.createBusinessObjectData(request);
            fail("Should throw an IllegalArgumentException when an invalid partition key value is specified.");
        }
        catch (IllegalArgumentException e)
        {
            assertEquals(String
                .format("Partition key \"%s\" doesn't match configured business object format partition key \"%s\".", request.getPartitionKey(), PARTITION_KEY),
                e.getMessage());
        }
    }

    @Test
    public void testCreateBusinessObjectDataStorageNotFound()
    {
        // Create relative database entities.
        createBusinessObjectFormatEntity(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, FORMAT_DESCRIPTION, true,
            PARTITION_KEY);
        createBusinessObjectDataStatusEntity(BDATA_STATUS);

        // Build a business object data create request with a non-existing storage name.
        String invalidStorageName = "I_DO_NOT_EXIST";
        BusinessObjectDataCreateRequest businessObjectDataCreateRequest =
            createBusinessObjectDataCreateRequest(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, PARTITION_KEY,
                PARTITION_VALUE, BDATA_STATUS, invalidStorageName, testS3KeyPrefix, getTestStorageFiles(testS3KeyPrefix, Arrays.asList(LOCAL_FILE)));

        try
        {
            // Try to create a business object data instance using a non-existing storage.
            businessObjectDataService.createBusinessObjectData(businessObjectDataCreateRequest);
            fail("Should throw an ObjectNotFoundException when specified storage name does not exist.");
        }
        catch (ObjectNotFoundException e)
        {
            assertEquals(String.format("Storage with name \"%s\" doesn't exist.", invalidStorageName), e.getMessage());
        }
    }

    @Test
    public void testCreateBusinessObjectDataInvalidStorageFile()
    {
        // Create relative database entities.
        createBusinessObjectFormatEntity(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, FORMAT_DESCRIPTION, true,
            PARTITION_KEY);
        createBusinessObjectDataStatusEntity(BDATA_STATUS);
        createStorageEntity(STORAGE_NAME);

        // Build a business object data create request with a storage file path not matching the storage directory path.
        String wrongS3KeyPrefix = "WRONG_S3_KEY_PREFIX";
        BusinessObjectDataCreateRequest request =
            createBusinessObjectDataCreateRequest(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, PARTITION_KEY,
                PARTITION_VALUE, BDATA_STATUS, STORAGE_NAME, testS3KeyPrefix, getTestStorageFiles(wrongS3KeyPrefix, Arrays.asList(LOCAL_FILE)));

        try
        {
            // Try to create a business object data instance.
            businessObjectDataService.createBusinessObjectData(request);
            fail("Should throw an IllegalArgumentException when a storage storage file path does not match the storage directory path.");
        }
        catch (IllegalArgumentException e)
        {
            assertEquals(
                String.format("Storage file path \"%s/%s\" does not match the storage directory path \"%s\".", wrongS3KeyPrefix, LOCAL_FILE, testS3KeyPrefix),
                e.getMessage());
        }
    }

    @Test
    public void testCreateBusinessObjectDataValidateFileExistenceNoValidatePrefix() throws Exception
    {
        // Create an S3 storage entity.
        List<Attribute> attributes = new ArrayList<>();
        attributes.add(new Attribute(configurationHelper.getProperty(ConfigurationValue.S3_ATTRIBUTE_NAME_BUCKET_NAME), S3_BUCKET_NAME));
        attributes.add(
            new Attribute(configurationHelper.getProperty(ConfigurationValue.S3_ATTRIBUTE_NAME_VALIDATE_FILE_EXISTENCE), Boolean.TRUE.toString()));
        createStorageEntity(STORAGE_NAME, StoragePlatformEntity.S3, attributes);

        // Create relative database entities.
        createBusinessObjectFormatEntity(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, FORMAT_DESCRIPTION, true,
            PARTITION_KEY);
        createBusinessObjectDataStatusEntity(BDATA_STATUS);

        // Build a list of storage files.
        List<StorageFile> storageFiles = getTestStorageFiles(testS3KeyPrefix, LOCAL_FILES);

        // Create and upload to S3 managed storage a set of test files.
        prepareTestS3Files(S3_BUCKET_NAME, testS3KeyPrefix, LOCAL_FILES, new ArrayList<String>());

        // Build a new business object data create request.
        BusinessObjectDataCreateRequest request =
            createBusinessObjectDataCreateRequest(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, PARTITION_KEY,
                PARTITION_VALUE, BDATA_STATUS, STORAGE_NAME, testS3KeyPrefix, storageFiles);

        // Try to create a business object data instance. It should go through without any errors.
        businessObjectDataService.createBusinessObjectData(request);
    }

    @Test
    public void testCreateBusinessObjectDataValidateFileExistenceNoValidatePrefixDirectoryInvalid() throws Exception
    {
        // Create an S3 storage entity.
        List<Attribute> attributes = new ArrayList<>();
        attributes.add(new Attribute(configurationHelper.getProperty(ConfigurationValue.S3_ATTRIBUTE_NAME_BUCKET_NAME), S3_BUCKET_NAME));
        attributes.add(
            new Attribute(configurationHelper.getProperty(ConfigurationValue.S3_ATTRIBUTE_NAME_VALIDATE_FILE_EXISTENCE), Boolean.TRUE.toString()));
        createStorageEntity(STORAGE_NAME, StoragePlatformEntity.S3, attributes);

        // Create relative database entities.
        createBusinessObjectFormatEntity(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, FORMAT_DESCRIPTION, true,
            PARTITION_KEY);
        createBusinessObjectDataStatusEntity(BDATA_STATUS);

        // Build a list of storage files.
        List<StorageFile> storageFiles = getTestStorageFiles(testS3KeyPrefix, LOCAL_FILES);

        // Create and upload to S3 managed storage a set of test files.
        prepareTestS3Files(S3_BUCKET_NAME, testS3KeyPrefix, LOCAL_FILES, new ArrayList<String>());

        // Build a new business object data create request.
        BusinessObjectDataCreateRequest request =
            createBusinessObjectDataCreateRequest(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, PARTITION_KEY,
                PARTITION_VALUE, BDATA_STATUS, STORAGE_NAME, "INVALID_DIRECTORY_PATH", storageFiles);

        try
        {
            // Try to create a business object data instance.
            businessObjectDataService.createBusinessObjectData(request);
            fail("Should throw an IllegalArgumentException when a directory path doesn't match the S3 uploaded files.");
        }
        catch (IllegalArgumentException e)
        {
            assertTrue(e.getMessage().contains(String.format("does not match the storage directory path \"%s\"", "INVALID_DIRECTORY_PATH")));
        }
    }

    @Test
    public void testCreateBusinessObjectDataInvalidValidationBooleanValue()
    {
        // Create an S3 storage entity.
        List<Attribute> attributes = new ArrayList<>();
        attributes.add(new Attribute(configurationHelper.getProperty(ConfigurationValue.S3_ATTRIBUTE_NAME_BUCKET_NAME), S3_BUCKET_NAME));
        attributes.add(
            new Attribute(configurationHelper.getProperty(ConfigurationValue.S3_ATTRIBUTE_NAME_VALIDATE_FILE_EXISTENCE), INVALID_BOOLEAN_VALUE));
        createStorageEntity(STORAGE_NAME, StoragePlatformEntity.S3, attributes);

        // Create relative database entities.
        createBusinessObjectFormatEntity(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, FORMAT_DESCRIPTION, true,
            PARTITION_KEY);
        createBusinessObjectDataStatusEntity(BDATA_STATUS);

        // Build a list of storage files.
        List<StorageFile> storageFiles = getTestStorageFiles(testS3KeyPrefix, SORTED_LOCAL_FILES);

        // Build a new business object data create request.
        BusinessObjectDataCreateRequest request =
            createBusinessObjectDataCreateRequest(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, PARTITION_KEY,
                PARTITION_VALUE, BDATA_STATUS, STORAGE_NAME, testS3KeyPrefix, storageFiles);

        try
        {
            // Try to create a business object data instance.
            businessObjectDataService.createBusinessObjectData(request);
            fail("Should throw an IllegalStateException when a validation boolean value is invalid.");
        }
        catch (IllegalStateException e)
        {
            assertEquals(String.format("Attribute \"%s\" for \"%s\" storage has an invalid boolean value: " + "\"%s\".",
                ConfigurationValue.S3_ATTRIBUTE_NAME_VALIDATE_FILE_EXISTENCE.getDefaultValue(), STORAGE_NAME, INVALID_BOOLEAN_VALUE), e.getMessage());
        }
    }

    @Test
    public void testCreateBusinessObjectDataValidateFlagIgnoredForNonS3Storage() throws Exception
    {
        // Create an S3 storage entity.
        List<Attribute> attributes = new ArrayList<>();
        attributes.add(new Attribute(configurationHelper.getProperty(ConfigurationValue.S3_ATTRIBUTE_NAME_BUCKET_NAME), S3_BUCKET_NAME));
        attributes.add(
            new Attribute(configurationHelper.getProperty(ConfigurationValue.S3_ATTRIBUTE_NAME_VALIDATE_FILE_EXISTENCE), Boolean.TRUE.toString()));
        createStorageEntity(STORAGE_NAME, "NON_S3_STORAGE", attributes);

        // Create relative database entities.
        createBusinessObjectFormatEntity(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, FORMAT_DESCRIPTION, true,
            PARTITION_KEY);
        createBusinessObjectDataStatusEntity(BDATA_STATUS);

        // Build a list of storage files.
        List<StorageFile> storageFiles = getTestStorageFiles(testS3KeyPrefix, LOCAL_FILES);

        // Build a new business object data create request.
        BusinessObjectDataCreateRequest request =
            createBusinessObjectDataCreateRequest(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, PARTITION_KEY,
                PARTITION_VALUE, BDATA_STATUS, STORAGE_NAME, testS3KeyPrefix, storageFiles);

        // Try to create a business object data instance. This should succeed since the bucket is not of type "S3", even though there are no files in S3.
        businessObjectDataService.createBusinessObjectData(request);
    }

    @Test
    public void testCreateBusinessObjectDataInvalidStorageFileRowCount()
    {
        // Create relative database entities.
        createBusinessObjectFormatEntity(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, FORMAT_DESCRIPTION, true,
            PARTITION_KEY);
        createStorageEntity(STORAGE_NAME);

        // Try to create a business object data instance when storage file row count has a negative value.
        List<StorageFile> storageFiles = getTestStorageFiles(testS3KeyPrefix, Arrays.asList(LOCAL_FILE));
        storageFiles.get(0).setRowCount(-1L);
        BusinessObjectDataCreateRequest request =
            createBusinessObjectDataCreateRequest(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, PARTITION_KEY,
                PARTITION_VALUE, BDATA_STATUS, STORAGE_NAME, testS3KeyPrefix, storageFiles);
        try
        {
            businessObjectDataService.createBusinessObjectData(request);
            fail("Should throw an IllegalArgumentException when storage file size is not specified.");
        }
        catch (IllegalArgumentException e)
        {
            assertEquals(String.format("File \"%s/%s\" has a row count which is < 0.", testS3KeyPrefix, LOCAL_FILE), e.getMessage());
        }
    }

    @Test
    public void testCreateBusinessObjectDataInitialDataVersionExists()
    {
        // Create relative database entities including the initial version of the business object data.
        createBusinessObjectDataEntity(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, PARTITION_VALUE,
            INITIAL_DATA_VERSION, true, BDATA_STATUS);
        createStorageEntity(STORAGE_NAME);

        // Build a list of storage files.
        List<StorageFile> storageFiles = getTestStorageFiles(testS3KeyPrefix, SORTED_LOCAL_FILES);

        // Build a new business object data create request.
        BusinessObjectDataCreateRequest request =
            createBusinessObjectDataCreateRequest(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, PARTITION_KEY,
                PARTITION_VALUE, BDATA_STATUS, STORAGE_NAME, testS3KeyPrefix, storageFiles);

        // Try to create the second version of the business object data (version 1) with createNewVersion flag set to null and to false.
        for (Boolean createNewVersionFlag : new Boolean[] {null, Boolean.FALSE})
        {
            request.setCreateNewVersion(createNewVersionFlag);
            try
            {
                businessObjectDataService.createBusinessObjectData(request);
                fail(String.format("Should throw an IllegalArgumentException when the initial data version exists and createNewVersion flag is set to \"%s\".",
                    createNewVersionFlag));
            }
            catch (AlreadyExistsException e)
            {
                assertEquals("Unable to create business object data because it already exists.", e.getMessage());
            }
        }

        // Try to create the second version of the business object data with createNewVersion flag set to true.
        request.setCreateNewVersion(true);
        BusinessObjectData resultBusinessObjectData = businessObjectDataService.createBusinessObjectData(request);

        // Validate the results for the second version if the business object data.
        validateBusinessObjectData(request, SECOND_DATA_VERSION, true, resultBusinessObjectData);

        // Confirm that the initial version of the business object data now does not have the latestFlag set.
        BusinessObjectDataEntity initialVersionBusinessObjectDataEntity = herdDao.getBusinessObjectDataByAltKey(
            new BusinessObjectDataKey(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, PARTITION_VALUE,
                NO_SUBPARTITION_VALUES, INITIAL_DATA_VERSION));
        assertEquals(false, initialVersionBusinessObjectDataEntity.getLatestVersion());
    }

    @Test(expected = IllegalArgumentException.class)
    public void testCreateBusinessObjectDataMissingRequiredAttribute()
    {
        // This will create a business object data create request with attributes. It will be associated with a format that has attribute definitions
        // that require the attribute to be specified.
        BusinessObjectDataCreateRequest businessObjectDataCreateRequest = getNewBusinessObjectDataCreateRequest(true);

        // Null out the attributes in the create request, even though the format requires them.
        businessObjectDataCreateRequest.setAttributes(null);

        // Create the business object data which should fail since required attributes are missing.
        businessObjectDataService.createBusinessObjectData(businessObjectDataCreateRequest);
    }

    @Test
    public void testCreateBusinessObjectDataDuplicateAttributeNames() throws Exception
    {
        // Create relative database entities.
        createBusinessObjectFormatEntity(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, FORMAT_DESCRIPTION, true,
            PARTITION_KEY);
        createStorageEntity(STORAGE_NAME);

        // Try to create a business object data instance when duplicate attribute names are specified.
        // Ensure different cases are still considered a duplicate.
        BusinessObjectDataCreateRequest request =
            createBusinessObjectDataCreateRequest(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, PARTITION_KEY,
                PARTITION_VALUE, BDATA_STATUS, STORAGE_NAME, testS3KeyPrefix, getTestStorageFiles(testS3KeyPrefix, LOCAL_FILES));
        List<Attribute> attributes = new ArrayList<>();
        request.setAttributes(attributes);
        attributes.add(new Attribute(ATTRIBUTE_NAME_1_MIXED_CASE.toUpperCase(), ATTRIBUTE_VALUE_1));
        attributes.add(new Attribute(ATTRIBUTE_NAME_1_MIXED_CASE.toLowerCase(), ATTRIBUTE_VALUE_1));
        try
        {
            // Try to create a business object data instance.
            businessObjectDataService.createBusinessObjectData(request);
            fail("Should throw an IllegalArgumentException when duplicate attribute names are specified.");
        }
        catch (IllegalArgumentException e)
        {
            assertEquals(String.format("Duplicate attribute name found: %s", ATTRIBUTE_NAME_1_MIXED_CASE.toLowerCase()), e.getMessage());
        }
    }

    @Test(expected = PersistenceException.class)
    public void testCreateBusinessObjectDataAttributeValueTooLarge() throws Exception
    {
        final BusinessObjectDataCreateRequest businessObjectDataCreateRequest = getNewBusinessObjectDataCreateRequest();

        // Create and add a duplicate attribute which is not allowed.
        Attribute newAttribute = new Attribute();
        newAttribute.setName("Valid Name");
        newAttribute.setValue(new String(new char[4001]).replace('\0', 'A')); // Test value greater than 4000 byte limit.
        businessObjectDataCreateRequest.getAttributes().add(newAttribute);

        executeWithoutLogging(SqlExceptionHelper.class, new Command()
        {
            @Override
            public void execute()
            {
                // Create the business object data which is invalid.
                businessObjectDataService.createBusinessObjectData(businessObjectDataCreateRequest);
            }
        });
    }

    @Test
    public void testCreateBusinessObjectDataS3ManagedBucket() throws Exception
    {
        // Create relative database entities.
        BusinessObjectFormatEntity businessObjectFormatEntity =
            createBusinessObjectFormatEntity(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, FORMAT_DESCRIPTION,
                false, PARTITION_KEY);
        createBusinessObjectDataStatusEntity(BDATA_STATUS);

        // Create and upload to S3 managed storage a set of test files.
        prepareTestS3Files(testS3KeyPrefix, LOCAL_FILES);

        // Build a new business object data create request.
        BusinessObjectDataCreateRequest request =
            createBusinessObjectDataCreateRequest(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, PARTITION_KEY,
                PARTITION_VALUE, BDATA_STATUS, StorageEntity.MANAGED_STORAGE, testS3KeyPrefix, getTestStorageFiles(testS3KeyPrefix, LOCAL_FILES));

        // Create the business object data.
        BusinessObjectData resultBusinessObjectData = businessObjectDataService.createBusinessObjectData(request);

        // Verify the results.
        validateBusinessObjectData(businessObjectFormatEntity, PARTITION_VALUE, NO_SUBPARTITION_VALUES, INITIAL_DATA_VERSION, true, BDATA_STATUS,
            StorageEntity.MANAGED_STORAGE, testS3KeyPrefix, getTestStorageFiles(testS3KeyPrefix, SORTED_LOCAL_FILES),
            CollectionUtils.isEmpty(request.getAttributes()) ? new ArrayList<Attribute>() : request.getAttributes(), resultBusinessObjectData);
    }

    @Test
    public void testCreateBusinessObjectDataS3ManagedBucketInvalidStorageDirectory()
    {
        // Create relative database entities.
        createBusinessObjectFormatEntity(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, FORMAT_DESCRIPTION, true,
            PARTITION_KEY);
        createBusinessObjectDataStatusEntity(BDATA_STATUS);

        // Build a business object data create request with directory path not matching the expected S3 key prefix.
        String invalidS3KeyPrefix = "INVALID_S3_KEY_PREFIX";
        BusinessObjectDataCreateRequest request =
            createBusinessObjectDataCreateRequest(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, PARTITION_KEY,
                PARTITION_VALUE, BDATA_STATUS, StorageEntity.MANAGED_STORAGE, invalidS3KeyPrefix, getTestStorageFiles(testS3KeyPrefix, LOCAL_FILES));

        try
        {
            // Try to create a business object data instance.
            businessObjectDataService.createBusinessObjectData(request);
            fail("Should throw an IllegalArgumentException when a storage directory path in S3 managed storage does not match the expected S3 key prefix.");
        }
        catch (IllegalArgumentException e)
        {
            assertEquals(
                String.format("Specified directory path \"%s\" does not match the expected S3 key prefix \"%s\".", invalidS3KeyPrefix, testS3KeyPrefix),
                e.getMessage());
        }
    }

    @Test
    public void testCreateBusinessObjectDataS3ManagedBucketInvalidStorageFile()
    {
        // Create relative database entities.
        createBusinessObjectFormatEntity(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, FORMAT_DESCRIPTION, true,
            PARTITION_KEY);
        createBusinessObjectDataStatusEntity(BDATA_STATUS);

        // Build a business object data create request with a storage file path not matching the expected S3 key prefix.
        String invalidS3KeyPrefix = "INVALID_S3_KEY_PREFIX";
        BusinessObjectDataCreateRequest request =
            createBusinessObjectDataCreateRequest(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, PARTITION_KEY,
                PARTITION_VALUE, BDATA_STATUS, StorageEntity.MANAGED_STORAGE, testS3KeyPrefix,
                getTestStorageFiles(invalidS3KeyPrefix, Arrays.asList(LOCAL_FILE)));

        try
        {
            // Try to create a business object data instance.
            businessObjectDataService.createBusinessObjectData(request);
            fail("Should throw an IllegalArgumentException when a storage storage file path in S3 managed storage does not match the expected S3 key prefix.");
        }
        catch (IllegalArgumentException e)
        {
            assertEquals(String
                .format("Specified storage file path \"%s/%s\" does not match the expected S3 key prefix \"%s\".", invalidS3KeyPrefix, LOCAL_FILE,
                    testS3KeyPrefix), e.getMessage());
        }
    }

    @Test
    public void testCreateBusinessObjectDataS3ManagedBucketDirectoryPathAlreadyRegistered()
    {
        // Create relative database entities including a storage unit for the business object data with PARTITION_VALUE_2 partition value,
        // but with a directory path that would actually match with a test business object data with PARTITION_VALUE partition value.
        BusinessObjectDataEntity businessObjectDataEntity =
            createBusinessObjectDataEntity(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, PARTITION_VALUE_2,
                INITIAL_DATA_VERSION, true, BDATA_STATUS);
        createStorageUnitEntity(herdDao.getStorageByName(StorageEntity.MANAGED_STORAGE), businessObjectDataEntity, StorageUnitStatusEntity.ENABLED,
            testS3KeyPrefix);

        // Build a new business object data create request containing the already registered storage directory path.
        BusinessObjectDataCreateRequest request =
            createBusinessObjectDataCreateRequest(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, PARTITION_KEY,
                PARTITION_VALUE, BDATA_STATUS, StorageEntity.MANAGED_STORAGE, testS3KeyPrefix, getTestStorageFiles(testS3KeyPrefix, Arrays.asList(LOCAL_FILE)));

        try
        {
            // Try to create a business object data instance.
            businessObjectDataService.createBusinessObjectData(request);
            fail("Should throw an AlreadyExistsException when directory path in S3 managed " +
                "storage matches the location of an already registered business object data.");
        }
        catch (AlreadyExistsException e)
        {
            assertEquals(String.format("Storage directory \"%s\" in \"%s\" storage is already registered by the business object data {%s}.", testS3KeyPrefix,
                StorageEntity.MANAGED_STORAGE,
                getExpectedBusinessObjectDataKeyAsString(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION,
                    PARTITION_VALUE_2, NO_SUBPARTITION_VALUES, INITIAL_DATA_VERSION)), e.getMessage());
        }
    }

    @Test
    public void testCreateBusinessObjectDataS3ManagedBucketFileAlreadyRegistered()
    {
        // Create relative database entities including a storage file entity registered by a test business object data with PARTITION_VALUE_2 partition value.
        BusinessObjectDataEntity businessObjectDataEntity =
            createBusinessObjectDataEntity(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, PARTITION_VALUE_2,
                INITIAL_DATA_VERSION, true, BDATA_STATUS);
        StorageUnitEntity storageUnitEntity =
            createStorageUnitEntity(herdDao.getStorageByName(StorageEntity.MANAGED_STORAGE), businessObjectDataEntity, StorageUnitStatusEntity.ENABLED,
                NO_STORAGE_DIRECTORY_PATH);
        createStorageFileEntity(storageUnitEntity, String.format("%s/%s", testS3KeyPrefix, LOCAL_FILE), FILE_SIZE_1_KB, ROW_COUNT_1000);

        // Build a new business object data create request containing the already registered storage file.
        BusinessObjectDataCreateRequest request =
            createBusinessObjectDataCreateRequest(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, PARTITION_KEY,
                PARTITION_VALUE, BDATA_STATUS, StorageEntity.MANAGED_STORAGE, testS3KeyPrefix, getTestStorageFiles(testS3KeyPrefix, Arrays.asList(LOCAL_FILE)));

        try
        {
            // Try to create a business object data instance.
            businessObjectDataService.createBusinessObjectData(request);
            fail("Should throw an AlreadyExistsException when a storage file in S3 managed storage is already registered by another business object data.");
        }
        catch (AlreadyExistsException e)
        {
            assertEquals(String
                .format("Found 1 storage file(s) matching \"%s\" S3 key prefix in \"%s\" storage that is registered with another business object data.",
                    testS3KeyPrefix, StorageEntity.MANAGED_STORAGE), e.getMessage());
        }
    }

    @Test
    public void testCreateBusinessObjectDataS3ManagedBucketS3FileNotFound()
    {
        // Create relative database entities.
        createBusinessObjectFormatEntity(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, FORMAT_DESCRIPTION, false,
            PARTITION_KEY);
        createBusinessObjectDataStatusEntity(BDATA_STATUS);

        // Build a new business object data create request with a storage file which was not uploaded to S3 managed storage.
        BusinessObjectDataCreateRequest businessObjectDataCreateRequest =
            createBusinessObjectDataCreateRequest(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, PARTITION_KEY,
                PARTITION_VALUE, BDATA_STATUS, StorageEntity.MANAGED_STORAGE, testS3KeyPrefix, getTestStorageFiles(testS3KeyPrefix, Arrays.asList(LOCAL_FILE)));

        try
        {
            // Try to create a business object data instance.
            businessObjectDataService.createBusinessObjectData(businessObjectDataCreateRequest);
            fail("Should throw an ObjectNotFoundException when a storage file does not exist in S3 managed storage.");
        }
        catch (ObjectNotFoundException e)
        {
            assertEquals(String.format("File not found at s3://%s/%s/%s location.", getS3ManagedBucketName(), testS3KeyPrefix, LOCAL_FILE), e.getMessage());
        }
    }

    @Test
    public void testCreateBusinessObjectDataS3ManagedBucketWithZeroByteDirectoryMarkers() throws Exception
    {
        // Create relative database entities.
        BusinessObjectFormatEntity businessObjectFormatEntity =
            createBusinessObjectFormatEntity(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, FORMAT_DESCRIPTION,
                false, PARTITION_KEY);
        createBusinessObjectDataStatusEntity(BDATA_STATUS);

        // Place test files and 0 byte S3 directory markers in the S3 managed storage.
        prepareTestS3Files(testS3KeyPrefix, LOCAL_FILES, S3_DIRECTORY_MARKERS);

        // Build a new business object data create request.
        BusinessObjectDataCreateRequest request =
            createBusinessObjectDataCreateRequest(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, PARTITION_KEY,
                PARTITION_VALUE, BDATA_STATUS, StorageEntity.MANAGED_STORAGE, testS3KeyPrefix, getTestStorageFiles(testS3KeyPrefix, LOCAL_FILES));

        // Create the business object data.
        BusinessObjectData resultBusinessObjectData = businessObjectDataService.createBusinessObjectData(request);

        // Verify the results.
        validateBusinessObjectData(businessObjectFormatEntity, PARTITION_VALUE, NO_SUBPARTITION_VALUES, INITIAL_DATA_VERSION, true, BDATA_STATUS,
            StorageEntity.MANAGED_STORAGE, testS3KeyPrefix, getTestStorageFiles(testS3KeyPrefix, SORTED_LOCAL_FILES),
            CollectionUtils.isEmpty(request.getAttributes()) ? new ArrayList<Attribute>() : request.getAttributes(), resultBusinessObjectData);
    }

    @Test
    public void testCreateBusinessObjectDataDuplicateParents()
    {
        // This will create a business object data create request with parents.
        BusinessObjectDataCreateRequest businessObjectDataCreateRequest = getNewBusinessObjectDataCreateRequest(true);

        // Add a duplicate parent.
        List<BusinessObjectDataKey> businessObjectDataKeys = businessObjectDataCreateRequest.getBusinessObjectDataParents();
        businessObjectDataKeys.add(businessObjectDataKeys.get(0));

        // Try to create a business object data with duplicate parents.
        try
        {
            businessObjectDataService.createBusinessObjectData(businessObjectDataCreateRequest);
            fail("Should throw an IllegalArgumentException when business object data create request contains duplicate parents.");
        }
        catch (IllegalArgumentException e)
        {
            assertEquals("Business object data keys can not contain duplicates.", e.getMessage());
        }
    }

    @Test
    public void testCreateBusinessObjectDataParentMissingBusinessObjectFormatVersion()
    {
        // This will create a business object data create request with parents.
        BusinessObjectDataCreateRequest businessObjectDataCreateRequest = getNewBusinessObjectDataCreateRequest(true);

        // Remove the business object format version required field.
        List<BusinessObjectDataKey> businessObjectDataKeys = businessObjectDataCreateRequest.getBusinessObjectDataParents();
        businessObjectDataKeys.get(0).setBusinessObjectFormatVersion(null);

        // Try to create a business object data which should fail since one of the required attributes is missing.
        try
        {
            businessObjectDataService.createBusinessObjectData(businessObjectDataCreateRequest);
            fail("Should throw an IllegalArgumentException when business object format version is not specified for a business object data parent.");
        }
        catch (IllegalArgumentException e)
        {
            assertEquals("A business object format version must be specified.", e.getMessage());
        }
    }

    @Test
    public void testCreateBusinessObjectDataParentMissingPartitionValue()
    {
        // This will create a business object data create request with parents.
        BusinessObjectDataCreateRequest businessObjectDataCreateRequest = getNewBusinessObjectDataCreateRequest(true);

        // Remove the partition value required field.
        List<BusinessObjectDataKey> businessObjectDataKeys = businessObjectDataCreateRequest.getBusinessObjectDataParents();
        businessObjectDataKeys.get(0).setPartitionValue(null);

        // Try to create a business object data which should fail since one of the required attributes is missing.
        try
        {
            businessObjectDataService.createBusinessObjectData(businessObjectDataCreateRequest);
            fail("Should throw an IllegalArgumentException when partition value is not specified for a business object data parent.");
        }
        catch (IllegalArgumentException e)
        {
            assertEquals("A business object data partition value must be specified.", e.getMessage());
        }
    }

    @Test
    public void testCreateBusinessObjectDataParentMissingBusinessObjectDataVersion()
    {
        // This will create a business object data create request with parents.
        BusinessObjectDataCreateRequest businessObjectDataCreateRequest = getNewBusinessObjectDataCreateRequest(true);

        // Remove the business object data version required field.
        List<BusinessObjectDataKey> businessObjectDataKeys = businessObjectDataCreateRequest.getBusinessObjectDataParents();
        businessObjectDataKeys.get(0).setBusinessObjectDataVersion(null);

        // Try to create a business object data which should fail since one of the required attributes is missing.
        try
        {
            businessObjectDataService.createBusinessObjectData(businessObjectDataCreateRequest);
            fail("Should throw an IllegalArgumentException when business object data version is not specified for a business object data parent.");
        }
        catch (IllegalArgumentException e)
        {
            assertEquals("A business object data version must be specified.", e.getMessage());
        }
    }

    @Test(expected = ObjectNotFoundException.class)
    public void testCreateBusinessObjectDataParentNoExists()
    {
        // This will create a business object data create request with parents.
        BusinessObjectDataCreateRequest businessObjectDataCreateRequest = getNewBusinessObjectDataCreateRequest(true);

        // Set the partition value to a business object data that doesn't exist.
        List<BusinessObjectDataKey> businessObjectDataKeys = businessObjectDataCreateRequest.getBusinessObjectDataParents();
        businessObjectDataKeys.get(0).setPartitionValue("Invalid_Partition_Value");

        // Create the business object data which should fail since required attributes are missing.
        businessObjectDataService.createBusinessObjectData(businessObjectDataCreateRequest);
    }

    /**
     * This test case validates that business object data registration succeeds even when specified business object data parents form a circular dependency. We
     * have B registered B as a child of C and C registered as a child of B (B->C and C->B), and now we try to register A as a child of C.  That business object
     * data registration is expected not to fail due to a pre-existing circular dependency between B and C.  Circular dependency for the already registered
     * business object data instances is an edge case and should not happen for normal operations and can only occur if the database was modified directly.
     */
    @Test
    public void testCreateBusinessObjectDataIgnoringCircularDependency()
    {
        // Create two business object data keys.
        BusinessObjectDataKey alphaBusinessObjectDataKey =
            new BusinessObjectDataKey(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, FORMAT_VERSION, PARTITION_VALUE, SUBPARTITION_VALUES,
                DATA_VERSION);
        BusinessObjectDataKey betaBusinessObjectDataKey =
            new BusinessObjectDataKey(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE_2, FORMAT_FILE_TYPE_CODE, FORMAT_VERSION, PARTITION_VALUE, SUBPARTITION_VALUES,
                DATA_VERSION);

        // Create the relative business object data entities.
        BusinessObjectDataEntity alphaBusinessObjectDataEntity = createBusinessObjectDataEntity(alphaBusinessObjectDataKey, true, BDATA_STATUS);
        BusinessObjectDataEntity betaBusinessObjectDataEntity = createBusinessObjectDataEntity(betaBusinessObjectDataKey, true, BDATA_STATUS);

        // Associate with each other the two business object data entities created above, so we get a circular dependency.
        // Make "alpha" a parent of "beta".
        alphaBusinessObjectDataEntity.getBusinessObjectDataChildren().add(betaBusinessObjectDataEntity);
        betaBusinessObjectDataEntity.getBusinessObjectDataParents().add(alphaBusinessObjectDataEntity);
        // Make "beta" a parent of "alpha".
        betaBusinessObjectDataEntity.getBusinessObjectDataChildren().add(alphaBusinessObjectDataEntity);
        alphaBusinessObjectDataEntity.getBusinessObjectDataParents().add(betaBusinessObjectDataEntity);

        // Create other database entities required for testing.
        createStorageEntity(STORAGE_NAME);

        // Create a business object data create request with one of the entities created above listed as a parent.
        BusinessObjectDataCreateRequest request =
            createBusinessObjectDataCreateRequest(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, FORMAT_VERSION, PARTITION_KEY,
                PARTITION_VALUE_2, BDATA_STATUS, STORAGE_NAME, testS3KeyPrefix, getTestStorageFiles(testS3KeyPrefix, Arrays.asList(LOCAL_FILE)));
        List<BusinessObjectDataKey> parents = new ArrayList<>();
        request.setBusinessObjectDataParents(parents);
        parents.add(betaBusinessObjectDataKey);

        // Create a business object data when a parent is part of a circular dependency.
        BusinessObjectData resultBusinessObjectData = businessObjectDataService.createBusinessObjectData(request);

        // Validate the results.
        assertNotNull(resultBusinessObjectData);
        assertNotNull(resultBusinessObjectData.getBusinessObjectDataParents());
        assertEquals(1, resultBusinessObjectData.getBusinessObjectDataParents().size());
        assertTrue(resultBusinessObjectData.getBusinessObjectDataParents().contains(betaBusinessObjectDataKey));
    }

    @Test
    public void testCreateBusinessObjectDataDiscoverStorageFiles() throws Exception
    {
        // Create a business object format entity.
        createBusinessObjectFormatEntity(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, FORMAT_DESCRIPTION,
            LATEST_VERSION_FLAG_SET, PARTITION_KEY);

        // Create a business object data status entity.
        createBusinessObjectDataStatusEntity(BDATA_STATUS);

        // Create and upload to S3 managed storage a set of test files.
        prepareTestS3Files(testS3KeyPrefix, LOCAL_FILES);

        // Build a new business object data create request with enabled discovery of storage files.
        BusinessObjectDataCreateRequest request =
            new BusinessObjectDataCreateRequest(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, PARTITION_KEY,
                PARTITION_VALUE, NO_SUBPARTITION_VALUES, BDATA_STATUS, Arrays.asList(
                new StorageUnitCreateRequest(StorageEntity.MANAGED_STORAGE, new StorageDirectory(testS3KeyPrefix), NO_STORAGE_FILES, DISCOVER_STORAGE_FILES)),
                NO_ATTRIBUTES, NO_BUSINESS_OBJECT_DATA_PARENTS, NO_CREATE_NEW_VERSION);

        // Create the business object data.
        BusinessObjectData resultBusinessObjectData = businessObjectDataService.createBusinessObjectData(request);

        // Verify the results.
        assertEquals(
            new BusinessObjectData(resultBusinessObjectData.getId(), NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION,
                PARTITION_KEY, PARTITION_VALUE, NO_SUBPARTITION_VALUES, INITIAL_DATA_VERSION, LATEST_VERSION_FLAG_SET, BDATA_STATUS, Arrays.asList(
                new StorageUnit(new Storage(StorageEntity.MANAGED_STORAGE, StoragePlatformEntity.S3, Arrays
                    .asList(new Attribute(configurationHelper.getProperty(ConfigurationValue.S3_ATTRIBUTE_NAME_BUCKET_NAME), getS3ManagedBucketName()),
                        new Attribute(configurationHelper.getProperty(ConfigurationValue.S3_ATTRIBUTE_NAME_VALIDATE_FILE_EXISTENCE), Boolean.TRUE.toString()),
                        new Attribute(configurationHelper.getProperty(ConfigurationValue.S3_ATTRIBUTE_NAME_VALIDATE_PATH_PREFIX), Boolean.TRUE.toString()))),
                    new StorageDirectory(testS3KeyPrefix), getTestStorageFiles(testS3KeyPrefix, SORTED_LOCAL_FILES, false))), NO_ATTRIBUTES,
                NO_BUSINESS_OBJECT_DATA_PARENTS, NO_BUSINESS_OBJECT_DATA_CHILDREN), resultBusinessObjectData);
    }

    @Test
    public void testCreateBusinessObjectDataDiscoverStorageFilesNoStorageDirectory()
    {
        // Try to create an initial version of the business object data when discovery of storage files is enabled and storage directory is not specified.
        try
        {
            businessObjectDataService.createBusinessObjectData(
                new BusinessObjectDataCreateRequest(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, FORMAT_VERSION, PARTITION_KEY,
                    PARTITION_VALUE, NO_SUBPARTITION_VALUES, BDATA_STATUS,
                    Arrays.asList(new StorageUnitCreateRequest(STORAGE_NAME, NO_STORAGE_DIRECTORY, NO_STORAGE_FILES, DISCOVER_STORAGE_FILES)), NO_ATTRIBUTES,
                    NO_BUSINESS_OBJECT_DATA_PARENTS, NO_CREATE_NEW_VERSION));
            fail("Should throw an IllegalArgumentException when discovery of storage files is enabled and storage directory is not specified.");
        }
        catch (IllegalArgumentException e)
        {
            assertEquals("A storage directory must be specified when discovery of storage files is enabled.", e.getMessage());
        }
    }

    @Test
    public void testCreateBusinessObjectDataDiscoverStorageFilesStorageFilesSpecified()
    {
        // Try to create an initial version of the business object data when discovery of storage files is enabled and storage files are specified.
        try
        {
            businessObjectDataService.createBusinessObjectData(
                new BusinessObjectDataCreateRequest(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, FORMAT_VERSION, PARTITION_KEY,
                    PARTITION_VALUE, NO_SUBPARTITION_VALUES, BDATA_STATUS, Arrays.asList(
                    new StorageUnitCreateRequest(STORAGE_NAME, new StorageDirectory(STORAGE_DIRECTORY_PATH),
                        Arrays.asList(new StorageFile(LOCAL_FILE, FILE_SIZE_1_KB, ROW_COUNT_1000, null)), DISCOVER_STORAGE_FILES)), NO_ATTRIBUTES,
                    NO_BUSINESS_OBJECT_DATA_PARENTS, NO_CREATE_NEW_VERSION));
            fail("Should throw an IllegalArgumentException when discovery of storage files is enabled and storage files are specified.");
        }
        catch (IllegalArgumentException e)
        {
            assertEquals("Storage files cannot be specified when discovery of storage files is enabled.", e.getMessage());
        }
    }

    @Test
    public void testCreateBusinessObjectDataDiscoverStorageFilesInvalidStoragePlatform()
    {
        // Create a business object format entity.
        createBusinessObjectFormatEntity(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, FORMAT_DESCRIPTION,
            LATEST_VERSION_FLAG_SET, PARTITION_KEY);

        // Create a business object data status entity.
        createBusinessObjectDataStatusEntity(BDATA_STATUS);

        // Create a non-S3 storage entity with a  bucket name attribute.
        createStorageEntity(STORAGE_NAME, STORAGE_PLATFORM_CODE, configurationHelper.getProperty(ConfigurationValue.S3_ATTRIBUTE_NAME_BUCKET_NAME),
            S3_BUCKET_NAME);

        // Try to create an initial version of the business object data when storage platform is not supported for discovery of storage files.
        try
        {
            businessObjectDataService.createBusinessObjectData(
                new BusinessObjectDataCreateRequest(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, PARTITION_KEY,
                    PARTITION_VALUE, NO_SUBPARTITION_VALUES, BDATA_STATUS,
                    Arrays.asList(new StorageUnitCreateRequest(STORAGE_NAME, new StorageDirectory(testS3KeyPrefix), NO_STORAGE_FILES, DISCOVER_STORAGE_FILES)),
                    NO_ATTRIBUTES, NO_BUSINESS_OBJECT_DATA_PARENTS, NO_CREATE_NEW_VERSION));
            fail("Should throw an IllegalArgumentException when storage platform is not supported for discovery of storage files.");
        }
        catch (IllegalArgumentException e)
        {
            assertEquals(String.format("Cannot discover storage files at \"%s\" storage platform.", STORAGE_PLATFORM_CODE), e.getMessage());
        }
    }

    @Test
    public void testCreateBusinessObjectDataDiscoverStorageFilesNoS3FilesExist()
    {
        // Create a business object format entity.
        createBusinessObjectFormatEntity(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, FORMAT_DESCRIPTION,
            LATEST_VERSION_FLAG_SET, PARTITION_KEY);

        // Create a business object data status entity.
        createBusinessObjectDataStatusEntity(BDATA_STATUS);

        // Try to create an initial version of the business object data when there are no files in S3 to discover.
        try
        {
            businessObjectDataService.createBusinessObjectData(
                new BusinessObjectDataCreateRequest(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, PARTITION_KEY,
                    PARTITION_VALUE, NO_SUBPARTITION_VALUES, BDATA_STATUS, Arrays.asList(
                    new StorageUnitCreateRequest(StorageEntity.MANAGED_STORAGE, new StorageDirectory(testS3KeyPrefix), NO_STORAGE_FILES,
                        DISCOVER_STORAGE_FILES)), NO_ATTRIBUTES, NO_BUSINESS_OBJECT_DATA_PARENTS, NO_CREATE_NEW_VERSION));
            fail("Should throw an ObjectNotFoundException when there are no files in S3 to discover.");
        }
        catch (ObjectNotFoundException e)
        {
            assertTrue(e.getMessage().startsWith(String.format("Found no files at \"s3://%s", getS3ManagedBucketName())));
        }
    }

    @Test
    public void testCreateBusinessObjectDataDiscoverStorageFilesStorageDirectoryEndsWithSlash() throws Exception
    {
        // Create a business object format entity.
        createBusinessObjectFormatEntity(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, FORMAT_DESCRIPTION,
            LATEST_VERSION_FLAG_SET, PARTITION_KEY);

        // Create a business object data status entity.
        createBusinessObjectDataStatusEntity(BDATA_STATUS);

        // Create and upload to S3 managed storage a set of test files.
        prepareTestS3Files(testS3KeyPrefix, LOCAL_FILES);

        // Create an S3 storage entity with a bucket name attribute with a value matching to the test S3 managed storage (required for unit test clean up).
        String testBucketName = getS3ManagedBucketName();
        createStorageEntity(STORAGE_NAME, StoragePlatformEntity.S3, configurationHelper.getProperty(ConfigurationValue.S3_ATTRIBUTE_NAME_BUCKET_NAME),
            testBucketName);

        // Build a new business object data create request with enabled discovery of storage files and with storage directory ending with a slash.
        String testStorageDirectoryPath = testS3KeyPrefix + "/";
        BusinessObjectDataCreateRequest request =
            new BusinessObjectDataCreateRequest(NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION, PARTITION_KEY,
                PARTITION_VALUE, NO_SUBPARTITION_VALUES, BDATA_STATUS, Arrays
                .asList(new StorageUnitCreateRequest(STORAGE_NAME, new StorageDirectory(testStorageDirectoryPath), NO_STORAGE_FILES, DISCOVER_STORAGE_FILES)),
                NO_ATTRIBUTES, NO_BUSINESS_OBJECT_DATA_PARENTS, NO_CREATE_NEW_VERSION);

        // Create the business object data.
        BusinessObjectData resultBusinessObjectData = businessObjectDataService.createBusinessObjectData(request);

        // Verify the results.
        assertEquals(
            new BusinessObjectData(resultBusinessObjectData.getId(), NAMESPACE_CD, BOD_NAME, FORMAT_USAGE_CODE, FORMAT_FILE_TYPE_CODE, INITIAL_FORMAT_VERSION,
                PARTITION_KEY, PARTITION_VALUE, NO_SUBPARTITION_VALUES, INITIAL_DATA_VERSION, LATEST_VERSION_FLAG_SET, BDATA_STATUS, Arrays.asList(
                new StorageUnit(new Storage(STORAGE_NAME, StoragePlatformEntity.S3,
                    Arrays.asList(new Attribute(configurationHelper.getProperty(ConfigurationValue.S3_ATTRIBUTE_NAME_BUCKET_NAME), testBucketName))),
                    new StorageDirectory(testStorageDirectoryPath), getTestStorageFiles(testS3KeyPrefix, SORTED_LOCAL_FILES, false))), NO_ATTRIBUTES,
                NO_BUSINESS_OBJECT_DATA_PARENTS, NO_BUSINESS_OBJECT_DATA_CHILDREN), resultBusinessObjectData);
    }
}

redpajama_set_name: RedPajamaGithub


text:
Q: Image thumbnail with at least a certain size in PIL I'm trying to resize an image maintaining its aspect ratio such that its smallest size is 500px.
Using the code from this answer works, but it resize the image based on largest size.
So if we have a 2000x1000 image and we set size= 500,500 the resulting image will be 500x250, while I desire 1000x500. How can I do it?
Obviously these are random numbers.

A: Use max instead of min. Observe the different results:
>>> maxwidth = 500
>>> maxheight = 500
>>> width = 2000
>>> height = 1000
>>> i = min(maxwidth/width, maxheight/height)
>>> a = max(maxwidth/width, maxheight/height)
>>> width*i, height*i
(500.0, 250.0)
>>> width*a, height*a
(1000.0, 500.0)

You can use these values to determine what size you should be sending to img.thumbnail:
x,y = 500, 500
# compute i and a above
m.thumbnail((x*a/i, y*a/i), Image.ANTIALIAS)

redpajama_set_name: RedPajamaStackExchange


Hurricane Irma lashed the Caribbean today, causing at least three deaths, while farther north in the U.S., Florida, Georgia and South Carolina are facing states of emergency as residents brace for the monster storm.
The storm — dubbed "extremely dangerous" by the National Hurricane Center (NHC) currently has sustained winds of 185 mph and even higher gusts.
On Wednesday afternoon, Irma was battering the Caribbean, destroying about 90 percent of the structures and vehicles on Barbuda, officials said.
In St. Martin and St. Barthelemy, at least two people died and at least two others were seriously wounded, according to a spokesperson for France's ministry for its overseas territories.
At least one person died in Barbuda, where there is widespread damage, said Midcie Francis, a spokeswoman for Antigua & Barbuda's National Office of Disaster Services.
Irma, which is approximately 450 miles wide, has had sustained winds of at least 180 mph for a longer period of time than any other Atlantic storm on record.
As of 5 p.m. ET, Irma was about 55 miles east of San Juan, Puerto Rico and moving west at about 16 mph. The hurricane is forecast to move north of U.S. territory this evening, unleashing strong winds and heavy rain.
Persistent heavy rain from Irma is expected to continue for hours and flash flood warnings have been issued across northeastern Puerto Rico, including San Juan.
Hurricane warnings have been issued for much of the central Bahamas, while a hurricane watch has been issued for much of the northwestern Bahamas.
Tropical storm warnings have been issued for parts of Cuba, including Guantanamo, Holguin, and Las Tunas provinces. The hurricane warning for Saint Martin has been discontinued.


text:
Wallace, George C. (George Corley) 7
Congress of Racial Equality 6
Farmer, James 5
Parks, Rosa 5
Theology 113
Martin Luther King, Jr. - Religion 107
Bible, O.T. 79
Philosophy 73
Ethics 65
Christianity, History 52
Martin Luther King, Jr. - Threats/attacks against 36
SAVFC-WHi, Social Action Vertical File, State Historical Society of Wisconsin, Madison, Wis. 18
MMFR-INP, Montgomery to Memphis Film Research Files, In Private Hands 13
WTWP-INP, Wyatt Tee Walker Papers, In Private Hands 10
BRP-DLC, Bayard Rustin Papers, Library of Congress, Washington, D.C. 9
EJBC-NN-Sc, Ella J. Baker Collection, New York Public Library, Schomburg Collection, New York, NY 9
SNCCP-GAMK, Student Nonviolent Coordinating Committee Papers, 1959-1972, Martin Luther King, Jr., Center for Nonviolent Social Change, Inc., Atlanta, Ga. 9
SDLC-INP, Stanley D. Levison Collection, In Private Hands 8
LDRP-NN-Sc, Lawrence Dunbar Reddick Papers, Manuscripts, Archives and Rare Books Division, Schomburg Center for Research in Black Culture, The New York Public Library, New York, N.Y. 7
''Financial Secretary-Treasurer's Report''
9/1/1962-11/30/1962
12/1/1962-2/28/1963
3/1/1963-3/31/1963
Macomson, W. O.
Albany Movement (Albany, Ga.)
Mutual Federal Savings and Loan Association
Western Union Telegraph Company
National Conference on Religion and Race, Chicago, 1963
Voter Education Project (Southern Regional Council)
Citizens Trust Bank (Atlanta, Ga.)
Community Federal Savings and Loan Association
Westchester County, N.Y.
Newsletter 1, no. 11
Horne, Lena
Ardrey, William C.
Franklin, C.L. (Clarence LaVaughn)
Cavanaugh, Jerome P.
Swainson, John B.
Wilkins, Roy [National Association for the Advancement of Colored People]
Ponder, Annell
Seymore, Eliza
White, Walter Francis
Kenyatta, Jomo
Kenyatta, Margaret
Bennett, Lerone
Faulkner, William J.
Clayton, Ed
Hoover, Carole F.
Blaylock McKinnon Construction
Shady Grove Baptist Church (Lee County, Ga.)
Congress of Federated Organizations (COFO)
Mount Olive Baptist Church (Terrell County, Ga.)
Mount Mary Baptist Church (Terrell County, Ga.)
Congress of Racial Equality (CORE)
Clarksdale, Miss.
McIntosh, Ga.
Negro Digest
New South
The Negro Mood and Other Essays
The Negro Politician: His Success and Failure
Echo In My Soul
The Desegregated Heart
MS 22, #74
MS 22, Folder 30
History, Program, Structure and Leadership
Student Nonviolent Coordinating Committee
Long, Worth
Sherrod, Charles
LaFayette, Bernard
Hansen, William
Robinson, Reginald
Lyon, Danny
Ruleville, Miss.
McComb, Miss.
''Biographical sketches of women being honored for their leadership''
Bates, Daisy [National Association for the Advancement of Colored People (NAACP)]
Nash, Diane [Student Nonviolent Coordinating Committee (U.S.)]
Evers-Williams, Myrlie
Evers, Medgar Wiley [National Association for the Advancement of Colored People (NAACP)]
Lee, Herbert (Mrs.) [Student Nonviolent Coordinating Committee (U.S.)]
Parks, Rosa [National Association for the Advancement of Colored People (NAACP)]
Richardson, Gloria [Student Nonviolent Coordinating Committee (U.S.)]
Central High School (Little Rock, Ark.)
MS22, 677
NAACPP-DLC, National Association for the Advancement of Colored People Collection, Manuscript Division, Library of Congress, Washington, D.C.
Group III, A229
BRP-DLC, Bayard Rustin Papers, Manuscript Division, Library of Congress, Washington, D.C.
Gordy, Berry [Motown Record Corporation]
Harris, Curtis
Douglas, Paul H. (Paul Howard)
Proctor, Samuel D.
Gregory, Dick
Coxey, Jacob
Waters, Walter W.
MacArthur, Douglas
Boyle, Sarah-Patton
Kilgore, Thomas
Baldwin, James.
Ishizaka, Kinji
Yamagami, Yoshitada
Sip, Emil
Steele, C. Kenzie
Cohen, Howard
Conine, Jolly
Lewis, Edson T.
Thorndike, Elizabeth
Motown Record Corporation
A&T College (Greensboro, N.C.)
First Baptist Church (Hampton, Va.)
First Baptist Church (Newport News, Va.)
Goodwill Community Chorus (Petersburg, Va.)
Sankei Shimbum
Pravo, Rude
American Broadcasting Company
Negro Ministers of Atlanta
Prague, Czechoslovakia
Stars for Freedom
EPP-GEU-S, Eliza Paschall Papers, Manuscript, Archives, and Rare Book Library, Emory University, Atlanta, Ga.
BAP-INP, Brent Ashworth Papers, In Private Hands
''Full Text of Dr. King's Washington Address,'' Address at the March on Washington for Jobs and Freedom on 8/28/1963
SCLC Newsletter 1 (September 1963): 5, 8.
Stone Mountain, Ga.
Lookout Mountain, Tenn.
Memo to Martin Luther King, Jr.
Ware, Virgil
Robinson, Johnny
Gadsden, Ala.
Huntsville, Ala.
Statement on New York Riots
Lamb, Frank [Rochester (N.Y.). Office of the Mayor]
Wagner, Robert F. (Robert Ferdinand) [New York (N.Y.). Office of the Mayor]
Rochester (N.Y.). Office of the Mayor
New York (N.Y.). Office of the Mayor
WEFP-DcWaGWG, Walter E. Fauntroy Papers, George Washington University. Gelman Library System, Special Collections and University Archives, Washington, D.C.
Statement before the Credentials Committee of the Democratic National Convention
Atlantic City, N.J.
Courts, Gus
Lee, Herbert
MS 21, folder 662
Box 106 #622
Annual Report, Address delivered at the eighth annual convention of the Southern Christian Leadership Conference
Vivian, C. T. [Southern Christian Leadership Conference]
Clayton, Ed [Southern Christian Leadership Conference]
Gray, Victoria Jackson
Hamer, Fannie Lou
Boyte, Harry G. [Southern Christian Leadership Conference]
Blackwell, Randolph T. [Southern Christian Leadership Conference]
Bevel, James L. (James Luther) [Southern Christian Leadership Conference]
Young, Andrew [Southern Christian Leadership Conference]
Robinson, Bernice [Southern Christian Leadership Conference]
Wells, Samuel B. [Southern Christian Leadership Conference]
Cantor, Adele [Southern Christian Leadership Conference]
Grier, Joseph
Holmes, Oliver
Abernathy, Juanita Odessa Jones
Seay, S. S. (Solomon Snowden)
Dungee, Erna A.
Gregory, Hazel R.
Nesbitt, R. D. (Robert D.)
Palmer, H. J.
Gilmore, Georgia
Pollard, Francis
Arnold, Benedict
Williams, Odessa
Thurmond, Strom
Lombard, William M. [Rochester (N.Y.). Police Dept.]
Homer, Porter W. [Rochester (N.Y.). Office of the City Manager]
Joshua (Biblical figure)
American Missionary Association
Democratic National Convention (1964: Atlantic City, N.J.)
Educational Heritage, Inc. (Yonkers, N.Y.)
United States. Dept. of Labor.
General Motors Corporation
Chrysler Corporation
Cow Palace (San Francisco, Calif.)
Rochester (N.Y.). Police Dept.
Rochester (N.Y.). Office of the City Manager
Monroe, N.C.
Valley Forge, Pa.
Jones County, Ga.
Presidential candidates 1964
Encyclopedia for Negro Life

redpajama_set_name: RedPajamaCommonCrawl


Three new members joined Duke's board of trustees in July, one as a full member and two as young trustees.
Xi-Qing Gao J.D. '86, president and chief executive officer of China Investment Corporation, which manages a part of China's foreign-exchange reserves, will fill a vacated term through June 2013. After graduating from Duke, Gao practiced law with a Wall Street firm from 1986 to 1988. He has since served on arbitration panels for hundreds of international trade or investment disputes between Chinese and foreign companies, and was instrumental in the establishment of the Shanghai and Shenzhen stock exchanges in 1990. He has written extensively on Chinese securities law, banking, and legal philosophy and has taught in adjunct faculty positions at Duke and several Chinese law and management schools.
Ryan Todd '08 was elected to a three-year term as a young trustee by members of Duke Student Government (DSG) and the Intercommunity Council. At Duke, the Jupiter, Florida, native majored in political science and philosophy, and minored in mathematics. He was a member of the Black Student Alliance, a DSG senator, and vice president of the student-run theater group Hoof 'n' Horn. He also served as president of Campus Council and was founder and chair of the Alumni Student Advisory Board.
Xing Zong, a rising sixth-year Ph.D. candidate from Yangzhou, China, will serve a two-year young-trustee term; he was elected to the position by members of the Graduate and Professional Student Council. Zong has served as president of the Duke Chinese Students and Scholars Association, as a student representative to the Duke board of trustees' institutional advancement committee, and as student liaison for President Richard H. Brodhead's first official visit to Asia in the summer of 2006. He also co-founded DukeChina.org, a bilingual website that features stories about Duke and interviews with school officials.


KAMY HAT is one hundred-year old company in the Hat sector.
Specialized since 1960 in making accessories mainly out of fake fur or imitation fur.
Hats, Scarves and Collars out of imitation fur or fake fur are connected to the name KAMY HAT. The manufacturing of all those products is still taking place at the factory in Oudenaarde , Made in Belgium.
Where every article is made individually and hand finished, using traditional skills during the whole manufacturing process.
The collection provides a selection of fur, fun fur, imitation fur in all kind of colors, lengths or prints. Our designs reflect the latest seasonal trend in colors and styles.
KAMY HAT supplies national as international department stores, wholesalers, specialized retailers and collections for private labels.


text:
Home plastic pollution The current war on plastic is detracting from the bigger threats to the environment
The current war on plastic is detracting from the bigger threats to the environment
Close-up of compressed paper and plastic gathered into bundles and stacked outdoors at waste management facility.
A team of leading environmental experts, spearheaded by the University of Nottingham, have warned that the current war on plastic is detracting from the bigger threats to the environment.
In an article published in the peer-reviewed scientific journal, Wiley Interdisciplinary Reviews (WIREs) Water, the 13 experts1 say that while plastic waste is an issue, its prominence in the general public's concern for the environment is overshadowing greater threats, for example, climate change and biodiversity loss.
The interdisciplinary team argue that much of the discourse around plastic waste is based on data that is not always representative of the environments that have been sampled. The aversion to plastic associated with this could encourage the use of alternative materials with potentially greater harmful effects.
The authors warn that plastic pollution dominates the public's concern for the environment and has been exploited politically, after capturing the attention of the world, for example through emotive imagery of wildlife caught in plastic waste and alarmist headlines. They say small political gestures such as legislation banning cosmetic microplastics, taxing plastic bags, and financial incentives for using reusable containers, as well as the promotion of products as 'green' for containing less plastic than alternatives, risks instilling a complacency in society towards other environmental problems that are not as tangible as plastic pollution.
The article's authors call on the media and others to ensure that the realities of plastic pollution are not misrepresented, particularly in the public dissemination of the issue, and urges government to minimise the environmental impact of over-consumption, however inconvenient, through product design, truly circular waste-management, and considered rather than reactionary policy.
We are seeing unprecedented engagement with environmental issues, particularly plastic pollution, from the public and we believe this presents a once in a generation opportunity to promote other, potentially greater environmental issues. This is a key moment in which to highlight and address areas such as 'throw-away' culture in society and overhaul waste management. However, if there is a continuation in prioritising plastic, this opportunity will be missed – and at great cost to our environment.
Dr Tom Stanton, co-author in the University of Nottingham's School of Geography and Food, Water, Waste Research Group
The article also highlights that plastics are not the only type of polluting material originating from human activity that contaminates the environment. Other examples include natural textile fibres such as cotton and wool, Spheroidal Carbonaceous Particles (remnants of fossil fuels), and brake-wear particles from vehicles – all of which are present in different places, where they may have adverse environmental effects. The authors note that these materials are often much more abundant than microplastics and some, such as glass, aluminium, paper, and natural fibres, are associated with 'plastic alternatives' that are marketed as solutions to plastic pollution, but in reality side-step the inconvenience of changing the consumption practices at the root of the problem. The eco-toxicological impacts of some of these materials are less well known than plastic and microplastic pollution, yet they could have significant impacts.
The authors conclude that that a behavioural science approach should be taken to assess society's relationship with single-use items and throw-away culture, and to overhaul waste mismanagement.
They say there is an understandable desire to minimise the global plastic debris in the environment which should not be discouraged, but positive action to minimise plastic pollution needs to be well informed and should not exacerbate or overshadow other forms of environmental degradation associated with alternative materials.
The article states that solutions are likely to come from a greater focus on designing materials and products that can be recycled, that have their end-of-life built in, and that markets and facilities exist to recycle all plastic waste.
Learn more: War on plastic is distracting from more urgent threats to environment, experts warn
Throw-away culture
Netflix: Joaquin Phoenix's 'The Master' Is a Hidden Gem That Will Have Your Head Spinning
Paul Thomas Anderson's 'The Master' truly knocked it out of the park with this one and multiple viewings needed to fully process the scope of the film's ideas.
Trouble at home may change Biden's hand in Iran nuke talks
A lot of the characters are the same for President-elect Joe Biden but the scene is far starker as he reassembles a team of veteran negotiators to get back into the 2015 nuclear deal with Iran.
An Exhibition in Singapore Challenges the Meaning of Repair
She broke the handle as an adult, and she couldn't bear to throw away the family heirloom ... we got sucked into this consumerist culture. I wanted to give repair a different spin." ...
Lib Dems to call for ban on single-use plastics to end UK's 'throw-away culture'
THE Liberal Democrats will today call for a complete ban on single-use plastics within the next three years to stem Britain's ...
Best Cities to Live in the UK as a Student
With over 150 top-ranking universities, the United Kingdom is one of the best places in the world to study. There's a seemingly endless number of places in the UK to choose from, which makes picking ...
Gadget Book: Repair Revolution - How Fixers Are Transforming Our Throwaway Culture - Electronics Weekly
So-called community policing perpetuates a throwaway culture - National Catholic Reporter
From fast fashion to fast cars, these companies are ditching throwaway culture - Wired.co.uk
Incoming USCCB doctrine chair: Death penalty is part of 'throwaway culture' - Angelus News
Rejecting the throwaway culture | Earthbeat - National Catholic Reporter
Ramsay saves more than 9 million plastic items from landfill
Ramsay Health Care Australia is making a meaningful, positive impact on the environment, preventing more than 9 million single-use plastic items ...
Virus job losses highlight flawed system that gathers foreigners to Japan for 'single-use'
The coronavirus pandemic has been aggravating the lives of technical intern trainees and other foreigners who are already placed in vulnerabl ...
Shun use of single-use plastic, thermocol items: Ludhiana MC chief
The Municipal Corporation Commissioner, Pardeep Kumar Sabharwal, has made an appeal to the people to not use single-use plastic and disposable thermocol items. He said the Municipal Corporation has op ...
Conversations That Matter: Does it make sense to focus on single-use plastics?
Chris DeArmitt figures we'd be better off focusing on paper rather than plastic if we are worried about single-use items. The leading plastics scientist has written "Phantom Plastics," a book in which ...
Solon pushes nationwide ban on single-use plastic
FOLLOWING the call of Finance Sec. and Climate Change Commission (CCC) Chairperson Carlos Dominguez to ban single-use plastic, a House leader has appealed to ...
5 Ways We Can Move Away From Single-Use Plastics - Honolulu Civil Beat
Mexico City ban on single-use plastics takes effect - ABC News
Mexico City Officially Bans Single-Use Plastics - AFAR Media
In Our View: Resolve now to ditch single-use plastic bags - The Columbian
Federal Ban on Single-Use Plastics - Lexology
Tags: plastic pollutionpolluting materialsingle-use itemsthrow-away cultureuniversity of nottinghamwar on plastic
Speeding up the charging of electric cars: 90% charge in 6 minutes
New ultrahigh-res OLED display features brighter images with purer colors and more than 10,000 pixels per inch
A new way to track nanoplastics in the environment
New machine learning program can greatly accelerate clean energy generation
3D printing electronic devices with useful properties such as an ability to convert light into electricity
The examination of micro and nanoplastics in human organs and tissues has begun
What are the real disease risks from ocean plastics?


i've been following the progress of sgine for a a week or two now, it looks great!
@hansi: I actually started out with JOGL and moved to LWJGL. My first reason is that LWJGL seems to be much more standards-based to OpenGL than JOGL, but it also seems to perform much faster and with fewer hard-coded ties to AWT.
All-in-all I'm quite a bit happier with LWJGL than with JOGL.