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.util.ArrayList;
import org.onap.cps.spi.entities.SchemaSetEntity;
import org.onap.cps.spi.entities.YangResourceEntity;
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;
Pattern.compile("\\[(\\@([^\\/]{0,9999}))\\]$");
@Override
- @Transactional
public void addChildDataNode(final String dataspaceName, final String anchorName, final String parentNodeXpath,
final DataNode newChildDataNode) {
- addChildDataNodes(dataspaceName, anchorName, parentNodeXpath, Collections.singleton(newChildDataNode));
+ addNewChildDataNode(dataspaceName, anchorName, parentNodeXpath, newChildDataNode);
}
@Override
- @Transactional
public void addListElements(final String dataspaceName, final String anchorName, final String parentNodeXpath,
final Collection<DataNode> newListElements) {
- addChildDataNodes(dataspaceName, anchorName, parentNodeXpath, newListElements);
+ addChildrenDataNodes(dataspaceName, anchorName, parentNodeXpath, newListElements);
+ }
+
+ @Override
+ public void addMultipleLists(final String dataspaceName, final String anchorName, final String parentNodeXpath,
+ final Collection<Collection<DataNode>> newLists) {
+ final Collection<String> failedXpaths = new HashSet<>();
+ newLists.forEach(newList -> {
+ try {
+ addChildrenDataNodes(dataspaceName, anchorName, parentNodeXpath, newList);
+ } catch (final AlreadyDefinedExceptionBatch e) {
+ failedXpaths.addAll(e.getAlreadyDefinedXpaths());
+ }
+ });
+
+ if (!failedXpaths.isEmpty()) {
+ throw new AlreadyDefinedExceptionBatch(failedXpaths);
+ }
+
}
- private void addChildDataNodes(final String dataspaceName, final String anchorName, final String parentNodeXpath,
- final Collection<DataNode> newChildren) {
+ private void addNewChildDataNode(final String dataspaceName, final String anchorName,
+ final String parentNodeXpath, final DataNode newChild) {
final FragmentEntity parentFragmentEntity = getFragmentByXpath(dataspaceName, anchorName, parentNodeXpath);
+ final FragmentEntity newChildAsFragmentEntity =
+ convertToFragmentWithAllDescendants(parentFragmentEntity.getDataspace(),
+ parentFragmentEntity.getAnchor(), newChild);
+ newChildAsFragmentEntity.setParentId(parentFragmentEntity.getId());
+ try {
+ fragmentRepository.save(newChildAsFragmentEntity);
+ } catch (final DataIntegrityViolationException e) {
+ throw AlreadyDefinedException.forDataNode(newChild.getXpath(), anchorName, e);
+ }
+
+ }
+
+ private void addChildrenDataNodes(final String dataspaceName, final String anchorName, final String parentNodeXpath,
+ final Collection<DataNode> newChildren) {
+ final FragmentEntity parentFragmentEntity = getFragmentByXpath(dataspaceName, anchorName, parentNodeXpath);
+ final List<FragmentEntity> fragmentEntities = new ArrayList<>(newChildren.size());
try {
- final List<FragmentEntity> fragmentEntities = new ArrayList<>();
newChildren.forEach(newChildAsDataNode -> {
- final FragmentEntity newChildAsFragmentEntity = convertToFragmentWithAllDescendants(
- parentFragmentEntity.getDataspace(),
- parentFragmentEntity.getAnchor(),
- newChildAsDataNode);
+ final FragmentEntity newChildAsFragmentEntity =
+ convertToFragmentWithAllDescendants(parentFragmentEntity.getDataspace(),
+ parentFragmentEntity.getAnchor(), newChildAsDataNode);
newChildAsFragmentEntity.setParentId(parentFragmentEntity.getId());
fragmentEntities.add(newChildAsFragmentEntity);
});
fragmentRepository.saveAll(fragmentEntities);
- } catch (final DataIntegrityViolationException exception) {
- final List<String> conflictXpaths = newChildren.stream()
- .map(DataNode::getXpath)
- .collect(Collectors.toList());
- throw AlreadyDefinedException.forDataNodes(conflictXpaths, anchorName, exception);
+ } catch (final DataIntegrityViolationException e) {
+ log.warn("Exception occurred : {} , While saving : {} children, retrying using individual save operations",
+ e, fragmentEntities.size());
+ retrySavingEachChildIndividually(dataspaceName, anchorName, parentNodeXpath, newChildren);
+ }
+ }
+
+ private void retrySavingEachChildIndividually(final String dataspaceName, final String anchorName,
+ 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) {
+ failedXpaths.add(newChild.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()) {
final DataspaceEntity dataspaceEntity = dataspaceRepository.getByName(dataspaceName);
final AnchorEntity anchorEntity = anchorRepository.getByDataspaceAndName(dataspaceEntity, anchorName);
if (isRootXpath(xpath)) {
- return fragmentRepository.findFirstRootByDataspaceAndAnchor(dataspaceEntity, anchorEntity);
+ return fragmentRepository.findFirstRootByDataspaceAndAnchor(
+ dataspaceEntity, anchorEntity);
} else {
final String normalizedXpath;
try {
} catch (final PathParsingException e) {
throw new CpsPathException(e.getMessage());
}
- return fragmentRepository.getByDataspaceAndAnchorAndXpath(dataspaceEntity, anchorEntity,
- normalizedXpath);
+
+ return fragmentRepository.getByDataspaceAndAnchorAndXpath(
+ dataspaceEntity, anchorEntity, normalizedXpath);
}
}
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();
}
} 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 -> getFragmentByXpath(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));
}
}