From eb0f7ea03074bb45c67d2ad877af3c5c85e6b3fc Mon Sep 17 00:00:00 2001 From: James Moger <james.moger@gitblit.com> Date: Mon, 03 Mar 2014 11:33:24 -0500 Subject: [PATCH] Fix compare page exception when a submodule changes (issue-375) --- src/main/java/com/gitblit/manager/RepositoryManager.java | 113 ++++++++++++++++++++++++++++++++++++++++++++------------ 1 files changed, 89 insertions(+), 24 deletions(-) diff --git a/src/main/java/com/gitblit/manager/RepositoryManager.java b/src/main/java/com/gitblit/manager/RepositoryManager.java index 9d38b30..e412deb 100644 --- a/src/main/java/com/gitblit/manager/RepositoryManager.java +++ b/src/main/java/com/gitblit/manager/RepositoryManager.java @@ -63,12 +63,9 @@ import com.gitblit.Constants.FederationStrategy; import com.gitblit.Constants.PermissionType; import com.gitblit.Constants.RegistrantType; -import com.gitblit.GCExecutor; import com.gitblit.GitBlitException; import com.gitblit.IStoredSettings; import com.gitblit.Keys; -import com.gitblit.LuceneExecutor; -import com.gitblit.MirrorExecutor; import com.gitblit.models.ForkModel; import com.gitblit.models.Metric; import com.gitblit.models.RefModel; @@ -77,6 +74,9 @@ import com.gitblit.models.SearchResult; import com.gitblit.models.TeamModel; import com.gitblit.models.UserModel; +import com.gitblit.service.GarbageCollectorService; +import com.gitblit.service.LuceneService; +import com.gitblit.service.MirrorService; import com.gitblit.utils.ArrayUtils; import com.gitblit.utils.ByteFormat; import com.gitblit.utils.CommitCache; @@ -118,11 +118,11 @@ private final File repositoriesFolder; - private LuceneExecutor luceneExecutor; + private LuceneService luceneExecutor; - private GCExecutor gcExecutor; + private GarbageCollectorService gcExecutor; - private MirrorExecutor mirrorExecutor; + private MirrorService mirrorExecutor; public RepositoryManager( IRuntimeManager runtimeManager, @@ -135,8 +135,8 @@ } @Override - public IManager setup() { - logger.info("Git repositories folder = " + repositoriesFolder.getAbsolutePath()); + public RepositoryManager start() { + logger.info("Repositories folder : {}", repositoriesFolder.getAbsolutePath()); // initialize utilities String prefix = settings.getString(Keys.git.userRepositoryPrefix, "~"); @@ -147,7 +147,7 @@ // build initial repository list if (settings.getBoolean(Keys.git.cacheRepositoryList, true)) { - logger.info("Identifying available repositories..."); + logger.info("Identifying repositories..."); getRepositoryList(); } @@ -157,16 +157,19 @@ configureJGit(); configureCommitCache(); + confirmWriteAccess(); + return this; } @Override - public IManager stop() { + public RepositoryManager stop() { scheduledExecutor.shutdownNow(); luceneExecutor.close(); gcExecutor.close(); mirrorExecutor.close(); + closeAll(); return this; } @@ -447,6 +450,7 @@ private void clearRepositoryMetadataCache(String repositoryName) { repositorySizeCache.remove(repositoryName); repositoryMetricsCache.remove(repositoryName); + CommitCache.instance().clear(repositoryName); } /** @@ -457,6 +461,9 @@ public void resetRepositoryListCache() { logger.info("Repository cache manually reset"); repositoryListCache.clear(); + repositorySizeCache.clear(); + repositoryMetricsCache.clear(); + CommitCache.instance().clear(); } /** @@ -975,7 +982,7 @@ settings.getStrings(Keys.git.searchExclusions)); for (String repository : repositories) { RepositoryModel model = getRepositoryModel(userProject + "/" + repository); - if (model.originRepository.equalsIgnoreCase(origin)) { + if (model.originRepository != null && model.originRepository.equalsIgnoreCase(origin)) { // user has a fork return model.name; } @@ -1078,12 +1085,49 @@ } /** + * Returns true if the repository is idle (not being accessed). + * + * @param repository + * @return true if the repository is idle + */ + @Override + public boolean isIdle(Repository repository) { + try { + // Read the use count. + // An idle use count is 2: + // +1 for being in the cache + // +1 for the repository parameter in this method + Field useCnt = Repository.class.getDeclaredField("useCnt"); + useCnt.setAccessible(true); + int useCount = ((AtomicInteger) useCnt.get(repository)).get(); + return useCount == 2; + } catch (Exception e) { + logger.warn(MessageFormat + .format("Failed to reflectively determine use count for repository {0}", + repository.getDirectory().getPath()), e); + } + return false; + } + + /** + * Ensures that all cached repository are completely closed and their resources + * are properly released. + */ + @Override + public void closeAll() { + for (String repository : getRepositoryList()) { + close(repository); + } + } + + /** * Ensure that a cached repository is completely closed and its resources * are properly released. * * @param repositoryName */ - private void closeRepository(String repositoryName) { + @Override + public void close(String repositoryName) { Repository repository = getRepository(repositoryName); if (repository == null) { return; @@ -1108,7 +1152,7 @@ repositoryName), e); } if (uses > 0) { - logger.info(MessageFormat + logger.debug(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++) { @@ -1246,7 +1290,7 @@ "Failed to rename ''{0}'' because ''{1}'' already exists.", repositoryName, repository.name)); } - closeRepository(repositoryName); + close(repositoryName); File folder = new File(repositoriesFolder, repositoryName); File destFolder = new File(repositoriesFolder, repository.name); if (destFolder.exists()) { @@ -1471,7 +1515,7 @@ @Override public boolean deleteRepository(String repositoryName) { try { - closeRepository(repositoryName); + close(repositoryName); // clear the repository cache clearRepositoryMetadataCache(repositoryName); @@ -1644,16 +1688,17 @@ } protected void configureLuceneIndexing() { - luceneExecutor = new LuceneExecutor(settings, this); - scheduledExecutor.scheduleAtFixedRate(luceneExecutor, 1, 2, TimeUnit.MINUTES); - logger.info("Lucene executor is scheduled to process indexed branches every 2 minutes."); + luceneExecutor = new LuceneService(settings, this); + int period = 2; + scheduledExecutor.scheduleAtFixedRate(luceneExecutor, 1, period, TimeUnit.MINUTES); + logger.info("Lucene will process indexed branches every {} minutes.", period); } protected void configureGarbageCollector() { // schedule gc engine - gcExecutor = new GCExecutor(settings, this); + gcExecutor = new GarbageCollectorService(settings, this); if (gcExecutor.isReady()) { - logger.info("GC executor is scheduled to scan repositories every 24 hours."); + logger.info("Garbage Collector (GC) will scan repositories every 24 hours."); Calendar c = Calendar.getInstance(); c.set(Calendar.HOUR_OF_DAY, settings.getInteger(Keys.git.garbageCollectionHour, 0)); c.set(Calendar.MINUTE, 0); @@ -1673,11 +1718,13 @@ } logger.info(MessageFormat.format("Next scheculed GC scan is in {0}", when)); scheduledExecutor.scheduleAtFixedRate(gcExecutor, delay, 60 * 24, TimeUnit.MINUTES); + } else { + logger.info("Garbage Collector (GC) is disabled."); } } protected void configureMirrorExecutor() { - mirrorExecutor = new MirrorExecutor(settings, this); + mirrorExecutor = new MirrorService(settings, this); if (mirrorExecutor.isReady()) { int mins = TimeUtils.convertFrequencyToMinutes(settings.getString(Keys.git.mirrorPeriod, "30 mins")); if (mins < 5) { @@ -1685,8 +1732,10 @@ } int delay = 1; scheduledExecutor.scheduleAtFixedRate(mirrorExecutor, delay, mins, TimeUnit.MINUTES); - logger.info("Mirror executor is scheduled to fetch updates every {} minutes.", mins); + logger.info("Mirror service will fetch updates every {} minutes.", mins); logger.info("Next scheduled mirror fetch is in {} minutes", delay); + } else { + logger.info("Mirror service is disabled."); } } @@ -1717,12 +1766,12 @@ protected void configureCommitCache() { int daysToCache = settings.getInteger(Keys.web.activityCacheDays, 14); if (daysToCache <= 0) { - logger.info("commit cache disabled"); + logger.info("Commit cache is disabled"); } else { long start = System.nanoTime(); long repoCount = 0; long commitCount = 0; - logger.info(MessageFormat.format("preparing {0} day commit cache. please wait...", daysToCache)); + logger.info(MessageFormat.format("Preparing {0} day commit cache. please wait...", daysToCache)); CommitCache.instance().setCacheDays(daysToCache); Date cutoff = CommitCache.instance().getCutoffDate(); for (String repositoryName : getRepositoryList()) { @@ -1749,4 +1798,20 @@ daysToCache, commitCount, repoCount, TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start))); } } + + protected void confirmWriteAccess() { + if (runtimeManager.isServingRepositories()) { + try { + File file = File.createTempFile(".test-", ".txt", getRepositoriesFolder()); + file.delete(); + } catch (Exception e) { + logger.error(""); + logger.error(Constants.BORDER2); + logger.error("Please check filesystem permissions!"); + logger.error("FAILED TO WRITE TO REPOSITORIES FOLDER!!", e); + logger.error(Constants.BORDER2); + logger.error(""); + } + } + } } -- Gitblit v1.9.1