Interesting code: Today we have a look at org.codehaus.plexus.util.FileUtils.forceDelete(File)
.
FileUtils.foreceDelete
is a very common helper function, found in nearly all util libs around the world. Why is this one from the plexus-utils
project so interesting to have a detailed look into it? Short answer: it turns nearly all my Eclipse full builds into a CPU consuming long running task.
As mentioned forceDelete
is very common and if you have look into the Appache commons-io
project you will found the nearly same function — but a little bit different.
forceDelete in the commons-io
This code is from Appache commons-io:2.5
(newest version):
[code lang=java]
public static void forceDelete(File file) throws IOException {
if (file.isDirectory()) {
deleteDirectory(file);
} else {
boolean filePresent = file.exists();
if (!file.delete()) {
if (!filePresent){
throw new FileNotFoundException("File does not exist: " + file);
}
String message =
"Unable to delete file: " + file;
throw new IOException(message);
}
}
}
[/code]
forceDelete in the plexus-utils
An the next code is from the plexus-utils:3.0.24
project (newest version).
[code lang=java]
public static void forceDelete( final File file ) throws IOException {
if ( file.isDirectory() ) {
deleteDirectory( file );
} else {
/* NOTE: Always try to delete the file even if it appears to be non-existent.
* This will ensure that a symlink whose target does not exist is deleted, too. */
boolean filePresent = file.getCanonicalFile().exists();
if ( !deleteFile( file ) && filePresent ) {
final String message = "File " + file + " unable to be deleted.";
throw new IOException( message );
}
}
}
[/code]
On the first view both do the same:
* first check if the given file is a directory,
* second check if the file exists
* third delete the file
* last throw an exception, if the file could not be deleted.
But the plexus version version does not call the file.delete
method, it calls a private delete(File)
method. So we have a look at this method:
[code lang=java]
private static boolean deleteFile( File file ) throws IOException {
if ( file.isDirectory() ) {
throw new IOException( "File " + file + " isn't a file." );
}
if ( !file.delete() ) {
if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) {
file = file.getCanonicalFile();
System.gc();
}
try {
Thread.sleep( 10 );
return file.delete();
} catch ( InterruptedException ignore ) {
return file.delete();
}
}
return true;
}
[/code]
This method does a lot of magic in case the file could not be deleted:
* first it starts a Garbage-Collection on a Windows system
* second it sleeps for 10 millis
* last: it just tries the delete a second time
Currently I have no idea, why a Garbage-Collection run and to wait 10 millis will change the result for the file.delete
on any Windows-Systems. In case the file was opened by an other application we can nothing do in our application to solve the situation. If the file was opened in our own application, I suggest to just close the file.
In the past I came around a lot of situations, where we could not delete a file in our application and in all those situations it was always the same reason: some one did not close the stream and it was always a problem on Windows-Systems — we choose to fix the code to close the stream and get every file file.delete()
deleted with any other magic 🙂 .
I assume the version of commons-io
will just du the right things and suggest to remove all the additional stuff from the `plexis-utils‘ version.
forceDelete in the yuicompressor-maven-plugin
If you call this method for one or two files you will not be aware of the System.gc()
call and of the 10 millis sleeping — but if your application used already 2 GB memory and you call it 1.000 or more times your application drastically slows down and the garbage collecter will consume 100% CPU.
This happened in the yuicompressor-maven-plugin
, if you have a lot of *.css
files and do your development with Eclipse on a Windows-System (Team members on Linux system did not have any problems.) — I assume it will slow down every IDE which uses this plugin.
Now we will have a look at the yuicompressor-maven-plugin
code:
[code lang=java]
@Override
protected void processFile(SourceFile src) throws Exception {
…
InputStreamReader in = null;
OutputStreamWriter out = null;
File outFileTmp = new File(outFile.getAbsolutePath() + ".tmp");
FileUtils.forceDelete(outFileTmp);
try {
in = new InputStreamReader(new FileInputStream(inFile), encoding);
…
[/code]
The {{forceDelete}} is used to just make sure to have no old tmp files laying around. I suggest to just use a ‚better‘ forceDelete
implementation or to put a file.exists
before the forceDelete
to workaround the „Windows-Problems“ of the original FileUtils.foreceDelete
.
In my environment I just disable the yuicompressor-maven-plugin
as quick fix and second I opend a bug on github and looking forward what happend. Stay tuned.
(1) https://github.com/codehaus-plexus/plexus-utils/issues/21