您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

ProjectManager.java 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. /*
  2. * Copyright 2013 gitblit.com.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package com.gitblit.manager;
  17. import java.io.File;
  18. import java.text.MessageFormat;
  19. import java.util.ArrayList;
  20. import java.util.Collections;
  21. import java.util.Date;
  22. import java.util.HashMap;
  23. import java.util.LinkedHashMap;
  24. import java.util.List;
  25. import java.util.Map;
  26. import java.util.TreeMap;
  27. import java.util.concurrent.ConcurrentHashMap;
  28. import org.eclipse.jgit.storage.file.FileBasedConfig;
  29. import org.eclipse.jgit.util.FS;
  30. import org.slf4j.Logger;
  31. import org.slf4j.LoggerFactory;
  32. import com.gitblit.IStoredSettings;
  33. import com.gitblit.Keys;
  34. import com.gitblit.models.ProjectModel;
  35. import com.gitblit.models.RepositoryModel;
  36. import com.gitblit.models.UserModel;
  37. import com.gitblit.utils.DeepCopier;
  38. import com.gitblit.utils.ModelUtils;
  39. import com.gitblit.utils.ObjectCache;
  40. import com.gitblit.utils.StringUtils;
  41. /**
  42. * Project manager handles project-related functions.
  43. *
  44. * @author James Moger
  45. *
  46. */
  47. public class ProjectManager implements IProjectManager {
  48. private final Logger logger = LoggerFactory.getLogger(getClass());
  49. private final Map<String, ProjectModel> projectCache = new ConcurrentHashMap<String, ProjectModel>();
  50. private final ObjectCache<String> projectMarkdownCache = new ObjectCache<String>();
  51. private final ObjectCache<String> projectRepositoriesMarkdownCache = new ObjectCache<String>();
  52. private final IStoredSettings settings;
  53. private final IRuntimeManager runtimeManager;
  54. private final IUserManager userManager;
  55. private final IRepositoryManager repositoryManager;
  56. private FileBasedConfig projectConfigs;
  57. public ProjectManager(
  58. IRuntimeManager runtimeManager,
  59. IUserManager userManager,
  60. IRepositoryManager repositoryManager) {
  61. this.settings = runtimeManager.getSettings();
  62. this.runtimeManager = runtimeManager;
  63. this.userManager = userManager;
  64. this.repositoryManager = repositoryManager;
  65. }
  66. @Override
  67. public ProjectManager start() {
  68. // load and cache the project metadata
  69. projectConfigs = new FileBasedConfig(runtimeManager.getFileOrFolder(Keys.web.projectsFile, "${baseFolder}/projects.conf"), FS.detect());
  70. getProjectConfigs();
  71. return this;
  72. }
  73. @Override
  74. public ProjectManager stop() {
  75. return this;
  76. }
  77. private void reloadProjectMarkdown(ProjectModel project) {
  78. // project markdown
  79. File pmkd = new File(repositoryManager.getRepositoriesFolder(), (project.isRoot ? "" : project.name) + "/project.mkd");
  80. if (pmkd.exists()) {
  81. Date lm = new Date(pmkd.lastModified());
  82. if (!projectMarkdownCache.hasCurrent(project.name, lm)) {
  83. String mkd = com.gitblit.utils.FileUtils.readContent(pmkd, "\n");
  84. projectMarkdownCache.updateObject(project.name, lm, mkd);
  85. }
  86. project.projectMarkdown = projectMarkdownCache.getObject(project.name);
  87. }
  88. // project repositories markdown
  89. File rmkd = new File(repositoryManager.getRepositoriesFolder(), (project.isRoot ? "" : project.name) + "/repositories.mkd");
  90. if (rmkd.exists()) {
  91. Date lm = new Date(rmkd.lastModified());
  92. if (!projectRepositoriesMarkdownCache.hasCurrent(project.name, lm)) {
  93. String mkd = com.gitblit.utils.FileUtils.readContent(rmkd, "\n");
  94. projectRepositoriesMarkdownCache.updateObject(project.name, lm, mkd);
  95. }
  96. project.repositoriesMarkdown = projectRepositoriesMarkdownCache.getObject(project.name);
  97. }
  98. }
  99. /**
  100. * Returns the map of project config. This map is cached and reloaded if
  101. * the underlying projects.conf file changes.
  102. *
  103. * @return project config map
  104. */
  105. private Map<String, ProjectModel> getProjectConfigs() {
  106. if (projectCache.isEmpty() || projectConfigs.isOutdated()) {
  107. try {
  108. projectConfigs.load();
  109. } catch (Exception e) {
  110. }
  111. // project configs
  112. String rootName = settings.getString(Keys.web.repositoryRootGroupName, "main");
  113. ProjectModel rootProject = new ProjectModel(rootName, true);
  114. Map<String, ProjectModel> configs = new HashMap<String, ProjectModel>();
  115. // cache the root project under its alias and an empty path
  116. configs.put("", rootProject);
  117. configs.put(rootProject.name.toLowerCase(), rootProject);
  118. for (String name : projectConfigs.getSubsections("project")) {
  119. ProjectModel project;
  120. if (name.equalsIgnoreCase(rootName)) {
  121. project = rootProject;
  122. } else {
  123. project = new ProjectModel(name);
  124. }
  125. project.title = projectConfigs.getString("project", name, "title");
  126. project.description = projectConfigs.getString("project", name, "description");
  127. reloadProjectMarkdown(project);
  128. configs.put(name.toLowerCase(), project);
  129. }
  130. projectCache.clear();
  131. projectCache.putAll(configs);
  132. }
  133. return projectCache;
  134. }
  135. /**
  136. * Returns a list of project models for the user.
  137. *
  138. * @param user
  139. * @param includeUsers
  140. * @return list of projects that are accessible to the user
  141. */
  142. @Override
  143. public List<ProjectModel> getProjectModels(UserModel user, boolean includeUsers) {
  144. Map<String, ProjectModel> configs = getProjectConfigs();
  145. // per-user project lists, this accounts for security and visibility
  146. Map<String, ProjectModel> map = new TreeMap<String, ProjectModel>();
  147. // root project
  148. map.put("", configs.get(""));
  149. for (RepositoryModel model : repositoryManager.getRepositoryModels(user)) {
  150. String projectPath = StringUtils.getRootPath(model.name);
  151. String projectKey = projectPath.toLowerCase();
  152. if (!map.containsKey(projectKey)) {
  153. ProjectModel project;
  154. if (configs.containsKey(projectKey)) {
  155. // clone the project model because it's repository list will
  156. // be tailored for the requesting user
  157. project = DeepCopier.copy(configs.get(projectKey));
  158. } else {
  159. project = new ProjectModel(projectPath);
  160. }
  161. map.put(projectKey, project);
  162. }
  163. map.get(projectKey).addRepository(model);
  164. }
  165. // sort projects, root project first
  166. List<ProjectModel> projects;
  167. if (includeUsers) {
  168. // all projects
  169. projects = new ArrayList<ProjectModel>(map.values());
  170. Collections.sort(projects);
  171. projects.remove(map.get(""));
  172. projects.add(0, map.get(""));
  173. } else {
  174. // all non-user projects
  175. projects = new ArrayList<ProjectModel>();
  176. ProjectModel root = map.remove("");
  177. for (ProjectModel model : map.values()) {
  178. if (!model.isUserProject()) {
  179. projects.add(model);
  180. }
  181. }
  182. Collections.sort(projects);
  183. projects.add(0, root);
  184. }
  185. return projects;
  186. }
  187. /**
  188. * Returns the project model for the specified user.
  189. *
  190. * @param name
  191. * @param user
  192. * @return a project model, or null if it does not exist
  193. */
  194. @Override
  195. public ProjectModel getProjectModel(String name, UserModel user) {
  196. for (ProjectModel project : getProjectModels(user, true)) {
  197. if (project.name.equalsIgnoreCase(name)) {
  198. return project;
  199. }
  200. }
  201. return null;
  202. }
  203. /**
  204. * Returns a project model for the Gitblit/system user.
  205. *
  206. * @param name a project name
  207. * @return a project model or null if the project does not exist
  208. */
  209. @Override
  210. public ProjectModel getProjectModel(String name) {
  211. Map<String, ProjectModel> configs = getProjectConfigs();
  212. ProjectModel project = configs.get(name.toLowerCase());
  213. if (project == null) {
  214. project = new ProjectModel(name);
  215. if (ModelUtils.isPersonalRepository(name)) {
  216. UserModel user = userManager.getUserModel(ModelUtils.getUserNameFromRepoPath(name));
  217. if (user != null) {
  218. project.title = user.getDisplayName();
  219. project.description = "personal repositories";
  220. }
  221. }
  222. } else {
  223. // clone the object
  224. project = DeepCopier.copy(project);
  225. }
  226. if (StringUtils.isEmpty(name)) {
  227. // get root repositories
  228. for (String repository : repositoryManager.getRepositoryList()) {
  229. if (repository.indexOf('/') == -1) {
  230. project.addRepository(repository);
  231. }
  232. }
  233. } else {
  234. // get repositories in subfolder
  235. String folder = name.toLowerCase() + "/";
  236. for (String repository : repositoryManager.getRepositoryList()) {
  237. if (repository.toLowerCase().startsWith(folder)) {
  238. project.addRepository(repository);
  239. }
  240. }
  241. }
  242. if (project.repositories.size() == 0) {
  243. // no repositories == no project
  244. return null;
  245. }
  246. reloadProjectMarkdown(project);
  247. return project;
  248. }
  249. /**
  250. * Returns the list of project models that are referenced by the supplied
  251. * repository model list. This is an alternative method exists to ensure
  252. * Gitblit does not call getRepositoryModels(UserModel) twice in a request.
  253. *
  254. * @param repositoryModels
  255. * @param includeUsers
  256. * @return a list of project models
  257. */
  258. @Override
  259. public List<ProjectModel> getProjectModels(List<RepositoryModel> repositoryModels, boolean includeUsers) {
  260. Map<String, ProjectModel> projects = new LinkedHashMap<String, ProjectModel>();
  261. for (RepositoryModel repository : repositoryModels) {
  262. if (!includeUsers && repository.isPersonalRepository()) {
  263. // exclude personal repositories
  264. continue;
  265. }
  266. if (!projects.containsKey(repository.projectPath)) {
  267. ProjectModel project = getProjectModel(repository.projectPath);
  268. if (project == null) {
  269. logger.warn(MessageFormat.format("excluding project \"{0}\" from project list because it is empty!",
  270. repository.projectPath));
  271. continue;
  272. }
  273. projects.put(repository.projectPath, project);
  274. // clear the repo list in the project because that is the system
  275. // list, not the user-accessible list and start building the
  276. // user-accessible list
  277. project.repositories.clear();
  278. project.repositories.add(repository.name);
  279. project.lastChange = repository.lastChange;
  280. } else {
  281. // update the user-accessible list
  282. // this is used for repository count
  283. ProjectModel project = projects.get(repository.projectPath);
  284. project.repositories.add(repository.name);
  285. if (project.lastChange.before(repository.lastChange)) {
  286. project.lastChange = repository.lastChange;
  287. }
  288. }
  289. }
  290. return new ArrayList<ProjectModel>(projects.values());
  291. }
  292. }