/*
* 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.apache.commons.lang.StringUtils.isBlank;
import static org.kaaproject.kaa.common.dto.ctl.CTLSchemaScopeDto.SYSTEM;
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.getStringFromFile;
import static org.kaaproject.kaa.server.common.dao.service.Validator.validateObject;
import static org.kaaproject.kaa.server.common.dao.service.Validator.validateSqlId;
import static org.kaaproject.kaa.server.common.dao.service.Validator.validateString;
import org.apache.avro.Schema;
import org.apache.avro.generic.GenericRecord;
import org.apache.commons.lang.Validate;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.node.ArrayNode;
import org.codehaus.jackson.node.ObjectNode;
import org.hibernate.LockMode;
import org.hibernate.LockOptions;
import org.kaaproject.kaa.common.avro.GenericAvroConverter;
import org.kaaproject.kaa.common.dto.ctl.CTLSchemaDto;
import org.kaaproject.kaa.common.dto.ctl.CTLSchemaScopeDto;
import org.kaaproject.kaa.common.dto.ctl.CtlSchemaMetaInfoDto;
import org.kaaproject.kaa.common.dto.file.FileData;
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.configuration.RawData;
import org.kaaproject.kaa.server.common.core.configuration.RawDataFactory;
import org.kaaproject.kaa.server.common.core.schema.RawSchema;
import org.kaaproject.kaa.server.common.dao.CtlService;
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.impl.CtlSchemaDao;
import org.kaaproject.kaa.server.common.dao.impl.CtlSchemaMetaInfoDao;
import org.kaaproject.kaa.server.common.dao.impl.DaoUtil;
import org.kaaproject.kaa.server.common.dao.model.sql.CtlSchema;
import org.kaaproject.kaa.server.common.dao.model.sql.CtlSchemaMetaInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
@Service
@Transactional
public class CtlServiceImpl implements CtlService {
private static final String JSON = "application/json";
private static final String ZIP = "application/zip";
private static final String VERSION = "version";
private static final String FQN = "fqn";
private static final Logger LOG = LoggerFactory.getLogger(CtlServiceImpl.class);
private static final String DEPENDENCIES = "dependencies";
private static final String DEFAULT_SYSTEM_EMPTY_SCHEMA_FILE = "/default_system_empty_schema"
+ ".avsc";
/**
* A template for naming exported CTL schemas.
*
* @see #shallowExport(CTLSchemaDto)
* @see #flatExport(CTLSchemaDto)
*/
private static final String CTL_EXPORT_TEMPLATE = "{0}.v{1}.avsc";
/**
* The name of the archive to put exported CTL schemas into.
*
* @see #deepExport(CTLSchemaDto)
*/
private static final String CTL_EXPORT_ZIP_NAME = "schemas.zip";
/**
* Used to format CTL schema body.
*/
private static final ObjectMapper FORMATTER = new ObjectMapper();
private final LockOptions lockOptions = new LockOptions(LockMode.PESSIMISTIC_WRITE);
@Autowired
private CtlSchemaDao<CtlSchema> ctlSchemaDao;
@Autowired
private CtlSchemaMetaInfoDao<CtlSchemaMetaInfo> ctlSchemaMetaInfoDao;
@Override
public CTLSchemaDto getOrCreateEmptySystemSchema(String createdUsername) {
CTLSchemaDto ctlSchema = findCtlSchemaByFqnAndVerAndTenantIdAndApplicationId(
DEFAULT_SYSTEM_EMPTY_SCHEMA_FQN, DEFAULT_SYSTEM_EMPTY_SCHEMA_VERSION, null, null);
if (ctlSchema == null) {
ctlSchema = new CTLSchemaDto();
CtlSchemaMetaInfoDto metaInfo = new CtlSchemaMetaInfoDto(DEFAULT_SYSTEM_EMPTY_SCHEMA_FQN);
ctlSchema.setMetaInfo(metaInfo);
ctlSchema.setVersion(DEFAULT_SYSTEM_EMPTY_SCHEMA_VERSION);
ctlSchema.setCreatedUsername(createdUsername);
ctlSchema.setDependencySet(new HashSet<CTLSchemaDto>());
String body = getStringFromFile(DEFAULT_SYSTEM_EMPTY_SCHEMA_FILE, CtlServiceImpl.class);
if (!body.isEmpty()) {
ctlSchema.setBody(body);
} else {
throw new RuntimeException("Can't read default system schema."); // NOSONAR
}
ctlSchema = saveCtlSchema(ctlSchema);
}
return ctlSchema;
}
@Override
public CTLSchemaDto saveCtlSchema(CTLSchemaDto unSavedSchema) {
validateCtlSchemaObject(unSavedSchema);
if (unSavedSchema.getDefaultRecord() == null) {
unSavedSchema = generateDefaultRecord(unSavedSchema);
} else {
validateDefaultRecord(unSavedSchema);
}
if (isBlank(unSavedSchema.getId())) {
CtlSchemaMetaInfoDto metaInfo = unSavedSchema.getMetaInfo();
CTLSchemaDto dto;
synchronized (this) {
List<CtlSchemaMetaInfo> existingFqns = ctlSchemaMetaInfoDao.findExistingFqns(metaInfo
.getFqn(), metaInfo.getTenantId(), metaInfo.getApplicationId());
if (existingFqns != null && !existingFqns.isEmpty()) {
throw new DatabaseProcessingException("Can't save common type due to an FQN conflict.");
}
metaInfo.setId(null);
CtlSchemaMetaInfo uniqueMetaInfo = ctlSchemaMetaInfoDao.save(
new CtlSchemaMetaInfo(metaInfo));
ctlSchemaMetaInfoDao.lockRequest(lockOptions).setScope(true).lock(uniqueMetaInfo);
CtlSchema ctlSchema = new CtlSchema(unSavedSchema);
ctlSchema.setMetaInfo(uniqueMetaInfo);
ctlSchema.setCreatedTime(System.currentTimeMillis());
ctlSchemaMetaInfoDao.refresh(uniqueMetaInfo);
try {
dto = getDto(ctlSchemaDao.save(ctlSchema, true));
} catch (DataIntegrityViolationException ex) {
throw new DatabaseProcessingException("Can't save common type: such FQN and version "
+ "already exist.");
} catch (Exception ex) {
throw new DatabaseProcessingException(ex);
}
}
return dto;
} else {
return updateCtlSchema(unSavedSchema);
}
}
private void validateDefaultRecord(CTLSchemaDto unSavedSchema) {
try {
String schemaBody = flatExportAsString(unSavedSchema);
GenericAvroConverter<GenericRecord> converter = new GenericAvroConverter<>(schemaBody);
converter.decodeJson(unSavedSchema.getDefaultRecord());
} catch (IOException | RuntimeException ex) {
LOG.error("Invalid default record for CTL schema with body: {}", unSavedSchema.getBody(), ex);
throw new RuntimeException("An unexpected exception occured: "
+ ex.toString());
}
}
private CTLSchemaDto generateDefaultRecord(CTLSchemaDto unSavedSchema) {
try {
String schemaBody = flatExportAsString(unSavedSchema);
LOG.debug("Generating default record for flat schema: {}", schemaBody);
RawSchema dataSchema = new RawSchema(schemaBody);
DefaultRecordGenerationAlgorithm<RawData> dataProcessor = new
DefaultRecordGenerationAlgorithmImpl<RawSchema, RawData>(
dataSchema, new RawDataFactory());
unSavedSchema.setDefaultRecord(dataProcessor.getRootData().getRawData());
return unSavedSchema;
} catch (StackOverflowError ex) {
LOG.error("Failed to generate default record. An endless recursion is detected. CTL schema "
+ "body: {}", unSavedSchema.getBody(), ex);
throw new RuntimeException("Unable to generate default record. An endless recursion is "
+ "detected! "
+ "Please check non-optional references to nested types.");
} catch (ConfigurationGenerationException | IOException | RuntimeException ex) {
LOG.error("Failed to generate default record for CTL schema with body: {}", unSavedSchema
.getBody(), ex);
throw new RuntimeException("An unexpected exception occured: "
+ ex.toString());
}
}
@Override
public CTLSchemaDto updateCtlSchema(CTLSchemaDto ctlSchema) {
validateCtlSchemaObject(ctlSchema);
LOG.debug("Update ctl schema with id [{}]", ctlSchema.getId());
CtlSchema schema = ctlSchemaDao.findById(ctlSchema.getId());
if (schema != null) {
synchronized (this) {
if (ctlSchema.getVersion()
!= schema.getVersion()) {
throw new DatabaseProcessingException("Can't change version of existing common type "
+ "version.");
}
CtlSchemaMetaInfo metaInfo = schema.getMetaInfo();
if (!ctlSchema.getMetaInfo().equals(metaInfo.toDto())) {
throw new DatabaseProcessingException("Can't update scope of existing common type "
+ "version within update procedure.");
}
ctlSchemaMetaInfoDao.lockRequest(lockOptions).setScope(true).lock(metaInfo);
schema.update(ctlSchema);
return DaoUtil.getDto(ctlSchemaDao.save(schema, true));
}
} else {
throw new DatabaseProcessingException("Can't find common type version by id.");
}
}
@Override
public CtlSchemaMetaInfoDto updateCtlSchemaMetaInfoScope(CtlSchemaMetaInfoDto ctlSchemaMetaInfo) {
validateObject(ctlSchemaMetaInfo, "Incorrect ctl schema meta info object");
LOG.debug("Update ctl schema meta info scope with id [{}]", ctlSchemaMetaInfo.getId());
CtlSchemaMetaInfo schemaMetaInfo = ctlSchemaMetaInfoDao.findById(ctlSchemaMetaInfo.getId());
if (schemaMetaInfo
!= null) {
synchronized (this) {
ctlSchemaMetaInfoDao.lockRequest(lockOptions).setScope(true).lock(schemaMetaInfo);
if (checkScopeUpdate(ctlSchemaMetaInfo, schemaMetaInfo.toDto())) {
List<CtlSchemaMetaInfo> others = ctlSchemaMetaInfoDao.findOthersByFqnAndTenantId(
ctlSchemaMetaInfo.getFqn(), ctlSchemaMetaInfo.getTenantId(), ctlSchemaMetaInfo
.getId());
if (others != null && !others.isEmpty()) {
throw new DatabaseProcessingException("Can't update scope of the common type due to "
+ "an FQN conflict.");
}
schemaMetaInfo = ctlSchemaMetaInfoDao.updateScope(
new CtlSchemaMetaInfo(ctlSchemaMetaInfo));
}
return DaoUtil.getDto(schemaMetaInfo);
}
} else {
throw new DatabaseProcessingException("Can't find common type by id.");
}
}
@Override
public List<CtlSchemaMetaInfoDto> findSiblingsByFqnTenantIdAndApplicationId(String fqn, String
tenantId, String applicationId) {
if (isBlank(fqn)) {
throw new IncorrectParameterException("Incorrect parameters for ctl schema request.");
}
LOG.debug("Find sibling ctl schemas by fqn {}, tenant id {} and application id {}", fqn,
tenantId, applicationId);
return convertDtoList(ctlSchemaMetaInfoDao.findSiblingsByFqnTenantIdAndApplicationId(fqn,
tenantId, applicationId));
}
private boolean checkScopeUpdate(CtlSchemaMetaInfoDto newSchemaMetaInfo, CtlSchemaMetaInfoDto
prevSchemaMetaInfo) {
if (!newSchemaMetaInfo.equals(prevSchemaMetaInfo)) {
if (isBlank(newSchemaMetaInfo.getFqn())) {
throw new DatabaseProcessingException("FQN can't be empty.");
}
if (!newSchemaMetaInfo.getFqn().equals(prevSchemaMetaInfo.getFqn())) {
throw new DatabaseProcessingException("Can't change FQN of the existing common type.");
}
CTLSchemaScopeDto newScope = newSchemaMetaInfo.getScope();
CTLSchemaScopeDto prevScope = prevSchemaMetaInfo.getScope();
if (newScope
!= prevScope) {
if (SYSTEM.equals(newScope)) {
throw new DatabaseProcessingException("Can't update scope to system.");
}
if (newScope.getLevel()
> prevScope.getLevel()) {
throw new DatabaseProcessingException("Can't update scope to lower.");
}
}
if (!isBlank(newSchemaMetaInfo.getTenantId())
&& !newSchemaMetaInfo.getTenantId().equals(prevSchemaMetaInfo.getTenantId())) {
throw new DatabaseProcessingException("Can't change tenant reference for the existing "
+ "common type.");
}
if (!isBlank(newSchemaMetaInfo.getApplicationId())
&& !newSchemaMetaInfo.getApplicationId().equals(prevSchemaMetaInfo.getApplicationId())) {
throw new DatabaseProcessingException("Can't change application reference for the "
+ "existing common type.");
}
return true;
}
return false;
}
@Override
public void removeCtlSchemaByFqnAndVerAndTenantIdAndApplicationId(String fqn, Integer version,
String tenantId, String
applicationId) {
if (isBlank(fqn) || version == null) {
throw new IncorrectParameterException("Incorrect parameters for ctl schema request.");
}
LOG.debug("Remove ctl schema by fqn {} version {}, tenant id {} and application id {}", fqn,
version, tenantId, applicationId);
CtlSchema ctlSchema = ctlSchemaDao.findByFqnAndVerAndTenantIdAndApplicationId(fqn, version,
tenantId, applicationId);
if (ctlSchema
!= null) {
List<CtlSchema> dependsList = ctlSchemaDao.findDependentSchemas(ctlSchema.getStringId());
if (dependsList.isEmpty()) {
synchronized (this) {
CtlSchemaMetaInfo metaInfo = ctlSchema.getMetaInfo();
ctlSchemaMetaInfoDao.lockRequest(lockOptions).setScope(true).lock(metaInfo);
try {
ctlSchemaDao.removeById(ctlSchema.getStringId());
List<CtlSchema> schemas = ctlSchemaDao.findAllByMetaInfoId(metaInfo.getStringId());
if (schemas == null || schemas.isEmpty()) {
ctlSchemaMetaInfoDao.removeById(metaInfo.getStringId());
}
} catch (DataIntegrityViolationException ex) {
throw new DatabaseProcessingException("Common type version can't be deleted because "
+ "it is referenced by system modules.");
}
}
} else {
throw new DatabaseProcessingException("Common type version can't be deleted because it is"
+ " referenced by other types.");
}
}
}
@Override
public CTLSchemaDto findCtlSchemaById(String schemaId) {
validateSqlId(schemaId, "Incorrect schema id for ctl request "
+ schemaId);
LOG.debug("Find ctl schema by id [{}]", schemaId);
return DaoUtil.getDto(ctlSchemaDao.findById(schemaId));
}
@Override
public CTLSchemaDto findCtlSchemaByFqnAndVerAndTenantIdAndApplicationId(String fqn, Integer
version, String tenantId, String applicationId) {
if (isBlank(fqn) || version == null) {
throw new IncorrectParameterException("Incorrect parameters for ctl schema request.");
}
LOG.debug("Find ctl schema by fqn {} version {}, tenant id {} and application id {}", fqn,
version, tenantId, applicationId);
return DaoUtil.getDto(ctlSchemaDao.findByFqnAndVerAndTenantIdAndApplicationId(fqn, version,
tenantId, applicationId));
}
@Override
public CTLSchemaDto findByMetaInfoIdAndVer(String metaInfoId, Integer version) {
if (isBlank(metaInfoId) || version == null) {
throw new IncorrectParameterException("Incorrect parameters for ctl schema request.");
}
LOG.debug("Find ctl schema by meta info id {} and version {}", metaInfoId, version);
return DaoUtil.getDto(ctlSchemaDao.findByMetaInfoIdAndVer(metaInfoId, version));
}
@Override
public CTLSchemaDto findAnyCtlSchemaByFqnAndVerAndTenantIdAndApplicationId(String fqn,
Integer version,
String tenantId,
String applicationId) {
if (isBlank(fqn) || version == null) {
throw new IncorrectParameterException("Incorrect parameters for ctl schema request.");
}
LOG.debug("Find any ctl schema by fqn {} version {}, tenant id {} and application id {}",
fqn, version, tenantId, applicationId);
return DaoUtil.getDto(ctlSchemaDao.findAnyByFqnAndVerAndTenantIdAndApplicationId(fqn,
version, tenantId, applicationId));
}
@Override
public List<CTLSchemaDto> findSystemCtlSchemas() {
LOG.debug("Find system ctl schemas");
return convertDtoList(ctlSchemaDao.findSystemSchemas());
}
@Override
public List<CtlSchemaMetaInfoDto> findSystemCtlSchemasMetaInfo() {
LOG.debug("Find system ctl schemas");
return getMetaInfoFromCtlSchema(ctlSchemaDao.findSystemSchemas());
}
@Override
public List<CtlSchemaMetaInfoDto> findAvailableCtlSchemasMetaInfoForTenant(String tenantId) {
LOG.debug("Find system and tenant scopes ctl schemas by tenant id {}", tenantId);
return getMetaInfoFromCtlSchema(ctlSchemaDao.findAvailableSchemasForTenant(tenantId));
}
@Override
public List<CtlSchemaMetaInfoDto> findAvailableCtlSchemasMetaInfoForApplication(
String tenantId, String applicationId) {
LOG.debug("Find system, tenant and application scopes ctl schemas by application id {}",
applicationId);
return getMetaInfoFromCtlSchema(ctlSchemaDao.findAvailableSchemasForApplication(tenantId,
applicationId));
}
@Override
public CTLSchemaDto findLatestCtlSchemaByFqnAndTenantIdAndApplicationId(String fqn, String
tenantId, String applicationId) {
validateString(fqn, "Incorrect fqn for ctl schema request.");
LOG.debug("Find latest ctl schema by fqn {}, tenantId {} and applicationId {}", fqn,
tenantId, applicationId);
return DaoUtil.getDto(ctlSchemaDao.findLatestByFqnAndTenantIdAndApplicationId(fqn, tenantId,
applicationId));
}
@Override
public CTLSchemaDto findLatestByMetaInfoId(String metaInfoId) {
validateString(metaInfoId, "Incorrect meta info id for ctl schema request.");
LOG.debug("Find latest ctl schema by meta info id {}", metaInfoId);
return DaoUtil.getDto(ctlSchemaDao.findLatestByMetaInfoId(metaInfoId));
}
@Override
public List<CTLSchemaDto> findAllCtlSchemasByFqnAndTenantIdAndApplicationId(String fqn, String
tenantId, String applicationId) {
validateString(fqn, "Incorrect fqn for ctl schema request.");
LOG.debug("Find all ctl schemas by fqn {}, tenantId {} and applicationId {}", fqn, tenantId,
applicationId);
return convertDtoList(ctlSchemaDao.findAllByFqnAndTenantIdAndApplicationId(fqn, tenantId,
applicationId));
}
@Override
public List<CTLSchemaDto> findCtlSchemas() {
LOG.debug("Find all ctl schemas");
return convertDtoList(ctlSchemaDao.find());
}
@Override
public List<CTLSchemaDto> findCtlSchemaDependents(String schemaId) {
validateSqlId(schemaId, "Incorrect schema id for ctl schema request.");
LOG.debug("Find dependents schemas for schema with id [{}]", schemaId);
List<CTLSchemaDto> list = Collections.emptyList();
CtlSchema schemaDto = ctlSchemaDao.findById(schemaId);
if (schemaDto
!= null) {
list = convertDtoList(ctlSchemaDao.findDependentSchemas(schemaDto.getStringId()));
}
return list;
}
@Override
public List<CTLSchemaDto> findCtlSchemaDependents(String fqn, Integer version, String tenantId,
String applicationId) {
if (isBlank(fqn) || version == null) {
throw new IncorrectParameterException("Incorrect parameters for ctl schema request.");
}
LOG.debug("Find dependents schemas for schema with fqn {} version {}, tenantId {} and "
+ "applicationId ()", fqn, version, tenantId, applicationId);
List<CTLSchemaDto> schemas = Collections.emptyList();
CtlSchema schema = ctlSchemaDao.findByFqnAndVerAndTenantIdAndApplicationId(fqn, version,
tenantId, applicationId);
if (schema
!= null) {
schemas = convertDtoList(ctlSchemaDao.findDependentSchemas(schema.getStringId()));
}
return schemas;
}
private void validateCtlSchemaObject(CTLSchemaDto ctlSchema) {
validateObject(ctlSchema, "Incorrect ctl schema object");
CtlSchemaMetaInfoDto metaInfo = ctlSchema.getMetaInfo();
if (metaInfo == null) {
throw new RuntimeException("Incorrect ctl schema object. CtlSchemaMetaInfoDto is mandatory "
+ "information.");
} else {
if (isBlank(metaInfo.getFqn()) || ctlSchema.getVersion() == null) {
throw new RuntimeException("Incorrect CTL schema, please add correct version and fqn.");
}
}
}
private List<CtlSchemaMetaInfoDto> getMetaInfoFromCtlSchema(List<CtlSchema> schemas) {
Map<String, CtlSchemaMetaInfoDto> metaInfoMap = new HashMap<>();
if (!schemas.isEmpty()) {
for (CtlSchema schema : schemas) {
String metaInfoId = schema.getMetaInfo().getStringId();
CtlSchemaMetaInfoDto metaInfoDto = metaInfoMap.get(metaInfoId);
if (metaInfoDto == null) {
metaInfoDto = getDto(schema.getMetaInfo());
metaInfoMap.put(metaInfoId, metaInfoDto);
}
List<Integer> versions = metaInfoDto.getVersions();
if (versions == null) {
versions = new ArrayList<>();
metaInfoDto.setVersions(versions);
}
versions.add(schema.getVersion());
}
}
List<CtlSchemaMetaInfoDto> result = new ArrayList<>(metaInfoMap.values());
return result;
}
@Override
public FileData shallowExport(CTLSchemaDto schema) {
try {
FileData result = new FileData();
result.setContentType(JSON);
result.setFileName(MessageFormat.format(CTL_EXPORT_TEMPLATE, schema.getMetaInfo().getFqn(),
schema.getVersion()));
// Format schema body
Object json = FORMATTER.readValue(schema.getBody(), Object.class);
result.setFileData(FORMATTER.writerWithDefaultPrettyPrinter().writeValueAsString(json)
.getBytes());
return result;
} catch (Exception cause) {
throw new RuntimeException("An unexpected exception occured: " + cause.toString());
}
}
@Override
public Schema flatExportAsSchema(CTLSchemaDto schema) {
try {
return this.parseDependencies(schema, new Schema.Parser());
} catch (Exception cause) {
LOG.error("Unable to export CTL schema as flat: {}", schema, cause);
throw new RuntimeException("An unexpected exception occured: " + cause.toString());
}
}
@Override
public String flatExportAsString(CTLSchemaDto schema) {
return flatExportAsSchema(schema).toString();
}
@Override
public FileData flatExport(CTLSchemaDto schema) {
try {
FileData result = new FileData();
result.setContentType(JSON);
result.setFileName(MessageFormat.format(CTL_EXPORT_TEMPLATE, schema.getMetaInfo().getFqn(),
schema.getVersion()));
// Get schema body
String body = flatExportAsString(schema);
// Format schema body
Object json = FORMATTER.readValue(body, Object.class);
result.setFileData(FORMATTER.writerWithDefaultPrettyPrinter().writeValueAsString(json)
.getBytes());
return result;
} catch (Exception cause) {
throw new RuntimeException("An unexpected exception occured: "
+ cause.toString());
}
}
@Override
public FileData deepExport(CTLSchemaDto schema) {
try {
ByteArrayOutputStream content = new ByteArrayOutputStream();
ZipOutputStream out = new ZipOutputStream(content);
List<FileData> files = this.recursiveShallowExport(new ArrayList<FileData>(), schema);
for (FileData file : files) {
out.putNextEntry(new ZipEntry(file.getFileName()));
out.write(file.getFileData());
out.closeEntry();
}
out.close();
FileData result = new FileData();
result.setContentType(ZIP);
result.setFileName(CTL_EXPORT_ZIP_NAME);
result.setFileData(content.toByteArray());
return result;
} catch (Exception cause) {
throw new RuntimeException("An unexpected exception occured: " + cause.toString());
}
}
private Schema parseDependencies(CTLSchemaDto schema, final Schema.Parser parser) throws
Exception {
if (schema.getDependencySet() != null) {
for (CTLSchemaDto dependency : schema.getDependencySet()) {
this.parseDependencies(dependency, parser);
}
}
ObjectNode object = new ObjectMapper().readValue(schema.getBody(), ObjectNode.class);
object.remove(DEPENDENCIES);
String body = new ObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(object);
return parser.parse(body);
}
private List<FileData> recursiveShallowExport(List<FileData> files, CTLSchemaDto parent) throws
Exception {
files.add(this.shallowExport(parent));
ObjectNode object = new ObjectMapper().readValue(parent.getBody(), ObjectNode.class);
ArrayNode dependencies = (ArrayNode) object.get(DEPENDENCIES);
if (dependencies != null) {
for (JsonNode node : dependencies) {
ObjectNode dependency = (ObjectNode) node;
String fqn = dependency.get(FQN).getTextValue();
Integer version = dependency.get(VERSION).getIntValue();
CTLSchemaDto child = this.findAnyCtlSchemaByFqnAndVerAndTenantIdAndApplicationId(
fqn, version, parent.getMetaInfo().getTenantId(),
parent.getMetaInfo().getApplicationId());
Validate.notNull(child, MessageFormat.format("The dependency [{0}] was not found!", fqn));
this.recursiveShallowExport(files, child);
}
}
return files;
}
}