From dd6f08950e36694d9757adc84ed6805620d0c032 Mon Sep 17 00:00:00 2001 From: James Moger Date: Wed, 20 Jul 2011 21:06:17 -0400 Subject: [PATCH] Fixed rename and delete repository (issue 10) This was due to use of the FileResolver which caches repository objects. Calling close() on a cached repository instance would decrement a counter, but the repository would never really close and release all its resources because the FileResolver "held" the first count/reference so the object and refs databases were never closed. The solution was to use reflection to determine the actual "useCnt" of the repository and then call close() that number of times. In practice, the "useCnt" is probably always 2, and that is the default value in case reflection fails. --- src/com/gitblit/GitBlit.java | 41 +++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/src/com/gitblit/GitBlit.java b/src/com/gitblit/GitBlit.java index e570e7bc..c54fbe14 100644 --- a/src/com/gitblit/GitBlit.java +++ b/src/com/gitblit/GitBlit.java @@ -17,6 +17,7 @@ package com.gitblit; import java.io.File; import java.io.IOException; +import java.lang.reflect.Field; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collections; @@ -24,6 +25,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.concurrent.atomic.AtomicInteger; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; @@ -141,7 +143,7 @@ public class GitBlit implements ServletContextListener { public static char getChar(String key, char defaultValue) { return self().settings.getChar(key, defaultValue); } - + /** * Returns the string value for the specified key. If the key does not exist * or the value for the key can not be interpreted as a string, the @@ -477,6 +479,41 @@ public class GitBlit implements ServletContextListener { return com.gitblit.utils.FileUtils.folderSize(gitDir); } + /** + * Ensure that a cached repository is completely closed and its resources + * are properly released. + * + * @param repositoryName + */ + private void closeRepository(String repositoryName) { + Repository repository = getRepository(repositoryName); + // assume 2 uses in case reflection fails + int uses = 2; + try { + // The FileResolver caches repositories which is very useful + // for performance until you want to delete a repository. + // I have to use reflection to call close() the correct + // number of times to ensure that the object and ref databases + // are properly closed before I can delete the repository from + // the filesystem. + Field useCnt = Repository.class.getDeclaredField("useCnt"); + useCnt.setAccessible(true); + uses = ((AtomicInteger) useCnt.get(repository)).get(); + } catch (Exception e) { + logger.warn(MessageFormat + .format("Failed to reflectively determine use count for repository {0}", + repositoryName), e); + } + if (uses > 0) { + logger.info(MessageFormat + .format("{0}.useCnt={1}, calling close() {2} time(s) to close object and ref databases", + repositoryName, uses, uses)); + for (int i = 0; i < uses; i++) { + repository.close(); + } + } + } + /** * Returns the gitblit string vlaue for the specified key. If key is not * set, returns defaultValue. @@ -540,6 +577,7 @@ public class GitBlit implements ServletContextListener { } else { // rename repository if (!repositoryName.equalsIgnoreCase(repository.name)) { + closeRepository(repositoryName); File folder = new File(repositoriesFolder, repositoryName); File destFolder = new File(repositoriesFolder, repository.name); if (destFolder.exists()) { @@ -615,6 +653,7 @@ public class GitBlit implements ServletContextListener { */ public boolean deleteRepository(String repositoryName) { try { + closeRepository(repositoryName); File folder = new File(repositoriesFolder, repositoryName); if (folder.exists() && folder.isDirectory()) { FileUtils.delete(folder, FileUtils.RECURSIVE | FileUtils.RETRY); -- 2.39.5