diff options
author | danielhanrahan <daniel.hanrahan@est.tech> | 2024-10-02 21:31:04 +0100 |
---|---|---|
committer | danielhanrahan <daniel.hanrahan@est.tech> | 2024-10-03 21:04:09 +0100 |
commit | 153aa48c6945cfc54ceb1b3f6a38508ad93f1d16 (patch) | |
tree | 9b4510292b2efd1e11d34f4493d7604ea98d2172 /cps-ri/src/main/java/org | |
parent | 2686ec95a09bafa2846860d403516f89cb2ed0c0 (diff) |
[BUG] Fix memory leak related to using arrays in Hibernate
The use of arrays like String[] instead of Collection<String> in
JpaRepository methods is causing a memory leak, most likely due to a
bug in the hypersistence-utils dependency which provides the feature.
Note code using arrays in JDBC (via jdbcTemplate) is not affected.
This patch removes most uses of arrays in Java, except one needed for
FragmentPrefetchRepository using JDBC setArray(), which is safe.
Issue-ID: CPS-2430
Signed-off-by: danielhanrahan <daniel.hanrahan@est.tech>
Change-Id: I94f8c3d4c8c32ebe0978c08d3226a196a95bf3b9
Diffstat (limited to 'cps-ri/src/main/java/org')
7 files changed, 59 insertions, 83 deletions
diff --git a/cps-ri/src/main/java/org/onap/cps/ri/CpsDataPersistenceServiceImpl.java b/cps-ri/src/main/java/org/onap/cps/ri/CpsDataPersistenceServiceImpl.java index ec46fea4cb..ee555f7dc8 100644 --- a/cps-ri/src/main/java/org/onap/cps/ri/CpsDataPersistenceServiceImpl.java +++ b/cps-ri/src/main/java/org/onap/cps/ri/CpsDataPersistenceServiceImpl.java @@ -341,8 +341,7 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService if (anchorIds.isEmpty()) { fragmentEntities = fragmentRepository.findByDataspaceAndXpathIn(dataspaceEntity, ancestorXpaths); } else { - fragmentEntities = fragmentRepository.findByAnchorIdsAndXpathIn( - anchorIds.toArray(new Long[0]), ancestorXpaths.toArray(new String[0])); + fragmentEntities = fragmentRepository.findByAnchorIdsAndXpathIn(anchorIds, ancestorXpaths); } } @@ -475,7 +474,7 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService fragmentRepository.findAllXpathByAnchorAndXpathIn(anchorEntity, deleteChecklist); if (onlySupportListDeletion) { final Collection<String> xpathsToExistingListElements = xpathsToExistingContainers.stream() - .filter(CpsPathUtil::isPathToListElement).collect(Collectors.toList()); + .filter(CpsPathUtil::isPathToListElement).toList(); deleteChecklist.removeAll(xpathsToExistingListElements); } else { deleteChecklist.removeAll(xpathsToExistingContainers); @@ -483,15 +482,19 @@ public class CpsDataPersistenceServiceImpl implements CpsDataPersistenceService final Collection<String> xpathsToExistingLists = deleteChecklist.stream() .filter(xpath -> fragmentRepository.existsByAnchorAndXpathStartsWith(anchorEntity, xpath + "[")) - .collect(Collectors.toList()); + .toList(); deleteChecklist.removeAll(xpathsToExistingLists); if (!deleteChecklist.isEmpty()) { throw new DataNodeNotFoundExceptionBatch(dataspaceName, anchorName, deleteChecklist); } - fragmentRepository.deleteByAnchorIdAndXpaths(anchorEntity.getId(), xpathsToExistingContainers); - fragmentRepository.deleteListsByAnchorIdAndXpaths(anchorEntity.getId(), xpathsToExistingLists); + if (!xpathsToExistingContainers.isEmpty()) { + fragmentRepository.deleteByAnchorIdAndXpaths(anchorEntity.getId(), xpathsToExistingContainers); + } + for (final String listXpath : xpathsToExistingLists) { + fragmentRepository.deleteListByAnchorIdAndXpath(anchorEntity.getId(), listXpath); + } } @Override diff --git a/cps-ri/src/main/java/org/onap/cps/ri/repository/AnchorRepository.java b/cps-ri/src/main/java/org/onap/cps/ri/repository/AnchorRepository.java index 7fe14b3173..f7f750c983 100755 --- a/cps-ri/src/main/java/org/onap/cps/ri/repository/AnchorRepository.java +++ b/cps-ri/src/main/java/org/onap/cps/ri/repository/AnchorRepository.java @@ -1,7 +1,7 @@ /* * ============LICENSE_START======================================================= * Copyright (C) 2021 Pantheon.tech - * Modifications Copyright (C) 2021-2023 Nordix Foundation + * Modifications Copyright (C) 2021-2024 Nordix Foundation * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -46,26 +46,26 @@ public interface AnchorRepository extends JpaRepository<AnchorEntity, Long> { Collection<AnchorEntity> findAllBySchemaSet(SchemaSetEntity schemaSetEntity); - @Query(value = "SELECT * FROM anchor WHERE dataspace_id = :dataspaceId AND name = ANY (:anchorNames)", + @Query(value = "SELECT * FROM anchor WHERE dataspace_id = :dataspaceId AND name IN (:anchorNames)", nativeQuery = true) Collection<AnchorEntity> findAllByDataspaceIdAndNameIn(@Param("dataspaceId") int dataspaceId, - @Param("anchorNames") String[] anchorNames); + @Param("anchorNames") Collection<String> anchorNames); default Collection<AnchorEntity> findAllByDataspaceAndNameIn(final DataspaceEntity dataspaceEntity, final Collection<String> anchorNames) { - return findAllByDataspaceIdAndNameIn(dataspaceEntity.getId(), anchorNames.toArray(new String[0])); + return findAllByDataspaceIdAndNameIn(dataspaceEntity.getId(), anchorNames); } @Query(value = "SELECT a.* FROM anchor a" + " LEFT OUTER JOIN schema_set s ON a.schema_set_id = s.id" - + " WHERE a.dataspace_id = :dataspaceId AND s.name = ANY (:schemaSetNames)", + + " WHERE a.dataspace_id = :dataspaceId AND s.name IN (:schemaSetNames)", nativeQuery = true) - Collection<AnchorEntity> findAllByDataspaceIdAndSchemaSetNameIn(@Param("dataspaceId") int dataspaceId, - @Param("schemaSetNames") String[] schemaSetNames); + Collection<AnchorEntity> findAllByDataspaceIdAndSchemaSetNameIn( + @Param("dataspaceId") int dataspaceId, @Param("schemaSetNames") Collection<String> schemaSetNames); default Collection<AnchorEntity> findAllByDataspaceAndSchemaSetNameIn(final DataspaceEntity dataspaceEntity, final Collection<String> schemaSetNames) { - return findAllByDataspaceIdAndSchemaSetNameIn(dataspaceEntity.getId(), schemaSetNames.toArray(new String[0])); + return findAllByDataspaceIdAndSchemaSetNameIn(dataspaceEntity.getId(), schemaSetNames); } Integer countByDataspace(DataspaceEntity dataspaceEntity); @@ -80,7 +80,7 @@ public interface AnchorRepository extends JpaRepository<AnchorEntity, Long> { JOIN anchor ON anchor.schema_set_id = schema_set.id WHERE schema_set.dataspace_id = :dataspaceId - AND module_name = ANY ( :moduleNames ) + AND module_name IN (:moduleNames) GROUP BY anchor.id, anchor.name, @@ -90,25 +90,18 @@ public interface AnchorRepository extends JpaRepository<AnchorEntity, Long> { COUNT(DISTINCT module_name) = :sizeOfModuleNames """, nativeQuery = true) Collection<String> getAnchorNamesByDataspaceIdAndModuleNames(@Param("dataspaceId") int dataspaceId, - @Param("moduleNames") String[] moduleNames, + @Param("moduleNames") Collection<String> moduleNames, @Param("sizeOfModuleNames") int sizeOfModuleNames); - default Collection<String> getAnchorNamesByDataspaceIdAndModuleNames(final int dataspaceId, - final Collection<String> moduleNames, - final int sizeOfModuleNames) { - final String[] moduleNamesArray = moduleNames.toArray(new String[0]); - return getAnchorNamesByDataspaceIdAndModuleNames(dataspaceId, moduleNamesArray, sizeOfModuleNames); - } - @Modifying - @Query(value = "DELETE FROM anchor WHERE dataspace_id = :dataspaceId AND name = ANY (:anchorNames)", + @Query(value = "DELETE FROM anchor WHERE dataspace_id = :dataspaceId AND name IN (:anchorNames)", nativeQuery = true) void deleteAllByDataspaceIdAndNameIn(@Param("dataspaceId") int dataspaceId, - @Param("anchorNames") String[] anchorNames); + @Param("anchorNames") Collection<String> anchorNames); default void deleteAllByDataspaceAndNameIn(final DataspaceEntity dataspaceEntity, final Collection<String> anchorNames) { - deleteAllByDataspaceIdAndNameIn(dataspaceEntity.getId(), anchorNames.toArray(new String[0])); + deleteAllByDataspaceIdAndNameIn(dataspaceEntity.getId(), anchorNames); } @Modifying diff --git a/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentRepository.java b/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentRepository.java index 8edc3f2311..9598230cdb 100755 --- a/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentRepository.java +++ b/cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentRepository.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2021-2023 Nordix Foundation. + * Copyright (C) 2021-2024 Nordix Foundation. * Modifications Copyright (C) 2020-2021 Bell Canada. * Modifications Copyright (C) 2020-2021 Pantheon.tech. * Modifications Copyright (C) 2023 TechMahindra Ltd. @@ -48,14 +48,14 @@ public interface FragmentRepository extends JpaRepository<FragmentEntity, Long>, new DataNodeNotFoundException(anchorEntity.getDataspace().getName(), anchorEntity.getName(), xpath)); } - @Query(value = "SELECT * FROM fragment WHERE anchor_id = :anchorId AND xpath = ANY (:xpaths)", + @Query(value = "SELECT * FROM fragment WHERE anchor_id = :anchorId AND xpath IN (:xpaths)", nativeQuery = true) List<FragmentEntity> findByAnchorIdAndXpathIn(@Param("anchorId") long anchorId, - @Param("xpaths") String[] xpaths); + @Param("xpaths") Collection<String> xpaths); default List<FragmentEntity> findByAnchorAndXpathIn(final AnchorEntity anchorEntity, final Collection<String> xpaths) { - return findByAnchorIdAndXpathIn(anchorEntity.getId(), xpaths.toArray(new String[0])); + return findByAnchorIdAndXpathIn(anchorEntity.getId(), xpaths); } @Query(value = "SELECT * FROM fragment WHERE anchor_id = :anchorId \n" @@ -70,58 +70,52 @@ public interface FragmentRepository extends JpaRepository<FragmentEntity, Long>, } @Query(value = "SELECT fragment.* FROM fragment JOIN anchor ON anchor.id = fragment.anchor_id " - + "WHERE dataspace_id = :dataspaceId AND xpath = ANY (:xpaths)", nativeQuery = true) + + "WHERE dataspace_id = :dataspaceId AND xpath IN (:xpaths)", nativeQuery = true) List<FragmentEntity> findByDataspaceIdAndXpathIn(@Param("dataspaceId") int dataspaceId, - @Param("xpaths") String[] xpaths); + @Param("xpaths") Collection<String> xpaths); default List<FragmentEntity> findByDataspaceAndXpathIn(final DataspaceEntity dataspaceEntity, final Collection<String> xpaths) { - return findByDataspaceIdAndXpathIn(dataspaceEntity.getId(), xpaths.toArray(new String[0])); + return findByDataspaceIdAndXpathIn(dataspaceEntity.getId(), xpaths); } @Query(value = "SELECT * FROM fragment WHERE anchor_id IN (:anchorIds)" - + " AND xpath = ANY (:xpaths)", nativeQuery = true) - List<FragmentEntity> findByAnchorIdsAndXpathIn(@Param("anchorIds") Long[] anchorIds, - @Param("xpaths") String[] xpaths); + + " AND xpath IN (:xpaths)", nativeQuery = true) + List<FragmentEntity> findByAnchorIdsAndXpathIn(@Param("anchorIds") Collection<Long> anchorIds, + @Param("xpaths") Collection<String> xpaths); @Query(value = "SELECT * FROM fragment WHERE anchor_id = :anchorId LIMIT 1", nativeQuery = true) Optional<FragmentEntity> findOneByAnchorId(@Param("anchorId") long anchorId); @Modifying - @Query(value = "DELETE FROM fragment WHERE anchor_id = ANY (:anchorIds)", nativeQuery = true) - void deleteByAnchorIdIn(@Param("anchorIds") long[] anchorIds); + @Query(value = "DELETE FROM fragment WHERE anchor_id IN (:anchorIds)", nativeQuery = true) + void deleteByAnchorIdIn(@Param("anchorIds") Collection<Long> anchorIds); default void deleteByAnchorIn(final Collection<AnchorEntity> anchorEntities) { - deleteByAnchorIdIn(anchorEntities.stream().map(AnchorEntity::getId).mapToLong(id -> id).toArray()); + deleteByAnchorIdIn(anchorEntities.stream().map(AnchorEntity::getId).toList()); } @Modifying - @Query(value = "DELETE FROM fragment WHERE anchor_id = :anchorId AND xpath = ANY (:xpaths)", nativeQuery = true) - void deleteByAnchorIdAndXpaths(@Param("anchorId") long anchorId, @Param("xpaths") String[] xpaths); - - default void deleteByAnchorIdAndXpaths(final long anchorId, final Collection<String> xpaths) { - deleteByAnchorIdAndXpaths(anchorId, xpaths.toArray(new String[0])); - } + @Query(value = "DELETE FROM fragment WHERE anchor_id = :anchorId AND xpath IN (:xpaths)", nativeQuery = true) + void deleteByAnchorIdAndXpaths(@Param("anchorId") long anchorId, @Param("xpaths") Collection<String> xpaths); @Modifying - @Query(value = "DELETE FROM fragment f WHERE anchor_id = :anchorId AND xpath LIKE ANY (:xpathPatterns)", + @Query(value = "DELETE FROM fragment f WHERE anchor_id = :anchorId AND xpath LIKE :xpathPattern", nativeQuery = true) - void deleteByAnchorIdAndXpathLikeAny(@Param("anchorId") long anchorId, - @Param("xpathPatterns") String[] xpathPatterns); + void deleteByAnchorIdAndXpathLike(@Param("anchorId") long anchorId, @Param("xpathPattern") String xpathPattern); - default void deleteListsByAnchorIdAndXpaths(long anchorId, Collection<String> xpaths) { - deleteByAnchorIdAndXpathLikeAny(anchorId, - xpaths.stream().map(xpath -> EscapeUtils.escapeForSqlLike(xpath) + "[@%").toArray(String[]::new)); + default void deleteListByAnchorIdAndXpath(final long anchorId, final String xpath) { + deleteByAnchorIdAndXpathLike(anchorId, EscapeUtils.escapeForSqlLike(xpath) + "[@%"); } - @Query(value = "SELECT xpath FROM fragment WHERE anchor_id = :anchorId AND xpath = ANY (:xpaths)", + @Query(value = "SELECT xpath FROM fragment WHERE anchor_id = :anchorId AND xpath IN (:xpaths)", nativeQuery = true) List<String> findAllXpathByAnchorIdAndXpathIn(@Param("anchorId") long anchorId, - @Param("xpaths") String[] xpaths); + @Param("xpaths") Collection<String> xpaths); default List<String> findAllXpathByAnchorAndXpathIn(final AnchorEntity anchorEntity, final Collection<String> xpaths) { - return findAllXpathByAnchorIdAndXpathIn(anchorEntity.getId(), xpaths.toArray(new String[0])); + return findAllXpathByAnchorIdAndXpathIn(anchorEntity.getId(), xpaths); } @Query(value = "SELECT EXISTS(SELECT 1 FROM fragment WHERE anchor_id = :anchorId" diff --git a/cps-ri/src/main/java/org/onap/cps/ri/repository/ModuleReferenceRepositoryImpl.java b/cps-ri/src/main/java/org/onap/cps/ri/repository/ModuleReferenceRepositoryImpl.java index c160fb1e38..702b5896c7 100644 --- a/cps-ri/src/main/java/org/onap/cps/ri/repository/ModuleReferenceRepositoryImpl.java +++ b/cps-ri/src/main/java/org/onap/cps/ri/repository/ModuleReferenceRepositoryImpl.java @@ -64,7 +64,7 @@ public class ModuleReferenceRepositoryImpl implements ModuleReferenceQuery { } final String tempTableName = tempTableCreator.createTemporaryTable( - "moduleReferencesToCheckTemp", sqlData, "module_name", "revision"); + "moduleReferencesToCheckTemp", sqlData, List.of("module_name", "revision")); return identifyNewModuleReferencesForCmHandle(tempTableName); } diff --git a/cps-ri/src/main/java/org/onap/cps/ri/repository/SchemaSetRepository.java b/cps-ri/src/main/java/org/onap/cps/ri/repository/SchemaSetRepository.java index 9357a5c6a7..b455bc04c0 100644 --- a/cps-ri/src/main/java/org/onap/cps/ri/repository/SchemaSetRepository.java +++ b/cps-ri/src/main/java/org/onap/cps/ri/repository/SchemaSetRepository.java @@ -2,7 +2,7 @@ * ============LICENSE_START======================================================= * Copyright (C) 2020 Pantheon.tech * Modifications Copyright (C) 2022 TechMahindra Ltd. - * Modifications Copyright (C) 2023 Nordix Foundation + * Modifications Copyright (C) 2023-2024 Nordix Foundation * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -61,10 +61,10 @@ public interface SchemaSetRepository extends JpaRepository<SchemaSetEntity, Inte } @Modifying - @Query(value = "DELETE FROM schema_set WHERE dataspace_id = :dataspaceId AND name = ANY (:schemaSetNames)", + @Query(value = "DELETE FROM schema_set WHERE dataspace_id = :dataspaceId AND name IN (:schemaSetNames)", nativeQuery = true) void deleteByDataspaceIdAndNameIn(@Param("dataspaceId") final int dataspaceId, - @Param("schemaSetNames") final String[] schemaSetNames); + @Param("schemaSetNames") final Collection<String> schemaSetNames); /** * Delete multiple schema sets in a given dataspace. @@ -73,7 +73,7 @@ public interface SchemaSetRepository extends JpaRepository<SchemaSetEntity, Inte */ default void deleteByDataspaceAndNameIn(final DataspaceEntity dataspaceEntity, final Collection<String> schemaSetNames) { - deleteByDataspaceIdAndNameIn(dataspaceEntity.getId(), schemaSetNames.toArray(new String[0])); + deleteByDataspaceIdAndNameIn(dataspaceEntity.getId(), schemaSetNames); } } diff --git a/cps-ri/src/main/java/org/onap/cps/ri/repository/TempTableCreator.java b/cps-ri/src/main/java/org/onap/cps/ri/repository/TempTableCreator.java index cc83ab7d94..25c1491502 100644 --- a/cps-ri/src/main/java/org/onap/cps/ri/repository/TempTableCreator.java +++ b/cps-ri/src/main/java/org/onap/cps/ri/repository/TempTableCreator.java @@ -1,6 +1,6 @@ /*- * ============LICENSE_START======================================================= - * Copyright (C) 2022-2023 Nordix Foundation. + * Copyright (C) 2022-2024 Nordix Foundation. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,10 +22,8 @@ package org.onap.cps.ri.repository; import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; -import java.util.Arrays; import java.util.Collection; import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.UUID; import java.util.stream.Collectors; @@ -54,7 +52,7 @@ public class TempTableCreator { */ public String createTemporaryTable(final String prefix, final Collection<List<String>> sqlData, - final String... columnNames) { + final Collection<String> columnNames) { final String tempTableName = prefix + UUID.randomUUID().toString().replace("-", ""); final StringBuilder sqlStringBuilder = new StringBuilder("CREATE TEMPORARY TABLE "); sqlStringBuilder.append(tempTableName); @@ -65,29 +63,21 @@ public class TempTableCreator { return tempTableName; } - private static void defineColumns(final StringBuilder sqlStringBuilder, final String[] columnNames) { - sqlStringBuilder.append('('); - final Iterator<String> it = Arrays.stream(columnNames).iterator(); - while (it.hasNext()) { - final String columnName = it.next(); - sqlStringBuilder.append(" "); - sqlStringBuilder.append(columnName); - sqlStringBuilder.append(" varchar NOT NULL"); - if (it.hasNext()) { - sqlStringBuilder.append(","); - } - } - sqlStringBuilder.append(")"); + private static void defineColumns(final StringBuilder sqlStringBuilder, final Collection<String> columnNames) { + final String columns = columnNames.stream() + .map(columnName -> " " + columnName + " varchar NOT NULL") + .collect(Collectors.joining(",")); + sqlStringBuilder.append('(').append(columns).append(')'); } private static void insertData(final StringBuilder sqlStringBuilder, final String tempTableName, - final String[] columnNames, + final Collection<String> columnNames, final Collection<List<String>> sqlData) { final Collection<String> sqlInserts = new HashSet<>(sqlData.size()); for (final Collection<String> rowValues : sqlData) { final Collection<String> escapedValues = - rowValues.stream().map(EscapeUtils::escapeForSqlStringLiteral).collect(Collectors.toList()); + rowValues.stream().map(EscapeUtils::escapeForSqlStringLiteral).toList(); sqlInserts.add("('" + String.join("','", escapedValues) + "')"); } sqlStringBuilder.append("INSERT INTO "); diff --git a/cps-ri/src/main/java/org/onap/cps/ri/repository/YangResourceRepository.java b/cps-ri/src/main/java/org/onap/cps/ri/repository/YangResourceRepository.java index 9a11592310..831766cc9a 100644 --- a/cps-ri/src/main/java/org/onap/cps/ri/repository/YangResourceRepository.java +++ b/cps-ri/src/main/java/org/onap/cps/ri/repository/YangResourceRepository.java @@ -35,11 +35,7 @@ import org.springframework.stereotype.Repository; public interface YangResourceRepository extends JpaRepository<YangResourceEntity, Integer>, YangResourceNativeRepository, SchemaSetYangResourceRepository { - List<YangResourceEntity> findAllByChecksumIn(String[] checksums); - - default List<YangResourceEntity> findAllByChecksumIn(final Collection<String> checksums) { - return findAllByChecksumIn(checksums.toArray(new String[0])); - } + List<YangResourceEntity> findAllByChecksumIn(Collection<String> checksums); @Query(value = """ SELECT DISTINCT |