Fix bug related to incorrect exception check when saving data nodes 55/142455/2
authorToineSiebelink <toine.siebelink@est.tech>
Wed, 19 Nov 2025 08:46:01 +0000 (08:46 +0000)
committerToine Siebelink <toine.siebelink@est.tech>
Wed, 19 Nov 2025 08:50:18 +0000 (08:50 +0000)
Issue-ID: CPS-3053
Change-Id: I32c14a64f3422f0d088752821519a4cc592e4509
Signed-off-by: ToineSiebelink <toine.siebelink@est.tech>
cps-ri/src/main/java/org/onap/cps/ri/CpsDataPersistenceServiceImpl.java
cps-ri/src/test/groovy/org/onap/cps/ri/CpsDataPersistenceServiceImplSpec.groovy

index 3e80e30..7c065f8 100644 (file)
@@ -43,7 +43,6 @@ import java.util.TreeMap;
 import java.util.stream.Collectors;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
-import org.hibernate.StaleStateException;
 import org.onap.cps.api.exceptions.AlreadyDefinedException;
 import org.onap.cps.api.exceptions.ConcurrencyException;
 import org.onap.cps.api.exceptions.CpsAdminException;
@@ -67,6 +66,7 @@ import org.onap.cps.ri.utils.SessionManager;
 import org.onap.cps.spi.CpsDataPersistenceService;
 import org.onap.cps.utils.JsonObjectMapper;
 import org.springframework.dao.DataIntegrityViolationException;
+import org.springframework.orm.ObjectOptimisticLockingFailureException;
 import org.springframework.stereotype.Service;
 
 @Service
@@ -145,7 +145,7 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
 
         try {
             fragmentRepository.saveAll(fragmentEntities);
-        } catch (final StaleStateException staleStateException) {
+        } catch (final ObjectOptimisticLockingFailureException objectOptimisticLockingFailureException) {
             retryUpdateDataNodesIndividually(anchorEntity, fragmentEntities);
         }
     }
@@ -173,7 +173,7 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
 
         try {
             fragmentRepository.saveAll(existingFragmentEntities);
-        } catch (final StaleStateException staleStateException) {
+        } catch (final ObjectOptimisticLockingFailureException objectOptimisticLockingFailureException) {
             retryUpdateDataNodesIndividually(anchorEntity, existingFragmentEntities);
         }
     }
@@ -184,7 +184,7 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService
         for (final FragmentEntity dataNodeFragment : fragmentEntities) {
             try {
                 fragmentRepository.save(dataNodeFragment);
-            } catch (final StaleStateException staleStateException) {
+            } catch (final ObjectOptimisticLockingFailureException objectOptimisticLockingFailureException) {
                 failedXpaths.add(dataNodeFragment.getXpath());
             }
         }
index 7fa9b2e..665201e 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * ============LICENSE_START=======================================================
  * Copyright (c) 2021 Bell Canada.
- * Modifications Copyright (C) 2021-2023 Nordix Foundation
+ * Modifications Copyright (C) 2021-2025 OpenInfra Foundation Europe.
  * Modifications Copyright (C) 2022-2023 TechMahindra Ltd.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
 package org.onap.cps.ri
 
 import com.fasterxml.jackson.databind.ObjectMapper
-import org.hibernate.StaleStateException
+import org.onap.cps.api.exceptions.ConcurrencyException
 import org.onap.cps.api.exceptions.DataNodeNotFoundExceptionBatch
+import org.onap.cps.api.exceptions.DataValidationException
+import org.onap.cps.api.model.DataNode
+import org.onap.cps.api.parameters.FetchDescendantsOption
+import org.onap.cps.impl.DataNodeBuilder
 import org.onap.cps.ri.models.AnchorEntity
 import org.onap.cps.ri.models.DataspaceEntity
 import org.onap.cps.ri.models.FragmentEntity
@@ -30,13 +34,9 @@ import org.onap.cps.ri.repository.AnchorRepository
 import org.onap.cps.ri.repository.DataspaceRepository
 import org.onap.cps.ri.repository.FragmentRepository
 import org.onap.cps.ri.utils.SessionManager
-import org.onap.cps.api.parameters.FetchDescendantsOption
-import org.onap.cps.api.exceptions.ConcurrencyException
-import org.onap.cps.api.exceptions.DataValidationException
-import org.onap.cps.api.model.DataNode
-import org.onap.cps.impl.DataNodeBuilder
 import org.onap.cps.utils.JsonObjectMapper
 import org.springframework.dao.DataIntegrityViolationException
