From 3a9e76b63f09a32e0b6812e18ffff00fab8e58e6 Mon Sep 17 00:00:00 2001 From: James Moger <james.moger@gitblit.com> Date: Fri, 29 Nov 2013 11:05:51 -0500 Subject: [PATCH] Flattened IGitblitManager, GitblitManager, and GitBlit classes --- src/main/java/com/gitblit/GitBlit.java | 415 +++++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 files changed, 371 insertions(+), 44 deletions(-) diff --git a/src/main/java/com/gitblit/GitBlit.java b/src/main/java/com/gitblit/GitBlit.java index d4e89b0..d736c57 100644 --- a/src/main/java/com/gitblit/GitBlit.java +++ b/src/main/java/com/gitblit/GitBlit.java @@ -15,7 +15,15 @@ */ package com.gitblit; +import java.io.BufferedReader; import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.lang.reflect.Type; +import java.text.MessageFormat; +import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.List; @@ -26,17 +34,22 @@ import javax.servlet.http.HttpServletResponse; import org.eclipse.jgit.lib.Repository; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.gitblit.Constants.AccessPermission; +import com.gitblit.Constants.AccessRestrictionType; import com.gitblit.Constants.FederationRequest; import com.gitblit.Constants.FederationToken; import com.gitblit.manager.IAuthenticationManager; import com.gitblit.manager.IFederationManager; -import com.gitblit.manager.IGitblitManager; +import com.gitblit.manager.IGitblit; import com.gitblit.manager.INotificationManager; import com.gitblit.manager.IProjectManager; import com.gitblit.manager.IRepositoryManager; import com.gitblit.manager.IRuntimeManager; import com.gitblit.manager.IUserManager; +import com.gitblit.manager.ServicesManager; import com.gitblit.models.FederationModel; import com.gitblit.models.FederationProposal; import com.gitblit.models.FederationSet; @@ -50,8 +63,19 @@ import com.gitblit.models.SearchResult; import com.gitblit.models.ServerSettings; import com.gitblit.models.ServerStatus; +import com.gitblit.models.SettingModel; import com.gitblit.models.TeamModel; import com.gitblit.models.UserModel; +import com.gitblit.utils.ArrayUtils; +import com.gitblit.utils.HttpUtils; +import com.gitblit.utils.JGitUtils; +import com.gitblit.utils.JsonUtils; +import com.gitblit.utils.ObjectCache; +import com.gitblit.utils.StringUtils; +import com.google.gson.Gson; +import com.google.gson.JsonIOException; +import com.google.gson.JsonSyntaxException; +import com.google.gson.reflect.TypeToken; /** * GitBlit is an aggregate interface delegate. It implements all the manager @@ -62,14 +86,13 @@ * @author James Moger * */ -public class GitBlit implements IRuntimeManager, - INotificationManager, - IUserManager, - IAuthenticationManager, - IRepositoryManager, - IProjectManager, - IGitblitManager, - IFederationManager { +public class GitBlit implements IGitblit { + + private final Logger logger = LoggerFactory.getLogger(getClass()); + + private final ObjectCache<Collection<GitClientApplication>> clientApplications = new ObjectCache<Collection<GitClientApplication>>(); + + private final IStoredSettings settings; private final IRuntimeManager runtimeManager; @@ -83,9 +106,9 @@ private final IProjectManager projectManager; - private final IGitblitManager gitblitManager; - private final IFederationManager federationManager; + + private final ServicesManager servicesManager; public GitBlit( IRuntimeManager runtimeManager, @@ -94,27 +117,362 @@ IAuthenticationManager authenticationManager, IRepositoryManager repositoryManager, IProjectManager projectManager, - IGitblitManager gitblitManager, IFederationManager federationManager) { + this.settings = runtimeManager.getSettings(); this.runtimeManager = runtimeManager; this.notificationManager = notificationManager; this.userManager = userManager; this.authenticationManager = authenticationManager; this.repositoryManager = repositoryManager; this.projectManager = projectManager; - this.gitblitManager = gitblitManager; this.federationManager = federationManager; + + this.servicesManager = new ServicesManager(this); } @Override public GitBlit start() { + loadSettingModels(runtimeManager.getSettingsModel()); + logger.info("Starting services manager..."); + servicesManager.start(); return this; } @Override public GitBlit stop() { + servicesManager.stop(); return this; + } + + /* + * IGITBLIT + */ + + /** + * Creates a personal fork of the specified repository. The clone is view + * restricted by default and the owner of the source repository is given + * access to the clone. + * + * @param repository + * @param user + * @return the repository model of the fork, if successful + * @throws GitBlitException + */ + @Override + public RepositoryModel fork(RepositoryModel repository, UserModel user) throws GitBlitException { + String cloneName = MessageFormat.format("{0}/{1}.git", user.getPersonalPath(), StringUtils.stripDotGit(StringUtils.getLastPathElement(repository.name))); + String fromUrl = MessageFormat.format("file://{0}/{1}", repositoryManager.getRepositoriesFolder().getAbsolutePath(), repository.name); + + // clone the repository + try { + JGitUtils.cloneRepository(repositoryManager.getRepositoriesFolder(), cloneName, fromUrl, true, null); + } catch (Exception e) { + throw new GitBlitException(e); + } + + // create a Gitblit repository model for the clone + RepositoryModel cloneModel = repository.cloneAs(cloneName); + // owner has REWIND/RW+ permissions + cloneModel.addOwner(user.username); + repositoryManager.updateRepositoryModel(cloneName, cloneModel, false); + + // add the owner of the source repository to the clone's access list + if (!ArrayUtils.isEmpty(repository.owners)) { + for (String owner : repository.owners) { + UserModel originOwner = userManager.getUserModel(owner); + if (originOwner != null) { + originOwner.setRepositoryPermission(cloneName, AccessPermission.CLONE); + updateUserModel(originOwner.username, originOwner, false); + } + } + } + + // grant origin's user list clone permission to fork + List<String> users = repositoryManager.getRepositoryUsers(repository); + List<UserModel> cloneUsers = new ArrayList<UserModel>(); + for (String name : users) { + if (!name.equalsIgnoreCase(user.username)) { + UserModel cloneUser = userManager.getUserModel(name); + if (cloneUser.canClone(repository)) { + // origin user can clone origin, grant clone access to fork + cloneUser.setRepositoryPermission(cloneName, AccessPermission.CLONE); + } + cloneUsers.add(cloneUser); + } + } + userManager.updateUserModels(cloneUsers); + + // grant origin's team list clone permission to fork + List<String> teams = repositoryManager.getRepositoryTeams(repository); + List<TeamModel> cloneTeams = new ArrayList<TeamModel>(); + for (String name : teams) { + TeamModel cloneTeam = userManager.getTeamModel(name); + if (cloneTeam.canClone(repository)) { + // origin team can clone origin, grant clone access to fork + cloneTeam.setRepositoryPermission(cloneName, AccessPermission.CLONE); + } + cloneTeams.add(cloneTeam); + } + userManager.updateTeamModels(cloneTeams); + + // add this clone to the cached model + repositoryManager.addToCachedRepositoryList(cloneModel); + return cloneModel; + } + + /** + * Updates the TeamModel object for the specified name. + * + * @param teamname + * @param team + * @param isCreate + */ + @Override + public void updateTeamModel(String teamname, TeamModel team, boolean isCreate) + throws GitBlitException { + if (!teamname.equalsIgnoreCase(team.name)) { + if (userManager.getTeamModel(team.name) != null) { + throw new GitBlitException(MessageFormat.format( + "Failed to rename ''{0}'' because ''{1}'' already exists.", teamname, + team.name)); + } + } + if (!userManager.updateTeamModel(teamname, team)) { + throw new GitBlitException(isCreate ? "Failed to add team!" : "Failed to update team!"); + } + } + + /** + * Adds/updates a complete user object keyed by username. This method allows + * for renaming a user. + * + * @see IUserService.updateUserModel(String, UserModel) + * @param username + * @param user + * @param isCreate + * @throws GitBlitException + */ + @Override + public void updateUserModel(String username, UserModel user, boolean isCreate) + throws GitBlitException { + if (!username.equalsIgnoreCase(user.username)) { + if (userManager.getUserModel(user.username) != null) { + throw new GitBlitException(MessageFormat.format( + "Failed to rename ''{0}'' because ''{1}'' already exists.", username, + user.username)); + } + + // rename repositories and owner fields for all repositories + for (RepositoryModel model : repositoryManager.getRepositoryModels(user)) { + if (model.isUsersPersonalRepository(username)) { + // personal repository + model.addOwner(user.username); + String oldRepositoryName = model.name; + model.name = user.getPersonalPath() + model.name.substring(model.projectPath.length()); + model.projectPath = user.getPersonalPath(); + repositoryManager.updateRepositoryModel(oldRepositoryName, model, false); + } else if (model.isOwner(username)) { + // common/shared repo + model.addOwner(user.username); + repositoryManager.updateRepositoryModel(model.name, model, false); + } + } + } + if (!userManager.updateUserModel(username, user)) { + throw new GitBlitException(isCreate ? "Failed to add user!" : "Failed to update user!"); + } + } + + /** + * Returns a list of repository URLs and the user access permission. + * + * @param request + * @param user + * @param repository + * @return a list of repository urls + */ + @Override + public List<RepositoryUrl> getRepositoryUrls(HttpServletRequest request, UserModel user, RepositoryModel repository) { + if (user == null) { + user = UserModel.ANONYMOUS; + } + String username = StringUtils.encodeUsername(UserModel.ANONYMOUS.equals(user) ? "" : user.username); + + List<RepositoryUrl> list = new ArrayList<RepositoryUrl>(); + // http/https url + if (settings.getBoolean(Keys.git.enableGitServlet, true)) { + AccessPermission permission = user.getRepositoryPermission(repository).permission; + if (permission.exceeds(AccessPermission.NONE)) { + list.add(new RepositoryUrl(getRepositoryUrl(request, username, repository), permission)); + } + } + + // git daemon url + String gitDaemonUrl = servicesManager.getGitDaemonUrl(request, user, repository); + if (!StringUtils.isEmpty(gitDaemonUrl)) { + AccessPermission permission = servicesManager.getGitDaemonAccessPermission(user, repository); + if (permission.exceeds(AccessPermission.NONE)) { + list.add(new RepositoryUrl(gitDaemonUrl, permission)); + } + } + + // add all other urls + // {0} = repository + // {1} = username + for (String url : settings.getStrings(Keys.web.otherUrls)) { + if (url.contains("{1}")) { + // external url requires username, only add url IF we have one + if (!StringUtils.isEmpty(username)) { + list.add(new RepositoryUrl(MessageFormat.format(url, repository.name, username), null)); + } + } else { + // external url does not require username + list.add(new RepositoryUrl(MessageFormat.format(url, repository.name), null)); + } + } + return list; + } + + protected String getRepositoryUrl(HttpServletRequest request, String username, RepositoryModel repository) { + StringBuilder sb = new StringBuilder(); + sb.append(HttpUtils.getGitblitURL(request)); + sb.append(Constants.R_PATH); + sb.append(repository.name); + + // inject username into repository url if authentication is required + if (repository.accessRestriction.exceeds(AccessRestrictionType.NONE) + && !StringUtils.isEmpty(username)) { + sb.insert(sb.indexOf("://") + 3, username + "@"); + } + return sb.toString(); + } + + + /** + * Returns the list of custom client applications to be used for the + * repository url panel; + * + * @return a collection of client applications + */ + @Override + public Collection<GitClientApplication> getClientApplications() { + // prefer user definitions, if they exist + File userDefs = new File(runtimeManager.getBaseFolder(), "clientapps.json"); + if (userDefs.exists()) { + Date lastModified = new Date(userDefs.lastModified()); + if (clientApplications.hasCurrent("user", lastModified)) { + return clientApplications.getObject("user"); + } else { + // (re)load user definitions + try { + InputStream is = new FileInputStream(userDefs); + Collection<GitClientApplication> clients = readClientApplications(is); + is.close(); + if (clients != null) { + clientApplications.updateObject("user", lastModified, clients); + return clients; + } + } catch (IOException e) { + logger.error("Failed to deserialize " + userDefs.getAbsolutePath(), e); + } + } + } + + // no user definitions, use system definitions + if (!clientApplications.hasCurrent("system", new Date(0))) { + try { + InputStream is = getClass().getResourceAsStream("/clientapps.json"); + Collection<GitClientApplication> clients = readClientApplications(is); + is.close(); + if (clients != null) { + clientApplications.updateObject("system", new Date(0), clients); + } + } catch (IOException e) { + logger.error("Failed to deserialize clientapps.json resource!", e); + } + } + + return clientApplications.getObject("system"); + } + + private Collection<GitClientApplication> readClientApplications(InputStream is) { + try { + Type type = new TypeToken<Collection<GitClientApplication>>() { + }.getType(); + InputStreamReader reader = new InputStreamReader(is); + Gson gson = JsonUtils.gson(); + Collection<GitClientApplication> links = gson.fromJson(reader, type); + return links; + } catch (JsonIOException e) { + logger.error("Error deserializing client applications!", e); + } catch (JsonSyntaxException e) { + logger.error("Error deserializing client applications!", e); + } + return null; + } + + /** + * Parse the properties file and aggregate all the comments by the setting + * key. A setting model tracks the current value, the default value, the + * description of the setting and and directives about the setting. + * + * @return Map<String, SettingModel> + */ + private void loadSettingModels(ServerSettings settingsModel) { + try { + // Read bundled Gitblit properties to extract setting descriptions. + // This copy is pristine and only used for populating the setting + // models map. + InputStream is = getClass().getResourceAsStream("/reference.properties"); + BufferedReader propertiesReader = new BufferedReader(new InputStreamReader(is)); + StringBuilder description = new StringBuilder(); + SettingModel setting = new SettingModel(); + String line = null; + while ((line = propertiesReader.readLine()) != null) { + if (line.length() == 0) { + description.setLength(0); + setting = new SettingModel(); + } else { + if (line.charAt(0) == '#') { + if (line.length() > 1) { + String text = line.substring(1).trim(); + if (SettingModel.CASE_SENSITIVE.equals(text)) { + setting.caseSensitive = true; + } else if (SettingModel.RESTART_REQUIRED.equals(text)) { + setting.restartRequired = true; + } else if (SettingModel.SPACE_DELIMITED.equals(text)) { + setting.spaceDelimited = true; + } else if (text.startsWith(SettingModel.SINCE)) { + try { + setting.since = text.split(" ")[1]; + } catch (Exception e) { + setting.since = text; + } + } else { + description.append(text); + description.append('\n'); + } + } + } else { + String[] kvp = line.split("=", 2); + String key = kvp[0].trim(); + setting.name = key; + setting.defaultValue = kvp[1].trim(); + setting.currentValue = setting.defaultValue; + setting.description = description.toString().trim(); + settingsModel.add(setting); + description.setLength(0); + setting = new SettingModel(); + } + } + } + propertiesReader.close(); + } catch (NullPointerException e) { + logger.error("Failed to find resource copy of gitblit.properties"); + } catch (IOException e) { + logger.error("Failed to load resource copy of gitblit.properties"); + } } /* @@ -719,36 +1077,5 @@ @Override public boolean deletePendingFederationProposal(FederationProposal proposal) { return federationManager.deletePendingFederationProposal(proposal); - } - - /* - * GITBLIT MANAGER - */ - - @Override - public RepositoryModel fork(RepositoryModel repository, UserModel user) throws GitBlitException { - return gitblitManager.fork(repository, user); - } - - @Override - public void updateTeamModel(String teamname, TeamModel team, boolean isCreate) - throws GitBlitException { - gitblitManager.updateTeamModel(teamname, team, isCreate); - } - - @Override - public void updateUserModel(String username, UserModel user, boolean isCreate) - throws GitBlitException { - gitblitManager.updateUserModel(username, user, isCreate); - } - - @Override - public List<RepositoryUrl> getRepositoryUrls(HttpServletRequest request, UserModel user, RepositoryModel repository) { - return gitblitManager.getRepositoryUrls(request, user, repository); - } - - @Override - public Collection<GitClientApplication> getClientApplications() { - return gitblitManager.getClientApplications(); } } -- Gitblit v1.9.1