/*
|
* 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 com.google.inject.Inject;
|
|
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;
|
|
@Inject
|
public ProjectManager(
|
IRuntimeManager runtimeManager,
|
IUserManager userManager,
|
IRepositoryManager repositoryManager) {
|
|
this.settings = runtimeManager.getSettings();
|
this.runtimeManager = runtimeManager;
|
this.userManager = userManager;
|
this.repositoryManager = repositoryManager;
|
}
|
|
@Override
|
public ProjectManager start() {
|
// load and cache the project metadata
|
projectConfigs = new FileBasedConfig(runtimeManager.getFileOrFolder(Keys.web.projectsFile, "${baseFolder}/projects.conf"), FS.detect());
|
getProjectConfigs();
|
|
return this;
|
}
|
|
@Override
|
public ProjectManager 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 projectPath = StringUtils.getRootPath(model.name);
|
String projectKey = projectPath.toLowerCase();
|
if (!map.containsKey(projectKey)) {
|
ProjectModel project;
|
if (configs.containsKey(projectKey)) {
|
// clone the project model because it's repository list will
|
// be tailored for the requesting user
|
project = DeepCopier.copy(configs.get(projectKey));
|
} else {
|
project = new ProjectModel(projectPath);
|
}
|
map.put(projectKey, project);
|
}
|
map.get(projectKey).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());
|
}
|
}
|