James Moger
2013-11-20 a1f27e2fac7b38b87645bd53b7e023484c796f1c
Extract ProjectManager from the GitBlit singleton

Change-Id: I93493a473e3d6ea9b2523c1913d6dc323642344d
1 files added
3 files modified
597 ■■■■■ changed files
src/main/java/com/gitblit/DaggerModule.java 12 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/GitBlit.java 256 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/manager/IProjectManager.java 2 ●●● patch | view | raw | blame | history
src/main/java/com/gitblit/manager/ProjectManager.java 327 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/DaggerModule.java
@@ -29,6 +29,7 @@
import com.gitblit.manager.ISessionManager;
import com.gitblit.manager.IUserManager;
import com.gitblit.manager.NotificationManager;
import com.gitblit.manager.ProjectManager;
import com.gitblit.manager.RepositoryManager;
import com.gitblit.manager.RuntimeManager;
import com.gitblit.manager.SessionManager;
@@ -125,8 +126,15 @@
                userManager);
    }
    @Provides @Singleton IProjectManager provideProjectManager() {
        return gitblit;
    @Provides @Singleton IProjectManager provideProjectManager(
            IRuntimeManager runtimeManager,
            IUserManager userManager,
            IRepositoryManager repositoryManager) {
        return new ProjectManager(
                runtimeManager,
                userManager,
                repositoryManager);
    }
    @Provides @Singleton IGitblitManager provideGitblitManager() {
src/main/java/com/gitblit/GitBlit.java
@@ -32,10 +32,8 @@
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 java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
@@ -50,8 +48,6 @@
import org.apache.wicket.resource.ContextRelativeResource;
import org.apache.wicket.util.resource.ResourceStreamNotFoundException;
import org.eclipse.jgit.storage.file.FileBasedConfig;
import org.eclipse.jgit.util.FS;
import org.slf4j.Logger;
import com.gitblit.Constants.AccessPermission;
@@ -77,7 +73,6 @@
import com.gitblit.models.FederationProposal;
import com.gitblit.models.FederationSet;
import com.gitblit.models.GitClientApplication;
import com.gitblit.models.ProjectModel;
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.RepositoryUrl;
import com.gitblit.models.ServerSettings;
@@ -86,12 +81,10 @@
import com.gitblit.models.UserModel;
import com.gitblit.utils.ArrayUtils;
import com.gitblit.utils.ContainerUtils;
import com.gitblit.utils.DeepCopier;
import com.gitblit.utils.FederationUtils;
import com.gitblit.utils.HttpUtils;
import com.gitblit.utils.JGitUtils;
import com.gitblit.utils.JsonUtils;
import com.gitblit.utils.ModelUtils;
import com.gitblit.utils.ObjectCache;
import com.gitblit.utils.StringUtils;
import com.gitblit.wicket.GitblitWicketFilter;
@@ -117,8 +110,7 @@
 */
@WebListener
public class GitBlit extends DaggerContextListener
                     implements IProjectManager,
                                IFederationManager,
                     implements IFederationManager,
                                IGitblitManager {
    private static GitBlit gitblit;
@@ -138,15 +130,7 @@
    private final Map<String, FederationModel> federationPullResults = new ConcurrentHashMap<String, FederationModel>();
    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 IStoredSettings settings;
    private FileBasedConfig projectConfigs;
    private FanoutService fanoutService;
@@ -442,237 +426,6 @@
        }
    }
    private void reloadProjectMarkdown(ProjectModel project) {
        // project markdown
        File pmkd = new File(getManager(IRepositoryManager.class).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(getManager(IRepositoryManager.class).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 : getManager(IRepositoryManager.class).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 = getManager(IUserManager.class).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 : getManager(IRepositoryManager.class).getRepositoryList()) {
                if (repository.indexOf('/') == -1) {
                    project.addRepository(repository);
                }
            }
        } else {
            // get repositories in subfolder
            String folder = name.toLowerCase() + "/";
            for (String repository : getManager(IRepositoryManager.class).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());
    }
    /**
     * Returns Gitblit's scheduled executor service for scheduling tasks.
@@ -1173,7 +926,7 @@
                        getManager(IUserManager.class),
                        getManager(ISessionManager.class),
                        getManager(IRepositoryManager.class),
                        this,
                        getManager(IProjectManager.class),
                        this,
                        this);
                gitDaemon = new GitDaemon(gitblit);
@@ -1242,14 +995,11 @@
        startManager(injector, IUserManager.class);
        startManager(injector, ISessionManager.class);
        startManager(injector, IRepositoryManager.class);
        startManager(injector, IProjectManager.class);
        logger.info("Gitblit base folder     = " + baseFolder.getAbsolutePath());
        loadSettingModels(runtime.getSettingsModel());
        // load and cache the project metadata
        projectConfigs = new FileBasedConfig(runtime.getFileOrFolder(Keys.web.projectsFile, "${baseFolder}/projects.conf"), FS.detect());
        getProjectConfigs();
        if (true/*startFederation*/) {
            configureFederation();
src/main/java/com/gitblit/manager/IProjectManager.java
@@ -21,7 +21,7 @@
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.UserModel;
public interface IProjectManager {
public interface IProjectManager extends IManager {
    /**
     * Returns a list of project models for the user.
src/main/java/com/gitblit/manager/ProjectManager.java
New file
@@ -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());
    }
}