From f76fee63ed9cb3a30d3c0c092d860b1cb93a481b Mon Sep 17 00:00:00 2001
From: Gerard Smyth <gerard.smyth@gmail.com>
Date: Thu, 08 May 2014 13:09:30 -0400
Subject: [PATCH] Updated the SyndicationServlet to provide an additional option to return details of the tags in the repository instead of the commits. This uses a new 'ot' request parameter to indicate the object type of the content to return, which can be ither TAG or COMMIT. If this is not provided, then COMMIT is assumed to maintain backwards compatability. If tags are returned, then the paging parameters, 'l' and 'pg' are still supported, but searching options are currently ignored.
---
src/main/java/com/gitblit/manager/RepositoryManager.java | 181 ++++++++++++++++++++++++++++++++++++---------
1 files changed, 144 insertions(+), 37 deletions(-)
diff --git a/src/main/java/com/gitblit/manager/RepositoryManager.java b/src/main/java/com/gitblit/manager/RepositoryManager.java
index 9d38b30..7351eb9 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;
}
@@ -418,8 +421,9 @@
// update the fork origin repository with this repository clone
if (!StringUtils.isEmpty(model.originRepository)) {
- if (repositoryListCache.containsKey(model.originRepository)) {
- RepositoryModel origin = repositoryListCache.get(model.originRepository);
+ String originKey = model.originRepository.toLowerCase();
+ if (repositoryListCache.containsKey(originKey)) {
+ RepositoryModel origin = repositoryListCache.get(originKey);
origin.addFork(model.name);
}
}
@@ -447,6 +451,19 @@
private void clearRepositoryMetadataCache(String repositoryName) {
repositorySizeCache.remove(repositoryName);
repositoryMetricsCache.remove(repositoryName);
+ CommitCache.instance().clear(repositoryName);
+ }
+
+ /**
+ * Reset all caches for this repository.
+ *
+ * @param repositoryName
+ * @since 1.5.1
+ */
+ @Override
+ public void resetRepositoryCache(String repositoryName) {
+ removeFromCachedRepositoryList(repositoryName);
+ clearRepositoryMetadataCache(repositoryName);
}
/**
@@ -457,6 +474,9 @@
public void resetRepositoryListCache() {
logger.info("Repository cache manually reset");
repositoryListCache.clear();
+ repositorySizeCache.clear();
+ repositoryMetricsCache.clear();
+ CommitCache.instance().clear();
}
/**
@@ -527,8 +547,9 @@
// rebuild fork networks
for (RepositoryModel model : repositoryListCache.values()) {
if (!StringUtils.isEmpty(model.originRepository)) {
- if (repositoryListCache.containsKey(model.originRepository)) {
- RepositoryModel origin = repositoryListCache.get(model.originRepository);
+ String originKey = model.originRepository.toLowerCase();
+ if (repositoryListCache.containsKey(originKey)) {
+ RepositoryModel origin = repositoryListCache.get(originKey);
origin.addFork(model.name);
}
}
@@ -778,10 +799,11 @@
model.projectPath = StringUtils.getFirstPathElement(repositoryName);
StoredConfig config = r.getConfig();
- boolean hasOrigin = !StringUtils.isEmpty(config.getString("remote", "origin", "url"));
+ boolean hasOrigin = false;
if (config != null) {
// Initialize description from description file
+ hasOrigin = !StringUtils.isEmpty(config.getString("remote", "origin", "url"));
if (getConfig(config,"description", null) == null) {
File descFile = new File(r.getDirectory(), "description");
if (descFile.exists()) {
@@ -794,6 +816,10 @@
model.description = getConfig(config, "description", "");
model.originRepository = getConfig(config, "originRepository", null);
model.addOwners(ArrayUtils.fromString(getConfig(config, "owner", "")));
+ model.acceptNewPatchsets = getConfig(config, "acceptNewPatchsets", true);
+ model.acceptNewTickets = getConfig(config, "acceptNewTickets", true);
+ model.requireApproval = getConfig(config, "requireApproval", settings.getBoolean(Keys.tickets.requireApproval, false));
+ model.mergeTo = getConfig(config, "mergeTo", null);
model.useIncrementalPushTags = getConfig(config, "useIncrementalPushTags", false);
model.incrementalPushTagPrefix = getConfig(config, "incrementalPushTagPrefix", null);
model.allowForks = getConfig(config, "allowForks", true);
@@ -844,6 +870,9 @@
}
}
model.HEAD = JGitUtils.getHEADRef(r);
+ if (StringUtils.isEmpty(model.mergeTo)) {
+ model.mergeTo = model.HEAD;
+ }
model.availableRefs = JGitUtils.getAvailableHeadTargets(r);
model.sparkleshareId = JGitUtils.getSparkleshareId(r);
model.hasCommits = JGitUtils.hasCommits(r);
@@ -928,26 +957,31 @@
*/
@Override
public String getFork(String username, String origin) {
+ if (StringUtils.isEmpty(origin)) {
+ return null;
+ }
String userProject = ModelUtils.getPersonalPath(username);
if (settings.getBoolean(Keys.git.cacheRepositoryList, true)) {
+ String originKey = origin.toLowerCase();
String userPath = userProject + "/";
// collect all origin nodes in fork network
Set<String> roots = new HashSet<String>();
- roots.add(origin);
- RepositoryModel originModel = repositoryListCache.get(origin);
+ roots.add(originKey);
+ RepositoryModel originModel = repositoryListCache.get(originKey);
while (originModel != null) {
if (!ArrayUtils.isEmpty(originModel.forks)) {
for (String fork : originModel.forks) {
if (!fork.startsWith(userPath)) {
- roots.add(fork);
+ roots.add(fork.toLowerCase());
}
}
}
if (originModel.originRepository != null) {
- roots.add(originModel.originRepository);
- originModel = repositoryListCache.get(originModel.originRepository);
+ String ooKey = originModel.originRepository.toLowerCase();
+ roots.add(ooKey);
+ originModel = repositoryListCache.get(ooKey);
} else {
// break
originModel = null;
@@ -958,7 +992,7 @@
if (repository.startsWith(userPath)) {
RepositoryModel model = repositoryListCache.get(repository);
if (!StringUtils.isEmpty(model.originRepository)) {
- if (roots.contains(model.originRepository)) {
+ if (roots.contains(model.originRepository.toLowerCase())) {
// user has a fork in this graph
return model.name;
}
@@ -975,7 +1009,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;
}
@@ -998,7 +1032,7 @@
// find the root, cached
RepositoryModel model = repositoryListCache.get(repository.toLowerCase());
while (model.originRepository != null) {
- model = repositoryListCache.get(model.originRepository);
+ model = repositoryListCache.get(model.originRepository.toLowerCase());
}
ForkModel root = getForkModelFromCache(model.name);
return root;
@@ -1078,12 +1112,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 +1179,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 +1317,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()) {
@@ -1292,7 +1363,7 @@
// update this repository's origin's fork list
if (!StringUtils.isEmpty(repository.originRepository)) {
- RepositoryModel origin = repositoryListCache.get(repository.originRepository);
+ RepositoryModel origin = repositoryListCache.get(repository.originRepository.toLowerCase());
if (origin != null && !ArrayUtils.isEmpty(origin.forks)) {
origin.forks.remove(repositoryName);
origin.forks.add(repository.name);
@@ -1362,6 +1433,18 @@
config.setString(Constants.CONFIG_GITBLIT, null, "description", repository.description);
config.setString(Constants.CONFIG_GITBLIT, null, "originRepository", repository.originRepository);
config.setString(Constants.CONFIG_GITBLIT, null, "owner", ArrayUtils.toString(repository.owners));
+ config.setBoolean(Constants.CONFIG_GITBLIT, null, "acceptNewPatchsets", repository.acceptNewPatchsets);
+ config.setBoolean(Constants.CONFIG_GITBLIT, null, "acceptNewTickets", repository.acceptNewTickets);
+ if (settings.getBoolean(Keys.tickets.requireApproval, false) == repository.requireApproval) {
+ // use default
+ config.unset(Constants.CONFIG_GITBLIT, null, "requireApproval");
+ } else {
+ // override default
+ config.setBoolean(Constants.CONFIG_GITBLIT, null, "requireApproval", repository.requireApproval);
+ }
+ if (!StringUtils.isEmpty(repository.mergeTo)) {
+ config.setString(Constants.CONFIG_GITBLIT, null, "mergeTo", repository.mergeTo);
+ }
config.setBoolean(Constants.CONFIG_GITBLIT, null, "useIncrementalPushTags", repository.useIncrementalPushTags);
if (StringUtils.isEmpty(repository.incrementalPushTagPrefix) ||
repository.incrementalPushTagPrefix.equals(settings.getString(Keys.git.defaultIncrementalPushTagPrefix, "r"))) {
@@ -1471,7 +1554,7 @@
@Override
public boolean deleteRepository(String repositoryName) {
try {
- closeRepository(repositoryName);
+ close(repositoryName);
// clear the repository cache
clearRepositoryMetadataCache(repositoryName);
@@ -1644,16 +1727,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 +1757,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 +1771,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 +1805,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 +1837,23 @@
daysToCache, commitCount, repoCount, TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start)));
}
}
+
+ protected void confirmWriteAccess() {
+ if (runtimeManager.isServingRepositories()) {
+ try {
+ if (!getRepositoriesFolder().exists()) {
+ getRepositoriesFolder().mkdirs();
+ }
+ 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