diff options
Diffstat (limited to 'cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java')
-rw-r--r-- | cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java | 214 |
1 files changed, 165 insertions, 49 deletions
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java index ac1be1cd3..06ee8ecad 100644 --- a/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java +++ b/cps-ri/src/main/java/org/onap/cps/spi/impl/CpsDataPersistenceServiceImpl.java @@ -1,8 +1,9 @@ /* * ============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. @@ -24,6 +25,7 @@ package org.onap.cps.spi.impl; 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; @@ -59,6 +61,7 @@ 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; @@ -77,9 +80,6 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService private final SessionManager sessionManager; 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}))\\]$"); - private static final String TOP_LEVEL_MODULE_PREFIX_PROPERTY_NAME = "topLevelModulePrefix"; @Override public void addChildDataNode(final String dataspaceName, final String anchorName, final String parentNodeXpath, @@ -88,6 +88,12 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService } @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) { addChildrenDataNodes(dataspaceName, anchorName, parentNodeXpath, newListElements); @@ -166,14 +172,45 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService @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); } } @@ -219,6 +256,27 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService 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) { @@ -229,37 +287,37 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService 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)) { final List<FragmentExtract> fragmentExtracts = fragmentRepository.getTopLevelFragments(dataspaceEntity, anchorEntity); - final FragmentEntity fragmentEntity = FragmentEntityArranger.toFragmentEntityTree(anchorEntity, - fragmentExtracts); - return fragmentEntity; + fragmentEntity = FragmentEntityArranger.toFragmentEntityTrees(anchorEntity, fragmentExtracts) + .stream().findFirst().orElse(null); } else { final String normalizedXpath = getNormalizedXpath(xpath); - final FragmentEntity fragmentEntity; if (FetchDescendantsOption.OMIT_DESCENDANTS.equals(fetchDescendantsOption)) { fragmentEntity = fragmentRepository.getByDataspaceAndAnchorAndXpath(dataspaceEntity, anchorEntity, normalizedXpath); } else { - fragmentEntity = buildFragmentEntityFromFragmentExtracts(anchorEntity, normalizedXpath); - } - if (fragmentEntity == null) { - throw new DataNodeNotFoundException(dataspaceEntity.getName(), anchorEntity.getName(), xpath); + fragmentEntity = buildFragmentEntitiesFromFragmentExtracts(anchorEntity, normalizedXpath) + .stream().findFirst().orElse(null); } - return fragmentEntity; } + if (fragmentEntity == null) { + throw new DataNodeNotFoundException(dataspaceEntity.getName(), anchorEntity.getName(), xpath); + } + return fragmentEntity; + } - private FragmentEntity buildFragmentEntityFromFragmentExtracts(final AnchorEntity anchorEntity, - final String normalizedXpath) { - final FragmentEntity 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); - fragmentEntity = FragmentEntityArranger.toFragmentEntityTree(anchorEntity, fragmentExtracts); - return fragmentEntity; + return FragmentEntityArranger.toFragmentEntityTrees(anchorEntity, fragmentExtracts); + } @Override @@ -273,32 +331,72 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService } 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()) { - final Set<String> ancestorXpaths = processAncestorXpath(fragmentEntities, cpsPathQuery); - fragmentEntities = ancestorXpaths.isEmpty() ? Collections.emptyList() - : fragmentRepository.findAllByAnchorAndXpathIn(anchorEntity, ancestorXpaths); + fragmentEntities = getAncestorFragmentEntities(anchorEntity.getId(), cpsPathQuery, fragmentEntities); } - return createDataNodesFromFragmentEntities(fetchDescendantsOption, anchorEntity, - fragmentEntities); + return createDataNodesFromProxiedFragmentEntities(fetchDescendantsOption, anchorEntity, fragmentEntities); } - private List<DataNode> createDataNodesFromFragmentEntities(final FetchDescendantsOption fetchDescendantsOption, - final AnchorEntity anchorEntity, - final List<FragmentEntity> fragmentEntities) { - final List<DataNode> dataNodes = new ArrayList<>(fragmentEntities.size()); - for (final FragmentEntity proxiedFragmentEntity : fragmentEntities) { - final DataNode dataNode; + 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); + } + 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)) { - dataNode = toDataNode(proxiedFragmentEntity, fetchDescendantsOption); + dataNodes.add(toDataNode(proxiedFragmentEntity, fetchDescendantsOption)); } else { final String normalizedXpath = getNormalizedXpath(proxiedFragmentEntity.getXpath()); - final FragmentEntity unproxiedFragmentEntity = buildFragmentEntityFromFragmentExtracts(anchorEntity, - normalizedXpath); - dataNode = toDataNode(unproxiedFragmentEntity, fetchDescendantsOption); + final Collection<FragmentEntity> unproxiedFragmentEntities = + buildFragmentEntitiesFromFragmentExtracts(anchorEntity, normalizedXpath); + for (final FragmentEntity unproxiedFragmentEntity : unproxiedFragmentEntities) { + dataNodes.add(toDataNode(unproxiedFragmentEntity, fetchDescendantsOption)); + } } - dataNodes.add(dataNode); + } + 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); } @@ -329,7 +427,7 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService 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 = @@ -347,7 +445,7 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService 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); } @@ -357,6 +455,15 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService .withChildDataNodes(childDataNodes).build(); } + 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.hasNext()) { @@ -369,9 +476,11 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService @Override public void updateDataLeaves(final String dataspaceName, final String anchorName, final String xpath, - final Map<String, Object> leaves) { + final Map<String, Serializable> updateLeaves) { final FragmentEntity fragmentEntity = getFragmentWithoutDescendantsByXpath(dataspaceName, anchorName, xpath); - fragmentEntity.setAttributes(jsonObjectMapper.asJsonString(leaves)); + final String currentLeavesAsString = fragmentEntity.getAttributes(); + final String mergedLeaves = mergeLeaves(updateLeaves, currentLeavesAsString); + fragmentEntity.setAttributes(mergedLeaves); fragmentRepository.save(fragmentEntity); } @@ -512,13 +621,10 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService if (isRootContainerNodeXpath(targetXpath)) { parentNodeXpath = targetXpath; } else { - parentNodeXpath = targetXpath.substring(0, targetXpath.lastIndexOf('/')); + parentNodeXpath = CpsPathUtil.getNormalizedParentXpath(targetXpath); } parentFragmentEntity = getFragmentWithoutDescendantsByXpath(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) { + if (CpsPathUtil.isPathToListElement(targetXpath)) { targetDeleted = deleteDataNode(parentFragmentEntity, targetXpath); } else { targetDeleted = deleteAllListElements(parentFragmentEntity, targetXpath); @@ -619,4 +725,14 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService 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); + } } |