diff options
author | James Moger <james.moger@gitblit.com> | 2013-11-20 22:12:54 -0500 |
---|---|---|
committer | James Moger <james.moger@gitblit.com> | 2013-11-29 11:05:51 -0500 |
commit | a1f27e2fac7b38b87645bd53b7e023484c796f1c (patch) | |
tree | 3bd6bcdfc5ae274e5bbb0c28d7d23941d3dc75fa /src/main/java/com/gitblit/manager/ProjectManager.java | |
parent | 95cdba46c79d952f2c2baba937e70796674ed57e (diff) | |
download | gitblit-a1f27e2fac7b38b87645bd53b7e023484c796f1c.tar.gz gitblit-a1f27e2fac7b38b87645bd53b7e023484c796f1c.zip |
Extract ProjectManager from the GitBlit singleton
Change-Id: I93493a473e3d6ea9b2523c1913d6dc323642344d
Diffstat (limited to 'src/main/java/com/gitblit/manager/ProjectManager.java')
-rw-r--r-- | src/main/java/com/gitblit/manager/ProjectManager.java | 327 |
1 files changed, 327 insertions, 0 deletions
diff --git a/src/main/java/com/gitblit/manager/ProjectManager.java b/src/main/java/com/gitblit/manager/ProjectManager.java new file mode 100644 index 00000000..83a63102 --- /dev/null +++ b/src/main/java/com/gitblit/manager/ProjectManager.java @@ -0,0 +1,327 @@ +/* + * Copyright 2013 gitblit.com. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.gitblit.manager; + +import java.io.File; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import java.util.concurrent.ConcurrentHashMap; + +import org.eclipse.jgit.storage.file.FileBasedConfig; +import org.eclipse.jgit.util.FS; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.gitblit.IStoredSettings; +import com.gitblit.Keys; +import com.gitblit.models.ProjectModel; +import com.gitblit.models.RepositoryModel; +import com.gitblit.models.UserModel; +import com.gitblit.utils.DeepCopier; +import com.gitblit.utils.ModelUtils; +import com.gitblit.utils.ObjectCache; +import com.gitblit.utils.StringUtils; + +/** + * Project manager handles project-related functions. + * + * @author James Moger + * + */ +public class ProjectManager implements IProjectManager { + + private final Logger logger = LoggerFactory.getLogger(getClass()); + + private final Map<String, ProjectModel> projectCache = new ConcurrentHashMap<String, ProjectModel>(); + + private final ObjectCache<String> projectMarkdownCache = new ObjectCache<String>(); + + private final ObjectCache<String> projectRepositoriesMarkdownCache = new ObjectCache<String>(); + + private final IStoredSettings settings; + + private final IRuntimeManager runtimeManager; + + private final IUserManager userManager; + + private final IRepositoryManager repositoryManager; + + private FileBasedConfig projectConfigs; + + public ProjectManager( + IRuntimeManager runtimeManager, + IUserManager userManager, + IRepositoryManager repositoryManager) { + + this.settings = runtimeManager.getSettings(); + this.runtimeManager = runtimeManager; + this.userManager = userManager; + this.repositoryManager = repositoryManager; + } + + @Override + public IManager setup() { + // load and cache the project metadata + projectConfigs = new FileBasedConfig(runtimeManager.getFileOrFolder(Keys.web.projectsFile, "${baseFolder}/projects.conf"), FS.detect()); + getProjectConfigs(); + + return this; + } + + @Override + public IManager stop() { + return this; + } + + private void reloadProjectMarkdown(ProjectModel project) { + // project markdown + File pmkd = new File(repositoryManager.getRepositoriesFolder(), (project.isRoot ? "" : project.name) + "/project.mkd"); + if (pmkd.exists()) { + Date lm = new Date(pmkd.lastModified()); + if (!projectMarkdownCache.hasCurrent(project.name, lm)) { + String mkd = com.gitblit.utils.FileUtils.readContent(pmkd, "\n"); + projectMarkdownCache.updateObject(project.name, lm, mkd); + } + project.projectMarkdown = projectMarkdownCache.getObject(project.name); + } + + // project repositories markdown + File rmkd = new File(repositoryManager.getRepositoriesFolder(), (project.isRoot ? "" : project.name) + "/repositories.mkd"); + if (rmkd.exists()) { + Date lm = new Date(rmkd.lastModified()); + if (!projectRepositoriesMarkdownCache.hasCurrent(project.name, lm)) { + String mkd = com.gitblit.utils.FileUtils.readContent(rmkd, "\n"); + projectRepositoriesMarkdownCache.updateObject(project.name, lm, mkd); + } + project.repositoriesMarkdown = projectRepositoriesMarkdownCache.getObject(project.name); + } + } + + + /** + * Returns the map of project config. This map is cached and reloaded if + * the underlying projects.conf file changes. + * + * @return project config map + */ + private Map<String, ProjectModel> getProjectConfigs() { + if (projectCache.isEmpty() || projectConfigs.isOutdated()) { + + try { + projectConfigs.load(); + } catch (Exception e) { + } + + // project configs + String rootName = settings.getString(Keys.web.repositoryRootGroupName, "main"); + ProjectModel rootProject = new ProjectModel(rootName, true); + + Map<String, ProjectModel> configs = new HashMap<String, ProjectModel>(); + // cache the root project under its alias and an empty path + configs.put("", rootProject); + configs.put(rootProject.name.toLowerCase(), rootProject); + + for (String name : projectConfigs.getSubsections("project")) { + ProjectModel project; + if (name.equalsIgnoreCase(rootName)) { + project = rootProject; + } else { + project = new ProjectModel(name); + } + project.title = projectConfigs.getString("project", name, "title"); + project.description = projectConfigs.getString("project", name, "description"); + + reloadProjectMarkdown(project); + + configs.put(name.toLowerCase(), project); + } + projectCache.clear(); + projectCache.putAll(configs); + } + return projectCache; + } + + /** + * Returns a list of project models for the user. + * + * @param user + * @param includeUsers + * @return list of projects that are accessible to the user + */ + @Override + public List<ProjectModel> getProjectModels(UserModel user, boolean includeUsers) { + Map<String, ProjectModel> configs = getProjectConfigs(); + + // per-user project lists, this accounts for security and visibility + Map<String, ProjectModel> map = new TreeMap<String, ProjectModel>(); + // root project + map.put("", configs.get("")); + + for (RepositoryModel model : repositoryManager.getRepositoryModels(user)) { + String rootPath = StringUtils.getRootPath(model.name).toLowerCase(); + if (!map.containsKey(rootPath)) { + ProjectModel project; + if (configs.containsKey(rootPath)) { + // clone the project model because it's repository list will + // be tailored for the requesting user + project = DeepCopier.copy(configs.get(rootPath)); + } else { + project = new ProjectModel(rootPath); + } + map.put(rootPath, project); + } + map.get(rootPath).addRepository(model); + } + + // sort projects, root project first + List<ProjectModel> projects; + if (includeUsers) { + // all projects + projects = new ArrayList<ProjectModel>(map.values()); + Collections.sort(projects); + projects.remove(map.get("")); + projects.add(0, map.get("")); + } else { + // all non-user projects + projects = new ArrayList<ProjectModel>(); + ProjectModel root = map.remove(""); + for (ProjectModel model : map.values()) { + if (!model.isUserProject()) { + projects.add(model); + } + } + Collections.sort(projects); + projects.add(0, root); + } + return projects; + } + + /** + * Returns the project model for the specified user. + * + * @param name + * @param user + * @return a project model, or null if it does not exist + */ + @Override + public ProjectModel getProjectModel(String name, UserModel user) { + for (ProjectModel project : getProjectModels(user, true)) { + if (project.name.equalsIgnoreCase(name)) { + return project; + } + } + return null; + } + + /** + * Returns a project model for the Gitblit/system user. + * + * @param name a project name + * @return a project model or null if the project does not exist + */ + @Override + public ProjectModel getProjectModel(String name) { + Map<String, ProjectModel> configs = getProjectConfigs(); + ProjectModel project = configs.get(name.toLowerCase()); + if (project == null) { + project = new ProjectModel(name); + if (ModelUtils.isPersonalRepository(name)) { + UserModel user = userManager.getUserModel(ModelUtils.getUserNameFromRepoPath(name)); + if (user != null) { + project.title = user.getDisplayName(); + project.description = "personal repositories"; + } + } + } else { + // clone the object + project = DeepCopier.copy(project); + } + if (StringUtils.isEmpty(name)) { + // get root repositories + for (String repository : repositoryManager.getRepositoryList()) { + if (repository.indexOf('/') == -1) { + project.addRepository(repository); + } + } + } else { + // get repositories in subfolder + String folder = name.toLowerCase() + "/"; + for (String repository : repositoryManager.getRepositoryList()) { + if (repository.toLowerCase().startsWith(folder)) { + project.addRepository(repository); + } + } + } + if (project.repositories.size() == 0) { + // no repositories == no project + return null; + } + + reloadProjectMarkdown(project); + return project; + } + + /** + * Returns the list of project models that are referenced by the supplied + * repository model list. This is an alternative method exists to ensure + * Gitblit does not call getRepositoryModels(UserModel) twice in a request. + * + * @param repositoryModels + * @param includeUsers + * @return a list of project models + */ + @Override + public List<ProjectModel> getProjectModels(List<RepositoryModel> repositoryModels, boolean includeUsers) { + Map<String, ProjectModel> projects = new LinkedHashMap<String, ProjectModel>(); + for (RepositoryModel repository : repositoryModels) { + if (!includeUsers && repository.isPersonalRepository()) { + // exclude personal repositories + continue; + } + if (!projects.containsKey(repository.projectPath)) { + ProjectModel project = getProjectModel(repository.projectPath); + if (project == null) { + logger.warn(MessageFormat.format("excluding project \"{0}\" from project list because it is empty!", + repository.projectPath)); + continue; + } + projects.put(repository.projectPath, project); + // clear the repo list in the project because that is the system + // list, not the user-accessible list and start building the + // user-accessible list + project.repositories.clear(); + project.repositories.add(repository.name); + project.lastChange = repository.lastChange; + } else { + // update the user-accessible list + // this is used for repository count + ProjectModel project = projects.get(repository.projectPath); + project.repositories.add(repository.name); + if (project.lastChange.before(repository.lastChange)) { + project.lastChange = repository.lastChange; + } + } + } + return new ArrayList<ProjectModel>(projects.values()); + } +} |