Java opening File Streams in one class and closing/deletion of file in another class

735 Views Asked by At

I want to delete the file which is opened and done writing but not closed. Please refer to code below:

Class A (can't be changed):

import java.io.FileOutputStream;

public class A {

    public void run(String file) throws Exception {
        FileOutputStream s = new FileOutputStream(file);

    }
}

Class B:

import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class B {

    public static void main(String[] args) throws Exception {
        String path = "D:\\CONFLUX_HOME\\TestClient\\Maps\\test\\newTest.txt";
        A a = new A();
        a.run(path);
        File f = new File(path);
        Files.delete(Paths.get(f.getAbsolutePath()));

    }

}

In Class A , just open the stream without closing the file. In class B , calling A's run method and then try to delete the file. Since the file is still opened. I'm unable to delete the file.

Error is : The process cannot access the file because it is being used by another process.

Actual Scenario is : We are loading the jars dynamically. Classes inside jar are creating the file. When there is an exception, a file gets created whose size will be 0 bytes. We need to delete this file. Since the file is not closed during the exception, we can't delete the file.

We could fix the issue if we could close the streams in the jar classes, but we can't modify the jars that create the files as they are client specific jars.

Please suggest how to delete the opened file, without modifying the code in class A.

6

There are 6 best solutions below

7
On

Make sure you close the file, even if there was an Exception when writing to it.

E.g.

public void run(String file) throws Exception {
    FileOutputStream s = null;
    try {
        s = new FileOutputStream(file);    
    } finally {
        try {
            s.close();
        } catch(Exception e) {
            // log this exception
        }
    }
}
0
On

You have to close the file before any delete operation as firstly its a bad practice and second is it will lead to memory leaks.

0
On

If you are using Tomcat, it is possible to set AntiLockingOption and antiJARLocking in $CATALINA_HOME/conf/context.xml for Windows:

<Context antiJARLocking="true" antiResourceLocking="true" >

Important note:

The antiResourceLocking option can stop JSPs from redeploying when they are edited requiring a redeploy.

Read more about this option: http://tomcat.apache.org/tomcat-7.0-doc/config/context.html

antiResourceLocking:

If true, Tomcat will prevent any file locking. This will significantly impact startup time of applications, but allows full webapp hot deploy and undeploy on platforms or configurations where file locking can occur. If not specified, the default value is false.

3
On

Updated according to OPs comment. Another approach could be to subclass A and override run().

public static void main(String[] args) throws Exception  {
        String path = "D:\\CONFLUX_HOME\\TestClient\\Maps\\test\\newTest.txt";


        A a = new A() {
            @Override
            public void run(String file) throws Exception {
                FileOutputStream s = new FileOutputStream(file);
                s.close();
            }
        };
        a.run(path);

        File f = new File(path);

        Files.delete(Paths.get(f.getAbsolutePath()));
        System.out.println("foo");
    }
0
On

Pass the resource as a parameter and it becomes the caller's responsibility to clear up the resources

   public void run(FileOutputStream stream) throws Exception {
     ...
   }

caller:

try(FileStream stream = new FileStream(path)){
      A a = new A();
      a.run(stream);
}catch(Exception e){
  .. exception handling
}
0
On

I don't think you'll find a pure java solution to this problem. One option is to install Unlocker (being careful to hit "Skip" on all the junkware) and invoke it from your code.

If you have UAC enabled, you'll also need to be running your java in an elevated process (e.g. start command prompt as Administrator). Then, assuming unlocker is in C:\Program Files\Unlocker:

Process p = new ProcessBuilder("c:\\Program Files\\Unlocker\\Unlocker.exe",path,"-s").start();
p.waitFor();

And after that you can delete the file as before. Or you could use "-d" instead of "-s" and Unlocker will delete the file for you.