<commit_before>/*
 * Copyright 2014 CyberVision, Inc.
 *
 * 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.kaaproject.kaa.server.common.dao.service;

import static org.apache.commons.lang.StringUtils.isBlank;
import static org.kaaproject.kaa.common.dto.UpdateStatus.ACTIVE;
import static org.kaaproject.kaa.common.dto.UpdateStatus.INACTIVE;
import static org.kaaproject.kaa.server.common.dao.impl.DaoUtil.convertDtoList;
import static org.kaaproject.kaa.server.common.dao.impl.DaoUtil.getDto;
import static org.kaaproject.kaa.server.common.dao.impl.DaoUtil.idToString;
import static org.kaaproject.kaa.server.common.dao.service.Validator.isValidId;
import static org.kaaproject.kaa.server.common.dao.service.Validator.validateId;
import static org.kaaproject.kaa.server.common.dao.service.Validator.validateSqlId;
import static org.kaaproject.kaa.server.common.dao.service.Validator.validateSqlObject;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import org.apache.avro.generic.GenericRecord;
import org.apache.commons.lang.StringUtils;
import org.kaaproject.kaa.common.avro.GenericAvroConverter;
import org.kaaproject.kaa.common.dto.ChangeConfigurationNotification;
import org.kaaproject.kaa.common.dto.ChangeDto;
import org.kaaproject.kaa.common.dto.ChangeNotificationDto;
import org.kaaproject.kaa.common.dto.ChangeType;
import org.kaaproject.kaa.common.dto.ConfigurationDto;
import org.kaaproject.kaa.common.dto.ConfigurationSchemaDto;
import org.kaaproject.kaa.common.dto.HistoryDto;
import org.kaaproject.kaa.common.dto.SchemaDto;
import org.kaaproject.kaa.common.dto.StructureRecordDto;
import org.kaaproject.kaa.common.dto.UpdateStatus;
import org.kaaproject.kaa.server.common.core.algorithms.generation.ConfigurationGenerationException;
import org.kaaproject.kaa.server.common.core.algorithms.generation.DefaultRecordGenerationAlgorithm;
import org.kaaproject.kaa.server.common.core.algorithms.generation.DefaultRecordGenerationAlgorithmImpl;
import org.kaaproject.kaa.server.common.core.algorithms.schema.SchemaCreationException;
import org.kaaproject.kaa.server.common.core.algorithms.schema.SchemaGenerationAlgorithm;
import org.kaaproject.kaa.server.common.core.algorithms.schema.SchemaGenerationAlgorithmFactory;
import org.kaaproject.kaa.server.common.core.algorithms.validator.DefaultUuidValidator;
import org.kaaproject.kaa.server.common.core.algorithms.validator.UuidValidator;
import org.kaaproject.kaa.server.common.core.configuration.BaseData;
import org.kaaproject.kaa.server.common.core.configuration.BaseDataFactory;
import org.kaaproject.kaa.server.common.core.configuration.KaaData;
import org.kaaproject.kaa.server.common.core.configuration.OverrideDataFactory;
import org.kaaproject.kaa.server.common.core.schema.BaseSchema;
import org.kaaproject.kaa.server.common.core.schema.DataSchema;
import org.kaaproject.kaa.server.common.core.schema.OverrideSchema;
import org.kaaproject.kaa.server.common.core.schema.ProtocolSchema;
import org.kaaproject.kaa.server.common.dao.ConfigurationService;
import org.kaaproject.kaa.server.common.dao.HistoryService;
import org.kaaproject.kaa.server.common.dao.exception.DatabaseProcessingException;
import org.kaaproject.kaa.server.common.dao.exception.IncorrectParameterException;
import org.kaaproject.kaa.server.common.dao.exception.UpdateStatusConflictException;
import org.kaaproject.kaa.server.common.dao.impl.ConfigurationDao;
import org.kaaproject.kaa.server.common.dao.impl.ConfigurationSchemaDao;
import org.kaaproject.kaa.server.common.dao.impl.EndpointGroupDao;
import org.kaaproject.kaa.server.common.dao.model.sql.Configuration;
import org.kaaproject.kaa.server.common.dao.model.sql.ConfigurationSchema;
import org.kaaproject.kaa.server.common.dao.model.sql.EndpointGroup;
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.Transactional;

@Service
@Transactional
public class ConfigurationServiceImpl implements ConfigurationService {

    private static final Logger LOG = LoggerFactory.getLogger(ConfigurationServiceImpl.class);

    private static final String DEFAULT_STRUCT_DESC = "Generated";

    @Autowired
    private ConfigurationDao<Configuration> configurationDao;

    @Autowired
    private EndpointGroupDao<EndpointGroup> endpointGroupDao;

    @Autowired
    private ConfigurationSchemaDao<ConfigurationSchema> configurationSchemaDao;

    @Autowired
    private HistoryService historyService;

    @Autowired
    private SchemaGenerationAlgorithmFactory schemaGeneratorFactory;

    private DefaultRecordGenerationAlgorithm<BaseData> configurationProcessor;

    private UuidValidator uuidValidator;

    @Override
    @Deprecated
    public ConfigurationDto findConfigurationByAppIdAndVersion(String applicationId, int version) {
        validateSqlId(applicationId, "Application id is incorrect. Can't find configuration by application id "
                + applicationId + " and version " + version);
        return getDto(configurationDao.findConfigurationByAppIdAndVersion(applicationId, version));
    }

    @Override
    public ConfigurationDto findConfigurationByEndpointGroupIdAndVersion(String endpointGroupId, int version) {
        validateId(endpointGroupId, "Endpoint group id is incorrect. Can't find configuration by endpoint group id "
                + endpointGroupId + " and version " + version);
        return getDto(configurationDao.findConfigurationByEndpointGroupIdAndVersion(endpointGroupId, version));
    }

    @Override
    public ConfigurationDto findDefaultConfigurationBySchemaId(String schemaId) {
        validateId(schemaId, "Schema id is incorrect. Can't find default configuration by schema id "
                + schemaId);
        ConfigurationDto configuration = null;
        ConfigurationSchema configurationSchema = configurationSchemaDao.findById(schemaId);
        if (configurationSchema != null) {
            String appId = configurationSchema.getApplicationId();
            EndpointGroup endpointGroup = endpointGroupDao.findByAppIdAndWeight(appId, 0);
            if (endpointGroup != null) {
                String endpointGroupId = String.valueOf(endpointGroup.getId());
                configuration = getDto(configurationDao.findConfigurationByEndpointGroupIdAndVersion(endpointGroupId, configurationSchema.getMajorVersion()));
            } else {
                LOG.warn("Can't find default group for application [{}]", appId);
            }
        } else {
            LOG.warn("Can't find configuration schema for id [{}]", schemaId);
        }
        return configuration;
    }

    @Override
    public ConfigurationDto findConfigurationById(String id) {
        validateSqlId(id, "Configuration id is incorrect. Can't find configuration by id " + id);
        return getDto(configurationDao.findById(id));
    }

    @Override
    public Collection<StructureRecordDto<ConfigurationDto>> findAllConfigurationRecordsByEndpointGroupId(
            String endpointGroupId, boolean includeDeprecated) {
        Collection<ConfigurationDto> configurations = convertDtoList(configurationDao.findActualByEndpointGroupId(endpointGroupId));
        List<StructureRecordDto<ConfigurationDto>> records = StructureRecordDto.convertToRecords(configurations);
        if (includeDeprecated) {
            List<SchemaDto> schemas = findVacantSchemasByEndpointGroupId(endpointGroupId);
            for (SchemaDto schema : schemas) {
                ConfigurationDto deprecatedConfiguration = getDto(configurationDao.findLatestDeprecated(schema.getId(), endpointGroupId));
                if (deprecatedConfiguration != null) {
                    StructureRecordDto<ConfigurationDto> record = new StructureRecordDto<>();
                    record.setActiveStructureDto(deprecatedConfiguration);
                    records.add(record);
                }
            }
        }
        Collections.sort(records);
        return records;
    }

    @Override
    public StructureRecordDto<ConfigurationDto> findConfigurationRecordBySchemaIdAndEndpointGroupId(
            String schemaId, String endpointGroupId) {
        StructureRecordDto<ConfigurationDto> record = new StructureRecordDto<>();
        Collection<ConfigurationDto> configurations = convertDtoList(configurationDao.findActualBySchemaIdAndGroupId(schemaId, endpointGroupId));
        if (configurations != null) {
            for (ConfigurationDto configuration : configurations) {
                if (configuration.getStatus()==UpdateStatus.ACTIVE) {
                    record.setActiveStructureDto(configuration);
                } else if (configuration.getStatus()==UpdateStatus.INACTIVE) {
                    record.setInactiveStructureDto(configuration);
                }
            }
        }
        if (!record.hasActive()) {
            ConfigurationDto deprecatedConfiguration = getDto(configurationDao.findLatestDeprecated(schemaId, endpointGroupId));
            if (deprecatedConfiguration != null) {
                record.setActiveStructureDto(deprecatedConfiguration);
            }
        }
        if (record.isEmpty()) {
            LOG.debug("Can't find related Configuration record.");
            throw new IncorrectParameterException("Configuration record not found, schemaId: " + schemaId + ", endpointGroupId: " + endpointGroupId); //NOSONAR
        }
        return record;
    }

    @Override
    public List<SchemaDto> findVacantSchemasByEndpointGroupId(String endpointGroupId) {
        validateId(endpointGroupId, "Can't find vacant schemas. Invalid endpoint group id: " + endpointGroupId);
        EndpointGroup group = endpointGroupDao.findById(endpointGroupId);
        List<Configuration> configurations = configurationDao.findActualByEndpointGroupId(endpointGroupId);
        List<String> usedSchemaIds = new ArrayList<>();
        for (Configuration configuration : configurations) {
            ConfigurationSchema schema = configuration.getConfigurationSchema();
            if (schema != null) {
                usedSchemaIds.add(idToString(schema.getId()));
            }
        }
        List<ConfigurationSchema> schemas = configurationSchemaDao.findVacantSchemas(group.getApplicationId(), usedSchemaIds);
        List<SchemaDto> schemaDtoList = new ArrayList<>();
        for (ConfigurationSchema schema : schemas) {
            schemaDtoList.add(schema.toSchemaDto());
        }
        return schemaDtoList;
    }

    @Override
    public ConfigurationDto saveConfiguration(ConfigurationDto configurationDto) {
        validateConfiguration(configurationDto);
        String id = configurationDto.getId();
        ConfigurationSchemaDto configurationSchemaDto;
        Configuration oldActiveConfiguration = null;
        if (StringUtils.isNotBlank(id)) {
            ConfigurationDto oldConfiguration = findConfigurationById(configurationDto.getId());
            if (oldConfiguration != null && oldConfiguration.getStatus() != INACTIVE) {
                throw new UpdateStatusConflictException("Can't update configuration, invalid id " + id);
            }
            configurationSchemaDto = findConfSchemaById(configurationDto.getSchemaId());
            LOG.debug("Update existing configuration with id: [{}]", configurationDto.getId());
        } else {
            String schemaId = configurationDto.getSchemaId();
            String groupId = configurationDto.getEndpointGroupId();
            configurationSchemaDto = findConfSchemaById(schemaId);
            if (configurationSchemaDto != null) {
                Configuration oldInactiveConfiguration = configurationDao.findInactiveBySchemaIdAndGroupId(schemaId, groupId);
                oldActiveConfiguration = configurationDao.findLatestActiveBySchemaIdAndGroupId(schemaId, groupId);
                if (oldInactiveConfiguration != null) {
                    configurationDto.setId(idToString(oldInactiveConfiguration.getId()));
                    configurationDto.setSequenceNumber(oldInactiveConfiguration.getSequenceNumber());
                } else if (oldActiveConfiguration != null) {
                    configurationDto.setSequenceNumber(oldActiveConfiguration.getSequenceNumber());
                }
                configurationDto.setApplicationId(configurationSchemaDto.getApplicationId());
                configurationDto.setMajorVersion(configurationSchemaDto.getMajorVersion());
                configurationDto.setMinorVersion(configurationSchemaDto.getMinorVersion());
                configurationDto.setProtocolSchema(configurationSchemaDto.getProtocolSchema());
                configurationDto.setCreatedTime(System.currentTimeMillis());
            } else {
                LOG.debug("Can't find related Configuration schema.");
                throw new IncorrectParameterException("Configuration schema not found, id:" + schemaId);
            }
        }
        ConfigurationDto oldActiveConfigurationDto = oldActiveConfiguration != null ? oldActiveConfiguration.toDto() : null;
        validateUuids(configurationDto, oldActiveConfigurationDto, configurationSchemaDto);

        configurationDto.setStatus(UpdateStatus.INACTIVE);
        configurationDto.setLastModifyTime(System.currentTimeMillis());
        return getDto(configurationDao.save(new Configuration(configurationDto)));
    }

    private void validateUuids(ConfigurationDto currentConfiguration, ConfigurationDto previousConfiguration, ConfigurationSchemaDto configurationSchema) {
        try {
            EndpointGroup endpointGroup = endpointGroupDao.findById(currentConfiguration.getEndpointGroupId());
            GenericAvroConverter<GenericRecord> avroConverter;
            KaaData body = null;
            if (endpointGroup != null) {
                if (endpointGroup.getWeight() == 0) {
                    LOG.debug("Create default UUID validator with base schema: {}", configurationSchema.getBaseSchema());
                    BaseSchema baseSchema = new BaseSchema(configurationSchema.getBaseSchema());
                    uuidValidator = new DefaultUuidValidator(baseSchema, new BaseDataFactory());
                    avroConverter = new GenericAvroConverter<GenericRecord>(baseSchema.getRawSchema());
                } else {
                    LOG.debug("Create default UUID validator with override schema: {}", configurationSchema.getOverrideSchema());
                    OverrideSchema overrideSchema = new OverrideSchema(configurationSchema.getOverrideSchema());
                    uuidValidator = new DefaultUuidValidator(overrideSchema, new OverrideDataFactory());
                    avroConverter = new GenericAvroConverter<GenericRecord>(overrideSchema.getRawSchema());
                }
                GenericRecord previousRecord = null;
                if (previousConfiguration != null) {
                    previousRecord = avroConverter.decodeJson(previousConfiguration.getBody());
                }
                GenericRecord currentRecord = avroConverter.decodeJson(currentConfiguration.getBody());
                body = uuidValidator.validateUuidFields(currentRecord, previousRecord);
            }
            if (body != null) {
                currentConfiguration.setBody(body.getRawData());<commit_msg>KAA-506: resolve versioning problem while merging<commit_after>/*
 * Copyright 2014 CyberVision, Inc.
 *
 * 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.kaaproject.kaa.server.common.dao.service;

import static org.apache.commons.lang.StringUtils.isBlank;
import static org.kaaproject.kaa.common.dto.UpdateStatus.ACTIVE;
import static org.kaaproject.kaa.common.dto.UpdateStatus.INACTIVE;
import static org.kaaproject.kaa.server.common.dao.impl.DaoUtil.convertDtoList;
import static org.kaaproject.kaa.server.common.dao.impl.DaoUtil.getDto;
import static org.kaaproject.kaa.server.common.dao.impl.DaoUtil.idToString;
import static org.kaaproject.kaa.server.common.dao.service.Validator.isValidId;
import static org.kaaproject.kaa.server.common.dao.service.Validator.validateId;
import static org.kaaproject.kaa.server.common.dao.service.Validator.validateSqlId;
import static org.kaaproject.kaa.server.common.dao.service.Validator.validateSqlObject;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import org.apache.avro.generic.GenericRecord;
import org.apache.commons.lang.StringUtils;
import org.kaaproject.kaa.common.avro.GenericAvroConverter;
import org.kaaproject.kaa.common.dto.ChangeConfigurationNotification;
import org.kaaproject.kaa.common.dto.ChangeDto;
import org.kaaproject.kaa.common.dto.ChangeNotificationDto;
import org.kaaproject.kaa.common.dto.ChangeType;
import org.kaaproject.kaa.common.dto.ConfigurationDto;
import org.kaaproject.kaa.common.dto.ConfigurationSchemaDto;
import org.kaaproject.kaa.common.dto.HistoryDto;
import org.kaaproject.kaa.common.dto.SchemaDto;
import org.kaaproject.kaa.common.dto.StructureRecordDto;
import org.kaaproject.kaa.common.dto.UpdateStatus;
import org.kaaproject.kaa.server.common.core.algorithms.generation.ConfigurationGenerationException;
import org.kaaproject.kaa.server.common.core.algorithms.generation.DefaultRecordGenerationAlgorithm;
import org.kaaproject.kaa.server.common.core.algorithms.generation.DefaultRecordGenerationAlgorithmImpl;
import org.kaaproject.kaa.server.common.core.algorithms.schema.SchemaCreationException;
import org.kaaproject.kaa.server.common.core.algorithms.schema.SchemaGenerationAlgorithm;
import org.kaaproject.kaa.server.common.core.algorithms.schema.SchemaGenerationAlgorithmFactory;
import org.kaaproject.kaa.server.common.core.algorithms.validator.DefaultUuidValidator;
import org.kaaproject.kaa.server.common.core.algorithms.validator.UuidValidator;
import org.kaaproject.kaa.server.common.core.configuration.BaseData;
import org.kaaproject.kaa.server.common.core.configuration.BaseDataFactory;
import org.kaaproject.kaa.server.common.core.configuration.KaaData;
import org.kaaproject.kaa.server.common.core.configuration.OverrideDataFactory;
import org.kaaproject.kaa.server.common.core.schema.BaseSchema;
import org.kaaproject.kaa.server.common.core.schema.DataSchema;
import org.kaaproject.kaa.server.common.core.schema.OverrideSchema;
import org.kaaproject.kaa.server.common.core.schema.ProtocolSchema;
import org.kaaproject.kaa.server.common.dao.ConfigurationService;
import org.kaaproject.kaa.server.common.dao.HistoryService;
import org.kaaproject.kaa.server.common.dao.exception.DatabaseProcessingException;
import org.kaaproject.kaa.server.common.dao.exception.IncorrectParameterException;
import org.kaaproject.kaa.server.common.dao.exception.UpdateStatusConflictException;
import org.kaaproject.kaa.server.common.dao.impl.ConfigurationDao;
import org.kaaproject.kaa.server.common.dao.impl.ConfigurationSchemaDao;
import org.kaaproject.kaa.server.common.dao.impl.EndpointGroupDao;
import org.kaaproject.kaa.server.common.dao.model.sql.Configuration;
import org.kaaproject.kaa.server.common.dao.model.sql.ConfigurationSchema;
import org.kaaproject.kaa.server.common.dao.model.sql.EndpointGroup;
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.Transactional;

@Service
@Transactional
public class ConfigurationServiceImpl implements ConfigurationService {

    private static final Logger LOG = LoggerFactory.getLogger(ConfigurationServiceImpl.class);

    private static final String DEFAULT_STRUCT_DESC = "Generated";

    @Autowired
    private ConfigurationDao<Configuration> configurationDao;

    @Autowired
    private EndpointGroupDao<EndpointGroup> endpointGroupDao;

    @Autowired
    private ConfigurationSchemaDao<ConfigurationSchema> configurationSchemaDao;

    @Autowired
    private HistoryService historyService;

    @Autowired
    private SchemaGenerationAlgorithmFactory schemaGeneratorFactory;

    private DefaultRecordGenerationAlgorithm<BaseData> configurationProcessor;

    private UuidValidator uuidValidator;

    @Override
    @Deprecated
    public ConfigurationDto findConfigurationByAppIdAndVersion(String applicationId, int version) {
        validateSqlId(applicationId, "Application id is incorrect. Can't find configuration by application id "
                + applicationId + " and version " + version);
        return getDto(configurationDao.findConfigurationByAppIdAndVersion(applicationId, version));
    }

    @Override
    public ConfigurationDto findConfigurationByEndpointGroupIdAndVersion(String endpointGroupId, int version) {
        validateId(endpointGroupId, "Endpoint group id is incorrect. Can't find configuration by endpoint group id "
                + endpointGroupId + " and version " + version);
        return getDto(configurationDao.findConfigurationByEndpointGroupIdAndVersion(endpointGroupId, version));
    }

    @Override
    public ConfigurationDto findDefaultConfigurationBySchemaId(String schemaId) {
        validateId(schemaId, "Schema id is incorrect. Can't find default configuration by schema id "
                + schemaId);
        ConfigurationDto configuration = null;
        ConfigurationSchema configurationSchema = configurationSchemaDao.findById(schemaId);
        if (configurationSchema != null) {
            String appId = configurationSchema.getApplicationId();
            EndpointGroup endpointGroup = endpointGroupDao.findByAppIdAndWeight(appId, 0);
            if (endpointGroup != null) {
                String endpointGroupId = String.valueOf(endpointGroup.getId());
                configuration = getDto(configurationDao.findConfigurationByEndpointGroupIdAndVersion(endpointGroupId, configurationSchema.getMajorVersion()));
            } else {
                LOG.warn("Can't find default group for application [{}]", appId);
            }
        } else {
            LOG.warn("Can't find configuration schema for id [{}]", schemaId);
        }
        return configuration;
    }

    @Override
    public ConfigurationDto findConfigurationById(String id) {
        validateSqlId(id, "Configuration id is incorrect. Can't find configuration by id " + id);
        return getDto(configurationDao.findById(id));
    }

    @Override
    public Collection<StructureRecordDto<ConfigurationDto>> findAllConfigurationRecordsByEndpointGroupId(
            String endpointGroupId, boolean includeDeprecated) {
        Collection<ConfigurationDto> configurations = convertDtoList(configurationDao.findActualByEndpointGroupId(endpointGroupId));
        List<StructureRecordDto<ConfigurationDto>> records = StructureRecordDto.convertToRecords(configurations);
        if (includeDeprecated) {
            List<SchemaDto> schemas = findVacantSchemasByEndpointGroupId(endpointGroupId);
            for (SchemaDto schema : schemas) {
                ConfigurationDto deprecatedConfiguration = getDto(configurationDao.findLatestDeprecated(schema.getId(), endpointGroupId));
                if (deprecatedConfiguration != null) {
                    StructureRecordDto<ConfigurationDto> record = new StructureRecordDto<>();
                    record.setActiveStructureDto(deprecatedConfiguration);
                    records.add(record);
                }
            }
        }
        Collections.sort(records);
        return records;
    }

    @Override
    public StructureRecordDto<ConfigurationDto> findConfigurationRecordBySchemaIdAndEndpointGroupId(
            String schemaId, String endpointGroupId) {
        StructureRecordDto<ConfigurationDto> record = new StructureRecordDto<>();
        Collection<ConfigurationDto> configurations = convertDtoList(configurationDao.findActualBySchemaIdAndGroupId(schemaId, endpointGroupId));
        if (configurations != null) {
            for (ConfigurationDto configuration : configurations) {
                if (configuration.getStatus()==UpdateStatus.ACTIVE) {
                    record.setActiveStructureDto(configuration);
                } else if (configuration.getStatus()==UpdateStatus.INACTIVE) {
                    record.setInactiveStructureDto(configuration);
                }
            }
        }
        if (!record.hasActive()) {
            ConfigurationDto deprecatedConfiguration = getDto(configurationDao.findLatestDeprecated(schemaId, endpointGroupId));
            if (deprecatedConfiguration != null) {
                record.setActiveStructureDto(deprecatedConfiguration);
            }
        }
        if (record.isEmpty()) {
            LOG.debug("Can't find related Configuration record.");
            throw new IncorrectParameterException("Configuration record not found, schemaId: " + schemaId + ", endpointGroupId: " + endpointGroupId); //NOSONAR
        }
        return record;
    }

    @Override
    public List<SchemaDto> findVacantSchemasByEndpointGroupId(String endpointGroupId) {
        validateId(endpointGroupId, "Can't find vacant schemas. Invalid endpoint group id: " + endpointGroupId);
        EndpointGroup group = endpointGroupDao.findById(endpointGroupId);
        List<Configuration> configurations = configurationDao.findActualByEndpointGroupId(endpointGroupId);
        List<String> usedSchemaIds = new ArrayList<>();
        for (Configuration configuration : configurations) {
            ConfigurationSchema schema = configuration.getConfigurationSchema();
            if (schema != null) {
                usedSchemaIds.add(idToString(schema.getId()));
            }
        }
        List<ConfigurationSchema> schemas = configurationSchemaDao.findVacantSchemas(group.getApplicationId(), usedSchemaIds);
        List<SchemaDto> schemaDtoList = new ArrayList<>();
        for (ConfigurationSchema schema : schemas) {
            schemaDtoList.add(schema.toSchemaDto());
        }
        return schemaDtoList;
    }

    @Override
    public ConfigurationDto saveConfiguration(ConfigurationDto configurationDto) {
        validateConfiguration(configurationDto);
        String id = configurationDto.getId();
        ConfigurationSchemaDto configurationSchemaDto;
        Configuration oldActiveConfiguration = null;
        if (StringUtils.isNotBlank(id)) {
            ConfigurationDto oldConfiguration = findConfigurationById(configurationDto.getId());
            if (oldConfiguration != null && oldConfiguration.getStatus() != INACTIVE) {
                throw new UpdateStatusConflictException("Can't update configuration, invalid id " + id);
            }
            configurationSchemaDto = findConfSchemaById(configurationDto.getSchemaId());
            LOG.debug("Update existing configuration with id: [{}]", configurationDto.getId());
        } else {
            String schemaId = configurationDto.getSchemaId();
            String groupId = configurationDto.getEndpointGroupId();
            configurationSchemaDto = findConfSchemaById(schemaId);
            if (configurationSchemaDto != null) {
                Configuration oldInactiveConfiguration = configurationDao.findInactiveBySchemaIdAndGroupId(schemaId, groupId);
                oldActiveConfiguration = configurationDao.findLatestActiveBySchemaIdAndGroupId(schemaId, groupId);
                if (oldInactiveConfiguration != null) {
                    configurationDto.setId(idToString(oldInactiveConfiguration.getId()));
                    configurationDto.setSequenceNumber(oldInactiveConfiguration.getSequenceNumber());
                } else if (oldActiveConfiguration != null) {
                    configurationDto.setSequenceNumber(oldActiveConfiguration.getSequenceNumber());
                }
                configurationDto.setApplicationId(configurationSchemaDto.getApplicationId());
                configurationDto.setMajorVersion(configurationSchemaDto.getMajorVersion());
                configurationDto.setMinorVersion(configurationSchemaDto.getMinorVersion());
                configurationDto.setProtocolSchema(configurationSchemaDto.getProtocolSchema());
                configurationDto.setCreatedTime(System.currentTimeMillis());
            } else {
                LOG.debug("Can't find related Configuration schema.");
                throw new IncorrectParameterException("Configuration schema not found, id:" + schemaId);
            }
        }
        ConfigurationDto oldActiveConfigurationDto = oldActiveConfiguration != null ? oldActiveConfiguration.toDto() : null;
        validateUuids(configurationDto, oldActiveConfigurationDto, configurationSchemaDto);

        configurationDto.setStatus(UpdateStatus.INACTIVE);
        configurationDto.setLastModifyTime(System.currentTimeMillis());
        return getDto(configurationDao.save(new Configuration(configurationDto), Configuration.class));
    }

    private void validateUuids(ConfigurationDto currentConfiguration, ConfigurationDto previousConfiguration, ConfigurationSchemaDto configurationSchema) {
        try {
            EndpointGroup endpointGroup = endpointGroupDao.findById(currentConfiguration.getEndpointGroupId());
            GenericAvroConverter<GenericRecord> avroConverter;
            KaaData body = null;
            if (endpointGroup != null) {
                if (endpointGroup.getWeight() == 0) {
                    LOG.debug("Create default UUID validator with base schema: {}", configurationSchema.getBaseSchema());
                    BaseSchema baseSchema = new BaseSchema(configurationSchema.getBaseSchema());
                    uuidValidator = new DefaultUuidValidator(baseSchema, new BaseDataFactory());
                    avroConverter = new GenericAvroConverter<GenericRecord>(baseSchema.getRawSchema());
                } else {
                    LOG.debug("Create default UUID validator with override schema: {}", configurationSchema.getOverrideSchema());
                    OverrideSchema overrideSchema = new OverrideSchema(configurationSchema.getOverrideSchema());
                    uuidValidator = new DefaultUuidValidator(overrideSchema, new OverrideDataFactory());
                    avroConverter = new GenericAvroConverter<GenericRecord>(overrideSchema.getRawSchema());
                }
                GenericRecord previousRecord = null;
                if (previousConfiguration != null) {
                    previousRecord = avroConverter.decodeJson(previousConfiguration.getBody());
                }
                GenericRecord currentRecord = avroConverter.decodeJson(currentConfiguration.getBody());
                body = uuidValidator.validateUuidFields(currentRecord, previousRecord);
            }
            if (body != null) {
                currentConfiguration.setBody(body.getRawData());
{'fraction_non_alphanumeric': 0.08439346106960897, 'fraction_numerical': 0.0008439346106960898, 'mean_word_length': 3.697401262663339, 'pattern_counts': {'":': 0, '<': 55, '<?xml version=': 0, '>': 55, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1700680', 'n_tokens_mistral': 7916, 'n_tokens_neox': 8044, 'n_words': 1457}