Skip to content

Commit 3ca2a52

Browse files
committed
Add better method to delete safe windows file when is locked
1 parent 1d6f958 commit 3ca2a52

File tree

1 file changed

+61
-25
lines changed
  • aspect-model-editor-service/src/main/java/org/eclipse/esmf/ame/services/utils

1 file changed

+61
-25
lines changed

aspect-model-editor-service/src/main/java/org/eclipse/esmf/ame/services/utils/ModelUtils.java

Lines changed: 61 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,15 @@
1616
import java.io.ByteArrayInputStream;
1717
import java.io.File;
1818
import java.io.IOException;
19+
import java.io.RandomAccessFile;
1920
import java.net.URI;
2021
import java.nio.channels.FileChannel;
22+
import java.nio.channels.FileLock;
23+
import java.nio.channels.OverlappingFileLockException;
2124
import java.nio.charset.StandardCharsets;
2225
import java.nio.file.Files;
2326
import java.nio.file.Path;
2427
import java.nio.file.Paths;
25-
import java.nio.file.StandardOpenOption;
2628
import java.util.Arrays;
2729
import java.util.List;
2830
import java.util.Objects;
@@ -32,7 +34,8 @@
3234
import java.util.stream.StreamSupport;
3335

3436
import org.eclipse.esmf.ame.config.ApplicationSettings;
35-
import org.eclipse.esmf.ame.exceptions.FileNotFoundException;
37+
import org.eclipse.esmf.ame.exceptions.FileHandlingException;
38+
import org.eclipse.esmf.ame.exceptions.FileReadException;
3639
import org.eclipse.esmf.aspectmodel.loader.AspectModelLoader;
3740
import org.eclipse.esmf.aspectmodel.urn.AspectModelUrn;
3841
import org.eclipse.esmf.metamodel.AspectModel;
@@ -58,26 +61,6 @@ public static ByteArrayInputStream createInputStream( final String turtleData )
5861
return new ByteArrayInputStream( turtleData.getBytes( StandardCharsets.UTF_8 ) );
5962
}
6063

61-
/**
62-
* Force remove the given file.
63-
*
64-
* @param file - that will be removed.
65-
* @throws FileNotFoundException will be thrown if file can not be found.
66-
*/
67-
public static void deleteFile( @Nonnull final File file ) {
68-
try {
69-
if ( !file.isDirectory() ) {
70-
file.createNewFile();
71-
final FileChannel channel = FileChannel.open( file.toPath(), StandardOpenOption.WRITE );
72-
channel.lock().release();
73-
channel.close();
74-
}
75-
FileUtils.forceDelete( file );
76-
} catch ( final IOException e ) {
77-
throw new FileNotFoundException( String.format( "File %s was not deleted successfully.", file.toPath() ), e );
78-
}
79-
}
80-
8164
/**
8265
* Finds and deletes all the parent folders that are empty for the given file.
8366
*
@@ -86,7 +69,7 @@ public static void deleteFile( @Nonnull final File file ) {
8669
public static void deleteEmptyFiles( @Nonnull final File file ) {
8770
if ( !ApplicationSettings.getEndFilePath().toFile().getName().equals( file.getName() ) ) {
8871
final File parentFile = file.getParentFile();
89-
deleteFile( file );
72+
deleteFileSafely( file );
9073

9174
final List<File> fileList = Arrays.stream( Objects.requireNonNull( parentFile.listFiles() ) )
9275
.filter( f -> filterOutUnVisibleFiles().test( f ) ).toList();
@@ -97,6 +80,60 @@ public static void deleteEmptyFiles( @Nonnull final File file ) {
9780
}
9881
}
9982

83+
/**
84+
* Safely deletes a file or directory. If the file is locked, it waits for the lock to be released
85+
* before attempting deletion. If the file is a directory, it deletes the directory and all its contents.
86+
*
87+
* @param file the file or directory to be deleted
88+
* @throws FileHandlingException if an I/O error occurs during deletion
89+
* @throws FileReadException if the thread is interrupted while waiting for the file lock to be released
90+
*/
91+
public static void deleteFileSafely( @Nonnull final File file ) {
92+
try {
93+
if ( !file.exists() ) {
94+
return;
95+
}
96+
97+
waitForFileUnlock( file, 10, 200 );
98+
99+
if ( !file.isDirectory() ) {
100+
Files.deleteIfExists( file.toPath() );
101+
} else {
102+
FileUtils.deleteDirectory( file );
103+
}
104+
} catch ( final IOException e ) {
105+
throw new FileHandlingException( "File could not be deleted: " + file.getAbsolutePath(), e );
106+
} catch ( final InterruptedException e ) {
107+
Thread.currentThread().interrupt();
108+
throw new FileReadException( "Interrupted while waiting to delete file: " + file.getAbsolutePath() );
109+
}
110+
}
111+
112+
private static void waitForFileUnlock( final File file, final int maxRetries, final long sleepMillis ) throws InterruptedException {
113+
int retry = 0;
114+
while ( isFileLocked( file ) ) {
115+
if ( retry++ >= maxRetries ) {
116+
throw new RuntimeException( "File is still locked after retries: " + file.getAbsolutePath() );
117+
}
118+
Thread.sleep( sleepMillis );
119+
}
120+
}
121+
122+
private static boolean isFileLocked( final File file ) {
123+
try ( final RandomAccessFile raf = new RandomAccessFile( file, "rw" ); final FileChannel channel = raf.getChannel() ) {
124+
125+
final FileLock lock = channel.tryLock();
126+
if ( lock != null ) {
127+
lock.release();
128+
return false;
129+
}
130+
} catch ( final IOException | OverlappingFileLockException e ) {
131+
return true;
132+
}
133+
134+
return true;
135+
}
136+
100137
private static Predicate<File> filterOutUnVisibleFiles() {
101138
return file -> !file.getName().equals( ".DS_Store" );
102139
}
@@ -168,8 +205,7 @@ public static Supplier<AspectModel> getAspectModelSupplier( final AspectModelUrn
168205
public static Supplier<AspectModel> getAspectModelSupplier( final String turtleData, final File newFile,
169206
final AspectModelLoader aspectModelLoader ) {
170207
final Optional<URI> sourceLocation = Optional.of( newFile.toURI() );
171-
final ByteArrayInputStream inputStream = new ByteArrayInputStream(
172-
turtleData.getBytes( StandardCharsets.UTF_8 ) );
208+
final ByteArrayInputStream inputStream = new ByteArrayInputStream( turtleData.getBytes( StandardCharsets.UTF_8 ) );
173209

174210
return createLazySupplier( () -> aspectModelLoader.load( inputStream, sourceLocation ) );
175211
}

0 commit comments

Comments
 (0)