Search code examples
javajsonjavafxzipapache-commons-vfs

Deleting zip file after accessing with apache commons vfs2 in Java


I'm trying to experiment with apache commons vfs2 to read content of a zip file as if it is just another directory. The zip files will ultimately hold all kinds of files (perhaps even a small database).

For illustration purposes, I have created some zip files containing a file called project.json which is json for a project with a name and a description. When I get the zip file, I want to read the json file to get the project definition information, etc. from within.

This all works great. Now, I want my users to be able to delete these files from within the application. This is where things go south on me.

I think I closed all of the input streams and such, but there appears to be a lock on the file from within the application that will not allow me to delete it.

  • If I create the zip file within the context of my application, I am allowed to delete it.
  • If I read it from the file system and DO NOT access it using vfs2, I AM allowed to delete it.
  • If I read it from the file system and access it using vfs2, I am NOT allowed to delete it.

Here's a simple json string if you want to create your own zip file to try it.

{"project": {"name": "ProjectX", "description": "X is a project."}}

Copy that to a file called project.json, and save it. Then, zip it into a zip file in the root directory of the Java project.

The following example illustrates my dilemma:

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;

import org.apache.commons.vfs2.FileContent;
import org.apache.commons.vfs2.FileObject;
import org.apache.commons.vfs2.FileSystemManager;
import org.apache.commons.vfs2.VFS;
import org.json.JSONObject;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.stage.Stage;

public class FileLockExample extends Application {

    @Override
    public void start(Stage arg0) throws Exception {
        ObservableList<File> files = retrieveFiles();
        deleteFiles(files);
        Platform.exit();
    }

    public static void main(String[] args) {
        Application.launch(args);
    }

    public ObservableList<File> retrieveFiles() {
        File[] files = new File(".").listFiles(new FilenameFilter() { 
            @Override 
            public boolean accept(File dir, String name) { 
                return name.endsWith(".zip");
            }
        });
        for (File file : files) {
            try {
                FileSystemManager fsManager = VFS.getManager();
                FileObject zipFile = fsManager.resolveFile("zip:" + file.toURI());
                FileObject projectJson = fsManager.resolveFile(zipFile, "project.json");
                FileContent content = projectJson.getContent();

                InputStream in = content.getInputStream();
                byte[] buffer = new byte[1024];
                StringBuilder sb = new StringBuilder();
                int len;
                while ((len = in.read(buffer)) > 0) {
                    sb.append(new String(buffer, 0, len));
                }
                in.close();
                String json = sb.toString();

                JSONObject obj = new JSONObject(json);
                String name = obj.getJSONObject("project").getString("name");
                String description = obj.getJSONObject("project").getString("description");
                System.out.println("Found project : " + name + "(" + description + ")");
                content.close();
                projectJson.close();
                zipFile.close();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return FXCollections.observableArrayList(files);
    }

    public void deleteFiles(ObservableList<File> files) {
        for (File file : files) {
            System.out.println("Deleting " + file.getName());
            file.deleteOnExit();
        }
    }
}

Solution

  • In light of the fact that no one seemed interested in my question, it caused me to reflect on the use of Apache Commons VFS in this case. I decided to explore other options, and was able to accomplish my short-term goal by using the java.nio API. Now, on to the longer-term goal of attempting to store other types of files in the zip file, and access them in a random fashion.

    In case anyone cares, here is my sample code to read from and delete zip files from the file system without having to resort to InputStream and OutputStream.

    import java.io.File;
    import java.io.FilenameFilter;
    import java.io.IOException;
    import java.net.URI;
    import java.net.URISyntaxException;
    import java.nio.file.*;
    import java.nio.file.spi.FileSystemProvider;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    import org.json.JSONObject;
    
    import javafx.application.Application;
    import javafx.application.Platform;
    import javafx.collections.FXCollections;
    import javafx.collections.ObservableList;
    import javafx.stage.Stage;
    
    public class FileLockExample extends Application {
    
        @Override
        public void start(Stage arg0) throws Exception {
            ObservableList<File> files = retrieveFiles();
            deleteFiles(files);
            Platform.exit();
        }
    
        public static void main(String[] args) {
            Application.launch(args);
        }
    
        public ObservableList<File> retrieveFiles() {
            File[] files = new File(".").listFiles(new FilenameFilter() { 
                @Override 
                public boolean accept(File dir, String name) { 
                    return name.endsWith(".zip");
                }
            });
            for (File file : files) {
                try {
                    Map<String, Object> env = new HashMap<>();
                    FileSystemProvider provider = getZipFSProvider();
                    URI uri = new URI("jar:" + file.toURI());
                    FileSystem zipfs = provider.newFileSystem(uri, env);
                    List<String> jsonList = Files.readAllLines(zipfs.getPath("/project.json"));
                    StringBuilder sb = new StringBuilder();
                    for (String string : jsonList) {
                        sb.append(string);
                    }
                    String json = sb.toString();
                    JSONObject obj = new JSONObject(json);
                    String name = obj.getJSONObject("project").getString("name");
                    String description = obj.getJSONObject("project").getString("description");
                    System.out.println("Found project : " + name + " (" + description + ")");
                } catch (URISyntaxException use) {
                    use.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return FXCollections.observableArrayList(files);
        }
    
        public void deleteFiles(ObservableList<File> files) {
            for (File file : files) {
                System.out.println("Deleting " + file.getName());
                file.deleteOnExit();
            }
        }
    
        private static FileSystemProvider getZipFSProvider() {
            for (FileSystemProvider provider : FileSystemProvider.installedProviders()) {
                if ("jar".equals(provider.getScheme()))
                    return provider;
            }
            return null;
        }
    
    }