javaderbyembedded-database

Unable to delete Derby System Directory for Embedded Database


I am unable to delete the system directory upon calling shutdown=true for a Derby Embedded database on a Windows computer.

Here's a minimal example of my challenge:

package derbytest;

import java.io.File;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author aelder
 */
public class DerbyTest {

    private static final String DRIVER = "org.apache.derby.jdbc.EmbeddedDriver";
    private static final String CONN_URL = "jdbc:derby:EmbeddedDBAudit";

    private static File derbySystemFolder;
    private static final String USER_HOME_DIR = System.getProperty("user.home", ".");

    public static Connection getConnection(boolean createDatabase) throws SQLException {
        return DriverManager.getConnection(CONN_URL + (createDatabase ? ";create=true" : ""));
    }

    public static void shutdownConnectionAndCleanup() {
        try {
            DriverManager.getConnection(CONN_URL + ";shutdown=true");
        } catch (SQLException ex) {
            if (!ex.getSQLState().equals("08006")) {
                ex.printStackTrace();
            }
        }

        deleteFolder(derbySystemFolder);
    }

    public static void deleteFolder(File folder) {
        File[] files = folder.listFiles();
        if (files != null) { //some JVMs return null for empty dirs
            for (File f : files) {
                if (f.isDirectory()) {
                    deleteFolder(f);
                } else {
                    f.delete();
                }
            }
        }
        folder.delete();
    }

    public static void setDerbyHome() {
        setDatabaseFile("");

        int index = 1;
        while (derbySystemFolder.exists()) {
            setDatabaseFile(String.valueOf(index++));
        }

        // Set the db system directory.
        System.setProperty("derby.system.home", derbySystemFolder.getAbsolutePath());
    }

    private static void setDatabaseFile(String auditFolderCount) {
        String databaseFilePATH = USER_HOME_DIR + File.separator + ".EmbeddedDBAudit" + auditFolderCount;

        derbySystemFolder = new File(databaseFilePATH);
        derbySystemFolder.deleteOnExit();
    }

    public static void initDerbyHomeAndDriver() {
        setDerbyHome();

        initDerbyDriverInstance();
    }

    public static void initDerbyDriverInstance() {
        try {
            Class.forName(DRIVER).newInstance();
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException ex) {
            Logger.getLogger(DerbyTest.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public static boolean tableAlreadyExists(SQLException e) {
        return e.getSQLState().equals("X0Y32");
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        try {
            initDerbyHomeAndDriver();
            getConnection(true);
            shutdownConnectionAndCleanup();
        } catch (SQLException ex) {
            Logger.getLogger(DerbyTest.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}

I have tried using external libraries to delete the folder as well such as the apache's org.apache.commons.io.FileDeleteStrategy.FORCE.delete(file); or import org.apache.commons.io.FileUtils.deleteDirectory(file);. It seems like the derby system still hangs onto the file even after the database is shutdown.

Desired behavior: Delete the system directory on exit.

Edit:

Windows process explorer shows me that derby.log is still open after the database connection is shutdown:

enter image description here


Solution

  • Assuming that it's Derby code itself which has failed to close this file, this seems like a bug to me.

    After you shut down the Engine, Derby clearly should close its log and release all its references to it.

    If you have access to a tool such as https://www.eclipse.org/mat/ you might be able to find which thread (and even which stack frame?) has allocated this reference to derby.log and failed to release it.

    If you can encapsulate this behavior in a small test program which demonstrates it, I encourage you to log a bug against Derby (http://db.apache.org/derby/DerbyBugGuidelines.html) so that the developers can reproduce it themselves and figure out how to fix it.

    In the meantime, you may be able to work around the problem by disabling the derby.log file, or by relocating the derby.log file to another directory.

    That's clearly not a fix, but it might be an improvement in the behavior such that this flaw isn't blocking your work anymore? Here's some documentation about how to control the derby.log file: https://builds.apache.org/job/Derby-docs/lastSuccessfulBuild/artifact/trunk/out/devguide/cdevdvlp25889.html