+import org.springframework.orm.ObjectOptimisticLockingFailureException
 import spock.lang.Specification
 
 import java.util.stream.Collectors
@@ -48,6 +48,7 @@ class CpsDataPersistenceServiceImplSpec extends Specification {
     def mockFragmentRepository = Mock(FragmentRepository)
     def jsonObjectMapper = new JsonObjectMapper(new ObjectMapper())
     def mockSessionManager = Mock(SessionManager)
+    def someCause = Mock(Throwable)
 
     def objectUnderTest = Spy(new CpsDataPersistenceServiceImpl(mockDataspaceRepository, mockAnchorRepository,
             mockFragmentRepository, jsonObjectMapper, mockSessionManager))
@@ -72,7 +73,7 @@ class CpsDataPersistenceServiceImplSpec extends Specification {
             2 * mockFragmentRepository.save(_)
     }
 
-    def 'Handling of StaleStateException (caused by concurrent updates) during patch operation for data nodes.'() {
+    def 'Handling of ObjectOptimisticLockingFailureException (caused by concurrent updates) during patch operation for data nodes.'() {
         given: 'the system can update one datanode and has two more datanodes that throw an exception while updating'
             def dataNodes = createDataNodesAndMockRepositoryMethodSupportingThem([
                     '/node1': 'OK',
@@ -81,7 +82,7 @@ class CpsDataPersistenceServiceImplSpec extends Specification {
             def updatedLeavesPerXPath = dataNodes.stream()
                     .collect(Collectors.toMap(DataNode::getXpath, DataNode::getLeaves))
         and: 'the batch update will therefore also fail'
-            mockFragmentRepository.saveAll(*_) >> { throw new StaleStateException("concurrent updates") }
+            mockFragmentRepository.saveAll(*_) >> { throw new ObjectOptimisticLockingFailureException('concurrent updates', someCause) }
         when: 'attempt batch update data nodes'
             objectUnderTest.batchUpdateDataLeaves('some-dataspace', 'some-anchor', updatedLeavesPerXPath)
         then: 'concurrency exception is thrown'
@@ -131,14 +132,14 @@ class CpsDataPersistenceServiceImplSpec extends Specification {
             assert thrown.message == 'DataNode not found'
     }
 
-    def 'Handling of StaleStateException (caused by concurrent updates) during update data nodes and descendants.'() {
+    def 'Handling of ObjectOptimisticLockingFailureException (caused by concurrent updates) during update data nodes and descendants.'() {
         given: 'the system can update one datanode and has two more datanodes that throw an exception while updating'
             def dataNodes = createDataNodesAndMockRepositoryMethodSupportingThem([
                 '/node1': 'OK',
                 '/node2': 'EXCEPTION',
                 '/node3': 'EXCEPTION'])
         and: 'the batch update will therefore also fail'
-            mockFragmentRepository.saveAll(*_) >> { throw new StaleStateException("concurrent updates") }
+            mockFragmentRepository.saveAll(*_) >> { throw new ObjectOptimisticLockingFailureException('concurrent updates', someCause) }
         when: 'attempt batch update data nodes'
             objectUnderTest.updateDataNodesAndDescendants('some-dataspace', 'some-anchor', dataNodes)
         then: 'concurrency exception is thrown'
@@ -261,7 +262,7 @@ class CpsDataPersistenceServiceImplSpec extends Specification {
         def fragmentEntity = new FragmentEntity(xpath: xpath, childFragments: [])
         mockFragmentRepository.findByAnchorIdAndXpath(_, xpath) >> fragmentEntity
         if ('EXCEPTION' == scenario) {
-            mockFragmentRepository.save(fragmentEntity) >> { throw new StaleStateException("concurrent updates") }
+            mockFragmentRepository.save(fragmentEntity) >> { throw new ObjectOptimisticLockingFailureException('concurrent updates', someCause) }
         }
         return dataNode
     }
@@ -278,7 +279,7 @@ class CpsDataPersistenceServiceImplSpec extends Specification {
             def fragmentEntity = new FragmentEntity(id: fragmentId, anchor: anchorEntity, xpath: xpath, childFragments: [])
             fragmentEntities.add(fragmentEntity)
             if ('EXCEPTION' == scenario) {
-                mockFragmentRepository.save(fragmentEntity) >> { throw new StaleStateException("concurrent updates") }
+                mockFragmentRepository.save(fragmentEntity) >> { throw new ObjectOptimisticLockingFailureException('concurrent updates', someCause) }
             }
             fragmentId++
         }