You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@streampipes.apache.org by ze...@apache.org on 2024/04/08 09:38:12 UTC

(streampipes) branch dev updated: refactor(#2708): Make all methods of FileManager none static (#2709)

This is an automated email from the ASF dual-hosted git repository.

zehnder pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/streampipes.git


The following commit(s) were added to refs/heads/dev by this push:
     new 34accf49b8 refactor(#2708): Make all methods of FileManager none static (#2709)
34accf49b8 is described below

commit 34accf49b829af252f1af3513d1f669136a2889d
Author: Philipp Zehnder <te...@users.noreply.github.com>
AuthorDate: Mon Apr 8 11:38:06 2024 +0200

    refactor(#2708): Make all methods of FileManager none static (#2709)
    
    * refactor(#2708): Make all methods of FileManager none static
    
    * refactor(#2708): Add unit tests for getAllFiles
    
    * refactor(#2708): Add unit tests for getFile
    
    * refactor(#2708): Add tests in FileManager for storeFile and deleteFile
    
    * refactor(#2708): Make FileHasher none static and add tests
    
    * refactor(#2708): Add unit test for checkFileContentChanged in FileManager
    
    * refactor(#2708): Remove hash code from FileMetadata
---
 .../streampipes/commons/file/FileHasher.java       |   7 +-
 .../streampipes/commons/file/FileHasherTest.java}  |  39 +++-
 .../export/generator/ExportPackageGenerator.java   |   2 +-
 .../connect/iiot/utils/FileProtocolUtils.java      |   2 +-
 .../streampipes/model/file/FileMetadata.java       |   1 +
 .../streampipes/manager/file/FileManager.java      |  77 ++++---
 .../streampipes/manager/file/TestFileManager.java  | 231 +++++++++++++++++++--
 .../apache/streampipes/rest/ResetManagement.java   |   5 +-
 .../rest/impl/AssetDashboardResource.java          |   6 +-
 .../streampipes/rest/impl/PipelineElementFile.java |  18 +-
 10 files changed, 320 insertions(+), 68 deletions(-)

diff --git a/streampipes-commons/src/main/java/org/apache/streampipes/commons/file/FileHasher.java b/streampipes-commons/src/main/java/org/apache/streampipes/commons/file/FileHasher.java
index 8aac95a126..7b8c30eb62 100644
--- a/streampipes-commons/src/main/java/org/apache/streampipes/commons/file/FileHasher.java
+++ b/streampipes-commons/src/main/java/org/apache/streampipes/commons/file/FileHasher.java
@@ -28,7 +28,12 @@ import java.nio.file.Paths;
 
 public class FileHasher {
 
-  public static String hash(File file) throws IOException {
+  public String hash(File file) throws IOException {
+
+    if (file == null) {
+      throw new IOException("Input file is null. Please provide a valid file to calculate the hash.");
+    }
+
     try (InputStream is = Files.newInputStream(Paths.get(file.toURI()))) {
       return DigestUtils.md5Hex(is);
     }
diff --git a/streampipes-commons/src/main/java/org/apache/streampipes/commons/file/FileHasher.java b/streampipes-commons/src/test/java/org/apache/streampipes/commons/file/FileHasherTest.java
similarity index 51%
copy from streampipes-commons/src/main/java/org/apache/streampipes/commons/file/FileHasher.java
copy to streampipes-commons/src/test/java/org/apache/streampipes/commons/file/FileHasherTest.java
index 8aac95a126..7b8d1b981d 100644
--- a/streampipes-commons/src/main/java/org/apache/streampipes/commons/file/FileHasher.java
+++ b/streampipes-commons/src/test/java/org/apache/streampipes/commons/file/FileHasherTest.java
@@ -18,19 +18,38 @@
 
 package org.apache.streampipes.commons.file;
 
-import org.apache.commons.codec.digest.DigestUtils;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
 
 import java.io.File;
 import java.io.IOException;
-import java.io.InputStream;
-import java.nio.file.Files;
-import java.nio.file.Paths;
 
-public class FileHasher {
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 
-  public static String hash(File file) throws IOException {
-    try (InputStream is = Files.newInputStream(Paths.get(file.toURI()))) {
-      return DigestUtils.md5Hex(is);
-    }
+class FileHasherTest {
+
+  private FileHasher fileHasher;
+
+  @BeforeEach
+  public void setup() {
+    this.fileHasher = new FileHasher();
+  }
+
+  @Test
+  void hash_returnsCorrectHashForFile() throws IOException {
+    var file = new File("src/test/resources/test.txt");
+    assertEquals("6df4d50a41a5d20bc4faad8a6f09aa8f", fileHasher.hash(file));
+  }
+
+  @Test
+  void hash_throwsIOExceptionForNonExistingFile() {
+    var file = new File("src/test/resources/nonExistingFile.txt");
+    assertThrows(IOException.class, () -> fileHasher.hash(file));
+  }
+
+  @Test
+  void hash_throwsIOExceptionForNullFile() {
+    assertThrows(IOException.class, () -> fileHasher.hash(null));
   }
-}
+}
\ No newline at end of file
diff --git a/streampipes-data-export/src/main/java/org/apache/streampipes/export/generator/ExportPackageGenerator.java b/streampipes-data-export/src/main/java/org/apache/streampipes/export/generator/ExportPackageGenerator.java
index fc3663929f..13abd92077 100644
--- a/streampipes-data-export/src/main/java/org/apache/streampipes/export/generator/ExportPackageGenerator.java
+++ b/streampipes-data-export/src/main/java/org/apache/streampipes/export/generator/ExportPackageGenerator.java
@@ -124,7 +124,7 @@ public class ExportPackageGenerator {
         String filename = fileResolver.findDocument(item.getResourceId()).getFilename();
         addDoc(builder, item, new FileResolver(), manifest::addFile);
         try {
-          builder.addBinary(filename, Files.readAllBytes(FileManager.getFile(filename).toPath()));
+          builder.addBinary(filename, Files.readAllBytes(new FileManager().getFile(filename).toPath()));
         } catch (IOException e) {
           e.printStackTrace();
         }
diff --git a/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/utils/FileProtocolUtils.java b/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/utils/FileProtocolUtils.java
index 91f0867a11..6c7ece4c44 100644
--- a/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/utils/FileProtocolUtils.java
+++ b/streampipes-extensions/streampipes-connect-adapters-iiot/src/main/java/org/apache/streampipes/connect/iiot/utils/FileProtocolUtils.java
@@ -56,7 +56,7 @@ public class FileProtocolUtils {
    */
   private static boolean checkIfFileChanged(String selectedFilename) {
     try {
-      var hash = FileHasher.hash(getFile(selectedFilename));
+      var hash = new FileHasher().hash(getFile(selectedFilename));
       StreamPipesClient client = getStreamPipesClientInstance();
       return client.fileApi()
                    .checkFileContentChanged(selectedFilename, hash);
diff --git a/streampipes-model/src/main/java/org/apache/streampipes/model/file/FileMetadata.java b/streampipes-model/src/main/java/org/apache/streampipes/model/file/FileMetadata.java
index f4ee123ecc..4696179903 100644
--- a/streampipes-model/src/main/java/org/apache/streampipes/model/file/FileMetadata.java
+++ b/streampipes-model/src/main/java/org/apache/streampipes/model/file/FileMetadata.java
@@ -91,4 +91,5 @@ public class FileMetadata {
   public void setFiletype(String filetype) {
     this.filetype = filetype;
   }
+
 }
diff --git a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/file/FileManager.java b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/file/FileManager.java
index 5038ec3af0..84bb7f47b7 100644
--- a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/file/FileManager.java
+++ b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/file/FileManager.java
@@ -34,17 +34,38 @@ import java.util.stream.Collectors;
 
 public class FileManager {
 
-  public static List<FileMetadata> getAllFiles() {
+  private final IFileMetadataStorage fileMetadataStorage;
+  private final FileHandler fileHandler;
+  private final FileHasher fileHasher;
+
+  public FileManager(IFileMetadataStorage fileMetadataStorage,
+                     FileHandler fileHandler,
+                     FileHasher fileHasher) {
+    this.fileMetadataStorage = fileMetadataStorage;
+    this.fileHandler = fileHandler;
+    this.fileHasher = fileHasher;
+  }
+
+  public FileManager() {
+    this.fileMetadataStorage = StorageDispatcher
+        .INSTANCE
+        .getNoSqlStore()
+        .getFileMetadataStorage();
+    this.fileHandler = new FileHandler();
+    this.fileHasher = new FileHasher();
+  }
+
+  public List<FileMetadata> getAllFiles() {
     return getAllFiles(null);
   }
 
-  public static List<FileMetadata> getAllFiles(String filetypes) {
-    List<FileMetadata> allFiles = getFileMetadataStorage().getAllFileMetadataDescriptions();
+  public List<FileMetadata> getAllFiles(String filetypes) {
+    List<FileMetadata> allFiles = fileMetadataStorage.getAllFileMetadataDescriptions();
     return filetypes != null ? filterFiletypes(allFiles, filetypes) : allFiles;
   }
 
-  public static File getFile(String filename) {
-    return new FileHandler().getFile(filename);
+  public File getFile(String filename) {
+    return fileHandler.getFile(filename);
   }
 
   /**
@@ -56,7 +77,7 @@ public class FileManager {
    * @param fileInputStream content of file
    * @return metadata of file
    */
-  public static FileMetadata storeFile(String user,
+  public FileMetadata storeFile(String user,
                                        String filename,
                                        InputStream fileInputStream) throws IOException {
 
@@ -72,13 +93,15 @@ public class FileManager {
   }
 
 
-  public static void deleteFile(String id) {
-    FileMetadata fileMetadata = getFileMetadataStorage().getMetadataById(id);
-    new FileHandler().deleteFile(fileMetadata.getFilename());
-    getFileMetadataStorage().deleteFileMetadata(id);
+  public void deleteFile(String id) {
+    var fileMetadata = fileMetadataStorage.getMetadataById(id);
+    if (fileMetadata != null) {
+      fileHandler.deleteFile(fileMetadata.getFilename());
+      fileMetadataStorage.deleteFileMetadata(id);
+    }
   }
 
-  private static InputStream validateFileNameAndCleanFile(String filename,
+  private InputStream validateFileNameAndCleanFile(String filename,
                                                   String filetype,
                                                   InputStream fileInputStream) {
     if (!validateFileType(filename)) {
@@ -95,7 +118,7 @@ public class FileManager {
    * @param filetype file of type
    * @return input stream without BOM
    */
-  protected static InputStream cleanFile(InputStream fileInputStream, String filetype) {
+  protected InputStream cleanFile(InputStream fileInputStream, String filetype) {
     if (Filetypes.CSV.getFileExtensions().contains(filetype.toLowerCase())) {
       fileInputStream = new BOMInputStream(fileInputStream);
     }
@@ -103,34 +126,26 @@ public class FileManager {
     return fileInputStream;
   }
 
-  public static boolean checkFileContentChanged(String filename, String hash) throws IOException {
-    var fileHash = FileHasher.hash(getFile(filename));
+  public boolean checkFileContentChanged(String filename, String hash) throws IOException {
+    var fileHash = fileHasher.hash(getFile(filename));
     return !fileHash.equals(hash);
   }
 
-  public static String sanitizeFilename(String filename) {
+  public String sanitizeFilename(String filename) {
     return filename.replaceAll("[^a-zA-Z0-9.\\-]", "_");
   }
 
-  public static boolean validateFileType(String filename) {
+  public boolean validateFileType(String filename) {
     return Filetypes.getAllFileExtensions()
                     .stream()
                     .anyMatch(filename::endsWith);
   }
 
-  protected static void writeToFile(String sanitizedFilename, InputStream fileInputStream) throws IOException {
-    new FileHandler().storeFile(sanitizedFilename, fileInputStream);
-  }
-
-
-  private static IFileMetadataStorage getFileMetadataStorage() {
-    return StorageDispatcher
-        .INSTANCE
-        .getNoSqlStore()
-        .getFileMetadataStorage();
+  protected void writeToFile(String sanitizedFilename, InputStream fileInputStream) throws IOException {
+    fileHandler.storeFile(sanitizedFilename, fileInputStream);
   }
 
-  protected static FileMetadata makeAndStoreFileMetadata(String user,
+  protected FileMetadata makeAndStoreFileMetadata(String user,
                                                        String sanitizedFilename,
                                                        String filetype) {
     var fileMetadata = makeFileMetadata(user, sanitizedFilename, filetype);
@@ -139,7 +154,7 @@ public class FileManager {
     return fileMetadata;
   }
 
-  private static FileMetadata makeFileMetadata(String user,
+  private FileMetadata makeFileMetadata(String user,
                                                String filename,
                                                String filetype) {
 
@@ -152,12 +167,12 @@ public class FileManager {
     return fileMetadata;
   }
 
-  private static void storeFileMetadata(FileMetadata fileMetadata) {
-    getFileMetadataStorage().addFileMetadata(fileMetadata);
+  private void storeFileMetadata(FileMetadata fileMetadata) {
+    fileMetadataStorage.addFileMetadata(fileMetadata);
   }
 
 
-  private static List<FileMetadata> filterFiletypes(List<FileMetadata> allFiles, String filetypes) {
+  private List<FileMetadata> filterFiletypes(List<FileMetadata> allFiles, String filetypes) {
     return allFiles
         .stream()
         .filter(fileMetadata -> Arrays
diff --git a/streampipes-pipeline-management/src/test/java/org/apache/streampipes/manager/file/TestFileManager.java b/streampipes-pipeline-management/src/test/java/org/apache/streampipes/manager/file/TestFileManager.java
index a02a309fa3..da62437bc6 100644
--- a/streampipes-pipeline-management/src/test/java/org/apache/streampipes/manager/file/TestFileManager.java
+++ b/streampipes-pipeline-management/src/test/java/org/apache/streampipes/manager/file/TestFileManager.java
@@ -17,34 +17,211 @@
  */
 package org.apache.streampipes.manager.file;
 
+import org.apache.streampipes.commons.file.FileHasher;
+import org.apache.streampipes.model.file.FileMetadata;
+import org.apache.streampipes.storage.api.IFileMetadataStorage;
+
 import org.apache.commons.io.IOUtils;
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
 
+import java.io.ByteArrayInputStream;
+import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.List;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 public class TestFileManager {
 
+  private FileManager fileManager;
+  private IFileMetadataStorage fileMetadataStorage;
+  private FileHandler fileHandler;
+  private FileHasher fileHasher;
+
+
+  private static final String TEST_USER = "testUser";
+
+  @BeforeEach
+  public void setup() {
+    fileMetadataStorage = mock(IFileMetadataStorage.class);
+    fileHandler = mock(FileHandler.class);
+    fileHasher = mock(FileHasher.class);
+    fileManager = new FileManager(fileMetadataStorage, fileHandler, fileHasher);
+  }
+
+  @Test
+  public void getAllFiles_returnsAllFiles() {
+    var expected = prepareFileMetadataStorageWithTwoSampleFiles();
+
+    var result = fileManager.getAllFiles();
+
+    assertEquals(expected, result);
+  }
+
+  @Test
+  public void getAllFiles_returnsAllFilesWhenFiletypesIsNull() {
+    var expected = prepareFileMetadataStorageWithTwoSampleFiles();
+
+    var result = fileManager.getAllFiles(null);
+
+    assertEquals(expected, result);
+  }
+
+  @Test
+  public void getAllFiles_returnsFilteredFilesWhenFiletypesIsNotNull() {
+    var files = prepareFileMetadataStorageWithTwoSampleFiles();
+
+    List<FileMetadata> result = fileManager.getAllFiles("csv");
+
+    assertEquals(1, result.size());
+    assertEquals(files.get(0), result.get(0));
+  }
+
+  @Test
+  public void getAllFiles_returnsEmptyListWhenNoMatchingFiletypes() {
+    prepareFileMetadataStorageWithTwoSampleFiles();
+
+    var result = fileManager.getAllFiles("xml");
+
+    assertEquals(0, result.size());
+  }
+
+  private List<FileMetadata> prepareFileMetadataStorageWithTwoSampleFiles() {
+    List<FileMetadata> allFiles = Arrays.asList(createFileMetadata("csv"), createFileMetadata("json"));
+    when(fileMetadataStorage.getAllFileMetadataDescriptions()).thenReturn(allFiles);
+
+    return allFiles;
+  }
+
+  private FileMetadata createFileMetadata(String fileType) {
+    FileMetadata file = new FileMetadata();
+    file.setFiletype(fileType);
+    return file;
+  }
+
+  @Test
+  public void getFile_returnsExistingFile() {
+    var filename = "existingFile.txt";
+    var expectedFile = new File(filename);
+    when(fileHandler.getFile(filename)).thenReturn(expectedFile);
+
+    var result = fileManager.getFile(filename);
+
+    assertEquals(expectedFile, result);
+  }
+
+  @Test
+  public void getFile_returnsNullForNonExistingFile() {
+    var filename = "fileDoesNotExist.txt";
+    when(fileHandler.getFile(filename)).thenReturn(null);
+
+    assertNull(fileManager.getFile(filename));
+  }
+
   @Test
   public void storeFile_throwsExceptionForInvalidFileType() {
     var filename = "testFile.invalid";
 
     assertThrows(IllegalArgumentException.class, () ->
-        FileManager.storeFile("", filename, mock(InputStream.class)));
+        fileManager.storeFile("", filename, mock(InputStream.class)));
+  }
+
+  @Test
+  public void storeFile_storesFileWithValidInput() throws IOException {
+    var filename = "testFile.csv";
+
+    var fileMetadata = fileManager.storeFile(TEST_USER, filename, mock(InputStream.class));
+
+    assertEquals(TEST_USER, fileMetadata.getCreatedByUser());
+    assertEquals(filename, fileMetadata.getFilename());
+    assertEquals("csv", fileMetadata.getFiletype());
+    verify(fileHandler, times(1)).storeFile(eq(filename), any(InputStream.class));
+    verify(fileMetadataStorage, times(1)).addFileMetadata(any(FileMetadata.class));
+  }
+
+  @Test
+  public void storeFile_sanitizesFilename() throws IOException {
+    var filename = "test@File.csv";
+    var expectedSanitizedFilename = "test_File.csv";
+
+    var fileMetadata = fileManager.storeFile(TEST_USER, filename, mock(InputStream.class));
+
+    assertEquals(expectedSanitizedFilename, fileMetadata.getFilename());
+    verify(fileHandler, times(1)).storeFile(eq(expectedSanitizedFilename), any(InputStream.class));
+    verify(fileMetadataStorage, times(1)).addFileMetadata(any(FileMetadata.class));
+  }
+
+  /**
+   * This test validates that the storeFile method removes the BOM from the file before it is stored.
+   */
+  @Test
+  public void storeFile_removesBom() throws IOException {
+    var expectedContent = "test content";
+    // prepare input stream with BOM
+    var fileInputStream = new ByteArrayInputStream(("\uFEFF" + expectedContent)
+                                                       .getBytes(StandardCharsets.UTF_8));
+
+    fileManager.storeFile(TEST_USER, "testfile.csv", fileInputStream);
+
+    var inputStreamCaptor = ArgumentCaptor.forClass(InputStream.class);
+    verify(fileHandler, times(1)).storeFile(any(), inputStreamCaptor.capture());
+
+    // Convert the captured InputStream to a String
+    var capturedInputStream = inputStreamCaptor.getValue();
+    var capturedContent = IOUtils.toString(capturedInputStream, StandardCharsets.UTF_8);
+
+    // Assert that the captured content is equal to the expected content
+    assertEquals(expectedContent, capturedContent);
+  }
+
+
+  @Test
+  public void deleteFile_removesExistingFile() {
+    var id = "existingFileId";
+    var fileMetadata = new FileMetadata();
+    fileMetadata.setFilename("existingFile.txt");
+
+    when(fileMetadataStorage.getMetadataById(id)).thenReturn(fileMetadata);
+
+    fileManager.deleteFile(id);
+
+    verify(fileHandler, times(1)).deleteFile(fileMetadata.getFilename());
+    verify(fileMetadataStorage, times(1)).deleteFileMetadata(id);
+  }
+
+  @Test
+  public void deleteFile_doesNothingForNonExistingFile() {
+    var id = "nonExistingFileId";
+
+    when(fileMetadataStorage.getMetadataById(id)).thenReturn(null);
+
+    fileManager.deleteFile(id);
+
+    verify(fileHandler, times(0)).deleteFile(anyString());
+    verify(fileMetadataStorage, times(0)).deleteFileMetadata(id);
   }
 
   @Test
   public void testCleanFileWithoutBom() throws IOException {
     var expected = "test";
     var inputStream = IOUtils.toInputStream(expected, StandardCharsets.UTF_8);
-    var resultStream = FileManager.cleanFile(inputStream, "CSV");
+    var resultStream = fileManager.cleanFile(inputStream, "CSV");
     var resultString = IOUtils.toString(resultStream, StandardCharsets.UTF_8);
 
     assertEquals(expected, resultString);
@@ -56,7 +233,7 @@ public class TestFileManager {
     var utf8Bom = "\uFEFF";
     var inputString = utf8Bom + expected;
     var inputStream = IOUtils.toInputStream(inputString, StandardCharsets.UTF_8);
-    var resultStream = FileManager.cleanFile(inputStream, "CSV");
+    var resultStream = fileManager.cleanFile(inputStream, "CSV");
     var resultString = IOUtils.toString(resultStream, StandardCharsets.UTF_8);
 
     assertEquals(expected, resultString);
@@ -68,7 +245,7 @@ public class TestFileManager {
     var utf8Bom = "\uFEFF";
     var inputString = utf8Bom + expected;
     var inputStream = IOUtils.toInputStream(inputString, StandardCharsets.UTF_8);
-    var resultStream = FileManager.cleanFile(inputStream, "CSV");
+    var resultStream = fileManager.cleanFile(inputStream, "CSV");
     var resultString = IOUtils.toString(resultStream, StandardCharsets.UTF_8);
 
     assertEquals(expected, resultString);
@@ -77,57 +254,85 @@ public class TestFileManager {
   @Test
   public void sanitizeFilename_replacesNonAlphanumericCharactersWithUnderscore() {
     var filename = "file@name#with$special%characters";
-    var sanitizedFilename = FileManager.sanitizeFilename(filename);
+    var sanitizedFilename = fileManager.sanitizeFilename(filename);
     assertEquals("file_name_with_special_characters", sanitizedFilename);
   }
 
   @Test
   public void sanitizeFilename_keepsAlphanumericAndDotAndHyphenCharacters() {
     var filename = "file.name-with_alphanumeric123";
-    var sanitizedFilename = FileManager.sanitizeFilename(filename);
+    var sanitizedFilename = fileManager.sanitizeFilename(filename);
     assertEquals(filename, sanitizedFilename);
   }
 
   @Test
   public void sanitizeFilename_returnsUnderscoreForFilenameWithAllSpecialCharacters() {
     var filename = "@#$%^&*()";
-    var sanitizedFilename = FileManager.sanitizeFilename(filename);
+    var sanitizedFilename = fileManager.sanitizeFilename(filename);
     assertEquals("_________", sanitizedFilename);
   }
 
   @Test
   public void sanitizeFilename_returnsEmptyStringForEmptyFilename() {
     var filename = "";
-    var sanitizedFilename = FileManager.sanitizeFilename(filename);
+    var sanitizedFilename = fileManager.sanitizeFilename(filename);
     assertEquals("", sanitizedFilename);
   }
 
   @Test
   public void sanitizeFilename_removesSingleParentDirectory() {
     var filename = "../file.csv";
-    var sanitizedFilename = FileManager.sanitizeFilename(filename);
+    var sanitizedFilename = fileManager.sanitizeFilename(filename);
     assertEquals(".._file.csv", sanitizedFilename);
   }
 
   @Test
   public void sanitizeFilename_removesDoubleParentDirectoryy() {
     var filename = "../../file";
-    var sanitizedFilename = FileManager.sanitizeFilename(filename);
+    var sanitizedFilename = fileManager.sanitizeFilename(filename);
     assertEquals(".._.._file", sanitizedFilename);
   }
 
   @Test
   public void validateFileName_returnsTrueForCsv() {
-    assertTrue(FileManager.validateFileType("file.csv"));
+    assertTrue(fileManager.validateFileType("file.csv"));
   }
 
   @Test
   public void validateFileName_returnsTrueForJson() {
-    assertTrue(FileManager.validateFileType("file.json"));
+    assertTrue(fileManager.validateFileType("file.json"));
   }
 
   @Test
   public void validateFileName_returnsFalseForSh() {
-    assertFalse(FileManager.validateFileType("file.sh"));
+    assertFalse(fileManager.validateFileType("file.sh"));
+  }
+
+  @Test
+  public void checkFileContentChanged_returnsTrueWhenContentHasChanged() throws IOException {
+    var filename = "testFile.csv";
+    var originalHash = "originalHash";
+    var changedHash = "changedHash";
+
+    when(fileHandler.getFile(filename)).thenReturn(new File(filename));
+    when(fileHasher.hash(any(File.class))).thenReturn(changedHash);
+
+    var result = fileManager.checkFileContentChanged(filename, originalHash);
+
+    assertTrue(result);
+  }
+
+  @Test
+  public void checkFileContentChanged_returnsFalseWhenContentHasNotChanged() throws IOException {
+    var filename = "testFile.csv";
+    var originalHash = "originalHash";
+
+    when(fileHandler.getFile(filename)).thenReturn(new File(filename));
+    when(fileHasher.hash(any(File.class))).thenReturn(originalHash);
+
+    var result = fileManager.checkFileContentChanged(filename, originalHash);
+
+    assertFalse(result);
   }
+
 }
\ No newline at end of file
diff --git a/streampipes-rest/src/main/java/org/apache/streampipes/rest/ResetManagement.java b/streampipes-rest/src/main/java/org/apache/streampipes/rest/ResetManagement.java
index 8319d0a4ee..d5e89b9671 100644
--- a/streampipes-rest/src/main/java/org/apache/streampipes/rest/ResetManagement.java
+++ b/streampipes-rest/src/main/java/org/apache/streampipes/rest/ResetManagement.java
@@ -131,8 +131,9 @@ public class ResetManagement {
   }
 
   private static void deleteAllFiles() {
-    List<FileMetadata> allFiles = FileManager.getAllFiles();
-    allFiles.forEach(fileMetadata -> FileManager.deleteFile(fileMetadata.getFileId()));
+    var fileManager = new FileManager();
+    List<FileMetadata> allFiles = fileManager.getAllFiles();
+    allFiles.forEach(fileMetadata -> fileManager.deleteFile(fileMetadata.getFileId()));
   }
 
   private static void removeAllDataInDataLake() {
diff --git a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/AssetDashboardResource.java b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/AssetDashboardResource.java
index 4433d51ae4..4687f7c962 100644
--- a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/AssetDashboardResource.java
+++ b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/AssetDashboardResource.java
@@ -106,7 +106,7 @@ public class AssetDashboardResource extends AbstractRestResource {
   @GetMapping(path = "/images/{imageName}")
   public ResponseEntity<byte[]> getDashboardImage(@PathVariable("imageName") String imageName) {
     try {
-      var sanitizedFileName = FileManager.sanitizeFilename(imageName);
+      var sanitizedFileName = new FileManager().sanitizeFilename(imageName);
       java.nio.file.Path path = Paths.get(getTargetFile(sanitizedFileName));
       File file = new File(path.toString());
       FileNameMap fileNameMap = URLConnection.getFileNameMap();
@@ -128,7 +128,7 @@ public class AssetDashboardResource extends AbstractRestResource {
     try {
       var fileName = fileDetail.getName();
 
-      if (!FileManager.validateFileType(fileName)) {
+      if (!new FileManager().validateFileType(fileName)) {
         LOG.error("File type of file " + fileName + " is not supported");
         fail();
       }
@@ -148,7 +148,7 @@ public class AssetDashboardResource extends AbstractRestResource {
       targetDirectory.mkdirs();
     }
 
-    var sanitizedFileName = FileManager.sanitizeFilename(fileName);
+    var sanitizedFileName = new FileManager().sanitizeFilename(fileName);
 
     var targetFile = new File(getTargetFile(sanitizedFileName));
 
diff --git a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/PipelineElementFile.java b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/PipelineElementFile.java
index 89373cce05..006f3f955d 100644
--- a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/PipelineElementFile.java
+++ b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/PipelineElementFile.java
@@ -53,6 +53,12 @@ import static org.springframework.http.HttpStatus.NOT_FOUND;
 @RequestMapping("/api/v2/files")
 public class PipelineElementFile extends AbstractAuthGuardedRestResource {
 
+  private final FileManager fileManager;
+
+  public PipelineElementFile() {
+    this.fileManager = new FileManager();
+  }
+
   @PostMapping(
       consumes = MediaType.MULTIPART_FORM_DATA_VALUE,
       produces = MediaType.APPLICATION_JSON_VALUE)
@@ -60,7 +66,7 @@ public class PipelineElementFile extends AbstractAuthGuardedRestResource {
   public ResponseEntity<?> storeFile(@RequestPart("file_upload") MultipartFile fileDetail) {
     try {
       FileMetadata metadata =
-          FileManager.storeFile(
+          fileManager.storeFile(
               getAuthenticatedUsername(),
               fileDetail.getOriginalFilename(),
               fileDetail.getInputStream()
@@ -74,7 +80,7 @@ public class PipelineElementFile extends AbstractAuthGuardedRestResource {
   @DeleteMapping(path = "{fileId}")
   @PreAuthorize(AuthConstants.IS_ADMIN_ROLE)
   public ResponseEntity<Void> deleteFile(@PathVariable("fileId") String fileId) {
-    FileManager.deleteFile(fileId);
+    fileManager.deleteFile(fileId);
     return ok();
   }
 
@@ -83,7 +89,7 @@ public class PipelineElementFile extends AbstractAuthGuardedRestResource {
   public ResponseEntity<List<FileMetadata>> getFileInfo(
       @RequestParam(value = "filetypes", required = false) String filetypes
   ) {
-    return ok(FileManager.getAllFiles(filetypes));
+    return ok(fileManager.getAllFiles(filetypes));
   }
 
   @GetMapping(path = "/{filename}", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
@@ -109,7 +115,7 @@ public class PipelineElementFile extends AbstractAuthGuardedRestResource {
       @PathVariable("filename") String filename
   ) {
     try {
-      return ok(getFileContents(FileManager.getFile(filename)));
+      return ok(getFileContents(fileManager.getFile(filename)));
     } catch (IOException e) {
       throw new SpMessageException(
           NOT_FOUND,
@@ -125,7 +131,7 @@ public class PipelineElementFile extends AbstractAuthGuardedRestResource {
   @GetMapping(path = "/allFilenames", produces = MediaType.APPLICATION_JSON_VALUE)
   @PreAuthorize(AuthConstants.HAS_READ_FILE_PRIVILEGE)
   public ResponseEntity<List<String>> getAllOriginalFilenames() {
-    return ok(FileManager.getAllFiles()
+    return ok(fileManager.getAllFiles()
                          .stream()
                          .map(fileMetadata -> fileMetadata.getFilename()
                                                           .toLowerCase())
@@ -141,7 +147,7 @@ public class PipelineElementFile extends AbstractAuthGuardedRestResource {
       @PathVariable(value = "hash") String hash
   ) {
     try {
-      return ok(FileManager.checkFileContentChanged(filename, hash));
+      return ok(fileManager.checkFileContentChanged(filename, hash));
     } catch (IOException e) {
       throw new SpMessageException(
           NOT_FOUND,