/*
* ============LICENSE_START=======================================================
- * Copyright (C) 2021-2022 Nordix Foundation
+ * Copyright (C) 2021-2023 Nordix Foundation
* Modifications Copyright (C) 2021 Pantheon.tech
* Modifications Copyright (C) 2020-2022 Bell Canada.
+ * Modifications Copyright (C) 2022 TechMahindra Ltd.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
package org.onap.cps.spi.impl;
-import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS;
-
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSet.Builder;
+import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import org.onap.cps.spi.entities.AnchorEntity;
import org.onap.cps.spi.entities.DataspaceEntity;
import org.onap.cps.spi.entities.FragmentEntity;
-import org.onap.cps.spi.entities.SchemaSetEntity;
-import org.onap.cps.spi.entities.YangResourceEntity;
+import org.onap.cps.spi.entities.FragmentEntityArranger;
+import org.onap.cps.spi.entities.FragmentExtract;
import org.onap.cps.spi.exceptions.AlreadyDefinedException;
import org.onap.cps.spi.exceptions.AlreadyDefinedExceptionBatch;
import org.onap.cps.spi.exceptions.ConcurrencyException;
import org.onap.cps.spi.model.DataNodeBuilder;
import org.onap.cps.spi.repository.AnchorRepository;
import org.onap.cps.spi.repository.DataspaceRepository;
+import org.onap.cps.spi.repository.FragmentNativeRepository;
+import org.onap.cps.spi.repository.FragmentQueryBuilder;
import org.onap.cps.spi.repository.FragmentRepository;
import org.onap.cps.spi.utils.SessionManager;
import org.onap.cps.utils.JsonObjectMapper;
-import org.onap.cps.yang.YangTextSchemaSourceSetBuilder;
-import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.stereotype.Service;
public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService {
private final DataspaceRepository dataspaceRepository;
-
private final AnchorRepository anchorRepository;
-
private final FragmentRepository fragmentRepository;
-
private final JsonObjectMapper jsonObjectMapper;
-
private final SessionManager sessionManager;
+ private final FragmentNativeRepository fragmentNativeRepositoryImpl;
private static final String REG_EX_FOR_OPTIONAL_LIST_INDEX = "(\\[@[\\s\\S]+?]){0,1})";
- private static final Pattern REG_EX_PATTERN_FOR_LIST_ELEMENT_KEY_PREDICATE =
- Pattern.compile("\\[(\\@([^\\/]{0,9999}))\\]$");
@Override
public void addChildDataNode(final String dataspaceName, final String anchorName, final String parentNodeXpath,
addNewChildDataNode(dataspaceName, anchorName, parentNodeXpath, newChildDataNode);
}
+ @Override
+ public void addChildDataNodes(final String dataspaceName, final String anchorName,
+ final String parentNodeXpath, final Collection<DataNode> dataNodes) {
+ addChildrenDataNodes(dataspaceName, anchorName, parentNodeXpath, dataNodes);
+ }
+
@Override
public void addListElements(final String dataspaceName, final String anchorName, final String parentNodeXpath,
final Collection<DataNode> newListElements) {
@Override
public void addMultipleLists(final String dataspaceName, final String anchorName, final String parentNodeXpath,
- final Collection<Collection<DataNode>> newLists) {
+ final Collection<Collection<DataNode>> newLists) {
final Collection<String> failedXpaths = new HashSet<>();
newLists.forEach(newList -> {
try {
}
private void addNewChildDataNode(final String dataspaceName, final String anchorName,
- final String parentNodeXpath, final DataNode newChild) {
- final FragmentEntity parentFragmentEntity = getFragmentByXpath(dataspaceName, anchorName, parentNodeXpath);
+ final String parentNodeXpath, final DataNode newChild) {
+ final FragmentEntity parentFragmentEntity =
+ getFragmentWithoutDescendantsByXpath(dataspaceName, anchorName, parentNodeXpath);
final FragmentEntity newChildAsFragmentEntity =
convertToFragmentWithAllDescendants(parentFragmentEntity.getDataspace(),
parentFragmentEntity.getAnchor(), newChild);
}
private void addChildrenDataNodes(final String dataspaceName, final String anchorName, final String parentNodeXpath,
- final Collection<DataNode> newChildren) {
- final FragmentEntity parentFragmentEntity = getFragmentByXpath(dataspaceName, anchorName, parentNodeXpath);
+ final Collection<DataNode> newChildren) {
+ final FragmentEntity parentFragmentEntity =
+ getFragmentWithoutDescendantsByXpath(dataspaceName, anchorName, parentNodeXpath);
final List<FragmentEntity> fragmentEntities = new ArrayList<>(newChildren.size());
try {
newChildren.forEach(newChildAsDataNode -> {
}
private void retrySavingEachChildIndividually(final String dataspaceName, final String anchorName,
- final String parentNodeXpath, final Collection<DataNode> newChildren) {
+ final String parentNodeXpath,
+ final Collection<DataNode> newChildren) {
final Collection<String> failedXpaths = new HashSet<>();
for (final DataNode newChild : newChildren) {
try {
@Override
public void storeDataNode(final String dataspaceName, final String anchorName, final DataNode dataNode) {
+ storeDataNodes(dataspaceName, anchorName, Collections.singletonList(dataNode));
+ }
+
+ @Override
+ public void storeDataNodes(final String dataspaceName, final String anchorName,
+ final Collection<DataNode> dataNodes) {
final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
final AnchorEntity anchorEntity = anchorRepository.getByDataspaceAndName(dataspaceEntity, anchorName);
- final FragmentEntity fragmentEntity = convertToFragmentWithAllDescendants(dataspaceEntity, anchorEntity,
- dataNode);
+ final List<FragmentEntity> fragmentEntities = new ArrayList<>(dataNodes.size());
try {
- fragmentRepository.save(fragmentEntity);
+ for (final DataNode dataNode: dataNodes) {
+ final FragmentEntity fragmentEntity = convertToFragmentWithAllDescendants(dataspaceEntity, anchorEntity,
+ dataNode);
+ fragmentEntities.add(fragmentEntity);
+ }
+ fragmentRepository.saveAll(fragmentEntities);
} catch (final DataIntegrityViolationException exception) {
- throw AlreadyDefinedException.forDataNode(dataNode.getXpath(), anchorName, exception);
+ log.warn("Exception occurred : {} , While saving : {} data nodes, Retrying saving data nodes individually",
+ exception, dataNodes.size());
+ storeDataNodesIndividually(dataspaceName, anchorName, dataNodes);
+ }
+ }
+
+ private void storeDataNodesIndividually(final String dataspaceName, final String anchorName,
+ final Collection<DataNode> dataNodes) {
+ final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
+ final AnchorEntity anchorEntity = anchorRepository.getByDataspaceAndName(dataspaceEntity, anchorName);
+ final Collection<String> failedXpaths = new HashSet<>();
+ for (final DataNode dataNode: dataNodes) {
+ try {
+ final FragmentEntity fragmentEntity = convertToFragmentWithAllDescendants(dataspaceEntity, anchorEntity,
+ dataNode);
+ fragmentRepository.save(fragmentEntity);
+ } catch (final DataIntegrityViolationException e) {
+ failedXpaths.add(dataNode.getXpath());
+ }
+ }
+ if (!failedXpaths.isEmpty()) {
+ throw new AlreadyDefinedExceptionBatch(failedXpaths);
}
}
* @return a Fragment built from current DataNode
*/
private FragmentEntity convertToFragmentWithAllDescendants(final DataspaceEntity dataspaceEntity,
- final AnchorEntity anchorEntity, final DataNode dataNodeToBeConverted) {
+ final AnchorEntity anchorEntity,
+ final DataNode dataNodeToBeConverted) {
final FragmentEntity parentFragment = toFragmentEntity(dataspaceEntity, anchorEntity, dataNodeToBeConverted);
final Builder<FragmentEntity> childFragmentsImmutableSetBuilder = ImmutableSet.builder();
for (final DataNode childDataNode : dataNodeToBeConverted.getChildDataNodes()) {
@Override
public DataNode getDataNode(final String dataspaceName, final String anchorName, final String xpath,
final FetchDescendantsOption fetchDescendantsOption) {
- final FragmentEntity fragmentEntity = getFragmentByXpath(dataspaceName, anchorName, xpath);
+ final FragmentEntity fragmentEntity = getFragmentByXpath(dataspaceName, anchorName, xpath,
+ fetchDescendantsOption);
return toDataNode(fragmentEntity, fetchDescendantsOption);
}
+ @Override
+ public Collection<DataNode> getDataNodes(final String dataspaceName, final String anchorName,
+ final Collection<String> xpaths,
+ final FetchDescendantsOption fetchDescendantsOption) {
+ final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
+ final AnchorEntity anchorEntity = anchorRepository.getByDataspaceAndName(dataspaceEntity, anchorName);
+
+ final Set<String> normalizedXpaths = new HashSet<>(xpaths.size());
+ for (final String xpath : xpaths) {
+ try {
+ normalizedXpaths.add(CpsPathUtil.getNormalizedXpath(xpath));
+ } catch (final PathParsingException e) {
+ log.warn("Error parsing xpath \"{}\" in getDataNodes: {}", xpath, e.getMessage());
+ }
+ }
+
+ final List<FragmentEntity> fragmentEntities =
+ fragmentRepository.findByAnchorAndMultipleCpsPaths(anchorEntity.getId(), normalizedXpaths);
+ return toDataNodes(fragmentEntities, fetchDescendantsOption);
+ }
+
+ private FragmentEntity getFragmentWithoutDescendantsByXpath(final String dataspaceName,
+ final String anchorName,
+ final String xpath) {
+ return getFragmentByXpath(dataspaceName, anchorName, xpath, FetchDescendantsOption.OMIT_DESCENDANTS);
+ }
+
private FragmentEntity getFragmentByXpath(final String dataspaceName, final String anchorName,
- final String xpath) {
+ final String xpath, final FetchDescendantsOption fetchDescendantsOption) {
final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
final AnchorEntity anchorEntity = anchorRepository.getByDataspaceAndName(dataspaceEntity, anchorName);
+ final FragmentEntity fragmentEntity;
if (isRootXpath(xpath)) {
- return fragmentRepository.findFirstRootByDataspaceAndAnchor(dataspaceEntity, anchorEntity);
+ final List<FragmentExtract> fragmentExtracts = fragmentRepository.getTopLevelFragments(dataspaceEntity,
+ anchorEntity);
+ fragmentEntity = FragmentEntityArranger.toFragmentEntityTrees(anchorEntity, fragmentExtracts)
+ .stream().findFirst().orElse(null);
} else {
- final String normalizedXpath;
- try {
- normalizedXpath = CpsPathUtil.getNormalizedXpath(xpath);
- } catch (final PathParsingException e) {
- throw new CpsPathException(e.getMessage());
+ final String normalizedXpath = getNormalizedXpath(xpath);
+ if (FetchDescendantsOption.OMIT_DESCENDANTS.equals(fetchDescendantsOption)) {
+ fragmentEntity =
+ fragmentRepository.getByDataspaceAndAnchorAndXpath(dataspaceEntity, anchorEntity, normalizedXpath);
+ } else {
+ fragmentEntity = buildFragmentEntitiesFromFragmentExtracts(anchorEntity, normalizedXpath)
+ .stream().findFirst().orElse(null);
}
- return fragmentRepository.getByDataspaceAndAnchorAndXpath(dataspaceEntity, anchorEntity,
- normalizedXpath);
}
+ if (fragmentEntity == null) {
+ throw new DataNodeNotFoundException(dataspaceEntity.getName(), anchorEntity.getName(), xpath);
+ }
+ return fragmentEntity;
+
+ }
+
+ private Collection<FragmentEntity> buildFragmentEntitiesFromFragmentExtracts(final AnchorEntity anchorEntity,
+ final String normalizedXpath) {
+ final List<FragmentExtract> fragmentExtracts =
+ fragmentRepository.findByAnchorIdAndParentXpath(anchorEntity.getId(), normalizedXpath);
+ log.debug("Fetched {} fragment entities by anchor {} and cps path {}.",
+ fragmentExtracts.size(), anchorEntity.getName(), normalizedXpath);
+ return FragmentEntityArranger.toFragmentEntityTrees(anchorEntity, fragmentExtracts);
+
}
@Override
} catch (final PathParsingException e) {
throw new CpsPathException(e.getMessage());
}
- List<FragmentEntity> fragmentEntities =
- fragmentRepository.findByAnchorAndCpsPath(anchorEntity.getId(), cpsPathQuery);
+
+ Collection<FragmentEntity> fragmentEntities;
+ if (canUseRegexQuickFind(fetchDescendantsOption, cpsPathQuery)) {
+ return getDataNodesUsingRegexQuickFind(fetchDescendantsOption, anchorEntity, cpsPathQuery);
+ }
+ fragmentEntities = fragmentRepository.findByAnchorAndCpsPath(anchorEntity.getId(), cpsPathQuery);
+ if (cpsPathQuery.hasAncestorAxis()) {
+ fragmentEntities = getAncestorFragmentEntities(anchorEntity.getId(), cpsPathQuery, fragmentEntities);
+ }
+ return createDataNodesFromProxiedFragmentEntities(fetchDescendantsOption, anchorEntity, fragmentEntities);
+ }
+
+ private static boolean canUseRegexQuickFind(final FetchDescendantsOption fetchDescendantsOption,
+ final CpsPathQuery cpsPathQuery) {
+ return fetchDescendantsOption.equals(FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS)
+ && !cpsPathQuery.hasLeafConditions()
+ && !cpsPathQuery.hasTextFunctionCondition();
+ }
+
+ private List<DataNode> getDataNodesUsingRegexQuickFind(final FetchDescendantsOption fetchDescendantsOption,
+ final AnchorEntity anchorEntity,
+ final CpsPathQuery cpsPathQuery) {
+ Collection<FragmentEntity> fragmentEntities;
+ final String xpathRegex = FragmentQueryBuilder.getXpathSqlRegex(cpsPathQuery, true);
+ final List<FragmentExtract> fragmentExtracts =
+ fragmentRepository.quickFindWithDescendants(anchorEntity.getId(), xpathRegex);
+ fragmentEntities = FragmentEntityArranger.toFragmentEntityTrees(anchorEntity, fragmentExtracts);
if (cpsPathQuery.hasAncestorAxis()) {
- final Set<String> ancestorXpaths = processAncestorXpath(fragmentEntities, cpsPathQuery);
- fragmentEntities = ancestorXpaths.isEmpty() ? Collections.emptyList()
- : fragmentRepository.findAllByAnchorAndXpathIn(anchorEntity, ancestorXpaths);
+ fragmentEntities = getAncestorFragmentEntities(anchorEntity.getId(), cpsPathQuery, fragmentEntities);
}
- return fragmentEntities.stream()
- .map(fragmentEntity -> toDataNode(fragmentEntity, fetchDescendantsOption))
- .collect(Collectors.toUnmodifiableList());
+ return createDataNodesFromFragmentEntities(fetchDescendantsOption, fragmentEntities);
+ }
+
+ private Collection<FragmentEntity> getAncestorFragmentEntities(final int anchorId,
+ final CpsPathQuery cpsPathQuery,
+ final Collection<FragmentEntity> fragmentEntities) {
+ final Collection<String> ancestorXpaths = processAncestorXpath(fragmentEntities, cpsPathQuery);
+ return ancestorXpaths.isEmpty() ? Collections.emptyList()
+ : fragmentRepository.findByAnchorAndMultipleCpsPaths(anchorId, ancestorXpaths);
+ }
+
+ private List<DataNode> createDataNodesFromProxiedFragmentEntities(
+ final FetchDescendantsOption fetchDescendantsOption,
+ final AnchorEntity anchorEntity,
+ final Collection<FragmentEntity> proxiedFragmentEntities) {
+ final List<DataNode> dataNodes = new ArrayList<>(proxiedFragmentEntities.size());
+ for (final FragmentEntity proxiedFragmentEntity : proxiedFragmentEntities) {
+ if (FetchDescendantsOption.OMIT_DESCENDANTS.equals(fetchDescendantsOption)) {
+ dataNodes.add(toDataNode(proxiedFragmentEntity, fetchDescendantsOption));
+ } else {
+ final String normalizedXpath = getNormalizedXpath(proxiedFragmentEntity.getXpath());
+ final Collection<FragmentEntity> unproxiedFragmentEntities =
+ buildFragmentEntitiesFromFragmentExtracts(anchorEntity, normalizedXpath);
+ for (final FragmentEntity unproxiedFragmentEntity : unproxiedFragmentEntities) {
+ dataNodes.add(toDataNode(unproxiedFragmentEntity, fetchDescendantsOption));
+ }
+ }
+ }
+ return Collections.unmodifiableList(dataNodes);
+ }
+
+ private List<DataNode> createDataNodesFromFragmentEntities(final FetchDescendantsOption fetchDescendantsOption,
+ final Collection<FragmentEntity> fragmentEntities) {
+ final List<DataNode> dataNodes = new ArrayList<>(fragmentEntities.size());
+ for (final FragmentEntity fragmentEntity : fragmentEntities) {
+ dataNodes.add(toDataNode(fragmentEntity, fetchDescendantsOption));
+ }
+ return Collections.unmodifiableList(dataNodes);
+ }
+
+ private static String getNormalizedXpath(final String xpathSource) {
+ final String normalizedXpath;
+ try {
+ normalizedXpath = CpsPathUtil.getNormalizedXpath(xpathSource);
+ } catch (final PathParsingException e) {
+ throw new CpsPathException(e.getMessage());
+ }
+ return normalizedXpath;
}
@Override
sessionManager.lockAnchor(sessionId, dataspaceName, anchorName, timeoutInMilliseconds);
}
- private static Set<String> processAncestorXpath(final List<FragmentEntity> fragmentEntities,
+ private static Set<String> processAncestorXpath(final Collection<FragmentEntity> fragmentEntities,
final CpsPathQuery cpsPathQuery) {
final Set<String> ancestorXpath = new HashSet<>();
final Pattern pattern =
private DataNode toDataNode(final FragmentEntity fragmentEntity,
final FetchDescendantsOption fetchDescendantsOption) {
final List<DataNode> childDataNodes = getChildDataNodes(fragmentEntity, fetchDescendantsOption);
- Map<String, Object> leaves = new HashMap<>();
+ Map<String, Serializable> leaves = new HashMap<>();
if (fragmentEntity.getAttributes() != null) {
leaves = jsonObjectMapper.convertJsonString(fragmentEntity.getAttributes(), Map.class);
}
return new DataNodeBuilder()
- .withModuleNamePrefix(getFirstModuleName(fragmentEntity))
.withXpath(fragmentEntity.getXpath())
.withLeaves(leaves)
.withChildDataNodes(childDataNodes).build();
}
- private String getFirstModuleName(final FragmentEntity fragmentEntity) {
- final SchemaSetEntity schemaSetEntity = fragmentEntity.getAnchor().getSchemaSet();
- final Map<String, String> yangResourceNameToContent =
- schemaSetEntity.getYangResources().stream().collect(
- Collectors.toMap(YangResourceEntity::getFileName, YangResourceEntity::getContent));
- final SchemaContext schemaContext = YangTextSchemaSourceSetBuilder.of(yangResourceNameToContent)
- .getSchemaContext();
- return schemaContext.getModules().iterator().next().getName();
+ private Collection<DataNode> toDataNodes(final Collection<FragmentEntity> fragmentEntities,
+ final FetchDescendantsOption fetchDescendantsOption) {
+ final Collection<DataNode> dataNodes = new ArrayList<>(fragmentEntities.size());
+ for (final FragmentEntity fragmentEntity : fragmentEntities) {
+ dataNodes.add(toDataNode(fragmentEntity, fetchDescendantsOption));
+ }
+ return dataNodes;
}
private List<DataNode> getChildDataNodes(final FragmentEntity fragmentEntity,
final FetchDescendantsOption fetchDescendantsOption) {
- if (fetchDescendantsOption == INCLUDE_ALL_DESCENDANTS) {
+ if (fetchDescendantsOption.hasNext()) {
return fragmentEntity.getChildFragments().stream()
- .map(childFragmentEntity -> toDataNode(childFragmentEntity, fetchDescendantsOption))
- .collect(Collectors.toUnmodifiableList());
+ .map(childFragmentEntity -> toDataNode(childFragmentEntity, fetchDescendantsOption.next()))
+ .collect(Collectors.toList());
}
return Collections.emptyList();
}
@Override
public void updateDataLeaves(final String dataspaceName, final String anchorName, final String xpath,
- final Map<String, Object> leaves) {
- final FragmentEntity fragmentEntity = getFragmentByXpath(dataspaceName, anchorName, xpath);
- fragmentEntity.setAttributes(jsonObjectMapper.asJsonString(leaves));
+ final Map<String, Serializable> updateLeaves) {
+ final FragmentEntity fragmentEntity = getFragmentWithoutDescendantsByXpath(dataspaceName, anchorName, xpath);
+ final String currentLeavesAsString = fragmentEntity.getAttributes();
+ final String mergedLeaves = mergeLeaves(updateLeaves, currentLeavesAsString);
+ fragmentEntity.setAttributes(mergedLeaves);
fragmentRepository.save(fragmentEntity);
}
@Override
public void updateDataNodeAndDescendants(final String dataspaceName, final String anchorName,
final DataNode dataNode) {
- final FragmentEntity fragmentEntity = getFragmentByXpath(dataspaceName, anchorName, dataNode.getXpath());
+ final FragmentEntity fragmentEntity =
+ getFragmentWithoutDescendantsByXpath(dataspaceName, anchorName, dataNode.getXpath());
updateFragmentEntityAndDescendantsWithDataNode(fragmentEntity, dataNode);
try {
fragmentRepository.save(fragmentEntity);
} catch (final StaleStateException staleStateException) {
throw new ConcurrencyException("Concurrent Transactions",
String.format("dataspace :'%s', Anchor : '%s' and xpath: '%s' is updated by another transaction.",
- dataspaceName, anchorName, dataNode.getXpath()),
- staleStateException);
+ dataspaceName, anchorName, dataNode.getXpath()));
}
}
public void updateDataNodesAndDescendants(final String dataspaceName,
final String anchorName,
final List<DataNode> dataNodes) {
+
final Map<DataNode, FragmentEntity> dataNodeFragmentEntityMap = dataNodes.stream()
- .collect(Collectors.toMap(
- dataNode -> dataNode, dataNode -> getFragmentByXpath(dataspaceName, anchorName, dataNode.getXpath())));
+ .collect(Collectors.toMap(
+ dataNode -> dataNode,
+ dataNode ->
+ getFragmentWithoutDescendantsByXpath(dataspaceName, anchorName, dataNode.getXpath())));
dataNodeFragmentEntityMap.forEach(
- (dataNode, fragmentEntity) -> updateFragmentEntityAndDescendantsWithDataNode(fragmentEntity, dataNode));
+ (dataNode, fragmentEntity) -> updateFragmentEntityAndDescendantsWithDataNode(fragmentEntity, dataNode));
try {
fragmentRepository.saveAll(dataNodeFragmentEntityMap.values());
} catch (final StaleStateException staleStateException) {
- throw new ConcurrencyException("Concurrent Transactions",
- String.format("A data node in dataspace :'%s' with Anchor : '%s' is updated by another transaction.",
- dataspaceName, anchorName),
- staleStateException);
+ retryUpdateDataNodesIndividually(dataspaceName, anchorName, dataNodeFragmentEntityMap.values());
+ }
+ }
+
+ private void retryUpdateDataNodesIndividually(final String dataspaceName, final String anchorName,
+ final Collection<FragmentEntity> fragmentEntities) {
+ final Collection<String> failedXpaths = new HashSet<>();
+
+ fragmentEntities.forEach(dataNodeFragment -> {
+ try {
+ fragmentRepository.save(dataNodeFragment);
+ } catch (final StaleStateException e) {
+ failedXpaths.add(dataNodeFragment.getXpath());
+ }
+ });
+
+ if (!failedXpaths.isEmpty()) {
+ final String failedXpathsConcatenated = String.join(",", failedXpaths);
+ throw new ConcurrencyException("Concurrent Transactions", String.format(
+ "DataNodes : %s in Dataspace :'%s' with Anchor : '%s' are updated by another transaction.",
+ failedXpathsConcatenated, dataspaceName, anchorName));
}
}
@Transactional
public void replaceListContent(final String dataspaceName, final String anchorName, final String parentNodeXpath,
final Collection<DataNode> newListElements) {
- final FragmentEntity parentEntity = getFragmentByXpath(dataspaceName, anchorName, parentNodeXpath);
+ final FragmentEntity parentEntity =
+ getFragmentWithoutDescendantsByXpath(dataspaceName, anchorName, parentNodeXpath);
final String listElementXpathPrefix = getListElementXpathPrefix(newListElements);
final Map<String, FragmentEntity> existingListElementFragmentEntitiesByXPath =
extractListElementFragmentEntitiesByXPath(parentEntity.getChildFragments(), listElementXpathPrefix);
final boolean onlySupportListNodeDeletion) {
final String parentNodeXpath;
FragmentEntity parentFragmentEntity = null;
- boolean targetDeleted = false;
+ boolean targetDeleted;
if (isRootXpath(targetXpath)) {
deleteDataNodes(dataspaceName, anchorName);
targetDeleted = true;
if (isRootContainerNodeXpath(targetXpath)) {
parentNodeXpath = targetXpath;
} else {
- parentNodeXpath = targetXpath.substring(0, targetXpath.lastIndexOf('/'));
+ parentNodeXpath = CpsPathUtil.getNormalizedParentXpath(targetXpath);
}
- parentFragmentEntity = getFragmentByXpath(dataspaceName, anchorName, parentNodeXpath);
- final String lastXpathElement = targetXpath.substring(targetXpath.lastIndexOf('/'));
- final boolean isListElement = REG_EX_PATTERN_FOR_LIST_ELEMENT_KEY_PREDICATE
- .matcher(lastXpathElement).find();
- if (isListElement) {
+ parentFragmentEntity = getFragmentWithoutDescendantsByXpath(dataspaceName, anchorName, parentNodeXpath);
+ if (CpsPathUtil.isPathToListElement(targetXpath)) {
targetDeleted = deleteDataNode(parentFragmentEntity, targetXpath);
} else {
targetDeleted = deleteAllListElements(parentFragmentEntity, targetXpath);
private boolean deleteDataNode(final FragmentEntity parentFragmentEntity, final String targetXpath) {
final String normalizedTargetXpath = CpsPathUtil.getNormalizedXpath(targetXpath);
if (parentFragmentEntity.getXpath().equals(normalizedTargetXpath)) {
- fragmentRepository.delete(parentFragmentEntity);
+ fragmentNativeRepositoryImpl.deleteFragmentEntity(parentFragmentEntity.getId());
return true;
}
if (parentFragmentEntity.getChildFragments()
private static boolean isRootXpath(final String xpath) {
return "/".equals(xpath) || "".equals(xpath);
}
+
+ private String mergeLeaves(final Map<String, Serializable> updateLeaves, final String currentLeavesAsString) {
+ final Map<String, Serializable> currentLeavesAsMap = currentLeavesAsString.isEmpty()
+ ? new HashMap<>() : jsonObjectMapper.convertJsonString(currentLeavesAsString, Map.class);
+ currentLeavesAsMap.putAll(updateLeaves);
+ if (currentLeavesAsMap.isEmpty()) {
+ return "";
+ }
+ return jsonObjectMapper.asJsonString(currentLeavesAsMap);
+ }
}