/* * Copyright 2015 herd contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.finra.herd.service.impl; import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import org.finra.herd.core.helper.ConfigurationHelper; import org.finra.herd.dao.BusinessObjectDataDao; import org.finra.herd.dao.StorageUnitDao; import org.finra.herd.dao.config.DaoSpringModuleConfig; import org.finra.herd.dao.helper.JsonHelper; import org.finra.herd.model.annotation.NamespacePermission; import org.finra.herd.model.annotation.PublishJmsMessages; import org.finra.herd.model.api.xml.BusinessObjectData; import org.finra.herd.model.api.xml.BusinessObjectDataAvailability; import org.finra.herd.model.api.xml.BusinessObjectDataAvailabilityCollectionRequest; import org.finra.herd.model.api.xml.BusinessObjectDataAvailabilityCollectionResponse; import org.finra.herd.model.api.xml.BusinessObjectDataAvailabilityRequest; import org.finra.herd.model.api.xml.BusinessObjectDataCreateRequest; import org.finra.herd.model.api.xml.BusinessObjectDataDdl; import org.finra.herd.model.api.xml.BusinessObjectDataDdlCollectionRequest; import org.finra.herd.model.api.xml.BusinessObjectDataDdlCollectionResponse; import org.finra.herd.model.api.xml.BusinessObjectDataDdlRequest; import org.finra.herd.model.api.xml.BusinessObjectDataInvalidateUnregisteredRequest; import org.finra.herd.model.api.xml.BusinessObjectDataInvalidateUnregisteredResponse; import org.finra.herd.model.api.xml.BusinessObjectDataKey; import org.finra.herd.model.api.xml.BusinessObjectDataKeys; import org.finra.herd.model.api.xml.BusinessObjectDataRetryStoragePolicyTransitionRequest; import org.finra.herd.model.api.xml.BusinessObjectDataSearchRequest; import org.finra.herd.model.api.xml.BusinessObjectDataSearchResult; import org.finra.herd.model.api.xml.BusinessObjectDataStatus; import org.finra.herd.model.api.xml.BusinessObjectDataVersion; import org.finra.herd.model.api.xml.BusinessObjectDataVersions; import org.finra.herd.model.api.xml.BusinessObjectDefinitionKey; import org.finra.herd.model.api.xml.BusinessObjectFormatKey; import org.finra.herd.model.api.xml.CustomDdlKey; import org.finra.herd.model.api.xml.NamespacePermissionEnum; import org.finra.herd.model.dto.BusinessObjectDataRestoreDto; import org.finra.herd.model.dto.ConfigurationValue; import org.finra.herd.model.dto.S3FileTransferRequestParamsDto; import org.finra.herd.model.jpa.BusinessObjectDataEntity; import org.finra.herd.model.jpa.BusinessObjectDataStatusEntity; import org.finra.herd.model.jpa.BusinessObjectDefinitionEntity; import org.finra.herd.model.jpa.BusinessObjectFormatEntity; import org.finra.herd.model.jpa.CustomDdlEntity; import org.finra.herd.model.jpa.NotificationEventTypeEntity; import org.finra.herd.model.jpa.StorageEntity; import org.finra.herd.model.jpa.StorageFileEntity; import org.finra.herd.model.jpa.StoragePlatformEntity; import org.finra.herd.model.jpa.StorageUnitEntity; import org.finra.herd.service.BusinessObjectDataInitiateRestoreHelperService; import org.finra.herd.service.BusinessObjectDataService; import org.finra.herd.service.NotificationEventService; import org.finra.herd.service.S3Service; import org.finra.herd.service.helper.BusinessObjectDataDaoHelper; import org.finra.herd.service.helper.BusinessObjectDataHelper; import org.finra.herd.service.helper.BusinessObjectDataInvalidateUnregisteredHelper; import org.finra.herd.service.helper.BusinessObjectDataRetryStoragePolicyTransitionHelper; import org.finra.herd.service.helper.BusinessObjectDataSearchHelper; import org.finra.herd.service.helper.BusinessObjectDataStatusDaoHelper; import org.finra.herd.service.helper.BusinessObjectDefinitionDaoHelper; import org.finra.herd.service.helper.BusinessObjectDefinitionHelper; import org.finra.herd.service.helper.BusinessObjectFormatDaoHelper; import org.finra.herd.service.helper.BusinessObjectFormatHelper; import org.finra.herd.service.helper.CustomDdlDaoHelper; import org.finra.herd.service.helper.DdlGeneratorFactory; import org.finra.herd.service.helper.S3KeyPrefixHelper; import org.finra.herd.service.helper.StorageDaoHelper; import org.finra.herd.service.helper.StorageHelper; import org.finra.herd.service.helper.StorageUnitHelper; /** * The business object data service implementation. */ @Service @Transactional(value = DaoSpringModuleConfig.HERD_TRANSACTION_MANAGER_BEAN_NAME) public class BusinessObjectDataServiceImpl implements BusinessObjectDataService { /** * A status reason of "not registered". */ public static final String REASON_NOT_REGISTERED = "NOT_REGISTERED"; private static final Logger LOGGER = LoggerFactory.getLogger(BusinessObjectDataServiceImpl.class); @Autowired private BusinessObjectDataDao businessObjectDataDao; @Autowired private BusinessObjectDataDaoHelper businessObjectDataDaoHelper; @Autowired private BusinessObjectDataHelper businessObjectDataHelper; @Autowired private BusinessObjectDataInitiateRestoreHelperService businessObjectDataInitiateRestoreHelperService; @Autowired private BusinessObjectDataInvalidateUnregisteredHelper businessObjectDataInvalidateUnregisteredHelper; @Autowired private BusinessObjectDataRetryStoragePolicyTransitionHelper businessObjectDataRetryStoragePolicyTransitionHelper; @Autowired private BusinessObjectDataSearchHelper businessObjectDataSearchHelper; @Autowired private BusinessObjectDataStatusDaoHelper businessObjectDataStatusDaoHelper; @Autowired private BusinessObjectDefinitionDaoHelper businessObjectDefinitionDaoHelper; @Autowired private BusinessObjectDefinitionHelper businessObjectDefinitionHelper; @Autowired private BusinessObjectFormatDaoHelper businessObjectFormatDaoHelper; @Autowired private BusinessObjectFormatHelper businessObjectFormatHelper; @Autowired private ConfigurationHelper configurationHelper; @Autowired private CustomDdlDaoHelper customDdlDaoHelper; @Autowired private DdlGeneratorFactory ddlGeneratorFactory; @Autowired private JsonHelper jsonHelper; @Autowired private NotificationEventService notificationEventService; @Autowired private S3KeyPrefixHelper s3KeyPrefixHelper; @Autowired private S3Service s3Service; @Autowired private StorageDaoHelper storageDaoHelper; @Autowired private StorageHelper storageHelper; @Autowired private StorageUnitDao storageUnitDao; @Autowired private StorageUnitHelper storageUnitHelper; @NamespacePermission(fields = "#request.namespace", permissions = NamespacePermissionEnum.READ) @Override @Transactional(propagation = Propagation.REQUIRES_NEW) public BusinessObjectDataAvailability checkBusinessObjectDataAvailability(BusinessObjectDataAvailabilityRequest request) { return checkBusinessObjectDataAvailabilityImpl(request); } @NamespacePermission(fields = "#request?.businessObjectDataAvailabilityRequests?.![namespace]", permissions = NamespacePermissionEnum.READ) @Override @Transactional(propagation = Propagation.REQUIRES_NEW) public BusinessObjectDataAvailabilityCollectionResponse checkBusinessObjectDataAvailabilityCollection( BusinessObjectDataAvailabilityCollectionRequest request) { return checkBusinessObjectDataAvailabilityCollectionImpl(request); } @PublishJmsMessages @NamespacePermission(fields = "#request.namespace", permissions = NamespacePermissionEnum.WRITE) @Override @Transactional(propagation = Propagation.REQUIRES_NEW) public BusinessObjectData createBusinessObjectData(BusinessObjectDataCreateRequest request) { return businessObjectDataDaoHelper.createBusinessObjectData(request); } @NamespacePermission(fields = "#businessObjectDataKey.namespace", permissions = NamespacePermissionEnum.WRITE) @Override public BusinessObjectData deleteBusinessObjectData(BusinessObjectDataKey businessObjectDataKey, Boolean deleteFiles) { // Validate and trim the business object data key. businessObjectDataHelper.validateBusinessObjectDataKey(businessObjectDataKey, true, true); // Validate the mandatory deleteFiles flag. Assert.notNull(deleteFiles, "A delete files flag must be specified."); // Retrieve the business object data and ensure it exists. BusinessObjectDataEntity businessObjectDataEntity = businessObjectDataDaoHelper.getBusinessObjectDataEntity(businessObjectDataKey); // Check if we are allowed to delete this business object data. if (!businessObjectDataEntity.getBusinessObjectDataChildren().isEmpty()) { throw new IllegalArgumentException(String .format("Can not delete a business object data that has children associated with it. Business object data: {%s}", businessObjectDataHelper.businessObjectDataEntityAltKeyToString(businessObjectDataEntity))); } // If the flag is set, clean up the data files from all storages of S3 storage platform type. LOGGER.info("deleteFiles={}", deleteFiles); if (deleteFiles) { // Loop over all storage units for this business object data. for (StorageUnitEntity storageUnitEntity : businessObjectDataEntity.getStorageUnits()) { StorageEntity storageEntity = storageUnitEntity.getStorage(); // Currently, we only support data file deletion from S3 platform type. if (storageEntity.getStoragePlatform().getName().equals(StoragePlatformEntity.S3)) { LOGGER.info("Deleting business object data files from the storage... storageName=\"{}\" businessObjectDataKey={}", storageEntity.getName(), jsonHelper.objectToJson(businessObjectDataHelper.getBusinessObjectDataKey(businessObjectDataEntity))); // Get the S3 validation flags. boolean validatePathPrefix = storageHelper .getBooleanStorageAttributeValueByName(configurationHelper.getProperty(ConfigurationValue.S3_ATTRIBUTE_NAME_VALIDATE_PATH_PREFIX), storageEntity, false, true); // If this storage conforms to the path prefix validation, then delete all keys found under the S3 key prefix. if (validatePathPrefix) { // Retrieve S3 key prefix velocity template storage attribute value and store it in memory. // Please note that it is not required, so we pass in a "false" flag. String s3KeyPrefixVelocityTemplate = storageHelper .getStorageAttributeValueByName(configurationHelper.getProperty(ConfigurationValue.S3_ATTRIBUTE_NAME_KEY_PREFIX_VELOCITY_TEMPLATE), storageEntity, false); // Validate that S3 key prefix velocity template is configured. Assert.isTrue(StringUtils.isNotBlank(s3KeyPrefixVelocityTemplate), String .format("Storage \"%s\" has enabled path validation without S3 key prefix velocity template configured.", storageEntity.getName())); // Build the S3 key prefix as per S3 Naming Convention Wiki page. String s3KeyPrefix = s3KeyPrefixHelper .buildS3KeyPrefix(s3KeyPrefixVelocityTemplate, businessObjectDataEntity.getBusinessObjectFormat(), businessObjectDataKey, storageEntity.getName()); // Get S3 bucket access parameters, such as bucket name, AWS access key ID, AWS secret access key, etc... S3FileTransferRequestParamsDto params = storageHelper.getS3BucketAccessParams(storageEntity); // Since the S3 key prefix represents a directory, we add a trailing '/' character to it. params.setS3KeyPrefix(s3KeyPrefix + "/"); // Delete a list of all keys/objects from S3 managed bucket matching the expected S3 key prefix. // Please note that when deleting S3 files, we also delete all 0 byte objects that represent S3 directories. s3Service.deleteDirectory(params); } // For a non S3 prefixed paths, delete the files explicitly or if only directory is registered, delete all files/subfolders found under it. else { // Get S3 bucket access parameters, such as bucket name, AWS access key ID, AWS secret access key, etc... S3FileTransferRequestParamsDto params = storageHelper.getS3BucketAccessParams(storageEntity); // If only directory is registered delete all files/sub-folders found under it. if (StringUtils.isNotBlank(storageUnitEntity.getDirectoryPath()) && storageUnitEntity.getStorageFiles().isEmpty()) { // Since the directory path represents a directory, we add a trailing '/' character to it. params.setS3KeyPrefix(storageUnitEntity.getDirectoryPath() + "/"); // Delete a list of all keys/objects from S3 bucket matching the directory path. // Please note that when deleting S3 files, we also delete all 0 byte objects that represent S3 directories. s3Service.deleteDirectory(params); } // Delete the files explicitly. else { // Create a list of files to delete. List<File> files = new ArrayList<>(); for (StorageFileEntity storageFileEntity : storageUnitEntity.getStorageFiles()) { files.add(new File(storageFileEntity.getPath())); } params.setFiles(files); s3Service.deleteFileList(params); } } } else { LOGGER.info("Skipping business object data file removal for a storage unit from the storage since it is not an S3 storage platform. " + " storageName=\"{}\" businessObjectDataKey={}", storageEntity.getName(), jsonHelper.objectToJson(businessObjectDataHelper.getBusinessObjectDataKey(businessObjectDataEntity))); } } } // Create the business object data object from the entity. BusinessObjectData deletedBusinessObjectData = businessObjectDataHelper.createBusinessObjectDataFromEntity(businessObjectDataEntity); // Delete this business object data. businessObjectDataDao.delete(businessObjectDataEntity); // If this business object data version is the latest, set the latest flag on the previous version of this object data, if it exists. if (businessObjectDataEntity.getLatestVersion()) { // Get the maximum version for this business object data, if it exists. Integer maxBusinessObjectDataVersion = businessObjectDataDao.getBusinessObjectDataMaxVersion(businessObjectDataKey); if (maxBusinessObjectDataVersion != null) { // Retrieve the previous version business object data entity. Since we successfully got the maximum // version for this business object data, the retrieved entity is not expected to be null. BusinessObjectDataEntity previousVersionBusinessObjectDataEntity = businessObjectDataDao.getBusinessObjectDataByAltKey( new BusinessObjectDataKey(businessObjectDataKey.getNamespace(), businessObjectDataKey.getBusinessObjectDefinitionName(), businessObjectDataKey.getBusinessObjectFormatUsage(), businessObjectDataKey.getBusinessObjectFormatFileType(), businessObjectDataKey.getBusinessObjectFormatVersion(), businessObjectDataKey.getPartitionValue(), businessObjectDataKey.getSubPartitionValues(), maxBusinessObjectDataVersion)); // Update the previous version business object data entity. previousVersionBusinessObjectDataEntity.setLatestVersion(true); businessObjectDataDao.saveAndRefresh(previousVersionBusinessObjectDataEntity); } } // Return the deleted business object data. return deletedBusinessObjectData; } @NamespacePermission(fields = "#request.namespace", permissions = NamespacePermissionEnum.READ) @Override @Transactional(propagation = Propagation.REQUIRES_NEW) public BusinessObjectDataDdl generateBusinessObjectDataDdl(BusinessObjectDataDdlRequest request) { return generateBusinessObjectDataDdlImpl(request, false); } @NamespacePermission(fields = "#request?.businessObjectDataDdlRequests?.![namespace]", permissions = NamespacePermissionEnum.READ) @Override @Transactional(propagation = Propagation.REQUIRES_NEW) public BusinessObjectDataDdlCollectionResponse generateBusinessObjectDataDdlCollection(BusinessObjectDataDdlCollectionRequest request) { return generateBusinessObjectDataDdlCollectionImpl(request); } @NamespacePermission(fields = "#businessObjectDefinitionKey.namespace", permissions = NamespacePermissionEnum.READ) @Override public BusinessObjectDataKeys getAllBusinessObjectDataByBusinessObjectDefinition(BusinessObjectDefinitionKey businessObjectDefinitionKey) { // Perform validation and trim. businessObjectDefinitionHelper.validateBusinessObjectDefinitionKey(businessObjectDefinitionKey); // Ensure that a business object definition already exists with the specified name. BusinessObjectDefinitionEntity businessObjectDefinitionEntity = businessObjectDefinitionDaoHelper.getBusinessObjectDefinitionEntity(businessObjectDefinitionKey); // Get the maximum number of records to return. Integer maxResults = configurationHelper.getProperty(ConfigurationValue.BUSINESS_OBJECT_DATA_SEARCH_MAX_RESULTS, Integer.class); // Gets the list of keys and return them. BusinessObjectDataKeys businessObjectDataKeys = new BusinessObjectDataKeys(); businessObjectDataKeys.getBusinessObjectDataKeys() .addAll(businessObjectDataDao.getBusinessObjectDataByBusinessObjectDefinition(businessObjectDefinitionEntity, maxResults)); return businessObjectDataKeys; } @NamespacePermission(fields = "#businessObjectFormatKey.namespace", permissions = NamespacePermissionEnum.READ) @Override public BusinessObjectDataKeys getAllBusinessObjectDataByBusinessObjectFormat(BusinessObjectFormatKey businessObjectFormatKey) { // Perform validation and trim. Please note that we specify business object format version parameter to be required. businessObjectFormatHelper.validateBusinessObjectFormatKey(businessObjectFormatKey, true); // Ensure that a business object definition already exists with the specified name. BusinessObjectFormatEntity businessObjectFormatEntity = businessObjectFormatDaoHelper.getBusinessObjectFormatEntity(businessObjectFormatKey); // Get the maximum number of records to return. Integer maxResults = configurationHelper.getProperty(ConfigurationValue.BUSINESS_OBJECT_DATA_SEARCH_MAX_RESULTS, Integer.class); // Gets the list of business object data keys and return them. BusinessObjectDataKeys businessObjectDataKeys = new BusinessObjectDataKeys(); businessObjectDataKeys.getBusinessObjectDataKeys() .addAll(businessObjectDataDao.getBusinessObjectDataByBusinessObjectFormat(businessObjectFormatEntity, maxResults)); return businessObjectDataKeys; } @NamespacePermission(fields = "#businessObjectDataKey.namespace", permissions = NamespacePermissionEnum.READ) @Override @Transactional(propagation = Propagation.REQUIRES_NEW) public BusinessObjectData getBusinessObjectData(BusinessObjectDataKey businessObjectDataKey, String businessObjectFormatPartitionKey, String businessObjectDataStatus, Boolean includeBusinessObjectDataStatusHistory) { return getBusinessObjectDataImpl(businessObjectDataKey, businessObjectFormatPartitionKey, businessObjectDataStatus, includeBusinessObjectDataStatusHistory); } @NamespacePermission(fields = "#businessObjectDataKey.namespace", permissions = NamespacePermissionEnum.READ) @Override public BusinessObjectDataVersions getBusinessObjectDataVersions(BusinessObjectDataKey businessObjectDataKey) { // Validate and trim the business object data key. businessObjectDataHelper.validateBusinessObjectDataKey(businessObjectDataKey, false, false); // Get the business object data versions based on the specified parameters. List<BusinessObjectDataEntity> businessObjectDataEntities = businessObjectDataDao.getBusinessObjectDataEntities(businessObjectDataKey); // Create the response. BusinessObjectDataVersions businessObjectDataVersions = new BusinessObjectDataVersions(); for (BusinessObjectDataEntity businessObjectDataEntity : businessObjectDataEntities) { BusinessObjectDataVersion businessObjectDataVersion = new BusinessObjectDataVersion(); BusinessObjectDataKey businessObjectDataVersionKey = businessObjectDataHelper.getBusinessObjectDataKey(businessObjectDataEntity); businessObjectDataVersion.setBusinessObjectDataKey(businessObjectDataVersionKey); businessObjectDataVersion.setStatus(businessObjectDataEntity.getStatus().getCode()); businessObjectDataVersions.getBusinessObjectDataVersions().add(businessObjectDataVersion); } return businessObjectDataVersions; } /** * {@inheritDoc} * <p/> * Delegates implementation to {@link org.finra.herd.service.helper.BusinessObjectDataInvalidateUnregisteredHelper}. Starts a new transaction. Meant for * Activiti wrapper usage. */ @PublishJmsMessages @NamespacePermission(fields = "#businessObjectDataInvalidateUnregisteredRequest.namespace", permissions = NamespacePermissionEnum.WRITE) @Override @Transactional(propagation = Propagation.REQUIRES_NEW) public BusinessObjectDataInvalidateUnregisteredResponse invalidateUnregisteredBusinessObjectData( BusinessObjectDataInvalidateUnregisteredRequest businessObjectDataInvalidateUnregisteredRequest) { return invalidateUnregisteredBusinessObjectDataImpl(businessObjectDataInvalidateUnregisteredRequest); } /** * {@inheritDoc} * <p/> * This implementation executes non-transactionally, suspends the current transaction if one exists. */ @NamespacePermission(fields = "#businessObjectDataKey.namespace", permissions = NamespacePermissionEnum.WRITE) @Override @Transactional(propagation = Propagation.NOT_SUPPORTED) public BusinessObjectData restoreBusinessObjectData(BusinessObjectDataKey businessObjectDataKey, Integer expirationInDays) { return restoreBusinessObjectDataImpl(businessObjectDataKey, expirationInDays); } @NamespacePermission(fields = "#businessObjectDataKey.namespace", permissions = NamespacePermissionEnum.WRITE) @Override public BusinessObjectData retryStoragePolicyTransition(BusinessObjectDataKey businessObjectDataKey, BusinessObjectDataRetryStoragePolicyTransitionRequest request) { return businessObjectDataRetryStoragePolicyTransitionHelper.retryStoragePolicyTransition(businessObjectDataKey, request); } /** * Search business object data based on the request * * @param request search request * * @return business data search result */ @NamespacePermission(fields = "#request.businessObjectDataSearchFilters[0].BusinessObjectDataSearchKeys[0].namespace", permissions = NamespacePermissionEnum.READ) @Override public BusinessObjectDataSearchResult searchBusinessObjectData(BusinessObjectDataSearchRequest request) { //TO DO check name space permission for all entries in the request. // validate search request businessObjectDataSearchHelper.validateBusinesObjectDataSearchRequest(request); // search business object data List<BusinessObjectData> businessObjectDataList = businessObjectDataDao.searchBusinessObjectData(request.getBusinessObjectDataSearchFilters()); BusinessObjectDataSearchResult result = new BusinessObjectDataSearchResult(); result.setBusinessObjectDataElements(businessObjectDataList); return result; } /** * Updates the list of not-available statuses by adding business object data status instances created from discovered "non-available" registered * sub-partitions as per list of "matched" partition filters to the specified list of not-available statuses. * * @param notAvailableStatuses the list of not-available statuses to be updated * @param businessObjectFormatKey the business object format key * @param matchedAvailablePartitionFilters the list of "matched" partition filters * @param availablePartitions the list of already discovered "available" partitions, where each partition consists of primary and optional sub-partition * values * @param storageNames the list of storage names */ protected void addNotAvailableBusinessObjectDataStatuses(List<BusinessObjectDataStatus> notAvailableStatuses, BusinessObjectFormatKey businessObjectFormatKey, List<List<String>> matchedAvailablePartitionFilters, List<List<String>> availablePartitions, List<String> storageNames) { // Now try to retrieve latest business object data per list of matched filters regardless of business object data and/or storage unit statuses. // This is done to include all registered sub-partitions in the response. // Business object data availability works across all storage platform types, so the storage platform type is not specified in the herdDao call. // We want to select any existing storage units regardless of their status, so we pass "false" for selectOnlyAvailableStorageUnits parameter. List<StorageUnitEntity> matchedNotAvailableStorageUnitEntities = storageUnitDao .getStorageUnitsByPartitionFiltersAndStorages(businessObjectFormatKey, matchedAvailablePartitionFilters, null, null, storageNames, null, null, false); // Exclude all storage units with business object data having "DELETED" status. matchedNotAvailableStorageUnitEntities = storageUnitHelper.excludeBusinessObjectDataStatus(matchedNotAvailableStorageUnitEntities, BusinessObjectDataStatusEntity.DELETED); // Exclude all already discovered "available" partitions. Please note that, since we got here, the list of matched partitions can not be empty. matchedNotAvailableStorageUnitEntities = storageUnitHelper.excludePartitions(matchedNotAvailableStorageUnitEntities, availablePartitions); // Keep processing the matched "not available" storage units only when the list is not empty. if (!CollectionUtils.isEmpty(matchedNotAvailableStorageUnitEntities)) { // Populate the "not available" statuses with all found "not available" registered sub-partitions. addNotAvailableBusinessObjectDataStatuses(notAvailableStatuses, matchedNotAvailableStorageUnitEntities); } } /** * Performs an availability check for a collection of business object data. * * @param businessObjectDataAvailabilityCollectionRequest the business object data availability collection requests * * @return the business object data availability information */ protected BusinessObjectDataAvailabilityCollectionResponse checkBusinessObjectDataAvailabilityCollectionImpl( BusinessObjectDataAvailabilityCollectionRequest businessObjectDataAvailabilityCollectionRequest) { // Perform the validation of the entire request, before we start processing the individual requests that requires the database access. validateBusinessObjectDataAvailabilityCollectionRequest(businessObjectDataAvailabilityCollectionRequest); // Process the individual requests and build the response. BusinessObjectDataAvailabilityCollectionResponse businessObjectDataAvailabilityCollectionResponse = new BusinessObjectDataAvailabilityCollectionResponse(); List<BusinessObjectDataAvailability> businessObjectDataAvailabilityResponses = new ArrayList<>(); businessObjectDataAvailabilityCollectionResponse.setBusinessObjectDataAvailabilityResponses(businessObjectDataAvailabilityResponses); boolean isAllDataAvailable = true; boolean isAllDataNotAvailable = true; for (BusinessObjectDataAvailabilityRequest request : businessObjectDataAvailabilityCollectionRequest.getBusinessObjectDataAvailabilityRequests()) { // Please note that when calling to process individual availability requests, we ask to skip the request validation and trimming step. BusinessObjectDataAvailability businessObjectDataAvailability = checkBusinessObjectDataAvailabilityImpl(request, true); businessObjectDataAvailabilityResponses.add(businessObjectDataAvailability); isAllDataAvailable = isAllDataAvailable && businessObjectDataAvailability.getNotAvailableStatuses().isEmpty(); isAllDataNotAvailable = isAllDataNotAvailable && businessObjectDataAvailability.getAvailableStatuses().isEmpty(); } businessObjectDataAvailabilityCollectionResponse.setIsAllDataAvailable(isAllDataAvailable); businessObjectDataAvailabilityCollectionResponse.setIsAllDataNotAvailable(isAllDataNotAvailable); return businessObjectDataAvailabilityCollectionResponse; } /** * Performs a search and returns a list of business object data key values and relative statuses for a range of requested business object data. * * @param request the business object data availability request * * @return the business object data availability information */ protected BusinessObjectDataAvailability checkBusinessObjectDataAvailabilityImpl(BusinessObjectDataAvailabilityRequest request) { // By default, validate and trim the request. return checkBusinessObjectDataAvailabilityImpl(request, false); } /** * Performs a search and returns a list of business object data key values and relative statuses for a range of requested business object data. * * @param request the business object data availability request * @param skipRequestValidation specifies whether to skip the request validation and trimming * * @return the business object data availability information */ protected BusinessObjectDataAvailability checkBusinessObjectDataAvailabilityImpl(BusinessObjectDataAvailabilityRequest request, boolean skipRequestValidation) { // Perform the validation. if (!skipRequestValidation) { validateBusinessObjectDataAvailabilityRequest(request); } // Get business object format key from the request. BusinessObjectFormatKey businessObjectFormatKey = getBusinessObjectFormatKey(request); // Make sure that specified business object format exists. BusinessObjectFormatEntity businessObjectFormatEntity = businessObjectFormatDaoHelper.getBusinessObjectFormatEntity(businessObjectFormatKey); // Get the list of storages from the request and validate that specified storages exist. List<String> storageNames = getStorageNames(request); storageDaoHelper.validateStorageExistence(storageNames); // Build partition filters based on the specified partition value filters. // Business object data availability works across all storage platform types, so the storage platform type is not specified in the call. List<List<String>> partitionFilters = businessObjectDataDaoHelper .buildPartitionFilters(request.getPartitionValueFilters(), request.getPartitionValueFilter(), businessObjectFormatKey, request.getBusinessObjectDataVersion(), storageNames, null, null, businessObjectFormatEntity); // Retrieve a list of storage unit entities for the specified partition values. The entities will be sorted by partition value that is identified // by partition column position. If a business object data version isn't specified, the latest VALID business object data version is returned. // Business object data availability works across all storage platform types, so the storage platform type is not specified in the herdDao call. // We want to select only "available" storage units, so we pass "true" for selectOnlyAvailableStorageUnits parameter. List<StorageUnitEntity> availableStorageUnitEntities = storageUnitDao .getStorageUnitsByPartitionFiltersAndStorages(businessObjectFormatKey, partitionFilters, request.getBusinessObjectDataVersion(), BusinessObjectDataStatusEntity.VALID, storageNames, null, null, true); // Create business object data availability object instance and initialise it with request field values. BusinessObjectDataAvailability businessObjectDataAvailability = createBusinessObjectDataAvailability(request); // Create "available" and "not available" business object data status lists. List<BusinessObjectDataStatus> availableStatuses = new ArrayList<>(); businessObjectDataAvailability.setAvailableStatuses(availableStatuses); List<BusinessObjectDataStatus> notAvailableStatuses = new ArrayList<>(); businessObjectDataAvailability.setNotAvailableStatuses(notAvailableStatuses); // Build a list of matched available partition filters and populate the available statuses list. Please note that each request partition filter // might result in multiple available business object data entities. If storage names are not specified, fail on "duplicate" business object data // (same business object data instance registered with multiple storages). Otherwise, remove possible "duplicates". List<List<String>> matchedAvailablePartitionFilters = new ArrayList<>(); List<List<String>> availablePartitions = new ArrayList<>(); Map<BusinessObjectDataEntity, StorageUnitEntity> businessObjectDataToStorageUnitMap = new HashMap<>(); for (StorageUnitEntity storageUnitEntity : availableStorageUnitEntities) { BusinessObjectDataEntity businessObjectDataEntity = storageUnitEntity.getBusinessObjectData(); if (businessObjectDataToStorageUnitMap.containsKey(businessObjectDataEntity)) { // If storage names are not specified, fail on a business object data registered in multiple storages. Otherwise, ignore that storage unit. if (CollectionUtils.isEmpty(storageNames)) { throw new IllegalArgumentException(String.format("Found business object data registered in more than one storage. " + "Please specify storage(s) in the request to resolve this. Business object data {%s}", businessObjectDataHelper.businessObjectDataEntityAltKeyToString(businessObjectDataEntity))); } } else { BusinessObjectDataKey businessObjectDataKey = businessObjectDataHelper.getBusinessObjectDataKey(businessObjectDataEntity); matchedAvailablePartitionFilters.add(businessObjectDataHelper.getPartitionFilter(businessObjectDataKey, partitionFilters.get(0))); availablePartitions.add(businessObjectDataHelper.getPrimaryAndSubPartitionValues(businessObjectDataKey)); availableStatuses.add(createAvailableBusinessObjectDataStatus(businessObjectDataEntity)); businessObjectDataToStorageUnitMap.put(businessObjectDataEntity, storageUnitEntity); } } // Check if request specifies to include all registered sub-partitions in the response. boolean includeAllRegisteredSubPartitions = request.getBusinessObjectDataVersion() == null && BooleanUtils.isTrue(request.isIncludeAllRegisteredSubPartitions()); // If request specifies to include all registered sub-partitions in the response, query all // matched partition filters one more time to discover any non-available registered sub-partitions. if (includeAllRegisteredSubPartitions && !CollectionUtils.isEmpty(matchedAvailablePartitionFilters)) { addNotAvailableBusinessObjectDataStatuses(notAvailableStatuses, businessObjectFormatKey, matchedAvailablePartitionFilters, availablePartitions, storageNames); } // Get a list of unmatched partition filters. List<List<String>> unmatchedPartitionFilters = new ArrayList<>(partitionFilters); unmatchedPartitionFilters.removeAll(matchedAvailablePartitionFilters); // We still need to try to retrieve business object data per list of unmatched filters regardless of business object data and/or storage unit statuses. // This is done to populate not-available statuses with legitimate reasons. // Business object data availability works across all storage platform types, so the storage platform type is not specified in the herdDao call. // We want to select any existing storage units regardless of their status, so we pass "false" for selectOnlyAvailableStorageUnits parameter. List<StorageUnitEntity> notAvailableStorageUnitEntities = storageUnitDao .getStorageUnitsByPartitionFiltersAndStorages(businessObjectFormatKey, unmatchedPartitionFilters, request.getBusinessObjectDataVersion(), null, storageNames, null, null, false); // Populate the not-available statuses list. addNotAvailableBusinessObjectDataStatuses(notAvailableStatuses, notAvailableStorageUnitEntities); // Build a list of matched "not-available" partition filters. // Please note that each request partition filter might result in multiple available business object data entities. List<List<String>> matchedNotAvailablePartitionFilters = getPartitionFilters(notAvailableStorageUnitEntities, partitionFilters.get(0)); // Update the list of unmatched partition filters. unmatchedPartitionFilters.removeAll(matchedNotAvailablePartitionFilters); // Populate the "not available" statuses per remaining unmatched filters. for (List<String> unmatchedPartitionFilter : unmatchedPartitionFilters) { notAvailableStatuses.add(createNotAvailableBusinessObjectDataStatus(request, unmatchedPartitionFilter, REASON_NOT_REGISTERED)); } return businessObjectDataAvailability; } /** * Retrieves the DDL to initialize the specified type of the database system to perform queries for a collection of business object data in the specified * storages. * * @param businessObjectDataDdlCollectionRequest the business object data DDL collection request * * @return the business object data DDL information */ protected BusinessObjectDataDdlCollectionResponse generateBusinessObjectDataDdlCollectionImpl( BusinessObjectDataDdlCollectionRequest businessObjectDataDdlCollectionRequest) { // Perform the validation of the entire request, before we start processing the individual requests that requires the database access. validateBusinessObjectDataDdlCollectionRequest(businessObjectDataDdlCollectionRequest); // Process the individual requests and build the response. BusinessObjectDataDdlCollectionResponse businessObjectDataDdlCollectionResponse = new BusinessObjectDataDdlCollectionResponse(); List<BusinessObjectDataDdl> businessObjectDataDdlResponses = new ArrayList<>(); businessObjectDataDdlCollectionResponse.setBusinessObjectDataDdlResponses(businessObjectDataDdlResponses); List<String> ddls = new ArrayList<>(); for (BusinessObjectDataDdlRequest request : businessObjectDataDdlCollectionRequest.getBusinessObjectDataDdlRequests()) { // Please note that when calling to process individual ddl requests, we ask to skip the request validation and trimming step. BusinessObjectDataDdl businessObjectDataDdl = generateBusinessObjectDataDdlImpl(request, true); businessObjectDataDdlResponses.add(businessObjectDataDdl); ddls.add(businessObjectDataDdl.getDdl()); } businessObjectDataDdlCollectionResponse.setDdlCollection(StringUtils.join(ddls, "\n\n")); return businessObjectDataDdlCollectionResponse; } /** * Retrieves the DDL to initialize the specified type of the database system to perform queries for a range of requested business object data in the * specified storage. * * @param request the business object data DDL request * @param skipRequestValidation specifies whether to skip the request validation and trimming * * @return the business object data DDL information */ protected BusinessObjectDataDdl generateBusinessObjectDataDdlImpl(BusinessObjectDataDdlRequest request, boolean skipRequestValidation) { // Perform the validation. if (!skipRequestValidation) { validateBusinessObjectDataDdlRequest(request); } // Get the business object format entity for the specified parameters and make sure it exists. // Please note that when format version is not specified, we should get back the latest format version. BusinessObjectFormatEntity businessObjectFormatEntity = businessObjectFormatDaoHelper.getBusinessObjectFormatEntity( new BusinessObjectFormatKey(request.getNamespace(), request.getBusinessObjectDefinitionName(), request.getBusinessObjectFormatUsage(), request.getBusinessObjectFormatFileType(), request.getBusinessObjectFormatVersion())); // Validate that format has schema information. Assert.notEmpty(businessObjectFormatEntity.getSchemaColumns(), String.format( "Business object format with namespace \"%s\", business object definition name \"%s\", format usage \"%s\", format file type \"%s\"," + " and format version \"%s\" doesn't have schema information.", businessObjectFormatEntity.getBusinessObjectDefinition().getNamespace().getCode(), businessObjectFormatEntity.getBusinessObjectDefinition().getName(), businessObjectFormatEntity.getUsage(), businessObjectFormatEntity.getFileType().getCode(), businessObjectFormatEntity.getBusinessObjectFormatVersion())); // If it was specified, retrieve the custom DDL and ensure it exists. CustomDdlEntity customDdlEntity = null; if (StringUtils.isNotBlank(request.getCustomDdlName())) { CustomDdlKey customDdlKey = new CustomDdlKey(businessObjectFormatEntity.getBusinessObjectDefinition().getNamespace().getCode(), businessObjectFormatEntity.getBusinessObjectDefinition().getName(), businessObjectFormatEntity.getUsage(), businessObjectFormatEntity.getFileType().getCode(), businessObjectFormatEntity.getBusinessObjectFormatVersion(), request.getCustomDdlName()); customDdlEntity = customDdlDaoHelper.getCustomDdlEntity(customDdlKey); } // Validate that specified storages exist and of a proper storage platform type. List<String> storageNames = new ArrayList<>(); if (StringUtils.isNotBlank(request.getStorageName())) { storageNames.add(request.getStorageName()); } if (!CollectionUtils.isEmpty(request.getStorageNames())) { storageNames.addAll(request.getStorageNames()); } List<StorageEntity> storageEntities = new ArrayList<>(); for (String storageName : storageNames) { StorageEntity storageEntity = storageDaoHelper.getStorageEntity(storageName); // Only S3 storage platform is currently supported. Assert.isTrue(storageEntity.getStoragePlatform().getName().equals(StoragePlatformEntity.S3), String.format("Cannot generate DDL for \"%s\" storage platform.", storageEntity.getStoragePlatform().getName())); storageEntities.add(storageEntity); } // Validate that all storages have S3 bucket name configured. Map<StorageEntity, String> s3BucketNames = new HashMap<>(); for (StorageEntity storageEntity : storageEntities) { // Please note that since S3 bucket name attribute value is required we pass a "true" flag. String s3BucketName = storageHelper .getStorageAttributeValueByName(configurationHelper.getProperty(ConfigurationValue.S3_ATTRIBUTE_NAME_BUCKET_NAME), storageEntity, true); s3BucketNames.put(storageEntity, s3BucketName); } // Create and initialize a business object data DDL object instance. BusinessObjectDataDdl businessObjectDataDdl = createBusinessObjectDataDdl(request); businessObjectDataDdl.setDdl(ddlGeneratorFactory.getDdlGenerator(request.getOutputFormat()) .generateCreateTableDdl(request, businessObjectFormatEntity, customDdlEntity, storageNames, storageEntities, s3BucketNames)); return businessObjectDataDdl; } /** * Retrieves existing business object data entry information. This method does not start a new transaction and instead continues with existing transaction, * if any. * * @param businessObjectDataKey the business object data key * @param businessObjectFormatPartitionKey the business object format partition key, may be null * @param businessObjectDataStatus the business object data status, may be null * @param includeBusinessObjectDataStatusHistory specifies to include business object data status history in the response * * @return the retrieved business object data information */ protected BusinessObjectData getBusinessObjectDataImpl(BusinessObjectDataKey businessObjectDataKey, String businessObjectFormatPartitionKey, String businessObjectDataStatus, Boolean includeBusinessObjectDataStatusHistory) { // Validate and trim the business object data key. businessObjectDataHelper.validateBusinessObjectDataKey(businessObjectDataKey, false, false); // If specified, trim the partition key parameter. String businessObjectFormatPartitionKeyLocal = businessObjectFormatPartitionKey != null ? businessObjectFormatPartitionKey.trim() : null; // If specified, trim the business object data status parameter; otherwise default to VALID status. String businessObjectDataStatusLocal = businessObjectDataStatus != null ? businessObjectDataStatus.trim() : BusinessObjectDataStatusEntity.VALID; // Validate the business object data status. BusinessObjectDataStatusEntity businessObjectDataStatusEntity = businessObjectDataStatusDaoHelper.getBusinessObjectDataStatusEntity(businessObjectDataStatusLocal); // Get the business object data based on the specified parameters. If a business object data version isn't specified, // the latest version of business object data of the specified business object data status is returned. BusinessObjectDataEntity businessObjectDataEntity = businessObjectDataDaoHelper.getBusinessObjectDataEntityByKeyAndStatus(businessObjectDataKey, businessObjectDataStatusEntity.getCode()); // If specified, ensure the partition key matches what's configured within the business object format. if (StringUtils.isNotBlank(businessObjectFormatPartitionKeyLocal)) { String configuredPartitionKey = businessObjectDataEntity.getBusinessObjectFormat().getPartitionKey(); Assert.isTrue(configuredPartitionKey.equalsIgnoreCase(businessObjectFormatPartitionKeyLocal), String .format("Partition key \"%s\" doesn't match configured business object format partition key \"%s\".", businessObjectFormatPartitionKeyLocal, configuredPartitionKey)); } // Create and return the business object definition object from the persisted entity. return businessObjectDataHelper.createBusinessObjectDataFromEntity(businessObjectDataEntity, includeBusinessObjectDataStatusHistory); } /** * Delegates implementation to {@link org.finra.herd.service.helper.BusinessObjectDataInvalidateUnregisteredHelper}. Keeps current transaction context. * * @param businessObjectDataInvalidateUnregisteredRequest {@link org.finra.herd.model.api.xml.BusinessObjectDataInvalidateUnregisteredRequest} * * @return {@link BusinessObjectDataInvalidateUnregisteredResponse} */ protected BusinessObjectDataInvalidateUnregisteredResponse invalidateUnregisteredBusinessObjectDataImpl( BusinessObjectDataInvalidateUnregisteredRequest businessObjectDataInvalidateUnregisteredRequest) { return businessObjectDataInvalidateUnregisteredHelper.invalidateUnregisteredBusinessObjectData(businessObjectDataInvalidateUnregisteredRequest); } /** * Initiates a restore request for a currently archived business object data. Keeps current transaction context. * * @param businessObjectDataKey the business object data key * @param expirationInDays the the time, in days, between when the business object data is restored to the S3 bucket and when it expires * * @return the business object data information */ protected BusinessObjectData restoreBusinessObjectDataImpl(BusinessObjectDataKey businessObjectDataKey, Integer expirationInDays) { // Execute the initiate a restore request before step. BusinessObjectDataRestoreDto businessObjectDataRestoreDto = businessObjectDataInitiateRestoreHelperService.prepareToInitiateRestore(businessObjectDataKey, expirationInDays); // Create storage unit notification for the origin storage unit. notificationEventService.processStorageUnitNotificationEventAsync(NotificationEventTypeEntity.EventTypesStorageUnit.STRGE_UNIT_STTS_CHG, businessObjectDataRestoreDto.getBusinessObjectDataKey(), businessObjectDataRestoreDto.getStorageName(), businessObjectDataRestoreDto.getNewStorageUnitStatus(), businessObjectDataRestoreDto.getOldStorageUnitStatus()); // Initiate the restore request. businessObjectDataInitiateRestoreHelperService.executeS3SpecificSteps(businessObjectDataRestoreDto); // On failure of the above step, execute the "after" step, and re-throw the exception. if (businessObjectDataRestoreDto.getException() != null) { // On failure, execute the after step that updates the origin storage unit status to DISABLED. businessObjectDataInitiateRestoreHelperService.executeInitiateRestoreAfterStep(businessObjectDataRestoreDto); // Create storage unit notification for the origin storage unit. notificationEventService.processStorageUnitNotificationEventAsync(NotificationEventTypeEntity.EventTypesStorageUnit.STRGE_UNIT_STTS_CHG, businessObjectDataRestoreDto.getBusinessObjectDataKey(), businessObjectDataRestoreDto.getStorageName(), businessObjectDataRestoreDto.getNewStorageUnitStatus(), businessObjectDataRestoreDto.getOldStorageUnitStatus()); // Re-throw the original exception. throw new IllegalStateException(businessObjectDataRestoreDto.getException()); } else { // Execute the after step for the initiate a business object data restore request // and return the business object data information. return businessObjectDataInitiateRestoreHelperService.executeInitiateRestoreAfterStep(businessObjectDataRestoreDto); } } /** * Adds business object data status instances created from the list of storage unit entities to the specified list of not-available statuses. * * @param notAvailableStatuses the list of not-available statuses * @param storageUnitEntities the list of storage unit entities */ private void addNotAvailableBusinessObjectDataStatuses(List<BusinessObjectDataStatus> notAvailableStatuses, List<StorageUnitEntity> storageUnitEntities) { for (StorageUnitEntity storageUnitEntity : storageUnitEntities) { notAvailableStatuses.add(createNotAvailableBusinessObjectDataStatus(storageUnitEntity)); } } /** * Creates a business object data status instance from the business object data entity. * * @param businessObjectDataEntity the business object data entity * * @return the business object data status instance */ private BusinessObjectDataStatus createAvailableBusinessObjectDataStatus(BusinessObjectDataEntity businessObjectDataEntity) { BusinessObjectDataStatus businessObjectDataStatus = new BusinessObjectDataStatus(); businessObjectDataStatus.setBusinessObjectFormatVersion(businessObjectDataEntity.getBusinessObjectFormat().getBusinessObjectFormatVersion()); businessObjectDataStatus.setPartitionValue(businessObjectDataEntity.getPartitionValue()); businessObjectDataStatus.setSubPartitionValues(businessObjectDataHelper.getSubPartitionValues(businessObjectDataEntity)); businessObjectDataStatus.setBusinessObjectDataVersion(businessObjectDataEntity.getVersion()); businessObjectDataStatus.setReason(businessObjectDataEntity.getStatus().getCode()); return businessObjectDataStatus; } /** * Creates business object data availability object instance and initialise it with the business object data availability request field values. * * @param request the business object data availability request * * @return the newly created BusinessObjectDataAvailability object instance */ private BusinessObjectDataAvailability createBusinessObjectDataAvailability(BusinessObjectDataAvailabilityRequest request) { BusinessObjectDataAvailability businessObjectDataAvailability = new BusinessObjectDataAvailability(); businessObjectDataAvailability.setNamespace(request.getNamespace()); businessObjectDataAvailability.setBusinessObjectDefinitionName(request.getBusinessObjectDefinitionName()); businessObjectDataAvailability.setBusinessObjectFormatUsage(request.getBusinessObjectFormatUsage()); businessObjectDataAvailability.setBusinessObjectFormatFileType(request.getBusinessObjectFormatFileType()); businessObjectDataAvailability.setBusinessObjectFormatVersion(request.getBusinessObjectFormatVersion()); businessObjectDataAvailability.setPartitionValueFilters(request.getPartitionValueFilters()); businessObjectDataAvailability.setPartitionValueFilter(request.getPartitionValueFilter()); businessObjectDataAvailability.setBusinessObjectDataVersion(request.getBusinessObjectDataVersion()); businessObjectDataAvailability.setStorageNames(request.getStorageNames()); businessObjectDataAvailability.setStorageName(request.getStorageName()); return businessObjectDataAvailability; } /** * Creates business object data ddl object instance and initialise it with the business object data ddl request field values. * * @param request the business object data ddl request * * @return the newly created BusinessObjectDataDdl object instance */ private BusinessObjectDataDdl createBusinessObjectDataDdl(BusinessObjectDataDdlRequest request) { BusinessObjectDataDdl businessObjectDataDdl = new BusinessObjectDataDdl(); businessObjectDataDdl.setNamespace(request.getNamespace()); businessObjectDataDdl.setBusinessObjectDefinitionName(request.getBusinessObjectDefinitionName()); businessObjectDataDdl.setBusinessObjectFormatUsage(request.getBusinessObjectFormatUsage()); businessObjectDataDdl.setBusinessObjectFormatFileType(request.getBusinessObjectFormatFileType()); businessObjectDataDdl.setBusinessObjectFormatVersion(request.getBusinessObjectFormatVersion()); businessObjectDataDdl.setPartitionValueFilters(request.getPartitionValueFilters()); businessObjectDataDdl.setPartitionValueFilter(request.getPartitionValueFilter()); businessObjectDataDdl.setBusinessObjectDataVersion(request.getBusinessObjectDataVersion()); businessObjectDataDdl.setStorageNames(request.getStorageNames()); businessObjectDataDdl.setStorageName(request.getStorageName()); businessObjectDataDdl.setOutputFormat(request.getOutputFormat()); businessObjectDataDdl.setTableName(request.getTableName()); businessObjectDataDdl.setCustomDdlName(request.getCustomDdlName()); return businessObjectDataDdl; } /** * Creates a business object data status instance from the storage unit entity. * * @param storageUnitEntity the storage unit entity * * @return the business object data status instance */ private BusinessObjectDataStatus createNotAvailableBusinessObjectDataStatus(StorageUnitEntity storageUnitEntity) { // Get the business object entity. BusinessObjectDataEntity businessObjectDataEntity = storageUnitEntity.getBusinessObjectData(); // Create and populate the business object data status instance. BusinessObjectDataStatus businessObjectDataStatus = new BusinessObjectDataStatus(); businessObjectDataStatus.setBusinessObjectFormatVersion(businessObjectDataEntity.getBusinessObjectFormat().getBusinessObjectFormatVersion()); businessObjectDataStatus.setPartitionValue(businessObjectDataEntity.getPartitionValue()); businessObjectDataStatus.setSubPartitionValues(businessObjectDataHelper.getSubPartitionValues(businessObjectDataEntity)); businessObjectDataStatus.setBusinessObjectDataVersion(businessObjectDataEntity.getVersion()); // If storage unit is "available", the business object data is selected as "non-available" due to its business object data status. if (storageUnitEntity.getStatus().getAvailable()) { businessObjectDataStatus.setReason(businessObjectDataEntity.getStatus().getCode()); } // Otherwise, report the storage unit status as a reason for the business object data not being available. else { businessObjectDataStatus.setReason(storageUnitEntity.getStatus().getCode()); } return businessObjectDataStatus; } /** * Creates the business object data status. * * @param businessObjectDataAvailabilityRequest the business object data availability request * @param unmatchedPartitionFilter the partition filter that got no matched business object data instances * @param reason the reason for the business object data not being available * * @return the business object data status */ private BusinessObjectDataStatus createNotAvailableBusinessObjectDataStatus(BusinessObjectDataAvailabilityRequest businessObjectDataAvailabilityRequest, List<String> unmatchedPartitionFilter, String reason) { BusinessObjectDataStatus businessObjectDataStatus = new BusinessObjectDataStatus(); // Populate business object data status values using the business object data availability request. businessObjectDataStatus.setBusinessObjectFormatVersion(businessObjectDataAvailabilityRequest.getBusinessObjectFormatVersion()); // When list of partition value filters is used, we populate primary and/or sub-partition values. if (businessObjectDataAvailabilityRequest.getPartitionValueFilters() != null) { // Replace all null partition values with an empty string. replaceAllNullsWithEmptyString(unmatchedPartitionFilter); // Populate primary and sub-partition values from the unmatched partition filter. businessObjectDataStatus.setPartitionValue(unmatchedPartitionFilter.get(0)); businessObjectDataStatus.setSubPartitionValues(unmatchedPartitionFilter.subList(1, unmatchedPartitionFilter.size())); } // Otherwise, for backwards compatibility, populate primary partition value only per expected single partition value from the unmatched filter. else { // Since the availability request contains a standalone partition value filter, // the unmatched partition filter is expected to contain only a single partition value. for (String partitionValue : unmatchedPartitionFilter) { if (partitionValue != null) { businessObjectDataStatus.setPartitionValue(partitionValue); break; } } } businessObjectDataStatus.setBusinessObjectDataVersion(businessObjectDataAvailabilityRequest.getBusinessObjectDataVersion()); businessObjectDataStatus.setReason(reason); return businessObjectDataStatus; } /** * Gets business object format key from the business object data availability request. * * @param request the business object data availability request * * @return the business object format key */ private BusinessObjectFormatKey getBusinessObjectFormatKey(BusinessObjectDataAvailabilityRequest request) { return new BusinessObjectFormatKey(request.getNamespace(), request.getBusinessObjectDefinitionName(), request.getBusinessObjectFormatUsage(), request.getBusinessObjectFormatFileType(), request.getBusinessObjectFormatVersion()); } /** * Gets a list of matched partition filters per specified list of storage unit entities and a sample partition filter. * * @param storageUnitEntities the list of storage unit entities * @param samplePartitionFilter the sample partition filter * * @return the list of partition filters */ private List<List<String>> getPartitionFilters(List<StorageUnitEntity> storageUnitEntities, List<String> samplePartitionFilter) { List<List<String>> partitionFilters = new ArrayList<>(); for (StorageUnitEntity storageUnitEntity : storageUnitEntities) { BusinessObjectDataKey businessObjectDataKey = businessObjectDataHelper.getBusinessObjectDataKey(storageUnitEntity.getBusinessObjectData()); partitionFilters.add(businessObjectDataHelper.getPartitionFilter(businessObjectDataKey, samplePartitionFilter)); } return partitionFilters; } /** * Gets storage names from the business object data availability request. * * @param request the business object data availability request * * @return the list of storage names */ private List<String> getStorageNames(BusinessObjectDataAvailabilityRequest request) { List<String> storageNames = new ArrayList<>(); if (StringUtils.isNotBlank(request.getStorageName())) { storageNames.add(request.getStorageName()); } if (!CollectionUtils.isEmpty(request.getStorageNames())) { storageNames.addAll(request.getStorageNames()); } return storageNames; } /** * Replaces all null values in the specified list with empty strings. * * @param list the list of strings */ private void replaceAllNullsWithEmptyString(List<String> list) { for (int i = 0; i < list.size(); i++) { if (list.get(i) == null) { list.set(i, ""); } } } /** * Validates a business object data availability collection request. This method also trims appropriate request parameters. * * @param businessObjectDataAvailabilityCollectionRequest the request * * @throws IllegalArgumentException if any validation errors were found */ private void validateBusinessObjectDataAvailabilityCollectionRequest( BusinessObjectDataAvailabilityCollectionRequest businessObjectDataAvailabilityCollectionRequest) { Assert.notNull(businessObjectDataAvailabilityCollectionRequest, "A business object data availability collection request must be specified."); Assert.isTrue(!CollectionUtils.isEmpty(businessObjectDataAvailabilityCollectionRequest.getBusinessObjectDataAvailabilityRequests()), "At least one business object data availability request must be specified."); for (BusinessObjectDataAvailabilityRequest request : businessObjectDataAvailabilityCollectionRequest.getBusinessObjectDataAvailabilityRequests()) { validateBusinessObjectDataAvailabilityRequest(request); } } /** * Validates the business object data availability request. This method also trims appropriate request parameters. * * @param request the request * * @throws IllegalArgumentException if any validation errors were found */ private void validateBusinessObjectDataAvailabilityRequest(BusinessObjectDataAvailabilityRequest request) { Assert.notNull(request, "A business object data availability request must be specified."); // Validate and trim the request parameters. Assert.hasText(request.getNamespace(), "A namespace must be specified."); request.setNamespace(request.getNamespace().trim()); Assert.hasText(request.getBusinessObjectDefinitionName(), "A business object definition name must be specified."); request.setBusinessObjectDefinitionName(request.getBusinessObjectDefinitionName().trim()); Assert.hasText(request.getBusinessObjectFormatUsage(), "A business object format usage must be specified."); request.setBusinessObjectFormatUsage(request.getBusinessObjectFormatUsage().trim()); Assert.hasText(request.getBusinessObjectFormatFileType(), "A business object format file type must be specified."); request.setBusinessObjectFormatFileType(request.getBusinessObjectFormatFileType().trim()); // Validate the partition value filters. Allow partition value tokens to be specified. businessObjectDataHelper.validatePartitionValueFilters(request.getPartitionValueFilters(), request.getPartitionValueFilter(), true); // Make sure that request does not contain both a list of storage names and a standalone storage name. Assert.isTrue(request.getStorageNames() == null || request.getStorageName() == null, "A list of storage names and a standalone storage name cannot be both specified."); // Trim the standalone storage name, if specified. if (request.getStorageName() != null) { Assert.hasText(request.getStorageName(), "A storage name must be specified."); request.setStorageName(request.getStorageName().trim()); } // Validate and trim the list of storage names. if (!CollectionUtils.isEmpty(request.getStorageNames())) { for (int i = 0; i < request.getStorageNames().size(); i++) { Assert.hasText(request.getStorageNames().get(i), "A storage name must be specified."); request.getStorageNames().set(i, request.getStorageNames().get(i).trim()); } } } /** * Validates a business object data DDL collection request. This method also trims appropriate request parameters. * * @param businessObjectDataDdlCollectionRequest the request * * @throws IllegalArgumentException if any validation errors were found */ private void validateBusinessObjectDataDdlCollectionRequest(BusinessObjectDataDdlCollectionRequest businessObjectDataDdlCollectionRequest) { Assert.notNull(businessObjectDataDdlCollectionRequest, "A business object data DDL collection request must be specified."); Assert.isTrue(!CollectionUtils.isEmpty(businessObjectDataDdlCollectionRequest.getBusinessObjectDataDdlRequests()), "At least one business object data DDL request must be specified."); for (BusinessObjectDataDdlRequest request : businessObjectDataDdlCollectionRequest.getBusinessObjectDataDdlRequests()) { validateBusinessObjectDataDdlRequest(request); } } /** * Validates the business object data DDL request. This method also trims appropriate request parameters. * * @param request the request * * @throws IllegalArgumentException if any validation errors were found */ private void validateBusinessObjectDataDdlRequest(BusinessObjectDataDdlRequest request) { Assert.notNull(request, "A business object data DDL request must be specified."); // Validate and trim the request parameters. Assert.hasText(request.getNamespace(), "A namespace must be specified."); request.setNamespace(request.getNamespace().trim()); Assert.hasText(request.getBusinessObjectDefinitionName(), "A business object definition name must be specified."); request.setBusinessObjectDefinitionName(request.getBusinessObjectDefinitionName().trim()); Assert.hasText(request.getBusinessObjectFormatUsage(), "A business object format usage must be specified."); request.setBusinessObjectFormatUsage(request.getBusinessObjectFormatUsage().trim()); Assert.hasText(request.getBusinessObjectFormatFileType(), "A business object format file type must be specified."); request.setBusinessObjectFormatFileType(request.getBusinessObjectFormatFileType().trim()); // Validate the partition value filters. Do not allow partition value tokens to be specified. businessObjectDataHelper.validatePartitionValueFilters(request.getPartitionValueFilters(), request.getPartitionValueFilter(), false); // Make sure that request does not contain both a list of storage names and a standalone storage name. Assert.isTrue(request.getStorageNames() == null || request.getStorageName() == null, "A list of storage names and a standalone storage name cannot be both specified."); // Trim the standalone storage name, if specified. if (request.getStorageName() != null) { Assert.hasText(request.getStorageName(), "A storage name must be specified."); request.setStorageName(request.getStorageName().trim()); } // Validate and trim the list of storage names. if (!CollectionUtils.isEmpty(request.getStorageNames())) { for (int i = 0; i < request.getStorageNames().size(); i++) { Assert.hasText(request.getStorageNames().get(i), "A storage name must be specified."); request.getStorageNames().set(i, request.getStorageNames().get(i).trim()); } } Assert.notNull(request.getOutputFormat(), "An output format must be specified."); Assert.hasText(request.getTableName(), "A table name must be specified."); request.setTableName(request.getTableName().trim()); if (StringUtils.isNotBlank(request.getCustomDdlName())) { request.setCustomDdlName(request.getCustomDdlName().trim()); } } }