/*
* ============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.
+ * Modifications Copyright (C) 2022-2023 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 com.google.common.base.Strings;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSet.Builder;
+import io.micrometer.core.annotation.Timed;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
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.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.exceptions.CpsAdminException;
import org.onap.cps.spi.exceptions.CpsPathException;
import org.onap.cps.spi.exceptions.DataNodeNotFoundException;
+import org.onap.cps.spi.exceptions.DataNodeNotFoundExceptionBatch;
import org.onap.cps.spi.model.DataNode;
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.FragmentQueryBuilder;
import org.onap.cps.spi.repository.FragmentRepository;
import org.onap.cps.spi.utils.SessionManager;
import org.onap.cps.utils.JsonObjectMapper;
private final JsonObjectMapper jsonObjectMapper;
private final SessionManager sessionManager;
- private static final String REG_EX_FOR_OPTIONAL_LIST_INDEX = "(\\[@[\\s\\S]+?]){0,1})";
-
- @Override
- public void addChildDataNode(final String dataspaceName, final String anchorName, final String parentNodeXpath,
- final DataNode newChildDataNode) {
- addNewChildDataNode(dataspaceName, anchorName, parentNodeXpath, newChildDataNode);
- }
+ private static final String REG_EX_FOR_OPTIONAL_LIST_INDEX = "(\\[@.+?])?)";
+ private static final String QUERY_ACROSS_ANCHORS = null;
+ private static final AnchorEntity ALL_ANCHORS = null;
@Override
public void addChildDataNodes(final String dataspaceName, final String anchorName,
final String parentNodeXpath, final Collection<DataNode> dataNodes) {
- addChildrenDataNodes(dataspaceName, anchorName, parentNodeXpath, dataNodes);
+ final AnchorEntity anchorEntity = getAnchorEntity(dataspaceName, anchorName);
+ addChildrenDataNodes(anchorEntity, parentNodeXpath, dataNodes);
}
@Override
public void addListElements(final String dataspaceName, final String anchorName, final String parentNodeXpath,
final Collection<DataNode> newListElements) {
- addChildrenDataNodes(dataspaceName, anchorName, parentNodeXpath, newListElements);
+ final AnchorEntity anchorEntity = getAnchorEntity(dataspaceName, anchorName);
+ addChildrenDataNodes(anchorEntity, parentNodeXpath, newListElements);
}
@Override
public void addMultipleLists(final String dataspaceName, final String anchorName, final String parentNodeXpath,
final Collection<Collection<DataNode>> newLists) {
+ final AnchorEntity anchorEntity = getAnchorEntity(dataspaceName, anchorName);
final Collection<String> failedXpaths = new HashSet<>();
- newLists.forEach(newList -> {
+ for (final Collection<DataNode> newList : newLists) {
try {
- addChildrenDataNodes(dataspaceName, anchorName, parentNodeXpath, newList);
- } catch (final AlreadyDefinedExceptionBatch e) {
- failedXpaths.addAll(e.getAlreadyDefinedXpaths());
+ addChildrenDataNodes(anchorEntity, parentNodeXpath, newList);
+ } catch (final AlreadyDefinedException alreadyDefinedException) {
+ failedXpaths.addAll(alreadyDefinedException.getAlreadyDefinedObjectNames());
}
- });
-
+ }
if (!failedXpaths.isEmpty()) {
- throw new AlreadyDefinedExceptionBatch(failedXpaths);
+ throw AlreadyDefinedException.forDataNodes(failedXpaths, anchorEntity.getName());
}
-
}
- private void addNewChildDataNode(final String dataspaceName, final String anchorName,
- final String parentNodeXpath, final DataNode newChild) {
- final FragmentEntity parentFragmentEntity =
- getFragmentWithoutDescendantsByXpath(dataspaceName, anchorName, parentNodeXpath);
- final FragmentEntity newChildAsFragmentEntity =
- convertToFragmentWithAllDescendants(parentFragmentEntity.getDataspace(),
- parentFragmentEntity.getAnchor(), newChild);
+ private void addNewChildDataNode(final AnchorEntity anchorEntity, final String parentNodeXpath,
+ final DataNode newChild) {
+ final FragmentEntity parentFragmentEntity = getFragmentEntity(anchorEntity, parentNodeXpath);
+ final FragmentEntity newChildAsFragmentEntity = convertToFragmentWithAllDescendants(anchorEntity, newChild);
newChildAsFragmentEntity.setParentId(parentFragmentEntity.getId());
try {
fragmentRepository.save(newChildAsFragmentEntity);
- } catch (final DataIntegrityViolationException e) {
- throw AlreadyDefinedException.forDataNode(newChild.getXpath(), anchorName, e);
+ } catch (final DataIntegrityViolationException dataIntegrityViolationException) {
+ throw AlreadyDefinedException.forDataNodes(Collections.singletonList(newChild.getXpath()),
+ anchorEntity.getName());
}
-
}
- private void addChildrenDataNodes(final String dataspaceName, final String anchorName, final String parentNodeXpath,
+ private void addChildrenDataNodes(final AnchorEntity anchorEntity, final String parentNodeXpath,
final Collection<DataNode> newChildren) {
- final FragmentEntity parentFragmentEntity =
- getFragmentWithoutDescendantsByXpath(dataspaceName, anchorName, parentNodeXpath);
+ final FragmentEntity parentFragmentEntity = getFragmentEntity(anchorEntity, parentNodeXpath);
final List<FragmentEntity> fragmentEntities = new ArrayList<>(newChildren.size());
try {
- newChildren.forEach(newChildAsDataNode -> {
+ for (final DataNode newChildAsDataNode : newChildren) {
final FragmentEntity newChildAsFragmentEntity =
- convertToFragmentWithAllDescendants(parentFragmentEntity.getDataspace(),
- parentFragmentEntity.getAnchor(), newChildAsDataNode);
+ convertToFragmentWithAllDescendants(anchorEntity, newChildAsDataNode);
newChildAsFragmentEntity.setParentId(parentFragmentEntity.getId());
fragmentEntities.add(newChildAsFragmentEntity);
- });
+ }
fragmentRepository.saveAll(fragmentEntities);
- } catch (final DataIntegrityViolationException e) {
+ } catch (final DataIntegrityViolationException dataIntegrityViolationException) {
log.warn("Exception occurred : {} , While saving : {} children, retrying using individual save operations",
- e, fragmentEntities.size());
- retrySavingEachChildIndividually(dataspaceName, anchorName, parentNodeXpath, newChildren);
+ dataIntegrityViolationException, fragmentEntities.size());
+ retrySavingEachChildIndividually(anchorEntity, parentNodeXpath, newChildren);
}
}
- private void retrySavingEachChildIndividually(final String dataspaceName, final String anchorName,
- final String parentNodeXpath,
+ private void retrySavingEachChildIndividually(final AnchorEntity anchorEntity, final String parentNodeXpath,
final Collection<DataNode> newChildren) {
final Collection<String> failedXpaths = new HashSet<>();
for (final DataNode newChild : newChildren) {
try {
- addNewChildDataNode(dataspaceName, anchorName, parentNodeXpath, newChild);
- } catch (final AlreadyDefinedException e) {
+ addNewChildDataNode(anchorEntity, parentNodeXpath, newChild);
+ } catch (final AlreadyDefinedException alreadyDefinedException) {
failedXpaths.add(newChild.getXpath());
}
}
if (!failedXpaths.isEmpty()) {
- throw new AlreadyDefinedExceptionBatch(failedXpaths);
+ throw AlreadyDefinedException.forDataNodes(failedXpaths, anchorEntity.getName());
}
}
- @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 AnchorEntity anchorEntity = getAnchorEntity(dataspaceName, anchorName);
final List<FragmentEntity> fragmentEntities = new ArrayList<>(dataNodes.size());
try {
for (final DataNode dataNode: dataNodes) {
- final FragmentEntity fragmentEntity = convertToFragmentWithAllDescendants(dataspaceEntity, anchorEntity,
- dataNode);
+ final FragmentEntity fragmentEntity = convertToFragmentWithAllDescendants(anchorEntity, dataNode);
fragmentEntities.add(fragmentEntity);
}
fragmentRepository.saveAll(fragmentEntities);
} catch (final DataIntegrityViolationException exception) {
log.warn("Exception occurred : {} , While saving : {} data nodes, Retrying saving data nodes individually",
exception, dataNodes.size());
- storeDataNodesIndividually(dataspaceName, anchorName, dataNodes);
+ storeDataNodesIndividually(anchorEntity, 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);
+ private void storeDataNodesIndividually(final AnchorEntity anchorEntity, final Collection<DataNode> dataNodes) {
final Collection<String> failedXpaths = new HashSet<>();
for (final DataNode dataNode: dataNodes) {
try {
- final FragmentEntity fragmentEntity = convertToFragmentWithAllDescendants(dataspaceEntity, anchorEntity,
- dataNode);
+ final FragmentEntity fragmentEntity = convertToFragmentWithAllDescendants(anchorEntity, dataNode);
fragmentRepository.save(fragmentEntity);
- } catch (final DataIntegrityViolationException e) {
+ } catch (final DataIntegrityViolationException dataIntegrityViolationException) {
failedXpaths.add(dataNode.getXpath());
}
}
if (!failedXpaths.isEmpty()) {
- throw new AlreadyDefinedExceptionBatch(failedXpaths);
+ throw AlreadyDefinedException.forDataNodes(failedXpaths, anchorEntity.getName());
}
}
* Convert DataNode object into Fragment and places the result in the fragments placeholder. Performs same action
* for all DataNode children recursively.
*
- * @param dataspaceEntity dataspace
* @param anchorEntity anchorEntity
* @param dataNodeToBeConverted dataNode
* @return a Fragment built from current DataNode
*/
- private FragmentEntity convertToFragmentWithAllDescendants(final DataspaceEntity dataspaceEntity,
- final AnchorEntity anchorEntity,
+ private FragmentEntity convertToFragmentWithAllDescendants(final AnchorEntity anchorEntity,
final DataNode dataNodeToBeConverted) {
- final FragmentEntity parentFragment = toFragmentEntity(dataspaceEntity, anchorEntity, dataNodeToBeConverted);
+ final FragmentEntity parentFragment = toFragmentEntity(anchorEntity, dataNodeToBeConverted);
final Builder<FragmentEntity> childFragmentsImmutableSetBuilder = ImmutableSet.builder();
for (final DataNode childDataNode : dataNodeToBeConverted.getChildDataNodes()) {
- final FragmentEntity childFragment =
- convertToFragmentWithAllDescendants(parentFragment.getDataspace(), parentFragment.getAnchor(),
- childDataNode);
+ final FragmentEntity childFragment = convertToFragmentWithAllDescendants(anchorEntity, childDataNode);
childFragmentsImmutableSetBuilder.add(childFragment);
}
parentFragment.setChildFragments(childFragmentsImmutableSetBuilder.build());
return parentFragment;
}
- private FragmentEntity toFragmentEntity(final DataspaceEntity dataspaceEntity,
- final AnchorEntity anchorEntity, final DataNode dataNode) {
+ private FragmentEntity toFragmentEntity(final AnchorEntity anchorEntity, final DataNode dataNode) {
return FragmentEntity.builder()
- .dataspace(dataspaceEntity)
.anchor(anchorEntity)
.xpath(dataNode.getXpath())
.attributes(jsonObjectMapper.asJsonString(dataNode.getLeaves()))
}
@Override
- public DataNode getDataNode(final String dataspaceName, final String anchorName, final String xpath,
- final FetchDescendantsOption fetchDescendantsOption) {
- final FragmentEntity fragmentEntity = getFragmentByXpath(dataspaceName, anchorName, xpath,
- fetchDescendantsOption);
- return toDataNode(fragmentEntity, fetchDescendantsOption);
+ @Timed(value = "cps.data.persistence.service.datanode.get",
+ description = "Time taken to get a data node")
+ public Collection<DataNode> getDataNodes(final String dataspaceName, final String anchorName,
+ final String xpath,
+ final FetchDescendantsOption fetchDescendantsOption) {
+ final String targetXpath = getNormalizedXpath(xpath);
+ final Collection<DataNode> dataNodes = getDataNodesForMultipleXpaths(dataspaceName, anchorName,
+ Collections.singletonList(targetXpath), fetchDescendantsOption);
+ if (dataNodes.isEmpty()) {
+ throw new DataNodeNotFoundException(dataspaceName, anchorName, xpath);
+ }
+ return dataNodes;
}
@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 List<FragmentEntity> fragmentEntities =
- fragmentRepository.findByAnchorAndMultipleCpsPaths(anchorEntity.getId(), xpaths);
- final Collection<DataNode> dataNodesCollection = new ArrayList<>(fragmentEntities.size());
+ @Timed(value = "cps.data.persistence.service.datanode.batch.get",
+ description = "Time taken to get data nodes")
+ public Collection<DataNode> getDataNodesForMultipleXpaths(final String dataspaceName, final String anchorName,
+ final Collection<String> xpaths,
+ final FetchDescendantsOption fetchDescendantsOption) {
+ final AnchorEntity anchorEntity = getAnchorEntity(dataspaceName, anchorName);
+ Collection<FragmentEntity> fragmentEntities = getFragmentEntities(anchorEntity, xpaths);
+ fragmentEntities = fragmentRepository.prefetchDescendantsOfFragmentEntities(fetchDescendantsOption,
+ fragmentEntities);
+ return createDataNodesFromFragmentEntities(fetchDescendantsOption, fragmentEntities);
+ }
+
+ private Collection<FragmentEntity> getFragmentEntities(final AnchorEntity anchorEntity,
+ final Collection<String> xpaths) {
+ final Collection<String> normalizedXpaths = getNormalizedXpaths(xpaths);
+
+ final boolean haveRootXpath = normalizedXpaths.removeIf(CpsDataPersistenceServiceImpl::isRootXpath);
+
+ final List<FragmentEntity> fragmentEntities = fragmentRepository.findByAnchorAndXpathIn(anchorEntity,
+ normalizedXpaths);
+
for (final FragmentEntity fragmentEntity : fragmentEntities) {
- dataNodesCollection.add(toDataNode(fragmentEntity, fetchDescendantsOption));
+ normalizedXpaths.remove(fragmentEntity.getXpath());
+ }
+
+ for (final String xpath : normalizedXpaths) {
+ if (!CpsPathUtil.isPathToListElement(xpath)) {
+ fragmentEntities.addAll(fragmentRepository.findListByAnchorAndXpath(anchorEntity, xpath));
+ }
+ }
+
+ if (haveRootXpath) {
+ fragmentEntities.addAll(fragmentRepository.findRootsByAnchorId(anchorEntity.getId()));
}
- return dataNodesCollection;
- }
- private FragmentEntity getFragmentWithoutDescendantsByXpath(final String dataspaceName,
- final String anchorName,
- final String xpath) {
- return getFragmentByXpath(dataspaceName, anchorName, xpath, FetchDescendantsOption.OMIT_DESCENDANTS);
+ return fragmentEntities;
}
- private FragmentEntity getFragmentByXpath(final String dataspaceName, final String anchorName,
- final String xpath, final FetchDescendantsOption fetchDescendantsOption) {
- final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
- final AnchorEntity anchorEntity = anchorRepository.getByDataspaceAndName(dataspaceEntity, anchorName);
+ private FragmentEntity getFragmentEntity(final AnchorEntity anchorEntity, final String xpath) {
final FragmentEntity fragmentEntity;
if (isRootXpath(xpath)) {
- final List<FragmentExtract> fragmentExtracts = fragmentRepository.getTopLevelFragments(dataspaceEntity,
- anchorEntity);
- fragmentEntity = FragmentEntityArranger.toFragmentEntityTrees(anchorEntity, fragmentExtracts)
- .stream().findFirst().orElse(null);
+ fragmentEntity = fragmentRepository.findOneByAnchorId(anchorEntity.getId()).orElse(null);
} else {
- 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);
- }
+ fragmentEntity = fragmentRepository.getByAnchorAndXpath(anchorEntity, getNormalizedXpath(xpath));
}
if (fragmentEntity == null) {
- throw new DataNodeNotFoundException(dataspaceEntity.getName(), anchorEntity.getName(), xpath);
+ throw new DataNodeNotFoundException(anchorEntity.getDataspace().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
+ @Timed(value = "cps.data.persistence.service.datanode.query",
+ description = "Time taken to query data nodes")
public List<DataNode> queryDataNodes(final String dataspaceName, final String anchorName, final String cpsPath,
final FetchDescendantsOption fetchDescendantsOption) {
final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
- final AnchorEntity anchorEntity = anchorRepository.getByDataspaceAndName(dataspaceEntity, anchorName);
+ final AnchorEntity anchorEntity = Strings.isNullOrEmpty(anchorName) ? ALL_ANCHORS
+ : anchorRepository.getByDataspaceAndName(dataspaceEntity, anchorName);
final CpsPathQuery cpsPathQuery;
try {
cpsPathQuery = CpsPathUtil.getCpsPathQuery(cpsPath);
- } catch (final PathParsingException e) {
- throw new CpsPathException(e.getMessage());
+ } catch (final PathParsingException pathParsingException) {
+ throw new CpsPathException(pathParsingException.getMessage());
}
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);
+ if (anchorEntity == ALL_ANCHORS) {
+ fragmentEntities = fragmentRepository.findByDataspaceAndCpsPath(dataspaceEntity, cpsPathQuery);
+ } else {
+ fragmentEntities = fragmentRepository.findByAnchorAndCpsPath(anchorEntity, cpsPathQuery);
}
- 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()) {
- fragmentEntities = getAncestorFragmentEntities(anchorEntity.getId(), cpsPathQuery, fragmentEntities);
+ final Collection<String> ancestorXpaths = processAncestorXpath(fragmentEntities, cpsPathQuery);
+ if (anchorEntity == ALL_ANCHORS) {
+ fragmentEntities = fragmentRepository.findByDataspaceAndXpathIn(dataspaceEntity, ancestorXpaths);
+ } else {
+ fragmentEntities = fragmentRepository.findByAnchorAndXpathIn(anchorEntity, ancestorXpaths);
+ }
}
+ fragmentEntities = fragmentRepository.prefetchDescendantsOfFragmentEntities(fetchDescendantsOption,
+ fragmentEntities);
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);
+ @Override
+ public List<DataNode> queryDataNodesAcrossAnchors(final String dataspaceName, final String cpsPath,
+ final FetchDescendantsOption fetchDescendantsOption) {
+ return queryDataNodes(dataspaceName, QUERY_ACROSS_ANCHORS, cpsPath, fetchDescendantsOption);
}
private List<DataNode> createDataNodesFromFragmentEntities(final FetchDescendantsOption fetchDescendantsOption,
}
private static String getNormalizedXpath(final String xpathSource) {
- final String normalizedXpath;
+ if (isRootXpath(xpathSource)) {
+ return xpathSource;
+ }
try {
- normalizedXpath = CpsPathUtil.getNormalizedXpath(xpathSource);
- } catch (final PathParsingException e) {
- throw new CpsPathException(e.getMessage());
+ return CpsPathUtil.getNormalizedXpath(xpathSource);
+ } catch (final PathParsingException pathParsingException) {
+ throw new CpsPathException(pathParsingException.getMessage());
+ }
+ }
+
+ private static Collection<String> getNormalizedXpaths(final Collection<String> xpaths) {
+ final Collection<String> normalizedXpaths = new HashSet<>(xpaths.size());
+ for (final String xpath : xpaths) {
+ try {
+ normalizedXpaths.add(getNormalizedXpath(xpath));
+ } catch (final CpsPathException cpsPathException) {
+ log.warn("Error parsing xpath \"{}\": {}", xpath, cpsPathException.getMessage());
+ }
}
- return normalizedXpath;
+ return normalizedXpaths;
}
@Override
final CpsPathQuery cpsPathQuery) {
final Set<String> ancestorXpath = new HashSet<>();
final Pattern pattern =
- Pattern.compile("([\\s\\S]*\\/" + Pattern.quote(cpsPathQuery.getAncestorSchemaNodeIdentifier())
- + REG_EX_FOR_OPTIONAL_LIST_INDEX + "\\/[\\s\\S]*");
+ Pattern.compile("(.*/" + Pattern.quote(cpsPathQuery.getAncestorSchemaNodeIdentifier())
+ + REG_EX_FOR_OPTIONAL_LIST_INDEX + "/.*");
for (final FragmentEntity fragmentEntity : fragmentEntities) {
final Matcher matcher = pattern.matcher(fragmentEntity.getXpath());
if (matcher.matches()) {
return new DataNodeBuilder()
.withXpath(fragmentEntity.getXpath())
.withLeaves(leaves)
+ .withDataspace(fragmentEntity.getAnchor().getDataspace().getName())
+ .withAnchor(fragmentEntity.getAnchor().getName())
.withChildDataNodes(childDataNodes).build();
}
}
@Override
- public void updateDataLeaves(final String dataspaceName, final String anchorName, final String xpath,
- final Map<String, Serializable> leaves) {
- final FragmentEntity fragmentEntity = getFragmentWithoutDescendantsByXpath(dataspaceName, anchorName, xpath);
- fragmentEntity.setAttributes(jsonObjectMapper.asJsonString(leaves));
- fragmentRepository.save(fragmentEntity);
- }
+ public void batchUpdateDataLeaves(final String dataspaceName, final String anchorName,
+ final Map<String, Map<String, Serializable>> updatedLeavesPerXPath) {
+ final AnchorEntity anchorEntity = getAnchorEntity(dataspaceName, anchorName);
+
+ final Collection<String> xpathsOfUpdatedLeaves = updatedLeavesPerXPath.keySet();
+ final Collection<FragmentEntity> fragmentEntities = getFragmentEntities(anchorEntity, xpathsOfUpdatedLeaves);
+
+ for (final FragmentEntity fragmentEntity : fragmentEntities) {
+ final Map<String, Serializable> updatedLeaves = updatedLeavesPerXPath.get(fragmentEntity.getXpath());
+ final String mergedLeaves = mergeLeaves(updatedLeaves, fragmentEntity.getAttributes());
+ fragmentEntity.setAttributes(mergedLeaves);
+ }
- @Override
- public void updateDataNodeAndDescendants(final String dataspaceName, final String anchorName,
- final DataNode dataNode) {
- final FragmentEntity fragmentEntity =
- getFragmentWithoutDescendantsByXpath(dataspaceName, anchorName, dataNode.getXpath());
- updateFragmentEntityAndDescendantsWithDataNode(fragmentEntity, dataNode);
try {
- fragmentRepository.save(fragmentEntity);
+ fragmentRepository.saveAll(fragmentEntities);
} 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()));
+ retryUpdateDataNodesIndividually(anchorEntity, fragmentEntities);
}
}
@Override
- 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 ->
- getFragmentWithoutDescendantsByXpath(dataspaceName, anchorName, dataNode.getXpath())));
- dataNodeFragmentEntityMap.forEach(
- (dataNode, fragmentEntity) -> updateFragmentEntityAndDescendantsWithDataNode(fragmentEntity, dataNode));
+ public void updateDataNodesAndDescendants(final String dataspaceName, final String anchorName,
+ final Collection<DataNode> updatedDataNodes) {
+ final AnchorEntity anchorEntity = getAnchorEntity(dataspaceName, anchorName);
+
+ final Map<String, DataNode> xpathToUpdatedDataNode = updatedDataNodes.stream()
+ .collect(Collectors.toMap(DataNode::getXpath, dataNode -> dataNode));
+
+ final Collection<String> xpaths = xpathToUpdatedDataNode.keySet();
+ Collection<FragmentEntity> existingFragmentEntities = getFragmentEntities(anchorEntity, xpaths);
+ existingFragmentEntities = fragmentRepository.prefetchDescendantsOfFragmentEntities(
+ FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS, existingFragmentEntities);
+
+ for (final FragmentEntity existingFragmentEntity : existingFragmentEntities) {
+ final DataNode updatedDataNode = xpathToUpdatedDataNode.get(existingFragmentEntity.getXpath());
+ updateFragmentEntityAndDescendantsWithDataNode(existingFragmentEntity, updatedDataNode);
+ }
+
try {
- fragmentRepository.saveAll(dataNodeFragmentEntityMap.values());
+ fragmentRepository.saveAll(existingFragmentEntities);
} catch (final StaleStateException staleStateException) {
- retryUpdateDataNodesIndividually(dataspaceName, anchorName, dataNodeFragmentEntityMap.values());
+ retryUpdateDataNodesIndividually(anchorEntity, existingFragmentEntities);
}
}
- private void retryUpdateDataNodesIndividually(final String dataspaceName, final String anchorName,
+ private void retryUpdateDataNodesIndividually(final AnchorEntity anchorEntity,
final Collection<FragmentEntity> fragmentEntities) {
final Collection<String> failedXpaths = new HashSet<>();
-
- fragmentEntities.forEach(dataNodeFragment -> {
+ for (final FragmentEntity dataNodeFragment : fragmentEntities) {
try {
fragmentRepository.save(dataNodeFragment);
- } catch (final StaleStateException e) {
+ } catch (final StaleStateException staleStateException) {
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));
+ failedXpathsConcatenated, anchorEntity.getDataspace().getName(), anchorEntity.getName()));
}
}
private void updateFragmentEntityAndDescendantsWithDataNode(final FragmentEntity existingFragmentEntity,
final DataNode newDataNode) {
-
existingFragmentEntity.setAttributes(jsonObjectMapper.asJsonString(newDataNode.getLeaves()));
final Map<String, FragmentEntity> existingChildrenByXpath = existingFragmentEntity.getChildFragments().stream()
.collect(Collectors.toMap(FragmentEntity::getXpath, childFragmentEntity -> childFragmentEntity));
final Collection<FragmentEntity> updatedChildFragments = new HashSet<>();
-
for (final DataNode newDataNodeChild : newDataNode.getChildDataNodes()) {
final FragmentEntity childFragment;
if (isNewDataNode(newDataNodeChild, existingChildrenByXpath)) {
- childFragment = convertToFragmentWithAllDescendants(
- existingFragmentEntity.getDataspace(), existingFragmentEntity.getAnchor(), newDataNodeChild);
+ childFragment = convertToFragmentWithAllDescendants(existingFragmentEntity.getAnchor(),
+ newDataNodeChild);
} else {
childFragment = existingChildrenByXpath.get(newDataNodeChild.getXpath());
updateFragmentEntityAndDescendantsWithDataNode(childFragment, newDataNodeChild);
@Transactional
public void replaceListContent(final String dataspaceName, final String anchorName, final String parentNodeXpath,
final Collection<DataNode> newListElements) {
- final FragmentEntity parentEntity =
- getFragmentWithoutDescendantsByXpath(dataspaceName, anchorName, parentNodeXpath);
+ final AnchorEntity anchorEntity = getAnchorEntity(dataspaceName, anchorName);
+ final FragmentEntity parentEntity = getFragmentEntity(anchorEntity, parentNodeXpath);
final String listElementXpathPrefix = getListElementXpathPrefix(newListElements);
final Map<String, FragmentEntity> existingListElementFragmentEntitiesByXPath =
extractListElementFragmentEntitiesByXPath(parentEntity.getChildFragments(), listElementXpathPrefix);
- deleteListElements(parentEntity.getChildFragments(), existingListElementFragmentEntitiesByXPath);
+ parentEntity.getChildFragments().removeAll(existingListElementFragmentEntitiesByXPath.values());
final Set<FragmentEntity> updatedChildFragmentEntities = new HashSet<>();
for (final DataNode newListElement : newListElements) {
final FragmentEntity existingListElementEntity =
existingListElementFragmentEntitiesByXPath.get(newListElement.getXpath());
final FragmentEntity entityToBeAdded = getFragmentForReplacement(parentEntity, newListElement,
existingListElementEntity);
-
updatedChildFragmentEntities.add(entityToBeAdded);
}
parentEntity.getChildFragments().addAll(updatedChildFragmentEntities);
public void deleteDataNodes(final String dataspaceName, final String anchorName) {
final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
anchorRepository.findByDataspaceAndName(dataspaceEntity, anchorName)
- .ifPresent(
- anchorEntity -> fragmentRepository.deleteByAnchorIn(Set.of(anchorEntity)));
+ .ifPresent(anchorEntity -> fragmentRepository.deleteByAnchorIn(Collections.singletonList(anchorEntity)));
}
@Override
@Transactional
- public void deleteListDataNode(final String dataspaceName, final String anchorName,
- final String targetXpath) {
- deleteDataNode(dataspaceName, anchorName, targetXpath, true);
+ public void deleteDataNodes(final String dataspaceName, final Collection<String> anchorNames) {
+ final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
+ final Collection<AnchorEntity> anchorEntities =
+ anchorRepository.findAllByDataspaceAndNameIn(dataspaceEntity, anchorNames);
+ fragmentRepository.deleteByAnchorIn(anchorEntities);
}
@Override
@Transactional
- public void deleteDataNode(final String dataspaceName, final String anchorName, final String targetXpath) {
- deleteDataNode(dataspaceName, anchorName, targetXpath, false);
+ public void deleteDataNodes(final String dataspaceName, final String anchorName,
+ final Collection<String> xpathsToDelete) {
+ deleteDataNodes(dataspaceName, anchorName, xpathsToDelete, false);
}
- private void deleteDataNode(final String dataspaceName, final String anchorName, final String targetXpath,
- final boolean onlySupportListNodeDeletion) {
- final String parentNodeXpath;
- FragmentEntity parentFragmentEntity = null;
- boolean targetDeleted;
- if (isRootXpath(targetXpath)) {
+ private void deleteDataNodes(final String dataspaceName, final String anchorName,
+ final Collection<String> xpathsToDelete, final boolean onlySupportListDeletion) {
+ final boolean haveRootXpath = xpathsToDelete.stream().anyMatch(CpsDataPersistenceServiceImpl::isRootXpath);
+ if (haveRootXpath) {
deleteDataNodes(dataspaceName, anchorName);
- targetDeleted = true;
+ return;
+ }
+
+ final AnchorEntity anchorEntity = getAnchorEntity(dataspaceName, anchorName);
+
+ final Collection<String> deleteChecklist = getNormalizedXpaths(xpathsToDelete);
+ final Collection<String> xpathsToExistingContainers =
+ fragmentRepository.findAllXpathByAnchorAndXpathIn(anchorEntity, deleteChecklist);
+ if (onlySupportListDeletion) {
+ final Collection<String> xpathsToExistingListElements = xpathsToExistingContainers.stream()
+ .filter(CpsPathUtil::isPathToListElement).collect(Collectors.toList());
+ deleteChecklist.removeAll(xpathsToExistingListElements);
} else {
- if (isRootContainerNodeXpath(targetXpath)) {
- parentNodeXpath = targetXpath;
- } else {
- parentNodeXpath = CpsPathUtil.getNormalizedParentXpath(targetXpath);
- }
- parentFragmentEntity = getFragmentWithoutDescendantsByXpath(dataspaceName, anchorName, parentNodeXpath);
- if (CpsPathUtil.isPathToListElement(targetXpath)) {
- targetDeleted = deleteDataNode(parentFragmentEntity, targetXpath);
- } else {
- targetDeleted = deleteAllListElements(parentFragmentEntity, targetXpath);
- final boolean tryToDeleteDataNode = !targetDeleted && !onlySupportListNodeDeletion;
- if (tryToDeleteDataNode) {
- targetDeleted = deleteDataNode(parentFragmentEntity, targetXpath);
- }
- }
+ deleteChecklist.removeAll(xpathsToExistingContainers);
}
- if (!targetDeleted) {
- final String additionalInformation = onlySupportListNodeDeletion
- ? "The target is probably not a List." : "";
- throw new DataNodeNotFoundException(parentFragmentEntity.getDataspace().getName(),
- parentFragmentEntity.getAnchor().getName(), targetXpath, additionalInformation);
+
+ final Collection<String> xpathsToExistingLists = deleteChecklist.stream()
+ .filter(xpath -> fragmentRepository.existsByAnchorAndXpathStartsWith(anchorEntity, xpath + "["))
+ .collect(Collectors.toList());
+ deleteChecklist.removeAll(xpathsToExistingLists);
+
+ if (!deleteChecklist.isEmpty()) {
+ throw new DataNodeNotFoundExceptionBatch(dataspaceName, anchorName, deleteChecklist);
}
+
+ fragmentRepository.deleteByAnchorIdAndXpaths(anchorEntity.getId(), xpathsToExistingContainers);
+ fragmentRepository.deleteListsByAnchorIdAndXpaths(anchorEntity.getId(), xpathsToExistingLists);
}
- private boolean deleteDataNode(final FragmentEntity parentFragmentEntity, final String targetXpath) {
- final String normalizedTargetXpath = CpsPathUtil.getNormalizedXpath(targetXpath);
- if (parentFragmentEntity.getXpath().equals(normalizedTargetXpath)) {
- fragmentRepository.delete(parentFragmentEntity);
- return true;
- }
- if (parentFragmentEntity.getChildFragments()
- .removeIf(fragment -> fragment.getXpath().equals(normalizedTargetXpath))) {
- fragmentRepository.save(parentFragmentEntity);
- return true;
- }
- return false;
+ @Override
+ @Transactional
+ public void deleteListDataNode(final String dataspaceName, final String anchorName,
+ final String targetXpath) {
+ deleteDataNode(dataspaceName, anchorName, targetXpath, true);
}
- private boolean deleteAllListElements(final FragmentEntity parentFragmentEntity, final String listXpath) {
- final String normalizedListXpath = CpsPathUtil.getNormalizedXpath(listXpath);
- final String deleteTargetXpathPrefix = normalizedListXpath + "[";
- if (parentFragmentEntity.getChildFragments()
- .removeIf(fragment -> fragment.getXpath().startsWith(deleteTargetXpathPrefix))) {
- fragmentRepository.save(parentFragmentEntity);
- return true;
- }
- return false;
+ @Override
+ @Transactional
+ public void deleteDataNode(final String dataspaceName, final String anchorName, final String targetXpath) {
+ deleteDataNode(dataspaceName, anchorName, targetXpath, false);
}
- private static void deleteListElements(
- final Collection<FragmentEntity> fragmentEntities,
- final Map<String, FragmentEntity> existingListElementFragmentEntitiesByXPath) {
- fragmentEntities.removeAll(existingListElementFragmentEntitiesByXPath.values());
+ private void deleteDataNode(final String dataspaceName, final String anchorName, final String targetXpath,
+ final boolean onlySupportListNodeDeletion) {
+ final String normalizedXpath = getNormalizedXpath(targetXpath);
+ try {
+ deleteDataNodes(dataspaceName, anchorName, Collections.singletonList(normalizedXpath),
+ onlySupportListNodeDeletion);
+ } catch (final DataNodeNotFoundExceptionBatch dataNodeNotFoundExceptionBatch) {
+ throw new DataNodeNotFoundException(dataspaceName, anchorName, targetXpath);
+ }
}
private static String getListElementXpathPrefix(final Collection<DataNode> newListElements) {
final DataNode newListElement,
final FragmentEntity existingListElementEntity) {
if (existingListElementEntity == null) {
- return convertToFragmentWithAllDescendants(
- parentEntity.getDataspace(), parentEntity.getAnchor(), newListElement);
+ return convertToFragmentWithAllDescendants(parentEntity.getAnchor(), newListElement);
}
if (newListElement.getChildDataNodes().isEmpty()) {
copyAttributesFromNewListElement(existingListElementEntity, newListElement);
return !existingListElementsByXpath.containsKey(replacementDataNode.getXpath());
}
- private static boolean isRootContainerNodeXpath(final String xpath) {
- return 0 == xpath.lastIndexOf('/');
- }
-
private void copyAttributesFromNewListElement(final FragmentEntity existingListElementEntity,
final DataNode newListElement) {
final FragmentEntity replacementFragmentEntity =
private static boolean isRootXpath(final String xpath) {
return "/".equals(xpath) || "".equals(xpath);
}
+
+ private String mergeLeaves(final Map<String, Serializable> updateLeaves, final String currentLeavesAsString) {
+ Map<String, Serializable> currentLeavesAsMap = new HashMap<>();
+ if (currentLeavesAsString != null) {
+ currentLeavesAsMap = jsonObjectMapper.convertJsonString(currentLeavesAsString, Map.class);
+ currentLeavesAsMap.putAll(updateLeaves);
+ }
+
+ if (currentLeavesAsMap.isEmpty()) {
+ return "";
+ }
+ return jsonObjectMapper.asJsonString(currentLeavesAsMap);
+ }
+
+ private AnchorEntity getAnchorEntity(final String dataspaceName, final String anchorName) {
+ final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
+ return anchorRepository.getByDataspaceAndName(dataspaceEntity, anchorName);
+ }
}