/*
* Copyright 2014-2016 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.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.service.Validator.isValidSqlId;
import static org.kaaproject.kaa.server.common.dao.service.Validator.isValidSqlObject;
import static org.kaaproject.kaa.server.common.dao.service.Validator.validateSqlId;
import org.apache.commons.lang.StringUtils;
import org.kaaproject.avro.ui.shared.NamesValidator;
import org.kaaproject.kaa.common.dto.event.AefMapInfoDto;
import org.kaaproject.kaa.common.dto.event.EventClassDto;
import org.kaaproject.kaa.common.dto.event.EventClassFamilyDto;
import org.kaaproject.kaa.common.dto.event.EventClassFamilyVersionDto;
import org.kaaproject.kaa.common.dto.event.EventClassType;
import org.kaaproject.kaa.server.common.dao.EventClassService;
import org.kaaproject.kaa.server.common.dao.exception.IncorrectParameterException;
import org.kaaproject.kaa.server.common.dao.impl.CtlSchemaDao;
import org.kaaproject.kaa.server.common.dao.impl.EventClassDao;
import org.kaaproject.kaa.server.common.dao.impl.EventClassFamilyDao;
import org.kaaproject.kaa.server.common.dao.model.sql.CtlSchema;
import org.kaaproject.kaa.server.common.dao.model.sql.EventClass;
import org.kaaproject.kaa.server.common.dao.model.sql.EventClassFamily;
import org.kaaproject.kaa.server.common.dao.model.sql.EventClassFamilyVersion;
import org.kaaproject.kaa.server.common.dao.schema.EventSchemaProcessor;
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;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
@Service
@Transactional
public class EventClassServiceImpl implements EventClassService {
private static final Logger LOG = LoggerFactory.getLogger(EventClassServiceImpl.class);
@Autowired
private EventClassFamilyDao<EventClassFamily> eventClassFamilyDao;
@Autowired
private EventClassDao<EventClass> eventClassDao;
@Autowired
private EventSchemaProcessor eventSchemaProcessor;
@Autowired
private CtlSchemaDao<CtlSchema> ctlSchemaDao;
@Override
public List<EventClassFamilyDto> findEventClassFamiliesByTenantId(
String tenantId) {
List<EventClassFamilyDto> eventClassFamilies;
if (isValidSqlId(tenantId)) {
LOG.debug("Find event class families by tenant id [{}]", tenantId);
eventClassFamilies = convertDtoList(eventClassFamilyDao.findByTenantId(tenantId));
} else {
throw new IncorrectParameterException("Incorrect tenant id: " + tenantId);
}
return eventClassFamilies;
}
@Override
public EventClassFamilyDto findEventClassFamilyByTenantIdAndName(String tenantId, String name) {
if (isValidSqlId(tenantId)) {
LOG.debug("Find event class family by tenant id [{}] and name {}", tenantId, name);
return eventClassFamilyDao.findByTenantIdAndName(tenantId, name).toDto();
} else {
throw new IncorrectParameterException("Incorrect tenant id: " + tenantId);
}
}
@Override
public EventClassFamilyDto findEventClassFamilyById(String id) {
validateSqlId(id, "Event class family id is incorrect. Can't find event class family by id "
+ id);
return getDto(eventClassFamilyDao.findById(id));
}
@Override
public EventClassFamilyDto findEventClassFamilyByEcfvId(String id) {
validateSqlId(id, "Event class family version id is incorrect. Can't find event class family "
+ "by ECF version id " + id);
return getDto(eventClassFamilyDao.findByEcfvId(id));
}
@Override
public List<EventClassFamilyVersionDto> findEventClassFamilyVersionsByEcfId(String ecfId) {
validateSqlId(ecfId, "Event class family id is incorrect. Can't find event class family by id "
+ ecfId);
EventClassFamily ecf = eventClassFamilyDao.findById(ecfId);
return convertDtoList(ecf.getSchemas());
}
@Override
public EventClassFamilyDto saveEventClassFamily(
EventClassFamilyDto eventClassFamilyDto) {
EventClassFamilyDto savedEventClassFamilyDto = null;
if (isValidSqlObject(eventClassFamilyDto)) {
if (eventClassFamilyDao.validateName(eventClassFamilyDto.getTenantId(),
eventClassFamilyDto.getId(), eventClassFamilyDto.getName())) {
if (StringUtils.isBlank(eventClassFamilyDto.getId())) {
if (NamesValidator.validateNamespace(eventClassFamilyDto.getNamespace())) {
if (NamesValidator.validateClassName(eventClassFamilyDto.getClassName())) {
if (eventClassFamilyDao.validateClassName(eventClassFamilyDto.getTenantId(),
eventClassFamilyDto.getId(), eventClassFamilyDto.getClassName())) {
eventClassFamilyDto.setCreatedTime(System.currentTimeMillis());
} else {
LOG.debug("Can't save event class family. Class name should be unique within the "
+ "tenant.");
throw new IncorrectParameterException("Incorrect event class family. Class name "
+ "should be unique within the tenant.");
}
} else {
LOG.debug("Can't save event class family. Class name [{}] is not valid.",
eventClassFamilyDto.getClassName());
throw new IncorrectParameterException("Incorrect event class family. Class name "
+ "is not valid. '"
+ eventClassFamilyDto.getClassName()
+ "' is not a valid identifier.");
}
} else {
LOG.debug("Can't save event class family. Namespace [{}] is not valid.",
eventClassFamilyDto.getNamespace());
throw new IncorrectParameterException("Incorrect event class family. Namespace is "
+ "not valid. '"
+ eventClassFamilyDto.getNamespace()
+ "' is not a valid identifier.");
}
}
savedEventClassFamilyDto = getDto(eventClassFamilyDao.save(new EventClassFamily(
eventClassFamilyDto)));
} else {
LOG.debug("Can't save event class family. Name should be unique within the tenant.");
throw new IncorrectParameterException("Incorrect event class family. Name should be unique"
+ " within the tenant.");
}
}
return savedEventClassFamilyDto;
}
@Override
public void addEventClassFamilyVersion(String eventClassFamilyId,
EventClassFamilyVersionDto eventClassFamilyVersion,
String createdUsername) {
EventClassFamilyDto eventClassFamily = findEventClassFamilyById(eventClassFamilyId);
if (eventClassFamily != null) {
List<EventClassDto> records = eventClassFamilyVersion.getRecords();
List<String> fqns = new ArrayList<>(records.size());
for (EventClassDto eventClass : records) {
fqns.add(eventClass.getFqn());
}
if (validateEventClassFamilyFqns(eventClassFamily.getId(), fqns)) {
List<EventClassFamilyVersionDto> schemasDto = findEventClassFamilyVersionsByEcfId(
eventClassFamilyId);
int version = 1;
if (schemasDto != null && !schemasDto.isEmpty()) {
Collections.sort(schemasDto, new Comparator<EventClassFamilyVersionDto>() {
@Override
public int compare(EventClassFamilyVersionDto o1,
EventClassFamilyVersionDto o2) {
return o1.getVersion() - o2.getVersion();
}
});
version = schemasDto.get(schemasDto.size() - 1).getVersion() + 1;
} else {
schemasDto = new ArrayList<>();
}
eventClassFamilyVersion.setVersion(version);
eventClassFamilyVersion.setCreatedTime(System.currentTimeMillis());
eventClassFamilyVersion.setCreatedUsername(createdUsername);
schemasDto.add(eventClassFamilyVersion);
List<EventClassFamilyVersion> schemas = new ArrayList<>();
for (EventClassDto eventClass : records) {
setEventClassProperties(eventClassFamily, eventClass);
}
schemasDto.forEach(s -> schemas.add(new EventClassFamilyVersion(s)));
setBackreference(schemas);
EventClassFamily ecf = new EventClassFamily(eventClassFamily);
ecf.setSchemas(schemas);
eventClassFamilyDao.save(ecf);
} else {
LOG.debug("Can't process event class family schema.");
throw new IncorrectParameterException("Incorrect event class family schema. FQNs should be"
+ " unique within the tenant.");
}
} else {
LOG.debug("Can't find related event class family.");
throw new IncorrectParameterException("Event class family not found, id:"
+ eventClassFamilyId);
}
}
private void setEventClassProperties(EventClassFamilyDto eventClassFamilyDto,
EventClassDto eventClass) {
eventClass.setTenantId(eventClassFamilyDto.getTenantId());
eventClass.setVersion(ctlSchemaDao.findById(eventClass.getCtlSchemaId()).getVersion());
}
private void setBackreference(List<EventClassFamilyVersion> ecfvList) {
ecfvList.forEach(ecfv -> ecfv.getRecords().forEach(ec -> ec.setEcfv(ecfv)));
}
@Override
public boolean validateEventClassFamilyFqns(String eventClassFamilyId, List<String> fqns) {
Set<String> storedFqns = getFqnSetForEcf(eventClassFamilyId);
for (String fqn : fqns) {
if (storedFqns.contains(fqn)) {
return false;
}
}
return true;
}
@Override
public List<EventClassDto> findEventClassesByFamilyIdVersionAndType(String ecfId, int version,
EventClassType type) {
List<EventClassDto> eventClasses = new ArrayList<>();
if (isValidSqlId(ecfId)) {
LOG.debug("Find event classes by family id [{}] version [{}] and type [{}]",
ecfId, version, type);
EventClassFamily ecf = eventClassFamilyDao.findById(ecfId);
Optional<EventClassFamilyVersion> ecfv = ecf.getSchemas().stream()
.filter(s -> s.getVersion() == version).findFirst();
if (type == null) {
ecfv.ifPresent(
e -> eventClasses.addAll(convertDtoList(e.getRecords())));
} else {
ecfv.ifPresent(
e -> eventClasses.addAll(convertDtoList(e.getRecords().stream()
.filter(ec -> ec.getType() == type)
.collect(Collectors.toList()))));
}
} else {
throw new IncorrectParameterException("Incorrect event class family id: " + ecfId);
}
return eventClasses;
}
@Override
public List<EventClassDto> findEventClassByTenantIdAndFqn(String tenantId, String fqn) {
if (isValidSqlId(tenantId)) {
LOG.debug("Find event class family by tenant id [{}] and fqn {}", tenantId, fqn);
return convertDtoList(eventClassDao.findByTenantIdAndFqn(tenantId, fqn));
} else {
throw new IncorrectParameterException("Incorrect tenant id: " + tenantId);
}
}
@Override
public EventClassDto findEventClassByTenantIdAndFqnAndVersion(String tenantId, String fqn,
int version) {
if (isValidSqlId(tenantId)) {
LOG.debug("Find event class family by tenant id [{}] and fqn {}", tenantId, fqn);
return getDto(eventClassDao.findByTenantIdAndFqnAndVersion(tenantId, fqn, version));
} else {
throw new IncorrectParameterException("Incorrect tenant id: " + tenantId);
}
}
@Override
public EventClassDto findEventClassById(String eventClassId) {
if (isValidSqlId(eventClassId)) {
LOG.debug("Find event class by id [{}] ", eventClassId);
return getDto(eventClassDao.findById(eventClassId));
} else {
throw new IncorrectParameterException("Incorrect event class id: " + eventClassId);
}
}
@Override
public boolean isValidEcfListInSdkProfile(List<AefMapInfoDto> aefList) {
Set<EventClass> ecList = new HashSet<>();
for (AefMapInfoDto aef : aefList) {
EventClassFamily ecf = eventClassFamilyDao.findById(aef.getEcfId());
Optional<EventClassFamilyVersion> optEcfv = ecf.getSchemas().stream()
.filter(ecfv -> ecfv.getVersion() == aef.getVersion())
.findFirst();
if (optEcfv.isPresent()) {
for (EventClass ec : optEcfv.get().getRecords()) {
if (!ecList.add(ec)) {
return false;
}
}
}
}
return true;
}
@Override
public Set<String> getFqnSetForEcf(String ecfId) {
if (isValidSqlId(ecfId)) {
LOG.debug("Get fqn list for event class family by id [{}] ", ecfId);
Set<String> storedFqns = new HashSet<>();
EventClassFamily ecf = eventClassFamilyDao.findById(ecfId);
ecf.getSchemas().forEach(ecfv -> ecfv.getRecords()
.forEach(ec -> storedFqns.add(ec.getFqn())));
return storedFqns;
} else {
throw new IncorrectParameterException("Incorrect event class family id: " + ecfId);
}
}
}