diff --git a/oap-storage/oap-storage-cloud-test/src/test/java/oap/storage/cloud/FileSystemFileTest.java b/oap-storage/oap-storage-cloud-test/src/test/java/oap/storage/cloud/FileSystemFileTest.java index b4354cf34..f6a246b6c 100644 --- a/oap-storage/oap-storage-cloud-test/src/test/java/oap/storage/cloud/FileSystemFileTest.java +++ b/oap-storage/oap-storage-cloud-test/src/test/java/oap/storage/cloud/FileSystemFileTest.java @@ -8,17 +8,18 @@ import oap.testng.SystemTimerFixture; import oap.testng.TestDirectoryFixture; import org.apache.commons.io.FilenameUtils; -import org.jetbrains.annotations.NotNull; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import org.testng.annotations.Test; +import javax.annotation.Nonnull; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URISyntaxException; import java.nio.file.Path; +import java.util.LinkedHashMap; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -144,13 +145,25 @@ public void testCopy() { } } - @NotNull + @Nonnull private FileSystemConfiguration getFileSystemConfiguration() { - return new FileSystemConfiguration( Map.of( + return getFileSystemConfiguration( false ); + } + + @Nonnull + private FileSystemConfiguration getFileSystemConfiguration( boolean removeEmptyFolders ) { + LinkedHashMap map = new LinkedHashMap<>(); + map.putAll( Map.of( "fs.default.clouds.scheme", "file", "fs.default.clouds.container", "", "fs.file.clouds.filesystem.basedir", basedir ) ); + + if( removeEmptyFolders ) { + map.put( "fs.file.clouds.filesystem.remove_empty_folders", true ); + } + + return new FileSystemConfiguration( map ); } @Test @@ -268,4 +281,38 @@ public void testFolder() { assertThat( fileSystem.getMetadata( new CloudURI( "file://test-bucket/folder/" ) ).getContentType() ).isEqualTo( "application/x-directory" ); } } + + @Test + public void testDeleteFileAndParentFolderIfEmpty() { + // 1. remove_empty_folders disabled -> parent folders remain after delete + try( FileSystem fileSystem = new FileSystem( getFileSystemConfiguration() ) ) { + fileSystem.upload( new CloudURI( "file://case1/folder1/folder2/file.txt" ), BlobData.builder().content( "content" ).build() ); + + fileSystem.deleteBlob( new CloudURI( "file://case1/folder1/folder2/file.txt" ) ); + + assertFile( basedir.resolve( "case1/folder1/folder2/file.txt" ) ).doesNotExist(); + assertThat( basedir.resolve( "case1/folder1/folder2" ) ).exists(); + } + + // 2. enabled -> empty folder2 removed, folder1 kept (still has file2.txt) + try( FileSystem fileSystem = new FileSystem( getFileSystemConfiguration( true ) ) ) { + fileSystem.upload( new CloudURI( "file://case2/folder1/folder2/file.txt" ), BlobData.builder().content( "content" ).build() ); + fileSystem.upload( new CloudURI( "file://case2/folder1/file2.txt" ), BlobData.builder().content( "content2" ).build() ); + + fileSystem.deleteBlob( new CloudURI( "file://case2/folder1/folder2/file.txt" ) ); + + assertThat( basedir.resolve( "case2/folder1/folder2" ) ).doesNotExist(); + assertThat( basedir.resolve( "case2/folder1" ) ).exists(); + } + + // 3. enabled -> whole empty chain removed up to basedir + try( FileSystem fileSystem = new FileSystem( getFileSystemConfiguration( true ) ) ) { + fileSystem.upload( new CloudURI( "file://case3/folder1/folder2/file.txt" ), BlobData.builder().content( "content" ).build() ); + + fileSystem.deleteBlob( new CloudURI( "file://case3/folder1/folder2/file.txt" ) ); + + assertThat( basedir.resolve( "case3/folder1/folder2" ) ).doesNotExist(); + assertThat( basedir.resolve( "case3/folder1" ) ).doesNotExist(); + } + } } diff --git a/oap-storage/oap-storage-cloud/src/main/java/oap/storage/cloud/FileSystemCloudApiLocalFs.java b/oap-storage/oap-storage-cloud/src/main/java/oap/storage/cloud/FileSystemCloudApiLocalFs.java index d0bc47f4a..447191c44 100644 --- a/oap-storage/oap-storage-cloud/src/main/java/oap/storage/cloud/FileSystemCloudApiLocalFs.java +++ b/oap-storage/oap-storage-cloud/src/main/java/oap/storage/cloud/FileSystemCloudApiLocalFs.java @@ -29,6 +29,7 @@ public class FileSystemCloudApiLocalFs implements FileSystemCloudApi { private final Path basedir; + private final boolean removeEmptyFolders; public FileSystemCloudApiLocalFs( FileSystemConfiguration fileSystemConfiguration, String container ) { String basedir = ( String ) fileSystemConfiguration.get( "file", container, "jclouds.filesystem.basedir" ); @@ -37,6 +38,9 @@ public FileSystemCloudApiLocalFs( FileSystemConfiguration fileSystemConfiguratio } this.basedir = Paths.get( basedir ); + + Object removeEmptyFolders = fileSystemConfiguration.get( "file", container, "jclouds.filesystem.remove_empty_folders" ); + this.removeEmptyFolders = removeEmptyFolders != null && Boolean.parseBoolean( removeEmptyFolders.toString() ); } @Override @@ -56,7 +60,17 @@ public CompletableFuture containerExistsAsync( CloudURI path ) throws C @Override public CompletableFuture deleteBlobAsync( CloudURI path ) { try { - Files.delete( getPath( path ) ); + Path fsPath = getPath( path ); + Files.delete( fsPath ); + + if( removeEmptyFolders ) { + Path parent = fsPath.getParent(); + while( parent != null && !parent.equals( basedir ) && parent.startsWith( basedir ) + && oap.io.Files.isDirectoryEmpty( parent ) ) { + Files.delete( parent ); + parent = parent.getParent(); + } + } return CompletableFuture.completedFuture( null ); } catch( IOException e ) { diff --git a/pom.xml b/pom.xml index 4861c79cd..3590b2b53 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ oap oap.maven - 25.1.0 + 25.3.0 pom @@ -19,7 +19,7 @@ oap oap-dependencies - 25.1.0 + 25.3.0 pom import @@ -66,7 +66,7 @@ - 25.9.0 + 25.9.1 25.0.1 25.0.0