[BUG] Fix memory leak related to using arrays in Hibernate 13/139113/5
authordanielhanrahan <daniel.hanrahan@est.tech>
Wed, 2 Oct 2024 20:31:04 +0000 (21:31 +0100)
committerdanielhanrahan <daniel.hanrahan@est.tech>
Thu, 3 Oct 2024 20:04:09 +0000 (21:04 +0100)
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

cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathUtil.java
cps-ri/src/main/java/org/onap/cps/ri/CpsDataPersistenceServiceImpl.java
cps-ri/src/main/java/org/onap/cps/ri/repository/AnchorRepository.java
cps-ri/src/main/java/org/onap/cps/ri/repository/FragmentRepository.java
cps-ri/src/main/java/org/onap/cps/ri/repository/ModuleReferenceRepositoryImpl.java
cps-ri/src/main/java/org/onap/cps/ri/repository/SchemaSetRepository.java
cps-ri/src/main/java/org/onap/cps/ri/repository/TempTableCreator.java
cps-ri/src/main/java/org/onap/cps/ri/repository/YangResourceRepository.java
cps-service/src/main/java/org/onap/cps/utils/YangParserHelper.java

index bde9b06..4ede0d9 100644 (file)
@@ -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.
@@ -59,12 +59,10 @@ public class CpsPathUtil {
         return getCpsPathBuilder(xpathSource).build().getNormalizedParentPath();
     }
 
-    public static String[] getXpathNodeIdSequence(final String xpathSource) {
-        final List<String> containerNames = getCpsPathBuilder(xpathSource).build().getContainerNames();
-        return containerNames.toArray(new String[containerNames.size()]);
+    public static List<String> getXpathNodeIdSequence(final String xpathSource) {
+        return getCpsPathBuilder(xpathSource).build().getContainerNames();
     }
 
-
     /**
      * Returns boolean indicating xpath is an absolute path to a list element.
      *
index ec46fea..ee555f7 100644 (file)
@@ -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
index 7fe14b3..f7f750c 100755 (executable)
@@ -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
index 8edc3f2..9598230 100755 (executable)
@@ -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"
index c160fb1..702b589 100644 (file)
@@ -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);
     }
index 9357a5c..b455bc0 100644 (file)
@@ -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);
     }
 
 }
index cc83ab7..25c1491 100644 (file)
@@ -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 ");
index 9a11592..831766c 100644 (file)
@@ -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
index 5971645..a5865be 100644 (file)
@@ -29,6 +29,7 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
 import javax.xml.parsers.ParserConfigurationException;
 import javax.xml.stream.XMLInputFactory;
@@ -181,12 +182,12 @@ public class YangParserHelper {
 
     private static Map<String, Object> getDataSchemaNodeAndIdentifiersByXpath(final String parentNodeXpath,
                                                                               final SchemaContext schemaContext) {
-        final String[] xpathNodeIdSequence = xpathToNodeIdSequence(parentNodeXpath);
+        final List<String> xpathNodeIdSequence = xpathToNodeIdSequence(parentNodeXpath);
         return findDataSchemaNodeAndIdentifiersByXpathNodeIdSequence(xpathNodeIdSequence, schemaContext.getChildNodes(),
                 new ArrayList<>());
     }
 
-    private static String[] xpathToNodeIdSequence(final String xpath) {
+    private static List<String> xpathToNodeIdSequence(final String xpath) {
         try {
             return CpsPathUtil.getXpathNodeIdSequence(xpath);
         } catch (final PathParsingException pathParsingException) {
@@ -196,17 +197,16 @@ public class YangParserHelper {
     }
 
     private static Map<String, Object> findDataSchemaNodeAndIdentifiersByXpathNodeIdSequence(
-            final String[] xpathNodeIdSequence,
+            final List<String> xpathNodeIdSequence,
             final Collection<? extends DataSchemaNode> dataSchemaNodes,
             final Collection<QName> dataSchemaNodeIdentifiers) {
-        final String currentXpathNodeId = xpathNodeIdSequence[0];
+        final String currentXpathNodeId = xpathNodeIdSequence.get(0);
         final DataSchemaNode currentDataSchemaNode = dataSchemaNodes.stream()
             .filter(dataSchemaNode -> currentXpathNodeId.equals(dataSchemaNode.getQName().getLocalName()))
             .findFirst().orElseThrow(() -> schemaNodeNotFoundException(currentXpathNodeId));
         dataSchemaNodeIdentifiers.add(currentDataSchemaNode.getQName());
-        if (xpathNodeIdSequence.length <= 1) {
-            final Map<String, Object> dataSchemaNodeAndIdentifiers =
-                    new HashMap<>();
+        if (xpathNodeIdSequence.size() <= 1) {
+            final Map<String, Object> dataSchemaNodeAndIdentifiers = new HashMap<>();
             dataSchemaNodeAndIdentifiers.put("dataSchemaNode", currentDataSchemaNode);
             dataSchemaNodeAndIdentifiers.put("dataSchemaNodeIdentifiers", dataSchemaNodeIdentifiers);
             return dataSchemaNodeAndIdentifiers;
@@ -217,13 +217,11 @@ public class YangParserHelper {
                     ((DataNodeContainer) currentDataSchemaNode).getChildNodes(),
                     dataSchemaNodeIdentifiers);
         }
-        throw schemaNodeNotFoundException(xpathNodeIdSequence[1]);
+        throw schemaNodeNotFoundException(xpathNodeIdSequence.get(1));
     }
 
-    private static String[] getNextLevelXpathNodeIdSequence(final String[] xpathNodeIdSequence) {
-        final String[] nextXpathNodeIdSequence = new String[xpathNodeIdSequence.length - 1];
-        System.arraycopy(xpathNodeIdSequence, 1, nextXpathNodeIdSequence, 0, nextXpathNodeIdSequence.length);
-        return nextXpathNodeIdSequence;
+    private static List<String> getNextLevelXpathNodeIdSequence(final List<String> xpathNodeIdSequence) {
+        return xpathNodeIdSequence.subList(1, xpathNodeIdSequence.size());
     }
 
     private static DataValidationException schemaNodeNotFoundException(final String schemaNodeIdentifier) {