Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.eurodyn.qlack2.be.rules.impl.DataModelsServiceImpl Maven / Gradle / Ivy
package com.eurodyn.qlack2.be.rules.impl;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.persistence.EntityManager;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import org.joda.time.DateTime;
import com.eurodyn.qlack2.be.rules.api.DataModelsService;
import com.eurodyn.qlack2.be.rules.api.dto.DataModelDTO;
import com.eurodyn.qlack2.be.rules.api.dto.DataModelFieldDTO;
import com.eurodyn.qlack2.be.rules.api.dto.DataModelFieldType;
import com.eurodyn.qlack2.be.rules.api.dto.DataModelFieldTypeDTO;
import com.eurodyn.qlack2.be.rules.api.dto.DataModelVersionDTO;
import com.eurodyn.qlack2.be.rules.api.dto.VersionState;
import com.eurodyn.qlack2.be.rules.api.dto.can.CanDeleteDataModelResult;
import com.eurodyn.qlack2.be.rules.api.dto.can.CanDeleteDataModelVersionResult;
import com.eurodyn.qlack2.be.rules.api.dto.can.CanDisableTestingDataModelResult;
import com.eurodyn.qlack2.be.rules.api.dto.can.CanEnableTestingDataModelResult;
import com.eurodyn.qlack2.be.rules.api.dto.can.CanFinalizeDataModelResult;
import com.eurodyn.qlack2.be.rules.api.dto.can.CanUpdateEnabledForTestingDataModelResult;
import com.eurodyn.qlack2.be.rules.api.dto.xml.XmlDataModelVersionDTO;
import com.eurodyn.qlack2.be.rules.api.exception.QImportExportException;
import com.eurodyn.qlack2.be.rules.api.exception.QInheritanceCycleException;
import com.eurodyn.qlack2.be.rules.api.exception.QInvalidActionException;
import com.eurodyn.qlack2.be.rules.api.exception.QNonUniqueFieldNamesException;
import com.eurodyn.qlack2.be.rules.api.request.EmptyRequest;
import com.eurodyn.qlack2.be.rules.api.request.datamodel.CreateDataModelRequest;
import com.eurodyn.qlack2.be.rules.api.request.datamodel.DeleteDataModelRequest;
import com.eurodyn.qlack2.be.rules.api.request.datamodel.GetDataModelByProjectAndNameRequest;
import com.eurodyn.qlack2.be.rules.api.request.datamodel.GetDataModelRequest;
import com.eurodyn.qlack2.be.rules.api.request.datamodel.UpdateDataModelRequest;
import com.eurodyn.qlack2.be.rules.api.request.datamodel.version.CreateDataModelVersionRequest;
import com.eurodyn.qlack2.be.rules.api.request.datamodel.version.DeleteDataModelVersionRequest;
import com.eurodyn.qlack2.be.rules.api.request.datamodel.version.EnableTestingDataModelVersionRequest;
import com.eurodyn.qlack2.be.rules.api.request.datamodel.version.ExportDataModelVersionRequest;
import com.eurodyn.qlack2.be.rules.api.request.datamodel.version.FinalizeDataModelVersionRequest;
import com.eurodyn.qlack2.be.rules.api.request.datamodel.version.GetDataModelIdByVersionIdRequest;
import com.eurodyn.qlack2.be.rules.api.request.datamodel.version.GetDataModelVersionByDataModelAndNameRequest;
import com.eurodyn.qlack2.be.rules.api.request.datamodel.version.GetDataModelVersionRequest;
import com.eurodyn.qlack2.be.rules.api.request.datamodel.version.GetDataModelVersionsRequest;
import com.eurodyn.qlack2.be.rules.api.request.datamodel.version.ImportDataModelVersionRequest;
import com.eurodyn.qlack2.be.rules.api.request.datamodel.version.LockDataModelVersionRequest;
import com.eurodyn.qlack2.be.rules.api.request.datamodel.version.UnlockDataModelVersionRequest;
import com.eurodyn.qlack2.be.rules.api.request.datamodel.version.UpdateDataModelFieldRequest;
import com.eurodyn.qlack2.be.rules.api.request.datamodel.version.UpdateDataModelVersionRequest;
import com.eurodyn.qlack2.be.rules.api.request.project.GetProjectDataModelsRequest;
import com.eurodyn.qlack2.be.rules.api.util.Constants;
import com.eurodyn.qlack2.be.rules.impl.dto.AuditDataModelDTO;
import com.eurodyn.qlack2.be.rules.impl.dto.AuditDataModelVersionDTO;
import com.eurodyn.qlack2.be.rules.impl.model.Category;
import com.eurodyn.qlack2.be.rules.impl.model.DataModel;
import com.eurodyn.qlack2.be.rules.impl.model.DataModelField;
import com.eurodyn.qlack2.be.rules.impl.model.DataModelVersion;
import com.eurodyn.qlack2.be.rules.impl.model.WorkingSetVersion;
import com.eurodyn.qlack2.be.rules.impl.util.AuditConstants.EVENT;
import com.eurodyn.qlack2.be.rules.impl.util.AuditConstants.GROUP;
import com.eurodyn.qlack2.be.rules.impl.util.AuditConstants.LEVEL;
import com.eurodyn.qlack2.be.rules.impl.util.AuditConverterUtil;
import com.eurodyn.qlack2.be.rules.impl.util.ConverterUtil;
import com.eurodyn.qlack2.be.rules.impl.util.KnowledgeBaseUtil;
import com.eurodyn.qlack2.be.rules.impl.util.SecurityUtils;
import com.eurodyn.qlack2.be.rules.impl.util.VersionStateUtils;
import com.eurodyn.qlack2.be.rules.impl.util.XmlConverterUtil;
import com.eurodyn.qlack2.fuse.auditing.api.AuditClientService;
import com.eurodyn.qlack2.fuse.eventpublisher.api.EventPublisherService;
import com.eurodyn.qlack2.fuse.idm.api.IDMService;
import com.eurodyn.qlack2.fuse.idm.api.annotations.ValidateTicket;
import com.eurodyn.qlack2.fuse.idm.api.signing.SignedTicket;
import com.eurodyn.qlack2.webdesktop.api.SecurityService;
import com.eurodyn.qlack2.webdesktop.api.request.security.CreateSecureResourceRequest;
import com.eurodyn.qlack2.webdesktop.api.request.security.DeleteSecureResourceRequest;
import com.eurodyn.qlack2.webdesktop.api.request.security.UpdateSecureResourceRequest;
public class DataModelsServiceImpl implements DataModelsService {
private static final Logger LOGGER = Logger.getLogger(DataModelsServiceImpl.class.getName());
private static final String DEFAULT_PACKAGE = "model";
@SuppressWarnings("unused")
private IDMService idmService;
private SecurityService securityService;
private AuditClientService audit;
private EventPublisherService eventPublisher;
private EntityManager em;
private ConverterUtil mapper;
private XmlConverterUtil xmlMapper;
private AuditConverterUtil auditMapper;
private SecurityUtils securityUtils;
private VersionStateUtils versionStateUtils;
private KnowledgeBaseUtil knowledgeBaseUtil;
public void setIdmService(IDMService idmService) {
this.idmService = idmService;
}
public void setSecurityService(SecurityService securityService) {
this.securityService = securityService;
}
public void setAudit(AuditClientService audit) {
this.audit = audit;
}
public void setEventPublisher(EventPublisherService eventPublisher) {
this.eventPublisher = eventPublisher;
}
public void setEm(EntityManager em) {
this.em = em;
}
public void setMapper(ConverterUtil mapper) {
this.mapper = mapper;
}
public void setXmlMapper(XmlConverterUtil xmlMapper) {
this.xmlMapper = xmlMapper;
}
public void setAuditMapper(AuditConverterUtil auditMapper) {
this.auditMapper = auditMapper;
}
public void setSecurityUtils(SecurityUtils securityUtils) {
this.securityUtils = securityUtils;
}
public void setVersionStateUtils(VersionStateUtils versionStateUtils) {
this.versionStateUtils = versionStateUtils;
}
public void setKnowledgeBaseUtil(KnowledgeBaseUtil knowledgeBaseUtil) {
this.knowledgeBaseUtil = knowledgeBaseUtil;
}
// -- Data models
@ValidateTicket
@Override
public List getDataModels(GetProjectDataModelsRequest request) {
String projectId = request.getProjectId();
boolean filterEmpty = request.isFilterEmpty();
LOGGER.log(Level.FINE, "Get data models for project {0}.", projectId);
List models = DataModel.findByProjectId(em, projectId);
List modelDtos = new ArrayList<>();
for (DataModel model : models) {
if (!filterEmpty || !model.getVersions().isEmpty()) {
// do not check security, summary is always viewable
modelDtos.add(mapper.mapDataModelSummary(model));
}
}
return modelDtos;
}
@ValidateTicket
@Override
public DataModelDTO getDataModel(GetDataModelRequest request) {
String modelId = request.getId();
LOGGER.log(Level.FINE, "Get data model {0}.", modelId);
DataModel model = DataModel.findById(em, modelId);
if (model == null) {
return null;
}
SignedTicket ticket = request.getSignedTicket();
securityUtils.checkCanViewDataModel(ticket, model);
DataModelDTO modelDto = mapper.mapDataModel(model, ticket);
List versions = DataModelVersion.findByDataModelId(em, modelId);
modelDto.setVersions(mapper.mapDataModelVersionSummaryList(versions, ticket));
AuditDataModelDTO auditDto = auditMapper.mapDataModel(model);
auditDto.setVersions(auditMapper.mapDataModelVersionList(versions));
audit.audit(LEVEL.QBE_RULES.toString(), EVENT.VIEW.toString(), GROUP.DATA_MODEL.toString(),
null, ticket.getUserID(), auditDto);
return modelDto;
}
@ValidateTicket
@Override
public DataModelDTO getDataModelByProjectAndName(GetDataModelByProjectAndNameRequest request) {
String projectId = request.getProjectId();
String name = request.getName();
LOGGER.log(Level.FINE, "Get data model by project {0} and name {1}.", new Object[]{projectId, name});
DataModel model = DataModel.findByProjectAndName(em, projectId, name);
if (model == null) {
return null;
}
DataModelDTO modelDto = mapper.mapDataModelSummary(model);
return modelDto;
}
@ValidateTicket
@Override
public String createDataModel(CreateDataModelRequest request) {
String projectId = request.getProjectId();
LOGGER.log(Level.FINE, "Create data model in project {0}.", projectId);
SignedTicket ticket = request.getSignedTicket();
securityUtils.checkCanCreateDataModel(ticket, projectId);
DataModel model = new DataModel();
String modelId = model.getId();
model.setProjectId(projectId);
model.setName(request.getName());
model.setDescription(request.getDescription());
model.setActive(request.isActive());
List categories = new ArrayList<>();
if (request.getCategoryIds() != null) {
for (String categoryId : request.getCategoryIds()) {
Category category = em.getReference(Category.class, categoryId);
categories.add(category);
}
}
model.setCategories(categories);
DateTime now = DateTime.now();
long millis = now.getMillis();
model.setCreatedBy(ticket.getUserID());
model.setCreatedOn(millis);
model.setLastModifiedBy(ticket.getUserID());
model.setLastModifiedOn(millis);
em.persist(model);
CreateSecureResourceRequest resourceRequest = new CreateSecureResourceRequest(modelId, model.getName(), "Data Model");
securityService.createSecureResource(resourceRequest);
publishEvent(ticket, Constants.EVENT_CREATE, modelId);
AuditDataModelDTO auditDto = auditMapper.mapDataModel(model);
audit.audit(LEVEL.QBE_RULES.toString(), EVENT.CREATE.toString(), GROUP.DATA_MODEL.toString(),
null, ticket.getUserID(), auditDto);
return modelId;
}
@ValidateTicket
@Override
public void updateDataModel(UpdateDataModelRequest request) {
String modelId = request.getId();
LOGGER.log(Level.FINE, "Update data model {0}.", modelId);
DataModel model = DataModel.findById(em, modelId);
if (model == null) {
return;
}
SignedTicket ticket = request.getSignedTicket();
securityUtils.checkCanUpdateDataModel(ticket, model);
model.setName(request.getName());
model.setDescription(request.getDescription());
model.setActive(request.isActive());
List categories = new ArrayList<>();
if (request.getCategoryIds() != null) {
for (String categoryId : request.getCategoryIds()) {
Category category = em.getReference(Category.class, categoryId);
categories.add(category);
}
}
model.setCategories(categories);
DateTime now = DateTime.now();
long millis = now.getMillis();
model.setLastModifiedBy(ticket.getUserID());
model.setLastModifiedOn(millis);
UpdateSecureResourceRequest resourceRequest = new UpdateSecureResourceRequest(modelId, model.getName(), "Data Model");
securityService.updateSecureResource(resourceRequest);
publishEvent(ticket, Constants.EVENT_UPDATE, modelId);
AuditDataModelDTO auditDto = auditMapper.mapDataModel(model);
audit.audit(LEVEL.QBE_RULES.toString(), EVENT.UPDATE.toString(), GROUP.DATA_MODEL.toString(),
null, ticket.getUserID(), auditDto);
UpdateDataModelVersionRequest versionRequest = request.getVersionRequest();
if (versionRequest != null) {
updateDataModelVersion(ticket, model, versionRequest);
}
}
@ValidateTicket
@Override
public CanDeleteDataModelResult canDeleteDataModel(DeleteDataModelRequest request) {
String modelId = request.getId();
LOGGER.log(Level.FINE, "Check can delete data model {0}.", modelId);
DataModel model = DataModel.findById(em, modelId);
if (model == null) {
return null;
}
SignedTicket ticket = request.getSignedTicket();
securityUtils.checkCanViewDataModel(ticket, model);
AuditDataModelDTO auditDto = auditMapper.mapDataModel(model);
audit.audit(LEVEL.QBE_RULES.toString(), EVENT.CAN_DELETE.toString(), GROUP.DATA_MODEL.toString(),
null, ticket.getUserID(), auditDto);
long countWorkingSets = WorkingSetVersion.countContainingDataModel(em, modelId);
if (countWorkingSets > 0) {
CanDeleteDataModelResult result = new CanDeleteDataModelResult();
result.setResult(false);
result.setContainedInWorkingSet(true);
return result;
}
long countChildren = DataModelVersion.countChildrenOfModel(em, modelId);
if (countChildren > 0) {
CanDeleteDataModelResult result = new CanDeleteDataModelResult();
result.setResult(false);
result.setParentOfDataModel(true);
return result;
}
long countContainers = DataModelVersion.countContainersOfModel(em, modelId);
if (countContainers > 0) {
CanDeleteDataModelResult result = new CanDeleteDataModelResult();
result.setResult(false);
result.setContainedInDataModel(true);
return result;
}
long countLockedByOther = DataModelVersion.countLockedByOtherUser(em, modelId, ticket.getUserID());
if (countLockedByOther > 0) {
CanDeleteDataModelResult result = new CanDeleteDataModelResult();
result.setResult(false);
result.setLockedByOtherUser(true);
return result;
}
CanDeleteDataModelResult result = new CanDeleteDataModelResult();
result.setResult(true);
return result;
}
@ValidateTicket
@Override
public void deleteDataModel(DeleteDataModelRequest request) {
String modelId = request.getId();
LOGGER.log(Level.FINE, "Delete data model {0}.", modelId);
DataModel model = DataModel.findById(em, modelId);
if (model == null) {
return;
}
SignedTicket ticket = request.getSignedTicket();
securityUtils.checkCanUpdateDataModel(ticket, model);
long countWorkingSets = WorkingSetVersion.countContainingDataModel(em, modelId);
if (countWorkingSets > 0) {
throw new QInvalidActionException("This data model has versions contained in working sets.");
}
long countChildren = DataModelVersion.countChildrenOfModel(em, modelId);
if (countChildren > 0) {
throw new QInvalidActionException("This data model has versions which are parents of other data model versions.");
}
long countContainers = DataModelVersion.countContainersOfModel(em, modelId);
if (countContainers > 0) {
throw new QInvalidActionException("This data model has versions which are types of fields contained in other data model versions.");
}
long countLockedByOther = DataModelVersion.countLockedByOtherUser(em, modelId, ticket.getUserID());
if (countLockedByOther > 0) {
throw new QInvalidActionException("This data model has versions locked by other users.");
}
AuditDataModelDTO auditDto = auditMapper.mapDataModel(model);
audit.audit(LEVEL.QBE_RULES.toString(), EVENT.DELETE.toString(), GROUP.DATA_MODEL.toString(),
null, ticket.getUserID(), auditDto);
em.remove(model);
DeleteSecureResourceRequest resourceRequest = new DeleteSecureResourceRequest(modelId);
securityService.deleteSecureResource(resourceRequest);
publishEvent(ticket, Constants.EVENT_DELETE, modelId);
}
// -- Data model versions
@ValidateTicket
@Override
public List getDataModelFieldTypes(EmptyRequest request) {
LOGGER.log(Level.FINE, "Get data model field types.");
// XXX compute this once
List types = new ArrayList<>();
for (DataModelFieldType type : DataModelFieldType.values()) {
DataModelFieldTypeDTO typeDto = new DataModelFieldTypeDTO();
typeDto.setId(String.valueOf(type.ordinal()));
typeDto.setName(type.name());
types.add(typeDto);
}
return types;
}
@ValidateTicket
@Override
public List getDataModelVersions(GetDataModelVersionsRequest request) {
String modelId = request.getId();
LOGGER.log(Level.FINE, "Get data model versions for data model {0}.", modelId);
SignedTicket ticket = request.getSignedTicket();
// do not check security, summary is always viewable
List versions = DataModelVersion.findByDataModelId(em, modelId);
String filterCycles = request.getFilterCycles();
if (filterCycles != null) {
versions = filterVersionsThatCauseCycles(versions, filterCycles);
}
List versionDtos = mapper.mapDataModelVersionSummaryList(versions, ticket);
return versionDtos;
}
@ValidateTicket
@Override
public DataModelVersionDTO getDataModelVersion(GetDataModelVersionRequest request) {
String versionId = request.getId();
LOGGER.log(Level.FINE, "Get data model version {0}.", versionId);
DataModelVersion version = DataModelVersion.findById(em, versionId);
if (version == null) {
return null;
}
DataModel model = version.getDataModel();
SignedTicket ticket = request.getSignedTicket();
securityUtils.checkCanViewDataModel(ticket, model);
DataModelVersionDTO versionDto = mapper.mapDataModelVersion(version, ticket);
List fields = DataModelField.findByContainerModelId(em, versionId);
List fieldDtos = mapper.mapDataModelFieldList(fields);
versionDto.setFields(fieldDtos);
AuditDataModelVersionDTO auditDto = auditMapper.mapDataModelVersion(version);
auditDto.setFields(fieldDtos);
audit.audit(LEVEL.QBE_RULES.toString(), EVENT.VIEW.toString(), GROUP.DATA_MODEL_VERSION.toString(),
null, ticket.getUserID(), auditDto);
return versionDto;
}
@ValidateTicket
@Override
public DataModelVersionDTO getDataModelVersionByDataModelAndName(GetDataModelVersionByDataModelAndNameRequest request) {
String modelId = request.getDataModelId();
String name = request.getName();
LOGGER.log(Level.FINE, "Get data model version by data model {0} and name {1}.", new Object[]{modelId, name});
DataModelVersion version = DataModelVersion.findByDataModelAndName(em, modelId, name);
if (version == null) {
return null;
}
SignedTicket ticket = request.getSignedTicket();
DataModelVersionDTO versionDto = mapper.mapDataModelVersionSummary(version, ticket);
return versionDto;
}
@ValidateTicket
@Override
public String getDataModelIdByVersionId(GetDataModelIdByVersionIdRequest request) {
String versionId = request.getId();
LOGGER.log(Level.FINE, "Get data model id for data model version {0}.", versionId);
String modelId = DataModelVersion.findDataModelIdById(em, versionId);
return modelId;
}
@ValidateTicket
@Override
public String createDataModelVersion(CreateDataModelVersionRequest request) {
String modelId = request.getDataModelId();
DataModel model = DataModel.findById(em, modelId); // load in full for security
LOGGER.log(Level.FINE, "Create data model version for data model {0}.", modelId);
SignedTicket ticket = request.getSignedTicket();
securityUtils.checkCanUpdateDataModel(ticket, model);
DataModelVersion version = new DataModelVersion();
String versionId = version.getId();
version.setDataModel(model);
version.setName(request.getName());
version.setDescription(request.getDescription());
version.setModelPackage(DEFAULT_PACKAGE); // XXX must set default value
String baseVersionId = request.getBasedOnId();
if (baseVersionId != null && !baseVersionId.isEmpty()) {
copyFromBaseVersion(version, baseVersionId);
}
DateTime now = DateTime.now();
long millis = now.getMillis();
version.setState(VersionState.DRAFT);
version.setCreatedBy(ticket.getUserID());
version.setCreatedOn(millis);
version.setLastModifiedBy(ticket.getUserID());
version.setLastModifiedOn(millis);
em.persist(version);
publishVersionEvent(ticket, Constants.EVENT_CREATE, versionId);
AuditDataModelVersionDTO auditDto = auditMapper.mapDataModelVersion(version);
// XXX audit fields ?
audit.audit(LEVEL.QBE_RULES.toString(), EVENT.CREATE.toString(), GROUP.DATA_MODEL_VERSION.toString(),
null, ticket.getUserID(), auditDto);
return versionId;
}
private void copyFromBaseVersion(DataModelVersion version, String baseVersionId) {
DataModelVersion baseVersion = DataModelVersion.findById(em, baseVersionId);
if (baseVersion == null) {
throw new IllegalArgumentException("Base data model version does not exist.");
}
DataModel model = version.getDataModel();
DataModel baseModel = baseVersion.getDataModel();
if (!baseModel.getId().equals(model.getId())) {
throw new IllegalArgumentException("Base data model version does not belong to current data model.");
}
version.setModelPackage(baseVersion.getModelPackage());
version.setParentModel(baseVersion.getParentModel());
List fields = new ArrayList<>();
List baseFields = DataModelField.findByContainerModelId(em, baseVersionId);
for (DataModelField baseField : baseFields) {
DataModelField field = new DataModelField();
field.setContainerModel(version);
field.setName(baseField.getName());
field.setFieldPrimitiveType(baseField.getFieldPrimitiveType());
field.setFieldModelType(baseField.getFieldModelType());
fields.add(field);
}
version.setFields(fields);
}
private void updateDataModelVersion(SignedTicket ticket, DataModel model, UpdateDataModelVersionRequest request) {
String versionId = request.getId();
LOGGER.log(Level.FINE, "Update data model version {0}.", versionId);
DataModelVersion version = DataModelVersion.findById(em, versionId);
if (version == null) {
return;
}
versionStateUtils.checkDataModelVersionNotFinalized(version);
versionStateUtils.checkCanModifyDataModelVersion(ticket.getUserID(), version);
DataModel existingModel = version.getDataModel();
if (!model.getId().equals(existingModel.getId())) {
throw new IllegalArgumentException("Data model version does not belong to data model.");
}
version.setDescription(request.getDescription());
version.setModelPackage(request.getModelPackage());
if (version.getState() == VersionState.TESTING) {
checkCanUpdateEnabledForTestingDataModelVersion(request);
}
String parentModelVersionId = request.getParentModelVersionId();
if (parentModelVersionId != null && !parentModelVersionId.isEmpty()) {
DataModelVersion parentModelVersion = DataModelVersion.findById(em, parentModelVersionId);
if (parentModelVersion == null) {
throw new IllegalArgumentException("Parent data model version does not exit.");
}
DataModel parentModel = parentModelVersion.getDataModel();
if (!parentModel.getProjectId().equals(model.getProjectId())) {
throw new IllegalArgumentException("Parent data model version does not belong to data model project.");
}
boolean hasCycle = hasInheritanceCycle(versionId, parentModelVersion);
if (hasCycle) {
throw new QInheritanceCycleException();
}
version.setParentModel(parentModelVersion);
}
else {
version.setParentModel(null);
}
checkDataModelFieldsUniqueNames(request.getFieldRequests());
List fields = DataModelField.findByContainerModelId(em, versionId);
updateDataModelFields(version, fields, request.getFieldRequests());
DateTime now = DateTime.now();
long millis = now.getMillis();
version.setLastModifiedBy(ticket.getUserID());
version.setLastModifiedOn(millis);
if (version.getState() == VersionState.TESTING) {
invalidateWorkingSets(ticket, version);
}
publishVersionEvent(ticket, Constants.EVENT_UPDATE, versionId);
AuditDataModelVersionDTO auditDto = auditMapper.mapDataModelVersion(version);
// XXX audit fields
audit.audit(LEVEL.QBE_RULES.toString(), EVENT.UPDATE.toString(), GROUP.DATA_MODEL_VERSION.toString(),
null, ticket.getUserID(), auditDto);
}
private void invalidateWorkingSets(SignedTicket ticket, DataModelVersion version) {
String versionId = version.getId();
List workingSetVersions = WorkingSetVersion.findContainingDataModelVersion(em, versionId);
for (WorkingSetVersion workingSetVersion : workingSetVersions) {
if (workingSetVersion.getState() == VersionState.TESTING) {
workingSetVersion.setDataModelsJar(null);
knowledgeBaseUtil.destroyKnowledgeBase(ticket, workingSetVersion);
}
}
}
private List filterVersionsThatCauseCycles(List parentVersions, String versionId) {
List filteredParentVersions = new ArrayList<>();
for (DataModelVersion parentVersion : parentVersions) {
if (hasInheritanceCycle(versionId, parentVersion)) {
continue;
}
filteredParentVersions.add(parentVersion);
}
return filteredParentVersions;
}
private boolean hasInheritanceCycle(String versionId, DataModelVersion parentVersion) {
DataModelVersion currentParentVersion = parentVersion;
while (currentParentVersion != null) {
if (currentParentVersion.getId().equals(versionId)) {
return true;
}
currentParentVersion = currentParentVersion.getParentModel();
}
return false;
}
private void checkDataModelFieldsUniqueNames(List requests) {
Set names = new HashSet<>();
for (UpdateDataModelFieldRequest request : requests) {
String name = request.getName();
if (names.contains(name)) {
throw new QNonUniqueFieldNamesException();
}
names.add(name);
}
}
private void updateDataModelFields(DataModelVersion version, List fields, List requests) {
for (DataModelField field : fields) {
UpdateDataModelFieldRequest request = findFieldRequestById(requests, field.getId());
if (request != null) {
updateField(field, request);
}
else {
em.remove(field);
}
}
for (UpdateDataModelFieldRequest request : requests) {
if (request.getId() == null) {
DataModelField field = new DataModelField();
field.setContainerModel(version);
updateField(field, request);
em.persist(field);
}
}
}
private void updateField(DataModelField field, UpdateDataModelFieldRequest request) {
field.setName(request.getName());
String fieldTypeId = request.getFieldTypeId();
DataModelFieldType primitiveFieldType = findFieldTypeById(fieldTypeId);
if (primitiveFieldType != null) {
field.setFieldPrimitiveType(primitiveFieldType);
field.setFieldModelType(null);
}
else {
// XXX check data model belongs to same project
String fieldTypeVersionId = request.getFieldTypeVersionId();
DataModelVersion fieldModelType = em.getReference(DataModelVersion.class, fieldTypeVersionId);
field.setFieldPrimitiveType(null);
field.setFieldModelType(fieldModelType);
}
}
private static UpdateDataModelFieldRequest findFieldRequestById(List requests, String id) {
for (UpdateDataModelFieldRequest request : requests) {
if (id.equals(request.getId())) {
return request;
}
}
return null;
}
private static DataModelFieldType findFieldTypeById(String id) {
for (DataModelFieldType type : DataModelFieldType.values()) {
if (id.equals(String.valueOf(type.ordinal()))) {
return type;
}
}
return null;
}
@ValidateTicket
@Override
public CanDeleteDataModelVersionResult canDeleteDataModelVersion(DeleteDataModelVersionRequest request) {
String versionId = request.getId();
LOGGER.log(Level.FINE, "Check can delete data model version {0}.", versionId);
DataModelVersion version = DataModelVersion.findById(em, versionId);
if (version == null) {
return null;
}
DataModel model = version.getDataModel();
SignedTicket ticket = request.getSignedTicket();
securityUtils.checkCanUpdateDataModel(ticket, model);
AuditDataModelVersionDTO auditDto = auditMapper.mapDataModelVersion(version);
audit.audit(LEVEL.QBE_RULES.toString(), EVENT.CAN_DELETE.toString(), GROUP.DATA_MODEL_VERSION.toString(),
null, ticket.getUserID(), auditDto);
List workingSetVersions = WorkingSetVersion.findContainingDataModelVersion(em, versionId);
if (!workingSetVersions.isEmpty()) {
CanDeleteDataModelVersionResult result = new CanDeleteDataModelVersionResult();
result.setResult(false);
result.setContainedInWorkingSetVersions(true);
List workingSetNames = new ArrayList<>();
for (WorkingSetVersion workingSetVersion : workingSetVersions) {
workingSetNames.add(workingSetVersion.getWorkingSet().getName() + " / " + workingSetVersion.getName());
}
result.setWorkingSetVersions(workingSetNames);
return result;
}
List children = DataModelVersion.findChildren(em, versionId);
if (!children.isEmpty()) {
CanDeleteDataModelVersionResult result = new CanDeleteDataModelVersionResult();
result.setResult(false);
result.setParentOfDataModelVersions(true);
List childrenNames = new ArrayList<>();
for (DataModelVersion child : children) {
childrenNames.add(child.getDataModel().getName() + " / " + child.getName());
}
result.setDataModelVersions(childrenNames);
return result;
}
List containers = DataModelVersion.findContainers(em, versionId);
if (!containers.isEmpty()) {
CanDeleteDataModelVersionResult result = new CanDeleteDataModelVersionResult();
result.setResult(false);
result.setContainedInDataModelVersions(true);
List containerNames = new ArrayList<>();
for (DataModelVersion child : containers) {
containerNames.add(child.getDataModel().getName() + " / " + child.getName());
}
result.setDataModelVersions(containerNames);
return result;
}
CanDeleteDataModelVersionResult result = new CanDeleteDataModelVersionResult();
result.setResult(true);
return result;
}
@ValidateTicket
@Override
public void deleteDataModelVersion(DeleteDataModelVersionRequest request) {
String versionId = request.getId();
LOGGER.log(Level.FINE, "Delete data model version {0}.", versionId);
DataModelVersion version = DataModelVersion.findById(em, versionId);
if (version == null) {
return;
}
DataModel model = version.getDataModel();
SignedTicket ticket = request.getSignedTicket();
securityUtils.checkCanUpdateDataModel(ticket, model);
versionStateUtils.checkCanModifyDataModelVersion(ticket.getUserID(), version);
List workingSetVersions = WorkingSetVersion.findContainingDataModelVersion(em, versionId);
if (!workingSetVersions.isEmpty()) {
throw new QInvalidActionException("The data model version is contained in a working set version.");
}
List children = DataModelVersion.findChildren(em, versionId);
if (!children.isEmpty()) {
throw new QInvalidActionException("The data model version is the parent of other data model versions.");
}
List containers = DataModelVersion.findContainers(em, versionId);
if (!containers.isEmpty()) {
throw new QInvalidActionException("The data model version is the type of fields of other data model versions.");
}
AuditDataModelVersionDTO auditDto = auditMapper.mapDataModelVersion(version);
audit.audit(LEVEL.QBE_RULES.toString(), EVENT.DELETE.toString(), GROUP.DATA_MODEL_VERSION.toString(),
null, ticket.getUserID(), auditDto);
em.remove(version);
publishVersionEvent(ticket, Constants.EVENT_DELETE, versionId);
}
@ValidateTicket
@Override
public void lockDataModelVersion(LockDataModelVersionRequest request) {
String versionId = request.getId();
LOGGER.log(Level.FINE, "Lock data model version {0}.", versionId);
DataModelVersion version = DataModelVersion.findById(em, versionId);
if (version == null) {
return;
}
DataModel model = version.getDataModel();
SignedTicket ticket = request.getSignedTicket();
securityUtils.checkCanLockDataModelVersion(ticket, model);
versionStateUtils.checkDataModelVersionNotFinalized(version);
versionStateUtils.checkCanLockDataModelVersion(ticket.getUserID(), version);
DateTime now = DateTime.now();
long millis = now.getMillis();
version.setLockedBy(ticket.getUserID());
version.setLockedOn(millis);
publishVersionEvent(ticket, Constants.EVENT_LOCK, versionId);
AuditDataModelVersionDTO auditDto = auditMapper.mapDataModelVersion(version);
audit.audit(LEVEL.QBE_RULES.toString(), EVENT.LOCK.toString(), GROUP.DATA_MODEL_VERSION.toString(),
null, ticket.getUserID(), auditDto);
}
@ValidateTicket
@Override
public void unlockDataModelVersion(UnlockDataModelVersionRequest request) {
String versionId = request.getId();
LOGGER.log(Level.FINE, "Unlock data model version {0}.", versionId);
DataModelVersion version = DataModelVersion.findById(em, versionId);
if (version == null) {
return;
}
DataModel model = version.getDataModel();
SignedTicket ticket = request.getSignedTicket();
securityUtils.checkCanUnlockDataModelVersion(ticket, model);
versionStateUtils.checkDataModelVersionNotFinalized(version);
boolean canUnlockAny = securityUtils.canUnlockAnyDataModelVersion(ticket, model);
versionStateUtils.checkCanUnlockDataModelVersion(ticket.getUserID(), canUnlockAny, version);
version.setLockedBy(null);
version.setLockedOn(null);
publishVersionEvent(ticket, Constants.EVENT_UNLOCK, versionId);
AuditDataModelVersionDTO auditDto = auditMapper.mapDataModelVersion(version);
audit.audit(LEVEL.QBE_RULES.toString(), EVENT.UNLOCK.toString(), GROUP.DATA_MODEL_VERSION.toString(),
null, ticket.getUserID(), auditDto);
}
// -- EnableTesting / Finalize (cascadable)
@ValidateTicket
@Override
public CanUpdateEnabledForTestingDataModelResult canUpdateEnabledForTestingDataModelVersion(UpdateDataModelVersionRequest request) {
String versionId = request.getId();
LOGGER.log(Level.FINE, "Check can update enabled-for-testing data model version {0}.", versionId);
DataModelVersion version = DataModelVersion.findById(em, versionId);
if (version == null) {
return null;
}
if (version.getState() != VersionState.TESTING) {
CanUpdateEnabledForTestingDataModelResult result = new CanUpdateEnabledForTestingDataModelResult();
result.setResult(true);
return result;
}
DataModel model = version.getDataModel();
SignedTicket ticket = request.getSignedTicket();
securityUtils.checkCanUpdateDataModel(ticket, model);
AuditDataModelVersionDTO auditDto = auditMapper.mapDataModelVersion(version);
audit.audit(LEVEL.QBE_RULES.toString(), EVENT.CAN_UPDATE.toString(), GROUP.DATA_MODEL_VERSION.toString(),
null, ticket.getUserID(), auditDto);
Set dependencyVersionIds = findDependencyVersionIds(request);
List violatedWorkingSetVersions = findViolatedWorkingSetVersions(version.getWorkingSets(), dependencyVersionIds);
if (!violatedWorkingSetVersions.isEmpty()) {
CanUpdateEnabledForTestingDataModelResult result = new CanUpdateEnabledForTestingDataModelResult();
result.setResult(false);
result.setRestrict(true);
List violatedWorkingSetVersionNames = new ArrayList<>();
for (WorkingSetVersion workingSetVersion : violatedWorkingSetVersions) {
violatedWorkingSetVersionNames.add(workingSetVersion.getWorkingSet().getName() + " / " + workingSetVersion.getName());
}
result.setWorkingSetVersions(violatedWorkingSetVersionNames);
return result;
}
CanUpdateEnabledForTestingDataModelResult result = new CanUpdateEnabledForTestingDataModelResult();
result.setResult(true);
return result;
}
private void checkCanUpdateEnabledForTestingDataModelVersion(UpdateDataModelVersionRequest request) {
String versionId = request.getId();
DataModelVersion version = DataModelVersion.findById(em, versionId);
if (version == null) {
return;
}
Set dependencyVersionIds = findDependencyVersionIds(request);
List violatedWorkingSetVersions = findViolatedWorkingSetVersions(version.getWorkingSets(), dependencyVersionIds);
if (!violatedWorkingSetVersions.isEmpty()) {
throw new QInvalidActionException("Update of the data model version will violate enabled-for-testing working sets");
}
}
private Set findDependencyVersionIds(UpdateDataModelVersionRequest request) {
Set dependencyVersionIds = new HashSet<>();
String parentModelVersionId = request.getParentModelVersionId();
if (parentModelVersionId != null) {
dependencyVersionIds.add(parentModelVersionId);
}
for (UpdateDataModelFieldRequest fieldRequest : request.getFieldRequests()) {
String fieldTypeVersionId = fieldRequest.getFieldTypeVersionId();
if (fieldTypeVersionId != null) {
dependencyVersionIds.add(fieldTypeVersionId);
}
}
return dependencyVersionIds;
}
private List findViolatedWorkingSetVersions(List workingSetVersions, Set dependencyVersionIds) {
List violatedWorkingSetVersions = new ArrayList<>();
for (WorkingSetVersion workingSetVersion : workingSetVersions) {
if (workingSetVersion.getState() == VersionState.TESTING) {
List containedVersionIds = DataModelVersion.findIdsByWorkingSetVersionId(em, workingSetVersion.getId());
if (!containedVersionIds.containsAll(dependencyVersionIds)) {
violatedWorkingSetVersions.add(workingSetVersion);
}
}
}
return violatedWorkingSetVersions;
}
@ValidateTicket
@Override
public CanEnableTestingDataModelResult canEnableTestingDataModelVersion(EnableTestingDataModelVersionRequest request) {
SignedTicket ticket = request.getSignedTicket();
String versionId = request.getId();
LOGGER.log(Level.FINE, "Check can enable testing data model version {0}.", versionId);
DataModelVersion version = DataModelVersion.findById(em, versionId);
if (version == null) {
return null;
}
AuditDataModelVersionDTO auditDto = auditMapper.mapDataModelVersion(version);
audit.audit(LEVEL.QBE_RULES.toString(), EVENT.CAN_ENABLE_TESTING.toString(), GROUP.DATA_MODEL_VERSION.toString(),
null, ticket.getUserID(), auditDto);
CollectNotTestingAction versionAction = new CollectNotTestingAction();
Set versionVisited = new HashSet<>();
visitNodeClosure(version, versionAction, versionVisited);
List notTestingVersions = versionAction.getNotTestingVersions();
notTestingVersions.remove(version);
if (!notTestingVersions.isEmpty()) {
boolean canModifyList = canModifyDataModelVersionList(ticket, notTestingVersions);
if (!canModifyList) {
CanEnableTestingDataModelResult result = new CanEnableTestingDataModelResult();
result.setResult(false);
return result;
}
else {
CanEnableTestingDataModelResult result = new CanEnableTestingDataModelResult();
result.setResult(true);
result.setCascade(true);
List versionNames = new ArrayList<>();
for (DataModelVersion notTestingVersion : notTestingVersions) {
versionNames.add(notTestingVersion.getDataModel().getName() + " / " + notTestingVersion.getName());
}
result.setVersions(versionNames);
return result;
}
}
else {
CanEnableTestingDataModelResult result = new CanEnableTestingDataModelResult();
result.setResult(true);
return result;
}
}
@ValidateTicket
@Override
public CanDisableTestingDataModelResult canDisableTestingDataModelVersion(EnableTestingDataModelVersionRequest request) {
String versionId = request.getId();
LOGGER.log(Level.FINE, "Check can disable testing data model version {0}.", versionId);
DataModelVersion version = DataModelVersion.findById(em, versionId);
if (version == null) {
return null;
}
DataModel model = version.getDataModel();
SignedTicket ticket = request.getSignedTicket();
securityUtils.checkCanUpdateDataModel(ticket, model);
AuditDataModelVersionDTO auditDto = auditMapper.mapDataModelVersion(version);
audit.audit(LEVEL.QBE_RULES.toString(), EVENT.CAN_DISABLE_TESTING.toString(), GROUP.DATA_MODEL_VERSION.toString(),
null, ticket.getUserID(), auditDto);
List enabledTestingWorkingSetVersions = filterEnabledTestingWorkingSetVersions(version.getWorkingSets());
if (!enabledTestingWorkingSetVersions.isEmpty()) {
List workingSetVersionNames = new ArrayList<>();
for (WorkingSetVersion workingSetVersion : enabledTestingWorkingSetVersions) {
workingSetVersionNames.add(workingSetVersion.getWorkingSet().getName() + " / " + workingSetVersion.getName());
}
CanDisableTestingDataModelResult result = new CanDisableTestingDataModelResult();
result.setResult(false);
result.setContainedInWorkingSetVersions(true);
result.setWorkingSetVersions(workingSetVersionNames);
return result;
}
CanDisableTestingDataModelResult result = new CanDisableTestingDataModelResult();
result.setResult(true);
return result;
}
private List filterEnabledTestingWorkingSetVersions(List versions) {
List enabledTestingVersions = new ArrayList<>();
for (WorkingSetVersion version : versions) {
if (version.getState() == VersionState.TESTING) {
enabledTestingVersions.add(version);
}
}
return enabledTestingVersions;
}
@ValidateTicket
@Override
public CanFinalizeDataModelResult canFinalizeDataModelVersion(FinalizeDataModelVersionRequest request) {
SignedTicket ticket = request.getSignedTicket();
String versionId = request.getId();
LOGGER.log(Level.FINE, "Check can finalize data model version {0}.", versionId);
DataModelVersion version = DataModelVersion.findById(em, versionId);
if (version == null) {
return null;
}
AuditDataModelVersionDTO auditDto = auditMapper.mapDataModelVersion(version);
audit.audit(LEVEL.QBE_RULES.toString(), EVENT.CAN_FINALISE.toString(), GROUP.DATA_MODEL_VERSION.toString(),
null, ticket.getUserID(), auditDto);
CollectNotFinalizedAction versionAction = new CollectNotFinalizedAction();
Set versionVisited = new HashSet<>();
visitNodeClosure(version, versionAction, versionVisited);
List notFinalizedVersions = versionAction.getNotFinalizedVersions();
notFinalizedVersions.remove(version);
if (!notFinalizedVersions.isEmpty()) {
boolean canModifyList = canModifyDataModelVersionList(ticket, notFinalizedVersions);
if (!canModifyList) {
CanFinalizeDataModelResult result = new CanFinalizeDataModelResult();
result.setResult(false);
return result;
}
else {
CanFinalizeDataModelResult result = new CanFinalizeDataModelResult();
result.setResult(true);
result.setCascade(true);
List versionNames = new ArrayList<>();
for (DataModelVersion notFinalizedVersion : notFinalizedVersions) {
versionNames.add(notFinalizedVersion.getDataModel().getName() + " / " + notFinalizedVersion.getName());
}
result.setVersions(versionNames);
return result;
}
}
else {
CanFinalizeDataModelResult result = new CanFinalizeDataModelResult();
result.setResult(true);
return result;
}
}
@Override
public boolean canModifyDataModelVersionIdList(SignedTicket ticket, List versionIds) {
List versions = new ArrayList<>();
for (String versionId : versionIds) {
DataModelVersion version = DataModelVersion.findById(em, versionId);
versions.add(version);
}
return canModifyDataModelVersionList(ticket, versions);
}
private boolean canModifyDataModelVersionList(SignedTicket ticket, List versions) {
for (DataModelVersion version : versions) {
if (!canModifySingleDataModelVersion(ticket, version)) {
return false;
}
}
return true;
}
private boolean canModifySingleDataModelVersion(SignedTicket ticket, DataModelVersion version) {
DataModel model = version.getDataModel();
if (!securityUtils.canUpdateDataModel(ticket, model)) {
return false;
}
if (!versionStateUtils.canModifyDataModelVersion(ticket.getUserID(), version)) {
return false;
}
return true;
}
@ValidateTicket
@Override
public void enableTestingDataModelVersion(EnableTestingDataModelVersionRequest request) {
SignedTicket ticket = request.getSignedTicket();
String versionId = request.getId();
boolean enableTesting = request.isEnableTesting();
LOGGER.log(Level.FINE, "Enable testing data model version {0} ({1}).", new Object[]{versionId, enableTesting});
DataModelVersion version = DataModelVersion.findById(em, versionId);
if (version == null) {
return;
}
if (enableTesting) {
CollectNotTestingAction versionAction = new CollectNotTestingAction();
Set versionVisited = new HashSet<>();
visitNodeClosure(version, versionAction, versionVisited);
List notTestingVersions = versionAction.getNotTestingVersions();
enableTestingDataModelVersionList(ticket, notTestingVersions);
} else {
List enabledTestingWorkingSetVersions = filterEnabledTestingWorkingSetVersions(version.getWorkingSets());
if (!enabledTestingWorkingSetVersions.isEmpty()) {
throw new QInvalidActionException("This data model version is contained in working set versions with testing enabled.");
}
enableTestingDataModelVersionSingle(ticket, version, false);
}
}
@Override
public void enableTestingDataModelVersionNoCascade(EnableTestingDataModelVersionRequest request) {
SignedTicket ticket = request.getSignedTicket();
String versionId = request.getId();
boolean enableTesting = request.isEnableTesting();
DataModelVersion version = DataModelVersion.findById(em, versionId);
if (version == null) {
return;
}
enableTestingDataModelVersionSingle(ticket, version, enableTesting);
}
private void enableTestingDataModelVersionList(SignedTicket ticket, List versions) {
for (DataModelVersion version : versions) {
enableTestingDataModelVersionSingle(ticket, version, true);
}
}
private void enableTestingDataModelVersionSingle(SignedTicket ticket, DataModelVersion version, boolean enableTesting) {
DataModel model = version.getDataModel();
LOGGER.log(Level.FINE, "Enable testing single data model version {0} ({1}).", new Object[]{version.getId(), enableTesting});
securityUtils.checkCanUpdateDataModel(ticket, model);
versionStateUtils.checkDataModelVersionNotFinalized(version);
versionStateUtils.checkCanModifyDataModelVersion(ticket.getUserID(), version);
if (enableTesting) {
version.setState(VersionState.TESTING);
}
else {
version.setState(VersionState.DRAFT);
}
String stringEvent = enableTesting ? Constants.EVENT_ENABLE_TESTING : Constants.EVENT_DISABLE_TESTING;
publishVersionEvent(ticket, stringEvent, version.getId());
EVENT event = enableTesting ? EVENT.ENABLE_TESTING : EVENT.DISABLE_TESTING;
AuditDataModelVersionDTO auditDto = auditMapper.mapDataModelVersion(version);
audit.audit(LEVEL.QBE_RULES.toString(), event.toString(), GROUP.DATA_MODEL_VERSION.toString(),
null, ticket.getUserID(), auditDto);
}
@ValidateTicket
@Override
public void finalizeDataModelVersion(FinalizeDataModelVersionRequest request) {
SignedTicket ticket = request.getSignedTicket();
String versionId = request.getId();
LOGGER.log(Level.FINE, "Finalize data model version {0}.", versionId);
DataModelVersion version = DataModelVersion.findById(em, versionId);
if (version == null) {
return;
}
CollectNotFinalizedAction versionAction = new CollectNotFinalizedAction();
Set versionVisited = new HashSet<>();
visitNodeClosure(version, versionAction, versionVisited);
List notFinalizedVersions = versionAction.getNotFinalizedVersions();
finalizeDataModelVersionList(ticket, notFinalizedVersions);
}
@Override
public void finalizeDataModelVersionNoCascade(FinalizeDataModelVersionRequest request) {
SignedTicket ticket = request.getSignedTicket();
String versionId = request.getId();
DataModelVersion version = DataModelVersion.findById(em, versionId);
if (version == null) {
return;
}
finalizeDataModelVersionSingle(ticket, version);
}
private void finalizeDataModelVersionList(SignedTicket ticket, List versions) {
for (DataModelVersion version : versions) {
finalizeDataModelVersionSingle(ticket, version);
}
}
private void finalizeDataModelVersionSingle(SignedTicket ticket, DataModelVersion version) {
DataModel model = version.getDataModel();
LOGGER.log(Level.FINE, "Finalize single data model version {0}.", version.getId());
securityUtils.checkCanUpdateDataModel(ticket, model);
versionStateUtils.checkDataModelVersionNotFinalized(version);
versionStateUtils.checkCanModifyDataModelVersion(ticket.getUserID(), version);
version.setState(VersionState.FINAL);
version.setLockedBy(null);
version.setLockedOn(null);
publishVersionEvent(ticket, Constants.EVENT_FINALISE, version.getId());
AuditDataModelVersionDTO auditDto = auditMapper.mapDataModelVersion(version);
audit.audit(LEVEL.QBE_RULES.toString(), EVENT.FINALISE.toString(), GROUP.DATA_MODEL_VERSION.toString(),
null, ticket.getUserID(), auditDto);
}
@ValidateTicket
@Override
public byte[] exportDataModelVersion(ExportDataModelVersionRequest request) {
String versionId = request.getId();
LOGGER.log(Level.FINE, "Export data model version {0}.", versionId);
DataModelVersion version = DataModelVersion.findById(em, versionId);
if (version == null) {
return null;
}
DataModel model = version.getDataModel();
SignedTicket ticket = request.getSignedTicket();
securityUtils.checkCanViewDataModel(ticket, model);
if (version.getState() != VersionState.FINAL) {
throw new QInvalidActionException("Version is not finalized.");
}
XmlDataModelVersionDTO xmlVersionDto = xmlMapper.mapDataModel(version);
byte[] xml = null;
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// XXX generated XML is immutable, generate once and cache ?
JAXBContext jaxbContext = JAXBContext.newInstance(XmlDataModelVersionDTO.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
jaxbMarshaller.marshal(xmlVersionDto, baos);
xml = baos.toByteArray();
}
catch (JAXBException e) {
throw new QImportExportException("Cannot export data model version.", e);
}
publishVersionEvent(ticket, Constants.EVENT_EXPORT, versionId);
AuditDataModelVersionDTO auditDto = auditMapper.mapDataModelVersion(version);
audit.audit(LEVEL.QBE_RULES.toString(), EVENT.EXPORT.toString(), GROUP.DATA_MODEL_VERSION.toString(),
null, ticket.getUserID(), auditDto);
return xml;
}
@ValidateTicket
@Override
public String importDataModelVersion(ImportDataModelVersionRequest request) {
String modelId = request.getDataModelId();
byte[] xml = request.getXml();
LOGGER.log(Level.FINE, "Import data model version in data model {0}.", modelId);
DataModel model = DataModel.findById(em, modelId);
if (model == null) {
throw new IllegalArgumentException("Data model does not exist");
}
SignedTicket ticket = request.getSignedTicket();
securityUtils.checkCanUpdateDataModel(ticket, model);
XmlDataModelVersionDTO xmlVersion = null;
try {
ByteArrayInputStream bais = new ByteArrayInputStream(xml);
JAXBContext jaxbContext = JAXBContext.newInstance(XmlDataModelVersionDTO.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
xmlVersion = (XmlDataModelVersionDTO) jaxbUnmarshaller.unmarshal(bais);
}
catch (JAXBException e) {
throw new QImportExportException("Cannot import data model version.", e);
}
DataModelVersion existingDataModelVersion = DataModelVersion.findByDataModelAndName(em, modelId, xmlVersion.getName());
if (existingDataModelVersion != null) {
throw new QImportExportException("Another data model version with the same name already exists.");
}
DataModelVersion version = xmlMapper.mapDataModelVersion(ticket, model, xmlVersion);
String versionId = version.getId();
em.persist(version);
String projectId = model.getProjectId();
xmlMapper.mapDataModelVersionParent(em, projectId, version, xmlVersion);
DataModelVersion parentModelVersion = version.getParentModel();
if (parentModelVersion != null) {
// the imported data model is new, only need to check for self-reference
boolean hasSelfRef = parentModelVersion.getId().equals(versionId);
if (hasSelfRef) {
throw new QImportExportException("The imported data model extends itself.");
}
}
xmlMapper.mapDataModelVersionFields(em, projectId, version, xmlVersion);
publishVersionEvent(ticket, Constants.EVENT_IMPORT, versionId);
AuditDataModelVersionDTO auditDto = auditMapper.mapDataModelVersion(version);
// XXX audit fields ?
audit.audit(LEVEL.QBE_RULES.toString(), EVENT.IMPORT.toString(), GROUP.DATA_MODEL_VERSION.toString(),
null, ticket.getUserID(), auditDto);
return versionId;
}
// -- Helpers
@Override
public List checkDataModelVersionClosureContained(List containedVersionIds) {
CollectNotContainedAction versionAction = new CollectNotContainedAction(containedVersionIds);
Set versionVisited = new HashSet<>();
for (String containedVersionId : containedVersionIds) {
DataModelVersion version = DataModelVersion.findById(em, containedVersionId);
visitNodeClosure(version, versionAction, versionVisited);
}
List notContainedVersions = versionAction.getNotContainedVersions();
List notContainedVersionIds = new ArrayList<>();
for (DataModelVersion notContainedVersion : notContainedVersions) {
notContainedVersionIds.add(notContainedVersion.getId());
}
return notContainedVersionIds;
}
private static interface DataModelVersionAction {
void preApply(DataModelVersion version);
void postApply(DataModelVersion version);
}
private static class CollectNotContainedAction implements DataModelVersionAction {
private List versionIds;
private List notContainedVersions;
CollectNotContainedAction(List versionIds) {
this.versionIds = versionIds;
this.notContainedVersions = new ArrayList<>();
}
public List getNotContainedVersions() {
return notContainedVersions;
}
@Override
public void preApply(DataModelVersion version) {
String versionId = version.getId();
if (!versionIds.contains(versionId)) {
notContainedVersions.add(version);
}
}
@Override
public void postApply(DataModelVersion version) {
}
}
private static class CollectNotTestingAction implements DataModelVersionAction {
private List notTestingVersions;
CollectNotTestingAction() {
this.notTestingVersions = new ArrayList<>();
}
public List getNotTestingVersions() {
return notTestingVersions;
}
@Override
public void preApply(DataModelVersion version) {
VersionState versionState = version.getState();
if (versionState != VersionState.TESTING && versionState != VersionState.FINAL) {
notTestingVersions.add(version);
}
}
@Override
public void postApply(DataModelVersion version) {
}
}
private static class CollectNotFinalizedAction implements DataModelVersionAction {
private List notFinalizedVersions;
CollectNotFinalizedAction() {
this.notFinalizedVersions = new ArrayList<>();
}
public List getNotFinalizedVersions() {
return notFinalizedVersions;
}
@Override
public void preApply(DataModelVersion version) {
VersionState versionState = version.getState();
if (versionState != VersionState.FINAL) {
notFinalizedVersions.add(version);
}
}
@Override
public void postApply(DataModelVersion version) {
}
}
private void visitNodeClosure(DataModelVersion version, DataModelVersionAction versionAction, Set versionVisited) {
String versionId = version.getId();
boolean visited = versionVisited.contains(versionId);
if (visited) {
return;
}
versionVisited.add(versionId);
versionAction.preApply(version);
// parent
DataModelVersion parentModelVersion = version.getParentModel();
if (parentModelVersion != null) {
visitNodeClosure(parentModelVersion, versionAction, versionVisited);
}
// fields
for (DataModelField field : version.getFields()) {
DataModelVersion fieldModelVersion = field.getFieldModelType();
if (fieldModelVersion != null) {
visitNodeClosure(fieldModelVersion, versionAction, versionVisited);
}
}
versionAction.postApply(version);
}
// -- Helpers
private void publishEvent(SignedTicket ticket, String event, String modelId) {
Map message = new HashMap<>();
message.put("srcUserId", ticket.getUserID());
message.put("event", event);
message.put(Constants.EVENT_DATA_DATA_MODEL_ID, modelId);
eventPublisher.publishSync(message, Constants.TOPIC_PREFIX + Constants.RESOURCE_TYPE_DATA_MODEL + "/" + event);
}
private void publishVersionEvent(SignedTicket ticket, String event, String versionId) {
Map message = new HashMap<>();
message.put("srcUserId", ticket.getUserID());
message.put("event", event);
message.put(Constants.EVENT_DATA_DATA_MODEL_VERSION_ID, versionId);
eventPublisher.publishSync(message, Constants.TOPIC_PREFIX + Constants.RESOURCE_TYPE_DATA_MODEL_VERSION + "/" + event);
}
}